scene: add back level code from an earlier boost version
This commit is contained in:
parent
24dda63900
commit
81afa3ff11
19 changed files with 2114 additions and 0 deletions
|
@ -0,0 +1,7 @@
|
||||||
|
local ActorIndex = {}
|
||||||
|
local Actor = "scenes.levels.actors"
|
||||||
|
|
||||||
|
ActorIndex[1] = Actor.Ring
|
||||||
|
ActorIndex[2] = Actor.Crystal
|
||||||
|
|
||||||
|
return ActorIndex
|
10
sonic-boost.love/scenes/subgame/sonic-boost/actors/block.lua
Normal file
10
sonic-boost.love/scenes/subgame/sonic-boost/actors/block.lua
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
local Block = Entity:extend()
|
||||||
|
|
||||||
|
function Block:new(level, x, y, z, w, h, d) -- On enregistre une nouvelle entité, avec par défaut sa hitbox.
|
||||||
|
Block.super.new(self, level, "block", x-16, y-10, 0, 31, 20, d)
|
||||||
|
self.name = "block"
|
||||||
|
|
||||||
|
self:setDebugColor(255, 0, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Block
|
466
sonic-boost.love/scenes/subgame/sonic-boost/actors/character.lua
Normal file
466
sonic-boost.love/scenes/subgame/sonic-boost/actors/character.lua
Normal file
|
@ -0,0 +1,466 @@
|
||||||
|
local Entity = require "scenes.levels.actors.parent"
|
||||||
|
local phys = require "datas.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
|
||||||
|
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
|
||||||
|
|
||||||
|
self.dash = 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
|
||||||
|
|
||||||
|
self.startx = self.x
|
||||||
|
self.starty = self.y
|
||||||
|
|
||||||
|
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 game.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
|
||||||
|
self.ymove = true
|
||||||
|
elseif (self.keys["down"].isDown) then
|
||||||
|
self.yspeed = 60 * phys.side
|
||||||
|
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
|
||||||
|
self.zspeed = 60 * self.data.stats.jumpaction_power
|
||||||
|
self.isInAerialAction = true
|
||||||
|
self.canAerialAction = false
|
||||||
|
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
|
||||||
|
self.xspeed = math.max(self.xspeed, self.data.stats.spd * 60)
|
||||||
|
self.isInAction = true
|
||||||
|
self.canAction = false
|
||||||
|
self:addTimer("action", .5)
|
||||||
|
self:addTimer("action_cooldown", 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Moving functions --
|
||||||
|
|
||||||
|
function Character:move()
|
||||||
|
local cols, coll_number
|
||||||
|
|
||||||
|
self:setFilter()
|
||||||
|
|
||||||
|
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: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.x = self.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
|
||||||
|
centery = 10
|
||||||
|
self.yspeed = 0
|
||||||
|
elseif (newyrail >= 4) and (self.yspeed > 0) then
|
||||||
|
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
|
||||||
|
self.life = self.life - 1
|
||||||
|
self.x = self.startx
|
||||||
|
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
|
||||||
|
elseif name == "action_cooldown" then
|
||||||
|
self.canAction = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Character
|
|
@ -0,0 +1,21 @@
|
||||||
|
local Entity = require("scenes.levels.actors.parent")
|
||||||
|
local Crystal = Entity:extend()
|
||||||
|
|
||||||
|
function Crystal:new(world, x, y, z)
|
||||||
|
local z = z or 8
|
||||||
|
Crystal.super.new(self, world, "collectible", x-8, y-8, z, 16, 16, 16)
|
||||||
|
|
||||||
|
self:setSprite("crystal", 8, 16, true)
|
||||||
|
|
||||||
|
self:setDebugColor(255, 255, 0)
|
||||||
|
|
||||||
|
self.depth = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function Crystal:pickedUp(player)
|
||||||
|
player:addScore(10)
|
||||||
|
|
||||||
|
self:destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
return Crystal
|
12
sonic-boost.love/scenes/subgame/sonic-boost/actors/init.lua
Normal file
12
sonic-boost.love/scenes/subgame/sonic-boost/actors/init.lua
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
local Actor = {}
|
||||||
|
|
||||||
|
Actor.Character = require "scenes.levels.actors.character"
|
||||||
|
Actor.Ring = require "scenes.levels.actors.ring"
|
||||||
|
Actor.Crystal = require "scenes.levels.actors.crystal"
|
||||||
|
Actor.Rail = require "scenes.levels.actors.rail"
|
||||||
|
|
||||||
|
Actor.Index = {}
|
||||||
|
Actor.Index[01] = Actor.Ring
|
||||||
|
Actor.Index[02] = Actor.Crystal
|
||||||
|
|
||||||
|
return Actor
|
223
sonic-boost.love/scenes/subgame/sonic-boost/actors/parent.lua
Normal file
223
sonic-boost.love/scenes/subgame/sonic-boost/actors/parent.lua
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
local ParentEntity = Object:extend() -- On créer la classe des entitées, c'est la classe de base
|
||||||
|
|
||||||
|
local Timer = require "scenes.levels.actors.utils.timers"
|
||||||
|
|
||||||
|
function ParentEntity:new(world, type, x, y, z, w, h, d) -- On enregistre une nouvelle entité, avec par défaut sa hitbox.
|
||||||
|
self:setHitbox(x, y, z, w, h, d)
|
||||||
|
self:register(world)
|
||||||
|
--self:initPhysics()
|
||||||
|
--self:resetTimers()
|
||||||
|
self:setDebugColor(0,0,0)
|
||||||
|
self.destroyed = false
|
||||||
|
self.invisible = false
|
||||||
|
self.appearence = "none"
|
||||||
|
|
||||||
|
self.type = type
|
||||||
|
|
||||||
|
self:setSprite("", 0, 0, false)
|
||||||
|
|
||||||
|
self:setFilter()
|
||||||
|
|
||||||
|
self.depth = 0
|
||||||
|
self.id = self.world.creationID
|
||||||
|
self.world.creationID = self.world.creationID + 1
|
||||||
|
|
||||||
|
self:resetTimers()
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:setHitbox(x, y, z, w, h, d)
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.z = z
|
||||||
|
self.w = w
|
||||||
|
self.h = h
|
||||||
|
self.d = d
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:setSprite(name, ox, oy, active)
|
||||||
|
self.sprite = {}
|
||||||
|
|
||||||
|
self.sprite.name = name
|
||||||
|
self.sprite.ox = ox
|
||||||
|
self.sprite.oy = oy
|
||||||
|
self.sprite.active = active or false
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:update(dt)
|
||||||
|
self:updateTimers(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:register(world)
|
||||||
|
-- On enregistre la hitbox dans le monde, pour l'instant les deux parties du programmes
|
||||||
|
-- sont séparé (génération et enregistrement, peut-être qu'elles seront fusionnées)
|
||||||
|
self.world = world
|
||||||
|
self.controller = world.controller
|
||||||
|
self.assets = self.controller.assets
|
||||||
|
|
||||||
|
self.world:register(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:destroy()
|
||||||
|
if self.destroyed == false then
|
||||||
|
self.world:remove(self)
|
||||||
|
self.destroyed = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:move()
|
||||||
|
local x, y, z, cols, coll_number = self.x, self.y, self.z, {}, 0
|
||||||
|
if self.destroyed == false then
|
||||||
|
x, y, z, cols, coll_number = self.world:move(
|
||||||
|
self,
|
||||||
|
self.x,
|
||||||
|
self.y,
|
||||||
|
self.z,
|
||||||
|
self.filter
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
return x, y, z, cols, coll_number
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:drawDebug()
|
||||||
|
if self.invisible == true then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
self:draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------- DEBUG FUNCTIONS ------------------------------------
|
||||||
|
|
||||||
|
function ParentEntity:setDebugColor(r,g,b)
|
||||||
|
self.debugColor = {}
|
||||||
|
|
||||||
|
self.debugColor.r = r
|
||||||
|
self.debugColor.g = g
|
||||||
|
self.debugColor.b = b
|
||||||
|
end
|
||||||
|
|
||||||
|
---------- COLLISION ----------
|
||||||
|
|
||||||
|
function ParentEntity:setFilter()
|
||||||
|
self.filter = function(item, other)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:set2DFilter()
|
||||||
|
self.filter2D = function(item, other)
|
||||||
|
return "cross"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:pickedUp()
|
||||||
|
self:destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------- COORDINATE FUNCTIONS ------------------------------------
|
||||||
|
|
||||||
|
function ParentEntity:getCenter()
|
||||||
|
local x, y, z
|
||||||
|
x = math.floor(self.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 ParentEntity:getOrigin()
|
||||||
|
local x, y, z
|
||||||
|
y = math.floor(self.y + self.h / 2)
|
||||||
|
x = math.floor(self.x + self.w / 2) + math.floor(y / 2)
|
||||||
|
z = math.floor(self.z)
|
||||||
|
|
||||||
|
return x, y, z
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:getSpritePosition()
|
||||||
|
local x, y, z = self:getOrigin()
|
||||||
|
y = y - z
|
||||||
|
|
||||||
|
return x, y, z
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------- DRAW FUNCTIONS ------------------------------------
|
||||||
|
|
||||||
|
function ParentEntity:draw()
|
||||||
|
self:drawSprite()
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:drawEcho()
|
||||||
|
self:drawSprite(self.world.width, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:drawSprite(tx, ty)
|
||||||
|
utils.draw.resetColor()
|
||||||
|
|
||||||
|
local x, y, z = self:getSpritePosition()
|
||||||
|
|
||||||
|
local tx = tx or 0
|
||||||
|
local ty = ty or 0
|
||||||
|
|
||||||
|
if (self.sprite.active) then
|
||||||
|
self.assets.sprites[self.sprite.name]:drawAnimation(x + tx, y + ty, 0, 1, 1, self.sprite.ox, self.sprite.oy)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:drawShadow(tx, ty)
|
||||||
|
local x, y, z = self:getOrigin()
|
||||||
|
love.graphics.setColor(0, 0, 1, 1)
|
||||||
|
local tx = tx or 0
|
||||||
|
local ty = ty or 0
|
||||||
|
x = math.floor(x + tx)
|
||||||
|
y = math.floor(y + ty)
|
||||||
|
self.assets:drawImage("shadow", x, y, 0, 1, 1, 12, 3)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:drawShadowEcho()
|
||||||
|
self:drawShadow(self.world.width, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:drawHitBox()
|
||||||
|
x, y, z = self:getOrigin()
|
||||||
|
|
||||||
|
local r, g, b, a = self.debugColor.r, self.debugColor.g, self.debugColor.b, self.debugColor.a
|
||||||
|
|
||||||
|
--utils.draw.box(self.x, self.y + self.z + self.h, self.w, self.d, self.h, r, g, b, a)
|
||||||
|
--utils.draw.box(x - (self.w/2), y - (self.w/2) - self.z, self.w, self.h, r, g, b, a)
|
||||||
|
--utils.draw.box(x - (self.w/2), y - (self.w/2) - self.z, self.w, self.d, r, g, b, a)
|
||||||
|
--utils.draw.box(x - (self.w/2), y + (self.w/2) - self.z - self.d, self.w, self.d, r, g, b, a)
|
||||||
|
--utils.draw.box(x - (self.w/2), y - (self.w/2) - self.z - self.d, self.w, self.h, r, g, b, a)
|
||||||
|
--utils.draw.box(x - (self.w/2), y + (self.w/2) - self.z - self.d, self.w, self.d, r, g, b, a)
|
||||||
|
|
||||||
|
utils.draw.box(x - (self.w/2), y - (self.h/2), self.w, self.h, r/5, g/5, b/5, 1)
|
||||||
|
|
||||||
|
utils.draw.box(x - (self.w/2), y - z - self.d, self.w, self.d, r, g, b, 1)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
----------- TIMER RELATED FUNCTIONS ----------
|
||||||
|
|
||||||
|
function ParentEntity:resetTimers()
|
||||||
|
self.timers = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:addTimer(name, t)
|
||||||
|
self.timers[name] = Timer(self, name, t)
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:updateTimers(dt)
|
||||||
|
for i, v in pairs(self.timers) do
|
||||||
|
v:update(dt)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ParentEntity:endedTimer(name)
|
||||||
|
if name == "destroy" then
|
||||||
|
self:destroy()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return ParentEntity
|
25
sonic-boost.love/scenes/subgame/sonic-boost/actors/rail.lua
Normal file
25
sonic-boost.love/scenes/subgame/sonic-boost/actors/rail.lua
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
local Entity = require("scenes.levels.actors.parent")
|
||||||
|
local Rail = Entity:extend()
|
||||||
|
|
||||||
|
function Rail:new(world, x, y, z, id)
|
||||||
|
local z = z or 32
|
||||||
|
self.railid = id or 2
|
||||||
|
|
||||||
|
Rail.super.new(self, world, "grind", x, y-4, 24, 31, 8, 8)
|
||||||
|
|
||||||
|
self:setDebugColor(.2, .2, .2)
|
||||||
|
|
||||||
|
self.depth = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function Rail:draw()
|
||||||
|
utils.draw.resetColor( )
|
||||||
|
love.graphics.draw(self.world.textures.rail, self.world.quads.rails[self.railid], self.x + math.floor(self.y / 2), self.y - self.z -(self.d / 2))
|
||||||
|
end
|
||||||
|
|
||||||
|
function Rail:drawEcho()
|
||||||
|
utils.draw.resetColor( )
|
||||||
|
love.graphics.draw(self.world.textures.rail, self.world.quads.rails[self.railid], self.x + math.floor(self.y / 2) + self.world.width, self.y - self.z -(self.d / 2))
|
||||||
|
end
|
||||||
|
|
||||||
|
return Rail
|
22
sonic-boost.love/scenes/subgame/sonic-boost/actors/ring.lua
Normal file
22
sonic-boost.love/scenes/subgame/sonic-boost/actors/ring.lua
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
local Entity = require("scenes.levels.actors.parent")
|
||||||
|
local Ring = Entity:extend()
|
||||||
|
|
||||||
|
function Ring:new(world, x, y, z)
|
||||||
|
local z = z or 8
|
||||||
|
Ring.super.new(self, world, "collectible", x-8, y-8, z, 16, 16, 16)
|
||||||
|
|
||||||
|
self:setSprite("ring", 8, 16, true)
|
||||||
|
|
||||||
|
self:setDebugColor(255, 255, 0)
|
||||||
|
|
||||||
|
self.depth = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function Ring:pickedUp(player)
|
||||||
|
player.rings = player.rings + 1
|
||||||
|
player:addScore(10)
|
||||||
|
|
||||||
|
self:destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
return Ring
|
|
@ -0,0 +1,21 @@
|
||||||
|
local Timer = Object:extend()
|
||||||
|
|
||||||
|
function Timer:new(entity, name, t)
|
||||||
|
self.time = t
|
||||||
|
self.entity = entity
|
||||||
|
self.name = name
|
||||||
|
end
|
||||||
|
|
||||||
|
function Timer:update(dt)
|
||||||
|
self.time = self.time - dt
|
||||||
|
if (self.time <= 0) then
|
||||||
|
self:finish()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Timer:finish()
|
||||||
|
self.entity:endedTimer(self.name)
|
||||||
|
self.entity.timers[self.name] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return Timer
|
|
@ -0,0 +1,51 @@
|
||||||
|
local Background = Object:extend()
|
||||||
|
|
||||||
|
function Background:new(controller, levelname)
|
||||||
|
self.controller = controller
|
||||||
|
local filename = self.controller.datas.background or "forest"
|
||||||
|
self.back1 = love.graphics.newImage("assets/levels/backgrounds/" .. filename .. "-back.png")
|
||||||
|
self.back2 = love.graphics.newImage("assets/levels/backgrounds/" .. filename .. "-fore.png")
|
||||||
|
end
|
||||||
|
|
||||||
|
function Background:destroy()
|
||||||
|
self.back1:release( )
|
||||||
|
self.back2:release( )
|
||||||
|
end
|
||||||
|
|
||||||
|
function Background:draw()
|
||||||
|
local x0, x1, x2
|
||||||
|
|
||||||
|
local turn = self.controller.camera.turn
|
||||||
|
|
||||||
|
x0 = self.controller.camera.view.x + (turn * self.controller.world.width)
|
||||||
|
x1 = x0 / 3 % 240
|
||||||
|
x2 = x0 / 9 % 480
|
||||||
|
|
||||||
|
local sx = 1
|
||||||
|
for i=1, 4 do
|
||||||
|
if (i == 2) or (i == 4) then
|
||||||
|
love.graphics.draw(self.back1, (i)*240 - x2, 20, 0, -1, 1)
|
||||||
|
else
|
||||||
|
love.graphics.draw(self.back1, (i-1)*240 - x2, 20, 0, 1, 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i=1, 3 do
|
||||||
|
love.graphics.draw(self.back2, (i-1)*240 - x1, 20, 0, 1, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
--self:drawBorders()
|
||||||
|
--self:drawGround(0, 90)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Background:drawGround(x, y)
|
||||||
|
for i=1, 5 do
|
||||||
|
for j=1, 16 do
|
||||||
|
local k = 1 + ((i + j) % 2)
|
||||||
|
print(k)
|
||||||
|
love.graphics.draw(self.normalTileImage, self.normalTile[k], x + (j-3)*31 + (i-1)*10 , y + (i-1)*20)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Background
|
|
@ -0,0 +1,122 @@
|
||||||
|
local Camera = require "libs.hump.camera"
|
||||||
|
local CameraSystem = Object:extend()
|
||||||
|
|
||||||
|
function CameraSystem:new(controller, x, y, target)
|
||||||
|
self.controller = controller
|
||||||
|
self.target = target
|
||||||
|
self.view = Camera(212, 30, 1, 0, true)
|
||||||
|
local width, height, flags = love.window.getMode( )
|
||||||
|
self.screen = {}
|
||||||
|
self.screen.width = width
|
||||||
|
self.screen.height = height
|
||||||
|
self.width = 424
|
||||||
|
self.height = 240
|
||||||
|
self.resolution = self.screen.width / self.width
|
||||||
|
--self:limit()
|
||||||
|
|
||||||
|
self.turn = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function CameraSystem:setTarget(target)
|
||||||
|
self.target = target
|
||||||
|
end
|
||||||
|
|
||||||
|
function CameraSystem:floorCoord()
|
||||||
|
self.view.x, self.view.y = utils.math.floorCoord(self.view.x, self.view.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function CameraSystem:update()
|
||||||
|
if (self.target ~= nil) then
|
||||||
|
self:followEntity(self.target)
|
||||||
|
end
|
||||||
|
self:limit()
|
||||||
|
self:floorCoord()
|
||||||
|
end
|
||||||
|
|
||||||
|
function CameraSystem:move(x, y)
|
||||||
|
self.view.x = x
|
||||||
|
self.view.y = y
|
||||||
|
|
||||||
|
self:limit()
|
||||||
|
end
|
||||||
|
|
||||||
|
function CameraSystem:getCoord()
|
||||||
|
local camx, camy, camh, camw
|
||||||
|
camx = self.view.x - (self.width/2)
|
||||||
|
camy = self.view.y - (self.height/2)
|
||||||
|
|
||||||
|
camw = self.width
|
||||||
|
camh = self.height
|
||||||
|
return camx, camy, camw, camh
|
||||||
|
end
|
||||||
|
|
||||||
|
function CameraSystem:worldCoord(x, y, ox, oy, w, h)
|
||||||
|
ox, oy = ox or 0, oy or 0
|
||||||
|
w,h = w or love.graphics.getWidth(), h or love.graphics.getHeight()
|
||||||
|
return self.view:worldCoords(x, y, ox, oy, w, h)
|
||||||
|
end
|
||||||
|
|
||||||
|
function CameraSystem:viewCoord(x, y)
|
||||||
|
return self.view:cameraCoords(x, y, ox, oy, w, h)
|
||||||
|
end
|
||||||
|
|
||||||
|
function CameraSystem:getScreenCoord()
|
||||||
|
local camx, camy, camh, camw
|
||||||
|
camx = self.view.x - (self.screen.width/2)
|
||||||
|
camy = self.view.y - (self.screen.height/2)
|
||||||
|
|
||||||
|
camw = self.screen.width
|
||||||
|
camh = self.screen.height
|
||||||
|
return camx, camy, camw, camh
|
||||||
|
end
|
||||||
|
|
||||||
|
function CameraSystem:limit()
|
||||||
|
local camx, camy = self.view.x, self.view.y
|
||||||
|
|
||||||
|
local currentTurn, maxTurn = self.turn, self.controller.missiondata.turns
|
||||||
|
|
||||||
|
if currentTurn == 1 then
|
||||||
|
camx = math.max(212, camx)
|
||||||
|
end
|
||||||
|
|
||||||
|
if currentTurn == maxTurn then
|
||||||
|
camx = math.min(camx, self.controller.world.width - 212 + 31)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.view.x, self.view.y = camx, 30
|
||||||
|
end
|
||||||
|
|
||||||
|
function CameraSystem:followEntity(entity)
|
||||||
|
local entity = entity
|
||||||
|
|
||||||
|
if (entity ~= nil) then
|
||||||
|
self.turn = entity.turn or 1
|
||||||
|
|
||||||
|
local playx, playy = entity:getCenter()
|
||||||
|
|
||||||
|
local camx, camy = self.view.x + (self.width/2),
|
||||||
|
self.view.y + (self.height/2)
|
||||||
|
|
||||||
|
|
||||||
|
playx, playy = self:viewCoord(playx-16+212, 30)
|
||||||
|
playx = playx - (self.width/2)
|
||||||
|
playy = playy - (self.height/2)
|
||||||
|
|
||||||
|
if (math.abs(playx) > 8) then
|
||||||
|
camx = camx + (playx - (8*utils.math.sign(playx)))
|
||||||
|
end
|
||||||
|
|
||||||
|
if (playy > 16) then
|
||||||
|
camy = camy + (playy - 16)
|
||||||
|
elseif (playy < -64) then
|
||||||
|
camy = camy + (playy + 64)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.view.x, self.view.y = camx - (self.width/2),
|
||||||
|
camy - (self.height/2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Anciennes Fonctions
|
||||||
|
|
||||||
|
return CameraSystem
|
|
@ -0,0 +1,120 @@
|
||||||
|
local CharManager = Object:extend()
|
||||||
|
local Dummy = Object:extend()
|
||||||
|
local Player = Dummy:extend()
|
||||||
|
local Rival = Dummy:extend()
|
||||||
|
local actor = require "scenes.levels.actors"
|
||||||
|
|
||||||
|
|
||||||
|
function CharManager:new(controller)
|
||||||
|
self.controller = controller
|
||||||
|
self.list = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharManager:newPlayer(character, rail)
|
||||||
|
local id = #self.list + 1
|
||||||
|
table.insert(self.list, Player(self, id, character, rail))
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharManager:newDummy(character, rail)
|
||||||
|
local id = #self.list + 1
|
||||||
|
table.insert(self.list, Dummy(self, id, character, rail))
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharManager:update(dt)
|
||||||
|
for i,v in ipairs(self.list) do
|
||||||
|
v:update(dt)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharManager:getType(id)
|
||||||
|
self.list[id]:getType()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- DUMMY CHARACTER FUNCTION --
|
||||||
|
|
||||||
|
function Dummy:new(manager, playerid, character, rail)
|
||||||
|
self.manager = manager
|
||||||
|
self.controller = manager.controller
|
||||||
|
self.type = "dummy"
|
||||||
|
|
||||||
|
local character = character or "sonic"
|
||||||
|
|
||||||
|
local rail = rail
|
||||||
|
|
||||||
|
local spritepath = "assets/sprites/characters/" .. character
|
||||||
|
self.controller.assets:addSprite("character" .. playerid, spritepath)
|
||||||
|
|
||||||
|
self.actor = actor.Character(self, rail, character, playerid)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Dummy:getKeys()
|
||||||
|
return game.input.fakekeys
|
||||||
|
end
|
||||||
|
|
||||||
|
function Dummy:update(dt)
|
||||||
|
-- le Dummy ne fait rien, donc inutile
|
||||||
|
end
|
||||||
|
|
||||||
|
function Dummy:newLap()
|
||||||
|
local currentTurn, maxTurn = self.actor.turn, self.controller.missiondata.turns
|
||||||
|
|
||||||
|
if (currentTurn < maxTurn) then
|
||||||
|
self:wrapActor()
|
||||||
|
else
|
||||||
|
self:finishLevel()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Dummy:wrapActor()
|
||||||
|
self.actor.x = math.floor(self.actor.x - self.controller.world.width)
|
||||||
|
self.actor.turn = self.actor.turn + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function Dummy:getRealPosition()
|
||||||
|
local turn = self.actor.turn - 1 -- on calcule le tour à partir de 0 pour
|
||||||
|
-- obtenir le vrai x. Techniquement, ça ne change rien pour le joueur,
|
||||||
|
-- mais c'est plus correct comme ça
|
||||||
|
|
||||||
|
local turnStart = (turn * self.controller.world.width)
|
||||||
|
|
||||||
|
return turnStart + self.actor.x
|
||||||
|
end
|
||||||
|
|
||||||
|
function Dummy:finishLevel()
|
||||||
|
-- Pour le Dummy ça n'a aucun effet
|
||||||
|
end
|
||||||
|
|
||||||
|
function Dummy:die()
|
||||||
|
self.actor:destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- PLAYER CONTROL FUNCTION --
|
||||||
|
|
||||||
|
function Player:new(manager, playerid, character, rail)
|
||||||
|
Player.super.new(self, manager, playerid, character, rail)
|
||||||
|
|
||||||
|
self.type = "player"
|
||||||
|
self.controller.camera:setTarget(self.actor)
|
||||||
|
self.controller.hud:setTarget(self.actor)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Player:getKeys()
|
||||||
|
return game.input.keys
|
||||||
|
end
|
||||||
|
|
||||||
|
function Player:wrapActor()
|
||||||
|
self.actor.x = math.floor(self.actor.x - self.controller.world.width)
|
||||||
|
self.actor.turn = self.actor.turn + 1
|
||||||
|
self.controller.camera.view.x = self.controller.camera.view.x - self.controller.world.width
|
||||||
|
end
|
||||||
|
|
||||||
|
function Player:finishLevel()
|
||||||
|
--love.event.quit()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Player:die()
|
||||||
|
love.event.quit()
|
||||||
|
end
|
||||||
|
|
||||||
|
return CharManager
|
127
sonic-boost.love/scenes/subgame/sonic-boost/controller/hud.lua
Normal file
127
sonic-boost.love/scenes/subgame/sonic-boost/controller/hud.lua
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
local BattleHUD = Object:extend()
|
||||||
|
|
||||||
|
function BattleHUD:new(controller)
|
||||||
|
self.controller = controller
|
||||||
|
self.target = nil
|
||||||
|
|
||||||
|
self:loadAssets()
|
||||||
|
end
|
||||||
|
|
||||||
|
function BattleHUD:setTarget(target)
|
||||||
|
self.target = target
|
||||||
|
end
|
||||||
|
|
||||||
|
function BattleHUD:loadAssets()
|
||||||
|
self.progressbarImage = love.graphics.newImage("assets/gui/progressbar.png")
|
||||||
|
self.barBack = love.graphics.newQuad(0, 0, 211, 12, 211, 24)
|
||||||
|
self.barFore = love.graphics.newQuad(0, 12, 211, 12, 211, 24)
|
||||||
|
self.hud1 = love.graphics.newImage("assets/gui/boosthud1.png")
|
||||||
|
self.hud2 = love.graphics.newImage("assets/gui/boosthud2.png")
|
||||||
|
|
||||||
|
self.ring = love.graphics.newImage("assets/gui/ring.png")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self.font = love.graphics.newImageFont("assets/gui/hudnumbers.png", " 0123456789:/", 1)
|
||||||
|
self.font2 = love.graphics.newImageFont("assets/gui/hudsmallnumbers.png", " 0123456789", 0)
|
||||||
|
|
||||||
|
self.time = love.graphics.newImage("assets/gui/hudtime.png")
|
||||||
|
self.score = love.graphics.newImage("assets/gui/hudscore.png")
|
||||||
|
self.bonus = love.graphics.newImage("assets/gui/hudbonus.png")
|
||||||
|
|
||||||
|
self.controller.assets:addTileset("lifeicon", "assets/sprites/characters/charicons")
|
||||||
|
end
|
||||||
|
|
||||||
|
function BattleHUD:destroy( )
|
||||||
|
self.progressbarImage:release( )
|
||||||
|
self.barBack:release( )
|
||||||
|
self.barFore:release( )
|
||||||
|
self.hud1:release( )
|
||||||
|
self.hud2:release( )
|
||||||
|
end
|
||||||
|
|
||||||
|
function BattleHUD:update(dt)
|
||||||
|
--
|
||||||
|
end
|
||||||
|
|
||||||
|
function BattleHUD:draw()
|
||||||
|
love.graphics.setFont( self.font )
|
||||||
|
self:drawFrame()
|
||||||
|
|
||||||
|
if self.target ~= nil then
|
||||||
|
self:drawTop(0, 0)
|
||||||
|
self:drawBottom(0, 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.controller:resetFont( )
|
||||||
|
end
|
||||||
|
|
||||||
|
function BattleHUD:drawFrame()
|
||||||
|
love.graphics.draw(self.hud1, 0, 0)
|
||||||
|
love.graphics.draw(self.hud2, 0, 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
function BattleHUD:drawTop(x, y)
|
||||||
|
|
||||||
|
local ringx, ringy = x + 289, y + 6
|
||||||
|
love.graphics.draw(self.ring, ringx, ringy)
|
||||||
|
local rings = utils.math.numberToString(self.target.rings, 3)
|
||||||
|
|
||||||
|
love.graphics.print(rings, ringx + 14, ringy + 1)
|
||||||
|
|
||||||
|
|
||||||
|
love.graphics.draw(self.score, x+8, y+3)
|
||||||
|
local score = utils.math.numberToString(self.target.score, 9)
|
||||||
|
local bonus = utils.math.numberToString(self.target.bonus, 3)
|
||||||
|
local combo = utils.math.numberToString(self.target.combo, 3)
|
||||||
|
|
||||||
|
love.graphics.print(score, x + 8 + 42, y + 4)
|
||||||
|
|
||||||
|
local bonusx, bonusy = x + 165, y + 1
|
||||||
|
love.graphics.draw(self.bonus, bonusx, bonusy)
|
||||||
|
love.graphics.setFont( self.font2 )
|
||||||
|
love.graphics.print(combo, bonusx + 28, bonusy)
|
||||||
|
love.graphics.print(bonus, bonusx + 28, bonusy + 8)
|
||||||
|
love.graphics.setFont( self.font )
|
||||||
|
|
||||||
|
self.controller.assets.tileset["lifeicon"]:drawTile(self.target.lifeicon, x + 360, y + 4)
|
||||||
|
local lives = utils.math.numberToString(self.target.life, 2)
|
||||||
|
love.graphics.print(lives, x + 360 + 18, y + 7 )
|
||||||
|
end
|
||||||
|
|
||||||
|
function BattleHUD:drawBottom(x, y)
|
||||||
|
local progressx, progressy = x + 5, y + 4
|
||||||
|
love.graphics.draw(self.progressbarImage, self.barBack, progressx, progressy)
|
||||||
|
local width = math.floor((self.target.x / self.controller.world.width) * (211 - 16))
|
||||||
|
width = math.max(0, width)
|
||||||
|
width = math.min(width, (211 - 16))
|
||||||
|
love.graphics.setScissor(progressx + 8, progressy, width, 12)
|
||||||
|
love.graphics.draw(self.progressbarImage, self.barFore, progressx, progressy)
|
||||||
|
love.graphics.setScissor( )
|
||||||
|
self.controller.assets.tileset["lifeicon"]:drawTile(self.target.lifeicon, progressx + width, progressy - 2)
|
||||||
|
if (self.controller.missiondata.turns > 1) then
|
||||||
|
local turn1 = utils.math.numberToString(self.target.turn, 2)
|
||||||
|
local turn2 = utils.math.numberToString(self.controller.missiondata.turns, 2)
|
||||||
|
love.graphics.printf(turn1 .. "/" .. turn2, progressx, progressy + 12 + 2, 210, "right")
|
||||||
|
end
|
||||||
|
|
||||||
|
local timex, timey = x + 228, y + 1
|
||||||
|
love.graphics.draw(self.time, timex, timey)
|
||||||
|
local timestring = self:getTimeString(self.controller.mission.timer)
|
||||||
|
|
||||||
|
love.graphics.print(timestring, timex + 32, timey + 1)
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function BattleHUD:getTimeString(numberOfSeconds)
|
||||||
|
local numberOfSeconds = numberOfSeconds or 0
|
||||||
|
local microseconds = math.floor((numberOfSeconds % 1) * 100)
|
||||||
|
local seconds = math.floor(numberOfSeconds % 60)
|
||||||
|
local minutes = math.floor(numberOfSeconds / 60)
|
||||||
|
return utils.math.numberToString(minutes, 2) .. ":"
|
||||||
|
.. utils.math.numberToString(seconds, 2) .. ":"
|
||||||
|
.. utils.math.numberToString(microseconds, 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
return BattleHUD
|
161
sonic-boost.love/scenes/subgame/sonic-boost/controller/init.lua
Normal file
161
sonic-boost.love/scenes/subgame/sonic-boost/controller/init.lua
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
local Controller = Object:extend()
|
||||||
|
local GUI = require "modules.gui"
|
||||||
|
local Assets = require "modules.assets"
|
||||||
|
|
||||||
|
local HUD = require "scenes.levels.controller.hud"
|
||||||
|
local Background = require "scenes.levels.controller.background"
|
||||||
|
local Camera = require "scenes.levels.controller.camera"
|
||||||
|
local World = require "scenes.levels.controller.world"
|
||||||
|
local PauseMenu = require "scenes.levels.controller.pause"
|
||||||
|
|
||||||
|
local CharacterManager = require "scenes.levels.controller.characters"
|
||||||
|
|
||||||
|
local zoneDatas = require "datas.levels.zones"
|
||||||
|
|
||||||
|
local LoadAssets = require "scenes.levels.controller.loader"
|
||||||
|
|
||||||
|
function Controller:new(levelid, character)
|
||||||
|
self:initMission()
|
||||||
|
|
||||||
|
self.assets = Assets()
|
||||||
|
self.gui = GUI()
|
||||||
|
LoadAssets(self)
|
||||||
|
|
||||||
|
self:initManagers()
|
||||||
|
|
||||||
|
self:resetFont()
|
||||||
|
|
||||||
|
self.levelid = levelid
|
||||||
|
self.character = character
|
||||||
|
|
||||||
|
if self.datas.music ~= nil then
|
||||||
|
local filepath = "assets/music/" .. self.datas.music .. ".mp3"
|
||||||
|
print(filepath)
|
||||||
|
self.assets:setMusic(filepath)
|
||||||
|
self.assets:playMusic()
|
||||||
|
end
|
||||||
|
|
||||||
|
self.pause = false
|
||||||
|
self.initiated = false
|
||||||
|
|
||||||
|
self.world:loadWorld()
|
||||||
|
|
||||||
|
self.characters:newPlayer("sonic", 2)
|
||||||
|
--self.characters:newDummy("sonic", 0)
|
||||||
|
--self.characters:newDummy("sonic", 1)
|
||||||
|
--self.characters:newDummy("sonic", 3)
|
||||||
|
--self.characters:newDummy("sonic", 4)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Controller:restartLevel()
|
||||||
|
self:new(self.levelid, self.character)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Controller:exitLevel()
|
||||||
|
Gamestate.switch(Scenes.options)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Controller:initManagers()
|
||||||
|
--self.loader = Loader()
|
||||||
|
self.world = World(self)
|
||||||
|
self.hud = HUD(self)
|
||||||
|
self.camera = Camera(self, 0, 0)
|
||||||
|
self.background = Background(self)
|
||||||
|
self.characters = CharacterManager(self)
|
||||||
|
self.pausemenu = PauseMenu(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Controller:destroy()
|
||||||
|
self.world:destroy()
|
||||||
|
|
||||||
|
|
||||||
|
self.world = nil
|
||||||
|
self.hud = nil
|
||||||
|
self.camera = nil
|
||||||
|
self.background = nil
|
||||||
|
self.characters = nil
|
||||||
|
self.pausemenu = nil
|
||||||
|
|
||||||
|
self.assets:clear()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Controller:initMission(levelid)
|
||||||
|
self:getLevelData(levelid)
|
||||||
|
|
||||||
|
self.mission = {}
|
||||||
|
self.mission.timer = 0
|
||||||
|
self.mission.completed = false
|
||||||
|
end
|
||||||
|
|
||||||
|
function Controller:startMission()
|
||||||
|
self.initiated = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function Controller:getLevelData(file)
|
||||||
|
local file = file or "testlevel.test1"
|
||||||
|
local level = require("datas.levels." .. file)
|
||||||
|
|
||||||
|
self.zone = level.datas.zone
|
||||||
|
|
||||||
|
local zone_data = zoneDatas[self.zone]
|
||||||
|
local bypass_data = level.datas.bypass_data
|
||||||
|
|
||||||
|
self.datas = {}
|
||||||
|
|
||||||
|
if (level.datas.bypass_zone) then
|
||||||
|
self.datas.name = bypass_data.name or zone_data.name
|
||||||
|
self.datas.borders = bypass_data.borders or zone_data.borders
|
||||||
|
self.datas.tiles = bypass_data.tiles or zone_data.tiles
|
||||||
|
self.datas.background = bypass_data.background or zone_data.background
|
||||||
|
self.datas.music = bypass_data.music or zone_data.music
|
||||||
|
else
|
||||||
|
self.datas = zone_data
|
||||||
|
end
|
||||||
|
|
||||||
|
self.layout = level.layout
|
||||||
|
self.parts = level.parts
|
||||||
|
self.endless_parts = level.endless_parts
|
||||||
|
|
||||||
|
self.missiondata = level.datas.missiondata
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function Controller:update(dt)
|
||||||
|
|
||||||
|
if self.pause == false then
|
||||||
|
self.assets:update(dt)
|
||||||
|
self.mission.timer = self.mission.timer + dt
|
||||||
|
self.world:update(dt)
|
||||||
|
self.camera:update(dt)
|
||||||
|
else
|
||||||
|
self.pausemenu:update(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
if game.input.keys["start"].isPressed then
|
||||||
|
self.pause = (self.pause == false)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function Controller:draw()
|
||||||
|
|
||||||
|
self.background:draw()
|
||||||
|
|
||||||
|
self.camera.view:attach()
|
||||||
|
self.world:draw()
|
||||||
|
self.camera.view:detach()
|
||||||
|
|
||||||
|
utils.draw.resetColor()
|
||||||
|
self.hud:draw()
|
||||||
|
|
||||||
|
if self.pause == true then
|
||||||
|
self.pausemenu:draw(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function Controller:resetFont()
|
||||||
|
self.assets.fonts["text"]:set()
|
||||||
|
end
|
||||||
|
|
||||||
|
return Controller
|
|
@ -0,0 +1,82 @@
|
||||||
|
local Loader = Object:extend()
|
||||||
|
|
||||||
|
function Loader:new(controller)
|
||||||
|
self.controller = controller
|
||||||
|
self.assetQueue = {}
|
||||||
|
self.pointer = 1
|
||||||
|
self.finished = false
|
||||||
|
end
|
||||||
|
|
||||||
|
function Loader:addElement(type, name, filepath, arg1, arg2, arg3, arg4)
|
||||||
|
local queueElement = {}
|
||||||
|
|
||||||
|
queueElement.type = type
|
||||||
|
queueElement.name = name
|
||||||
|
queueElement.file = filepath
|
||||||
|
table.insert(self.assetQueue, queueElement)
|
||||||
|
|
||||||
|
self.finished = false
|
||||||
|
end
|
||||||
|
|
||||||
|
function Loader:update(dt)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function Loader:treatAssetQueueElement(id)
|
||||||
|
if self.assetQueue[id] ~= nil then
|
||||||
|
local asset = self.assetQueue[id]
|
||||||
|
if asset.type == "image" then
|
||||||
|
self.controller.assets:addImage(asset.name, asset.file)
|
||||||
|
elseif asset.type == "sprite" then
|
||||||
|
self.controller.assets:addSprite(asset.name, asset.file)
|
||||||
|
elseif asset.type == "sfx" then
|
||||||
|
self.controller.assets:addSFX(asset.name, asset.file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Loader:treatAssetQueue()
|
||||||
|
for i = self.pointer, 10 do
|
||||||
|
if i <= #self.assetQueue then
|
||||||
|
self.finished = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.pointer = i
|
||||||
|
end
|
||||||
|
|
||||||
|
local function LoadAssets(controller)
|
||||||
|
controller.assets:addImage("shadow", "assets/sprites/shadow.png")
|
||||||
|
controller.assets:addSprite("ring", "assets/sprites/items/ring")
|
||||||
|
controller.assets:addSprite("crystal", "assets/sprites/items/crystal")
|
||||||
|
|
||||||
|
controller.assets:addFont("text", "assets/fonts/PixelOperator.ttf", 16.5)
|
||||||
|
controller.gui:addTextBox("solid", "assets/gui/dialogbox.png")
|
||||||
|
|
||||||
|
controller.assets:addSFX("ring", "assets/sfx/gameplay/objects/ring.wav")
|
||||||
|
controller.assets:addSFX("booster", "assets/sfx/gameplay/objects/booster.wav")
|
||||||
|
controller.assets:addSFX("jumpboard", "assets/sfx/gameplay/objects/jumpboard.wav")
|
||||||
|
|
||||||
|
controller.assets:addSFX("jump", "assets/sfx/gameplay/actions/jump-modern.wav")
|
||||||
|
controller.assets:addSFX("doublejump", "assets/sfx/gameplay/actions/doublejump.wav")
|
||||||
|
controller.assets:addSFX("spin", "assets/sfx/gameplay/actions/spin.wav")
|
||||||
|
|
||||||
|
controller.assets:addSFX("grind", "assets/sfx/gameplay/grind/grind.wav")
|
||||||
|
controller.assets:addSFX("grindland", "assets/sfx/gameplay/grind/land.wav")
|
||||||
|
|
||||||
|
controller.assets:addFont("menu", "assets/fonts/RussoOne-Regular.ttf", 15)
|
||||||
|
controller.assets.fonts["menu"]:setAlign("center")
|
||||||
|
controller.assets.fonts["menu"]:setFilter("border")
|
||||||
|
controller.assets.fonts["menu"]:setSpacing(true, 1)
|
||||||
|
|
||||||
|
controller.assets:addFont("title", "assets/fonts/Teko-Bold.ttf", 22)
|
||||||
|
controller.assets.fonts["title"]:setAlign("center")
|
||||||
|
controller.assets.fonts["title"]:setFilter("doubleborder")
|
||||||
|
controller.assets.fonts["title"]:setSpacing(true, 2)
|
||||||
|
|
||||||
|
controller.assets:addSFX("select", "assets/sfx/menu/select.wav")
|
||||||
|
controller.assets:addSFX("validate", "assets/sfx/menu/validate.wav")
|
||||||
|
controller.assets:addSFX("cancel", "assets/sfx/menu/cancel.wav")
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return LoadAssets
|
102
sonic-boost.love/scenes/subgame/sonic-boost/controller/pause.lua
Normal file
102
sonic-boost.love/scenes/subgame/sonic-boost/controller/pause.lua
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
local PauseMenu = Object:extend()
|
||||||
|
local Menu = require "modules.menus"
|
||||||
|
|
||||||
|
local ResumeWidget = Menu.Widget.Base:extend()
|
||||||
|
local RestartWidget = Menu.Widget.Base:extend()
|
||||||
|
local ExitWidget = Menu.Widget.Base:extend()
|
||||||
|
|
||||||
|
function PauseMenu:new(controller)
|
||||||
|
self.controller = controller
|
||||||
|
self.active = false
|
||||||
|
|
||||||
|
self.height = 56+16
|
||||||
|
|
||||||
|
self.assets = self.controller.assets
|
||||||
|
|
||||||
|
self.menusystem = Menu.Controller()
|
||||||
|
self.menu = Menu.TextMenu(424/2, self.height+8, self.assets.fonts["menu"], 6)
|
||||||
|
|
||||||
|
self.menusystem:addMenu(self.menu)
|
||||||
|
self.menu:centerText(180)
|
||||||
|
self.menu:setSound(self.assets.sfx["select"])
|
||||||
|
self.menu.focus = true
|
||||||
|
|
||||||
|
self.menu:addWidget(ResumeWidget(self))
|
||||||
|
self.menu:addWidget(RestartWidget(self))
|
||||||
|
self.menu:addWidget(ExitWidget(self))
|
||||||
|
|
||||||
|
self.canvas = nil
|
||||||
|
self.activeCanvas = false
|
||||||
|
self.width = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function PauseMenu:update(dt)
|
||||||
|
self.menusystem:update(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
function PauseMenu:draw()
|
||||||
|
if (self.activeCanvas == false) then
|
||||||
|
local width = self.menu:getWidth() or 10
|
||||||
|
self.width = self:drawCanvas(width, 64)
|
||||||
|
end
|
||||||
|
|
||||||
|
love.graphics.draw(self.canvas, (424 - self.width)/2, self.height - 28)
|
||||||
|
self.menusystem:draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
function PauseMenu:drawCanvas(width, height)
|
||||||
|
local width = (math.floor( width / 16 ) + 1) * 16
|
||||||
|
local height = height or 80
|
||||||
|
self.canvas = love.graphics.newCanvas(width + 64, height + 64)
|
||||||
|
|
||||||
|
CScreen:cease()
|
||||||
|
love.graphics.setCanvas( self.canvas )
|
||||||
|
|
||||||
|
self.controller.gui.textbox["solid"]:draw(32, 32, width, height)
|
||||||
|
self.controller.assets.fonts["title"]:draw("PAUSE", (width + 64)/2, 12, -1)
|
||||||
|
|
||||||
|
love.graphics.setCanvas( )
|
||||||
|
CScreen:cease()
|
||||||
|
|
||||||
|
self.activeCanvas = true
|
||||||
|
|
||||||
|
return width + 64
|
||||||
|
end
|
||||||
|
|
||||||
|
--- MENU WIDGETS
|
||||||
|
|
||||||
|
function ResumeWidget:new(menusystem)
|
||||||
|
ResumeWidget.super.new(self)
|
||||||
|
self.label = "resume"
|
||||||
|
self.menusystem = menusystem
|
||||||
|
self.controller = menusystem.controller
|
||||||
|
end
|
||||||
|
|
||||||
|
function ResumeWidget:action()
|
||||||
|
self.controller.pause = false
|
||||||
|
end
|
||||||
|
|
||||||
|
function RestartWidget:new(menusystem)
|
||||||
|
RestartWidget.super.new(self)
|
||||||
|
self.label = "restart"
|
||||||
|
self.menusystem = menusystem
|
||||||
|
self.controller = menusystem.controller
|
||||||
|
end
|
||||||
|
|
||||||
|
function RestartWidget:action()
|
||||||
|
self.controller:restartLevel()
|
||||||
|
end
|
||||||
|
|
||||||
|
function ExitWidget:new(menusystem)
|
||||||
|
ExitWidget.super.new(self)
|
||||||
|
self.label = "exit"
|
||||||
|
self.menusystem = menusystem
|
||||||
|
self.controller = menusystem.controller
|
||||||
|
end
|
||||||
|
|
||||||
|
function ExitWidget:action()
|
||||||
|
self.controller:exitLevel()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return PauseMenu
|
510
sonic-boost.love/scenes/subgame/sonic-boost/controller/world.lua
Normal file
510
sonic-boost.love/scenes/subgame/sonic-boost/controller/world.lua
Normal file
|
@ -0,0 +1,510 @@
|
||||||
|
local World = Object:extend()
|
||||||
|
|
||||||
|
local bump3dpd = require 'libs.bump-3dpd'
|
||||||
|
local bump2d = require 'libs.bump'
|
||||||
|
local tsort = require 'libs.tsort'
|
||||||
|
|
||||||
|
local actor = require "scenes.levels.actors"
|
||||||
|
|
||||||
|
local tilesize = 31
|
||||||
|
|
||||||
|
---------- WORLD FUNCTIONS ----------
|
||||||
|
|
||||||
|
function World:new(controller)
|
||||||
|
self.controller = controller
|
||||||
|
self.creationID = 1
|
||||||
|
|
||||||
|
self.entities = bump3dpd.newWorld()
|
||||||
|
self.chunks = {}
|
||||||
|
|
||||||
|
self:loadAssets()
|
||||||
|
|
||||||
|
self.worldloaded = false
|
||||||
|
end
|
||||||
|
|
||||||
|
function World:destroy()
|
||||||
|
self.chunks = {}
|
||||||
|
for i,v in ipairs(self.entities:getItems()) do
|
||||||
|
v:destroy( )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---------- ENTITIES MANAGEMENT FUNCTIONS ----------
|
||||||
|
|
||||||
|
function World:addEntitiesFromChunk(chunkpos)
|
||||||
|
local chunkdata, isTrue = self:getChunkData(chunkpos, 1)
|
||||||
|
local objectdata = chunkdata.objects
|
||||||
|
|
||||||
|
local chunkx = (chunkpos - 1) * (8*31)
|
||||||
|
print("trying to get objects from chunk at pos " .. chunkpos)
|
||||||
|
|
||||||
|
for i=1, 5 do
|
||||||
|
for j=1, 8 do
|
||||||
|
local y = (i-1)*20 + 10
|
||||||
|
local x = chunkx + (j-1)*31 + 16
|
||||||
|
|
||||||
|
local id = objectdata[i][j]
|
||||||
|
--print(chunkpos)
|
||||||
|
self:addEntityFromID(id, x, y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function World:addRailsFromChunk(chunkpos)
|
||||||
|
local chunkdata, isTrue = self:getChunkData(chunkpos, 1)
|
||||||
|
local objectdata = chunkdata.grind
|
||||||
|
|
||||||
|
local chunkx = (chunkpos - 1) * (8*31)
|
||||||
|
print("trying to get rails from chunk at pos " .. chunkpos)
|
||||||
|
|
||||||
|
for i=1, 5 do
|
||||||
|
for j=1, 8 do
|
||||||
|
local y = (i-1)*20 + 10
|
||||||
|
local x = chunkx + (j-1)*31
|
||||||
|
|
||||||
|
local id = objectdata[i][j]
|
||||||
|
--print(chunkpos)
|
||||||
|
if id ~= 0 then
|
||||||
|
actor.Rail(self, x, y, 10, id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function World:addEntityFromID(id, x, y)
|
||||||
|
if actor.Index[id] == nil then
|
||||||
|
if (id ~= 0) then
|
||||||
|
print("WARNING: Object " .. id .. " doesn't exist")
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
actor.Index[id](self, x, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function World:register(entity)
|
||||||
|
self.entities:add(entity, entity.x, entity.y, entity.z, entity.w, entity.h, entity.d)
|
||||||
|
end
|
||||||
|
|
||||||
|
function World:remove(entity)
|
||||||
|
if entity.destroyed == false then
|
||||||
|
self.entities:remove(entity)
|
||||||
|
print("entity " .. entity.id .. " removed")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function World:move(entity, dx, dy, dz, filter)
|
||||||
|
local dx, dy, dz = dx, dy, dz
|
||||||
|
local newx, newy, newz
|
||||||
|
newx, newy, newz, cols, coll_number = self.entities:move(entity, dx, dy, dz, filter)
|
||||||
|
return newx, newy, newz, cols, coll_number
|
||||||
|
end
|
||||||
|
|
||||||
|
function World:updateEntity(entity, dx, dy, dz)
|
||||||
|
newx, newy, newz, cols, coll_number = self.entities:update(entity, dx, dy, dz)
|
||||||
|
end
|
||||||
|
|
||||||
|
---------- UPDATE FUNCTIONS ----------
|
||||||
|
|
||||||
|
function World:update(dt)
|
||||||
|
for _, item in ipairs(self.entities:getItems()) do
|
||||||
|
item:update(dt)
|
||||||
|
--self.world2D:update(item, item.x, item.y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---------- DRAW FUNCTIONS ----------
|
||||||
|
|
||||||
|
|
||||||
|
function World:draw()
|
||||||
|
local chunksize = 8 * tilesize
|
||||||
|
local camstart = math.floor(math.max(0, (self.controller.camera.view.x - 212) / chunksize)) --
|
||||||
|
|
||||||
|
self:drawBorder(-10)
|
||||||
|
|
||||||
|
for i=0, 3 do
|
||||||
|
-- on fait commencer la boucle à 0 pour regarder celui *avant* la caméra,
|
||||||
|
-- pour éviter des soucis d'affichage.
|
||||||
|
local chunkid = camstart + i
|
||||||
|
self:drawChunk((chunkid - 1) * chunksize, 0, chunkid)
|
||||||
|
end
|
||||||
|
|
||||||
|
local itemsToDraw = self.entities:getItems()
|
||||||
|
|
||||||
|
for _, item in ipairs(itemsToDraw) do
|
||||||
|
item:drawShadow()
|
||||||
|
if self.controller.camera.turn < self.controller.missiondata.turns then
|
||||||
|
item:drawShadowEcho()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.sort(itemsToDraw, sortItemsToDraw)
|
||||||
|
|
||||||
|
for _, item in ipairs(itemsToDraw) do
|
||||||
|
item:draw()
|
||||||
|
if self.controller.camera.turn < self.controller.missiondata.turns then
|
||||||
|
item:drawEcho()
|
||||||
|
end
|
||||||
|
--item:drawHitBox()
|
||||||
|
end
|
||||||
|
|
||||||
|
self:drawBorder(100)
|
||||||
|
end
|
||||||
|
|
||||||
|
function World:drawChunk(x, y, chunkid, turn)
|
||||||
|
local chunkdata, isTrue = self:getChunkData(chunkid, self.controller.camera.turn)
|
||||||
|
local terraindata = chunkdata.terrain
|
||||||
|
|
||||||
|
for i=1, 5 do
|
||||||
|
for j=1, 8 do
|
||||||
|
local tiley = (i-1)*20 - 4
|
||||||
|
local tilex = x + (j-1)*31 + (i-1)*10
|
||||||
|
local tileid = terraindata[i][j]
|
||||||
|
|
||||||
|
local variant = 1 + ((i + j) % 2)
|
||||||
|
if (isTrue == false) then
|
||||||
|
love.graphics.setColor(.75, .75, .75, 1)
|
||||||
|
end
|
||||||
|
self:drawTile(tilex, tiley, tileid, variant)
|
||||||
|
utils.draw.resetColor()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function World:drawTile(x, y, type, variant)
|
||||||
|
if type == 0 then
|
||||||
|
love.graphics.draw(self.textures.tiles, self.quads.tiles[variant], x, y)
|
||||||
|
else
|
||||||
|
love.graphics.draw(self.textures.sptiles, self.quads.sptiles[type], x, y-2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function World:drawBorder(y)
|
||||||
|
local x = math.floor((self.controller.camera.view.x - 212) / 80) * 80
|
||||||
|
|
||||||
|
for i=1, 7 do
|
||||||
|
love.graphics.draw(self.textures.borders, self.quads.borders, x + (i-1)*80, y, 0, 1, 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---------- LOADING FUNCTIONS ----------
|
||||||
|
|
||||||
|
function World:loadWorld()
|
||||||
|
self.chunks = {}
|
||||||
|
|
||||||
|
self:addChunk("basics", 00)
|
||||||
|
|
||||||
|
local layout = self.controller.layout
|
||||||
|
for i,v in ipairs(layout) do
|
||||||
|
--print("Decompressing group of parts " .. i .. " located in " .. v[1] .. " (containing " .. #v[2] .. " parts)")
|
||||||
|
|
||||||
|
-- self.controller.layout est un tableau composée d'une série de tableau,
|
||||||
|
-- chacuns en deux partie : le premier (v[1]) contient le nom du fichier
|
||||||
|
-- où se trouve des partie de niveau, et la seconde partie (v[2]) une liste
|
||||||
|
-- d'identifiant de partie de niveau à rajouter.
|
||||||
|
|
||||||
|
-- Ce format permet de simplifier l'écriture de layout, si on a besoin de
|
||||||
|
-- plusieur partie de niveau se trouvant dans le même fichier.
|
||||||
|
|
||||||
|
for j,w in ipairs(v[2]) do
|
||||||
|
-- On dit au gestionnaire de terrain d'ajouter la partie numéro j se trouvant
|
||||||
|
-- dans v[2]. Le nom du fichier contenant les parties sera toujours v[1]
|
||||||
|
self:addPart(v[1], w)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function World:addPart(filename, part)
|
||||||
|
local filename = filename or "self"
|
||||||
|
local partdata = {}
|
||||||
|
|
||||||
|
-- On recupère les données de chunks
|
||||||
|
|
||||||
|
if (filename == "self") or (filename == "") then
|
||||||
|
|
||||||
|
-- Si le nom est "self", cela veut dire que fichier est le même que celui ou
|
||||||
|
-- se trouve le layout, qui est forcément le fichier du niveau. Cela veut dire
|
||||||
|
-- qu'il faut utiliser les parties de niveau du niveau actuel, localisée dans
|
||||||
|
-- self.controller.parts
|
||||||
|
|
||||||
|
partdata = self.controller.parts
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Si c'est un autre nom, on charge dans "partdata" la liste des partie de niveau
|
||||||
|
-- du fichier qu'on réfère.
|
||||||
|
|
||||||
|
local chunkfile = require("datas.chunks." .. filename)
|
||||||
|
partdata = chunkfile.parts
|
||||||
|
end
|
||||||
|
|
||||||
|
-- On charge ensuite la partie de niveau voulue, qui correspond à l'identifiant
|
||||||
|
-- qu'on a envoyé dans cette fonction (part) dans la liste partdata.
|
||||||
|
|
||||||
|
local chunklist = partdata[part]
|
||||||
|
|
||||||
|
for i, v in ipairs(chunklist) do
|
||||||
|
-- chunklist fonctionne de la même manière que partlist :
|
||||||
|
-- chaque entrée dans la liste (v) contient deux partie.
|
||||||
|
-- v[1]: l'identifiant du fichier de chunk où se trouve le chunk voulu
|
||||||
|
-- v[2]: la liste de chunks voulu. chaque entrée dedans est un nombre,
|
||||||
|
-- correspondant à un chunk
|
||||||
|
|
||||||
|
--print("- Decompressing group of chunk " .. i .. " located in " .. v[1] .. " (containing " .. #v[2] .. " chunk)")
|
||||||
|
|
||||||
|
for j, w in ipairs(v[2]) do
|
||||||
|
local chunkfile = v[1]
|
||||||
|
if (chunkfile == "") or (chunkfile == "self") then
|
||||||
|
-- si le fichier de chunk est "self", on lui indique que le chunk se trouve
|
||||||
|
-- dans le même fichier que le fichier de partie.
|
||||||
|
chunkfile = filename
|
||||||
|
end
|
||||||
|
self:addChunk(chunkfile, w)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function World:addChunk(filename, chunk)
|
||||||
|
if filename == "self" then filename = "basic" end
|
||||||
|
--print("--- Adding chunk id " .. chunk .. " located in " .. filename)
|
||||||
|
|
||||||
|
local chunkfile = require("datas.chunks." .. filename)
|
||||||
|
chunkdata = chunkfile.chunks
|
||||||
|
|
||||||
|
local chunkpos = #self.chunks
|
||||||
|
|
||||||
|
table.insert(self.chunks, chunkdata[chunk])
|
||||||
|
self:addEntitiesFromChunk(chunkpos)
|
||||||
|
self:addRailsFromChunk(chunkpos)
|
||||||
|
self:updateSize()
|
||||||
|
end
|
||||||
|
|
||||||
|
---------- ASSETS LOADING FUNCTIONS ----------
|
||||||
|
|
||||||
|
function World:loadAssets()
|
||||||
|
self.textures = {}
|
||||||
|
self.textures.tiles = love.graphics.newImage("assets/levels/normaltile.png")
|
||||||
|
self.textures.sptiles = love.graphics.newImage("assets/levels/specialtile.png")
|
||||||
|
self.textures.borders = love.graphics.newImage("assets/levels/borders.png")
|
||||||
|
|
||||||
|
self.textures.rail = love.graphics.newImage("assets/levels/rails/basic.png")
|
||||||
|
|
||||||
|
self.quads = {}
|
||||||
|
self.quads.tiles = {}
|
||||||
|
self.quads.sptiles = {}
|
||||||
|
self.quads.rails = {}
|
||||||
|
|
||||||
|
local tileline = self.controller.datas.tiles or 0
|
||||||
|
local borderline = self.controller.datas.borders or 0
|
||||||
|
|
||||||
|
|
||||||
|
local w, h = self.textures.tiles:getDimensions()
|
||||||
|
self.quads.tiles[1] = love.graphics.newQuad( 0, tileline*24, 40, 24, w, h)
|
||||||
|
self.quads.tiles[2] = love.graphics.newQuad(40, tileline*24, 40, 24, w, h)
|
||||||
|
|
||||||
|
local w, h = self.textures.sptiles:getDimensions()
|
||||||
|
local h2 = math.floor(h / 26)
|
||||||
|
for i=1, h2 do
|
||||||
|
self.quads.sptiles[i] = love.graphics.newQuad(0, (i-1)*26, 40, 26, w, h)
|
||||||
|
end
|
||||||
|
|
||||||
|
local w, h = self.textures.borders:getDimensions()
|
||||||
|
self.quads.borders = love.graphics.newQuad(0, borderline*10, 80, 10, w, h)
|
||||||
|
|
||||||
|
local w, h = self.textures.rail:getDimensions()
|
||||||
|
for i=1, 3 do
|
||||||
|
self.quads.rails[i] = love.graphics.newQuad((i-1)*31, 0, 31, 8, w, h)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---------- DATA FUNCTIONS ----------
|
||||||
|
|
||||||
|
function World:getChunkData(chunkid, turn)
|
||||||
|
local turn = turn or 1
|
||||||
|
local currentTurn, maxTurn = turn, self.controller.missiondata.turns
|
||||||
|
|
||||||
|
if chunkid <= 0 then
|
||||||
|
if currentTurn <= 1 then
|
||||||
|
return self:getFakeChunk()
|
||||||
|
else
|
||||||
|
chunkid = (chunkid - 1)
|
||||||
|
chunkid = utils.math.wrap(chunkid, 0, #self.chunks)
|
||||||
|
return self.chunks[chunkid + 1], true
|
||||||
|
end
|
||||||
|
elseif chunkid > #self.chunks then
|
||||||
|
if currentTurn >= maxTurn then
|
||||||
|
return self:getFakeChunk()
|
||||||
|
else
|
||||||
|
chunkid = (chunkid - 1)
|
||||||
|
chunkid = utils.math.wrap(chunkid, 0, #self.chunks)
|
||||||
|
return self.chunks[chunkid + 1], true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return self.chunks[chunkid], true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function World:getFakeChunk()
|
||||||
|
local fakedata = {}
|
||||||
|
local emptyline = {00, 00, 00, 00, 00, 00, 00, 00}
|
||||||
|
fakedata.objects = {emptyline, emptyline, emptyline, emptyline, emptyline}
|
||||||
|
fakedata.terrain = {emptyline, emptyline, emptyline, emptyline, emptyline}
|
||||||
|
fakedata.grind = {emptyline, emptyline, emptyline, emptyline, emptyline}
|
||||||
|
|
||||||
|
return fakedata, false
|
||||||
|
end
|
||||||
|
|
||||||
|
function World:getTerrainAtPoint(x, y, turn)
|
||||||
|
local chunkid = self:getChunkAtPoint(x, y)
|
||||||
|
local chunkdata, isTrue = self:getChunkData(chunkid, turn)
|
||||||
|
|
||||||
|
local i = math.floor(y / 20) + 1
|
||||||
|
local j = math.floor((x % (8*31)) / 31) + 1
|
||||||
|
|
||||||
|
local terrain = chunkdata.terrain[i][j]
|
||||||
|
|
||||||
|
--print(terrain)
|
||||||
|
return terrain
|
||||||
|
end
|
||||||
|
|
||||||
|
function World:getGrindAtPoint(x, y, turn)
|
||||||
|
local chunkid = self:getChunkAtPoint(x, y)
|
||||||
|
local chunkdata, isTrue = self:getChunkData(chunkid, turn)
|
||||||
|
|
||||||
|
local i = math.floor(y / 20) + 1
|
||||||
|
local j = math.floor((x % (8*31)) / 31) + 1
|
||||||
|
|
||||||
|
local grind = chunkdata.grind[i][j]
|
||||||
|
|
||||||
|
--print(terrain)
|
||||||
|
return grind > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function World:getChunkAtPoint(x, y)
|
||||||
|
return math.floor(x / (8*31)) + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---------- USEFULL FUNCTIONS ----------
|
||||||
|
|
||||||
|
function World:updateSize()
|
||||||
|
self.size = #self.chunks * 8
|
||||||
|
self.width = self.size * 31
|
||||||
|
end
|
||||||
|
|
||||||
|
function World:getZSortedItems()
|
||||||
|
local bump2d = require 'libs.bump'
|
||||||
|
local tsort = require 'libs.tsort'
|
||||||
|
local world2d = bump2d.newWorld()
|
||||||
|
|
||||||
|
local world = self.entities
|
||||||
|
|
||||||
|
for _, item in ipairs(world:getItems()) do
|
||||||
|
if item.invisible ~= true and item.destroyed ~= true then
|
||||||
|
local x,y,z,w,h,d = world:getCube(item)
|
||||||
|
if world2d:hasItem(item) then
|
||||||
|
world2d:update(item, x, y + z)
|
||||||
|
else
|
||||||
|
world2d:add(item, x, y + z, w, h + d)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local graph = tsort.new()
|
||||||
|
local noOverlap = {}
|
||||||
|
|
||||||
|
-- Iterate through all visible items, and calculate ordering of all pairs
|
||||||
|
-- of overlapping items.
|
||||||
|
-- TODO: Each pair is calculated twice currently. Maybe this is slow?
|
||||||
|
for _, itemA in ipairs(world2d:getItems()) do repeat
|
||||||
|
local x, y, w, h = world2d:getRect(itemA)
|
||||||
|
local otherItemsFilter = function(other) return other ~= itemA end
|
||||||
|
local overlapping, len = world2d:queryRect(x, y, w, h, otherItemsFilter)
|
||||||
|
|
||||||
|
if len == 0 then
|
||||||
|
table.insert(noOverlap, itemA)
|
||||||
|
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local _, aY, aZ, _, aH, aD = world:getCube(itemA)
|
||||||
|
aDepth = itemA.depth
|
||||||
|
aID = itemA.id
|
||||||
|
aType = itemA.type
|
||||||
|
|
||||||
|
--print("testing overlap with itemA " .. aID .. " (" .. aType .. ")")
|
||||||
|
|
||||||
|
|
||||||
|
for _, itemB in ipairs(overlapping) do
|
||||||
|
local _, bY, bZ, _, bH, bD = world:getCube(itemB)
|
||||||
|
bDepth = itemB.depth
|
||||||
|
bID = itemB.id
|
||||||
|
bType = itemB.type
|
||||||
|
|
||||||
|
--print("itemB " .. aID .. " overlap with item " .. bID .. " (" .. bType .. ")")
|
||||||
|
|
||||||
|
if aZ + aD <= bZ then
|
||||||
|
-- item A is completely above item B
|
||||||
|
graph:add(itemB, itemA)
|
||||||
|
--print("itemA is completely above itemB")
|
||||||
|
elseif bZ + bD <= aZ then
|
||||||
|
-- item B is completely above item A
|
||||||
|
graph:add(itemA, itemB)
|
||||||
|
--print("itemB is completely above itemA")
|
||||||
|
elseif aY + aH <= bY then
|
||||||
|
-- item A is completely behind item B
|
||||||
|
graph:add(itemA, itemB)
|
||||||
|
--print("itemA is completely behind itemB")
|
||||||
|
elseif bY + bH <= aY then
|
||||||
|
-- item B is completely behind item A
|
||||||
|
graph:add(itemB, itemA)
|
||||||
|
--print("itemB is completely behind itemA")
|
||||||
|
elseif aY + aZ + aH + aD == bY + bZ + bH + bD then
|
||||||
|
-- item A's forward-most point is the same than item B's forward-most point
|
||||||
|
--print("itemA's forward-most point is the same than itemB's forward-most point")
|
||||||
|
if aDepth > bDepth then
|
||||||
|
graph:add(itemB, itemA)
|
||||||
|
elseif aDepth < bDepth then
|
||||||
|
graph:add(itemA, itemB)
|
||||||
|
else
|
||||||
|
if aID > bID then
|
||||||
|
graph:add(itemA, itemB)
|
||||||
|
elseif aID < bID then
|
||||||
|
graph:add(itemB, itemA)
|
||||||
|
else
|
||||||
|
err("two object can't have the same ID")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif aY + aZ + aH + aD >= bY + bZ + bH + bD then
|
||||||
|
-- item A's forward-most point is in front of item B's forward-most point
|
||||||
|
graph:add(itemB, itemA)
|
||||||
|
--print("itemA's forward-most point is in front of itemB's forward-most point")
|
||||||
|
else
|
||||||
|
-- item B's forward-most point is in front of item A's forward-most point
|
||||||
|
graph:add(itemA, itemB)
|
||||||
|
--print("itemB's forward-most point is in front of itemA's forward-most point")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
until true end
|
||||||
|
|
||||||
|
local sorted, err = graph:sort()
|
||||||
|
if err then
|
||||||
|
error(err)
|
||||||
|
end
|
||||||
|
for _, item in ipairs(noOverlap) do
|
||||||
|
table.insert(sorted, item)
|
||||||
|
end
|
||||||
|
|
||||||
|
return sorted
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function sortItemsToDraw(a, b)
|
||||||
|
return a.y < b.y
|
||||||
|
end
|
||||||
|
|
||||||
|
return World
|
32
sonic-boost.love/scenes/subgame/sonic-boost/init.lua
Normal file
32
sonic-boost.love/scenes/subgame/sonic-boost/init.lua
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
local Levels = {}
|
||||||
|
local Controller = require "scenes.levels.controller"
|
||||||
|
|
||||||
|
function Levels:enter()
|
||||||
|
self.controller = Controller()
|
||||||
|
print("battle started")
|
||||||
|
--self.background = love.graphics.newImage("assets/background/testbackground.png")
|
||||||
|
end
|
||||||
|
|
||||||
|
function Levels:update(dt)
|
||||||
|
game:update(dt)
|
||||||
|
game.input:update(dt)
|
||||||
|
self.controller:update(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Levels:draw()
|
||||||
|
--CScreen:apply()
|
||||||
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
|
love.graphics.rectangle("fill", 0, 0, 424, 240)
|
||||||
|
--love.graphics.draw(self.background, 0, 0)
|
||||||
|
self.controller:draw()
|
||||||
|
--CScreen:cease()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Levels:leave()
|
||||||
|
self.controller:destroy()
|
||||||
|
self.controller = nil
|
||||||
|
|
||||||
|
collectgarbage()
|
||||||
|
end
|
||||||
|
|
||||||
|
return Levels
|
Loading…
Reference in a new issue