0+S~%S‡Q«R6_WOÂÃÇÿÿÿÿMÿj<ÿì'õ<ÏýƒvœÿDýÿ̪ õÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîwwîîwwwwwwP` €s0o`!÷2"?1_Ÿi9úÆVûàpàîààîàî~wààpàààîàîààîààpàààîîîîpwpîîîàîàààpàîwîîpîwîîîîpîîîîîîîwààîàààîîîàpààpàîwààîîîîàwîîîîîîpwîàààîwàààîîîîîppîîîwîîwîîàîpàwwàpwàîààîîàwpààîàîàîîpàîîàîàîppîîîîîîààwàîààîàîîîwpîîîîàîwwàwàpàààîwàîîààwààààwà~ààààîàîwîppîîîîpààààwàwppîîîîîîààîàààààààààwI™I™™™™™ ™ ™™ ”™I�™ ™™™�™I™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™I™™™™�™™”™™™I™™™™™™™™™™@™™�™™™™™I™™™™I�™™™™�™™I™™™™ ™™ �™™™™�™™I™I™™ ™™ �™™™™�™™ ™ ™™™™ @™™™™�™™ ™™™™™ ”ÌÌî°î°»°»»{°ÌÌḬ̀ ° »» °» ·w» »» ÌÌÌÌ»»»»»»»»»w{·»»»Ì î»î»»»îÀÀîîÀ @I™™ �™ff``ff``À ÌÀÌ ™™™™™™™™w™™™™™™™™™™™™w�™™�� �™ ™™™™™™™™�™ � � ÌÀ ÌÌ À À ÁÁ| ÀÌpwÌÌ|wwwÌÌ|wÌpwww™™™™™w™™w™™™w™™™™w—™™™w™™ww™™™™™wwwwÀ wÌÌÌwwwwÌÌÌwÀ ÌÁ ÀÀÀÌÀÁÌwÌÀ pwÌÌ|wwwÌÌ|wÀ pwww™™™™™w™™w™™™w™™™™w—™™™w™™ww™™™™™wwwwÌwÌÌÌwwwwÌÌÌwÌ ÀÇÌÁ À   ÀÀÀ|ÀÀ ÀÌpwÌÌ|wwwÌÌ|wÀÌpw ww™™™™™w™™w™™™w™™™™w—™™™w™™ww™™™™™www wÀÌwÌÌÌwwwwÌÌÌwÀÌ À ÁÀÀÀÀÀw™ ™ ™ ÀÀÀÀw™ ™ ™ ÀÀÀÀw™ ™ ™ ÀÀÀÀw™ ™ ™ ÀÀÀÀw™ ™ ™ ff`ff`fff`ffffff`fff`ff`fffffff`fff`fppwwpwwwwpwwpwwpwwwwpwwpwwpwwwwpwpwpwwpwwwpwwwwpwwwpwwwwpwwwwwwwwpwpwwpwwwwpwwpwwpwwwwpw| ÀwÀwÇ| | ÀÀwÀwÇÀ|Ì| | | ÀÀwwÀwÇÀ|Ì| | | ÀwÀwÇ|ww À|Ì| | À `ff`ff`ff`fff`fffff`ff`ff`fff`fff`fffffwpwwpwwpwwwpwpwwpwwpwwwpwpwpwwpwwwwpwpwwpwwwwpwwpwwpwwwpwwpwwpwwpwwwwwwpwwwwpwwwwwpwwpwwpwwww ÀÀÀÀÀ ÀÀÀÀÀÀÀwwwwwwwwwwÀÀÀ ÀÀ ™Ì™œ™ À ™À ™Ì™ÉwwwwwwÌ™À ™À ÀÀÀ™Ì™À À pwwwpwppppppwwpppwwppwwwpppwppppwwwwwwwwwwpppppwwpwwppppppww?(>>>T>j>€>–>¬>Â>(4>4T4j4€4–4¬4Â4(*>*T*j*€*–*¬*Â*> T j € – ¬ ?U—­>HTHjH€H–H¬H(>>>T>j>€>–>¬>Â>(4>4T4j4€4–4¬4Â4(*>*T*j*€*–*¬*Â*> T j € – ¬ ?U—­(>>>T>j>€>–>¬>Â>(4>4T4j4€4–4¬4Â4(*>*T*j*€*–*¬*Â*( > T j € – ¬  >H?Uk�—­¬H>HTH–H¬H(>>>T>j>€>–>¬>Â>(4>4T4j4€4–4¬4Â4(*>*T*j*€*–*¬*Â*> T j € – ¬ ?Uk�—­UHkH�H—H?>U>k>�>—>­>?4U4k4�4—4­4(*>*T*j*€*–*¬*Â*( > T j € – ¬  )?Uk�—­Ã(>>>T>j>€>–>¬>Â>(4>4T4j4€4–4¬4Â4(*>*T*j*€*–*¬*Â*( > T j € – ¬  )?Uk�—­Ã)H?HUHkH�H—H­HÃH)>?>U>k>�>—>­>Ã>)4?4U4—4­4Ã4)*?*U*—*­*Ã*? U k � — ­ > T j € – ¬ (H>HTHjH€H–H¬HÂH?>k>—>Ã>)4U4�4­4(*>*T*j*€*–*¬*Â*( > T j € – ¬  )?Uk�—­Ã(>>>T>j>€>–>¬>Â>(4>4T4j4€4–4¬4Â4(*>*T*j*€*–*¬*Â*( > T j € – ¬  ) @U l� ˜­ Ä)>?HU>kH�>—H­>ÃH(4>4T4j4€4–4¬4Â4(*>*T*j*€*–*¬*Â*( > T j € – ¬  ) @U l� ˜­ Äýþÿýÿÿûÿýÿûýÿý û ý ÿ û ýýýÿúùýùûùþùûü ù þ ù ú ýýú÷þ÷üùü  ÷  ùýÿöÿøÿ÷ö ÷ ú  ûûüýøÿúõþúÿþ  õ ÷ ÿ ýûþûýÿýÿúô÷ ü ÷úùÿûùýöýûýôöô  ö ôùþõ÷ùúùüùû÷ûýûýôò õ óýûÿýýýùûÿÿÿùýüþý ýû÷ùõùûõýùúûùýýýõó   ü ÿÿ ù  ø÷ùÿ÷óÿõûüùÿÿú úûÿý ÿÿÿÿþþÿýÿÿüüûýýþûü   þûûüÿùû  øþùþöþ÷ ûö÷ûñÿÿñöøþùþöþ÷ ûùîÿíõÿö÷ûñÿÿñöþÿþÿÿÿùîÿíõÿþþþÿþþýýÿþþþÿþþýüþüÿüýüýüýþýÿýþþÿÿüüýýþÿýýûþüþüýýýüüüüüýýýûþûÿûûûúüüûþúÿúûýýýúúûûþúýýúþùüû   üúüúûûûûúüú ý ýúüûûûú  üüÿ(8HHHHHHHHHHH(8HHHHHHHHHHH(8HHHHHHHHHHH(8HHHHHHHHHHH(8HHHHHHHHHHH(8HHHHHHHHHHH(8HHHHHHHHHHH(8HHHHHHHHHHHXhxˆ˜˜˜˜˜˜˜˜˜˜˜Xhxˆ˜˜˜˜˜˜˜˜˜˜˜Xhxˆ˜˜˜˜˜˜˜˜˜˜˜Xhxˆ˜˜˜˜˜˜˜˜˜˜˜Xhxˆ˜˜˜˜˜˜˜˜˜˜˜Xhxˆ˜˜˜˜˜˜˜˜˜˜˜Xhxˆ˜˜˜˜˜˜˜˜˜˜˜Xhxˆ˜˜˜˜˜˜˜˜˜˜˜ @@``€€  ÀÀàà @@``€€  ÀÀàà!!AAaa��¡¡ÁÁáá!!AAaa��¡¡ÁÁáá""BBbb‚‚¢¢ÂÂââ""BBbb‚‚¢¢ÂÂââ##CCccƒƒ££ÃÃãã##CCccƒƒ££ÃÃãã$$DDdd„„¤¤ÄÄää$$DDdd„„¤¤ÄÄää%%EEee……¥¥ÅÅåå%%EEee……¥¥ÅÅåå&&FFff††¦¦ÆÆææ&&FFff††¦¦ÆÆææ''GGgg‡‡§§ÇÇçç''GGgg‡‡§§ÇÇçç ))99IIYYiiyy ))99IIYYiiyy‰‰™™©©¹¹ÉÉÙÙééùù‰‰™™©©¹¹ÉÉÙÙééùù **::JJZZjjzz **::JJZZjjzzŠŠššªªººÊÊÚÚêêúúŠŠššªªººÊÊÚÚêêúú ++;;KK[[kk{{ ++;;KK[[kk{{‹‹››««»»ËËÛÛëëûû‹‹››««»»ËËÛÛëëûû ,,<n�pÞŽš�{-‘khol™h˜Œtlƒ}VoHqQC’�¥™‡Ÿ„Œ�}N_cB$'H�¸œ¹ƒf†|¢††~Hd�qY\gd{zupf‘žzƒ‹p�oR‚{‹ˆZ[W;K~°¿¡‚typ�x[rJ-Ze–tŒ¨””€k||cvXNhW„˜ª—d^PdiŠ„o†•¬ƒ~UUŠ–�?Z8E~b£:egy¿H¥¯Š“Œ‚q€b|O`q#C]¤©}ŽTy~¤œP‘d^f€vx{n¼TL]vº„SKs¦�J…YLVu¦uTŠYcŒlq�°�„�œ†`€GJ36mS¸”wŒe¤‰˜Æ‡S@;ghy›rw�ƒcujR‹W€•i–Œ¢©vonRP=:Od�_y¢˜¿¡œŠgsg\cl]g}~lWuˆ‹Ž€eagv�ƒ�nWmZ{‚w�fbtf’—ucVc~ŽƒˆnK\dw‹‰—�}x_t��}oe]d{„ކpfZo|espuyv”ˆƒ„uztum[l{fzliˆ|’}z|vŽjikqveznnuhtp|�€‡pv†|x„ibkPci}‰v{p…Š…Žƒvksltvmumqfm~mnlrttukper}r“˜nerd||btL|rp‹q©c”†X�Bs†x�^APn…¤|†uj‚c‡�{ydon�~Isƒzˆƒ�|ry£›…•KNL2qYjsEcnœ´—¦Ÿ…�ro]o„O^MZ˜x” ykXVirrhf˜nr‹§ YtYƒaZt] †Ä‰q–PF;šiQM@•k¾¢~©�¡I“`|FŽuvŸ~}u‹YaVQ[+s€“†­¦˜²£efjN) 1P‘�™¡iŸ¦²£‡�EIpoTAX^r‘ •�˜{en[UYFoz‹ª€~ƒg`k�›}sglt†˜|€rMQa^b_gˆ€‹’�›œ†„€TT;Jn†‘`gcu„Ÿªt}l�{^[2‚¦ÄƒkyL‘V…K^,ŸYnÍ¥´¤£~Œgpc7q-5[ŽÊ�¢rn~w¸HfxR{{’~€h²s(Q?£šeZx‘®¿eu€W=9Z}~S}…t¢‹|›©…grŒ�U„jU\/k“¸[sHeŽy¾µo[Pp‰u‘nT\g\ZzZ�vÁ�”¥�˜i7UD:O8\u¦›u¨–ª—p{OQYZb‚‡v•�Šc\|sz{g]mr—¦—�bbbdxWj^P€j‘¯�’h_s†nGFH^Ž’›Ÿƒ~ih‘}z}^TVn‘�…{trw`jvkrry|yŽŠ|ƒunffxacgb|t�ˆ„‘}�€rwZdU]g_om~�“—�‘}qp[Qag_yjk|„ž‹ŽyjkX`]cgzŒ—¦’“†sUdM:KR]z�‰˜Ž�“w|‹aSO\^h–ˆ�z|˜hŒY}nZ‘;{icªœ¨�_CS^Š|cnX‚w‡¼’’sYk]{PGxxƒ›�’‚�®t†e0G5foeˆdjl¯ƒ’�y‰rstkžx[]?rokŠneXZlŒ¡ƒ{q—‡d†¢em`lˆR~T„€�¥d™cK1‚¥c|?…nwªb™k�xV°lŠz‡œh•|_PkVSr[€Tb›Ž ˆŠ˜sy�\HheU0Ia•±•¬dg}|›uŽv[ˆ“zaaUZiruepŒ�‚–�yŽbbp\fRg[Yd‡µ¶¡‹{sr€f`pPIfvƒ‡wŒ‹}}edxsf|gkqd„“¨pZWOtz¦€•š³{g/M|‰‚=f=€  ¾V‰TŽ ‘wygmjz‹ˆ¬cŒ\ =128 and v-256 or v end function clamp(lo,v,hi)return math.max(lo,math.min(v,hi))end function scrx(ax)return math.floor((ax-fld_x0)*scale_x+fld_ox)end function scry(ay)return math.floor((ay-fld_y0)*scale_y+fld_oy)end function clip_field()clip(fld_ox,0,fld_w,scr_h)end local heli_tip={[0]={4,12},[1]={7,0}} function heli_blade_span(hface) return heli_tip[hface or 0][1+oframe%2],4 end function heli_blade_black(sx,sy,hface) local tx,tw=heli_blade_span(hface) rect(sx+tx,sy,tw,1,0) end draw_fraction=1 local last_ms,dt_ema,g_dt function frame_dt() if fixed_dt then return 1 end local now=time() if last_ms and now-last_ms<2 then return g_dt or 1 end local dt=1 if last_ms then local raw=(now-last_ms)/(1000/60) if raw>4 then raw=4 end dt_ema=dt_ema and(dt_ema+0.2*(raw-dt_ema))or 1 dt=dt_ema end last_ms=now g_dt=dt return dt end function lerpd(a,b) if smooth==0 or a==nil then return b end local d=b-a if d>24 or d<-24 then return b end return a+d*draw_fraction end function lerpw(a,b) if smooth==0 or a==nil then return b end local d=(b-a+128)%256-128 if d>24 or d<-24 then return b end return(a+d*draw_fraction)%256 end function pal(c,d)poke4(0x7FE0+c,d)end local pal_ident={} for i=0,7 do pal_ident[i]=(2*i+1)*16+2*i end function pal_reset() for i=0,7 do poke(0x3FF0+i,pal_ident[i])end end function pal_white() poke(0x3FF0,0x70) for i=1,7 do poke(0x3FF0+i,0x77)end end function fp()return btnp(4)or btnp(5)end function xp()return(btnp(4)or btnp(5))and not(btn(0)or btn(1))end function lp()return btnp(2)end function rp()return btnp(3)end function prune(list,keep) for i=#list,1,-1 do if not keep(list[i])then table.remove(list,i)end end end end package.preload["a2boot"]=function(...) local PAL,BORDER=0x3fc0,0x3ff8 local PCM_START=1000 apple2={defaults={ at_ms=800,beep_ms=100,wipe_ms=60,title_ms=3000,hold_ms=2000, fg=12,bg=0,rgb={0xf4,0xf4,0xf4}, bg_rgb={0,0,0}, volume=15,skip=false, drive=true, at_spr=504, title_spr=505,title_w=7, }} local GLYPH=7 local MAP=0x3ff0*2+GLYPH local CW,CH,COLS,ROWS=6,8,40,17 local o,t0,saved local function glyph(id,x,y,w,c) local old=peek4(MAP) poke4(MAP,c) spr(id,x,y,0,1,0,0,w,1) poke4(MAP,old) end local function at_screen(cleared) rect(0,0,CW,CH,o.fg) glyph(o.at_spr,0,0,1,o.bg) for ln=0,CH-1 do for c=1,COLS-1 do memcpy(ln*120+c*3,ln*120,3)end end for r=1,ROWS-1 do memcpy(r*960,0,960)end rect(0,0,240,cleared*CH,o.bg) end local function bell() poke(0xffd2,916&0xff) poke(0xffd3,(916>>8)|(o.volume<<4)) for i=0,15 do poke(0xffd4+i,i<8 and 0xff or 0)end end local PCM,BLOCK=0x14e24,128 local BASE=0x0EC00 local MID=118 local bank={ knock={0,2442},cycle={2442,591},hiss={3033,949}, thump={3982,752},short={4734,193}, } local script={ {"knock",1},{"cycle",16},{"hiss",8},{"short",1},{"thump",1}, {"short",1},{"thump",1},{"short",2},{"thump",1},{"short",16}, {"thump",1},{"short",16},{"thump",4},{"hiss",8},{"thump",4}, {"short",32}, } local step local rep,pos local warmup local has_pcm local probed local function drive_update(t) if has_pcm==nil then probed=probed+1 if probed==1 then poke(PCM+BLOCK-1,0xff) return end has_pcm=peek(PCM+BLOCK-1)==0 if not has_pcm then return end elseif has_pcm==false then return end warmup=math.min(30,warmup+1) local silence=math.floor(MID*warmup/30+0.5) local due=t>=PCM_START for i=0,BLOCK-1 do local v=silence if due and warmup>=30 and step then local entry=script[step] local blk=bank[entry[1]] v=peek(BASE+blk[1]+pos) pos=pos+1 if pos>=blk[2]then pos,rep=0,rep+1 if rep>entry[2]then rep,step=1,step+1 end if step>#script then step=nil end end end poke(PCM+i,v) end end local function drive_release() if has_pcm then for i=0,BLOCK-1 do poke(PCM+i,MID)end end end function apple2.boot(opts) apple2.finish() o=setmetatable(opts or{},{__index=apple2.defaults}) saved={border=peek(BORDER)} for i=0,47 do saved[i]=peek(PAL+i)end for i=0,2 do poke(PAL+o.bg*3+i,o.bg_rgb[i+1]) poke(PAL+o.fg*3+i,o.rgb[i+1]) end poke(BORDER,o.bg) step,rep,pos=o.drive and 1 or nil,1,0 warmup,has_pcm,probed=0,nil,0 t0=time() end function apple2.finish() if not t0 then return end for i=0,47 do poke(PAL+i,saved[i])end poke(BORDER,saved.border) drive_release() step,t0=nil,nil end function apple2.tic() if not t0 then return false end local t=time()-t0 local wipe,title=o.at_ms,o.at_ms+o.wipe_ms if o.drive then drive_update(t)end local hold=(has_pcm==false)and 1000 or o.hold_ms if o.skip and(btnp()~=0 or keyp())or t>=title+o.title_ms+hold then apple2.finish() return false end if t=wipe-o.beep_ms then bell()end elseif t0 then strips(0,keys[2])end vbank(0) end local SOLID=7 local tic_print=print function print(s,x,y,c,...) if mono.phosphor[mono.mode]then c=SOLID end return tic_print(s,x,y,c,...) end function mono.draw_solid(f,...) if not mono.phosphor[mono.mode]then return f(...)end local m={} for i=0,7 do m[i]=peek(0x3ff0+i)end for c=1,15 do poke4(0x7fe0+c,SOLID)end f(...) for i=0,7 do poke(0x3ff0+i,m[i])end end function mono.set(i) mono.index=i%#mono.modes mono.mode=mono.modes[mono.index+1] pmem(mono.slot,mono.index) end function mono.cycle()mono.set(mono.index+1)end function mono.load()mono.set(pmem(mono.slot))end function mono.fg()return(mono.phosphor[mono.mode]or{}).lit or apple2.defaults.rgb end function mono.bg()return(mono.phosphor[mono.mode]or{}).dark or apple2.defaults.bg_rgb end end package.preload["sfx"]=function(...) local dur={[0]=18,[2]=10,[3]=3,[4]=29,[5]=3,[6]=27, [7]=8,[8]=21,[9]=10,[10]=10,[11]=5,[12]=8, [14]=19,[15]=2,[16]=17} local busy={[0]=0,[2]=0,[3]=0} loop_on={} function psfx(id,ch) if demo then return end if not ch then ch=0 local now=time() for _,c in ipairs{0,3,2}do if not loop_on[c]and now>=busy[c]then ch=c break end end end sfx(id,nil,dur[id],ch) if busy[ch]then busy[ch]=time()+dur[id]*16.7 end end function fire_sound() if demo then return end if not pcm.play("fire")then psfx(sfx_fire)end end function lsfx(id,ch) if demo then return end sfx(id,nil,-1,ch) loop_on[ch]=true end function sfx_stop(ch) sfx(-1,nil,nil,ch) loop_on[ch]=nil end function chopper_sound() local on=false if ens and not demo then for _,e in ipairs(ens)do if e.heli and e.st==3 then on=true break end end end if on and not loop_on[2]then lsfx(sfx_chopper,2) elseif not on and loop_on[2]then sfx_stop(2) end end local hf_queue,hf_wait=0,0 function heli_fire_round() if demo then return end hf_queue=hf_queue+1 end function hf_pull() if hf_wait>0 then hf_wait=hf_wait-1 end if hf_queue>0 and hf_wait<=0 then hf_queue=hf_queue-1 hf_wait=hf_gap_f psfx(sfx_helifire,1) end end function hf_clear() hf_queue,hf_wait=0,0 end song_trk={0,1,2,3,2,4,5,6,7,3} song_frame={0,0,0,0,4,0,0,0,0,3} song_rowend={76,68,186,104,508,124,288,100,198,236} song_names={ "SHAVE AND A HAIRCUT","DRUNKEN SAILOR","POPEYE THE SAILOR MAN", "ROW, ROW, ROW YOUR BOAT","SPANISH LADIES","PAY ME MY MONEY DOWN", "MY BONNIE LIES OVER THE OCEAN","BLOW THE MAN DOWN", "A SAILOR WENT TO SEA","FLEET SUNK DIRGE"} local cur_song=nil function play_song(id) if demo then return end if id==10 then psfx(sfx_blip) return end sfx_stop(1) sfx_stop(2) music(song_trk[id+1],song_frame[id+1],0,false) cur_song=id end function song_stop() music() cur_song=nil end function music_playing() if not cur_song then return false end if peek(0x13FFC)~=song_trk[cur_song+1]then cur_song=nil return false end local pos=peek(0x13FFD)*64+peek(0x13FFE) if pos>=song_rowend[cur_song+1]then song_stop() return false end return true end end package.preload["pcm"]=function(...) pcm={} local ADDR=0x14e24 local BLOCK=128 local base=0x0AA00 local midpoint=47 local bank={ fire={0,2336}, } local position=nil local remaining=0 local gain=1 local warmup=0 local from=0 local supported=nil local probed=0 function pcm.ok()return supported==true end function pcm.ready()return supported==true and warmup>=30 end function pcm.playing()return position~=nil end function pcm.play(name,volume) local s=bank[name] if not s or not pcm.ready()then return false end gain=math.max(0,math.min(1,volume or 1)) position,remaining=s[1],s[2] return true end function pcm.stop()position=nil remaining=0 end function pcm.update() if supported==nil then if not pcm_enabled then supported=false return end probed=probed+1 if probed==1 then poke(ADDR+BLOCK-1,0xff) return end supported=peek(ADDR+BLOCK-1)==0 if not supported then return end elseif supported==false then return end if warmup==0 then from=peek(ADDR)end warmup=math.min(30,warmup+1) local silence=math.floor(from+(midpoint-from)*warmup/30+0.5) for i=0,BLOCK-1 do local value=silence if warmup>=30 and position then value=math.floor(midpoint+(peek(base+position)-midpoint)*gain+0.5) position,remaining=position+1,remaining-1 if remaining<=0 then position=nil end end poke(ADDR+i,value) end end end package.preload["waves"]=function(...) function waves_init() sea_ph=0 prev_ph=0 end function waves_tick() prev_ph=sea_ph sea_ph=sea_ph+wcfg_seavel[math.min(wave,10)] end function waves_y(ax) return sea_mid+sea_amp*math.sin((ax+sea_ph)/sea_len*2*math.pi) end function waves_dph()return lerpd(prev_ph,sea_ph)end function waves_dy(ax,ph) return sea_mid+sea_amp*math.sin((ax+(ph or waves_dph()))/sea_len*2*math.pi) end function waves_draw() local ph=waves_dph() for x=fld_ox,fld_ox+fld_w-1 do local ay=waves_dy((x-fld_ox)/scale_x+fld_x0,ph) line(x,scry(ay),x,scr_h-1,12) end end function wspr(id,sx,sy,w,h,flip,ax) local surf=scry(waves_dy(ax)) if sy>=surf then pal_white() spr(id,sx,sy,0,1,flip,0,w,h) pal_reset() elseif sy+h*8<=surf then spr(id,sx,sy,0,1,flip,0,w,h) else clip(fld_ox,0,fld_w,surf) spr(id,sx,sy,0,1,flip,0,w,h) clip(fld_ox,surf,fld_w,scr_h-surf) pal_white() spr(id,sx,sy,0,1,flip,0,w,h) pal_reset() clip_field() end end end package.preload["explode"]=function(...) xpl_p={0,8,nil} xpl_r={0,3,{0,36,74,110}} xpl_h={0,23,{0,20,40,60,80,100,120,140,160,180,200,220,240,260,280, 300,320,340,360,378,398,418,438,458}} function fx_init() xpl_p[1],xpl_r[1],xpl_h[1]=xplp_b,xplr_b,xplh_b booms={} puffs={} splashes={} if not blast_ph then fx_bake()end end function boom(ax,ay,heli) table.insert(booms,{x=ax,y=ay,ph=0,heli=heli}) end function splash(ax) table.insert(splashes,{x=ax,ph=0}) end function fx_tick() prune(booms,function(b)b.ph=b.ph+1 return b.ph<expl_of end) prune(splashes,function(s)s.ph=s.ph+1 return s.ph<8 end) end boom_dense=0.54 xpl_rim={[9]=4,[12]=1} local function bhash(x,y) return(x*x*7+y*y*13+x*y*29+x*53+y*97)%23/23 end local function gen_fball(t,plot) local rx,ry=6.75+5.7*t,2.6+5.6*t local irx,iry=1/rx,1/ry local frx,fry=math.floor(rx),math.floor(ry) local yof=math.floor(ry*.18) local hole=math.max(0,t-.78)*3 local hole2=hole*hole local e1=1-math.min(t/.9,1) local cut=.24-t*.1+.11*e1*e1-.14 for dy=-fry-2,fry+3 do local v=(dy-yof)*iry local v2=v*v for dx=-frx-2,frx+2 do local u=dx*irx local q=u*u+v2 local h=bhash(dx,dy) if q<=1+h*.3 and q>=hole2 and h>cut and(dx~=0 or h>.75)and(dx%2==0 or h>.55)then plot(dx,dy,q>.55+h*.25 and 4 or 9) end end end if t<.55 then plot(-3,0,7)plot(-2,0,7) plot(2,0,7)plot(3,0,7) end end local function gen_puff(t,plot) local rx,ry=5.25+4.5*t,3.15+2.7*t local irx,iry=1/rx,1/ry local frx,fry=math.floor(rx),math.floor(ry) local wf=math.min(1,t*1.7-.2) local wlim=wf>0 and wf*1.3 or-1 local hole=math.max(0,t-.62)*2.5 local hole2=hole*hole local dcut=math.max(0,t-.72)*2.2-.15 local bcut=.74-t*.4 for dy=-fry-2,fry+2 do local v=dy*iry local v2=v*v for dx=-frx-3,frx+3 do local u=dx*irx local q=u*u+v2 local h=bhash(dx,dy) if q<=1+h*.3 and q>=hole2 and h>dcut then if q<wlim then plot(dx,dy,7) elseif h>bcut and(dx%2==0 or h>.55)then plot(dx,dy,12) end end end end end local function gen_phase(kind,ph,plot) if kind==1 then gen_fball(ph/(expl_of*boom_dense),plot) else gen_puff(ph/expl_of,plot) end end local function gen_list(kind,ph) local list,n={},0 gen_phase(kind,ph,function(dx,dy,c) list[n+1],list[n+2],list[n+3]=dx,dy,c n=n+3 end) return list,n end local TILE_B,TILE4=0x4000,0x8000 local skyline local function tile_alloc(cw,ch) local best,besty for c=0,16-cw do local y=0 for k=c,c+cw-1 do if skyline[k]>y then y=skyline[k]end end if y+ch<=16 and(not besty or y<besty)then best,besty=c,y end end if not best then return nil end for k=best,best+cw-1 do skyline[k]=besty+ch end return besty*16+best end local function bake_list(list,n) local x0,y0,x1,y1=999,999,-999,-999 for i=1,n,3 do local dx,dy=list[i],list[i+1] if dx<x0 then x0=dx end if dx>x1 then x1=dx end if dy<y0 then y0=dy end if dy>y1 then y1=dy end end local cw,ch=(x1-x0+8)//8,(y1-y0+8)//8 local id=tile_alloc(cw,ch) if not id then return nil end for r=0,ch-1 do memset(TILE_B+(id+r*16)*32,0,cw*32) end for i=1,n,3 do local px,py=list[i]-x0,list[i+1]-y0 local cell=id+(py//8)*16+px//8 poke4(TILE4+cell*64+(py%8)*8+px%8,list[i+2]) end return{id=id,cw=cw,ch=ch,ox=x0,oy=y0} end blast_ph=nil function fx_bake() skyline={} for c=0,15 do skyline[c]=0 end local phases={} local nfb=math.ceil(expl_of*boom_dense) for ph=0,nfb-1 do if ph/expl_of<boom_dense then phases[#phases+1]={kind=1,ph=ph}end end for ph=0,expl_of-1 do phases[#phases+1]={kind=2,ph=ph}end for _,p in ipairs(phases)do p.px,p.n=gen_list(p.kind,p.ph)end table.sort(phases,function(a,b)return a.n>b.n end) blast_ph={{},{}} baked_cells=0 for _,p in ipairs(phases)do local b=p.n>0 and bake_list(p.px,p.n) if b then baked_cells=baked_cells+b.cw*b.ch else b={px=p.px,n=p.n} end blast_ph[p.kind][p.ph]=b end end local function draw_blast(kind,ph,x,y) local b=blast_ph and blast_ph[kind][ph] if not b then gen_phase(kind,ph,function(dx,dy,c)pix(x+dx,y+dy,c)end) elseif b.id then spr(b.id,x+b.ox,y+b.oy,0,1,0,0,b.cw,b.ch) else local l=b.px for i=1,b.n,3 do pix(x+l[i],y+l[i+1],l[i+2])end end end function draw_boom(ax,ay,ph,frames,base,loops,heli) local x,y=scrx(ax),scry(ay) local f=ph/expl_of if not loops then if heli then draw_blast(2,math.floor(ph),x,y) elseif f<boom_dense then local remap=base~=9 if remap then pal(9,base)pal(4,xpl_rim[base])end draw_blast(1,math.floor(ph),x,y) if remap then pal_reset()end if f<boom_dense*.7 then return end end end local fb,n,idx=frames[1],frames[2],frames[3] local fi=math.floor(f*n*(loops or 1))%n local a,b if idx then a,b=fb+idx[fi+1],fb+idx[fi+2] else a=fb+fi*32 b=a+32 end for i=a,b-2,2 do local dx,dy=sgnb(peek(i)),sgnb(peek(i+1)) local c=(math.abs(dx)+math.abs(dy)<=2)and 7 or base pix(x+math.floor(dx*scale_x),y+math.floor(dy*scale_y),c) end end local function draw_splash(ax,ph) local x,y=scrx(ax),scry(waves_dy(ax)) local hgt=5-math.abs(ph-3) for dx=-2,2 do local hh=math.floor(hgt*(3-math.abs(dx))/3) if hh>0 then line(x+dx,y-hh,x+dx,y,dx==0 and 7 or 12) end end end local function mix(a,b) local h=(a*374761393+b*668265263)%4294967296 h=(h~(h>>13))*1274126177%4294967296 return h~(h>>16) end function add_puff(ax,ay) local h=math.floor(ax)*7+math.floor(ay)*13 local s=h*2654435761%4294967296 local function u(k)return mix(s,k)%1000/1000 end local rate,drift=0.7+0.6*u(1),0.3+0.45*u(2) local a1=0.55+0.25*u(3) local w1,w2=0.35+0.35*u(4),1.1+0.9*u(5) table.insert(puffs,{x=ax,y=ay, v=1+h//3%2, r0=rate*drift, c1=rate*a1/w1,w1=w1,p1=6.2832*u(6), c2=rate*(1-a1)/w2,w2=w2,p2=6.2832*u(7), off=8*u(8), j=h%11/44}) end local function puff_phase(p,now) local t=now/1000 local ph=p.off+p.r0*t-p.c1*math.cos(p.w1*t+p.p1) -p.c2*math.cos(p.w2*t+p.p2) local w=now/2600-p.x/180+p.j local q=w%1 local g=mix(math.floor(w),math.floor(p.y/12)) if q<0.25 and g%3==0 then ph=ph+(0.7+(g>>8)%100/110)*math.sin(q*12.566)^2 end return math.floor(ph)%8+1 end function draw_puffs(dy) local now=time() for _,p in ipairs(puffs)do p.f=puff_phase(p,now) local d=(p.f-puff_peak[p.v])%8 local g=(d<=1 or d==7)and 2 or 1 spr(256+puff_grey[p.v][g],scrx(p.x)-3,scry(p.y)-dy,0) end for _,p in ipairs(puffs)do spr(256+puff_tops[p.v][p.f],scrx(p.x)-3,scry(p.y)-dy,0) end end function fx_draw() draw_puffs(3) for _,s in ipairs(splashes)do draw_splash(s.x,s.ph) end for _,b in ipairs(booms)do draw_boom(b.x,b.y,b.ph,b.heli and xpl_h or xpl_p,b.heli and 12 or 9, nil,b.heli) end end end package.preload["dive"]=function(...) function dive_map(mi,ex,oy,hi16) local page=(mi==4 and 1 or mi-1) local v=peek(mapl_b+page*256 +(math.floor(ex)&0xf0)+((math.floor(oy)&0xf0)>>4)) return(v%16+hi16)*256+(v>>4)*16 end function dive_start(e,off) e.pc=pbase+off-1 e.stk={} e.lp={} e.seg=nil e.mir=nil e.burst=0 if not e.heli then e.ex=e.ex or e.x end end local function jump(e,off) e.pc=pbase+off-1 e.mir=nil end local function fetch(e) e.pc=e.pc+1 return peek(e.pc) end local function ror5(a) local c=0 for _=1,5 do local nc=a&1 a=(a>>1)|(c<<7) c=nc end return a&0xf0 end function dive_facing(e,dx,dy) if e.heli then if dx~=0 then e.hface=dx<0 and 1 or 0 end return end local v=peek(bank_b+(ror5(dx&0xff)|(dy&0x0f))) if v<0x80 then e.facing=v end end local function dive_move(e,dx,dy) dx=dx*(e.mir or 1) e.x=(e.x+dx)%256 e.y=(e.y+dy)%256 if e.ex then e.ex=e.ex+dx end e.fdx=dx e.fdy=dy dive_facing(e,dx,dy) end local offx_lo=fld_x0-10 local offx_hi=fld_x1+10 function dive_offx(e) if not e.ex then return e.x>=246 end return e.ex<offx_lo or e.ex>offx_hi end function dive_bounce(e) e.mir=-(e.mir or 1) end function dive_redive(e) dive_start(e,dive_map(2,e.x,p_x,0)) end function dive_offy(e) return e.y>=192 and e.y<208 end function dive_tick(e) for _=1,48 do if e.seg then local s=e.seg dive_move(e,s[1],s[2]) s[3]=s[3]-1 if s[3]<=0 then e.seg=nil end return not dive_offy(e) end local b=fetch(e) if b==0x80 then return false elseif b==0x81 then local dx,dy=sgnb(fetch(e)),sgnb(fetch(e)) e.seg={dx,dy,math.max(1,fetch(e))} elseif b==0x82 then local n=fetch(e) table.insert(e.lp,{e.pc,n}) e.burst=0 elseif b==0x83 then local l=e.lp[#e.lp] if l then l[2]=l[2]-1 if l[2]>0 then e.pc=l[1] else table.remove(e.lp) end end elseif b==0x84 then local hi,lo=fetch(e),fetch(e) table.insert(e.stk,e.pc) e.pc=pbase+(hi-0x70)*256+lo-1 elseif b==0x85 then if#e.stk>0 then e.pc=table.remove(e.stk) end elseif b==0x86 then jump(e,dive_map(2,e.x,p_x,0)) elseif b==0x87 then if wcfg_f87[math.min(wave,10)]==1 and not beg_eased()then jump(e,dive_map(3,e.x,p_x,0)) end elseif b==0x88 then jump(e,dive_map(4,e.x,p_x,16)) elseif b==0x7c then add_puff(e.x,e.y) e.pf=title_t elseif b>=0x7d and b<=0x7f then drop_bomb(e,b) else dive_move(e,sgnb(b),sgnb(fetch(e))) return not dive_offy(e) end end return false end end package.preload["boat"]=function(...) local boat_tower={7,6,3,2,2,7,6,3,2,2,8,5,3,3,2,8,5,3,3,2,8,6,2,2,2, 8,6,2,2,2,8,6,2,2,2,8,6,2,2,2,8,6,2,2,2,8,6,2,2,2,8,6,2,2,2,8,6,2,2,3, 8,6,2,2,3,8,6,2,2,3,8,6,2,2,3, 7,5,2,3,3,8,5,3,3,2} local tower_sink_r,tower_sink_l=15,16 local function draw_tower(e,x,y,surf) local i=5*e local cx,top=x+boat_tower[i+1],y+boat_tower[i+2] for c=0,2 do for py=top,top+boat_tower[i+3+c]-1 do pix(cx-1+c,py,surf and py>=surf and 7 or 9) end end for py=top-3,top-1 do pix(cx,py,surf and py>=surf and 7 or 12) end end local function boat_hull(s,x,y) spr(256+spr_boat+2*math.abs(s),x,y,0,1,s<0 and 1 or 0,0,2,2) end local function boat_body(step,x,y) boat_hull(step,x,y) draw_tower(step+7,x,y) end function boat_spr(step,x,y) mono.draw_solid(boat_body,step,x,y) end local function boat_sink(right,x,y,ax) wspr(256+spr_boat+14,x,y,2,2,right and 0 or 1,ax) draw_tower(right and tower_sink_r or tower_sink_l,x,y,scry(waves_dy(ax))) end local boat_bot={12,11,11,11,11,11,11,11,11,11,11,11,11,11,12} function boat_init(sx) p_x=sx or boat_start p_st=0 p_t=0 hazards_clear() bombs={} p_y=waves_y(p_x) p_px,p_py=p_x,p_y msl=nil want_fire=false p_dir=1 fire_held=btn(4)or btn(5) want_skip=false armed=false end local function respawn() boat_init() mines_offscreen() end function boat_input() if demo then return end local f=btn(4)or btn(5) if f and not fire_held then want_fire=true end fire_held=f if p_st==0 and f then armed=true end if p_st>=2 and fp()then want_skip=true end end local function player_upd() local tx=p_x if demo then tx=demo_tx else if btn(2)then tx=clamp_lo end if btn(3)then tx=clamp_hi end end local d=tx-p_x local spd=boat_dx*(armed and 1 or 2) if math.abs(d)>1 then p_x=p_x+spd*sgn(d) p_dir=sgn(d) end p_y=waves_y(p_x) end function boat_move() if p_st==0 then player_upd() player_upd() if demo then want_fire=not msl end if want_fire and not msl then msl={x=p_x,y=p_y-4,px=p_x,py=p_y-4} shots_fired=shots_fired+1 fire_sound() end end want_fire=false end function lose_life() if not died then died=true if not demo then lives=lives-1 end if lives<=0 then go_gameover() return true end recall_enemies() form_t=0 end end function skip_death() want_skip=false sfx_stop(3) if lose_life()then return end force_formation() respawn() end function boat_tick() if p_st~=0 and want_skip then skip_death()return end if p_st==1 then local dy=math.floor(p_t/3)-5 p_y=p_y+dy p_x=p_x+2*p_dir wrap_x() p_t=p_t+1 if p_t%11==0 and p_t<33 then psfx(sfx_playerhit,3)end if dy>0 and p_y>=waves_y(p_x)then p_st=2 lsfx(sfx_playersink,3) end elseif p_st==2 then p_x=p_x+1.25*p_dir wrap_x() p_y=p_y+sink_dy if p_y>=216 then p_st=3 p_t=0 end elseif p_st==3 then p_t=p_t+1 if p_t==sink_cut then sfx_stop(3)end if p_t==25 then if lose_life()then return end elseif p_t>25 and formation_ready()then form_t=form_t+1 if form_t>=28 then respawn()end elseif p_t>=25+recall_ofs then force_formation() respawn() end end end function wrap_x() if p_x>fld_x1 then p_x=fld_x0 elseif p_x<fld_x0 then p_x=fld_x1 end end function player_hit() p_st=1 p_t=0 died=false armed=false hf_clear() psfx(sfx_playerhit,3) end function recall_enemies() armed=false for _,e in ipairs(ens)do if e.st==3 then redescend(e) end end booms={} msl=nil end function force_formation() armed=false for _,e in ipairs(ens)do if e.st==1 then e.st=2 elseif e.st~=2 then e.st=0 e.x=e.tx e.y=e.ty e.ph=0 e.fdx=slide e.fdy=0 end end bombs={} booms={} hazards_clear() msl=nil end function formation_ready() for _,e in ipairs(ens)do if e.st==1 or e.st==3 or e.st==4 or e.st==5 then return false end end return true end function msl_tick() if not msl then return end local y0=msl.y msl.y=msl.y-msl_dy if enemies_hit(msl.x,msl.y,y0)or hazards_hit(msl.x,msl.y)then shots_hit=shots_hit+1 msl=nil elseif msl.y<fld_y0 then msl=nil end end local spin_s={0,4,7,-7,-4} local function draw_missile() if not msl then return end local age=p_y-4-msl.y local f=age<12 and 0 or age<24 and 1 or 2+oframe%3 local hgt,nose=({2,2,4,4,4})[f+1],({0,0,2,2,2})[f+1] spr(256+spr_missile+f,scrx(lerpd(msl.px,msl.x))-3, scry(lerpd(msl.py,msl.y))-nose,0,1,0,0,1,hgt) end local function sea_tilt(ax) local sl=waves_dy(ax+7)-waves_dy(ax-7) local s=math.floor(math.atan(sl*scale_y,14*scale_x)/(math.pi/36)+.5) return math.max(-7,math.min(7,s)) end function boat_draw() if p_st==3 then return end local ax=lerpd(p_px,p_x) local x,y=scrx(ax),scry(lerpd(p_py,p_y)) if p_st==1 then boat_spr(spin_s[p_t//2%5+1],x-8,y-8) return end if p_st==2 then mono.draw_solid(boat_sink,p_dir>=0,x-8,y-8,ax) return end y=scry(waves_dy(ax)) local s=sea_tilt(ax) boat_spr(s,x-8,y-boat_bot[s+8]+1) draw_missile() end end package.preload["bombs"]=function(...) function drop_bomb(e,op) if op==0x7d and(wcfg_bomb[math.min(wave,10)]==0 or e.heli)then return end if e.heli then if(e.bt or 999)<heli_round_gap then return end if diff==0 and(e.burst or 0)>=beg_heli_burst then return end e.burst=(e.burst or 0)+1 e.bt=0 heli_fire_round() end if#bombs>=32 then return end local dx,dy=0,bomb_dy if op==0x7f then local v=peek(aim_b+math.floor(e.x/16)*16+math.floor(p_x/16)) dy=v%16 dx=v>>4 if dx>=8 then dx=dx-16 end end local w=math.min(wave,10) local bc=e.heli and 14 or w==4 and 9 or(w>=5 and not e.heli and e.ty>0x2a)and 14 or 11 table.insert(bombs,{x=e.x,y=e.y+4,px=e.x,py=e.y+4, dx=dx,dy=dy, grn=op==0x7f and not e.heli,heli=e.heli, bc=bc}) end function bombs_tick() prune(bombs,function(b) b.x=b.x+b.dx b.y=b.y+b.dy if p_st==0 and math.abs(b.x-p_x)<8 and math.abs(b.y-p_y)<5 then boom(p_x,p_y) psfx(sfx_bombsplash) player_hit() return false elseif b.y>=waves_y(b.x)then splash(b.x) psfx(sfx_bombsplash) return false end return true end) end function bombs_draw() for _,b in ipairs(bombs)do local x,y=scrx(lerpd(b.px,b.x)),scry(lerpd(b.py,b.y)) if b.heli then rect(x,y-1,2,2,7) else if b.bc and b.bc~=14 then pal(14,b.bc)end if b.grn then pal(7,11)end mono.draw_solid(spr,256+spr_bomb,x-3,y-2,0) if(b.bc and b.bc~=14)or b.grn then pal_reset()end end end end end package.preload["enemies"]=function(...) function enemies_init() ens={} bombs={} local w=math.min(wave,10) local h=tmpl_b+(w-1)*120 for i=0,39 do local x,y,st=peek(h+i*3),peek(h+i*3+1),peek(h+i*3+2) local e={x=x,y=y,tx=x,ty=y,st=st,ph=0,i=i, heli=st==0 and y<16,pts=0,fdx=1,fdy=0} dive_facing(e,1,0) table.insert(ens,e) end local lo,hi=300,0 for _,e in ipairs(ens)do if e.st==0 then lo=math.min(lo,e.tx) hi=math.max(hi,e.tx) end end slide_mag=math.max(0.25,(170-(hi-lo))/(form_secs*60*ostep*gspeed)) slide=slide_mag lper,lt={},{} local base,flo=diff_base[diff+1],diff_floor[diff+1] local ram={ramp1,ramp2,ramp3} for k=1,3 do lper[k]=math.max(flo[k],base[k]-(wave-1)*ram[k]) lt[k]=lper[k] end llists={llist1,llist2,llist3} end local function y_crowded(e,y,states) for _,o in ipairs(ens)do if o~=e and states[o.st]and math.abs(o.y-y)<enemy_dy then return o end end return nil end local climbing={[4]=true} function redescend(e) local y=224 if enemy_dy>0 then local o=y_crowded(e,y,climbing) while o do y=o.y-enemy_dy o=y_crowded(e,y,climbing) end end e.st=4 e.y=y%256 e.ex=nil end local function launch(e) e.st=3 e.bt=999 if e.heli then e.pts=50 psfx(sfx_helilaunch) else e.pts=20 end dive_start(e,dive_map(1,e.x,e.y,0)) end local diving={[3]=true} function dive_separate(e) if enemy_dy<=0 then return end local o=y_crowded(e,e.y,diving) if o and math.abs(o.x-e.x)<16 then local lower=(e.y>o.y or(e.y==o.y and e.i>o.i))and e or o lower.y=(lower.y+dive_push)%256 end end function beg_eased() return diff==0 and wave>=beg_ease_wave end local function n_diving() local n=0 for _,e in ipairs(ens)do if e.st==3 then n=n+1 end end return n end function launch_tick() if not armed or p_st~=0 then return end for k=1,3 do lt[k]=lt[k]-1 if lt[k]<=0 then lt[k]=lper[k] if not(beg_eased()and n_diving()>=beg_max_dive)then for _,i in ipairs(llists[k])do local e=ens[i+1] if e.st==0 then launch(e) break end end end end end end function enemies_tick() local lo,hi=300,0 for _,e in ipairs(ens)do if e.st==0 then lo=math.min(lo,e.tx) hi=math.max(hi,e.tx) end end if lo<32 then slide=slide_mag elseif hi>=202 then slide=-slide_mag end alive=0 for _,e in ipairs(ens)do if e.st~=2 then alive=alive+1 end e.tx=e.tx+slide if e.st==0 then e.x=e.tx e.fdx=slide e.fdy=0 e.facing=slide>0 and 0 or 8 e.hface=slide>0 and 0 or 1 elseif e.st==1 or e.st==5 then e.ph=e.ph+1 if e.ph>=expl_of then if e.st==1 then e.st=2 else redescend(e) end end elseif e.st==3 then e.bt=(e.bt or 0)+1 if e.y>=waves_y(e.x)then e.st=5 e.ph=0 else local live=dive_tick(e) dive_separate(e) if dive_offx(e)then if e.heli then redescend(e)else dive_bounce(e)end elseif not live then if e.heli or dive_offy(e)then redescend(e) else dive_redive(e) end elseif p_st==0 and math.abs(e.x-p_x)<12 and math.abs(e.y-p_y)<7 then e.st=5 e.ph=0 psfx(sfx_ram) player_hit() end end elseif e.st==4 then e.x=e.tx e.y=(e.y+1)%256 dive_facing(e,0,1) if e.y==e.ty then e.st=0 end end end if p_st==0 and alive==0 and not bomber and not cruise and#bombs==0 then wave_cleared() end end local function cellpx(id,c,r) local v=peek(0x6000+id*32+r*4+c//2) return c%2==0 and v&15 or v>>4 end local function glyph_px(e,sx,sy) local base,col,row,w,h if e.heli then base=spr_heli+2*(e.hface or 0) col,row,w,h=sx-scrx(e.x)+8,sy-scry(e.y)+4,16,8 else local f=e.facing or 0 base=spr_plane+(f<8 and 2*f or 32+2*(f-8)) col,row,w,h=sx-scrx(e.x)+8,sy-scry(e.y)+8,16,16 end if col<0 or col>=w or row<0 or row>=h then return false end if e.heli and row==0 then local tx,tw=heli_blade_span(e.hface) if col>=tx and col<tx+tw then return false end end return cellpx(base+col//8+row//8*16,col%8,row%8)~=0 end local function msl_hit(e,mx,my,my0) if mx>e.x-7 and mx<=e.x+6 and my-14>=e.y-20 and my-14<e.y then return true end if math.abs(mx-e.x)>13 then return false end local bx=scrx(mx) for sy=scry(my)-2,scry(my0)do if glyph_px(e,bx,sy)or glyph_px(e,bx+1,sy)then return true end end return false end function enemies_hit(mx,my,my0) for _,e in ipairs(ens)do if(e.st==0 or e.st==3 or e.st==4) and msl_hit(e,mx,my,my0 or my)then e.st=1 e.ph=0 local v=e.pts if v==0 then v=e.heli and 20 or 10 end add_score(v) if e.heli then sd_heli=sd_heli+1 psfx(v>20 and sfx_divehelikill or sfx_helikill) else sd_plane=sd_plane+1 psfx(v>10 and sfx_diveplanekill or sfx_planekill) end return true end end return false end local function is_green(e,w) return w>=5 and not e.heli and e.ty>0x2a end local function enemy_pal(e,w) if w==4 then pal(14,12) pal(11,9) return true elseif is_green(e,w)then pal(14,11) return true end return false end function enemies_draw() local w=math.min(wave,10) for _,e in ipairs(ens)do if e.st==1 then draw_boom(e.x,e.y,e.ph,e.heli and xpl_h or xpl_p, (e.heli or is_green(e,w))and 12 or 9,nil,e.heli) elseif e.st==5 then draw_boom(e.x,e.y,e.ph,xpl_r,is_green(e,w)and 12 or 9,3) elseif e.st~=2 then local ax if e.ex then ax=lerpd(e.pex,e.ex) else ax=lerpw(e.px,e.x) end local x,y=scrx(ax),scry(lerpw(e.py,e.y)) local remapped=enemy_pal(e,w) if e.heli then wspr(256+spr_heli+2*(e.hface or 0),x-8,y-4,2,1,0,ax) heli_blade_black(x-8,y-4,e.hface) else local f=e.facing or 0 local base=f<8 and 2*f or 32+2*(f-8) wspr(256+spr_plane+base,x-8,y-8,2,2,0,ax) end if remapped then pal_reset()end end end bombs_draw() end end package.preload["hazards"]=function(...) function hazards_init() mines={} local w=math.min(wave,10) local mx={48,160} local my={sea_mid+10,sea_mid+12} for i=1,wcfg_mine[w]do table.insert(mines,{x=mx[i],y=my[i],dir=-1}) end bomber=nil cruise=nil bomber_t=bomber_reload cruise_t=cruise_reload bomber_q=wcfg_bomber[w]==1 and quota or 0 cruise_q=wcfg_cruise[w]==1 and quota or 0 hz_side=0 ck_n,ck_t=0,0 end function hazards_clear() if mines==nil then hazards_init()end bomber=nil cruise=nil ck_n=0 end function mines_offscreen() for i,m in ipairs(mines)do m.x=fld_x1+12+(i-1)*112 m.px=m.x end end local function mines_tick() for _,m in ipairs(mines)do m.x=m.x+mine_dx*m.dir if m.dir<0 and m.x<fld_x0-8 then m.x=fld_x1+8 elseif m.dir>0 and m.x>fld_x1+8 then m.x=fld_x0-8 end if p_st==0 and math.abs(m.x-p_x)<9 and math.abs(p_y-m.y)<7 and waves_y(m.x)>m.y-4 then boom(m.x,m.y) psfx(sfx_minehit) player_hit() end end end local function bomber_tick() if not bomber then return end local b=bomber b.x=b.x+bomber_dx*b.dir b.drop=b.drop+1 if b.drop>=6 and#bombs<32 then b.drop=0 table.insert(bombs,{x=b.x,y=b.y+4,px=b.x,py=b.y+4, dx=0,dy=bomb_dy,bc=11}) end if b.x<fld_x0-8 or b.x>fld_x1+8 then bomber=nil end end local function cruise_tick() if not cruise then return end local c=cruise c.x=c.x+cruise_dx*c.dir if p_st==0 and math.abs(c.x-p_x)<10 and math.abs(c.y-p_y)<8 then boom(p_x,p_y) psfx(sfx_ram) player_hit() cruise=nil elseif c.x<fld_x0-8 or c.x>fld_x1+8 then if c.pass==1 then c.pass=2 c.dir=-c.dir c.x=c.dir>0 and fld_x0 or fld_x1 c.y=sea_mid-sea_amp-2 else cruise=nil end end end local function cruise_kill_tick() if ck_n and ck_n>0 then ck_t=ck_t-1 if ck_t<=0 then ck_t=cruise_kill_gap ck_n=ck_n-1 psfx(sfx_cruisekill) end end end function hazards_tick() mines_tick() bomber_tick() cruise_tick() cruise_kill_tick() end local bomber_alt={26,88,96,120} function hazards_spawn() if not armed or p_st~=0 then return end if alive==0 then return end if not bomber and bomber_q>0 then bomber_t=bomber_t-1 if bomber_t<=0 then bomber_t=bomber_reload hz_side=hz_side+1 local r=hz_side%2==0 bomber={x=r and fld_x0 or fld_x1,y=bomber_alt[hz_side%4+1], dir=r and 1 or-1,drop=0} end end if not cruise and cruise_q>0 then cruise_t=cruise_t-1 if cruise_t<=0 then cruise_t=cruise_reload hz_side=hz_side+1 local r=hz_side%2==0 cruise={x=r and fld_x0 or fld_x1,y=26,dir=r and 1 or-1,pass=1} psfx(sfx_cruisealert) end end end local cru_body={[1]={-6,16},[-1]={-18,4}} local function cruise_x_hit(c,mx) local sx,cx,s=scrx(mx),scrx(c.x),cru_body[c.dir] return sx+1>=cx+s[1]and sx<=cx+s[2] end local function crosser_hit(c,mx,my,snd,xhit) if c and(xhit and xhit(c,mx)or not xhit and math.abs(mx-c.x)<8) and math.abs(my-c.y)<8 then boom(c.x,c.y) add_score(100) psfx(snd) return true end end function hazards_hit(mx,my) if crosser_hit(bomber,mx,my,sfx_bomberkill)then bomber_q=bomber_q-1 bomber=nil sd_bomber=sd_bomber+1 return true end if crosser_hit(cruise,mx,my,sfx_cruisekill,cruise_x_hit)then cruise_q=cruise_q-1 cruise=nil sd_cruise=sd_cruise+1 ck_n,ck_t=cruise_kill_n-1,cruise_kill_gap return true end return false end local function mine_draw(m) local ax=lerpd(m.px,m.x) local sx,sy=scrx(ax)-8,scry(m.y)-8 local surf=scry(waves_dy(ax)) if sy>=surf then spr(256+spr_mine,sx,sy,0,1,0,0,2,2) elseif sy+16<=surf then spr(256+spr_mine_air,sx,sy,0,1,0,0,2,2) else clip(fld_ox,0,fld_w,surf) spr(256+spr_mine_air,sx,sy,0,1,0,0,2,2) clip(fld_ox,surf,fld_w,scr_h-surf) spr(256+spr_mine,sx,sy,0,1,0,0,2,2) clip_field() end end function mines_draw() for _,m in ipairs(mines)do mono.draw_solid(mine_draw,m)end end local cru_fr={{spr_cruise,0},{spr_cruise+10,0},{spr_cruise+21,0}} local cru_fl={{spr_cruise+5,0},{spr_cruise+16,0},{spr_cruise+26,0}} function cruise_frame(dir) local f=(time()//130)%3 local fr=(dir>0 and cru_fr or cru_fl)[f+1] return 256+fr[1],fr[2] end function hazards_draw() if bomber then spr(256+spr_bomber+(bomber.dir>0 and 0 or 4), scrx(lerpd(bomber.px,bomber.x))-12,scry(bomber.y)-4,0,1,0,0,3,1) end if cruise then local id,dx=cruise_frame(cruise.dir) spr(id,scrx(lerpd(cruise.px,cruise.x))-20+dx, scry(lerpd(cruise.py,cruise.y))-4,0,1,0,0,5,1) end end end package.preload["status"]=function(...) function add_score(v) if demo then return end score=(score+v)%10000 if score>hi then hi=score end end function hitop() if not hi or hi<=0 then return end cprint("HISCORE "..hi,1,12) end function reveal_lines(lines,t,step,y0,dy,c) for i=1,math.min(#lines,t//step+1)do cprint(lines[i],y0+i*dy,c) end end function cprint(s,y,c) local w=print(s,0,-8) print(s,(scr_w-w)//2,y,c) end function bigtext(s,y,ct,cb) cb=cb or ct local w=print(s,0,-16,ct,false,2) local x=(scr_w-w)//2 clip(0,y,scr_w,6) print(s,x,y,ct,false,2) clip(0,y+6,scr_w,6) print(s,x,y,cb,false,2) clip() end function status_draw() print(string.format("SCORE %06d",score),fld_ox+2,0,status_c,true) local s="WAVE "..wave if diff>0 then s=s..string.rep("+",diff)end local w=print(s,0,-8,status_c,true) print(s,fld_ox+(fld_w-w)//2+wave_dx,0,status_c,true) for i=1,math.min(math.max(lives-1,0),lives_max)do mono.draw_solid(spr,256+spr_life,fld_ox+fld_w-2-i*7,1,0) end end end package.preload["state_title"]=function(...) local orig_txt="ORIGINAL BY RODNEY MCAULEY" function title_init() title_t=0 fx_init() tbooms={} title_acc=0 sea_ph=0 p_x=0x70 tplanes={{x=0x10,y=0xe1,facing=0},{x=0xb0,y=0xe1,facing=0}} dive_start(tplanes[1],0x1800) dive_start(tplanes[2],0x1a00) tdone=false tdone_t=0 converge=false cx=0 lfast_t=nil show_orig=false show_remake=false remake_t=0 rplane_x=nil diff_blink=0 hs_t=0 disp_t=0 end local function title_step(e) if not dive_tick(e)then e.done=true end if#e.stk==0 and e.fdx==e.ldx and e.fdy==e.ldy then e.run=(e.run or 0)+1 else e.run=0 e.ldx=e.fdx e.ldy=e.fdy end if e.run>=20 and e.pf then e.fast=true lfast_t=lfast_t or title_t end end function title_settle() for _,e in ipairs(tplanes)do for _=1,4000 do if e.done or(e.run or 0)>=20 and e.pf then break end title_step(e) end end tdone=true tdone_t=expl_of+24 show_orig=true show_remake=true remake_t=title_t tbooms={} tplanes={} end function title_update() local dt=frame_dt() title_t=title_t+dt if diff_blink>0 then diff_blink=diff_blink-dt end sea_ph=sea_ph-0.5*dt if not tdone then title_acc=title_acc+title_vspeed*dt local steps=math.floor(title_acc) title_acc=title_acc-steps local fin=false if converge then cx=math.max(0,cx-4*steps) tplanes[1].x=123-cx tplanes[2].x=123+cx if cx<=0 then fin=true end else if lfast_t and title_t>=lfast_t+22 then tplanes[2].fast=true end for _,e in ipairs(tplanes)do if not e.done and not converge then for _=1,steps*(e.fast and 2 or 1)do if not e.done then title_step(e)end if e.fast and e.fdy==0 and e.y>100 then tplanes[1].x=24 tplanes[1].y=110 tplanes[1].fdx=2 tplanes[1].facing=0 tplanes[2].x=222 tplanes[2].y=110 tplanes[2].fdx=-2 tplanes[2].facing=8 converge=true cx=99 end end end end end if fin then tdone=true for i=0,5 do table.insert(tbooms,{x=45+i*31,y=98,ph=0}) end end else tdone_t=tdone_t+dt if tdone_t>=expl_of then show_orig=true if not rplane_x and tdone_t>=expl_of+90 then rplane_x=-8 end end if rplane_x and not show_remake then rplane_x=rplane_x+2*dt if rplane_x>250 then show_remake=true remake_t=title_t end end end prune(tbooms,function(b)b.ph=b.ph+0.5*dt return b.ph<expl_of end) if title_t>20 and not combo_tick()then if xp()then start_game() elseif lp()or rp()then set_diff(lp()and-1 or 1) if not show_remake then title_settle()end remake_t=title_t elseif show_remake and title_t>=remake_t+300 then goto_demo() end end end local function diff_draw() if diff_blink<=0 or time()%500<300 then cprint("DIFFICULTY: "..diff_names[diff+1],95,10) end cprint("<- / -> TO CHANGE DIFFICULTY",103,5) end function title_draw() cls(0) clip_field() waves_draw() clip() hitop() clip_field() draw_puffs(6) for _,b in ipairs(tbooms)do draw_boom(b.x,b.y,b.ph,xpl_p,9) end clip() if tdone then if show_orig then local ramp={1,5,4,9} cprint(orig_txt,68,ramp[math.min((tdone_t-expl_of)//8+1,4)]) end if show_remake then cprint("REMAKE BY DAVE TEMPLIN",78,14) if diff_blink>0 or time()%1000<600 then cprint("PRESS FIRE TO START",87,7) end diff_draw() elseif rplane_x then clip(0,78,math.max(rplane_x,0),8) cprint("REMAKE BY DAVE TEMPLIN",78,14) clip_field() spr(256+spr_plane,rplane_x-8,72,0,1,0,0,2,2) clip() end else for _,e in ipairs(tplanes)do if not e.done then local f=e.facing or 0 local base=f<8 and 2*f or 32+2*(f-8) spr(256+spr_plane+base,scrx(e.x)-6,scry(e.y)-11,0,1,0,0,2,2) end end end end end package.preload["state_attract"]=function(...) local pg_hold=360 function set_diff(delta,to_title) local nd=clamp(0,diff+delta,2) if nd==diff then return false end diff=nd pmem(1,diff) psfx(sfx_blip) if to_title then goto_title() title_settle() end diff_blink=120 return true end local hs_hold=240 function hs_reset_tick() if not(btn(4)and btn(1))then hs_t=0 return false end local before=hs_t or 0 hs_t=before+frame_dt() if before<hs_hold and hs_t>=hs_hold then hi=0 prev_hi=0 pmem(0,0) psfx(sfx_blip) end return true end function disp_mode_tick() if not(btn(4)and btn(0))then disp_t=0 return false end disp_t=(disp_t or 0)+frame_dt() if disp_t>=hs_hold then disp_t=disp_t-hs_hold mono.cycle() psfx(sfx_blip) end return true end function combo_tick() local hs=hs_reset_tick() local dm=disp_mode_tick() return hs or dm end local function pg_tick(nxt,hold) local dt=frame_dt() pg_t=pg_t+dt oframe=(oframe+1)%4 sea_ph=sea_ph-0.5*dt if combo_tick()then return end if xp()then start_game() elseif lp()or rp()then set_diff(lp()and-1 or 1,true) elseif pg_t>(hold or pg_hold)then nxt() end end function page_init() pg_t=0 sea_ph=0 oframe=0 hs_t=0 disp_t=0 end local demo_dwell={40,96,48,64,96,80,32,64,96,48,24,16,64,80,48,64} function demo_ai() if demo_dir>0 and demo_tx>=0xd0 then demo_dir=-1 elseif demo_dir<0 and demo_tx<=0x14 then demo_dir=1 end demo_tx=clamp(clamp_lo,demo_tx+demo_dir*2,clamp_hi) demo_dt=demo_dt+1 if demo_dt>=demo_dwell[demo_seg+1]then demo_dt=0 demo_dir=-demo_dir demo_seg=(demo_seg+1)%16 end end function demo_init() demo=true wave=1 score=0 lives=start_lives sd_plane,sd_heli,sd_bomber,sd_cruise=0,0,0,0 shots_fired,shots_hit=0,0 demo_tx=120 demo_dir=1 demo_seg=0 demo_dt=0 demo_t=0 hs_t=0 disp_t=0 play_init() armed=true end function demo_update() demo_t=demo_t+frame_dt() play_update() if combo_tick()then return end if lp()or rp()then demo=false sfx_stop(2) set_diff(lp()and-1 or 1,true) return end local dying=p_st==1 or p_st==2 or(p_st==3 and p_t<25) local len=demo_secs*60 local timeout=demo_t>len and not dying or demo_t>len+pg_hold if timeout or wave>1 or xp()then demo=false sfx_stop(2) if xp()then start_game()else goto_points()end end end local function tbl_head(s) cls(0) waves_draw() bigtext("WAVY NAVY",18,11,9) if s then cprint(s,36,14)end hitop() end local pt_rows={ {"plane","10 OR 20 POINTS"}, {"heli","20 OR 50 POINTS"}, {"bomber","100 POINTS"}, {"cruise","100 POINTS"}, } local pt_step=120 local pt_done=(#pt_rows-1)*pt_step+300 function points_update() pg_tick(goto_ranks,pt_done) end function points_draw() tbl_head() local shown=math.min(#pt_rows,pg_t//pt_step+1) local y=48 for i=1,shown do local r=pt_rows[i] if r[1]=="plane"then spr(256+spr_plane,46,y-4,0,1,0,0,2,2) elseif r[1]=="heli"then spr(256+spr_heli,46,y,0,1,0,0,2,1) heli_blade_black(46,y) elseif r[1]=="bomber"then spr(256+spr_bomber,42,y,0,1,0,0,3,1) else local cid,cdx=cruise_frame(1) spr(cid,32+cdx,y,0,1,0,0,5,1) end print(r[2],80,y+1,7) y=y+16 end end function ranks_update() pg_tick(goto_title) end function ranks_draw() tbl_head("RANK TABLE") for i=1,10 do local col=(i-1)//5 print(wcfg_rank[i],30+col*110,56+((i-1)%5)*12,7) end end end package.preload["state_ready"]=function(...) function ready_init() waves_init() ready_t=0 racc=0 p_x=120 p_st=0 p_y=waves_y(p_x) p_px,p_py=p_x,p_y msl=nil end function ready_update() local dt=frame_dt() ready_t=ready_t+dt racc=(racc or 0)+ostep*gspeed*dt while racc>=1 do racc=racc-1 p_px=p_x if btn(2)then p_x=math.max(p_x-2*boat_dx,clamp_lo)end if btn(3)then p_x=math.min(p_x+2*boat_dx,clamp_hi)end end draw_fraction=racc p_y=waves_y(p_x) if ready_t>20 and fp()then goto_play()end end function ready_draw() cls(0) clip_field() waves_draw() boat_draw() clip() status_draw() if time()%1000<600 then cprint("GET READY! PRESS FIRE",70,7)end end end package.preload["state_play"]=function(...) function play_init() waves_init() fx_init() enemies_init() hazards_init() boat_init(p_x) oacc=0 oframe=0 end local function snap_prev() p_px,p_py=p_x,p_y if msl then msl.px,msl.py=msl.x,msl.y end for _,e in ipairs(ens)do e.px=e.x e.py=e.y e.pex=e.ex end for _,b in ipairs(bombs)do b.px=b.x b.py=b.y end for _,m in ipairs(mines)do m.px=m.x end if bomber then bomber.px=bomber.x end if cruise then cruise.px,cruise.py=cruise.x,cruise.y end end function otick() snap_prev() oframe=(oframe+1)%4 boat_move() if demo then demo_ai()end if armed or p_st~=0 then waves_tick() hazards_tick() end boat_tick() msl_tick() enemies_tick() bombs_tick() if oframe==0 then launch_tick() hazards_spawn() end fx_tick() end function play_update() boat_input() chopper_sound() oacc=oacc+ostep*gspeed*frame_dt() while oacc>=1 do oacc=oacc-1 otick() end draw_fraction=oacc end function clear_board() ens={} bombs={} mines={} bomber=nil cruise=nil msl=nil p_st=3 armed=false end function wave_cleared() sfx_stop(2) hf_clear() wave=wave+1 if demo then return end bonus_ships=math.max(lives,0) goto_clear() end function play_draw() cls(0) clip_field() waves_draw() mines_draw() fx_draw() if armed or p_st~=0 then enemies_draw() hazards_draw() end boat_draw() clip() if demo then hitop()else status_draw()end if not armed and p_st==0 and time()%1000<600 then cprint("GET READY! PRESS FIRE",70,7) end end end package.preload["state_clear"]=function(...) function clear_init() clear_t=0 sea_ph=0 clear_ph="jingle" tally=0 tally_t=0 hold_t=0 dtally=0 dcleared=waves_cleared() dbonus=diff_bonus[diff+1]*dcleared clear_song=wcfg_song[math.min(wave-1,10)] play_song(clear_song) end local function clear_tally() tally_t=tally_t+frame_dt() if tally_t>12 and fp()then while tally<bonus_ships do tally=tally+1 add_score(50) end elseif tally<bonus_ships and tally_t>=bonus_step then tally_t=0 tally=tally+1 add_score(50) play_song(10) end if tally>=bonus_ships and tally_t>bonus_step then if bonus_ships<9 then local extra=wave-1>=late_lives_wave and diff_wave_lives[diff+1]or 0 lives=math.min(lives+1+extra,9) end clear_ph=dbonus>0 and"dwait"or"hold" hold_t=0 end end local function clear_dtally() tally_t=tally_t+frame_dt() local per=diff_bonus[diff+1] local step=math.max(bonus_step//math.max(wave//6,1),8) if tally_t>12 and fp()then while dtally<dcleared do dtally=dtally+1 add_score(per) end elseif dtally<dcleared and tally_t>=step then tally_t=0 dtally=dtally+1 add_score(per) play_song(10) end if dtally>=dcleared and tally_t>step then clear_ph="hold" hold_t=0 end end function clear_update() clear_t=clear_t+frame_dt() if clear_ph=="jingle"then if clear_t>40 and(not music_playing()or fp())or clear_t>2400 then song_stop() clear_ph="bonus" tally_t=0 end elseif clear_ph=="bonus"then clear_tally() elseif clear_ph=="dwait"then hold_t=hold_t+frame_dt() if hold_t>=120 or fp()then clear_ph="dbonus" tally_t=0 end elseif clear_ph=="dbonus"then clear_dtally() elseif clear_ph=="hold"then hold_t=hold_t+frame_dt() if hold_t>=240 or fp()then goto_ready()end end end function clear_draw() cls(0) waves_draw() local r=math.min(wave,10,diff_rankcap[diff+1]) cprint(r>=10 and"YOU HAVE BECOME"or"YOU HAVE BEEN PROMOTED TO",30,6) local odd=r==4 or r==5 bigtext(wcfg_rank[r],42,odd and 14 or 11,odd and 12 or 9) if clear_ph=="jingle"then local s=song_names[clear_song+1] local w=print(s,0,-8) local x=(scr_w-w-19)//2 local y=(scr_h-5)//2 spr(256+spr_note,x,y,0) print(s,x+10,y,6) spr(256+spr_note,x+w+12,y,0) else status_draw() local ships=clear_ph=="bonus"and tally or bonus_ships print("BONUS",70,70,9) if clear_ph~="bonus"or(clear_t//8)%2==0 then print("50 X",106,70,7) end clip_field() for i=1,ships do boat_spr(0,130+(i-1)*14,63) end clip() end if dbonus>0 and(clear_ph=="dwait"or clear_ph=="dbonus"or clear_ph=="hold")then local y=88 local function rprint(s,ry,c)print(s,100-print(s,0,-8),ry,c)end rprint(diff_names[diff+1],y,10) rprint("BONUS",y+10,10) if clear_ph=="hold"or(clear_t//8)%2==0 then print(diff_bonus[diff+1].." X",106,y+5,7) end local nw=print(dcleared,0,-16,9,false,2) local x=142 if dtally>0 and(clear_ph=="hold"or(clear_t//8)%2==0)then print(dtally,x+(nw-print(dtally,0,-16,9,false,2))//2,y+3,9, false,2) end print("WAVES",x+nw+5,y,7) print("CLEARED",x+nw+5,y+10,7) end end end package.preload["state_gameover"]=function(...) function gameover_init() gameover_t=0 gameover_ph=1 oacc=oacc or 0 if not booms then fx_init()end gameover_dirgeend=nil gameover_newhigh=hi>(prev_hi or 0) clear_board() sfx_stop(2) sfx_stop(3) hf_clear() play_song(9) end function gameover_update() local dt=frame_dt() gameover_t=gameover_t+dt oacc=oacc+ostep*gspeed*dt while oacc>=1 do oacc=oacc-1 waves_tick() fx_tick() end draw_fraction=oacc if gameover_ph==1 then if gameover_t>40 and(not music_playing()or gameover_t>2400)then gameover_dirgeend=gameover_dirgeend or gameover_t if gameover_t>=gameover_dirgeend+120 then gameover_ph=2 gameover_t=0 end end else if gameover_t<960 then if gameover_t>40 and fp()then gameover_t=960 end elseif gameover_t>960+1800 or fp()then goto_title() end end end local function breakdown_draw(y) local numw=0 for _,n in ipairs({sd_plane,sd_heli,sd_bomber,sd_cruise})do numw=math.max(numw,#tostring(n)*6) end local x0=80 local function cell(n,cx,cy) local s=tostring(n) print(s,cx+numw-#s*6,cy+1,7,true) end cell(sd_plane,x0,y) spr(256+spr_plane,x0+numw+6,y-4,0,1,0,0,2,2) cell(sd_heli,x0,y+12) spr(256+spr_heli,x0+numw+6,y+12,0,1,0,0,2,1) heli_blade_black(x0+numw+6,y+12) local x1=126 cell(sd_bomber,x1,y) spr(256+spr_bomber,x1+numw+6,y,0,1,0,0,3,1) cell(sd_cruise,x1,y+12) local cid,cdx=cruise_frame(-1) spr(cid,x1+numw+6+cdx,y+12,0,1,0,0,5,1) end function gameover_draw() if gameover_ph==1 then play_draw() cprint("YOUR ENTIRE FLEET WAS SUNK",62,8) return end cls(0) waves_draw() oframe=(time()//130)%4 cprint("YOU REACHED THE RANK OF",16,6) local r=math.min(wave,10,diff_rankcap[diff+1]) local odd=r==4 or r==5 bigtext(wcfg_rank[r],26,odd and 14 or 11,odd and 12 or 9) hitop() local total=sd_plane+sd_heli+sd_bomber+sd_cruise local acc=shots_fired>0 and math.floor(shots_hit*100/shots_fired)or 0 local lines={ "SCORE "..score, "DIFFICULTY: "..diff_names[diff+1], waves_cleared().." WAVES CLEARED", shots_fired.." SHOTS FIRED", "ACCURACY "..acc.."%", total.." ENEMIES SHOT DOWN", } reveal_lines(lines,math.max(gameover_t-120,0),120,34,9,7) if gameover_t>=#lines*120 then breakdown_draw(100)end if gameover_newhigh and time()%1000<600 then cprint("NEW HIGH SCORE!",8,10)end end end package.preload["game_state"]=function(...) local function set_state(label,init,update,draw) game_state=label draw_fraction=1 init() game_update=update game_draw=draw end function goto_title() set_state("title",title_init,title_update,title_draw) end function goto_demo() set_state("demo",demo_init,demo_update,play_draw) end function goto_points() set_state("points",page_init,points_update,points_draw) end function goto_ranks() set_state("ranks",page_init,ranks_update,ranks_draw) end function goto_ready() set_state("ready",ready_init,ready_update,ready_draw) end function goto_play() set_state("play",play_init,play_update,play_draw) armed=true end function goto_clear() set_state("clear",clear_init,clear_update,clear_draw) end function go_gameover() if hi>prev_hi then pmem(0,hi)end set_state("gameover",gameover_init,gameover_update,gameover_draw) end function waves_cleared() return math.max(wave-1-diff_start_wave[diff+1],0) end function start_game() demo=false score=0 lives=start_lives+diff_start_lives[diff+1] wave=start_wave+diff_start_wave[diff+1] sd_plane,sd_heli,sd_bomber,sd_cruise=0,0,0,0 shots_fired,shots_hit=0,0 prev_hi=hi goto_ready() end end require"constants" require"helpers" require"a2boot" require"sfx" require"pcm" require"waves" require"explode" require"dive" require"boat" require"bombs" require"enemies" require"hazards" require"status" require"state_title" require"state_attract" require"state_ready" require"state_play" require"state_clear" require"state_gameover" require"game_state" function BOOT() hi=pmem(0) diff=clamp(0,pmem(1),2) mono.load() demo=false title_t=0 goto_title() apple2.boot{skip=true,rgb=mono.fg(),bg_rgb=mono.bg()} end function TIC() if not game_update then BOOT()end if apple2.tic()then return end pcm.update() hf_pull() game_update() game_draw() mono.apply() end