Skip to content

Instantly share code, notes, and snippets.

@egordorichev
Created July 31, 2017 06:43
Show Gist options
  • Select an option

  • Save egordorichev/5f96027c36214ca030f184852650ddcd to your computer and use it in GitHub Desktop.

Select an option

Save egordorichev/5f96027c36214ca030f184852650ddcd to your computer and use it in GitHub Desktop.

Revisions

  1. egordorichev created this gist Jul 31, 2017.
    917 changes: 917 additions & 0 deletions Spacy adventure
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,917 @@
    -- 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