7 Commits

2 changed files with 246 additions and 211 deletions

61
the_parser.p8 Normal file
View File

@ -0,0 +1,61 @@
pico-8 cartridge // http://www.pico-8.com
version 41
__lua__
-- the parser
parser = {}
mknew(parser)
-- calls parse_into with a nop
-- emit function.
function parser:parse(str)
self:parse_into(str, function() end)
end
-- read a file of commands and
-- execute them, emitting the
-- results from each call into
-- `emit` as a table per row.
--
-- a "command" is a method on
-- self. a row alternates
-- commands with args. when
-- calling a command, it also
-- gets a table of previous
-- results as the first arg.
-- args are split on ','.
function parser:parse_into(str, emit)
for row in all(split(str, "\n")) do
local prev = {}
local sectors = split(row, ":")
for i=1,#sectors,2 do
local x = self[sectors[i]](self, prev, usplit(sectors[i+1]))
if (x) add(prev, x)
end
emit(prev)
end
end
-- saves prev[sel] as self.name.
-- if sel is unspecified, saves
-- all of prev (as a table).
function parser:saveas(prev, name, sel)
self[name] = sel and prev[sel] or prev
end
-- returns its args, ignoring
-- prev. Used to stuff things
-- into prev. args are packed
-- if there's multiple.
function parser:val(_, ...)
local ret := pack(...)
if (#ret == 1) return ret[1]
return ret
end
function parser:bind(_, fn, ...)
local f = self[fn]
return function()
f(...)
end
end

View File

@ -26,22 +26,19 @@ end
-- ret is definitely not nil -- ret is definitely not nil
-- before calling setmetatable. -- before calling setmetatable.
-- use to initialize mutables. -- use to initialize mutables.
--
-- if there was a previous new,
-- it is invoked on the new
-- object *after* more, because
-- this works better with the
-- `more` impls i use.
function mknew(tt, more) function mknew(tt, more)
local mt = {__index=tt} local mt,oldnew = {__index=tt},tt.new
-- check "more" only once ever
if more then
tt.new=function(ret) tt.new=function(ret)
if(not ret) ret = {} if(not ret) ret = {}
more(ret) if(more) more(ret)
if(oldnew) oldnew(ret)
setmetatable(ret, mt) setmetatable(ret, mt)
return ret
end
else
tt.new=function(ret)
if (not ret) ret = {}
setmetatable(ret, mt)
return ret
end
end end
end end
@ -121,7 +118,7 @@ function _init()
init_blip_pals() init_blip_pals()
wipe_level() wipe_level()
primary_ship.main_gun = zap_gun.new() primary_ship.main_gun = zap_gun.new()
load_level(example_level) load_level(example_level_csv)
state = game state = game
pal(2,129) pal(2,129)
pal() pal()
@ -416,59 +413,6 @@ function grab_p1_butts()
} }
end end
-->8
-- the parser
parser = {}
mknew(parser)
-- calls parse_into with a nop
-- emit function.
function parser:parse(str)
self:parse_into(str, function() end)
end
-- read a file of commands and
-- execute them, emitting the
-- results from each call into
-- `emit` as a table per row.
--
-- a "command" is a method on
-- self. a row alternates
-- commands with args. when
-- calling a command, it also
-- gets a table of previous
-- results as the first arg.
-- args are split on ','.
function parser:parse_into(str, emit)
for row in all(split(str, "\n")) do
local prev = {}
local sectors = split(row, ":")
for i=1,#sectors,2 do
local x = self[sectors[i]](self, prev, usplit(sectors[i+1]))
if (x) add(prev, x)
end
emit(prev)
end
end
-- saves prev[sel] as self.name.
-- if sel is unspecified, saves
-- all of prev (as a table).
function parser:saveas(prev, name, sel)
self[name] = sel and prev[sel] or prev
end
-- returns its args, ignoring
-- prev. Used to stuff things
-- into prev. args are packed
-- if there's multiple.
function parser:val(_, ...)
local ret := pack(...)
if (#ret == 1) return ret[1]
return ret
end
-->8 -->8
--ship behavior --ship behavior
@ -725,10 +669,7 @@ blocky = frownie.new{
} }
mknew(blocky) mknew(blocky)
function spawn_spewy_at(x, y) spewy = frownie.new{
local spewy = frownie.new{
x = x,
y = y,
sprite=26, sprite=26,
power=-20, power=-20,
hurt = { hurt = {
@ -737,22 +678,20 @@ function spawn_spewy_at(x, y)
width=8, width=8,
height=5 height=5
}, },
hp=1, hp=1,
maxpower=70, maxpower=70,
generator=0.5, generator=0.5,
main_gun = protron_gun.new{enemy=true},
fire_off_x=4, fire_off_x=4,
fire_off_y = 7, fire_off_y = 7,
grab_butts=function() grab_butts=function()
local butts=frownie.grab_butts() local butts=frownie.grab_butts()
butts[5]=1 butts[5]=1
return butts return butts
end,
}
eships:push_back(spewy)
return spewy
end end
}
mknew(spewy, function(ship)
ship.main_gun=ship.main_gun or protron_gun.new{enemy=true}
end})
chasey = ship_m.new{ chasey = ship_m.new{
sprite = 5, sprite = 5,
@ -777,32 +716,20 @@ chasey = ship_m.new{
thrust = 0.2, thrust = 0.2,
drag = 0.075, drag = 0.075,
slip = true, slip = true,
}
mknew(chasey, function(ship)
ship.main_gun=ship.main_gun or zap_gun.new{enemy=true}
end)
grab_butts = function(self) function chasey:grab_butts()
local butts = {0,0,0,0,0} local butts = {[0]=0,0,0,0,0,0}
butts[0] = 0
if (self.x < primary_ship.x) butts[1] = 1 if (self.x < primary_ship.x) butts[1] = 1
if (self.x > primary_ship.x) butts[0] = 1 if (self.x > primary_ship.x) butts[0] = 1
if (self.x - 16 < primary_ship.x and self.x + 16 > primary_ship.x) butts[5] = 1 if (self.x - 16 < primary_ship.x and self.x + 16 > primary_ship.x) butts[5] = 1
return butts return butts
end,
}
mknew(chasey)
function spawn_chasey_at(x, y)
local c = chasey.new{
x = x,
y = y,
main_gun = zap_gun.new{enemy=true},
}
eships:push_back(c)
return c
end end
function spawn_xl_chasey_at(x, y) xl_chasey=chasey.new{
local c = chasey.new{
x = x,
y = y,
size=2, size=2,
maxspd=1.25, maxspd=1.25,
hurt = { hurt = {
@ -827,9 +754,9 @@ function spawn_xl_chasey_at(x, y)
pal() pal()
end, end,
} }
eships:push_back(c) mknew(xl_chasey, function(ship)
return c ship.main_gun=ship.main_gun or zap_gun.new{enemy=true}
end end)
-->8 -->8
-- collisions -- collisions
@ -900,7 +827,9 @@ end
-- a level is a map from -- a level is a map from
-- effective frame number to -- effective frame number to
-- callback for that frame. -- a list of actions for that
-- frame. an action is a
-- method name and its args.
-- effective frame number stops -- effective frame number stops
-- when freeze count is nonzero -- when freeze count is nonzero
@ -924,18 +853,26 @@ freeze = 0
eol = {} eol = {}
function load_level(lvltbl) function load_level(levelfile)
distance = 0 distance = 0
lframe = 0 lframe = 0
freeze = 0 freeze = 0
current_level = {}
-- copy the level so we can
-- modify it dynamically
-- without losing the original
for frame, cb in pairs(lvltbl) do
current_level[frame] = cb
end
leveldone = false leveldone = false
current_level = {}
local found_eol = false
if (type(levelfile)=="string") levelfile = csv(levelfile)
for row in all(levelfile) do
local x = current_level[row[1]]
if row[2] == "eol" then
found_eol = true
assert(x==nil, "events on eol frame")
current_level[row[1]] = eol
else
row.next = x
currentlevel[row[1]]=row
end
end
assert found_eol
end end
function level_frame() function level_frame()
@ -943,13 +880,19 @@ function level_frame()
if (current_level == nil) return true if (current_level == nil) return true
if freeze == 0 then if freeze == 0 then
distance += 1 distance += 1
local cb = current_level[distance] local cbs = current_level[distance]
if cb ~= nil then if cbs ~= nil then
if cb == eol then if cbs == eol then
current_level = nil current_level = nil
return true return true
else else
cb() while cbs do
assert(cbs[1] == distance)
local f = _ENV[cbs[2]]
assert(type(f) == "function", cbs[2].." at "..distance..." is not a function")
f(unpack(cbs, 3))
cbs=cbs.next
end
end end
end end
end end
@ -958,14 +901,7 @@ end
-->8 -->8
-- example level -- example level
function spawn_rnd_x(typ)
s = typ.new{
x = rnd(104),
y = -(typ.size * 8 - 1)
}
eships:push_back(s)
return s
end
function spawn_blocking_rnd_x(typ) function spawn_blocking_rnd_x(typ)
freeze += 1 freeze += 1
@ -1071,51 +1007,89 @@ function spawn_blocking_boss_chasey()
return c return c
end end
example_level = { function std_spawn(tnm, n, blocking, goodie,altspr)
[1]=spawn_frownie, local typ = _ENV[tnm]
[60]=spawn_bonus_vulcan_chasey, assert(typ and typ.new, tostr(tnm).." not a class")
[61]=spawn_blocky, for i=1,(n or 1) do
[85]=spawn_spewy, spawn_rnd(typ, blocking, goodie,altspr)
[100]=spawn_spewy, end
[115]=spawn_spewy, end
[130]=spawn_bonus_frownie,
[145]=spawn_spewy, -- blocking: 1 or 0
[200]=spawn_chasey, function spawn_rnd(typ, blocking, goodie,altspr)
[250]=spawn_blocking_blocky, freeze += blocking
[285]=function() s = typ.new{
spawn_spec_gun_at(35, -11, blast_gun) x = rnd(104),
end, y = -(typ.size * 8 - 1)
[310]=function() ice=blocking
spawn_blocking_blocky() die=function(self)
spawn_blocking_blocky() freeze -= self.ice
spawn_blocking_blocky() self.ice=0
end, typ.die(self)
[311]=spawn_frownie, spawn_goodie(goodie, self.x, self.y, self.size)
[350]=function() end
spawn_main_gun_at(70, -11, protron_gun)
end,
[401]=spawn_frownie,
[420]=spawn_blocking_frownie,
[430]=spawn_bonus_vulcan_chasey,
[450]=spawn_frownie,
[465]=spawn_bonus_frownie,
[480]=spawn_chasey,
[500]=function()
local tnext = lframe
local remain = 20
events:push_back{move=function()
if (lframe < tnext) return false
spawn_blocking_blocky()
tnext = lframe + 0x0.000c
remain -= 1
return (remain <= 0)
end}
end,
[501]=spawn_bonus_frownie,
[620]=spawn_blocking_blocky,
[700]=spawn_blocking_boss_chasey,
[701]=eol
} }
if (altspr) s.spr = altspr
eships:push_back(s)
return s
end
-- TODO: spawn_goodie compatible versions of gun drops
-- TODO: goodie table
function spawn_goodie(goodie_name, x, y, sz)
if (not goodie_name or #goodie_name == 0) return
local sh = sz and sz/2 or 0
goodies[goodie_name].new{}:spawn_at(x+sh,y+sh)
end
function multi(times, interval, fnm, ...)
local f,irm = _ENV[fnm],interval
assert(type(f) == "function", fnm.." not a function")
fnm(...)
events:push_back{move=function()
irm-=1
if irm <= 0 then
irm=interval
times-=1
fnm(...)
return times <= 1
end
end}
end
-- then convert sample_level to csv.
-- spawn_spec_gun_at and spawn_main_gun_at will need parsed forms.
-- the boss also needs to be reachable, but one-off is fine.
-- each row of level csv is offset,event,event-args...
-- where offset,eol is a special case.
example_level_csv=[[1,spawn_frownie
60,spawn_bonus_vulcan_chasey
61,spawn_blocky
85,spawn_spewy
100,spawn_spewy
115,spawn_spewy
130,spawn_bonus_frownie
145,spawn_spewy
200,spawn_chasey
250,spawn_blocking_blocky
285,spawn_spec_gun_at,35,-11,blast_gun
310,spawn_blocking_blocky
310,spawn_blocking_blocky
310,spawn_blocking_blocky
311,spawn_frownie
350,spawn_main_gun_at,70,-11,protron_gun
401,spawn_frownie
420,spawn_blocking_frownie
430,spawn_bonus_vulcan_chasey
450,spawn_frownie
465,spawn_bonus_frownie
480,spawn_chasey
500,multi,20,12,spawn_blocking_blocky
501,spawn_bonus_frownie
620,spawn_blocking_blocky
700,spawn_blocking_boss_chasey
701,eol]]
-->8 -->8
-- bullet and gun behaviors -- bullet and gun behaviors