local Player = {} local data = require("data") local Vec = require("lib.brinevector") local controls = data.controls local BOLT_COLLIDER = {-3, -3, 3, 3} local function getDirection(up_key, down_key, left_key, right_key) local dx = (love.keyboard.isDown(right_key) and 1 or 0) - (love.keyboard.isDown(left_key) and 1 or 0) local dy = (love.keyboard.isDown(down_key) and 1 or 0) - (love.keyboard.isDown(up_key) and 1 or 0) return Vec(dx, dy).normalized end function Player:init() -- Spawn in the entity that the local player will be using local player = self:spawnPlayer() player.controllable = true end function Player:getMoveDirection() return getDirection( controls.move_up, controls.move_down, controls.move_left, controls.move_right ) end function Player:getAimDirection(player) if self.use_mouse_aim then local ScreenScaler = self.pool:getSystem(require("systems.screen-scaler")) local MAX_DIRECTIONS = 8 local angle_segment = math.pi*2/MAX_DIRECTIONS local angle = (Vec(ScreenScaler:getMousePosition()) - player.pos).angle local new_angle = math.floor(angle/angle_segment+0.5)*angle_segment return Vec(math.cos(new_angle), math.sin(new_angle)) else return getDirection( controls.aim_up, controls.aim_down, controls.aim_left, controls.aim_right ) end end function Player:addToGroup(group, e) if group == "player" then e.bolt_cooldown_timer = e.bolt_cooldown_timer or 0 e.aim_dir = e.aim_dir or Vec() e.move_dir = e.move_dir or Vec() end end function Player:update(dt) if love.keyboard.isDown(controls.aim_up, controls.aim_down, controls.aim_left, controls.aim_right) then self.use_mouse_aim = false end -- Update controls for "my" players local move_direction = self:getMoveDirection() for _, e in ipairs(self.pool.groups.controllable_player.entities) do -- Update where they are aiming/looking local aim_direction = self:getAimDirection(e) if e.aim_dir ~= aim_direction then e.aim_dir = aim_direction self.pool:emit("playerAimed", e) end -- Update acceleration to make the player move if e.move_dir ~= move_direction then e.move_dir = move_direction self.pool:emit("playerMoved", e) end -- Check if the player tried to shoot a bolt if love.keyboard.isDown(controls.shoot) then if self:tryShootingBolt(e) then self.pool:emit("playerShot", e) end end end -- Update each player for _, e in ipairs(self.pool.groups.player.entities) do e.acc = e.move_dir * e.speed -- Decrease bolt cooldown timer if e.bolt_cooldown_timer > 0 then e.bolt_cooldown_timer = math.max(0, e.bolt_cooldown_timer - dt) end -- If the player is nearby a non-moving bolt, pick it up for _, bolt in ipairs(self.pool.groups.bolt.entities) do if (e.pos-bolt.pos).length < 20 and bolt.vel.length < 3 then bolt.collider = nil bolt.bolt = nil bolt.sprite.variant = "idle" bolt.hidden = true self.pool:queue(bolt) table.insert(e.bolts, bolt) end end if e.acc.x ~= 0 or e.acc.y ~= 0 then e.sprite.variant = "walk" else e.sprite.variant = "idle" end if e.acc.x < 0 then e.sprite.flip = true elseif e.acc.x > 0 then e.sprite.flip = false end if #e.bolts > 0 then local bolt = e.bolts[1] if e.aim_dir.x ~= 0 or e.aim_dir.y ~= 0 then bolt.hidden = nil bolt.pos = e.pos + e.aim_dir * 15 else bolt.hidden = true end end end end function Player:mousemoved() self.use_mouse_aim = true end function Player:tryShootingBolt(player) if (player.aim_dir.x ~= 0 or player.aim_dir.y ~= 0) and player.bolt_cooldown_timer == 0 and #player.bolts > 0 then player.bolt_cooldown_timer = player.bolt_cooldown self:shootBolt(player) return true end end function Player:shootBolt(player) local bolt = table.remove(player.bolts, 1) if not bolt then return end bolt.pos = player.pos + player.aim_dir * 30 bolt.vel = player.aim_dir * player.bolt_speed bolt.bolt = true bolt.sprite.variant = "active" bolt.collider = BOLT_COLLIDER bolt.hidden = nil self.pool:queue(bolt) end function Player:storeBolt(player, bolt) bolt.collider = nil bolt.sprite.variant = "idle" bolt.hidden = true bolt.bolt = nil self.pool:queue(bolt) table.insert(player.bolts, bolt) end function Player:spawnPlayer() local player = self.pool:queue{ pos = Vec(100, 100), vel = Vec(0, 0), acc = Vec(), sprite = { name = "player", variant = "walk" }, friction = 0.998, speed = 800, bolt_speed = 2000, bolts = {}, bolt_cooldown = 0.1, collider = {-5, -5, 4, 8} } for _=1, 10 do local bolt = self.pool:queue{ pos = Vec(100, 100), vel = Vec(), acc = Vec(), sprite = { name = "bolt", variant = "idle", }, friction = 0.98, max_speed = 400, } self:storeBolt(player, bolt) end return player end return Player