Skip to content

Instantly share code, notes, and snippets.

@egordorichev
Created July 31, 2017 06:43
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save egordorichev/5f96027c36214ca030f184852650ddcd to your computer and use it in GitHub Desktop.
Save egordorichev/5f96027c36214ca030f184852650ddcd to your computer and use it in GitHub Desktop.
-- 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