From f3df6c674a6210672b72038e92a2aa4f1e87b4bb Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Mon, 19 Dec 2022 23:15:55 -0800 Subject: [PATCH 01/12] Rope rewrite, part one --- chameleonic.p8 | 498 ++++++++++++------------------------------------- 1 file changed, 124 insertions(+), 374 deletions(-) diff --git a/chameleonic.p8 b/chameleonic.p8 index fb75cbe..aede61f 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -415,35 +415,27 @@ function add_adjacent_anchors(tbl,mx,my) end end -function level:reanchor(remove) - if remove or not self._anch then - self._anch={} - end - - for ax0=0,31 do - local ax1 = ax0-1+2*(ax0%2) - local mx0,mx1 = ax0\2,ax1\2 - for ay0=0,31 do - local ay1=ay0-1+2*(ay0%2) - local my0,my1=ay0\2,ay1\2 - - if ( - not self:mcoll(mx0,my0) and - not self:mcoll(mx0,my1) and - not self:mcoll(mx1,my0) and - self:mcoll(mx1,my1) - ) then - local px0,py0=level:a2p(ax0,ay0) - self._anch[_amix(ax0,ay0)]={ax=ax0,ay=ay0,x=px0,y=py0} +function level:reanchor() + self._anch={} + 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 + not self:mcoll(mx0,my1) and + not self:mcoll(mx1,my0) and + not self:mcoll(mx1,my1) + ) then + add(self._anch, { + ax=max(mx0,mx1),ay=max(my0,my1),adx=-dx,ady=-dy + }) + end end end end - - for _,cr in pairs(self._crates) do - add_adjacent_anchors(self._anch,cr.mx,cr.my) - end - - if (player.rope!=nil) player.rope:make_dirty() end function level:win_at(mx,my) @@ -453,20 +445,6 @@ function level:anchor_points() return pairs(self._anch) end -function level:anchors_in(px0,py0,px1,py1) - local anch,ax,xlim,sy,ylim=self._anch,px0\4,(px1+3)\4,py0\4,(py1+3)\4 - local ay=sy-1 - return function() - while true do - ay+=1 - if(ay>ylim)ay,ax=sy,ax+1 - if(ax>xlim)return nil - local anc=anch[_amix(ax,ay)] - if (anc) return anc - end - end -end - function level:get_open_pit(mx,my) local pit=self._pits[_mix(mx,my)] if (pit and pit.contents==nil) return pit @@ -574,8 +552,8 @@ function level:get_latch(dx,dy,px,py) return { el="crate", dx=dx1,dy=dy1, - px_offset=px-crate.px+dx1, - py_offset=py-crate.py+dy1, + ax_offset=dx1*0.5, + ay_offset=dy1*0.5, rec=crate } end @@ -758,7 +736,6 @@ end function player:any_busy() if (#self.todo>0) return true if (level:busy()) return true - if (self.rope!=nil and self.rope:busy()) return true return false end @@ -826,10 +803,17 @@ function player:update() self.todo=f4({{orienty=1,py=2},{py=7},{y=self.y+1}}) else wrongbleep:bleep() end elseif self.rope==nil and kbd:btnr(4) then - local rx,ry,rx2,ry2=self:_rope_pos() - local dx,dy=12*self.orientx,12*self.orienty + local dx,dy=self.orientx,self.orienty if (dy!=0) dx=0 - self.rope=rope:new(rx,ry,rx2,ry2,dx,dy) + + local x,y=self.x,self.y + while not level:mcoll(x,y) do x+=dx y+=dy end + + self.rope=rope:new( + x+0.5-dx*0.5,y+0.5-dy*0.5, + self.x+0.5,self.y+0.5, + level:get_latch(dx,dy,x*8,y*8) + ) self.todo={{ update=function() @@ -848,8 +832,7 @@ function player:update() if self.rope then self.rope:update() - local rx,ry=self:_rope_pos() - self.rope:drag_dst(rx,ry) + self.rope:drag_dst(self.x+0.5,self.y+0.5) local tdx,tdy=self.rope:tug_orientxy() if (tdx!=0) self.orientx=tdx @@ -881,14 +864,6 @@ function player:_fall() if (self.fall_frame<10) self.fall_frame+=1 end -function player:_rope_pos() - local px=self.x*8+self.px+4 - local px2=px - local py=self.y*8+self.py+3 - local py2=py - return px,py,px2,py2 -end - function player:draw() local px=self.x*8+self.px local py=self.y*8+self.py @@ -956,18 +931,21 @@ rope={} rope.__index=rope function rope:new( - x,y,src_x,src_y,dx,dy + src_ax,src_ay,dst_ax,dst_ay,latch ) local r={ id=0, - src={x=src_x,y=src_y,todo={}}, - ancs={}, - dst={x=x,y=y,todo={}}, - state={name="cast",dx=dx,dy=dy}, - dirty=true, - latch=nil, - latch_frame=0, + anchors={ + {ax=src_ax,ay=src_ay,prev=nil,next=nil}, + {ax=dst_ax,ay=dst_ay,prev=nil,next=nil} + }, + state={name="cast",frame=0}, + latch=latch, } + r.src=r.anchors[1] + r.dst=r.anchors[2] + r.src.next=r.dst + r.dst.prev=r.src setmetatable(r,rope) return r end @@ -980,30 +958,12 @@ function rope:done() return self.state.name=="done" end -function rope:busy() - for i=0,#self.ancs+1 do - if (#(self:_anc(i).todo)>0) return true - end - return false -end - function rope:update() - local was_busy=self:busy() - for i=0,#self.ancs+1 do - local anc=self:_anc(i) - _apply(anc,anc.todo,i) - end - local is_busy=self:busy() - if (was_busy and not is_busy) self.dirty=true - if self.state.name=="cast" then - self:continue_cast() - elseif self.state.name=="latched" then - self.latch_frame+=1 - if self.latch_frame>=10 then - self.latch_frame=10 - end + self.state.frame+=1 + if (self.state.frame>=3) self.state={name="latched"} + elseif self.state.name=="latched" then if (self.latch==nil) wrongbleep:bleep(3) self:destroy() return if @@ -1011,8 +971,8 @@ function rope:update() self.latch.rec!=nil then self:drag_src( - self.latch.rec.px+self.latch.px_offset, - self.latch.rec.py+self.latch.py_offset + self.latch.rec.mx+0.5+self.latch.ax_offset, + self.latch.rec.my+0.5+self.latch.ay_offset ) if #self.latch.rec.todo==0 then @@ -1022,7 +982,6 @@ function rope:update() end end - if (not is_busy) self:_tidy_up_gen() elseif self.state.name=="destroy" then -- destroy self.state.frame+=1 if (self.state.frame>=5) self.state={name="done"} @@ -1036,41 +995,11 @@ function rope:destroy() self.state={name="destroy",frame=0} end -function rope:continue_cast() - local dx,dy=self.state.dx,self.state.dy - local x0=self.src.x - local y0=self.src.y - local x1=x0+dx - local y1=y0+dy - - for x,y in _rast( - x0,y0,x1,y1 - ) do - local latch= - level:get_latch(dx,dy,x,y) - - if latch!=nil or level:pcoll(x,y) then - self.latch=latch - self.state={name="latched"} - break - end - self.src={x=x,y=y,todo={},dirty=true} - end -end - -function rope:_reindex() - self.src.ix=0 - self.dst.ix=#self.ancs - for i,anc in ipairs(self.ancs) do - anc.ix=i - end -end - function rope:draw(artificial_dx,artificial_dy) local points,highlight=self:_tug(true) - if (self:busy()) highlight=nil if (self.state.name=="done") return local perc_to_show=1.0 + if (self.state.name=="cast") perc_to_show=self.state.frame/2 if (self.state.name=="destroy") perc_to_show=(1.0-self.state.frame/5)^2 points[#points]={x=points[#points].x+artificial_dx,y=points[#points].y+artificial_dy} @@ -1123,7 +1052,13 @@ function rope:draw(artificial_dx,artificial_dy) rectfill(x,y-1,x-1,y-3,color) end end + for _,p in pairs(level._anch) do + pset(p.ax*8,p.ay*8,11) + pset(p.ax*8+p.adx,p.ay*8,11) + pset(p.ax*8,p.ay*8+p.ady,11) + end + --[[ for i=0,#self.ancs+1 do p=self:_anc(i) local c=12 @@ -1131,10 +1066,6 @@ function rope:draw(artificial_dx,artificial_dy) 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,12) end - --[[ - for _,p in pairs(level._anch) do - pset(p.x,p.y,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) @@ -1147,247 +1078,72 @@ function rope:draw(artificial_dx,artificial_dy) ]]-- end -function rope:_anc(i) - if (i==0) return self.src - if (i==#self.ancs+1) return self.dst - return self.ancs[i] -end - function rope:drag_dst(x,y) - self:drag(function() return #self.ancs+1 end,x,y) + self:drag(self.dst,x,y) end function rope:drag_src(x,y) - self:drag(function() return 0 end,x,y) + self:drag(self.src,x,y) end function rope:drag( - i,x,y + n1,ax_new,ay_new ) - local anc=self:_anc(i()) - local busy=self:busy() - - for x,y in _rast( - anc.x,anc.y,x,y - ) do - local a=self:_anc(i()) - if not (_point_eq(a,{x=x,y=y})) then - a.x=x - a.y=y - a.dirty=true - self.dirty=true - if (not busy) self:_tidy_up_gen() - end - end + -- TODO: stepwise? + rope:_drag1(n1,ax_new,ay_new) end -function rope:make_dirty(only_if_invalid) - local invalid=false - for a=0,#self.ancs do - local a0=self:_anc(a) - local a1=self:_anc(a+1) - if not level:can_stretch(a0,a1) then - a0.dirty=true - a1.dirty=true - invalid=true - end - end - if (invalid or not only_if_invalid) self.dirty=true -end +-- TODO: Upon adding a point, start from there to see if we need another +-- rather than adding at most one +function rope:_drag1(n1,ax1_new,ay1_new) + local ax1_old,ay1_old=n1.ax,n1.ay + local n0=n1.prev -function rope:_tidy_up_gen() - self:make_dirty(true) - - local busy=self:busy() - if (not self:latched()) return - if (not self.dirty) return - - local settled=true - local loop=function(f) - local a=0 - while a<=#self.ancs+1 do - local anc=self:_anc(a) - if anc.dirty then - anc.seen=true - if self[f](self,a,busy) then - settled=false anc.changed=true - a=0 - end - end - a+=1 - end - end - - local mark_unseen=function() - touched={} - for a=0,#self.ancs+1,1 do - local anc=self:_anc(a) - anc.seen=false - anc.changed=false - end - end - - local propagate_dirty=function(f) - for a=0,#self.ancs+1,1 do - local a1=self:_anc(a) - if a1.dirty then - local a0=self:_anc(a-1) - if (a0!=nil) a0.dirty=true - - local a2=self:_anc(a+1) - if (a2!=nil) a2.dirty=true + if n0!=nil then + local ax0,ay0=n0.ax,n0.ay + for _,anchor in level:anchor_points() do + if + (_in_box(anchor.ax,anchor.ay,ax0,ay0,ax1_old,ay1_old) or + _in_box(anchor.ax,anchor.ay,ax0,ay0,ax1_new,ay1_new)) and + _which_side(anchor.ax,anchor.ay,ax0,ay0,ax1_old,ay1_old) != + _which_side(anchor.ax,anchor.ay,ax0,ay0,ax1_new,ay1_new) + then + local n05={ax=anchor.ax,ay=anchor.ay,prev=n0,next=n1} + n0.next = n05 + n1.prev = n05 end end end - while true do - settled=true - - mark_unseen() - propagate_dirty() - - loop("_find_needed_anchors") - loop("_find_touched_anchors") - loop("_elide_point") - - for a=0,#self.ancs+1,1 do - local anc=self:_anc(a) - if (anc.seen) anc.dirty=anc.changed - if (anc.dirty) settled=false - end - - if (settled) break - end - - if (self:busy()) return - - for i=0,#self.ancs do - local a0=self:_anc(i) - local a1=self:_anc(i+1) - if not level:can_stretch(a0,a1) then - self:destroy() + local n2=n1.next + if n2!=nil then + local ax2,ay2=n2.ax,n2.ay + for _,anchor in level:anchor_points() do + if + (_in_box(anchor.ax,anchor.ay,ax1_old,ay1_old,ax2,ay2) or + _in_box(anchor.ax,anchor.ay,ax1_new,ay1_new,ax2,ay2)) and + _which_side(anchor.ax,anchor.ay,ax1_old,ay1_old,ax2,ay2) != + _which_side(anchor.ax,anchor.ay,ax1_new,ay1_new,ax2,ay2) + then + local n15={ax=anchor.ax,ay=anchor.ay,prev=n1,next=n2} + n1.next = n15 + n2.prev = n15 + end end end - self.dirty=false + n1.ax=ax1_new + n1.ay=ay1_new end -function rope:_find_needed_anchors(i,busy) - if (i<=0) return false - if (#self.ancs+13) tdx=1 - if (dx<-3) tdx=-1 + if (dx>3/8) tdx=1 + if (dx<-3/8) tdx=-1 - local dy=a0.y-a1.y + local dy=a0.ay-a1.ay local tdy=0 if abs(dy)>abs(dx)/2 then - if (dy>3) tdy=1 - if (dy<-3) tdy=-1 + if (dy>3/8) tdy=1 + if (dy<-3/8) tdy=-1 end return tdx,tdy end function rope:tug() - self:_tidy_up_gen() if (not self:latched()) return - local rc=self:_tug() - self:_tidy_up_gen() - return rc + return self:_tug() end function rope:_tug(hypothetically) @@ -1568,12 +1323,7 @@ function rope:_tug(hypothetically) then if (hypothetically) return ancs,0 - level:tug_crate( - mx0,my0, - dmx,dmy - ) - -- be busy for 4 ticks while the crate moves - self:_anc(0).todo={{},{},{},{},{}} + level:tug_crate(mx0,my0,dmx,dmy) return true end end @@ -1666,26 +1416,26 @@ end function rope:_anchors_simplified() -- todo: cache this - self:_reindex() - local points={} local _slope = function(p0,p1) return atan2(p1.y-p0.y,p1.x-p0.x) end - for i=0,#self.ancs+1,1 do - local anc=self:_anc(i) + a=self.src + while a!=nil do + local point={x=a.ax*8,y=a.ay*8,ax=a.ax,ay=a.ay} if #points<=1 then - add(points,anc) + add(points,point) elseif abs( _slope(points[#points-1],points[#points])- - _slope(points[#points],anc) + _slope(points[#points],point) )==0 then -- epsilon? - points[#points]=anc + points[#points]=point else - add(points,anc) + add(points,point) end + a=a.next + assert(#points<100) end - return points end -- 2.34.1 From a7b016c4b2b1eae07599fb97f95902720bf5f2d2 Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Mon, 19 Dec 2022 23:20:48 -0800 Subject: [PATCH 02/12] We should only care about anchors on the old path --- chameleonic.p8 | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/chameleonic.p8 b/chameleonic.p8 index aede61f..414ce32 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -1103,8 +1103,7 @@ function rope:_drag1(n1,ax1_new,ay1_new) local ax0,ay0=n0.ax,n0.ay for _,anchor in level:anchor_points() do if - (_in_box(anchor.ax,anchor.ay,ax0,ay0,ax1_old,ay1_old) or - _in_box(anchor.ax,anchor.ay,ax0,ay0,ax1_new,ay1_new)) and + _in_box(anchor.ax,anchor.ay,ax0,ay0,ax1_old,ay1_old) and _which_side(anchor.ax,anchor.ay,ax0,ay0,ax1_old,ay1_old) != _which_side(anchor.ax,anchor.ay,ax0,ay0,ax1_new,ay1_new) then @@ -1120,8 +1119,7 @@ function rope:_drag1(n1,ax1_new,ay1_new) local ax2,ay2=n2.ax,n2.ay for _,anchor in level:anchor_points() do if - (_in_box(anchor.ax,anchor.ay,ax1_old,ay1_old,ax2,ay2) or - _in_box(anchor.ax,anchor.ay,ax1_new,ay1_new,ax2,ay2)) and + _in_box(anchor.ax,anchor.ay,ax1_old,ay1_old,ax2,ay2) and _which_side(anchor.ax,anchor.ay,ax1_old,ay1_old,ax2,ay2) != _which_side(anchor.ax,anchor.ay,ax1_new,ay1_new,ax2,ay2) then -- 2.34.1 From 675ef20115a374c2d494f7d82b8da18c811d0021 Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Mon, 19 Dec 2022 23:22:37 -0800 Subject: [PATCH 03/12] Add comment expressing uncertainty --- chameleonic.p8 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/chameleonic.p8 b/chameleonic.p8 index 414ce32..f594227 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -1103,6 +1103,11 @@ function rope:_drag1(n1,ax1_new,ay1_new) local ax0,ay0=n0.ax,n0.ay for _,anchor in level:anchor_points() do if + -- NOTE: Do we need to look at the new path too? Something + -- just feels wrong about this check. + -- Really our goal is to figure out if moving the end of the + -- path crosses over the anchor, which feels like a point/triangle + -- collision _in_box(anchor.ax,anchor.ay,ax0,ay0,ax1_old,ay1_old) and _which_side(anchor.ax,anchor.ay,ax0,ay0,ax1_old,ay1_old) != _which_side(anchor.ax,anchor.ay,ax0,ay0,ax1_new,ay1_new) -- 2.34.1 From 6821d6cb535b1f0b2b7be7eae8845ca6eed6c49e Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Mon, 19 Dec 2022 23:28:26 -0800 Subject: [PATCH 04/12] Add further algo notes --- chameleonic.p8 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/chameleonic.p8 b/chameleonic.p8 index f594227..3993ee0 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -1108,6 +1108,14 @@ function rope:_drag1(n1,ax1_new,ay1_new) -- Really our goal is to figure out if moving the end of the -- path crosses over the anchor, which feels like a point/triangle -- collision + -- We also want to pick the anchor that the ray sweeps over first, not + -- the closest one. + -- Maybe we should + -- (1) sort by angle difference from the original line + -- (2) ignore cases where the angle is not between the two lines + -- (3) ignore cases where the created point is further from the original point + -- than the _intended_ one would be + -- _in_box(anchor.ax,anchor.ay,ax0,ay0,ax1_old,ay1_old) and _which_side(anchor.ax,anchor.ay,ax0,ay0,ax1_old,ay1_old) != _which_side(anchor.ax,anchor.ay,ax0,ay0,ax1_new,ay1_new) -- 2.34.1 From b0d356aeee4eee3b6d89a2dfdbfc2d81f62cf03e Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Mon, 19 Dec 2022 23:29:22 -0800 Subject: [PATCH 05/12] One final note --- chameleonic.p8 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chameleonic.p8 b/chameleonic.p8 index 3993ee0..e9408bc 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -1115,6 +1115,8 @@ function rope:_drag1(n1,ax1_new,ay1_new) -- (2) ignore cases where the angle is not between the two lines -- (3) ignore cases where the created point is further from the original point -- than the _intended_ one would be + -- Note that if x or y is held constant, this can be implemented + -- with a sort on x or y. For our game this is true. -- _in_box(anchor.ax,anchor.ay,ax0,ay0,ax1_old,ay1_old) and _which_side(anchor.ax,anchor.ay,ax0,ay0,ax1_old,ay1_old) != -- 2.34.1 From 60f1c2e1123275f6bc905e2b69bec7869cd0c248 Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Tue, 20 Dec 2022 13:48:23 -0800 Subject: [PATCH 06/12] Well, this is closer to right!! --- chameleonic.p8 | 126 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 85 insertions(+), 41 deletions(-) diff --git a/chameleonic.p8 b/chameleonic.p8 index e9408bc..220c8eb 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -7,6 +7,10 @@ real_modules={} frame=0 function _init() + -- cls(0) + -- for i in _stepfrom(0.5,-2.5) do print(i) end + -- assert(false) + _doall("init") end function _update() frame+=1 @@ -1096,59 +1100,99 @@ end -- TODO: Upon adding a point, start from there to see if we need another -- rather than adding at most one function rope:_drag1(n1,ax1_new,ay1_new) - local ax1_old,ay1_old=n1.ax,n1.ay - local n0=n1.prev + local function _sweep_radar(ax_pivot,ay_pivot,ax_far0,ay_far0,ax_far1,ay_far1) + if ax_far0==ax_far1 then + local ax_far=ax_far0 + local ax0,ax1=_mnmx(ax_pivot,ax_far) - if n0!=nil then - local ax0,ay0=n0.ax,n0.ay - for _,anchor in level:anchor_points() do - if - -- NOTE: Do we need to look at the new path too? Something - -- just feels wrong about this check. - -- Really our goal is to figure out if moving the end of the - -- path crosses over the anchor, which feels like a point/triangle - -- collision - -- We also want to pick the anchor that the ray sweeps over first, not - -- the closest one. - -- Maybe we should - -- (1) sort by angle difference from the original line - -- (2) ignore cases where the angle is not between the two lines - -- (3) ignore cases where the created point is further from the original point - -- than the _intended_ one would be - -- Note that if x or y is held constant, this can be implemented - -- with a sort on x or y. For our game this is true. - -- - _in_box(anchor.ax,anchor.ay,ax0,ay0,ax1_old,ay1_old) and - _which_side(anchor.ax,anchor.ay,ax0,ay0,ax1_old,ay1_old) != - _which_side(anchor.ax,anchor.ay,ax0,ay0,ax1_new,ay1_new) - then - local n05={ax=anchor.ax,ay=anchor.ay,prev=n0,next=n1} - n0.next = n05 - n1.prev = n05 + ay_far_old=ay_far0 + for ay_far_new in _stepfrom(ay_far0,ay_far1) do + for _,anchor in level:anchor_points() do + if + (ax0<=anchor.ax and anchor.ax<=ax1) and + _which_side(anchor.ax,anchor.ay,ax_pivot,ay_pivot,ax_far,ay_far_old)!= + _which_side(anchor.ax,anchor.ay,ax_pivot,ay_pivot,ax_far,ay_far_new) + then + return anchor + end + end + ay_far_old=ay_far_new end + elseif ay_far0==ay_far1 then + local ay_far=ay_far0 + local ay0,ay1=_mnmx(ay_pivot,ay_far) + + ax_far_old=ax_far0 + for ax_far_new in _stepfrom(ax_far0,ax_far1) do + for _,anchor in level:anchor_points() do + if + (ay0<=anchor.ay and anchor.ay<=ay1) and + _which_side(anchor.ax,anchor.ay,ax_pivot,ay_pivot,ax_far_old,ay_far)!= + _which_side(anchor.ax,anchor.ay,ax_pivot,ay_pivot,ax_far_new,ay_far) + then + return anchor + end + end + ax_far_old=ax_far_new + end + else + assert(false, "wtf?") end end + local ax1_old,ay1_old=n1.ax,n1.ay + + local n0=n1.prev + while true do + if (n0==nil) break + local anch=_sweep_radar(n0.ax,n0.ay,ax1_old,ay1_old,ax1_new,ay1_new) + if (anch==nil) break + local n05={ax=anch.ax,ay=anch.ay,prev=n0,next=n1} + n0.next=n05 + n1.prev=n05 + n0=n05 + end + local n2=n1.next - if n2!=nil then - local ax2,ay2=n2.ax,n2.ay - for _,anchor in level:anchor_points() do - if - _in_box(anchor.ax,anchor.ay,ax1_old,ay1_old,ax2,ay2) and - _which_side(anchor.ax,anchor.ay,ax1_old,ay1_old,ax2,ay2) != - _which_side(anchor.ax,anchor.ay,ax1_new,ay1_new,ax2,ay2) - then - local n15={ax=anchor.ax,ay=anchor.ay,prev=n1,next=n2} - n1.next = n15 - n2.prev = n15 - end - end + while true do + if (n2==nil) break + local anch=_sweep_radar(n2.ax,n2.ay,ax1_old,ay1_old,ax1_new,ay1_new) + if (anch==nil) break + local n15={ax=anch.ax,ay=anch.ay,prev=n1,next=n2} + n1.next=n15 + n2.prev=n15 + n2=n15 end n1.ax=ax1_new n1.ay=ay1_new end +function _stepfrom(x0,x1) + local done=false + if x0==x1 then + return function() + if (done) return nil + done=true return x0 + end + end + + local mul=1 + if (x0>x1) x0,x1,mul=-x0,-x1,-mul + local i=flr(x0) + local top=flr(x1) + return function() + if (done) return nil + i+=1 + if i>top then + done = true + if (x1!=flr(x1)) return mul*x1 + return nil + end + return mul*i + end +end + function _in_box(x,y,x0,y0,x1,y1) x0,x1=_mnmx(x0,x1) y0,y1=_mnmx(y0,y1) -- 2.34.1 From 281ed4b40fa090f057643ef9419ea08ab002bfd6 Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Tue, 20 Dec 2022 15:04:00 -0800 Subject: [PATCH 07/12] Elide points as needed --- chameleonic.p8 | 81 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 69 insertions(+), 12 deletions(-) diff --git a/chameleonic.p8 b/chameleonic.p8 index 220c8eb..3348edd 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -1047,9 +1047,9 @@ function rope:draw(artificial_dx,artificial_dy) local color=8 if (highlight==0) color=12 if self.latch.dx==-1 and self.latch.dy==0 then - rectfill(x+1,y,x+3,y+1,color) + rectfill(x+1,y-1,x+3,y,color) elseif self.latch.dx==1 and self.latch.dy==0 then - rectfill(x-1,y,x-3,y+1,color) + rectfill(x-1,y-1,x-3,y,color) elseif self.latch.dx==0 and self.latch.dy==-1 then rectfill(x,y+1,x-1,y+3,color) elseif self.latch.dx==0 and self.latch.dy==1 then @@ -1090,17 +1090,69 @@ function rope:drag_src(x,y) self:drag(self.src,x,y) end -function rope:drag( - n1,ax_new,ay_new -) +function rope:drag(n1,ax_new,ay_new) -- TODO: stepwise? - rope:_drag1(n1,ax_new,ay_new) + self:_relax() + self:_drag(n1,ax_new,ay_new) + self:_relax() +end + +function rope:_relax() + local n0=self.src + while true do + if (n0==nil) return + local n1=n0.next + if (n1==nil) return + local n2=n1.next + 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 + + if (x0>x2) x0,y0,x2,y2=x2,y2,x0,y0 + + local dx=x2-x0 + local dy=y2-y0 + + local adx,ady + local x1_new,y1_new + if abs(dx)>abs(dy) then + local dprop=(x1-x0)/dx + x1_new,y1_new=x1,y0+dprop*(y2-y0) + ady=sgn0(y1_new-y1) + adx=0 + if (y0>y2) adx=ady + if (y0y2) ady=adx + if (y0 Date: Tue, 20 Dec 2022 15:18:01 -0800 Subject: [PATCH 08/12] Save current changes --- chameleonic.p8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chameleonic.p8 b/chameleonic.p8 index 3348edd..0c176b5 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -1134,7 +1134,7 @@ function rope:_relax() if (y0 Date: Tue, 20 Dec 2022 16:27:08 -0800 Subject: [PATCH 09/12] First version I couldn't immediately break --- chameleonic.p8 | 118 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 38 deletions(-) diff --git a/chameleonic.p8 b/chameleonic.p8 index 0c176b5..abd61e5 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -1056,6 +1056,38 @@ function rope:draw(artificial_dx,artificial_dy) rectfill(x,y-1,x-1,y-3,color) 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) + sy+=7 + + local n0=n1.prev + local n2=n1.next + if n0!=nil and n2!=nil then + if n1.associated_with then + local _,_,_,adx,ady=would_stick(n0.ax,n0.ay,n1.associated_with,n2.ax,n2.ay) + assert(adx==-1 or adx==0 or adx==1) + 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) + pset(x+adx*2,y,9) + pset(x,y+ady*2,9) + else + rectfill(x+2,y+2,x+4,y+4,2) + end + else + rectfill(x+2,y+2,x+4,y+4,4) + end + + n1=n1.next + end + for _,p in pairs(level._anch) do pset(p.ax*8,p.ay*8,11) pset(p.ax*8+p.adx,p.ay*8,11) @@ -1063,13 +1095,6 @@ function rope:draw(artificial_dx,artificial_dy) 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,12) - 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) @@ -1111,31 +1136,9 @@ function rope:_relax() local x1,y1=n1.ax,n1.ay local x2,y2=n2.ax,n2.ay - if (x0>x2) x0,y0,x2,y2=x2,y2,x0,y0 - - local dx=x2-x0 - local dy=y2-y0 - - local adx,ady - local x1_new,y1_new - if abs(dx)>abs(dy) then - local dprop=(x1-x0)/dx - x1_new,y1_new=x1,y0+dprop*(y2-y0) - ady=sgn0(y1_new-y1) - adx=0 - if (y0>y2) adx=ady - if (y0y2) ady=adx - if (y0x2) x0,y0,x2,y2=x2,y2,x0,y0 + + local dx=x2-x0 + local dy=y2-y0 + + local adx,ady + local x1_new,y1_new + if abs(dx)>abs(dy) then + local dprop=(x1-x0)/dx + x1_new,y1_new=x1,y0+dprop*(y2-y0) + ady=sgn0(y1_new-y1) + adx=0 + if (y0>y2) adx=ady + if (y0y2) ady=adx + if (y0 Date: Tue, 20 Dec 2022 20:45:33 -0800 Subject: [PATCH 10/12] Everything _seems_ to work --- chameleonic.p8 | 415 ++++++++++++++++++++++++++----------------------- 1 file changed, 222 insertions(+), 193 deletions(-) diff --git a/chameleonic.p8 b/chameleonic.p8 index abd61e5..abc25a6 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -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<"..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"..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 -- 2.34.1 From 535fcf760133175d376818540facb621351ab456 Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Tue, 20 Dec 2022 21:37:09 -0800 Subject: [PATCH 11/12] Clean up some residual messes --- chameleonic.p8 | 200 +++++++++++++++++++++---------------------------- 1 file changed, 85 insertions(+), 115 deletions(-) diff --git a/chameleonic.p8 b/chameleonic.p8 index abc25a6..af59265 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -7,10 +7,6 @@ real_modules={} frame=0 function _init() - -- cls(0) - -- for i in _stepfrom(0.5,-2.5) do print(i) end - -- assert(false) - _doall("init") end function _update() frame+=1 @@ -97,13 +93,13 @@ function _apply(x,ts,a) local t=deli(ts,1) for k,v in pairs(t) do if k=="update" then - if not v(x,a) then - add(ts,t,1) - end + -- else x[k]=v end end + + if (t and t.update and not t.update(x,a)) add(ts,t,1) end function sgn0(x) @@ -401,28 +397,16 @@ 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]= - self._coll_nocrate[mxy] or + fget(self:_mget(mx,my),7) or self._crates[mxy]!=nil end end end -function add_adjacent_anchors(tbl,mx,my) - for ax in all{mx*2-1,mx*2+2} do - for ay in all{my*2-1,my*2+2} do - local px,py=level:a2p(ax,ay) - tbl[_amix(ax,ay)]={ax=ax0,ay=ay0,x=px,y=py} - end - end -end - function level:reanchor() local anch_new={} for dxy in all{{-1,-1},{1,-1},{-1,1},{1,1}} do @@ -433,7 +417,7 @@ function level:reanchor() local mx1,my1=mx0+dx,my0+dy if ( - self:mcoll_nocrate(mx0,my0) and + self:mcoll(mx0,my0) and not self:get_crate(mx0,my0) and not self:mcoll(mx0,my1) and not self:mcoll(mx1,my0) and not self:mcoll(mx1,my1) @@ -497,12 +481,6 @@ function level:get_open_pit(mx,my) if (pit and pit.contents==nil) return pit end -function level:point_anchor(px,py) - local ax,ay=self:p2a(px,py) - local anc=self._anch[_amix(ax,ay)] - return anc -end - function level:spawn_exit() self._wins={} local spawned=false @@ -516,9 +494,11 @@ function level:spawn_exit() end local win_at=function(x,y) if (self:_mget(x,y)!=18) return - for n in all(neighbors{x=x,y=y}) do - if n.x<0 or n.y<0 or n.x>15 or n.y>15 then - self._wins[_mix(n.x,n.y)]=true + for nx=x-1,x+1 do + for ny=y-1,y+1 do + if nx<0 or ny<0 or nx>15 or ny>15 then + self._wins[_mix(nx,ny)]=true + end end end end @@ -529,22 +509,9 @@ function level:spawn_exit() assert(spawned) end -function level:p2a(px,py) - return px\4,py\4 -end - -function level:a2p(ax,ay) - local px=ax*4+3*(ax%2) - local py=ay*4+3*(ay%2) - return px,py -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) @@ -671,20 +638,16 @@ function level:tug_crate(mx0,my0,dmx,dmy) local mx1,my1=mx0+dmx,my0+dmy local mxy1=_mix(mx1,my1) - existing.mx=mx1 - existing.my=my1 existing.todo={ - {px=mx0*8+dmx*2,py=my0*8+dmy*2}, - {px=mx0*8+dmx*7,py=my0*8+dmy*7}, - {px=mx1*8,py=my1*8,update=function() - self:reanchor(true) + {px=mx1*8+dmx,py=my1*8+dmy,mx=mx1,my=my1,update=function() + self:recollide() + self:reanchor() return true - end} + end}, + {px=mx1*8,py=my1*8} } self._crates[mxy1]=existing - self:recollide() - self:reanchor(false) end -->8 @@ -955,8 +918,8 @@ function rope:update() self.latch.rec!=nil then self:drag_src( - self.latch.rec.px/8+0.5+self.latch.ax_offset, - self.latch.rec.py/8+0.5+self.latch.ay_offset + self.latch.rec.mx+0.5+self.latch.ax_offset, + self.latch.rec.my+0.5+self.latch.ay_offset ) if #self.latch.rec.todo==0 then @@ -1023,28 +986,34 @@ function rope:draw(artificial_dx,artificial_dy) end -- draw latch - if self.latch!=nil and perc_to_show>=1.0 then - local x,y=points[1].x,points[1].y + if self.latch!=nil and self.latch.rec and perc_to_show>=1.0 then + local x,y=self.latch.rec.px,self.latch.rec.py local ldx,ldy=self.latch.dx,self.latch.dy local color=8 if (highlight==0) color=12 if self.latch.dx==-1 and self.latch.dy==0 then - rectfill(x+1,y-1,x+3,y,color) + rectfill(x,y+3,x+2,y+4,color) elseif self.latch.dx==1 and self.latch.dy==0 then - rectfill(x-1,y-1,x-3,y,color) + rectfill(x+5,y+3,x+7,y+4,color) elseif self.latch.dx==0 and self.latch.dy==-1 then - rectfill(x,y+1,x-1,y+3,color) + rectfill(x+3,y,x+4,y+2,color) elseif self.latch.dx==0 and self.latch.dy==1 then - rectfill(x,y-1,x-1,y-3,color) + rectfill(x+3,y+5,x+4,y+7,color) end end - local n1=self.src + + -- debug --[[ + local n1=self.src local sy=0 while true do if (n1==nil) break local x=n1.ax*8 local y=n1.ay*8 + if n1.associated_with then + if (n1.associated_with.adx>0) x-=1 + if (n1.associated_with.ady>0) y-=1 + end rectfill(x-1,y-1,x+1,y+1,12) print("ax="..n1.ax..",ay="..n1.ay,0,sy) sy+=7 @@ -1074,11 +1043,14 @@ function rope:draw(artificial_dx,artificial_dy) end for _,p in pairs(level._anch) do - pset(p.ax*8,p.ay*8,11) - pset(p.ax*8+p.adx,p.ay*8,11) - pset(p.ax*8,p.ay*8+p.ady,11) + local x,y=p.ax*8,p.ay*8 + if (p.adx>0) x-=1 + if (p.ady>0) y-=1 + pset(x,y,11) + pset(x+p.adx,y,11) + pset(x,y+p.ady,11) end - ]]-- + ]] end function rope:drag_dst(x,y) @@ -1125,7 +1097,7 @@ function rope:relax() local would,x1_new,y1_new=would_stick(x0,y0,n1.associated_with,x2,y2) 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)) + --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 @@ -1133,7 +1105,6 @@ 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 @@ -1143,7 +1114,6 @@ 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={},{} @@ -1152,32 +1122,62 @@ function rope:_check_sane() 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) + if not (qx==qxs[#qxs] and qy==qys[#qys]) then + add(qxs,qx) + add(qys,qy) + end end n0=n1 end - local function _blocked(qx,qy) + local function _possible_tiles(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) + local poss={} + for mx=mx0,mx1 do + for my=my0,my1 do + add(poss,{mx=mx,my=my}) + end + end + return poss end - for i=1,#qxs do - if (_blocked(qxs[i],qys[i])) printh("blocked"..qxs[i]..","..qys[i]) return false + local function _blocked(qx,qy) + for i in all(_possible_tiles(qx,qy)) do + if (not level:mcoll(i.mx,i.my)) return false + end + return true end + -- find cases where i move through an impassable zone + for i=1,#qxs do + if (_blocked(qxs[i],qys[i])) return false + end + + -- find cases where i am cut off diagonally 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 + local ok=false + for m0 in all(_possible_tiles(qxs[i-2],qys[i-2])) do + for m2 in all(_possible_tiles(qxs[i],qys[i])) do + local mx0,my0=m0.mx,m0.my + local mx2,my2=m2.mx,m2.my + if not (level:mcoll(mx0,my0) or level:mcoll(mx2,my2)) then + local dmx,dmy=abs(mx2-mx0),abs(my2-my0) + + if dmx==1 and dmy==1 and level:mcoll(mx0,my2) and level:mcoll(mx2,my0) then + else + --printh("ok! "..tostring({qxs[i-2],qys[i-2]})..tostring({qxs[i],qys[i]})..tostring(m0)..tostring(m2)) + ok=true + end + end + end + end + + if (not ok) return false end end return true @@ -1213,8 +1213,6 @@ function would_stick(x0,y0,anchor,x2,y2) return not wouldnt,x1_new,y1_new,adx,ady end --- TODO: Upon adding a point, start from there to see if we need another --- rather than adding at most one function rope:_drag(n1,ax1_new,ay1_new) local function _sweep_radar(ax_pivot,ay_pivot,ax_far0,ay_far0,ax_far1,ay_far1) if (ax_far0==ax_far1 and ay_far0==ay_far1) return nil @@ -1268,8 +1266,6 @@ function rope:_drag(n1,ax1_new,ay1_new) end ax_far_old=ax_far_new end - else - assert(false, "wtf?") end end @@ -1283,7 +1279,7 @@ function rope:_drag(n1,ax1_new,ay1_new) local anch=_sweep_radar(n0.ax,n0.ay,ax1_old,ay1_old,ax1_new,ay1_new) if (anch==nil) 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)) + --printh("creating post: "..tostring(n0.associated_with).."->"..tostring(n05.associated_with).."->"..tostring(n1.associated_with)) n0.next=n05 n1.prev=n05 n0=n05 @@ -1295,7 +1291,7 @@ function rope:_drag(n1,ax1_new,ay1_new) local anch=_sweep_radar(n2.ax,n2.ay,ax1_old,ay1_old,ax1_new,ay1_new) if (anch==nil) 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)) + --printh("creating post: "..tostring(n1.associated_with).."->"..tostring(n15.associated_with).."->"..tostring(n2.associated_with)) n1.next=n15 n2.prev=n15 n2=n15 @@ -1329,24 +1325,10 @@ function _stepfrom(x0,x1) end end -function _in_box(x,y,x0,y0,x1,y1) - x0,x1=_mnmx(x0,x1) - y0,y1=_mnmx(y0,y1) - return x0<=x and y0<=y and x<=x1 and y<=y1 -end - function _which_side(x,y,x0,y0,x1,y1) return sgn0((x1-x0)*(y-y0) - (y1-y0)*(x-x0)) end -function _linedist(x0,v,x1) - return 100 * (sum_distance(x0,v,x1)-distance(x0,x1))/distance(x0,x1) -end - -function sum_distance(x,y,z) - return distance(x,y) + distance(y,z) -end - function distance_dxy(dx,dy) return sqrt(dx*dx+dy*dy) end @@ -1361,10 +1343,10 @@ 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 + mx0+=0.4 + my0+=0.4 + mx1-=0.4 + my1-=0.4 while true do local n1=n0.next @@ -1401,18 +1383,6 @@ function _line_line(x1,y1,x2,y2,x3,y3,x4,y4) return true end -function neighbors(p) - local r={} - for dx=-1,1,1 do - for dy=-1,1,1 do - if dx!=0 or dy!=0 then - add(r,{x=p.x+dx,y=p.y+dy}) - end - end - end - return r -end - -->8 -- moved here because it's complicated -- 2.34.1 From 709be933322baec830170511da9d0c20f6b3f30b Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Tue, 20 Dec 2022 21:42:20 -0800 Subject: [PATCH 12/12] Tidy up more loose ends --- chameleonic.p8 | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/chameleonic.p8 b/chameleonic.p8 index af59265..68be32d 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -589,7 +589,7 @@ function level:get_latch(dx,dy,px,py) dx=dx1,dy=dy1, ax_offset=dx1*0.5, ay_offset=dy1*0.5, - mx=mx,my=my + rec={mx=mx,my=my,px=mx*8,py=my*8}, } end end @@ -778,7 +778,7 @@ function player:update() if self.rope then self.rope:update() - self.rope:drag_dst(self.x+0.5,self.y+0.1) + self.rope:drag_dst(self.x+self.px/8+0.5,self.y+self.py/8+0.1) local tdx,tdy=self.rope:tug_orientxy() if (tdx!=0) self.orientx=tdx @@ -922,10 +922,8 @@ function rope:update() self.latch.rec.my+0.5+self.latch.ay_offset ) - if #self.latch.rec.todo==0 then - if self.latch.rec.dead==true then - self:destroy() - end + if self.latch.rec.dead==true then + self:destroy() end end -- 2.34.1