Cliffside2

Created Diff never expires
18 removals
1168 lines
10 additions
1160 lines
pico-8 cartridge // http://www.pico-8.com
pico-8 cartridge // http://www.pico-8.com
version 43
version 43
__lua__
__lua__
-- cliffside 2 long
-- by leo22344
level_index,level_intro=0,0
level_index,level_intro=0,0


function game_start()
function game_start()
-- reset state
-- reset state
snow,clouds,
snow,clouds,
freeze_time,frames,seconds,minutes,shake,sfx_timer,
freeze_time,frames,seconds,minutes,shake,sfx_timer,
berry_count,death_count,
berry_count,death_count,
collected,show_score,
collected,show_score,
camera_x,camera_y,
camera_x,camera_y,
have_grapple=
have_grapple=
{},{},
{},{},
0,0,0,0,0,0,
0,0,0,0,0,0,
0,0,
0,0,
{},0,
{},0,
0,0,
0,0,
true
true


for i=0,25 do
for i=0,25 do
add(snow,{x=rnd(132),y=rnd(132)})
add(snow,{x=rnd(132),y=rnd(132)})
add(clouds,{x=rnd(132),y=rnd(132),s=16+rnd(32)})
add(clouds,{x=rnd(132),y=rnd(132),s=16+rnd(32)})
end
end


-- goto titlescreen or level
-- goto titlescreen or level
if level_index==0 then
if level_index==0 then
current_music=38
current_music=38
music(current_music)
music(current_music)
else
else
goto_level(level_index)
goto_level(level_index)
end
end
end
end


function _init()
function _init()
game_start()
game_start()
--goto_level(14)
--goto_level(14)
end
end


function _update()
function _update()


-- titlescreen
-- titlescreen
if level_index==0 then
if level_index==0 then
if titlescreen_flash then
if titlescreen_flash then
titlescreen_flash-=1
titlescreen_flash-=1
if titlescreen_flash<-30 then goto_level(1) end
if titlescreen_flash<-30 then goto_level(1) end
elseif btn(4) or btn(5) then
elseif btn(4) or btn(5) then
titlescreen_flash=50
titlescreen_flash=50
sfx(22,3)
sfx(22,3)
end
end
-- level intro card
-- level intro card
elseif level_intro>0 then
elseif level_intro>0 then
level_intro-=1
level_intro-=1
if level_intro==0 then psfx(17,24,9) end
if level_intro==0 then psfx(17,24,9) end
-- normal level
-- normal level
else
else
-- timers
-- timers
sfx_timer=max(sfx_timer-1)
sfx_timer=max(sfx_timer-1)
infade=min(infade+1,60)
infade=min(infade+1,60)
shake=max(shake-1)
shake=max(shake-1)
if level_index~=8 and level_index~=14 then
if level_index~=8 and level_index~=14 then
frames+=1
frames+=1
seconds+=frames\30
seconds+=frames\30
minutes+=seconds\60
minutes+=seconds\60
seconds%=60
seconds%=60
frames%=30
frames%=30
end
end


update_input()
update_input()


--freeze
--freeze
if freeze_time>0 then
if freeze_time>0 then
freeze_time-=1
freeze_time-=1
else
else
--objects
--objects
for o in all(objects) do
for o in all(objects) do
if o.freeze>0 then
if o.freeze>0 then
o.freeze-=1
o.freeze-=1
else
else
o:update()
o:update()
end
end
if o.destroyed then
if o.destroyed then
del(objects,o)
del(objects,o)
end
end
end
end
end
end
end
end
end
end


function _draw()
function _draw()
pal()
pal()
if level_index==0 then
if level_index==0 then
cls(0)
cls(0)
if titlescreen_flash then
if titlescreen_flash then
local c=titlescreen_flash>10 and (titlescreen_flash%10<5 and 7 or 10) or titlescreen_flash>5 and 2 or titlescreen_flash>0 and 1 or 0
local c=titlescreen_flash>10 and (titlescreen_flash%10<5 and 7 or 10) or titlescreen_flash>5 and 2 or titlescreen_flash>0 and 1 or 0
if c<10 then for i=0,15 do pal(i,c) end end
if c<10 then for i=0,15 do pal(i,c) end end
end
end
sspr(64,32,64,32,36,32)
sspr(64,32,64,32,36,32)
local icoy=titlescreen_flash and (50-titlescreen_flash) or 0
local icoy=titlescreen_flash and (50-titlescreen_flash) or 0
spr(114, 114, 118+icoy)
spr(114, 114, 118+icoy)
rect(0,0,127,127,7)
rect(0,0,127,127,7)
print_center("mod by",70,1)
print_center("mod by",4,1)
print_center("rubyred",77,5)
print_center("leo22344",11,5)
print_center("inspired by a mod by",18,1)
print_center("✽calverin✽", 26, 9)
print_center("llllllllllllllllllllllllllllllllllllloong",65,14)
print_center("mod of a mod by",72,1)
print_center("rubyred",79,5)
print_center("original game by",88,1)
print_center("original game by",88,1)
print_center("maddy thorson",95,5)
print_center("maddy thorson",95,5)
print_center("noel berry",102,5)
print_center("noel berry",102,5)
print_center("lena raine",109,5)
print_center("lena raine",109,5)
draw_snow()
draw_snow()
return
return
end
end


if level_intro>0 then
if level_intro>0 then
cls()
cls()
camera()
camera()
draw_time(4,4)
draw_time(4,4)
if level_index>8 then
if level_index>8 then
local d=level_intro==60 and level_index==9 and "\^d3" or ""
local d=level_intro==60 and level_index==9 and "\^d3" or ""
local d2=level_intro==60 and level_index==9 and "\^4" or ""
local d2=level_intro==60 and level_index==9 and "\^4" or ""
print("\^i"..d.."black diamond"..d2,37,56, 7)
print("\^i"..d.."black diamond"..d2,37,56, 7)
end
end
print_center(level.title,64,7)
print_center(level.title,64,7)
return
return
end
end


local camera_x,camera_y=peek2(0x5f28),peek2(0x5f2a)
local camera_x,camera_y=peek2(0x5f28),peek2(0x5f2a)


if shake>0 then
if shake>0 then
camera(camera_x-2+rnd(5),camera_y-2+rnd(5))
camera(camera_x-2+rnd(5),camera_y-2+rnd(5))
end
end


-- clear screen
-- clear screen
cls(level and level.bg or 0)
cls(level and level.bg or 0)


-- draw clouds
-- draw clouds
draw_clouds(1,0,0,1,1,level.clouds or 13,#clouds)
draw_clouds(1,0,0,1,1,level.clouds or 13,#clouds)


-- columns
-- columns
if level.columns then
if level.columns then
fillp(0b0000100000000010.1)
fillp(0b0000100000000010.1)
local x=0
local x=0
while x<level.width do
while x<level.width do
local tx=x*8+camera_x*0.1
local tx=x*8+camera_x*0.1
rectfill(tx,0,tx+(x%2)*8+8,level.height*8,level.columns)
rectfill(tx,0,tx+(x%2)*8+8,level.height*8,level.columns)
x+=1+x%7
x+=1+x%7
end
end
fillp()
fillp()
end
end


-- draw tileset
-- draw tileset
for x=mid(0,camera_x\8,level.width),mid(0,(camera_x+128)\8,level.width) do
for x=mid(0,camera_x\8,level.width),mid(0,(camera_x+128)\8,level.width) do
for y=mid(0,camera_y\8,level.height),mid(0,(camera_y+128)\8,level.height) do
for y=mid(0,camera_y\8,level.height),mid(0,(camera_y+128)\8,level.height) do
local tile=tile_at(x,y)
local tile=tile_at(x,y)
if level.pal and fget(tile,7) then level.pal() end
if level.pal and fget(tile,7) then level.pal() end
if tile~=0 and fget(tile,0) then spr(tile,x*8,y*8) end
if tile~=0 and fget(tile,0) then spr(tile,x*8,y*8) end
pal() palt()
pal() palt()
end
end
end
end


-- score
-- score
if show_score>175 then
if show_score>175 then
local off=level_index==14 and 40 or 0
local off=level_index==14 and 40 or 0
local b=level_index==14 and 10 or 7
local b=level_index==14 and 10 or 7
rectfill(34,5+off,98,47+off,1)
rectfill(34,5+off,98,47+off,1)
rectfill(32,3+off,96,45+off,0)
rectfill(32,3+off,96,45+off,0)
rect(32,3+off,96,45+off,b)
rect(32,3+off,96,45+off,b)
local gb=level_index==8 and berry_count==8 and 115 or level_index==14 and 115 or 21
local gb=level_index==8 and berry_count==8 and 115 or level_index==14 and 115 or 21
local gt=minutes<1 and 116 or minutes==1 and seconds<30 and 116 or berry_count==8 and minutes<2 and 116 or level_index==14 and minutes<10 and 116 or 87
local gt=minutes<1 and 116 or minutes==1 and seconds<30 and 116 or berry_count==8 and minutes<2 and 116 or level_index==14 and minutes<10 and 116 or 87
local gd=death_count<=0 and 117 or 71
local gd=death_count<=0 and 117 or 71
spr(gb,44,9+off)
spr(gb,44,9+off)
?"x "..berry_count..(level_index==8 and "/8" or ""),56,11+off,7
?"x "..berry_count..(level_index==8 and "/8" or ""),56,11+off,7
spr(gt,44,21+off)
spr(gt,44,21+off)
draw_time(56,21+off)
draw_time(56,21+off)
spr(gd,44,33+off)
spr(gd,44,33+off)
?"x "..death_count,56,34+off,7
?"x "..death_count,56,34+off,7
end
end


for o in all(objects) do
for o in all(objects) do
if o.base==player and o.draw_back then o:draw() end
if o.base==player and o.draw_back then o:draw() end
end
end


-- draw objects
-- draw objects
local p
local p
for o in all(objects) do
for o in all(objects) do
if o.base==player then p=o else o:draw() end
if o.base==player then p=o else o:draw() end
end
end
if p and not p.draw_back then p:draw() end
if p and not p.draw_back then p:draw() end


-- draw snow
-- draw snow
draw_snow()
draw_snow()


-- draw fg clouds
-- draw fg clouds
if level.fogmode then
if level.fogmode then
if level.fogmode==1 then fillp(0b0101101001011010.1) end
if level.fogmode==1 then fillp(0b0101101001011010.1) end
draw_clouds(1.25,0,level.height*8+10,1,0,7,#clouds-10)
draw_clouds(1.25,0,level.height*8+10,1,0,7,#clouds-10)
fillp()
fillp()
end
end


camera()
camera()


-- screen wipes
-- screen wipes
-- very similar functions ... can they be compressed into one?
-- very similar functions ... can they be compressed into one?
if p and p.wipe_timer>5 then
if p and p.wipe_timer>5 then
for i=0,127 do
for i=0,127 do
rectfill(0,i,191*(p.wipe_timer-5)/12-32+sin(i*0.2)*16+(127-i)*0.25,i,0)
rectfill(0,i,191*(p.wipe_timer-5)/12-32+sin(i*0.2)*16+(127-i)*0.25,i,0)
end
end
end
end


if infade<15 then
if infade<15 then
for i=0,127 do
for i=0,127 do
rectfill(191*infade/12-32+sin(i*0.2)*16+(127-i)*0.25,i,128,i,0)
rectfill(191*infade/12-32+sin(i*0.2)*16+(127-i)*0.25,i,128,i,0)
end
end
end
end


-- game timer
-- game timer
if infade<45 then
if infade<45 then
draw_time(4,4)
draw_time(4,4)
end
end


camera(camera_x,camera_y)
camera(camera_x,camera_y)
end
end


function draw_time(x,y)
function draw_time(x,y)
local m,h=minutes%60,minutes\60
local m,h=minutes%60,minutes\60
rectfill(x,y,x+32,y+6,0)
rectfill(x,y,x+32,y+6,0)
?two_digit_str(h)..":"..two_digit_str(m)..":"..two_digit_str(seconds),x+1,y+1,7
?two_digit_str(h)..":"..two_digit_str(m)..":"..two_digit_str(seconds),x+1,y+1,7
end
end


function two_digit_str(x)
function two_digit_str(x)
return x<10 and "0"..x or x
return x<10 and "0"..x or x
end
end


function draw_clouds(scale,ox,oy,sx,sy,color,count)
function draw_clouds(scale,ox,oy,sx,sy,color,count)
for i=1,count do
for i=1,count do
local c=clouds[i]
local c=clouds[i]
local s=c.s*scale
local s=c.s*scale
local x,y=ox+(camera_x+(c.x-camera_x*0.9)%(128+s)-s/2)*sx,oy+(camera_y+(c.y-camera_y*0.9)%(128+s/2))*sy
local x,y=ox+(camera_x+(c.x-camera_x*0.9)%(128+s)-s/2)*sx,oy+(camera_y+(c.y-camera_y*0.9)%(128+s/2))*sy
clip(x-s/2-camera_x,y-s/2-camera_y,s,s/2)
clip(x-s/2-camera_x,y-s/2-camera_y,s,s/2)
circfill(x,y,s/3,color)
circfill(x,y,s/3,color)
if i%2==0 then
if i%2==0 then
circfill(x-s/3,y,s/5,color)
circfill(x-s/3,y,s/5,color)
circfill(x+s/3,y,s/6,color)
circfill(x+s/3,y,s/6,color)
end
end
c.x+=(4-i%4)*0.25
c.x+=(4-i%4)*0.25
end
end
clip(0,0,128,128)
clip(0,0,128,128)
end
end


function draw_snow()
function draw_snow()
for i,s in pairs(snow) do
for i,s in pairs(snow) do
circfill(camera_x+(s.x-camera_x*0.5)%132-2,camera_y+(s.y-camera_y*0.5)%132,i%2,7)
circfill(camera_x+(s.x-camera_x*0.5)%132-2,camera_y+(s.y-camera_y*0.5)%132,i%2,7)
s.x+=4-i%4
s.x+=4-i%4
s.y+=sin(time()*0.25+i*0.1)
s.y+=sin(time()*0.25+i*0.1)
end
end
end
end


function print_center(text,y,c)
function print_center(text,y,c)
?text,64-2*#text-0.5,y,c
?text,64-2*#text-0.5,y,c
end
end


function approach(x,target,max_delta)
function approach(x,target,max_delta)
return x<target and min(x+max_delta,target) or max(x-max_delta,target)
return x<target and min(x+max_delta,target) or max(x-max_delta,target)
end
end


function psfx(id,off,len,lock)
function psfx(id,off,len,lock)
if sfx_timer<=0 or lock then
if sfx_timer<=0 or lock then
sfx(id,3,off,len)
sfx(id,3,off,len)
if lock then sfx_timer=lock end
if lock then sfx_timer=lock end
end
end
end
end


function draw_sine_h(x0,x1,y,col,amplitude,time_freq,x_freq,fade_x_dist)
function draw_sine_h(x0,x1,y,col,amplitude,time_freq,x_freq,fade_x_dist)
pset(x0,y,col)
pset(x0,y,col)
pset(x1,y,col)
pset(x1,y,col)
local x_sign,x_max,last_y,this_y=sgn(x1-x0),abs(x1-x0)-1,y
local x_sign,x_max,last_y,this_y=sgn(x1-x0),abs(x1-x0)-1,y
for i=1,x_max do
for i=1,x_max do
local fade=i<=fade_x_dist and i/(fade_x_dist+1) or i>x_max-fade_x_dist+1 and (x_max+1-i)/(fade_x_dist+1) or 1
local fade=i<=fade_x_dist and i/(fade_x_dist+1) or i>x_max-fade_x_dist+1 and (x_max+1-i)/(fade_x_dist+1) or 1
local ax,ay=x0+i*x_sign,y+sin(time()*time_freq+i*x_freq)*amplitude*fade
local ax,ay=x0+i*x_sign,y+sin(time()*time_freq+i*x_freq)*amplitude*fade
pset(ax,ay+1,1)
pset(ax,ay+1,1)
pset(ax,ay,col)
pset(ax,ay,col)
this_y=ay
this_y=ay
while abs(ay-last_y)>1 do
while abs(ay-last_y)>1 do
ay-=sgn(this_y-last_y)
ay-=sgn(this_y-last_y)
pset(ax-x_sign,ay+1,1)
pset(ax-x_sign,ay+1,1)
pset(ax-x_sign,ay,col)
pset(ax-x_sign,ay,col)
end
end
last_y=this_y
last_y=this_y
end
end
end
end


function bdpal() pal(1,1) pal(2,13) pal(5,5) end
function bdpal() pal(1,1) pal(2,13) pal(5,5) end


levels={
levels={
{
{
x=0,
x=0,
y=0,
y=0,
offset=0,
offset=0,
width=32,
width=32,
height=16,
height=16,
camera_mode=0,
camera_mode=0,
music=38,
music=38,
},
},
{
{
x=32,
x=32,
y=0,
y=0,
offset=343,
offset=343,
width=48,
width=48,
height=19,
height=19,
camera_mode=1,
camera_mode=1,
music=36
music=36
},
},
{
{
x=64,
x=64,
y=19,
y=19,
width=16,
width=16,
height=29,
height=29,
camera_mode=0,
camera_mode=0,
title="ruins",
title="ruins",
index=1
index=1
},
},
{
{
x=80,
x=80,
y=0,
y=0,
width=16,
width=16,
height=16,
height=16,
camera_mode=0
camera_mode=0
},
},
{
{
x=96,
x=96,
y=0,
y=0,
width=32,
width=32,
height=16,
height=16,
camera_mode=0
camera_mode=0
},
},
{
{
x=96,
x=96,
y=16,
y=16,
width=32,
width=32,
height=48,
height=48,
camera_mode=2,
camera_mode=2,
title="ravine",
title="ravine",
index=2,
index=2,
music=2,
music=2,
columns=1,
columns=1,
},
},
{
{
x=16,
x=16,
y=48,
y=48,
width=80,
width=80,
height=16,
height=16,
camera_mode=0,
camera_mode=0,
title="tundra",
title="tundra",
index=3,
index=3,
pal=function() pal(1,13) pal(2,14) pal(5,2) end,
pal=function() pal(1,13) pal(2,14) pal(5,2) end,
bg=13,
bg=13,
clouds=15,
clouds=15,
fogmode=1
fogmode=1
},
},
{
{
x=80,
x=80,
y=16,
y=16,
width=16,
width=16,
height=16,
height=16,
camera_mode=0,
camera_mode=0,
right_edge=true,
right_edge=true,
title="safe haven",
title="safe haven",
pal=function() pal(1,13) pal(2,14) pal(5,2) end,
pal=function() pal(1,13) pal(2,14) pal(5,2) end,
bg=13,
bg=13,
clouds=15,
clouds=15,
fogmode=1
fogmode=1
},
},
{
{
x=0,
x=0,
y=32,
y=32,
width=32,
width=32,
height=16,
height=16,
camera_mode=0,
camera_mode=0,
title="dangerous overhang",
title="dangerous overhang",
music=45,
music=45,
pal=bdpal,
pal=bdpal,
clouds=1,
clouds=1,
fogmode=2
fogmode=2
},
},
{
{
x=0,
x=0,
y=16,
y=16,
width=32,
width=32,
height=16,
height=16,
camera_mode=0,
camera_mode=0,
title="glacial pillars",
title="glacial pillars",
pal=bdpal,
pal=bdpal,
clouds=1
clouds=1
},
},
{
{
x=0,
x=0,
y=48,
y=48,
width=16,
width=16,
height=16,
height=16,
camera_mode=0,
camera_mode=0,
title="cave-in",
title="cave-in",
pal=bdpal,
pal=bdpal,
clouds=1
clouds=1
},
},
{
{
x=32,
x=32,
y=19,
y=19,
width=16,
width=16,
height=29,
height=29,
camera_mode=0,
camera_mode=0,
title="beyond juggling",
title="beyond juggling",
pal=bdpal,
pal=bdpal,
clouds=1
clouds=1
},
},
{
{
x=48,
x=48,
y=19,
y=19,
width=16,
width=16,
height=29,
height=29,
camera_mode=0,
camera_mode=0,
title="frustration",
title="frustration",
pal=bdpal,
pal=bdpal,
clouds=1
clouds=1
},
},
{
{
x=80,
x=80,
y=32,
y=32,
width=16,
width=16,
height=16,
height=16,
camera_mode=0,
camera_mode=0,
right_edge=true,
right_edge=true,
title="shattered summit",
title="shattered summit",
pal=bdpal,
pal=bdpal,
clouds=1
clouds=1
}
}
--[[
--[[
{
{
offset=679,
offset=679,
width=128,
width=128,
height=22,
height=22,
camera_mode=3,
camera_mode=3,
camera_barriers_x={38},
camera_barriers_x={38},
camera_barrier_y=6,
camera_barrier_y=6,
music=2,
music=2,
title="trailhead"
title="trailhead"
},
},
{
{
offset=1313,
offset=1313,
width=128,
width=128,
height=32,
height=32,
camera_mode=4,
camera_mode=4,
music=2,
music=2,
title = "glacial caves",
title = "glacial caves",
pal=function() pal(2,12) pal(5,2) end,
pal=function() pal(2,12) pal(5,2) end,
columns = 1
columns = 1
},
},
{
{
offset=2411,
offset=2411,
width=128,
width=128,
height=16,
height=16,
camera_mode=5,
camera_mode=5,
music=2,
music=2,
title="golden valley",
title="golden valley",
pal=function() pal(2,14) pal(5,2) end,
pal=function() pal(2,14) pal(5,2) end,
bg=13,
bg=13,
clouds=15,
clouds=15,
fogmode=2
fogmode=2
},
},
{
{
offset=2645,
offset=2645,
width=128,
width=128,
height=16,
height=16,
camera_mode=6,
camera_mode=6,
camera_barriers_x={105},
camera_barriers_x={105},
music=2,
music=2,
pal=function() pal(2,14) pal(5,2) end,
pal=function() pal(2,14) pal(5,2) end,
bg=13,
bg=13,
clouds=15,
clouds=15,
fogmode=2
fogmode=2
},
},
{
{
offset=2880,
offset=2880,
width=128,
width=128,
height=16,
height=16,
camera_mode=7,
camera_mode=7,
music=2,
music=2,
pal=function() pal(2,12) pal(5,2) end,
pal=function() pal(2,12) pal(5,2) end,
bg=13,
bg=13,
clouds=7,
clouds=7,
fogmode=2,
fogmode=2,
},
},
{
{
offset=3079,
offset=3079,
width=16,
width=16,
height=62,
height=62,
title="destination",
title="destination",
camera_mode=8,
camera_mode=8,
music=2,
music=2,
pal=function() pal(2, 1) pal(7, 11) end,
pal=function() pal(2, 1) pal(7, 11) end,
bg=15,
bg=15,
clouds=7,
clouds=7,
fogmode=2,
fogmode=2,
right_edge=true
right_edge=true
}
}
--]]
--]]
}
}


function camera_x_barrier(tile_x,px,py)
function camera_x_barrier(tile_x,px,py)
local bx=tile_x*8
local bx=tile_x*8
if px<bx-8 then
if px<bx-8 then
camera_target_x=min(camera_target_x,bx-128)
camera_target_x=min(camera_target_x,bx-128)
elseif px>bx+8 then
elseif px>bx+8 then
camera_target_x=max(camera_target_x,bx)
camera_target_x=max(camera_target_x,bx)
end
end
end
end


c_offset=0
c_offset=0
camera_modes={
camera_modes={
-- 0: free
-- 0: free
[0]=function(px,py)
[0]=function(px,py)
camera_target_x,camera_target_y=mid(px-64,0,level.width*8-128),mid(py-64,0,level.height*8-128)
camera_target_x,camera_target_y=mid(px-64,0,level.width*8-128),mid(py-64,0,level.height*8-128)
end,
end,
-- 1: intro
-- 1: intro
function (px,py)
function (px,py)
camera_target_x=(py > 24 and px<256) and mid(px-64,0,128) or (py <= 24) and mid(px-64,0,256) or 256
camera_target_x=(py > 24 and px<256) and mid(px-64,0,128) or (py <= 24) and mid(px-64,0,256) or 256
camera_target_y=py > 24 and 24 or 0
camera_target_y=py > 24 and 24 or 0
end,
end,
-- 2: intro 2
-- 2: intro 2
function (px,py)
function (px,py)
camera_target_x=px < 128 and 0 or 128
camera_target_x=px < 128 and 0 or 128
camera_target_y=mid(py-64,0,level.height*8-128)
camera_target_y=mid(py-64,0,level.height*8-128)
end,
end,
-- 3: level 1
-- 3: level 1
function (px,py)
function (px,py)
camera_target_x,camera_target_y=mid(px-56,0,level.width*8-128),py<level.camera_barrier_y*8+3 and 0 or level.camera_barrier_y*8
camera_target_x,camera_target_y=mid(px-56,0,level.width*8-128),py<level.camera_barrier_y*8+3 and 0 or level.camera_barrier_y*8
for i,b in ipairs(level.camera_barriers_x) do
for i,b in ipairs(level.camera_barriers_x) do
camera_x_barrier(b,px,py)
camera_x_barrier(b,px,py)
end
end
end,
end,
-- 4: level 2
-- 4: level 2
function (px,py)
function (px,py)
if px%128>8 and px%128<120 then
if px%128>8 and px%128<120 then
px=(px\128)*128+64
px=(px\128)*128+64
end
end
if py%128>4 and py%128<124 then
if py%128>4 and py%128<124 then
py=(py\128)*128+64
py=(py\128)*128+64
end
end
camera_target_x,camera_target_y=mid(px-64,0,level.width*8-128),mid(py-64,0,level.height*8-128)
camera_target_x,camera_target_y=mid(px-64,0,level.width*8-128),mid(py-64,0,level.height*8-128)
end
end
}
}


function snap_camera()
function snap_camera()
camera_x,camera_y=camera_target_x,camera_target_y
camera_x,camera_y=camera_target_x,camera_target_y
camera(camera_x,camera_y)
camera(camera_x,camera_y)
end
end


function tile_y(py)
function tile_y(py)
return mid(py\8,0,level.height-1)--max(0,min(py\8,level.height-1))
return mid(py\8,0,level.height-1)--max(0,min(py\8,level.height-1))
end
end


function goto_level(index)
function goto_level(index)
-- set level
-- set level
level,level_index,level_checkpoint=levels[index],index,nil
level,level_index,level_checkpoint=levels[index],index,nil


if level.title then
if level.title then
level_intro=60
level_intro=60
end
end


if level_index==2 then
if level_index==2 then
psfx(17,8,16)
psfx(17,8,16)
end
end


-- start music
-- start music
if current_music~=level.music and level.music then
if current_music~=level.music and level.music then
current_music=level.music
current_music=level.music
music(level.music)
music(level.music)
end
end
-- load level contents
-- load level contents
restart_level()
restart_level()
end
end


function next_level()
function next_level()
level_index+=1
level_index+=1
goto_level(level_index)
goto_level(level_index)
end
end


function restart_level()
function restart_level()
camera_x,camera_y,camera_target_x,camera_target_y,
camera_x,camera_y,camera_target_x,camera_target_y,
objects,
objects,
infade,have_grapple,sfx_timer=
infade,have_grapple,sfx_timer=
0,0,0,0,
0,0,0,0,
{},
{},
0,level_index>2,0
0,level_index>2,0


for i=0,level.width-1 do
for i=0,level.width-1 do
for j=0,level.height-1 do
for j=0,level.height-1 do
local t=types[tile_at(i,j)]
local t=types[tile_at(i,j)]
if t and not collected[id(i,j)] and (not level_checkpoint or t~=player) then
if t and not collected[id(i,j)] and (not level_checkpoint or t~=player) then
create(t,i*8,j*8)
create(t,i*8,j*8)
end
end
end
end
end
end
end
end


-- gets the tile at the given location from the loaded level
-- gets the tile at the given location from the loaded level
function tile_at(x,y)
function tile_at(x,y)
return x<0 or y<0 or x>=level.width or y>=level.height and 0 or mget(level.x+x,level.y+y)--peek(0x4300+x+y*level.width)
return x<0 or y<0 or x>=level.width or y>=level.height and 0 or mget(level.x+x,level.y+y)--peek(0x4300+x+y*level.width)
end
end


input_x,input_jump_pressed,input_grapple_pressed,axis_x_value=0,0,0,0
input_x,input_jump_pressed,input_grapple_pressed,axis_x_value=0,0,0,0


function update_input()
function update_input()
-- axes
-- axes
local prev_x=axis_x_value
local prev_x=axis_x_value
if btn(0) then
if btn(0) then
if btn(1) then
if btn(1) then
if axis_x_turned then
if axis_x_turned then
axis_x_value,input_x=prev_x,prev_x
axis_x_value,input_x=prev_x,prev_x
else
else
axis_x_turned,axis_x_value,input_x=true,-prev_x,-prev_x
axis_x_turned,axis_x_value,input_x=true,-prev_x,-prev_x
end
end
else
else
axis_x_turned,axis_x_value,input_x=false,-1,-1
axis_x_turned,axis_x_value,input_x=false,-1,-1
end
end
else
else
local b=btn(1) and 1 or 0
local b=btn(1) and 1 or 0
axis_x_turned,axis_x_value,input_x=false,b,b
axis_x_turned,axis_x_value,input_x=false,b,b
end
end
-- input_jump
-- input_jump
local jump=btn(4)
local jump=btn(4)
input_jump_pressed,input_jump=jump and not input_jump and 4 or jump and max(0,input_jump_pressed-1) or 0,jump
input_jump_pressed,input_jump=jump and not input_jump and 4 or jump and max(0,input_jump_pressed-1) or 0,jump
-- input_grapple
-- input_grapple
local grapple=btn(5)
local grapple=btn(5)
input_grapple_pressed,input_grapple=grapple and not input_grapple and 4 or grapple and max(0,input_grapple_pressed-1) or 0,grapple
input_grapple_pressed,input_grapple=grapple and not input_grapple and 4 or grapple and max(0,input_grapple_pressed-1) or 0,grapple
end
end


function consume_jump_press()
function consume_jump_press()
local val=input_jump_pressed>0
local val=input_jump_pressed>0
input_jump_pressed=0
input_jump_pressed=0
return val
return val
end
end


function consume_grapple_press()
function consume_grapple_press()
local val=input_grapple_pressed>0
local val=input_grapple_pressed>0
input_grapple_pressed=0
input_grapple_pressed=0
return val
return val
end
end


objects,types,lookup={},{},{}
objects,types,lookup={},{},{}
function lookup.__index(self, i) return self.base[i] end
function lookup.__index(self, i) return self.base[i] end


object = {
object = {
speed_x=0,
speed_x=0,
speed_y=0,
speed_y=0,
remainder_x=0,
remainder_x=0,
remainder_y=0,
remainder_y=0,
hit_x=0,
hit_x=0,
hit_y=0,
hit_y=0,
hit_w=8,
hit_w=8,
hit_h=8,
hit_h=8,
grapple_mode=0,
grapple_mode=0,
hazard=0,
hazard=0,
facing=1,
facing=1,
freeze=0
freeze=0
}
}


function object.move_x(self,x,on_collide)
function object.move_x(self,x,on_collide)
self.remainder_x+=x
self.remainder_x+=x
local mx=flr(self.remainder_x+0.5)
local mx=flr(self.remainder_x+0.5)
self.remainder_x-=mx
self.remainder_x-=mx


local total,mxs=mx,sgn(mx)
local total,mxs=mx,sgn(mx)
while mx~=0
while mx~=0
do
do
if self:check_solid(mxs,0) then
if self:check_solid(mxs,0) then
if on_collide then
if on_collide then
return on_collide(self,total-mx,total)
return on_collide(self,total-mx,total)
end
end
return true
return true
else
else
self.x+=mxs
self.x+=mxs
mx-=mxs
mx-=mxs
end
end
end
end
end
end


function object.move_y(self,y,on_collide)
function object.move_y(self,y,on_collide)
self.remainder_y+=y
self.remainder_y+=y
local my=flr(self.remainder_y+0.5)
local my=flr(self.remainder_y+0.5)
self.remainder_y-=my
self.remainder_y-=my
local total,mys=my,sgn(my)
local total,mys=my,sgn(my)
local mys=sgn(my)
local mys=sgn(my)
while my~=0
while my~=0
do
do
if self:check_solid(0,mys) then
if self:check_solid(0,mys) then
if on_collide then
if on_collide then
return on_collide(self,total-my,total)
return on_collide(self,total-my,total)
end
end
return true
return true
else
else
self.y+=mys
self.y+=mys
my-=mys
my-=mys
end
end
end
end
end
end


function object.on_collide_x(self,moved,target)
function object.on_collide_x(self,moved,target)
self.remainder_x,self.speed_x=0,0
self.remainder_x,self.speed_x=0,0
return true
return true
end
end


function object.on_collide_y(self,moved,target)
function object.on_collide_y(self,moved,target)
self.remainder_y,self.speed_y=0,0
self.remainder_y,self.speed_y=0,0
return true
return true
end
end


function object.update() end
function object.update() end


function object.draw(self)
function object.draw(self)
spr(self.spr,self.x,self.y,1,1,self.flip_x,self.flip_y)
spr(self.spr,self.x,self.y,1,1,self.flip_x,self.flip_y)
end
end


function object.overlaps(self,b,ox,oy)
function object.overlaps(self,b,ox,oy)
if self==b then return end
if self==b then return end
ox,oy=ox or 0,oy or 0
ox,oy=ox or 0,oy or 0
return
return
ox+self.x+self.hit_x+self.hit_w>b.x+b.hit_x and
ox+self.x+self.hit_x+self.hit_w>b.x+b.hit_x and
oy+self.y+self.hit_y+self.hit_h>b.y+b.hit_y and
oy+self.y+self.hit_y+self.hit_h>b.y+b.hit_y and
ox+self.x+self.hit_x<b.x+b.hit_x+b.hit_w and
ox+self.x+self.hit_x<b.x+b.hit_x+b.hit_w and
oy+self.y+self.hit_y<b.y+b.hit_y+b.hit_h
oy+self.y+self.hit_y<b.y+b.hit_y+b.hit_h
end
end


function object.contains(self,px,py)
function object.contains(self,px,py)
return
return
px>=self.x+self.hit_x and
px>=self.x+self.hit_x and
px<self.x+self.hit_x+self.hit_w and
px<self.x+self.hit_x+self.hit_w and
py>=self.y+self.hit_y and
py>=self.y+self.hit_y and
py<self.y+self.hit_y+self.hit_h
py<self.y+self.hit_y+self.hit_h
end
end


function object.check_solid(self, ox, oy)
function object.check_solid(self, ox, oy)
ox,oy=ox or 0,oy or 0
ox,oy=ox or 0,oy or 0
for i=(ox+self.x+self.hit_x)\8,(ox+self.x+self.hit_x+self.hit_w-1)\8 do
for i=(ox+self.x+self.hit_x)\8,(ox+self.x+self.hit_x+self.hit_w-1)\8 do
for j=tile_y(oy+self.y+self.hit_y),tile_y(oy+self.y+self.hit_y+self.hit_h-1) do
for j=tile_y(oy+self.y+self.hit_y),tile_y(oy+self.y+self.hit_y+self.hit_h-1) do
if fget(tile_at(i,j),1) then
if fget(tile_at(i,j),1) then
return true
return true
end
end
end
end
end
end
for o in all(objects) do
for o in all(objects) do
if o.solid and o~=self and not o.destroyed and self:overlaps(o, ox, oy) then
if o.solid and o~=self and not o.destroyed and self:overlaps(o, ox, oy) then
return true
return true
end
end
end
end
end
end


function object.corner_correct(self,dir_x,dir_y,side_dist,look_ahead,only_sign,func)
function object.corner_correct(self,dir_x,dir_y,side_dist,look_ahead,only_sign,func)
look_ahead,only_sign=look_ahead or 1,only_sign or 1
look_ahead,only_sign=look_ahead or 1,only_sign or 1
if dir_x~=0 then
if dir_x~=0 then
for i=1,side_dist do
for i=1,side_dist do
for s=1,-2,-2 do
for s=1,-2,-2 do
if s==-only_sign then
if s==-only_sign then
goto continue_x
goto continue_x
end
end
if not self:check_solid(dir_x,i*s) and (not func or func(self,dir_x,i*s)) then
if not self:check_solid(dir_x,i*s) and (not func or func(self,dir_x,i*s)) then
self.x+=dir_x
self.x+=dir_x
self.y+=i*s
self.y+=i*s
return true
return true
end
end
::continue_x::
::continue_x::
end
end
end
end
elseif dir_y~=0 then
elseif dir_y~=0 then
for i=1,side_dist do
for i=1,side_dist do
for s=1,-1,-2 do
for s=1,-1,-2 do
if s==-only_sign then
if s==-only_sign then
goto continue_y
goto continue_y
end
end
if not self:check_solid(i*s,dir_y) and (not func or func(self,i*s,dir_y)) then
if not self:check_solid(i*s,dir_y) and (not func or func(self,i*s,dir_y)) then
self.x+=i*s
self.x+=i*s
self.y+=dir_y
self.y+=dir_y
return true
return true
end
end
::continue_y::
::continue_y::
end
end
end
end
end
end
end
end


function id(tx,ty) return level_index*100+flr(tx)+flr(ty)*128 end
function id(tx,ty) return level_index*100+flr(tx)+flr(ty)*128 end


function create(type,x,y)
function create(type,x,y)
local obj=setmetatable({
local obj=setmetatable({
base=type,
base=type,
x=x,
x=x,
y=y,
y=y,
id=id(x\8,y\8)
id=id(x\8,y\8)
},lookup)
},lookup)
add(objects, obj);
add(objects, obj);
(obj.init or stat)(obj)
(obj.init or stat)(obj)
return obj
return obj
end
end


function new_type(spr)
function new_type(spr)
local obj=setmetatable({
local obj=setmetatable({
spr=spr,
spr=spr,
base=object
base=object
},lookup)
},lookup)
types[spr]=obj--add(types, obj)
types[spr]=obj--add(types, obj)
return obj
return obj
end
end




grapple_pickup=new_type(20)
grapple_pickup=new_type(20)
function grapple_pickup.draw(self)
function grapple_pickup.draw(self)
spr(self.spr,self.x,self.y+sin(time())*2,1,1,not self.right)
spr(self.spr,self.x,self.y+sin(time())*2,1,1,not self.right)
end
end


spike_v=new_type(36)
spike_v=new_type(36)
function spike_v.init(self)
function spike_v.init(self)
if not self:check_solid(0,1) then
if not self:check_solid(0,1) then
self.flip_y,self.hazard=true,3
self.flip_y,self.hazard=true,3
else
else
self.hit_y,self.hazard=5,2
self.hit_y,self.hazard=5,2
end
end
self.hit_h = 3
self.hit_h = 3
end
end


spike_h=new_type(37)
spike_h=new_type(37)
function spike_h.init(self)
function spike_h.init(self)
if self:check_solid(-1,0) then
if self:check_solid(-1,0) then
self.flip_x,self.hazard=true,4
self.flip_x,self.hazard=true,4
else
else
self.hit_x,self.hazard=5,5
self.hit_x,self.hazard=5,5
end
end
self.hit_w=3
self.hit_w=3
end
end


snowball=new_type(62)
snowball=new_type(62)
snowball.grapple_mode,snowball.thrown_timer,snowball.hp=3,0,6
snowball.grapple_mode,snowball.thrown_timer,snowball.hp=3,0,6
function snowball.update(self)
function snowball.update(self)
if not self.held then
if not self.held then
self.thrown_timer -= 1
self.thrown_timer -= 1
--speed
--speed
if self.stop then
if self.stop then
self.speed_x=approach(self.speed_x,0,0.25)
self.speed_x=approach(self.speed_x,0,0.25)
if self.speed_x==0 then
if self.speed_x==0 then
self.stop=false
self.stop=false
end
end
else
else
if self.speed_x~=0 then
if self.speed_x~=0 then
self.speed_x=approach(self.speed_x,sgn(self.speed_x)*2,0.1)
self.speed_x=approach(self.speed_x,sgn(self.speed_x)*2,0.1)
end
end
end
end


--gravity
--gravity
if not self:check_solid(0,1) then
if not self:check_solid(0,1) then
self.speed_y=approach(self.speed_y,4,0.4)
self.speed_y=approach(self.speed_y,4,0.4)
end
end


--apply
--apply
self:move_x(self.speed_x,self.on_collide_x)
self:move_x(self.speed_x,self.on_collide_x)
self:move_y(self.speed_y,self.on_collide_y)
self:move_y(self.speed_y,self.on_collide_y)


--bounds
--bounds
if self.y>level.height*8+24 then
if self.y>level.height*8+24 then
self.destroyed=true
self.destroyed=true
end
end
end
end
end
end
function snowball.on_collide_x(self,moved,total)
function snowball.on_collide_x(self,moved,total)
if self:corner_correct(sgn(self.speed_x),0,2,2,1) then
if self:corner_correct(sgn(self.speed_x),0,2,2,1) then
return
return
end
end
if self:hurt() then
if self:hurt() then
return true
return true
end
end
self.speed_x*=-1
self.speed_x*=-1
self.remainder_x,self.freeze=0,1
self.remainder_x,self.freeze=0,1
psfx(17,0,2)
psfx(17,0,2)
return true
return true
end
end
function snowball.on_collide_y(self)--,moved,total)
function snowball.on_collide_y(self)--,moved,total)
if self.speed_y>=4 then
if self.speed_y>=4 then
self.speed_y=-2
self.speed_y=-2
psfx(17,0,2)
psfx(17,0,2)
elseif self.speed_y>=1 then
elseif self.speed_y>=1 then
self.speed_y=-1
self.speed_y=-1
psfx(17,0,2)
psfx(17,0,2)
else
else
self.speed_y=0
self.speed_y=0
end
end
self.remainder_y=0
self.remainder_y=0
return true
return true
end
end
function snowball.on_release(self,thrown)
function snowball.on_release(self,thrown)
if not thrown then
if not thrown then
self.stop=true
self.stop=true
end
end
self.thrown_timer=8
self.thrown_timer=8
end
end
function snowball.hurt(self)
function snowball.hurt(self)
--self.hp-=1
--self.hp-=1
if self.hp<=0 then
if self.hp<=0 then
psfx(8,16,4)
psfx(8,16,4)
self.destroyed=true
self.destroyed=true
return true
return true
end
end
end
end
function snowball.bounce_overlaps(self,o)
function snowball.bounce_overlaps(self,o)
if self.speed_x~=0 then
if self.speed_x~=0 then
self.hit_w,self.hit_x=12,-2
self.hit_w,self.hit_x=12,-2
local ret=self:overlaps(o)
local ret=self:overlaps(o)
self.hit_w,self.hit_x=8,0
self.hit_w,self.hit_x=8,0
return ret
return ret
end
end
return self:overlaps(o)
return self:overlaps(o)
end
end
function snowball.contains(self,px,py)
function snowball.contains(self,px,py)
return px>=self.x and px<self.x+8 and py>=self.y-1 and py<self.y+10
return px>=self.x and px<self.x+8 and py>=self.y-1 and py<self.y+10
end
end
function snowball.draw(self)
function snowball.draw(self)
pal(7,1)
pal(7,1)
spr(self.spr,self.x,self.y+1)
spr(self.spr,self.x,self.y+1)
pal()
pal()
spr(self.spr,self.x,self.y)
spr(self.spr,self.x,self.y)
end
end


springboard=new_type(11)
springboard=new_type(11)
springboard.grapple_mode,springboard.thrown_timer=3,0
springboard.grapple_mode,springboard.thrown_timer=3,0
function springboard.update(self)
function springboard.update(self)
if not self.held then
if not self.held then
self.thrown_timer-=1
self.thrown_timer-=1
--friction and gravity
--friction and gravity
if self:check_solid(0,1) then
if self:check_solid(0,1) then
self.speed_x=approach(self.speed_x,0,1)
self.speed_x=approach(self.speed_x,0,1)
else
else
self.speed_x=approach(self.speed_x,0,0.2)
self.speed_x=approach(self.speed_x,0,0.2)
self.speed_y=approach(self.speed_y,4,0.4)
self.speed_y=approach(self.speed_y,4,0.4)
end
end
--apply
--apply
self:move_x(self.speed_x,self.on_collide_x)
self:move_x(self.speed_x,self.on_collide_x)
self:move_y(self.speed_y,self.on_collide_y)
self:move_y(self.speed_y,self.on_collide_y)
if self.player then
if self.player then
self.player:move_y(self.speed_y)
self.player:move_y(self.speed_y)
end
end
self.destroyed=self.y>level.height*8+24
self.destroyed=self.y>level.height*8+24
end
end
end
end
function springboard.on_collide_x(self,moved,total)
function springboard.on_collide_x(self,moved,total)
self.speed_x*=-0.2
self.speed_x*=-0.2
self.remainder_x,self.freeze=0,1
self.remainder_x,self.freeze=0,1
return true
return true
end
end
function springboard.on_collide_y(self,moved,total)
function springboard.on_collide_y(self,moved,total)
if self.speed_y<0 then
if self.speed_y<0 then
self.speed_y,self.remainder_y=0,0
self.speed_y,self.remainder_y=0,0
return true
return true
end
end
if self.speed_y>=2 then
if self.speed_y>=2 then
self.speed_y*=-0.4
self.speed_y*=-0.4
else
else
self.speed_y=0
self.speed_y=0
end
end
self.remainder_y=0
self.remainder_y=0
self.speed_x*=0.5
self.speed_x*=0.5
return true
return true
end
end
function springboard.on_release(self,thrown)
function springboard.on_release(self,thrown)
if thrown then
if thrown then
self.thrown_timer=5
self.thrown_timer=5
end
end
end
end


grappler=new_type(46)
grappler=new_type(46)
grappler.grapple_mode,grappler.hit_x,grappler.hit_y,grappler.hit_w,grappler.hit_h=2,-1,-1,10,10
grappler.grapple_mode,grappler.hit_x,grappler.hit_y,grappler.hit_w,grappler.hit_h=2,-1,-1,10,10


berry=new_type(21)
berry=new_type(21)
function berry.update(self)
function berry.update(self)
if self.collected then
if self.collected then
self.timer+=1
self.timer+=1
self.y-=0.2*(self.timer > 5 and 1 or 0)
self.y-=0.2*(self.timer > 5 and 1 or 0)
self.destroyed=self.timer > 30
self.destroyed=self.timer > 30
elseif self.player then
elseif self.player then
self.x+=(self.player.x-self.x)/8
self.x+=(self.player.x-self.x)/8
self.y+=(self.player.y-4-self.y)/8
self.y+=(self.player.y-4-self.y)/8
self.flash-=1
self.flash-=1


if self.player:check_solid(0,1) and self.player.state~=99 then self.ground+=1 else self.ground=0 end
if self.player:check_solid(0,1) and self.player.state~=99 then self.ground+=1 else self.ground=0 end


if self.ground>3 or self.player.x>level.width*8-7 or self.player.last_berry~=self then
if self.ground>3 or self.player.x>level.width*8-7 or self.player.last_berry~=self then
psfx(8,8,8,20)
psfx(8,8,8,20)
collected[self.id]=true
collected[self.id]=true
berry_count+=1
berry_count+=1
self.collected,self.timer,self.draw=true,0,score
self.collected,self.timer,self.draw=true,0,score
end
end
end
end
end
end
function berry.collect(self,player)
function berry.collect(self,player)