2025-11-12 21:31:35 -05:00
|
|
|
local love = require("love")
|
2025-11-16 12:06:35 -05:00
|
|
|
local phys = require("phys")
|
2025-11-17 20:21:42 -05:00
|
|
|
local inspect = require("inspect")
|
2025-11-12 21:31:35 -05:00
|
|
|
|
2025-11-14 21:06:24 -05:00
|
|
|
local function initGame()
|
|
|
|
|
return {
|
|
|
|
|
width = 800,
|
|
|
|
|
height = 600,
|
|
|
|
|
ship = {
|
2025-11-14 17:10:40 -05:00
|
|
|
radius = 30,
|
2025-11-14 21:06:24 -05:00
|
|
|
bullet = {
|
|
|
|
|
radius = 5,
|
2025-11-17 20:31:14 -05:00
|
|
|
recoil = 10,
|
2025-11-14 21:38:46 -05:00
|
|
|
speed = 500,
|
|
|
|
|
time = 4,
|
2025-11-15 20:46:59 -05:00
|
|
|
sound = love.sound.newSoundData('resources/bullet.mp3'),
|
2025-11-14 21:35:05 -05:00
|
|
|
},
|
2025-11-17 23:10:15 -05:00
|
|
|
sprite = love.graphics.newImage('resources/playerShip1_blue.png'),
|
2025-11-14 16:33:24 -05:00
|
|
|
},
|
2025-11-14 21:06:24 -05:00
|
|
|
asteroids = {
|
|
|
|
|
stages = {
|
2025-11-17 23:10:15 -05:00
|
|
|
{ speed = 120, radius = 15, sprite = love.graphics.newImage('resources/meteorBrown_tiny1.png') },
|
|
|
|
|
{ speed = 70, radius = 30, sprite = love.graphics.newImage('resources/meteorBrown_small1.png') },
|
|
|
|
|
{ speed = 50, radius = 50, sprite = love.graphics.newImage('resources/meteorBrown_med1.png') },
|
|
|
|
|
{ speed = 20, radius = 80, sprite = love.graphics.newImage('resources/meteorBrown_big1.png') },
|
2025-11-14 21:06:24 -05:00
|
|
|
},
|
2025-11-14 17:10:40 -05:00
|
|
|
},
|
2025-11-14 21:35:05 -05:00
|
|
|
controls = {
|
|
|
|
|
ship = {
|
|
|
|
|
turnRate = 10,
|
2025-11-15 14:25:47 -05:00
|
|
|
maxThrust = 200,
|
2025-11-14 21:35:05 -05:00
|
|
|
},
|
|
|
|
|
},
|
2025-11-14 16:33:24 -05:00
|
|
|
}
|
2025-11-14 21:06:24 -05:00
|
|
|
end
|
2025-11-14 17:10:40 -05:00
|
|
|
|
2025-11-17 20:44:11 -05:00
|
|
|
-- The constant properties of the game.
|
2025-11-14 21:06:24 -05:00
|
|
|
local game = initGame()
|
|
|
|
|
|
2025-11-14 21:27:55 -05:00
|
|
|
local function initState()
|
|
|
|
|
local asteroids = {
|
|
|
|
|
{
|
|
|
|
|
x = 100,
|
|
|
|
|
y = 100,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
x = game.width - 100,
|
|
|
|
|
y = 100,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
x = game.width / 2,
|
|
|
|
|
y = game.height - 100,
|
2025-11-14 17:10:40 -05:00
|
|
|
}
|
2025-11-14 21:27:55 -05:00
|
|
|
}
|
|
|
|
|
for _, asteroid in ipairs(asteroids) do
|
2025-11-17 20:21:42 -05:00
|
|
|
asteroid.angle = love.math.random() * phys.tau
|
2025-11-14 21:27:55 -05:00
|
|
|
asteroid.stage = #game.asteroids.stages
|
2025-11-15 21:43:10 -05:00
|
|
|
local stage = game.asteroids.stages[asteroid.stage]
|
|
|
|
|
asteroid.speed = stage.speed
|
2025-11-17 23:10:15 -05:00
|
|
|
asteroid.facing = math.random() * phys.tau
|
2025-11-17 23:15:21 -05:00
|
|
|
asteroid.spin = 0.1 + math.random() * 0.1
|
2025-11-15 21:43:10 -05:00
|
|
|
asteroid.radius = stage.radius
|
2025-11-17 23:10:15 -05:00
|
|
|
asteroid.sprite = stage.sprite
|
2025-11-14 16:33:24 -05:00
|
|
|
end
|
2025-11-15 14:25:47 -05:00
|
|
|
local joystick = nil
|
|
|
|
|
if love.joystick.getJoystickCount() == 1 then
|
|
|
|
|
joystick = love.joystick.getJoysticks()[1]
|
|
|
|
|
end
|
2025-11-14 21:27:55 -05:00
|
|
|
return {
|
2025-11-20 20:12:23 -05:00
|
|
|
debug = true,
|
2025-11-14 21:27:55 -05:00
|
|
|
ship = {
|
|
|
|
|
x = game.width / 2,
|
|
|
|
|
y = game.height / 2,
|
2025-11-17 20:21:42 -05:00
|
|
|
angle = 0,
|
|
|
|
|
speed = 0,
|
2025-11-17 23:10:15 -05:00
|
|
|
spin = 0,
|
|
|
|
|
facing = phys.tau * 3 / 4,
|
2025-11-16 12:06:35 -05:00
|
|
|
radius = game.ship.radius,
|
2025-11-14 21:27:55 -05:00
|
|
|
},
|
|
|
|
|
bullets = {},
|
|
|
|
|
asteroids = asteroids,
|
2025-11-15 14:25:47 -05:00
|
|
|
controls = {
|
|
|
|
|
joystick = joystick,
|
|
|
|
|
}
|
2025-11-14 21:27:55 -05:00
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
2025-11-17 20:44:11 -05:00
|
|
|
-- The varying properties of a game being played.
|
2025-11-14 21:27:55 -05:00
|
|
|
local state = {}
|
|
|
|
|
|
|
|
|
|
local function reset()
|
|
|
|
|
state = initState()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function love.load()
|
2025-11-14 17:10:40 -05:00
|
|
|
reset()
|
2025-11-12 21:35:42 -05:00
|
|
|
end
|
|
|
|
|
|
2025-11-16 12:06:35 -05:00
|
|
|
-- updates the actor's location by applying the passage of time within the game world
|
2025-11-15 21:43:10 -05:00
|
|
|
local function update(actor, dt)
|
2025-11-16 12:06:35 -05:00
|
|
|
phys.update(actor, dt)
|
|
|
|
|
actor.x = actor.x % game.width
|
|
|
|
|
actor.y = actor.y % game.height
|
2025-11-15 21:43:10 -05:00
|
|
|
end
|
|
|
|
|
|
2025-11-12 21:31:35 -05:00
|
|
|
function love.update(dt)
|
2025-11-15 14:25:47 -05:00
|
|
|
if state.controls.joystick ~= nil then
|
|
|
|
|
local thrust = state.controls.joystick:getGamepadAxis('triggerright') * game.controls.ship.maxThrust
|
2025-11-17 20:21:42 -05:00
|
|
|
if thrust > 0 then
|
|
|
|
|
phys.apply(state.ship, { speed = thrust * dt, angle = state.ship.facing })
|
|
|
|
|
end
|
2025-11-15 16:29:07 -05:00
|
|
|
|
|
|
|
|
local spin = state.controls.joystick:getGamepadAxis('leftx') * game.controls.ship.turnRate
|
2025-11-17 20:31:14 -05:00
|
|
|
state.ship.facing = (state.ship.facing + spin * dt)
|
2025-11-12 21:31:35 -05:00
|
|
|
end
|
2025-11-17 20:21:42 -05:00
|
|
|
update(state.ship, dt)
|
2025-11-13 16:29:57 -05:00
|
|
|
|
2025-11-14 21:27:55 -05:00
|
|
|
for bulletIndex = #state.bullets, 1, -1 do
|
|
|
|
|
local bullet = state.bullets[bulletIndex]
|
2025-11-13 16:35:51 -05:00
|
|
|
bullet.timeLeft = bullet.timeLeft - dt
|
|
|
|
|
if bullet.timeLeft <= 0 then
|
2025-11-14 21:27:55 -05:00
|
|
|
table.remove(state.bullets, bulletIndex)
|
2025-11-13 16:35:51 -05:00
|
|
|
else
|
2025-11-15 21:43:10 -05:00
|
|
|
update(bullet, dt)
|
2025-11-13 16:35:51 -05:00
|
|
|
end
|
2025-11-14 21:27:55 -05:00
|
|
|
for asteroidIndex = #state.asteroids, 1, -1 do
|
|
|
|
|
local asteroid = state.asteroids[asteroidIndex]
|
2025-11-16 12:06:35 -05:00
|
|
|
if phys.intersects(bullet, asteroid) then
|
2025-11-14 21:27:55 -05:00
|
|
|
table.remove(state.bullets, bulletIndex)
|
2025-11-14 17:10:40 -05:00
|
|
|
if asteroid.stage > 1 then
|
2025-11-17 20:31:14 -05:00
|
|
|
local angle1 = love.math.random() * phys.tau
|
|
|
|
|
local angle2 = angle1 - phys.tau / 2
|
2025-11-17 23:10:15 -05:00
|
|
|
local stage = game.asteroids.stages[asteroid.stage - 1]
|
|
|
|
|
local radius = stage.radius
|
|
|
|
|
local speed = stage.speed
|
|
|
|
|
local sprite = stage.sprite
|
2025-11-14 21:27:55 -05:00
|
|
|
table.insert(state.asteroids, {
|
2025-11-14 17:10:40 -05:00
|
|
|
x = asteroid.x,
|
|
|
|
|
y = asteroid.y,
|
|
|
|
|
angle = angle1,
|
|
|
|
|
stage = asteroid.stage - 1,
|
2025-11-15 21:43:10 -05:00
|
|
|
radius = radius,
|
|
|
|
|
speed = speed,
|
2025-11-17 23:10:15 -05:00
|
|
|
facing = math.random() * phys.tau,
|
2025-11-17 23:15:21 -05:00
|
|
|
spin = 0.1 + math.random() * 0.1,
|
2025-11-17 23:10:15 -05:00
|
|
|
sprite = sprite,
|
2025-11-14 17:10:40 -05:00
|
|
|
})
|
2025-11-14 21:27:55 -05:00
|
|
|
table.insert(state.asteroids, {
|
2025-11-14 17:10:40 -05:00
|
|
|
x = asteroid.x,
|
|
|
|
|
y = asteroid.y,
|
|
|
|
|
angle = angle2,
|
|
|
|
|
stage = asteroid.stage - 1,
|
2025-11-15 21:43:10 -05:00
|
|
|
radius = radius,
|
|
|
|
|
speed = speed,
|
2025-11-17 23:10:15 -05:00
|
|
|
facing = math.random() * phys.tau,
|
2025-11-17 23:15:21 -05:00
|
|
|
spin = 0.1 + math.random() * 0.1,
|
2025-11-17 23:10:15 -05:00
|
|
|
sprite = sprite,
|
2025-11-14 17:10:40 -05:00
|
|
|
})
|
|
|
|
|
end
|
2025-11-14 21:27:55 -05:00
|
|
|
table.remove(state.asteroids, asteroidIndex)
|
2025-11-14 16:50:50 -05:00
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
2025-11-13 16:29:57 -05:00
|
|
|
end
|
|
|
|
|
|
2025-11-14 21:27:55 -05:00
|
|
|
for _, asteroid in ipairs(state.asteroids) do
|
2025-11-15 21:43:10 -05:00
|
|
|
update(asteroid, dt)
|
2025-11-14 16:41:04 -05:00
|
|
|
|
2025-11-16 12:06:35 -05:00
|
|
|
if phys.intersects(state.ship, asteroid) then
|
2025-11-14 17:10:40 -05:00
|
|
|
reset()
|
2025-11-14 16:41:04 -05:00
|
|
|
break
|
|
|
|
|
end
|
2025-11-14 16:33:24 -05:00
|
|
|
end
|
2025-11-14 17:10:40 -05:00
|
|
|
|
2025-11-14 21:27:55 -05:00
|
|
|
if #state.asteroids == 0 then
|
2025-11-17 20:31:14 -05:00
|
|
|
reset()
|
2025-11-14 17:10:40 -05:00
|
|
|
end
|
2025-11-12 21:31:35 -05:00
|
|
|
end
|
|
|
|
|
|
2025-11-15 16:42:02 -05:00
|
|
|
function love.gamepadpressed(joystick, button)
|
|
|
|
|
if joystick ~= state.controls.joystick then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
if button == "b" then
|
|
|
|
|
-- debounce, perhaps
|
2025-11-15 20:46:59 -05:00
|
|
|
local bullet = {
|
2025-11-17 20:21:42 -05:00
|
|
|
x = state.ship.x + math.cos(state.ship.facing) * game.ship.radius,
|
|
|
|
|
y = state.ship.y + math.sin(state.ship.facing) * game.ship.radius,
|
|
|
|
|
angle = state.ship.facing,
|
2025-11-15 21:43:10 -05:00
|
|
|
speed = game.ship.bullet.speed,
|
|
|
|
|
radius = game.ship.bullet.radius,
|
2025-11-15 16:42:02 -05:00
|
|
|
timeLeft = game.ship.bullet.time,
|
2025-11-15 20:46:59 -05:00
|
|
|
sound = love.audio.newSource(game.ship.bullet.sound),
|
2025-11-17 23:10:15 -05:00
|
|
|
facing = state.ship.facing,
|
|
|
|
|
spin = 0,
|
2025-11-15 20:46:59 -05:00
|
|
|
}
|
2025-11-17 20:44:11 -05:00
|
|
|
phys.apply(bullet, state.ship)
|
2025-11-15 20:46:59 -05:00
|
|
|
table.insert(state.bullets, bullet)
|
|
|
|
|
bullet.sound:play()
|
2025-11-17 20:31:14 -05:00
|
|
|
phys.apply(state.ship, { speed = game.ship.bullet.recoil, angle = state.ship.facing + phys.tau / 2 })
|
2025-11-17 20:21:42 -05:00
|
|
|
end
|
|
|
|
|
if button == "x" then
|
|
|
|
|
print(inspect(state.ship))
|
2025-11-15 16:42:02 -05:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2025-11-12 21:31:35 -05:00
|
|
|
function love.draw()
|
2025-11-20 20:12:23 -05:00
|
|
|
if state.debug then
|
|
|
|
|
love.graphics.setColor(1, 1, 1)
|
|
|
|
|
love.graphics.line(game.width / 2, 0, game.width / 2, game.height)
|
|
|
|
|
love.graphics.line(0, game.height / 2, game.width, game.height / 2)
|
|
|
|
|
end
|
2025-11-12 21:31:35 -05:00
|
|
|
for y = -1, 1 do
|
|
|
|
|
for x = -1, 1 do
|
|
|
|
|
love.graphics.origin()
|
2025-11-14 21:06:24 -05:00
|
|
|
love.graphics.translate(x * game.width, y * game.height)
|
2025-11-20 20:12:23 -05:00
|
|
|
|
|
|
|
|
love.graphics.push()
|
|
|
|
|
love.graphics.translate(state.ship.x, state.ship.y)
|
2025-11-15 20:32:13 -05:00
|
|
|
love.graphics.setColor(1, 1, 1)
|
|
|
|
|
local scale = 2 * game.ship.radius / math.max(game.ship.sprite:getWidth(), game.ship.sprite:getHeight())
|
|
|
|
|
love.graphics.draw(game.ship.sprite,
|
2025-11-20 20:12:23 -05:00
|
|
|
0,
|
|
|
|
|
0,
|
2025-11-17 20:31:14 -05:00
|
|
|
(state.ship.facing + phys.tau / 4) % phys.tau,
|
2025-11-15 20:32:13 -05:00
|
|
|
scale,
|
|
|
|
|
scale,
|
|
|
|
|
game.ship.sprite:getWidth() / 2,
|
|
|
|
|
game.ship.sprite:getHeight() / 2
|
2025-11-12 21:31:35 -05:00
|
|
|
)
|
2025-11-20 20:12:23 -05:00
|
|
|
-- TODO rocket exhaust
|
|
|
|
|
love.graphics.pop()
|
2025-11-12 21:35:42 -05:00
|
|
|
|
2025-11-14 21:27:55 -05:00
|
|
|
for _, bullet in ipairs(state.bullets) do
|
2025-11-12 21:35:42 -05:00
|
|
|
love.graphics.setColor(0, 1, 0)
|
2025-11-14 21:06:24 -05:00
|
|
|
love.graphics.circle('fill', bullet.x, bullet.y, game.ship.bullet.radius)
|
2025-11-12 21:35:42 -05:00
|
|
|
end
|
2025-11-14 16:33:24 -05:00
|
|
|
|
2025-11-14 21:27:55 -05:00
|
|
|
for _, asteroid in ipairs(state.asteroids) do
|
2025-11-17 23:10:15 -05:00
|
|
|
love.graphics.setColor(1, 1, 1)
|
|
|
|
|
scale = 2 * asteroid.radius / math.max(asteroid.sprite:getWidth(), asteroid.sprite:getHeight())
|
|
|
|
|
love.graphics.draw(asteroid.sprite,
|
|
|
|
|
asteroid.x,
|
|
|
|
|
asteroid.y,
|
|
|
|
|
asteroid.facing,
|
|
|
|
|
scale,
|
|
|
|
|
scale,
|
|
|
|
|
asteroid.sprite:getWidth() / 2,
|
|
|
|
|
asteroid.sprite:getHeight() / 2
|
|
|
|
|
)
|
2025-11-14 16:33:24 -05:00
|
|
|
end
|
2025-11-12 21:31:35 -05:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|