diff --git a/README.md b/README.md index f5f5e57..e0e65ca 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,12 @@ A dungeon crawler game made with LoveDOS for the DOSember Game Jam https://itch. ### Sprites - [B&W Ornamental Cursor by qubodup](https://opengameart.org/content/bw-ornamental-cursor-19x19) -- [Microchip texture by ZeptoBARS](https://opengameart.org/content/microchip-texture) - [Eris in colour by Stephen](https://openclipart.org/detail/329762/eris-in-colour) +### Textures +- [Microchip texture by ZeptoBARS](https://opengameart.org/content/microchip-texture) +- [Assorted textures by Makkon](https://www.slipseer.com/index.php?resources/makkon-textures.28/) + ### Sound effects - [Click.wav by frosty ham](https://opengameart.org/content/click-0) - [Sci-Fi Sound Effects Library by Little Robot Sound Factory](https://opengameart.org/content/sci-fi-sound-effects-library) diff --git a/assets/imgs/concb03.png b/assets/imgs/concb03.png new file mode 100644 index 0000000..6f6dd07 Binary files /dev/null and b/assets/imgs/concb03.png differ diff --git a/main.lua b/main.lua index 673b6fd..5e03888 100644 --- a/main.lua +++ b/main.lua @@ -24,8 +24,8 @@ Debug = false local function parse_args(args) for _, v in pairs(args) do - -- Enable debug mode. - if v == '-debug' then Debug = true end + if v == '-debug' then Debug = true end + if v == '-skip-intro' then Current_state = 2 end end end @@ -133,7 +133,7 @@ end function love.mousepressed(x, y, btn) -- Send events to the active game state if there is no fade active. if Fade.done then - game_states[Current_state]:mousemoved(x, y, btn) + game_states[Current_state]:mousepressed(x, y, btn) end end diff --git a/src/graphics/drawable.lua b/src/graphics/drawable.lua index 71f4c02..86c989b 100644 --- a/src/graphics/drawable.lua +++ b/src/graphics/drawable.lua @@ -3,21 +3,19 @@ ------------------------------------------------------------------------------ local make_class = require 'src.utils.classes' -local Asset = require 'src.utils.asset' ------------------------------------------------------------------------------ -- Class definitions ------------------------------------------------------------------------------ -local Drawable = make_class(Asset) +local Drawable = make_class() ------------------------------------------------------------------------------ -- Class methods ------------------------------------------------------------------------------ -function Drawable:_init(file_name) - Asset._init(self, file_name) +function Drawable:_init() end diff --git a/src/graphics/sprite.lua b/src/graphics/sprite.lua index 914eda8..9c00a73 100644 --- a/src/graphics/sprite.lua +++ b/src/graphics/sprite.lua @@ -4,13 +4,14 @@ local love = require 'love' local make_class = require 'src.utils.classes' +local Asset = require 'src.utils.asset' local Drawable = require 'src.graphics.drawable' ------------------------------------------------------------------------------ -- Class definitions ------------------------------------------------------------------------------ -local Sprite = make_class(Drawable) +local Sprite = make_class(Drawable, Asset) ------------------------------------------------------------------------------ @@ -18,14 +19,26 @@ local Sprite = make_class(Drawable) ------------------------------------------------------------------------------ function Sprite:_init(file_name, x, y) - Drawable._init(self, file_name) + Asset._init(self, file_name) self.x = (x ~= nil and x) or 0 self.y = (y ~= nil and y) or 0 end function Sprite:load() - self.sprite = love.graphics.newImage(self.file_name) + if not self:is_loaded() then + self.sprite = love.graphics.newImage(self.file_name) + end +end + + +function Sprite:unload() + self.sprite = nil +end + + +function Sprite:is_loaded() + return self.sprite ~= nil end @@ -36,11 +49,6 @@ function Sprite:draw() end -function Sprite:unload() - self.sprite = nil -end - - ------------------------------------------------------------------------------ -- Module return ------------------------------------------------------------------------------ diff --git a/src/gstates/menu.lua b/src/gstates/menu.lua index df90fb0..dd74525 100644 --- a/src/gstates/menu.lua +++ b/src/gstates/menu.lua @@ -9,7 +9,9 @@ local GameState = require 'src.gstates.gstate' local Fader = require 'src.graphics.fader' local Sprite = require 'src.graphics.sprite' local Cursor = require 'src.ui.cursor' +local Font = require 'src.ui.font' local SoundEffect = require 'src.sound.sfx' +local ButtonGroup = require 'src.ui.btngrp' ------------------------------------------------------------------------------ @@ -25,11 +27,20 @@ local MainMenu = make_class(GameState) function MainMenu:_init(name, index) GameState._init(self, name, index) - self.skip = false - self.fade = Fader() + self.next_state = self.index + self.fade = Fader() -- Create sprites and buttons. - self.background = Sprite('imgs/cpu.png') + self.background = Sprite('imgs/concb03.png') + self.btn_font = Font('fonts/Concrete.ttf') + + -- Create UI elements. + self.btns = ButtonGroup(15, 15) + self.btns:add_text_button('New Game', self.btn_font) + self.btns:add_text_button('Load Game', self.btn_font) + self.btns:add_text_button('Options', self.btn_font) + self.btns:add_text_button('Help & Story', self.btn_font) + self.btns:add_text_button('Quit', self.btn_font, function() self.next_state = -1 end) -- Create a mouse cursor object at the current mouse position. local mx, my = love.mouse.getPosition() @@ -39,9 +50,11 @@ function MainMenu:_init(name, index) self.bgm = SoundEffect('bgm/eskisky.wav') -- Register all assets. + assets:register(self.name, self.btn_font) assets:register(self.name, self.background) assets:register(self.name, self.cursor) assets:register(self.name, self.bgm) + assets:register(self.name, self.btns) end @@ -51,18 +64,18 @@ function MainMenu:update(dt) -- Update the fader. self.fade:update(dt) - - -- Move on to the next game state if the user skipped the intro or all stages are complete. - if not self.skip then return self.index else return -1 end else assets:update(self.name) end + + return self.next_state end function MainMenu:draw() if assets:done(self.name) then self.background:draw() + self.btns:draw() self.cursor:draw() self.fade:draw() else @@ -71,19 +84,26 @@ function MainMenu:draw() end -function MainMenu:keypressed(key) - -- Skip the intro on any key press. - if key == "escape" then - self.skip = true - end +function MainMenu:keypressed(_) end function MainMenu:mousemoved(x, y, dx, dy) + self.btns:mousemoved(x, y, dx, dy) self.cursor:mousemoved(x, y, dx, dy) end +function MainMenu:mousepressed(x, y, btn) + self.btns:mousepressed(x, y, btn) +end + + +function MainMenu:mousereleased(x, y, btn) + self.btns:mousereleased(x, y, btn) +end + + ------------------------------------------------------------------------------ -- Module return ------------------------------------------------------------------------------ diff --git a/src/ui/btngrp.lua b/src/ui/btngrp.lua new file mode 100644 index 0000000..9f942ed --- /dev/null +++ b/src/ui/btngrp.lua @@ -0,0 +1,100 @@ +------------------------------------------------------------------------------ +-- Imports +------------------------------------------------------------------------------ + +local make_class = require 'src.utils.classes' +local Asset = require 'src.utils.asset' +local Drawable = require 'src.graphics.drawable' +local TextButton = require 'src.ui.textbtn' + + +------------------------------------------------------------------------------ +-- Class definitions +------------------------------------------------------------------------------ + +local ButtonGroup = make_class(Drawable, Asset) + + +------------------------------------------------------------------------------ +-- Class methods +------------------------------------------------------------------------------ + +function ButtonGroup:_init(x, y, spacing) + self.x = x ~= nil and x or 0 + self.y = y ~= nil and y or 0 + self.s = spacing ~= nil and spacing or 10 + self.btns = {} +end + + +function ButtonGroup:add_text_button(text, font, callback, base_col, sel_color, press_col) + table.insert(self.btns, TextButton(text, font, 0, 0, callback, base_col, sel_color, press_col)) +end + + +function ButtonGroup:load() + local y = self.y + + -- Load required assets if needed and then compute the coordinates of each button. + for i, v in pairs(self.btns) do + if v.is_a[Asset] then + v:load() + end + + y = y + ((i > 1 and (self.btns[i - 1].h + self.s)) or 0) + + v.x = self.x + v.y = y + end +end + + +function ButtonGroup:unload() + for _, v in pairs(self.btns) do + if v.is_a[Asset] then + v:unload() + end + end +end + + +function ButtonGroup:update(dt) + for _, v in pairs(self.btns) do + v:update(dt) + end +end + + +function ButtonGroup:draw() + for _, v in pairs(self.btns) do + v:draw() + end +end + + +function ButtonGroup:mousemoved(x, y, dx, dy) + for _, v in pairs(self.btns) do + v:mousemoved(x, y, dx, dy) + end +end + + +function ButtonGroup:mousepressed(x, y, btn) + for _, v in pairs(self.btns) do + v:mousepressed(x, y, btn) + end +end + + +function ButtonGroup:mousereleased(x, y, btn) + for _, v in pairs(self.btns) do + v:mousereleased(x, y, btn) + end +end + + +------------------------------------------------------------------------------ +-- Module return +------------------------------------------------------------------------------ + +return ButtonGroup diff --git a/src/ui/button.lua b/src/ui/button.lua new file mode 100644 index 0000000..16a4f53 --- /dev/null +++ b/src/ui/button.lua @@ -0,0 +1,89 @@ +------------------------------------------------------------------------------ +-- Imports +------------------------------------------------------------------------------ + +local make_class = require 'src.utils.classes' +local collisions = require 'src.utils.colls' +local Asset = require 'src.utils.asset' +local Drawable = require 'src.graphics.drawable' + + +------------------------------------------------------------------------------ +-- Class definitions +------------------------------------------------------------------------------ + +local Button = make_class(Drawable, Asset) + + +------------------------------------------------------------------------------ +-- Class methods +------------------------------------------------------------------------------ + +function Button:_init(x, y, callback) + self.x = x + self.y = y + self.w = nil + self.h = nil + self.selected = false + self.pressed = false + self.was_pressed = false + self.callback = callback +end + + +function Button:set_dimensions() + self.w = 0 + self.h = 0 +end + + +function Button:mousemoved(x, y) + -- Select if the pointer is inside the button's bounding rect. + self.selected = collisions.point_in_square(x, y, self.x, self.y, self.w, self.h) + + -- If the button was pressed then check if the pointer is no longer inside the button. + if self.pressed then + -- If it's not, then mark the button as "was pressed". + self.pressed = collisions.point_in_square(x, y, self.x, self.y, self.w, self.h) + self.was_pressed = true + end + + -- If the button was pressed and the pointer is inside the button right now. + if self.was_pressed and collisions.point_in_square(x, y, self.x, self.y, self.w, self.h) then + -- Then mark the button as pressed again. + self.pressed = true + self.was_pressed = false + end +end + + +function Button:mousepressed(x, y, btn) + -- If the pointer was inside the button when it was pressed then mark the button as pressed. + if btn == 0 then + self.pressed = collisions.point_in_square(x, y, self.x, self.y, self.w, self.h) + end +end + + +function Button:mousereleased(_, _, btn) + if btn == 0 then + if self.pressed then + -- If the button was pressed then execute the callback if any and unmark as pressed. + if self.callback ~= nil then + self.callback() + end + + self.pressed = false + end + + -- The button no longer was pressed. + self.was_pressed = false + end +end + + +------------------------------------------------------------------------------ +-- Module return +------------------------------------------------------------------------------ + +return Button diff --git a/src/ui/font.lua b/src/ui/font.lua index bc2d95a..1472405 100644 --- a/src/ui/font.lua +++ b/src/ui/font.lua @@ -27,7 +27,9 @@ end function Font:load() - self.f = love.graphics.newFont(self.file_name, self.size) + if not self:is_loaded() then + self.f = love.graphics.newFont(self.file_name, self.size) + end end @@ -36,6 +38,11 @@ function Font:unload() end +function Font:is_loaded() + return self.f ~= nil +end + + function Font:set() if self.f ~= nil then love.graphics.setFont(self.f) end end @@ -46,6 +53,16 @@ function Font:unset() end +function Font:get_width(text) + if self.f ~= nil then return self.f:getWidth(text) else return nil end +end + + +function Font:get_height(text) + if self.f ~= nil then return self.f:getHeight(text) else return nil end +end + + ------------------------------------------------------------------------------ -- Module return ------------------------------------------------------------------------------ diff --git a/src/ui/textbtn.lua b/src/ui/textbtn.lua new file mode 100644 index 0000000..6019efa --- /dev/null +++ b/src/ui/textbtn.lua @@ -0,0 +1,73 @@ +------------------------------------------------------------------------------ +-- Imports +------------------------------------------------------------------------------ + +local love = require 'love' +local make_class = require 'src.utils.classes' +local Button = require 'src.ui.button' +local Color = require 'src.utils.color' + + +------------------------------------------------------------------------------ +-- Class definitions +------------------------------------------------------------------------------ + +local TextButton = make_class(Button) + + +------------------------------------------------------------------------------ +-- Class methods +------------------------------------------------------------------------------ + +function TextButton:_init(text, font, x, y, callback, base_col, sel_color, press_col) + Button._init(self, x, y, callback) + self.font = font + self.text = text + self.base_col = base_col ~= nil and base_col or Color(255, 255, 255) + self.sel_color = sel_color ~= nil and sel_color or Color(215, 0, 0) + self.press_col = press_col ~= nil and press_col or Color(99, 99, 139) +end + + +function TextButton:load() + if not self.font:is_loaded() then + self.font:load() + end + + -- If the label's size has not been computed then do it. + if self.w == nil or self.h == nil then self:set_dimensions() end +end + + +function TextButton:unload() + self.font:unload() +end + + +function TextButton:is_loaded() + return self.font:is_loaded() and self.w ~= nil and self.h ~= nil +end + + +function TextButton:set_dimensions() + self.w = self.font:get_width(self.text) + self.h = self.font:get_height(self.text) +end + + +function TextButton:draw() + local color = (self.pressed and self.press_col) or ((self.selected and self.sel_color) or self.base_col) + + love.graphics.setColor(color.r, color.g, color.b) + self.font:set() + love.graphics.print(self.text, self.x, self.y) + self.font:unset() + love.graphics.setColor() +end + + +------------------------------------------------------------------------------ +-- Module return +------------------------------------------------------------------------------ + +return TextButton diff --git a/src/utils/asset.lua b/src/utils/asset.lua index cfa74c2..5792bf6 100644 --- a/src/utils/asset.lua +++ b/src/utils/asset.lua @@ -31,6 +31,11 @@ function Asset:unload() end +function Asset:is_loaded() + return false +end + + ------------------------------------------------------------------------------ -- Module return ------------------------------------------------------------------------------ diff --git a/src/utils/asstmngr.lua b/src/utils/asstmngr.lua index b53884a..4e7ec3e 100644 --- a/src/utils/asstmngr.lua +++ b/src/utils/asstmngr.lua @@ -42,7 +42,7 @@ function AssetManager:_init() Drawable._init(self) self.assets = {} self.bckg = Sprite('imgs/floppy.png') - self.font = Font('fonts/Concrete.ttf', 40) + self.font = Font('fonts/BBrick.ttf', 40) self.co = nil self.bckg:load() diff --git a/src/utils/classes.lua b/src/utils/classes.lua index 760e2c6..f302812 100644 --- a/src/utils/classes.lua +++ b/src/utils/classes.lua @@ -2,15 +2,14 @@ -- Methods ------------------------------------------------------------------------------ --- Single inheritance version of the sample code from --- http://lua-users.org/wiki/ObjectOrientationTutorial -local function make_class(BaseClass) +-- Code from http://lua-users.org/wiki/ObjectOrientationTutorial +local function make_class(...) -- "cls" is the new class - local cls = {} + local cls, bases = {}, {...} -- copy base class contents into the new class - if BaseClass ~= nil then - for k, v in pairs(BaseClass) do + for _, base in ipairs(bases) do + for k, v in pairs(base) do cls[k] = v end end @@ -19,10 +18,11 @@ local function make_class(BaseClass) -- so you can do an "instance of" check using my_instance.is_a[MyClass] cls.__index, cls.is_a = cls, {[cls] = true} - if BaseClass ~= nil then - for k, _ in pairs(BaseClass.is_a) do - cls.is_a[k] = true + for _, base in ipairs(bases) do + for c in pairs(base.is_a) do + cls.is_a[c] = true end + cls.is_a[base] = true end -- the class's __call metamethod diff --git a/src/utils/colls.lua b/src/utils/colls.lua new file mode 100644 index 0000000..0c61605 --- /dev/null +++ b/src/utils/colls.lua @@ -0,0 +1,17 @@ +------------------------------------------------------------------------------ +-- Module definitions +------------------------------------------------------------------------------ + +local collisions = { + -- Default setting values. + point_in_square = function(x, y, ox, oy, w, h) + return x >= ox and x <= ox + w and y >= oy and y <= oy + h + end, +} + + +------------------------------------------------------------------------------ +-- Module return +------------------------------------------------------------------------------ + +return collisions diff --git a/src/utils/color.lua b/src/utils/color.lua new file mode 100644 index 0000000..9843f30 --- /dev/null +++ b/src/utils/color.lua @@ -0,0 +1,30 @@ +------------------------------------------------------------------------------ +-- Imports +------------------------------------------------------------------------------ + +local make_class = require 'src.utils.classes' + + +------------------------------------------------------------------------------ +-- Class definitions +------------------------------------------------------------------------------ + +local Color = make_class() + + +------------------------------------------------------------------------------ +-- Class methods +------------------------------------------------------------------------------ + +function Color:_init(r, g, b) + self.r = r ~= nil and r or 255 + self.g = g ~= nil and g or 255 + self.b = b ~= nil and b or 255 +end + + +------------------------------------------------------------------------------ +-- Module return +------------------------------------------------------------------------------ + +return Color