sonic-radiance/sonic-radiance.love/birb/modules/world/baseworld.lua
2021-05-05 08:30:32 +02:00

449 lines
10 KiB
Lua

-- baseworld.lua :: the base world object, that contain just a fast implementation
-- of a 2D world. It doesn't support collision and stuff.
--[[
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('%.baseworld$', '') .. "."
local BaseWorld = Object:extend()
local mapObjects = require(cwd .. "maps")
local CameraSystem = require(cwd .. "camera")
local PADDING_VALUE = 10/100
-- INIT FUNCTIONS
-- All functions to init the world and the map
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, maptype)
self:setGravity()
self:register()
self.isActive = true
end
function BaseWorld:setActorList(actorlist)
if actorlist == nil then
error("FATAL: You must set an actorlist to your world")
else
self.obj = require(actorlist)
end
end
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(grav, isDefault)
local grav = grav or 0
local isDefault = isDefault or false
self.gravity = {}
self.gravity.grav = grav
self.gravity.isDefault = isDefault
end
function BaseWorld:register()
self.scene:registerWorld(self)
end
function BaseWorld:reset()
self:initActors()
self:initPlayers()
self:initMap(self.mapfile, self.maptype)
self.cameras:initViews()
collectgarbage()
self:loadMap()
end
-- ACTOR MANAGEMENT FUNCTIONS
-- Basic function to handle actors
function BaseWorld:initActors( )
self.actors = {}
self.currentCreationID = 0
end
function BaseWorld:newActor(name, x, y, z, properties, mapname)
local debugstring = " at (" .. x .. ";" .. y .. ")."
core.debug:print("world2D", "adding actor " .. name .. debugstring)
local actor = self.obj.index[name](self, x, y)
if (mapname ~= nil) then
actor.mapname = mapname
end
if (properties ~= nil) then
actor:setProperties(properties)
end
end
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
function BaseWorld:registerActor(actor)
actor.creationID = self.currentCreationID
self.currentCreationID = self.currentCreationID + 1
table.insert(self.actors, actor)
end
function BaseWorld:removeActor(actor)
for i,v in ipairs(self.actors) do
if v == actor then
table.remove(self.actors, i)
end
end
end
function BaseWorld:countActors()
return #self.actors
end
function BaseWorld:getActors()
return self.actors
end
function BaseWorld:getActorsInRect(x, y, w, h)
local query = {}
local x2, y2 = x + w, y + h
for i,v in ipairs(self.actors) do
if (v.x >= x) and (v.x + v.w >= x2) and
(v.y >= y) and (v.y + v.h >= y2) then
table.insert(query, v)
end
end
return query
end
function BaseWorld: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
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 self.x, self.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
-- Give infos about the world
function BaseWorld:isActorIndexed(name)
return (self.obj.index[name] ~= nil)
end
function BaseWorld:isCollisionIndexed(name)
return (self.obj.collisions[name] ~= nil)
end
-- PLAYER MANAGEMENT FUNCTIONS
-- Basic function to handle player actors
function BaseWorld:initPlayers()
self.players = {}
self.playerNumber = 1
end
function BaseWorld:setPlayerNumber(playerNumber)
self.playerNumber = playerNumber or 1
end
function BaseWorld:addPlayer(x, y, z, id)
local player = {}
if id <= self.playerNumber then
player.actor = self:newPlayer(x, y, z)
player.sourceid = id or 1
table.insert(self.players, player)
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
local keys = self.scene.sources[v.sourceid].keys
v.actor:getInput(keys)
end
end
function BaseWorld:removePlayer(actor)
for i,v in ipairs(self.players) do
if (v.actor == actor) then
table.remove(self.players, i)
end
end
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()
if (self.map ~= nil) then
self.map:loadObjects()
end
end
function BaseWorld:getBox()
if (self.map ~= nil) then
return self.map:getBox()
else
local w, h = core.screen:getDimensions()
return 0, 0, w, h
end
end
function BaseWorld:getDimensions()
if (self.map ~= nil) then
return self.map:getDimensions()
else
return core.screen:getDimensions()
end
end
function BaseWorld:setBackgroundColor(r, g, b)
if (self.map ~= nil) then
self.map:setBackgroundColor(r, g, b)
end
end
function BaseWorld:removeBackgroundColor()
if (self.map ~= nil) then
self.map:setBackgroundColor()
end
end
function BaseWorld:getBackgroundColor()
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
-- Basic function to handle the lock
function BaseWorld:setActivity(activity)
self.isActive = activity
end
function BaseWorld:switchActivity()
self.isActive = (self.isActive == false)
end
function BaseWorld:getActivity()
return self.isActive
end
-- UPDATE FUNCTIONS
-- All update functions
function BaseWorld:update(dt)
if (core.screen:isActive()) then
self:updateMap(dt)
self:sendInputToPlayers(dt)
self:updateActors(dt)
end
self.cameras:update(dt)
end
function BaseWorld:updateActors(dt)
local actors = self:getActors()
for i,v in ipairs(actors) do
v:update(dt)
end
end
function BaseWorld:updateMap(dt)
if (self.map ~= nil) then
self.map:update(dt)
end
end
function BaseWorld:getTileTypeAtPoint(x, y)
if (self.map.getTileTypeAtPoint ~= nil) then
return self.map:getTileTypeAtPoint(x, y)
end
end
function BaseWorld:haveTileTypeInRect(x, y, x2, y2, type)
if (self.map.haveTileTypeInRect ~= nil) then
return self.map:haveTileTypeInRect(x, y, x2, y2, type)
end
end
-- DRAW FUNCTIONS
-- All function to draw the map, world and actors
function BaseWorld:draw(dt)
self:drawBackgroundColor()
local camNumber = self.cameras:getViewNumber()
if (camNumber == 0) then
self:drawLowerLayers()
self:drawActors()
self:drawUpperLayers()
else
for i=1, camNumber do
self.cameras:attachView(i)
self:drawLowerLayers(i)
self:drawActors(i)
self:drawUpperLayers(i)
self.cameras:detachView(i)
end
end
end
function BaseWorld:drawActors(id)
local actors = self:getVisibleActors(id)
for i,v in ipairs(actors) do
v:draw()
end
end
function BaseWorld:drawUpperLayers()
if (self.map ~= nil) then
self.map:drawUpperLayers()
end
end
function BaseWorld:drawLowerLayers()
if (self.map ~= nil) then
self.map:drawLowerLayers()
end
end
function BaseWorld:drawMap(id)
if (self.map ~= nil) then
self.map:draw()
end
end
function BaseWorld:drawBackgroundColor()
if (self.map ~= nil) then
self.map:drawBackgroundColor()
end
end
return BaseWorld