0,]']±>Sï}WÿÍu§ðp8·d%qym(mÆ;¸ôBâsï÷ôôô”°ÂVl†3²=DDDDDDDDDDDDDDDDDDDDÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝDDDDDDDDDDÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝDDDDDDDDDDÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÌÌÌÌÌÌÌÌÝÝÝÝÌÌÌÌÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÌÌÌÌÝÝÝÝÝÝÝÝÝÝÝÝÌÌÌÌÌÌÌÌÝÝÝÝÌÌÌÌÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÌÌÌÌÝÝÝÝÝÝÝÝÝÝÝÝÌÌÌÌÌÌÌÌÝÝÝÝÌÌÌÌÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÌÌÌÌÝÝÝÝÝÝÝÝÝÝÝÝÌÌÌÌÌÌÌÌÝÝÝÝÌÌÌÌÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÌÌÌÌÝÝÝÝÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌîîîîîîÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌàîîîÌÌÌÌÌÌÌÌÌÌÌÌÌÌîîîîîîÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌDDDäîîîÌÌÌÌÌÌÌÌÌÌÌÌÌÌîîîîîîÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌDDDäîîîÌÌÌÌÌÌÌÌÌÌÌÌÌÌîîîîÌÌÌÌîîÌÌÌÌÌÌDÌÌÌÌîîÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌîîîîîîÌÌàîÌÌÌÌîîÌÌÌÌDDÌÌÌÌîîîîÌÌÌÌÌÌÌÌÌÌÌÌîîîîîîÌÌàîÌÌÌÌîîÌÌÌÌÌÌÌÌîîîîÌÌÌÌÌÌÌÌÌÌÌÌîîîîîîÌÌàîÌÌÌÌîîÌÌÌÌÌÌÌÌîîîîÌÌÌÌÀ àîÌÌÌÌÌÌÌ ÌÌÌÌîîîîîîÌÌàîÌÌÌÌîîÌÌÌÌÌÌÌÌîîîîîîÀ ÌÀÀÌÌÌÌÌÌÌÎ ÌÌÌÌÌîîÌÌàîÌÌÌÌîîÌÌÌÌîîÌÌÌÌîîàîÀ À ÌÌ ÌÀ ÌÌÌÌÌÌÎÌÀÌÌÌÌîîÌÌàîÌÌÌÌîîÌÌÌÌîîÌÌÌÌîîàîÀ À Ì ÌÌÌÌÌÌÎ ÀÀÌÌÌÌîîÌÌàîÌÌÌÌîîÌÌÌÌîîÌÌÌÌîîàîÀ À ÌÌÌÌÌÌÌÌîîÌ À ÀÌÌÌÌÌÌÌÌÌÌÌÌÌÌîîÌÌÌÌîîÌÌÌÌîîàîÌÌÌÌÌÌÌÌîîîîÌÌÌÌÌÌÌÌÌÌÌÌÌÌîîÌÌÌÌîîÌÌÌÌîîÌÌÌÌÌÌÌÌîîîîÌÌÌÌÌÌÌÌÌÌÌÌÌÌîîÌÌÌÌîîÌÌÌÌîîÌÌÌÌÌÌÌÌîîîîÌÌÌÌÌÌÌÌÌÌÌÌÌÌîîÌÌÌÌîîÌÌÌÌîîàîîîîîàîîîàîàîîîàîîîàîîîàîîîîîîîàîîîîîàîîîîîîîàîîîàîîîàîîîîîîîàîîîîîàîîîîîîîàîîîàîîîàîîîîîîîàîîîîîàîîîîîîîàîîîàîîîàîîîîîîîàîîîîîàîîîîîîîàîîîàîîîÀÌÌÌÌÌÌÌÌÌ ÄÌÌÌÌÌÌÌÌÌ ÄÌÌÌÌÌÌÌÌÌ ÄÌÌÌÌÌÌÌÌÌ DDDDDDÄÌÌÌ ÀÌÌÌ ÀÌ ÀÌÌÌ ÀÌ ÀÌÌÌÌÌÌÌ ÀÌÌÌÌÌ ÀÌÌÌÌÌÌÌ îîîÎÌÌÌìîÎÌÌÌ ÀÌ ÀÌÌÌ ÀÌ ÀÌÌÌÌÌÌÌ ÀÌÌÌÌÌ ÀÌÌÌÌÌÌÌ îîîÎÌÌÌìîÎÌÌÌ ÀÌ ÀÌÌÌ ÀÌ ÀÌÌÌÌÌÌÌ ÀÌÌÌÌÌ ÀÌÌÌÌÌÌÌ îîîÎÌÌÌìîÎÌÌÌ ÀÌ ÀÌÌÌ ÀÌ ÀÌÌÌÌÌÌÌ ÀÌÌÌÌÌ ÀÌÌÌÌÌÌÌ îîîÎÌÌÌìîÎÌÌÌ ÀÌ ÀÌÌÌÌÌÌÌÌÌ ÀÌÌÌ ÀÌ ÀÌÌÌ ÀÌÌÌ ÀÌÌÌ ÀÌ ÀÌÌÌìîÎÌÌÌìîîÀÌ îÎÌÌÌÌÌÌÌÌÌìÀÌÌÌìîîîÎÌìÀÌÌÌ îÎÌÌÌìÀÌÌÌìîîîÎÌìÀÌÌÌìîÎÌÌÌìîîÀÌ îÎÌÌÌÌÌÌÌÌÌìÀÌÌÌìîîîÎÌìÀÌÌÌ îÎÌÌÌìÀÌÌÌìîîîÎÌìÀÌÌÌìîÎÌÌÌìîîÀÌ îÎÌÌÌÌÌÌÌÌÌìÀÌÌÌìîîîÎÌìÀÌÌÌ îÎÌÌÌìÀÌÌÌìîîîÎÌìÀÌÌÌ ÀÌÌÌìîÎÌÌÌìîîÀÌ îÎÌ îÎÌìÀÌìÀÌÌÌìîîîÎÌìÀÌÌÌÌÌìîîîîÀÌÌÌìîîîîîîÀÌÌÌ ÀÌÌÌìîÎÌÌÌìîîÀÌ îÎÌ îÎÌìîÎÌìîÎÌÌÌìîîÀÌ îÎÌÌÌÌÌìîîîÎÌÌÌìîîîîÀÌÌÌ ÀÌÌÌìîÎÌÌÌìîîÀÌ îÎÌ îÎÌìîÎÌìîÎÌÌÌìîîÀÌ îÎÌÌÌÌÌìîîîÎÌÌÌìîîîîÀÌÌÌ ÀÌÌÌìîÎÌÌÌìîîÀÌ îÎÌ îÎÌìîÎÌìîÎÌÌÌìîîÀÌ îÎÌÌÌÌÌìîîîÎÌÌÌìîîîîÀÌÌÌÌÌ îîîîÀÌÌÌÌÌ îÎÌ îÎÌìîÎÌìîÎÌÌÌÌÌÌÌ îîÀÌÌÌÌÌ îîîÎÌÌÌìîîîîÀÌÌÌÌÌ îîîîÀÌÌÌÌÌ îÎÌ îÎÌ îÎÌ îÎÌÌÌÌÌÌÌ îîÀÌÌÌÌÌìÀÌÌÌìîîÀÌÌÌÌÌ îîîîÀÌÌÌÌÌ îÎÌ îÎÌ îÎÌ îÎÌÌÌÌÌÌÌ îîÀÌÌÌÌÌìÀÌÌÌìîîÀÌÌÌÌÌ îîîîÀÌÌÌÌÌ îÎÌ îÎÌ îÎÌ îÎÌÌÌÌÌÌÌ îîÀÌÌÌÌÌìÀÌÌÌìîîîîîîîîîîîîîîîîîîîîîÎÌÌÌìîîîîîîîîîîîîîîDDDDDDDDDDîîîîîîîîîîîîîîîîîÎÌÌÌìîîîîîîîîîîîîîîîîDDDDDDDDDDîîîîîîîîîîîîîîîîîÎÌÌÌìîîîîîîîîîîîîîîîîDDDDDDDDDDîîîîîîîîîîîîîîîîîÎÌÌÌìîîîîîîîîîîîîîîîîDDDDDDDDDDîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîDDDDDDDDDDDDDDDDDDDDDDDDDDÌDLÌÀ À ÌÌ ÌÌ ÀÌDDDDDDÌDLDÌ ÀÌ ÌÌÌÌÌ ÀÌ Ì À À À Ì ÌÌ ÌÀÌÀ ÀÀÌÀÌÀ ÀÀ ÀÀ ÌÌÌÀÌÌ ÌÀÌÌÀ ÀÀÌÀÌÀ ÀÀ ÀÌÀ À À Ì Ì ÌÌÀ ÀÀÌ ÌÌÌ À Ì À ÀÌÀÌÀÌÌ DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDÀÌÌÌÌÌ ÌˆˆˆˆˆÌÌÌ ¬ªªªªªÀ À À À Ì ÌÌÀÌÀÌ ÀÌ ÀÀÌÌ̈ˆˆ¨ÀÀÌÌ ÀÌÀÌ ÌÌ ÀÌ ÌÌÌÌ Ì Ì ÌÀ Ì Ì Ì Ì Ì ÌÌÌÌÌÌÌÌ À Ì Ì À À Ì À ÌÌÌÌ Ì Ì ÀÌ ÌÌÀÌÀ¬ÀÀÀÀ Ì Ì À À ÌÀ ÌÌÀÌÌÌÌÌ ÀÌÌÌÀÌÀ¬ÀÀ Ì ÀÌÌÌÌ ÀÌ ÌÌ ¬ÌÀÌÀ¬ÀÀ¬ÌÌÌ̬̬ªªªªªÀ ¬ª¬ª¬ªÀ¬ªÊ̪ªÀ¬ªªªªªÀŒˆˆˆˆˆÌÌÀÌÀ ÀÌÌÀÌÌÌÌÌ DDÌLDDDDDÄÌ ÀÌ Ì ÀÌ ÀÌ ÀÌ À Ì ÀÌÌ ÀÌ ÌÌÀÌLÄLDÄÌLDDLÀ ÀÀ ÀÌ ÌÀ ÌÌÌ ÌÌ À ÌÌ ÌÀ Ì À Ì ÀLÌÌLLÄLDDLÀ ÀÀ ÀÀÌÌ ÀÌÀÌ Ì Ì ÀÌ Ì Ì ÀÌ À Ì ÀLÄLDLÄLDDLÀÌÌÀ ÀÀÌÌÀ À ÀÀ À ÌÌÀ Ì ÀÌ ÀÌ Ì ÀÌ À ÀÌ ÀÀ ÀÌ ÌÀÌÌÌ ÀÌÌÀÌÌÀÀÌÌ ¥-- title: Stair Jumper -- author: Hevanafa -- desc: A game where you go reach the highest position as fast as possible! -- license: MIT License -- version: 0.1 -- script: lua -- 31-08-2022: first version, not yet refactored -- Done: detect inputs -- Done: render stairs -- Done: render sample character -- Done: implement game timer -- Done: implement stop movement when it's a wrong move -- Done: stun effect -- Done: rewrite the stair to be like a class -- Done: use relative camera coords -- Done: rewrite the coordinate system so that there can be a camera system -- Done: rotate the sprite depending on the movement -- Done: add sine linear interpolation -- Done: add new sprite local random, pi, sin, cos = math.random, math.pi, math.sin, math.cos -- seconds to ticks function sec(n) return n * 60 end function deg2rad(deg) return deg % 360 / 180 * pi end local ww, wh = 240, 136 local px, py = 0, 1 local prev_px = 0 -- y will be relative to the current table index -- player orientation local por = 0 -- for camera animation -- p_anim: player animation local p_anim_seek = 0 local p_anim_dur = sec(0.15) -- sec(2) -- sec(0.15) -- animation progress percentage function p_anim_p() return p_anim_seek / p_anim_dur end function getPlayerDisplayX() -- normal: px * 20 local p = sine_out(p_anim_p()) return lerp(prev_px * 20, px * 20, p) end function getPlayerDisplayY() -- normal: py * -20 local p = sine_out(p_anim_p()) return lerp((py - 1) * -20, py * -20, p) end -- p: 0..1, float, inclusive function lerp(a, z, p) -- clamp p to 0..1 p = p < 0 and 0 or p > 1 and 1 or p return a + (z - a) * p end -- p: percentage function lerp_sine(p) local val = lerp(-pi, pi, p) val = sin(val) return val / 2.0 + 0.5 end function sine_in(p) local val = lerp(pi / 2, pi, p) val = sin(val) return val end function sine_out(p) local val = lerp(0, pi / 2, p) val = sin(val) return val end -- player display x function pdx() return px end local R, A, D, LEFT, RIGHT = 18, 1, 4, 60, 61 local gameover = false local score = 0 local highscore_pmem_idx = 1 local highscore = pmem(highscore_pmem_idx) or 0 local misses = 0 local stairs = {} local started = false -- default: 60 local timeleft = sec(60) local stun_tl = 0 local blink_tl = 0 local blink_on = true local ticks = 0 -- simple table clear function -- used in generate() function t_clear(t) for k in pairs(t) do t[k] = nil end end -- simple slice function, doesn't handle weird inputs -- (unused) function t_slice(t, a, z) local t_out = {} for i = a, z do if t[i] then table.insert(t_out, t[i]) end end return t_out end function newStair(x) return { -- to be checked in step x = x, display_x = x * 20 } end function getStairDisplayY(idx) return idx * -20 end -- generate staircase function generate(clear) local x = 0 if clear then t_clear(stairs) table.insert(stairs, newStair(0)) for a = 2, 50 do local i = random(0, 1) x = x + (i == 0 and -1 or i) table.insert(stairs, newStair(x)) end else x = stairs[#stairs].x for a = 1, 50 do local i = random(0, 1) x = x + (i == 0 and -1 or i) table.insert(stairs, newStair(x)) end end end generate(true) -- step check (used in input) -- inc: increment function step(inc) if stairs[py + 1].x == px + inc then score = score + 1 por = por + inc prev_px = px px = px + inc py = py + 1 p_anim_seek = 0 if py + 8 >= #stairs then generate(false) end else misses = misses + 1 stun() end end function isStunned() return stun_tl > 0 end function stun() stun_tl = stun_tl + sec(2) end function endGame() if highscore < score then highscore = score pmem(highscore_pmem_idx, highscore) new_highscore = true end gameover = true end function renderGameOver() local acc = score > 0 and ((score - misses) / score * 100) or 0 local t1, t2 = { "Stairs", "Misses", "Accuracy" }, { score, misses, ("%.1f%%"):format(acc) } rect(40, 40, 160, 8, 0) rect(40, 48, 160, 8 + 12 * #t1, 14) centre("Performance", ww / 2, 40, 12) for i, s in pairs(t1) do local y = 56 + 12 * (i - 1) print(s, 48, y, 12) print(t2[i], ww / 2, y, 12) end if new_highscore then centre("New highscore!", ww / 2, wh - 36, 12) end if blink_on then centre("Press R to restart", ww / 2, wh - 20, 12) end end function renderScore() rect(8, 8, 100, 16, 13) local s = score .. "" local w = print(s, 0, -1000, 0, true, 2) print(s, 54 - w / 2, 12, 0, true, 2) --print(("px: %d, py: %d"):format(px, py), 8, 24, 12) --print(("gameover: %s"):format(gameover), 8, 32, 12) print(("Time: %.1fs"):format(timeleft > 0 and timeleft / 60 or 0), 8, 32, 12) --print(("stun_tl: %.1fs"):format(stun_tl / 60), 8, 48, 12) --print(("misses: %d"):format(misses), 8, 56, 12) end function renderStairs() for a = 0, 8 do local iy = py + a local x_diff = stairs[py + a].x - stairs[py].x rect( ww / 2 - 10 + x_diff * 20, wh - 10 - a * 20, 20, 4, 4) end end function renderStairs() for a = 0, 8 do local iy = py + a local stair = stairs[iy] rect( ww / 2 - 10 + stair.display_x - getPlayerDisplayX(), wh - 10 + getStairDisplayY(iy) - getPlayerDisplayY(), 20, 4, 4 ) end end function renderSprite() spr(isStunned() and 3 or 1, ww / 2 - 8, wh - 26, 5, 1, 0, por, 2, 2) end function renderStunStars() for a = 0, 1 do local angle = deg2rad(a * 180 + ticks * 8) local x, y = sin(angle) * 16, cos(angle) * 4 spr(32, ww / 2 - 4 + x, wh - 34 + y, 5) end end -- the parameters are the same as print() -- x: centre x function centre(text,x,y,color,fixed,scale,smallfont)x=x or 0 y=y or 0 color=color or 15 fixed=fixed or false scale=scale or 1 smallfont=smallfont or false local w=print(text,0,-1000,0,fixed,scale,smallfont)print(text,x-w/2,y,color,fixed,scale,smallfont)end function renderTitleScr() local s = "Stair" print(s, 25, 25, 14, false, 4) print(s, 20, 20, 12, false, 4) s = "Jumper" local w = print(s, 0, -1000, 0, false, 4) print(s, ww - w - 20, 50, 14, false, 4) print(s, ww - w - 25, 45, 12, false, 4) s = ("Highscore: %d"):format(highscore) centre(s, ww / 2, wh / 2 + 20, 12) s = "Press <-- or --> to start" if blink_on then centre(s, ww / 2, wh - 24, 12) end s = "By Hevanafa (Aug 2022)" -- w = print(s, 0, -1000) -- print(s, (ww - w) / 2, wh - 10, 12) centre(s, ww / 2, wh - 10, 12) end function TIC() cls() renderStairs() renderSprite() if isStunned() then renderStunStars() end renderScore() if not started then renderTitleScr() end if gameover then renderGameOver() end -- update local stunned = isStunned() if not gameover then if stun_tl > 0 then stun_tl = stun_tl - 1 end if timeleft <= 0 then endGame() end if not stunned then if keyp(LEFT) or keyp(A) then started = true step(-1) elseif keyp(RIGHT) or keyp(D) then started = true step(1) end end if started then timeleft = timeleft - 1 end else if key(R) then reset() end end blink_tl = blink_tl - 1 if blink_tl < 0 then blink_tl = sec(1) blink_on = not blink_on end ticks = ticks + 1 if p_anim_seek < p_anim_dur then p_anim_seek = p_anim_seek + 1 end end