Created
July 31, 2017 06:43
-
-
Save egordorichev/5f96027c36214ca030f184852650ddcd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- spacy adventure | |
-- by @egordorichev | |
-- | |
-- main callbacks | |
-- | |
cartdata("ld39") | |
t=1 | |
dmg=5 | |
hscore=dget(0) or 0 | |
function _init() | |
music(-1) | |
music(5,1) | |
-- init engine | |
record=false | |
drk=parse"0=0,0,1,1,2,1,5,13,2,4,9,3,1,1,2,4" | |
t,shx,shy,cx,cy=0,0,0,0,0 | |
menu=true -- \145\132\152\148\144\145\132 | |
nt=0 | |
objs=parse[[ | |
to_update={}, | |
collidable={}, | |
enemies={} | |
]] | |
for i=0,4 do | |
objs["to_draw"..i]={} | |
end | |
-- init game | |
prc={50,30,30} | |
local sc={1,1,13,13} | |
for i=0,30 do | |
local n=flr(rnd(#sc)+1) | |
def([[ | |
regs={to_draw1} | |
]],{ | |
x=rnd(128), | |
y=rnd(128), | |
c=sc[n], | |
vy=n*1.5, | |
draw=draw_star | |
}) | |
end | |
energy=60 | |
lenergy=60 | |
pdmg=1 | |
pimt=60 | |
local fcc={10,8,11,13,15} | |
local n=flr(rnd(#fcc))+1 | |
local fc=fcc[n] | |
local scc={9,2,3,1,14} | |
local sc=scc[n] | |
player=def([[ | |
at=0, | |
x=60, | |
f=0, | |
y=88, | |
pl=true, | |
w=8, | |
h=8, | |
st=0, | |
s=0.5, | |
t=0, | |
slc=11, | |
it=0, | |
vx=0, | |
vy=0, | |
health=3, | |
regs={to_update,to_draw3,collidable} | |
]],{ | |
update=update_player, | |
draw=draw_player, | |
fc=fc, | |
sc=sc | |
}) | |
stt=0 | |
end | |
function _update() | |
t=(t+1)%180 | |
if energy~=lenergy then | |
local d=(lenergy-energy) | |
lenergy-=d*0.1 | |
end | |
if menu then | |
for obj in all(objs.to_update) do | |
obj.update(obj) | |
end | |
-- update menu | |
if btn(4) then | |
menu=false | |
music(-1) | |
--music(0,1) | |
shake_screen(10) | |
energy=60 | |
score=0 | |
lenergy=energy | |
gameover=false | |
distance=0 | |
pdmg=1 | |
player.it=pimt | |
spef=false | |
cb="get ready!" | |
cbt=0 | |
t=0 | |
shop=false | |
end | |
return | |
end | |
-- update game | |
if abs(shx)+abs(shy)<0.5 then | |
shx,shy=0,0 | |
end | |
shx*=-0.7 | |
shy*=-0.7 | |
-- update objects | |
for obj in all(objs.to_update) do | |
obj.update(obj) | |
end | |
-- game stuff | |
if player.hidden then return end | |
if(not shop and not menu) energy-=0.03 | |
if energy<=0 then | |
energy=0 | |
-- todo: gameover | |
player.hidden=true | |
new_explosion(player.x+4,player.y+4) | |
return | |
end | |
if shop then | |
if btnp(0) or btnp(2) then | |
sae-=1 | |
if(sae<=0) sae=3 | |
sfx(10) | |
end | |
if btnp(1) or btnp(3) then | |
sae+=1 | |
if(sae>=4) sae=1 | |
sfx(10) | |
end | |
if btnp(4) then shop=false end | |
if btnp(5) then | |
if score<prc[sae] then | |
sfx(9) | |
else | |
sfx(7) | |
score-=prc[sae] | |
spawn_particles(player.x+4,player.y+4,8) | |
prc[sae]=flr(prc[sae]*1.8) | |
if sae==1 then | |
pimt+=30 | |
elseif sae==2 then | |
pdmg+=1 | |
elseif sae==3 then | |
player.s+=0.2 | |
player.pc=9 | |
end | |
end | |
end | |
return | |
end | |
if(not bsa and btnp(4)) sfx(10) sae=1 shop=true | |
stt=max(0,stt-1) | |
if gameover then return end | |
distance+=0.1 | |
if t>=40 and not spef then | |
spef=true | |
spawn_enemies() | |
end | |
--if bsa then return end | |
if t==0 then | |
nt=(nt+1)%60 | |
local r=rnd() | |
local x=rnd(116)+8 | |
if r>0.6 or (energy<21 and rnd()<0.6) then | |
new_pickup("bat",x,-8) | |
elseif not bsa and r>0.5 then | |
new_pickup("bmb",x,-8) | |
elseif not bsa and r>0.4 then | |
new_pickup("clk",x,-8) | |
else | |
new_pickup("sld",x,-8) | |
end | |
end | |
if not bsa and #objs.enemies==0 and spef then | |
spawn_enemies() | |
end | |
if #objs.enemies<6 and not bsa and t==0 and spef then | |
spawn_enemies(1) | |
end | |
end | |
function _draw() | |
cls() | |
if menu then | |
-- draw menu | |
draw_objects() | |
printc("spacy adventure",10) | |
printc("by @egordorichev",20) | |
printc("\139\145 - move ",40) | |
printc("\151 - shoot ",50) | |
printc("\142 - shop ",60) | |
text("press \142 to start",32,110) | |
return | |
end | |
camera(cx+shx,cy+shy) | |
-- draw game | |
draw_objects() | |
camera(0,0) | |
-- draw ui | |
print("energy",2,2,7) | |
print(score.." $",127-#(score.." $")*4,2,7) | |
print(flr(distance).." p",127-#(flr(distance).." p")*4,8,7) | |
if(max(lenergy,0)>0) rectfill(2,8,max(lenergy,0)+2,8,14) | |
if(max(energy,0)>0) rectfill(2,8,max(energy,0)+2,8,7) | |
if gameover then | |
printc("new score: "..flr(distance),30) | |
if record then printc("new record!",40) else printc("high score: "..hscore,40) end | |
printc("out of power!",70) | |
if(t%30>10) printc("press \142 to retry ",80) | |
if btnp(4) then sfx(10) _init() end | |
end | |
if shop then -- \145\148\136\142\132\145 | |
printc("shop",22) | |
-- dont forget to change if values here, if you change price | |
printc("upgrade shield "..prc[1].."$",50,sae==1 and 14 or (score>=prc[1] and 1 or 5)) | |
printc("upgrade cannon "..prc[2].."$",60,sae==2 and 14 or (score>=prc[2] and 1 or 5)) | |
printc("upgrade engine "..prc[3].."$",70,sae==3 and 14 or (score>=prc[3] and 1 or 5)) | |
printc("\151 - buy, \142 - exit ",90) | |
return | |
end | |
if not gameover and energy<11 | |
and t%30>15 then | |
printc("!!!low energy!!!",12,8) | |
end | |
if distance>10 and distance%50<1 and cb==nil then | |
cb="passed "..flr(distance).." p" | |
cbt=0 | |
end | |
if cb~=nil then | |
cbt+=1 | |
printc(cb,60) | |
if cbt>60 then | |
cbt=0 | |
if cb=="passed 150 p" then | |
cb="boss stage!" | |
cbt=0 | |
for e in all(objs.enemies) do | |
e.mva=true | |
e.tx=rnd()<0.5 and -30 or 150 | |
end | |
elseif cb=="boss stage!" then | |
cb=nil | |
new_enemy(4) | |
else cb=nil end | |
end | |
end | |
end | |
function printc(t,y,c) | |
text(t,(128-#t*4)/2,y,c) | |
end | |
-- | |
-- game stuff | |
-- | |
-- player | |
function update_player(p) | |
if gameover or shop then return end | |
if p.hidden then | |
gameover=true | |
sfx(1) | |
if flr(distance)>hscore then hscore=flr(distance) record=true end | |
dset(0,hscore) | |
cls(7) | |
flip() | |
flip() | |
return | |
end | |
p.st=max(0,p.st-1) | |
p.it=max(0,p.it-1) | |
if(p.it==0) p.slc=11 | |
if(btn(0)) p.vx-=p.s | |
if(btn(1)) p.vx+=p.s | |
if(btn(2)) p.vy-=p.s | |
if(btn(3)) p.vy+=p.s | |
if btnp(5) then | |
new_bullet(p.x,p.y) | |
p.st=2 | |
end | |
p.x+=p.vx | |
p.y+=p.vy | |
--if abs(p.vx)+abs(p.vy)<0.5 then | |
-- energy-=0.1 | |
--end | |
p.vx*=0.8 | |
p.vy*=0.8 | |
if p.x<0 then p.x=0 end | |
if p.x>120 then p.x=120 end | |
if p.y<0 then p.y=0 end | |
if p.y>120 then p.y=120 end | |
--p.at=(p.at+1)%5 | |
--if(p.at==0) p.f=(p.f+1)%3 | |
p.t=(p.t+1)%2 | |
if p.t==0 then | |
new_particle(p.x+2+rnd(3),p.y+6+rnd(2),p.pc or 2,0,rnd(2),2) | |
end | |
end | |
function draw_player(p) | |
if p.it%4>1 and p.it<60 then | |
for i=0,15 do | |
pal(i,7) | |
end | |
else | |
pal(8,p.fc) | |
pal(2,p.sc) | |
end | |
spr(48,p.x,p.y) | |
if p.it%4>1 and p.it<60 then | |
for i=0,15 do | |
pal(i,i) | |
end | |
else | |
pal(8,8) | |
pal(2,2) | |
end | |
if p.st>0 then spr(3,p.x,p.y-4) end | |
if p.it>0 then | |
for i=0,15 do | |
circ(sin(i/15+t/90)*10+player.x+4, | |
cos(i/15+t/90)*10+player.y+4,1,p.slc) | |
end | |
end | |
end | |
-- enemies | |
function spawn_enemies(c) | |
lst={} | |
if(distance<75) add(lst,1) | |
if(distance>=50) add(lst,2) | |
if(distance>=160) add(lst,3) | |
for i=0,(c or 3)-1 do | |
new_enemy(lst[flr(rnd(#lst))+1]) | |
end | |
end | |
function new_enemy(t) | |
t=t or 1 | |
local boss=false | |
if t==1 then | |
dmg=dmg | |
h=1 | |
s=1 | |
sp=rnd()>0.5 and 16 or 32 | |
shp=20 | |
up=function(e) | |
e.tx=rnd(128) | |
e.ty=10+rnd(48) | |
end | |
elseif t==2 then | |
dmg=dmg+2 | |
h=2 | |
s=0.7 | |
sp=rnd()>0.5 and 18 or 34 | |
shp=14 | |
up=function(e) | |
if abs(e.x-player.x)<10 then | |
e.tx=20+rnd(108) | |
else | |
e.tx=player.x-8+rnd(16) | |
end | |
e.ty=10+rnd(48) | |
end | |
elseif t==3 then | |
dmg=dmg+3 | |
h=3 | |
s=0.7 | |
sp=rnd()<0.5 and 20 or 37 | |
shp=14 | |
up=function(e) | |
if abs(e.x-player.x)<10 then | |
e.tx=20+rnd(108) | |
else | |
e.tx=player.x-8+rnd(16) | |
end | |
e.ty=e.y | |
end | |
elseif t==4 then | |
-- boss! \132\132\148\136\150\132\150 | |
dmg=dmg+2 | |
lbt=t | |
h=10 | |
s=0.6 | |
sp=6 | |
shp=36 | |
boss=true | |
he=16 | |
we=16 | |
bsa=true | |
up=function(e) | |
if abs(e.x-player.x)<10 then | |
e.tx=20+rnd(108) | |
else | |
e.tx=player.x-8+rnd(16) | |
end | |
e.ty=e.y | |
if e.rt%shp==0 and rnd()<0.5 then | |
new_bullet(e.x+4,e.y+4,-1,e) | |
e.st=2 | |
end | |
if (t-lbt)<30 then | |
lbt=t | |
new_pickup("bmb",e.x,e.y) | |
end | |
end | |
end | |
local d=1 | |
if(rnd()<0.5) d=-1 | |
return def([[ | |
en=true, | |
it=0, | |
t=0, | |
af=0, | |
at=0, | |
st=0, | |
rt=0, | |
regs={enemies,to_update,to_draw3,collidable} | |
]],{ | |
x=rnd(128), | |
d=d, | |
health=h, | |
s=s, | |
w=we or 8, | |
h=he or 8, | |
boss=boss or false, | |
dmg=dmg, | |
sp=sp, | |
y=-28, | |
up=up, | |
tx=rnd(128), | |
ty=8, | |
r=rnd(20)+40, | |
update=update_enemy, | |
draw=draw_enemy, | |
shp=shp | |
}) | |
end | |
function update_enemy(e) | |
e.st=max(0,e.st-1) | |
e.it=max(0,e.it-1) | |
e.t=(e.t+1)%2 | |
e.rt=(e.rt+1)%60 | |
if(stt~=0 or shop) return | |
if e.rt%shp==0 and rnd()<0.5 then | |
if e.boss then new_bullet(e.x+4,e.y+4,-1,e) | |
else new_bullet(e.x,e.y,-1,e) end | |
e.st=2 | |
end | |
if e.t==0 then | |
new_particle(e.x+2+rnd(3),e.y-1+rnd(2),2,0,-rnd(2),2) | |
end | |
if not mva and abs(e.x-e.tx)+abs(e.y-e.ty)<3 then | |
e.up(e) | |
else | |
e.x+=mid(e.s,e.tx-e.x,-e.s) | |
e.y+=mid(e.s,e.ty-e.y,-e.s) | |
end | |
if e.x<-10 or e.x>130 then | |
deregister(e) | |
return | |
end | |
e.at=(e.at+1)%8 | |
if(e.at==0) e.af=(e.af+1)%2 | |
if not player.hidden and collide(e.x,e.y,e.w,e.h, | |
player.x,player.y,player.w,player.h) then | |
shake_screen(4) | |
if player.it>0 and not e.boss then | |
e.health=0 | |
deregister(e) | |
new_explosion(e.x+4,e.y+4) | |
score+=5 | |
else | |
energy=0 | |
end | |
end | |
end | |
function draw_enemy(e) | |
if e.it%4>1 then | |
for i=0,15 do | |
pal(i,7) | |
end | |
end | |
if e.boss then | |
sspr(48+e.af*16,0,16,16,e.x,e.y) | |
else | |
spr(e.sp+e.af,e.x,e.y) | |
end | |
if e.it%4>1 then | |
for i=0,15 do | |
pal(i,i) | |
end | |
end | |
if e.boss then | |
if(e.st>0) spr(0,e.x+4,e.y+12) | |
else | |
if(e.st>0) spr(0,e.x,e.y+4) | |
end | |
end | |
-- bullets | |
function new_bullet(x,y,s,b) | |
sfx(2) | |
if(not b and not menu) energy-=1 | |
return def([[ | |
regs={to_update,to_draw2} | |
]],{ | |
x=x, | |
y=y, | |
dmg=b and b.dmg or pdmg, | |
vy=-9*(s or 1), | |
d=s or 1, | |
bad=b or false, | |
update=update_bullet, | |
draw=draw_bullet | |
}) | |
end | |
function update_bullet(b) | |
if b.hidden or shop then return end | |
b.y+=b.vy | |
if b.x<=-8 or b.x>=136 | |
or b.y<=-8 or b.y>=136 then | |
deregister(b) | |
b.hidden=true | |
sfx(8) | |
for i=0,5 do | |
new_particle(b.x+4,b.y+10,rnd(2)+9,rnd(2)-1,rnd(1)+0.5) | |
end | |
return | |
end | |
for o in all(objs.collidable) do | |
if o.en and not b.bad or | |
o.pl and b.bad then | |
if collide(o.x,o.y,o.w,o.h, | |
b.x+3,b.y+1,2,5) then | |
deregister(b) | |
b.hidden=true | |
shake_screen(4) | |
sfx(8) | |
if o.en then | |
if o.it==0 then | |
o.health-=b.dmg | |
o.it=15 | |
if o.health<=0 then | |
if(o.boss) shake_screen(4) bsa=false cb="boss defeated!" cbt=0 | |
deregister(o) | |
new_explosion(b.x+4,b.y+4) | |
if(not b.bad) score+=5 | |
end | |
end -- todo: feed back | |
else | |
if o.it==0 then | |
energy-=b.dmg | |
o.it=pimt | |
end -- todo: feed back | |
end | |
end | |
end | |
end | |
end | |
function draw_bullet(b) | |
spr(1,b.x,b.y) | |
--print(b.dmg,b.x,b.y,7) | |
if(t%3) new_particle(b.x+3+rnd(2),b.y+4+b.d*4,9,0,b.d*rnd(2),1) | |
end | |
-- pickups | |
function new_pickup(t,x,y) | |
if t=="bat" then | |
s=2 | |
elseif t=="sld" then | |
s=4 | |
elseif t=="clk" then | |
s=5 | |
elseif t=="bmb" then | |
s=36 | |
end | |
return def([[ | |
regs={to_draw2}, | |
w=8, | |
h=8 | |
]],{ | |
t=t, | |
s=s, | |
x=x, | |
y=y, | |
draw=draw_pickup | |
}) | |
end | |
function draw_pickup(p) | |
if shop then return end | |
p.y+=2 | |
if (p.y>136) deregister(p) return | |
if not player.hidden and collide(p.x,p.y,p.w,p.h, | |
player.x,player.y,player.w,player.h) then | |
deregister(p) | |
spawn_particles(p.x+4,p.y+4,10) | |
sfx(7) | |
if p.t=="bat" then | |
energy=min(energy+40,60) | |
elseif p.t=="sld" then | |
player.it=90+pimt | |
player.slc=12 | |
elseif p.t=="clk" then | |
stt=180 | |
elseif p.t=="bmb" then | |
energy-=dmg*2 | |
new_explosion(player.x+4,player.y+4) | |
end | |
end | |
if(t%2) new_particle(p.x+3+rnd(2),p.y-1,7,0,-rnd(2),2) | |
spr(p.s,p.x,p.y) | |
end | |
-- explosions | |
function new_explosion(x,y) | |
sfx(3) | |
spawn_particles(x,y) | |
for i=0,5 do | |
new_particle(x,y,rnd(2)+5,rnd(10)-5,rnd(10)-5,2) | |
end | |
for i=0,5 do | |
new_particle(x,y,5,rnd(5)-2.5,rnd(5)-2.5,rnd(2)+4) | |
end | |
return def([[ | |
regs={to_draw4}, | |
r=0 | |
]],{ | |
x=x, | |
y=y, | |
draw=draw_explosion | |
}) | |
end | |
function draw_explosion(e) | |
e.r+=2--todo: better way | |
if(e.r>=29) deregister(e) | |
circ(e.x,e.y,e.r,min(e.r/10+8,10)) | |
end | |
-- particles | |
function new_particle(x,y,cl,vx,vy,r) | |
return def([[ | |
regs={to_draw4} | |
]],{ | |
draw=draw_particle, | |
x=x, | |
y=y, | |
vx=vx or (rnd(2)-1), | |
vy=vy or (rnd(2)-1-1), | |
r=r or (rnd(4)+2), | |
cl=cl or rnd(16) | |
}) | |
end | |
function draw_particle(p) | |
p.x+=p.vx | |
p.y+=p.vy | |
p.vx*=0.95 | |
p.vy*=0.95 | |
p.r-=0.1 | |
circfill(p.x,p.y,p.r,p.cl) | |
if p.r<=0 then | |
deregister(p) | |
end | |
end | |
function spawn_particles(x,y,c) | |
for i=0,5+rnd(10) do | |
new_particle(x,y,c or (rnd(2)+5)) | |
end | |
end | |
-- stars | |
function draw_star(s) | |
s.y+=s.vy | |
if s.y>128 then | |
s.x=rnd(128) | |
s.y=-1 | |
end | |
rect(s.x,s.y,s.x,s.y+s.vy/1.5,s.c) | |
end | |
-- | |
-- engine | |
-- | |
-- graphics | |
function darken(double,scrn) | |
if double then | |
for i=0,15 do | |
pal(i,drk[drk[i]],scrn) | |
end | |
else | |
for i=0,15 do | |
pal(i,drk[i],scrn) | |
end | |
end | |
end | |
function darkout() | |
darken(false,1) | |
flip() | |
flip() | |
darken(true,1) | |
flip() | |
flip() | |
cls() | |
flip() | |
flip() | |
end | |
function darkin() | |
_draw() | |
flip() | |
flip() | |
darken(false,1) | |
flip() | |
flip() | |
for i=0,15 do | |
pal(i,i,1) | |
end | |
end | |
function shake_screen(am) | |
local a=rnd() | |
shx=am*cos(a or 1) | |
shy=am*sin(a or 1) | |
end | |
function text(s,x,y,c) | |
for ty=y+2,y-1,-1 do | |
for tx=x-1,x+1 do | |
print(s,tx,ty,ty==y+2 and 6 or 7) | |
end | |
end | |
print(s,x,y,c or 1) | |
end | |
-- objects | |
function draw_objects() | |
for i=0,4 do | |
local dobjs=objs["to_draw"..i] | |
-- sort by y | |
for i=2,#dobjs do | |
if dobjs[i-1].y>dobjs[i].y then | |
local k=i | |
while(k>1 and dobjs[k-1].y>dobjs[k].y) do | |
local s=dobjs[k] | |
dobjs[k],dobjs[k-1]=dobjs[k-1],s | |
k-=1 | |
end | |
end | |
end | |
for obj in all(dobjs) do | |
if not obj.hidden then | |
obj.draw(obj) | |
end | |
end | |
end | |
end | |
-- collisions | |
function collide(x1,y1,w1,h1, x2,y2,w2,h2) | |
return x1<x2+w2 and | |
x2<x1+w1 and | |
y1<y2+h2 and | |
y2<y1+h1 | |
end | |
function collide_map(o) -- 70 tokens! | |
local x1=o.x//8 | |
local y1=o.y//8 | |
local x2=(o.x+7)//8 | |
local y2=(o.y+7)//8 | |
local a=fget(mget(x1,y1),0) | |
local b=fget(mget(x1,y2),0) | |
local c=fget(mget(x2,y2),0) | |
local d=fget(mget(x2,y1),0) | |
return a or b or c or d | |
end | |
-- objects | |
function register(o) | |
for r in all(o.regs) do | |
add(objs[r],o) | |
end | |
end | |
function deregister(o) | |
for r in all(o.regs) do | |
del(objs[r],o) | |
end | |
end | |
-- string tricks (~300 tokens) | |
nums={} | |
for i=0,9 do nums[""..i]=true end | |
function parse(str,ar) | |
local c,lc,ar,field=1,1,{} | |
while c<=#str do | |
local char=sub(str,c,c) | |
if char=='{' then | |
local sc,k=c+1,0 | |
while sub(str,c,c)~='}' or k>1 do | |
char=sub(str,c,c) | |
if char=='{' then k+=1 | |
elseif char=='}' then k-=1 end | |
c+=1 | |
end | |
local v=parse(sub(str,sc,c-1)) | |
if field then | |
ar[field]=v | |
else | |
add(ar,v) | |
end | |
lc=c+2 | |
c+=1 | |
elseif char=='=' then | |
field,lc=sub(str,lc,c-1),c+1 | |
elseif char==',' or c==#str then | |
if c==#str then c+=1 end | |
local v,vb=sub(str,lc,c-1),sub(str,lc+1,c-1) | |
local fc=sub(v,1,1) | |
if nums[fc] then v=v*1 | |
elseif fc=='%' then v=rnd(vb) | |
elseif v=='true' then v=true | |
elseif v=='false' then v=false | |
end | |
if field then | |
if nums[sub(field,1,1)] then field=field*1 end | |
ar[field]=v | |
else | |
add(ar,v) | |
end | |
field,lc=nil,c+1 | |
elseif char=='\n' then | |
lc+=1 | |
end | |
c+=1 | |
end | |
return ar | |
end | |
function merge(a,b) | |
for k,v in pairs(b) do | |
a[k]=v | |
end | |
return a | |
end | |
function def(s,p) | |
local o=merge(parse(s),p) | |
register(o) | |
return o | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment