Mothback/code/entities/player.lua

362 lines
8.9 KiB
Lua

Player = Entity:New()
function Player:New(x,y)
local o = Entity:New(x,y)
o.type = "player"
-- physics
o.moveSpeed = 1.3 -- gameworld pixels
o.zeroSpeed = 0.01 -- gameworld pixels
o.move_x = 0 -- gameworld pixels
o.noDriftFrames = 0 -- frames
o.airFriction = 0.01 -- gameworld pixels
o.groundFriction = 0.3 -- gameworld pixels
o.jumpImpulse = 3.5 -- gameworld pixels
o.coyoteValue = 5 -- frames
o.coyoteAmount = 5 -- int
o.dashCooldownTime = 0.1 -- seconds
o.dashCooldownTimer = 0 -- seconds
-- dash values
o.dashTimer = 0 -- seconds
o.dashTime = 0.15 -- seconds
o.dashDistance = 40 -- gameworld pixels
o.dashSpeed = o.dashDistance / (o.dashTime*60) -- pixels
o.dashCount = 1 -- int
o.dashAmount = 10 -- int
-- hook values
o.hookSwingSpeed = math.rad(0.05)
o.hookAnchor = {
x = nil,
y = nil
}
-- walljump values
o.walljumpNoDriftAmount = 12
o.walljumpImpulse = { x = 2.5, y = 3.5 }
o.walljumpFriction = 0.3 -- gameworld pixels
-- light values
o.lightRange = 40 -- screen pixels
-- status
o.canJump = true
o.canFall = true
o.canFriction = true
o.canHook = true
o.canWalljump = true
o.isDashing = false
o.isHooked = false
o.isSliding = false
o.isJumping = false
o.isOnGround = false
o.isOnLadder = false
o.maskType = animation.moth_mask
o.wallHit = 0
o.anchorRespawn = {
x = o.pos.x,
y = o.pos.y
}
-- sprite
o.target_offset = {x = 0, y = 0}
o.body = Animation:New(animation.nancy.idle)
o.mask = Animation:New(animation.moth_mask.idle)
o:centerOffset(o.body)
o:getBoundingBox(o.body,0,3,-1,-3)
-- lights
o.light = Light:New(o.pos.x,o.pos.y,o.lightRange)
table.insert(LoadedObjects.Entities,o)
o.id = #LoadedObjects.Entities
setmetatable(o, self)
self.__index = self
return o
end
function Player:Smart()
self:LightAdjust(self.target_offset.x,self.target_offset.y)
-- reset coyoteValue
if self.isOnGround then
self.coyoteValue = self.coyoteAmount
elseif self.coyoteValue > 0 then
self.coyoteValue = self.coyoteValue - 1
end
-- not dashing, normal movment
if self.dashTimer <= 0 then
-- horizontal movement
if not self.isHooked then
if self.noDriftFrames > 0 then
self.move_x = 0
elseif Keybind:CheckDown(Keybind.move.left) then
self.move_x = -1
self.vel.x = math.min(self.vel.x, -self.moveSpeed)
elseif Keybind:CheckDown(Keybind.move.right) then
self.move_x = 1
self.vel.x = math.max(self.vel.x, self.moveSpeed)
end
end
-- jump if on ground (coyotevalue) or if 0
if self.canJump and Keybind:CheckPressed(Keybind.move.jump) then
if self.canWalljump and self.wallHit ~= 0 then
self.isSliding = false
self.vel.y = -self.walljumpImpulse.y
self.vel.x = -self.walljumpImpulse.x * self.wallHit
self.move_x = 0
self.sprite_flip.x = -self.sprite_flip.x
self.noDriftFrames = self.walljumpNoDriftAmount
elseif self.coyoteValue > 0 then
self.vel.y = -self.jumpImpulse
self.coyoteValue = 0
end
end
end
-- dash timer
self.dashCooldownTimer = math.max(0,self.dashCooldownTimer - current_dt)
-- try to dash
if Keybind:CheckDown(Keybind.move.dash) then
if self.dashCooldownTimer == 0
and not self.isDashing
and self.dashCount > 0 then
self:Unhook()
-- state player
self.isDashing = true
self.dashCount = self.dashCount - 1
-- get dash direction
local vertical = 0
if Keybind:CheckDown(Keybind.move.down) then vertical = vertical + 1 end
if Keybind:CheckDown(Keybind.move.up) then vertical = vertical - 1 end
local horizontal = 0
if Keybind:CheckDown(Keybind.move.right) then horizontal = horizontal + 1 end
if Keybind:CheckDown(Keybind.move.left) then horizontal = horizontal - 1 end
-- if no direction, then dash forward
if horizontal == 0 and vertical == 0 then
horizontal = self.sprite_flip.x
end
-- set dash values
self.dashDirection = GetAngleFromVector(horizontal, vertical)
self.dashTimer = math.floor(self.dashTime * game.framerate)
end
else
-- not dashing!
self.isDashing = false
end
if self.canHook and Keybind:CheckPressed(Keybind.move.hook) then
if self.isHooked then
self:Unhook()
else
local anchor = self:CheckNearest("hook_anchor",self.hookDistance)
if anchor then
self.isHooked = true
self.hookDistance = anchor.hookDistance
self.hookAnchor = {
x = anchor.pos.x,
y = anchor.pos.y
}
end
end
end
end
function Player:DoPhysics()
if self.dashTimer <= 0 then
if self.isOnGround then
self.vel.x = self.vel.x * (1-self.groundFriction)
else
self.vel.x = self.vel.x * (1-self.airFriction)
end
self.isSliding = false
if self.wallHit == 0 then
self.vel.y = self.vel.y * (1-self.airFriction)
elseif self.noDriftFrames ~= self.walljumpNoDriftAmount then
self.isSliding = true
self.vel.y = self.vel.y * (1-self.walljumpFriction)
end
if math.abs(self.vel.x) < self.zeroSpeed then self.vel.x = 0 end
end
-- reset state
self.canFall = true
self.isOnGround = false
-- adjust timers
self.dashTimer = self.dashTimer - 1
self.noDriftFrames = self.noDriftFrames - 1
-- DASH STATE
if self.dashTimer > 0 then
self.canFall = false
-- dash particle
local particle_data = {
animation = self.body,
sprite_tint = HEX2RGB("#fed100"),
sprite_alpha = 0.5,
sprite_flip = {
x = self.sprite_flip.x,
y = self.sprite_flip.y
}
}
Particle:New(self.pos.x,self.pos.y,particle_data)
self.dashCooldownTimer = self.dashCooldownTime
-- dash movement
self.vel.x = self.dashSpeed * math.cos(self.dashDirection)
self.vel.y = self.dashSpeed * math.sin(self.dashDirection)
end
-- hook state
if self.isHooked then
self.move_x = 0
local hook = Vector(self.pos.x, self.pos.y, self.hookAnchor.x, self.hookAnchor.y)
local dist = math.min(GetVectorValue(hook), self.hookDistance)
local hook_angle = GetAngleFromVector(hook[1],hook[2])-math.rad(180)
if Keybind:CheckDown(Keybind.move.right) then
hook_angle = hook_angle - self.hookSwingSpeed
end
if Keybind:CheckDown(Keybind.move.left) then
hook_angle = hook_angle + self.hookSwingSpeed
end
local particle_data = {
animation = self.body,
sprite_tint = HEX2RGB("#fed100"),
sprite_alpha = 0.5,
sprite_flip = {
x = self.sprite_flip.x,
y = self.sprite_flip.y
}
}
Particle:New(self.pos.x,self.pos.y,particle_data)
local pos_x = self.hookAnchor.x + dist * math.cos(hook_angle)
local pos_y = self.hookAnchor.y + dist * math.sin(hook_angle)
self.vel.x = self.vel.x + pos_x - self.pos.x
self.vel.y = self.vel.y + pos_y - self.pos.y
self.pos.x = pos_x
self.pos.y = pos_y
end
if self.canFall then
-- not in dash
self.dashTimer = 0
self.vel.y = self.vel.y + gravity
end
-- horizontal collision
if not self:isCollidingAt(self.pos.x + self.vel.x, self.pos.y, LoadedObjects.Collisions) then
self.pos.x = self.pos.x + self.vel.x
self.wallHit = 0
else
self.wallHit = math.sign(self.vel.x)
self.vel.x = 0
end
-- vertical collision
if not self:isCollidingAt(self.pos.x, self.pos.y + self.vel.y, LoadedObjects.Collisions) then
self.pos.y = self.pos.y + self.vel.y
else
if self.vel.y > 0 then
self.isOnGround = true
self.dashCount = self.dashAmount
end
self.vel.y = 0
end
-- if u collision w hazard, respawn
if self:isCollidingAt(self.pos.x, self.pos.y, LoadedObjects.Hazards) then
self:Respawn()
end
end
function Player:Respawn()
self.pos.x = self.anchorRespawn.x
self.pos.y = self.anchorRespawn.y
end
function Player:HandleAnimation()
-- flip sprite to look in the direction is moving
if self.isHooked then
if self.vel.x ~= 0 then
self.sprite_flip.x = math.sign(self.vel.x)
end
elseif self.move_x ~= 0 then
self.sprite_flip.x = math.sign(self.move_x)
end
-- animation priority
if self.vel.y > 1.25 or self.isSliding then
self.body = self.body:ChangeTo(animation.nancy.fall)
self.mask = self.mask:ChangeTo(self.maskType.fall)
elseif self.vel.y < 0 then
self.body = self.body:ChangeTo(animation.nancy.jump)
self.mask = self.mask:ChangeTo(self.maskType.jump)
elseif self.vel.x + self.move_x ~= 0 then
self.body = self.body:ChangeTo(animation.nancy.run)
self.mask = self.mask:ChangeTo(self.maskType.run)
else
self.body = self.body:ChangeTo(animation.nancy.idle)
self.mask = self.mask:ChangeTo(self.maskType.idle)
end
-- special case: idle animation gets slower by time
if self.body.anim_path == animation.nancy.idle.path then
if self.body.anim_speed < 0.5 then
self.body.anim_speed = self.body.anim_speed + 0.001
end
end
if self.isHooked then
love.graphics.line(
-Camera.pos.x + self.pos.x,
-Camera.pos.y + self.pos.y,
-Camera.pos.x + self.hookAnchor.x,
-Camera.pos.y + self.hookAnchor.y
)
end
self.body:Animate()
self:Draw(self.body)
if self.dashCount > 0 then
self:Draw(self.mask)
end
self.move_x = 0
end
function Player:Unhook()
self.isHooked = false
self.hookAnchor = nil
end
function Player:Debug()
Entity.Debug(self)
love.graphics.print("wallHit: "..self.wallHit)
end