Note: This is not the official code. This is a shortened version of the code that uses 5932 of the 8192 tokens of code made by AFBU. I speedrun this game so I am quite knowledgable about it and from what i can tell there is no difference other than the code is shorter. This is handy if you need extra room in the editor for modding the game. Hope you enjoy. C: -Cupps -- ~celeste~ -- matt thorson + noel berry -- globals -- ------------- room = { x=0, y=0 } objects = {} types = {} freeze=0 shake=0 --will_restart=false delay_restart=0 got_fruit={} --has_dashed=false sfx_timer=0 has_key=false pause_player=false flash_bg=false music_timer=0 k_left=0 k_right=1 k_up=2 k_down=3 k_jump=4 k_dash=5 -- entry point -- ----------------- function _init() title_screen() end function title_screen() got_fruit = {} for i=0,29 do add(got_fruit,false) end frames=0 deaths=0 max_djump=1 start_game=false start_game_flash=0 music(40,0,7) load_room(7,3) end function begin_game() frames=0 seconds=0 minutes=0 music_timer=0 start_game=false music(0,0,7) load_room(0,0) end function level_index() return room.x%8+room.y*8 end function is_title() return level_index()==31 end -- effects -- ------------- clouds = {} for i=0,16 do add(clouds,{ x=rnd(128), y=rnd(128), spd=1+rnd(4), w=32+rnd(32) }) end particles = {} for i=0,24 do add(particles,{ x=rnd(128), y=rnd(128), s=0+flr(rnd(5)/4), spd=0.25+rnd(5), off=rnd(1), c=6+flr(0.5+rnd(1)) }) end dead_particles = {} -- player entity -- ------------------- player = { init=function(this) this.p_jump=false this.p_dash=false this.grace=0 this.jbuffer=0 this.djump=max_djump this.dash_time=0 this.dash_effect_time=0 this.dash_target={x=0,y=0} this.dash_accel={x=0,y=0} this.hitbox = {x=1,y=3,w=6,h=5} this.spr_off=0 this.was_on_ground=false create_hair(this) end, update=function(this) if (pause_player) return local input = btn(k_right) and 1 or (btn(k_left) and -1 or 0) -- spikes collide if spikes_at(this.x+this.hitbox.x,this.y+this.hitbox.y,this.hitbox.w,this.hitbox.h,this.spd.x,this.spd.y) then kill_player(this) end -- bottom death if this.y>128 then kill_player(this) end local on_ground=this.is_solid(0,1) local on_ice=this.is_ice(0,1) -- smoke particles if on_ground and not this.was_on_ground then init_object(smoke,this.x,this.y+4) end local jump = btn(k_jump) and not this.p_jump this.p_jump = btn(k_jump) if (jump) then this.jbuffer=4 elseif this.jbuffer>0 then this.jbuffer-=1 end local dash = btn(k_dash) and not this.p_dash this.p_dash = btn(k_dash) if on_ground then this.grace=6 if this.djump 0 then this.grace-=1 end this.dash_effect_time -=1 if this.dash_time > 0 then init_object(smoke,this.x,this.y) this.dash_time-=1 this.spd.x=appr(this.spd.x,this.dash_target.x,this.dash_accel.x) this.spd.y=appr(this.spd.y,this.dash_target.y,this.dash_accel.y) else -- move local maxrun=1 local accel=0.6 local deccel=0.15 if not on_ground then accel=0.4 elseif on_ice then accel=0.05 if input==(this.flip.x and -1 or 1) then accel=0.05 end end if abs(this.spd.x) > maxrun then this.spd.x=appr(this.spd.x,sign(this.spd.x)*maxrun,deccel) else this.spd.x=appr(this.spd.x,input*maxrun,accel) end --facing if this.spd.x!=0 then this.flip.x=(this.spd.x<0) end -- gravity local maxfall=2 local gravity=0.21 if abs(this.spd.y) <= 0.15 then gravity*=0.5 end -- wall slide if input!=0 and this.is_solid(input,0) and not this.is_ice(input,0) then maxfall=0.4 if rnd(10)<2 then init_object(smoke,this.x+input*6,this.y) end end if not on_ground then this.spd.y=appr(this.spd.y,maxfall,gravity) end -- jump if this.jbuffer>0 then if this.grace>0 then -- normal jump psfx(1) this.jbuffer=0 this.grace=0 this.spd.y=-2 init_object(smoke,this.x,this.y+4) else -- wall jump local wall_dir=(this.is_solid(-3,0) and -1 or this.is_solid(3,0) and 1 or 0) if wall_dir!=0 then psfx(2) this.jbuffer=0 this.spd.y=-2 this.spd.x=-wall_dir*(maxrun+1) if not this.is_ice(wall_dir*3,0) then init_object(smoke,this.x+wall_dir*6,this.y) end end end end -- dash local d_full=5 local d_half=d_full*0.70710678118 if this.djump>0 and dash then init_object(smoke,this.x,this.y) this.djump-=1 this.dash_time=4 has_dashed=true this.dash_effect_time=10 local v_input=(btn(k_up) and -1 or (btn(k_down) and 1 or 0)) if input!=0 then if v_input!=0 then this.spd.x=input*d_half this.spd.y=v_input*d_half else this.spd.x=input*d_full this.spd.y=0 end elseif v_input!=0 then this.spd.x=0 this.spd.y=v_input*d_full else this.spd.x=(this.flip.x and -1 or 1) this.spd.y=0 end psfx(3) freeze=2 shake=6 this.dash_target.x=2*sign(this.spd.x) this.dash_target.y=2*sign(this.spd.y) this.dash_accel.x=1.5 this.dash_accel.y=1.5 if this.spd.y<0 then this.dash_target.y*=.75 end if this.spd.y!=0 then this.dash_accel.x*=0.70710678118 end if this.spd.x!=0 then this.dash_accel.y*=0.70710678118 end elseif dash and this.djump<=0 then psfx(9) init_object(smoke,this.x,this.y) end end -- animation this.spr_off+=0.25 if not on_ground then if this.is_solid(input,0) then this.spr=5 else this.spr=3 end elseif btn(k_down) then this.spr=6 elseif btn(k_up) then this.spr=7 elseif (this.spd.x==0) or (not btn(k_left) and not btn(k_right)) then this.spr=1 else this.spr=1+this.spr_off%4 end -- next level if this.y<-4 and level_index()<30 then next_room() end -- was on the ground this.was_on_ground=on_ground end, --121 then this.x=clamp(this.x,-1,121) this.spd.x=0 end set_hair_color(this.djump) draw_hair(this,this.flip.x and -1 or 1) spr(this.spr,this.x,this.y,1,1,this.flip.x,this.flip.y) unset_hair_color() end } psfx=function(num) if sfx_timer<=0 then sfx(num) end end create_hair=function(obj) obj.hair={} for i=0,4 do add(obj.hair,{x=obj.x,y=obj.y,size=max(1,min(2,3-i))}) end end set_hair_color=function(djump) pal(8,(djump==1 and 8 or djump==2 and (7+flr((frames/3)%2)*4) or 12)) end draw_hair=function(obj,facing) local last={x=obj.x+4-facing*2,y=obj.y+(btn(k_down) and 4 or 3)} foreach(obj.hair,function(h) h.x+=(last.x-h.x)/1.5 h.y+=(last.y+0.5-h.y)/1.5 circfill(h.x,h.y,h.size,8) last=h end) end unset_hair_color=function() pal(8,8) end player_spawn = { tile=1, init=function(this) sfx(4) this.spr=3 this.target= {x=this.x,y=this.y} this.y=128 this.spd.y=-4 this.state=0 this.delay=0 this.solids=false create_hair(this) end, update=function(this) -- jumping up if this.state==0 then if this.y < this.target.y+16 then this.state=1 this.delay=3 end -- falling elseif this.state==1 then this.spd.y+=0.5 if this.spd.y>0 and this.delay>0 then this.spd.y=0 this.delay-=1 end if this.spd.y>0 and this.y > this.target.y then this.y=this.target.y this.spd = {x=0,y=0} this.state=2 this.delay=5 shake=5 init_object(smoke,this.x,this.y+4) sfx(5) end -- landing elseif this.state==2 then this.delay-=1 this.spr=6 if this.delay<0 then destroy_object(this) init_object(player,this.x,this.y) end end end, draw=function(this) set_hair_color(max_djump) draw_hair(this,1) spr(this.spr,this.x,this.y,1,1,this.flip.x,this.flip.y) unset_hair_color() end } add(types,player_spawn) spring = { tile=18, init=function(this) this.hide_in=0 this.hide_for=0 end, update=function(this) if this.hide_for>0 then this.hide_for-=1 if this.hide_for<=0 then this.spr=18 this.delay=0 end elseif this.spr==18 then local hit = this.collide(player,0,0) if hit ~=nil and hit.spd.y>=0 then this.spr=19 hit.y=this.y-4 hit.spd.x*=0.2 hit.spd.y=-3 hit.djump=max_djump this.delay=10 init_object(smoke,this.x,this.y) -- breakable below us local below=this.collide(fall_floor,0,1) if below~=nil then break_fall_floor(below) end psfx(8) end elseif this.delay>0 then this.delay-=1 if this.delay<=0 then this.spr=18 end end -- begin hiding if this.hide_in>0 then this.hide_in-=1 if this.hide_in<=0 then this.hide_for=60 this.spr=0 end end end } add(types,spring) function break_spring(obj) obj.hide_in=15 end balloon = { tile=22, init=function(this) this.offset=rnd(1) this.start=this.y this.timer=0 this.hitbox={x=-1,y=-1,w=10,h=10} end, update=function(this) if this.spr==22 then this.offset+=0.01 this.y=this.start+sin(this.offset)*2 local hit = this.collide(player,0,0) if hit~=nil and hit.djump0 then this.timer-=1 else psfx(7) init_object(smoke,this.x,this.y) this.spr=22 end end, draw=function(this) if this.spr==22 then spr(13+(this.offset*8)%3,this.x,this.y+6) spr(this.spr,this.x,this.y) end end } add(types,balloon) fall_floor = { tile=23, init=function(this) this.state=0 this.solid=true end, update=function(this) -- idling if this.state == 0 then if this.check(player,0,-1) or this.check(player,-1,0) or this.check(player,1,0) then break_fall_floor(this) end -- shaking elseif this.state==1 then this.delay-=1 if this.delay<=0 then this.state=2 this.delay=60--how long it hides for this.collideable=false end -- invisible, waiting to reset elseif this.state==2 then this.delay-=1 if this.delay<=0 and not this.check(player,0,0) then psfx(7) this.state=0 this.collideable=true init_object(smoke,this.x,this.y) end end end, draw=function(this) if this.state!=2 then if this.state!=1 then spr(23,this.x,this.y) else spr(23+(15-this.delay)/5,this.x,this.y) end end end } add(types,fall_floor) function break_fall_floor(obj) if obj.state==0 then psfx(15) obj.state=1 obj.delay=15--how long until it falls init_object(smoke,obj.x,obj.y) local hit=obj.collide(spring,0,-1) if hit~=nil then break_spring(hit) end end end smoke={ init=function(this) this.spr=29 this.spd.y=-0.1 this.spd.x=0.3+rnd(0.2) this.x+=-1+rnd(2) this.y+=-1+rnd(2) this.flip.x=maybe() this.flip.y=maybe() this.solids=false end, update=function(this) this.spr+=0.2 if this.spr>=32 then destroy_object(this) end end } fruit={ tile=26, if_not_fruit=true, init=function(this) this.start=this.y this.off=0 end, update=function(this) local hit=this.collide(player,0,0) if hit~=nil then hit.djump=max_djump sfx_timer=20 sfx(13) got_fruit[1+level_index()] = true init_object(lifeup,this.x,this.y) destroy_object(this) end this.off+=1 this.y=this.start+sin(this.off/40)*2.5 end } add(types,fruit) fly_fruit={ tile=28, if_not_fruit=true, init=function(this) this.start=this.y this.fly=false this.step=0.5 this.solids=false this.sfx_delay=8 end, update=function(this) --fly away if this.fly then if this.sfx_delay>0 then this.sfx_delay-=1 if this.sfx_delay<=0 then sfx_timer=20 sfx(14) end end this.spd.y=appr(this.spd.y,-3.5,0.25) if this.y<-16 then destroy_object(this) end -- wait else if has_dashed then this.fly=true end this.step+=0.05 this.spd.y=sin(this.step)*0.5 end -- collect local hit=this.collide(player,0,0) if hit~=nil then hit.djump=max_djump sfx_timer=20 sfx(13) got_fruit[1+level_index()] = true init_object(lifeup,this.x,this.y) destroy_object(this) end end, draw=function(this) local off=0 if not this.fly then local dir=sin(this.step) if dir<0 then off=1+max(0,sign(this.y-this.start)) end else off=(off+0.25)%3 end spr(45+off,this.x-6,this.y-2,1,1,true,false) spr(this.spr,this.x,this.y) spr(45+off,this.x+6,this.y-2) end } add(types,fly_fruit) lifeup = { init=function(this) this.spd.y=-0.25 this.duration=30 this.x-=2 this.y-=4 this.flash=0 this.solids=false end, update=function(this) this.duration-=1 if this.duration<= 0 then destroy_object(this) end end, draw=function(this) this.flash+=0.5 print("1000",this.x-2,this.y,7+this.flash%2) end } fake_wall = { tile=64, if_not_fruit=true, update=function(this) this.hitbox={x=-1,y=-1,w=18,h=18} local hit = this.collide(player,0,0) if hit~=nil and hit.dash_effect_time>0 then hit.spd.x=-sign(hit.spd.x)*1.5 hit.spd.y=-1.5 hit.dash_time=-1 sfx_timer=20 sfx(16) destroy_object(this) init_object(smoke,this.x,this.y) init_object(smoke,this.x+8,this.y) init_object(smoke,this.x,this.y+8) init_object(smoke,this.x+8,this.y+8) init_object(fruit,this.x+4,this.y+4) end this.hitbox={x=0,y=0,w=16,h=16} end, draw=function(this) spr(64,this.x,this.y) spr(65,this.x+8,this.y) spr(80,this.x,this.y+8) spr(81,this.x+8,this.y+8) end } add(types,fake_wall) key={ tile=8, if_not_fruit=true, update=function(this) local was=flr(this.spr) this.spr=9+(sin(frames/30)+0.5)*1 local is=flr(this.spr) if is==10 and is!=was then this.flip.x=not this.flip.x end if this.check(player,0,0) then sfx(23) sfx_timer=10 destroy_object(this) has_key=true end end } add(types,key) chest={ tile=20, if_not_fruit=true, init=function(this) this.x-=4 this.start=this.x this.timer=20 end, update=function(this) if has_key then this.timer-=1 this.x=this.start-1+rnd(3) if this.timer<=0 then sfx_timer=20 sfx(16) init_object(fruit,this.x,this.y-4) destroy_object(this) end end end } add(types,chest) platform={ init=function(this) this.x-=4 this.solids=false this.hitbox.w=16 this.last=this.x end, update=function(this) this.spd.x=this.dir*0.65 if this.x<-16 then this.x=128 elseif this.x>128 then this.x=-16 end if not this.check(player,0,0) then local hit=this.collide(player,0,-1) if hit~=nil then hit.move_x(this.x-this.last,1) end end this.last=this.x end, draw=function(this) spr(11,this.x,this.y-1) spr(12,this.x+8,this.y-1) end } message={ tile=86, last=0, draw=function(this) this.text="-- celeste mountain --#this memorial to those# perished on the climb" if this.check(player,4,0) then if this.index<#this.text then this.index+=0.5 if this.index>=this.last+1 then this.last+=1 sfx(35) end end this.off={x=8,y=96} for i=1,this.index do if sub(this.text,i,i)~="#" then rectfill(this.off.x-2,this.off.y-2,this.off.x+7,this.off.y+6 ,7) print(sub(this.text,i,i),this.off.x,this.off.y,0) this.off.x+=5 else this.off.x=8 this.off.y+=7 end end else this.index=0 this.last=0 end end } add(types,message) big_chest={ tile=96, init=function(this) this.state=0 this.hitbox.w=16 end, draw=function(this) if this.state==0 then local hit=this.collide(player,0,8) if hit~=nil and hit.is_solid(0,1) then music(-1,500,7) sfx(37) pause_player=true hit.spd.x=0 hit.spd.y=0 this.state=1 init_object(smoke,this.x,this.y) init_object(smoke,this.x+8,this.y) this.timer=60 this.particles={} end spr(96,this.x,this.y) spr(97,this.x+8,this.y) elseif this.state==1 then this.timer-=1 shake=5 flash_bg=true if this.timer<=45 and count(this.particles)<50 then add(this.particles,{ x=1+rnd(14), y=0, h=32+rnd(32), spd=8+rnd(8) }) end if this.timer<0 then this.state=2 this.particles={} flash_bg=false new_bg=true init_object(orb,this.x+4,this.y+4) pause_player=false end foreach(this.particles,function(p) p.y+=p.spd line(this.x+p.x,this.y+8-p.y,this.x+p.x,min(this.y+8-p.y+p.h,this.y+8),7) end) end spr(112,this.x,this.y+8) spr(113,this.x+8,this.y+8) end } add(types,big_chest) orb={ init=function(this) this.spd.y=-4 this.solids=false this.particles={} end, draw=function(this) this.spd.y=appr(this.spd.y,0,0.5) local hit=this.collide(player,0,0) if this.spd.y==0 and hit~=nil then music_timer=45 sfx(51) freeze=10 shake=10 destroy_object(this) max_djump=2 hit.djump=2 end spr(102,this.x,this.y) local off=frames/30 for i=0,7 do circfill(this.x+4+cos(off+i/8)*8,this.y+4+sin(off+i/8)*8,1,7) end end } flag = { tile=118, init=function(this) this.x+=5 this.score=0 this.show=false for i=1,count(got_fruit) do if got_fruit[i] then this.score+=1 end end end, draw=function(this) this.spr=118+(frames/5)%3 spr(this.spr,this.x,this.y) if this.show then rectfill(32,2,96,31,0) spr(26,55,6) print("x"..this.score,64,9,7) draw_time(49,16) print("deaths:"..deaths,48,24,7) elseif this.check(player,0,0) then sfx(55) sfx_timer=30 this.show=true end end } add(types,flag) room_title = { init=function(this) this.delay=5 end, draw=function(this) this.delay-=1 if this.delay<-30 then destroy_object(this) elseif this.delay<0 then rectfill(24,58,104,70,0) --rect(26,64-10,102,64+10,7) --print("---",31,64-2,13) if room.x==3 and room.y==1 then print("old site",48,62,7) elseif level_index()==30 then print("summit",52,62,7) else local level=(1+level_index())*100 print(level.." m",52+(level<1000 and 2 or 0),62,7) end --print("---",86,64-2,13) draw_time(4,4) end end } -- object functions -- ----------------------- function init_object(type,x,y) if type.if_not_fruit~=nil and got_fruit[1+level_index()] then return end local obj = {} obj.type = type obj.collideable=true obj.solids=true obj.spr = type.tile obj.flip = {x=false,y=false} obj.x = x obj.y = y obj.hitbox = { x=0,y=0,w=8,h=8 } obj.spd = {x=0,y=0} obj.rem = {x=0,y=0} obj.is_solid=function(ox,oy) if oy>0 and not obj.check(platform,ox,0) and obj.check(platform,ox,oy) then return true end return solid_at(obj.x+obj.hitbox.x+ox,obj.y+obj.hitbox.y+oy,obj.hitbox.w,obj.hitbox.h) or obj.check(fall_floor,ox,oy) or obj.check(fake_wall,ox,oy) end obj.is_ice=function(ox,oy) return ice_at(obj.x+obj.hitbox.x+ox,obj.y+obj.hitbox.y+oy,obj.hitbox.w,obj.hitbox.h) end obj.collide=function(type,ox,oy) local other for i=1,count(objects) do other=objects[i] if other ~=nil and other.type == type and other != obj and other.collideable and other.x+other.hitbox.x+other.hitbox.w > obj.x+obj.hitbox.x+ox and other.y+other.hitbox.y+other.hitbox.h > obj.y+obj.hitbox.y+oy and other.x+other.hitbox.x < obj.x+obj.hitbox.x+obj.hitbox.w+ox and other.y+other.hitbox.y < obj.y+obj.hitbox.y+obj.hitbox.h+oy then return other end end return nil end obj.check=function(type,ox,oy) return obj.collide(type,ox,oy) ~=nil end obj.move=function(ox,oy) local amount -- [x] get move amount obj.rem.x += ox amount = flr(obj.rem.x + 0.5) obj.rem.x -= amount obj.move_x(amount,0) -- [y] get move amount obj.rem.y += oy amount = flr(obj.rem.y + 0.5) obj.rem.y -= amount obj.move_y(amount) end obj.move_x=function(amount,start) if obj.solids then local step = sign(amount) for i=start,abs(amount) do if not obj.is_solid(step,0) then obj.x += step else obj.spd.x = 0 obj.rem.x = 0 break end end else obj.x += amount end end obj.move_y=function(amount) if obj.solids then local step = sign(amount) for i=0,abs(amount) do if not obj.is_solid(0,step) then obj.y += step else obj.spd.y = 0 obj.rem.y = 0 break end end else obj.y += amount end end add(objects,obj) if obj.type.init~=nil then obj.type.init(obj) end return obj end function destroy_object(obj) del(objects,obj) end function kill_player(obj) sfx_timer=12 sfx(0) deaths+=1 shake=10 destroy_object(obj) dead_particles={} for dir=0,7 do local angle=(dir/8) add(dead_particles,{ x=obj.x+4, y=obj.y+4, t=10, spd={ x=sin(angle)*3, y=cos(angle)*3 } }) restart_room() end end -- room functions -- -------------------- function restart_room() will_restart=true delay_restart=15 end function next_room() if room.x==2 and room.y==1 then music(30,500,7) elseif room.x==3 and room.y==1 then music(20,500,7) elseif room.x==4 and room.y==2 then music(30,500,7) elseif room.x==5 and room.y==3 then music(30,500,7) end if room.x==7 then load_room(0,room.y+1) else load_room(room.x+1,room.y) end end function load_room(x,y) has_dashed=false has_key=false --remove existing objects foreach(objects,destroy_object) --current room room.x = x room.y = y -- entities for tx=0,15 do for ty=0,15 do local tile = mget(room.x*16+tx,room.y*16+ty); if tile==11 then init_object(platform,tx*8,ty*8).dir=-1 elseif tile==12 then init_object(platform,tx*8,ty*8).dir=1 else foreach(types, function(type) if type.tile == tile then init_object(type,tx*8,ty*8) end end) end end end if not is_title() then init_object(room_title,0,0) end end -- update function -- ----------------------- function _update() frames=((frames+1)%30) if frames==0 and level_index()<30 then seconds=((seconds+1)%60) if seconds==0 then minutes+=1 end end if music_timer>0 then music_timer-=1 if music_timer<=0 then music(10,0,7) end end if sfx_timer>0 then sfx_timer-=1 end -- cancel if freeze if freeze>0 then freeze-=1 return end -- screenshake if shake>0 then shake-=1 camera() if shake>0 then camera(-2+rnd(5),-2+rnd(5)) end end -- restart (soon) if will_restart and delay_restart>0 then delay_restart-=1 if delay_restart<=0 then will_restart=false load_room(room.x,room.y) end end -- update each object foreach(objects,function(obj) obj.move(obj.spd.x,obj.spd.y) if obj.type.update~=nil then obj.type.update(obj) end end) -- start game if is_title() then if not start_game and (btn(k_jump) or btn(k_dash)) then music(-1) start_game_flash=50 start_game=true sfx(38) end if start_game then start_game_flash-=1 if start_game_flash<=-30 then begin_game() end end end end -- drawing functions -- ----------------------- function _draw() if freeze>0 then return end -- reset all palette values pal() -- start game flash if start_game then local c=10 if start_game_flash>10 then if frames%10<5 then c=7 end elseif start_game_flash>5 then c=2 elseif start_game_flash>0 then c=1 else c=0 end if c<10 then pal(6,c) pal(12,c) pal(13,c) pal(5,c) pal(1,c) pal(7,c) end end -- clear screen local bg_col = 0 if flash_bg then bg_col = frames/5 elseif new_bg~=nil then bg_col=2 end rectfill(0,0,128,128,bg_col) -- clouds if not is_title() then foreach(clouds, function(c) c.x += c.spd rectfill(c.x,c.y,c.x+c.w,c.y+4+(1-c.w/64)*12,new_bg~=nil and 14 or 1) if c.x > 128 then c.x = -c.w c.y=rnd(128-8) end end) end -- draw bg terrain map(room.x * 16,room.y * 16,0,0,16,16,4) -- platforms/big chest foreach(objects, function(o) if o.type==platform or o.type==big_chest then draw_object(o) end end) -- draw terrain local off=is_title() and -4 or 0 map(room.x*16,room.y * 16,off,0,16,16,2) -- draw objects foreach(objects, function(o) if o.type~=platform and o.type~=big_chest then draw_object(o) end end) -- draw fg terrain map(room.x * 16,room.y * 16,0,0,16,16,8) -- particles foreach(particles, function(p) p.x += p.spd p.y += sin(p.off) p.off+= min(0.05,p.spd/32) rectfill(p.x,p.y,p.x+p.s,p.y+p.s,p.c) if p.x>128+4 then p.x=-4 p.y=rnd(128) end end) -- dead particles foreach(dead_particles, function(p) p.x += p.spd.x p.y += p.spd.y p.t -=1 if p.t <= 0 then del(dead_particles,p) end rectfill(p.x-p.t/5,p.y-p.t/5,p.x+p.t/5,p.y+p.t/5,14+p.t%2) end) -- draw outside of the screen for screenshake rectfill(-5,-5,-1,133,0) rectfill(-5,-5,133,-1,0) rectfill(-5,128,133,133,0) rectfill(128,-5,133,133,0) -- credits if is_title() then print("x+c",58,80,5) print("matt thorson",42,96,5) print("noel berry",46,102,5) end if level_index()==30 then local p for i=1,count(objects) do if objects[i].type==player then p = objects[i] break end end if p~=nil then local diff=min(24,40-abs(p.x+4-64)) rectfill(0,0,diff,128,0) rectfill(128-diff,0,128,128,0) end end end function draw_object(obj) if obj.type.draw ~=nil then obj.type.draw(obj) elseif obj.spr > 0 then spr(obj.spr,obj.x,obj.y,1,1,obj.flip.x,obj.flip.y) end end function draw_time(x,y) local s=seconds local m=minutes%60 local h=flr(minutes/60) rectfill(x,y,x+32,y+6,0) print((h<10 and "0"..h or h)..":"..(m<10 and "0"..m or m)..":"..(s<10 and "0"..s or s),x+1,y+1,7) end -- helper functions -- ---------------------- function clamp(val,a,b) return max(a, min(b, val)) end function appr(val,target,amount) return val > target and max(val - amount, target) or min(val + amount, target) end function sign(v) return v>0 and 1 or v<0 and -1 or 0 end function maybe() return rnd(1)<0.5 end function solid_at(x,y,w,h) return tile_flag_at(x,y,w,h,0) end function ice_at(x,y,w,h) return tile_flag_at(x,y,w,h,4) end function tile_flag_at(x,y,w,h,flag) for i=max(0,flr(x/8)),min(15,(x+w-1)/8) do for j=max(0,flr(y/8)),min(15,(y+h-1)/8) do if fget(tile_at(i,j),flag) then return true end end end return false end function tile_at(x,y) return mget(room.x * 16 + x, room.y * 16 + y) end function spikes_at(x,y,w,h,xspd,yspd) for i=max(0,flr(x/8)),min(15,(x+w-1)/8) do for j=max(0,flr(y/8)),min(15,(y+h-1)/8) do local tile=tile_at(i,j) if tile==17 and ((y+h-1)%8>=6 or y+h==j*8+8) and yspd>=0 then return true elseif tile==27 and y%8<=2 and yspd<=0 then return true elseif tile==43 and x%8<=2 and xspd<=0 then return true elseif tile==59 and ((x+w-1)%8>=6 or x+w==i*8+8) and xspd>=0 then return true end end end return false end