WIP: gamecore-0.6.0 test
This commit is contained in:
parent
1768ccd30a
commit
3ae4916a3a
|
@ -36,4 +36,26 @@ function DebugSystem:update(dt)
|
|||
lovebird.update(dt)
|
||||
end
|
||||
|
||||
-- PRINT FUNCTIONS
|
||||
-- Print and log debug string
|
||||
|
||||
function DebugSystem:print(context, string)
|
||||
if (self.active) then
|
||||
print("[DEBUG] ".. context .. ": " .. string)
|
||||
end
|
||||
end
|
||||
|
||||
function DebugSystem:warning(context, string)
|
||||
if (self.active) then
|
||||
print("[WARNING] " .. context .. ": " .. string)
|
||||
end
|
||||
end
|
||||
|
||||
function DebugSystem:error(context, string)
|
||||
if (self.active) then
|
||||
error("[ERROR] " .. context .. ": " .. string)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return DebugSystem
|
||||
|
|
|
@ -47,8 +47,8 @@ require(cwd .. "callbacks")
|
|||
-- INIT FUNCTIONS
|
||||
-- Initialize and configure the core object
|
||||
|
||||
function CoreSystem:new()
|
||||
self.debug = DebugSystem(self)
|
||||
function CoreSystem:new(DEBUGMODE)
|
||||
self.debug = DebugSystem(self, DEBUGMODE)
|
||||
self.options = Options(self)
|
||||
self.input = Input(self)
|
||||
self.screen = Screen(self)
|
||||
|
@ -56,6 +56,10 @@ function CoreSystem:new()
|
|||
self.lang = Lang(self)
|
||||
end
|
||||
|
||||
function CoreSystem:registerGameSystem(gamesystem)
|
||||
self.game = gamesystem
|
||||
end
|
||||
|
||||
-- MOUSE FUNCTIONS
|
||||
-- get directly the mouse when needed
|
||||
|
||||
|
@ -88,6 +92,10 @@ function CoreSystem:update(dt)
|
|||
self.debug:update(dt)
|
||||
self.input:update(dt)
|
||||
|
||||
if (self.game ~= nil) then
|
||||
self.game:update(dt)
|
||||
end
|
||||
|
||||
self.scenemanager:update(dt)
|
||||
end
|
||||
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local InputManager = Object:extend()
|
||||
local InputManager = Object:extend()
|
||||
local VirtualPad = Object:extend()
|
||||
|
||||
-- INIT FUNCTIONS
|
||||
-- Initialize and configure the controller system
|
||||
|
@ -31,40 +32,23 @@ function InputManager:new(controller)
|
|||
self.controller = controller
|
||||
self.data = self.controller.options:getInputData()
|
||||
|
||||
self:initKeys()
|
||||
self:initSources()
|
||||
end
|
||||
|
||||
function InputManager:initKeys()
|
||||
self.fakekeys = self:getKeyList(1)
|
||||
|
||||
self.sources = self:getSources()
|
||||
self.fakesources = self:getSources()
|
||||
function InputManager:initSources()
|
||||
self.sources = {}
|
||||
for sourceid, data in ipairs(self.data) do
|
||||
local source = VirtualPad(self, sourceid, data)
|
||||
table.insert(self.sources, source)
|
||||
end
|
||||
end
|
||||
|
||||
-- INFO FUNCTIONS
|
||||
-- Get functions from the controller object
|
||||
|
||||
function InputManager:isDown(sourceid, padkey)
|
||||
local isdown = false
|
||||
|
||||
if self.data[sourceid].type == "keyboard" then
|
||||
local key = self.data[sourceid].keys[padkey]
|
||||
isdown = love.keyboard.isDown(key)
|
||||
else
|
||||
print("Warning: unsupported input device")
|
||||
end
|
||||
|
||||
return isdown
|
||||
end
|
||||
|
||||
function InputManager:getSources()
|
||||
local sources = {}
|
||||
for i,v in ipairs(self.data) do
|
||||
sources[i] = {}
|
||||
sources[i].keys = self:getKeyList(i)
|
||||
end
|
||||
|
||||
return sources
|
||||
function InputManager:isDown(sourceid, key)
|
||||
self.controller.debug:warning("core/input", "core.input:isDown is deprecated since 0.7.0 and will be removed in 0.8.0")
|
||||
return self.sources[sourceid]:isDown(key)
|
||||
end
|
||||
|
||||
function InputManager:getKeyList(sourceid)
|
||||
|
@ -75,7 +59,6 @@ function InputManager:getKeyList(sourceid)
|
|||
keys[k].isDown = false
|
||||
keys[k].isPressed = false
|
||||
keys[k].isReleased = false
|
||||
keys[k].test = "ok"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -90,60 +73,132 @@ function InputManager:getKey(sourceid, padkey)
|
|||
return key
|
||||
end
|
||||
|
||||
function InputManager:getSources()
|
||||
return self.sources
|
||||
end
|
||||
|
||||
-- KEY MANAGEMENT FUNCTIONS
|
||||
-- Manage pressed keys
|
||||
|
||||
function InputManager:flushKeys()
|
||||
for i,v in ipairs(self.sources) do
|
||||
self:flushSourceKeys(i)
|
||||
source:flushKeys()
|
||||
end
|
||||
end
|
||||
|
||||
function InputManager:flushSourceKeys(sourceid)
|
||||
self.keys = {}
|
||||
for k,v in pairs(self.sources[sourceid].keys) do
|
||||
v = {}
|
||||
v.isDown = false
|
||||
v.isPressed = false
|
||||
v.isReleased = false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function InputManager:checkKeys(sourceid)
|
||||
for k,v in pairs(self.sources[sourceid].keys) do
|
||||
local isDown = self:isDown(sourceid, k)
|
||||
if (isDown) then
|
||||
if not (self.sources[sourceid].keys[k].isDown) then
|
||||
self.sources[sourceid].keys[k].isDown = true
|
||||
self.sources[sourceid].keys[k].isPressed = true
|
||||
self.sources[sourceid].keys[k].isReleased = false
|
||||
else
|
||||
if (self.sources[sourceid].keys[k].isPressed) then
|
||||
self.sources[sourceid].keys[k].isPressed = false
|
||||
end
|
||||
end
|
||||
else
|
||||
if (self.sources[sourceid].keys[k].isDown) then
|
||||
self.sources[sourceid].keys[k].isDown = false
|
||||
self.sources[sourceid].keys[k].isPressed = false
|
||||
self.sources[sourceid].keys[k].isReleased = true
|
||||
else
|
||||
if (self.sources[sourceid].keys[k].isReleased) then
|
||||
self.sources[sourceid].keys[k].isReleased = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self.sources[sourceid]:flushKeys()
|
||||
end
|
||||
|
||||
-- UPDATE FUNCTIONS
|
||||
-- Check every step pressed keys
|
||||
|
||||
function InputManager:update(dt)
|
||||
for i,v in ipairs(self.sources) do
|
||||
self:checkKeys(i)
|
||||
for i, source in ipairs(self.sources) do
|
||||
source:checkKeys()
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------ VIRTUALPADS -------------------------------
|
||||
-- Virtual representation of a pad
|
||||
-- The role of the virtualpad is to return all the data a controller at any time
|
||||
-- They can be flushed and deactivated for a while when needed
|
||||
|
||||
-- INIT FUNCTIONS
|
||||
-- Initialize and configure the controller system
|
||||
|
||||
function VirtualPad:new(controller, id, data)
|
||||
self.controller = controller
|
||||
self.id = id
|
||||
self.data = data
|
||||
|
||||
self.type = self.data.type or "nil"
|
||||
|
||||
self:initKeys()
|
||||
end
|
||||
|
||||
function VirtualPad:initKeys()
|
||||
local keys = {}
|
||||
if (self.data ~= nil) then
|
||||
for k,v in pairs(self.data.keys) do
|
||||
keys[k] = {}
|
||||
keys[k].isDown = false
|
||||
keys[k].isPressed = false
|
||||
keys[k].isReleased = false
|
||||
end
|
||||
end
|
||||
|
||||
self.keys = keys
|
||||
self.fakekeys = keys
|
||||
end
|
||||
|
||||
function VirtualPad:isDown(key)
|
||||
local isdown = false
|
||||
|
||||
if self.type == "keyboard" then
|
||||
isdown = love.keyboard.isDown(self.data.keys[key])
|
||||
else
|
||||
local warnstring = "unsupported input device " .. self.type .. " for source " .. self.id
|
||||
core.debug:warning("core/input", warnstring)
|
||||
end
|
||||
|
||||
return isdown
|
||||
end
|
||||
|
||||
function VirtualPad:checkKeys()
|
||||
for key, keydata in pairs(self.keys) do
|
||||
self:checkKey(key)
|
||||
end
|
||||
end
|
||||
|
||||
function VirtualPad:checkKey(key)
|
||||
local isDown = self:isDown(key)
|
||||
if (isDown) then
|
||||
if not (self.keys[key].isDown) then
|
||||
core.debug:print("virtualpad", "key " .. key .. " is Pressed")
|
||||
self.keys[key].isDown = true
|
||||
self.keys[key].isPressed = true
|
||||
self.keys[key].isReleased = false
|
||||
else
|
||||
if (self.keys[key].isPressed) then
|
||||
core.debug:print("virtualpad", "key " .. key .. " is Down")
|
||||
self.keys[key].isPressed = false
|
||||
end
|
||||
end
|
||||
else
|
||||
if (self.keys[key].isDown) then
|
||||
self.keys[key].isDown = false
|
||||
self.keys[key].isPressed = false
|
||||
self.keys[key].isReleased = true
|
||||
else
|
||||
if (self.keys[key].isReleased) then
|
||||
core.debug:print("virtualpad", "key " .. key .. " is Released")
|
||||
self.keys[key].isReleased = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function VirtualPad:getKeys()
|
||||
return self.keys
|
||||
end
|
||||
|
||||
function VirtualPad:getKey(key)
|
||||
return self.keys[key]
|
||||
end
|
||||
|
||||
function VirtualPad:flushKeys()
|
||||
for key, _ in pairs(self.keys) do
|
||||
self:flushKey(key)
|
||||
end
|
||||
end
|
||||
|
||||
function VirtualPad:flushKey(key)
|
||||
self.keys[key].isDown = false
|
||||
self.keys[key].isPressed = false
|
||||
self.keys[key].isReleased = false
|
||||
end
|
||||
|
||||
|
||||
return InputManager
|
||||
|
|
|
@ -95,7 +95,7 @@ function LanguageManager:getTranslationStringList(lang, library)
|
|||
if fileinfo ~= nil then
|
||||
list = require(_path)
|
||||
else
|
||||
print("WARNING: file " .. _path .. " do not exists")
|
||||
core.debug:warning("core/lang","file " .. _path .. " do not exists")
|
||||
end
|
||||
|
||||
return list
|
||||
|
@ -120,7 +120,7 @@ function LanguageManager:translate(library, string)
|
|||
|
||||
if (translation == nil) then
|
||||
translation = string
|
||||
print("WARNING: no translation path found for " .. string .. " in " .. library)
|
||||
core.debug:warning("core/lang", "no translation path found for " .. string .. " in " .. library)
|
||||
end
|
||||
|
||||
return translation
|
||||
|
|
|
@ -87,6 +87,14 @@ function CScreen.project(x, y)
|
|||
return math.floor((x - tx) / fsv), math.floor((y - ty) / fsv)
|
||||
end
|
||||
|
||||
function CScreen.getScale()
|
||||
return fsv
|
||||
end
|
||||
|
||||
function CScreen.getScreenCoordinate(x, y)
|
||||
return math.floor((x + tx) * fsv), math.floor((y + ty) * fsv)
|
||||
end
|
||||
|
||||
-- Change letterbox color
|
||||
function CScreen.setColor(r, g, b, a)
|
||||
cr = r
|
||||
|
|
|
@ -48,7 +48,7 @@ end
|
|||
|
||||
function Animator:update(dt)
|
||||
if (self.currentAnimation == "") then
|
||||
print("warning: no current animation data")
|
||||
core.debug:warning("animator", "no current animation data")
|
||||
return 0
|
||||
end
|
||||
|
||||
|
@ -94,6 +94,10 @@ end
|
|||
-- INFO FUNCTIONS
|
||||
-- get information with these functions
|
||||
|
||||
function Animator:getCurrentAnimation()
|
||||
return self.currentAnimation
|
||||
end
|
||||
|
||||
function Animator:getAnimationDuration(animation)
|
||||
return (self.animationData.endAt - self.animationData.startAt) / self.animationData.speed
|
||||
end
|
||||
|
@ -102,6 +106,10 @@ function Animator:getFrame()
|
|||
return self.frame
|
||||
end
|
||||
|
||||
function Animator:getRelativeFrame()
|
||||
return self.frame - (self.animationData.startAt) + 1
|
||||
end
|
||||
|
||||
function Animator:animationExist(name)
|
||||
return (self.sprite.data.animations[self.currentAnimation] ~= nil)
|
||||
end
|
||||
|
|
|
@ -90,7 +90,7 @@ function Assets:batchImport(datafile)
|
|||
elseif (asset_type == "sfx") then
|
||||
self:importSFX(assets)
|
||||
else
|
||||
print("Unkown asset type : " .. asset_type)
|
||||
core.debug:warning("assets/importer", "unkown asset type " .. asset_type)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -66,6 +66,10 @@ end
|
|||
-- INFO FUNCTIONS
|
||||
-- get information with these functions
|
||||
|
||||
function Sprite:getCurrentAnimation()
|
||||
return self.animator:getCurrentAnimation()
|
||||
end
|
||||
|
||||
function Sprite:animationExist(name)
|
||||
return self.animator:animationExist(name)
|
||||
end
|
||||
|
@ -74,6 +78,18 @@ function Sprite:getDimensions()
|
|||
return self.tileset:getDimensions()
|
||||
end
|
||||
|
||||
function Sprite:getFrame()
|
||||
return self.animator:getFrame()
|
||||
end
|
||||
|
||||
function Sprite:getAnimationDuration(animation)
|
||||
return self.animator:getAnimationDuration(animation)
|
||||
end
|
||||
|
||||
function Sprite:getRelativeFrame()
|
||||
return self.animator:getRelativeFrame()
|
||||
end
|
||||
|
||||
-- DRAW FUNCTIONS
|
||||
-- Draw sprites using these functions
|
||||
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
-- game :: The main game subsystem. Basically a big object that handle all the
|
||||
-- game-related data like characters, monsters, etc. While the core aim to be
|
||||
-- reusable at will, the game is specifically made for the current game.
|
||||
|
||||
-- It's also what handle the savedata for games
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local cwd = (...):gsub('%.init$', '') .. "."
|
||||
local cwd2 = (...):gsub('%.gamesystem.init$', '') .. "."
|
||||
|
||||
local GameSystem = Object:extend()
|
||||
local binser = require(cwd2 .. "libs.binser")
|
||||
|
||||
local DEFAULT_SAVENUMBER = 3
|
||||
|
||||
function GameSystem:new()
|
||||
self.currentSlot = -1
|
||||
|
||||
self.submodules = {}
|
||||
self.playtime = 0
|
||||
self:register()
|
||||
end
|
||||
|
||||
function GameSystem:register()
|
||||
core:registerGameSystem(self)
|
||||
end
|
||||
|
||||
function GameSystem:registerSubmodules(submodule)
|
||||
local name = submodule.name
|
||||
self.submodules[name] = submodule
|
||||
end
|
||||
|
||||
-- UPDATE FUNCTIONS
|
||||
-- Update every submodules
|
||||
|
||||
function GameSystem:update(dt)
|
||||
self.playtime = self.playtime + dt
|
||||
for k, submodule in pairs(self.submodules) do
|
||||
submodule:update(dt)
|
||||
end
|
||||
end
|
||||
|
||||
-- DATA MANAGEMENT FUNCTIONS
|
||||
-- Get and set data in the gamesystem object
|
||||
|
||||
function GameSystem:setData(data)
|
||||
local data = data
|
||||
self.playtime = data.playtime
|
||||
for k, submodule in pairs(self.submodules) do
|
||||
submodule:setData(data[k])
|
||||
end
|
||||
end
|
||||
|
||||
function GameSystem:getData()
|
||||
local data = {}
|
||||
data.playtime = self.playtime
|
||||
for k, submodule in pairs(self.submodules) do
|
||||
data[k] = submodule:getData()
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
-- SAVE MANAGEMENT FUNCTIONS
|
||||
-- Get and set data in the gamesystem object
|
||||
|
||||
function GameSystem:getSaveNumber()
|
||||
return DEFAULT_SAVENUMBER
|
||||
end
|
||||
|
||||
function GameSystem:resetSave(saveid)
|
||||
if utils.filesystem.exists("save" .. saveid .. ".save") then
|
||||
love.filesystem.remove( "save" .. saveid .. ".save" )
|
||||
end
|
||||
end
|
||||
|
||||
function GameSystem:resetAllSaves()
|
||||
for i=1, self:getSaveNumber() do
|
||||
self:resetSave(i)
|
||||
end
|
||||
end
|
||||
|
||||
function GameSystem:getSavePath(saveid, absolute)
|
||||
local saveDir = ""
|
||||
if (absolute) then
|
||||
saveDir = love.filesystem.getSaveDirectory() .. "/"
|
||||
if not utils.filesystem.exists(saveDir) then
|
||||
love.filesystem.createDirectory( "" )
|
||||
end
|
||||
end
|
||||
|
||||
local filepath = saveDir .. self:getSaveName(saveid)
|
||||
|
||||
return filepath
|
||||
end
|
||||
|
||||
function GameSystem:getSaveName(saveid)
|
||||
return "save" .. saveid .. ".save"
|
||||
end
|
||||
|
||||
function GameSystem:saveFileExist(saveid)
|
||||
return utils.filesystem.exists(self:getSaveName(saveid))
|
||||
end
|
||||
|
||||
function GameSystem:read(saveid)
|
||||
self.currentSlot = saveid or self.currentSlot
|
||||
if (self.currentSlot > 0) then
|
||||
local savepath = self:getSavePath(self.currentSlot, true)
|
||||
if self:saveFileExist(self.currentSlot) then
|
||||
local datas = binser.readFile(savepath)
|
||||
|
||||
self:setData(datas[1])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function GameSystem:write()
|
||||
if (self.currentSlot > 0) then
|
||||
local data = self:getData()
|
||||
|
||||
savepath = self:getSavePath(self.currentSlot, true)
|
||||
binser.writeFile(savepath, data)
|
||||
end
|
||||
end
|
||||
|
||||
return GameSystem
|
|
@ -0,0 +1,27 @@
|
|||
local SubModule = Object:extend()
|
||||
|
||||
function SubModule:new(game, name)
|
||||
self.name = name or error("SUBMODULE must have a name")
|
||||
self.game = game
|
||||
self.data = {}
|
||||
|
||||
self:register()
|
||||
end
|
||||
|
||||
function SubModule:update(dt)
|
||||
-- nothing by default
|
||||
end
|
||||
|
||||
function SubModule:register()
|
||||
self.game:registerSubmodules(self)
|
||||
end
|
||||
|
||||
function SubModule:getData()
|
||||
return self.data
|
||||
end
|
||||
|
||||
function SubModule:setData(data)
|
||||
self.data = data
|
||||
end
|
||||
|
||||
return SubModule
|
|
@ -150,7 +150,7 @@ end
|
|||
|
||||
function Scene:getKeys(sourceid)
|
||||
if sourceid == nil then
|
||||
print("WARNING", "no sourceid detected, will default to 1")
|
||||
core.debug:warning("scene", "no sourceid detected, will default to 1")
|
||||
end
|
||||
|
||||
local sourceid = sourceid or 1
|
||||
|
|
|
@ -26,30 +26,32 @@ local cwd = (...):gsub('%.actor2D$', '') .. "."
|
|||
local BaseActor = require(cwd .. "baseactor")
|
||||
local Actor2D = BaseActor:extend()
|
||||
|
||||
local Hitbox = require(cwd .. "utils.hitbox2D")
|
||||
|
||||
-- INIT FUNCTIONS
|
||||
-- Initialise the actor and its base functions
|
||||
|
||||
function Actor2D:new(world, type, x, y, w, h, isSolid)
|
||||
self:initHitbox(x, y, w, h)
|
||||
Actor2D.super.new(self, world, type, isSolid)
|
||||
Actor2D.super.new(self, world, type, x, y, 0, w, h, 0, isSolid)
|
||||
self:initHitboxes()
|
||||
end
|
||||
|
||||
-- MOVEMENT FUNCTIONS
|
||||
-- Basic functions from the movement.
|
||||
|
||||
function Actor2D:initMovement()
|
||||
self.xsp = 0
|
||||
self.ysp = 0
|
||||
|
||||
self.xfrc = 0
|
||||
self.yfrc = 0
|
||||
function Actor2D:destroy()
|
||||
self.world:removeActor(self)
|
||||
self.mainHitbox:destroy()
|
||||
self.isDestroyed = true
|
||||
end
|
||||
|
||||
-- PHYSICS FUNCTIONS
|
||||
-- Handle movement and collisions.
|
||||
|
||||
function Actor2D:autoMove(dt)
|
||||
self:updateHitboxes()
|
||||
self.onGround = false
|
||||
self:applyGravity(dt)
|
||||
|
||||
local newx, newy, cols, colNumber = self:move(self.x + self.xsp * dt, self.y + self.ysp * dt)
|
||||
local dx, dy = self:getFuturePosition(dt)
|
||||
local newx, newy, cols, colNumber = self:move(dx, dy)
|
||||
|
||||
-- apply after the movement the friction, until the player stop
|
||||
-- note: the friction is applied according to the delta time,
|
||||
|
@ -57,25 +59,12 @@ function Actor2D:autoMove(dt)
|
|||
|
||||
self:solveAllCollisions(cols)
|
||||
|
||||
self.xsp = utils.math.toZero(self.xsp, self.xfrc * dt)
|
||||
self.ysp = utils.math.toZero(self.ysp, self.yfrc * dt)
|
||||
self:applyFriction(dt)
|
||||
end
|
||||
|
||||
function Actor2D:solveAllCollisions(cols)
|
||||
for i, col in ipairs(cols) do
|
||||
self:collisionResponse(col)
|
||||
if (col.type == "touch") or (col.type == "bounce") or (col.type == "slide") then
|
||||
self:changeSpeedToCollisionNormal(col.normal.x, col.normal.y)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Actor2D:collisionResponse(collision)
|
||||
-- here come the response to the collision
|
||||
end
|
||||
|
||||
function Actor2D:changeSpeedToCollisionNormal(nx, ny)
|
||||
function Actor2D:changeSpeedToCollisionNormal(normal)
|
||||
local xsp, ysp = self.xsp, self.ysp
|
||||
local nx, ny = normal.x, normal.y
|
||||
|
||||
if (nx < 0 and xsp > 0) or (nx > 0 and xsp < 0) then
|
||||
xsp = -xsp * self.bounceFactor
|
||||
|
@ -88,36 +77,11 @@ function Actor2D:changeSpeedToCollisionNormal(nx, ny)
|
|||
self.xsp, self.ysp = xsp, ysp
|
||||
end
|
||||
|
||||
function Actor2D:checkGroundX()
|
||||
local dx, dy = self.x + utils.math.sign(self.xgrav), self.y
|
||||
local newx, newy, cols, colNumber = self:checkCollision(dx, dy)
|
||||
|
||||
for i, col in ipairs(cols) do
|
||||
if (col.type == "touch") or (col.type == "bounce") or (col.type == "slide") then
|
||||
if not (self.ygrav == 0) then
|
||||
if col.normal.x ~= utils.math.sign(self.xgrav) then self.onGround = true end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Actor2D:checkGroundY()
|
||||
local dx, dy = self.x, self.y + utils.math.sign(self.ygrav)
|
||||
local newx, newy, cols, colNumber = self:checkCollision(dx, dy)
|
||||
|
||||
for i, col in ipairs(cols) do
|
||||
if (col.type == "touch") or (col.type == "bounce") or (col.type == "slide") then
|
||||
if not (self.ygrav == 0) then
|
||||
if col.normal.y ~= utils.math.sign(self.ygrav) then self.onGround = true end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Actor2D:move(dx, dy)
|
||||
local cols, colNumber = {}, 0
|
||||
if (self.isDestroyed == false) then
|
||||
self.x, self.y, cols, colNumber = self.world:moveActor(self, dx, dy, self.filter)
|
||||
self.x, self.y, cols, colNumber = self.mainHitbox:checkCollision(dx, dy, self.filter)
|
||||
self.mainHitbox:updatePosition()
|
||||
end
|
||||
return self.x, self.y, cols, colNumber
|
||||
end
|
||||
|
@ -125,73 +89,114 @@ end
|
|||
function Actor2D:checkCollision(dx, dy)
|
||||
local x, y, cols, colNumber = dx, dy, {}, 0
|
||||
if (self.isDestroyed == false) then
|
||||
x, y, cols, colNumber = self.world:moveActor(self, dx, dy, self.filter)
|
||||
x, y, cols, colNumber = self.mainHitbox:checkCollision(dx, dy, self.filter)
|
||||
end
|
||||
return self.x, self.y, cols, colNumber
|
||||
end
|
||||
|
||||
function Actor2D:initGravity()
|
||||
local xgrav, ygrav
|
||||
|
||||
if (self.world.gravity.isDefault) then
|
||||
self.xgrav = self.world.gravity.xgrav
|
||||
self.ygrav = self.world.gravity.ygrav
|
||||
else
|
||||
self.xgrav = 0
|
||||
self.ygrav = 0
|
||||
end
|
||||
|
||||
self.onGround = false
|
||||
end
|
||||
|
||||
function Actor2D:setXGravity(grav)
|
||||
self.xgrav = grav
|
||||
end
|
||||
|
||||
function Actor2D:setYGravity(grav)
|
||||
self.ygrav = grav
|
||||
end
|
||||
-- GRAVITY SYSTEM FUNCTIONS
|
||||
-- All functions related to gravity
|
||||
|
||||
function Actor2D:applyGravity(dt)
|
||||
self.xsp = self.xsp + self.xgrav * dt
|
||||
self.ysp = self.ysp + self.ygrav * dt
|
||||
self.ysp = self.ysp + self.grav * dt
|
||||
|
||||
if utils.math.sign(self.ysp) == utils.math.sign(self.ygrav) then
|
||||
self:checkGroundY( )
|
||||
end
|
||||
|
||||
if utils.math.sign(self.xsp) == utils.math.sign(self.xgrav) then
|
||||
self:checkGroundX( )
|
||||
if utils.math.sign(self.ysp) == utils.math.sign(self.grav) then
|
||||
self:checkGround( )
|
||||
end
|
||||
end
|
||||
|
||||
-- COORDINATE FUNCTIONS
|
||||
-- Functions related to coordinate and hitbox
|
||||
function Actor2D:checkGround()
|
||||
local dx, dy = self.x, self.y + utils.math.sign(self.grav)
|
||||
local newx, newy, cols, colNumber = self:checkCollision(dx, dy)
|
||||
|
||||
function Actor2D:initHitbox(x, y, w, h)
|
||||
self.x = x or 0
|
||||
self.y = y or 0
|
||||
self.w = w or 0
|
||||
self.h = h or 0
|
||||
for i, col in ipairs(cols) do
|
||||
if (col.type == "touch") or (col.type == "bounce") or (col.type == "slide") then
|
||||
if not (self.grav == 0) then
|
||||
if col.normal.y ~= utils.math.sign(self.grav) then self.onGround = true end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Actor2D:getCenter()
|
||||
return (self.x + (self.w / 2)), (self.y + (self.h / 2))
|
||||
end
|
||||
-- COORDINATE/MOVEMENT FUNCTIONS
|
||||
-- Handle coordinate
|
||||
|
||||
function Actor2D:getViewCenter()
|
||||
return self:getCenter()
|
||||
local x, y = self:getCenter()
|
||||
return x, y
|
||||
end
|
||||
|
||||
function Actor2D:drawHitbox()
|
||||
local x, y = math.floor(self.x), math.floor(self.y)
|
||||
love.graphics.setColor(self.debug.r, self.debug.g, self.debug.b, 1)
|
||||
utils.graphics.box(x, y, self.w, self.h)
|
||||
-- HITBOXES FUNCTIONS
|
||||
-- Functions related to actor hitboxes
|
||||
|
||||
function Actor2D:addHitboxFromFrameData(framedata, animationID, frameID, hitboxID)
|
||||
local sx, sy = self:getSpriteScalling()
|
||||
local type = framedata[1]
|
||||
local ox = framedata[2]
|
||||
local oy = framedata[3]
|
||||
local w = framedata[4]
|
||||
local h = framedata[5]
|
||||
local isSolid = framedata[6] or false
|
||||
local anim = animationID or "null"
|
||||
local frame = frameID or 0
|
||||
local id = hitboxID or 0
|
||||
if (sx < 0) then
|
||||
ox = self.w - ox - w
|
||||
end
|
||||
if (sy < 0) then
|
||||
oy = self.h - oy - h
|
||||
end
|
||||
|
||||
if (type == "main") then
|
||||
self.mainHitbox:modify(ox, oy, w, h)
|
||||
else
|
||||
local hitboxName = anim .. frame .. type .. id
|
||||
self:addHitbox(hitboxName, type, ox, oy, w, h, isSolid)
|
||||
return hitboxName
|
||||
end
|
||||
end
|
||||
|
||||
function Actor2D:initMainHitbox()
|
||||
self.mainHitbox = Hitbox(self, self.type, 0, 0, self.w, self.h, self.isSolid)
|
||||
self.mainHitbox:advertiseAsMainHitbox()
|
||||
end
|
||||
|
||||
function Actor2D:addHitbox(name, type, ox, oy, w, h, isSolid)
|
||||
if (self.hitboxes[name] ~= nil) then
|
||||
core.debug:warning("actor2D", "the hitbox " .. name .. " already exists")
|
||||
else
|
||||
local hitbox = Hitbox(self, type, ox, oy, w, h, isSolid)
|
||||
self.hitboxes[name] = hitbox
|
||||
return hitbox
|
||||
end
|
||||
end
|
||||
|
||||
function Actor2D:checkHitboxCollisions(name, filter)
|
||||
self:checkHitboxCollisionsAtPoint(name, self.x, self.y, filter)
|
||||
end
|
||||
|
||||
function Actor2D:checkHitboxCollisionsAtPoint(name, dx, dy, filter)
|
||||
local x, y, cols, colNumber = dx, dy, {}, 0
|
||||
local filter = filter or self.filter
|
||||
if (self.isDestroyed == false) and (self.hitboxes[name] ~= nil) then
|
||||
x, y, cols, colNumber = self.hitboxes[name]:checkCollision(dx, dy, filter)
|
||||
local type = self.hitboxes[name].type
|
||||
|
||||
for i, col in ipairs(cols) do
|
||||
self:hitboxResponse(name, type, col)
|
||||
end
|
||||
end
|
||||
|
||||
return x, y, cols, colNumber
|
||||
end
|
||||
|
||||
-- DRAW FUNCTIONS
|
||||
-- Draw the actors.
|
||||
|
||||
function Actor2D:getShape()
|
||||
return (self.x), (self.y), self.w, (self.h)
|
||||
end
|
||||
|
||||
function Actor2D:draw()
|
||||
self:drawStart()
|
||||
local x, y = math.floor(self.x), math.floor(self.y)
|
||||
|
|
|
@ -0,0 +1,297 @@
|
|||
-- actor3D.lua :: the implementation of a 2D actor. It contain every element
|
||||
-- needed to create your own 2D actors.
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local cwd = (...):gsub('%.actor3D$', '') .. "."
|
||||
local BaseActor = require(cwd .. "baseactor")
|
||||
local Actor3D = BaseActor:extend()
|
||||
|
||||
local Hitbox = require(cwd .. "utils.hitbox3D")
|
||||
local Boxes = require(cwd .. "utils.boxes")
|
||||
|
||||
-- INIT FUNCTIONS
|
||||
-- Initialise the actor and its base functions
|
||||
|
||||
function Actor3D:new(world, type, x, y, z, w, h, d, isSolid)
|
||||
Actor3D.super.new(self, world, type, x, y, z, w, h, d, isSolid)
|
||||
self:initHitboxes()
|
||||
self.world:registerShape(self)
|
||||
self.boxes = Boxes
|
||||
self.doCastShadows = true
|
||||
end
|
||||
|
||||
function Actor3D:destroy()
|
||||
self:removeOldShadowTargets()
|
||||
if self.box ~= nil then
|
||||
self.world:removeTerrain(self)
|
||||
end
|
||||
self.world:removeActor(self)
|
||||
self.mainHitbox:destroy()
|
||||
self.world:removeShape(self)
|
||||
self.isDestroyed = true
|
||||
end
|
||||
|
||||
-- PHYSICS FUNCTIONS
|
||||
-- Handle movement and collisions.
|
||||
|
||||
function Actor3D:autoMove(dt)
|
||||
self:updateHitboxes()
|
||||
self.onGround = false
|
||||
self:applyGravity(dt)
|
||||
|
||||
local dx, dy, dz = self:getFuturePosition(dt)
|
||||
local newx, newy, newz, cols, colNumber = self:move(dx, dy, dz)
|
||||
|
||||
-- apply after the movement the friction, until the player stop
|
||||
-- note: the friction is applied according to the delta time,
|
||||
-- thus the friction should be how much speed is substracted in 1 second
|
||||
|
||||
self:solveAllCollisions(cols)
|
||||
|
||||
self:applyFriction(dt)
|
||||
end
|
||||
|
||||
function Actor3D:changeSpeedToCollisionNormal(normal)
|
||||
local xsp, ysp, zsp = self.xsp, self.ysp, self.zsp
|
||||
local nx, ny, nz = normal.x, normal.y, normal.z
|
||||
|
||||
if (nx < 0 and xsp > 0) or (nx > 0 and xsp < 0) then
|
||||
xsp = -xsp * self.bounceFactor
|
||||
end
|
||||
|
||||
if (ny < 0 and ysp > 0) or (ny > 0 and ysp < 0) then
|
||||
ysp = -ysp * self.bounceFactor
|
||||
end
|
||||
|
||||
if (nz < 0 and zsp > 0) or (nz > 0 and zsp < 0) then
|
||||
zsp = -zsp * self.bounceFactor
|
||||
end
|
||||
|
||||
self.xsp, self.ysp, self.zsp = xsp, ysp, zsp
|
||||
end
|
||||
|
||||
function Actor3D:move(dx, dy, dz)
|
||||
local cols, colNumber = {}, 0
|
||||
local oldx, oldy, oldz = self.x, self.y, self.z
|
||||
if (self.isDestroyed == false) then
|
||||
self.x, self.y, self.z, cols, colNumber = self.mainHitbox:checkCollision(dx, dy, dz, self.filter)
|
||||
self.mainHitbox:updatePosition()
|
||||
self.world:updateShape(self)
|
||||
end
|
||||
|
||||
if (oldx ~= self.x) or (oldy ~= self.y) or (oldz ~= self.z) or (self.shadowTargetsPrevious == nil) then
|
||||
if (self.doCastShadows) then
|
||||
self:castShadow()
|
||||
end
|
||||
end
|
||||
|
||||
return self.x, self.y, self.z, cols, colNumber
|
||||
end
|
||||
|
||||
function Actor3D:checkCollision(dx, dy, dz)
|
||||
local x, y, z, cols, colNumber = dx, dy, dz, {}, 0
|
||||
if (self.isDestroyed == false) then
|
||||
x, y, z, cols, colNumber = self.mainHitbox:checkCollision(dx, dy, dz, self.filter)
|
||||
end
|
||||
return self.x, self.y, self.z, cols, colNumber
|
||||
end
|
||||
|
||||
-- GRAVITY SYSTEM FUNCTIONS
|
||||
-- All functions related to gravity
|
||||
|
||||
function Actor3D:applyGravity(dt)
|
||||
local grav = self.grav * -1
|
||||
self.zsp = self.zsp + (grav * dt)
|
||||
|
||||
if utils.math.sign(self.zsp) == utils.math.sign(grav) then
|
||||
self:checkGround( )
|
||||
end
|
||||
end
|
||||
|
||||
function Actor3D:checkGround()
|
||||
local dx, dy, dz = self.x, self.y, self.z - utils.math.sign(self.grav)
|
||||
local newx, newy, newz, cols, colNumber = self:checkCollision(dx, dy, dz)
|
||||
for i, col in ipairs(cols) do
|
||||
if (col.type == "touch") or (col.type == "bounce") or (col.type == "slide") then
|
||||
if not (self.grav == 0) then
|
||||
if col.normal.z == utils.math.sign(self.grav) then self.onGround = true end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- COORDINATE/MOVEMENT FUNCTIONS
|
||||
-- Handle coordinate
|
||||
|
||||
function Actor3D:getViewCenter()
|
||||
local x, y, z = self:getCenter()
|
||||
return x, y - (self.d/2)
|
||||
end
|
||||
|
||||
-- HITBOXES FUNCTIONS
|
||||
-- Functions related to actor hitboxes
|
||||
|
||||
function Actor3D:addHitboxFromFrameData(framedata, animationID, frameID, hitboxID)
|
||||
local sx, sy = self:getSpriteScalling()
|
||||
local type = framedata[1]
|
||||
local ox = framedata[2]
|
||||
local oy = framedata[3]
|
||||
local oz = framedata[4]
|
||||
local w = framedata[5]
|
||||
local h = framedata[6]
|
||||
local d = framedata[7]
|
||||
local isSolid = framedata[8] or false
|
||||
local anim = animationID or "null"
|
||||
local frame = frameID or 0
|
||||
local id = hitboxID or 0
|
||||
if (sx < 0) then
|
||||
ox = self.w - ox - w
|
||||
end
|
||||
if (sy < 0) then
|
||||
oz = self.d - oz - d
|
||||
end
|
||||
|
||||
if (type == "main") then
|
||||
self.mainHitbox:modify(ox, oy, oz, w, h, d)
|
||||
else
|
||||
local hitboxName = anim .. frame .. type .. id
|
||||
self:addHitbox(hitboxName, type, ox, oy, oz, w, h, d, isSolid)
|
||||
return hitboxName
|
||||
end
|
||||
end
|
||||
|
||||
function Actor3D:initMainHitbox()
|
||||
self.mainHitbox = Hitbox(self, self.type, 0, 0, 0, self.w, self.h, self.d, self.isSolid)
|
||||
self.mainHitbox:advertiseAsMainHitbox()
|
||||
end
|
||||
|
||||
function Actor3D:addHitbox(name, type, ox, oy, oz, w, h, d, isSolid)
|
||||
if (self.hitboxes[name] ~= nil) then
|
||||
core.debug:warning("actor3D", "the hitbox " .. name .. " already exists")
|
||||
else
|
||||
local hitbox = Hitbox(self, type, ox, oy, oz, w, h, d, isSolid)
|
||||
self.hitboxes[name] = hitbox
|
||||
return hitbox
|
||||
end
|
||||
end
|
||||
|
||||
function Actor3D:checkHitboxCollisions(name, filter)
|
||||
self:checkHitboxCollisionsAtPoint(name, self.x, self.y, self.z, filter)
|
||||
end
|
||||
|
||||
function Actor3D:checkHitboxCollisionsAtPoint(name, dx, dy, dz, filter)
|
||||
local x, y, z, cols, colNumber = dx, dy, dz, {}, 0
|
||||
local filter = filter or self.filter
|
||||
if (self.isDestroyed == false) and (self.hitboxes[name] ~= nil) then
|
||||
x, y, z, cols, colNumber = self.hitboxes[name]:checkCollision(dx, dy, dz, filter)
|
||||
local type = self.hitboxes[name].type
|
||||
|
||||
for i, col in ipairs(cols) do
|
||||
self:hitboxResponse(name, type, col)
|
||||
end
|
||||
end
|
||||
|
||||
return x, y, z, cols, colNumber
|
||||
end
|
||||
|
||||
-- SHADOW FUNCTIONS
|
||||
-- Handle everything related to shadow
|
||||
|
||||
function Actor3D:castShadow()
|
||||
local shadowTargets = self.world:getTerrainInRect(self.x, self.y, self.w, self.d)
|
||||
-- initialize the shadowTargetsPrevious variable if it doesn't exist
|
||||
if (self.shadowTargetsPrevious == nil) then
|
||||
self.shadowTargetsPrevious = {}
|
||||
end
|
||||
|
||||
for i, target in ipairs(shadowTargets) do
|
||||
-- We test if the actor is below the current actor
|
||||
if (target ~= self) and (target.box ~= nil) then
|
||||
|
||||
if (target.z + target.d <= self.z + self.d) then
|
||||
-- Remove the target of the list of item targeted last update,
|
||||
-- in order to only have object no longer shadowed after the
|
||||
-- end of the loop
|
||||
for j, oldtarget in ipairs(self.shadowTargetsPrevious) do
|
||||
if (target == oldtarget) then
|
||||
table.remove(self.shadowTargetsPrevious, j)
|
||||
end
|
||||
end
|
||||
|
||||
-- We update the shadow source
|
||||
local x, y = math.floor(self.x - target.x), math.floor(self.y - target.y)
|
||||
target.box:setShadowSource(self, x, y)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- At this point, if a target is still in the shadowTargetsPrevious list,
|
||||
-- it mean that it's not shadowed. So we can simply remove the shadow.
|
||||
self:removeOldShadowTargets()
|
||||
|
||||
self.shadowTargetsPrevious = shadowTargets
|
||||
end
|
||||
|
||||
function Actor3D:removeOldShadowTargets()
|
||||
if (self.shadowTargetsPrevious ~= nil) then
|
||||
for i, target in ipairs(self.shadowTargetsPrevious) do
|
||||
if (target.box ~= nil) then
|
||||
target.box:removeShadowSource(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Actor3D:redrawShadowCanvas()
|
||||
if (self.box ~= nil) then
|
||||
self.box:redrawShadowCanvas()
|
||||
end
|
||||
end
|
||||
|
||||
-- DRAW FUNCTIONS
|
||||
-- Draw the actors.
|
||||
|
||||
function Actor3D:drawShadow(x, y)
|
||||
love.graphics.setColor(0, 0, 0, 1)
|
||||
love.graphics.rectangle("fill", x, y, self.w, self.h)
|
||||
utils.graphics.resetColor()
|
||||
end
|
||||
|
||||
function Actor3D:getShape()
|
||||
return (self.x), (self.y - self.z - self.d), self.w, (self.h + self.d)
|
||||
end
|
||||
|
||||
function Actor3D:draw()
|
||||
self:drawStart()
|
||||
if (self.box ~= nil) then
|
||||
self.box:draw(self.x, self.y, self.z)
|
||||
else
|
||||
local x, y = math.floor(self.x), math.floor(self.y - self.z - self.d + (self.h/2))
|
||||
self:drawSprite(x, y)
|
||||
end
|
||||
self:drawEnd()
|
||||
end
|
||||
|
||||
return Actor3D
|
|
@ -1,5 +1,5 @@
|
|||
-- actor2D.lua :: the global implementation of an actor. Basically, it abstract
|
||||
-- everything that isn't 2D or 3D related to the actor system.
|
||||
-- BaseActor.lua :: the global implementation of an actor. Basically, it abstract
|
||||
-- everything that isn't only 2D or 3D related to the actor system.
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
@ -30,7 +30,7 @@ local Timer = require(cwd .. "utils.timer")
|
|||
-- INIT FUNCTIONS
|
||||
-- Initialise the actor and its base functions
|
||||
|
||||
function BaseActor:new(world, type, isSolid)
|
||||
function BaseActor:new(world, type, x, y, z, w, h, d, isSolid)
|
||||
self.type = type or ""
|
||||
self.isSolid = isSolid or false
|
||||
self.depth = 0
|
||||
|
@ -39,7 +39,7 @@ function BaseActor:new(world, type, isSolid)
|
|||
self:initKeys()
|
||||
self:initTimers()
|
||||
self:setSprite()
|
||||
self:initPhysics()
|
||||
self:initPhysics(x, y, z, w, h, d)
|
||||
|
||||
self:setDebugColor(1, 1, 1)
|
||||
self:register()
|
||||
|
@ -69,17 +69,36 @@ function BaseActor:destroy()
|
|||
self.isDestroyed = true
|
||||
end
|
||||
|
||||
-- PHYSICS INITIALISATION
|
||||
-- Basic initialization of the physic systems
|
||||
-- PHYSICS FUNCTIONS
|
||||
-- Raw implementation of everything common in physics
|
||||
|
||||
function BaseActor:initPhysics(x, y, z, w, h, d)
|
||||
self:setCoordinate(x, y, z)
|
||||
|
||||
self.w = w or 0
|
||||
self.h = h or 0
|
||||
self.d = d or 0
|
||||
|
||||
self.xsp = 0
|
||||
self.ysp = 0
|
||||
self.zsp = 0
|
||||
|
||||
self.xfrc = 0
|
||||
self.yfrc = 0
|
||||
self.zfrc = 0
|
||||
|
||||
function BaseActor:initPhysics()
|
||||
self:initMovement()
|
||||
self:initGravity()
|
||||
|
||||
self:setBounceFactor()
|
||||
self:setFilter()
|
||||
end
|
||||
|
||||
function BaseActor:setCoordinate(x, y, z, w, h, d)
|
||||
self.x = x or self.x
|
||||
self.y = y or self.y
|
||||
self.z = z or self.z
|
||||
end
|
||||
|
||||
function BaseActor:setBounceFactor(newBounceFactor)
|
||||
self.bounceFactor = newBounceFactor or 0
|
||||
end
|
||||
|
@ -87,7 +106,10 @@ end
|
|||
function BaseActor:setFilter()
|
||||
-- Init the bump filter
|
||||
self.filter = function(item, other)
|
||||
if (other.isSolid) then
|
||||
if (other.owner == self) then
|
||||
-- ignore every collision with our own hitboxes
|
||||
return nil
|
||||
elseif (other.isSolid) then
|
||||
return "slide"
|
||||
else
|
||||
return "cross"
|
||||
|
@ -95,14 +117,77 @@ function BaseActor:setFilter()
|
|||
end
|
||||
end
|
||||
|
||||
function BaseActor:initMovement( )
|
||||
-- Empty placeholder function
|
||||
function BaseActor:getFuturePosition(dt)
|
||||
local dx, dy
|
||||
dx = self.x + self.xsp * dt
|
||||
dy = self.y + self.ysp * dt
|
||||
dz = self.z + self.zsp * dt
|
||||
|
||||
return dx, dy, dz
|
||||
end
|
||||
|
||||
function BaseActor:initGravity( )
|
||||
-- Empty placeholder function
|
||||
function BaseActor:applyFriction(dt)
|
||||
self.xsp = utils.math.toZero(self.xsp, self.xfrc * dt)
|
||||
self.ysp = utils.math.toZero(self.ysp, self.yfrc * dt)
|
||||
self.zsp = utils.math.toZero(self.zsp, self.zfrc * dt)
|
||||
end
|
||||
|
||||
function BaseActor:solveAllCollisions(cols)
|
||||
for i, col in ipairs(cols) do
|
||||
self:collisionResponse(col)
|
||||
if (col.type == "touch") or (col.type == "bounce") or (col.type == "slide") then
|
||||
self:changeSpeedToCollisionNormal(col.normal)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BaseActor:collisionResponse(collision)
|
||||
-- here come the response to the collision
|
||||
end
|
||||
|
||||
function BaseActor:changeSpeedToCollisionNormal(normal)
|
||||
-- Empty function in baseactor
|
||||
end
|
||||
|
||||
-- COORDINATE/MOVEMENT FUNCTIONS
|
||||
-- Handle coordinate
|
||||
|
||||
function BaseActor:getCenter()
|
||||
return (self.x + (self.w / 2)), (self.y + (self.h / 2)), (self.z + (self.d / 2))
|
||||
end
|
||||
|
||||
function BaseActor:getViewCenter()
|
||||
return self:getCenter()
|
||||
end
|
||||
|
||||
-- GRAVITY SYSTEM FUNCTIONS
|
||||
-- All functions related to gravity
|
||||
|
||||
function BaseActor:initGravity()
|
||||
if (self.world.gravity.isDefault) then
|
||||
self.grav = self.world.gravity.grav
|
||||
else
|
||||
self.grav = 0
|
||||
end
|
||||
|
||||
self.onGround = false
|
||||
end
|
||||
|
||||
function BaseActor:setGravity(grav)
|
||||
-- It's basically now a function with two roles at once :
|
||||
-- - activate the gravity
|
||||
-- - use the gravity value the dev want
|
||||
|
||||
self.grav = grav or self.world.gravity.grav
|
||||
end
|
||||
|
||||
function BaseActor:applyGravity(dt)
|
||||
-- Empty function in baseactor
|
||||
end
|
||||
|
||||
function BaseActor:checkGround()
|
||||
-- Empty function in baseactor
|
||||
end
|
||||
|
||||
-- UPDATE FUNCTIONS
|
||||
-- Theses functions are activated every steps
|
||||
|
@ -161,6 +246,101 @@ function BaseActor:timerResponse(name)
|
|||
-- here come the timer responses
|
||||
end
|
||||
|
||||
-- HITBOX FUNCTIONS
|
||||
-- All functions to handle hitboxes
|
||||
|
||||
function BaseActor:initHitboxes()
|
||||
self:initMainHitbox()
|
||||
|
||||
self.hitboxes = {}
|
||||
self.hitboxListFile = ""
|
||||
self.hitboxList = nil
|
||||
end
|
||||
|
||||
function BaseActor:initMainHitbox()
|
||||
-- Empty function : don't load ANY real hitbox function into baseactor
|
||||
end
|
||||
|
||||
function BaseActor:setHitboxFile(file)
|
||||
self.hitboxList = require(file)
|
||||
self.hitboxListFile = file
|
||||
end
|
||||
|
||||
function BaseActor:getAutomaticHitboxLoading()
|
||||
return (self.hitboxList ~= nil)
|
||||
end
|
||||
|
||||
function BaseActor:getHitboxFile()
|
||||
return self.hitboxListFile
|
||||
end
|
||||
|
||||
function BaseActor:getHitboxList(animation, frame)
|
||||
if (animation == nil) or (self.hitboxList == nil) then
|
||||
return self.hitboxList
|
||||
else
|
||||
local list = self.hitboxList[animation]
|
||||
|
||||
if (frame == nil) or (list == nil) then
|
||||
return list
|
||||
else
|
||||
return list[frame]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BaseActor:updateHitboxes()
|
||||
if (self.hitboxList ~= nil) then
|
||||
self:purgeHitbox()
|
||||
local animation, frame
|
||||
animation = self:getCurrentAnimation()
|
||||
frame = self:getRelativeFrame()
|
||||
local hitboxList = self:getHitboxList(animation, frame)
|
||||
|
||||
if (hitboxList ~= nil) then
|
||||
for i,v in ipairs(hitboxList) do
|
||||
self:addHitboxFromFrameData(v, animation, frame, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BaseActor:checkHitboxesCollisions(filter)
|
||||
for k,v in pairs(self.hitboxes) do
|
||||
self:checkHitboxCollisions(k, filter)
|
||||
end
|
||||
end
|
||||
|
||||
function BaseActor:hitboxResponse(name, type, collision)
|
||||
-- just a blank placeholder function
|
||||
end
|
||||
|
||||
function BaseActor:removeHitbox(name)
|
||||
if (self.hitboxes[name] ~= nil) then
|
||||
self.hitboxes[name]:destroy()
|
||||
self.hitboxes[name] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function BaseActor:purgeHitbox()
|
||||
for k,v in pairs(self.hitboxes) do
|
||||
v:destroy()
|
||||
end
|
||||
self.hitboxes = {}
|
||||
end
|
||||
|
||||
function BaseActor:drawHitboxes()
|
||||
for k,v in pairs(self.hitboxes) do
|
||||
v:draw()
|
||||
end
|
||||
self:drawMainHitbox()
|
||||
end
|
||||
|
||||
function BaseActor:drawMainHitbox()
|
||||
if (self.mainHitbox ~= nil) then
|
||||
self.mainHitbox:draw()
|
||||
end
|
||||
end
|
||||
|
||||
-- DRAW FUNCTIONS
|
||||
-- Draw the actors.
|
||||
|
||||
|
@ -181,7 +361,6 @@ function BaseActor:drawHUD(id, height, width)
|
|||
|
||||
end
|
||||
|
||||
|
||||
-- SPRITES FUNCTIONS
|
||||
-- Handle the sprite of the actor
|
||||
|
||||
|
@ -236,6 +415,49 @@ function BaseActor:setSpriteScallingY(sy)
|
|||
self.sprite.sy = sy
|
||||
end
|
||||
|
||||
function BaseActor:getCurrentAnimation()
|
||||
if (self.sprite.clone == nil) then
|
||||
return self.assets.sprites[self.sprite.name]:getCurrentAnimation()
|
||||
else
|
||||
return self.sprite.clone:getCurrentAnimation()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function BaseActor:getSpriteScalling()
|
||||
return self.sprite.sx, self.sprite.sy
|
||||
end
|
||||
|
||||
function BaseActor:getFrame()
|
||||
if (self.sprite.name ~= nil) then
|
||||
if (self.sprite.clone ~= nil) then
|
||||
return self.sprite.clone:getFrame()
|
||||
else
|
||||
return self.assets.sprites[self.sprite.name]:getFrame()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BaseActor:getRelativeFrame()
|
||||
if (self.sprite.name ~= nil) then
|
||||
if (self.sprite.clone ~= nil) then
|
||||
return self.sprite.clone:getRelativeFrame()
|
||||
else
|
||||
return self.assets.sprites[self.sprite.name]:getRelativeFrame()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BaseActor:getAnimationDuration()
|
||||
if (self.sprite.name ~= nil) then
|
||||
if (self.sprite.clone ~= nil) then
|
||||
return self.sprite.clone:getAnimationDuration()
|
||||
else
|
||||
return self.assets.sprites[self.sprite.name]:getAnimationDuration()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BaseActor:drawSprite(x, y, r, sx, sy, ox, oy, kx, ky)
|
||||
if (self.sprite.name ~= nil) then
|
||||
local x = x + self.sprite.ox
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
-- gfx.lua :: a basic 2D GFX.
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local cwd = (...):gsub('%.gfx3D$', '') .. "."
|
||||
local Actor3D = require(cwd .. "actor3D")
|
||||
local GFX = Actor3D:extend()
|
||||
|
||||
function GFX:new(world, x, y, z, spritename)
|
||||
local width, height = world.scene.assets.sprites[spritename]:getDimensions()
|
||||
|
||||
GFX.super.new(self, world, "gfx", x - (width/2), y - (width/2), z - (height/2), width, width, height)
|
||||
self:setSprite(spritename)
|
||||
self:cloneSprite()
|
||||
|
||||
local duration = self.sprite.clone:getAnimationDuration()
|
||||
self:addTimer("destroy", duration)
|
||||
self.depth = -100
|
||||
end
|
||||
|
||||
function GFX:timerResponse(name)
|
||||
if (name == "destroy") then
|
||||
self:destroy()
|
||||
end
|
||||
end
|
||||
|
||||
return GFX
|
|
@ -0,0 +1,32 @@
|
|||
-- box3D :: drawable box with shadow handling for fake3D actors
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local cwd = (...):gsub('%.init$', '') .. "."
|
||||
|
||||
local Boxes = {}
|
||||
|
||||
Boxes.Base = require(cwd .. "parent")
|
||||
Boxes.Textured = require(cwd .. "textured")
|
||||
Boxes.Mapped = require(cwd .. "mapped")
|
||||
|
||||
return Boxes
|
|
@ -0,0 +1,51 @@
|
|||
-- mapped.lua :: a sti-mapped box
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local cwd = (...):gsub('%.mapped$', '') .. "."
|
||||
local Box3D = require(cwd .. "parent")
|
||||
|
||||
local MappedBox = Box3D:extend()
|
||||
|
||||
function MappedBox:new(owner, x, y, z, w, h, d)
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.z = z
|
||||
|
||||
MappedBox.super.new(self, owner, w, h, d)
|
||||
self.haveLine = false
|
||||
end
|
||||
|
||||
function MappedBox:drawTextureContent()
|
||||
local tx, ty = self.x, self.y - (self.z + self.d)
|
||||
core.debug:print("mappedbox", "getting map layers at position " .. tx .. ";" .. ty)
|
||||
love.graphics.push()
|
||||
love.graphics.origin()
|
||||
love.graphics.translate(math.floor(-tx), math.floor(-ty))
|
||||
|
||||
self.world:drawMap()
|
||||
|
||||
love.graphics.pop()
|
||||
end
|
||||
|
||||
|
||||
return MappedBox
|
|
@ -0,0 +1,160 @@
|
|||
-- box3D :: drawable box with shadow handling for fake3D actors
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local Box3D = Object:extend()
|
||||
|
||||
function Box3D:new(owner, w, h, d, isVisible)
|
||||
self.owner = owner
|
||||
self.world = owner.world
|
||||
self.cameras = self.world.cameras
|
||||
|
||||
self.w = w
|
||||
self.h = h
|
||||
self.d = d
|
||||
|
||||
self.haveLine = true
|
||||
|
||||
self.shadowSources = {}
|
||||
self.needRedraw = false
|
||||
|
||||
self.isVisible = isVisible or true
|
||||
|
||||
if (self.isVisible) then
|
||||
self:setTexture()
|
||||
end
|
||||
|
||||
self.shadows = love.graphics.newCanvas(self.w, self.h)
|
||||
|
||||
self:register()
|
||||
end
|
||||
|
||||
function Box3D:register()
|
||||
self.owner.box = self
|
||||
self.world:registerTerrain(self.owner)
|
||||
end
|
||||
|
||||
function Box3D:setSizeFromOwner()
|
||||
self:setSize(self.owner.w, self.owner.h, self.owner.d)
|
||||
end
|
||||
|
||||
function Box3D:setSize()
|
||||
self.w = w
|
||||
self.h = h
|
||||
self.d = d
|
||||
|
||||
self:regenerate()
|
||||
end
|
||||
|
||||
function Box3D:setTexture()
|
||||
local canvas = love.graphics.newCanvas(self.w, self.h + self.d)
|
||||
love.graphics.setCanvas( canvas )
|
||||
utils.graphics.resetColor()
|
||||
|
||||
self:drawTextureContent()
|
||||
|
||||
love.graphics.setCanvas( )
|
||||
local imagedata = canvas:newImageData()
|
||||
self.texture = love.graphics.newImage( imagedata )
|
||||
imagedata:release()
|
||||
canvas:release()
|
||||
end
|
||||
|
||||
function Box3D:drawTextureContent()
|
||||
self:drawTopTexture()
|
||||
self:drawBottomTexture()
|
||||
end
|
||||
|
||||
function Box3D:drawTopTexture()
|
||||
utils.graphics.resetColor()
|
||||
love.graphics.rectangle("fill", 0, 0, self.w, self.h)
|
||||
end
|
||||
|
||||
function Box3D:drawBottomTexture()
|
||||
love.graphics.setColor(0.9, 0.9, 0.9, 1)
|
||||
love.graphics.rectangle("fill", 0, self.h, self.w, self.d)
|
||||
end
|
||||
|
||||
function Box3D:setShadowSource(actor, x, y)
|
||||
local foundShadow = false
|
||||
|
||||
for i,v in ipairs(self.shadowSources) do
|
||||
if (v.actor == actor) then
|
||||
if (v.x ~= x) or (v.y ~= y) then
|
||||
v.x = x
|
||||
v.y = y
|
||||
self.needRedraw = true
|
||||
end
|
||||
foundShadow = true
|
||||
end
|
||||
end
|
||||
|
||||
if (foundShadow == false) then
|
||||
local shadow = {}
|
||||
shadow.actor = actor
|
||||
shadow.x = x
|
||||
shadow.y = y
|
||||
self.needRedraw = true
|
||||
|
||||
table.insert(self.shadowSources, shadow)
|
||||
end
|
||||
end
|
||||
|
||||
function Box3D:removeShadowSource(actor)
|
||||
for i,v in ipairs(self.shadowSources) do
|
||||
if (v.actor == actor) then
|
||||
table.remove(self.shadowSources, i)
|
||||
self.needRedraw = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Box3D:redrawShadowCanvas()
|
||||
if (self.needRedraw) then
|
||||
love.graphics.setCanvas( self.shadows )
|
||||
love.graphics.clear()
|
||||
for i,v in ipairs(self.shadowSources) do
|
||||
v.actor:drawShadow(v.x, v.y)
|
||||
end
|
||||
|
||||
love.graphics.setCanvas( )
|
||||
|
||||
self.needRedraw = false
|
||||
end
|
||||
end
|
||||
|
||||
function Box3D:draw(x, y, z)
|
||||
if (self.isVisible) then
|
||||
love.graphics.setColor(0, 0, 0, 1)
|
||||
if (self.haveLine) then
|
||||
love.graphics.rectangle("line", x, (y-z) - (self.d), self.w, self.d + self.h)
|
||||
end
|
||||
utils.graphics.resetColor()
|
||||
love.graphics.draw(self.texture, x, (y-z) - (self.d))
|
||||
end
|
||||
|
||||
if (self.shadows ~= nil) and (#self.shadowSources > 0) then
|
||||
love.graphics.draw(self.shadows, x, (y-z) - (self.d))
|
||||
end
|
||||
end
|
||||
|
||||
return Box3D
|
|
@ -0,0 +1,53 @@
|
|||
-- textured.lua :: a textured box
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local cwd = (...):gsub('%.textured$', '') .. "."
|
||||
local Box3D = require(cwd .. "parent")
|
||||
|
||||
local TexturedBox = Box3D:extend()
|
||||
|
||||
function TexturedBox:new(owner, w, h, d, topTexture, bottomTexture)
|
||||
local bottomTexture = bottomTexture or topTexture
|
||||
|
||||
self.topTexture = owner.assets.images[topTexture]
|
||||
self.bottomTexture = owner.assets.images[bottomTexture]
|
||||
|
||||
TexturedBox.super.new(self, owner, w, h, d)
|
||||
self.haveLine = false
|
||||
end
|
||||
|
||||
function TexturedBox:drawTopTexture()
|
||||
local w, h = self.topTexture:getDimensions()
|
||||
local sx = self.w / w
|
||||
local sy = self.h / h
|
||||
self.topTexture:draw(0, 0, 0, sx, sy)
|
||||
end
|
||||
|
||||
function TexturedBox:drawBottomTexture()
|
||||
local w, h = self.topTexture:getDimensions()
|
||||
local sx = self.w / w
|
||||
local sy = self.d / h
|
||||
self.bottomTexture:draw(0, self.h, 0, sx, sy)
|
||||
end
|
||||
|
||||
return TexturedBox
|
|
@ -0,0 +1,123 @@
|
|||
-- hitbox2D.lua :: a basic 2D hitbox object. It's used by the actors to check
|
||||
-- collisions and to handle different type of responses.
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local Hitbox2D = Object:extend()
|
||||
|
||||
-- INIT FUNCTIONS
|
||||
-- Initialise the actor and its base functions
|
||||
|
||||
function Hitbox2D:new(owner, type, ox, oy, w, h, isSolid)
|
||||
self.owner = owner
|
||||
self.world = owner.world
|
||||
|
||||
self.type = type
|
||||
self.ox = ox
|
||||
self.oy = oy
|
||||
self.x, self.y = self:getPosition()
|
||||
self.w = w
|
||||
self.h = h
|
||||
self.isSolid = isSolid
|
||||
|
||||
self.isMainHitBox = false
|
||||
|
||||
self:setDebugColor(0,0,0)
|
||||
self:register()
|
||||
end
|
||||
|
||||
function Hitbox2D:advertiseAsMainHitbox()
|
||||
self.isMainHitBox = true
|
||||
end
|
||||
|
||||
function Hitbox2D:modify(ox, oy, w, h)
|
||||
self.ox = ox
|
||||
self.oy = oy
|
||||
self.x, self.y = self:getPosition()
|
||||
self.w = w
|
||||
self.h = h
|
||||
end
|
||||
|
||||
function Hitbox2D:setDebugColor(r,g,b)
|
||||
self.debug = {}
|
||||
self.debug.r = r
|
||||
self.debug.g = g
|
||||
self.debug.b = b
|
||||
end
|
||||
|
||||
function Hitbox2D:register()
|
||||
self.world:registerBody(self)
|
||||
end
|
||||
|
||||
function Hitbox2D:destroy()
|
||||
self.world:removeBody(self)
|
||||
end
|
||||
|
||||
-- COORDINATE FUNCTIONS
|
||||
-- Handle Hitbox position
|
||||
|
||||
function Hitbox2D:updatePosition()
|
||||
self.x, self.y = self:getPosition()
|
||||
self.world:updateBody(self)
|
||||
return self.x, self.y
|
||||
end
|
||||
|
||||
function Hitbox2D:getPosition()
|
||||
return self.ox + self.owner.x, self.oy + self.owner.y
|
||||
end
|
||||
|
||||
function Hitbox2D:getOwnerPosition()
|
||||
return self.x - self.ox, self.y - self.oy
|
||||
end
|
||||
|
||||
function Hitbox2D:getNewOwnerPosition(x, y)
|
||||
return x - self.ox, y - self.oy
|
||||
end
|
||||
|
||||
function Hitbox2D:getCenter()
|
||||
return self.x + (self.w/2), self.y + (self.h/2)
|
||||
end
|
||||
|
||||
-- COLLISION FUNCTIONS
|
||||
-- Handle Hitbox position
|
||||
|
||||
function Hitbox2D:checkCollision(dx, dy, filter)
|
||||
self:updatePosition()
|
||||
|
||||
local dx, dy = self.ox + dx, self.oy + dy
|
||||
local x, y, cols, colNumber = self.world:checkCollision(self, dx, dy, filter)
|
||||
local newx, newy = self:getNewOwnerPosition(x, y)
|
||||
|
||||
return newx, newy, cols, colNumber
|
||||
end
|
||||
|
||||
-- DRAW FUNCTIONS
|
||||
-- Just some debug function to draw hitbox
|
||||
|
||||
function Hitbox2D:draw()
|
||||
local x, y = self:getPosition()
|
||||
love.graphics.setColor(self.debug.r, self.debug.g, self.debug.b, 1)
|
||||
utils.graphics.box(x, y, self.w, self.h)
|
||||
utils.graphics.resetColor()
|
||||
end
|
||||
|
||||
return Hitbox2D
|
|
@ -0,0 +1,129 @@
|
|||
-- hitbox3D.lua :: a basic 3D hitbox object. It's used by the actors to check
|
||||
-- collisions and to handle different type of responses.
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local Hitbox3D = Object:extend()
|
||||
|
||||
-- INIT FUNCTIONS
|
||||
-- Initialise the actor and its base functions
|
||||
|
||||
function Hitbox3D:new(owner, type, ox, oy, oz, w, h, d, isSolid)
|
||||
self.owner = owner
|
||||
self.world = owner.world
|
||||
|
||||
self.type = type
|
||||
self.ox = ox
|
||||
self.oy = oy
|
||||
self.oz = oz
|
||||
self.x, self.y, self.z = self:getPosition()
|
||||
self.w = w
|
||||
self.h = h
|
||||
self.d = d
|
||||
self.isSolid = isSolid
|
||||
|
||||
self.isMainHitBox = false
|
||||
|
||||
self:setDebugColor(0,0,0)
|
||||
self:register()
|
||||
end
|
||||
|
||||
function Hitbox3D:advertiseAsMainHitbox()
|
||||
self.isMainHitBox = true
|
||||
end
|
||||
|
||||
function Hitbox3D:modify(ox, oy, oz, w, h, d)
|
||||
self.ox = ox
|
||||
self.oy = oy
|
||||
self.oz = oz
|
||||
self.x, self.y, self.z = self:getPosition()
|
||||
self.w = w
|
||||
self.h = h
|
||||
self.d = d
|
||||
end
|
||||
|
||||
function Hitbox3D:setDebugColor(r,g,b)
|
||||
self.debug = {}
|
||||
self.debug.r = r
|
||||
self.debug.g = g
|
||||
self.debug.b = b
|
||||
end
|
||||
|
||||
function Hitbox3D:register()
|
||||
self.world:registerBody(self)
|
||||
end
|
||||
|
||||
function Hitbox3D:destroy()
|
||||
self.world:removeBody(self)
|
||||
end
|
||||
|
||||
-- COORDINATE FUNCTIONS
|
||||
-- Handle Hitbox position
|
||||
|
||||
function Hitbox3D:updatePosition()
|
||||
self.x, self.y, self.z = self:getPosition()
|
||||
self.world:updateBody(self)
|
||||
return self.x, self.y, self.z
|
||||
end
|
||||
|
||||
function Hitbox3D:getPosition()
|
||||
return self.ox + self.owner.x, self.oy + self.owner.y, self.oz + self.owner.z
|
||||
end
|
||||
|
||||
function Hitbox3D:getOwnerPosition()
|
||||
return self.x - self.ox, self.y - self.oy, self.z - self.oz
|
||||
end
|
||||
|
||||
function Hitbox3D:getNewOwnerPosition(x, y, z)
|
||||
return x - self.ox, y - self.oy, z - self.oz
|
||||
end
|
||||
|
||||
function Hitbox3D:getCenter()
|
||||
return self.x + (self.w/2), self.y + (self.h/2), self.z + (self.d/2)
|
||||
end
|
||||
|
||||
-- COLLISION FUNCTIONS
|
||||
-- Handle Hitbox position
|
||||
|
||||
function Hitbox3D:checkCollision(dx, dy, dz, filter)
|
||||
self:updatePosition()
|
||||
|
||||
local dx, dy = self.ox + dx, self.oy + dy, self.oz + dz
|
||||
local x, y, z, cols, colNumber = self.world:checkCollision(self, dx, dy, dz, filter)
|
||||
local newx, newy, newz = self:getNewOwnerPosition(x, y, z)
|
||||
|
||||
return newx, newy, newz, cols, colNumber
|
||||
end
|
||||
|
||||
-- DRAW FUNCTIONS
|
||||
-- Just some debug function to draw hitbox
|
||||
|
||||
function Hitbox3D:draw()
|
||||
local x, y, z = self:getPosition()
|
||||
love.graphics.setColor(self.debug.r, self.debug.g, self.debug.b, 1)
|
||||
utils.graphics.box(x, (y-z) - (self.d), self.w, self.h)
|
||||
love.graphics.setColor(self.debug.r/2, self.debug.g/2, self.debug.b/2, 1)
|
||||
utils.graphics.box(x, (y-z) - (self.d) + (self.h), self.w, self.d)
|
||||
utils.graphics.resetColor()
|
||||
end
|
||||
|
||||
return Hitbox3D
|
|
@ -26,7 +26,7 @@ local cwd = (...):gsub('%.baseworld$', '') .. "."
|
|||
|
||||
local BaseWorld = Object:extend()
|
||||
|
||||
local Sti = require(cwd .. "libs.sti")
|
||||
local mapObjects = require(cwd .. "maps")
|
||||
local CameraSystem = require(cwd .. "camera")
|
||||
|
||||
local PADDING_VALUE = 10/100
|
||||
|
@ -34,17 +34,18 @@ local PADDING_VALUE = 10/100
|
|||
-- INIT FUNCTIONS
|
||||
-- All functions to init the world and the map
|
||||
|
||||
function BaseWorld:new(scene, actorlist, mapfile)
|
||||
function BaseWorld:new(scene, actorlist, mapfile, maptype)
|
||||
self.scene = scene
|
||||
self.actorlist = actorlist
|
||||
self.mapfile = mapfile
|
||||
self.mapObjects = mapObjects
|
||||
|
||||
self.cameras = CameraSystem(self)
|
||||
self:initActors()
|
||||
|
||||
self:initPlayers()
|
||||
self:setActorList(self.actorlist)
|
||||
self:initMap(self.mapfile)
|
||||
self:initMap(self.mapfile, maptype)
|
||||
self:setGravity()
|
||||
|
||||
self:register()
|
||||
|
@ -60,20 +61,22 @@ function BaseWorld:setActorList(actorlist)
|
|||
end
|
||||
end
|
||||
|
||||
function BaseWorld:initMap(mapfile)
|
||||
self.haveMap = false
|
||||
self.haveBackgroundColor = false
|
||||
self.backcolor = {128, 128, 128}
|
||||
function BaseWorld:initMap(mapfile, maptype)
|
||||
if (mapfile ~= nil) then
|
||||
self.maptype = maptype or "sti"
|
||||
else
|
||||
self.maptype = "empty"
|
||||
end
|
||||
|
||||
self.mapfile = mapfile
|
||||
end
|
||||
|
||||
function BaseWorld:setGravity(xgrav, ygrav, isDefault)
|
||||
local xgrav = xgrav or 0
|
||||
local ygrav = ygrav or 0
|
||||
local isDefault = isDefault or 0
|
||||
function BaseWorld:setGravity(grav, isDefault)
|
||||
local grav = grav or 0
|
||||
local isDefault = isDefault or false
|
||||
|
||||
self.gravity = {}
|
||||
self.gravity.xgrav, self.gravity.ygrav = xgrav, ygrav
|
||||
self.gravity.grav = grav
|
||||
self.gravity.isDefault = isDefault
|
||||
end
|
||||
|
||||
|
@ -84,7 +87,7 @@ end
|
|||
function BaseWorld:reset()
|
||||
self:initActors()
|
||||
self:initPlayers()
|
||||
self:initMap(self.mapfile)
|
||||
self:initMap(self.mapfile, self.maptype)
|
||||
self.cameras:initViews()
|
||||
collectgarbage()
|
||||
|
||||
|
@ -99,11 +102,17 @@ function BaseWorld:initActors( )
|
|||
self.currentCreationID = 0
|
||||
end
|
||||
|
||||
function BaseWorld:newActor(name, x, y)
|
||||
function BaseWorld:newActor(name, x, y, z)
|
||||
local debugstring = " at (" .. x .. ";" .. y .. ")."
|
||||
core.debug:print("world2D", "adding actor " .. name .. debugstring)
|
||||
self.obj.index[name](self, x, y)
|
||||
end
|
||||
|
||||
function BaseWorld:newCollision(name, x, y, w, h)
|
||||
function BaseWorld:newCollision(name, x, y, z, w, h, d)
|
||||
local debugstringpos = "at (" .. x .. ";" .. y .. ")"
|
||||
local debugstringsize = "size is (" .. w .. ";" .. h .. ")"
|
||||
local debugstring = " " .. debugstringpos .. ". " .. debugstringsize .. "."
|
||||
core.debug:print("world2D", "creating collision " .. name .. debugstring)
|
||||
self.obj.collisions[name](self, x, y, w, h)
|
||||
end
|
||||
|
||||
|
@ -121,19 +130,15 @@ function BaseWorld:removeActor(actor)
|
|||
end
|
||||
end
|
||||
|
||||
function BaseWorld:moveActor(actor, x, y, filter)
|
||||
-- as the baseworld have no collision function, we return empty collision
|
||||
-- datas, but from the same type than bump2D will return
|
||||
return x, y, {}, 0
|
||||
function BaseWorld:countActors()
|
||||
return #self.actors
|
||||
end
|
||||
|
||||
function BaseWorld:checkCollision(actor, x, y, filter)
|
||||
-- as the baseworld have no collision function, we return empty collision
|
||||
-- datas, but from the same type than bump2D will return
|
||||
return x, y, {}, 0
|
||||
function BaseWorld:getActors()
|
||||
return self.actors
|
||||
end
|
||||
|
||||
function BaseWorld:queryRect(x, y, w, h)
|
||||
function BaseWorld:getActorsInRect(x, y, w, h)
|
||||
local query = {}
|
||||
local x2, y2 = x + w, y + h
|
||||
for i,v in ipairs(self.actors) do
|
||||
|
@ -147,23 +152,64 @@ function BaseWorld:queryRect(x, y, w, h)
|
|||
return v
|
||||
end
|
||||
|
||||
function BaseWorld:countActors()
|
||||
return #self.actors
|
||||
end
|
||||
|
||||
function BaseWorld:getActors()
|
||||
return self.actors
|
||||
end
|
||||
|
||||
function BaseWorld:getVisibleActors(id)
|
||||
local camx, camy, camw, camh = self.cameras:getViewCoordinate(id)
|
||||
local paddingw = camw * PADDING_VALUE
|
||||
local paddingh = camh * PADDING_VALUE
|
||||
local x = camx - paddingw
|
||||
local y = camy - paddingh
|
||||
local w = camw + paddingw * 2
|
||||
local h = camh + paddingh * 2
|
||||
return self:queryRect(x, y, w, h)
|
||||
local actors = {}
|
||||
if (id ~= nil) then
|
||||
local camx, camy, camw, camh = self.cameras:getViewCoordinate(id)
|
||||
local paddingw = camw * PADDING_VALUE
|
||||
local paddingh = camh * PADDING_VALUE
|
||||
local x = camx - paddingw
|
||||
local y = camy - paddingh
|
||||
local w = camw + paddingw * 2
|
||||
local h = camh + paddingh * 2
|
||||
|
||||
actors = self:getActorsInRect(x, y, w, h)
|
||||
else
|
||||
actors = self:getActors()
|
||||
end
|
||||
|
||||
table.sort(actors, function(a,b)
|
||||
if (a.depth == b.depth) then
|
||||
return a.creationID < b.creationID
|
||||
else
|
||||
return a.depth > b.depth
|
||||
end
|
||||
end)
|
||||
|
||||
return actors
|
||||
end
|
||||
|
||||
-- BODIES MANAGEMENT FUNCTIONS
|
||||
-- Basic function to handle bodies. Empty function here as baseworld doesn't
|
||||
-- handle bodies
|
||||
|
||||
function BaseWorld:registerBody(body)
|
||||
return nil
|
||||
end
|
||||
|
||||
function BaseWorld:updateBody(body)
|
||||
return x, y, {}, 0
|
||||
end
|
||||
|
||||
function BaseWorld:removeBody(body)
|
||||
return nil
|
||||
end
|
||||
|
||||
function BaseWorld:moveActor(actor, x, y, filter)
|
||||
-- as the baseworld have no collision function, we return empty collision
|
||||
-- datas, but from the same type than bump2D will return
|
||||
return x, y, {}, 0
|
||||
end
|
||||
|
||||
function BaseWorld:checkCollision(actor, x, y, filter)
|
||||
-- as the baseworld have no collision function, we return empty collision
|
||||
-- datas, but from the same type than bump2D will return
|
||||
return x, y, {}, 0
|
||||
end
|
||||
|
||||
function BaseWorld:getBodiesInRect(x, y, w, h)
|
||||
return {}
|
||||
end
|
||||
|
||||
-- INFO FUNCTIONS
|
||||
|
@ -180,21 +226,6 @@ end
|
|||
-- PLAYER MANAGEMENT FUNCTIONS
|
||||
-- Basic function to handle player actors
|
||||
|
||||
function BaseWorld:loadMap()
|
||||
local mapfile = self.mapfile
|
||||
if mapfile == nil then
|
||||
self.haveMap = false
|
||||
self.haveBackgroundColor = false
|
||||
self.backcolor = {128, 128, 128}
|
||||
else
|
||||
self.haveMap = true
|
||||
self.map = Sti(mapfile)
|
||||
self.haveBackgroundColor = true
|
||||
self.backcolor = self.map.backgroundcolor or {128, 128, 128}
|
||||
self:loadMapObjects()
|
||||
end
|
||||
end
|
||||
|
||||
function BaseWorld:initPlayers()
|
||||
self.players = {}
|
||||
self.playerNumber = 1
|
||||
|
@ -204,19 +235,22 @@ function BaseWorld:setPlayerNumber(playerNumber)
|
|||
self.playerNumber = playerNumber or 1
|
||||
end
|
||||
|
||||
function BaseWorld:addPlayer(actor, sourceid, haveCam)
|
||||
function BaseWorld:addPlayer(x, y, z, id)
|
||||
local player = {}
|
||||
player.actor = actor
|
||||
player.sourceid = sourceid or 1
|
||||
if id <= self.playerNumber then
|
||||
player.actor = self:newPlayer(x, y, z)
|
||||
player.sourceid = sourceid or 1
|
||||
|
||||
table.insert(self.players, player)
|
||||
table.insert(self.players, player)
|
||||
|
||||
if (haveCam) then
|
||||
local xx, yy = player.actor:getViewCenter()
|
||||
self.cameras:addView(xx, yy, player.actor)
|
||||
self.cameras:addTarget(player.actor)
|
||||
end
|
||||
end
|
||||
|
||||
function BaseWorld:newPlayer(x, y, z)
|
||||
return self.obj.Player(self, x, y)
|
||||
end
|
||||
|
||||
function BaseWorld:sendInputToPlayers(actor)
|
||||
for i,v in ipairs(self.players) do
|
||||
--TODO: make the player get from a selected source inputs
|
||||
|
@ -236,95 +270,58 @@ end
|
|||
-- MAP FUNCTIONS
|
||||
-- All map wrappers
|
||||
|
||||
function BaseWorld:loadMap()
|
||||
self:createMapController()
|
||||
self:loadMapObjects()
|
||||
end
|
||||
|
||||
function BaseWorld:createMapController()
|
||||
if (self.maptype == "empty") then
|
||||
self.mapObjects.Base(self)
|
||||
elseif (self.maptype == "sti") then
|
||||
self.mapObjects.Sti(self, self.mapfile)
|
||||
end
|
||||
end
|
||||
|
||||
function BaseWorld:loadMapObjects()
|
||||
self:loadMapCollisions()
|
||||
self:loadMapPlayers()
|
||||
self:loadMapActors()
|
||||
end
|
||||
|
||||
function BaseWorld:loadMapCollisions()
|
||||
for k, objectlayer in pairs(self.map.layers) do
|
||||
if self:isCollisionIndexed(objectlayer.name) then
|
||||
print("DEBUG: loading actors in " .. objectlayer.name .. " collision layer")
|
||||
for k, object in pairs(objectlayer.objects) do
|
||||
self:newCollision(objectlayer.name, object.x, object.y, object.width, object.height)
|
||||
end
|
||||
self.map:removeLayer(objectlayer.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BaseWorld:loadMapActors()
|
||||
for k, objectlayer in pairs(self.map.layers) do
|
||||
if self:isActorIndexed(objectlayer.name) then
|
||||
print("DEBUG: loading actors in " .. objectlayer.name .. " actor layer")
|
||||
for k, object in pairs(objectlayer.objects) do
|
||||
if (object.properties.batchActor) then
|
||||
self:batchActor(objectlayer.name, object)
|
||||
else
|
||||
self:newActor(objectlayer.name, object.x, object.y)
|
||||
end
|
||||
end
|
||||
self.map:removeLayer(objectlayer.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BaseWorld:batchActor(name, object)
|
||||
local gwidth = object.properties.gwidth or self.map.tilewidth
|
||||
local gheight = object.properties.gheight or self.map.tileheight
|
||||
local x = object.x
|
||||
local y = object.y
|
||||
local w = object.width
|
||||
local h = object.height
|
||||
|
||||
local cellHor = math.ceil(w / gwidth)
|
||||
local cellVert = math.ceil(h / gheight)
|
||||
|
||||
for i=1, cellHor do
|
||||
for j=1, cellVert do
|
||||
self:newActor(name, x + (i-1)*gwidth, y + (j-1)*gheight)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BaseWorld:loadMapPlayers()
|
||||
for k, objectlayer in pairs(self.map.layers) do
|
||||
if (objectlayer.name == "player") then
|
||||
print("DEBUG: loading actors in player layer")
|
||||
local i = 1
|
||||
for k, object in pairs(objectlayer.objects) do
|
||||
if (i <= self.playerNumber) then
|
||||
-- TODO: don't hardcode camera handling
|
||||
self:addPlayer(self.obj.Player(self, object.x, object.y), i, true)
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
self.map:removeLayer(objectlayer.name)
|
||||
end
|
||||
if (self.map ~= nil) then
|
||||
self.map:loadObjects()
|
||||
end
|
||||
end
|
||||
|
||||
function BaseWorld:getDimensions()
|
||||
if self.haveMap then
|
||||
return self.map.width * self.map.tilewidth,
|
||||
self.map.height * self.map.tileheight
|
||||
if (self.map ~= nil) then
|
||||
return self.map:getDimensions()
|
||||
else
|
||||
return core.screen:getDimensions()
|
||||
end
|
||||
end
|
||||
|
||||
function BaseWorld:setBackgroundColor(r, g, b)
|
||||
self.backcolor = {r, g, b}
|
||||
self.haveBackgroundColor = true
|
||||
if (self.map ~= nil) then
|
||||
self.map:setBackgroundColor(r, g, b)
|
||||
end
|
||||
end
|
||||
|
||||
function BaseWorld:removeBackgroundColor()
|
||||
self.haveBackgroundColor = false
|
||||
if (self.map ~= nil) then
|
||||
self.map:setBackgroundColor()
|
||||
end
|
||||
end
|
||||
|
||||
function BaseWorld:getBackgroundColor()
|
||||
return self.backcolor[1]/256, self.backcolor[2]/256, self.backcolor[3]/256
|
||||
if (self.map ~= nil) then
|
||||
return self.map:getBackgroundColor()
|
||||
else
|
||||
return 0, 0, 0
|
||||
end
|
||||
end
|
||||
|
||||
function BaseWorld:resizeMap(w, h)
|
||||
if (self.map ~= nil) then
|
||||
local w, h = utils.math.floorCoord(w, h)
|
||||
self.map:resize(w, h)
|
||||
end
|
||||
end
|
||||
|
||||
-- Lock MANAGEMENT FUNCTIONS
|
||||
|
@ -360,12 +357,11 @@ function BaseWorld:updateActors(dt)
|
|||
end
|
||||
|
||||
function BaseWorld:updateMap(dt)
|
||||
if self.haveMap then
|
||||
if (self.map ~= nil) then
|
||||
self.map:update(dt)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- DRAW FUNCTIONS
|
||||
-- All function to draw the map, world and actors
|
||||
|
||||
|
@ -382,26 +378,12 @@ function BaseWorld:draw(dt)
|
|||
self:drawMap(i)
|
||||
self:drawActors(i)
|
||||
self.cameras:detachView(i)
|
||||
self.cameras:drawHUD(i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BaseWorld:drawActors(id)
|
||||
local actors
|
||||
if (id == nil) then
|
||||
actors = self:getActors()
|
||||
else
|
||||
actors = self:getVisibleActors(id)
|
||||
end
|
||||
|
||||
table.sort(actors, function(a,b)
|
||||
if (a.depth == b.depth) then
|
||||
return a.creationID < b.creationID
|
||||
else
|
||||
return a.depth > b.depth
|
||||
end
|
||||
end)
|
||||
local actors = self:getVisibleActors(id)
|
||||
|
||||
for i,v in ipairs(actors) do
|
||||
v:draw()
|
||||
|
@ -409,28 +391,14 @@ function BaseWorld:drawActors(id)
|
|||
end
|
||||
|
||||
function BaseWorld:drawMap(id)
|
||||
local tx, ty, scale = 0, 0, 1
|
||||
if id ~= nil then
|
||||
-- Du à la manière dont fonctionne STI, on est obligé de récupérer les info
|
||||
-- de position de camera pour afficher la carte par rapport à ces infos
|
||||
tx, ty = self.cameras:getViewCoordinate(id)
|
||||
scale = self.cameras:getViewScale(id) or 1
|
||||
local vx, vy = self.cameras:getOnScreenViewRelativePosition(id)
|
||||
tx = math.floor(tx - math.abs(vx))
|
||||
ty = math.floor(ty - math.abs(vy))
|
||||
end
|
||||
|
||||
if self.haveMap then
|
||||
self.map:draw(-tx, -ty, scale, scale)
|
||||
if (self.map ~= nil) then
|
||||
self.map:draw()
|
||||
end
|
||||
end
|
||||
|
||||
function BaseWorld:drawBackgroundColor()
|
||||
if self.haveBackgroundColor then
|
||||
local r, g, b = self.backcolor[1], self.backcolor[2], self.backcolor[3]
|
||||
love.graphics.setColor(r/256, g/256, b/256)
|
||||
love.graphics.rectangle("fill", 0, 0, 480, 272)
|
||||
utils.graphics.resetColor()
|
||||
if (self.map ~= nil) then
|
||||
self.map:drawBackgroundColor()
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,337 +0,0 @@
|
|||
-- camera.lua :: a basic camera adapted to the asset/world system.
|
||||
-- Use hump.camera as the view backend.
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local cwd = (...):gsub('%.camera$', '') .. "."
|
||||
|
||||
local CameraSystem = Object:extend()
|
||||
local View = require(cwd .. "libs.hump.camera")
|
||||
|
||||
local SPLITSCREEN_ISVERTICAL = false
|
||||
local SCREEN_LIMIT = 4
|
||||
|
||||
-- INIT FUNCTIONS
|
||||
-- Initialize the camera system
|
||||
|
||||
function CameraSystem:new(world)
|
||||
self.scene = world.scene
|
||||
self.world = world
|
||||
|
||||
self.verticalSplit = SPLITSCREEN_ISVERTICAL
|
||||
|
||||
self:initViews()
|
||||
end
|
||||
|
||||
function CameraSystem:initViews()
|
||||
self.views = {}
|
||||
|
||||
self.views.list = {}
|
||||
self.views.basewidth, self.views.baseheight = core.screen:getDimensions()
|
||||
self.views.width, self.views.height = self:getViewsDimensions()
|
||||
|
||||
self.views.posList = {}
|
||||
self.views.posList.dual = {}
|
||||
self.views.posList.multi = {}
|
||||
|
||||
if (self.verticalSplit) then
|
||||
self.views.posList.dual[1] = {}
|
||||
self.views.posList.dual[1].x = 0
|
||||
self.views.posList.dual[1].y = (self.views.baseheight/4)
|
||||
|
||||
self.views.posList.dual[2] = {}
|
||||
self.views.posList.dual[2].x = 0
|
||||
self.views.posList.dual[2].y = -(self.views.baseheight/4)
|
||||
else
|
||||
self.views.posList.dual[1] = {}
|
||||
self.views.posList.dual[1].x = -(self.views.basewidth/4)
|
||||
self.views.posList.dual[1].y = 0
|
||||
|
||||
self.views.posList.dual[2] = {}
|
||||
self.views.posList.dual[2].x = (self.views.basewidth/4)
|
||||
self.views.posList.dual[2].y = 0
|
||||
end
|
||||
|
||||
self.views.posList.multi[1] = {}
|
||||
self.views.posList.multi[1].x = -(self.views.basewidth /4)
|
||||
self.views.posList.multi[1].y = (self.views.baseheight/4)
|
||||
|
||||
self.views.posList.multi[2] = {}
|
||||
self.views.posList.multi[2].x = (self.views.basewidth /4)
|
||||
self.views.posList.multi[2].y = (self.views.baseheight/4)
|
||||
|
||||
self.views.posList.multi[3] = {}
|
||||
self.views.posList.multi[3].x = -(self.views.basewidth /4)
|
||||
self.views.posList.multi[3].y = -(self.views.baseheight/4)
|
||||
|
||||
self.views.posList.multi[4] = {}
|
||||
self.views.posList.multi[4].x = (self.views.basewidth /4)
|
||||
self.views.posList.multi[4].y = -(self.views.baseheight/4)
|
||||
end
|
||||
|
||||
-- INFO FUNCTIONS
|
||||
-- Get informations from the camera system
|
||||
|
||||
function CameraSystem:getViewNumber()
|
||||
return #self.views.list
|
||||
end
|
||||
|
||||
function CameraSystem:haveView()
|
||||
return (self:getViewNumber() == 0)
|
||||
end
|
||||
|
||||
function CameraSystem:getViewsDimensions(viewNumber)
|
||||
local basewidth, baseheight = self.views.basewidth, self.views.baseheight
|
||||
local viewnumber = viewNumber or self:getViewNumber()
|
||||
|
||||
if (viewnumber <= 1) then
|
||||
return basewidth, baseheight
|
||||
elseif (viewnumber == 2) then
|
||||
if (self.verticalSplit) then
|
||||
return (basewidth), (baseheight/2)
|
||||
else
|
||||
return (basewidth/2), (baseheight)
|
||||
end
|
||||
else
|
||||
return (basewidth/2), (baseheight/2)
|
||||
end
|
||||
end
|
||||
|
||||
function CameraSystem:recalculateViewsPositions()
|
||||
if #self.views.list == 1 then
|
||||
self.views.list[1].pos.onScreen.x = 0
|
||||
self.views.list[1].pos.onScreen.y = 0
|
||||
else
|
||||
for i,v in ipairs(self.views.list) do
|
||||
local x, y = self:getViewPositions(i)
|
||||
self.views.list[i].pos.onScreen.x = x
|
||||
self.views.list[i].pos.onScreen.y = y
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function CameraSystem:getViewPositions(id)
|
||||
local viewNumber = #self.views.list
|
||||
|
||||
if (viewNumber == 2) and ((id == 1) or (id == 2)) then
|
||||
return self.views.posList.dual[id].x, self.views.posList.dual[id].y
|
||||
elseif (viewNumber > 2) then
|
||||
return self.views.posList.multi[id].x, self.views.posList.multi[id].y
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- WRAPPER and UTILS
|
||||
-- Access data from the views
|
||||
|
||||
function CameraSystem:addView(x, y, target)
|
||||
if (#self.views.list < SCREEN_LIMIT) then
|
||||
local id = #self.views.list + 1
|
||||
local view = {}
|
||||
|
||||
view.pos = {}
|
||||
view.pos.x = x or 0
|
||||
view.pos.y = y or 0
|
||||
view.pos.onScreen = {}
|
||||
|
||||
view.cam = View(view.pos.x, view.pos.y, 1, 0, true)
|
||||
-- TODO: add a target system in order to make a camera able
|
||||
-- to target a specific object
|
||||
view.target = target
|
||||
|
||||
table.insert(self.views.list, view)
|
||||
self.views.width, self.views.height = self:getViewsDimensions()
|
||||
self:recalculateViewsPositions()
|
||||
end
|
||||
end
|
||||
|
||||
function CameraSystem:getView(id)
|
||||
return self.views.list[id]
|
||||
end
|
||||
|
||||
function CameraSystem:getViewCam(id)
|
||||
local view = self:getView(id)
|
||||
|
||||
return view.cam
|
||||
end
|
||||
|
||||
function CameraSystem:attachView(id)
|
||||
if (id ~= nil) then
|
||||
local cam = self:getViewCam(id)
|
||||
|
||||
local viewx, viewy, vieww, viewh = self:getOnScreenViewCoordinate(id)
|
||||
cam:attach()
|
||||
love.graphics.setScissor(viewx, viewy, vieww, viewh)
|
||||
end
|
||||
end
|
||||
|
||||
function CameraSystem:detachView(id)
|
||||
if (id ~= nil) then
|
||||
local cam = self:getViewCam(id)
|
||||
|
||||
love.graphics.setScissor( )
|
||||
cam:detach()
|
||||
end
|
||||
end
|
||||
|
||||
function CameraSystem:getViewCoordinate(id)
|
||||
local cam = self:getViewCam(id)
|
||||
|
||||
local camx, camy, camw, camh
|
||||
camx = cam.x - (self.views.width/2)
|
||||
camy = cam.y - (self.views.height/2)
|
||||
|
||||
camw = self.views.width
|
||||
camh = self.views.height
|
||||
return camx, camy, camw, camh
|
||||
end
|
||||
|
||||
function CameraSystem:getOnScreenViewCoordinate(id)
|
||||
local view = self:getView(id)
|
||||
|
||||
local viewx, viewy, vieww, viewh
|
||||
local basex, basey = (self.views.basewidth / 2), (self.views.baseheight / 2)
|
||||
viewx = (basex) + view.pos.onScreen.x - (self.views.width / 2)
|
||||
viewy = (basey) + view.pos.onScreen.y - (self.views.height / 2)
|
||||
|
||||
vieww = self.views.width
|
||||
viewh = self.views.height
|
||||
return viewx, viewy, vieww, viewh
|
||||
end
|
||||
|
||||
function CameraSystem:getOnScreenViewRelativePosition(id)
|
||||
local view = self:getView(id)
|
||||
|
||||
local viewx, viewy
|
||||
local basex, basey = (self.views.basewidth / 2), (self.views.baseheight / 2)
|
||||
viewx = view.pos.onScreen.x
|
||||
viewy = view.pos.onScreen.y
|
||||
|
||||
return viewx, viewy
|
||||
end
|
||||
|
||||
function CameraSystem:getOnScreenViewCenter(id)
|
||||
local view = self:getView(id)
|
||||
|
||||
local viewx, viewy
|
||||
local basex, basey = (self.views.basewidth / 2), (self.views.baseheight / 2)
|
||||
viewx = (basex) + view.pos.onScreen.x
|
||||
viewy = (basey) + view.pos.onScreen.y
|
||||
|
||||
return viewx, viewy
|
||||
end
|
||||
|
||||
function CameraSystem:getViewScale(id)
|
||||
local cam = self:getViewCam(id)
|
||||
|
||||
return cam.scale
|
||||
end
|
||||
|
||||
function CameraSystem:limitView(id)
|
||||
local viewx, viewy, vieww, viewh = self:getViewCoordinate(id)
|
||||
local worldw, worldh = self.world:getDimensions()
|
||||
local posx = self.views.list[id].pos.x
|
||||
local posy = self.views.list[id].pos.y
|
||||
local minx = self.views.width / 2
|
||||
local miny = self.views.height / 2
|
||||
local maxx = worldw - minx
|
||||
local maxy = worldh - miny
|
||||
|
||||
self.views.list[id].pos.x = utils.math.between(posx, minx, maxx)
|
||||
self.views.list[id].pos.y = utils.math.between(posy, miny, maxy)
|
||||
|
||||
self:computeCamPosition(id)
|
||||
end
|
||||
|
||||
-- UPDATE and MOVE functions
|
||||
-- Move and update the camera system
|
||||
|
||||
function CameraSystem:update(dt)
|
||||
for i,v in ipairs(self.views.list) do
|
||||
self:followActor(i)
|
||||
end
|
||||
end
|
||||
|
||||
function CameraSystem:moveView(id, x, y)
|
||||
self.views.list[id].pos.x = x
|
||||
self.views.list[id].pos.y = y
|
||||
|
||||
self:computeCamPosition(id)
|
||||
self:limitView(id)
|
||||
end
|
||||
|
||||
function CameraSystem:computeCamPosition(id)
|
||||
local decalx = self.views.list[id].pos.onScreen.x
|
||||
local decaly = self.views.list[id].pos.onScreen.y
|
||||
|
||||
local realx = self.views.list[id].pos.x
|
||||
local realy = self.views.list[id].pos.y
|
||||
|
||||
self.views.list[id].cam.x = realx - decalx
|
||||
self.views.list[id].cam.y = realy + decaly
|
||||
-- FIXME: this workaround certainly will cause some problem but that's the only
|
||||
-- solution we seem to have right now
|
||||
-- We invert the y decalage for the camera in order to work around a problem
|
||||
-- that invert the y between it and the clipping.
|
||||
end
|
||||
|
||||
function CameraSystem:followActor(id)
|
||||
local view = self:getView(id)
|
||||
|
||||
if view.target ~= nil then
|
||||
local x, y = view.target:getViewCenter()
|
||||
x = math.floor(x)
|
||||
y = math.floor(y)
|
||||
self:moveView(id, x, y)
|
||||
end
|
||||
end
|
||||
|
||||
-- DRAW FUNCTIONS
|
||||
-- Basic callback to draw stuff
|
||||
|
||||
function CameraSystem:drawDebugViewBox(id)
|
||||
local viewx, viewy, vieww, viewh = self:getOnScreenViewCoordinate(id)
|
||||
utils.graphics.box(viewx, viewy, vieww, viewh)
|
||||
|
||||
local xx, yy = self:getOnScreenViewCenter(id)
|
||||
love.graphics.line(xx-3, yy, xx+3, yy)
|
||||
love.graphics.line(xx, yy-3, xx, yy+3)
|
||||
local string = id .. " x:" .. viewx .. " y:" .. viewy
|
||||
love.graphics.print(string, viewx + 4, viewy + 4)
|
||||
print(viewy)
|
||||
end
|
||||
|
||||
function CameraSystem:drawHUD(id)
|
||||
local viewx, viewy, vieww, viewh = self:getOnScreenViewCoordinate(id)
|
||||
local view = self:getView(id)
|
||||
local string2 = id .. " (" .. viewx .. ":" .. (viewh-viewy) .. ") "
|
||||
|
||||
|
||||
love.graphics.setScissor(viewx, viewy, vieww, viewh)
|
||||
love.graphics.translate(viewx, viewy)
|
||||
view.target:drawHUD(id, vieww, viewh)
|
||||
|
||||
love.graphics.translate(-viewx, -(viewh-viewy))
|
||||
love.graphics.setScissor( )
|
||||
end
|
||||
|
||||
return CameraSystem
|
|
@ -0,0 +1,373 @@
|
|||
-- camera.lua :: a basic camera adapted to the asset/world system.
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local cwd = (...):gsub('%.init$', '') .. "."
|
||||
|
||||
local CameraSystem = Object:extend()
|
||||
--local View = require(cwd .. "libs.hump.camera")
|
||||
|
||||
local camutils = require(cwd .. "utils")
|
||||
|
||||
local SPLITSCREEN_ISVERTICAL = false
|
||||
local SCREEN_LIMIT = 4
|
||||
|
||||
-- INIT FUNCTIONS
|
||||
-- Initialize the camera system
|
||||
|
||||
function CameraSystem:new(world)
|
||||
self.scene = world.scene
|
||||
self.world = world
|
||||
|
||||
self.mode = "split"
|
||||
self.verticalSplit = SPLITSCREEN_ISVERTICAL
|
||||
self.targets = {}
|
||||
|
||||
self:initViews()
|
||||
end
|
||||
|
||||
function CameraSystem:setMode(mode)
|
||||
self.mode = mode
|
||||
core.debug:print("camera", "mode is now set to " .. mode)
|
||||
return mode
|
||||
end
|
||||
|
||||
function CameraSystem:initViews()
|
||||
self.views = {}
|
||||
|
||||
self.views.list = {}
|
||||
self.views.basewidth, self.views.baseheight = core.screen:getDimensions()
|
||||
self.views.width, self.views.height = self:getViewsDimensions()
|
||||
|
||||
self.views.posList = camutils.getViewsPositions(self.views.basewidth, self.views.baseheight, self.verticalSplit)
|
||||
end
|
||||
|
||||
-- INFO FUNCTIONS
|
||||
-- Get informations from the camera system
|
||||
|
||||
function CameraSystem:getViewNumber()
|
||||
return #self.views.list
|
||||
end
|
||||
|
||||
function CameraSystem:haveView()
|
||||
return (self:getViewNumber() == 0)
|
||||
end
|
||||
|
||||
function CameraSystem:getViewsDimensions()
|
||||
local basewidth, baseheight = self.views.basewidth, self.views.baseheight
|
||||
local viewnumber = self:getViewNumber()
|
||||
|
||||
return camutils.getViewsDimensions(viewnumber, basewidth, baseheight, self.verticalSplit)
|
||||
end
|
||||
|
||||
function CameraSystem:recalculateViewsPositions()
|
||||
if #self.views.list == 1 then
|
||||
self.views.list[1].onScreen.x = 0
|
||||
self.views.list[1].onScreen.y = 0
|
||||
else
|
||||
for i,v in ipairs(self.views.list) do
|
||||
local x, y = self:getViewPositions(i)
|
||||
self.views.list[i].onScreen.x = x
|
||||
self.views.list[i].onScreen.y = y
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function CameraSystem:getViewPositions(id)
|
||||
local viewNumber = #self.views.list
|
||||
|
||||
if (viewNumber == 2) and ((id == 1) or (id == 2)) then
|
||||
return self.views.posList.dual[id].x, self.views.posList.dual[id].y
|
||||
elseif (viewNumber > 2) then
|
||||
return self.views.posList.multi[id].x, self.views.posList.multi[id].y
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- WRAPPER and UTILS
|
||||
-- Access data from the views
|
||||
|
||||
function CameraSystem:addTarget(target)
|
||||
if (#self.views < SCREEN_LIMIT) then
|
||||
if (#self.views.list == 0) or (self.mode == "split") then
|
||||
local x, y = target:getViewCenter()
|
||||
self:addView(x, y, target)
|
||||
table.insert(self.targets, target)
|
||||
elseif (self.mode == "middle") or (self.mode == "zoom") then
|
||||
table.insert(self.targets, target)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function CameraSystem:addView(x, y, target)
|
||||
if (#self.views.list < SCREEN_LIMIT) then
|
||||
local id = #self.views.list + 1
|
||||
local view = {}
|
||||
|
||||
view.pos = {}
|
||||
view.x = x or 0
|
||||
view.y = y or 0
|
||||
view.scale = 1
|
||||
|
||||
view.onScreen = {}
|
||||
|
||||
-- TODO: add a target system in order to make a camera able
|
||||
-- to target a specific object
|
||||
view.target = target
|
||||
|
||||
table.insert(self.views.list, view)
|
||||
self.views.width, self.views.height = self:getViewsDimensions()
|
||||
self:regenerateCanvases()
|
||||
self:recalculateViewsPositions()
|
||||
end
|
||||
end
|
||||
|
||||
function CameraSystem:regenerateCanvases()
|
||||
for i, view in ipairs(self.views.list) do
|
||||
view.canvas = love.graphics.newCanvas(self.views.width, self.views.height)
|
||||
end
|
||||
end
|
||||
|
||||
function CameraSystem:getView(id)
|
||||
return self.views.list[id]
|
||||
end
|
||||
|
||||
function CameraSystem:attachView(id)
|
||||
if (id ~= nil) then
|
||||
local view = self:getView(id)
|
||||
|
||||
self.current_canvas = love.graphics.getCanvas()
|
||||
love.graphics.setCanvas(view.canvas)
|
||||
love.graphics.clear()
|
||||
|
||||
if id ~= nil then
|
||||
-- Du à la manière dont fonctionne STI, on est obligé de récupérer les info
|
||||
-- de position de camera pour afficher la carte par rapport à ces infos
|
||||
tx, ty = self:getViewCoordinate(id)
|
||||
scale = self:getViewScale(id) or 1
|
||||
tx = math.floor(tx) * -1
|
||||
ty = math.floor(ty) * -1
|
||||
|
||||
local w, h = core.screen:getDimensions()
|
||||
end
|
||||
|
||||
love.graphics.push()
|
||||
love.graphics.origin()
|
||||
love.graphics.translate(math.floor(tx), math.floor(ty))
|
||||
end
|
||||
end
|
||||
|
||||
function CameraSystem:detachView(id)
|
||||
if (id ~= nil) then
|
||||
local view = self:getView(id)
|
||||
love.graphics.pop()
|
||||
|
||||
love.graphics.setCanvas(self.current_canvas)
|
||||
local tx, ty = self:getOnScreenViewCoordinate(id)
|
||||
local scale = core.screen:getScale() * view.scale
|
||||
|
||||
love.graphics.push()
|
||||
love.graphics.origin()
|
||||
love.graphics.translate(math.floor(tx), math.floor(ty))
|
||||
love.graphics.scale(scale, scale)
|
||||
|
||||
love.graphics.draw(view.canvas)
|
||||
|
||||
local unscale = 1 / view.scale
|
||||
love.graphics.scale(unscale, unscale)
|
||||
self:drawHUD(id)
|
||||
|
||||
love.graphics.pop()
|
||||
end
|
||||
end
|
||||
|
||||
function CameraSystem:getViewCoordinate(id)
|
||||
local view = self:getView(id)
|
||||
|
||||
local viewx, viewy, vieww, viewh
|
||||
|
||||
vieww = self.views.width / view.scale
|
||||
viewh = self.views.height / view.scale
|
||||
|
||||
viewx = view.x - (vieww / 2)
|
||||
viewy = view.y - (viewh / 2)
|
||||
|
||||
return viewx, viewy, vieww, viewh
|
||||
end
|
||||
|
||||
function CameraSystem:getOnScreenViewCoordinate(id)
|
||||
local view = self:getView(id)
|
||||
|
||||
local viewx, viewy, vieww, viewh
|
||||
local basex, basey = (self.views.basewidth / 2), (self.views.baseheight / 2)
|
||||
viewx = (basex) + view.onScreen.x - (self.views.width / 2)
|
||||
viewy = (basey) + view.onScreen.y - (self.views.height / 2)
|
||||
|
||||
viewx, viewy = core.screen:getScreenCoordinate(viewx, viewy)
|
||||
|
||||
vieww = self.views.width * core.screen:getScale()
|
||||
viewh = self.views.height * core.screen:getScale()
|
||||
return viewx, viewy, vieww, viewh
|
||||
end
|
||||
|
||||
function CameraSystem:getOnScreenViewRelativePosition(id)
|
||||
local view = self:getView(id)
|
||||
|
||||
local viewx, viewy
|
||||
local basex, basey = (self.views.basewidth / 2), (self.views.baseheight / 2)
|
||||
viewx = view.onScreen.x
|
||||
viewy = view.onScreen.y
|
||||
|
||||
return core.screen:getScreenCoordinate(viewx, viewy)
|
||||
end
|
||||
|
||||
function CameraSystem:getOnScreenViewCenter(id)
|
||||
local view = self:getView(id)
|
||||
|
||||
local viewx, viewy
|
||||
local basex, basey = (self.views.basewidth / 2), (self.views.baseheight / 2)
|
||||
viewx = (basex) + view.onScreen.x
|
||||
viewy = (basey) + view.onScreen.y
|
||||
|
||||
return core.screen:getScreenCoordinate(viewx, viewy)
|
||||
end
|
||||
|
||||
function CameraSystem:setViewScale(id, scale)
|
||||
local view = self:getView(id)
|
||||
|
||||
view.scale = scale
|
||||
|
||||
local _, _, w, h = self:getViewCoordinate(id)
|
||||
|
||||
view.canvas = love.graphics.newCanvas(math.ceil(w), math.ceil(h))
|
||||
end
|
||||
|
||||
function CameraSystem:getViewScale(id)
|
||||
local view = self:getView(id)
|
||||
|
||||
return view.scale
|
||||
end
|
||||
|
||||
function CameraSystem:limitView(id)
|
||||
local viewx, viewy, vieww, viewh = self:getViewCoordinate(id)
|
||||
local worldw, worldh = self.world:getDimensions()
|
||||
local posx = self.views.list[id].x
|
||||
local posy = self.views.list[id].y
|
||||
local minx = self.views.width / 2
|
||||
local miny = self.views.height / 2
|
||||
local maxx = worldw - minx
|
||||
local maxy = worldh - miny
|
||||
|
||||
self.views.list[id].x = utils.math.between(posx, minx, maxx)
|
||||
self.views.list[id].y = utils.math.between(posy, miny, maxy)
|
||||
end
|
||||
|
||||
-- UPDATE and MOVE functions
|
||||
-- Move and update the camera system
|
||||
|
||||
function CameraSystem:update(dt)
|
||||
for i,v in ipairs(self.views.list) do
|
||||
if (self.mode == "middle") or (self.mode == "zoom") then
|
||||
self:followAllActors(i)
|
||||
else
|
||||
self:followActor(i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function CameraSystem:moveView(id, x, y)
|
||||
self.views.list[id].x = x
|
||||
self.views.list[id].y = y
|
||||
|
||||
self:limitView(id)
|
||||
end
|
||||
|
||||
function CameraSystem:followActor(id)
|
||||
local view = self:getView(id)
|
||||
|
||||
if view.target ~= nil then
|
||||
local x, y = view.target:getViewCenter()
|
||||
x = math.floor(x)
|
||||
y = math.floor(y)
|
||||
self:moveView(id, x, y)
|
||||
end
|
||||
end
|
||||
|
||||
function CameraSystem:followAllActors(id)
|
||||
local view = self:getView(id)
|
||||
local PADDING = 16
|
||||
-- TODO: make all the padding and stuff part of object definition instead ?
|
||||
-- It would especially allow better work in future fake3D worlds
|
||||
|
||||
if (#self.targets > 0) then
|
||||
local minX, minY, maxX, maxY
|
||||
for i, target in ipairs(self.targets) do
|
||||
local x, y, w, h = target:getShape()
|
||||
local x2, y2 = x + w, y + h
|
||||
-- If start by initializing the value at the first found value
|
||||
if (minX == nil) then minX = x end
|
||||
if (maxX == nil) then maxX = x2 end
|
||||
if (minY == nil) then minY = y end
|
||||
if (maxY == nil) then maxY = y2 end
|
||||
|
||||
|
||||
minX = math.min(minX, x)
|
||||
maxX = math.max(maxX, x2)
|
||||
minY = math.min(minY, y)
|
||||
maxY = math.max(maxY, y2)
|
||||
end
|
||||
|
||||
-- Add padding
|
||||
minX = minX - PADDING
|
||||
minY = minY - PADDING
|
||||
maxX = maxX + PADDING
|
||||
maxY = maxY + PADDING
|
||||
local x, y
|
||||
x = (minX + maxX) / 2
|
||||
y = (minY + maxY) / 2
|
||||
|
||||
local scale = 1
|
||||
if (self.mode == "zoom") then
|
||||
local ww, hh = core.screen:getDimensions()
|
||||
local scalex = (maxX-minX)/ww
|
||||
local scaley = (maxY-minY)/hh
|
||||
scale = math.max(scale, scalex, scaley)
|
||||
self:setViewScale(id, 1/scale)
|
||||
self.world:resizeMap(ww * 3, hh * 3)
|
||||
end
|
||||
|
||||
self:moveView(id, x, y)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- DRAW FUNCTIONS
|
||||
-- Basic callback to draw stuff
|
||||
|
||||
function CameraSystem:drawHUD(id)
|
||||
local view = self:getView(id)
|
||||
local viewx, viewy, vieww, viewh = self:getOnScreenViewCoordinate(id)
|
||||
|
||||
view.target:drawHUD(id, vieww, viewh)
|
||||
end
|
||||
|
||||
return CameraSystem
|
|
@ -0,0 +1,83 @@
|
|||
-- camutils.lua :: some camera utilities
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local camutils = {}
|
||||
|
||||
function camutils.getViewsPositions(basewidth, baseheight, verticalSplit)
|
||||
local posList = {}
|
||||
|
||||
posList.dual = {}
|
||||
posList.multi = {}
|
||||
|
||||
if (verticalSplit) then
|
||||
posList.dual[1] = {}
|
||||
posList.dual[1].x = 0
|
||||
posList.dual[1].y = (baseheight/4)
|
||||
|
||||
posList.dual[2] = {}
|
||||
posList.dual[2].x = 0
|
||||
posList.dual[2].y = -(baseheight/4)
|
||||
else
|
||||
posList.dual[1] = {}
|
||||
posList.dual[1].x = -(basewidth/4)
|
||||
posList.dual[1].y = 0
|
||||
|
||||
posList.dual[2] = {}
|
||||
posList.dual[2].x = (basewidth/4)
|
||||
posList.dual[2].y = 0
|
||||
end
|
||||
|
||||
posList.multi[1] = {}
|
||||
posList.multi[1].x = -(basewidth /4)
|
||||
posList.multi[1].y = -(baseheight/4)
|
||||
|
||||
posList.multi[2] = {}
|
||||
posList.multi[2].x = (basewidth /4)
|
||||
posList.multi[2].y = -(baseheight/4)
|
||||
|
||||
posList.multi[3] = {}
|
||||
posList.multi[3].x = -(basewidth /4)
|
||||
posList.multi[3].y = (baseheight/4)
|
||||
|
||||
posList.multi[4] = {}
|
||||
posList.multi[4].x = (basewidth /4)
|
||||
posList.multi[4].y = (baseheight/4)
|
||||
|
||||
return posList
|
||||
end
|
||||
|
||||
function camutils.getViewsDimensions(viewnumber, basewidth, baseheight, verticalSplit)
|
||||
if (viewnumber <= 1) then
|
||||
return basewidth, baseheight
|
||||
elseif (viewnumber == 2) then
|
||||
if (verticalSplit) then
|
||||
return (basewidth), (baseheight/2)
|
||||
else
|
||||
return (basewidth/2), (baseheight)
|
||||
end
|
||||
else
|
||||
return (basewidth/2), (baseheight/2)
|
||||
end
|
||||
end
|
||||
|
||||
return camutils
|
|
@ -1,216 +0,0 @@
|
|||
--[[
|
||||
Copyright (c) 2010-2015 Matthias Richter
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
Except as contained in this notice, the name(s) of the above copyright holders
|
||||
shall not be used in advertising or otherwise to promote the sale, use or
|
||||
other dealings in this Software without prior written authorization.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
]]--
|
||||
|
||||
local _PATH = (...):match('^(.*[%./])[^%.%/]+$') or ''
|
||||
local cos, sin = math.cos, math.sin
|
||||
|
||||
local camera = {}
|
||||
camera.__index = camera
|
||||
|
||||
-- Movement interpolators (for camera locking/windowing)
|
||||
camera.smooth = {}
|
||||
|
||||
function camera.smooth.none()
|
||||
return function(dx,dy) return dx,dy end
|
||||
end
|
||||
|
||||
function camera.smooth.linear(speed)
|
||||
assert(type(speed) == "number", "Invalid parameter: speed = "..tostring(speed))
|
||||
return function(dx,dy, s)
|
||||
-- normalize direction
|
||||
local d = math.sqrt(dx*dx+dy*dy)
|
||||
local dts = math.min((s or speed) * love.timer.getDelta(), d) -- prevent overshooting the goal
|
||||
if d > 0 then
|
||||
dx,dy = dx/d, dy/d
|
||||
end
|
||||
|
||||
return dx*dts, dy*dts
|
||||
end
|
||||
end
|
||||
|
||||
function camera.smooth.damped(stiffness)
|
||||
assert(type(stiffness) == "number", "Invalid parameter: stiffness = "..tostring(stiffness))
|
||||
return function(dx,dy, s)
|
||||
local dts = love.timer.getDelta() * (s or stiffness)
|
||||
return dx*dts, dy*dts
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function new(x,y, zoom, rot, smoother)
|
||||
x,y = x or love.graphics.getWidth()/2, y or love.graphics.getHeight()/2
|
||||
zoom = zoom or 1
|
||||
rot = rot or 0
|
||||
smoother = smoother or camera.smooth.none() -- for locking, see below
|
||||
return setmetatable({x = x, y = y, scale = zoom, rot = rot, smoother = smoother}, camera)
|
||||
end
|
||||
|
||||
function camera:lookAt(x,y)
|
||||
self.x, self.y = x, y
|
||||
return self
|
||||
end
|
||||
|
||||
function camera:move(dx,dy)
|
||||
self.x, self.y = self.x + dx, self.y + dy
|
||||
return self
|
||||
end
|
||||
|
||||
function camera:position()
|
||||
return self.x, self.y
|
||||
end
|
||||
|
||||
function camera:rotate(phi)
|
||||
self.rot = self.rot + phi
|
||||
return self
|
||||
end
|
||||
|
||||
function camera:rotateTo(phi)
|
||||
self.rot = phi
|
||||
return self
|
||||
end
|
||||
|
||||
function camera:zoom(mul)
|
||||
self.scale = self.scale * mul
|
||||
return self
|
||||
end
|
||||
|
||||
function camera:zoomTo(zoom)
|
||||
self.scale = zoom
|
||||
return self
|
||||
end
|
||||
|
||||
function camera:attach(x,y,w,h, noclip)
|
||||
x,y = x or 0, y or 0
|
||||
w,h = w or love.graphics.getWidth(), h or love.graphics.getHeight()
|
||||
|
||||
self._sx,self._sy,self._sw,self._sh = love.graphics.getScissor()
|
||||
if not noclip then
|
||||
love.graphics.setScissor(x,y,w,h)
|
||||
end
|
||||
|
||||
local cx,cy = x+w/2, y+h/2
|
||||
love.graphics.push()
|
||||
love.graphics.translate(cx, cy)
|
||||
love.graphics.scale(self.scale)
|
||||
love.graphics.rotate(self.rot)
|
||||
love.graphics.translate(-self.x, -self.y)
|
||||
end
|
||||
|
||||
function camera:detach()
|
||||
love.graphics.pop()
|
||||
love.graphics.setScissor(self._sx,self._sy,self._sw,self._sh)
|
||||
end
|
||||
|
||||
function camera:draw(...)
|
||||
local x,y,w,h,noclip,func
|
||||
local nargs = select("#", ...)
|
||||
if nargs == 1 then
|
||||
func = ...
|
||||
elseif nargs == 5 then
|
||||
x,y,w,h,func = ...
|
||||
elseif nargs == 6 then
|
||||
x,y,w,h,noclip,func = ...
|
||||
else
|
||||
error("Invalid arguments to camera:draw()")
|
||||
end
|
||||
|
||||
self:attach(x,y,w,h,noclip)
|
||||
func()
|
||||
self:detach()
|
||||
end
|
||||
|
||||
-- world coordinates to camera coordinates
|
||||
function camera:cameraCoords(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()
|
||||
|
||||
-- x,y = ((x,y) - (self.x, self.y)):rotated(self.rot) * self.scale + center
|
||||
local c,s = cos(self.rot), sin(self.rot)
|
||||
x,y = x - self.x, y - self.y
|
||||
x,y = c*x - s*y, s*x + c*y
|
||||
return x*self.scale + w/2 + ox, y*self.scale + h/2 + oy
|
||||
end
|
||||
|
||||
-- camera coordinates to world coordinates
|
||||
function camera:worldCoords(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()
|
||||
|
||||
-- x,y = (((x,y) - center) / self.scale):rotated(-self.rot) + (self.x,self.y)
|
||||
local c,s = cos(-self.rot), sin(-self.rot)
|
||||
x,y = (x - w/2 - ox) / self.scale, (y - h/2 - oy) / self.scale
|
||||
x,y = c*x - s*y, s*x + c*y
|
||||
return x+self.x, y+self.y
|
||||
end
|
||||
|
||||
function camera:mousePosition(ox,oy,w,h)
|
||||
local mx,my = love.mouse.getPosition()
|
||||
return self:worldCoords(mx,my, ox,oy,w,h)
|
||||
end
|
||||
|
||||
-- camera scrolling utilities
|
||||
function camera:lockX(x, smoother, ...)
|
||||
local dx, dy = (smoother or self.smoother)(x - self.x, self.y, ...)
|
||||
self.x = self.x + dx
|
||||
return self
|
||||
end
|
||||
|
||||
function camera:lockY(y, smoother, ...)
|
||||
local dx, dy = (smoother or self.smoother)(self.x, y - self.y, ...)
|
||||
self.y = self.y + dy
|
||||
return self
|
||||
end
|
||||
|
||||
function camera:lockPosition(x,y, smoother, ...)
|
||||
return self:move((smoother or self.smoother)(x - self.x, y - self.y, ...))
|
||||
end
|
||||
|
||||
function camera:lockWindow(x, y, x_min, x_max, y_min, y_max, smoother, ...)
|
||||
-- figure out displacement in camera coordinates
|
||||
x,y = self:cameraCoords(x,y)
|
||||
local dx, dy = 0,0
|
||||
if x < x_min then
|
||||
dx = x - x_min
|
||||
elseif x > x_max then
|
||||
dx = x - x_max
|
||||
end
|
||||
if y < y_min then
|
||||
dy = y - y_min
|
||||
elseif y > y_max then
|
||||
dy = y - y_max
|
||||
end
|
||||
|
||||
-- transform displacement to movement in world coordinates
|
||||
local c,s = cos(-self.rot), sin(-self.rot)
|
||||
dx,dy = (c*dx - s*dy) / self.scale, (s*dx + c*dy) / self.scale
|
||||
|
||||
-- move
|
||||
self:move((smoother or self.smoother)(dx,dy,...))
|
||||
end
|
||||
|
||||
-- the module
|
||||
return setmetatable({new = new, smooth = camera.smooth},
|
||||
{__call = function(_, ...) return new(...) end})
|
|
@ -0,0 +1,7 @@
|
|||
local cwd = (...):gsub('%.init$', '') .. "."
|
||||
|
||||
local mapObjects = {}
|
||||
mapObjects.Sti = require(cwd .. "sti")
|
||||
mapObjects.Base = require(cwd .. "parent")
|
||||
|
||||
return mapObjects
|
|
@ -0,0 +1,81 @@
|
|||
local ParentMap = Object:extend()
|
||||
|
||||
-- INIT FUNCTION
|
||||
-- Initialize the map
|
||||
|
||||
function ParentMap:new(world, r, g, b)
|
||||
self.world = world
|
||||
|
||||
local r = r or 128
|
||||
local g = g or 128
|
||||
local b = b or 128
|
||||
self.backgroundColor = {r, g, b}
|
||||
|
||||
self:register()
|
||||
end
|
||||
|
||||
function ParentMap:register()
|
||||
self.world.map = self
|
||||
end
|
||||
|
||||
-- UPDATE FUNCTION
|
||||
-- Update or modify the map
|
||||
|
||||
function ParentMap:resize(w, h)
|
||||
-- Empty Placeholder function
|
||||
end
|
||||
|
||||
function ParentMap:update(dt)
|
||||
-- Empty Placeholder function
|
||||
end
|
||||
|
||||
function ParentMap:loadObjects()
|
||||
self:loadCollisions()
|
||||
self:loadPlayers()
|
||||
self:loadActors()
|
||||
end
|
||||
|
||||
function ParentMap:loadCollisions()
|
||||
-- Empty Placeholder function
|
||||
end
|
||||
|
||||
function ParentMap:loadPlayers()
|
||||
-- Empty Placeholder function
|
||||
end
|
||||
|
||||
function ParentMap:loadActors()
|
||||
-- Empty Placeholder function
|
||||
end
|
||||
|
||||
function ParentMap:setBackgroundColor(r, g, b)
|
||||
local r = r or 128
|
||||
local g = g or 128
|
||||
local b = b or 128
|
||||
self.backgroundColor = {r, g, b}
|
||||
end
|
||||
|
||||
function ParentMap:setBackgroundColorFromTable(backgroundColor)
|
||||
self.backgroundColor = backgroundColor or {128, 128, 128}
|
||||
end
|
||||
|
||||
function ParentMap:getBackgroundColor()
|
||||
return self.backgroundColor[1]/256, self.backgroundColor[2]/256, self.backgroundColor[3]/256
|
||||
end
|
||||
|
||||
function ParentMap:getDimensions()
|
||||
return core.screen:getDimensions()
|
||||
end
|
||||
|
||||
function ParentMap:getBox()
|
||||
local w, h = self:getDimensions()
|
||||
return 0, 0, w, h
|
||||
end
|
||||
|
||||
function ParentMap:drawBackgroundColor()
|
||||
local r, g, b = self.backgroundColor[1], self.backgroundColor[2], self.backgroundColor[3]
|
||||
love.graphics.setColor(r/256, g/256, b/256)
|
||||
love.graphics.rectangle("fill", 0, 0, 480, 272)
|
||||
utils.graphics.resetColor()
|
||||
end
|
||||
|
||||
return ParentMap
|
|
@ -0,0 +1,150 @@
|
|||
local cwd = (...):gsub('%.sti$', '') .. "."
|
||||
|
||||
local Parent = require(cwd .. "parent")
|
||||
local STI = require(cwd .. "libs.sti")
|
||||
|
||||
local StiMap = Parent:extend()
|
||||
|
||||
function StiMap:new(world, mapfile)
|
||||
self.sti = STI(mapfile)
|
||||
StiMap.super.new(self, world)
|
||||
self:setBackgroundColorFromTable(self.sti.backgroundcolor)
|
||||
end
|
||||
|
||||
function StiMap:getDimensions()
|
||||
return self.sti.width * self.sti.tilewidth,
|
||||
self.sti.height * self.sti.tileheight
|
||||
end
|
||||
|
||||
-- UPDATE FUNCTION
|
||||
-- Update or modify the map
|
||||
|
||||
function StiMap:resize(w, h)
|
||||
self.sti:resize(w, h)
|
||||
end
|
||||
|
||||
function StiMap:update(dt)
|
||||
self.sti:update(dt)
|
||||
end
|
||||
|
||||
-- LOADING FUNCTION
|
||||
-- Load actors directly into the world
|
||||
|
||||
function StiMap:loadCollisions()
|
||||
for k, objectlayer in pairs(self.sti.layers) do
|
||||
if self.world:isCollisionIndexed(objectlayer.name) then
|
||||
local debugstring = "loading " .. #objectlayer.objects .. " objects in " .. objectlayer.name .. " collision layer"
|
||||
core.debug:print("map/sti", debugstring)
|
||||
for k, object in pairs(objectlayer.objects) do
|
||||
self:newCollision(objectlayer, object)
|
||||
end
|
||||
self.sti:removeLayer(objectlayer.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function StiMap:loadActors()
|
||||
for k, objectlayer in pairs(self.sti.layers) do
|
||||
if self.world:isActorIndexed(objectlayer.name) then
|
||||
local debugstring = "loading " .. #objectlayer.objects .. " objects in " .. objectlayer.name .. " actor layer"
|
||||
core.debug:print("map/sti", debugstring)
|
||||
for k, object in pairs(objectlayer.objects) do
|
||||
if (object.properties.batchActor) then
|
||||
self:batchActor(objectlayer, object)
|
||||
else
|
||||
self:newActor(objectlayer, object)
|
||||
end
|
||||
end
|
||||
self.sti:removeLayer(objectlayer.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function StiMap:loadPlayers()
|
||||
for k, objectlayer in pairs(self.sti.layers) do
|
||||
if (objectlayer.name == "player") then
|
||||
local debugstring = "loading at most " .. #objectlayer.objects .. " actors in " .. objectlayer.name .. " actor layer"
|
||||
core.debug:print("map/sti", debugstring)
|
||||
local i = 1
|
||||
for k, object in pairs(objectlayer.objects) do
|
||||
self:newPlayer(object, i)
|
||||
i = i + 1
|
||||
end
|
||||
self.sti:removeLayer(objectlayer.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function StiMap:batchActor(objectlayer, object)
|
||||
local name = objectlayer.name
|
||||
local gwidth = object.properties.gwidth or self.sti.tilewidth
|
||||
local gheight = object.properties.gheight or self.sti.tileheight
|
||||
local x = object.x
|
||||
local y = object.y
|
||||
local z = object.properties.z or 0
|
||||
local w = object.width
|
||||
local h = object.height
|
||||
|
||||
local cellHor = math.ceil(w / gwidth)
|
||||
local cellVert = math.ceil(h / gheight)
|
||||
|
||||
for i=1, cellHor do
|
||||
for j=1, cellVert do
|
||||
self.world:newActor(name, x + (i-1)*gwidth, y + (j-1)*gheight, z)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function StiMap:newActor(objectlayer, object)
|
||||
local z = object.properties.z or 0
|
||||
local adaptPosition = object.properties.adaptPosition or false
|
||||
|
||||
local y = object.y
|
||||
if (adaptPosition) then
|
||||
y = y + z
|
||||
end
|
||||
|
||||
self.world:newActor(objectlayer.name, object.x, y, z)
|
||||
end
|
||||
|
||||
function StiMap:newCollision(objectlayer, object)
|
||||
local z = object.properties.z or 0
|
||||
local d = object.properties.d or 16
|
||||
local fromTop = object.properties.fromTop or false
|
||||
|
||||
local y = object.y
|
||||
if (fromTop) then
|
||||
local poschange = z .. ";" .. y .. " => " .. z-d .. ";" .. y+z
|
||||
core.debug:print("map/sti", "create from top, set z and y: " .. poschange)
|
||||
y = y + z
|
||||
z = z - d
|
||||
end
|
||||
|
||||
self.world:newCollision(objectlayer.name, object.x, y, z, object.width, object.height, d)
|
||||
end
|
||||
|
||||
function StiMap:newPlayer(object, i)
|
||||
local z = object.properties.z or 0
|
||||
local adaptPosition = object.properties.adaptPosition or false
|
||||
|
||||
local y = object.y
|
||||
if (adaptPosition) then
|
||||
core.debug:print("map/sti", "adapting position, set y: " .. y .. " => ", y+z)
|
||||
y = y + z
|
||||
end
|
||||
|
||||
self.world:addPlayer(object.x, y, z, i)
|
||||
end
|
||||
|
||||
-- DRAW FUNCTIONS
|
||||
-- Draw the map
|
||||
|
||||
function StiMap:draw()
|
||||
for _, layer in ipairs(self.sti.layers) do
|
||||
if layer.visible and layer.opacity > 0 and (layer.type == "tilelayer") then
|
||||
self.sti:drawLayer(layer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return StiMap
|
|
@ -26,7 +26,6 @@ local cwd = (...):gsub('%.world2D$', '') .. "."
|
|||
local BaseWorld = require(cwd .. "baseworld")
|
||||
local World2D = BaseWorld:extend()
|
||||
|
||||
local Sti = require(cwd .. "libs.sti")
|
||||
local Bump = require(cwd .. "libs.bump")
|
||||
local CameraSystem = require(cwd .. "camera")
|
||||
|
||||
|
@ -35,41 +34,56 @@ function World2D:new(scene, actorlist, mapfile)
|
|||
end
|
||||
|
||||
-- ACTORS FUNCTIONS
|
||||
-- Wrappers around Bump2D functions
|
||||
-- Add support for bodies in Actor functions
|
||||
|
||||
function World2D:initActors()
|
||||
self.currentCreationID = 0
|
||||
self.actors = Bump.newWorld(50)
|
||||
self.actors = {}
|
||||
self.bodies = Bump.newWorld(50)
|
||||
end
|
||||
|
||||
function World2D:registerActor(actor)
|
||||
actor.creationID = self.currentCreationID
|
||||
self.currentCreationID = self.currentCreationID + 1
|
||||
return self.actors:add(actor, actor.x, actor.y, actor.w, actor.h)
|
||||
end
|
||||
|
||||
function World2D:removeActor(actor)
|
||||
return self.actors:remove(actor)
|
||||
World2D.super.registerActor(self, actor)
|
||||
end
|
||||
|
||||
function World2D:moveActor(actor, x, y, filter)
|
||||
return self.actors:move(actor, x, y, filter)
|
||||
return self.bodies:move(actor.mainHitbox, x, y, filter)
|
||||
end
|
||||
|
||||
function World2D:checkCollision(actor, x, y, filter)
|
||||
return self.actors:check(actor, x, y, filter)
|
||||
function World2D:getActorsInRect(x, y, w, h)
|
||||
local bodies = self.bodies:queryRect(x, y, w, h)
|
||||
local returnquery = {}
|
||||
|
||||
for i,body in ipairs(bodies) do
|
||||
if (body.isMainHitBox) then
|
||||
table.insert(returnquery, body.owner)
|
||||
end
|
||||
end
|
||||
|
||||
return returnquery
|
||||
end
|
||||
|
||||
function World2D:queryRect(x, y, w, h)
|
||||
return self.actors:queryRect(x, y, w, h)
|
||||
-- BODIES MANAGEMENT FUNCTIONS
|
||||
-- Basic function to handle bodies. Wrappers around Bump2D functions
|
||||
|
||||
function World2D:registerBody(body)
|
||||
return self.bodies:add(body, body.x, body.y, body.w, body.h)
|
||||
end
|
||||
|
||||
function World2D:countActors()
|
||||
return self.actors:countItems()
|
||||
function World2D:updateBody(body)
|
||||
return self.bodies:update(body, body.x, body.y, body.w, body.h)
|
||||
end
|
||||
|
||||
function World2D:getActors()
|
||||
return self.actors:getItems()
|
||||
function World2D:removeBody(body)
|
||||
return self.bodies:remove(body)
|
||||
end
|
||||
|
||||
function World2D:checkCollision(body, x, y, filter)
|
||||
return self.bodies:check(body, x, y, filter)
|
||||
end
|
||||
|
||||
function World2D:getBodiesInRect(x, y, w, h)
|
||||
return self.bodies:queryRect(x, y, w, h)
|
||||
end
|
||||
|
||||
return World2D
|
||||
|
|
|
@ -0,0 +1,268 @@
|
|||
-- world3D.lua :: a basic fake3D world based on bump-2dpd.
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local cwd = (...):gsub('%.world3D$', '') .. "."
|
||||
|
||||
local BaseWorld = require(cwd .. "baseworld")
|
||||
local World3D = BaseWorld:extend()
|
||||
|
||||
local Bump = require(cwd .. "libs.bump")
|
||||
local Bump3D = require(cwd .. "libs.bump-3dpd")
|
||||
local Tsort = require(cwd .. "libs.tsort")
|
||||
local CameraSystem = require(cwd .. "camera")
|
||||
|
||||
local PADDING_VALUE = 10/100
|
||||
|
||||
function World3D:new(scene, actorlist, mapfile)
|
||||
World3D.super.new(self, scene, actorlist, mapfile)
|
||||
end
|
||||
|
||||
-- ACTORS FUNCTIONS
|
||||
-- Add support for bodies in Actor functions
|
||||
|
||||
function World3D:initActors()
|
||||
self.currentCreationID = 0
|
||||
self.actors = {}
|
||||
self.bodies = Bump3D.newWorld(50)
|
||||
self:initShapes()
|
||||
self:initTerrain()
|
||||
end
|
||||
|
||||
function World3D:newActor(name, x, y, z)
|
||||
self.obj.index[name](self, x, y, z)
|
||||
end
|
||||
|
||||
function World3D:newCollision(name, x, y, z, w, h, d)
|
||||
self.obj.collisions[name](self, x, y, z, w, h, d)
|
||||
end
|
||||
|
||||
function World3D:moveActor(actor, x, y, z, filter)
|
||||
return self.bodies:move(actor.mainHitbox, x, y, z, filter)
|
||||
end
|
||||
|
||||
function World3D:getActorsInRect(x, y, w, h)
|
||||
return self:getShapeInRect(x, y, w, h)
|
||||
end
|
||||
|
||||
function World3D:getVisibleActors(id)
|
||||
local actors = {}
|
||||
if (id ~= nil) then
|
||||
local camx, camy, camw, camh = self.cameras:getViewCoordinate(id)
|
||||
local paddingw = camw * PADDING_VALUE
|
||||
local paddingh = camh * PADDING_VALUE
|
||||
local x = camx - paddingw
|
||||
local y = camy - paddingh
|
||||
local w = camw + paddingw * 2
|
||||
local h = camh + paddingh * 2
|
||||
|
||||
actors = self:getActorsInRect(x, y, w, h)
|
||||
else
|
||||
actors = self:getActors()
|
||||
end
|
||||
|
||||
actors = self:zSortItems(actors)
|
||||
|
||||
return actors
|
||||
end
|
||||
|
||||
|
||||
-- PLAYER FUNCTIONS
|
||||
-- Load player stuff
|
||||
|
||||
function World3D:newPlayer(x, y, z)
|
||||
return self.obj.Player(self, x, y, z)
|
||||
end
|
||||
|
||||
-- BODIES MANAGEMENT FUNCTIONS
|
||||
-- Basic function to handle bodies. Wrappers around Bump2D functions
|
||||
|
||||
function World3D:registerBody(body)
|
||||
return self.bodies:add(body, body.x, body.y, body.z, body.w, body.h, body.d)
|
||||
end
|
||||
|
||||
function World3D:updateBody(body)
|
||||
return self.bodies:update(body, body.x, body.y, body.z, body.w, body.h, body.d)
|
||||
end
|
||||
|
||||
function World3D:removeBody(body)
|
||||
return self.bodies:remove(body)
|
||||
end
|
||||
|
||||
function World3D:checkCollision(body, x, y, z, filter)
|
||||
return self.bodies:check(body, x, y, z, filter)
|
||||
end
|
||||
|
||||
function World3D:getBodiesInRect(x, y, w, h)
|
||||
return {} --self.bodies:queryRect(x, y, w, h)
|
||||
end
|
||||
|
||||
-- UPDATE
|
||||
-- Update everything in the world
|
||||
|
||||
function World3D:updateActors(dt)
|
||||
World3D.super.updateActors(self, dt)
|
||||
local actors = self:getActors()
|
||||
for i,v in ipairs(actors) do
|
||||
v:redrawShadowCanvas()
|
||||
end
|
||||
end
|
||||
|
||||
-- SHAPE SYSTEM
|
||||
-- Handle onscreen shapes
|
||||
|
||||
function World3D:initShapes()
|
||||
self.shapes = Bump.newWorld(50)
|
||||
end
|
||||
|
||||
function World3D:registerShape(actor)
|
||||
local x, y, w, h = actor:getShape()
|
||||
return self.shapes:add(actor, x, y, w, h)
|
||||
end
|
||||
|
||||
function World3D:updateShape(actor)
|
||||
local x, y, w, h = actor:getShape()
|
||||
return self.shapes:update(actor, x, y, w, h)
|
||||
end
|
||||
|
||||
function World3D:removeShape(actor)
|
||||
return self.shapes:remove(actor)
|
||||
end
|
||||
|
||||
function World3D:checkShapeIntersection(actor, x, y)
|
||||
return self.shapes:check(actor, x, y)
|
||||
end
|
||||
|
||||
function World3D:getShapeInRect(x, y, w, h)
|
||||
return self.shapes:queryRect(x, y, w, h)
|
||||
end
|
||||
|
||||
-- TERRAIN SYSTEM
|
||||
-- Handle onscreen shapes
|
||||
|
||||
function World3D:initTerrain()
|
||||
self.terrains = Bump.newWorld(50)
|
||||
end
|
||||
|
||||
function World3D:registerTerrain(actor)
|
||||
return self.terrains:add(actor, actor.x, actor.y, actor.w, actor.h)
|
||||
end
|
||||
|
||||
function World3D:updateTerrain(actor)
|
||||
return self.terrains:update(actor, actor.x, actor.y, actor.w, actor.h)
|
||||
end
|
||||
|
||||
function World3D:removeTerrain(actor)
|
||||
return self.terrains:remove(actor)
|
||||
end
|
||||
|
||||
function World3D:getTerrainInRect(x, y, w, h)
|
||||
return self.terrains:queryRect(x, y, w, h)
|
||||
end
|
||||
|
||||
-- DRAW FUNCTIONS
|
||||
-- Functions to draw the world
|
||||
|
||||
function World3D:zSortItems(items)
|
||||
-- zSorting algorithm taken from bump3D example, adapted to gamecore.
|
||||
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(items) do repeat
|
||||
local x, y, w, h = self.shapes:getRect(itemA)
|
||||
local otherItemsFilter = function(other) return other ~= itemA end
|
||||
local overlapping, len = self.shapes:queryRect(x, y, w, h, otherItemsFilter)
|
||||
|
||||
if len == 0 then
|
||||
table.insert(noOverlap, itemA)
|
||||
|
||||
break
|
||||
end
|
||||
|
||||
local _, aY, aZ, _, aH, aD = self.bodies:getCube(itemA.mainHitbox)
|
||||
aDepth = itemA.depth
|
||||
aID = itemA.id
|
||||
aType = itemA.type
|
||||
aZ = math.ceil(aZ)
|
||||
aY = math.ceil(aY)
|
||||
|
||||
for _, itemB in ipairs(overlapping) do
|
||||
local _, bY, bZ, _, bH, bD = self.bodies:getCube(itemB.mainHitbox)
|
||||
bDepth = itemB.depth
|
||||
bID = itemB.id
|
||||
bType = itemB.type
|
||||
bZ = math.ceil(bZ)
|
||||
bY = math.ceil(bY)
|
||||
|
||||
if aZ >= bZ + bD then
|
||||
-- item A is completely above item B
|
||||
graph:add(itemB, itemA)
|
||||
elseif bZ >= aZ + aD then
|
||||
-- item B is completely above item A
|
||||
graph:add(itemA, itemB)
|
||||
elseif aY + aH <= bY then
|
||||
-- item A is completely behind item B
|
||||
graph:add(itemA, itemB)
|
||||
elseif bY + bH <= aY then
|
||||
-- item B is completely behind item A
|
||||
graph:add(itemB, itemA)
|
||||
elseif (aY - aZ) + aH > (bY - bZ) + bH then
|
||||
-- item A's forward-most point is in front of item B's forward-most point
|
||||
graph:add(itemB, itemA)
|
||||
elseif (aY - aZ) + aH < (bY - bZ) + bH then
|
||||
-- item B's forward-most point is in front of item A's forward-most point
|
||||
graph:add(itemA, itemB)
|
||||
else
|
||||
-- item A's forward-most point is the same than item B'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
|
||||
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
|
||||
|
||||
return World3D
|
|
@ -25,7 +25,7 @@
|
|||
local OptionsManager = Object:extend()
|
||||
|
||||
local cwd = (...):gsub('%.options$', '') .. "."
|
||||
local binser = require(cwd .. "libs.binser")
|
||||
local binser = require(cwd .. "modules.gamesystem.libs.binser")
|
||||
|
||||
local TRANSLATION_PATH = "datas/languages/"
|
||||
|
||||
|
@ -33,10 +33,10 @@ local TRANSLATION_PATH = "datas/languages/"
|
|||
-- Initialize and configure the game options
|
||||
|
||||
function OptionsManager:new(controller)
|
||||
self.controller = controller
|
||||
-- We begin by creating an empty data table before reading the data.
|
||||
self.data = {}
|
||||
self:read()
|
||||
self.controller = controller
|
||||
end
|
||||
|
||||
function OptionsManager:reset()
|
||||
|
@ -45,7 +45,7 @@ function OptionsManager:reset()
|
|||
self.data.video.crtfilter = false
|
||||
self.data.video.resolution = 1
|
||||
self.data.video.border = true
|
||||
self.data.video.vsync = false
|
||||
self.data.video.vsync = true
|
||||
self.data.video.fullscreen = false
|
||||
|
||||
-- We load the default files
|
||||
|
@ -152,11 +152,11 @@ function OptionsManager:read()
|
|||
filepath = self:getFile(true)
|
||||
if utils.filesystem.exists("options.data") then
|
||||
local loadedDatas = binser.readFile(filepath)
|
||||
print("data file found, loading it")
|
||||
self.controller.debug:print("core/options", "data file found, loading it")
|
||||
self:setData(loadedDatas[1])
|
||||
else
|
||||
self:reset()
|
||||
print("no data file found, reseting data")
|
||||
self.controller.debug:print("core/options", "no data file found, reseting data")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -66,6 +66,14 @@ function ScreenManager:getMousePosition()
|
|||
return CScreen.project(love.mouse.getX(), love.mouse.getY())
|
||||
end
|
||||
|
||||
function ScreenManager:getScale()
|
||||
return CScreen.getScale()
|
||||
end
|
||||
|
||||
function ScreenManager:getScreenCoordinate(x, y)
|
||||
return CScreen.getScreenCoordinate(x, y)
|
||||
end
|
||||
|
||||
-- INFO FUNCTIONS
|
||||
-- Get screen informations
|
||||
|
||||
|
|
|
@ -27,5 +27,6 @@ local cwd = (...):gsub('%.init$', '') .. "."
|
|||
return {
|
||||
math = require(cwd .. "math"),
|
||||
graphics = require(cwd .. "graphics"),
|
||||
filesystem = require(cwd .. "filesystem")
|
||||
filesystem = require(cwd .. "filesystem"),
|
||||
table = require(cwd .. "table")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
-- loveutils.table : simple functions for table manipulation and computation.
|
||||
-- TODO: could be a part of loveutils.math ?
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local Table = {}
|
||||
|
||||
function Table.reduce(list, fn)
|
||||
local acc
|
||||
for k, v in ipairs(list) do
|
||||
if 1 == k then
|
||||
acc = v
|
||||
else
|
||||
acc = fn(acc, v)
|
||||
end
|
||||
end
|
||||
return acc
|
||||
end
|
||||
|
||||
function Table.sum(table)
|
||||
local sum = 0
|
||||
for _, v in pairs(table) do
|
||||
sum = sum + v
|
||||
end
|
||||
|
||||
return sum
|
||||
end
|
||||
|
||||
function Table.average(table)
|
||||
return Table.sum(table) / #table
|
||||
end
|
||||
|
||||
return Table
|
|
@ -23,88 +23,25 @@
|
|||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local Game = Object:extend()
|
||||
local GameSystem = require "core.modules.gamesystem"
|
||||
local Game = GameSystem:extend()
|
||||
|
||||
local PigManager = require "game.pigmanager"
|
||||
local Inventory = require "game.inventory"
|
||||
|
||||
local binser = require "core.libs.binser"
|
||||
|
||||
Game.gui = require "game.modules.gui"
|
||||
|
||||
function Game:new()
|
||||
self.slot = -1
|
||||
self.gametime = 0
|
||||
Game.super.new(self)
|
||||
|
||||
self.inventory = Inventory(self)
|
||||
self.pigmanager = PigManager(self)
|
||||
end
|
||||
|
||||
function Game:setData(data)
|
||||
local data = data
|
||||
self.gametime = data.gametime
|
||||
end
|
||||
|
||||
function Game:getData()
|
||||
local data = {}
|
||||
data.gametime = self.gametime
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
function Game:read(save_id)
|
||||
self.slot = save_id
|
||||
if (self.slot > 0) then
|
||||
filepath = self:getSaveFile(self.slot, true)
|
||||
if love.filesystem.exists("save" .. self.slot .. ".save") then
|
||||
local loadedDatas = binser.readFile(filepath)
|
||||
|
||||
self:setData(loadedDatas[1])
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Game:write(save_id)
|
||||
if (self.slot > 0) then
|
||||
local data = self:getData()
|
||||
|
||||
filepath = self:getSaveFile(self.slot, true)
|
||||
binser.writeFile(filepath, data)
|
||||
end
|
||||
end
|
||||
|
||||
function Game:getSaveFile(saveslot, absolute)
|
||||
local dir = ""
|
||||
if absolute then
|
||||
dir = love.filesystem.getSaveDirectory() .. "/"
|
||||
if not love.filesystem.exists(dir) then
|
||||
love.filesystem.createDirectory( "" )
|
||||
end
|
||||
end
|
||||
|
||||
local filepath = dir .. "save" .. saveslot .. ".save"
|
||||
|
||||
return filepath
|
||||
end
|
||||
|
||||
function Game:resetSaves()
|
||||
for i=1, 3 do
|
||||
filepath = self:getSaveFile(i, true)
|
||||
if love.filesystem.exists("save" .. i .. ".save") then
|
||||
love.filesystem.remove( "save" .. i .. ".save" )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Game:update(dt)
|
||||
self.gametime = self.gametime + dt
|
||||
end
|
||||
|
||||
function Game:getTime()
|
||||
local time = self.gametime
|
||||
local time = self.playtime
|
||||
local hours, minute, seconds
|
||||
seconds = math.floor(self.gametime)
|
||||
seconds = math.floor(self.playtime)
|
||||
minutes = math.floor(seconds / 60)
|
||||
hours = math.floor(minutes / 60)
|
||||
seconds = seconds % 60
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
local Inventory = Object:extend()
|
||||
local Submodule = require "core.modules.gamesystem.submodule"
|
||||
local Inventory = Submodule:extend()
|
||||
|
||||
function Inventory:new(controller)
|
||||
self.controller = controller
|
||||
Inventory.super.new(self, controller, "inventory")
|
||||
|
||||
self.data = {}
|
||||
self.data.weapons = {}
|
||||
|
@ -19,12 +20,4 @@ function Inventory:addItem(name)
|
|||
self.data.items[name] = self.data.items[name] + 1
|
||||
end
|
||||
|
||||
function Inventory:setData(data)
|
||||
self.data = data
|
||||
end
|
||||
|
||||
function Inventory:getData(data)
|
||||
return self.data
|
||||
end
|
||||
|
||||
return Inventory
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
local PigManager = Object:extend()
|
||||
local Submodule = require "core.modules.gamesystem.submodule"
|
||||
local PigManager = Submodule:extend()
|
||||
|
||||
function PigManager:new(controller)
|
||||
self.controller = controller
|
||||
PigManager.super.new(self, controller, "pigmanager")
|
||||
|
||||
self.data = {}
|
||||
end
|
||||
|
@ -39,12 +40,4 @@ function PigManager:getPig(pigID)
|
|||
return pigData
|
||||
end
|
||||
|
||||
function PigManager:getData()
|
||||
return self.data
|
||||
end
|
||||
|
||||
function PigManager:setData(data)
|
||||
self.data = data
|
||||
end
|
||||
|
||||
return PigManager
|
||||
|
|
|
@ -14,9 +14,9 @@ end
|
|||
|
||||
function Entity:setGravityFlag(flag)
|
||||
if (flag) then
|
||||
self:setYGravity(self.gacc)
|
||||
self:setGravity(self.gacc)
|
||||
else
|
||||
self:setYGravity()
|
||||
self:setGravity()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -50,11 +50,7 @@ function Entity:purge()
|
|||
self:remove()
|
||||
end
|
||||
|
||||
function Entity:checkGround(ny)
|
||||
if not (self.grav == 0) then
|
||||
if ny < 0 then self.onGround = true end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Entity:getDirection()
|
||||
if not (utils.math.sign(self.xsp) == 0) then
|
||||
|
|
Loading…
Reference in New Issue