0,]']±>Sï}WÿÍu§ðp8·d%qy)6o;]ÉA¦ösï÷ôôô”°ÂVl†3f?o@          .¡-µ-W-Y-Ù-V-c-a-å-x-{-|-}-}-}-}.}.m.m.m.]/]/ /@/0/0/0/0/@/PV--- - ------- - - - - - ----- - - ------/u           f`8ÀÌÌÌ ÀÌÌÌ ÀÌÌ ÀÌÌ ÀÌÌ ÀÌ À ÀÌÌÌÌ ÀÌÌÌ ÀÌÌÌ ÀÌÌÌ ÀÌÌ ÀÌÌ ÀÌÌ ÀÌ À ÀÌÌÌÌ ÀÌÌÌ ÀÌ À ÀÌ ÀÌ À ÀÌ À ÀÌ À ÀÌ À ÀÌ ÀÌ ÀÌ À ÀÌ ÀÌ À ÀÌ À ÀÌ À ÀÌ À ÀÌ ÀÌ ÀÌ À ÀÌ ÀÌ ÀÌ À ÀÌ ÀÌÌÌÌ ÀÌÌÌ ÀÌ ÀÌ À ÀÌ ÀÌ ÀÌ À ÀÌ ÀÌÌÌÌ ÀÌÌÌ ÀÌ ÀÌÌÌ ÀÌ ÀÌ À ÀÌ À ÀÌ À ÀÌ À ÀÌ ÀÌ ÀÌÌÌ ÀÌ ÀÌ À ÀÌ À ÀÌ À ÀÌ À ÀÌ ÀÌ ÀÌ À ÀÌÌÌ ÀÌÌ ÀÌÌ ÀÌÌ ÀÌ À ÀÌÌÌÌ ÀÌ ÀÌ À ÀÌÌÌ ÀÌÌ ÀÌÌ ÀÌÌ ÀÌ À ÀÌÌÌÌ ÀÌ  ªªª  ªª  ª  ª  ª    ªªª  ªª  ª  ª  ª    ª    ª    ª  ª  ª    ª    ª    ª  ª  ª    ª    ª    ª  ª  ªªª  ª    ª    ª  ª  ªªª  ªªª  ªªªª  ª  ª  ª  ªªª  ªªªª  ª  ª  ª  ª    ª    ªªªª  ªªªª  ª  ª    ª    ªªªª  ªªªª  ª ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÀÌ»ËÌ»ËÌ» ÀÌ»ËÌ»ËÌ» ÀÌ»ËÌ»ËÌ» ̼»Ì¼»Ì¼»Ì¼»Ì¼»Ì¼»Ì¼»Ì¼»Ì¼»Ì¼»Ì¼»Ì¼»ªº»ªº»ªº»ªº»ªº»ªº»ªº»ªº»ªº»ªº»ªº»ªº»�™»«ª»«ª» �™»«ª»«ª» �™»«ª»«ª» ™¹»ªº»™¹»ªº»™¹»ªº»�™» �™» �™» ÀÌ ÀÌ ÀÌÌÌÌÌÌÌ ÀÌ ÀÌÀÌ ÌÌÌ ÀÌ ÀÌ À ÀÀ ÀÀ ÀÌÀÌÀ ÀÌÌ Ì À À ÀÀ ÀÌÀ ÀÀ ÀÀÌ Ì Ì À ÀÌÌ ÀÌÀ À ÀÀ ÀÌÀÌ ÀÌ À ÀÌÀÌÀÌÌÌÌ Ì À ÀÌÌÀÌ ÌÀ À ÀÀÌÌÀÌ ÀÌ À ÀÌÀÌÌÌÀ À ÀÀ ÀÌ:-- title: Ricochet Rallly -- author: Claw86 -- desc: Control green marble to collect gems. Dodge eyes and ricocheting baddies. -- license: MIT License -- version: 1.0 -- script: lua gameOverPlayed = false playerX = 0 playerY = 0 playerSpeedX = 0 playerSpeedY = 0 gemX = 0 gemY = 0 gemBob = 0 score = 0 scorePop = 0 highScore = 0 timeLeft = 0 lives = 3 maxLives =5 maxHp = 3 hp = 3 INVINCIBLE_TIME = 90 invincibleTime= 0 SCREEN_W = 240 SCREEN_H = 136 TILE = 8 sparkles = {} sparkDelay = 0 eyes = {} EYE_RESPAWN_TIME = 300 rickys = {} RICKY_RESPAWN_TIME = 300 SFX_GAMEOVER = 0 SFX_GEM = 1 SFX_HURT = 2 SFX_SPOTTED = 3 SFX_BOUNCE = 4 SFX_DEATH = 5 SFX_GEM10 = 8 SFX_GEM20 = 9 SFX_GEM25 = 10 SFX_CHANNEL_PLAYER = 0 SFX_CHANNEL_EYE = 1 SFX_CHANNEL_PHYSICS = 2 SFX_CHANNEL_MISC = 3 SFX_VOLUME = 15 TITLE_GEM_X = 108 TITLE_GEM_Y = 70 GAMEOVER_GEM_X = 108 GAMEOVER_GEM_Y = 80 flyingRewards = {} function drawHUD() print("Score:", 100, 5, 12) local scoreScale = 1 if scorePop > 5 then scoreScale = 2 end print(score, 133, 5, 12, false, scoreScale) print("Time: "..math.ceil(timeLeft/60), 190, 5, 11) print("Lives: "..lives, 5, 5, 2) print("HP: "..hp, 5, 15, 2) end function drawSpikes() for x = 0, SCREEN_W - TILE, TILE do spr(10, x, 136-8) end for x = 0, SCREEN_W - TILE, TILE do spr(10, x, 0, 0, 1, 2) end for y = 0, SCREEN_H - TILE, TILE do spr(10, 0, y, 0, 1, 3, 3) end for y = SCREEN_H - TILE, 0, TILE do spr(10, 0, y, 0, 1, 3, 3) end for y = 0, 128, 8 do spr(10, 232, y, 0, 1, 3, 1) end end function drawPlayer() if invincibleTime > 0 then if invincibleTime % 20 < 10 then spr(4, playerX, playerY, 0, 1, 0, 0, 2, 2) else spr(7, playerX, playerY, 0, 1, 0, 0, 2, 2) end else spr(1, playerX, playerY, 0, 1, 0, 0, 2, 2) end end function movePlayer() if btn(3) then playerSpeedX = playerSpeedX + 0.1 end if btn(2) then playerSpeedX = playerSpeedX - 0.1 end if btn(1) then playerSpeedY = playerSpeedY + 0.1 end if btn(0) then playerSpeedY = playerSpeedY - 0.1 end playerX = playerX + playerSpeedX playerY = playerY + playerSpeedY playerSpeedX = playerSpeedX * 0.95 playerSpeedY = playerSpeedY * 0.95 end function checkSpikes() if playerX <= 0 and invincibleTime <= 0 then hp = hp - 1 sfx(SFX_HURT, 50, -1, SFX_CHANNEL_PLAYER, SFX_VOLUME) invincibleTime = 180 playerX = 1 playerSpeedX = 3 end if playerY <= 0 and invincibleTime <= 0 then hp = hp - 1 sfx(SFX_HURT, 50, -1, SFX_CHANNEL_PLAYER, SFX_VOLUME) invincibleTime = 180 playerY = 1 playerSpeedY = 3 end if playerX >= 224 and invincibleTime <= 0 then hp = hp - 1 sfx(SFX_HURT, 50, -1, SFX_CHANNEL_PLAYER, SFX_VOLUME) invincibleTime = 180 playerX = 223 playerSpeedX = -3 end if playerY >= 120 and invincibleTime <= 0 then hp = hp - 1 sfx(SFX_HURT, 50, -1, SFX_CHANNEL_PLAYER, SFX_VOLUME) invincibleTime = 120 playerY = 119 playerSpeedY = - 3 end end function checkDeath() if hp <= 0 then lives = lives - 1 sfx(SFX_DEATH, 10, 80, SFX_CHANNEL_PLAYER) if lives > 0 then respawnPlayer() end end end function respawnPlayer() hp = maxHp playerX = 50 playerY = 50 playerSpeedX = 0 playerSpeedY = 0 invincibleTime = INVINCIBLE_TIME end function keepPlayerOnScreen() if playerX > 224 then playerX = 224 end if playerY > 120 then playerY = 120 end if playerY < 0 then playerY = 0 end if playerX < 0 then playerX = 0 end end function spawnGem() local newX local newY local safe repeat newX = math.random(16,216) newY = math.random(16,112) safe = true -- Stay away from the player if math.abs(newX - playerX) < 30 and math.abs(newY - playerY) < 30 then safe = false end -- Stay away from Eyes for i, eye in ipairs(eyes) do if math.abs(newX - eye.x) < 20 and math.abs(newY - eye.y) < 20 then safe = false end end until safe gemX = newX gemY = newY end function checkGem() if math.abs(playerX - gemX) < 10 and math.abs(playerY - gemY) < 10 then score = score + 1 table.insert(flyingRewards, { x = gemX, y = gemY, targetX = 133, targetY = 5, life = 30, trailTimer = 0 }) timeLeft = timeLeft + 60 if score % 10 == 0 and hp < maxHp then hp = hp + 1 end if score % 20 == 0 and lives < maxLives then lives = lives + 1 end if score % 25 == 0 then timeLeft = timeLeft + 600 end local burst = 10 if score % 25 == 0 then burst = 60 elseif score % 20 == 0 then burst = 40 elseif score % 10 == 0 then burst = 20 end createGemBurst(burst) if score % 25 == 0 then sfx(SFX_GEM25, 80, -10, SFX_CHANNEL_MISC) elseif score % 20 == 0 then sfx(SFX_GEM20, 70, -1, SFX_CHANNEL_MISC) elseif score % 10 == 0 then sfx(SFX_GEM10, 70, -1, SFX_CHANNEL_MISC) else sfx(SFX_GEM, 100, -1, SFX_CHANNEL_PLAYER) end spawnGem() end end function drawGem() local gemFrames = {65, 81, 65, 97} local frame = math.floor(time() / 200) % 4 + 1 gemBob = math.sin(time()/300)*2 spr(gemFrames[frame], gemX, gemY + gemBob) end function createGemSparkle() table.insert(sparkles, { x = gemX + math.random(0,7), y = gemY + math.random(0,7), dx = math.random(-4,4) / 20, dy = math.random(-2,4) / 20, life = 50 }) end function createFlyingRewardSparkle(x, y) table.insert(sparkles, { x = x, y = y, dx = math.random(-4,4) / 20, dy = math.random(-2,4) / 20, life = 20 }) end function createGemBurst(amount) for i = 1, amount do createGemSparkle() end end function drawGemSparkle() for i, s in ipairs(sparkles) do if s.life > 20 then pix(s.x, s.y + gemBob, 12) else pix(s.x, s.y + gemBob, 13) end end end function updateGemSparkle() sparkDelay = sparkDelay - 1 if sparkDelay <= 0 then createGemSparkle() sparkDelay = 20 end for i, s in ipairs(sparkles) do s.x = s.x + s.dx s.y = s.y + s.dy s.life = s.life - 1 if s.life <= 0 then table.remove(sparkles, i) end end end function updateFlyingRewards() for i = #flyingRewards, 1, -1 do local reward = flyingRewards[i] reward.x = reward.x + (reward.targetX - reward.x) * 0.12 reward.y = reward.y + (reward.targetY - reward.y) * 0.12 reward.life = reward.life - 1 reward.trailTimer = reward.trailTimer + 1 if reward.trailTimer >= 4 then createFlyingRewardSparkle(reward.x, reward.y) reward.trailTimer = 0 end if reward.life <= 0 then scorePop = 15 table.remove(flyingRewards, i) end end end function drawFlyingRewards() for i = 1, #flyingRewards do local reward = flyingRewards[i] local scale = 1 if reward.life < 10 then scale = reward.life / 10 end print("+1", reward.x, reward.y, 12, false, scale) end end function drawEye() for i, eye in ipairs(eyes) do if eye.alive then spr(12, eye.x, eye.y) else spr(28, eye.x, eye.y) end end end function moveEye() for i, eye in ipairs(eyes) do if eye.alive then if math.abs(playerX - eye.x) < 100 and math.abs(playerY - eye.y) < 100 then if not eye.spotted then sfx(SFX_SPOTTED, 60, 60, SFX_CHANNEL_EYE) eye.spotted = true end if playerX < eye.x then eye.x = eye.x - 0.1 end if playerX > eye.x then eye.x = eye.x + 0.1 end if playerY < eye.y then eye.y = eye.y - 0.1 end if playerY > eye.y then eye.y = eye.y + 0.1 end end end end end function checkEyeCollision() for i, eye in ipairs(eyes) do if math.abs((playerX + 8) - (eye.x + 4)) < 12 and math.abs((playerY + 8) - (eye.y + 4)) < 12 and invincibleTime <= 0 then if eye.alive then hp = hp - 1 sfx(SFX_HURT, 50, -1, SFX_CHANNEL_PLAYER, SFX_VOLUME) invincibleTime = INVINCIBLE_TIME -- Damage this eye eye.hp = eye.hp - 1 if eye.hp <= 0 then eye.alive = false eye.respawnTimer = EYE_RESPAWN_TIME end -- Strong knockback if playerX < eye.x then playerSpeedX = -3 else playerSpeedX = 3 end if playerY < eye.y then playerSpeedY = -3 else playerSpeedY = 3 end else -- Gentle bounce from stone eye if playerX < eye.x then playerSpeedX = -1.5 sfx(SFX_BOUNCE, -1, -1, SFX_CHANNEL_PHYSICS) else playerSpeedX = 1.5 sfx(SFX_BOUNCE, -1, -1, SFX_CHANNEL_PHYSICS) end if playerY < eye.y then playerSpeedY = -1.5 sfx(SFX_BOUNCE, -1, -1, SFX_CHANNEL_PHYSICS) else playerSpeedY = 1.5 sfx(SFX_BOUNCE, -1, -1, SFX_CHANNEL_PHYSICS) end end end end end function spawnEye() local newX local newY local safe repeat newX = math.random(20,220) newY = math.random(20,116) safe = true -- Stay away from the player if math.abs(newX - playerX) < 40 and math.abs(newY - playerY) < 40 then safe = false end -- Stay away from the gem if math.abs(newX - gemX) < 20 and math.abs(newY - gemY) < 20 then safe = false end until safe return newX, newY end function updateEyes() for i, eye in ipairs(eyes) do if not eye.alive and eye.respawnTimer then eye.respawnTimer = eye.respawnTimer - 1 if eye.respawnTimer <= 0 then local newX, newY = spawnEye() table.insert(eyes, { x = newX, y = newY, hp = 1, radius = 3.5, alive = true, spotted = false }) eye.respawnTimer = nil end end end end function drawRicky() for i, ricky in ipairs(rickys) do spr(14, ricky.x, ricky.y) end end function moveRicky() for i, ricky in ipairs(rickys) do -- Move Ricky at a constantt speed ricky.x = ricky.x + ricky.speedX ricky.y = ricky.y + ricky.speedY -- Bounce Ricky off walls if ricky.x <= 0 then ricky.x = 0 ricky.speedX = -ricky.speedX sfx(SFX_BOUNCE, 1, -1, SFX_CHANNEL_PHYSICS) end if ricky.x >= 232 then ricky.x = 232 ricky.speedX = -ricky.speedX sfx(SFX_BOUNCE, 1, -1, SFX_CHANNEL_PHYSICS) end if ricky.y <= 0 then ricky.y = 0 ricky.speedY = -ricky.speedY sfx(SFX_BOUNCE, 1, -1, SFX_CHANNEL_PHYSICS) end if ricky.y >= 128 then ricky.y = 128 ricky.speedY = -ricky.speedY sfx(SFX_BOUNCE, 1, -1, SFX_CHANNEL_PHYSICS) end end end function checkRickyCollision() for i, ricky in ipairs(rickys) do if math.abs(playerX - ricky.x) < 12 and math.abs(playerY - ricky.y) < 12 and invincibleTime <= 0 then hp = hp - ricky.damage sfx(SFX_HURT, 50, -1, SFX_CHANNEL_PLAYER, SFX_VOLUME) invincibleTime = INVINCIBLE_TIME ricky.speedX = -ricky.speedX ricky.speedY = -ricky.speedY -- Strong knockback if playerX < ricky.x then ricky.speedX = 0.7 else ricky.speedX = -0.7 end if playerY < ricky.y then ricky.speedY = 0.7 else ricky.speedY = -0.7 end end end end function checkRickyEyeCollision() for i, ricky in ipairs(rickys) do for j, eye in ipairs(eyes) do if not eye.alive then if checkCollision(ricky, eye) then bounceRicky(ricky, eye) sfx(SFX_BOUNCE, -1, -1, SFX_CHANNEL_PHYSICS) end end end end end function bounceRicky(ricky, eye) local dx = ricky.x - eye.x local dy = ricky.y - eye.y if math.abs(dx) > math.abs(dy) then if dx > 0 then ricky.x = eye.x + ricky.radius + eye.radius else ricky.x = eye.x - ricky.radius - eye.radius end ricky.speedX = -ricky.speedX else if dy > 0 then ricky.y = eye.y + ricky.radius + eye.radius else ricky.y = eye.y - ricky.radius - eye.radius end ricky.speedY = -ricky.speedY end end function checkCollision(a, b) local dx = a.x - b.x local dy = a.y - b.y local distance = math.sqrt(dx*dx + dy*dy) if distance < a.radius + b.radius then return true end return false end function drawGameOver() print("GAME OVER", 65, 35, 4, false, 2) print("SCORE: "..score, 72, 55, 3, false, 2) local highScoreText = "HIGH SCORE: "..highScore local highScoreX = (240 - #highScoreText * 6) / 2 print(highScoreText, highScoreX, 70, 12) local gemFrames = {65, 81, 65, 97} local frame = math.floor(time() / 200) % 4 + 1 local gemBob = math.sin(time() / 300) * 2 spr( gemFrames[frame], GAMEOVER_GEM_X, GAMEOVER_GEM_Y + gemBob, 0, 3, 0, 0, 1, 1 ) print("PRESS A TO PLAY AGAIN", 60, 115, 12) end function drawStartScreen() print("RICOCHET", 75, 35, 12, false, 2) print("RALLY", 91, 55, 10, false, 2) local gemFrames = {65, 81, 65, 97} local frame = math.floor(time() / 200) % 4 + 1 local gemBob = math.sin(time() / 300) * 2 spr( gemFrames[frame], TITLE_GEM_X, TITLE_GEM_Y + gemBob, 0, 3, 0, 0, 1, 1) print("PRESS A TO START", 75, 115, 12) end function resetGame() gameOverPlayed = false flyingRewards = {} sparkles = {} scorePop = 0 eyes = {} rickys = {} playerX = 50 playerY = 50 spawnGem() score = 0 timeLeft = 60 * 60 lives = 3 hp = maxHp local eyeX, eyeY = spawnEye() table.insert(eyes, { x = eyeX, y = eyeY, hp = 1, radius = 3.5, alive = true, spotted = false }) table.insert(rickys, { x = 100, y = 70, speedX = 0.7, speedY = 0.7, hp = 3, damage = 1, radius = 3.5, alive = true }) end gameStarted = false resetGame() function TIC() cls() -- START SCREEN if not gameStarted then drawStartScreen() if btnp(4) then gameStarted = true resetGame() end return end -- INVINCIBILITY if invincibleTime > 0 then invincibleTime = invincibleTime - 1 end -- TIMER timeLeft = timeLeft - 1 -- GAME OVER if timeLeft <= 0 or lives <= 0 then if score > highScore then highScore = score end if not gameOverPlayed then sfx(SFX_GAMEOVER) gameOverPlayed = true end drawGameOver() if btnp(4) then resetGame() end -- Occasional sparkle on Game Over if time() % 500 < 20 then createGemSparkle() end return end -- GAMEPLAY drawHUD() drawFlyingRewards() movePlayer() drawSpikes() drawPlayer() checkSpikes() checkDeath() keepPlayerOnScreen() checkGem() updateFlyingRewards() if scorePop > 0 then scorePop = scorePop - 1 end drawGem() drawGemSparkle() updateGemSparkle() updateEyes() moveEye() checkEyeCollision() drawEye() moveRicky() checkRickyCollision() checkRickyEyeCollision() drawRicky() end