Cache for collision checks
This commit is contained in:
parent
f67b617a78
commit
27692ba208
280
chameleonic.p8
280
chameleonic.p8
@ -112,6 +112,73 @@ function _mnmx(x,y)
|
||||
return x,y
|
||||
end
|
||||
|
||||
function _rastn(
|
||||
x0,y0,x1,y1,dx,dy
|
||||
)
|
||||
-- todo: more optimized implementation?
|
||||
local iter=_rast(x0,y0,x1,y1)
|
||||
local prevx,prevy=nil,nil
|
||||
|
||||
local done=false
|
||||
return function()
|
||||
while not done do
|
||||
local x,y=iter()
|
||||
|
||||
if (x==nil) done=true return x1, y1
|
||||
|
||||
local x8 = x\dx
|
||||
local y8 = y\dy
|
||||
if not (x8==prevx and y8==prevy) then
|
||||
prevx,prevy=x8,y8
|
||||
return x,y
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function _rast(
|
||||
x0,y0,x1,y1
|
||||
)
|
||||
local dx=abs(x1-x0)
|
||||
local dy=abs(y1-y0)
|
||||
local x=x0
|
||||
local y=y0
|
||||
|
||||
local sx=-1
|
||||
local sy=-1
|
||||
if (x0<x1) sx=1
|
||||
if (y0<y1) sy=1
|
||||
|
||||
local done=false,err
|
||||
if dx>dy then
|
||||
err=dx/2.0
|
||||
return function()
|
||||
if (done) return
|
||||
if (x==x1) done=true return x1,y1
|
||||
local oldx,oldy=x,y
|
||||
err-=dy
|
||||
if (err<0) y+=sy err+=dx+dy return oldx,y
|
||||
x+=sx
|
||||
return oldx,oldy
|
||||
end
|
||||
else
|
||||
err=dy/2.0
|
||||
return function()
|
||||
if (done) return
|
||||
if (y==y1) done=true return x1,y1
|
||||
local oldx,oldy=x,y
|
||||
err-=dx
|
||||
if (err<0) x+=sx err+=dy+dx return x,oldy
|
||||
y+=sy
|
||||
return oldx,oldy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function _point_eq(p1,p2)
|
||||
return p1.x==p2.x and p1.y==p2.y
|
||||
end
|
||||
|
||||
-->8
|
||||
-- input
|
||||
kbd={}
|
||||
@ -222,6 +289,7 @@ function level:reinit(n)
|
||||
self.todo={}
|
||||
self.bigx=(n%8)
|
||||
self.bigy=(n\8)
|
||||
self.cache_can_stretch=dcache:new()
|
||||
|
||||
self:load_dynobjs()
|
||||
self:recollide()
|
||||
@ -335,6 +403,7 @@ function level:recollide()
|
||||
self._crates[mxy]!=nil
|
||||
end
|
||||
end
|
||||
self.cache_can_stretch:clear()
|
||||
end
|
||||
|
||||
function add_adjacent_anchors(tbl,mx,my)
|
||||
@ -588,6 +657,75 @@ function level:tug_crate(mx0,my0,dmx,dmy)
|
||||
self:recollide()
|
||||
self:reanchor(false)
|
||||
end
|
||||
-->8
|
||||
-- collision checks
|
||||
function level:can_stretch(
|
||||
p1,p2
|
||||
)
|
||||
local key=p1.x..","..p1.y..","..p2.x..","..p2.y
|
||||
return self.cache_can_stretch:wrap(key,function()
|
||||
return self:_can_stretch(p1,p2)
|
||||
end)
|
||||
end
|
||||
|
||||
function level:_can_stretch(
|
||||
p1,p2
|
||||
)
|
||||
-- faster implementation for straight lines
|
||||
if p1.y\8==p2.y\8 then
|
||||
local my=p2.y\8
|
||||
for mx=p1.x\8,p2.x\8 do
|
||||
if (level:mcoll(mx,my)) return false
|
||||
end
|
||||
end
|
||||
|
||||
if p1.x\8==p2.x\8 then
|
||||
local mx=p2.x\8
|
||||
for my=p1.y\8,p2.y\8 do
|
||||
if (level:mcoll(mx,my)) return false
|
||||
end
|
||||
end
|
||||
|
||||
if (level:pcoll(p1.x,p1.y)) return false
|
||||
if (level:pcoll(p2.x,p2.y)) return false
|
||||
|
||||
local res=true
|
||||
for x,y in _rastn(p1.x,p1.y,p2.x,p2.y,8,8) do
|
||||
if level:pcoll(x,y) then
|
||||
res=false
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
-->8
|
||||
--cache impl for collision checks
|
||||
dcache={}
|
||||
dcache.__index=dcache
|
||||
|
||||
function dcache:new()
|
||||
local d={}
|
||||
setmetatable(d,dcache)
|
||||
d:clear()
|
||||
return d
|
||||
end
|
||||
function dcache:clear()
|
||||
self.old,self.new,self.new_n={},{},0
|
||||
end
|
||||
|
||||
function dcache:wrap(key,f)
|
||||
local el=self.new[key]
|
||||
if (el!=nil) return el
|
||||
local el=self.old[key]
|
||||
if (el==nil) el=f()
|
||||
self.new[key]=el
|
||||
self.new_n+=1
|
||||
if (self.new_n>1000) self.old,self.new,self.new_n=self.new,{},0
|
||||
return el
|
||||
end
|
||||
|
||||
-->8
|
||||
--player handling
|
||||
player={}
|
||||
@ -905,7 +1043,7 @@ function rope:continue_cast()
|
||||
local x1=x0+dx
|
||||
local y1=y0+dy
|
||||
|
||||
for x,y in self:_rast(
|
||||
for x,y in _rast(
|
||||
x0,y0,x1,y1
|
||||
) do
|
||||
local latch=
|
||||
@ -986,14 +1124,14 @@ function rope:draw(artificial_dx,artificial_dy)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
for i=0,#self.ancs+1 do
|
||||
p=self:_anc(i)
|
||||
local c=12
|
||||
if (p.dirty) c=13
|
||||
rectfill(p.x-1,p.y-1,p.x+1,p.y+1,c)
|
||||
print(tostr(p.id)..":"..p.x..","..p.y..","..#p.todo,0,i*8,9)
|
||||
print(tostr(p.id)..":"..p.x..","..p.y..","..#p.todo,0,i*8,12)
|
||||
end
|
||||
--[[
|
||||
for _,p in pairs(level._anch) do
|
||||
pset(p.x,p.y,11)
|
||||
end
|
||||
@ -1029,7 +1167,7 @@ function rope:drag(
|
||||
local anc=self:_anc(i())
|
||||
local busy=self:busy()
|
||||
|
||||
for x,y in self:_rast(
|
||||
for x,y in _rast(
|
||||
anc.x,anc.y,x,y
|
||||
) do
|
||||
local a=self:_anc(i())
|
||||
@ -1038,8 +1176,6 @@ function rope:drag(
|
||||
a.y=y
|
||||
a.dirty=true
|
||||
self.dirty=true
|
||||
--self:_find_needed_anchors(i())
|
||||
--self:_find_needed_anchors(i()+1)
|
||||
if (not busy) self:_tidy_up_gen()
|
||||
end
|
||||
end
|
||||
@ -1050,7 +1186,7 @@ function rope:make_dirty(only_if_invalid)
|
||||
for a=0,#self.ancs do
|
||||
local a0=self:_anc(a)
|
||||
local a1=self:_anc(a+1)
|
||||
if not self:_can_stretch(a0,a1) then
|
||||
if not level:can_stretch(a0,a1) then
|
||||
a0.dirty=true
|
||||
a1.dirty=true
|
||||
invalid=true
|
||||
@ -1075,6 +1211,7 @@ function rope:_tidy_up_gen()
|
||||
anc.seen=true
|
||||
if self[f](self,a,busy) then
|
||||
settled=false anc.changed=true
|
||||
a=0
|
||||
end
|
||||
end
|
||||
a+=1
|
||||
@ -1127,7 +1264,7 @@ function rope:_tidy_up_gen()
|
||||
for i=0,#self.ancs do
|
||||
local a0=self:_anc(i)
|
||||
local a1=self:_anc(i+1)
|
||||
if not self:_can_stretch(a0,a1) then
|
||||
if not level:can_stretch(a0,a1) then
|
||||
self:destroy()
|
||||
end
|
||||
end
|
||||
@ -1144,7 +1281,7 @@ function rope:_find_needed_anchors(i,busy)
|
||||
|
||||
if (level:pcoll(a2.x,a2.y)) return false
|
||||
if (level:pcoll(a0.x,a0.y)) return false
|
||||
if (self:_can_stretch(a0,a2)) return false
|
||||
if (level:can_stretch(a0,a2)) return false
|
||||
|
||||
local anchors_bydist={}
|
||||
local x0,x2=_mnmx(a0.x,a2.x)
|
||||
@ -1156,8 +1293,8 @@ function rope:_find_needed_anchors(i,busy)
|
||||
|
||||
for a1 in all(anchors_bydist) do
|
||||
a1=a1.el
|
||||
if self:_can_stretch(a0,a1) and
|
||||
self:_can_stretch(a1,a2)
|
||||
if level:can_stretch(a0,a1) and
|
||||
level:can_stretch(a1,a2)
|
||||
then
|
||||
local id=self.id
|
||||
add(self.ancs,{id=id,x=a1.x,y=a1.y,dirty=true,todo={}},i)
|
||||
@ -1179,12 +1316,11 @@ function rope:_find_touched_anchors(i)
|
||||
if (level:pcoll(a0.x,a0.y)) return false
|
||||
if (level:pcoll(a2.x,a2.y)) return false
|
||||
|
||||
for bx,by in self:_rast(a0.x,a0.y,a2.x,a2.y) do
|
||||
for bx,by in _rast(a0.x,a0.y,a2.x,a2.y) do
|
||||
local a1=level:point_anchor(bx,by)
|
||||
if
|
||||
a1!=nil and not _point_eq(a0,a1) and not _point_eq(a1,a2)
|
||||
and _linedist(a0,a1,a2)<ELIDE_POINT
|
||||
-- and self:_can_stretch(p,a2)
|
||||
then
|
||||
local id=self.id
|
||||
add(self.ancs,{id=id,x=a1.x,y=a1.y,dirty=true,todo={}},i)
|
||||
@ -1212,7 +1348,7 @@ function rope:_elide_point(i,busy)
|
||||
return false
|
||||
end
|
||||
|
||||
if not self:_can_stretch(a0,a2) then
|
||||
if not level:can_stretch(a0,a2) then
|
||||
return false
|
||||
end
|
||||
|
||||
@ -1233,26 +1369,21 @@ function rope:_elide_point(i,busy)
|
||||
end
|
||||
|
||||
function rope:_can_move_midpoint(a0,a1_0,a1_1,a2)
|
||||
if (level:pcoll(a0.x,a0.y)) return false
|
||||
if (level:pcoll(a2.x,a2.y)) return false
|
||||
if (level:pcoll(a1_0.x,a1_0.y)) return false
|
||||
if (level:pcoll(a1_1.x,a1_1.y)) return false
|
||||
|
||||
if not self:_can_stretch(a1_0, a1_1) then
|
||||
if not level:can_stretch(a1_0, a1_1) then
|
||||
return false
|
||||
end
|
||||
if not self:_can_stretch(a0,a1_1) then
|
||||
if not level:can_stretch(a0,a1_1) then
|
||||
return false
|
||||
end
|
||||
if not self:_can_stretch(a1_1,a2) then
|
||||
if not level:can_stretch(a1_1,a2) then
|
||||
return false
|
||||
end
|
||||
for x,y in self:_rastn(a1_0.x,a1_0.y,a1_1.x,a1_1.y,8,8) do
|
||||
for x,y in _rastn(a1_0.x,a1_0.y,a1_1.x,a1_1.y,8,8) do
|
||||
local tm={x=x,y=y}
|
||||
if not self:_can_stretch(a0,tm) then
|
||||
if not level:can_stretch(a0,tm) then
|
||||
return false
|
||||
end
|
||||
if not self:_can_stretch(tm,a2) then
|
||||
if not level:can_stretch(tm,a2) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
@ -1304,105 +1435,6 @@ function _line_line(x1,y1,x2,y2,x3,y3,x4,y4)
|
||||
return true
|
||||
end
|
||||
|
||||
function rope:_can_stretch(
|
||||
p1,p2
|
||||
)
|
||||
-- faster implementation for straight lines
|
||||
if p1.y\8==p2.y\8 then
|
||||
local my=p2.y\8
|
||||
for mx=p1.x\8,p2.x\8 do
|
||||
if (level:mcoll(mx,my)) return false
|
||||
end
|
||||
end
|
||||
|
||||
if p1.x\8==p2.x\8 then
|
||||
local mx=p2.x\8
|
||||
for my=p1.y\8,p2.y\8 do
|
||||
if (level:mcoll(mx,my)) return false
|
||||
end
|
||||
end
|
||||
|
||||
if (level:pcoll(p1.x,p1.y)) return false
|
||||
if (level:pcoll(p2.x,p2.y)) return false
|
||||
|
||||
local res=true
|
||||
for x,y in self:_rastn(p1.x,p1.y,p2.x,p2.y,8,8) do
|
||||
if level:pcoll(x,y) then
|
||||
res=false
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
function rope:_rastn(
|
||||
x0,y0,x1,y1,dx,dy
|
||||
)
|
||||
-- todo: more optimized implementation?
|
||||
local iter=self:_rast(x0,y0,x1,y1)
|
||||
local prevx,prevy=nil,nil
|
||||
|
||||
local done=false
|
||||
return function()
|
||||
while not done do
|
||||
local x,y=iter()
|
||||
|
||||
if (x==nil) done=true return x1, y1
|
||||
|
||||
local x8 = x\dx
|
||||
local y8 = y\dy
|
||||
if not (x8==prevx and y8==prevy) then
|
||||
prevx,prevy=x8,y8
|
||||
return x,y
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function rope:_rast(
|
||||
x0,y0,x1,y1
|
||||
)
|
||||
local dx=abs(x1-x0)
|
||||
local dy=abs(y1-y0)
|
||||
local x=x0
|
||||
local y=y0
|
||||
|
||||
local sx=-1
|
||||
local sy=-1
|
||||
if (x0<x1) sx=1
|
||||
if (y0<y1) sy=1
|
||||
|
||||
local done=false,err
|
||||
if dx>dy then
|
||||
err=dx/2.0
|
||||
return function()
|
||||
if (done) return
|
||||
if (x==x1) done=true return x1,y1
|
||||
local oldx,oldy=x,y
|
||||
err-=dy
|
||||
if (err<0) y+=sy err+=dx+dy return oldx,y
|
||||
x+=sx
|
||||
return oldx,oldy
|
||||
end
|
||||
else
|
||||
err=dy/2.0
|
||||
return function()
|
||||
if (done) return
|
||||
if (y==y1) done=true return x1,y1
|
||||
local oldx,oldy=x,y
|
||||
err-=dx
|
||||
if (err<0) x+=sx err+=dy+dx return x,oldy
|
||||
y+=sy
|
||||
return oldx,oldy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function _point_eq(p1,p2)
|
||||
return p1.x==p2.x and p1.y==p2.y
|
||||
end
|
||||
|
||||
function neighbors(p)
|
||||
local r={}
|
||||
for dx=-1,1,1 do
|
||||
|
Loading…
Reference in New Issue
Block a user