Compare commits

...

13 Commits

39 changed files with 2053 additions and 108 deletions

2
.gitignore vendored
View File

@@ -111,4 +111,4 @@ flycheck_*.el
# Swap Files #
.*.kate-swp
.swp.*
.kateproject*

View File

@@ -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)

BIN
assets/imgs/concb03.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
assets/imgs/floppy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

BIN
assets/imgs/tchcmp1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

97
licenses/OFL.TXT Normal file
View File

@@ -0,0 +1,97 @@
Copyright (c) <dates>, <Copyright Holder> (<URL|email>),
with Reserved Font Name <Reserved Font Name>.
Copyright (c) <dates>, <additional Copyright Holder> (<URL|email>),
with Reserved Font Name <additional Reserved Font Name>.
Copyright (c) <dates>, <additional Copyright Holder> (<URL|email>).
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -2,10 +2,10 @@
-- Imports
------------------------------------------------------------------------------
local love = require 'love'
local game_states = require 'src.states'
local settings = require 'src.utils.settings'
local Fader = require 'src.graphics.fader'
local love = require 'love'
local game_states = require 'src.states'
local settings = require 'src.utils.settings'
local Fader = require 'src.graphics.fader'
------------------------------------------------------------------------------
@@ -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
@@ -74,7 +74,6 @@ function love.update(dt)
if Change_state and Fade.done then
game_states[Current_state]:unload(dt)
Current_state = new_state
game_states[Current_state]:load()
Change_state = false
Fade:fade_in()
end
@@ -88,13 +87,14 @@ function love.draw()
Fade:draw()
if Debug then
love.graphics.setColor(0, 0, 0)
love.graphics.rectangle('fill', 0, 0, 140, 50)
love.graphics.setColor()
-- love.graphics.setColor(0, 0, 0)
-- love.graphics.rectangle('fill', 0, 0, 140, 50)
love.graphics.setColor(255, 250, 0)
love.graphics.print(string.format('OS: %s', love.system.getOS()), 5, 5)
love.graphics.print(string.format('Love version: %s', love.getVersion()), 5, 15)
love.graphics.print(string.format('Memory usage: %s KiB', love.system.getMemUsage()), 5, 25)
love.graphics.print(string.format('FPS: %.2f', 1.0 / love.timer.getAverageDelta()), 5, 35)
love.graphics.setColor()
end
end
@@ -134,7 +134,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

View File

@@ -3,13 +3,13 @@
------------------------------------------------------------------------------
local make_class = require 'src.utils.classes'
local Asset = require 'src.utils.asset'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
local Drawable = make_class(Asset)
---@class Drawable
local Drawable = make_class()
------------------------------------------------------------------------------

View File

@@ -4,28 +4,41 @@
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)
------------------------------------------------------------------------------
-- Class methods
------------------------------------------------------------------------------
function Sprite:_init(sprite_name, x, y)
self.sprite_name = string.format('assets/%s', sprite_name)
function Sprite:_init(file_name, x, y)
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.sprite_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
------------------------------------------------------------------------------

View File

@@ -3,8 +3,10 @@
------------------------------------------------------------------------------
local make_class = require 'src.utils.classes'
local assets = require 'src.utils.asstmngr'
local Drawable = require 'src.graphics.drawable'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
@@ -18,13 +20,23 @@ local GameState = make_class(Drawable)
function GameState:_init(name, index)
Drawable._init(self)
self.name = name
self.index = index
self.name = name
self.index = index
self.next_state = self.index
end
function GameState:update(_)
return self.index
return self.next_state
end
function GameState:load()
end
function GameState:unload()
assets:unload(self.name)
end

View File

@@ -2,92 +2,184 @@
-- Imports
------------------------------------------------------------------------------
local love = require 'love'
local make_class = require 'src.utils.classes'
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 SoundEffect = require 'src.sound.sfx'
local love = require 'love'
local assets = require 'src.utils.asstmngr'
local sound_manager = require 'src.sound.sndmngr'
local dialog_manager = require 'src.ui.dlgmngr'
local make_class = require 'src.utils.classes'
local constants = require 'src.gstates.menus.const'
local GameState = require 'src.gstates.gstate'
local MainMenu = require 'src.gstates.menus.mainmenu'
local OptionsMenu = require 'src.gstates.menus.options'
local Fader = require 'src.graphics.fader'
local Cursor = require 'src.ui.cursor'
local Font = require 'src.ui.font'
local SoundEffect = require 'src.sound.sfx'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
local MainMenu = make_class(GameState)
local Menu = make_class(GameState)
------------------------------------------------------------------------------
-- Class methods
-- Class methods
------------------------------------------------------------------------------
function MainMenu:_init(name, index)
function Menu:_init(name, index)
GameState._init(self, name, index)
self.skip = false
self.fade = Fader()
self.fade = Fader()
self.current_menu = constants.MAIN_MENU
self.next_menu = constants.MAIN_MENU
self.all_loaded = false
-- Create sprites and buttons.
self.background = Sprite('imgs/cpu.png')
self.btn_font = Font('fonts/Concrete.ttf')
self.title_font = Font('fonts/BBrick.ttf', 35)
-- Create a mouse cursor object at the current mouse position.
local mx, my = love.mouse.getPosition()
self.cursor = Cursor(mx, my)
self.cursor = Cursor(mx, my)
-- Create sound effects.
self.bgm = SoundEffect('bgm/eskisky.wav')
-- Create the sub-menus.
self.menus = { }
self.menus[constants.MAIN_MENU] = MainMenu(self, self.title_font, self.btn_font)
self.menus[constants.OPTIONS_MENU] = OptionsMenu(self, self.title_font, self.btn_font)
-- Register all assets.
assets:register(self.name, self.btn_font)
assets:register(self.name, self.title_font)
assets:register(self.name, self.cursor)
assets:register(self.name, self.bgm)
end
function MainMenu:load()
-- Load sprites.
self.background:load()
self.cursor:load()
function Menu:update(dt)
if not self.bgm:isPlaying() then sound_manager:play_music(self.bgm) end
-- Load sound effects and start playing the background music
self.bgm:load()
self.bgm:play()
if self.all_loaded then
self.menus[self.current_menu]:update(dt)
dialog_manager:update(dt)
-- If the game state changed then trigger a fade out.
if self.next_menu ~= self.current_menu and self.fade.done then
self.fade:fade_out()
end
self.fade:update(dt)
-- Update the game state.
if self.menus[self.next_menu] == nil then
error(string.format('%s: switched to unknown menu', self.next_menu))
else
-- If the new state exists then unload it's data and set the new state.
if self.next_menu ~= self.current_menu and self.fade.done then
self.current_menu = self.next_menu
self.fade:fade_in()
end
end
else
local done = true
assets:update(self.name)
for _, v in pairs(self.menus) do
assets:update(v.name)
done = done and assets:done(v.name)
end
self.all_loaded = assets:done(self.name) and done
end
return self.next_state
end
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
function Menu:draw()
if self.all_loaded then
self.menus[self.current_menu]:draw()
dialog_manager:draw()
self.cursor:draw()
self.fade:draw()
else
assets:draw()
end
end
function MainMenu:draw()
self.background:draw()
self.cursor:draw()
self.fade:draw()
end
function MainMenu:unload()
self.background:unload()
self.cursor:unload()
self.bgm:unload()
end
function MainMenu:keypressed(key)
-- Skip the intro on any key press.
if key == "escape" then
self.skip = true
function Menu:keypressed(key, code, isrepeat)
-- Send events to the active game state if there is no fade active.
if Fade.done then
if dialog_manager:empty() then
self.menus[self.current_menu]:keypressed(key, code, isrepeat)
else
dialog_manager:keypressed(key, code, isrepeat)
end
end
end
function MainMenu:mousemoved(x, y, dx, dy)
function Menu:keyreleased(key, code)
-- Send events to the active game state if there is no fade active.
if Fade.done then
if dialog_manager:empty() then
self.menus[self.current_menu]:keyreleased(key, code)
else
dialog_manager:keyreleased(key, code)
end
end
end
function Menu:textinput(text)
-- Send events to the active game state if there is no fade active.
if Fade.done then
if dialog_manager:empty() then
self.menus[self.current_menu]:textinput(text)
else
dialog_manager:textinput(text)
end
end
end
function Menu:mousemoved(x, y, dx, dy)
if dialog_manager:empty() then
self.menus[self.current_menu]:mousemoved(x, y, dx, dy)
else
dialog_manager:mousemoved(x, y, dx, dy)
end
self.cursor:mousemoved(x, y, dx, dy)
end
function Menu:mousepressed(x, y, btn)
if self.fade.done then
if dialog_manager:empty() then
self.menus[self.current_menu]:mousepressed(x, y, btn)
else
dialog_manager:mousepressed(x, y, btn)
end
end
end
function Menu:mousereleased(x, y, btn)
if self.fade.done then
if dialog_manager:empty() then
self.menus[self.current_menu]:mousereleased(x, y, btn)
else
dialog_manager:mousereleased(x, y, btn)
end
end
end
------------------------------------------------------------------------------
-- Module return
------------------------------------------------------------------------------
return MainMenu
return Menu

View File

@@ -0,0 +1,75 @@
------------------------------------------------------------------------------
-- Imports
------------------------------------------------------------------------------
local love = require 'love'
local assets = require 'src.utils.asstmngr'
local make_class = require 'src.utils.classes'
local GameState = require 'src.gstates.gstate'
local Sprite = require 'src.graphics.sprite'
local VBox = require 'src.ui.vbox'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
local BaseMenu = make_class(GameState)
------------------------------------------------------------------------------
-- Class methods
------------------------------------------------------------------------------
function BaseMenu:_init(parent, name, index, bckg)
GameState._init(self, name, index)
self.parent = parent
-- Create background sprite and container.
self.background = Sprite(bckg)
self.container = VBox(15, 5, love.graphics.getWidth() - 15)
-- Register the background and container into the asset manager.
assets:register(self.name, self.background)
assets:register(self.name, self.container)
end
function BaseMenu:mousemoved(x, y, dx, dy)
self.container:mousemoved(x, y, dx, dy)
end
function BaseMenu:mousepressed(x, y, btn)
self.container:mousepressed(x, y, btn)
end
function BaseMenu:mousereleased(x, y, btn)
self.container:mousereleased(x, y, btn)
end
function BaseMenu:update()
if not assets:done(self.name) then
assets:update(self.name)
end
end
function BaseMenu:draw()
if assets:done(self.name) then
self.background:draw()
self.container:draw()
else
assets:draw()
end
end
------------------------------------------------------------------------------
-- Module return
------------------------------------------------------------------------------
return BaseMenu

View File

@@ -0,0 +1,17 @@
------------------------------------------------------------------------------
-- Constants
------------------------------------------------------------------------------
local constants = {
MAIN_MENU = 1,
OPTIONS_MENU = 2,
STORY = 3,
LOAD_MENU = 4,
}
------------------------------------------------------------------------------
-- Module return
------------------------------------------------------------------------------
return constants

View File

@@ -0,0 +1,41 @@
------------------------------------------------------------------------------
-- Imports
------------------------------------------------------------------------------
local make_class = require 'src.utils.classes'
local constants = require 'src.gstates.menus.const'
local BaseMenu = require 'src.gstates.menus.base'
local Color = require 'src.utils.color'
local Label = require 'src.ui.label'
local TextButton = require 'src.ui.textbtn'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
local MainMenu = make_class(BaseMenu)
------------------------------------------------------------------------------
-- Main Menu Class methods
------------------------------------------------------------------------------
function MainMenu:_init(parent, title_font, button_font)
BaseMenu._init(self, parent, "Main Menu", constants.MAIN_MENU, 'imgs/concb03.png')
-- Create UI elements of the main menu.
self.container:add(Label(nil, nil, 'LoveDOS', title_font, Color(215, 0, 0), true))
self.container:add(TextButton('New Game', button_font))
self.container:add(TextButton('Load Game', button_font))
self.container:add(TextButton('Options', button_font, nil, nil, function() parent.next_menu = constants.OPTIONS_MENU end))
self.container:add(TextButton('Help & Story', button_font))
self.container:add(TextButton('Quit', button_font, nil, nil, function() parent.next_state = -1 end))
end
------------------------------------------------------------------------------
-- Module return
------------------------------------------------------------------------------
return MainMenu

View File

@@ -0,0 +1,178 @@
------------------------------------------------------------------------------
-- Imports
------------------------------------------------------------------------------
local make_class = require 'src.utils.classes'
local constants = require 'src.gstates.menus.const'
local settings = require 'src.utils.settings'
local sound_manager = require 'src.sound.sndmngr'
local assets = require 'src.utils.asstmngr'
local BaseMenu = require 'src.gstates.menus.base'
local Color = require 'src.utils.color'
local HBox = require 'src.ui.hbox'
local Label = require 'src.ui.label'
local TextButton = require 'src.ui.textbtn'
local TextInput = require 'src.ui.textinpt'
local Checkbox = require 'src.ui.chkbox'
local Bar = require 'src.ui.bar'
local Dialog = require 'src.ui.dialog'
local Font = require 'src.ui.font'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
local OptionsMenu = make_class(BaseMenu)
------------------------------------------------------------------------------
-- Class methods
------------------------------------------------------------------------------
function OptionsMenu:_init(parent, title_font, button_font)
BaseMenu._init(self, parent, "Options Menu", constants.OPTIONS_MENU, 'imgs/cpu.png')
self.subtitle_font = Font('fonts/BBrick.ttf', 25)
-- Create UI elements of the menu.
self.container:add(Label(nil, nil, 'Options', title_font, Color(215, 0, 0), true))
-- Permadeath checkbox.
local box = HBox()
box:add(Label(nil, nil, 'Enable Permadeath: ', button_font))
box:add(Checkbox(
function()
settings.permadeath = not settings.permadeath
end,
function()
return settings.permadeath
end,
17,
15))
self.container:add(box)
-- Control binding dialog.
local dlg = Dialog('imgs/tchcmp1.png', nil, 15, nil, nil, button_font)
dlg:add(Label(nil, nil, 'Set Controls', self.subtitle_font, Color(215, 0, 0), true))
box = HBox()
box.spacing = 30
-- text, font, x, y, callback, value, float, base_col, sel_color, press_col
box:add(TextInput('Step Forw.',
button_font,
nil, nil,
function(v) settings.forward = v end,
function() return settings.forward end))
box:add(TextInput('Step Back',
button_font,
nil, nil,
function(v) settings.backward = v end,
function() return settings.backward end))
dlg:add(box)
box = HBox()
box.spacing = 40
box:add(TextInput('Step Left',
button_font,
nil, nil,
function(v) settings.stepleft = v end,
function() return settings.stepleft end))
box:add(TextInput('Step Right',
button_font,
nil, nil,
function(v) settings.stepright = v end,
function() return settings.stepright end))
dlg:add(box)
box = HBox()
box.spacing = 42
box:add(TextInput('Turn Left',
button_font,
nil, nil,
function(v) settings.turnleft = v end,
function() return settings.turnleft end))
box:add(TextInput('Turn Right',
button_font,
nil, nil,
function(v) settings.turnright = v end,
function() return settings.turnright end))
dlg:add(box)
box = HBox()
box:add(TextButton(
'Set Controls',
button_font,
nil,
nil,
function()
dlg:show()
end))
self.container:add(box)
-- Sound volume bar.
box = HBox()
box:add(Label(nil, nil, 'Sound Volume: ', button_font))
box:add(Bar(
function(v)
settings.soundvol = math.ceil(v)
end,
function()
return settings.soundvol
end,
0, 100,
170, 15))
self.container:add(box)
-- Music volume bar.
box = HBox()
box.spacing = 13
box:add(Label(nil, nil, 'Music Volume: ', button_font))
box:add(Bar(
function(v)
settings.musicvol = math.ceil(v)
sound_manager:update_volumes()
end,
function()
return settings.musicvol
end,
0, 100,
170, 15))
self.container:add(box)
-- Back button.
box = HBox()
box:add(TextButton(
'Go Back',
button_font,
nil,
nil,
function()
settings:save_settings()
parent.next_menu = constants.MAIN_MENU
end))
self.container:add(box)
-- Register the dialog with the asset manager.
assets:register(self.name, dlg)
assets:register(self.name, self.subtitle_font)
end
------------------------------------------------------------------------------
-- Module return
------------------------------------------------------------------------------
return OptionsMenu

View File

@@ -11,7 +11,7 @@ local Asset = require 'src.utils.asset'
------------------------------------------------------------------------------
-- SoundEffect is a simple wrapper around Löve's Source class to allow for
-- easy loading/unloading and automatically hecking if a sound effect is valid
-- easy loading/unloading and automatically checking if a sound effect is valid
-- before using it.
local SoundEffect = make_class(Asset)
@@ -21,12 +21,14 @@ local SoundEffect = make_class(Asset)
------------------------------------------------------------------------------
function SoundEffect:_init(file_name)
self.file_name = string.format('assets/%s', file_name)
Asset._init(self, file_name)
end
function SoundEffect:load()
self.sfx = love.audio.newSource(self.file_name)
if not self:is_loaded() then
self.sfx = love.audio.newSource(self.file_name)
end
end
@@ -36,6 +38,11 @@ function SoundEffect:unload()
end
function SoundEffect:is_loaded()
return self.sfx ~= nil
end
function SoundEffect:setVolume(volume)
if self.sfx ~= nil then self.sfx:setVolume(volume) end
end

57
src/sound/sndmngr.lua Normal file
View File

@@ -0,0 +1,57 @@
------------------------------------------------------------------------------
-- Imports
------------------------------------------------------------------------------
local make_class = require 'src.utils.classes'
local settings = require 'src.utils.settings'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
local SoundManager = make_class()
------------------------------------------------------------------------------
-- Class methods
------------------------------------------------------------------------------
function SoundManager:_init()
self.active_bgm = nil
end
function SoundManager:play_sound(sfx)
local vol = math.max(math.min(settings.soundvol / 100, 1.0), 0.0)
sfx:setVolume(vol)
sfx:play()
end
function SoundManager:play_music(bgm)
local vol = math.max(math.min(settings.musicvol / 100, 1.0), 0.0)
if self.active_bgm ~= nil then
self.active_bgm:stop()
end
self.active_bgm = bgm
self.active_bgm:setVolume(vol)
self.active_bgm:play()
end
function SoundManager:update_volumes()
if self.active_bgm ~= nil then
local vol = math.max(math.min(settings.musicvol / 100, 1.0), 0.0)
self.active_bgm:setVolume(vol)
end
end
------------------------------------------------------------------------------
-- Module return
------------------------------------------------------------------------------
return SoundManager()

View File

@@ -2,8 +2,8 @@
-- Imports
------------------------------------------------------------------------------
local Intro = require 'src.gstates.intro'
local MainMenu = require 'src.gstates.menu'
local Intro = require 'src.gstates.intro'
local Menu = require 'src.gstates.menu'
------------------------------------------------------------------------------
@@ -13,7 +13,7 @@ local MainMenu = require 'src.gstates.menu'
-- Table of all valid game states.
local states = {
Intro('intro', 1),
MainMenu('menu', 2),
Menu('menu', 2),
}

121
src/ui/bar.lua Normal file
View File

@@ -0,0 +1,121 @@
------------------------------------------------------------------------------
-- Imports
------------------------------------------------------------------------------
local love = require 'love'
local make_class = require 'src.utils.classes'
local collisions = require 'src.utils.colls'
local UIElement = require 'src.ui.element'
local Color = require 'src.utils.color'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
local Bar = make_class(UIElement)
------------------------------------------------------------------------------
-- Class methods
------------------------------------------------------------------------------
function Bar:_init(callback, val_fn, min, max, w, h, color, sel_color, press_col, float, x, y)
UIElement._init(self, x, y, float, false)
self.callback = callback
self.val_fn = val_fn
self.min = min
self.max = max
self.w = w
self.h = h
self.base_col = color ~= nil and color 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)
self.disbl_col = Color(95, 63, 75)
end
function Bar:set_dimensions()
self.w = self.w
self.h = self.h
end
function Bar:draw()
local color = self.callback == nil and self.disbl_col or ((self.pressed and self.press_col) or ((self.selected and self.sel_color) or self.base_col))
-- Convert the variable's value to the [0, 1] space.
local v = (self.val_fn() + math.abs(self.min)) / (self.max + math.abs(self.min))
love.graphics.setColor(color.r, color.g, color.b)
-- Render the bar's outline.
love.graphics.rectangle('line', self.x, self.y, self.w, self.h)
-- Render the bar's content using the [0, 1] value as a proportion of the bar's width.
love.graphics.rectangle('fill', self.x, self.y, self.w * v, self.h)
love.graphics.setColor()
end
function Bar:_mouse2bar(x)
-- Take the x coord to the [0, 1] space, with clamping.
local v = math.max(math.min(((x - self.x) / self.w), 1.0), 0.0)
-- Convert the value to a horizontal bar coordinate and return it.
return (v * (self.max + math.abs(self.min))) - self.min
end
function Bar: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
self.callback(self:_mouse2bar(x))
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
self.callback(self:_mouse2bar(x))
end
end
function Bar: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 Bar:mousereleased(x, _, 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(self:_mouse2bar(x))
end
self.pressed = false
end
-- The button no longer was pressed.
self.was_pressed = false
end
end
------------------------------------------------------------------------------
-- Module return
------------------------------------------------------------------------------
return Bar

90
src/ui/button.lua Normal file
View File

@@ -0,0 +1,90 @@
------------------------------------------------------------------------------
-- Imports
------------------------------------------------------------------------------
local make_class = require 'src.utils.classes'
local collisions = require 'src.utils.colls'
local UIElement = require 'src.ui.element'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
---@class Button: UIElement
local Button = make_class(UIElement)
------------------------------------------------------------------------------
-- Class methods
------------------------------------------------------------------------------
---@param x number
---@param y number
---@param callback function
---@param float boolean
function Button:_init(x, y, callback, float)
UIElement._init(self, x, y, float, false)
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

55
src/ui/chkbox.lua Normal file
View File

@@ -0,0 +1,55 @@
------------------------------------------------------------------------------
-- 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 Checkbox = make_class(Button)
------------------------------------------------------------------------------
-- Class methods
------------------------------------------------------------------------------
function Checkbox:_init(callback, value, w, h, color, sel_color, press_col, float, x, y)
Button._init(self, x, y, callback, float)
self.value = value
self.w = w ~= nil and w or 20
self.h = h ~= nil and h or 20
self.base_col = color ~= nil and color 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)
self.disbl_col = Color(95, 63, 75)
end
function Checkbox:set_dimensions()
self.w = self.w
self.h = self.h
end
function Checkbox:draw()
local color = self.callback == nil and self.disbl_col or ((self.pressed and self.press_col) or ((self.selected and self.sel_color) or self.base_col))
local mode = nil
if self.value() then mode = 'fill' else mode = 'line' end
love.graphics.setColor(color.r, color.g, color.b)
love.graphics.rectangle(mode, self.x, self.y, self.w, self.h)
love.graphics.setColor()
end
------------------------------------------------------------------------------
-- Module return
------------------------------------------------------------------------------
return Checkbox

117
src/ui/dialog.lua Normal file
View File

@@ -0,0 +1,117 @@
------------------------------------------------------------------------------
-- Imports
------------------------------------------------------------------------------
local love = require 'love'
local make_class = require 'src.utils.classes'
local dialog_manager = require 'src.ui.dlgmngr'
local VBox = require 'src.ui.vbox'
local Sprite = require 'src.graphics.sprite'
local TextButton = require 'src.ui.textbtn'
local HBox = require 'src.ui.hbox'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
local Dialog = make_class(VBox)
------------------------------------------------------------------------------
-- Class methods
------------------------------------------------------------------------------
function Dialog:_init(bckg, spacing, padding, close_btn, accept_btn, font)
VBox._init(self, nil, nil, nil, nil, spacing, false)
self.spacing = spacing ~= nil and spacing or 10
self.padding = padding ~= nil and padding or 15
self.background = Sprite(bckg)
self.shown = false
local close = nil
if close_btn ~= nil then
close = close_btn
else
close = TextButton(
'Close',
font,
nil,
nil,
function()
self:hide()
end)
end
local accept = nil
if accept_btn ~= nil then accept = accept_btn end
-- Create a container for the close and accept button and add them.
local box = HBox(nil, nil, nil, nil, 10, true)
box:add(close)
if accept ~= nil then box:add(accept) end
self.elements[9001] = box
end
function Dialog:add(element)
table.insert(self.elements, element)
end
function Dialog:load()
self.background:load()
self:set_dimensions()
VBox.load(self)
end
function Dialog:set_dimensions()
self.w = self.background.sprite:getWidth()
self.h = self.background.sprite:getHeight()
-- Center the dialog on the screen.
self.x = (320 - self.w) / 2
self.y = (200 - self.h) / 2
self.background.x = self.x
self.background.y = self.y
-- Add padding.
self.x = self.x + self.padding
self.y = self.y + self.padding / 2
end
function Dialog:draw()
love.graphics.setColor(0, 0, 0)
love.graphics.rectangle('line', self.x - self.padding - 1, self.y - (self.padding / 2) - 1, self.w + 2, self.h + 2)
love.graphics.setColor()
self.background:draw()
VBox.draw(self)
end
function Dialog:show()
if not self.shown then
dialog_manager:add(self)
self.shown = true
end
end
function Dialog:hide()
dialog_manager:remove(self)
self.shown = false
end
------------------------------------------------------------------------------
-- Module return
------------------------------------------------------------------------------
return Dialog

104
src/ui/dlgmngr.lua Normal file
View File

@@ -0,0 +1,104 @@
------------------------------------------------------------------------------
-- Imports
------------------------------------------------------------------------------
local make_class = require 'src.utils.classes'
local Drawable = require 'src.graphics.drawable'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
local DialogManager = make_class(Drawable)
------------------------------------------------------------------------------
-- Class methods
------------------------------------------------------------------------------
function DialogManager:_init()
Drawable._init(self)
self.dialogs = {}
self.count = 0
end
function DialogManager:add(dialog)
self.dialogs[dialog] = true
self.count = self.count + 1
end
function DialogManager:remove(dialog)
self.dialogs[dialog] = nil
self.count = math.max(self.count - 1, 0)
end
function DialogManager:empty()
return self.count == 0
end
function DialogManager:update(dt)
for d, _ in pairs(self.dialogs) do
d:update(dt)
end
end
function DialogManager:draw()
for d, _ in pairs(self.dialogs) do
d:draw()
end
end
function DialogManager:keypressed(key, code, isrepeat)
for d, _ in pairs(self.dialogs) do
d:keypressed(key, code, isrepeat)
end
end
function DialogManager:textinput(text)
for d, _ in pairs(self.dialogs) do
d:textinput(text)
end
end
function DialogManager:keyreleased(key, code)
for d, _ in pairs(self.dialogs) do
d:keyreleased(key, code)
end
end
function DialogManager:mousemoved(x, y, dx, dy)
for d, _ in pairs(self.dialogs) do
d:mousemoved(x, y, dx, dy)
end
end
function DialogManager:mousepressed(x, y, btn)
for d, _ in pairs(self.dialogs) do
d:mousepressed(x, y, btn)
end
end
function DialogManager:mousereleased(x, y, btn)
for d, _ in pairs(self.dialogs) do
d:mousereleased(x, y, btn)
end
end
------------------------------------------------------------------------------
-- Module return
------------------------------------------------------------------------------
return DialogManager()

84
src/ui/element.lua Normal file
View File

@@ -0,0 +1,84 @@
------------------------------------------------------------------------------
-- Imports
------------------------------------------------------------------------------
local make_class = require 'src.utils.classes'
local Asset = require 'src.utils.asset'
local Drawable = require 'src.graphics.drawable'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
local focused_element = nil
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
---@class UIElement: Drawable, Asset
local UIElement = make_class(Drawable, Asset)
------------------------------------------------------------------------------
-- Class methods
------------------------------------------------------------------------------
---@param x integer
---@param y integer
---@param float boolean
---@param focusable boolean
function UIElement:_init(x, y, float, focusable)
Asset._init(self)
self.x = x
self.y = y
self.w = nil
self.h = nil
self.float_right = float and float or false
self.focusable = focusable ~= nil and focusable or false
end
function UIElement:set_dimensions()
self.w = 0
self.h = 0
end
function UIElement:load()
end
function UIElement:unload()
end
function UIElement:is_loaded()
return true
end
function UIElement:focus()
if self.focusable then
focused_element = self
end
end
function UIElement:blur()
if self.focusable and focused_element == self then
focused_element = nil
end
end
function UIElement:is_focused()
return self.focusable and focused_element == self
end
------------------------------------------------------------------------------
-- Module return
------------------------------------------------------------------------------
return UIElement

73
src/ui/font.lua Normal file
View File

@@ -0,0 +1,73 @@
------------------------------------------------------------------------------
-- Imports
------------------------------------------------------------------------------
local love = require 'love'
local make_class = require 'src.utils.classes'
local Asset = require 'src.utils.asset'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
---@class Font: Asset
-- Font is a simple wrapper around Löve's own Font class to allow for
-- easy loading/unloading and automatically checking if it's is valid
-- before using it.
local Font = make_class(Asset)
------------------------------------------------------------------------------
-- Class methods
------------------------------------------------------------------------------
---@param file_name string
---@param size number
function Font:_init(file_name, size)
self.file_name = string.format('assets/%s', file_name)
self.size = (size ~= nil and size) or 20
end
function Font:load()
if not self:is_loaded() then
self.f = love.graphics.newFont(self.file_name, self.size)
end
end
function Font:unload()
self.f = nil
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
function Font:unset()
love.graphics.setFont()
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
------------------------------------------------------------------------------
return Font

63
src/ui/hbox.lua Normal file
View File

@@ -0,0 +1,63 @@
------------------------------------------------------------------------------
-- Imports
------------------------------------------------------------------------------
local make_class = require 'src.utils.classes'
local Layout = require 'src.ui.layout'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
---@class HBox: Layout
local HBox = make_class(Layout)
------------------------------------------------------------------------------
-- Class methods
------------------------------------------------------------------------------
function HBox:_init(x, y, w, h, spacing, float)
Layout._init(self, x, y, w, h, spacing, float)
end
function HBox:load()
Layout.load(self)
local _x = self.x
-- Load required assets if needed and then compute the coordinates of each button.
for _, v in pairs(self.elements) do
v.y = self.y
v.x = _x
_x = _x + v.w + self.spacing
end
end
function HBox:set_dimensions()
if self.w == nil then
self.w = 0
for i, v in pairs(self.elements) do
self.w = self.w + v.w
if i < #self.elements then self.w = self.w + self.spacing end
end
end
if self.h == nil then
self.h = 0
for _, v in pairs(self.elements) do
if v.h > self.h then self.h = v.h end
end
end
end
------------------------------------------------------------------------------
-- Module return
------------------------------------------------------------------------------
return HBox

74
src/ui/label.lua Normal file
View File

@@ -0,0 +1,74 @@
------------------------------------------------------------------------------
-- Imports
------------------------------------------------------------------------------
local love = require 'love'
local make_class = require 'src.utils.classes'
local UIElement = require 'src.ui.element'
local Color = require 'src.utils.color'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
---@class Label: UIElement
local Label = make_class(UIElement)
------------------------------------------------------------------------------
-- Class methods
------------------------------------------------------------------------------
---@param x number
---@param y number
---@param text string
---@param font Font
---@param color Color
---@param float boolean
function Label:_init(x, y, text, font, color, float)
UIElement._init(self, x, y, float, false)
self.text = text
self.font = font
self.color = color ~= nil and color or Color(255, 255, 255)
end
function Label:set_dimensions()
self.w = self.font:get_width(self.text)
self.h = self.font:get_height(self.text)
end
function Label:load()
self.font:load()
-- 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 Label:unload()
self.font:unload()
end
function Label:is_loaded()
return self.font:is_loaded() and self.w ~= nil and self.h ~= nil
end
function Label:draw()
love.graphics.setColor(self.color.r, self.color.g, self.color.b)
self.font:set()
love.graphics.print(self.text, self.x, self.y)
self.font:unset()
love.graphics.setColor()
end
------------------------------------------------------------------------------
-- Module return
------------------------------------------------------------------------------
return Label

118
src/ui/layout.lua Normal file
View File

@@ -0,0 +1,118 @@
------------------------------------------------------------------------------
-- Imports
------------------------------------------------------------------------------
local make_class = require 'src.utils.classes'
local UIElement = require 'src.ui.element'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
---@class Layout: UIElement
local Layout = make_class(UIElement)
------------------------------------------------------------------------------
-- Class methods
------------------------------------------------------------------------------
function Layout:_init(x, y, w, h, spacing, float)
UIElement._init(self, x, y, float, false)
self.x = x ~= nil and x or 0
self.y = y ~= nil and y or 0
self.w = w
self.h = h
self.spacing = spacing ~= nil and spacing or 10
self.elements = {}
end
function Layout:add(element)
table.insert(self.elements, element)
end
function Layout:load()
-- Load required assets if needed and then compute the coordinates of each button.
for _, v in pairs(self.elements) do
if v.is_a[UIElement] then
v:load()
end
end
self:set_dimensions()
end
function Layout:unload()
for _, v in pairs(self.elements) do
if v.is_a[UIElement] then
v:unload()
end
end
end
function Layout:update(dt)
for _, v in pairs(self.elements) do
v:update(dt)
end
end
function Layout:draw()
for _, v in pairs(self.elements) do
v:draw()
end
end
function Layout:keypressed(key, code, isrepeat)
for _, v in pairs(self.elements) do
v:keypressed(key, code, isrepeat)
end
end
function Layout:textinput(text)
for _, v in pairs(self.elements) do
v:textinput(text)
end
end
function Layout:keyreleased(key, code)
for _, v in pairs(self.elements) do
v:keyreleased(key, code)
end
end
function Layout:mousemoved(x, y, dx, dy)
for _, v in pairs(self.elements) do
v:mousemoved(x, y, dx, dy)
end
end
function Layout:mousepressed(x, y, btn)
for _, v in pairs(self.elements) do
v:mousepressed(x, y, btn)
end
end
function Layout:mousereleased(x, y, btn)
for _, v in pairs(self.elements) do
v:mousereleased(x, y, btn)
end
end
------------------------------------------------------------------------------
-- Module return
------------------------------------------------------------------------------
return Layout

74
src/ui/textbtn.lua Normal file
View File

@@ -0,0 +1,74 @@
------------------------------------------------------------------------------
-- 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, float, base_col, sel_color, press_col)
Button._init(self, x, y, callback, float)
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)
self.disbl_col = Color(95, 63, 75)
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.callback == nil and self.disbl_col or ((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

118
src/ui/textinpt.lua Normal file
View File

@@ -0,0 +1,118 @@
------------------------------------------------------------------------------
-- Imports
------------------------------------------------------------------------------
local love = require 'love'
local make_class = require 'src.utils.classes'
local collisions = require 'src.utils.colls'
local UIElement = require 'src.ui.element'
local Color = require 'src.utils.color'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
---@class CharInput: UIElement
local TextInput = make_class(UIElement)
------------------------------------------------------------------------------
-- Class methods
------------------------------------------------------------------------------
---@param text string
---@param font Font
---@param x number
---@param y number
---@param callback function
---@param value function
---@param float boolean
---@param base_col Color
---@param sel_color Color
---@param press_col Color
function TextInput:_init(text, font, x, y, callback, value, float, base_col, sel_color, press_col)
UIElement._init(self, x, y, float, true)
self.callback = callback
self.value = value
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)
self.disbl_col = Color(95, 63, 75)
self.selected = false
self.pressed = false
end
function TextInput:load()
self.font:load()
-- 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 TextInput:unload()
self.font:unload()
end
function TextInput:is_loaded()
return self.font:is_loaded() and self.w ~= nil and self.h ~= nil
end
function TextInput:set_dimensions()
self.w = self.font:get_width(self.text .. 'm')
self.h = self.font:get_height(self.text)
end
function TextInput:draw()
local color = self.callback == nil and self.disbl_col or (((self.pressed or self:is_focused()) 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()
if self:is_focused() then
love.graphics.print(self.text .. ': ?', self.x, self.y)
else
love.graphics.print(self.text .. ': ' .. self.value(), self.x, self.y)
end
self.font:unset()
love.graphics.setColor()
end
function TextInput: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)
end
function TextInput:mousepressed(_, _, btn)
-- If the pointer was inside the button when it was pressed then mark the button as pressed.
if btn == 0 and self.selected then
self:focus()
end
end
function TextInput:keypressed(key)
if self:is_focused() then
if #key == 1 then
if self.callback ~= nil then self.callback(key) end
self:blur()
end
end
end
------------------------------------------------------------------------------
-- Module return
------------------------------------------------------------------------------
return TextInput

66
src/ui/vbox.lua Normal file
View File

@@ -0,0 +1,66 @@
------------------------------------------------------------------------------
-- Imports
------------------------------------------------------------------------------
local make_class = require 'src.utils.classes'
local Layout = require 'src.ui.layout'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
---@class VBox: Layout
local VBox = make_class(Layout)
------------------------------------------------------------------------------
-- Class methods
------------------------------------------------------------------------------
function VBox:_init(x, y, w, h, spacing, float)
Layout._init(self, x, y, w, h, spacing, float)
end
function VBox:load()
Layout.load(self)
local _y = self.y
-- Load required assets if needed and then compute the coordinates of each button.
for _, v in pairs(self.elements) do
if v.float_right then
v.x = self.w - v.w
else
v.x = self.x
end
v.y = _y
_y = _y + v.h + self.spacing
end
end
function VBox:set_dimensions()
if self.w == nil then
self.w = 0
for _, v in pairs(self.elements) do
if v.w > self.w then self.w = v.w end
end
end
if self.h == nil then
self.h = 0
for i, v in pairs(self.elements) do
self.h = self.h + v.h
if i < #self.elements then self.h = self.h + self.spacing end
end
end
end
------------------------------------------------------------------------------
-- Module return
------------------------------------------------------------------------------
return VBox

View File

@@ -9,6 +9,7 @@ local make_class = require 'src.utils.classes'
-- Class definitions
------------------------------------------------------------------------------
---@class Asset
local Asset = make_class()
@@ -16,15 +17,23 @@ local Asset = make_class()
-- Class methods
------------------------------------------------------------------------------
function Asset:_init()
function Asset:_init(file_name)
self.file_name = string.format('assets/%s', file_name)
end
function Asset:load()
error 'Attempted to load unimplemented asset.'
end
function Asset:unload()
error 'Attempted to unload unimplemented asset.'
end
function Asset:is_loaded()
return false
end

135
src/utils/asstmngr.lua Normal file
View File

@@ -0,0 +1,135 @@
------------------------------------------------------------------------------
-- Imports
------------------------------------------------------------------------------
local love = require 'love'
local make_class = require 'src.utils.classes'
local Drawable = require 'src.graphics.drawable'
local Asset = require 'src.utils.asset'
local Sprite = require 'src.graphics.sprite'
local Font = require 'src.ui.font'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
local AssetManager = make_class(Drawable)
------------------------------------------------------------------------------
-- Helper functions
------------------------------------------------------------------------------
local function _load(manager, owner)
if manager.assets[owner] ~= nil then
for k, _ in pairs(manager.assets[owner]) do
if k.is_a ~= nil and k.is_a[Asset] then
k:load()
end
coroutine.yield(false)
end
end
return true
end
------------------------------------------------------------------------------
-- Class methods
------------------------------------------------------------------------------
function AssetManager:_init()
Drawable._init(self)
self.assets = {}
self.bckg = Sprite('imgs/floppy.png')
self.font = Font('fonts/BBrick.ttf', 40)
self.co = nil
self.bckg:load()
self.font:load()
end
function AssetManager:unload(owner)
-- If the owner is known...
if self.assets[owner] ~= nil then
-- Iterate over it's asset set and unload everything.
for k, _ in pairs(self.assets[owner]) do
if k.is_a ~= nil and k.is_a[Asset] then
k:unload()
end
end
end
end
function AssetManager:update(owner)
if self.assets[owner] ~= nil then
if self.assets[owner].co == nil then
self.assets[owner].co = coroutine.create(_load)
end
local errorfree, retval = coroutine.resume(self.assets[owner].co, self, owner)
if errorfree then
self.assets[owner].done = retval
if retval then
self.assets[owner].co = nil
end
else
error(retval)
end
end
end
function AssetManager:draw()
self.bckg:draw()
love.graphics.setColor(195, 147, 123)
self.font:set()
love.graphics.print('Loading...', 5, 150)
self.font:unset()
love.graphics.setColor()
end
function AssetManager:register(owner, asset)
-- If the owner is new, then create an asset set for it.
if self.assets[owner] == nil then
self.assets[owner] = {
done = false,
co = nil,
}
end
-- Store the asset in the set.
self.assets[owner][asset] = true
end
function AssetManager:remove(owner, asset)
-- If the owner is new, then create an asset set for it.
if self.assets[owner] ~= nil then
-- Unload the asset just in case.
if self.assets[owner][asset] ~= nil then
self.assets[owner][asset]:unload()
end
-- Then remove it from the set.
self.assets[owner][asset] = nil
end
end
function AssetManager:done(owner)
return self.assets[owner] ~= nil and self.assets[owner].done
end
------------------------------------------------------------------------------
-- Module return
------------------------------------------------------------------------------
return AssetManager()

View File

@@ -2,15 +2,16 @@
-- 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
---@param ... table Optional base classes
---@return table cls A class definition
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,8 +20,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
cls.is_a[BaseClass] = 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

17
src/utils/colls.lua Normal file
View File

@@ -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

31
src/utils/color.lua Normal file
View File

@@ -0,0 +1,31 @@
------------------------------------------------------------------------------
-- Imports
------------------------------------------------------------------------------
local make_class = require 'src.utils.classes'
------------------------------------------------------------------------------
-- Class definitions
------------------------------------------------------------------------------
---@class Color
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

View File

@@ -4,6 +4,7 @@
local love = require 'love'
local magiclines = require 'src.utils.mgclines'
local make_class = require 'src.utils.classes'
------------------------------------------------------------------------------
@@ -17,25 +18,29 @@ local SETTINGS_PATH = 'settings.ini'
-- Module definitions
------------------------------------------------------------------------------
local settings = {
-- Default setting values.
permadeath = true,
forward = 'w',
backward = 's',
stepleft = 'a',
stepright = 'd',
turnleft = 'q',
turnright = 'e',
musicvol = 75,
soundvol = 100,
}
---@class Settings
local Settings = make_class()
------------------------------------------------------------------------------
-- Private module methods
------------------------------------------------------------------------------
function settings:_parse_settings_str(s)
function Settings:_init()
-- Default setting values.
self.permadeath = true
self.forward = 'w'
self.backward = 's'
self.stepleft = 'a'
self.stepright = 'd'
self.turnleft = 'q'
self.turnright = 'e'
self.musicvol = 75
self.soundvol = 100
end
function Settings:_parse_settings_str(s)
-- For each line in the data string.
for line in magiclines(s) do
-- Check if the line matches a key=value pair
@@ -67,7 +72,7 @@ function settings:_parse_settings_str(s)
end
function settings:_get_settings_str()
function Settings:_get_settings_str()
-- Start with the settings category.
local s = '[settings]\n'
@@ -84,7 +89,7 @@ function settings:_get_settings_str()
end
function settings:_open_or_create()
function Settings:_open_or_create()
if not love.filesystem.exists(SETTINGS_PATH) then
-- If the settings file doesn't exist then create it with the default values.
local setts = self:_get_settings_str()
@@ -106,14 +111,14 @@ end
-- Public module methods
------------------------------------------------------------------------------
function settings:load_settings()
function Settings:load_settings()
-- Obtain the settings data and parse it.
local s = self:_open_or_create()
self:_parse_settings_str(s)
end
function settings:save_settings()
function Settings:save_settings()
if not love.filesystem.exists(SETTINGS_PATH) or love.filesystem.isFile(SETTINGS_PATH) then
-- If the settings file doesn't exist or it exists and is a file then save the current data.
love.filesystem.write(SETTINGS_PATH, self:_get_settings_str())
@@ -128,4 +133,4 @@ end
-- Module return
------------------------------------------------------------------------------
return settings
return Settings()