diff --git a/debug.lua b/debug.lua new file mode 100644 index 0000000..9a85811 --- /dev/null +++ b/debug.lua @@ -0,0 +1,38 @@ +-- core/debug.lua :: Debug functions for the core 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 DebugSystem = Object:extend() + +local lovebird = require("libs.lovebird") + +function DebugSystem:new(controller, active) + self.controller = controller + lovebird.update() + self.active = active or false +end + +function DebugSystem:update(dt) + lovebird.update(dt) +end + +return DebugSystem diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..63bba08 --- /dev/null +++ b/init.lua @@ -0,0 +1,71 @@ +-- core/init.lua :: The main file of the core system, an object full of subsystem +-- loaded by the game to handle the main functions (like screen, translation, +-- inputs…) + +--[[ + 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 CoreSystem = Object:extend() + +local DebugSystem = require "core.debug" + +local Options = require "core.options" +local Input = require "core.input" +local Screen = require "core.screen" +local Lang = require "core.lang" +local SceneManager= require "core.scenemanager" + +function CoreSystem:new() + self.debug = DebugSystem(self) + self.options = Options(self) + self.input = Input(self) + self.screen = Screen(self) + self.scenemanager = SceneManager(self) +end + +function CoreSystem:mousemoved(x, y, dx, dy) + local x, y = self.screen:project(x, y) + local dx, dy = self.screen:project(dx, dy) + self.scenemanager:mousemoved(x, y, dx, dy) +end + +function CoreSystem:mousepressed( x, y, button, istouch ) + local x, y = self.screen:project(x, y) + self.scenemanager:mousepressed( x, y, button, istouch ) +end + +function CoreSystem:update(dt) + self.debug:update(dt) + self.input:update(dt) + + self.scenemanager:update(dt) +end + +function CoreSystem:draw() + self.scenemanager:draw() +end + +function CoreSystem:exit() + self.options:save() + love.event.quit() +end + +return CoreSystem diff --git a/input.lua b/input.lua new file mode 100644 index 0000000..7513d25 --- /dev/null +++ b/input.lua @@ -0,0 +1,117 @@ +-- core/input.lua :: The input system. This object take care of transforming the +-- differents inputs source in a virtual controller for the game. + +--[[ + 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 InputManager = Object:extend() + +function InputManager:new(controller) + self.controller = controller + self.data = self.controller.options.data.input[1] + + self.keys = self:getKeyList() + self.fakekeys = self:getKeyList() +end + +function InputManager:isDown(padkey) + local isdown = false + if self.data.type == "keyboard" then + local key = self.data.keys[padkey] + isdown = love.keyboard.isDown(key) + if isdown then + end + else + print("Warning: unsupported input device") + end + + return isdown +end + +function InputManager:getKeyList() + local keys = {} + for k,v in pairs(self.data.keys) do + keys[k] = {} + keys[k].isDown = false + keys[k].isPressed = false + keys[k].isReleased = false + keys[k].test = "ok" + end + + return keys +end + +function InputManager:translateAction(key) + --TODO:depreciated function + local padkey = "" + for k,v in pairs(self.data.keys) do + if v == key then padkey = k end + end + return padkey +end + +function InputManager:getKey(padkey) + local padkey = padkey + for k,v in pairs(self.data.keys) do + if (k == padkey) then key = v end + end + return key +end + +function InputManager:flushKeys() + self.keys = {} + for k,v in pairs(self.data.keys) do + self.keys[k] = {} + self.keys[k].isDown = false + self.keys[k].isPressed = false + self.keys[k].isReleased = false + self.keys[k].test = "ok" + end +end + +function InputManager:update(dt) + for k,v in pairs(self.keys) do + local isDown = self:isDown(k) + if (isDown) then + if not (self.keys[k].isDown) then + self.keys[k].isDown = true + self.keys[k].isPressed = true + self.keys[k].isReleased = false + else + if (self.keys[k].isPressed) then + self.keys[k].isPressed = false + end + end + else + if (self.keys[k].isDown) then + self.keys[k].isDown = false + self.keys[k].isPressed = false + self.keys[k].isReleased = true + else + if (self.keys[k].isReleased) then + self.keys[k].isReleased = false + end + end + end + end +end + +return InputManager diff --git a/lang.lua b/lang.lua new file mode 100644 index 0000000..1144806 --- /dev/null +++ b/lang.lua @@ -0,0 +1,52 @@ +-- core/langs.lua :: The translation system. Transform a string to another +-- according to the translations files in the datas/ folder. + +--[[ + 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 LanguageManager = Object:extend() +local langs = require "datas.languages" + +function LanguageManager:new(controller) + self.controller = controller + self:setLang(self.controller.options.data.language) +end + +function LanguageManager:getStringList(library, file) + return require(self.lang .. "." .. library .. "." .. file) +end + +function LanguageManager:getLangName(lang) + local langnames = langs.available_langs + return langnames[lang] +end + +function LanguageManager:getCurrentLangName() + local langnames = langs.available_langs + return langnames[self.lang] +end + +function LanguageManager:setLang(lang) + self.controller.options.data.language = lang + self.lang = self.controller.options.data.language +end + +return LanguageManager diff --git a/modules/assets/animator.lua b/modules/assets/animator.lua new file mode 100644 index 0000000..7bce90c --- /dev/null +++ b/modules/assets/animator.lua @@ -0,0 +1,101 @@ +-- assets/animator :: the animator object. The animator object handle what +-- frame a sprite should draw. + +--[[ + 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 Animator = Object:extend() + +function Animator:new(sprite) + self.sprite = sprite + self.frame = 1 + self.frameTimer = 0 + self.currentAnimation = "" + self.animationData = {} + + self.customSpeed = 0 + + self:changeToDefaultAnimation() +end + +function Animator:setCustomSpeed(customSpeed) + self.customSpeed = customSpeed or 0 +end + +function Animator:update(dt) + if (self.currentAnimation == "") then + print("warning: no current animation data") + return 0 + end + + local speed = self.animationData.speed + if (self.animationData.speed) == -1 then + speed = self.customSpeed --math.abs(self.xsp / 16) + end + self.frameTimer = self.frameTimer + (speed * dt) + if self.frameTimer > 1 then + self.frameTimer = 0 + if self.frame == self.animationData.endAt then + self.frame = self.animationData.loop + else + self.frame = self.frame + 1 + end + end +end + +function Animator:getAnimationDuration(animation) + return (self.animationData.endAt - self.animationData.startAt) / self.animationData.speed +end + +function Animator:getFrame() + return self.frame +end + +function Animator:animationExist(name) + return (self.sprite.data.animations[self.currentAnimation] ~= nil) +end + +function Animator:draw(x, y, r, sx, sy, ox, oy, kx, ky) + self.sprite:drawFrame(self.frame, x, y, r, sx, sy, ox, oy, kx, ky) +end + +function Animator:changeAnimation(name, restart) + -- Force restart if animation name is different + if (self.currentAnimation ~= name) then + restart = true + else + restart = restart or false + end + + self.currentAnimation = name + self.animationData = self.sprite.data.animations[self.currentAnimation] + + if (restart == true) then + self.frame = self.animationData.startAt + self.frameTimer = 0 + end +end + +function Animator:changeToDefaultAnimation(restart) + self:changeAnimation(self.sprite.data.metadata.defaultAnim, restart) +end + +return Animator diff --git a/modules/assets/autotile.lua b/modules/assets/autotile.lua new file mode 100644 index 0000000..4104303 --- /dev/null +++ b/modules/assets/autotile.lua @@ -0,0 +1,110 @@ +-- assets/autotile :: The autotile object : this is an object that draw tiles +-- automatically with borders in rectangles. +-- It works with a 3×3 tileset showing all borders. + +--[[ + 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 Tileset = require "core.modules.assets.tileset" +local Autotile = Object:extend() + +function Autotile:new(filepath) + self.tileset = Tileset(filepath) + + self.data = require(filepath .. ".lua") + self.metadata = self.data.metadata + + self.tilesize = self.metadata.width +end + +function Autotile:drawtile(i, j, x, y, r, sx, sy, ox, oy, kx, ky) + local i = i or 1 + local j = j or 1 + local tilesize = self.tilesize / 2 + i = (i - 1) * 2 + 1 + j = (j - 1) * 2 + 1 + self.tileset:drawTile_Grid(i , j , x , y , r, sx, sy, ox, oy, kx, ky) + self.tileset:drawTile_Grid(i + 1, j , x + tilesize, y , r, sx, sy, ox, oy, kx, ky) + self.tileset:drawTile_Grid(i , j + 1, x , y + tilesize, r, sx, sy, ox, oy, kx, ky) + self.tileset:drawTile_Grid(i + 1, j + 1, x + tilesize, y + tilesize, r, sx, sy, ox, oy, kx, ky) +end + +function Autotile:draw(x, y, w, h) + local w = w or self.tilesize + local h = h or self.tilesize + w = math.max(math.floor(w / self.tilesize), 1) + h = math.max(math.floor(h / self.tilesize), 1) + local halfsize = self.tilesize / 2 + local tilesize = self.tilesize + if (w == 1) then + self.tileset:drawtile_Grid(1, 1, x , y) + self.tileset:drawtile_Grid(1, 6, x , y + (h*2 - 1) * halfsize) + self.tileset:drawtile_Grid(6, 1, x + (w*2 - 1) * halfsize, y) + self.tileset:drawtile_Grid(6, 6, x + (w*2 - 1) * halfsize, y + (h*2 - 1) * halfsize) + if (h > 1) then + h = h - 1 + for i = 1, h do + self.tileset:drawtile_Grid(1, 3, x, y + (i * tilesize) - halfsize) + self.tileset:drawtile_Grid(6, 3, x + halfsize, y + (i * tilesize) - halfsize) + + self.tileset:drawtile_Grid(1, 4, x , y + (i * tilesize)) + self.tileset:drawtile_Grid(6, 4, x + halfsize, y + (i * tilesize)) + end + end + -- draw just one stuff + else + if (h == 1) then + self.tileset:drawtile_Grid(1, 1, x , y) + self.tileset:drawtile_Grid(1, 6, x , y + (h*2 - 1) * halfsize) + self.tileset:drawtile_Grid(6, 1, x + (w*2 - 1) * halfsize, y) + self.tileset:drawtile_Grid(6, 6, x + (w*2 - 1) * halfsize, y + (h*2 - 1) * halfsize) + w = w - 1 + for i = 1, w do + self.tileset:drawtile_Grid(3, 1, x + (i * tilesize) - halfsize, y) + self.tileset:drawtile_Grid(3, 6, x + (i * tilesize) - halfsize, y + halfsize) + + self.tileset:drawtile_Grid(4, 1, x + (i * tilesize) , y ) + self.tileset:drawtile_Grid(4, 6, x + (i * tilesize), y +halfsize) + end + else + self:drawtile(1, 1, x , y) + self:drawtile(1, 3, x , y + (h - 1) * tilesize) + self:drawtile(3, 1, x + (w - 1) * tilesize, y) + self:drawtile(3, 3, x + (w - 1) * tilesize, y + (h - 1) * tilesize) + w = w - 2 + h = h - 2 + for i=1, w do + self:drawtile(2, 1, i * tilesize, y) + self:drawtile(2, 3, i * tilesize, y + (h + 1) * tilesize) + for j=1, h do + self:drawtile(2, 2, i * tilesize, j * tilesize) + end + end + + for i=1, h do + self:drawtile(1, 2, x , i * tilesize) + self:drawtile(3, 2, x + (w + 1) * tilesize, i * tilesize) + end + end + end +end + +return Autotile diff --git a/modules/assets/background.lua b/modules/assets/background.lua new file mode 100644 index 0000000..862050b --- /dev/null +++ b/modules/assets/background.lua @@ -0,0 +1,51 @@ +-- assets/sprite :: the background object, which is an image that draw itself +-- automatically to fill a texture. + +--[[ + 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 Background = Object:extend() + +function Background:new(filepath) + self.image = love.graphics.newImage(filepath) + self.batch = love.graphics.newSpriteBatch(self.image , 1000 ) + + self.width, self.height = self.image:getDimensions() + screenwidth, screenheight = core.screen:getDimensions() + + local w = math.floor(screenwidth / self.width) * self.width + 1 + local h = math.floor(screenheight / self.height) * self.height + 1 + + for i=-1, w do + for j=-1, h do + self.batch:add(i * self.width, j * self.height) + j = j + 1 + end + i = i + 1 + end +end + +function Background:draw(ox, oy) + love.graphics.setColor(1, 1, 1) + love.graphics.draw(self.batch, ox, oy) +end + +return Background diff --git a/modules/assets/fonts.lua b/modules/assets/fonts.lua new file mode 100644 index 0000000..f2bc7c9 --- /dev/null +++ b/modules/assets/fonts.lua @@ -0,0 +1,178 @@ +-- assets/fonts :: the fonts object, which is a simple way to draw text with font. +-- Some of these functions are quite slow, so it's better to use them to generate +-- texture instead of text. + +--[[ + 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 Font = Object:extend() + +-- Initilizing and configuring option + +function Font:new(filename, size) + local filename = filename + self.font = love.graphics.newFont(filename, size) + self.filter = "" + self:setColor(1, 1, 1, 1) + self:setSpacing(false, 0) + self.align = "left" +end + +function Font:set() + love.graphics.setFont(self.font) +end + +function Font:setColor(r, g, b, a) + self.color = {} + self.color.r = r + self.color.g = g + self.color.b = b + self.color.a = a +end + +function Font:setColorFromTable(color) + self.color = color +end + +function Font:setSpacing(use_custom, size) + self.spacing = {} + self.spacing.active = use_custom + self.spacing.size = size +end + +function Font:setAlign(align) + self.align = align +end + +function Font:setFilter(filter) + self.filter = filter +end + +function Font:setLineHeight(height) + self.font:setLineHeight(height) +end + +-- get information functions + +function Font:getHeight() + local font = self.font + return font:getHeight() +end + +function Font:getWidth(string) + local spacing = 0 + if (self.spacing.active == true) then + local charNumber = string.len(string) + spacing = self.spacing.size * charNumber + end + local width = self.font:getWidth(string) + spacing + return width +end + +function Font:getColor() + return self.color +end + +-- print functions + +function Font:draw(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky) + -- draw text with color and effect applied + local limit = limit or 0 + local align = align or self.align + + self:applyFilter(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky) + love.graphics.setColor(self.color.r, self.color.g, self.color.b, self.color.a) + self:printf(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky) +end + +function Font:print(text, x, y, align, r, sx, sy, ox, oy, kx, ky) + + self:set() + if (self.spacing.active) then + utils.graphics.printWithSpacing(text, x, y, self.spacing.size, align, r, sx, sy, ox, oy, kx, ky) + else + utils.graphics.print(text, x, y, align, r, sx, sy, ox, oy, kx, ky) + end + +end + +function Font:printf(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky) + self:set() + if (limit > 0) then + love.graphics.printf(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky) + else + self:print(text, x, y, align, r, sx, sy, ox, oy, kx, ky) + end +end + +-- FILTER SYSTEM + +function Font:applyFilter(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky) + if self.filter == "shadow" then + self:applyFilterShadow(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky) + elseif self.filter == "border" then + self:applyFilterBorder(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky) + elseif self.filter == "doubleborder" then + self:applyFilterDoubleBorder(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky) + end +end + +function Font:applyFilterShadow(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky) + love.graphics.setColor(0, 0, 0, 1) + self:printf(text, x+1, y+1, limit, align, align, r, sx, sy, ox, oy, kx, ky) + utils.graphics.resetColor() +end + +function Font:applyFilterBorder(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky) + love.graphics.setColor(0, 0, 0, 1) + + self:printf(text, x-1, y-1, limit, align, r, sx, sy, ox, oy, kx, ky) + self:printf(text, x , y-1, limit, align, r, sx, sy, ox, oy, kx, ky) + self:printf(text, x+1, y-1, limit, align, r, sx, sy, ox, oy, kx, ky) + + self:printf(text, x+1, y , limit, align, r, sx, sy, ox, oy, kx, ky) + self:printf(text, x-1, y , limit, align, r, sx, sy, ox, oy, kx, ky) + + self:printf(text, x-1, y+1, limit, align, r, sx, sy, ox, oy, kx, ky) + self:printf(text, x , y+1, limit, align, r, sx, sy, ox, oy, kx, ky) + self:printf(text, x+1, y+1, limit, align, r, sx, sy, ox, oy, kx, ky) + + utils.graphics.resetColor() +end + +function Font:applyFilterDoubleBorder(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky) + love.graphics.setColor(0, 0, 0, 1) + + self:printf(text, x-2, y-2, limit, align, r, sx, sy, ox, oy, kx, ky) + self:printf(text, x , y-2, limit, align, r, sx, sy, ox, oy, kx, ky) + self:printf(text, x+2, y-2, limit, align, r, sx, sy, ox, oy, kx, ky) + + self:printf(text, x+2, y , limit, align, r, sx, sy, ox, oy, kx, ky) + self:printf(text, x-2, y , limit, align, r, sx, sy, ox, oy, kx, ky) + + self:printf(text, x-2, y+2, limit, align, r, sx, sy, ox, oy, kx, ky) + self:printf(text, x , y+2, limit, align, r, sx, sy, ox, oy, kx, ky) + self:printf(text, x+2, y+2, limit, align, r, sx, sy, ox, oy, kx, ky) + + utils.graphics.resetColor() +end + +return Font diff --git a/modules/assets/imagefonts.lua b/modules/assets/imagefonts.lua new file mode 100644 index 0000000..16e0b7a --- /dev/null +++ b/modules/assets/imagefonts.lua @@ -0,0 +1,14 @@ +local Font = require "core.modules.assets.fonts" +local ImageFont = Font:extend() + +function ImageFont:new(filename, extraspacing) + local data = require(filename) + local extraspacing = extraspacing or data.extraspacing or 1 + self.font = love.graphics.newImageFont(filename .. ".png", data.glyphs, extraspacing) + self.filter = "" + self:setColor(1, 1, 1, 1) + self:setSpacing(false, 0) + self.align = "left" +end + +return ImageFont diff --git a/modules/assets/init.lua b/modules/assets/init.lua new file mode 100644 index 0000000..b6e11dc --- /dev/null +++ b/modules/assets/init.lua @@ -0,0 +1,198 @@ +-- modules/assets :: a simple assets manager, aim to put every assets in a simple +-- serie of table in order to find them easily. + +--[[ + 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 Assets = Object:extend() + +local Sprite = require "core.modules.assets.sprites" +local Font = require "core.modules.assets.fonts" +local ImageFont = require "core.modules.assets.imagefonts" + +local Tileset = require "core.modules.assets.tileset" +local Autotile = require "core.modules.assets.autotile" +local Background = require "core.modules.assets.background" + + +function Assets:new() + self.sprites = {} + self.sfx = {} + self.fonts = {} + self.music = nil + self:clearBackgrounds() + self:clearFonts() + self:clearAutotile() + self:clearTileset() + + self.images = {} + + self.isActive = true +end + +function Assets:init() + self.sprites = {} + self.sfx = {} + self.fonts = {} + self.music = nil + self.backgrounds= {} + self:clearFonts() + + self.images = {} +end + +function Assets:clear() + -- TODO: destroy individually each texture/image when assets are cleared + self.sprites = {} + self.sfx = {} + self.fonts = {} + self.music = nil + self.backgrounds= {} + self:clearFonts() + + self.images = {} +end + +function Assets:update(dt) + if (self.isActive) then + self:animationsUpdate(dt) + end +end + +-- SFX et Musique + +function Assets:addSFX(name, filepath) + self:newSFX(name, filepath) +end + +function Assets:newSFX(name, filepath) + self.sfx[name] = love.audio.newSource( filepath, "static" ) +end + +function Assets:clearSFX() + love.audio.stop( ) + self.sfx = {} +end + +function Assets:setMusic(filename) + if filename ~= nil then + love.audio.stop( ) + self.music = love.audio.newSource(filename, "stream" ) + self.music:setVolume(core.options.data.audio.music / 100) + end +end + +function Assets:playSFX(filename) + if not (self.sfx[filename] == nil) then + self.sfx[filename]:stop() + self.sfx[filename]:setVolume(core.options.data.audio.sfx / 100) + love.audio.play( self.sfx[filename] ) + end +end + +function Assets:playMusic() + if not (self.music == nil) then + love.audio.play(self.music) + end +end + +function Assets:silence() + love.audio.stop() +end + +-- Background -- + +function Assets:addImage(name, filename) + self.images[name] = love.graphics.newImage(filename) +end + +function Assets:drawImage(name, x, y, r, sx, sy, ox, oy, kx, ky) + love.graphics.draw(self.images[name], x, y, r, sx, sy, ox, oy, kx, ky) +end + +-- Images -- + +function Assets:clearBackgrounds() + self.backgrounds = {} +end + +function Assets:addBackground(name, filepath) + self.backgrounds[name] = Background(filepath) +end + +-- SPRITES -- + + +function Assets:addSprite(name, filepath) + self.sprites[name] = Sprite(filepath) +end + +function Assets:clearSprites() + self.sprites = {} +end + +function Assets:animationsUpdate(dt) + for i,v in pairs(self.sprites) do + v:update(dt) + end +end + +-- FONTS -- + +function Assets:clearFonts() + self.fonts = {} +end + +function Assets:addFont(key, filename, size) + local font = Font(filename, size) + self.fonts[key] = font +end + +function Assets:addImageFont(key, filename, extraspacing) + local font = ImageFont(filename, extraspacing) + self.fonts[key] = font +end + +function Assets:getFont(filename) + return self.fonts[filename] +end + +-- Tileset + +function Assets:addTileset(name, filepath) + self.tileset[name] = Tileset(filepath) +end + +function Assets:clearTileset() + self.tileset = {} +end + +-- Autotile + +function Assets:addAutotile(name, tilesize) + self.autotile[name] = Autotile(name, tilesize) +end + +function Assets:clearAutotile() + self.autotile = {} +end + +return Assets diff --git a/modules/assets/sprites.lua b/modules/assets/sprites.lua new file mode 100644 index 0000000..326db34 --- /dev/null +++ b/modules/assets/sprites.lua @@ -0,0 +1,86 @@ +-- assets/sprite :: the assets object, which is basically a tileset animated by +-- an animator object. An animator object is always tied to a sprite, but a sprite +-- can use different animator in order to make several animator share the same sprite + +--[[ + 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 Sprite = Object:extend() +local Animator = require("core.modules.assets.animator") +local Tileset = require("core.modules.assets.tileset") + +function Sprite:new(filepath) + self.tileset = Tileset(filepath) + self.data = require(filepath) + + self.animator = Animator(self) + + self.customSpeed = 0 + + self:changeToDefaultAnimation(true) +end + +function Sprite:update(dt) + self.animator:update(dt) +end + +function Sprite:clone() + return Animator(self) +end + +function Sprite:setCustomSpeed(customSpeed) + self.animator:setCustomSpeed(customSpeed) +end + +function Sprite:changeToDefaultAnimation(restart) + self.animator:changeToDefaultAnimation(restart) +end + +function Sprite:changeAnimation(name, restart) + self.animator:changeAnimation(name, restart) +end + +function Sprite:animationExist(name) + return self.animator:animationExist(name) +end + +function Sprite:drawAnimation(x, y, r, sx, sy, ox, oy, kx, ky) + self.animator:draw(x, y, r, sx, sy, ox, oy, kx, ky) +end + +function Sprite:drawFrame(frame, x, y, r, sx, sy, ox, oy, kx, ky) + self.tileset:drawTile(frame, x, y, r, sx, sy, ox, oy, kx, ky) +end + +function Sprite:drawPart(x, y, w, h, r, sx, sy, ox, oy, kx, ky) + local w = math.floor(w) + local h = math.floor(h) + + if w >= 0 and h <= 0 then + return 0 + end + + love.graphics.setScissor(x - ox, y - oy, w, h) + self:drawAnimation(x, y, r, sx, sy, ox, oy, kx, ky) + love.graphics.setScissor( ) +end + +return Sprite diff --git a/modules/assets/tileset.lua b/modules/assets/tileset.lua new file mode 100644 index 0000000..c86aa44 --- /dev/null +++ b/modules/assets/tileset.lua @@ -0,0 +1,89 @@ +-- assets/tileset :: tileset are automatic breakage of texture into quads. they +-- have the adventage of being automatized, reducing the ammount of code needed +-- to create quads. + +-- They have two manners to be draw: with their quad id (in 1D) or by using their +-- place in the grid, in 2D. + +--[[ + 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 Tileset = Object:extend() + +function Tileset:new(filepath) + self.texture = love.graphics.newImage(filepath .. ".png") + + local data = require(filepath) + self.metadata = data.metadata + + self:createQuads() +end + +function Tileset:createGrid() + self.textureWidth, self.textureHeight = self.texture:getDimensions() + self.width, self.height = self.metadata.width, self.metadata.height + self.gridWidth, self.gridHeight = math.floor(self.textureWidth / self.width), + math.floor(self.textureHeight / self.height) +end + +function Tileset:createQuads() + self.quads = {} + + + self:createGrid() + + local quad, n + + n = 1 + for i=0, (self.gridHeight-1) do + for j=0, (self.gridWidth-1) do + quad = love.graphics.newQuad(j * self.width, i * self.height, self.width, self.height, self.textureWidth, self.textureHeight) + self.quads[n] = quad + n = n + 1 + end + end + +end + +function Tileset:getTileID_Grid(x, y) + local n = (y - 1) * self.gridWidth + x + + return n +end + +function Tileset:getTile_Grid(x, y) + return self:getTile(self:getTileID_Grid(x, y)) +end + +function Tileset:getTile(n) + return self.quads[n] +end + +function Tileset:drawTile_Grid(i, j, x, y, r, sx, sy, ox, oy, kx, ky) + local tileID = self:getTileID_Grid(i, j) + love.graphics.draw(self.texture, self.quads[tileID], x, y, r, sx, sy, ox, oy, kx, ky) +end + +function Tileset:drawTile(id, x, y, r, sx, sy, ox, oy, kx, ky) + love.graphics.draw(self.texture, self.quads[id], x, y, r, sx, sy, ox, oy, kx, ky) +end + +return Tileset diff --git a/modules/menusystem/flowbox.lua b/modules/menusystem/flowbox.lua new file mode 100644 index 0000000..a120f80 --- /dev/null +++ b/modules/menusystem/flowbox.lua @@ -0,0 +1,186 @@ +local cwd = (...):gsub('%.flowbox$', '') .. "." +local Menu = require(cwd .. "parent") + +local FlowBox = Menu:extend() + +local menuutils = require(cwd .. "widgets.utils") + +function FlowBox:new(menusystem, name, x, y, w, h, slots_hor, slots_vert) + self.view = {} + self.view.slotNumber = slots_hor * slots_vert + self.view.lineNumber = slots_vert + self.view.colNumber = slots_hor + self.view.firstSlot = 1 + FlowBox.super.new(self, menusystem, name, x, y, w, h) + self.widget.h = math.floor( self.h / slots_vert ) + self.widget.w = math.floor( self.w / slots_hor ) + self.h = slots_vert * self.widget.h -- On fait en sorte que la hauteur + self.w = slots_hor * self.widget.w -- et la largeur + -- soit un multiple du nombre de slot et de leur dimensions +end + +function FlowBox:updateWidgetSize() + self.widget.h = math.floor( self.h / self.view.lineNumber ) + self.widget.w = math.floor( self.w / self.view.colNumber ) +end + +function FlowBox:update(dt) + self:updateView() +end + +function FlowBox:updateView() + local col, line = self:getCoord(self.widget.selected) + local begincol, beginline = self:getCoord(self.view.firstSlot) + + if line < beginline then + beginline = line + end + + if line > beginline + self.view.lineNumber - 1 then + beginline = line - self.view.lineNumber + 1 + end + + if beginline < 0 then + beginline = 0 + end + + self.view.firstSlot = beginline * self.view.colNumber + 1 +end + +function FlowBox:getCoord(id_selected) + id_selected = id_selected - 1 -- On simplifie les calcul en prenant 0 comme départ + local line, col + line = math.floor(id_selected / self.view.colNumber) + col = id_selected - (line * self.view.colNumber) + return col, line +end + +function FlowBox:moveCursor(new_col, new_line) + local col, line = self:getCoord(self.widget.selected) + local lastcol, lastline = self:getCoord(#self.widget.list) + + + if new_line < 0 then + new_line = lastline + end + + if new_line > lastline then + new_line = 0 + end + + if (new_line == lastline) then + if new_col < 0 then + new_col = lastcol + end + + if new_col > lastcol then + if (line == lastline) then + new_col = 0 + else + new_col = lastcol + end + end + else + if new_col < 0 then + new_col = self.view.colNumber - 1 + end + + if new_col == self.view.colNumber then + new_col = 0 + end + end + + self.widget.selected = (new_line * self.view.colNumber) + new_col + 1 +end + +function FlowBox:keyreleased(key, code) + local col, line = self:getCoord(self.widget.selected) + if key == 'left' then + self:moveCursor(col - 1, line) + end + + if key == 'right' then + self:moveCursor(col + 1, line) + end + + if key == 'up' then + self:moveCursor(col, line - 1) + end + + if key == 'down' then + self:moveCursor(col, line + 1) + end + + if key == "A" then + if (self.widget.selected >= 1 and self.widget.selected <= #self.widget.list) then + self.widget.list[self.widget.selected]:action() + end + end +end + +function FlowBox:mousemoved(x, y) + local col, line = self:getCoord(self.widget.selected) + local begincol, beginline = self:getCoord(self.view.firstSlot) + local newcol, newline, widget_selected + + newline = beginline + math.floor(y / self.widget.h) + newcol = math.floor(x / self.widget.w) + widget_selected = (newline * self.view.colNumber) + newcol + 1 + + if widget_selected >= 1 and widget_selected <= #self.widget.list then + self.widget.selected = widget_selected + self:getFocus() + end +end + +function FlowBox:mousepressed(x, y, button, isTouch) + local col, line = self:getCoord(self.widget.selected) + local begincol, beginline = self:getCoord(self.view.firstSlot) + local newline, newcol, widget_selected + + newline = beginline + math.floor(y / self.widget.h) + newcol = math.floor(x / self.widget.w) + widget_selected = (newline * self.view.colNumber) + newcol + 1 + + if widget_selected >= 1 and widget_selected <= #self.widget.list then + self.widget.selected = widget_selected + self:getFocus() + self.widget.list[self.widget.selected]:action() + end + +end + +function FlowBox:draw() + self:updateView() + local widgety = self.y + local widgetx = self.x + for i,v in ipairs(self.widget.list) do + if (i >= self.view.firstSlot) and (i < self.view.firstSlot + self.view.slotNumber) then + v:draw(widgetx, widgety, self.widget.w, self.widget.h) + if self.widget.selected == i and self:haveFocus() == true then + v:drawSelected(widgetx, widgety, self.widget.w, self.widget.h) + else + v:draw(widgetx, widgety, self.widget.w, self.widget.h) + end + widgetx = widgetx + self.widget.w + if widgetx == (self.x + self.w) then + widgetx = self.x + widgety = widgety + self.widget.h + end + end + end +end + +function FlowBox:drawCursor() + self:updateView() + local begincol, beginline = self:getCoord(self.view.firstSlot) + if (self.widget.selected >= 1 and self.widget.selected <= #self.widget.list) then + local w, h = self:getWidgetSize() + local col, line = self:getCoord(self.widget.selected) + local x = (col) * h + local y = (line - beginline) * h + menuutils.drawCursor(self.x + x, self.y + y, w, h) + end +end + +return FlowBox diff --git a/modules/menusystem/grid.lua b/modules/menusystem/grid.lua new file mode 100644 index 0000000..7dec4b3 --- /dev/null +++ b/modules/menusystem/grid.lua @@ -0,0 +1,235 @@ +local cwd = (...):gsub('%.grid$', '') .. "." +local Menu = require(cwd .. "parent") + +local GridBox = Menu:extend() + +local menuutils = require(cwd .. "widgets.utils") + +function GridBox:new(menusystem, name, x, y, w, h, colNumber, lineNumber) + self.view = {} + self.view.slotNumber = colNumber * lineNumber + self.view.colNumber = colNumber + self.view.lineNumber = lineNumber + self.view.firstSlot = 1 + GridBox.super.new(self, menusystem, name, x, y, w, h) + self.h = lineNumber * self.widget.h -- On fait en sorte que la hauteur + self.w = colNumber * self.widget.w -- et la largeur + -- soit un multiple du nombre de slot et de leur dimensions + self.cursor = {} + self.cursor.x = 0 + self.cursor.y = 0 + + -- La gridbox possède la particularité de pouvoir fusioner des slots, on fait + -- donc une liste de slots disponibles, qui serviront par la suite. + self.slots = {} +end + +function GridBox:addSlot(widgetID, x, y, w, h) + local slot = {} + slot.x = x + slot.y = y + slot.w = w + slot.h = h + slot.widgetID = widgetID + + table.insert(self.slots, slot) +end + +function GridBox:updateWidgetSize() + self.widget.h = math.floor( self.h / self.view.lineNumber ) + self.widget.w = math.floor( self.w / self.view.colNumber ) +end + +function GridBox:getWidgetSize(id) + local slot = self:getWidgetSlot(id) + if slot == 0 then + return 1, 1 + else + return self.widget.w * self.slots[slot].w, self.widget.h * self.slots[slot].h + end +end + +function GridBox:getSlotHitbox(slot) + local x, y, w, h + x = self.slots[slot].x * self.widget.w + y = self.slots[slot].y * self.widget.h + w = self.slots[slot].w * self.widget.w + h = self.slots[slot].h * self.widget.h + + return x, y, w, h +end + +function GridBox:getSlotCenter(slot) + local x, y, w, h = self:getSlotHitbox(slot) + + return x + (w/2), y + (h/2) +end + +function GridBox:getWidgetID(slot) + local widgetID + if self.slots[slot] ~= nil then + widgetID = self.slots[slot].widgetID + else + widgetID = 0 + end + return widgetID +end + +function GridBox:haveWidget(slot) + local id = self:getWidgetID(slot) + return self.widget.list[id] ~= nil +end + +function GridBox:getWidgetSlot(widgetID) + local slot = 0 + for i,v in ipairs(self.slots) do + if (self.slots[i].widgetID == widgetID) then + slot = i + end + end + + return slot +end + +function GridBox:getWidgetAtPoint(x, y) + local x = x or 0 + local y = y or 0 + local widgetID = nil + + for i,v in ipairs(self.slots) do + local xx, yy, ww, hh = self:getSlotHitbox(i) + if (x >= xx) and (y >= yy) and (x < xx + ww) and (y < yy + hh) then + widgetID = v.widgetID + end + end + + return widgetID +end + +function GridBox:update(dt) + self.view.firstSlot = 1 +end + +function GridBox:keyreleased(key, code) + slotID = self:getWidgetSlot(self.widget.selected) + local col, line = self.cursor.x, self.cursor.y + if key == 'left' then + self:moveCol(-1) + end + + if key == 'right' then + self:moveCol(1) + end + + if key == 'up' then + self:moveLine(-1) + end + + if key == 'down' then + self:moveLine(1) + end + + if key == "A" and self.widget.selected <= #self.widget.list then + self.widget.list[self.widget.selected]:action() + end +end + +function GridBox:moveCol(direction) + local orig_x, orig_y = self:getSlotCenter(self.widget.selected) + local distance = self.w -- on met directement à la distance max possible le système + local nearastWidget = 0 + for i,v in ipairs(self.slots) do + local xx, yy = self:getSlotCenter(i) + -- On commence par vérifier si le slot est bien positionné par rapport au + -- widget de base + if utils.math.sign(xx - orig_x) == direction then + if utils.math.pointDistance(orig_x, orig_y, xx, yy) < distance then + distance = utils.math.pointDistance(orig_x, orig_y, xx, yy) + nearestWidget = v.widgetID + end + end + end + + if nearestWidget ~= 0 then + self.widget.selected = nearestWidget + end +end + +function GridBox:moveLine(direction) + local orig_x, orig_y = self:getSlotCenter(self.widget.selected) + local distance = self.h -- on met directement à la distance max possible le système + local nearastWidget = 0 + for i,v in ipairs(self.slots) do + local xx, yy = self:getSlotCenter(i) + -- On commence par vérifier si le slot est bien positionné par rapport au + -- widget de base + if utils.math.sign(yy - orig_y) == direction then + if utils.math.pointDistance(orig_x, orig_y, xx, yy) < distance then + distance = utils.math.pointDistance(orig_x, orig_y, xx, yy) + nearestWidget = v.widgetID + end + end + end + + if nearestWidget ~= 0 then + self.widget.selected = nearestWidget + end +end + +function GridBox:mousemoved(x, y) + local widgetID = self:getWidgetAtPoint(x, y) + + if widgetID ~= nil then + self.widget.selected = widgetID + self:getFocus() + end + + if self.widget.selected < 1 then + self.widget.selected = 1 + end + if self.widget.selected > #self.widget.list then + self.widget.selected = #self.widget.list + end + +end + +function GridBox:mousepressed(x, y, button, isTouch) + local widgetID = self:getWidgetAtPoint(x, y) + + if widgetID ~= nil then + self.widget.selected = widgetID + self:getFocus() + + if #self.widget.list > 0 and self.widget.selected > 1 and self.widget.selected <= #self.widget.list then + self.widget.list[self.widget.selected]:action() + end + end +end + +function GridBox:draw() + + for i,v in ipairs(self.slots) do + if self:haveWidget(i) then + local widgetx = self.x + (v.x * self.widget.w) + local widgety = self.y + (v.y * self.widget.h) + if self.widget.selected == v.widgetID and self:haveFocus() == true then + self.widget.list[v.widgetID]:drawSelected(widgetx, widgety) + else + self.widget.list[v.widgetID]:draw(widgetx, widgety) + end + end + end +end + +function GridBox:drawCursor() + self:updateView() + if (self.widget.selected >= 1 and self.widget.selected <= #self.widget.list) then + local slot = self:getWidgetSlot(self.widget.selected) + local w, h = self:getWidgetSize(slot) + local x = self.slots[slot].x * self.widget.w + local y = self.slots[slot].y * self.widget.h + menuutils.drawCursor(self.x + x, self.y + y, w, h) + end +end + +return GridBox diff --git a/modules/menusystem/init.lua b/modules/menusystem/init.lua new file mode 100644 index 0000000..db5ee4d --- /dev/null +++ b/modules/menusystem/init.lua @@ -0,0 +1,111 @@ +local MenuSystem = Object:extend() + +local cwd = (...):gsub('%.init$', '') .. "." + +MenuSystem.Parent = require(cwd .. "parent") +MenuSystem.ListBox = require(cwd .. "listbox") +MenuSystem.FlowBox = require(cwd .. "flowbox") +MenuSystem.Grid = require(cwd .. "grid") +MenuSystem.TextMenu = require(cwd .. "textmenu") + +MenuSystem.Widget = require(cwd .. "widgets") + + +--local VirtualPad = require "modules.virtualpad" + +function MenuSystem:new() + self.menus = {} + self.focusedMenu = "" +end + +function MenuSystem:reset() + self.menus = {} +end + +function MenuSystem:addMenu(name, menu) + self.menus[name] = menu +end + +function MenuSystem:update(dt) + self:removeDestroyedMenus() + for k,v in pairs(self.menus) do + v:update(dt) + v:updateWidgets(dt) + end + + if self.menus[self.focusedMenu] ~= nil then + -- Only check buttons if the current focused menu is actually active + if self.menus[self.focusedMenu].isActive then + for k,v in pairs(self.keys) do + if self.keys[k].isPressed then + self.menus[self.focusedMenu]:keyreleased(k) + end + end + end + end + +end + +function MenuSystem:setAllMenuVisibility(visibility) + for k,v in pairs(self.menus) do + v.isVisible = visibility + end +end + +function MenuSystem:setAllMenuActivity(activity) + for k,v in pairs(self.menus) do + v.isActive = activity + end +end + +function MenuSystem:removeDestroyedMenus() + -- On retire les entitées marquées comme supprimées + for k,v in pairs(self.menus) do + if (v.isDestroyed == true) then + self.menus[k] = nil + end + end +end + +function MenuSystem:keyreleased(key, code) + -- TODO:depreciated function +end + +function MenuSystem:mousemoved(x, y, dx, dy) + for k,v in pairs(self.menus) do + if v.isActive then + if (x > v.x) and (x < v.x + v.w) and (y > v.y) and (y < v.y + v.h) then + v:mousemoved(x - v.x, y - v.y) + break; + end + end + end +end + +function MenuSystem:mousepressed( x, y, button, istouch ) + for k,v in pairs(self.menus) do + if v.isActive then + if (x > v.x) and (x < v.x + v.w) and (y > v.y) and (y < v.y + v.h) then + v:mousepressed(x - v.x, y - v.y, button, istouch ) + break; + end + end + end +end + +function MenuSystem:draw(dt) -- On dessine les entitées + for k,v in pairs(self.menus) do + if (v.isVisible) then + v:draw(dt) + end + end + + if self.menus[self.focusedMenu] ~= nil then + if (self.menus[self.focusedMenu].isVisible) then + self.menus[self.focusedMenu]:drawCursor() + end + end + +end + +return MenuSystem diff --git a/modules/menusystem/listbox.lua b/modules/menusystem/listbox.lua new file mode 100644 index 0000000..944c465 --- /dev/null +++ b/modules/menusystem/listbox.lua @@ -0,0 +1,110 @@ +local cwd = (...):gsub('%.listbox$', '') .. "." +local Menu = require(cwd .. "parent") + +local menuutils = require(cwd .. "widgets.utils") + +local ListBox = Menu:extend() + +function ListBox:new(menusystem, name, x, y, w, h, slotNumber) + self.view = {} + self.view.slotNumber = slotNumber + self.view.firstSlot = 1 + ListBox.super.new(self, menusystem, name, x, y, w, h) + self.h = slotNumber * self.widget.h -- On fait en sorte que la hauteur + -- soit un multiple du nombre de slot et de leur hauteur +end + +function ListBox:updateWidgetSize() + self.widget.h = math.floor( self.h / self.view.slotNumber ) + self.widget.w = self.w +end + +function ListBox:update(dt) + self:updateView() +end + +function ListBox:updateView() + if self.widget.selected < self.view.firstSlot then + self.view.firstSlot = self.widget.selected + end + if self.widget.selected > self.view.firstSlot + self.view.slotNumber - 1 then + self.view.firstSlot = self.widget.selected - self.view.slotNumber + 1 + end + + if self.view.firstSlot < 1 then + self.view.firstSlot = 1 + end +end + +function ListBox:keyreleased(key, code) + + if key == 'up' then + self:moveCursor(self.widget.selected - 1) + end + + if key == 'down' then + self:moveCursor(self.widget.selected + 1) + end + + if key == "A" then + if (self.widget.selected >= 1 and self.widget.selected <= #self.widget.list) then + self.widget.list[self.widget.selected]:action() + end + end + + if key == "B" then + if (self.widget.cancel >= 1 and self.widget.cancel <= #self.widget.list) then + self.widget.list[self.widget.cancel]:action() + end + end + +end + +function ListBox:mousemoved(x, y) + local widget_selected = self.view.firstSlot + math.floor(y / self.widget.h) + + if widget_selected >= 1 and widget_selected <= #self.widget.list then + self.widget.selected = widget_selected + self:getFocus() + end +end + +function ListBox:mousepressed(x, y, button, isTouch) + local widget_selected = self.view.firstSlot + math.floor(y / self.widget.h) + + if widget_selected >= 1 and widget_selected <= #self.widget.list then + self.widget.selected = widget_selected + self:getFocus() + if #self.widget.list > 0 then + self.widget.list[self.widget.selected]:action() + end + end + +end + +function ListBox:draw() + self:updateView() + local widgety = self.y + for i,v in ipairs(self.widget.list) do + if (i >= self.view.firstSlot) and (i < self.view.firstSlot + self.view.slotNumber) then + v:draw(self.x, widgety, self.w, self.widget.h) + if self.widget.selected == i and self:haveFocus() == true then + v:drawSelected(self.x, widgety, self.w, self.widget.h) + else + v:draw(self.x, widgety, self.w, self.widget.h) + end + widgety = widgety + self.widget.h + end + end +end + +function ListBox:drawCursor() + self:updateView() + if (self.widget.selected >= 1 and self.widget.selected <= #self.widget.list) then + local w, h = self:getWidgetSize() + local y = (self.widget.selected - self.view.firstSlot) * h + menuutils.drawCursor(self.x,self.y + y, w, h) + end +end + +return ListBox diff --git a/modules/menusystem/parent.lua b/modules/menusystem/parent.lua new file mode 100644 index 0000000..a6bb2a5 --- /dev/null +++ b/modules/menusystem/parent.lua @@ -0,0 +1,179 @@ +local Menu = Object:extend() + +function Menu:new(menusystem, name, x, y, w, h) + self.menusystem = menusystem + self.name = name + + self.x = x + self.y = y + self.w = w + self.h = h + + self.widget = {} + self.widget.list = {} + self.widget.selected = 0 + self.widget.selectedPrevious = 0 + self.widget.cancel = 0 + self:updateWidgetSize() + + self.isDestroyed = false + self.isVisible = true + self.isActive = true + + self.sound = {} + self.sound.asset = nil + self.sound.active = false + + self:register() +end + +function Menu:getFocus() + self.menusystem.focusedMenu = self.name +end + +function Menu:haveFocus() + return (self.menusystem.focusedMenu == self.name) +end + +function Menu:register() + self.menusystem:addMenu(self.name, self) +end + +function Menu:setCancelWidget(id) + self.widget.cancel = #self.widget.list +end + +function Menu:updateWidgetSize() + self.widget.h = 0 + self.widget.w = 0 +end + +function Menu:getWidgetSize(id) + return self.widget.w, self.widget.h +end + +function Menu:cancelAction() + if (self.widget.cancel ~= 0) then + self.widget.list[self.widget.cancel]:action() + end +end + +function Menu:update(dt) + -- Cette fonction ne contient rien par défaut +end + +function Menu:clear() + self.widget.list = {} + self.widget.cancel = 0 +end + +function Menu:resize(x,y,w,h) + self.x = x + self.y = y + self.w = w + self.h = h + + self:updateWidgetSize() +end + +function Menu:destroy() + self.destroyed = true +end + +function Menu:draw() + -- nothing here +end + +function Menu:drawCursor() + -- nothing here +end + +function Menu:drawCanvas() + +end + +function Menu:keyreleased(key) + -- Cette fonction ne contient rien par défaut +end + +function Menu:mousemoved(x, y) + -- Cette fonction ne contient rien par défaut +end + +function Menu:mousepressed( x, y, button, istouch ) + -- Cette fonction ne contient rien par défaut +end + +function Menu:addWidget(newwidget) + if #self.widget.list == 0 then + self.widget.selected = 1 + end + table.insert(self.widget.list, newwidget) + self:updateWidgetsID() +end + +function Menu:updateWidgets(dt) + self:removeDestroyedWidgets() + for i,v in ipairs(self.widget.list) do + v.id = i + v:update(dt) + end +end + +function Menu:updateWidgetsID() + for i,v in ipairs(self.widget.list) do + v.id = i + end +end + +function Menu:removeDestroyedWidgets() -- On retire les widgets marquées comme supprimées + for i,v in ipairs(self.widget.list) do + if (v.destroyed == true) then + table.remove(self.widget.list, i) + end + end +end + +function Menu:setCursor(cursorid) + self.widget.selected = cursorid --math.max(1, math.min(cursorid, #self.widget.list)) +end + +function Menu:moveCursor(new_selected) + self:playSelectSound() + if new_selected < 1 then + self.widget.selected = #self.widget.list + new_selected + else + if new_selected > #self.widget.list then + self.widget.selected = new_selected - #self.widget.list + else + self.widget.selected = new_selected + end + end +end + +function Menu:setSound(soundasset) + self.sound.active = true + self.sound.asset = soundasset +end + +function Menu:playSelectSound() + if self.sound.active == true then + love.audio.stop( self.sound.asset ) + self.sound.asset:setVolume(core.options.data.audio.sfx / 100) + love.audio.play( self.sound.asset ) + end +end + +function Menu:resetView() + -- ne sert à rien ici, c'est juste pour éviter des crash +end + +function Menu:updateView() + -- ne sert à rien ici, c'est juste pour éviter des crash +end + +function Menu:moveView() + -- ne sert à rien ici, c'est juste pour éviter des crash +end + +return Menu diff --git a/modules/menusystem/textmenu.lua b/modules/menusystem/textmenu.lua new file mode 100644 index 0000000..6703039 --- /dev/null +++ b/modules/menusystem/textmenu.lua @@ -0,0 +1,221 @@ +local cwd = (...):gsub('%.textmenu$', '') .. "." +local Menu = require(cwd .. "parent") + +local TextMenu = Menu:extend() + +function TextMenu:new(menusystem, name, x, y, font, slots) + self.slots = slots + TextMenu.super.new(self, menusystem, name, x, y, 0, 0) + self.ox = x + self.oy = y + self.font = font + self.align = "left" + + self.begin = 1 + + self:getBoundingBox() +end + +function TextMenu:getBoundingBox() + self:setWidthAuto() + self.widget.h = self.font:getHeight() + self.h = self.widget.h * self.slots + + if self.align == "right" then + self.x = self.ox - self.w + elseif self.align == "center" then + self.x = self.ox - self.w / 2 + else + self.x = self.ox + end + + self.y = self.oy +end + +function TextMenu:centerText() + self:setAlign("center") +end + +function TextMenu:setAlign(align) + self.align = align + self.font:setAlign("center") +end + +function TextMenu:update(dt) + self:getBoundingBox() + if self.widget.selected ~= 0 then + if self.widget.selected < self.begin then + self.begin = self.widget.selected + end + if self.widget.selected > self.begin + self.slots - 1 then + self.begin = self.widget.selected - self.slots + 1 + end + end + + if self.begin < 1 then + self.begin = 1 + end +end + +function TextMenu:setWidthAuto() + local width = self.w + + for i,v in ipairs(self.widget.list) do + local stringWidth = self.font:getWidth(v.beginlabel .. v.label .. v.endlabel) + width = math.max(stringWidth, width) + end + if width ~= self.w then + self.canvas.needRedraw = true + end + self.w = width +end + +function TextMenu:getWidth() + self:setWidthAuto() + return self.w +end + +function TextMenu:getHeight() + return self.h +end + +function TextMenu:keyreleased(key, code) + + if key == 'up' then + self:moveCursor(self.widget.selected - 1) + end + + if key == 'down' then + self:moveCursor(self.widget.selected + 1) + end + + if key == "A" then + if (self.widget.selected > 0) and (self.widget.selected <= #self.widget.list) then + self.widget.list[self.widget.selected]:action() + end + end + + if key == "B" then + self:cancelAction() + end + +end + +function TextMenu:mousemoved(x, y) + local selectedPrevous = self.widget.selected + self.widget.selected = self.begin + math.floor(y / self.widget.h) + if self.widget.selected < 1 then + self.widget.selected = 1 + end + if self.widget.selected > #self.widget.list then + self.widget.selected = #self.widget.list + end + + if self.widget.selected ~= selectedPrevious then + self.canvas.needRedraw = true + end +end + +function TextMenu:mousepressed(x, y, button, isTouch) + self.widget.selected = self.begin + math.floor(y / self.widget.h) + if self.widget.selected < 1 then + self.widget.selected = 1 + end + if self.widget.selected > #self.widget.list then + self.widget.selected = #self.widget.list + end + if #self.widget.list > 0 then + self.widget.list[self.widget.selected]:action() + end + + if self.widget.selected ~= selectedPrevious then + self.canvas.needRedraw = true + end +end + +function TextMenu:drawCanvas() + print("redraw menu") + + self.canvas.texture = love.graphics.newCanvas(self.w, self.h) + love.graphics.setCanvas( self.canvas.texture ) + + local ox, x + + local widgety = 0 + local ox = self.w / 2 + local x = 0 + + self.font:set() + for i, v in ipairs(self.widget.list) do + if (i >= self.begin) and (i < self.begin + self.slots) then + self:drawWidget(i, widgety) + + widgety = widgety + self.widget.h + end + end + utils.draw.resetColor() + + love.graphics.setCanvas( ) +end + +function TextMenu:drawWidget(widgetID, y) + local widget = self.widget.list[widgetID] + print(widget) + if widget.canvas.needRedraw == true then + self:drawWidgetCanvas(widget) + widget.canvas.needRedraw = false + end + + if self.widget.selected == widgetID and self.focus == true then + love.graphics.setColor(1, 1, 0, 1) + else + love.graphics.setColor(1, 1, 1, 1) + end + + love.graphics.draw(widget.canvas.texture, 0, y) +end + +function TextMenu:drawWidgetCanvas(widget) + widget.canvas.texture = love.graphics.newCanvas(self.w, self.widget.h) + + love.graphics.setCanvas( widget.canvas.texture ) + + self.font:draw(widget.label, math.floor(self.w / 2), 0, -1, self.align) + self.font:draw(widget.beginlabel, math.floor(0), 0, -1, "left") + self.font:draw(widget.endlabel, math.floor(self.w), 0, -1, "right") + + love.graphics.setCanvas( self.canvas.texture ) +end + +function TextMenu:resetView() + self.begin = 1 + self.canvas.needRedraw = true +end + +function Menu:moveView(begin, absolute) + --local absolute = absolute or true + self.widget.selected = 0 + + if (absolute) then + self.begin = begin + else + self.begin = self.begin + begin + end + -- ne sert à rien ici, c'est juste pour éviter des crash + + self.canvas.needRedraw = true +end + +function Menu:getView() + return self.begin +end + +function Menu:isViewAtBeggining() + return (self.begin <= 1) +end + +function Menu:isViewAtEnd() + return ((self.begin + self.slots) > (#self.widget.list)) +end + +return TextMenu diff --git a/modules/menusystem/widgets/init.lua b/modules/menusystem/widgets/init.lua new file mode 100644 index 0000000..03006df --- /dev/null +++ b/modules/menusystem/widgets/init.lua @@ -0,0 +1,98 @@ +local Widget = {} + +BaseWidget = Object:extend() +TextWidget = BaseWidget:extend() + +function BaseWidget:new(menu) + self.menu = menu + + self.destroyed = false + self.selectable = false + self.selection_margin = 0 + self.margin = 2 + + self:register() + + self.canvas = {} + self.canvas.texture = nil + self.canvas.needRedraw = true +end + +function BaseWidget:register() + self.menu:addWidget(self) +end + +function BaseWidget:redrawCanvas() + self.width, self.height = self.menu:getWidgetSize(self.id) + + self.canvas.texture = love.graphics.newCanvas(self.width, self.height) + love.graphics.setCanvas( self.canvas.texture ) + + self:drawCanvas() + self.canvas.needRedraw = false + + love.graphics.setCanvas( ) +end + +function BaseWidget:drawCanvas() + self.r = love.math.random(128)/256 + self.g = love.math.random(128)/256 + self.b = love.math.random(128)/256 + + love.graphics.setColor(self.r, self.g, self.b, 70) + love.graphics.rectangle("fill", 0, 0, self.width, self.height) + love.graphics.setColor(self.r, self.g, self.b) + love.graphics.rectangle("line", 0, 0, self.width, self.height) + utils.graphics.resetColor() +end + +function BaseWidget:selectAction() + -- Do nothing +end + +function BaseWidget:draw(x, y) + if self.canvas.texture ~= nil then + utils.graphics.resetColor() + love.graphics.draw(self.canvas.texture, x, y) + end +end + +function BaseWidget:drawSelected(x,y,w,h) + self:draw(x, y, w, h) +end + +function BaseWidget:update(dt) + if (self.canvas.needRedraw) then + self:redrawCanvas() + end + -- N/A +end + +function BaseWidget:action() + --self:destroy() +end + +function BaseWidget:destroy() + self.destroyed = true +end + +-- Simple text widget + +function TextWidget:new(menu, font, label) + TextWidget.super.new(self, menu) + self.font = font + self.label = label +end + +function TextWidget:drawCanvas() + local w, h + w = math.floor(self.width / 2) + h = math.floor(self.height / 2) - (self.font:getHeight() / 2) + self.font:draw(self.label, w, h, -1, "center") +end + + +Widget.Base = BaseWidget +Widget.Text = TextWidget + +return Widget diff --git a/modules/menusystem/widgets/utils.lua b/modules/menusystem/widgets/utils.lua new file mode 100644 index 0000000..ab4a48c --- /dev/null +++ b/modules/menusystem/widgets/utils.lua @@ -0,0 +1,34 @@ +local menuUtils = {} + +function menuUtils.drawCursor(x, y, w, h) + love.graphics.setColor(0,0,0) + + love.graphics.rectangle("fill", x, y, 4, 8) + love.graphics.rectangle("fill", x, y, 8, 4) + + love.graphics.rectangle("fill", x + w, y, -4, 8) + love.graphics.rectangle("fill", x + w, y, -8, 4) + + love.graphics.rectangle("fill", x, y + h, 4, -8) + love.graphics.rectangle("fill", x, y + h, 8, -4) + + love.graphics.rectangle("fill", x + w, y + h, -4, -8) + love.graphics.rectangle("fill", x + w, y + h, -8, -4) + + love.graphics.setColor(255,255,255) + + love.graphics.rectangle("fill", x + 1, y + 1, 2, 6) + love.graphics.rectangle("fill", x + 1, y + 1, 6, 2) + + love.graphics.rectangle("fill", x + w - 1, y + 1, -2, 6) + love.graphics.rectangle("fill", x + w - 1, y + 1, -6, 2) + + love.graphics.rectangle("fill", x + 1, y + h - 1, 2, -6) + love.graphics.rectangle("fill", x + 1, y + h - 1, 6, -2) + + love.graphics.rectangle("fill", x + w - 1, y + h - 1, -2, -6) + love.graphics.rectangle("fill", x + w - 1, y + h - 1, -6, -2) + +end + +return menuUtils diff --git a/modules/scenes.lua b/modules/scenes.lua new file mode 100644 index 0000000..c87f29e --- /dev/null +++ b/modules/scenes.lua @@ -0,0 +1,67 @@ +-- scenes.lua :: the scene object, that aim to give a better control to the engine +-- to the different scene, without having to call too much boilerplate + +--[[ + 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 Scene = Object:extend() +local Assets = require "core.modules.assets" +local MenuSystem = require "core.modules.menusystem" + +function Scene:new() + self.mouse = {} + self.mouse.x, self.mouse.y = core.screen:getMousePosition() + + self.assets = Assets() + self.menusystem = MenuSystem() + self.keys = core.input:getKeyList() +end + +function Scene:register() + core.scenemanager.currentScene = self +end + +function Scene:update(dt) + -- Empty function, is just here to avoid crash +end + +function Scene:mousemoved(x, y, dx, dy) + -- Empty function, is just here to avoid crash +end + +function Scene:mousepressed( x, y, button, istouch ) + -- Empty function, is just here to avoid crash +end + +function Scene:draw() + +end + +function Scene:clear() + +end + +function Scene:flushKeys() + core.input:flushKeys() + self.keys = core.input.keys +end + +return Scene diff --git a/options.lua b/options.lua new file mode 100644 index 0000000..57111c9 --- /dev/null +++ b/options.lua @@ -0,0 +1,96 @@ +-- core/options.lua :: The options loading/saving system. Is used by the other +-- modules to save their settings. + +--[[ + 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 OptionsManager = Object:extend() + +local binser = require "libs.binser" + +function OptionsManager:new() + -- We begin by creating an empty data table before reading the data. + self.data = {} + self:read() +end + +function OptionsManager:reset() + -- Reset the option to the game defaults. + self.data.video = {} + self.data.video.crtfilter = false + self.data.video.resolution = 1 + self.data.video.border = true + self.data.video.vsync = false + self.data.video.fullscreen = false + + -- We load the default files + self.data.input = require "datas.inputs" + + -- TODO: have a way to auto-load a language according to the OS ? + self.data.language = "en" + + self.data.audio = {} + self.data.audio.music = 100 + self.data.audio.sfx = 100 +end + +function OptionsManager:getFile(absolute) + local dir = "" + if absolute then + dir = love.filesystem.getSaveDirectory() .. "/" + if not utils.filesystem.exists(dir) then + love.filesystem.createDirectory( "" ) + end + end + + local filepath = dir .. "options.data" + + return filepath +end + +function OptionsManager:write() + local data = self:getData() + + filepath = self:getFile(true) + binser.writeFile(filepath, data) +end + +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:setData(loadedDatas[1]) + else + self:reset() + print("no data file found, reseting data") + end +end + +function OptionsManager:getData(data) + return self.data +end + +function OptionsManager:setData(data) + self.data = data +end + +return OptionsManager diff --git a/scenemanager.lua b/scenemanager.lua new file mode 100644 index 0000000..5d403ca --- /dev/null +++ b/scenemanager.lua @@ -0,0 +1,69 @@ +-- scene.lua :: a basic scene management system, that work by sending the different +-- core functions to the scene, normally without the scene itself having to manage +-- them. + +--[[ + 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 SceneManager = Object:extend() + +function SceneManager:new(controller) + self.controller = controller + self.currentScene = nil +end + +function SceneManager:update(dt) + if (self.currentScene ~= nil) then + local keys = self.controller.input.keys + self.currentScene.keys = keys + self.currentScene.menusystem.keys = keys + self.currentScene.assets:update(dt) + self.currentScene.menusystem:update(dt) + self.currentScene:update(dt) + end +end + +function SceneManager:mousemoved(x, y, dx, dy) + self.currentScene.mouse.x, + self.currentScene.mouse.y = x, y + self.currentScene:mousemoved(x, y, dx, dy) + self.currentScene.menusystem:mousemoved(x, y, dx, dy) +end + +function SceneManager:mousepressed( x, y, button, istouch ) + self.currentScene:mousepressed( x, y, button, istouch ) + self.currentScene.menusystem:mousepressed( x, y, button, istouch ) +end + +function SceneManager:clearScene() + self.currentScene = nil +end + +function SceneManager:draw() + self.controller.screen:apply() + if (self.currentScene ~= nil) then + self.currentScene:draw(dt) + self.currentScene.menusystem:draw() + end + self.controller.screen:cease() +end + +return SceneManager diff --git a/screen.lua b/screen.lua new file mode 100644 index 0000000..4e962a8 --- /dev/null +++ b/screen.lua @@ -0,0 +1,74 @@ +-- core/screen.lua :: Basic screen manager. Use CScreen as a backend, and works +-- as an abstraction layer around CScreen. + +--[[ + 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 ScreenManager = Object:extend() + +local CScreen = require "libs.cscreen" + +function ScreenManager:new(controller) + self.controller = controller + self.data = self.controller.options.data.video + self.width, self.height = love.graphics.getDimensions() + self:applySettings() + CScreen.init(self.width, self.height, true) + CScreen.setColor(0, 0, 0, 1) + + love.graphics.setDefaultFilter( "nearest", "nearest", 1 ) +end + +function ScreenManager:applySettings() + self.data = self.controller.options.data.video + + local flags = {} + flags.vsync = self.data.vsync + flags.borderless = (self.data.border == false) + + love.window.setMode(self.width * self.data.resolution, self.height * self.data.resolution, flags) + love.window.setFullscreen( self.data.fullscreen ) + + local width, height = love.window.getMode() + CScreen.update(width, height) +end + +function ScreenManager:project(x, y) + return CScreen.project(x, y) +end + +function ScreenManager:getMousePosition() + return CScreen.project(love.mouse.getX(), love.mouse.getY()) +end + +function ScreenManager:getDimensions() + return self.width, self.height +end + +function ScreenManager:apply() + CScreen.apply() +end + +function ScreenManager:cease() + CScreen.cease() +end + +return ScreenManager