285 lines
9.4 KiB
Lua
285 lines
9.4 KiB
Lua
-- NAME: Asteroids
|
|
-- DESC: Destroy asteroids, avoid collisions
|
|
|
|
-- Game states
|
|
local STATE_MENU = 0
|
|
local STATE_PLAYING = 1
|
|
local STATE_GAME_OVER = 2
|
|
|
|
-- Game constants
|
|
local SHIP_SPEED = 2
|
|
local SHIP_ROTATION_SPEED = 8
|
|
local BULLET_SPEED = 5
|
|
local ASTEROID_SPAWN_RATE = 120
|
|
local ASTEROID_SPEEDS = {1.5, 2, 2.5, 3}
|
|
|
|
function init()
|
|
game.vars.state = STATE_MENU
|
|
game.vars.score = 0
|
|
game.vars.level = 1
|
|
|
|
-- Ship
|
|
game.vars.ship_x = game.width() / 2
|
|
game.vars.ship_y = game.height() / 2
|
|
game.vars.ship_angle = 0 -- Radians
|
|
game.vars.ship_vel_x = 0
|
|
game.vars.ship_vel_y = 0
|
|
game.vars.thrusting = false
|
|
|
|
-- Bullets
|
|
game.vars.bullets = {}
|
|
game.vars.bullet_cooldown = 0
|
|
|
|
-- Asteroids
|
|
game.vars.asteroids = {}
|
|
game.vars.frame_count = 0
|
|
|
|
-- Enable continuous updates
|
|
game.set_frame_updates(true)
|
|
|
|
print("Asteroids initialized")
|
|
end
|
|
|
|
function update(event)
|
|
local state = game.vars.state
|
|
|
|
if state == STATE_MENU then
|
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
|
reset_game()
|
|
game.vars.state = STATE_PLAYING
|
|
return true
|
|
end
|
|
|
|
elseif state == STATE_PLAYING then
|
|
-- Handle input
|
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.TOUCH_MOVE then
|
|
if event.x < game.width() / 2 then
|
|
-- Left side: rotate counter-clockwise
|
|
game.vars.ship_angle = game.vars.ship_angle - SHIP_ROTATION_SPEED * 0.017
|
|
else
|
|
-- Right side: rotate clockwise
|
|
game.vars.ship_angle = game.vars.ship_angle + SHIP_ROTATION_SPEED * 0.017
|
|
end
|
|
|
|
-- Thrust
|
|
game.vars.thrusting = true
|
|
else
|
|
game.vars.thrusting = false
|
|
end
|
|
|
|
-- Update physics on frame tick
|
|
if event.type == INPUT.FRAME_TICK then
|
|
update_ship()
|
|
update_bullets()
|
|
update_asteroids()
|
|
check_collisions()
|
|
|
|
-- Spawn asteroids
|
|
game.vars.frame_count = game.vars.frame_count + 1
|
|
if game.vars.frame_count >= ASTEROID_SPAWN_RATE then
|
|
spawn_asteroid(game.width() / 2, game.height() / 2, 3)
|
|
game.vars.frame_count = 0
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
elseif state == STATE_GAME_OVER then
|
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
|
game.vars.state = STATE_MENU
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function draw()
|
|
renderer.clear(false) -- Black background
|
|
|
|
local state = game.vars.state
|
|
|
|
if state == STATE_MENU then
|
|
renderer.text_scaled(game.width() / 2 - 35, game.height() / 2 - 30, "ASTEROIDS", true, 2)
|
|
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2, "Tap to Start", true, 2)
|
|
|
|
elseif state == STATE_PLAYING or state == STATE_GAME_OVER then
|
|
-- Draw asteroids (convert to integers)
|
|
for i = 1, #game.vars.asteroids do
|
|
local ast = game.vars.asteroids[i]
|
|
renderer.circle(math.floor(ast.x + 0.5), math.floor(ast.y + 0.5), ast.size, true, false)
|
|
end
|
|
|
|
-- Draw bullets
|
|
for i = 1, #game.vars.bullets do
|
|
local bullet = game.vars.bullets[i]
|
|
renderer.pixel(bullet.x, bullet.y, true)
|
|
end
|
|
|
|
-- Draw ship
|
|
local ship_size = 6
|
|
local nose_x = game.vars.ship_x + math.cos(game.vars.ship_angle) * ship_size
|
|
local nose_y = game.vars.ship_y + math.sin(game.vars.ship_angle) * ship_size
|
|
local back_x = game.vars.ship_x - math.cos(game.vars.ship_angle) * (ship_size / 2)
|
|
local back_y = game.vars.ship_y - math.sin(game.vars.ship_angle) * (ship_size / 2)
|
|
|
|
renderer.line(game.vars.ship_x, game.vars.ship_y, nose_x, nose_y, true, 1)
|
|
renderer.line(back_x, back_y, nose_x, nose_y, true, 1)
|
|
|
|
-- Draw thrust indicator
|
|
if game.vars.thrusting then
|
|
local flame_x = game.vars.ship_x - math.cos(game.vars.ship_angle) * ship_size
|
|
local flame_y = game.vars.ship_y - math.sin(game.vars.ship_angle) * ship_size
|
|
renderer.line(game.vars.ship_x, game.vars.ship_y, flame_x, flame_y, true, 1)
|
|
end
|
|
|
|
-- Draw score
|
|
renderer.text_scaled(10, 10, "Score: " .. tostring(game.vars.score), true, 2)
|
|
renderer.text_scaled(10, 20, "Level: " .. tostring(game.vars.level), true, 2)
|
|
|
|
if state == STATE_GAME_OVER then
|
|
renderer.text_scaled(game.width() / 2 - 40, game.height() / 2, "GAME OVER", true, 2)
|
|
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2 + 20, "Tap to Menu", true, 2)
|
|
end
|
|
end
|
|
end
|
|
|
|
function update_ship()
|
|
if game.vars.thrusting then
|
|
game.vars.ship_vel_x = game.vars.ship_vel_x + math.cos(game.vars.ship_angle) * SHIP_SPEED
|
|
game.vars.ship_vel_y = game.vars.ship_vel_y + math.sin(game.vars.ship_angle) * SHIP_SPEED
|
|
end
|
|
|
|
-- Friction
|
|
game.vars.ship_vel_x = game.vars.ship_vel_x * 0.98
|
|
game.vars.ship_vel_y = game.vars.ship_vel_y * 0.98
|
|
|
|
-- Move ship
|
|
game.vars.ship_x = game.vars.ship_x + game.vars.ship_vel_x
|
|
game.vars.ship_y = game.vars.ship_y + game.vars.ship_vel_y
|
|
|
|
-- Wrap around screen
|
|
if game.vars.ship_x < 0 then game.vars.ship_x = game.width() end
|
|
if game.vars.ship_x > game.width() then game.vars.ship_x = 0 end
|
|
if game.vars.ship_y < 0 then game.vars.ship_y = game.height() end
|
|
if game.vars.ship_y > game.height() then game.vars.ship_y = 0 end
|
|
end
|
|
|
|
function update_bullets()
|
|
-- Update existing bullets
|
|
for i = #game.vars.bullets, 1, -1 do
|
|
local bullet = game.vars.bullets[i]
|
|
bullet.x = bullet.x + bullet.vel_x
|
|
bullet.y = bullet.y + bullet.vel_y
|
|
|
|
-- Remove if off-screen
|
|
if bullet.x < 0 or bullet.x > game.width() or bullet.y < 0 or bullet.y > game.height() then
|
|
table.remove(game.vars.bullets, i)
|
|
end
|
|
end
|
|
|
|
-- Fire bullet
|
|
game.vars.bullet_cooldown = math.max(0, game.vars.bullet_cooldown - 1)
|
|
if game.vars.thrusting and game.vars.bullet_cooldown == 0 then
|
|
local bullet_x = game.vars.ship_x + math.cos(game.vars.ship_angle) * 8
|
|
local bullet_y = game.vars.ship_y + math.sin(game.vars.ship_angle) * 8
|
|
|
|
table.insert(game.vars.bullets, {
|
|
x = bullet_x,
|
|
y = bullet_y,
|
|
vel_x = math.cos(game.vars.ship_angle) * BULLET_SPEED + game.vars.ship_vel_x,
|
|
vel_y = math.sin(game.vars.ship_angle) * BULLET_SPEED + game.vars.ship_vel_y
|
|
})
|
|
|
|
game.vars.bullet_cooldown = 5
|
|
end
|
|
end
|
|
|
|
function update_asteroids()
|
|
for i = 1, #game.vars.asteroids do
|
|
local ast = game.vars.asteroids[i]
|
|
ast.x = ast.x + ast.vel_x
|
|
ast.y = ast.y + ast.vel_y
|
|
|
|
-- Wrap around screen
|
|
if ast.x < -ast.size then ast.x = game.width() + ast.size end
|
|
if ast.x > game.width() + ast.size then ast.x = -ast.size end
|
|
if ast.y < -ast.size then ast.y = game.height() + ast.size end
|
|
if ast.y > game.height() + ast.size then ast.y = -ast.size end
|
|
end
|
|
end
|
|
|
|
function check_collisions()
|
|
-- Bullet-asteroid collisions
|
|
for b = #game.vars.bullets, 1, -1 do
|
|
local bullet = game.vars.bullets[b]
|
|
for a = #game.vars.asteroids, 1, -1 do
|
|
local ast = game.vars.asteroids[a]
|
|
|
|
local dx = bullet.x - ast.x
|
|
local dy = bullet.y - ast.y
|
|
local dist = math.sqrt(dx * dx + dy * dy)
|
|
|
|
if dist < ast.size then
|
|
-- Hit!
|
|
table.remove(game.vars.bullets, b)
|
|
table.remove(game.vars.asteroids, a)
|
|
game.vars.score = game.vars.score + (4 - ast.size) * 50
|
|
|
|
-- Spawn smaller asteroids
|
|
if ast.size > 1 then
|
|
for _ = 1, 2 do
|
|
spawn_asteroid(ast.x, ast.y, ast.size - 1)
|
|
end
|
|
end
|
|
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Ship-asteroid collisions
|
|
for i = 1, #game.vars.asteroids do
|
|
local ast = game.vars.asteroids[i]
|
|
|
|
local dx = game.vars.ship_x - ast.x
|
|
local dy = game.vars.ship_y - ast.y
|
|
local dist = math.sqrt(dx * dx + dy * dy)
|
|
|
|
if dist < ast.size + 6 then
|
|
game.vars.state = STATE_GAME_OVER
|
|
end
|
|
end
|
|
end
|
|
|
|
function spawn_asteroid(x, y, size)
|
|
if size < 1 then return end
|
|
|
|
local speed = ASTEROID_SPEEDS[size]
|
|
local angle = math.random() * math.pi * 2
|
|
|
|
table.insert(game.vars.asteroids, {
|
|
x = x,
|
|
y = y,
|
|
size = size,
|
|
vel_x = math.cos(angle) * speed,
|
|
vel_y = math.sin(angle) * speed
|
|
})
|
|
end
|
|
|
|
function reset_game()
|
|
game.vars.score = 0
|
|
game.vars.level = 1
|
|
game.vars.ship_x = game.width() / 2
|
|
game.vars.ship_y = game.height() / 2
|
|
game.vars.ship_angle = 0
|
|
game.vars.ship_vel_x = 0
|
|
game.vars.ship_vel_y = 0
|
|
game.vars.bullets = {}
|
|
game.vars.asteroids = {}
|
|
game.vars.frame_count = 0
|
|
|
|
spawn_asteroid(game.width() / 2 - 40, game.height() / 2 - 40, 3)
|
|
spawn_asteroid(game.width() / 2 + 40, game.height() / 2 + 40, 3)
|
|
end
|