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 table.insert(state.asteroids, state.actors.asteroids.new({ x = asteroid.x, y = asteroid.y, angle = angle1, stage = asteroid.stage - 1, })) table.insert(state.asteroids, state.actors.asteroids.new({ x = asteroid.x, y = asteroid.y, angle = angle2, stage = asteroid.stage - 1, })) 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