18 Commits

Author SHA1 Message Date
c9d7437ffe trim up draw costs a bit 2025-06-21 16:40:23 -07:00
d0de757b0e test the worst case scenario for shots
way too slow
2025-06-21 16:36:36 -07:00
a8b5b9dbe6 fast shot rendering prototype
it's slow *and* it sucks, and making it not suck will make it much slower. this is bad
2025-06-21 16:07:23 -07:00
2596f8aa6c I can't spell 2025-06-21 15:13:07 -07:00
ef40c245f8 multi-step shot prototype
nothing reaches this new logic yet, and multiple steps aren't drawn
2025-06-21 15:12:40 -07:00
6d6e13cf3b special case strip(call_move) to stripmove()
this gets called so much the extra function overhead actually seems bad
2025-06-21 14:55:20 -07:00
99323be298 despawn shots aggressively when offscreen
avoids hitting enemies before they spawn in.

may need to revisit this for the X coordinate if I want to implement a "Wild Ball" kind of weapon, but I think I won't have the tokens for that anyway
2025-06-20 19:13:28 -07:00
85c5091804 pbullets also move just before the collision check.
This also sets up for the "fast shots" refactor.
2025-06-20 19:08:58 -07:00
a77180d89a bullet_base:die doesn't actually make sense 2025-06-20 18:55:48 -07:00
c01c3400b7 fix eternal horizontal bullets 2025-06-20 18:51:58 -07:00
0f791b193c more efficient collision iteration, fix eternal shots 2025-06-20 18:50:04 -07:00
d3351d9a05 Use eship_collider for ship collisions, too. 2025-06-20 17:56:57 -07:00
ecddb56d72 loops work better when you increment them 2025-06-20 17:50:03 -07:00
723c0f791c Refactor collider to collaborate with linked_list.
The only use of a collider is intertwined with the ship list, so I can combine the "prepare to yoink" and "populate collider" and "iterate list" steps, and I can combine the "hide" and "yoink" steps. This doesn't save tokens now but it's about to, when I use the eship collider to test pship collision.
2025-06-20 17:48:02 -07:00
e018578754 keep animating bullets while dead 2025-06-20 16:36:18 -07:00
bf8297eb72 prevify eships when setting up collider
I will refactor this next: collider.new will take the linked list to ingest, perform the `insert` and "prevify" loops itself, then replace `hide` with `yoink`, which yoinks the item out of the original list. This pairs the `yoink` operation with the context that makes it possible to do (that is, the context when prevification was implemented); eships therefore cannot be edited in complex ways while the collider is still valid, but we can append to it as long as we don't expect those items to be procesesd correctly this frame. a new_eships+vore plan might be better if we turn out to need it, but presently we don't; flotilla spawning and raider spawning happen at a different point.
2025-06-20 16:31:18 -07:00
1c8bcae44c put ebullet moves inside the collision check loop 2025-06-20 16:01:59 -07:00
325d7444e7 invert eship/pbullet collision
Also mildly trims up linked_list impl.
2025-06-20 15:09:18 -07:00

View File

@ -60,7 +60,7 @@ function mknew(tt)
end
-- intrusive singly-linked list.
-- cannot be nested!
-- cannot be nested or crossed!
linked_list = mknew{
is_linked_list=true,
init = function(x)
@ -93,9 +93,7 @@ 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.
-- which f(x) returns true.
function linked_list:strip(f)
local p, n = self, self.next
while n do
@ -107,7 +105,22 @@ function linked_list:strip(f)
n = n.next
end
self.tail = p
return p
end
-- stripmove calls x:move() for
-- each node, removing each node
-- for which x:move() is true.
function linked_list:stripmove()
local p, n = self, self.next
while n do
if n:move() then
p.next = n.next
else
p = n
end
n = n.next
end
self.tail = p
end
-- optimized special case -
@ -188,14 +201,6 @@ function _update60()
mode:update()
end
function call_f(x)
return x:f()
end
function call_move(x)
return x:move()
end
function ones(n)
local ret = 0
while n != 0 do
@ -233,58 +238,52 @@ function updategame()
interlude -= 1
else
current_wave = flotilla.new()
current_wave:load(0, 0, min(ones(waves_complete)\2, 4))
current_wave:load(rnd() > 0.5 and 7 or 0, 0, min(ones(waves_complete)\2, 4))
end
events:vore(new_events)
events:strip(call_move)
for _, lst in ipairs{intangibles_bg, eships, pbullets, ebullets} do
lst:strip(call_move)
for _, lst in ipairs{events, intangibles_bg, eships} do
lst:stripmove()
end
-- eship collider will be used
-- both for pship and pbullets.
local eship_collider = collider.new{from=eships}
if not ps.dead then
ps:move()
local pbox = hurtbox(ps)
eships:strip(function(es)
if(not collides(pbox, hurtbox(es))) return
for es in eship_collider:iterate_collisions(pbox) do
ps:hitship(es)
return es:hitship(ps)
end)
if(es:hitship(ps)) eship_collider:yoink(es)
end
ebullets:strip(function(eb)
if (not collides(pbox, hurtbox(eb))) return
local disposition
repeat
disposition=eb:step()
if collides(pbox, hurtbox(eb)) then
ps:hitbullet(eb)
return eb:hitship(ps)
if (eb:hitship(ps)) return true
end
until disposition
return disposition == "dead"
end)
end
-- many bullets and many enemy ships;
-- use bucket collider for efficiency
local pbullet_collider = collider.new()
local p, n = pbullets, pbullets.next
while n do
n.prev = p
pbullet_collider:insert(n)
p = n
n = p.next
end
eships:strip(
function(es)
for pb in all(pbullet_collider:get_collisions(es)) do
if pb:hitship(es) then
pbullet_collider:hide(pb)
pb.prev.next = pb.next
if pb.next then
pb.next.prev = pb.prev
else
pbullets.tail = pb.prev
ebullets:strip(function(eb) repeat until eb:step() end)
end
end
if (es:hitbullet(pb)) return true
end
end
)
intangibles_fg:strip(call_move)
pbullets:strip(function(pb)
local disposition
repeat
disposition=pb:step()
for es in eship_collider:iterate_collisions(hurtbox(pb)) do
if (es:hitbullet(pb)) eship_collider:yoink(es)
if (pb:hitship(es)) return true
end
until disposition
return disposition == "dead"
end)
intangibles_fg:stripmove()
if waves_complete == 32767 and not eships.next and not ebullets.next and not events.next then
game_state = win
@ -701,9 +700,10 @@ end
-- default: die, return true.
-- returns whether to delete
-- the bullet
-- die -- on-removal event,
-- default no-op
bullet_base = mknew{ }
bullet_base = mknew{
steps=1,
current_step=0
}
gun_base = mknew{
shoot_ready = -32768,
@ -826,26 +826,32 @@ remainder:
end
function bullet_base:hitship(_)
self:die()
return true
end
function bullet_base:die()
end
function bullet_base:move()
function bullet_base:step()
self.current_step=(self.current_step+1)%self.steps
self.x += self.dx
self.y += self.dy
if (self.f) self.f -= 1
if (self.y > 145) or (self.y < -8 * self.height) or (self.f and self.f < 0) then
self:die()
return true
end
return false
if ((self.y > 130) or (self.y < -self.height*8) or (self.f and self.f < 0) or (self.x > 128) or (self.x < -self.width*8)) return "dead"
if (self.current_step == 0) return "stop"
end
warmpal = {
[0]=0,1,2,1,2,1,5,10,2,4,9,3,13,5,8,9
}
function bullet_base:draw()
spr(self.sprite, self.x, self.y, self.width, self.height)
local s,bx,by,dx,dy,w,h,st = self.sprite,self.x,self.y,self.dx,self.dy,self.width,self.height,self.steps
if st > 1 then
pal(warmpal)
for n=st-1,1,-1 do
spr(s, bx-n*dx, by-n*dy, w, h)
end
pal()
end
spr(s, bx, by, w, h)
end
function bullet_base:spawn_at(x, y)
@ -909,44 +915,33 @@ end
-->8
-- bullets and guns
zap_e = mknew(bullet_base.new{
zap_p = mknew(bullet_base.new{
--shape
sprite = 9, --index of ammo sprite
width = 1, --in 8x8 blocks
height = 1,
sprite = 8, --index of ammo sprite
width = 0.25, --in 8x8 blocks
height = 0.25,
hurt = { -- hurtbox - where this ship can be hit
x_off = 0, -- upper left corner
y_off = 0, -- relative to sprite
width = 2,
height = 8
height = 2,
},
x_off = 1, -- how to position by ship
y_off = 8,
y_off = 0,
steps=4,
damage = 1,
hitship = const_fxn(true),
category = enemy_blt_cat,
})
zap_p = mknew(zap_e.new{
sprite = 8,
y_off = 0,
category = player_blt_cat,
})
zap_gun_e = mknew(gun_base.new{
cooldown = 0x0.0020, -- frames between shots
veloc = 4,
munition = zap_e,
})
zap_gun_p = mknew(zap_gun_e.new{
zap_gun_p = mknew(gun_base.new{
icon = 19,
munition = zap_p,
veloc = 8,
cooldown = 0x0.0020, -- frames between shots
veloc = 2,
aim = 0.25,
munition = zap_p,
hdr = "mAIN gUN",
})
@ -1078,8 +1073,8 @@ rate: 2/sec
vulcan_e = mknew(bullet_base.new{
--shape
sprite = 21,
width = 1, --in 8x8 blocks
height = 1,
width = 0.125, --in 8x8 blocks
height = 0.5,
hurt = { -- hurtbox - where this ship can be hit
x_off = 0, -- upper left corner
y_off = 0, -- relative to sprite
@ -1339,7 +1334,7 @@ chasey = mknew(ship_m.new{
drag = 0.075,
init = function(ship)
ship.main_gun=ship.main_gun or zap_gun_e.new{}
--ship.main_gun=ship.main_gun or zap_gun_e.new{}
end
})
@ -1445,6 +1440,22 @@ end
collider = mknew{
init = function(x)
x.suppress = {}
local p, n = x.from, x.from.next
while n do
-- insert
for i in all(collider_indexes(hurtbox(n))) do
local a = x[i]
if not a then
a = {}
x[i] = a
end
add(a, n)
end
-- prepare yoink
n.prev = p
p = n
n = n.next
end
end,
}
@ -1458,40 +1469,39 @@ function collider_indexes(box)
return ret
end
function collider:insert(item)
-- todo: separate "big items" list?
local bdx = collider_indexes(hurtbox(item))
for i in all(bdx) do
local x = self[i]
if not x then
x = {}
self[i] = x
end
add(x, item)
end
end
function collider:hide(item)
function collider:yoink(item)
self.suppress[item]=true
local p,n = item.prev,item.next
p.next = n
if n then
n.prev = p
else
self.from.tail = p
end
end
function collider:get_collisions(item)
local found = { }
function collider:iterate_collisions(box)
local seen = { }
local box = hurtbox(item)
local bucket_ids = collider_indexes(box)
for b_idx in all(bucket_ids) do
local bucket = self[b_idx]
if bucket then
for candidate in all(bucket) do
if not (seen[candidate] or self.suppress[candidate]) then
local bii, bidl, bucket, bi, blen = 1, #bucket_ids, false, 1, 0
return function()
while bii <= bidl do
if not bucket then
bucket,blen = self[bucket_ids[bii]],0
if (bucket) blen=#bucket
end
while bi <= blen do
local candidate = bucket[bi]
bi += 1
if not seen[candidate] then
seen[candidate] = true
if (collides(box, hurtbox(candidate))) add(found, candidate)
if (not self.suppress[candidate] and collides(box, hurtbox(candidate))) return candidate
end
end -- done with this bucket
bi=1
bii += 1
end
end
end
return found
end -- end of closure def
end
-->8
@ -1770,7 +1780,6 @@ function xp_gem:draw()
end
function xp_gem:move()
if not primary_ship.dead and abs(self.x + 1 - primary_ship.x - primary_ship.hurt.x_off) <= primary_ship.magnet and abs(self.y + 1 - primary_ship.y - primary_ship.hurt.y_off) <= primary_ship.magnet then
if (self.x < primary_ship.x + 3) self.x += 1
if (self.x > primary_ship.x + 5) self.x -= 1
@ -1780,9 +1789,6 @@ function xp_gem:move()
return bullet_base.move(self)
end
-- todo: "magnetic" behavior
-- when near player ship
function xp_gem:hitship(ship)
if (ship ~= primary_ship or primary_ship.dead) return false
primary_ship.xp += self.val
@ -2026,14 +2032,14 @@ function rearm_mode:update()
end
__gfx__
00000000000650000000000000000000bb0b50b59909209200cc0c00000000003b00000082000000e00e8002e00e800200333300002222000000000000000000
00000000006765000000000000cccc00b50b3055920940220c0000c000bbbb0037000000a2000000e0e8880240e8480403bbbb30028888200000000000000000
00700700006d6500000000000cddddd00b33335009444420c00c000c0b333330b7000000a8000000e88e2882e48e24823bbaabb3288aa8820000000000000000
00077000067c665000000000cdd10cd10b3dd350094dd42000c0000cb3350b35b7000000a8000000e88e2882484e24423ba77ab328a77a820000000000000000
00077000067d665000000000cd10cdd100b3350000944200c0000000b350b335b7000000a8000000e88e2882e84e28823ba77ab328a77a820000000000000000
0070070065666765000000000ddddd100b33355009444220c000000c03333350b7000000a800000008888820048488203bbaabb3288aa8820000000000000000
000000006506506500000000001111000b0b5050090920200c0000c00055550037000000a2000000008882000048420003bbbb30028888200000000000000000
00000000650000650000000000000000000b50000009200000c0cc00000000003b00000082000000000820000008200000333300002222000000000000000000
00000000000650000000000000000000bb0b50b59909209200cc0c00000000007b00000082000000e00e8002e00e800200333300002222000000000000000000
00000000006765000000000000cccc00b50b3055920940220c0000c000bbbb00bb000000a2000000e0e8880240e8480403bbbb30028888200000000000000000
00700700006d6500000000000cddddd00b33335009444420c00c000c0b33333000000000a8000000e88e2882e48e24823bbaabb3288aa8820000000000000000
00077000067c665000000000cdd10cd10b3dd350094dd42000c0000cb3350b3500000000a8000000e88e2882484e24423ba77ab328a77a820000000000000000
00077000067d665000000000cd10cdd100b3350000944200c0000000b350b33500000000a8000000e88e2882e84e28823ba77ab328a77a820000000000000000
0070070065666765000000000ddddd100b33355009444220c000000c0333335000000000a800000008888820048488203bbaabb3288aa8820000000000000000
000000006506506500000000001111000b0b5050090920200c0000c00055550000000000a2000000008882000048420003bbbb30028888200000000000000000
00000000650000650000000000000000000b50000009200000c0cc00000000000000000082000000000820000008200000333300002222000000000000000000
0000000000065000000650000003b0000070070080000000700000000bb0000008800000000000000009200000000000cccccccd000650000000000000000000
000000000067500000076500000370000005500080000000b0000000b76300008a920000000000009009200200000000c111111d006765000000000000000000
00000000006d6500006d6500000b7000700660079000000030000000b663000089920000000550009994444200000000c111111d006d65000000000000000000
@ -2224,8 +2230,11 @@ __gff__
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000212060a0e01091119000000000000002232363a3e050d151d
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
__map__
00006b6b00006f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
7b6a00006a7b6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
7778686878776c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
6767777767676c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
7877676777787d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00006b6b00006f6a6a6a6a6a6a6a6a6a6a6a6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
7b6a00006a7b6e6a6a6a6a6a6a6a6a6a6a6a6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
7778686878776c6a6a6a6a6a6a6a6a6a6a6a6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
6767777767676c6a6a6a6a6a6a6a6a6a6a6a6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
7877676777787d6a6a6a6a6a6a6a6a6a6a6a6d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000006a6a6a6a6a6a6a6a6a6a6a6d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000006a6a6a6a6a6a6a6a6a6a6a6c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000006a6a6a6a6a6a6a6a6a6a6a7c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000