Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- pico-8 cartridge // http://www.pico-8.com
- version 18
- __lua__
- -- main
- -- > by suwacrab
- _init = function()
- -- clear wram
- memset(mem_wrk,0x1b00)
- end
- _update60 = function()
- state_updt()
- end
- _draw = function()
- state_draw()
- -- print cpu usage
- local cpu = shl(stat(0),6)
- cpu = tostr(cpu,true)
- print(cpu,0,0,7)
- end
- -->8
- -- data
- -- > memory
- mem_chr = 0x0000 --> 0x2000
- mem_map = 0x2000 --> 0x1000
- mem_flg = 0x3000 --> 0x0100
- mem_bgm = 0x3100 --> 0x0100
- mem_sfx = 0x3200 --> 0x1100
- mem_wrk = 0x4300 --> 0x1b00
- mem_sav = 0x5e00 --> 0x0100
- mem_dst = 0x5f00 --> 0x0040
- mem_hst = 0x5f40 --> 0x0040
- mem_gpi = 0x5f80 --> 0x0080
- mem_fb0 = 0x6000 --> 0x2000
- mem_fb1 = mem_wrk
- -- > lookup tables
- lut_mesh = {
- [0]=
- 0x8000,0x8020,0xa020,0xa0a0;
- 0xa4a0,0xa4a1,0xa5a1,0xa5a5;
- 0xe5a5,0xe5b5,0xf5b5,0xf5f5;
- 0xfdf5,0xfdf7,0xfff7,0xffff;
- }
- state_name_lut = {
- title = 0;
- menu = 1;
- game = 2;
- }
- state_srct_lut = {}
- -- functions
- fsin = function(a)
- a = shr(a,16)/0x1.0000
- return -sin(a)
- end
- fcos = function(a)
- return fsin(a + 0x4000)
- end
- clmp = function(x,a,b)
- return
- x<a and a
- or x>b and b
- or x
- end
- -- keiki
- keiki = {}
- keiki.__index = keiki
- -->8
- -- keiki
- --|==== function reference ===|
- --[[
- * > function list:
- * keiki.new(len,def): creates new list w/ length `len` and optional callback `def`
- * keiki:add(mode,f) : adds new object w/ mode `mode` and optional callback `f`
- * keiki:del(id,f) : deletes object [id] and uses optional callback `f`
- * keiki:iter(f) : iterates through pool with callback `f`
- *
- * > structure info:
- * every pool has 4 attributes:
- ・ス> objs: the (0-indexed!) array of objects
- ・ス> len: the length of objects
- ・ス> last: a pointer to the last object in the
- pool. this is used for adding and removing
- objects from the pool's free list
- ・ス> alive: the number of alive objects
- * every object has 4 attributes, but you're free to add more to them:
- ・ス> mode: the object's "type". you can use this
- for using different types of objects inside one pool.
- ・ス> dead: whether or not the object's alive.
- ・ス> id: the object's index in the array.
- ・ >スnext: a pointer to the next object. used for the pool's free
- list.
- ]]
- --|==== functions =============|
- -- * > creates a new object pool
- -- * ・> len: length of army, must be under 0x10000.
- -- * ・> def: callback on every object. 1st is the list,2nd is index.
- keiki.new = function(len,def)
- local may = { objs = {},len = len,last = 0,alive = 0 }
- for i = 0,len-1 do
- may.objs[i] = { mode=0,dead=true,id=i,next=i+1 }
- if def then def(may,i,may.objs[i]) end
- end
- may.objs[len-1].next = 0xffff
- return setmetatable(may,keiki)
- end
- --> adds a new object to pool.
- -- > may: the list to add to
- -- > mode: the type of object
- -- > f: the callback on the new object. works the same as keiki.new's callback.
- keiki.add = function(may,mode,f)
- -- if there's no space, return nil.
- if may.alive == may.len then return end
- -- find the nearest object, reset it's attributes
- local hani = may.objs[may.last]
- hani.dead = false
- hani.mode = mode or 0;
- if f then f(may,hani.id,hani) end
- -- make sure this object will be the next free object once it's dead
- may.last = hani.next
- -- increment the alive count, then return the new object.
- may.alive = may.alive+1
- return hani;
- end
- --> deletes object [id] from the pool.
- -- > may: the list to remove from
- -- > id: the id of the object to remove.
- -- > f: callback on the deleted object. works the same as keiki.new's callback.
- keiki.del = function(may,id,f)
- -- if the deleted object doesn't exist, return nil.
- local hani = may.objs[id]
- if(hani==nil) then return end
- if(hani.dead) then return end
- -- reset variables.
- hani.dead = true
- if f then f(may,hani.id,hani) end
- -- hani's next object should be the last free object
- -- the new free object should be hani
- hani.next = may.last
- may.last = id
- -- decrement alive counter
- may.alive = may.alive-1
- return hani
- end
- --> iterates through pool with the callback f.
- -- > may: the pool to iterate through
- -- > f: the callback to use.
- -- >> 1st argument is the list, 2nd is the id, 3rd is the current object.
- keiki.iter = function(may,f)
- for i = 0,may.len-1 do
- f(may,i,may.objs[i])
- end
- end
- -->8
- -- game state
- game = {
- cur_state = 0
- }
- state_swap = function(new)
- game.cur_state = new
- end
- state_updt = function()
- local state = game.cur_state
- state_srct_lut[state]:updt()
- end
- state_draw = function()
- local state = game.cur_state
- state_srct_lut[state]:draw()
- end
- -- state objs
- state_title = {
- t = 0;
- swap_flag = false;
- updt = function(state)
- if state.t == 15 then
- state.swap_flag = true
- end
- state.t += 1
- end;
- draw = function(state)
- cls(0)
- if state.swap_flag then
- state_swap(state_name_lut.menu)
- end
- end;
- }
- state_menu = {
- t = 0;
- updt = function() end;
- draw = function()
- cls(0)
- state_swap(state_name_lut.game)
- end;
- }
- state_game = {
- -- vars
- did_init = false;
- t = 0;
- -- misc
- fx_objs = nil;
- fx_updt = function(state)
- local objs = state.fx_objs
- objs:iter(function(m,i,o)
- if o.dead then return end
- local spd,ang = o.spd,o.ang
- local px,py = o.px,o.py
- local t = o.t
- -- > 00h: orb lo
- if o.mode == 0 then
- local dt = shr(t,1)
- -- move
- local vx,vy = 0,0
- local x,y = px,py
- px += fcos(ang) * spd
- py += fsin(ang) * spd
- o.vx,o.vy = vx,vy
- o.px,o.py = px,py
- spd -= 0.2
- if(spd<0) spd=0
- o.spd = spd
- -- handle removal
- if dt >= 4 then
- objs:del(i)
- end
- -- > 01h: star
- elseif o.mode == 1 then
- py += o.spd
- o.px,o.py = px,py
- if(py >= 128+32) m:del(i)
- -- > 02h: explosion
- elseif o.mode == 2 then
- px += fcos(ang) * spd
- py += fsin(ang) * spd
- spd = clmp(spd-.1,0,32)
- o.spd = spd
- o.px,o.py = px,py
- -- > delete
- if(t>=16) objs:del(i)
- -- > 03h: debris 1
- elseif ({[3]=1,[4]=1,[5]=1})[o.mode] then
- px += fcos(ang) * spd
- py += fsin(ang) * spd
- spd = clmp(spd-.01,0,32)
- o.spd = spd
- o.px,o.py = px,py
- if(t>=140) objs:del(i)
- end
- end)
- end;
- fx_draw = function(state)
- local objs = state.fx_objs
- objs:iter(function(m,i,o)
- if o.dead then return end
- local px,py = o.px,o.py
- local t = o.t
- local odd = t%2==0
- -- > 00h: orb lo
- if o.mode == 0 then
- local dt = shr(t,1)
- if(odd) spr(56+dt,px-4,py-4)
- -- > 01h: star
- elseif o.mode == 1 then
- if(odd) spr(1,px-4,py-4)
- -- > 02h: explosion
- elseif o.mode == 2 then
- local dt = flr(shr(t,2))
- if(odd) then
- spr(192 + dt*2,px-8,py-8,2,2)
- end
- -- > 03h: debris 1
- elseif o.mode == 3 then
- local dt = flr(shr(t,2))%4
- spr(80+dt,px-4,py-4)
- -- > 04h: debris 2
- elseif o.mode == 4 then
- local dt = flr(shr(t,2))%4
- spr(84+dt,px-4,py-4)
- -- > 05h: debris 3
- elseif o.mode == 5 then
- local dt = flr(shr(t,2))%4
- spr(96+dt,px-4,py-4)
- end
- o.t = t+1
- end)
- end;
- fx_addbomb = function(state,x,y,spd,ang)
- local objs = state.fx_objs
- objs:add(2,function(m,i,o)
- o.px,o.py = x,y
- o.spd = spd
- o.ang = ang or 0
- o.t = 0
- end)
- end;
- -- player
- p1 = {};
- p2 = {};
- p1_shot = nil;
- p2_shot = nil;
- plr_updt = function(state)
- local p1 = state.p1
- state:plr_updtlock()
- state:plr_updtshot()
- state:plr_move()
- if(not p1.enter) state:plr_shoot()
- end;
- plr_updtlock = function(state)
- local p1 = state.p1
- local enmy = state.enm_objs
- -- > lock vars
- local lock_enmy = p1.lock_enmy
- -- > don't lock dead foes
- if lock_enmy then
- if enmy.objs[lock_enmy].dead then
- lock_enmy = nil
- end
- end
- -- > set vars
- p1.lock_enmy = lock_enmy
- end;
- plr_updtshot = function(state)
- local p1 = state.p1
- local p1_shot = state.p1_shot
- p1_shot:iter(function(m,i,o)
- if(o.dead) return;
- if o.mode == 0b00000000 then
- local x,y = o.px,o.py
- local vx,vy = o.vx,o.vy
- local ang = o.ang
- vx = fcos(ang) * o.spd
- vy = fsin(ang) * o.spd
- o.vx,o.vy = vx,vy
- x += vx; y += vy;
- o.px,o.py = x,y
- o.t += 1
- -- oob check
- local ox = x>=192 or x<-64
- local oy = y>=192 or y<-64
- if(ox or oy) then
- p1_shot:del(i)
- end
- end
- end)
- end;
- plr_shoot = function(state)
- local p1 = state.p1
- local p1_shot = state.p1_shot
- -- > update presses
- if btn(4) then
- if not p1.shot_press then
- if p1.power == 0 then
- if p1.shota_timer <= 0 then
- p1.shota_timer = 0x10
- end
- end
- p1.shot_press = true
- else
- -- handle focus
- if p1.shota_timer <= -8 then
- p1.shot_focus = true
- end
- end
- end
- if not btn(4) then
- p1.shot_focus = false
- p1.shot_press = false
- p1.focus_timer = 0
- end
- -- > update shots
- do
- if p1.power == 0 then
- if p1.shota_timer <= -8 then
- if p1.shot_focus then
- if p1.focus_timer%6==0 then
- for a = -1,1,2 do
- p1_shot:add(
- 0b00000000,
- function(m,i,o)
- local spread = (a*4)
- local shtspr = p1.focus_timer%16
- if(p1.vx~=0) spread *= .5
- o.t = 0
- o.ang =
- (shtspr*0x0040*a) - 0x4000
- o.ang = band(o.ang,0xffff)
- o.spd = 8
- o.px = p1.x + spread
- o.py = p1.y
- spr(56,p1.x + spread-4,o.py)
- sfx(0,.1)
- end
- )
- end
- end
- p1.focus_timer += 1
- end
- elseif p1.shota_timer <= 0 then
- elseif p1.shota_timer%4==0 then
- -- shoot middle shot
- local shf = 0x1.8 - p1.shota_timer/12
- shf *= 0x0c00
- local spd = 8
- p1_shot:add(0b00000000,
- function(m,i,o)
- o.t = 0
- o.ang = 0xc000
- o.spd = spd
- o.px,o.py = p1.x,p1.y-8
- spr(56,o.px-4,o.py-4)
- end
- )
- -- > shoot side shots
- for a = -1,1,2 do
- p1_shot:add(
- 0b00000000,
- function(m,i,o)
- local spread = (a*4)
- if(p1.vx~=0) spread *= .5
- o.t = 0
- o.ang = shf*a - 0x4000
- o.ang = band(o.ang,0xffff)
- o.spd = spd
- o.px = p1.x + spread
- o.py = p1.y
- spr(
- 56,
- (p1.x + spread*1)-4,
- o.py-4)
- end
- )
- end
- sfx(0)
- end
- end
- end
- -- > update timers
- p1.shota_timer -= 1
- if p1.shota_timer < -64 then
- p1.shota_timer = -64
- end
- end;
- plr_move = function(state)
- local p1 = state.p1
- if p1.enter then
- local et = p1.enter_time
- p1.y -= et/3
- et -= .15
- if et <= -3 then
- p1.enter = false
- end
- p1.enter_time = et
- else
- local spd = 1
- if p1.shot_focus then
- spd = .2
- end
- local vx,vy = 0,0
- if(btn(0)) vx -= spd
- if(btn(1)) vx += spd
- if(btn(2)) vy -= spd
- if(btn(3)) vy += spd
- -- > move
- p1.vx,p1.vy = vx,vy
- p1.x += vx
- p1.y += vy
- end
- end;
- plr_draw = function(state)
- local p1 = state.p1
- state:plr_drawlock()
- state:plr_drawstat()
- state:plr_drawshot()
- state:plr_drawship()
- end;
- plr_drawlock = function(state)
- local p1 = state.p1
- local enmy = state.enm_objs
- local lock_enmy = p1.lock_enmy
- -- draw lockon
- if lock_enmy then
- -- > reticle
- local obj = enmy.objs[lock_enmy]
- local t = state.t
- local dt = band(shr(t,1),3)
- local px,py = obj.px,obj.py
- local indx = 60
- if t%2==0 then
- indx += dt
- spr(indx,px-4,py-4)
- else
- indx = 14 - (dt*2)
- spr(indx,px-8,py-8,2,2)
- end
- -- > text
- if t%2==0 then
- indx = 2
- spr(indx,px-32,py-16,3,1)
- end
- end
- end;
- plr_drawstat = function(state)
- local p1 = state.p1
- local x,y = 0,8
- -- > draw lives
- if p1.lives > 0 then
- for i = 1,p1.lives do
- spr(21,x,y)
- x += 8
- end
- end
- -- > draw bombs
- x,y = 0,y+8
- if p1.bombs > 0 then
- for i = 1,p1.bombs do
- spr(22,x,y)
- x += 8
- end
- end
- end;
- plr_drawship = function(state)
- local p1 = state.p1
- -- > get pos & index
- local x,y = p1.x,p1.y
- local vx,vy = p1.vx,p1.vy
- local indx = 32
- if(vy<0) indx = 34
- if(vx<0) indx = 36
- if(vx>0) indx = 36 + 2
- if(p1.enter) indx = 34
- -- > draw afterburner
- if state.t%2==0 then
- local x = x - vx
- local y = y - vy
- local indx = 56
- local t = shr(state.t,1)
- indx += (t%4)
- y -= vy*(t%4)
- x -= vx*(t%4)
- spr(indx,x-4,y+2)
- palt()
- end
- -- > draw ship
- spr(
- indx,x-8,y-8,
- 2,2
- )
- end;
- plr_drawshot = function(state)
- local p1 = state.p1
- local p1_shot = state.p1_shot
- p1_shot:iter(function(m,i,o)
- if o.dead then return end
- if o.mode == 0b00000000 then
- local ang = o.ang
- local x,y = o.px,o.py
- local indx = 40
- local fx = false
- local rnd_ang = shr(ang + 0x0800,12)
- indx += rnd_ang%8
- if(ang<0) fx=true
- spr(
- indx,x-4,y-4,
- 1,1,
- fx,fx
- )
- end
- end)
- end;
- -- enemy
- stg_time = 0;
- stg_index = 0;
- enm_shot = nil;
- enm_obj = nil;
- enm_coro = cocreate(function(state)
- local t = state.stg_time
- local objs = state.enm_objs
- local function fwait(f)
- f = f or 1
- for i = 1,f do
- yield()
- t += 1
- state.stg_time = t
- end
- end
- -- main coroutine
- -- > stage 1
- if state.stg_index == 0 then
- -- > level text
- do
- local lim = 112
- for i = -lim,lim,2 do
- local s = 1
- if(i>0) s -= i/lim
- local x,y = 64 + i,64
- local oy = 8*s
- -- get color
- local clut = {
- [0]=7,15,14,13,
- 5,2,1,0
- }
- local clr = clut[8-flr(s*8)]
- rectfill(0,y-oy,128,y+oy,clr)
- -- get print x
- local is = (i+lim)/(lim*2)+.5
- local px = -48 + cos(is)*64
- print("stage 1",px,y-4,0)
- print("get ready! 笙・",px+32,y+2,0)
- fwait()
- end
- end
- fwait(30)
- -- > missiles
- do
- for i = 1,50 do
- local x = 8 + rnd(112)
- local y = -16
- objs:add(0,function(m,i,o)
- o.px,o.py = x,y
- o.t = 0
- o.hp = 5
- o.hbsize = 8
- local off = (rnd()-.5)
- o.d[0] =
- 0x4000 + off*0x2000
- o.d[1] = 3
- end)
- fwait(15/2)
- end
- end
- state.stg_index += 1
- end
- -- infinite loop
- while true do fwait() end
- end);
- enm_updt = function(state)
- -- > vars
- local p1 = state.p1
- local coro = state.enm_coro
- local objs = state.enm_objs
- -- > coroutine
- local on,excep = coresume(coro,state)
- if not on then
- stop(trace(co,excep))
- end
- -- deal damage
- objs:iter(function(m,i,o)
- if o.dead then return end
- local hp = o.hp
- local px,py = o.px,o.py
- -- > damaging
- local hbsize = o.hbsize
- state.p1_shot:iter(
- function(psht,ind,sht)
- if sht.dead then return end
- local x1,y1 =
- px-hbsize,py-hbsize
- local x2,y2 =
- px+hbsize,py+hbsize
- if sht.px<x2 and sht.px>=x1 then
- if sht.py<y2 and sht.py>=y1 then
- if(sht.dmg<=hp) psht:del(ind)
- hp -= sht.dmg
- p1.lock_enmy = i
- sfx(2)
- end
- end
- end
- )
- o.hp = hp
- end)
- -- update objects
- objs:iter(function(m,i,o)
- if o.dead then return end
- local px,py = o.px,o.py
- local hp = o.hp
- -- > 0 - straight missile
- if o.mode == 0 then
- local ang = o.d[0]
- local spd = o.d[1]
- px += fcos(ang)*spd
- py += fsin(ang)*spd
- o.px,o.py = px,py
- -- > death
- local ox = px<-64 or px>128+64
- local oy = py<-64 or py>128+64
- if ox or oy then
- m:del(o.id)
- end
- if hp <= 0 then
- for a = 0,15,4 do
- state:fx_addbomb(
- px,py,(a/4) * rnd(3),
- 0x1000*(a+state.t)
- )
- end
- for a = 0,4 do
- state.fx_objs:add(3+(a%3),
- function(m,i,o)
- o.px,o.py = px,py
- o.ang = a*0x2000
- o.ang += rnd()*0x1430
- o.spd = 1 + rnd(4)
- o.t = 0
- end
- )
- end
- end
- end
- -- damaging
- if hp <= 0 then
- circfill(px,py,12,7)
- objs:del(i)
- sfx(1)
- end
- end)
- end;
- enm_draw = function(state)
- local objs = state.enm_objs
- objs:iter(function(m,i,o)
- if o.dead then return end
- local px,py = o.px,o.py
- local odd = o.t%2==1
- -- > 0 - straight missile
- if o.mode == 0 then
- local ang = o.d[0]
- local indx = 64
- indx += shr(ang + 0x0800,12)
- if(o.hp==1) for i = 1,15 do pal(i,7) end
- if(o.hp!=1) odd=true
- if(odd) spr(indx,px-4,py-4)
- pal()
- o.t += 1
- -- > afterburner
- if o.t%2 == 0 then
- state.fx_objs:add(0,
- function(m,i,o)
- o.t = 0
- o.px,o.py = px,py
- o.ang,o.spd = ang+0x8000,0
- end
- )
- end
- -- > 1 - raizin
- elseif mode == 1 then
- end
- end)
- end;
- -- functions
- init = function(state)
- if not state.did_init then
- -- > player
- state.p1 = {
- -- > coordinates
- x=64,y=128+32;
- vx = 0,vy=0;
- -- > stats
- lives = 2;
- bombs = 4;
- -- > entrance
- enter = true;
- enter_time = 8;
- -- > shot vars
- shota_timer = 0; -- shot a
- focus_timer = 0;
- power = 0
- }
- state.p1_shot = keiki.new(
- 0x0080,
- function(m,i,o)
- o.px,o.py = 0,0
- o.vx,o.vy = 0,0
- o.ang,o.spd = 0,0
- o.t = 0
- o.dmg = 2
- end
- )
- -- > effects
- state.fx_objs = keiki.new(0x0100,function(m,i,o)
- o.px,o.py = 0,0
- o.vx,o.vy = 0,0
- o.ang,o.spd = 0,0
- o.t = 0
- end)
- -- > enemy
- state.enm_objs = keiki.new(
- 0x0020,
- function(m,i,o)
- o.px,o.py = 0,0
- o.hbsize = 0
- o.hp = 0
- o.d = {}
- end
- )
- state.enm_shot = keiki.new(
- 0x0100,
- function(m,i,o)
- o.px,o.py = 0,0
- o.vx,o.vy = 0,0
- o.ang = 0
- end
- )
- state.did_init = true
- end
- end;
- star_add = function(state)
- local objs = state.fx_objs
- if state.t%9==0 then
- objs:add(1,function(m,i,o)
- o.t = 0
- o.px = rnd(128)
- o.py = -32
- o.spd = .1+rnd()*4
- end)
- end
- end;
- updt = function(state)
- -- > dither?
- local pat = lut_mesh[8]
- pat += 0b0.1
- pat = bxor(pat,0xffff * (state.t%2))
- --fillp(pat)
- rectfill(0,0,128,128,0)
- fillp()
- -- > update
- state:init()
- state:plr_updt()
- state:enm_updt()
- state:fx_updt()
- state:star_add()
- end;
- draw = function(state)
- state:fx_draw()
- state:enm_draw()
- state:plr_draw()
- state.t += 1
- end;
- }
- -- forwrad declarations
Add Comment
Please, Sign In to add comment