19 Commits

Author SHA1 Message Date
137a390b65 Golf tostring() 2022-12-22 18:51:05 -08:00
58bf1d70bf Golf keyboard 2022-12-22 18:49:03 -08:00
1be4846698 Golf _rast 2022-12-22 18:42:44 -08:00
ed25ef0f94 Golf sgn0 2022-12-22 18:35:24 -08:00
94e4aea20b Golf _apply 2022-12-22 18:33:48 -08:00
2d565873b3 Golf linefill slightly more 2022-12-22 18:32:14 -08:00
f3a0b04cc4 Golf linefill 2022-12-22 18:30:05 -08:00
d782f6eb14 Fix another rope bug 2022-12-22 11:25:31 -08:00
791b49934f Add another case I missed 2022-12-21 21:55:21 -08:00
197c68dd88 Actually, that special case causes unnecessary rope shortening 2022-12-21 21:40:16 -08:00
4072499c4c This case shouldn't need to be special 2022-12-21 21:37:26 -08:00
e2334f55f7 Allow reeling in 2022-12-21 19:54:09 -08:00
6658f71ba2 Correct when the rope crashes into the other side of something 2022-12-21 19:12:21 -08:00
672f1b3951 Hint text for certain levels 2022-12-21 18:11:19 -08:00
a27cc2e6ef Re-center rope 2022-12-21 17:50:06 -08:00
0606c0417f New crate sprites 2022-12-20 23:22:27 -08:00
838214c5c5 Prototype tutorial (#12)
Prototype tutorial starting at level 10 using existing mechanics.

Reviewed-on: #12
Co-authored-by: Kistaro Windrider <kistaro@gmail.com>
Co-committed-by: Kistaro Windrider <kistaro@gmail.com>
2022-12-21 07:22:11 +00:00
6be3fd51b0 Player can only walk into pit if they want to 2022-12-20 21:57:22 -08:00
d2ee3d2078 Rewrite rope (#11)
Rope rewrite, part one

We should only care about anchors on the old path

Add comment expressing uncertainty

Add further algo notes

One final note

Well, this is closer to right!!

Elide points as needed

Save current changes

First version I couldn't immediately break

Everything _seems_ to work

Clean up some residual messes

Tidy up more loose ends

Co-authored-by: Nyeogmi <economicsbat@gmail.com>
Reviewed-on: #11
2022-12-21 05:44:15 +00:00

View File

@ -53,21 +53,18 @@ function linefill(ax,ay,bx,by,r,c)
local dx,dy=bx-ax,by-ay local dx,dy=bx-ax,by-ay
-- avoid overflow -- avoid overflow
-- credits: https://www.lexaloffle.com/bbs/?tid=28999 -- credits: https://www.lexaloffle.com/bbs/?tid=28999
local d=max(abs(dx),abs(dy)) local n,d=_mnmx(abs(dx),abs(dy))
local n=min(abs(dx),abs(dy))/d n/=d
d*=sqrt(n*n+1) d*=sqrt(n*n+1)
if(d<0.001) return if(d<0.001) return
local ca,sa=dx/d,-dy/d local ca,sa=dx/d,-dy/d
-- polygon points -- polygon points
local s={ local spans={}
{0,-r},{d,-r},{d,r},{0,r} local function calcxy(u,v) return ax+u*ca+v*sa,ay-u*sa+v*ca end
} local x0,y0=calcxy(0,r)
local u,v,spans=s[4][1],s[4][2],{} for s in all{{0,-r},{d,-r},{d,r},{0,r}} do
local x0,y0=ax+u*ca+v*sa,ay-u*sa+v*ca local x1,y1=calcxy(unpack(s))
for i=1,4 do
local u,v=s[i][1],s[i][2]
local x1,y1=ax+u*ca+v*sa,ay-u*sa+v*ca
local _x1,_y1=x1,y1 local _x1,_y1=x1,y1
if(y0>y1) x0,y0,x1,y1=x1,y1,x0,y0 if(y0>y1) x0,y0,x1,y1=x1,y1,x0,y0
local dx=(x1-x0)/(y1-y0) local dx=(x1-x0)/(y1-y0)
@ -78,11 +75,8 @@ function linefill(ax,ay,bx,by,r,c)
for y=y0\1+1,min(y1\1,127) do for y=y0\1+1,min(y1\1,127) do
-- open span? -- open span?
local span=spans[y] local span=spans[y]
if span then if (span) rectfill(x0,y,span,y)
rectfill(x0,y,span,y) spans[y]=x0
else
spans[y]=x0
end
x0+=dx x0+=dx
end end
x0,y0=_x1,_y1 x0,y0=_x1,_y1
@ -92,19 +86,14 @@ end
function _apply(x,ts,a) function _apply(x,ts,a)
local t=deli(ts,1) local t=deli(ts,1)
for k,v in pairs(t) do for k,v in pairs(t) do
if k=="update" then if (k!="update") x[k]=v
--
else
x[k]=v
end
end end
if (t and t.update and not t.update(x,a)) add(ts,t,1) if (t and t.update and not t.update(x,a)) add(ts,t,1)
end end
function sgn0(x) function sgn0(x)
if (x==0) return x return x!=0 and sgn(x) or 0
return sgn(x)
end end
function _mnmx(x,y) function _mnmx(x,y)
@ -114,13 +103,14 @@ end
function _rast( function _rast(
x0,y0,x1,y1 xs,ys,x0,y0,x1,y1
) )
local dx=abs(x1-x0) local function _add()
local dy=abs(y1-y0) local n=#xs
local x=x0 if (n==0 or xs[n]!=x0 or ys[n]!=y0) add(xs,x0) add(ys,y0)
local y=y0 end
local dx,dy=abs(x1-x0),abs(y1-y0)
local sx=-1 local sx=-1
local sy=-1 local sy=-1
if (x0<x1) sx=1 if (x0<x1) sx=1
@ -129,27 +119,22 @@ function _rast(
local done=false,err local done=false,err
if dx>dy then if dx>dy then
err=dx/2.0 err=dx/2.0
return function() while x0!=x1 do
if (done) return _add()
if (x==x1) done=true return x1,y1
local oldx,oldy=x,y
err-=dy err-=dy
if (err<0) y+=sy err+=dx if (err<0) y0+=sy err+=dx
x+=sx x0+=sx
return oldx,oldy
end end
else else
err=dy/2.0 err=dy/2.0
return function() while y0!=y1 do
if (done) return _add()
if (y==y1) done=true return x1,y1
local oldx,oldy=x,y
err-=dx err-=dx
if (err<0) x+=sx err+=dy if (err<0) x0+=sx err+=dy
y+=sy y0+=sy
return oldx,oldy
end end
end end
_add()
end end
-->8 -->8
@ -158,74 +143,48 @@ kbd={}
add(real_modules,kbd) add(real_modules,kbd)
function kbd:init() function kbd:init()
self.real=btn() self.real=btn()
self.down=0 self.state={btn=0}
end end
function kbd:update() function kbd:update()
-- figure out what keys are _really_ pressed -- figure out what keys are _really_ pressed
local now_real=btn() local now_real,was_real=btn(),self.real
local was_real=self.real
self.real=now_real self.real=now_real
-- add keys that are really pressed -- add keys that are really pressed
-- if they weren't really pressed before -- if they weren't really pressed before
-- (they may have been force- -- (they may have been force-
-- released by :release()) -- released by :release())
local real_pressed=(~was_real)&now_real local real_pressed=~was_real&now_real
local now_down=(self.down&now_real)|real_pressed local state=self.state
local was_down=self.down local now_down=state.btn&now_real|real_pressed
self.down=now_down local was_down=state.btn
-- deduce pressed/released by changes in down -- deduce pressed/released by changes in down
local pressed=(~was_down)&now_down state.btn,state.btnp,state.btnr=
local released=(~now_down)&was_down now_down,
~was_down&now_down,
self.pressed=pressed ~now_down&was_down
self.released=released
end end
function kbd:btn(i) for _kbdi in all(split"btn,btnp,btnr") do
return self.down&(1<<i)!=0 kbd[_kbdi]=function(self,i,t) return 1<<i&self.state[_kbdi]!=0 end
end
function kbd:btnp(i)
return self.pressed&(1<<i)!=0
end
function kbd:btnr(i)
return self.released&(1<<i)!=0
end end
function kbd:release(i) function kbd:release(i)
self.down&=~(1<<i) self.state.btn&=~(1<<i)
end end
function tostring(any) 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 if type(any)=="table" then
local str = "{ " local str = "{ "
for k,v in pairs(any) do for k,v in pairs(any) do
str=str..tostring(k).."->"..tostring(v).." " str=str..tostring(k).."->"..tostring(v).." "
end end
return str.."}" return str.."}"
end end
if type(any)=="number" then return tostr(any)
return ""..any
end
return "unknown" -- should never show
end end
-->8 -->8
@ -305,7 +264,7 @@ function level:advance()
self:reinit(self.ix+1) self:reinit(self.ix+1)
end end
pitpal = {[0]=1,[7]=0} pitpal = {[0]=1,[7]=0,[6]=1,[4]=1}
function level:draw() function level:draw()
cls(1) cls(1)
pal(1,0) pal(1,0)
@ -450,7 +409,7 @@ function level:reanchor()
local old=anch_old[k] local old=anch_old[k]
if old then if old then
anch_new[k]=old anch_new[k]=old
old.ax,old.ay,old.adx,old.ady=new.ax,new.ay,new.adx,new.ady old.ax_old,old.ay_old,old.ax,old.ay,old.adx,old.ady=old.ax,old.ay,new.ax,new.ay,new.adx,new.ady
old.dropped=nil old.dropped=nil
end end
end end
@ -459,6 +418,17 @@ function level:reanchor()
for k,_ in pairs(self._anch) do for k,_ in pairs(self._anch) do
add(self._anch_keys,{key=k}) add(self._anch_keys,{key=k})
end end
shellsort(self._anch_keys)
for point in self:anchor_points() do
if point.ax_old!=nil then
if (player.rope!=nil) player.rope:be_pushed_by(point,point.ax_old,point.ay_old)
point.ax_old=nil
point.ay_old=nil
end
end
for point in self:anchor_points() do
assert(not point.dropped)
end
if (player.rope!=nil) player.rope:relax() if (player.rope!=nil) player.rope:relax()
end end
@ -601,6 +571,9 @@ function level:can_move(
if is_player and self:win_at(mx0+dmx,my0+dmy) then if is_player and self:win_at(mx0+dmx,my0+dmy) then
return true return true
end end
if is_player and self:get_open_pit(mx0+dmx,my0+dmy) then
return wrongbleep:adequately_warned()
end
if self:mcoll(mx0+dmx,my0+dmy) then if self:mcoll(mx0+dmx,my0+dmy) then
return false return false
@ -614,13 +587,13 @@ function level:can_move(
if player.rope then if player.rope then
local chk=false local chk=false
if dmx==0 and dmy==-1 then if dmx==0 and dmy==-1 then
chk=player.rope:collide_mrect(mx0,my0-1,1,2,exclude_src,exclude_dst) chk=player.rope:collide_mrect(mx0+0.4,my0-0.8,0.2,1.6,exclude_src,exclude_dst)
elseif dmx==0 and dmy==1 then elseif dmx==0 and dmy==1 then
chk=player.rope:collide_mrect(mx0,my0,1,2,exclude_src,exclude_dst) chk=player.rope:collide_mrect(mx0+0.4,my0+0.2,0.2,1.6,exclude_src,exclude_dst)
elseif dmx==-1 and dmy==0 then elseif dmx==-1 and dmy==0 then
chk=player.rope:collide_mrect(mx0-1,my0,2,1,exclude_src,exclude_dst) chk=player.rope:collide_mrect(mx0-0.8,my0+0.4,1.6,0.2,exclude_src,exclude_dst)
elseif dmx==1 and dmy==0 then elseif dmx==1 and dmy==0 then
chk=player.rope:collide_mrect(mx0,my0,2,1,exclude_src,exclude_dst) chk=player.rope:collide_mrect(mx0+0.2,my0+0.4,1.6,0.2,exclude_src,exclude_dst)
end end
if (chk) return false if (chk) return false
@ -757,7 +730,7 @@ function player:update()
self.rope=rope:new( self.rope=rope:new(
x+0.5-dx*0.5,y+0.5-dy*0.5, x+0.5-dx*0.5,y+0.5-dy*0.5,
self.x+0.5,self.y+0.1, self.x+0.5,self.y+0.5,
level:get_latch(dx,dy,x*8,y*8) level:get_latch(dx,dy,x*8,y*8)
) )
@ -768,17 +741,28 @@ function player:update()
}} }}
elseif kbd:btnp(5) then elseif kbd:btnp(5) then
if self.rope!=nil then if self.rope!=nil then
if (not self.rope:tug()) wrongbleep:bleep(7) if (not self.rope:tug()) wrongbleep:bleep(9)
end end
end end
end end
end end
if self.rope then
self.rope:update()
end
_apply(self,self.todo) _apply(self,self.todo)
if self.rope then if self.rope then
self.rope:update() self.rope:update()
self.rope:drag_dst(self.x+self.px/8+0.5,self.y+self.py/8+0.1) if self.rope:done_reeling() then
self.x=self.rope.latch.rec.mx+self.rope.latch.dx
self.y=self.rope.latch.rec.my+self.rope.latch.dy
end
local rx=self.x+self.px/8+0.5
local ry=self.y+self.py/8+0.5
-- do the hokey pokey to work out kinks in the rope
self.rope:drag_dst(rx,ry)
local tdx,tdy=self.rope:tug_orientxy() local tdx,tdy=self.rope:tug_orientxy()
if (tdx!=0) self.orientx=tdx if (tdx!=0) self.orientx=tdx
@ -812,8 +796,9 @@ function player:_fall()
end end
function player:draw() function player:draw()
local px=self.x*8+self.px local px=self.x*8+self.px+wrongbleep:vibrate()
local py=self.y*8+self.py local py=self.y*8+self.py+wrongbleep:vibrate()
local head=1-self.orienty local head=1-self.orienty
@ -856,17 +841,29 @@ function player:draw()
end end
end end
local rx,ry=self.x*8+self.px+1,self.y*8+self.py+2
if (self.orientx==1) rx+=6
if self.rope then
local rx_adj,ry_adj=self.rope:affected_src_xy(rx,ry)
if rx_adj!=nil then
local drx,dry=rx_adj-rx,ry_adj-ry
rx,ry=rx+drx,ry+dry
px,py=px+drx,py+dry
end
end
if self.orientx==-1 then if self.orientx==-1 then
setpal() setpal()
spr(16,px+6,py-2,1,1) spr(16,px+6,py-2,1,1)
spr(17,px+1,py,1,1) spr(17,px+1,py,1,1)
if (self.rope and invis_level<=0.25) pal() self.rope:draw(-2,-1) setpal() if (self.rope and invis_level<=0.25) pal() self.rope:draw(self.x*8+self.px+1,self.y*8+self.py+2) setpal()
spr(head,px-3,py-3,1,1) spr(head,px-3,py-3,1,1)
else else
setpal() setpal()
spr(16,px-6,py-2,1,1,true) spr(16,px-6,py-2,1,1,true)
spr(17,px-1,py,1,1,true) spr(17,px-1,py,1,1,true)
if (self.rope and invis_level<=0.25) pal() self.rope:draw(2,-1) setpal() if (self.rope and invis_level<=0.25) pal() self.rope:draw(self.x*8+self.px+7,self.y*8+self.py+2) setpal()
spr(head,px+3,py-3,1,1,true) spr(head,px+3,py-3,1,1,true)
end end
pal() pal()
@ -905,13 +902,17 @@ function rope:done()
return self.state.name=="done" return self.state.name=="done"
end end
function rope:done_reeling()
return self.state.name=="done" and self.state.reelin
end
function rope:update() function rope:update()
if self.state.name=="cast" then if self.state.name=="cast" then
self.state.frame+=1 self.state.frame+=1
if (self.state.frame>=3) self.state={name="latched"} if (self.state.frame>=3) self.state={name="latched"}
elseif self.state.name=="latched" then elseif self.state.name=="latched" then
if (self.latch==nil) wrongbleep:bleep(3) self:destroy() return if (self.latch==nil) wrongbleep:bleep(5) self:destroy() return
if if
self.latch!=nil and self.latch!=nil and
@ -931,34 +932,31 @@ function rope:update()
elseif self.state.name=="destroy" then -- destroy elseif self.state.name=="destroy" then -- destroy
self.state.frame+=1 self.state.frame+=1
if (self.state.frame>=5) self.state={name="done"} if (self.state.frame>=5) self.state={name="done",reelin=self.state.reelin}
else else
-- done state -- done state
end end
end end
function rope:destroy() function rope:destroy(reelin)
if (self.state.name=="destroy" or self.state.name=="done") return if (self.state.name=="destroy" or self.state.name=="done") return
self.state={name="destroy",frame=0} self.state={name="destroy",frame=0,reelin=reelin}
end end
function rope:draw(artificial_dx,artificial_dy) function rope:affected_src_xy(artificial_px,artificial_py)
local points,highlight=self:_tug(true) -- this is the loop from :draw but simplified
if (self.state.name=="done") return if (not self.state.reelin) 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} perc_to_show=(1.0-self.state.frame/8)^2
local points=self:_anchors_simplified()
points[#points]={x=artificial_px,y=artificial_py}
local len=0 local len=0
for i=1,#points-1 do for i=1,#points-1 do len+=distance(points[i],points[i+1]) end
len+=distance(points[i],points[i+1])
end
local len_to_show=perc_to_show*len local len_to_show=perc_to_show*len
local len_cumulative=0 local len_cumulative=0
for i=#points-1,1,-1 do for i=1,#points-1 do
local src=points[i] local src=points[i]
local dst=points[i+1] local dst=points[i+1]
@ -971,7 +969,54 @@ function rope:draw(artificial_dx,artificial_dy)
if len_here>0 and dist_base>0 then if len_here>0 and dist_base>0 then
local coef=min(len_here/dist_base,1.0) local coef=min(len_here/dist_base,1.0)
dx,dy=dx*coef,dy*coef
return x+dx-dx*coef,y+dy-dy*coef
end
end
return points[1]
end
function rope:draw(artificial_px,artificial_py)
local points,highlight=self:_tug(true)
if (self.state.name=="done") return
local perc_to_show=1.0
local from_end=false
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/8)^2
if (self.state.reelin) from_end=true
points[#points]={x=artificial_px,y=artificial_py}
local len=0
for i=1,#points-1 do
len+=distance(points[i],points[i+1])
end
local len_to_show=perc_to_show*len
local len_cumulative=0
local ia,iz,istep=#points-1,1,-1
if (from_end) ia,iz,istep=1,#points-1,1
for i=ia,iz,istep do
local src=points[i]
local dst=points[i+1]
local x,y=dst.x,dst.y
local dx,dy=src.x-x,src.y-y
local len_here=len_to_show-len_cumulative
local dist_base=distance_dxy(dx,dy)
len_cumulative+=dist_base
if len_here>0 and dist_base>0 then
local coef=min(len_here/dist_base,1.0)
if from_end then
x,y=x+dx-dx*coef,y+dy-dy*coef
dx,dy=dx*coef,dy*coef
else
dx,dy=dx*coef,dy*coef
end
local color=8 local color=8
if (highlight==i) color=12 if (highlight==i) color=12
@ -984,7 +1029,7 @@ function rope:draw(artificial_dx,artificial_dy)
end end
-- draw latch -- draw latch
if self.latch!=nil and self.latch.rec and perc_to_show>=1.0 then if self.latch!=nil and self.latch.rec and (perc_to_show>=1.0 or from_end) then
local x,y=self.latch.rec.px,self.latch.rec.py local x,y=self.latch.rec.px,self.latch.rec.py
local ldx,ldy=self.latch.dx,self.latch.dy local ldx,ldy=self.latch.dx,self.latch.dy
local color=8 local color=8
@ -1001,7 +1046,6 @@ function rope:draw(artificial_dx,artificial_dy)
end end
-- debug -- debug
--[[
local n1=self.src local n1=self.src
local sy=0 local sy=0
while true do while true do
@ -1026,7 +1070,7 @@ function rope:draw(artificial_dx,artificial_dy)
--assert(not (adx==0 and ady==0)) --assert(not (adx==0 and ady==0))
local c=3 local c=3
if (n1.associated_with.dropped) c=8 if (n1.associated_with.dropped) c=4
rectfill(x+2,y+2,x+4,y+4,c) rectfill(x+2,y+2,x+4,y+4,c)
pset(x+adx*2,y,9) pset(x+adx*2,y,9)
pset(x,y+ady*2,9) pset(x,y+ady*2,9)
@ -1048,7 +1092,6 @@ function rope:draw(artificial_dx,artificial_dy)
pset(x+p.adx,y,11) pset(x+p.adx,y,11)
pset(x,y+p.ady,11) pset(x,y+p.ady,11)
end end
]]
end end
function rope:drag_dst(x,y) function rope:drag_dst(x,y)
@ -1067,16 +1110,18 @@ function rope:drag(n1,ax_new,ay_new)
end end
function rope:relax() function rope:relax()
local n=self.src local n0=self.src
while true do while true do
if (n==nil) break local n1=n0.next
if (n1==nil) break
local n2=n1.next
if (n.associated_with) then if n0.ax==n1.ax and n0.ay==n1.ay then
self:_drag(n,n.associated_with.ax,n.associated_with.ay) n0.next=n2
if (n2!=nil) n2.prev=n0
else
n0=n0.next
end end
n=n.next
end end
local n0=self.src local n0=self.src
@ -1093,9 +1138,14 @@ function rope:relax()
local x1,y1=n1.ax,n1.ay local x1,y1=n1.ax,n1.ay
local x2,y2=n2.ax,n2.ay local x2,y2=n2.ax,n2.ay
if x1!=n1.associated_with.ax or y1!=n1.associated_with.ay then
-- printh("dragging home: "..tostring{n1.ax,n1.ay}.."->"..tostring(n1.associated_with))
self:_drag(n1,n1.associated_with.ax,n1.associated_with.ay)
end
local would,x1_new,y1_new=would_stick(x0,y0,n1.associated_with,x2,y2) 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 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) self:_drag(n1,x1_new,y1_new)
n0=n1.prev n0=n1.prev
n2=n1.next n2=n1.next
@ -1109,7 +1159,7 @@ function rope:relax()
end end
function rope:_check_sane() function rope:_check_sane()
if (self.state.name!="latched") return true if (not self:latched()) return true
if (level:busy()) return true if (level:busy()) return true
local n0=self.src local n0=self.src
@ -1119,12 +1169,7 @@ function rope:_check_sane()
local n1=n0.next local n1=n0.next
if (n1==nil) break 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 _rast(qxs,qys,flr(n0.ax*2),flr(n0.ay*2),flr(n1.ax*2),flr(n1.ay*2))
if not (qx==qxs[#qxs] and qy==qys[#qys]) then
add(qxs,qx)
add(qys,qy)
end
end
n0=n1 n0=n1
end end
@ -1183,14 +1228,22 @@ end
function would_stick(x0,y0,anchor,x2,y2) function would_stick(x0,y0,anchor,x2,y2)
local x1,y1=anchor.ax,anchor.ay local x1,y1=anchor.ax,anchor.ay
if (x0>x2) x0,y0,x2,y2=x2,y2,x0,y0
local dx=x2-x0 local dx=x2-x0
local dy=y2-y0 local dy=y2-y0
-- there is no reason for an acute angle to stick around in this world
--[[
local ang0=atan2(x2-x1,y2-y1)
local ang2=atan2(x0-x1,y0-y1)
local diff=abs((ang0-ang2 + 0.5)%1-0.5)
if (diff<0.25) return false,x0,y0,0,0
]]--
local adx,ady local adx,ady
local x1_new,y1_new local x1_new,y1_new
if abs(dx)>abs(dy) then if abs(dx)>abs(dy) then
if (x0>x2) dx,x0,y0,x2,y2=-dx,x2,y2,x0,y0
local dprop=(x1-x0)/dx local dprop=(x1-x0)/dx
x1_new,y1_new=x1,y0+dprop*(y2-y0) x1_new,y1_new=x1,y0+dprop*(y2-y0)
ady=sgn0(y1_new-y1) ady=sgn0(y1_new-y1)
@ -1198,25 +1251,69 @@ function would_stick(x0,y0,anchor,x2,y2)
if (y0>y2) adx=ady if (y0>y2) adx=ady
if (y0<y2) adx=-ady if (y0<y2) adx=-ady
else else
if (y0>y2) dy,x0,y0,x2,y2=-dy,x2,y2,x0,y0
local dprop=(y1-y0)/dy local dprop=(y1-y0)/dy
x1_new,y1_new=x0+dprop*(x2-x0),y1 x1_new,y1_new=x0+dprop*(x2-x0),y1
adx=sgn0(x1_new-x1) adx=sgn0(x1_new-x1)
ady=0 ady=0
if (y0>y2) ady=adx if (x0>x2) ady=adx
if (y0<y2) ady=-adx if (x0<x2) ady=-adx
end end
local wouldnt=anchor.dropped or (anchor.adx==-adx or anchor.ady==-ady) local wouldnt=anchor.dropped or (anchor.adx!=adx or anchor.ady!=ady)
return not wouldnt,x1_new,y1_new,adx,ady return not wouldnt,x1_new,y1_new,adx,ady
end end
function rope:be_pushed_by(anchor,ax_old,ay_old)
local n0=self.src
local ax_new,ay_new=anchor.ax,anchor.ay
while true do
n1=n0.next
if (n1==nil) return
local nx0,ny0=n0.ax,n0.ay
local nx1,ny1=n1.ax,n1.ay
if
(ax_new==ax_old and nx0<=anchor.ax and anchor.ax<=nx1) and
(ay_new==ay_old and ny0<=anchor.ay and anchor.ay<=ny1) and
not (anchor.ax==nx0 and anchor.ay==ny0) and
not (anchor.ax==nx1 and anchor.ay==ny1) and
(_which_side(ax_old,ay_old,nx0,ny0,nx1,ny1)!=
_which_side(ax_new,ay_new,nx0,ny0,nx1,ny1)
) and would_stick(nx0,ny0,anchor,nx1,ny1)
then
local nx05,ny05
if ax_new==ax_old then
nx05=anchor.ax
ny05=ny0+(nx05-nx0)/(nx1-nx0) * (ny1-ny0)
--printh("found (x): "..tostring({nx05,ny05}))
elseif ay_new==ay_old then
ny05=anchor.ay
nx05=nx0+(ny05-ny0)/(ny1-ny0) * (nx1-nx0)
--printh("found (y): "..tostring({nx05,ny05}))
else
assert(false,"wtf?")
end
local n05={ax=nx05,ay=ny05,associated_with=anchor,prev=n0,next=n1}
--printh("adding: "..tostring({nx05,ny05,anchor}))
n0.next=n05
n1.prev=n05
self:_drag(n05,anchor.ax,anchor.ay)
-- printh("dragged: "..tostring({n05.ax,n05.ay,anchor}))
-- printh("local: "..tostring(n0.associated_with).."->"..tostring(n05.associated_with).."->"..tostring(n1.associated_with))
else
n0=n0.next
end
end
end
function rope:_drag(n1,ax1_new,ay1_new) function rope:_drag(n1,ax1_new,ay1_new)
local function _sweep_radar(ax_pivot,ay_pivot,ax_far0,ay_far0,ax_far1,ay_far1) 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 if (ax_far0==ax_far1 and ay_far0==ay_far1) return nil
local function crossed(x,y)
return x!=y
end
if ax_far0==ax_far1 then if ax_far0==ax_far1 then
local ax_far=ax_far0 local ax_far=ax_far0
@ -1227,12 +1324,12 @@ function rope:_drag(n1,ax1_new,ay1_new)
for anchor in level:anchor_points() do for anchor in level:anchor_points() do
if if
not (anchor.ax==ax_pivot and anchor.ay==ay_pivot) and not (anchor.ax==ax_pivot and anchor.ay==ay_pivot) and
not (anchor.ax==ax_far0 and anchor.ay==ay_far0) and -- not (anchor.ax==ax_far0 and anchor.ay==ay_far0) and
not (anchor.ax==ax_far1 and anchor.ay==ay_far1) and not (anchor.ax==ax_far1 and anchor.ay==ay_far1) and
(ax0<=anchor.ax and anchor.ax<=ax1) and (ax0<=anchor.ax and anchor.ax<=ax1) and
would_stick(ax_pivot,ay_pivot,anchor,ax_far,ay_far_new) and would_stick(ax_pivot,ay_pivot,anchor,ax_far,ay_far_new) and
crossed( (
_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_old) !=
_which_side(anchor.ax,anchor.ay,ax_pivot,ay_pivot,ax_far,ay_far_new) _which_side(anchor.ax,anchor.ay,ax_pivot,ay_pivot,ax_far,ay_far_new)
) )
then then
@ -1250,12 +1347,12 @@ function rope:_drag(n1,ax1_new,ay1_new)
for anchor in level:anchor_points() do for anchor in level:anchor_points() do
if if
not (anchor.ax==ax_pivot and anchor.ay==ay_pivot) and not (anchor.ax==ax_pivot and anchor.ay==ay_pivot) and
not (anchor.ax==ax_far0 and anchor.ay==ay_far0) and -- not (anchor.ax==ax_far0 and anchor.ay==ay_far0) and
not (anchor.ax==ax_far1 and anchor.ay==ay_far1) 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 would_stick(ax_pivot,ay_pivot,anchor,ax_far_new,ay_far) and
(ay0<=anchor.ay and anchor.ay<=ay1) and (ay0<=anchor.ay and anchor.ay<=ay1) and
crossed( (
_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_old,ay_far) !=
_which_side(anchor.ax,anchor.ay,ax_pivot,ay_pivot,ax_far_new,ay_far) _which_side(anchor.ax,anchor.ay,ax_pivot,ay_pivot,ax_far_new,ay_far)
) )
then then
@ -1277,7 +1374,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) local anch=_sweep_radar(n0.ax,n0.ay,ax1_old,ay1_old,ax1_new,ay1_new)
if (anch==nil) break if (anch==nil) break
local n05={ax=anch.ax,ay=anch.ay,associated_with=anch,prev=n0,next=n1} 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 pre: "..tostring(n0.associated_with).."->"..tostring(n05.associated_with).."->"..tostring(n1.associated_with))
n0.next=n05 n0.next=n05
n1.prev=n05 n1.prev=n05
n0=n05 n0=n05
@ -1289,7 +1386,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) local anch=_sweep_radar(n2.ax,n2.ay,ax1_old,ay1_old,ax1_new,ay1_new)
if (anch==nil) break if (anch==nil) break
local n15={ax=anch.ax,ay=anch.ay,associated_with=anch,prev=n1,next=n2} 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 n1.next=n15
n2.prev=n15 n2.prev=n15
n2=n15 n2=n15
@ -1341,11 +1438,6 @@ function rope:collide_mrect(mx0,my0,mw,mh,exclude_src,exclude_dst)
local mx1,my1=mx0+mw,my0+mh local mx1,my1=mx0+mw,my0+mh
local n0=self.src local n0=self.src
mx0+=0.4
my0+=0.4
mx1-=0.4
my1-=0.4
while true do while true do
local n1=n0.next local n1=n0.next
if (n1==nil) return false if (n1==nil) return false
@ -1356,31 +1448,30 @@ function rope:collide_mrect(mx0,my0,mw,mh,exclude_src,exclude_dst)
if (nd==nil) return false if (nd==nil) return false
end end
local x1,y1,x2,y2=n0.ax,n0.ay,n1.ax,n1.ay
local function _line_line(x3,y3,x4,y4)
local denom=((y4-y3)*(x2-x1)-(x4-x3)*(y2-y1))
local ua=
((x4-x3)*(y1-y3)-(y4-y3)*(x1-x3))/denom
if (ua<0 or ua>1) return false
local ub=
((x2-x1)*(y1-y3)-(y2-y1)*(x1-x3))/denom
if (ub<0 or ub>1) return false
return true
end
if exclude_src<=0 then 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(mx0,my0,mx1,my0) or _line_line(mx0,my0,mx0,my1) or _line_line(mx0,my1,mx1,my1) or _line_line(mx1,my0,mx1,my1)) 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 end
exclude_src-=1 exclude_src-=1
n0=n1 n0=n1
end end
end end
function _line_line(x1,y1,x2,y2,x3,y3,x4,y4)
local denom=((y4-y3)*(x2-x1)-(x4-x3)*(y2-y1))
local ua=
((x4-x3)*(y1-y3)-(y4-y3)*(x1-x3))/denom
if (ua<0 or ua>1) return false
local ub=
((x2-x1)*(y1-y3)-(y2-y1)*(x1-x3))/denom
if (ub<0 or ub>1) return false
return true
end
-->8 -->8
-- moved here because it's complicated -- moved here because it's complicated
@ -1434,6 +1525,18 @@ function rope:_tug(hypothetically)
end end
local latch=self.latch local latch=self.latch
if latch!=nil and latch.el=="eyehook" then
if (hypothetically) return ancs,0
player.todo={{
update=function(s)
if s.rope==nil or s.rope:done() then
return true
end
end
}}
self:destroy(true)
return true
end
if latch!=nil and latch.el=="crate" then if latch!=nil and latch.el=="crate" then
local dmx,dmy= local dmx,dmy=
sgn0(latch.dx), sgn0(latch.dx),
@ -1569,20 +1672,20 @@ function rope:_anchors_simplified()
x=flr(a.ax*8+0.5),y=flr(a.ay*8+0.5), x=flr(a.ax*8+0.5),y=flr(a.ay*8+0.5),
ax=a.ax,ay=a.ay ax=a.ax,ay=a.ay
} }
if a.associated_with then local aw=a.associated_with
if (a.associated_with.adx==1) point.x-=1 local l=self.latch
if (a.associated_with.ady==1) point.y-=1 if aw then
elseif a.prev==nil and self.latch then if (aw.adx==1) point.x-=1
if (self.latch.ax_offset<0) point.x-=1 if (aw.ady==1) point.y-=1
if (self.latch.ay_offset<0) point.y-=1 elseif a.prev==nil and l then
if (l.ax_offset<0) point.x-=1
if (l.ay_offset<0) point.y-=1
end end
if #points<=1 then local p0,p1=points[#points-1],points[#points]
if p0==nil then
add(points,point) add(points,point)
elseif abs( elseif _slope(p0,p1)==_slope(p1,point) then -- epsilon?
_slope(points[#points-1],points[#points])-
_slope(points[#points],point)
)==0 then -- epsilon?
points[#points]=point points[#points]=point
else else
add(points,point) add(points,point)
@ -1598,17 +1701,48 @@ wrongbleep={}
add(real_modules,wrongbleep) add(real_modules,wrongbleep)
function wrongbleep:init() function wrongbleep:init()
self.duration=0 self.duration=0
self.continuous=0
end end
function wrongbleep:update() function wrongbleep:update()
if (self.duration>5) self.duration=5 if (self.duration>5) self.duration=5
if (self.duration>0) sfx(63,3) if self.duration>0 then sfx(63,3) self.continuous+=1
self.duration=max(self.duration-1,-2) else self.continuous=0 end
self.duration=max(self.duration-1,-4)
end end
function wrongbleep:bleep(duration) function wrongbleep:bleep(duration)
self.duration+=duration or 2 self.duration+=duration or 2
end end
function wrongbleep:vibrate(duration)
if (self.continuous<10) return 0
return (rnd()*2-1)*min(self.continuous/10,2)
end
function wrongbleep:adequately_warned(duration)
return self.continuous>45
end
-->8
-- text
level_text={by_lvl={}}
add(real_modules,level_text)
level_text_raw={
"9`9`11`\f7\#0press 🅾️ to mlem & unmlem",
"9`33`17`\f7\#0❎ to yoink"
}
function level_text:init()
for i=0,32 do level_text.by_lvl[i]={} end
for row in all(level_text_raw) do
if row then
lvlxys=split(row,"`")
add(level_text.by_lvl[lvlxys[1]],lvlxys)
end
end
end
function level_text:draw()
for xys in all(level_text.by_lvl[level.ix]) do
print(xys[4],xys[2],xys[3],6)
end
end
__gfx__ __gfx__
000030000000002200003000000cc0cc0cccccccccccccccccccccccccccccccc0bb0000000000000000000000000000dddddddd000000000005000000000000 000030000000002200003000000cc0cc0cccccccccccccccccccccccccccccccc0bb0000000000000000000000000000dddddddd000000000005000000000000
003333300000332200333330000cc0cc0000cc0000000000000000000cc0000000bb0bb0bbbb0bbbbb0bbbbb0bbb0000dddddddd000000000000500000000000 003333300000332200333330000cc0cc0000cc0000000000000000000cc0000000bb0bb0bbbb0bbbbb0bbbbb0bbb0000dddddddd000000000000500000000000
@ -1642,14 +1776,14 @@ eeee0000cc0440550044440000000000000000000000888888800000000000000000000000000000
0000000077a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000ddddddddddddddddddd77ddddddddddd 0000000077a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000ddddddddddddddddddd77ddddddddddd
00000007777a000000000000000000000000000000000000000000000000000000000000000000000000000000000000ddddddddddddddddddd00ddddddddddd 00000007777a000000000000000000000000000000000000000000000000000000000000000000000000000000000000ddddddddddddddddddd00ddddddddddd
000444444444440000000000000000000000000000000000000000000000000000000000000000000000000000000000ddddddddddddddddddd00ddddddddddd 000444444444440000000000000000000000000000000000000000000000000000000000000000000000000000000000ddddddddddddddddddd00ddddddddddd
77077077777007777707707777700777770770777770077777077077777007777707707777700777770770777770077777077077777007777707707777700777 44444444444004444444444444444444444444444440044444444444444004444444444444400444444444444440044444444444444004444444444444400444
77777777777007777777777777700777777777777770077777777777777007777777777777700777777777777770077777777777777007777777777777700777 47766774477007744776677447700774477667744770077447766774477007744776677447700774477667744770077447766774477007744776677447700774
07777770077777700777777707777777077777700777777007777777077777777777777077777770777777777777777777777770777777707777777777777777 47777774477777744777777447777774477777744777777447777774477777744777777447777774477777744777777447777774477777744777777447777774
77700777777007777770070077700700777007777770077777700700777007000070077700700777007007000070070000700777007007770070070000700700 46700764467007644670070046700700467007644670076446700700467007000070076400700764007007000070070000700764007007640070070000700700
77700777777007777770070077700700777007777770077777700700777007000070077700700777007007000070070000700777007007770070070000700700 46700764467007644670070046700700467007644670076446700700467007000070076400700764007007000070070000700764007007640070070000700700
07777770077777700777777707777777077777700777777007777777077777777777777077777770777777777777777777777770777777707777777777777777 47777774477777744777777447777774477777744777777447777774477777744777777447777774477777744777777447777774477777744777777447777774
77777777777777777777777777777777777007777770077777700777777007777777777777777777777777777777777777700777777007777770077777700777 47766774477667744776677447766774477007744770077447700774477007744776677447766774477667744776677447700774477007744770077447700774
77077077770770777707707777077077777007777770077777700777777007777707707777077077770770777707707777700777777007777770077777700777 44444444444444444444444444444444444004444440044444400444444004444444444444444444444444444444444444400444444004444440044444400444
__label__ __label__
77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777 77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777
77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777 77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777
@ -1784,38 +1918,38 @@ __gff__
000000000808080808080808c00000000000000008080808080808080000000040400000080808080808080800000000404000000808080808080808c0c0c0c000000000080808080808080800000000000000000808080808080808000000000000000008080808080808080000000000000000080808080808080800000000 000000000808080808080808c00000000000000008080808080808080000000040400000080808080808080800000000404000000808080808080808c0c0c0c000000000080808080808080800000000000000000808080808080808000000000000000008080808080808080000000000000000080808080808080800000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
__map__ __map__
0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e
0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c00000000000000000000000000000c000000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c00000000000000000000000000000c0c00000000000000000000000000000c0e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000
0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c00000000000000000000000000000c000000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c00000000000000000000000000000c0c0000000000000c001c1c000c0c000c0e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000
0c000000000044000c0c0c0c0c0c0c0c0c00000000000000000000000000000c000000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000 0c000000000044000c0c0c0c0c0c0c0c0c00000000000000000000000000000c0c0000000000000000000000000c000c0e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000
0c0000000000000000000000000000000c00000000000000000000000000000c000000000000000000000000000000000e00000000000e0e0e0e0000000000000e000000000e000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000 0c0000000000000000000000000000000c00000000000000000000000000000c0c00004f0c0c0000000c000000001c120e00000000000e0e0e0e0000000000000e000000000e000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000
0c00000000001c0000000000000000000000000000000000000000000000000c000000000000000000000000000000000e00000000000000000e0000000000000e000000000e000e00000000000000000e0000000000000000000000000000000e0000000000000e0e000000000000000e00000000000e0e0e0e000000000000 3d1c000000001c0000000000000000000000000000000000000000000000000c3d000c000c0c0000003f0c0c0000000c0e00000000000000000e0000000000000e000000000e000e00000000000000000e0000000000000000000000000000000e0000000000000e0e000000000000000e00000000000e0e0e0e000000000000
0c0000000000000000000000000000410000000000000000000000000000000c0000000000000e0e0e000000000000000e00000000000000000e0000000000000e000000000e000e00000000000000000e00000000000e0e0e000000000000000e00000000000e0000000000000000000e00000000000000000e000000000000 0c0000000000000000000000000000410000000000000000000000000000000c0c000c0000000000000000000000000c0e00000000000000000e0000000000000e000000000e000e00000000000000000e00000000000e0e0e000000000000000e00000000000e0000000000000000000e00000000000000000e000000000000
0c000000000000000c0c0c0c0c0c0c0c0c00000000000020210000000000000c00000000000e00000e0e0000000000000e0000000000000e0e0e0000000000000e000000000e0e0e0e0e0000000000000e00000000000e0000000000000000000e00000000000e0e0e000000000000000e000000000000000e00000000000000 0c000000000000000c0c0c0c0c0c0c0c0c00000000000020210000000000000c01000c0c00000000000c0c004f00000c0e0000000000000e0e0e0000000000000e000000000e0e0e0e0e0000000000000e00000000000e0000000000000000000e00000000000e0e0e000000000000000e000000000000000e00000000000000
0c0c0c0c0c0c00000000000c0c0c0c0c0c00000000000030310000000000000c000000000000000e0e000000000000000e00000000000000000e0000000000000e0000000000000e00000000000000000e00000000000e0e0e000000000000000e00000000000e00000e0000000000000e0000000000000e0000000000000000 0c0c0c0c0c0c00000000000c0c0c0c0c0c00000000000030310000000000000c0c0000000000000c00000c000000000c0e00000000000000000e0000000000000e0000000000000e00000000000000000e00000000000e0e0e000000000000000e00000000000e00000e0000000000000e0000000000000e0000000000000000
0c00000000000c0c004f000c0c0c0c0c0c00000000000000000000000000000c0000000000000e0e00000000000000000e00000000000e0e0e0e0000000000000e0000000000000e00000000000000000e000000000000000e000000000000000e0000000000000e0e000000000000000e0000000000000e0000000000000000 0c00000000000c0c004f000c0c0c0c0c0c00000000000000000000000000000c0c00004f0000000c0c000c0000000c0c0e00000000000e0e0e0e0000000000000e0000000000000e00000000000000000e000000000000000e000000000000000e0000000000000e0e000000000000000e0000000000000e0000000000000000
0c00000c4f00000000000000000000120100000000000000000000000000000c00000000000e0e0e0e0e0000000000000e0000000000000000000000000000000e0000000000000e00000000000000000e00000000000e0e0e000000000000000e0000000000000000000000000000000e000000000000000000000000000000 0c00000c4f00000000000000000000120100000000000000000000000000000c0c00000000000000000000000000000c0e0000000000000000000000000000000e0000000000000e00000000000000000e00000000000e0e0e000000000000000e0000000000000000000000000000000e000000000000000000000000000000
3d0000000000003f0c000c0c0c0c0c0c0c00000000000000000000000000000c000000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000 3d0000000000003f0c000c0c0c0c0c0c0c00000000000000000000000000000c0c00000c00000000000000000000000c0e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000
0c0000000000000000000c0c0c0c0c0c0c00000000000000000000000000000c000000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000 0c0000000000000000000c0c0c0c0c0c0c00000000000000000000000000000c0c000c0c0c0000000c0c0c004f00000c0e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000
010000000000000c0c0c0c0c0c0c0c0c0c00000000000000000000000000000c000000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000 010000000000000c0c0c0c0c0c0c0c0c0c00000000000000000000000000000c0c00000c00000000000000000000000c0e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000
0c0c0c0c00000c0c0c0c0c0c0c0c0c0c0c00000000000000000000000000000c000000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000 0c0c0c0c00000c0c0c0c0c0c0c0c0c0c0c00000000000000000000000000000c0c00000000001c00000000000000000c0e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000
0c0c0c0c00410c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c000000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000 0c0c0c0c00410c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000
0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e
0c00000000000000000000000000000c0e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000 0c00000000000000000000000000000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0000000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0e0000000000000000000000000000000e000000000000000000000000000000
0c004f000000000c001c1c000c0c000c0e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000 0c004f000000000c001c1c000c0c000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0000000000000c0c0c0c0c0c0c0c0c0c0c0c00000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c3e0c0c0c0c0c0e0000000000000000000000000000000e000000000000000000000000000000
0c0000000000000000000000000c000c0e00000000000e0e00000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000e000000000000000000000e000000000000000000000000000000 0c0000000000000000000000000c000c0c00000000000000000c00000000000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c000000000000000000000c0c0c0c0c0c0000000000000000000c000000000c0c000000000000000000001c0000000c0e000000000e000000000000000000000e000000000000000000000000000000
0c0000000c0c0000000c000000001c120e000000000e00000e000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e00000000000e0000000e0e000000000e000000000e0000000e0000000000000e000000000e00000e0e0e0000000000 0c0000000c0c0000000c000000001c120100000000000000004f000000000012010000000000001c004f0000000000120100000000000000004f1c000000001201000000000000000047000000000012010000000000000000401c00000000120e000000000e0000000e0000000000000e000000000e00000e0e0e0000000000
0c000c000c0c0000000c0c0c0000000c0e000000000e00000e000000000000000e00000000000e000000000e000000000e0000000000000e0000000e000000000e00000000000e00000e0e00000000000e00000000000e00000000000e0000000e000000000e0000000e000e000000000e000000000e00000e00000000000000 0c000c000c0c0000000c0c0c0000000c0c00000000000000000c00000000000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c000000000000000000000c0c0c0c0c0c0000000000000000000c000000000c0c000000000000000000001c0000000c0e000000000e0000000e000e000000000e000000000e00000e00000000000000
0c000c0000000000000000000000000c0e00000000000e0e0e000000000000000e00000000000e0000000e000e0000000e0000000000000e0000000e000000000e00000000000e000000000e000000000e00000000000e0000000e0e000000000e000000000e0000000e0e0e0e0000000e000000000e00000e0e0e0000000000 0c000c0000000000000000000000000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0000000000000c0c0c0c0c0c0c0c0c0c0c0c00000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c3c0c0c0c0c0c0e000000000e0000000e0e0e0e0000000e000000000e00000e0e0e0000000000
01000c0c00000000000c0c004f00000c0e000000000000000e000000000000000e00000000000e0000000e000e0000000e0000000000000e0000000e000000000e00000000000e0000000e00000000000e00000000000e00000000000e0000000e000000000e00000000000e000000000e000000000e000000000e0000000000 01000c0c00000000000c0c004f00000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0000000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0e000000000e00000000000e000000000e000000000e000000000e0000000000
0c0000000000000c00000c000000000c0e00000000000e0e00000000000000000e00000000000e000000000e000000000e0000000000000e0000000e000000000e00000000000e00000e0e0e000000000e00000000000e0000000e0e000000000e000000000e00000000000e000000000e000000000e000000000e0000000000 0c0000000000000c00000c000000000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0000000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0e000000000e00000000000e000000000e000000000e000000000e0000000000
0c00004f0000000c0c000c0000000c0c0e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e00000000000e0000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000e00000e0e0e0000000000 0c00004f0000000c0c000c0000000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0e0000000000000000000000000000000e000000000e00000e0e0e0000000000
0c00000000000000000000000000000c0e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000 0c00000000000000000000000000000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0e0000000000000000000000000000000e000000000000000000000000000000
0c00000c00000000000000000000000c0e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000 0c00000c00000000000000000000000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0e0000000000000000000000000000000e000000000000000000000000000000
0c000c0c0c0000000c0c0c004f00000c0e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000 0c000c0c0c0000000c0c0c004f00000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0e0000000000000000000000000000000e000000000000000000000000000000
0c00000c00000000000000000000000c0e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000 0c00000c00000000000000000000000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0e0000000000000000000000000000000e000000000000000000000000000000
0c00000000001c00000000000000000c0e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e0000000000000000000000000000000e000000000000000000000000000000 0c00000000001c00000000000000000c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0e0000000000000000000000000000000e000000000000000000000000000000
0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e
__sfx__ __sfx__
01280000050550c05511055180551d05500000000000000000055070550c0550f055130550f0550c0550705501055080550d055140551905500000000000000000055070550f0551305518055130550f0550c055 01280000050550c05511055180551d05500000000000000000055070550c0550f055130550f0550c0550705501055080550d055140551905500000000000000000055070550f0551305518055130550f0550c055
0128000000000000001f0001f055200551f0551d055180551b055000000000000000000000000000000000000000000000000001b0551d0551b05519055140551805500000000000000000000000000000000000 0128000000000000001f0001f055200551f0551d055180551b055000000000000000000000000000000000000000000000000001b0551d0551b05519055140551805500000000000000000000000000000000000