Compare commits

..

No commits in common. "e0b8386849d72ac531a2d44fb51e3d057f3db22f" and "bd67006e3c614b849cf69debe029f71bc1c6f5c6" have entirely different histories.

View File

@ -21,102 +21,6 @@ function csv(s)
return ret return ret
end end
-- generate standard "overlay"
-- constructor for type tt.
-- if more is defined, generated
-- new calls more(ret) after
-- ret is definitely not nil
-- before calling setmetatable.
-- use to initialize mutables.
function mknew(tt, more)
local mt = {__index=tt}
-- check "more" only once ever
if more then
tt.new = function(ret)
if (not ret) ret = {}
more(ret)
setmetatable(ret, mt)
return ret
end
else
tt.new=function(ret)
if (not ret) ret = {}
setmetatable(ret, mt)
return ret
end
end
end
linked_list = {}
mknew(linked_list, function(x)
x.next=nil
x.tail=x
end)
function linked_list:push_back(x)
self.tail.next = x
self.tail = x
end
function linked_list:push_front(x)
if (not self.next) self.tail = x
x.next = self.next
self.next = x
end
-- vore eats another linked list
-- by appending its contents.
-- the ingested linked is empty.
function linked_list:vore(x)
if (not x.next) return
self.tail.next = x.next
self.tail = x.tail
x.next = nil
x.tail = x
end
-- strip calls f(x) for each
-- node, removing each node for
-- which f(x) returns true. it
-- returns the new tail; nil
-- if the list is now empty.
function linked_list:strip(f)
local p, n = self, self.next
while n do
if f(n) then
p.next = n.next
else
p = n
end
n = n.next
end
self.tail = p
return p
end
-- optimized special case -
-- could be done with strip but
-- this avoids extra function
-- calls and comparisions since
-- draw isn't allowed to kill
-- the item
function linked_list:draw()
local n = self.next
while n do
n:draw()
n = n.next
end
end
function linked_list:pop_front()
local ret = self.next
if (not ret) return
self.next = ret.next
if (not ret.next) ret.tail = nil
return ret
end
function _init() function _init()
init_bullet_mt() init_bullet_mt()
init_powerup_mt() init_powerup_mt()
@ -146,98 +50,128 @@ end
function wipe_level() function wipe_level()
primary_ship = new_p1() primary_ship = new_p1()
init_hpcols() init_hpcols()
pships = linked_list.new() pships = {primary_ship}
pships:push_back(primary_ship) eships = {}
eships = linked_list.new() pbullets = {}
pbullets = linked_list.new() ebullets = {}
ebullets = linked_list.new() intangibles_fg = {}
intangibles_fg = linked_list.new() intangibles_bg = {}
intangibles_bg = linked_list.new() events = {}
events = linked_list.new()
new_events = linked_list.new()
end end
function _update60() function _update60()
updategame() updategame()
end end
function call_f(x)
return x:f()
end
function call_move(x)
return x:move()
end
function updategame() function updategame()
leveldone = level_frame() leveldone = level_frame()
events:vore(new_events) new_events = {}
events:strip(call_move) local deaths = {}
for _, lst in ipairs{intangibles_bg, pships, eships, pbullets, ebullets} do for i, e in ipairs(events) do
lst:strip(call_move) if (e()) add(deaths, i)
end end
bury_the_dead(events, deaths)
pships:strip( foreach(new_events, function(e)
function(ps) add(events, e)
local pbox, pded = hurtbox(ps), false end)
eships:strip( deaths = {}
function(es) for i, x in ipairs(intangibles_bg) do
if (not collides(pbox, hurtbox(es))) return if (x:move()) add(deaths, i)
pded = pded or ps:hitship(es)
return es:hitship(ps)
end end
) bury_the_dead(intangibles_bg, deaths)
return pded for _, tbl in ipairs({pships, eships, pbullets, ebullets, intangibles}) do
local deaths = {}
for i, x in ipairs(tbl) do
if (x:move()) add(deaths, i)
end end
) bury_the_dead(tbl, deaths)
pships:strip(
function(ps)
local pbox, pded = hurtbox(ps), false
ebullets:strip(
function(eb)
if (not collides(pbox, hurtbox(eb))) return
pded = pded or ps:hitbullet(eb)
return eb:hitship(ps)
end end
) --then, calculate collisions
return pded local pdeaths = {}
local edeaths = {}
local eskips = {}
-- todo: always use a collider,
-- it saves if pships is as low as 2s
-- pships usually contians 1 thing,
-- so don't bother with a bucket collider
for ip, ps in ipairs(pships) do
for ie, es in ipairs(eships) do
if not eskips[ie] then
if collides(hurtbox(ps), hurtbox(es)) then
if (es:hitship(ps)) then
add(edeaths, ie)
eskips[ie] = true
end end
) if ps:hitship(es) then
add(pdeaths, ip)
break
end
end
end
end
end
bury_the_dead(pships, pdeaths)
bury_the_dead(eships, edeaths)
pdeaths = {}
edeaths = {}
eskips = {}
for ip, ps in ipairs(pships) do
for ie, eb in ipairs(ebullets) do
if not eskips[ie] then
if collides(hurtbox(ps), hurtbox(eb)) then
local dead = false
if ps:hitbullet(eb) then
add(pdeaths, ip)
dead = true
end
if (eb:hitship(ps)) then
add(edeaths, ie)
eskips[ie] =true
end
if (dead) break
end
end
end
end
bury_the_dead(pships, pdeaths)
bury_the_dead(ebullets, edeaths)
pdeaths = {}
edeaths = {}
-- many bullets and many enemy ships; -- many bullets and many enemy ships;
-- use bucket collider for efficiency -- use bucket collider for efficiency
local pbullet_collider = new_collider() local pbullet_collider = new_collider()
local p, n = pbullets, pbullets.next for idx, pb in ipairs(pbullets) do
while n do pb.___pbullets_idx = idx
n.prev = p pbullet_collider:insert(pb)
pbullet_collider:insert(n)
p = n
n = p.next
end end
eships:strip( for es_idx, es in ipairs(eships) do
function(es)
for pb in all(pbullet_collider:get_collisions(es)) do for pb in all(pbullet_collider:get_collisions(es)) do
local dead = false
if es:hitbullet(pb) then
add(edeaths, es_idx)
dead=true
end
if pb:hitship(es) then if pb:hitship(es) then
add(pdeaths, pb.___pbullets_idx)
pbullet_collider:hide(pb) pbullet_collider:hide(pb)
pb.prev.next = pb.next end
if pb.next then if (dead) break
pb.next.prev = pb.prev
else
pbullets.tail = pb.prev
end end
end end
if (es:hitbullet(pb)) return true bury_the_dead(eships, edeaths)
bury_the_dead(pbullets, pdeaths)
for i, x in ipairs(intangibles_fg) do
if (x:move()) add(deaths, i)
end end
end bury_the_dead(intangibles_fg, deaths)
)
intangibles_fg:strip(call_move) if leveldone and ((#eships + #ebullets + #events) == 0) then
if leveldone and not eships.next and not ebullets.next and not events.next then
state = win state = win
end end
if (not pships.next) state = lose if #pships == 0 then
state = lose
end
end end
function _draw() function _draw()
@ -302,8 +236,10 @@ end
function drawgame() function drawgame()
cls() cls()
clip(0,0,112,128) clip(0,0,112,128)
for slist in all{intangibles_bg, pbullets, pships, eships, ebullets, intangibles_fg} do for tbl in all{intangibles_bg, pbullets, pships, eships, ebullets, intangibles_fg} do
slist:draw() for x in all(tbl) do
x:draw()
end
end end
clip(0,0,128,128) clip(0,0,128,128)
drawhud() drawhud()
@ -404,11 +340,30 @@ function grab_p1_butts()
} }
end end
function bury_the_dead(tbl, dead)
if (#dead == 0) return
local tail = dead[1]
local head = tail + 1
local deaddex = 2
while head <= #tbl do
while deaddex <= #dead and head == dead[deaddex] do
deaddex += 1
head += 1
end
if (head > #tbl) break
tbl[tail] = tbl[head]
head += 1
tail += 1
end
for i=1,(head-tail) do
deli(tbl)
end
end
-->8 -->8
--ships, including player --ships, including player
-- XXX BOOKMARK XXX
function init_ship_mt() function init_ship_mt()
setmetatable(player, ship_t) setmetatable(player, ship_t)
setmetatable(frownie, ship_t) setmetatable(frownie, ship_t)
@ -573,7 +528,7 @@ function spawn_spewy_at(x, y)
end, end,
} }
setmetatable(spewy, frownie_t) setmetatable(spewy, frownie_t)
eships:push_back(spewy) add(eships, spewy)
return spewy return spewy
end end
@ -620,7 +575,7 @@ function spawn_chasey_at(x, y)
main_gun = new_gun_of(zap_gun_t, true) main_gun = new_gun_of(zap_gun_t, true)
} }
setmetatable(c, chasey_t) setmetatable(c, chasey_t)
eships:push_back(c) add(eships, c)
return c return c
end end
@ -653,7 +608,7 @@ function spawn_xl_chasey_at(x, y)
end, end,
} }
setmetatable(c, chasey_t) setmetatable(c, chasey_t)
eships:push_back(c) add(eships, c)
return c return c
end end
-->8 -->8
@ -687,50 +642,50 @@ ship_t = {
__index = ship_m, __index = ship_m,
} }
function ship_m:die() function ship_m.die(s)
self.dead = true s.dead = true
if (self.hp <= 0) boom(self.x+self.size*4, self.y+self.size*4,12*self.size, self.boss) if (s.hp <= 0) boom(s.x+s.size*4, s.y+s.size*4,12*s.size, s.boss)
end end
function ship_m:move() function ship_m.move(ship)
self:refresh_shield() ship:refresh_shield()
self.power = min(self.max_power, self.power + self.generator) ship.power = min(ship.max_power, ship.power + ship.generator)
butt = self:grab_butts() butt = ship:grab_butts()
if (butt[5] > 0) self:maybe_shoot(self.main_gun) if (butt[5] > 0) ship:maybe_shoot(ship.main_gun)
if (butt[4] > 0) self:maybe_shoot(self.special_gun) if (butt[4] > 0) ship:maybe_shoot(ship.special_gun)
if (butt[0]-butt[1] ~= 0 or butt[2]-butt[3] ~= 0) spark(self.sparks, self.x + 4*self.size, self.y + 4*self.size, butt, self.thrust, self.sparkodds) if (butt[0]-butt[1] ~= 0 or butt[2]-butt[3] ~= 0) spark(ship.sparks, ship.x + 4*ship.size, ship.y + 4*ship.size, butt, ship.thrust, ship.sparkodds)
self.xmomentum += (self.thrust * butt[1]) - (self.thrust * butt[0]) ship.xmomentum += (ship.thrust * butt[1]) - (ship.thrust * butt[0])
self.ymomentum += (self.thrust * butt[3]) - (self.thrust * butt[2]) ship.ymomentum += (ship.thrust * butt[3]) - (ship.thrust * butt[2])
self.xmomentum = mid(-self.maxspd, self.maxspd, self.xmomentum) ship.xmomentum = mid(-ship.maxspd, ship.maxspd, ship.xmomentum)
self.ymomentum = mid(-self.maxspd, self.maxspd, self.ymomentum) ship.ymomentum = mid(-ship.maxspd, ship.maxspd, ship.ymomentum)
self.x += self.xmomentum ship.x += ship.xmomentum
self.y += self.ymomentum ship.y += ship.ymomentum
if self == primary_self then if ship == primary_ship then
self.x = mid(0, 112 - 8 * self.size, self.x) ship.x = mid(0, 112 - 8 * ship.size, ship.x)
self.y = mid(0, 128 - 8 * self.size, self.y) ship.y = mid(0, 128 - 8 * ship.size, ship.y)
end end
--friction --friction
local d = self.drag local d = ship.drag
self.xmomentum -= mid(d, -d, self.xmomentum) ship.xmomentum -= mid(d, -d, ship.xmomentum)
self.ymomentum -= mid(d, -d, self.ymomentum) ship.ymomentum -= mid(d, -d, ship.ymomentum)
-- "scrolling" behavior -- "scrolling" behavior
if self.slip then if ship.slip then
self.y += scrollrate ship.y += scrollrate
if self.y >= 128 then if ship.y >= 128 then
self:die() ship:die()
return true return true
end end
end end
return false return false
end end
function ship_m:draw() function ship_m.draw(ship)
if(self.fx_pal) pal(self.fx_pal) if(ship.fx_pal) pal(ship.fx_pal)
spr(self.sprite, self.x, self.y, self.size, self.size) spr(ship.sprite, ship.x, ship.y, ship.size, ship.size)
pal() pal()
end end
@ -933,7 +888,7 @@ function spawn_rnd_x(mt)
y = -(mt.__index.size * 8 - 1) y = -(mt.__index.size * 8 - 1)
} }
setmetatable(s, mt) setmetatable(s, mt)
eships:push_back(s) add(eships, s)
return s return s
end end
@ -950,7 +905,7 @@ function spawn_blocking_rnd_x(mt)
end end
} }
setmetatable(s, mt) setmetatable(s, mt)
eships:push_back(s) add(eships, s)
return s return s
end end
@ -1030,13 +985,13 @@ function spawn_blocking_boss_chasey()
end end
local nextspawn = lframe + 120 local nextspawn = lframe + 120
events:push_back{move=function() add(events, function()
if lframe >= nextspawn then if lframe >= nextspawn then
helpers[flr(rnd(#helpers))+1]() helpers[flr(rnd(#helpers))+1]()
nextspawn += 60 nextspawn += 60
end end
return c.dead return c.dead
end} end)
return c return c
end end
@ -1073,13 +1028,13 @@ example_level = {
[500]=function() [500]=function()
local tnext = lframe local tnext = lframe
local remain = 20 local remain = 20
events:push_back{move=function() add(events, function()
if (lframe < tnext) return false if (lframe < tnext) return false
spawn_blocking_blocky() spawn_blocking_blocky()
tnext = lframe + 12 tnext = lframe + 12
remain -= 1 remain -= 1
return (remain <= 0) return (remain <= 0)
end} end)
end, end,
[501]=spawn_bonus_frownie, [501]=spawn_bonus_frownie,
[620]=spawn_blocking_blocky, [620]=spawn_blocking_blocky,
@ -1167,31 +1122,21 @@ blast = {
damage = 4, damage = 4,
dx = 0, -- px/frame dx = 0, -- px/frame
dy = 2, dy = 2,
awaitcancel = false,
-- disable damage for 2 frames -- disable damage for 2 frames
-- when hitting something -- when hitting something
hitship = function(self, _) hitship = function(self, _)
if self.damage > 0 and not self.awaitcancel then
self.awaitcancel = true
events:push_back{move = function()
new_events:push_back{
wait = 2,
obj = self,
saved_dmg = self.damage,
move = function(self)
self.wait -= 1
if self.wait <= 0 then
self.obj.damage = self.saved_dmg
return true
end
end,
}
self.damage = 0 self.damage = 0
self.awaitcancel = false local wait = 2
e = function()
wait -= 1
if wait <= 0 then
self.damage = 4
return true return true
end}
end end
return false
end
add(events, e)
end end
} }
blast_t = { blast_t = {
@ -1385,10 +1330,10 @@ function bullet_base:spawn_at(x, y)
self.dx *= self.enemyspd self.dx *= self.enemyspd
self.dy *= self.enemyspd self.dy *= self.enemyspd
self.y = y + self.top_y_off self.y = y + self.top_y_off
ebullets:push_back(self) add(ebullets, self)
else else
self.y = y - (8 * self.height) + self.bottom_y_off self.y = y - (8 * self.height) + self.bottom_y_off
pbullets:push_back(self) add(pbullets, self)
end end
end end
@ -1833,10 +1778,14 @@ weird coding conventions
-- standard events -- standard events
blip_fx = { blip_fx = {
cancel=false abort = function(self)
self.cancel = true
end
} }
function blip_fx:move() blip_fx_t = {
__index = blink_fx,
__call = function(self)
if (self.cancel) return true if (self.cancel) return true
self.frames -= 1 self.frames -= 1
if self.frames < 0 then if self.frames < 0 then
@ -1845,19 +1794,19 @@ function blip_fx:move()
end end
return false return false
end end
}
function blip_fx:abort()
self.cancel=true
end
mknew(blip_fx)
function blip(obj, col, frames) function blip(obj, col, frames)
local p = {[0]=0} local p = {[0]=0}
obj.fx_pal = p obj.fx_pal = p
for i=1,15 do p[i]=col end for i=1,15 do p[i]=col end
if (obj.___fx_pal_event) obj.___fx_pal_event:abort() if (obj.___fx_pal_event) obj.___fx_pal_event:abort()
events:push_back(blip_fx.new{frames=frames, obj=obj}) local e = {
frames = frames,
obj = obj
}
setmetatable(e, blip_fx_t)
add(events, e)
end end
bossspark = split"7,7,10,10,9,9,9,8,8,8,2,2,5,5" bossspark = split"7,7,10,10,9,9,9,8,8,8,2,2,5,5"
@ -1882,8 +1831,7 @@ end
function spark(sprs, x, y, butts, thrust, odds, fg) function spark(sprs, x, y, butts, thrust, odds, fg)
if (sprs==nil or flr(rnd(odds)) ~= 0) return if (sprs==nil or flr(rnd(odds)) ~= 0) return
thrust *= 2.5 thrust *= 2.5
local target = fg and intangibles_fg or intangibles_bg add(fg and intangibles_fg or intangibles_bg, {
target:push_back{
x = x + rnd(4) - 2, x = x + rnd(4) - 2,
y = y + rnd(4) - 2, y = y + rnd(4) - 2,
sprs = sprs, sprs = sprs,
@ -1901,7 +1849,7 @@ function spark(sprs, x, y, butts, thrust, odds, fg)
self.dx -= mid(0.05,-0.05, self.dx) self.dx -= mid(0.05,-0.05, self.dx)
self.dy -= mid(0.05,-0.05, self.dy) self.dy -= mid(0.05,-0.05, self.dy)
end end
} })
end end
-->8 -->8
-- powerups -- powerups