sonic-bluestreak/sonic-bluestreak.love/scenes/subgame/sonic-boost/actors/character.lua

541 lines
13 KiB
Lua
Raw Permalink Normal View History

local folder = "scenes.subgame.sonic-boost.actors."
local Entity = require(folder .. "parent")
2019-12-28 11:44:54 +01:00
local phys = require "datas.gamedata.physics"
local playerHeight = 32
local grindheight = 32
local Character = Entity:extend()
-- Un "personnage" est une entité unique, dans le sens où elle possède un controller
-- special, qui fait le lien entre elle et les différents éléments du niveau
-- Le code des personnages normaux et rivaux est exactement le même, les différence
-- sont géré par les controlleurs, qui s'occupent de leur envoyer les données
-- de touche, et d'effecter les réactions logique en cas de disparition de l'entité
-- (terminer le niveau pour un joueur, juste le détruire pour un rival)
-- Le personnage ne sait pas à l'intérieur de lui même s'il est contrôler par une
-- IA ou par un vrai joueur.
-- Il n'a pas non plus accès aux détails de la mission, qui sont aussi géré par
-- son controller special (vu par "self.charcontroller")
function Character:new(charcontroller, rail, character, id)
self.charcontroller = charcontroller
2019-02-06 07:51:20 +01:00
local world = self.charcontroller.controller.world
local rail = rail or 2
local y = 10 + (rail*20)
Character.super.new(self, world, "character", 16-8, y-8, 0, 16, 16, playerHeight)
self.name = "player"
self.charid = id or 1
self.yspeed = 0
self.xspeed = 0
self.zspeed = 0
self.ymove = false
self.onGround = true
self.groundBellow = true
self.groundTerrain = 0
self.isJumping = false
self.isInAerialAction = false
self.canAerialAction = true
self.isInAction = false
self.canAction = true
2019-02-06 07:51:20 +01:00
self.dash = false
self.dashCam = false
self:setDebugColor(0, 255, 0)
self:characterInit(character)
self:setSprite("character" .. self.charid, 32, 48, true)
self.depth = -1
self.rings = 0
self.score = 0
self.combo = 0
self.bonus = 0
self.turn = 1
self.life = 3
2019-02-06 07:51:20 +01:00
self.startx = self.x
self.starty = self.y
self.view = {}
self.view.x = self.x
self.view.y = self.y
self.view.dx = 0
2019-02-06 07:51:20 +01:00
self.relx = 0
self.relxspeed = 0
self.grind = false
self.ai = isAIControlled or false
end
function Character:characterInit(char)
self.data = game.characters:getCharacterData(char)
self.lifeicon = self.data.assets.lifeicon
end
function Character:update(dt)
self:updateTimers(dt)
-- On initialise les variables de directions
local dx, dy, dz = 0, 0, 0
-- On commence par récupérer les inputs fait sur le pad virtuel, qui est géré
-- dans core.input.keys. On le garde dans une variable qui sera utilisée dans
-- tout l'objet joueur
self.groundTerrain, self.groundBellow = self:getTerrain()
self.keys = self.charcontroller:getKeys()
self:sideSteps()
self:jumpAction()
self:jump()
self:normalAction()
self:applyVerticalVelocity(dt)
self:autoMove(dt)
self:snaptoRails(dt)
self:move()
end
-- ACTIONS FUNCTIONS --
function Character:sideSteps()
if self.grind == true then
if (self.keys["up"].isPressed) then
self.yspeed = - 1 * 60 * phys.side
self.ymove = true
elseif (self.keys["down"].isPressed) then
self.yspeed = 60 * phys.side
self.ymove = true
else
self.ymove = false
end
else
if (self.keys["up"].isDown) then
self.yspeed = - 1 * 60 * phys.side
2019-02-06 07:51:20 +01:00
self.ymove = true
elseif (self.keys["down"].isDown) then
self.yspeed = 60 * phys.side
2019-02-06 07:51:20 +01:00
self.ymove = true
else
self.ymove = false
end
end
end
function Character:jump()
if (self.keys["A"].isPressed) and (self.onGround) then
self.zspeed = 60 * self.data.stats.jmp
self.onGround = false
self.isJumping = true
self.isInAction = false
end
end
function Character:jumpAction()
if self.data.stats.jumpaction == "doublejump" then
if (self.keys["A"].isPressed) and (self.isJumping) and (self.canAerialAction) then
2019-02-06 07:51:20 +01:00
self.zspeed = 60 * self.data.stats.jumpaction_power
self.isInAerialAction = true
2019-02-06 07:51:20 +01:00
self.canAerialAction = false
end
2019-02-04 23:19:13 +01:00
elseif self.data.stats.jumpaction == "jumpdash" then
if (self.keys["A"].isPressed) and (self.isJumping) and (self.canAerialAction) then
2019-02-06 07:51:20 +01:00
self.zspeed = 60 * 1
self.xspeed = math.max(self.xspeed, self.data.stats.spd * 60 * 1.2)
2019-02-04 23:19:13 +01:00
self.isInAerialAction = true
2019-02-06 07:51:20 +01:00
self.canAerialAction = false
2019-02-04 23:23:49 +01:00
self.dashCam = true
self:addTimer("dashCam", .5)
2019-02-04 23:19:13 +01:00
end
end
end
function Character:normalAction()
if self.data.stats.action == "spinattack" then
if (self.keys["B"].isPressed) and (self.onGround) and (self.canAction) and (self.grind == false) then
2019-02-06 07:51:20 +01:00
self.xspeed = math.max(self.xspeed, self.data.stats.spd * 60 * 1.5)
self.isInAction = true
2019-02-06 07:51:20 +01:00
self.canAction = false
self.dashCam = true
self:addTimer("action", .5)
self:addTimer("action_cooldown", 1)
2019-02-04 23:23:49 +01:00
self:addTimer("dashCam", .5)
end
end
end
-- Moving functions --
function Character:move()
local cols, coll_number
self:setFilter()
self:updateRelativePosition()
self.x, self.y, self.z, cols, coll_number = Character.super.move(self)
for i=1, coll_number do
local col = cols[i]
if col.other.type == "collectible" then
col.other:pickedUp(self)
end
self.collisionResponse(cols[i])
end
end
function Character:updateRelativePosition()
local MAXVIEW = 80
local MINVIEW = -20
if not ((self.onGround == false) and (self.z < 0)) then
if (self.dashCam) or (self.dash) then
self.relx = math.min(self.relx + 0.4, MAXVIEW)
else
if self.keys["left"].isDown then
if self.relx > 0 then
self.relx = math.max(self.relx - 0.3, 0)
elseif self.relx <= 0 then
self.relx = math.max(self.relx - 0.1, MINVIEW)
end
elseif self.keys["right"].isDown then
if self.relx >= 0 then
if self.relx <= MAXVIEW*3/4 then
self.relx = math.min(self.relx + 0.2, MAXVIEW*3/4)
else
self.relx = math.max(self.relx - 0.1, MAXVIEW*3/4)
end
elseif self.relx < 0 then
self.relx = math.min(self.relx + 0.3, 0)
end
else
if self.relx > 0 then
self.relx = math.max(self.relx - 0.3, 0)
elseif self.relx < 0 then
self.relx = math.min(self.relx + 0.3, 0)
end
end
end
end
self.x = self.view.x + self.relx
end
function Character:getViewCenter()
local x, y, z
x = math.floor(self.view.x + self.w / 2)
y = math.floor(self.y + self.h / 2)
z = math.floor(self.z + self.d / 2)
return x, y, z
end
function Character:collisionResponse(other)
if other == nil then return 0 end
if other.type == "collectible" then
other:pickedUp()
end
end
function Character:setFilter()
self.filter = function(item, other)
if other.type == "collectible" then return "cross"
else return "cross" end
end
end
function Character:autoMove(dt)
local max = self.data.stats.spd
self.lockspeed = false
acc = phys.acc
if (self.groundTerrain == 1) and (self.z <= 0) then
self.dash = true
self:addTimer("dash", 2)
acc = phys.acc
end
if (self.groundTerrain == 2) and (self.z <= 0) then
max = phys.max * 1/3
self.dash = false
self.acc = acc*2
end
if (self.groundTerrain == 4) and (self.z <= 0) then
self.dash = true
self:addTimer("dash", 2)
self.zspeed = 60 * (phys.spring * 4/5)
self.onGround = false
self.isJumping = true
self.assets.sfx["jumpboard"]:play()
end
if (self.groundTerrain == 3) and (self.z <= 0) then
max = 0
self.dash = false
self.acc = acc*3
self.xspeed = self.xspeed * 3/4
--self.lockspeed = true
end
if (self.lockspeed == false) then
if self.dash == true then
self.xspeed = max * 60 * phys.boost
elseif self.grind == true then
self.xspeed = max * 60
else
if self.xspeed < (max*60) then
self.xspeed = self.xspeed + acc
elseif (self.xspeed - acc*2) > (phys.max*60) then
self.xspeed = self.xspeed - acc*2
else
self.xspeed = (max*60)
end
end
self.view.x = self.view.x + self.xspeed * dt
end
if self.x >= self.controller.world.width then
self:overflowLevel()
end
end
function Character:applyVerticalVelocity(dt)
if (self.groundBellow == false) and (self.onGround == true) then
self.onGround = false
end
local grindBellow = self.world:getGrindAtPoint(self.x, self.y, self.turn)
if (grindBellow == false) and (self.onGround == true) and self.z > 0 then
self.onGround = false
self.grind = false
end
if (self.onGround == false) then
self.zspeed = self.zspeed - phys.grv
if self.zspeed < 0 then
if (self.z + self.zspeed*dt <= 0) and (self.groundBellow == true) then
self.z = 0
self.onGround = true
self.isJumping = false
self.grind = false -- pas vraiment nécessaire de retirer le grind ici
self.isInAerialAction = false
self.canAerialAction = true
end
if (self.z + self.zspeed*dt <= grindheight) and (self.z + self.zspeed*dt > grindheight - 8) and (grindBellow == true) then
self.z = grindheight
self.onGround = true
self.isJumping = false
self.grind = true
self.isInAerialAction = false
self.canAerialAction = true
end
if (self.groundBellow == false) and (self.z < -64) then
self:die()
end
end
self.z = self.z + self.zspeed*dt
end
if self.ground == true then
self.zspeed = 0
end
end
function Character:snaptoRails(dt)
local centery = self.y + 8
local yrail = math.floor((centery - 10) / 20)
local newyrail = math.floor((centery + self.yspeed*dt - 10) / 20)
if (newyrail < 0) and (self.yspeed < 0) then
2019-02-06 07:51:20 +01:00
centery = 10
self.yspeed = 0
elseif (newyrail >= 4) and (self.yspeed > 0) then
2019-02-06 07:51:20 +01:00
centery = 90
self.yspeed = 0
else
if (self.ymove == false) and (yrail ~= newyrail) then
if self.yspeed > 0 then
centery = newyrail * 20 + 10
end
if self.yspeed < 0 then
centery = yrail * 20 + 10
end
self.yspeed = 0
else
centery = centery + self.yspeed*dt
end
end
self.y = centery - 8
end
---------- INTERACTION FUNCTIONS ---------
function Character:getTerrain()
local terrain, groundbellow
terrain = self.world:getTerrainAtPoint(self.x, self.y, self.turn)
if (terrain == 3) then
groundbellow = false
else
groundbellow = true
end
return terrain, groundbellow
end
function Character:addScore(score)
self.combo = self.combo + 1
self.bonus = math.floor(self.combo / 5) * 2
self.score = self.score + score + math.floor(1/10 * score * self.bonus)
self:addTimer("combo", 3)
end
---------- DRAWING FUNCTIONS ---------
function Character:animations()
self.assets.sprites["character" .. self.charid]:setCustomSpeed(math.abs(self.xspeed / 16))
if self.onGround then
if self.grind == true then
self:setAnimation("grind")
elseif self.isInAction == true then
self:setAnimation("action")
else
if self.xspeed == 0 then
self:setAnimation("idle")
elseif self.xspeed < phys.max*60 then
self:setAnimation("run2")
elseif self.xspeed >= phys.max*60 then
self:setAnimation("dash")
end
end
else
if self.isJumping then
self:setAnimation("jump")
else
self:setAnimation("fall")
end
end
end
function Character:setAnimation(animation)
if (self.animation ~= animation) then
self.animation = animation
self.assets.sprites["character" .. self.charid]:changeAnimation(animation, true)
end
end
function Character:draw()
self:animations()
if (self.z < 0) and (self.onGround == false) then
z = self.z
if self.z < -48 then z = -48 end
love.graphics.setScissor(0, 0, 424, self.y + 100)
end
self:drawSprite()
love.graphics.setScissor()
end
function Character:drawEcho()
self:animations()
if (self.z < 0) and (self.onGround == false) then
z = self.z
if self.z < -48 then z = -48 end
love.graphics.setScissor(0, 0, 424, self.y + 100)
end
self:drawSprite(self.world.width, 0)
love.graphics.setScissor()
end
function Character:drawShadow(tx, ty)
if (self.z < 0) and (self.onGround == false) then
return 0
end
Character.super.drawShadow(self, tx, ty)
end
function Character:drawShadowEcho()
if (self.z < 0) and (self.onGround == false) then
return 0
end
Character.super.drawShadow(self, self.world.width, 0)
end
---------- LEVEL FINISH FUNCTIONS ----------
function Character:overflowLevel()
self.charcontroller:newLap()
end
function Character:finishLevel()
--love.event.quit()
end
function Character:die()
if self.life == 0 then
self.charcontroller:die()
else
2019-02-06 07:51:20 +01:00
self.life = self.life - 1
self.view.x = self.startx
self.relx = 0
self.dash = false
self.dashCam = false
self.y = self.starty
self.z = 0
self.rings = 0
end
end
----- TIMERS -----
function Character:endedTimer(name)
if name == "dash" then
self.dash = false
elseif name == "combo" then
self.combo = 0
self.bonus = 0
elseif name == "action" then
self.isInAction = false
2019-02-04 23:23:49 +01:00
elseif name == "dashCam" then
self.dashCam = false
elseif name == "action_cooldown" then
self.canAction = true
end
end
return Character