16 Commits

Author SHA1 Message Date
e028209adf assert on unrecognized directive
saves four tokens and is more diagnostic.
2023-01-01 14:11:11 -08:00
ce3fc83221 fmt in debugmouse:draw3.
This is literally the reason I implemented fmt in the first place
2023-01-01 13:54:28 -08:00
3e2229be65 use fmt in a commented-out debug logging block 2023-01-01 13:54:28 -08:00
46f1339e19 golf recollide_reanchor
Uses fmt in recollide_reanchor. This has a performance impact in code that is already slow so this might need to be reverted; I think that should be part of a comprehensive optimization pass, however, and making it worse for now as part of a general code cleanup is probably better.
2023-01-01 13:54:28 -08:00
f86e52d3bd tostring golf
this could be golfed further by rewriting the str..= line in terms of fmt, but the performance impact of inserting the extra `fmt` calls and parsing into the tostring recursion seems likely to be a problem.
2023-01-01 13:54:28 -08:00
e6c35dbeda Use fmt on title screen. 2023-01-01 13:54:28 -08:00
3516d2855e fix section before first "%"
oops, need to special-case the first part of the split list.

amusingly, I don't need to special-case zero-length format strings because that will skip the entire loop and output "", which seems correct. (I am also not special-casing zero-length segments because `s[1] == nil` will go into the error handler and that seems fine.
2023-01-01 13:54:28 -08:00
2264349e72 fix double-consume, add %!
I discovered your tostring function for debugging, might as well make it reachable from fmt.  I also realized I forgot to convert to using "p" when I introduced it so I fixed that
2023-01-01 13:54:27 -08:00
93c154f876 fix sprintf and rename to fmt
the "split on %" strategy makes parsing "%%" complicated, so the "actually literally %" placeholder is now "%~" rather than "%%". since thsi resembles a real sprintf even less now I renamed it to "fmt".

also actually closes the `if m == "~"` block. (which was `if m == "%"` before this patch)
2023-01-01 13:54:27 -08:00
3494c48e74 implement fake sprintf
this spends extra tokens to handle "invalid format character" to catch using a % where %% is intended. this is designed to grow with further format chars if needed; I have ideas for not-exactly-POSIX %d, %x, %h, %!, %#, %c, and possibly others but there is absolutely no reason to spend tokens on these things until we need them. (that said, existing debugging output might benefit from some of these other formats, but that debug code is commented out, so maybe nevert.)
2023-01-01 13:54:27 -08:00
e878717c31 Better debug mouse
Debug mouse is now its own module, so it can be separated from the hint system, since it is useful for more than just positioning hints. It now has the following enhancements:

1. Clock-based table cyclng now has a helper function (cycle)
2. Debug mouse color cycling is distinct from hint color cycling, so debug position readout remains legible
3. Debug position readout now stays on screen even when the cursor is near or past the edges
4. Debug cursor cycles between a mouse sprite specifically marking the exact pixel that is being sampled, an "X" for text character sizing, and a "□" for positioning the centered 3x3 characters often used as hint target markers
5. Map cell coordinates (in square brackets) are displayed in addition to pixel coordnates (in parentheses)

Sprite 50 is now the mouse cursor. Color 15 is color cycling for debug readouts.

Debug mouse features can be disabled by commenting out `add(real_modules, debugmouse)`.

I've done a little bit of golfing but this is stiill a token expense. I'm going to write a crappy sprintf function to save tokens everywhere we're assembling strings from their component parts.
2023-01-01 13:44:45 -08:00
a6debc3974 hints through room 3; two line hints (#19)
most hints need to be 2 lines due to limited space.

Reviewed-on: pyrex/chameleonic#19
Co-authored-by: Kistaro Windrider <kistaro@gmail.com>
Co-committed-by: Kistaro Windrider <kistaro@gmail.com>
2023-01-01 21:30:28 +00:00
693cdaa11d Implement hint system and music mute. (#18)
Test hints now both display on level 0

Allows testing progressive display of more hints.

Code review feedback, debug mode

Reviewed-on: pyrex/chameleonic#18
Co-authored-by: Kistaro Windrider <kistaro@gmail.com>
Co-committed-by: Kistaro Windrider <kistaro@gmail.com>
2023-01-01 07:56:37 +00:00
f052186c97 Optimize raycast in sweep 2022-12-31 19:03:08 -08:00
0d0a2c41a7 Make all APIs point-oriented 2022-12-31 17:27:00 -08:00
71b150cb33 Golf the sweep code 2022-12-31 16:18:24 -08:00

View File

@ -8,6 +8,7 @@ real_modules={}
frame=0
function _init()
-- printh("restarting")
music_on()
_doall("init") end
function _update()
frame+=1
@ -15,6 +16,61 @@ function _update()
function _draw()
_doall("draw") end
function music_on()
music(0)
menuitem(3, "music: on", music_off)
end
function music_off()
music(-1)
menuitem(3, "music: off", music_on)
end
function gsv(s)
local ret=split(s,"\n")
for i,v in ipairs(ret) do
ret[i] = type(v) == "string" and split(v,"`") or {v} end
return ret
end
function cycle(tbl,period)
period = period or 1
return tbl[t()%period*#tbl\period+1]
end
-- fake sprintf function
-- %~ for literal "%"
-- %v for param
-- %! for tostring(param)
-- which dumps tables
function fmt(f, ...)
local out, i = "", 0
for s in all(split(f,"%")) do
if i == 0 then
-- before first format directive
out ..= s
i = 1
else
local m = s[1]
if m == "~" then
out ..= "%"
else
local p = select(i,...)
i+=1
if m == "v" then
out ..= p
elseif m == "!" then
out ..= tostring(p)
else
assert(false, tostr(m).."is not a formatting directive")
end
end
out ..=sub(s,2)
end
end
return out
end
mnames={}
function names(root)
local n=mnames[root]
@ -97,6 +153,15 @@ function sgn0(x)
return x!=0 and sgn(x) or 0
end
function inorder(tbl)
local prev
for v in all(tbl) do
if (prev and v < prev) return
prev = v
end
return true
end
function _mnmx(x,y)
if (x>y)return y,x
return x,y
@ -138,6 +203,41 @@ function _rast(
_add()
end
-->8
-- anchor operations (for the rope code)
function _anch_eq(a0,a1)
if (a0 and a1) return a0[1]==a1[1] and a0[2]==a1[2]
return a0==a1
end
--[[
function _anch_unpack(anch)
assert(anch[1])
assert(anch[2])
return anch[1],anch[2]
end
]]--
_anch_unpack=unpack
function _anch_new(prev,next,xy)
local out={prev=prev,next=next,_anch_unpack(xy)}
if (prev) prev.next=out
if (next) next.prev=out
return out
end
function _anch_del(n1,xy)
local n0,n2=n1.prev,n1.next
if (n0) n0.next=n2
if (n2) n2.prev=n0
end
function _anch_update(a0,a1)
a0[1]=a1[1]
a0[2]=a1[2]
end
-->8
-- input
@ -179,14 +279,12 @@ function kbd:release(i)
end
function tostring(any)
if type(any)=="table" then
local str = "{ "
for k,v in pairs(any) do
str=str..tostring(k).."->"..tostring(v).." "
end
return str.."}"
if (type(any)!="table") return tostr(any)
local str = "{ "
for k,v in pairs(any) do
str..=tostring(k).."->"..tostring(v).." "
end
return tostr(any)
return str.."}"
end
-->8
@ -202,7 +300,7 @@ function title:draw()
print("pyrex",32,73,7)
print("[nyeogmi]",62,73,7)
print("kistaro",32,79,7)
local lvlstr = "⬅️ "..start_level.." ➡️"
local lvlstr = fmt("⬅️ %v ➡️",start_level)
print(lvlstr,50,91,1)
print(lvlstr,51,90,blinkcol)
end
@ -231,6 +329,7 @@ function level:init()
end
function level:reinit(n)
self.hintlevel = 0
self.ix=n
self.todo={}
self.bigx,self.bigy=n%8,n\8
@ -371,20 +470,18 @@ function level:recollide_reanchor()
not self:mcoll(mx1,my0) and
not self:mcoll(mx1,my1)
) then
local key="GEOM"..mx0..","..my0..","..dx..","..dy
anch_new[key]= {
ax=max(mx0,mx1),ay=max(my0,my1),adx=-dx,ady=-dy
anch_new[fmt("GEOM%v,%v,%v,%v",mx0,my0,dx,dy)]= {
max(mx0,mx1),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
anch_new[fmt("CRATE%v,%v,%v",cr.id,dx,dy)]={
max(mx0,mx1),max(my0,my1),adx=-dx,ady=-dy
}
end
end
@ -394,17 +491,19 @@ function level:recollide_reanchor()
local old=(self._anch or {})[k]
if old then
anch_new[k]=new
if (old.ax!=new.ax or old.ay!=new.ay) add(moves,{old.ax,old.ay,new.ax,new.ay,old,key=k})
if (not _anch_eq(old,new)) add(moves,{old,new,key=k})
end
end
self._anch=anch_new
self._anch_keys={}
for k,_ in pairs(self._anch) do
self._anch_by_position={}
for k,v in pairs(self._anch) do
add(self._anch_keys,{key=k})
local pkey=_mix(_anch_unpack(v))
self._anch_by_position[pkey]=self._anch_by_position[pkey] or v
end
shellsort(self._anch_keys)
shellsort(moves)
--printh("!!STARTING!!")
if player.rope then
player.rope:experience_anchor_moves(moves)
@ -428,10 +527,8 @@ function level:anchor_points()
end
end
function level:anchor_at(ax,ay)
for i in self:anchor_points() do
if (i.ax==ax and i.ay==ay) return i
end
function level:anchor_at(point)
return self._anch_by_position[_mix(_anch_unpack(point))]
end
function level:get_open_pit(mx,my)
@ -485,10 +582,6 @@ function level:_mget(mx,my)
)
end
function _amix(ax,ay)
return ax..","..ay
end
function _mix(mx,my)
return mx..","..my
end
@ -672,8 +765,8 @@ function player:update()
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,
{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)
)
@ -700,10 +793,10 @@ function player:update()
self.y=latch.rec.my+latch.dy
end
self.rope:drag_dst(
self.rope:drag_dst{
self.x+self.px/8+0.5,
self.y+self.py/8+0.5
)
}
local tdx,tdy=self.rope:tug_orientxy()
if (tdx) self.orientx=tdx
@ -815,19 +908,15 @@ rope={}
rope.__index=rope
function rope:new(
src_ax,src_ay,dst_ax,dst_ay,latch
src,dst,latch
)
local r={
id=0,
anchors={
{ax=src_ax,ay=src_ay},
{ax=dst_ax,ay=dst_ay}
},
state={name="cast",frame=0},
latch=latch,
}
r.src=r.anchors[1]
r.dst=r.anchors[2]
r.src=src
r.dst=dst
r.src.next=r.dst
r.dst.prev=r.src
setmetatable(r,rope)
@ -855,10 +944,10 @@ function rope:update()
if (not self.latch) wrongbleep:bleep(5) self:destroy() return
if self.latch.rec then
self:drag_src(
self:drag_src{
self.latch.rec.mx+0.5+self.latch.ax_offset,
self.latch.rec.my+0.5+self.latch.ay_offset
)
}
if self.latch.rec.dead==true then
self:destroy()
@ -991,7 +1080,7 @@ function rope:draw(artificial_px,artificial_py)
local sy=0
while true do
if (n1==nil) break
local anch=level:anchor_at(n1.ax,n1.ay)
local anch=level:anchor_at(n1)
local x=n1.ax*8
local y=n1.ay*8
if anch then
@ -999,7 +1088,7 @@ function rope:draw(artificial_px,artificial_py)
if (anch.ady>0) y-=1
end
rectfill(x-1,y-1,x+1,y+1,12)
print("ax="..n1.ax..",ay="..n1.ay,72,sy)
print(fmt("ax=%v,ay=%v",n1.ax,n1.ay),72,sy)
sy+=7
local n0=n1.prev
@ -1032,18 +1121,18 @@ function rope:draw(artificial_px,artificial_py)
]]
end
function rope:drag_dst(x,y)
self:drag(self.dst,x,y)
function rope:drag_dst(xy)
self:drag(self.dst,xy)
end
function rope:drag_src(x,y)
self:drag(self.src,x,y)
function rope:drag_src(xy)
self:drag(self.src,xy)
end
function rope:drag(n1,ax_new,ay_new)
function rope:drag(n1,axy)
self:relax()
self:_drag(n1,ax_new,n1.ay)
self:_drag(n1,ax_new,ay_new)
self:_drag(n1,{axy[1],n1[2]})
self:_drag(n1,axy)
self:relax()
end
@ -1053,11 +1142,8 @@ function rope:relax()
local n1=n0.next
if (not n1) break
local n2=n1.next
if n0.ax==n1.ax and n0.ay==n1.ay then
n0.next=n2
if (n2) n2.prev=n0
if _anch_eq(n0,n1) then
_anch_del(n1)
else
n0=n0.next
end
@ -1070,20 +1156,11 @@ function rope:relax()
local n2=n1.next
if (not n2) return
local x0,y0=n0.ax,n0.ay
local x1,y1=n1.ax,n1.ay
local x2,y2=n2.ax,n2.ay
local anch=level:anchor_at(n1.ax,n1.ay)
local would,x1_new,y1_new=would_stick(anch,x0,y0,x1,y1,x2,y2)
if not would and not (n1.ax==x1_new and n1.ay==y1_new) then
self:_drag(n1,x1_new,y1_new,n1.ax,n1.ay)
n0=n1.prev
n2=n1.next
n0.next=n2
n2.prev=n0
n1.next=nil
n1.prev=nil
local anch=level:anchor_at(n1)
local wouldstick,position_new=would_stick(anch,n0,n1,n2)
if not (wouldstick or _anch_eq(n1,position_new)) then
self:_drag(n1,position_new,{_anch_unpack(n1)})
_anch_del(n1)
else n0=n0.next end
end
end
@ -1099,7 +1176,9 @@ function rope:_check_sane()
local n1=n0.next
if (not n1) break
_rast(qxs,qys,flr(n0.ax*2),flr(n0.ay*2),flr(n1.ax*2),flr(n1.ay*2))
local n0ax,n0ay=_anch_unpack(n0)
local n1ax,n1ay=_anch_unpack(n1)
_rast(qxs,qys,flr(n0ax*2),flr(n0ay*2),flr(n1ax*2),flr(n1ay*2))
n0=n1
end
@ -1143,7 +1222,6 @@ function rope:_check_sane()
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
@ -1156,12 +1234,12 @@ function rope:_check_sane()
return true
end
function would_stick(anchor,x0,y0,x1,y1,x2,y2)
x1,y1=x1 or anchor.ax,y1 or anchor.ay
function would_stick(anchor,xy0,xy1,xy2)
local x0,y0=_anch_unpack(xy0)
local x1,y1=_anch_unpack(xy1 or anchor)
local x2,y2=_anch_unpack(xy2)
local dx,dy=x2-x0,y2-y0
if (x1==x0 and y1==y0) return
if (x1==x2 and y1==y2) return
local function switch_ends()
dx,dy,x0,y0,x2,y2=-dx,-dy,x2,y2,x0,y0
@ -1185,7 +1263,7 @@ function would_stick(anchor,x0,y0,x1,y1,x2,y2)
return
anchor and anchor.adx==adx and anchor.ady==ady,
x1_new,y1_new
{x1_new,y1_new}
end
function rope:experience_anchor_moves(moves)
@ -1198,15 +1276,15 @@ function rope:_be_dragged_by(moves)
local n=self.src
while n do
for t in all(moves) do
local ax_old,ay_old,ax_new,ay_new=unpack(t)
if (ax_old==n.ax and ay_old==n.ay) n.dest={ax_new,ay_new} break
local xy_old,xy_new=unpack(t)
if (_anch_eq(xy_old,n)) n.dest=xy_new break
end
n=n.next
end
n=self.src
while n do
if (n.dest) self:_drag(n,unpack(n.dest)) n.dest=nil
if (n.dest) self:_drag(n,n.dest) n.dest=nil
n=n.next
end
end
@ -1217,14 +1295,18 @@ function rope:_be_pushed_by(moves)
end
end
function rope:_be_pushed_by1(ax_old,ay_old,ax_new,ay_new,anch)
function rope:_be_pushed_by1(anch_old,anch_new)
local n0=self.src
while true do
local ax_old,ay_old=_anch_unpack(anch_old)
local ax_new,ay_new=_anch_unpack(anch_new)
while n0 do
n1=n0.next
if (not n1) return
local nx0,ny0=n0.ax,n0.ay
local nx1,ny1=n1.ax,n1.ay
local nx0,ny0=_anch_unpack(n0)
local nx1,ny1=_anch_unpack(n1)
local nxmn,nxmx = _mnmx(nx0,nx1)
local nymn,nymx = _mnmx(ny0,ny1)
@ -1233,9 +1315,9 @@ function rope:_be_pushed_by1(ax_old,ay_old,ax_new,ay_new,anch)
(ax_new!=ax_old or (nxmn<ax_new and ax_new<nxmx)) and
(ay_new!=ay_old or (nymn<ay_new and ay_new<nymx)) and
(_which_side(ax_old,ay_old,nx0,ny0,nx1,ny1)!=
_which_side(ax_new,ay_new,nx0,ny0,nx1,ny1)
) and would_stick(anch,nx0,ny0,nil,nil,nx1,ny1)
(_which_side(anch_old,n0,n1)!=
_which_side(anch_new,n0,n1)
) and would_stick(anch_new,n0,nil,n1)
then
local nx05,ny05
if ax_new==ax_old then
@ -1246,101 +1328,84 @@ function rope:_be_pushed_by1(ax_old,ay_old,ax_new,ay_new,anch)
nx05=nx0+(ny05-ny0)/(ny1-ny0) * (nx1-nx0)
end
local n05={ax=nx05,ay=ny05,prev=n0,next=n1}
n0.next=n05
n1.prev=n05
-- printh("creating: "..tostring{anch,{n0.ax,n0.ay},{n05.ax,n05.ay},{n1.ax,n1.ay}})
-- printh("dragging: "..tostring{anch,{n05.ax,n05.ay},{ax_new,ay_new}})
self:_drag(n05,ax_new,ay_new)
local n05=_anch_new(n0,n1,{nx05,ny05})
self:_drag(n05,{ax_new,ay_new})
else
n0=n0.next
end
end
end
function rope:_drag(n1,ax1_new,ay1_new,ax_removing,ay_removing)
local function _sweep_radar(ax_lhs,ay_lhs,ax_rhs,ay_rhs,ax_pivot,ay_pivot,ax_far0,ay_far0,ax_far1,ay_far1)
function rope:_drag(n1,new,removing)
local function _sweep_radar(lhs,rhs,pivot,src,dst)
if (_anch_eq(src,dst)) return
local function _uncreatable(anchor)
return (anchor.ax==ax_lhs and anchor.ay==ay_lhs) or
(anchor.ax==ax_rhs and anchor.ay==ay_rhs) or
(anchor.ax==ax_removing and anchor.ay==ay_removing)
return _anch_eq(anchor,lhs) or _anch_eq(anchor,lhs) or _anch_eq(anchor,removing)
end
if (ax_far0==ax_far1 and ay_far0==ay_far1) return
local function _sweep(extent_orig,extent_final,cb_in_bounds,cb_make_point)
local eligible={}
local point_orig=cb_make_point(extent_orig)
local point_final=cb_make_point(extent_final)
if ax_far0==ax_far1 then
local ax_far=ax_far0
local ax0,ax1=_mnmx(ax_pivot,ax_far)
for anchor in level:anchor_points() do
-- figure out which anchors we even can stick to
if cb_in_bounds(anchor) and not _uncreatable(anchor) then
local side_orig=_which_side(anchor,pivot,point_orig)
local side_final=_which_side(anchor,pivot,point_final)
ay_far_old=ay_far0
for ay_far_new in _stepfrom(ay_far0,ay_far1) do
for anchor in level:anchor_points() do
if
not _uncreatable(anchor) and
(ax0<=anchor.ax and anchor.ax<=ax1) and
would_stick(anchor,ax_pivot,ay_pivot,nil,nil,ax_far,ay_far_new) 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
if (side_orig!=side_final and would_stick(anchor,pivot,nil,point_final)) add(eligible,{anchor,side_final})
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
not _uncreatable(anchor) and
(ay0<=anchor.ay and anchor.ay<=ay1) and
would_stick(anchor,ax_pivot,ay_pivot,nil,nil,ax_far_new,ay_far) 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
-- if no one's in the race, don't raycast
if (#eligible==0) return
if (#eligible==1) return eligible[1][1]
-- see who wins the race
for extent in _stepfrom(extent_orig,extent_final) do
local point = cb_make_point(extent)
for anchor_final in all(eligible) do
if (_which_side(anchor_final[1],pivot,point)==anchor_final[2]) return anchor_final[1]
end
ax_far_old=ax_far_new
end
end
local ax_pivot,ay_pivot=_anch_unpack(pivot)
local ax_src,ay_src=_anch_unpack(src)
local ax_dst,ay_dst=_anch_unpack(dst)
if ax_src==ax_dst then
return _sweep(
ay_src,ay_dst,
function(anchor) return mid(ax_pivot,anchor[1],ax_dst)==anchor[1] end,
function(ay) return {ax_dst,ay} end
)
else
return _sweep(
ax_src,ax_dst,
function(anchor) return mid(ay_pivot,anchor[2],ay_dst)==anchor[2] end,
function(ax) return {ax,ay_dst} end
)
end
end
local ax1_old,ay1_old=n1.ax,n1.ay
n1.ax=ax1_new
n1.ay=ay1_new
local old={_anch_unpack(n1)}
_anch_update(n1,new)
local n0=n1.prev
while n0 do
local anch=_sweep_radar(
n0.ax,n0.ay,n1.ax,n1.ay,
n0.ax,n0.ay,ax1_old,ay1_old,ax1_new,ay1_new
)
local anch=_sweep_radar(n0,n1,n0,old,new)
if (not anch) break
local n05={ax=anch.ax,ay=anch.ay,prev=n0,next=n1}
n0.next=n05
n1.prev=n05
n0=n05
n0=_anch_new(n0,n1,anch)
end
local n2=n1.next
while n2 do
local anch=_sweep_radar(
n1.ax,n1.ay,n2.ax,n2.ay,
n2.ax,n2.ay,ax1_old,ay1_old,ax1_new,ay1_new
)
local anch=_sweep_radar(n1,n2,n2,old,new)
if (not anch) break
local n15={ax=anch.ax,ay=anch.ay,prev=n1,next=n2}
n1.next=n15
n2.prev=n15
n2=n15
n2=_anch_new(n1,n2,anch)
end
end
@ -1353,9 +1418,7 @@ function _stepfrom(x0,x1)
end
end
local mul=0.5
x0*=2
x1*=2
local mul=1
if (x0>x1) x0,x1,mul=-x0,-x1,-mul
local i=flr(x0)
local top=flr(x1)
@ -1371,7 +1434,11 @@ function _stepfrom(x0,x1)
end
end
function _which_side(x,y,x0,y0,x1,y1)
function _which_side(xy,x0y0,x1y1)
local x,y=_anch_unpack(xy)
local x0,y0=_anch_unpack(x0y0)
local x1,y1=_anch_unpack(x1y1)
return sgn0((x1-x0)*(y-y0) - (y1-y0)*(x-x0))
end
@ -1399,7 +1466,8 @@ function rope:collide_mrect(mx0,my0,mw,mh,exclude_src,exclude_dst)
if (not nd) return
end
local x1,y1,x2,y2=n0.ax,n0.ay,n1.ax,n1.ay
local x1,y1=_anch_unpack(n0)
local x2,y2=_anch_unpack(n1)
local function _line_line(x3,y3,x4,y4)
local denom=(y4-y3)*(x2-x1)-(x4-x3)*(y2-y1)
@ -1429,12 +1497,15 @@ end
function rope:tug_orientxy()
local a1=self.dst
local a0=self.dst.prev
local dx=a0.ax-a1.ax
local ax0,ay0=_anch_unpack(a0)
local ax1,ay1=_anch_unpack(a1)
local dx=ax0-ax1
local tdx
if (dx>3/8) tdx=1
if (dx<-3/8) tdx=-1
local dy=a0.ay-a1.ay
local dy=ay0-ay1
local tdy
if abs(dy)>abs(dx)/2 then
if (dy>3/8) tdy=1
@ -1619,11 +1690,12 @@ function rope:_anchors_simplified()
end
a=self.src
while a do
local ax,ay=_anch_unpack(a)
local point={
x=flr(a.ax*8+0.5),y=flr(a.ay*8+0.5),
ax=a.ax,ay=a.ay
ax,ay,
x=flr(ax*8+0.5),y=flr(ay*8+0.5),
}
local aw=level:anchor_at(a.ax,a.ay)
local aw=level:anchor_at(a)
local l=self.latch
if aw then
if (aw.adx==1) point.x-=1
@ -1694,6 +1766,107 @@ function level_text:draw()
print(xys[4],xys[2],xys[3],6)
end
end
-->8
--hint system
function rot13(s)
local sord = pack(ord(s,1,#s))
for i,c in ipairs(sord) do
if (inorder{65, c, 77} or inorder{97, c, 109}) sord[i]=c+13
if (inorder{78, c, 90} or inorder{110, c, 122}) sord[i]=c-13
end
return chr(unpack(sord))
end
--hint file format:
-- each row is one hint.
-- 4 or 5 columns
-- separated with a
-- grave (`) character
-- [1] room#
-- [2] x coord
-- [3] y coord
-- [4] message line 1 (rot13)
-- [5] message llne 2 (rot13)
-- row 5 can be omitted
-- for a 1-line hint
--
-- multiple hints for the same
-- room are revealed in order
hints = {}
add(real_modules,hints)
function hints:init()
local h = gsv[[0`42`57`🅾️ yVPX` ■
0`42`73`❎, ❎ cHYY
1`35`34`⁘ sVYY
1`99`82`■ cHYY
1`42`98`⁘`VTABER
2`75`65`i <`⁘
2`104`73` ■`cHYY
2`27`42`⁘
3`51`106`■ cHYY
3`27`81`⁘ HAOYBPX ZR
3`91`33`■ FGNAQ` URER]]
for rec in all(h) do
rec[4]=rot13(rec[4])
if(rec[5]) rec[5]=rot13(rec[5])
local lh = self[rec[1]]
if lh then
add(lh,rec)
else
self[rec[1]] = {rec}
end
end
menuitem(1,"get hint",function() level.hintlevel+=1 end)
menuitem(2,"hide hints",function() level.hintlevel=0 end)
end
function shdprint(txt,x,y,c)
print(txt,x-1,y+1,1)
print(txt,x,y,c)
end
hintflicker=split"7,10,9,8,8,9,10,7"
function hints:draw2()
pal()
local c=cycle(hintflicker)
for i,h in ipairs(self[level.ix]) do
if (i > level.hintlevel) return
local _,x,y,txt,txt2=unpack(h)
shdprint(txt,x,y,c)
if (txt2) shdprint(txt2,x,y+8,c)
end
end
-->8
-- debug mouse support
debugmouse = {}
-- comment this out to disable debug mode
add(real_modules, debugmouse)
function debugmouse:init()
poke(0x5f2d,1)
end
debugflicker=split"5,6,7,15,14,8,2,4,9,10,11,3,12,13"
debugchs = split" ,x, ,□"
function debugmouse:draw3()
if (stat(34) == 0) return
pal(15,cycle(debugflicker,1.5))
local x, y, c = stat(32), stat(33), cycle(debugchs,2)
if (c == " ") spr(50,x,y)
print(c,x,y,15)
local px, py = mid(0,x,89), mid(0, y > 111 and y - 12 or y + 6, 117)
print(fmt("(%v, %v)\n[%v, %v]",x,y,x\8,y\8),px,py,15)
pal()
end
__gfx__
000030000000002200003000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeff1ff1ff1fffffff1ffffff1fffffff1dddddddd111111110005000000000000
003333300000332200333330eeffffffffffffffffffffeee5e555e55e555e5eff1ff1ffffffffffffffffffffffffffdddddddd111111110000500000000000
@ -1719,10 +1892,10 @@ eeee0000cc04405500444400efeeee5e11111111e5eeeefeeeeeeeeeeeeeeeeeffffffffffffffff
0a000aa4441a91a1bbabbbbbeffeeeeeeeeeeeeeeeeeeffeff1ff1ff11111111ff1111ff00000000000000000000000000000000000000000000000000000000
00a0044449a110a1bbbbbbbbeeffffffffffffffffffffeeff1ff1ffffffffffffffffff00000000000000000000000000000000000000000000000000000000
000aa111991111103bbbbbb3eeeeeeeeeeeeeeeeeeeeeeeeff1ff1ffffffffffffffffff00000000000000000000000000000000000000000000000000000000
00000000991000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111111111111111999999111111111
00000000990000000000000000000000000000000000000000000000000000000000000000000000000000000000000019911991999999911999999119999999
00000000990000000000000000000000000000000000000000000000000000000000000000000000000000000000000019977991999999911999999119999999
00000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000019911991999117111991199111711999
0000000099100000f765000000000000000000000000000000000000000000000000000000000000000000000000000011111111111111111999999111111111
00000000990000007700000000000000000000000000000000000000000000000000000000000000000000000000000019911991999999911999999119999999
00000000990000006060000000000000000000000000000000000000000000000000000000000000000000000000000019977991999999911999999119999999
00000000090000005005000000000000000000000000000000000000000000000000000000000000000000000000000019911991999117111991199111711999
00000000aa0000000000000000000000000000000000000000000000000000000000000000000000000000000000000019911991999117111991199111711999
0000000077a000000000000000000000000000000000000000000000000000000000000000000000000000000000000019999991999999911997799119999999
00000007777a00000000000000000000000000000000000000000000000000000000000000000000000000000000000019999991999999911991199119999999