Skip to content

Instantly share code, notes, and snippets.

@samneggs
Created August 29, 2023 23:41
Show Gist options
  • Save samneggs/741a1835ea3c50b91547da89fa2b7ef5 to your computer and use it in GitHub Desktop.
Save samneggs/741a1835ea3c50b91547da89fa2b7ef5 to your computer and use it in GitHub Desktop.
Astro Miner game on PI Pico in MicroPython
# AstroMiner
from lcd_1_8 import LCD_1inch8
import machine
from machine import Pin, PWM
from uctypes import addressof
from time import sleep, ticks_us, ticks_diff, ticks_ms, sleep_ms
import gc, _thread, array
from sys import exit
from micropython import const
from random import randint
from math import sin,cos,tan,radians,sqrt
from rp2 import bootsel_button as RESET_PB
from cavetiles import map_texture as MAP
from pot import Pot
from astro_sound import Astro_Sound
# Tile set https://rottingpixels.itch.io/cave-platformer-tileset-16x16free
with open ('cavetextures.bin', "rb") as file:
TILES = file.read()
file.close()
MAXSCREEN_X = const(160)
MAXSCREEN_Y = const(128)
SCALE = const(13)
NUM_STARS = const(100)
NUM_MISSILES= const(10)
NUM_ENEMY = const(7)
NUM_EXPLODE = const(11)
NUM_STRUCT = const(10)
WHITE = const(0xffff)
MAP_WIDTH = const(6)
MAP_HEIGHT = const(6)
BORDER = const(90)
SHADOW_MASK = const(0b0111_1111)
SHIP_SIZE = const(2)
PLAYER_PARAMS = const(20)
X = const(0)
Y = const(1)
DEG = const(2)
VX = const(3)
VY = const(4)
AX = const(5)
AY = const(6)
M = const(7)
MAP_X = const(8)
MAP_Y = const(9)
SHIELD= const(10)
OLD_X = const(11)
OLD_Y = const(12)
MISL = const(13)
S_EXP = const(14)
SEGS = const(15)
TYPE = const(16)
BUTTON= const(17)
EXPLODE_PARAMS = const(20)
X1 = const(0)
X2 = const(1)
Y1 = const(2)
Y2 = const(3)
DX = const(4)
DY = const(5)
E_STR = const(6)
#S_EXP = const(14)
#SEGS = const(15)
#TYPE = const(16)
MISSILE_PARAMS = const(15)
#X,Y
MISS_LIFE = const(2)
#VX,VY
GAME_PARAMS = const(10)
FPS = const(0)
LIVES = const(1)
DEBUG = const(2)
M_MAP = const(3)
G_OVER= const(4)
STRUCT_PARAMS = const(7) # exact
#X,Y, DEG
S_TYPE = const(3)
S_HITS = const(4)
ST_EXP = const(5)
E_START = const(6) # start of exp index
SHIP_POINTS = const(16)
SHIP_SEGMENTS = const(30)
DEG_LIST = []
RADIUS_LIST = []
DEG_LIST.extend([0, 306, 180, 54, 0, 333, 349, 333, 189, 333, 0, 27, 11, 27, 171, 27 ]+[0]*14) # starcastle ship
RADIUS_LIST.extend([0, 17, 8, 17, 0, 4, 10, 9, 12, 4, 0, 4, 10, 9, 12, 4]+[0]*14)
DEG_LIST.extend( [172, 188, 207, 219, 233, 236, 225, 270, 270, 256, 330, 270, 270, 351, 9, 90, 90, 30, 104, 90, 90, 135, 124, 127, 141, 153, 172]+[0]*3 )
RADIUS_LIST.extend( [14, 14, 13, 13, 10, 7, 3, 2, 8, 8, 16, 8, 2, 12, 12, 2, 8, 16, 8, 8, 2, 3, 7, 10, 13, 13, 14]+[0]*3 ) # enterprise
DEG_LIST.extend([90, 56, 67, 45, 45, 0, 323, 292, 248, 217, 180, 135, 135, 113, 124, 90]+[0]*14 ) # enterprise2
RADIUS_LIST.extend([2, 7, 15, 8, 3, 8, 10, 11, 11, 10, 8, 3, 8, 15, 7, 2]+[0]*14 )
DEG_LIST.extend( [180, 320, 323, 37, 40, 180]+[0]*24 ) #asteroids ship
RADIUS_LIST.extend( [14, 16, 10, 10, 16, 14]+[0]*24 )
DEG_LIST.extend([98, 82, 278, 262, 98, 79, 108, 45, 225, 288, 259, 278, 262, 281, 252, 315, 135, 72, 101, 82]+[0]*10 )
RADIUS_LIST.extend([14, 14, 14, 14, 14, 10, 6, 3, 3, 6, 10, 14, 14, 10, 6, 3, 3, 6, 10, 14] +[0]*10) # dense scaffold tower
DEG_LIST.extend([248, 292, 315, 338, 0, 27, 56, 124, 153, 180, 202, 225, 248]+[0]*17) # oval tank
RADIUS_LIST.extend([11, 11, 11, 11, 10, 9, 7, 7, 9, 10, 11, 11, 11]+[0]*17 )
DEG_LIST.extend( [37, 270, 143, 37, 22, 270, 22, 50, 60, 37, 120, 143, 60, 120]+[0]*16) #pyramid
RADIUS_LIST.extend( [10, 8, 10, 10, 11, 8, 11, 16, 16, 10, 16, 10, 16, 16]+[0]*16)
SHIP_SEG_NUMS = bytearray([15,26,15,5,19,12,14,0,0,0])
SHIP_DEG = array.array('H',DEG_LIST)
SHIP_RADIUS = array.array('H',RADIUS_LIST)
del DEG_LIST, RADIUS_LIST
STRUCT_LOC = array.array('i',(400,115,90,0,3,0,0, # X,Y,DEG,TYPE,HITS,EXP,INDEX
430,115,90,1,3,0,0,
460,115,00,2,3,0,0,
490,115,90,3,3,0,0,
530, 42,00,4,3,0,0,
530, 70,00,4,3,0,0,
530, 88,00,4,3,0,0,
530,115,00,4,3,0,0,
560,125,00,5,3,0,0,
590,115,00,6,3,0,0,
630,115,00,5,3,0,0,
000,000,00,0,0,0,0))
dark_grey = const(0x4711) #pico_display.create_pen(20, 40, 60)
black = const(0) #pico_display.create_pen(0, 0, 0)
dark_green = const(0x0024) #pico_display.create_pen(32, 128, 0)
lt_blue = const(0x3e44) #pico_display.create_pen(66, 133, 244)
lt_yellow = const(0x33ff) #pico_display.create_pen(255, 229, 153)
lt_green = const(0xe007) #pico_display.create_pen(0, 255, 0)
brown = const(0xe079) #pico_display.create_pen(120, 63, 4)
white = const(0xffff) #pico_display.create_pen(255, 255, 255)
sky_blue = const(0x1f66) #pico_display.create_pen(96, 192, 255)
blue = const(0x1f00)
red = const(0xf8) # rrrrr_gggggg_bbbbb
lt_brown = const(0x52de) # ggg_bbbbb_rrrrr_ggg
MOD_TILES = bytearray(16)
SHADOW = array.array('i',(0,0,0,0,0,0))
colors3 = array.array('H',(0x0,0xffff,0x0,0x4eeb,0xadbb,0x8a8a,0x772,0x5b1,0x8759,0xa570,0xe320,0x93f6,0xa8fe,0x3fd,0x2ca4,
0x41fb,0xa8bb,0x685a,0xc641,0xc89e,0x715d,0x515,0x2c34,0xc71b,0x2923,0x8722,0xb826,0x411,0xfb24,
0xf82a,0xac11,0xd079,0x53ba,0x2641,0xa438,0x8210,0x0))
INTERP0_ACCUM0 = const(0x080)
INTERP0_ACCUM1 = const(0x084)
INTERP0_BASE0 = const(0x088)
INTERP0_BASE1 = const(0x08c)
INTERP0_BASE2 = const(0x090)
INTERP0_POP_FULL = const(0x09c)
INTERP0_CTRL_LANE0 = const(0x0ac)
INTERP0_CTRL_LANE1 = const(0x0b0)
INTERP1_ACCUM0 = const(0x0c0)
INTERP1_ACCUM1 = const(0x0c4)
INTERP1_BASE0 = const(0x0c8)
INTERP1_BASE1 = const(0x0cc)
INTERP1_BASE2 = const(0x0d0)
INTERP1_POP_FULL = const(0x0dc)
INTERP1_CTRL_LANE0 = const(0x0ec)
INTERP1_CTRL_LANE1 = const(0x0f0)
@micropython.viper
def interp_setup(map_width_bits:int, map_height_bits:int, uv_fractional_bits:int, tile_width_bits:int, tile_height_bits:int):
# texture width
sio=ptr32(0xd000_0000) # shift raw mask MSB LSB
sio[INTERP0_CTRL_LANE0//4] = 16 | 1<<18 | (map_width_bits-1)<<10 | 0<<5
sio[INTERP0_CTRL_LANE1//4] = 16-map_width_bits | 1<<18 | (map_width_bits+map_height_bits-1)<<10 | map_width_bits<<5
sio[INTERP1_CTRL_LANE0//4] = 16-4 | 1<<18 | 3<<10 | 0<<5 #tile
sio[INTERP1_CTRL_LANE1//4] = 16-8 | 1<<18 | 7<<10 | 4<<5
du = 65536 // 16
dv = 0
sio[INTERP0_BASE0//4] = du
sio[INTERP0_BASE1//4] = dv
sio[INTERP1_BASE0//4] = du
sio[INTERP1_BASE1//4] = dv
SCREEN_CTL = const(0)
COLORS_CTL = const(4)
TILES_CTL = const(8)
MAP_CTL = const(12)
PLAYER_CTL = const(16)
SIO_CTL = const(20)
@micropython.asm_thumb
def draw_tiles_asm(r0):
ldr(r1,[r0,PLAYER_CTL])
ldr(r1,[r1,MAP_X*4]) # r1 : u = player_ptr[MAP_X]
mov(r2,0) # r2 : y
label(LOOP_Y)
ldr(r3,[r0,PLAYER_CTL])
ldr(r3,[r3,MAP_Y*4]) # player_ptr[MAP_Y]
asr(r3,r3,SCALE) # player_ptr[MAP_Y]>>SCALE
add(r3,r3,r2) # player_ptr[MAP_Y]>>SCALE)+y
lsl(r3,r3,12) # r3 : v = ((player_ptr[MAP_Y]>>SCALE)+y)<<12
ldr(r4,[r0,SIO_CTL]) # 0xd000_0080
str(r1,[r4,0]) # sio[INTERP0_ACCUM0//4] = u
str(r1,[r4,0x40]) # sio[INTERP1_ACCUM0//4] = u
str(r3,[r4,0x4]) # sio[INTERP0_ACCUM1//4] = v
str(r3,[r4,0x44]) # sio[INTERP1_ACCUM1//4] = v
mov(r5,MAXSCREEN_X)
mul(r5,r2) # r5 : y_offset = y * MAXSCREEN_X
mov(r6,0) # r6 : x
label(LOOP_X)
ldr(r4,[r0,SIO_CTL]) # 0xd000_0080
ldr(r7,[r4,0x1c]) # t = sio[INTERP0_POP_FULL//4] # map
ldr(r3,[r0,MAP_CTL]) # map_ptr
add(r7,r3,r7) # [t]
ldrb(r3,[r7,0]) # map_ptr[t] tile number
data(2,0b010001_10__1__0_111_000) #MOV(r8,r7) # tile address
data(2,0b010001_10__1__0_011_001) #MOV(r9,r3) # tile number, with mask
mov(r7,SHADOW_MASK)
and_(r3,r7) # mask high bit
data(2,0b010001_10__1__0_011_010) #MOV(r10,r3) # tile number, unmasked
lsl(r3,r3,8) # map_ptr[t]<<8
ldr(r7,[r4,0x5c]) # c = sio[INTERP1_POP_FULL//4] # tile
add(r3,r3,r7) # c+(map_ptr[t]<<8)
ldr(r7,[r0,TILES_CTL]) # tiles_ptr
add(r3,r3,r7)
ldrb(r7,[r3,0]) # tiles_ptr[c+(map_ptr[t]<<8)]
add(r7,r7,r7) # double for ldrh
ldr(r3,[r0,COLORS_CTL]) # colors_ptr
add(r3,r3,r7) #
ldrh(r3,[r3,0]) # r3 : pixel = colors_ptr[tiles_ptr[c+(map_ptr[t]<<8)]]
ldr(r7,[r0,SCREEN_CTL]) #
add(r4,r5,r6) # y_offset+x
add(r4,r4,r4) # double for strh
add(r7,r7,r4) # output[y_offset+x]
ldrh(r4,[r7,0]) # t4 = shadow pixel
data(2,0b010001_01__1__1_010_001) #CMP(r9,r10)
beq(SHOW)
cmp(r4,0) # check for shadow mask
beq(NO_SHOW)
label(SHOW)
eor(r3,r4)#####
strh(r3,[r7,0]) # output[y_offset+x] = pixel
data(2,0b010001_10__0__1_000_111) #MOV(r7,r8) # tile address
data(2,0b010001_10__0__1_010_011) #MOV(r3,r10) # tile number, unmasked
strb(r3,[r7,0]) # remove shadow
label(NO_SHOW)
add(r6,r6,1)
cmp(r6,MAXSCREEN_X)
blt(LOOP_X)
add(r2,r2,1)
cmp(r2,MAXSCREEN_Y)
blt(LOOP_Y)
label(EXIT)
char_map=array.array('b',(
0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00, # U+0030 (0)
0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00, # U+0031 (1)
0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00, # U+0032 (2)
0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00, # U+0033 (3)
0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00, # U+0034 (4)
0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00, # U+0035 (5)
0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00, # U+0036 (6)
0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00, # U+0037 (7)
0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00, # U+0038 (8)
0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00)) # U+0039 (9)
@micropython.viper
def show_num_viper(num:int,x_offset:int,y_offset:int,color:int):
char_ptr = ptr8(char_map)
screen_ptr = ptr16(LCD.buffer)
size = 1 # 1,2,3
char = 0
offset = MAXSCREEN_X*y_offset+x_offset
while num > 0:
total = num//10
digit = num - (total * 10)
num = total
for y in range(8):
row_data = char_ptr[digit*8+y]
for x in range(8):
if row_data & (1<<x) > 0:
addr = size*y*MAXSCREEN_X+x-(char*8)+offset
screen_ptr[addr] = color
if size>1:
screen_ptr[MAXSCREEN_X+addr] = color
if size>2:
screen_ptr[2*MAXSCREEN_X+addr] = color
char += 1
@micropython.viper
def read_pot():
global EXIT
player = ptr32(PLAYER)
isin = ptr32(ISIN)
icos = ptr32(ICOS)
game = ptr32(GAME)
pot_ary = ptr32(pot.ary)
x_inc = pot_ary[0]
y_inc = pot_ary[1]
player[BUTTON] = pot_ary[2]
if y_inc>0:
y_inc=0
game[M_MAP] += 1
else:
game[M_MAP] = 0
player[DEG] += x_inc
if player[DEG] >= 360: player[DEG] -= 360
if player[DEG] < 0 : player[DEG] += 360
deg = player[DEG]
x = (200*y_inc * int(icos[deg]))>>SCALE
y = (200*y_inc * int(isin[deg]))>>SCALE
player[VX] += x
player[VY] += y
def init_player():
global PLAYER , SHIP_COORDS, MISSILES, DRAW_TILES_CTL, EXPLODE
PLAYER = array.array('i',0 for _ in range(PLAYER_PARAMS))
EXPLODE = array.array('i',0 for _ in range(EXPLODE_PARAMS*NUM_EXPLODE*SHIP_SEGMENTS))
SHIP_COORDS = array.array('i',0 for _ in range((NUM_ENEMY * SHIP_SEGMENTS + SHIP_SEGMENTS)*2))
MISSILES = array.array('i',0 for _ in range(MISSILE_PARAMS*NUM_MISSILES))
DRAW_TILES_CTL = array.array('I',(addressof(LCD.buffer),addressof(colors3),addressof(TILES)-(16*16),
addressof(MAP),addressof(PLAYER),0xd000_0080))
PLAYER[X] = 80<<SCALE
PLAYER[Y] = 64<<SCALE
PLAYER[DEG]= 180
PLAYER[VX] = 0
PLAYER[VY] = 0 #23000
PLAYER[AX] = 0
PLAYER[AY] = 0
PLAYER[M] = 0
PLAYER[TYPE] = 0
PLAYER[SEGS] = SHIP_SEG_NUMS[PLAYER[TYPE]]
PLAYER[MAP_X] = 0<<SCALE
PLAYER[MAP_Y] = 30<<SCALE
def init_imath(): #integer math
global ISIN,ICOS
ISIN = array.array('i', int(sin(radians(i)) * (1 << SCALE)) for i in range(360))
ICOS = array.array('i', int(cos(radians(i)) * (1 << SCALE)) for i in range(360))
def init_game():
global GAME, FPS_ARRY
GAME = array.array('i',0 for _ in range(GAME_PARAMS))
FPS_ARRY = bytearray(35)
GAME[FPS] = 0
GAME[LIVES] = 3
GAME[G_OVER] = 0
@micropython.viper
def init_struct():
segments = ptr8(SHIP_SEG_NUMS)
struct = ptr32(STRUCT_LOC)
isin = ptr32(ISIN)
icos = ptr32(ICOS)
deg = ptr16(SHIP_DEG)
radius = ptr16(SHIP_RADIUS)
coords = ptr32(SHIP_COORDS)
for index2 in range(0,NUM_STRUCT):
i2 = index2 * STRUCT_PARAMS
ship_type = struct[i2 + S_TYPE]
index_coords = index2 * SHIP_SEGMENTS * 2 + 60
segs = segments[ship_type] + 2
for index in range(ship_type*SHIP_SEGMENTS,ship_type*SHIP_SEGMENTS+segs): # calc ship points
i = index * 2
pt_deg = deg[index] + struct[i2 + DEG]
if pt_deg >= 360: pt_deg -= 360
if pt_deg < 0 : pt_deg += 360
coords[index_coords] = ((radius[index] * SHIP_SIZE * icos[pt_deg])>>14)
coords[index_coords+1] = ((radius[index] * SHIP_SIZE * isin[pt_deg])>>14)
index_coords += 2
@micropython.viper
def init_tiles():
back_map = ptr8(MAP)
tiles = ptr8(TILES)
#rand_percent = int(randint(0,100))
for i in range(4): # 5
rand_location = int(randint(0,16*16))
rand_color = int(randint(0,2))
tiles[16*16*45+rand_location] = 4 + rand_color
for i in range(64*64): # MAP size
continue
back_map[i] = back_map[i] | 0b1000_0000 # make invis
@micropython.viper
def init_missile():
miss = ptr32(MISSILES)
player = ptr32(PLAYER)
isin = ptr32(ISIN)
icos = ptr32(ICOS)
game = ptr32(GAME)
player[MISL] = 0
if game[LIVES] < 1: return
x = player[X]
y = player[Y]
vx = player[VX]
vy = player[VY]
deg = player[DEG] + 180
if deg >= 360: deg -= 360
for index in range(NUM_MISSILES):
i = index * MISSILE_PARAMS
if miss[i + MISS_LIFE] == 0:
miss[i + MISS_LIFE] = 200
miss[i + X] = icos[deg]*0 + x
miss[i + Y] = isin[deg]*0 + y
abs_vx = (vx + icos[deg])>>1
abs_vy = (vy + isin[deg])>>1
abs_vx = abs_vx if abs_vx > 0 else abs_vx*-1
abs_vy = abs_vy if abs_vy > 0 else abs_vy*-1
if abs_vx < 5000 and abs_vy < 5000 or 1:
miss[i + VX] = (icos[deg])<<1
miss[i + VY] = (isin[deg])<<1
else:
miss[i + VX] = (vx + icos[deg])<<1 # buggy
miss[i + VY] = (vy + isin[deg])<<1
ASTRO_SOUND.reset_fire()
ASTRO_SOUND.SOUND = int(ASTRO_SOUND.SOUND) | 1<<0
return
def reset_player():
PLAYER[MAP_X] = 5<<SCALE
PLAYER[MAP_Y] = 5<<SCALE
PLAYER[DEG] = 180
PLAYER[VX] = 0
PLAYER[VY] = 0
@micropython.viper
def init_explode(ship_type:int,degrees:int,struct_num:int):
player = ptr32(PLAYER)
deg = ptr16(SHIP_DEG)
radius = ptr16(SHIP_RADIUS)
coords = ptr32(SHIP_COORDS)
isin = ptr32(ISIN)
icos = ptr32(ICOS)
game = ptr32(GAME)
segs = ptr8(SHIP_SEG_NUMS)
explode = ptr32(EXPLODE)
struct = ptr32(STRUCT_LOC)
num_segs = segs[ship_type]
for slot in range(NUM_EXPLODE): # find free explosion slot
i = slot * EXPLODE_PARAMS * SHIP_SEGMENTS
if slot == NUM_EXPLODE-1 : return # no more slots
elif explode[i + S_EXP] == 0: break # found slot
explode[i + TYPE] = ship_type
explode[i + S_EXP] = 19
explode[i + E_STR] = struct_num
struct_index = struct_num * STRUCT_PARAMS
struct[struct_index + E_START] = i # pass exp index to structure
explode_index = i
index_start = i
explode_index = 0
for index in range(ship_type*SHIP_SEGMENTS,ship_type*SHIP_SEGMENTS+(num_segs)): # calc ship points
e_i = index_start + explode_index * EXPLODE_PARAMS
#print(slot,i,explode_index,e_i)
pt_deg1 = deg[index] + degrees
if index == ship_type*SHIP_SEGMENTS+num_segs - 1: #SHIP_POINTS-1:
pt_deg2 = deg[0] + degrees
else:
pt_deg2 = deg[index+1] + degrees
if pt_deg1 >= 360: pt_deg1 -= 360
if pt_deg1 < 0 : pt_deg1 += 360
if pt_deg2 >= 360: pt_deg2 -= 360
if pt_deg2 < 0 : pt_deg2 += 360
explode[e_i+X1] = ((radius[index] * SHIP_SIZE * icos[pt_deg1]))
explode[e_i+Y1] = ((radius[index] * SHIP_SIZE * isin[pt_deg1]))
explode[e_i+DX] = icos[pt_deg1]
explode[e_i+DY] = isin[pt_deg1]
if index == ship_type*SHIP_SEGMENTS+num_segs - 1:# SHIP_POINTS-1:
explode[e_i+X2] = ((radius[0] * SHIP_SIZE * icos[pt_deg2]))
explode[e_i+Y2] = ((radius[0] * SHIP_SIZE * isin[pt_deg2]))
else:
explode[e_i+X2] = ((radius[index+1] * SHIP_SIZE * icos[pt_deg2]))
explode[e_i+Y2] = ((radius[index+1] * SHIP_SIZE * isin[pt_deg2]))
explode_index += 1
ASTRO_SOUND.SOUND = int(ASTRO_SOUND.SOUND) | 1<<2
@micropython.viper
def ship_explode():
return
player = ptr32(PLAYER)
game = ptr32(GAME)
explode = ptr32(EXPLODE)
player[S_EXP] -= 1
if player[S_EXP] == 0:
game[LIVES] -= 1
if game[LIVES] == 0:
game[G_OVER] = 1
reset_player()
return
ship_type = player[TYPE]
ship_segs = player[SEGS]
for index in range(ship_segs): # calc ship points
i = index * EXPLODE_PARAMS
if int(randint(0,15)) == 0:
explode[i + S_EXP] = 0
explode[i+X1] = 0
explode[i+Y1] = 0
explode[i+X2] = 0
explode[i+Y2] = 0
explode[i+DX] = 0
explode[i+DY] = 0
explode[i+DX] = 0
explode[i+DY] = 0
else:
explode[i+X1] += explode[i+DX]
explode[i+Y1] += explode[i+DY]
explode[i+X2] += explode[i+DX]
explode[i+Y2] += explode[i+DY]
@micropython.viper
def struct_explode():
global MAX_I
player = ptr32(PLAYER)
game = ptr32(GAME)
explode = ptr32(EXPLODE)
segs = ptr8(SHIP_SEG_NUMS)
struct = ptr32(STRUCT_LOC)
deg = struct[2+7*4] + 10 # mod struct deg
if deg >= 360: deg -= 360
struct[2+7*4] = deg
struct[2+7*5] = deg
struct[2+7*6] = deg
struct[2+7*7] = deg
init_struct()
if player[S_EXP] > 0: # player explode
player[S_EXP] -= 1
if player[S_EXP] == 0:
game[LIVES] -= 1
if game[LIVES] == 0:
game[G_OVER] = 1
reset_player()
return
for slot in range(NUM_EXPLODE): # find active explosion
i = slot * EXPLODE_PARAMS * SHIP_SEGMENTS
if explode[i + S_EXP] > 0:
explode[i + S_EXP] -= 1
ship_type = explode[i + TYPE]
ship_segs = segs[ship_type]
struct_num = explode[i + E_STR]
struct_exp = struct[struct_num * STRUCT_PARAMS + ST_EXP]
struct[struct_num * STRUCT_PARAMS + ST_EXP] = explode[i + S_EXP]
for index2 in range(ship_segs): # calc struct points
i2 = (index2 * EXPLODE_PARAMS) + i
if int(randint(0,15)) == 0 or explode[i + S_EXP] == 0:
explode[i2+X1] = 0
explode[i2+Y1] = 0
explode[i2+X2] = 0
explode[i2+Y2] = 0
explode[i2+DX] = 0
explode[i2+DY] = 0
explode[i2+DX] = 0
explode[i2+DY] = 0
else:
explode[i2+X1] += explode[i2+DX]
explode[i2+Y1] += explode[i2+DY]
explode[i2+X2] += explode[i2+DX]
explode[i2+Y2] += explode[i2+DY]
@micropython.viper
def move():
player = ptr32(PLAYER)
deg = ptr16(SHIP_DEG)
radius = ptr16(SHIP_RADIUS)
coords = ptr32(SHIP_COORDS)
isin = ptr32(ISIN)
icos = ptr32(ICOS)
game = ptr32(GAME)
back_map = ptr8(MAP)
struct = ptr32(STRUCT_LOC)
cx = ((player[MAP_X])>>16)+5 # & 63
cy = ((player[MAP_Y])>>17)+4 # & 63
color = back_map[64*cy+cx] & SHADOW_MASK
game[DEBUG] = color
#back_map[64*cy+cx] = 0
d = player[DEG]
for index in range(NUM_STRUCT):
i = index * STRUCT_PARAMS
if struct[i + S_HITS] == 0: continue
s_x = struct[i + X] - (player[MAP_X]>>(SCALE-1)) - 60
s_y = struct[i + Y] - (player[MAP_Y]>>SCALE) - 50
if s_x > 0 and s_x < 40 and s_y > 0 and s_y < 40:
player[S_EXP] = 19
player[SHIELD] = 0
init_explode(player[TYPE],player[DEG],0)
struct[i + S_HITS] = 0
struct[i + ST_EXP] = 20
init_explode(struct[i + S_TYPE], struct[i + DEG],index) # type, deg, number
return
if color == 72 or color == 80 or color == 87 or color == 88: # hit spike
player[S_EXP] = 19
player[SHIELD] = 0
init_explode(player[TYPE],player[DEG],0)
return
if 80 < color < 85: # hit diamond
back_map[64*cy+cx] = 46
game[LIVES] += 1
color = 46
ASTRO_SOUND.SOUND = int(ASTRO_SOUND.SOUND) | 1<<3
if color != 46 and player[SHIELD] == 0: # hit wall, shield
d += 180
if d >= 360:
d -= 360
player[SHIELD] = 5
player[VX] = -1 * (player[VX]>>1)
player[VY] = -1 * (player[VY]>>1)
ASTRO_SOUND.SOUND = int(ASTRO_SOUND.SOUND) | 1<<1
else:
player[SHIELD] -= 1
if player[SHIELD] < 0 : player[SHIELD] = 0
player[VX] -= player[VX]>>5 # auto slow to stop
player[VY] -= player[VY]>>5
player[MAP_X] += player[VX]
player[MAP_Y] += player[VY]
ship_type = player[TYPE]
index_coords = 0
for index in range(ship_type*SHIP_SEGMENTS,ship_type*SHIP_SEGMENTS+SHIP_SEGMENTS): # calc ship points
i = index * 2
pt_deg = deg[index] + player[DEG]
if pt_deg >= 360: pt_deg -= 360
if pt_deg < 0 : pt_deg += 360
coords[index_coords] = ((radius[index] * SHIP_SIZE * icos[pt_deg])>>14)
coords[index_coords+1] = ((radius[index] * SHIP_SIZE * isin[pt_deg])>>14)
index_coords += 2
@micropython.viper
def move_missile():
miss = ptr32(MISSILES)
isin = ptr32(ISIN)
icos = ptr32(ICOS)
game = ptr32(GAME)
player = ptr32(PLAYER)
back_map = ptr8(MAP)
struct = ptr32(STRUCT_LOC)
for index in range(NUM_MISSILES): # calc missile positions
i = index * MISSILE_PARAMS
if miss[i + MISS_LIFE] > 0:
miss[i + MISS_LIFE] -= 1
x = miss[i + X]
y = miss[i + Y]
x += miss[i + VX]
y += miss[i + VY]
cx = (((x>>1) + player[MAP_X])>>16) # & 63
cy = ((y + player[MAP_Y])>>17) # & 63
color = back_map[64*cy+cx]
#back_map[64*cy+cx] = 0
#print(x>>SCALE, y>>SCALE,cx,cy)
#color = 46
if 1 and color != 46 or x < 0 or y < 0 or x > MAXSCREEN_X<<SCALE or y > MAXSCREEN_Y<<SCALE: # miss hit map
miss[i + MISS_LIFE] = 0
ASTRO_SOUND.reset_fire()
ASTRO_SOUND.SOUND = int(ASTRO_SOUND.SOUND) & 0b1101
continue
miss[i + X] = x
miss[i + Y] = y
#miss[i + VY] += 1000
m_x = x >> SCALE
m_y = y >> SCALE
for index2 in range(0,NUM_STRUCT):
i2 = index2 * STRUCT_PARAMS
if struct[i2 + S_HITS] == 0: continue
s_x = struct[i2 + X] - (player[MAP_X]>>(SCALE-1)) - 10
s_y = struct[i2 + Y] - (player[MAP_Y]>>SCALE) - 10
if m_x > s_x-10 and m_x < s_x+20 and m_y > s_y-10 and m_y < s_y+20 and struct[i2 + S_HITS] > 0: # miss hit struct
struct[i2 + S_HITS] -= 1
miss[i + MISS_LIFE] = 0
if struct[i2 + S_HITS] == 0:
struct[i2 + ST_EXP] = 20
init_explode(struct[i2 + S_TYPE], struct[i2 + DEG],index2) # type, deg, number
break
@micropython.viper
def mod_tiles():
tiles = ptr8(TILES)
player = ptr32(PLAYER)
buffer = ptr8(MOD_TILES)
rand0 = int(randint(0,16))
rand1 = int(randint(0,16*16))
rand2 = int(randint(0,16*16))
rand3 = int(randint(0,100))
offset_p = 1000
offset_m = -1000
if player[VY] < offset_m:
for x in range(16):
buffer[x] = tiles[16*16*45+240+x] # save last row
for y in range(15,0,-1):
for x in range(16):
tiles[16*16*45+y*16+x] = tiles[16*16*45+(y-1)*16+x]
for x in range(16):
tiles[16*16*45+x] = buffer[x] # store in first row
if player[VY] > offset_p:
for x in range(16):
buffer[x] = tiles[16*16*45+x] # save first row
for y in range(0,15):
for x in range(16):
tiles[16*16*45+y*16+x] = tiles[16*16*45+(y+1)*16+x]
for x in range(16):
tiles[16*16*45+240+x] = buffer[x] # store in last row
if player[VX] < offset_m:
for y in range(16):
buffer[y] = tiles[16*16*45+(16*y)+15] # save last column
for x in range(15,0,-1):
for y in range(16):
tiles[16*16*45+y*16+x] = tiles[16*16*45+y*16+(x-1)]
for y in range(16):
tiles[16*16*45+(16*y)] = buffer[y] # store in first column
if player[VX] > offset_p:
for y in range(16):
buffer[y] = tiles[16*16*45+(16*y)] # save first column
for x in range(0,15):
for y in range(16):
tiles[16*16*45+y*16+x] = tiles[16*16*45+y*16+(x+1)]
for y in range(16):
tiles[16*16*45+(16*y)+15] = buffer[y] # store in last column
def draw_colors(): # 37 colors utility
index = 0
for y in range(6):
for x in range(6):
LCD.rect(x*26,y*21,26,21,colors3[index],1)
LCD.text(str(index),x*26,y*21,0xffff)
index += 1
@micropython.viper
def draw_dark(): # scan cone
player = ptr32(PLAYER)
shadow = ptr32(SHADOW)
miss = ptr32(MISSILES)
if player[S_EXP] > 0: return
x = player[X]>>SCALE
y = player[Y]>>SCALE
isin = ptr32(ISIN)
icos = ptr32(ICOS)
#LCD.ellipse(x,y,20,20,0xffff,1)
d = player[DEG] + 180
d1 = d - 10
d2 = d + 10
if d1 < 0: d1 += 360
if d1 >= 360: d1 -= 360
if d2 >= 360: d2 -= 360
x1 = ((3 * icos[d1])>>8)
y1 = ((3 * isin[d1])>>8)
x2 = ((3 * icos[d2])>>8)
y2 = ((3 * isin[d2])>>8)
shadow[0] = 0
shadow[1] = 0
shadow[2] = x1
shadow[3] = y1
shadow[4] = x2
shadow[5] = y2
LCD.poly(x,y,SHADOW,0b111000,1) # 0xffff
for index in range(NUM_MISSILES): # draw missiles
i = index * MISSILE_PARAMS
if miss[i + MISS_LIFE] > 0:
r1 = int(randint(1,3))
r2 = int(randint(1,3))
if randint(0,1):
color = red
else:
color = 0x33ff
LCD.ellipse(miss[i + X]>>SCALE, miss[i + Y]>>SCALE,r1,r2, color,1) # 33ff
@micropython.viper
def draw_struct():
player = ptr32(PLAYER)
struct = ptr32(STRUCT_LOC)
segs = ptr8(SHIP_SEG_NUMS)
coords = ptr32(SHIP_COORDS)
explode = ptr32(EXPLODE)
x = player[MAP_X]>>(SCALE-1)
y = player[MAP_Y]>>SCALE
for index in range(0,NUM_STRUCT):
i = index * STRUCT_PARAMS
s_type = struct[i + S_TYPE]
s_seg = segs[s_type]
s_x = struct[i + X]
s_y = struct[i + Y]
x1 = s_x - x
y1 = s_y - y
if x1 > MAXSCREEN_X or x1 < 0 or y1 > MAXSCREEN_Y or y1 < 0: continue
if struct[i + S_HITS] > 0:
for index2 in range(index*SHIP_SEGMENTS,index*SHIP_SEGMENTS+s_seg):
i2 = index2 * 2 + 60
x2 = x1+(coords[i2+0])
y2 = y1+(coords[i2+1])
x3 = x1+(coords[i2+2])
y3 = y1+(coords[i2+3])
LCD.line(x2,y2,x3,y3,WHITE) # whole struct
elif struct[i + ST_EXP] > 0:
for index2 in range(s_seg-1):
i2 = (index2 * EXPLODE_PARAMS) + struct[i + E_START]
x2 = x1+(explode[i2+X1]>>14)
y2 = y1+(explode[i2+Y1]>>14)
x3 = x1+(explode[i2+X2]>>14)
y3 = y1+(explode[i2+Y2]>>14)
LCD.line(x2,y2,x3,y3,WHITE) # exploding struct
LCD.pixel(x,y,0)
@micropython.viper
def draw():
game = ptr32(GAME)
player = ptr32(PLAYER)
miss = ptr32(MISSILES)
coords = ptr32(SHIP_COORDS)
explode = ptr32(EXPLODE)
x = player[X]>>SCALE
y = player[Y]>>SCALE
if game[LIVES] > 0:
ship_type = player[TYPE]
ship_segs = player[SEGS]
if player[S_EXP] > 0:
for index in range(ship_segs):
i = index * EXPLODE_PARAMS
x1 = x+(explode[i+X1]>>14)
y1 = y+(explode[i+Y1]>>14)
x2 = x+(explode[i+X2]>>14)
y2 = y+(explode[i+Y2]>>14)
LCD.line(x1,y1,x2,y2,WHITE) # exploding ship
LCD.pixel(x,y,0)
else:
for index in range(ship_segs):
i = index * 2
x1 = x+(coords[i+0])
y1 = y+(coords[i+1])
x2 = x+(coords[i+2])
y2 = y+(coords[i+3])
LCD.line(x1,y1,x2,y2,WHITE) # draw ship
map_x = (player[MAP_X])<<1
map_y = (player[MAP_Y])
draw_struct()
LCD.text('FPS',0,0,0xff)
if player[SHIELD]:
LCD.ellipse(x,y,9*SHIP_SIZE,9*SHIP_SIZE,0xffff) # draw shield
show_num_viper(game[FPS],20,7,0xff)
#show_num_viper(gc.mem_free(),40,17,0xff)
LCD.text('LIVES',115,0,0xff)
show_num_viper(game[LIVES],125,10,0xff)
#show_num_viper(game[DEBUG],60,0,0xff)
if game[M_MAP] > 20:
if game[G_OVER]:
reset_game()
return
mini_map() # draw mini map
if game[G_OVER]:
LCD.text('GAME OVER',50,50,0xff)
LCD.show()
LCD.rect(0,0,MAXSCREEN_X,MAXSCREEN_Y,0,1)
@micropython.asm_thumb
def avg_fps_asm(r0,r1): # r0 = fps[] , r1 = current_fps
ldrb(r2,[r0,0]) # r2 = fps[0]
add(r2,r2,1) # fps[0] += 1
cmp(r2,33)
blt(LT_32) # if fps[0] > 32:
mov(r2,1)
label(LT_32)
strb(r2,[r0,0]) # fps[0] = new index
add(r2,r2,r0)
strb(r1,[r2,0]) # fps[fps[0]] = current_fps
mov(r2,1) # r2 = i
mov(r3,0) # r3 = tot
label(LOOP)
add(r0,r0,1)
ldrb(r4,[r0,0]) # r4 = fps[i]
add(r3,r3,r4) # tot += fps[i]
add(r2,r2,1)
cmp(r2,33) #33
blt(LOOP)
asr(r0,r3,5)
@micropython.viper
def mini_map()-> int:
screen = ptr16(LCD.buffer)
back_map = ptr8(MAP)
player = ptr32(PLAYER)
cx = ((0 + player[MAP_X])>>16) # & 63
cy = ((0 + player[MAP_Y])>>17) # & 63
offset = 0
for y in range(64):
for x in range(64):
c = back_map[y*64+x]
c = black if c == 46 else brown #red
screen[y*MAXSCREEN_X+x+offset] = c
if cx + cy & 1 or 1:
dot = (5+cy)*MAXSCREEN_X+cx+5
screen[dot] = 0xffff
screen[dot+1] = 0xffff
screen[dot-1] = 0xffff
screen[dot+MAXSCREEN_X+1] = 0xffff
screen[dot-MAXSCREEN_X+1] = 0xffff
def reset_game():
GAME[LIVES] = 3
GAME[G_OVER] = 0
PLAYER[X] = 80<<SCALE
PLAYER[Y] = 64<<SCALE
PLAYER[DEG]= 180
PLAYER[TYPE] = 0
PLAYER[SEGS] = SHIP_SEG_NUMS[PLAYER[TYPE]]
PLAYER[MAP_X] = 0<<SCALE
PLAYER[MAP_Y] = 30<<SCALE
@micropython.viper
def main():
global pot
init_tiles()
interp_setup(MAP_WIDTH, MAP_HEIGHT, 16, 4, 4)
init_imath()
pot = Pot()
_thread.start_new_thread(core1, ())
init_game()
init_player()
#init_enemy()
init_struct()
player = ptr32(PLAYER)
game = ptr32(GAME)
twinkle_ticks = 0
pot_ticks = 0
move_ticks = 0
missile_ticks = 0
ship_exp_ticks = 0
background_ticks = 0
struct_explode_ticks = 0
gc.collect()
print(gc.mem_free())
while not EXIT and not RESET_PB():
gticks = int(ticks_ms())
sleep(0.001) # catch ctrl-C exit
if game[G_OVER]:
read_pot()
draw()
continue
if gticks-pot_ticks>20:
pot_ticks = int(ticks_ms())
read_pot()
if player[S_EXP]>0:
if gticks-ship_exp_ticks>80: #20
ship_exp_ticks = int(ticks_ms())
ship_explode()
else:
if gticks-move_ticks>40:
move_ticks = int(ticks_ms())
move()
if gticks-missile_ticks>200 and not player[BUTTON]: #200
missile_ticks = int(ticks_ms())
init_missile()
if gticks-struct_explode_ticks>80:
struct_explode_ticks = int(ticks_ms())
struct_explode()
if gticks-background_ticks>80: #500
background_ticks = int(ticks_ms())
mod_tiles()
move_missile()
draw_dark()
draw_tiles_asm(DRAW_TILES_CTL)
draw()
game[FPS] = int(avg_fps_asm(FPS_ARRY,1_000//int(ticks_diff(ticks_ms(),gticks))))
shutdown()
def shutdown():
global EXIT
EXIT = True
Pin(16,Pin.OUT).low() # buzzer off
pwm.deinit()
ASTRO_SOUND.deinit()
Pin(13,Pin.OUT).low() # screen off
#gc.collect()
print(gc.mem_free())
machine.freq(125_000_000)
for i in range(10000): pass
print('Core0 Stop')
print(PLAYER[MAP_X]>>SCALE,PLAYER[MAP_Y]>>SCALE)
exit()
def core1():
global pot, EXIT, ASTRO_SOUND
ASTRO_SOUND = Astro_Sound()
ASTRO_SOUND.OFF = 0
while not EXIT:
pot.read_xyb(0)
ASTRO_SOUND.callback1()
#for delay in range(1000): pass
sleep_ms(10)
print('Core1 Stop')
if __name__=='__main__':
machine.freq(200_000_000)
machine.mem32[0x40008048] = 1<<11 # enable peri_ctrl clock
pwm = PWM(Pin(13))
pwm.freq(1000)
pwm.duty_u16(0x8fff)#max 0xffff
LCD = LCD_1inch8()
LCD.fill(0)
draw_colors()
LCD.show()
#exit()
EXIT = False
try:
main()
shutdown()
except KeyboardInterrupt :
shutdown()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment