summaryrefslogtreecommitdiff
path: root/player.lua
diff options
context:
space:
mode:
Diffstat (limited to 'player.lua')
-rw-r--r--player.lua181
1 files changed, 181 insertions, 0 deletions
diff --git a/player.lua b/player.lua
new file mode 100644
index 0000000..4e39f21
--- /dev/null
+++ b/player.lua
@@ -0,0 +1,181 @@
+local Entity = require("entity")
+local Animation = require("animation")
+
+local Player = {}
+Player.__index = Player
+setmetatable(Player, { __index = Entity })
+
+local MOVE_SPEED = 70
+local JUMP_FORCE = -200
+local GROUND_LAYER = "ground"
+
+function Player.new(world, spawnX, spawnY)
+ local w, h = 16, 16
+ local pw, ph = 8, 8 -- physics body size
+ local self = setmetatable(Entity:new(spawnX or 0, spawnY or 0, w, h, pw, ph), Player)
+
+ self.directionState = { 'side', 'up', 'down', 'side_up', 'side_down' }
+ self.directionIndex = 1
+ self.direction = self.directionState[self.directionIndex]
+
+ self.availableJumps = 0
+ self:enablePhysics(world, "dynamic")
+
+ self.animations = {
+ idle = Animation.new("assets/player/idle.png", 16, 16, 0.6),
+ running = Animation.new("assets/player/running.png", 16, 16, 1),
+ going_up = Animation.new("assets/player/going_up.png", 16, 16, 0.8),
+ going_down = Animation.new("assets/player/going_down.png", 16, 16, 0.5),
+ stop_running = Animation.new("assets/player/stop_running.png", 16, 16, 0.3),
+ ground_hit = Animation.new("assets/player/ground_hit.png", 16, 16, 0.3)
+ }
+ if love and love.filesystem and love.filesystem.getDirectoryItems then
+ local ok, items = pcall(love.filesystem.getDirectoryItems, "assets/player")
+ if ok and items then
+ for _, filename in ipairs(items) do
+ if filename:match("%.png$") then
+ local name = filename:gsub("%.png$", "")
+ if not self.animations[name] then
+ self.animations[name] = Animation.new("assets/player/" .. filename, 16, 16, 0.3)
+ end
+ end
+ end
+ end
+ end
+
+ self.animations.ground_hit.looping = false
+ self.animations.stop_running.looping = false
+
+ self.currentAnim = self.animations.idle
+
+ self.lastFacing = 1
+
+ self.state = "idle"
+ self.wasOnFloor = false
+
+ self.vx = 0
+ self.vy = 0
+
+ self.grounded = false
+ return self
+end
+
+function Player:trackDirectionByKeyPressed()
+ local left = love.keyboard.isDown("left", "a")
+ local right = love.keyboard.isDown("right", "d")
+ local up = love.keyboard.isDown("up", "w")
+ local down = love.keyboard.isDown("down", "s")
+
+ if up and left then
+ self.directionIndex = 4
+ elseif up and right then
+ self.directionIndex = 4
+ elseif down and left then
+ self.directionIndex = 5
+ elseif down and right then
+ self.directionIndex = 5
+
+ elseif up then
+ self.directionIndex = 2
+ elseif down then
+ self.directionIndex = 3
+ elseif left then
+ self.directionIndex = 1
+ elseif right then
+ self.directionIndex = 1
+ end
+
+ self.direction = self.directionState[self.directionIndex]
+end
+
+function Player:update(dt)
+ self:syncFromPhysicsBody()
+ self:trackDirectionByKeyPressed()
+
+ local vx, vy = self.body:getLinearVelocity()
+ self.vx = vx
+ self.vy = vy
+
+ local c = self.contact or { floor = 0, wall = 0, ceiling = 0 }
+ local onFloor = c.floor == 1
+ local prevOnFloor = self.wasOnFloor
+ local prevState = self.state
+
+ local move = 0
+ if love.keyboard.isDown("left", "a") then move = move - 1 end
+ if love.keyboard.isDown("right", "d") then move = move + 1 end
+
+ if onFloor then
+ self.availableJumps = 1
+ if move ~= 0 then
+ self.lastFacing = move
+ self.body:setLinearVelocity(move * MOVE_SPEED, vy)
+ else
+ self.body:setLinearVelocity(0, vy)
+ end
+ else
+ if move ~= 0 then self.lastFacing = move end
+ self.body:setLinearVelocity(move * MOVE_SPEED * 0.7, vy)
+ end
+
+ local desiredState
+ if onFloor then
+ desiredState = (move ~= 0) and "running" or "idle"
+ else
+ desiredState = (vy < 0) and "jumping" or "falling"
+ end
+
+ if self.state == "ground_hit" or self.state == "stop_running" then
+ if not onFloor or self.currentAnim:isFinished() then
+ self.state = desiredState
+ end
+ else
+ local wasAirborne = prevState == "jumping" or prevState == "falling"
+ if onFloor and not prevOnFloor and wasAirborne then
+ self.state = "ground_hit"
+ self.animations.ground_hit:reset()
+ elseif onFloor and prevState == "running" and move == 0 then
+ self.state = "stop_running"
+ self.animations.stop_running:reset()
+ else
+ self.state = desiredState
+ end
+ end
+
+ self.wasOnFloor = onFloor
+
+ local animMap = {
+ idle = self.animations.idle,
+ running = self.animations.running,
+ jumping = self.animations.going_up,
+ falling = self.animations.going_down,
+ stop_running = self.animations.stop_running,
+ ground_hit = self.animations.ground_hit,
+ }
+ self.currentAnim = animMap[self.state] or self.animations.idle
+ self.currentAnim:update(dt)
+end
+
+function Player:jump()
+ local c = self.contact or { floor = 0, wall = 0, ceiling = 0 }
+ local canJump = (c.floor == 1 or c.wall == 1) and (self.availableJumps or 0) > 0
+ if canJump then
+ self.body:setLinearVelocity(self.body:getLinearVelocity(), JUMP_FORCE)
+ self.state = "jumping"
+ self.animations.going_up:reset()
+ self.availableJumps = self.availableJumps - 1
+ return true
+ end
+ return false
+end
+
+function Player:draw()
+ local flip = (self.lastFacing < 0)
+ self.currentAnim:draw(self.x, self.y, flip)
+end
+
+function Player:getBody()
+ return self.body
+end
+
+return Player