lovexp/asteroids/main.lua

251 lines
8.1 KiB
Lua

local love = require("love")
local phys = require("phys")
local inspect = require("inspect")
local actors = require("actors")
local function initGame()
return {
width = 800,
height = 600,
ship = {
radius = 30,
bullet = {
radius = 5,
recoil = 10,
speed = 500,
time = 4,
sound = love.sound.newSoundData('resources/bullet.mp3'),
},
sprite = love.graphics.newImage('resources/playerShip1_blue.png'),
},
asteroids = {
stages = {
{ 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') },
},
},
controls = {
ship = {
turnRate = 10,
maxThrust = 200,
},
},
}
end
-- The constant properties of the game.
local game = initGame()
local function initState()
---@diagnostic disable-next-line: redefined-local
local actors = actors.buildActors(game)
local asteroids = {
{
x = 100,
y = 100,
},
{
x = game.width - 100,
y = 100,
},
{
x = game.width / 2,
y = game.height - 100,
}
}
for i, template in ipairs(asteroids) do
asteroids[i] = actors.asteroids.new(template)
end
local joystick = nil
if love.joystick.getJoystickCount() == 1 then
joystick = love.joystick.getJoysticks()[1]
end
return {
debug = true,
actors = actors,
ship = {
x = game.width / 2,
y = game.height / 2,
angle = 0,
speed = 0,
spin = 0,
facing = phys.tau * 3 / 4,
radius = game.ship.radius,
},
bullets = {},
asteroids = asteroids,
controls = {
joystick = joystick,
}
}
end
-- The varying properties of a game being played.
local state = {}
local function reset()
state = initState()
end
function love.load()
reset()
end
-- updates the actor's location by applying the passage of time within the game world
local function update(actor, dt)
phys.update(actor, dt)
actor.x = actor.x % game.width
actor.y = actor.y % game.height
end
function love.update(dt)
if state.controls.joystick ~= nil then
local thrust = state.controls.joystick:getGamepadAxis('triggerright') * game.controls.ship.maxThrust
if thrust > 0 then
phys.apply(state.ship, { speed = thrust * dt, angle = state.ship.facing })
end
local spin = state.controls.joystick:getGamepadAxis('leftx') * game.controls.ship.turnRate
state.ship.facing = (state.ship.facing + spin * dt)
end
update(state.ship, dt)
for bulletIndex = #state.bullets, 1, -1 do
local bullet = state.bullets[bulletIndex]
bullet.timeLeft = bullet.timeLeft - dt
if bullet.timeLeft <= 0 then
table.remove(state.bullets, bulletIndex)
else
update(bullet, dt)
end
for asteroidIndex = #state.asteroids, 1, -1 do
local asteroid = state.asteroids[asteroidIndex]
if phys.intersects(bullet, asteroid) then
table.remove(state.bullets, bulletIndex)
if asteroid.stage > 1 then
local angle1 = love.math.random() * phys.tau
local angle2 = angle1 - phys.tau / 2
local stage = game.asteroids.stages[asteroid.stage - 1]
local radius = stage.radius
local speed = stage.speed
local sprite = stage.sprite
table.insert(state.asteroids, {
x = asteroid.x,
y = asteroid.y,
angle = angle1,
stage = asteroid.stage - 1,
radius = radius,
speed = speed,
facing = math.random() * phys.tau,
spin = 0.1 + math.random() * 0.1,
sprite = sprite,
})
table.insert(state.asteroids, {
x = asteroid.x,
y = asteroid.y,
angle = angle2,
stage = asteroid.stage - 1,
radius = radius,
speed = speed,
facing = math.random() * phys.tau,
spin = 0.1 + math.random() * 0.1,
sprite = sprite,
})
end
table.remove(state.asteroids, asteroidIndex)
break
end
end
end
for _, asteroid in ipairs(state.asteroids) do
update(asteroid, dt)
if phys.intersects(state.ship, asteroid) then
reset()
break
end
end
if #state.asteroids == 0 then
reset()
end
end
function love.gamepadpressed(joystick, button)
if joystick ~= state.controls.joystick then
return
end
if button == "b" then
-- debounce, perhaps
local bullet = {
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,
speed = game.ship.bullet.speed,
radius = game.ship.bullet.radius,
timeLeft = game.ship.bullet.time,
sound = love.audio.newSource(game.ship.bullet.sound),
facing = state.ship.facing,
spin = 0,
}
phys.apply(bullet, state.ship)
table.insert(state.bullets, bullet)
bullet.sound:play()
phys.apply(state.ship, { speed = game.ship.bullet.recoil, angle = state.ship.facing + phys.tau / 2 })
end
if button == "x" then
print(inspect(state.ship))
end
end
function love.draw()
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
for y = -1, 1 do
for x = -1, 1 do
love.graphics.origin()
love.graphics.translate(x * game.width, y * game.height)
love.graphics.push()
love.graphics.translate(state.ship.x, state.ship.y)
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,
0,
0,
(state.ship.facing + phys.tau / 4) % phys.tau,
scale,
scale,
game.ship.sprite:getWidth() / 2,
game.ship.sprite:getHeight() / 2
)
-- TODO rocket exhaust
love.graphics.pop()
for _, bullet in ipairs(state.bullets) do
love.graphics.setColor(0, 1, 0)
love.graphics.circle('fill', bullet.x, bullet.y, game.ship.bullet.radius)
end
for _, asteroid in ipairs(state.asteroids) do
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
)
end
end
end
end