Everything _seems_ to work

This commit is contained in:
Pyrex 2022-12-20 20:45:33 -08:00
parent c71cd312b1
commit c98194550e

View File

@ -116,29 +116,6 @@ 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
@ -161,7 +138,7 @@ function _rast(
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
if (err<0) y+=sy err+=dx
x+=sx
return oldx,oldy
end
@ -172,17 +149,13 @@ function _rast(
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
if (err<0) x+=sx err+=dy
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={}
@ -232,6 +205,33 @@ function kbd:release(i)
self.down&=~(1<<i)
end
function tostring(any)
if type(any)=="function" then
return "function"
end
if any==nil then
return "nil"
end
if type(any)=="string" then
return any
end
if type(any)=="boolean" then
if any then return "true" end
return "false"
end
if type(any)=="table" then
local str = "{ "
for k,v in pairs(any) do
str=str..tostring(k).."->"..tostring(v).." "
end
return str.."}"
end
if type(any)=="number" then
return ""..any
end
return "unknown" -- should never show
end
-->8
-- title screen
title={}
@ -293,7 +293,7 @@ function level:reinit(n)
self.todo={}
self.bigx=(n%8)
self.bigy=(n\8)
self.cache_can_stretch=dcache:new()
self.next_crate_id=1
self:load_dynobjs()
self:recollide()
@ -326,16 +326,16 @@ function level:draw()
spr(pit.contents,pit.px,pit.py)
pal()
pal(1,0)
end
end
for _,crate in pairs(self._crates) do
spr(crate.s,crate.px,crate.py)
end
end
pal()
end
function level:busy()
for _,crate in pairs(self.crates) do
for _,crate in pairs(self._crates) do
if (#crate.todo>0) return true
end
return false
@ -379,10 +379,12 @@ function level:load_dynobjs()
if def then
self._crates[mxy]={
s=s,def=def,
id=self.next_crate_id,
mx=mx,my=my,
px=px,py=py,
todo={}
}
self.next_crate_id+=1
end
if s==28 then -- pit
@ -399,15 +401,17 @@ end
function level:recollide()
self._coll={}
self._coll_nocrate={}
for mx=0,15 do
for my=0,15 do
local mxy=_mix(mx,my)
self._coll_nocrate[mxy]=
fget(self:_mget(mx,my),7)
self._coll[mxy]=
fget(self:_mget(mx,my),7) or
self._coll_nocrate[mxy] or
self._crates[mxy]!=nil
end
end
self.cache_can_stretch:clear()
end
function add_adjacent_anchors(tbl,mx,my)
@ -420,33 +424,72 @@ function add_adjacent_anchors(tbl,mx,my)
end
function level:reanchor()
self._anch={}
local anch_new={}
for dxy in all{{-1,-1},{1,-1},{-1,1},{1,1}} do
local dx,dy=unpack(dxy)
assert(dx!=0 and dy!=0)
for mx0=0,15 do
for my0=0,15 do
local mx1,my1=mx0+dx,my0+dy
if (
self:mcoll(mx0,my0) and
self:mcoll_nocrate(mx0,my0) and
not self:mcoll(mx0,my1) and
not self:mcoll(mx1,my0) and
not self:mcoll(mx1,my1)
) then
add(self._anch, {
local key="GEOM"..mx0..","..my0..","..dx..","..dy
anch_new[key]= {
ax=max(mx0,mx1),ay=max(my0,my1),adx=-dx,ady=-dy
})
}
end
end
end
for _,cr in pairs(self._crates) do
local key="CRATE"..cr.id..","..dx..","..dy
local mx0,my0=cr.mx,cr.my
local mx1,my1=mx0+dx,my0+dy
anch_new[key]={
ax=max(mx0,mx1),ay=max(my0,my1),adx=-dx,ady=-dy
}
end
end
local anch_old=self._anch
if (anch_old==nil) anch_old={}
for _,old in pairs(anch_old) do
old.dropped=true
end
for k,new in pairs(anch_new) do
local old=anch_old[k]
if old then
anch_new[k]=old
old.ax,old.ay,old.adx,old.ady=new.ax,new.ay,new.adx,new.ady
old.dropped=nil
end
end
self._anch=anch_new
self._anch_keys={}
for k,_ in pairs(self._anch) do
add(self._anch_keys,{key=k})
end
if (player.rope!=nil) player.rope:relax()
end
function level:win_at(mx,my)
return self._wins[_mix(mx,my)]
end
function level:anchor_points()
return pairs(self._anch)
keys=all(self._anch_keys)
return function()
local k=keys()
if (k==nil) return nil
return self._anch[k.key]
end
end
function level:get_open_pit(mx,my)
@ -499,6 +542,9 @@ end
function level:mcoll(mx,my)
return self._coll[_mix(mx,my)]!=false
end
function level:mcoll_nocrate(mx,my)
return self._coll_nocrate[_mix(mx,my)]!=false
end
function level:pcoll(px,py)
return self:mcoll(px\8,py\8)
@ -574,6 +620,8 @@ function level:get_latch(dx,dy,px,py)
return {
el="eyehook",
dx=dx1,dy=dy1,
ax_offset=dx1*0.5,
ay_offset=dy1*0.5,
mx=mx,my=my
}
end
@ -597,16 +645,15 @@ function level:can_move(
-- todo: check tongue collision
if player.rope then
local px,py=mx0*8,my0*8
local chk=false
if dmx==0 and dmy==-1 then
chk=player.rope:collide_rect(px+3,py-5,px+4,py+5,exclude_src,exclude_dst)
chk=player.rope:collide_mrect(mx0,my0-1,1,2,exclude_src,exclude_dst)
elseif dmx==0 and dmy==1 then
chk=player.rope:collide_rect(px+3,py+3,px+4,py+13,exclude_src,exclude_dst)
chk=player.rope:collide_mrect(mx0,my0,1,2,exclude_src,exclude_dst)
elseif dmx==-1 and dmy==0 then
chk=player.rope:collide_rect(px-5,py+3,px+5,py+4,exclude_src,exclude_dst)
chk=player.rope:collide_mrect(mx0-1,my0,2,1,exclude_src,exclude_dst)
elseif dmx==1 and dmy==0 then
chk=player.rope:collide_rect(px+3,py+3,px+13,py+4,exclude_src,exclude_dst)
chk=player.rope:collide_mrect(mx0,my0,2,1,exclude_src,exclude_dst)
end
if (chk) return false
@ -639,74 +686,6 @@ 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
@ -815,7 +794,7 @@ function player:update()
self.rope=rope:new(
x+0.5-dx*0.5,y+0.5-dy*0.5,
self.x+0.5,self.y+0.5,
self.x+0.5,self.y+0.1,
level:get_latch(dx,dy,x*8,y*8)
)
@ -836,7 +815,7 @@ function player:update()
if self.rope then
self.rope:update()
self.rope:drag_dst(self.x+0.5,self.y+0.5)
self.rope:drag_dst(self.x+0.5,self.y+0.1)
local tdx,tdy=self.rope:tug_orientxy()
if (tdx!=0) self.orientx=tdx
@ -853,6 +832,7 @@ function player:_vanish_if_requested()
self.vanish_frame+=1
if (self.fall_frame>0 or self.vanish_frame>20) then
self.rope=nil
level:restart()
kbd:release(5)
self.vanish_frame=20
@ -975,8 +955,8 @@ function rope:update()
self.latch.rec!=nil
then
self:drag_src(
self.latch.rec.mx+0.5+self.latch.ax_offset,
self.latch.rec.my+0.5+self.latch.ay_offset
self.latch.rec.px/8+0.5+self.latch.ax_offset,
self.latch.rec.py/8+0.5+self.latch.ay_offset
)
if #self.latch.rec.todo==0 then
@ -986,6 +966,8 @@ function rope:update()
end
end
if (not self:_check_sane()) self:destroy()
elseif self.state.name=="destroy" then -- destroy
self.state.frame+=1
if (self.state.frame>=5) self.state={name="done"}
@ -1057,13 +1039,14 @@ function rope:draw(artificial_dx,artificial_dy)
end
end
local n1=self.src
--[[
local sy=0
while true do
if (n1==nil) break
local x=n1.ax*8
local y=n1.ay*8
rectfill(x-1,y-1,x+1,y+1,12)
--print("ax="..n1.ax..",ay="..n1.ay,0,sy)
print("ax="..n1.ax..",ay="..n1.ay,0,sy)
sy+=7
local n0=n1.prev
@ -1075,7 +1058,9 @@ function rope:draw(artificial_dx,artificial_dy)
assert(ady==-1 or ady==0 or ady==1)
--assert(not (adx==0 and ady==0))
rectfill(x+2,y+2,x+4,y+4,3)
local c=3
if (n1.associated_with.dropped) c=8
rectfill(x+2,y+2,x+4,y+4,c)
pset(x+adx*2,y,9)
pset(x,y+ady*2,9)
else
@ -1093,17 +1078,6 @@ function rope:draw(artificial_dx,artificial_dy)
pset(p.ax*8+p.adx,p.ay*8,11)
pset(p.ax*8,p.ay*8+p.ady,11)
end
--[[
print("dirty:"..tostr(self.dirty),32,0,9)
print("busy:"..tostr(self:busy()),32,7,9)
print("state:"..tostr(self.state.name),32,14,9)
if self.all_ops!=nil then
for i,o in ipairs(self.all_ops) do
rect(o.mx*8,o.my*8,o.mx*8+7,o.my*8+7,4)
--print(o.mx..","..o.my,0,i*8,3)
end
end
]]--
end
@ -1116,13 +1090,25 @@ function rope:drag_src(x,y)
end
function rope:drag(n1,ax_new,ay_new)
-- TODO: stepwise?
self:_relax()
self:relax()
self:_drag(n1,ax_new,n1.ay)
self:_drag(n1,ax_new,ay_new)
self:_relax()
self:relax()
end
function rope:_relax()
function rope:relax()
local n=self.src
while true do
if (n==nil) break
if (n.associated_with) then
self:_drag(n,n.associated_with.ax,n.associated_with.ay)
end
n=n.next
end
local n0=self.src
while true do
if (n0==nil) return
@ -1132,12 +1118,14 @@ function rope:_relax()
if (n2==nil) return
if n1.associated_with!=nil then
local x0,y0=n0.ax,n0.ay
local x1,y1=n1.ax,n1.ay
local x2,y2=n2.ax,n2.ay
local would,x1_new,y1_new=would_stick(x0,y0,n1.associated_with,x2,y2)
if not would then
if not would and not (n1.ax==x1_new and n1.ay==y1_new) then
printh("relaxing: "..tostring(n0.associated_with).."->"..tostring(n1.associated_with).."->"..tostring(n2.associated_with))
self:_drag(n1,x1_new,y1_new)
n0=n1.prev
n2=n1.next
@ -1145,11 +1133,56 @@ function rope:_relax()
n2.prev=n0
n1.next=nil
n1.prev=nil
--n0=n0.next
else n0=n0.next end
else n0=n0.next end
end
end
function rope:_check_sane()
if (self.state.name!="latched") return true
if (level:busy()) return true
printh("start")
local n0=self.src
local qxs,qys={},{}
while true do
local n1=n0.next
if (n1==nil) break
for qx,qy in _rast(flr(n0.ax*2),flr(n0.ay*2),flr(n1.ax*2),flr(n1.ay*2)) do
add(qxs,qx)
add(qys,qy)
end
n0=n1
end
local function _blocked(qx,qy)
local mx0=(qx-1)\2
local mx1=qx\2
local my0=(qy-1)\2
local my1=qy\2
return level:mcoll(mx0,my0) and level:mcoll(mx1,my1)
end
for i=1,#qxs do
if (_blocked(qxs[i],qys[i])) printh("blocked"..qxs[i]..","..qys[i]) return false
end
for i=3,#qxs do
local qx1,qy1=qxs[i-1],qys[i-1]
if qx1%2==0 and qy1%2==0 then
local qx0,qy0=qxs[i-2],qys[i-2]
local qx2,qy2=qxs[i],qys[i]
local mx0,my0=qx0\2,qy0\2
local mx2,my2=qx2\2,qy2\2
if (level:mcoll(mx0,my2) and level:mcoll(mx2,my0)) printh("not traversable") return false
end
end
return true
end
function would_stick(x0,y0,anchor,x2,y2)
local x1,y1=anchor.ax,anchor.ay
if (x0>x2) x0,y0,x2,y2=x2,y2,x0,y0
@ -1175,7 +1208,9 @@ function would_stick(x0,y0,anchor,x2,y2)
if (y0<y2) ady=-adx
end
return anchor.adx!=-adx and anchor.ady!=-ady,x1_new,y1_new,adx,ady
local wouldnt=anchor.dropped or (anchor.adx==-adx or anchor.ady==-ady)
return not wouldnt,x1_new,y1_new,adx,ady
end
-- TODO: Upon adding a point, start from there to see if we need another
@ -1193,10 +1228,11 @@ function rope:_drag(n1,ax1_new,ay1_new)
ay_far_old=ay_far0
for ay_far_new in _stepfrom(ay_far0,ay_far1) do
for _,anchor in level:anchor_points() do
for anchor in level:anchor_points() do
if
not (anchor.ax==ax_pivot and anchor.ay==ay_pivot) and
not (anchor.ax==ax_new and anchor.ay==ay_new) and
not (anchor.ax==ax_far0 and anchor.ay==ay_far0) and
not (anchor.ax==ax_far1 and anchor.ay==ay_far1) and
(ax0<=anchor.ax and anchor.ax<=ax1) and
would_stick(ax_pivot,ay_pivot,anchor,ax_far,ay_far_new) and
crossed(
@ -1215,10 +1251,11 @@ function rope:_drag(n1,ax1_new,ay1_new)
ax_far_old=ax_far0
for ax_far_new in _stepfrom(ax_far0,ax_far1) do
for _,anchor in level:anchor_points() do
for anchor in level:anchor_points() do
if
not (anchor.ax==ax_pivot and anchor.ay==ay_pivot) and
not (anchor.ax==ax_new and anchor.ay==ay_new) and
not (anchor.ax==ax_far0 and anchor.ay==ay_far0) and
not (anchor.ax==ax_far1 and anchor.ay==ay_far1) and
would_stick(ax_pivot,ay_pivot,anchor,ax_far_new,ay_far) and
(ay0<=anchor.ay and anchor.ay<=ay1) and
crossed(
@ -1245,9 +1282,8 @@ function rope:_drag(n1,ax1_new,ay1_new)
if (n0==nil) break
local anch=_sweep_radar(n0.ax,n0.ay,ax1_old,ay1_old,ax1_new,ay1_new)
if (anch==nil) break
if (anch.ax==n0.ax and anch.y==n0.ay) break
if (anch.ax==n1.ax and anch.y==n1.ay) break
local n05={ax=anch.ax,ay=anch.ay,associated_with=anch,prev=n0,next=n1}
printh("creating post: "..tostring(n0.associated_with).."->"..tostring(n05.associated_with).."->"..tostring(n1.associated_with))
n0.next=n05
n1.prev=n05
n0=n05
@ -1258,9 +1294,8 @@ function rope:_drag(n1,ax1_new,ay1_new)
if (n2==nil) break
local anch=_sweep_radar(n2.ax,n2.ay,ax1_old,ay1_old,ax1_new,ay1_new)
if (anch==nil) break
if (anch.ax==n1.ax and anch.y==n1.ay) break
if (anch.ax==n2.ax and anch.y==n2.ay) break
local n15={ax=anch.ax,ay=anch.ay,associated_with=anch,prev=n1,next=n2}
printh("creating post: "..tostring(n1.associated_with).."->"..tostring(n15.associated_with).."->"..tostring(n2.associated_with))
n1.next=n15
n2.prev=n15
n2=n15
@ -1276,7 +1311,9 @@ function _stepfrom(x0,x1)
end
end
local mul=1
local mul=0.5
x0*=2
x1*=2
if (x0>x1) x0,x1,mul=-x0,-x1,-mul
local i=flr(x0)
local top=flr(x1)
@ -1320,18 +1357,33 @@ function distance(p1,p2)
return sqrt(dx*dx+dy*dy)
end
function rope:collide_rect(x1,y1,x2,y2,exclude_src,exclude_dst)
local a0=self.src
function rope:collide_mrect(mx0,my0,mw,mh,exclude_src,exclude_dst)
local mx1,my1=mx0+mw,my0+mh
local n0=self.src
mx0+=0.1
my0+=0.1
mx1-=0.1
my1-=0.1
while true do
local a1=a0.next
if (a1==nil) return false
--[[
if (_line_line(a0.x,a0.y,a1.x,a1.y,x1,y1,x2,y1)) return true
if (_line_line(a0.x,a0.y,a1.x,a1.y,x1,y1,x1,y2)) return true
if (_line_line(a0.x,a0.y,a1.x,a1.y,x1,y2,x2,y2)) return true
if (_line_line(a0.x,a0.y,a1.x,a1.y,x2,y1,x2,y2)) return true
]]--
a0=a0.next
local n1=n0.next
if (n1==nil) return false
local nd=n0
for i=1,exclude_dst do
nd=nd.next
if (nd==nil) return false
end
if exclude_src<=0 then
if (_line_line(n0.ax,n0.ay,n1.ax,n1.ay,mx0,my0,mx1,my0)) return true
if (_line_line(n0.ax,n0.ay,n1.ax,n1.ay,mx0,my0,mx0,my1)) return true
if (_line_line(n0.ax,n0.ay,n1.ax,n1.ay,mx0,my1,mx1,my1)) return true
if (_line_line(n0.ax,n0.ay,n1.ax,n1.ay,mx1,my0,mx1,my1)) return true
end
exclude_src-=1
n0=n1
end
end
@ -1391,12 +1443,12 @@ function rope:_tug(hypothetically)
local touched={}
for i=#ancs-1,2,-1 do
local ops_before_trash,hit_end1=self:_calc_push(ancs[i+1],ancs[i],ancs[i-1],ancs[i-2])
local ops_before_trash=self:_calc_push(ancs[i+1],ancs[i],ancs[i-1],ancs[i-2])
local ops_to_do,corners={}
if #ops_before_trash>0 then
ops_to_do=ops_before_trash
else
local ops_after_trash,hit_end2=self:_calc_push(ancs[i-2],ancs[i-1],ancs[i],ancs[i+1])
local ops_after_trash=self:_calc_push(ancs[i-2],ancs[i-1],ancs[i],ancs[i+1])
ops_to_do=ops_after_trash
end
@ -1406,39 +1458,9 @@ function rope:_tug(hypothetically)
if (hypothetically) return ancs,i-1
local dmx,dmy=ops[1].dmx,ops[1].dmy
local adjacent_ancs={}
for o in all(ops) do
add_adjacent_anchors(adjacent_ancs,o.mx,o.my)
level:tug_crate(o.mx,o.my,o.dmx,o.dmy)
end
for node=ancs[i-1].ix,ancs[i].ix do
local anc=self:_anc(node)
local x0,y0=anc.x,anc.y
local upd=function(x,y,force)
return {update=function(s,i)
if force or not level:pcoll(x,y) then
s.x=x
s.y=y
end
s.dirty=true
self.dirty=true
return true
end}
end
local dmxh,dmyh=dmx,dmy
local ax,ay=level:p2a(x0,y0)
if (adjacent_ancs[_amix(ax,ay)]==nil) dmxh,dmyh=0,0
anc.todo={
{},
upd(x0+dmxh*2,y0+dmyh*2),
upd(x0+dmxh*7,y0+dmyh*7),
upd(x0+dmxh*8,y0+dmyh*8),
}
end
for node=ancs[i-1].ix-1,ancs[i].ix+1 do
local anc=self:_anc(node)
end
return true
end
end
@ -1548,7 +1570,6 @@ function rope:_calc_push(
end
end
local hit_end=true
local ops2={}
for o in all(ops) do
if not level:mcoll(o.mx,o.my) then
@ -1556,18 +1577,16 @@ function rope:_calc_push(
else
local crate=level:get_crate(o.mx,o.my)
if crate==nil then
hit_end=false
break
else
if not level:can_move(false,o.mx,o.my,o.dmx,o.dmy,0,0) then
hit_end=false
break
end
end
add(ops2,o)
end
end
return ops2,hit_end
return ops2
end
function rope:_anchors_simplified()
@ -1578,7 +1597,18 @@ function rope:_anchors_simplified()
end
a=self.src
while a!=nil do
local point={x=a.ax*8,y=a.ay*8,ax=a.ax,ay=a.ay}
local point={
x=flr(a.ax*8+0.5),y=flr(a.ay*8+0.5),
ax=a.ax,ay=a.ay
}
if a.associated_with then
if (a.associated_with.adx==1) point.x-=1
if (a.associated_with.ady==1) point.y-=1
elseif a.prev==nil and self.latch then
if (self.latch.ax_offset<0) point.x-=1
if (self.latch.ay_offset<0) point.y-=1
end
if #points<=1 then
add(points,point)
elseif abs(
@ -1590,7 +1620,6 @@ function rope:_anchors_simplified()
add(points,point)
end
a=a.next
assert(#points<100)
end
return points
end