12 Commits

Author SHA1 Message Date
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 end
-- intrusive singly-linked list. -- intrusive singly-linked list.
-- cannot be nested! -- cannot be nested or crossed!
linked_list = mknew{ linked_list = mknew{
is_linked_list=true, is_linked_list=true,
init = function(x) init = function(x)
@ -93,9 +93,7 @@ end
-- strip calls f(x) for each -- strip calls f(x) for each
-- node, removing each node for -- node, removing each node for
-- which f(x) returns true. it -- which f(x) returns true.
-- returns the new tail; nil
-- if the list is now empty.
function linked_list:strip(f) function linked_list:strip(f)
local p, n = self, self.next local p, n = self, self.next
while n do while n do
@ -107,7 +105,6 @@ function linked_list:strip(f)
n = n.next n = n.next
end end
self.tail = p self.tail = p
return p
end end
-- optimized special case - -- optimized special case -
@ -237,52 +234,39 @@ function updategame()
end end
events:vore(new_events) events:vore(new_events)
events:strip(call_move) events:strip(call_move)
for _, lst in ipairs{intangibles_bg, eships, pbullets, ebullets} do for _, lst in ipairs{intangibles_bg, eships} do
lst:strip(call_move) lst:strip(call_move)
end end
-- eship collider will be used
-- both for pship and pbullets.
local eship_collider = collider.new{from=eships}
if not ps.dead then if not ps.dead then
ps:move() ps:move()
local pbox = hurtbox(ps) local pbox = hurtbox(ps)
eships:strip(function(es) for es in eship_collider:iterate_collisions(pbox) do
if(not collides(pbox, hurtbox(es))) return
ps:hitship(es) ps:hitship(es)
return es:hitship(ps) if(es:hitship(ps)) eship_collider:yoink(es)
end) end
ebullets:strip(function(eb) ebullets:strip(function(eb)
-- loopify this when split moves implemented
if (eb:move()) return true
if (not collides(pbox, hurtbox(eb))) return if (not collides(pbox, hurtbox(eb))) return
ps:hitbullet(eb) ps:hitbullet(eb)
return eb:hitship(ps) return eb:hitship(ps)
end) 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 else
pbullets.tail = pb.prev ebullets:strip(call_move)
end end
pbullets:strip(function(pb)
if (pb:move()) return true
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 end
if (es:hitbullet(pb)) return true end)
end
end
)
intangibles_fg:strip(call_move) intangibles_fg:strip(call_move)
@ -826,22 +810,14 @@ remainder:
end end
function bullet_base:hitship(_) function bullet_base:hitship(_)
self:die()
return true return true
end end
function bullet_base:die()
end
function bullet_base:move() function bullet_base:move()
self.x += self.dx self.x += self.dx
self.y += self.dy self.y += self.dy
if (self.f) self.f -= 1 if (self.f) self.f -= 1
if (self.y > 145) or (self.y < -8 * self.height) or (self.f and self.f < 0) then return (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)
self:die()
return true
end
return false
end end
function bullet_base:draw() function bullet_base:draw()
@ -1445,6 +1421,22 @@ end
collider = mknew{ collider = mknew{
init = function(x) init = function(x)
x.suppress = {} 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, end,
} }
@ -1458,40 +1450,39 @@ function collider_indexes(box)
return ret return ret
end end
function collider:insert(item) function collider:yoink(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)
self.suppress[item]=true 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 end
function collider:get_collisions(item) function collider:iterate_collisions(box)
local found = { }
local seen = { } local seen = { }
local box = hurtbox(item)
local bucket_ids = collider_indexes(box) local bucket_ids = collider_indexes(box)
for b_idx in all(bucket_ids) do local bii, bidl, bucket, bi, blen = 1, #bucket_ids, false, 1, 0
local bucket = self[b_idx] return function()
if bucket then while bii <= bidl do
for candidate in all(bucket) do if not bucket then
if not (seen[candidate] or self.suppress[candidate]) 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 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
end -- done with this bucket
bi=1
bii += 1
end end
end end -- end of closure def
end
return found
end end
-->8 -->8
@ -1770,7 +1761,6 @@ function xp_gem:draw()
end end
function xp_gem:move() 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 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 + 3) self.x += 1
if (self.x > primary_ship.x + 5) self.x -= 1 if (self.x > primary_ship.x + 5) self.x -= 1
@ -1780,9 +1770,6 @@ function xp_gem:move()
return bullet_base.move(self) return bullet_base.move(self)
end end
-- todo: "magnetic" behavior
-- when near player ship
function xp_gem:hitship(ship) function xp_gem:hitship(ship)
if (ship ~= primary_ship or primary_ship.dead) return false if (ship ~= primary_ship or primary_ship.dead) return false
primary_ship.xp += self.val primary_ship.xp += self.val