First commit.
@@ -0,0 +1,114 @@
|
|||||||
|
# ---> Lua
|
||||||
|
# Compiled Lua sources
|
||||||
|
luac.out
|
||||||
|
|
||||||
|
# luarocks build files
|
||||||
|
*.src.rock
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
|
||||||
|
# Object files
|
||||||
|
*.o
|
||||||
|
*.os
|
||||||
|
*.ko
|
||||||
|
*.obj
|
||||||
|
*.elf
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
*.lib
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lo
|
||||||
|
*.def
|
||||||
|
*.exp
|
||||||
|
|
||||||
|
# Shared objects (inc. Windows DLLs)
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
*.i*86
|
||||||
|
*.x86_64
|
||||||
|
*.hex
|
||||||
|
|
||||||
|
|
||||||
|
# ---> Linux
|
||||||
|
*~
|
||||||
|
|
||||||
|
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||||
|
.fuse_hidden*
|
||||||
|
|
||||||
|
# KDE directory preferences
|
||||||
|
.directory
|
||||||
|
|
||||||
|
# Linux trash folder which might appear on any partition or disk
|
||||||
|
.Trash-*
|
||||||
|
|
||||||
|
# .nfs files are created when an open file is removed but is still being accessed
|
||||||
|
.nfs*
|
||||||
|
|
||||||
|
# ---> Emacs
|
||||||
|
# -*- mode: gitignore; -*-
|
||||||
|
*~
|
||||||
|
\#*\#
|
||||||
|
/.emacs.desktop
|
||||||
|
/.emacs.desktop.lock
|
||||||
|
*.elc
|
||||||
|
auto-save-list
|
||||||
|
tramp
|
||||||
|
.\#*
|
||||||
|
|
||||||
|
# Org-mode
|
||||||
|
.org-id-locations
|
||||||
|
*_archive
|
||||||
|
|
||||||
|
# flymake-mode
|
||||||
|
*_flymake.*
|
||||||
|
|
||||||
|
# eshell files
|
||||||
|
/eshell/history
|
||||||
|
/eshell/lastdir
|
||||||
|
|
||||||
|
# elpa packages
|
||||||
|
/elpa/
|
||||||
|
|
||||||
|
# reftex files
|
||||||
|
*.rel
|
||||||
|
|
||||||
|
# AUCTeX auto folder
|
||||||
|
/auto/
|
||||||
|
|
||||||
|
# cask packages
|
||||||
|
.cask/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Flycheck
|
||||||
|
flycheck_*.el
|
||||||
|
|
||||||
|
# server auth directory
|
||||||
|
/server/
|
||||||
|
|
||||||
|
# projectiles files
|
||||||
|
.projectile
|
||||||
|
|
||||||
|
# directory configuration
|
||||||
|
.dir-locals.el
|
||||||
|
|
||||||
|
# network security
|
||||||
|
/network-security.data
|
||||||
|
|
||||||
|
|
||||||
|
# ---> Kate
|
||||||
|
# Swap Files #
|
||||||
|
.*.kate-swp
|
||||||
|
.swp.*
|
||||||
|
.kateproject*
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
Guidance for coding agents working in this repository.
|
||||||
|
|
||||||
|
## Project overview
|
||||||
|
|
||||||
|
- Language: Lua (LÖVE game project)
|
||||||
|
- Entry point: `main.lua`
|
||||||
|
- Core code: `src/`
|
||||||
|
- Assets: `assets/`
|
||||||
|
- Runtime config present: `dosbox.conf`
|
||||||
|
- Build system: none (run directly with LÖVE)
|
||||||
|
- Test framework: none configured in-repo
|
||||||
|
|
||||||
|
## Repository structure
|
||||||
|
|
||||||
|
- `main.lua`: top-level LÖVE callbacks and state switching
|
||||||
|
- `src/states.lua`: game-state registry
|
||||||
|
- `src/gstates/`: game states and menu flows
|
||||||
|
- `src/ui/`: UI primitives and layout classes
|
||||||
|
- `src/graphics/`: sprites/faders/drawable abstractions
|
||||||
|
- `src/sound/`: sound managers/effects
|
||||||
|
- `src/utils/`: class helper, settings parsing, assets manager, helpers
|
||||||
|
|
||||||
|
## Build, run, lint, and test commands
|
||||||
|
|
||||||
|
This repo does not define a Makefile/npm scripts/CI workflow. Use direct commands.
|
||||||
|
|
||||||
|
### Run the game
|
||||||
|
|
||||||
|
- Run locally with LÖVE:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
love .
|
||||||
|
```
|
||||||
|
|
||||||
|
- Run with debug/skip flags (project-defined args):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
love . -- -debug -skip-intro
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build/package
|
||||||
|
|
||||||
|
- No packaging script is committed.
|
||||||
|
- If packaging is needed, use standard LÖVE packaging flow externally.
|
||||||
|
|
||||||
|
### Lint
|
||||||
|
|
||||||
|
- No linter config file is committed (`.luacheckrc` not found).
|
||||||
|
- If `luacheck` is available locally, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
luacheck main.lua src
|
||||||
|
```
|
||||||
|
|
||||||
|
### Format
|
||||||
|
|
||||||
|
- No formatter config file is committed (`stylua.toml` not found).
|
||||||
|
- If formatting is needed and `stylua` exists locally, use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
stylua main.lua src
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
- No automated tests are currently committed (no `tests/`, `spec/`, or framework config detected).
|
||||||
|
- There is no native "single test" command in the current repo because no test suite exists yet.
|
||||||
|
|
||||||
|
If you add Busted tests, use this convention:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# all tests
|
||||||
|
busted
|
||||||
|
|
||||||
|
# single test file
|
||||||
|
busted spec/path/to/file_spec.lua
|
||||||
|
|
||||||
|
# single test by name filter
|
||||||
|
busted spec/path/to/file_spec.lua --filter "case name"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Coding conventions
|
||||||
|
|
||||||
|
Match existing style before introducing new patterns.
|
||||||
|
|
||||||
|
### Imports and module layout
|
||||||
|
|
||||||
|
- Put imports at the top under a section banner.
|
||||||
|
- Use `local X = require 'path'` with single-quoted module paths.
|
||||||
|
- Keep related imports vertically aligned when practical.
|
||||||
|
- Return module/class value at file end under `-- Module return` banner.
|
||||||
|
|
||||||
|
### Formatting
|
||||||
|
|
||||||
|
- Use 4-space indentation (no tabs).
|
||||||
|
- Keep blank lines between logical sections and method blocks.
|
||||||
|
- Preserve section banner style used throughout the codebase:
|
||||||
|
- `------------------------------------------------------------------------------`
|
||||||
|
- `-- Section Name`
|
||||||
|
- Prefer readable, compact methods; avoid deeply nested branches when simple guard clauses work.
|
||||||
|
|
||||||
|
### Types and annotations
|
||||||
|
|
||||||
|
- Use EmmyLua-style annotations where helpful and already used:
|
||||||
|
- `---@class`
|
||||||
|
- `---@param`
|
||||||
|
- `---@return`
|
||||||
|
- Keep annotations accurate when signatures change.
|
||||||
|
|
||||||
|
### Naming conventions
|
||||||
|
|
||||||
|
- Files/modules: lowercase with short names (e.g. `gstate.lua`, `sndmngr.lua`).
|
||||||
|
- Class-like tables: PascalCase locals (e.g. `GameState`, `SoundEffect`).
|
||||||
|
- Methods/functions: snake_case (except LÖVE callback names like `love.update`).
|
||||||
|
- Fields: snake_case (`next_state`, `current_menu`, `all_loaded`).
|
||||||
|
- Constants: UPPER_CASE for module constants (`SETTINGS_PATH`).
|
||||||
|
- Existing globals in `main.lua` (`Current_state`, `Fade`, etc.) are intentional legacy style; avoid creating new globals unless required.
|
||||||
|
|
||||||
|
### Object model and inheritance
|
||||||
|
|
||||||
|
- Use `make_class(...)` from `src/utils/classes.lua` for class-like inheritance.
|
||||||
|
- Constructors are `_init(...)` methods.
|
||||||
|
- Call base constructors explicitly (`Base._init(self, ...)`) when subclassing.
|
||||||
|
- Use colon syntax for instance methods (`function Class:method(...)`).
|
||||||
|
|
||||||
|
### State-management patterns
|
||||||
|
|
||||||
|
- Keep game-flow transitions explicit and deterministic.
|
||||||
|
- For game states, maintain the established lifecycle shape:
|
||||||
|
- `_init`
|
||||||
|
- `load`
|
||||||
|
- `update`
|
||||||
|
- `draw`
|
||||||
|
- `unload`
|
||||||
|
- input handlers as needed
|
||||||
|
- Return the next state index from `update` methods where applicable.
|
||||||
|
|
||||||
|
### Error handling and validation
|
||||||
|
|
||||||
|
- Prefer fail-fast `error(...)` for invariant violations (unknown keys/states, invalid config shape).
|
||||||
|
- Validate parsed external data before assignment (see `src/utils/settings.lua`).
|
||||||
|
- Keep error messages specific and actionable.
|
||||||
|
- Do not silently swallow invalid state transitions.
|
||||||
|
|
||||||
|
### Side effects and resources
|
||||||
|
|
||||||
|
- Keep resource load/unload explicit for assets, sounds, and UI elements.
|
||||||
|
- Ensure objects implementing asset-like behavior support `load`, `unload`, and `is_loaded` consistently.
|
||||||
|
- Be careful with global/shared mutable state (focus handling, active state IDs, fade transitions).
|
||||||
|
|
||||||
|
### Agent editing rules for this repo
|
||||||
|
|
||||||
|
- Prefer minimal, surgical changes that preserve existing architecture.
|
||||||
|
- Do not introduce new third-party dependencies unless explicitly requested.
|
||||||
|
- Do not rewrite naming/style globally unless asked.
|
||||||
|
- When fixing a bug, align with existing state-machine and class patterns.
|
||||||
|
- Update docs when behavior or developer workflow changes.
|
||||||
|
|
||||||
|
## Cursor and Copilot rules check
|
||||||
|
|
||||||
|
- `.cursor/rules/`: not present
|
||||||
|
- `.cursorrules`: not present
|
||||||
|
- `.github/copilot-instructions.md`: not present
|
||||||
|
|
||||||
|
If these files are added later, merge their guidance into this document and treat project-specific rules as higher priority.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
Copyright (c) 2025 WallyHackenslacker
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Colin's Spades for DOS
|
||||||
|
|
||||||
|
A port of the classic Freeverse card game Colin's Spades for MS-DOS. Made with LöveDOS.
|
||||||
|
|
||||||
|
## Assets used
|
||||||
|
|
||||||
|
All assets used are from Colin's Classic Cards, except as follows:
|
||||||
|
|
||||||
|
### Sprites
|
||||||
|
- [Cursors by Kenney](https://opengameart.org/content/cursor-pack-1)
|
||||||
|
|
||||||
|
### Textures
|
||||||
|
- [Floppy Disk image by Kutejnikov](https://opengameart.org/content/floppy-disk-0)
|
||||||
|
|
||||||
|
### Fonts
|
||||||
|
|
||||||
|
- [ChicagoFLF by Robin Casady](https://fontlibrary.org/en/font/chicagoflf)
|
||||||
|
- [Banana Brick by artmaker](https://fontlibrary.org/en/font/banana-brick)
|
||||||
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 318 B |
@@ -0,0 +1,242 @@
|
|||||||
|
# This is the configurationfile for DOSBox 0.74. (Please use the latest version of DOSBox)
|
||||||
|
# Lines starting with a # are commentlines and are ignored by DOSBox.
|
||||||
|
# They are used to (briefly) document the effect of each option.
|
||||||
|
|
||||||
|
[sdl]
|
||||||
|
# fullscreen: Start dosbox directly in fullscreen. (Press ALT-Enter to go back)
|
||||||
|
# fulldouble: Use double buffering in fullscreen. It can reduce screen flickering, but it can also result in a slow DOSBox.
|
||||||
|
# fullresolution: What resolution to use for fullscreen: original or fixed size (e.g. 1024x768).
|
||||||
|
# Using your monitor's native resolution with aspect=true might give the best results.
|
||||||
|
# If you end up with small window on a large screen, try an output different from surface.
|
||||||
|
# windowresolution: Scale the window to this size IF the output device supports hardware scaling.
|
||||||
|
# (output=surface does not!)
|
||||||
|
# output: What video system to use for output.
|
||||||
|
# Possible values: surface, overlay, opengl, openglnb, ddraw.
|
||||||
|
# autolock: Mouse will automatically lock, if you click on the screen. (Press CTRL-F10 to unlock)
|
||||||
|
# sensitivity: Mouse sensitivity.
|
||||||
|
# waitonerror: Wait before closing the console if dosbox has an error.
|
||||||
|
# priority: Priority levels for dosbox. Second entry behind the comma is for when dosbox is not focused/minimized.
|
||||||
|
# pause is only valid for the second entry.
|
||||||
|
# Possible values: lowest, lower, normal, higher, highest, pause.
|
||||||
|
# mapperfile: File used to load/save the key/event mappings from. Resetmapper only works with the defaul value.
|
||||||
|
# usescancodes: Avoid usage of symkeys, might not work on all operating systems.
|
||||||
|
|
||||||
|
fullscreen=false
|
||||||
|
fulldouble=true
|
||||||
|
fullresolution=original
|
||||||
|
windowresolution=original
|
||||||
|
output=opengl
|
||||||
|
autolock=true
|
||||||
|
sensitivity=100
|
||||||
|
waitonerror=true
|
||||||
|
priority=higher,normal
|
||||||
|
mapperfile=mapper.map
|
||||||
|
usescancodes=true
|
||||||
|
|
||||||
|
[dosbox]
|
||||||
|
# language: Select another language file.
|
||||||
|
# machine: The type of machine tries to emulate.
|
||||||
|
# Possible values: hercules, cga, tandy, pcjr, ega, vgaonly, svga_s3, svga_et3000, svga_et4000, svga_paradise, vesa_nolfb, vesa_oldvbe.
|
||||||
|
# captures: Directory where things like wave, midi, screenshot get captured.
|
||||||
|
# memsize: Amount of memory DOSBox has in megabytes.
|
||||||
|
# This value is best left at its default to avoid problems with some games,
|
||||||
|
# though few games might require a higher value.
|
||||||
|
# There is generally no speed advantage when raising this value.
|
||||||
|
|
||||||
|
language=
|
||||||
|
machine=svga_s3
|
||||||
|
captures=capture
|
||||||
|
memsize=128
|
||||||
|
|
||||||
|
[render]
|
||||||
|
# frameskip: How many frames DOSBox skips before drawing one.
|
||||||
|
# aspect: Do aspect correction, if your output method doesn't support scaling this can slow things down!.
|
||||||
|
# scaler: Scaler used to enlarge/enhance low resolution modes.
|
||||||
|
# If 'forced' is appended, then the scaler will be used even if the result might not be desired.
|
||||||
|
# Possible values: none, normal2x, normal3x, advmame2x, advmame3x, advinterp2x, advinterp3x, hq2x, hq3x, 2xsai, super2xsai, supereagle, tv2x, tv3x, rgb2x, rgb3x, scan2x, scan3x.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
frameskip=0
|
||||||
|
aspect=true
|
||||||
|
scaler=normal2x
|
||||||
|
|
||||||
|
[cpu]
|
||||||
|
# core: CPU Core used in emulation. auto will switch to dynamic if available and appropriate.
|
||||||
|
# Possible values: auto, dynamic, normal, simple.
|
||||||
|
# cputype: CPU Type used in emulation. auto is the fastest choice.
|
||||||
|
# Possible values: auto, 386, 386_slow, 486_slow, pentium_slow, 386_prefetch.
|
||||||
|
# cycles: Amount of instructions DOSBox tries to emulate each millisecond.
|
||||||
|
# Setting this value too high results in sound dropouts and lags.
|
||||||
|
# Cycles can be set in 3 ways:
|
||||||
|
# 'auto' tries to guess what a game needs.
|
||||||
|
# It usually works, but can fail for certain games.
|
||||||
|
# 'fixed #number' will set a fixed amount of cycles. This is what you usually need if 'auto' fails.
|
||||||
|
# (Example: fixed 4000).
|
||||||
|
# 'max' will allocate as much cycles as your computer is able to handle.
|
||||||
|
#
|
||||||
|
# Possible values: auto, fixed, max.
|
||||||
|
# cycleup: Amount of cycles to decrease/increase with keycombo.(CTRL-F11/CTRL-F12)
|
||||||
|
# cycledown: Setting it lower than 100 will be a percentage.
|
||||||
|
|
||||||
|
core=auto
|
||||||
|
cputype=auto
|
||||||
|
cycles=max
|
||||||
|
cycleup=1000
|
||||||
|
cycledown=1000
|
||||||
|
|
||||||
|
[mixer]
|
||||||
|
# nosound: Enable silent mode, sound is still emulated though.
|
||||||
|
# rate: Mixer sample rate, setting any device's rate higher than this will probably lower their sound quality.
|
||||||
|
# Possible values: 44100, 48000, 32000, 22050, 16000, 11025, 8000, 49716.
|
||||||
|
# blocksize: Mixer block size, larger blocks might help sound stuttering but sound will also be more lagged.
|
||||||
|
# Possible values: 1024, 2048, 4096, 8192, 512, 256.
|
||||||
|
# prebuffer: How many milliseconds of data to keep on top of the blocksize.
|
||||||
|
|
||||||
|
nosound=false
|
||||||
|
rate=44100
|
||||||
|
blocksize=1024
|
||||||
|
prebuffer=240
|
||||||
|
|
||||||
|
[midi]
|
||||||
|
# mpu401: Type of MPU-401 to emulate.
|
||||||
|
# Possible values: intelligent, uart, none.
|
||||||
|
# mididevice: Device that will receive the MIDI data from MPU-401.
|
||||||
|
# Possible values: default, win32, alsa, oss, coreaudio, coremidi, none.
|
||||||
|
# midiconfig: Special configuration options for the device driver. This is usually the id of the device you want to use.
|
||||||
|
# See the README/Manual for more details.
|
||||||
|
|
||||||
|
mpu401=intelligent
|
||||||
|
mididevice=default
|
||||||
|
midiconfig=
|
||||||
|
|
||||||
|
[sblaster]
|
||||||
|
# sbtype: Type of Soundblaster to emulate. gb is Gameblaster.
|
||||||
|
# Possible values: sb1, sb2, sbpro1, sbpro2, sb16, gb, none.
|
||||||
|
# sbbase: The IO address of the soundblaster.
|
||||||
|
# Possible values: 220, 240, 260, 280, 2a0, 2c0, 2e0, 300.
|
||||||
|
# irq: The IRQ number of the soundblaster.
|
||||||
|
# Possible values: 7, 5, 3, 9, 10, 11, 12.
|
||||||
|
# dma: The DMA number of the soundblaster.
|
||||||
|
# Possible values: 1, 5, 0, 3, 6, 7.
|
||||||
|
# hdma: The High DMA number of the soundblaster.
|
||||||
|
# Possible values: 1, 5, 0, 3, 6, 7.
|
||||||
|
# sbmixer: Allow the soundblaster mixer to modify the DOSBox mixer.
|
||||||
|
# oplmode: Type of OPL emulation. On 'auto' the mode is determined by sblaster type. All OPL modes are Adlib-compatible, except for 'cms'.
|
||||||
|
# Possible values: auto, cms, opl2, dualopl2, opl3, none.
|
||||||
|
# oplemu: Provider for the OPL emulation. compat might provide better quality (see oplrate as well).
|
||||||
|
# Possible values: default, compat, fast.
|
||||||
|
# oplrate: Sample rate of OPL music emulation. Use 49716 for highest quality (set the mixer rate accordingly).
|
||||||
|
# Possible values: 44100, 49716, 48000, 32000, 22050, 16000, 11025, 8000.
|
||||||
|
|
||||||
|
sbtype=sb16
|
||||||
|
sbbase=220
|
||||||
|
irq=7
|
||||||
|
dma=1
|
||||||
|
hdma=5
|
||||||
|
sbmixer=true
|
||||||
|
oplmode=opl2
|
||||||
|
oplemu=compat
|
||||||
|
oplrate=44100
|
||||||
|
|
||||||
|
[gus]
|
||||||
|
# gus: Enable the Gravis Ultrasound emulation.
|
||||||
|
# gusrate: Sample rate of Ultrasound emulation.
|
||||||
|
# Possible values: 44100, 48000, 32000, 22050, 16000, 11025, 8000, 49716.
|
||||||
|
# gusbase: The IO base address of the Gravis Ultrasound.
|
||||||
|
# Possible values: 240, 220, 260, 280, 2a0, 2c0, 2e0, 300.
|
||||||
|
# gusirq: The IRQ number of the Gravis Ultrasound.
|
||||||
|
# Possible values: 5, 3, 7, 9, 10, 11, 12.
|
||||||
|
# gusdma: The DMA channel of the Gravis Ultrasound.
|
||||||
|
# Possible values: 3, 0, 1, 5, 6, 7.
|
||||||
|
# ultradir: Path to Ultrasound directory. In this directory
|
||||||
|
# there should be a MIDI directory that contains
|
||||||
|
# the patch files for GUS playback. Patch sets used
|
||||||
|
# with Timidity should work fine.
|
||||||
|
|
||||||
|
gus=false
|
||||||
|
gusrate=44100
|
||||||
|
gusbase=240
|
||||||
|
gusirq=5
|
||||||
|
gusdma=3
|
||||||
|
ultradir=C:\ULTRASND
|
||||||
|
|
||||||
|
[speaker]
|
||||||
|
# pcspeaker: Enable PC-Speaker emulation.
|
||||||
|
# pcrate: Sample rate of the PC-Speaker sound generation.
|
||||||
|
# Possible values: 44100, 48000, 32000, 22050, 16000, 11025, 8000, 49716.
|
||||||
|
# tandy: Enable Tandy Sound System emulation. For 'auto', emulation is present only if machine is set to 'tandy'.
|
||||||
|
# Possible values: auto, on, off.
|
||||||
|
# tandyrate: Sample rate of the Tandy 3-Voice generation.
|
||||||
|
# Possible values: 44100, 48000, 32000, 22050, 16000, 11025, 8000, 49716.
|
||||||
|
# disney: Enable Disney Sound Source emulation. (Covox Voice Master and Speech Thing compatible).
|
||||||
|
|
||||||
|
pcspeaker=true
|
||||||
|
pcrate=44100
|
||||||
|
tandy=auto
|
||||||
|
tandyrate=44100
|
||||||
|
disney=true
|
||||||
|
|
||||||
|
[joystick]
|
||||||
|
# joysticktype: Type of joystick to emulate: auto (default), none,
|
||||||
|
# 2axis (supports two joysticks),
|
||||||
|
# 4axis (supports one joystick, first joystick used),
|
||||||
|
# 4axis_2 (supports one joystick, second joystick used),
|
||||||
|
# fcs (Thrustmaster), ch (CH Flightstick).
|
||||||
|
# none disables joystick emulation.
|
||||||
|
# auto chooses emulation depending on real joystick(s).
|
||||||
|
# (Remember to reset dosbox's mapperfile if you saved it earlier)
|
||||||
|
# Possible values: auto, 2axis, 4axis, 4axis_2, fcs, ch, none.
|
||||||
|
# timed: enable timed intervals for axis. Experiment with this option, if your joystick drifts (away).
|
||||||
|
# autofire: continuously fires as long as you keep the button pressed.
|
||||||
|
# swap34: swap the 3rd and the 4th axis. can be useful for certain joysticks.
|
||||||
|
# buttonwrap: enable button wrapping at the number of emulated buttons.
|
||||||
|
|
||||||
|
joysticktype=auto
|
||||||
|
timed=true
|
||||||
|
autofire=false
|
||||||
|
swap34=false
|
||||||
|
buttonwrap=false
|
||||||
|
|
||||||
|
[serial]
|
||||||
|
# serial1: set type of device connected to com port.
|
||||||
|
# Can be disabled, dummy, modem, nullmodem, directserial.
|
||||||
|
# Additional parameters must be in the same line in the form of
|
||||||
|
# parameter:value. Parameter for all types is irq (optional).
|
||||||
|
# for directserial: realport (required), rxdelay (optional).
|
||||||
|
# (realport:COM1 realport:ttyS0).
|
||||||
|
# for modem: listenport (optional).
|
||||||
|
# for nullmodem: server, rxdelay, txdelay, telnet, usedtr,
|
||||||
|
# transparent, port, inhsocket (all optional).
|
||||||
|
# Example: serial1=modem listenport:5000
|
||||||
|
# Possible values: dummy, disabled, modem, nullmodem, directserial.
|
||||||
|
# serial2: see serial1
|
||||||
|
# Possible values: dummy, disabled, modem, nullmodem, directserial.
|
||||||
|
# serial3: see serial1
|
||||||
|
# Possible values: dummy, disabled, modem, nullmodem, directserial.
|
||||||
|
# serial4: see serial1
|
||||||
|
# Possible values: dummy, disabled, modem, nullmodem, directserial.
|
||||||
|
|
||||||
|
serial1=dummy
|
||||||
|
serial2=dummy
|
||||||
|
serial3=disabled
|
||||||
|
serial4=disabled
|
||||||
|
|
||||||
|
[dos]
|
||||||
|
# xms: Enable XMS support.
|
||||||
|
# ems: Enable EMS support.
|
||||||
|
# umb: Enable UMB support.
|
||||||
|
# keyboardlayout: Language code of the keyboard layout (or none).
|
||||||
|
|
||||||
|
xms=true
|
||||||
|
ems=true
|
||||||
|
umb=true
|
||||||
|
keyboardlayout=auto
|
||||||
|
|
||||||
|
[ipx]
|
||||||
|
# ipx: Enable ipx over UDP/IP emulation.
|
||||||
|
|
||||||
|
Enable=0
|
||||||
|
Connection=0
|
||||||
|
ipx=false
|
||||||
@@ -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.
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Imports
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local love = require 'love'
|
||||||
|
local game_states = require 'src.states'
|
||||||
|
local settings = require 'src.utils.settings'
|
||||||
|
local Fader = require 'src.graphics.fader'
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Variables
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Current_state = 1
|
||||||
|
Fade = Fader()
|
||||||
|
Change_state = false
|
||||||
|
Debug = false
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Helper methods
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local function parse_args(args)
|
||||||
|
for _, v in pairs(args) do
|
||||||
|
if v == '-debug' then Debug = true end
|
||||||
|
if v == '-skip-intro' then Current_state = 2 end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Game methods
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function love.load(args)
|
||||||
|
parse_args(args)
|
||||||
|
|
||||||
|
-- Obtain the settings for the game.
|
||||||
|
settings:load_settings()
|
||||||
|
|
||||||
|
-- Load the assets of the intro game state.
|
||||||
|
game_states[Current_state]:load()
|
||||||
|
|
||||||
|
-- Call a fade in after the intro assets are loaded.
|
||||||
|
Fade:fade_in()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function love.update(dt)
|
||||||
|
local new_state = game_states[Current_state]:update(dt)
|
||||||
|
|
||||||
|
-- If the game state changed then trigger a fade out.
|
||||||
|
if new_state ~= Current_state and Fade.done then
|
||||||
|
Fade:fade_out()
|
||||||
|
Change_state = true
|
||||||
|
end
|
||||||
|
|
||||||
|
Fade:update(dt)
|
||||||
|
|
||||||
|
-- Update the game state.
|
||||||
|
if game_states[new_state] == nil then
|
||||||
|
-- If the new state doesn't exist then wait for the fade out to end and quit.
|
||||||
|
if Fade.done then
|
||||||
|
game_states[Current_state]:unload(dt)
|
||||||
|
love.event.quit()
|
||||||
|
|
||||||
|
-- Save the settings before quitting just in case.
|
||||||
|
settings:save_settings()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- If the new state exists then unload it's data and set the new state.
|
||||||
|
if Change_state and Fade.done then
|
||||||
|
game_states[Current_state]:unload(dt)
|
||||||
|
Current_state = new_state
|
||||||
|
Change_state = false
|
||||||
|
Fade:fade_in()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function love.draw()
|
||||||
|
love.graphics.clear()
|
||||||
|
game_states[Current_state]:draw()
|
||||||
|
Fade:draw()
|
||||||
|
|
||||||
|
if Debug then
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
|
||||||
|
function love.keypressed(key, code, isrepeat)
|
||||||
|
-- Send events to the active game state if there is no fade active.
|
||||||
|
if Fade.done then
|
||||||
|
game_states[Current_state]:keypressed(key, code, isrepeat)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function love.keyreleased(key, code)
|
||||||
|
-- Send events to the active game state if there is no fade active.
|
||||||
|
if Fade.done then
|
||||||
|
game_states[Current_state]:keyreleased(key, code)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function love.textinput(text)
|
||||||
|
-- Send events to the active game state if there is no fade active.
|
||||||
|
if Fade.done then
|
||||||
|
game_states[Current_state]:textinput(text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function love.mousemoved(x, y, dx, dy)
|
||||||
|
-- Send events to the active game state if there is no fade active.
|
||||||
|
if Fade.done then
|
||||||
|
game_states[Current_state]:mousemoved(x, y, dx, dy)
|
||||||
|
end
|
||||||
|
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]:mousepressed(x, y, btn)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function love.mousereleased(x, y, btn)
|
||||||
|
-- Send events to the active game state if there is no fade active.
|
||||||
|
if Fade.done then
|
||||||
|
game_states[Current_state]:mousereleased(x, y, btn)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Imports
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local make_class = require 'src.utils.classes'
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Class definitions
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
---@class Drawable
|
||||||
|
local Drawable = make_class()
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Class methods
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function Drawable:_init()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Drawable:update(_)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Drawable:draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Drawable:keypressed(_, _ , _)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Drawable:textinput(_)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Drawable:keyreleased(_, _)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Drawable:mousemoved(_, _, _, _)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Drawable:mousepressed(_, _, _)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Drawable:mousereleased(_, _, _)
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Module return
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return Drawable
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Imports
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local love = require 'love'
|
||||||
|
local make_class = require 'src.utils.classes'
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Class definitions
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local Fader = make_class()
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Class methods
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function Fader:_init()
|
||||||
|
self.t = 0.0
|
||||||
|
self.step = 0
|
||||||
|
self.done = true
|
||||||
|
self.reverse = false
|
||||||
|
self.callback = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Fader:_reset()
|
||||||
|
self.t = 0
|
||||||
|
self.step = 0
|
||||||
|
self.done = false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Fader:fade_in(callback)
|
||||||
|
self:_reset()
|
||||||
|
self.reverse = true
|
||||||
|
self.callback = callback
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Fader:fade_out(callback)
|
||||||
|
self:_reset()
|
||||||
|
self.reverse = false
|
||||||
|
self.callback = callback
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Fader:update(dt)
|
||||||
|
-- While the fader is active.
|
||||||
|
if not self.done then
|
||||||
|
-- Update the internal timer.
|
||||||
|
self.t = self.t + dt
|
||||||
|
|
||||||
|
-- Advance the fade if enough time has passed.
|
||||||
|
if self.t >= 0.009 then
|
||||||
|
self.step = self.step + 1
|
||||||
|
self.t = 0.0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Mark as done when all fade tiles have been shown/hidden.
|
||||||
|
if self.step > 175 then
|
||||||
|
self.done = true
|
||||||
|
|
||||||
|
-- Execute the callback if any.
|
||||||
|
if self.callback ~= nil then
|
||||||
|
self.callback()
|
||||||
|
self.callback = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Fader:draw()
|
||||||
|
local c = 1
|
||||||
|
|
||||||
|
-- Set the render color to black.
|
||||||
|
love.graphics.setColor(0, 0, 0)
|
||||||
|
|
||||||
|
-- For every fader tile.
|
||||||
|
for i = 0, 16 do
|
||||||
|
for j = 0, 10 do
|
||||||
|
-- Check if this is a fade in or fade out.
|
||||||
|
if not self.reverse then
|
||||||
|
-- If it's a fade out then draw black tiles until c.
|
||||||
|
if c <= self.step then
|
||||||
|
love.graphics.rectangle('fill', 20 * i, 20 * j, 20, 20)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- If it's a fade in then draw black tiles with indices higher than c.
|
||||||
|
if c > self.step then
|
||||||
|
love.graphics.rectangle('fill', 20 * i, 20 * j, 20, 20)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Advance to the next tile to show/hide.
|
||||||
|
c = c + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Reset the render color to white.
|
||||||
|
love.graphics.setColor()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Module return
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return Fader
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Imports
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
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, Asset)
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Class methods
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
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()
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
function Sprite:draw()
|
||||||
|
if self.sprite ~= nil then
|
||||||
|
love.graphics.draw(self.sprite, self.x, self.y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Module return
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return Sprite
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Imports
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local make_class = require 'src.utils.classes'
|
||||||
|
local GameState = require 'src.gstates.gstate'
|
||||||
|
local Sprite = require 'src.graphics.sprite'
|
||||||
|
local Fader = require 'src.graphics.fader'
|
||||||
|
local SoundEffect = require 'src.sound.sfx'
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Class definitions
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local Game = make_class(GameState)
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Class methods
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function Game:_init(name, index)
|
||||||
|
GameState._init(self, name, index)
|
||||||
|
self.skip = false
|
||||||
|
self.fade = Fader()
|
||||||
|
self.stage = 0
|
||||||
|
self.timer = 0
|
||||||
|
|
||||||
|
self.splash = Sprite('imgs/splash.png')
|
||||||
|
self.author = Sprite('imgs/wally.png')
|
||||||
|
self.title = Sprite('imgs/title.png')
|
||||||
|
|
||||||
|
self.splash_snd = SoundEffect('snd/jachiev.wav')
|
||||||
|
self.author_snd = SoundEffect('snd/alang.wav')
|
||||||
|
self.title_snd = SoundEffect('snd/ablhole.wav')
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Game:load()
|
||||||
|
-- Load all assets.
|
||||||
|
self.splash:load()
|
||||||
|
self.author:load()
|
||||||
|
self.title:load()
|
||||||
|
self.splash_snd:load()
|
||||||
|
self.author_snd:load()
|
||||||
|
self.title_snd:load()
|
||||||
|
|
||||||
|
-- Play the initial sound effect.
|
||||||
|
self.splash_snd:play()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Game:update(dt)
|
||||||
|
local total_time
|
||||||
|
|
||||||
|
-- Set time to wait for each intro stage.
|
||||||
|
if self.stage == 0 then
|
||||||
|
total_time = 6.5
|
||||||
|
elseif self.stage == 1 then
|
||||||
|
total_time = 5.0
|
||||||
|
else
|
||||||
|
total_time = 8.0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Start counting the stage time when the fade in is done.
|
||||||
|
if self.fade.done then
|
||||||
|
self.timer = self.timer + dt
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if it's time to change stage.
|
||||||
|
if self.timer >= total_time then
|
||||||
|
self.fade:fade_out(
|
||||||
|
function ()
|
||||||
|
-- Change the stage and play the corresponding sound.
|
||||||
|
self.stage = self.stage + 1
|
||||||
|
|
||||||
|
if self.stage == 1 then
|
||||||
|
self.author_snd:play()
|
||||||
|
elseif self.stage == 2 then
|
||||||
|
self.title_snd:play()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Request a fade in.
|
||||||
|
self.fade:fade_in()
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Reset the stage timer.
|
||||||
|
self.timer = 0.0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 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 and self.stage < 3 then return self.index else return 2 end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Game:draw()
|
||||||
|
-- If there is data to draw, then draw the current stage's backdrop.
|
||||||
|
if self.stage == 0 then self.splash:draw()
|
||||||
|
elseif self.stage == 1 then self.author:draw()
|
||||||
|
elseif self.stage == 2 then self.title:draw() end
|
||||||
|
|
||||||
|
-- Draw the fader if needed.
|
||||||
|
self.fade:draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Game:unload()
|
||||||
|
-- Null all assets so they can be claimed by the GC.
|
||||||
|
self.splash:unload()
|
||||||
|
self.author:unload()
|
||||||
|
self.title:unload()
|
||||||
|
self.splash_snd:unload()
|
||||||
|
self.author_snd:unload()
|
||||||
|
self.title_snd:unload()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Game:keypressed(_)
|
||||||
|
-- Skip the intro on any key press.
|
||||||
|
self.skip = true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Module return
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return Game
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Imports
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local make_class = require 'src.utils.classes'
|
||||||
|
local assets = require 'src.utils.asstmngr'
|
||||||
|
local Drawable = require 'src.graphics.drawable'
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Class definitions
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local GameState = make_class(Drawable)
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Class methods
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function GameState:_init(name, index)
|
||||||
|
Drawable._init(self)
|
||||||
|
self.name = name
|
||||||
|
self.index = index
|
||||||
|
self.next_state = self.index
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function GameState:update(_)
|
||||||
|
return self.next_state
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function GameState:load()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function GameState:unload()
|
||||||
|
assets:unload(self.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Module return
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return GameState
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Imports
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local love = require 'love'
|
||||||
|
local assets = require 'src.utils.asstmngr'
|
||||||
|
local dialog_manager = require 'src.ui.dlgmngr'
|
||||||
|
local make_class = require 'src.utils.classes'
|
||||||
|
local GameState = require 'src.gstates.gstate'
|
||||||
|
local Fader = require 'src.graphics.fader'
|
||||||
|
local Cursor = require 'src.ui.cursor'
|
||||||
|
local SoundEffect = require 'src.sound.sfx'
|
||||||
|
local Sprite = require 'src.graphics.sprite'
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Class definitions
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local Menu = make_class(GameState)
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Class methods
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function Menu:_init(name, index)
|
||||||
|
GameState._init(self, name, index)
|
||||||
|
self.fade = Fader()
|
||||||
|
|
||||||
|
-- Sprites.
|
||||||
|
self.backg = Sprite('imgs/main.png')
|
||||||
|
self.gamesel = Sprite('imgs/mainsel1.png')
|
||||||
|
self.quitsel = Sprite('imgs/mainsel2.png')
|
||||||
|
self.gameclick = Sprite('imgs/mnclck1.png')
|
||||||
|
self.quitclick = Sprite('imgs/mnclck2.png')
|
||||||
|
|
||||||
|
-- Create sound effects.
|
||||||
|
self.splash = SoundEffect('bgm/Opening.wav')
|
||||||
|
self.quit_snd = SoundEffect('snd/byebye.wav')
|
||||||
|
self.click_snd = SoundEffect('snd/MouseDwn.wav')
|
||||||
|
self.roll_snd = SoundEffect('snd/RollOver.wav')
|
||||||
|
|
||||||
|
-- Create a mouse cursor object at the current mouse position.
|
||||||
|
local mx, my = love.mouse.getPosition()
|
||||||
|
self.cursor = Cursor(mx, my)
|
||||||
|
|
||||||
|
-- Register all assets.
|
||||||
|
assets:register(self.name, self.cursor)
|
||||||
|
assets:register(self.name, self.splash)
|
||||||
|
assets:register(self.name, self.quit_snd)
|
||||||
|
assets:register(self.name, self.click_snd)
|
||||||
|
assets:register(self.name, self.roll_snd)
|
||||||
|
assets:register(self.name, self.backg)
|
||||||
|
assets:register(self.name, self.gamesel)
|
||||||
|
assets:register(self.name, self.quitsel)
|
||||||
|
assets:register(self.name, self.gameclick)
|
||||||
|
assets:register(self.name, self.quitclick)
|
||||||
|
|
||||||
|
-- Attributes
|
||||||
|
self.game_selected = false
|
||||||
|
self.quit_selected = false
|
||||||
|
self.game_clicked = false
|
||||||
|
self.quit_clicked = false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Menu:load()
|
||||||
|
-- Load basic assets.
|
||||||
|
self.backg:load()
|
||||||
|
self.splash:load()
|
||||||
|
|
||||||
|
-- Play the initial sound effect.
|
||||||
|
self.splash:play()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Menu:update(dt)
|
||||||
|
if self.all_loaded then
|
||||||
|
self.fade:update(dt)
|
||||||
|
else
|
||||||
|
local done = true
|
||||||
|
assets:update(self.name)
|
||||||
|
self.all_loaded = assets:done(self.name) and done
|
||||||
|
end
|
||||||
|
|
||||||
|
return self.next_state
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Menu:draw()
|
||||||
|
if self.all_loaded then
|
||||||
|
if self.game_selected and not self.game_clicked then
|
||||||
|
self.gamesel:draw()
|
||||||
|
elseif self.game_selected and self.game_clicked then
|
||||||
|
self.gameclick:draw()
|
||||||
|
elseif self.quit_selected and not self.quit_clicked then
|
||||||
|
self.quitsel:draw()
|
||||||
|
elseif self.quit_selected and self.quit_clicked then
|
||||||
|
self.quitclick:draw()
|
||||||
|
else
|
||||||
|
self.backg:draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
self.cursor:draw()
|
||||||
|
self.fade:draw()
|
||||||
|
else
|
||||||
|
assets:draw()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Menu:mousemoved(x, y, dx, dy)
|
||||||
|
self.cursor:mousemoved(x, y, dx, dy)
|
||||||
|
|
||||||
|
if self:IsCursorInGameBtn(x, y) then
|
||||||
|
if not self.game_selected then
|
||||||
|
self.roll_snd:play()
|
||||||
|
end
|
||||||
|
self.game_selected = true
|
||||||
|
|
||||||
|
else
|
||||||
|
self.game_selected = false
|
||||||
|
end
|
||||||
|
|
||||||
|
if self:IsCursorInQuitBtn(x, y) then
|
||||||
|
if not self.quit_selected then
|
||||||
|
self.roll_snd:play()
|
||||||
|
end
|
||||||
|
self.quit_selected = true
|
||||||
|
|
||||||
|
else
|
||||||
|
self.quit_selected = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Menu:mousepressed(x, y, btn)
|
||||||
|
if self.fade.done then
|
||||||
|
if self.game_selected then
|
||||||
|
self.game_clicked = true
|
||||||
|
self.quit_clicked = false
|
||||||
|
elseif self.quit_selected then
|
||||||
|
self.game_clicked = false
|
||||||
|
self.quit_clicked = true
|
||||||
|
else
|
||||||
|
self.game_clicked = false
|
||||||
|
self.quit_clicked = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Menu:mousereleased(x, y, btn)
|
||||||
|
if self.fade.done then
|
||||||
|
if self.game_selected and self.game_clicked then
|
||||||
|
self.next_state = 2
|
||||||
|
elseif self.quit_selected and self.quit_clicked then
|
||||||
|
self.next_state = -1
|
||||||
|
self.quit_snd:play()
|
||||||
|
end
|
||||||
|
|
||||||
|
self.game_clicked = false
|
||||||
|
self.quit_clicked = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Menu:IsCursorInGameBtn(x, y)
|
||||||
|
return x >= 224 and x <= 224 + 89 and y >= 131 and y <= 131 + 30
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Menu:IsCursorInQuitBtn(x, y)
|
||||||
|
return x >= 243 and x <= 243 + 51 and y >= 173 and y <= 173 + 24
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Module return
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return Menu
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Imports
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local love = require 'love'
|
||||||
|
local make_class = require 'src.utils.classes'
|
||||||
|
local Asset = require 'src.utils.asset'
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Class definitions
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- SoundEffect is a simple wrapper around Löve's Source class to allow for
|
||||||
|
-- easy loading/unloading and automatically checking if a sound effect is valid
|
||||||
|
-- before using it.
|
||||||
|
local SoundEffect = make_class(Asset)
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Class methods
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function SoundEffect:_init(file_name)
|
||||||
|
Asset._init(self, file_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function SoundEffect:load()
|
||||||
|
if not self:is_loaded() then
|
||||||
|
self.sfx = love.audio.newSource(self.file_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function SoundEffect:unload()
|
||||||
|
if self:isPlaying() then self:stop() end
|
||||||
|
self.sfx = nil
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
function SoundEffect:setPitch(pitch)
|
||||||
|
if self.sfx ~= nil then self.sfx:setPitch(pitch) end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function SoundEffect:setLooping(enable)
|
||||||
|
if self.sfx ~= nil then self.sfx:setLooping(enable) end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function SoundEffect:getDuration()
|
||||||
|
if self.sfx ~= nil then
|
||||||
|
return self.sfx:getDuration()
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function SoundEffect:isPlaying()
|
||||||
|
return self.sfx ~= nil and self.sfx:isPlaying()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function SoundEffect:isPaused()
|
||||||
|
return self.sfx ~= nil and self.sfx:isPaused()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function SoundEffect:isStopped()
|
||||||
|
return self.sfx ~= nil and self.sfx:isStopped()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function SoundEffect:tell()
|
||||||
|
if self.sfx ~= nil then
|
||||||
|
return self.sfx:tell()
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function SoundEffect:play()
|
||||||
|
if self.sfx ~= nil then self.sfx:play() end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function SoundEffect:pause()
|
||||||
|
if self.sfx ~= nil then self.sfx:pause() end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function SoundEffect:stop()
|
||||||
|
if self.sfx ~= nil then self.sfx:stop() end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Module return
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return SoundEffect
|
||||||
@@ -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()
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Imports
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local Menu = require 'src.gstates.menu'
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Variables
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Table of all valid game states.
|
||||||
|
local states = {
|
||||||
|
Menu('menu', 1),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Module return
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return states
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Imports
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local make_class = require 'src.utils.classes'
|
||||||
|
local Sprite = require 'src.graphics.sprite'
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Class definitions
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local Cursor = make_class(Sprite)
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Class methods
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function Cursor:_init(x, y)
|
||||||
|
Sprite._init(self, 'imgs/pointer.png', x, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Cursor:mousemoved(x, y)
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Module return
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return Cursor
|
||||||
@@ -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
|
||||||
@@ -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()
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Imports
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local make_class = require 'src.utils.classes'
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Class definitions
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
---@class Asset
|
||||||
|
local Asset = make_class()
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Class methods
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Module return
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return Asset
|
||||||
@@ -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()
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Methods
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- 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, bases = {}, {...}
|
||||||
|
|
||||||
|
-- copy base class contents into the new class
|
||||||
|
for _, base in ipairs(bases) do
|
||||||
|
for k, v in pairs(base) do
|
||||||
|
cls[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set the class's __index, and start filling an "is_a" table that contains this class and all of its bases
|
||||||
|
-- so you can do an "instance of" check using my_instance.is_a[MyClass]
|
||||||
|
cls.__index, cls.is_a = cls, {[cls] = 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
|
||||||
|
setmetatable(cls, {__call = function (c, ...)
|
||||||
|
local instance = setmetatable({}, c)
|
||||||
|
|
||||||
|
-- run the init method if it's there
|
||||||
|
local init = instance._init
|
||||||
|
|
||||||
|
if init then init(instance, ...) end
|
||||||
|
return instance
|
||||||
|
end})
|
||||||
|
|
||||||
|
-- return the new class table, that's ready to fill with methods
|
||||||
|
return cls
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Module return
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return make_class
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Methods
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- From https://stackoverflow.com/a/19327160
|
||||||
|
-- Cargo cult programming ahoy!!
|
||||||
|
local function magiclines( str )
|
||||||
|
local pos
|
||||||
|
pos = 1
|
||||||
|
|
||||||
|
return function()
|
||||||
|
if not pos then return nil end
|
||||||
|
|
||||||
|
local p1, p2 = string.find( str, "\r?\n", pos )
|
||||||
|
local line
|
||||||
|
|
||||||
|
if p1 then
|
||||||
|
line = str:sub( pos, p1 - 1 )
|
||||||
|
pos = p2 + 1
|
||||||
|
else
|
||||||
|
line = str:sub( pos )
|
||||||
|
pos = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return line
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Module return
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return magiclines
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Imports
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local love = require 'love'
|
||||||
|
local magiclines = require 'src.utils.mgclines'
|
||||||
|
local make_class = require 'src.utils.classes'
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Constants
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local SETTINGS_PATH = 'settings.ini'
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Module definitions
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
---@class Settings
|
||||||
|
local Settings = make_class()
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Private module methods
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
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
|
||||||
|
local k, v = string.match(line, '(%w+)=(.+)')
|
||||||
|
|
||||||
|
if k and v then
|
||||||
|
-- If the line matches then check if it has a known settings key.
|
||||||
|
if self[k] ~= nil then
|
||||||
|
-- If the key is valid then cast the value to it's expected data type and store it.
|
||||||
|
-- Fail if the cast is not doable.
|
||||||
|
if v == 'true' then
|
||||||
|
if type(self[k]) ~= 'boolean' then error(string.format('Invalid value for %s in settings', k)) end
|
||||||
|
self[k] = true
|
||||||
|
elseif v == 'false' then
|
||||||
|
if type(self[k]) ~= 'boolean' then error(string.format('Invalid value for %s in settings', k)) end
|
||||||
|
self[k] = false
|
||||||
|
elseif tonumber(v) then
|
||||||
|
if type(self[k]) ~= 'number' then error(string.format('Invalid value for %s in settings', k)) end
|
||||||
|
self[k] = tonumber(v)
|
||||||
|
else
|
||||||
|
self[k] = v
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Fail on an unknown key.
|
||||||
|
error(string.format('Unknown settings key "%s"', k))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Settings:_get_settings_str()
|
||||||
|
-- Start with the settings category.
|
||||||
|
local s = '[settings]\n'
|
||||||
|
|
||||||
|
-- For each value in the settings table.
|
||||||
|
for k, v in pairs(self) do
|
||||||
|
-- If the value isn't a function then it is a setting.
|
||||||
|
if type(v) ~= 'function' then
|
||||||
|
-- Format the value as key=value.
|
||||||
|
s = s .. string.format('%s=%s\n', k, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
love.filesystem.write(SETTINGS_PATH, setts)
|
||||||
|
return setts
|
||||||
|
else
|
||||||
|
if not love.filesystem.isFile(SETTINGS_PATH) then
|
||||||
|
-- Fail if the settings path points to a directory.
|
||||||
|
error ("Settings path exists and isn't a file")
|
||||||
|
else
|
||||||
|
-- Else read the settings.
|
||||||
|
return love.filesystem.read(SETTINGS_PATH)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Public module methods
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
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()
|
||||||
|
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())
|
||||||
|
else
|
||||||
|
-- Fail if the settings path points to a directory.
|
||||||
|
error("Settings path exists and isn't a file")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- Module return
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return Settings()
|
||||||