diff --git a/CHANGELOG.md b/CHANGELOG.md index 751c4ab..84d802a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **world:** Add a way to automatically load hitbox from a file +- **utils:** Add basic table functions + +- **camera:** Add two new camera types: "middle" and "zoom". + ### Changed - **world2D:** Use a list for bodies (hitboxes, etc) and one other for actors @@ -35,6 +39,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **world:** Make object creation more customizable by worlds +- **camera:** Make mode configuration configurable + ### Fixed - **world:** Remove a forgotten camera debug function diff --git a/gamecore/modules/world/baseworld.lua b/gamecore/modules/world/baseworld.lua index a1f707a..858cbf1 100644 --- a/gamecore/modules/world/baseworld.lua +++ b/gamecore/modules/world/baseworld.lua @@ -225,17 +225,14 @@ function BaseWorld:setPlayerNumber(playerNumber) self.playerNumber = playerNumber or 1 end -function BaseWorld:addPlayer(actor, sourceid, haveCam) +function BaseWorld:addPlayer(actor, sourceid) local player = {} player.actor = actor player.sourceid = sourceid or 1 table.insert(self.players, player) - if (haveCam) then - local xx, yy = player.actor:getViewCenter() - self.cameras:addView(xx, yy, player.actor) - end + self.cameras:addTarget(player.actor) end function BaseWorld:sendInputToPlayers(actor) @@ -319,7 +316,7 @@ function BaseWorld:newCollisionFromMap(objectlayer, object) end function BaseWorld:addPlayerFromMap(object, i) - self:addPlayer(self.obj.Player(self, object.x, object.y), i, true) + self:addPlayer(self.obj.Player(self, object.x, object.y), i) end function BaseWorld:loadMapPlayers() @@ -361,6 +358,13 @@ function BaseWorld:getBackgroundColor() return self.backcolor[1]/256, self.backcolor[2]/256, self.backcolor[3]/256 end +function BaseWorld:resizeMap(w, h) + if self.haveMap then + local w, h = utils.math.floorCoord(w, h) + self.map:resize(w, h) + end +end + -- Lock MANAGEMENT FUNCTIONS -- Basic function to handle the lock @@ -412,8 +416,8 @@ function BaseWorld:draw(dt) self:drawActors() else for i=1, camNumber do - self.cameras:attachView(i) self:drawMap(i) + self.cameras:attachView(i) self:drawActors(i) self.cameras:detachView(i) self.cameras:drawHUD(i) @@ -448,10 +452,12 @@ function BaseWorld:drawMap(id) -- 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:getInternalCamCoordinate(id) - scale = self.cameras:getViewScale(id) or 1 + scale = self.cameras:getViewScale(id) or 2 local vx, vy = self.cameras:getOnScreenViewRelativePosition(id) tx = math.floor(tx - math.abs(vx)) ty = math.floor(ty - math.abs(vy)) + + local w, h = core.screen:getDimensions() end if self.haveMap then diff --git a/gamecore/modules/world/camera.lua b/gamecore/modules/world/camera.lua index 0fb9329..33fe4aa 100644 --- a/gamecore/modules/world/camera.lua +++ b/gamecore/modules/world/camera.lua @@ -37,11 +37,19 @@ function CameraSystem:new(world) self.scene = world.scene self.world = world - self.verticalSplit = SPLITSCREEN_ISVERTICAL + self.mode = "split" + self.verticalSplit = SPLITSCREEN_ISVERTICAL + self.targets = {} self:initViews() end +function CameraSystem:setMode(mode) + self.mode = mode + print("Camera system mode set to " .. mode) + return mode +end + function CameraSystem:initViews() self.views = {} @@ -143,6 +151,18 @@ 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 @@ -203,13 +223,14 @@ end function CameraSystem:getViewCoordinate(id) local view = self:getView(id) + local cam = self:getViewCam(id) local viewx, viewy, vieww, viewh - viewx = view.pos.x - (self.views.width/2) - viewy = view.pos.y - (self.views.height/2) + viewx = view.pos.x - ((self.views.width/2) / cam.scale) + viewy = view.pos.y - ((self.views.height/2) / cam.scale) - vieww = self.views.width - viewh = self.views.height + vieww = self.views.width / cam.scale + viewh = self.views.height / cam.scale return viewx, viewy, vieww, viewh end @@ -218,10 +239,12 @@ function CameraSystem:getInternalCamCoordinate(id) local cam = self:getViewCam(id) local viewx, viewy, vieww, viewh - viewx = cam.x - (self.views.width/2) - viewy = cam.y - (self.views.height/2) + viewx = cam.x - ((self.views.width/2) / cam.scale) + viewy = cam.y - ((self.views.height/2) / cam.scale) vieww, viewh = core.screen:getDimensions() + vieww = vieww / cam.scale + viewh = viewh / cam.scale return viewx, viewy, vieww, viewh end @@ -287,7 +310,11 @@ end function CameraSystem:update(dt) for i,v in ipairs(self.views.list) do - self:followActor(i) + if (self.mode == "middle") or (self.mode == "zoom") then + self:followAllActors(i) + else + self:followActor(i) + end end end @@ -321,6 +348,53 @@ function CameraSystem:followActor(id) 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 xx, yy = target:getViewCenter() + -- If start by initializing the value at the first found value + if (minX == nil) then minX = xx - (target.w/2) end + if (maxX == nil) then maxX = xx + (target.w/2) end + if (minY == nil) then minY = yy - (target.h/2) end + if (maxY == nil) then maxY = yy + (target.h/2) end + + + minX = math.min(minX, xx - (target.w/2)) + maxX = math.max(maxX, xx + (target.w/2)) + minY = math.min(minY, yy - (target.h/2)) + maxY = math.max(maxY, yy + (target.h/2)) + 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) + view.cam.scale = 1/scale + self.world:resizeMap(ww * 3, hh * 3) + end + + self:moveView(id, x, y) + end +end + + -- DRAW FUNCTIONS -- Basic callback to draw stuff diff --git a/gamecore/modules/world/world2D.lua b/gamecore/modules/world/world2D.lua index 6200c93..23e7c8e 100644 --- a/gamecore/modules/world/world2D.lua +++ b/gamecore/modules/world/world2D.lua @@ -82,10 +82,7 @@ function World2D:addPlayer(actor, sourceid, haveCam) table.insert(self.players, player) - if (haveCam) then - local xx, yy = player.actor:getViewCenter() - self.cameras:addView(xx, yy, player.actor) - end + self.cameras:addTarget(player.actor) end -- MAP LOADING FUNCTIONS @@ -119,7 +116,7 @@ function World2D:newCollisionFromMap(objectlayer, object) end function World2D:addPlayerFromMap(object, i) - self:addPlayer(self.obj.Player(self, object.x, object.y), i, true) + self:addPlayer(self.obj.Player(self, object.x, object.y), i) end -- BODIES MANAGEMENT FUNCTIONS diff --git a/gamecore/utils/init.lua b/gamecore/utils/init.lua index 765016a..54c4dc0 100644 --- a/gamecore/utils/init.lua +++ b/gamecore/utils/init.lua @@ -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") } diff --git a/gamecore/utils/table.lua b/gamecore/utils/table.lua new file mode 100644 index 0000000..46cd624 --- /dev/null +++ b/gamecore/utils/table.lua @@ -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