72 Commits
main ... reset

Author SHA1 Message Date
57995a0e00 Remove train station. 2024-11-24 11:18:08 -06:00
53dc496f2f Dawn progress 2024-11-07 18:28:54 -06:00
6a0ffd4a45 about to start work on downtown 2024-10-25 23:02:55 -05:00
ef84c85527 Cleaned a bit of the tile code. Built train station 2024-10-25 10:30:49 -05:00
39d74cc375 Map loading fixes and improvements 2024-10-22 08:00:48 -05:00
ddbef64f43 Started work on using tiled 2024-10-21 20:50:16 -05:00
b2a3ca4411 Example sign with conversation. 2024-10-20 15:31:04 -07:00
a74f285cb2 Memory functions 2024-10-17 09:30:36 -07:00
5fb36aad35 Moved tile interactions over. 2024-10-17 08:44:17 -07:00
258976b76c Made NPCs interactable with the conversation system. 2024-10-17 07:36:33 -07:00
7867076bbe Added language support to main menu and sign 2024-10-17 07:17:36 -07:00
3baafec9cb Adding language 2024-10-16 14:37:34 -07:00
6c062df9bb Added main menu. 2024-10-09 09:47:05 -05:00
825cc88e17 Moving some logic into separate files 2024-10-08 09:36:15 -05:00
9f4cb283c1 UI Menu more or less done perfectly. 2024-10-08 09:17:00 -05:00
86feb7e56a Mid menu coding 2024-10-07 09:49:22 -05:00
a7dc7fdcf8 Remove testmap2 2024-10-07 00:29:59 -05:00
cb3a58e456 Door and map changing 2024-10-07 00:27:20 -05:00
5334f0944e Add direction to the door 2024-10-06 23:21:28 -05:00
a1d4b0a1d7 Door 2024-10-06 23:11:45 -05:00
ecb3b9c5d1 Fixed JSON parsing throwing errors 2024-10-06 22:46:16 -05:00
bf3912bb7f Added sign 2024-10-06 22:23:26 -05:00
b5d7b7e229 Bit more cleanup 2024-10-06 21:01:05 -05:00
b7987401af Map loading basically done for now 2024-10-06 19:03:22 -05:00
ffc46c677c Added triggers to parsing. 2024-10-06 18:57:28 -05:00
7a8ca2fca1 Test loading asset data. 2024-10-06 18:52:52 -05:00
5751f7c83c Add asset loading support. 2024-10-06 18:27:54 -05:00
c69d0ec1cc Adding triggers, prepping for map loading. 2024-10-06 18:03:46 -05:00
e14445680f Add newlines to draw text 2024-10-06 17:30:04 -05:00
5256b32055 Textbox rendering back. 2024-10-06 17:16:24 -05:00
5444b0b8c7 Ensure linux terminal is still working 2024-10-06 16:43:54 -05:00
45b3cf9478 Cleaned old frame code. 2024-10-06 16:34:47 -05:00
0224ddd36b Getting around to cleaning drawing code finally. 2024-10-06 16:28:34 -05:00
40b0395552 idk what I coded 2024-10-06 01:30:45 -05:00
b7829fda5c Add tile interactions. 2024-10-05 10:29:15 -05:00
bdef59bbe1 Make map rendering centered 2024-10-05 10:17:12 -05:00
27ce6526e6 Vastly improve UI 2024-10-05 09:16:41 -05:00
704002e671 Moved TERM files around 2024-10-05 08:35:36 -05:00
cfe4cf6cad Testing mostly 2024-10-04 22:43:33 -05:00
69672cf8a6 Rendering working 2024-10-04 21:51:38 -05:00
c1345218a3 glfw 2024-10-04 16:41:01 -05:00
be27408cf4 Adding back GLFW 2024-10-04 09:37:32 -05:00
4205b919d4 Adding back GLFW 2024-10-04 09:37:24 -05:00
e14083b4b4 t 2024-10-03 22:35:22 -05:00
ad317da97e Reset 2024-10-03 20:09:10 -05:00
2ff1a159bc idk 2024-10-03 20:07:35 -05:00
c770953b2e VN Dummy 2024-09-29 00:47:12 -05:00
2cb1d745b2 Add JSON parser 2024-09-27 21:55:53 -05:00
8ce0e8f9f6 Changed more deps to use FetchContent 2024-09-25 15:11:04 -04:00
5101a856be Remove glm 2024-09-25 15:01:26 -04:00
303d0c0a6f Add camera pixel perfect. 2024-09-16 21:35:04 -05:00
e3a4368d1e Basic tile code ready. 2024-09-16 07:07:01 -05:00
7c34127900 Some more chunk work. 2024-09-16 06:50:47 -05:00
49d90b3362 Working on chunks 2024-09-15 08:41:44 -05:00
935398d45e Minigolf is pog 2024-09-15 08:06:45 -05:00
4065556d4a Switched map loops to weakptrs 2024-09-15 07:50:11 -05:00
fb8f6e3f95 Commit prog 2024-09-14 10:23:31 -05:00
b4c2ce16a0 idk if I like this structure. 2024-09-13 09:26:21 -05:00
b309478922 Trying to tune turning. 2024-09-11 10:07:23 -05:00
ad3974bde5 Testing walking. 2024-09-11 10:04:54 -05:00
e5349cc093 Created basic movement. 2024-09-11 09:21:56 -05:00
01c56477aa Basic movement example 2024-09-11 08:21:26 -05:00
916396e175 Fixed input 2024-09-10 20:17:32 -05:00
0e5b85633c Worked a bit on prefabs of all things 2024-09-10 18:26:14 -05:00
e5f3f69120 Restored UI Label support 2024-09-10 17:36:04 -05:00
ca240bc180 Moved poker code to main dawn code. 2024-09-10 08:46:57 -05:00
a3a891ddb2 Cleanup of poker code done, about to start testing. 2024-09-10 06:51:04 -05:00
5fae94c722 More refactoring 2024-09-10 00:07:15 -05:00
d5b3b6d619 Cleaning poker code pt 1. 2024-09-08 23:12:03 -05:00
b4e261d954 Move over old poker code 2024-09-08 21:54:19 -05:00
856bc306fe More cleanup complete 2024-09-08 15:30:41 -05:00
cffe7f73a2 Cleanup complete 2024-09-08 15:30:32 -05:00
219 changed files with 16358 additions and 2496 deletions

View File

@@ -0,0 +1,56 @@
name: build-helloworld-vita
on:
push:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
env:
VITASDK: /usr/local/vitasdk
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Cache VITASDK
id: cache-vitasdk-restore
uses: actions/cache/restore@v3
with:
path: /usr/local/vitasdk
key: ${{ runner.os }}-vitasdk
- name: Install Vita Toolchain
if: steps.cache-vitasdk.outputs.cache-hit != 'true'
run: ./ci/install-vita-toolchain.sh
- name: Save VITASDK
id: cache-vitasdk-save
uses: actions/cache/save@v3
with:
path: /usr/local/vitasdk
key: ${{ steps.cache-vitasdk-restore.outputs.cache-primary-key }}
- name: Install Libraries
run: ./ci/install-libraries.sh
- name: Build Tools
run: ./ci/build-tools.sh
- name: Build Game
run: |
export PATH=$VITASDK/bin:$PATH
mkdir build
cd build
cmake .. -DDAWN_BUILD_TARGET=target-helloworld-vita -DCMAKE_BUILD_TYPE=Debug
make
- name: Deploying
env:
DAWN_SSH_KEY: ${{ secrets.DAWN_SSH_KEY }}
run: |
mkdir -p ~/.ssh
echo -e "${DAWN_SSH_KEY}" > ~/.ssh/id_rsa
chmod og-rwx ~/.ssh/id_rsa
ssh-keyscan -H wish.moe >> ~/.ssh/known_hosts
ssh -t yourwishes@wish.moe "mkdir -p /home/yourwishes/Dawn/vita/debug"
scp ./build/src/dawnvita/HelloWorld.vpk yourwishes@wish.moe:/home/yourwishes/Dawn/vita/debug/

View File

@@ -0,0 +1,26 @@
name: build-liminal-glfw-linux64
on:
push:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Toolchain
run: ./ci/install-linux-toolchain.sh
- name: Install Libraries
run: ./ci/install-libraries.sh
- name: Build Tools
run: ./ci/build-tools.sh
- name: Build Game
run: |
mkdir build
cd build
cmake .. -DDAWN_BUILD_TARGET=target-liminial-linux64-glfw
make

2
.gitignore vendored
View File

@@ -65,7 +65,7 @@ CTestTestfile.cmake
_deps
# Custom
build
/build/*
.vscode
assets/testworld/tileset.png

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "lib/font8x8"]
path = lib/font8x8
url = https://github.com/dhepper/font8x8

View File

@@ -1,125 +1,42 @@
# Copyright (c) 2025 Dominic Masters
# Copyright (c) 2024 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Setup
cmake_minimum_required(VERSION 3.13)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
# Variable Caches
set(DAWN_CACHE_TARGET "dawn-target")
set(DAWN_TARGET_NAME "Dawn")
if(NOT DEFINED DAWN_TARGET_SYSTEM)
set(DAWN_TARGET_SYSTEM "linux")
if(NOT DEFINED DAWN_TARGET)
set(DAWN_TARGET linux-x64-glfw CACHE INTERNAL ${DAWN_CACHE_TARGET})
# set(DAWN_TARGET linux-x64-terminal CACHE INTERNAL ${DAWN_CACHE_TARGET})
endif()
# Prep cache
set(DAWN_CACHE_TARGET "dawn-target")
# Build variables
# Set Common Build Variables
set(DAWN_ROOT_DIR "${CMAKE_SOURCE_DIR}")
set(DAWN_BUILD_DIR "${CMAKE_BINARY_DIR}")
set(DAWN_SOURCES_DIR "${DAWN_ROOT_DIR}/src")
set(DAWN_TEMP_DIR "${DAWN_BUILD_DIR}/temp")
set(DAWN_TOOLS_DIR "${DAWN_ROOT_DIR}/tools")
set(DAWN_DATA_DIR "${DAWN_ROOT_DIR}/data")
set(DAWN_ASSETS_DIR "${DAWN_ROOT_DIR}/assets")
set(DAWN_BUILT_ASSETS_DIR "${DAWN_BUILD_DIR}/built_assets" CACHE INTERNAL ${DAWN_CACHE_TARGET})
set(DAWN_GENERATED_HEADERS_DIR "${DAWN_BUILD_DIR}/generated")
set(DAWN_TARGET_NAME "Dawn" CACHE INTERNAL ${DAWN_CACHE_TARGET})
set(DAWN_BUILD_BINARY ${DAWN_BUILD_DIR}/Dawn CACHE INTERNAL ${DAWN_CACHE_TARGET})
set(DAWN_ASSETS "" CACHE INTERNAL ${DAWN_CACHE_TARGET})
set(DAWN_ASSETS_SOURCE_DIR "${DAWN_ROOT_DIR}/assets")
set(DAWN_ASSETS_BUILD_DIR "${DAWN_BUILD_DIR}/assets")
set(DAWN_GENERATED_DIR "${DAWN_BUILD_DIR}/generated")
set(DAWN_TEMP_DIR "${DAWN_BUILD_DIR}/temp")
# Create directories
file(MAKE_DIRECTORY ${DAWN_GENERATED_HEADERS_DIR})
# Find packages
find_package(Python3 COMPONENTS Interpreter REQUIRED)
# Toolchains
if(DAWN_TARGET_SYSTEM STREQUAL "psp")
find_package(pspsdk REQUIRED)
endif()
# Init Project
project(${DAWN_TARGET_NAME}
# Initialize Project.
project(Dawn
VERSION 1.0.0
LANGUAGES C CXX
LANGUAGES C
)
# Executable
add_executable(${DAWN_TARGET_NAME})
# Add tools
# add_subdirectory(tools)
add_subdirectory(tools)
# Assets
# add_subdirectory(assets)
# Add Libraries
add_subdirectory(lib)
# Add libraries
if(DAWN_TARGET_SYSTEM STREQUAL "linux")
find_package(SDL2 REQUIRED)
find_package(OpenGL REQUIRED)
target_link_libraries(${DAWN_TARGET_NAME}
PRIVATE
SDL2
OpenGL::GL
GL
)
elseif(DAWN_TARGET_SYSTEM STREQUAL "psp")
find_package(SDL2 REQUIRED)
find_package(OpenGL REQUIRED)
target_link_libraries(${DAWN_TARGET_NAME}
PUBLIC
${SDL2_LIBRARIES}
SDL2
OpenGL::GL
zip
bz2
z
mbedtls
mbedcrypto
lzma
)
target_include_directories(${DAWN_TARGET_NAME} PRIVATE
${SDL2_INCLUDE_DIRS}
)
endif()
# Add code
# Add Project Files
add_subdirectory(src)
# Include generated headers
target_include_directories(${DAWN_TARGET_NAME} PUBLIC
${DAWN_GENERATED_HEADERS_DIR}
)
# # Build assets
# add_custom_target(DAWN_ASSETS_BUILT ALL
# COMMAND
# ${Python3_EXECUTABLE} ${DAWN_TOOLS_DIR}/assetstool/main.py
# --assets ${DAWN_ASSETS_DIR}
# --build-type wad
# --output-assets ${DAWN_BUILT_ASSETS_DIR}
# --output-file ${DAWN_BUILD_DIR}/dusk.dsk
# --output-headers ${DAWN_GENERATED_HEADERS_DIR}/asset/assetbundle.h
# --headers-dir ${DAWN_GENERATED_HEADERS_DIR}
# --input ${DAWN_ASSETS}
# )
# add_dependencies(${DAWN_TARGET_NAME} DAWN_ASSETS_BUILT)
# # Postbuild
# if(DAWN_TARGET_SYSTEM STREQUAL "psp")
# create_pbp_file(
# TARGET "${DAWN_TARGET_NAME}"
# ICON_PATH NULL
# BACKGROUND_PATH NULL
# PREVIEW_PATH NULL
# TITLE "${DAWN_TARGET_NAME}"
# VERSION 01.00
# )
# endif()

View File

@@ -1,3 +0,0 @@
# Dawn Project
Simple in code, complex in structure game engine for creating small, fast and
reusable games.

42
assets/en.json Normal file
View File

@@ -0,0 +1,42 @@
{
"main_menu": {
"new_game": "New Game",
"load_game": "Load Game",
"options": "Options",
"exit": "Exit"
},
"tiles": {
"water": {
"interact": "A refreshing body of water."
},
"lamp": {
"interact": "An electric lamp.\nA real lightbulb idea."
},
"rail": {
"interact": "Train tracks.\n...Better not cross them."
}
},
"entities": {
"sign": {
"name": "Sign"
}
},
"maps": {
"testmap": {
"bob": "Hello, I am Bob.",
"sign": "This is a sign.",
"sign2": {
"1": "This is another sign.",
"2": "It has two lines."
}
},
"train_station": {
"stair_sign": {
"0": "Stairs slippery when wet.\n\n<- West to Town.\n-> East to lakefront."
}
}
},
"battle": {
"start": "Battle Start!"
}
}

46
assets/maps/testmap.tmx Normal file
View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="8" tileheight="8" infinite="0" nextlayerid="3" nextobjectid="7">
<tileset firstgid="1" source="../tilemaps/tilemap.tsx"/>
<tileset firstgid="65" source="../tilemaps/entities.tsx"/>
<layer id="1" name="Map" width="30" height="20" locked="1">
<data encoding="csv">
1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,2,2,2,2,
1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,2,2,2,2,
1,1,1,1,1,1,2,2,2,2,2,2,6,6,6,6,6,2,2,2,2,2,2,3,3,3,2,2,2,2,
1,1,1,1,1,1,2,2,2,2,2,2,6,6,6,6,6,2,2,2,2,2,3,3,3,3,2,2,2,2,
1,1,1,1,1,2,2,2,2,2,2,2,5,5,5,5,5,2,2,2,2,2,3,3,3,2,2,2,2,2,
1,1,1,1,1,2,2,2,2,2,2,2,5,5,5,5,5,2,2,2,2,2,3,3,3,2,2,2,2,2,
1,1,1,1,1,1,2,2,2,2,2,2,5,5,1,5,5,2,2,2,2,3,3,3,3,2,2,2,2,2,
1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,2,2,2,2,2,
1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,2,2,2,2,2,2,
1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,2,2,2,2,2,2,
1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,2,2,2,2,2,2,2,
1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,2,2,2,2,2,2,2,
1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,2,2,2,2,2,2,2,2,
1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,2,2,2,2,2,2,2,2,
1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,3,3,3,2,2,2,2,2,2,2,2,2,
1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,2,2,2,2,2,2,2,2,2,
1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,2,2,2,2,2,2,2,2,2,2,
1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,2,2,2,2,2,2,2,2,2,2,2,
1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,
1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2
</data>
</layer>
<objectgroup id="2" name="Entities">
<object id="4" name="Player" type="player" gid="66" x="87.7275" y="119.497" width="8" height="8"/>
<object id="5" name="Sign" type="sign" gid="68" x="86.8498" y="55.0415" width="8" height="8">
<properties>
<property name="texts_0" value="maps.testmap.sign2.1"/>
<property name="texts_1" value="maps.testmap.sign2.2"/>
</properties>
</object>
<object id="6" name="Door" type="door" gid="69" x="111.791" y="55.7608" width="8" height="8">
<properties>
<property name="direction" type="int" value="2"/>
<property name="map" value="testmap2.map"/>
<property name="x" type="int" value="11"/>
<property name="y" type="int" value="2"/>
</properties>
</object>
</objectgroup>
</map>

17
assets/maps/testmap2.tmx Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="8" tileheight="8" infinite="0" nextlayerid="2" nextobjectid="1">
<tileset firstgid="1" source="../tilemaps/tilemap.tsx"/>
<tileset firstgid="67" source="../tilemaps/entities.tsx"/>
<layer id="1" name="Tile Layer 1" width="5" height="5">
<data encoding="csv">
2,2,2,2,2,
2,2,2,3,2,
2,2,3,3,2,
2,2,3,2,2,
2,3,3,2,2
</data>
</layer>
<objectgroup id="2" name="Entities">
<object id="4" name="Player" type="player" gid="68" x="6.54983" y="15.3437" width="8" height="8"/>
</objectgroup>
</map>

View File

@@ -0,0 +1,14 @@
{
"automappingRulesFile": "",
"commands": [
],
"compatibilityVersion": 1100,
"extensionsPath": "extensions",
"folders": [
"."
],
"properties": [
],
"propertyTypes": [
]
}

View File

@@ -0,0 +1,114 @@
{
"Map/SizeTest": {
"height": 4300,
"width": 2
},
"activeFile": "maps/testmap.tmx",
"expandedProjectPaths": [
".",
"maps"
],
"file.lastUsedOpenFilter": "All Files (*)",
"fileStates": {
"": {
"scaleInDock": 1
},
"entities.tsx": {
"scaleInDock": 1,
"scaleInEditor": 1
},
"maps/downtown.tmx": {
"scale": 4.9072,
"selectedLayer": 0,
"viewCenter": {
"x": 39.94131072709489,
"y": 40.0432018258885
}
},
"maps/testmap.tmx": {
"expandedObjectLayers": [
2
],
"scale": 3.404166666666667,
"selectedLayer": 1,
"viewCenter": {
"x": 95.17747858017135,
"y": 86.80538555691552
}
},
"maps/testmap2.tmx": {
"expandedObjectLayers": [
2
],
"scale": 5.0383,
"selectedLayer": 1,
"viewCenter": {
"x": 19.84796459123116,
"y": 19.947204414187325
}
},
"maps/train_station.tmx": {
"expandedObjectLayers": [
2
],
"scale": 2.7603,
"selectedLayer": 1,
"viewCenter": {
"x": 160.48980183313407,
"y": 120.4579212404449
}
},
"testmap.tmx": {
"scale": 3.23,
"selectedLayer": 1,
"viewCenter": {
"x": 122.60061919504646,
"y": 56.965944272445824
}
},
"testmap2.tmx": {
"scale": 3.281458333333333,
"selectedLayer": 0,
"viewCenter": {
"x": 119.91619579709226,
"y": 80.14729223541363
}
},
"tilemap.tsx": {
"scaleInDock": 1,
"scaleInEditor": 4
},
"tilemaps/entities.tsx": {
"scaleInDock": 2,
"scaleInEditor": 9.7785
},
"tilemaps/tilemap.tsx": {
"scaleInDock": 2
}
},
"last.externalTilesetPath": "/home/yourwishes/htdocs/Dawn/assets",
"last.imagePath": "/home/yourwishes/htdocs/Dawn/assets",
"map.height": 10,
"map.lastUsedFormat": "tmx",
"map.tileHeight": 8,
"map.tileWidth": 8,
"map.width": 10,
"openFiles": [
"maps/testmap.tmx"
],
"project": "tiled_project.tiled-project",
"property.type": "string",
"recentFiles": [
"maps/testmap.tmx",
"maps/downtown.tmx",
"maps/testmap2.tmx",
"maps/train_station.tmx",
"tilemaps/entities.tsx"
],
"tileset.lastUsedFilter": "All Files (*)",
"tileset.lastUsedFormat": "tsx",
"tileset.tileSize": {
"height": 8,
"width": 8
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.10" tiledversion="1.11.0" name="entities" tilewidth="8" tileheight="8" tilecount="64" columns="8">
<image source="entities.png" width="64" height="64"/>
<tile id="1" type="player"/>
<tile id="2" type="npc"/>
<tile id="3" type="sign"/>
<tile id="4" type="door"/>
</tileset>

BIN
assets/tilemaps/tilemap.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.10" tiledversion="1.11.0" name="tilemap" tilewidth="8" tileheight="8" tilecount="64" columns="8">
<image source="tilemap.png" width="64" height="64"/>
</tileset>

View File

@@ -1,14 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
include(FetchContent)
FetchContent_Declare(
glm
GIT_REPOSITORY https://github.com/g-truc/glm.git
GIT_TAG 1.0.1
)
FetchContent_MakeAvailable(glm)
set(glm_FOUND ON)

View File

@@ -1,106 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
if(NOT TARGET pspsdk)
message(STATUS "Looking for PSPSDK...")
set(PSPSDK_FOUND FALSE CACHE INTERNAL "PSPSDK found")
set(PSPSDK_DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/_pspsdk")
set(PSPSDK_SEARCH_ROOTS
"${PSPSDK_ROOT}"
"$ENV{PSPDEV}"
"$ENV{HOME}/pspdev"
"/usr/local/pspdev"
"/opt/pspdev"
"/usr/pspdev"
"${PSPSDK_DOWNLOAD_DIR}/pspdev"
)
foreach(root IN LISTS PSPSDK_SEARCH_ROOTS)
list(APPEND PSPSDK_BIN_HINTS "${root}/bin")
list(APPEND PSPSDK_INCLUDE_HINTS "${root}/include")
list(APPEND PSPSDK_LIB_HINTS "${root}/lib")
endforeach()
# Find PSP GCC
find_program(PSPSDK_PSP_GCC NAMES psp-gcc HINTS ${PSPSDK_BIN_HINTS})
# If we did not find it, download it.
if(NOT PSPSDK_PSP_GCC)
message(STATUS "psp-gcc not found in system paths. Downloading PSPSDK tarball...")
file(DOWNLOAD
"https://github.com/pspdev/pspdev/releases/latest/download/pspdev-ubuntu-latest-x86_64.tar.gz"
"${CMAKE_BINARY_DIR}/pspsdk.tar.gz"
EXPECTED_HASH SHA256=2befe2ad83afd88934c106dbe98a72a7ad5ede8d272b7f1e79fda256a22f1062
SHOW_PROGRESS
)
# Make output dir
file(MAKE_DIRECTORY "${PSPSDK_DOWNLOAD_DIR}")
# Extract the tarball
execute_process(
COMMAND
${CMAKE_COMMAND} -E tar xzf "${CMAKE_BINARY_DIR}/pspsdk.tar.gz"
WORKING_DIRECTORY
"${PSPSDK_DOWNLOAD_DIR}"
RESULT_VARIABLE
tar_result
)
if(NOT tar_result EQUAL 0)
message(FATAL_ERROR "Failed to extract PSPSDK tarball")
endif()
# Retry discovery with extracted fallback
find_program(PSPSDK_PSP_GCC NAMES psp-gcc HINTS ${PSPSDK_BIN_HINTS})
endif()
if(PSPSDK_PSP_GCC)
get_filename_component(PSPSDK_BIN_ROOT "${PSPSDK_PSP_GCC}" DIRECTORY)
get_filename_component(PSPSDK_ROOT "${PSPSDK_BIN_ROOT}" DIRECTORY)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
set(ENV{PSPDEV} "${PSPSDK_ROOT}")
set(CMAKE_TOOLCHAIN_FILE "${PSPSDK_ROOT}/psp/share/pspdev.cmake")
set(BUILD_PRX ON CACHE BOOL "Build PRX modules")
include("${PSPSDK_ROOT}/psp/share/pspdev.cmake")
set(CMAKE_C_COMPILER ${PSPSDK_BIN_ROOT}/psp-gcc)
set(CMAKE_CXX_COMPILER ${PSPSDK_BIN_ROOT}/psp-g++)
if(NOT DEFINED PSP)
message(FATAL_ERROR "PSP environment variable is not set correctly.")
endif()
add_library(pspsdk INTERFACE IMPORTED)
set_target_properties(pspsdk PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${PSPSDK_INCLUDE_DIR}"
INTERFACE_LINK_DIRECTORIES "${PSPSDK_LIB_DIR}"
)
target_include_directories(pspsdk
INTERFACE
${PSPDEV}/psp/include
${PSPDEV}/psp/sdk/include
)
target_link_directories(pspsdk
INTERFACE
${PSPDEV}/lib
${PSPDEV}/psp/lib
${PSPDEV}/psp/sdk/lib
)
target_link_libraries(pspsdk INTERFACE
pspdebug
pspdisplay
pspge
pspctrl
pspgu
pspaudio
pspaudiolib
psputility
pspvfpu
pspvram
psphprm
)
endif()
endif()

234
gentileset.py Normal file
View File

@@ -0,0 +1,234 @@
# font is a 2D array of 8x8 pixel characters where each bit is a pixel on the
# row.
font8x8_basic = [
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0000 (nul)
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0001
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0002
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0003
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0004
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0005
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0006
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0007
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0008
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0009
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+000A
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+000B
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+000C
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+000D
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+000E
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+000F
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0010
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0011
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0012
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0013
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0014
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0015
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0016
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0017
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0018
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0019
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+001A
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+001B
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+001C
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+001D
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+001E
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+001F
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0020 (space)
[0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00], # U+0021 (!)
[0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0022 (")
[0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00], # U+0023 (#)
[0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00], # U+0024 ($)
[0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00], # U+0025 (%)
[0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00], # U+0026 (&)
[0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0027 (')
[0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00], # U+0028 (()
[0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00], # U+0029 ())
[0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00], # U+002A (*)
[0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00], # U+002B (+)
[0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06], # U+002C (,)
[0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00], # U+002D (-)
[0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00], # U+002E (.)
[0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00], # U+002F (/)
[0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00], # U+0030 (0)
[0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00], # U+0031 (1)
[0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00], # U+0032 (2)
[0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00], # U+0033 (3)
[0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00], # U+0034 (4)
[0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00], # U+0035 (5)
[0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00], # U+0036 (6)
[0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00], # U+0037 (7)
[0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00], # U+0038 (8)
[0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00], # U+0039 (9)
[0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00], # U+003A (:)
[0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06], # U+003B (;)
[0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00], # U+003C (<)
[0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00], # U+003D (=)
[0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00], # U+003E (>)
[0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00], # U+003F (?)
[0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00], # U+0040 (@)
[0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00], # U+0041 (A)
[0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00], # U+0042 (B)
[0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00], # U+0043 (C)
[0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00], # U+0044 (D)
[0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00], # U+0045 (E)
[0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00], # U+0046 (F)
[0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00], # U+0047 (G)
[0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00], # U+0048 (H)
[0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00], # U+0049 (I)
[0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00], # U+004A (J)
[0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00], # U+004B (K)
[0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00], # U+004C (L)
[0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00], # U+004D (M)
[0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00], # U+004E (N)
[0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00], # U+004F (O)
[0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00], # U+0050 (P)
[0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00], # U+0051 (Q)
[0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00], # U+0052 (R)
[0x1E, 0x33, 0x07, 0x1E, 0x38, 0x33, 0x1E, 0x00], # U+0053 (S)
[0x3F, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x00], # U+0054 (T)
[0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00], # U+0055 (U)
[0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00], # U+0056 (V)
[0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00], # U+0057 (W)
[0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00], # U+0058 (X)
[0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x0C, 0x00], # U+0059 (Y)
[0x3F, 0x33, 0x19, 0x0C, 0x26, 0x33, 0x3F, 0x00], # U+005A (Z)
[0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00], # U+005B ([)
[0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00], # U+005C (\)
[0x1E, 0x30, 0x30, 0x30, 0x30, 0x30, 0x1E, 0x00], # U+005D (])
[0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00], # U+005E (^)
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF], # U+005F (_)
[0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00], # U+0060 (`)
[0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00], # U+0061 (a)
[0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00], # U+0062 (b)
[0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00], # U+0063 (c)
[0x38, 0x30, 0x30, 0x3E, 0x33, 0x33, 0x6E, 0x00], # U+0064 (d)
[0x00, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00], # U+0065 (e)
[0x1C, 0x36, 0x06, 0x0F, 0x06, 0x06, 0x0F, 0x00], # U+0066 (f)
[0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1E], # U+0067 (g)
[0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00], # U+0068 (h)
[0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00], # U+0069 (i)
[0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E], # U+006A (j)
[0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00], # U+006B (k)
[0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00], # U+006C (l)
[0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00], # U+006D (m)
[0x00, 0x00, 0x1B, 0x37, 0x33, 0x33, 0x33, 0x00], # U+006E (n)
[0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00], # U+006F (o)
[0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F], # U+0070 (p)
[0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78], # U+0071 (q)
[0x00, 0x00, 0x1B, 0x36, 0x36, 0x06, 0x0F, 0x00], # U+0072 (r)
[0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00], # U+0073 (s)
[0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00], # U+0074 (t)
[0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00], # U+0075 (u)
[0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00], # U+0076 (v)
[0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00], # U+0077 (w)
[0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00], # U+0078 (x)
[0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1E], # U+0079 (y)
[0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00], # U+007A (z)
[0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00], # U+007B ({)
[0x0C, 0x0C, 0x0C, 0x00, 0x0C, 0x0C, 0x0C, 0x00], # U+007C (|)
[0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00], # U+007D (})
[0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # U+007E (~)
]
# COLOR4F(0.0f, 0.0f, 0.0f, 1.0f), # Black
# COLOR4F(1.0f, 1.0f, 1.0f, 1.0f), # White
# COLOR4F(0.8f, 0.0f, 0.0f, 1.0f), # Red
# COLOR4F(0.0f, 0.5f, 0.0f, 1.0f), # Green
# COLOR4F(0.0f, 0.0f, 0.8f, 1.0f), # Blue
# COLOR4F(0.8f, 0.8f, 0.0f, 1.0f), # Yellow
# COLOR4F(0.8f, 0.0f, 0.8f, 1.0f), # Magenta
# COLOR4F(0.0f, 0.8f, 0.8f, 1.0f), # Cyan
# COLOR4F(0.5f, 0.5f, 0.5f, 1.0f), # Gray
# COLOR4F(0.5f, 0.25f, 0.0f, 1.0f) # Brown
# Tilemap
COLORS = {
"BLACK": [ 0, 0, 0 ],
"WHITE": [ 255, 255, 255 ],
"RED": [ 204, 0, 0 ],
"GREEN": [ 0, 128, 0 ],
"BLUE": [ 0, 0, 204 ],
"YELLOW": [ 204, 204, 0 ],
"MAGENTA": [ 204, 0, 204 ],
"CYAN": [ 0, 204, 204 ],
"GRAY": [ 128, 128, 128 ],
"BROWN": [ 128, 64, 0 ]
}
TILES = {
# NAME: [ id, symbol, color ]
"NULL": [ 0, 'N', COLORS["RED"] ],
"GRASS": [ 1, '#', COLORS["GREEN"] ],
"WATER": [ 2, '\\', COLORS["BLUE"] ],
# "DOOR": [ 3, 'D', COLORS["BROWN"] ],
"BUILDING_WALL": [ 4, '-', COLORS["GRAY"] ],
"ROOF": [ 5, '/', COLORS["BROWN"] ],
"WALKABLE_NULL": [ 6, ' ', COLORS["BLACK"] ],
"COBBLESTONE": [ 7, '#', COLORS["GRAY"] ],
"STAIRS": [ 8, '=', COLORS["GRAY"] ],
"RAILING": [ 9, 'n', COLORS["BROWN"] ],
"COLUMN": [ 10, 'I', COLORS["WHITE"] ],
"RAIL_TRACK": [ 11, '|', COLORS["GRAY"] ],
"RAIL_SLEEPER": [ 12, '-', COLORS["BROWN"] ],
"CARPET": [ 13, '#', COLORS["RED"] ],
"LAMP": [ 14, 'i', COLORS["YELLOW"] ],
}
ENTITIES = {
"NULL": [ 0, 'N', COLORS["RED"] ],
"PLAYER": [ 1, 'v', COLORS["RED"] ],
"NPC": [ 2, 'V', COLORS["RED"] ],
"SIGN": [ 3, '+', COLORS["YELLOW"] ],
"DOOR": [ 4, 'D', COLORS["BROWN"] ],
}
# Create 64 x 64 RGB pixel buffer.
import png
WIDTH = 64
HEIGHT = 64
def genTileset(filename, tileset, background):
# Create 64x64 pixel buffer with black
PIXEL_BUFFER = []
for y in range(HEIGHT):
row = []
for x in range(WIDTH):
row.append(background)
PIXEL_BUFFER.append(row)
# For each TILES
for tile in tileset:
# Get the tile data
tile_data = tileset[tile]
tile_id = tile_data[0]
tile_char = tile_data[1]
tile_color = tile_data[2]
# Get char pixels
char_pixels = font8x8_basic[ord(tile_char)]
tile_color_rgba = [ tile_color[0], tile_color[1], tile_color[2], 255 ]
# Write char pixels to output buffer, using tile_id to determine pixels with
# each row holding 8 characters of 8 pixels
for y in range(8):
row = char_pixels[y]
for x in range(8):
if row & (1 << x):
PIXEL_BUFFER[(tile_id // 8) * 8 + y][(tile_id % 8) * 8 + x] = tile_color_rgba
# Convert from [ [ [ r, g, b ], [ r, g, b] ], [ [ r, g, b ], [ r, g, b ] ] ] to
# [ [R, G, B, A, R, G, B, A] ] (Flatten level 2 and add alpha channel)
FLATTENED_PIXEL_BUFFER = [ [ 0 for i in range(WIDTH * 4) ] for j in range(HEIGHT) ]
for y in range(HEIGHT):
for x in range(WIDTH):
FLATTENED_PIXEL_BUFFER[y][x * 4 + 0] = PIXEL_BUFFER[y][x][0]
FLATTENED_PIXEL_BUFFER[y][x * 4 + 1] = PIXEL_BUFFER[y][x][1]
FLATTENED_PIXEL_BUFFER[y][x * 4 + 2] = PIXEL_BUFFER[y][x][2]
FLATTENED_PIXEL_BUFFER[y][x * 4 + 3] = PIXEL_BUFFER[y][x][3]
# Now write PIXEL_BUFFER to png
png.from_array(FLATTENED_PIXEL_BUFFER, 'RGBA').save(filename)
genTileset("./assets/tilemaps/tilemap.png", TILES, [ 0, 0, 0, 255 ])
genTileset("./assets/tilemaps/entities.png", ENTITIES, [ 0, 0, 0, 100 ])

31
lib/CMakeLists.txt Normal file
View File

@@ -0,0 +1,31 @@
# Copyright (c) 2024 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
include(FetchContent)
add_library(font8x8 INTERFACE)
target_include_directories(font8x8 INTERFACE font8x8)
add_subdirectory(glad)
# GLFW
FetchContent_Declare(glfw URL https://github.com/glfw/glfw/releases/download/3.4/glfw-3.4.zip)
FetchContent_MakeAvailable(glfw)
# GLM
FetchContent_Declare(
cglm
GIT_REPOSITORY https://github.com/recp/cglm
GIT_TAG 1796cc5ce298235b615dc7a4750b8c3ba56a05dd
)
FetchContent_MakeAvailable(cglm)
#LibArchive
FetchContent_Declare(
libarchive
GIT_REPOSITORY https://github.com/libarchive/libarchive
GIT_TAG v3.7.6
)
FetchContent_MakeAvailable(libarchive)

1
lib/font8x8 Submodule

Submodule lib/font8x8 added at 8e279d2d86

12
lib/glad/CMakeLists.txt Normal file
View File

@@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.11)
project(glad)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
add_library(${PROJECT_NAME}
src/glad.c
)
target_include_directories(${PROJECT_NAME}
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)

View File

@@ -0,0 +1,290 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
# define KHRONOS_STATIC 1
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(KHRONOS_STATIC)
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
* header compatible with static linking. */
# define KHRONOS_APICALL
#elif defined(_WIN32)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef _WIN64
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

4068
lib/glad/include/glad/glad.h Normal file

File diff suppressed because it is too large Load Diff

2522
lib/glad/src/glad.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,52 +1,22 @@
# Copyright (c) 2025 Dominic Masters
# Copyright (c) 2024 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
find_package(glm REQUIRED)
find_package(libzip REQUIRED)
# Build Project
add_executable(${DAWN_TARGET_NAME})
# Libs
target_link_libraries(${DAWN_TARGET_NAME}
PUBLIC
m
glm
zip
)
# Add base
add_subdirectory(dawn)
# Includes
target_include_directories(${DAWN_TARGET_NAME}
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
main.cpp
)
# Subdirs
add_subdirectory(assert)
add_subdirectory(asset)
add_subdirectory(console)
add_subdirectory(display)
add_subdirectory(engine)
add_subdirectory(input)
add_subdirectory(rpg)
add_subdirectory(time)
# Platform-specific settings
if(DAWN_TARGET_SYSTEM STREQUAL "linux")
target_compile_definitions(${DAWN_TARGET_NAME}
PRIVATE
DAWN_SDL2=1
DAWN_SDL2_GAMEPAD=1
)
elseif(DAWN_TARGET_SYSTEM STREQUAL "psp")
target_compile_definitions(${DAWN_TARGET_NAME}
PRIVATE
DAWN_SDL2=1
DAWN_SDL2_GAMEPAD=1
)
# Compile entries
if(DAWN_TARGET STREQUAL "linux-x64-terminal")
add_subdirectory(dawnlinux)
add_subdirectory(dawntermlinux)
elseif(DAWN_TARGET STREQUAL "linux-x64-glfw")
add_subdirectory(dawnlinux)
add_subdirectory(dawnglfw)
add_subdirectory(dawnopengl)
else()
message(FATAL_ERROR "Unknown target: ${DAWN_TARGET}")
endif()

View File

@@ -1,85 +0,0 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "Assert.hpp"
#ifndef ASSERTIONS_FAKED
void assertTrueImpl(
const char_t *file,
const int32_t line,
const bool_t x,
const char_t *message
) {
if(x != true) {
fprintf(
stderr,
"Assertion Failed in %s:%i\n\n%s\n",
file,
line,
message
);
abort();
}
}
void assertFalseImpl(
const char_t *file,
const int32_t line,
bool_t x,
const char_t *message
) {
assertTrueImpl(file, line, !x, message);
}
void assertUnreachableImpl(
const char_t *file,
const int32_t line,
const char_t *message
) {
assertTrueImpl(file, line, false, message);
}
void assertNotNullImpl(
const char_t *file,
const int32_t line,
const void *pointer,
const char_t *message
) {
assertTrueImpl(
file,
line,
pointer != NULL && pointer != nullptr,
message
);
// Ensure we can touch it
volatile char_t temp;
temp = *((char_t*)pointer);
}
void assertNullImpl(
const char_t *file,
const int32_t line,
const void *pointer,
const char_t *message
) {
assertTrueImpl(
file,
line,
pointer == NULL,
message
);
}
void assertDeprecatedImpl(
const char_t *file,
const int32_t line,
const char_t *message
) {
assertUnreachableImpl(file, line, message);
}
#endif

View File

@@ -1,143 +0,0 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawn.hpp"
#ifndef ASSERTIONS_FAKED
/**
* Assert a given value to be true.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param x Value to assert as true.
* @param message Message to throw against assertion failure.
*/
void assertTrueImpl(
const char_t *file,
const int32_t line,
const bool_t x,
const char_t *message
);
/**
* Asserts a given statement to be false.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param x Value to assert as false.
* @param message Message to throw against assertion failure.
*/
void assertFalseImpl(
const char_t *file,
const int32_t line,
const bool_t x,
const char_t *message
);
/**
* Asserts that a given line of code is unreachable. Essentially a forced
* assertion failure, good for "edge cases"
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param message Message to throw against assertion failure.
*/
void assertUnreachableImpl(
const char_t *file,
const int32_t line,
const char_t *message
);
/**
* Assert a given pointer to not point to a null pointer.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param pointer Pointer to assert is not a null pointer.
* @param message Message to throw against assertion failure.
*/
void assertNotNullImpl(
const char_t *file,
const int32_t line,
const void *pointer,
const char_t *message
);
/**
* Asserts a given pointer to be a nullptr.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param pointer Pointer to assert is nullptr.
* @param message Message to throw against assertion failure.
*/
void assertNullImpl(
const char_t *file,
const int32_t line,
const void *pointer,
const char_t *message
);
/**
* Asserts a function as being deprecated.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param message Message to throw against assertion failure.
*/
void assertDeprecatedImpl(
const char_t *file,
const int32_t line,
const char_t *message
);
void assertMemoryRangeMatchesImpl(
const char_t *file,
const int32_t line,
const void *start,
const void *end,
const size_t size,
const char_t *message
);
#define assertTrue(x, message) \
assertTrueImpl(__FILE__, __LINE__, x, message)
#define assertFalse(x, message) \
assertFalseImpl(__FILE__, __LINE__, x, message)
#define assertUnreachable(message) \
assertUnreachableImpl(__FILE__, __LINE__, message)
#define assertNotNull(pointer, message) \
assertNotNullImpl(__FILE__, __LINE__, pointer, message)
#define assertNull(pointer, message) \
assertNullImpl(__FILE__, __LINE__, pointer, message)
#define assertDeprecated(message) \
assertDeprecatedImpl(__FILE__, __LINE__, message)
#define assertStrLenMax(str, len, message) \
assertTrue(strlen(str) < len, message)
#define assertStrLenMin(str, len, message) \
assertTrue(strlen(str) >= len, message)
#else
// If assertions are faked, we define the macros to do nothing.
#define assertTrue(x, message) ((void)0)
#define assertFalse(x, message) ((void)0)
#define assertUnreachable(message) ((void)0)
#define assertNotNull(pointer, message) ((void)0)
#define assertNull(pointer, message) ((void)0)
#define assertDeprecated(message) ((void)0)
#define assertStrLenMax(str, len, message) ((void)0)
#define assertStrLenMin(str, len, message) ((void)0)
#endif

View File

@@ -1,16 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "Asset.hpp"
using namespace Dawn;
Asset::Asset(const std::string &filename) :
filename(filename)
{
}
Asset::~Asset() {
}

View File

@@ -1,19 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawn.hpp"
#include <zip.h>
namespace Dawn {
struct Asset {
private:
std::string filename;
public:
Asset(const std::string &filename);
~Asset();
};
}

View File

@@ -1,21 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "AssetManager.hpp"
using namespace Dawn;
AssetManager::AssetManager() :
zip(nullptr)
{
// Find the zip file.
}
AssetManager::~AssetManager() {
if(zip) {
zip_close(zip);
zip = nullptr;
}
}

View File

@@ -1,25 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "Asset.hpp"
namespace Dawn {
struct AssetManager {
private:
zip_t *zip;
public:
/**
* Constructor for the Asset manager.
*/
AssetManager();
/**
* Destructor for the Asset manager.
*/
~AssetManager();
};
}

View File

@@ -1,167 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "Console.hpp"
#include "assert/Assert.hpp"
using namespace Dawn;
Console::Console(void) :
commands(),
variables(),
buffer()
{
this->registerCommand("echo", [](auto console, auto args) {
if(args.size() == 0) {
console.print("Usage: echo <message>");
return;
}
console.print("%s", args[0].c_str());
});
}
void Console::registerCommand(
const std::string &cmd,
const std::function<void(Console&, const std::vector<std::string>&)> &cb
) {
assertTrue(cmd.length() > 0, "cmd length must be > 0");
assertTrue(cb != nullptr, "callback must not be null");
this->commands[cmd] = cb;
}
template<typename T>
void Console::registerVariable(
const std::string &var,
const std::function<T()> &getter,
const std::function<void(const T &)> &setter
) {
assertTrue(var.length() > 0, "var length must be > 0");
assertTrue(getter != nullptr, "getter must not be null");
assertTrue(setter != nullptr, "setter must not be null");
this->variables[var] = std::make_pair(getter, setter);
}
void Console::update() {
enum class ParseState { FIND_CMD, FIND_ARG, COMMAND, ARG, ARG_QUOTED };
for (const auto &cmd : buffer) {
std::string cmdName;
std::vector<std::string> args;
ParseState state = ParseState::FIND_CMD;
std::string buff;
for(size_t i = 0; i < cmd.length(); i++) {
char_t c = cmd[i];
switch(state) {
case ParseState::FIND_CMD:
if(isspace(c)) continue;
state = ParseState::COMMAND;
buff += c;
break;
case ParseState::FIND_ARG:
if(isspace(c)) continue;
if(c == '\"') {
state = ParseState::ARG_QUOTED;
break;
}
state = ParseState::ARG;
buff += c;
break;
case ParseState::COMMAND:
if(isspace(c)) {
cmdName = buff;
buff.clear();
state = ParseState::FIND_ARG;
break;
}
buff += c;
break;
case ParseState::ARG:
if(isspace(c)) {
args.push_back(buff);
buff.clear();
state = ParseState::FIND_ARG;
break;
}
buff += c;
break;
case ParseState::ARG_QUOTED:
if(c == '\"') {
args.push_back(buff);
buff.clear();
state = ParseState::FIND_ARG;
break;
}
buff += c;
break;
}
}
if(buff.length() > 0) {
if(state == ParseState::COMMAND) {
cmdName = buff;
} else {
args.push_back(buff);
}
}
// Find command
auto itCmd = commands.find(cmdName);
if(itCmd == commands.end()) {
// Find variable
auto itVar = variables.find(cmdName);
if(itVar == variables.end()) {
std::cout << "Console: Unknown command or variable '" << cmdName << "'" << std::endl;
continue;
}
// Variable get/set
printf("Console: Variable '%s' ", cmdName.c_str());
continue;
}
itCmd->second(*this, args);
}
buffer.clear();
}
void Console::exec(const std::string &str) {
// Split strings by command seperator
std::istringstream iss(str);
std::string token;
while(std::getline(iss, token, COMMAND_SEPARATOR)) {
// Seperate by newlines too
std::istringstream lineIss(token);
std::string lineToken;
while(std::getline(lineIss, lineToken)) {
if(lineToken.length() == 0) continue;
buffer.push_back(lineToken);
}
}
}
void Console::print(const char_t *fmt, ...) {
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
printf("\n");
va_end(args);
}
void Console::print(std::string str) {
this->print("%s", str.c_str());
}
Console::~Console(void) {
}

View File

@@ -1,88 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawn.hpp"
namespace Dawn {
struct Console {
private:
std::map<
std::string,
std::function<void(Console&, const std::vector<std::string>&)>
> commands;
std::map<std::string, std::pair<
std::function<std::any()>, std::function<void(std::any&)>
>> variables;
std::vector<std::string> buffer;
public:
static constexpr char_t COMMAND_SEPARATOR = ';';
/**
* Constructs the console.
*/
Console(void);
/**
* Registers a command with the console.
*
* @param cmd The command string.
* @param cb The callback function for the command.
*/
void registerCommand(
const std::string &cmd,
const std::function<void(Console&, const std::vector<std::string>&)> &cb
);
/**
* Registers a variable with the console.
*
* @param var The variable name.
* @param getter The getter function for the variable.
* @param setter The setter function for the variable.
*/
template<typename T>
void registerVariable(
const std::string &var,
const std::function<T()> &getter,
const std::function<void(const T &)> &setter
);
/**
* Updates the console state, this will exec the command buffer.
*/
void update();
/**
* Executes a command string.
*
* @param str The command string to execute.
*/
void exec(const std::string &str);
/**
* Prints a formatted string to the console output.
*
* @param fmt The format string.
* @param ... The format arguments.
*/
void print(const char_t *fmt, ...);
/**
* Prints a string to the console output.
*
* @param str The string to print.
*/
void print(std::string str);
/**
* Destructs the console.
*/
~Console(void);
};
}

View File

@@ -1,64 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
extern "C" {
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <errno.h>
#include <ctype.h>
#include <stdarg.h>
#include <float.h>
// #include <glm/gtc/matrix_transform.hpp>
// #include <glm/gtc/type_ptr.hpp>
// #include <glm/gtc/constants.hpp>
// #include <glm/gtx/string_cast.hpp>
// #include <glm/gtx/rotate_vector.hpp>
// #include <glm/gtx/vector_angle.hpp>
// #include <glm/gtx/norm.hpp>
// #include <glm/gtx/compatibility.hpp>
// #include <glm/gtx/color_space.hpp>
#if PSP
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspdisplay.h>
#include <pspctrl.h>
#include <psphprm.h>
#endif
typedef bool bool_t;
typedef int int_t;
typedef float float_t;
typedef char char_t;
}
#if DAWN_SDL2
#include <SDL2/SDL.h>
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#endif
#include <memory>
#include <string>
#include <vector>
#include <functional>
#include <map>
#include <array>
#include <any>
#include <iostream>
#include <sstream>
#include <glm/glm.hpp>

39
src/dawn/CMakeLists.txt Normal file
View File

@@ -0,0 +1,39 @@
# Copyright (c) 2024 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Libraries
target_link_libraries(${DAWN_TARGET_NAME}
PUBLIC
archive_static
)
# Includes
target_include_directories(${DAWN_TARGET_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
# Subdirs
add_subdirectory(assert)
add_subdirectory(asset)
add_subdirectory(display)
add_subdirectory(game)
add_subdirectory(rpg)
add_subdirectory(ui)
add_subdirectory(locale)
add_subdirectory(util)
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
input.c
)
# Assets
tool_map(testmap maps/testmap.tmx)
tool_map(testmap2 maps/testmap2.tmx)
tool_copy(en en.json)
add_dependencies(${DAWN_TARGET_NAME} dawnassets)

View File

@@ -1,10 +1,12 @@
# Copyright (c) 2025 Dominic Masters
# Copyright (c) 2024 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Subdirs
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
Mesh.cpp
assert.c
)

37
src/dawn/assert/assert.c Normal file
View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2022 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert.h"
#include <stdarg.h>
void assertTrueImplement(
const char *file,
const int32_t line,
const char *func,
const bool_t result,
const char *message,
...
) {
if(result) return;
// Print file info.
fprintf(
stderr,
"Assert failed in %s:%i :: %s\n",
file,
line,
func
);
// Print message.
va_list args;
va_start(args, message);
vfprintf(stderr, message, args);
va_end(args);
fprintf(stderr, "\n");
abort();
}

117
src/dawn/assert/assert.h Normal file
View File

@@ -0,0 +1,117 @@
/**
* Copyright (c) 2022 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawn.h"
/**
* Asserts that a given statement must evaluate to true or the assertion fails
* and the game will close semi-gracefully.
*
* @param file String filename of the file that has the assertion.
* @param line Integer line number within the file that the assertion is on.
* @param func Called function that has the assertion.
* @param result The statement that must equate to true.
* @param message Message (sprintf format) to send to the stdout.
* @param ... Varargs of the sprintf arguments.
*/
void assertTrueImplement(
const char *file,
const int32_t line,
const char *func,
const bool_t result,
const char *message,
...
);
/**
* Asserts that a statement must be true in order for the assertion to not cause
* an error. Basically this is a throw statement in disguise.
*
* @param statement Statement of the assertion.
* @param message Message (sprintf format) to send to the logger.
* @param args Optional TParam args for the sprintf message to accept.
*/
#define assertTrue(...) assertTrueImplement( \
__FILE__, __LINE__, __func__, __VA_ARGS__ \
)
/**
* Asserts that a statement must be false in order for the assertion to not
* cause an error.
*
* @param statement Statement of the assertion.
* @param message Message (sprintf format) to send to the logger.
* @param args Optional TParam args for the sprintf message to accept.
*/
#define assertFalse(x, ...) assertTrue(!(x), __VA_ARGS__)
/**
* Asserts that a specified piece of code should be entirely unreachable.
* @param message Message (sprintf format) to send to the logger.
* @param args Optional TParam args for the sprintf message to accept.
*/
#define assertUnreachable(...) assertTrue(false, __VA_ARGS__)
/**
* Asserts that a given pointer is not null.
* @param x Pointer to check.
* @param message Message (sprintf format) to send to the logger.
* @param args Optional TParam args for the sprintf message to accept.
*/
#define assertNotNull(x, ...) assertTrue(x != NULL, __VA_ARGS__)
/**
* Asserts that a given pointer is null.
* @param x Pointer to check.
* @param message Message (sprintf format) to send to the logger.
* @param args Optional TParam args for the sprintf message to accept.
*/
#define assertNull(x, ...) assertTrue(x == NULL, __VA_ARGS__)
/**
* Asserts that a given value has a specific flag turned off.
*
* @param value Value to check.
* @param flag Flag to check for.
* @param message Message (sprintf format) to send to the logger.
* @param args Optional TParam args for the sprintf message to accept.
*/
#define assertFlagOff(value, flag, ...) assertTrue( \
(value & flag) == 0, __VA_ARGS__ \
)
/**
* Asserts that a given value has a specific flag turned on.
*
* @param value Value to check.
* @param flag Flag to check for.
* @param message Message (sprintf format) to send to the logger.
* @param args Optional TParam args for the sprintf message to accept.
*/
#define assertFlagOn(value, flag, ...) assertTrue( \
(value & flag) == flag, __VA_ARGS__ \
)
/**
* Asserts that the given string is not null, and has a length that is less
* the maximum buffer size, including the NULL terminator.
*
* @param str String to check.
* @param bufferSize Maximum buffer size.
* @param message Message (sprintf format) to send to the logger.
* @param args Optional TParam args for the sprintf message to accept.
*/
#define assertStringValid(str, bufferSize, ...) assertTrue( \
((str != NULL) && ((strlen(str)+1) < bufferSize)), __VA_ARGS__ \
)
/**
* Asserts that the current code is deprecated and should not be used anymore.
* @deprecated
*/
#define assertDeprecated(...) assertUnreachable(__VA_ARGS__)

View File

@@ -0,0 +1,14 @@
# Copyright (c) 2024 Dominic Masters
#
# This software is released under the MIT License.
# https:#opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
asset.c
assetarchive.c
assetjson.c
assetmap.c
assetlanguage.c
)

159
src/dawn/asset/asset.c Normal file
View File

@@ -0,0 +1,159 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "asset.h"
#include "assetarchive.h"
#include "assert/assert.h"
#include "util/math.h"
void assetInit() {
// TODO: Works on Windows? path sep probs wrong.
// const char_t *assetFilename = "dawn.tar";
// char_t *assetPath = malloc(sizeof(char_t) * (
// strlen(SYSTEM.executableDirectory) + strlen(assetFilename) + 1
// ));
// sprintf(assetPath, "%s/%s", SYSTEM.executableDirectory, assetFilename);
char_t *assetPath = "/home/yourwishes/htdocs/Dawn/build/dawn.tar";
ASSET_FILE = fopen(assetPath, "rb");
// free(assetPath);
assertNotNull(ASSET_FILE, "assetInit: Failed to open asset file!");
ASSET_ARCHIVE = NULL;
ASSET_ENTRY = NULL;
}
size_t assetReadUntil(uint8_t *buffer, const char_t c, const size_t maxLength) {
if(buffer == NULL) {
assertTrue(
maxLength == -1, "If no buffer is provided, maxLength must be -1."
);
uint8_t tBuffer[1];
size_t read = 0;
while(assetRead(tBuffer, 1) == 1 && (char_t)tBuffer[0] != c) read++;
return read;
} else {
size_t read = 0;
while(read < maxLength) {
// TODO: Read more than 1 char at a time.
read += assetRead(buffer + read, 1);
if((char_t)buffer[read-1] == c) return read - 1;
}
return -1;
}
}
void assetOpen(const char_t *path) {
assertNull(ASSET_ARCHIVE, "assetOpenFile: Archive is not NULL!");
assertNull(ASSET_ENTRY, "assetOpenFile: Entry is not NULL!");
assertStringValid(path, 1024, "assetOpenFile: Path is not valid!");
// Store path
strcpy(ASSET_PATH_CURRENT, path);
// Prepare data
ASSET_ARCHIVE = archive_read_new();
assertNotNull(ASSET_ARCHIVE, "assetOpenFile: Failed to create archive!");
// Set up the reader
// archive_read_support_filter_bzip2(ASSET_ARCHIVE);
archive_read_support_format_tar(ASSET_ARCHIVE);
// Open reader
archive_read_set_open_callback(ASSET_ARCHIVE, &assetArchiveOpen);
archive_read_set_read_callback(ASSET_ARCHIVE, &assetArchiveRead);
archive_read_set_seek_callback(ASSET_ARCHIVE, &assetArchiveSeek);
archive_read_set_close_callback(ASSET_ARCHIVE, &assetArchiveOpen);
archive_read_set_callback_data(ASSET_ARCHIVE, ASSET_ARCHIVE_BUFFER);
int32_t ret = archive_read_open1(ASSET_ARCHIVE);
assertTrue(ret == ARCHIVE_OK, "assetOpenFile: Failed to open archive!");
// Iterate over each file.
while(archive_read_next_header(ASSET_ARCHIVE, &ASSET_ENTRY) == ARCHIVE_OK) {
const char_t *headerFile = (char_t*)archive_entry_pathname(ASSET_ENTRY);
if(strcmp(headerFile, ASSET_PATH_CURRENT) == 0) return;
int32_t ret = archive_read_data_skip(ASSET_ARCHIVE);
assertTrue(ret == ARCHIVE_OK, "assetOpenFile: Failed to skip data!");
}
assertUnreachable("assetOpenFile: Failed to find file!");
}
size_t assetGetSize() {
assertNotNull(ASSET_ARCHIVE, "assetGetSize: Archive is NULL!");
assertNotNull(ASSET_ENTRY, "assetGetSize: Entry is NULL!");
assertTrue(
archive_entry_size_is_set(ASSET_ENTRY),
"assetGetSize: Entry size is not set!"
);
size_t n = archive_entry_size(ASSET_ENTRY);
// Remnant when get size was doing some incorrect stuff.
// char_t path[2048];
// sprintf(
// path, "/home/yourwishes/htdocs/dusk/build/assets/%s", ASSET_PATH_CURRENT
// );
// FILE *temp = fopen(path, "rb");
// assertNotNull(temp, "assetGetSize: Failed to open temp file!");
// fseek(temp, 0, SEEK_END);
// size_t size = ftell(temp);
// assertTrue(size == n, "assetGetSize: Size is not equal!");
// fclose(temp);
return n;
}
size_t assetRead(uint8_t *buffer, size_t bufferSize) {
assertNotNull(ASSET_ARCHIVE, "assetRead: Archive is NULL!");
assertNotNull(ASSET_ENTRY, "assetRead: Entry is NULL!");
assertNotNull(buffer, "assetRead: Buffer is NULL!");
assertTrue(bufferSize > 0, "assetRead: Buffer size must be greater than 0!");
ssize_t read = archive_read_data(ASSET_ARCHIVE, buffer, bufferSize);
if(read == ARCHIVE_FATAL) {
assertUnreachable(archive_error_string(ASSET_ARCHIVE));
}
assertTrue(read != ARCHIVE_RETRY, "assetRead: Failed to read data (RETRY)!");
assertTrue(read != ARCHIVE_WARN, "assetRead: Failed to read data (WARN)!");
return read;
}
void assetSkip(const size_t length) {
assertNotNull(ASSET_ARCHIVE, "assetSkip: Archive is NULL!");
assertNotNull(ASSET_ENTRY, "assetSkip: Entry is NULL!");
assertTrue(length > 0, "assetSkip: Length must be greater than 0!");
// Asset archive does not support skipping, so we have to read and discard.
uint8_t buffer[1024];
size_t remaining = length;
do {
size_t toRead = mathMin(remaining, 1024);
size_t read = assetRead(buffer, toRead);
assertTrue(read == toRead, "assetSkip: Failed to skip data! (overskip?)");
remaining -= read;
} while(remaining > 0);
}
void assetClose() {
assertNotNull(ASSET_ARCHIVE, "assetClose: Archive is NULL!");
assertNotNull(ASSET_ENTRY, "assetClose: Entry is NULL!");
int32_t ret = archive_read_free(ASSET_ARCHIVE);
assertTrue(ret == ARCHIVE_OK, "assetClose: Failed to close archive!");
ASSET_ARCHIVE = NULL;
ASSET_ENTRY = NULL;
}
void assetDispose() {
assertNull(ASSET_ARCHIVE, "assetDestroy: Archive is not NULL!");
assertNull(ASSET_ENTRY, "assetDestroy: Entry is not NULL!");
int32_t result = fclose(ASSET_FILE);
assertTrue(result == 0, "assetDestroy: Failed to close asset file!");
}

73
src/dawn/asset/asset.h Normal file
View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawn.h"
/**
* Initializes the asset manager.
*/
void assetInit();
/**
* Opens an asset by its filename (within the asset archive). Asset paths should
* always use the unix forward slash '/' as a path separator.
*
* @param path The path to the asset within the archive.
*/
void assetOpen(const char_t *path);
/**
* Returns the size of the asset.
*
* @return The size of the asset.
*/
size_t assetGetSize();
/**
* Reads the asset into the buffer.
*
* @param buffer The buffer to read the asset into.
* @param bufferSize The size of the buffer.
* @return The amount of data read.
*/
size_t assetRead(uint8_t *buffer, size_t bufferSize);
/**
* Reads ahead in the buffer until either the end of the buffer, or the
* specified character is found. Return value will be -1 if the character was
* not found.
*
* Buffer can be NULL if you just want to skip ahead.
*
* Returned value will be either the amount of data read into the buffer, which
* excludes the extra 1 character that was read from the asset. If the character
* was not found, -1 will be returned.
*
* @param buffer Buffer to read into.
* @param c Character to read until.
* @param maxLength Maximum length to read.
* @return -1 if the character was not found, otherwise the amount of data read.
*/
size_t assetReadUntil(uint8_t *buffer, const char_t c, const size_t maxLength);
/**
* Skips ahead in the buffer by the specified length.
*
* @param length The length to skip ahead by.
*/
void assetSkip(const size_t length);
/**
* Closes the asset.
*/
void assetClose();
/**
* Destroys and cleans up the asset manager.
*/
void assetDispose();

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "asset/assetarchive.h"
#include "assert/assert.h"
#include "util/math.h"
FILE *ASSET_FILE;
struct archive *ASSET_ARCHIVE;
struct archive_entry *ASSET_ENTRY;
uint8_t ASSET_ARCHIVE_BUFFER[ASSET_BUFFER_SIZE];
char_t ASSET_PATH_CURRENT[ASSET_PATH_MAX];
ssize_t assetArchiveRead(
struct archive *archive,
void *data,
const void **buffer
) {
assertNotNull(archive, "assetArchiveRead: Archive is NULL!");
assertNotNull(data, "assetArchiveRead: Data is NULL!");
assertNotNull(buffer, "assetArchiveRead: Buffer is NULL!");
*buffer = data;
size_t read = fread(data, 1, ASSET_BUFFER_SIZE, ASSET_FILE);
if(ferror(ASSET_FILE)) return ARCHIVE_FATAL;
return read;
}
int64_t assetArchiveSeek(
struct archive *archive,
void *data,
int64_t offset,
int32_t whence
) {
assertNotNull(archive, "assetArchiveSeek: Archive is NULL!");
assertNotNull(data, "assetArchiveSeek: Data is NULL!");
assertTrue(offset > 0, "assetArchiveSeek: Offset must be greater than 0!");
int32_t ret = fseek(ASSET_FILE, offset, whence);
assertTrue(ret == 0, "assetArchiveSeek: Failed to seek!");
return ftell(ASSET_FILE);
}
int32_t assetArchiveOpen(struct archive *a, void *data) {
int32_t ret = fseek(ASSET_FILE, 0, SEEK_SET);
assertTrue(ret == 0, "assetArchiveOpen: Failed to seek to start of file!");
return ARCHIVE_OK;
}
int32_t assetArchiveClose(struct archive *a, void *data) {
return assetArchiveOpen(a, data);
}

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "asset/asset.h"
#include <archive.h>
#include <archive_entry.h>
#define ASSET_BUFFER_SIZE 32768
#define ASSET_PATH_MAX 1024
extern FILE *ASSET_FILE;
extern struct archive *ASSET_ARCHIVE;
extern struct archive_entry *ASSET_ENTRY;
extern uint8_t ASSET_ARCHIVE_BUFFER[ASSET_BUFFER_SIZE];
extern char_t ASSET_PATH_CURRENT[ASSET_PATH_MAX];
/**
* Internal read method provided to libarchive api.
*
* @param archive The archive to read from.
* @param data The data to read into.
* @param buffer The buffer to read from.
* @return The amount of data read.
*/
ssize_t assetArchiveRead(
struct archive *archive,
void *data,
const void **buffer
);
/**
* Internal seek method provided to libarchive api.
*
* @param archive The archive to seek in.
* @param data The data to seek in.
* @param offset Offset bytes to seek.
* @param whence Relative to whence to seek.
* @return The new position.
*/
int64_t assetArchiveSeek(
struct archive *archive,
void *data,
int64_t offset,
int32_t whence
);
/**
* Internal open method provided to libarchive api.
*
* @param archive The archive to open.
* @param data The data to open.
* @return The result of the open.
*/
int32_t assetArchiveOpen(struct archive *a, void *data);
/**
* Internal close method provided to libarchive api.
*
* @param archive The archive to close.
* @param data The data to close.
* @return The result of the close.
*/
int32_t assetArchiveClose(struct archive *a, void *data);

560
src/dawn/asset/assetjson.c Normal file
View File

@@ -0,0 +1,560 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assetjson.h"
#include "assert/assert.h"
#include "util/memory.h"
size_t assetJsonParse(const char_t *json, assetjson_t **out) {
size_t offset = assetJsonParseSub(json, out);
// We only expect whitespace or EOF here
char_t c;
size_t len = strlen(json);
while(offset <= len) {
c = json[offset];
if(c == '\0') break;
if(c == ' ' || c == '\t' || c == '\n' || c == '\r') {
offset++;
continue;
}
assertUnreachable("Unexpected character found after JSON data.");
}
assertTrue(c == '\0', "Unexpected character found after JSON data.");
assertTrue(offset == len, "Unexpected character found after JSON data.");
return offset;
}
size_t assetJsonParseSub(
const char_t *json,
assetjson_t **out
) {
size_t offset = 0;
char_t c;
// Skip whitespace
while((c = json[offset]) == ' ' || c == '\t' || c == '\n' || c == '\r') {
if(c == '\0') assertUnreachable("Unexpected end of JSON data.");
offset++;
}
// Read first character
c = json[offset];
switch(c) {
case '{':
offset += assetJsonParseAsObject(json + offset, out);
break;
case '[':
offset += assetJsonParseAsArray(json + offset, out);
break;
case '"':
offset += assetJsonParseAsString(json + offset, out);
break;
case '-':
case '0' ... '9':
case '.':
offset += assetJsonParseAsNumber(json + offset, out);
break;
case 't':
case 'f':
offset += assetJsonParseAsBoolean(json + offset, out);
break;
case 'n':
offset += assetJsonParseAsNull(json + offset, out);
break;
default:
assertUnreachable("Invalid JSON data type found.");
break;
}
return offset;
}
size_t assetJsonParseAsNull(
const char_t *json,
assetjson_t **out
) {
assetjson_t *obj = (assetjson_t*)memoryAllocate(sizeof(assetjson_t));
// Read "null"
assertTrue(json[0] == 'n', "Expected NULL data type. (n0)");
assertTrue(json[1] == 'u', "Expected NULL data type. (u0)");
assertTrue(json[2] == 'l', "Expected NULL data type. (l0)");
assertTrue(json[3] == 'l', "Expected NULL data type. (l1)");
obj->type = ASSET_JSON_DATA_TYPE_NULL;
*out = obj;
return 4;
}
size_t assetJsonParseAsBoolean(
const char_t *json,
assetjson_t **out
) {
assetjson_t *obj = (assetjson_t*)memoryAllocate(sizeof(assetjson_t));
obj->type = ASSET_JSON_DATA_TYPE_BOOLEAN;
*out = obj;
if(json[0] == 't') {
// Read "true"
assertTrue(json[0] == 't', "Expected TRUE data type. (t0)");
assertTrue(json[1] == 'r', "Expected TRUE data type. (r0)");
assertTrue(json[2] == 'u', "Expected TRUE data type. (u0)");
assertTrue(json[3] == 'e', "Expected TRUE data type. (e0)");
obj->boolean = true;
return 4;
} else {
// Read "false"
assertTrue(json[0] == 'f', "Expected FALSE data type. (f0)");
assertTrue(json[1] == 'a', "Expected FALSE data type. (a0)");
assertTrue(json[2] == 'l', "Expected FALSE data type. (l0)");
assertTrue(json[3] == 's', "Expected FALSE data type. (s0)");
assertTrue(json[4] == 'e', "Expected FALSE data type. (e0)");
obj->boolean = false;
return 5;
}
}
size_t assetJsonParseAsString(
const char_t *json,
assetjson_t **out
) {
assetjson_t *obj = memoryAllocate(sizeof(assetjson_t));
obj->type = ASSET_JSON_DATA_TYPE_STRING;
// For each char
size_t offset = 1;// Skip opening quote
size_t outOffset = 0;
char c;
bool_t inEscape = false;
size_t bufferSize = 2;
char_t *string = (char_t*)memoryAllocate(bufferSize * sizeof(char_t));
while(true) {
c = json[offset];
if(c == '\0') assertUnreachable("Unexpected end of string.");
if(inEscape) {
inEscape = false;
switch(c) {
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 't':
c = '\t';
break;
case 'b':
c = '\b';
break;
case 'f':
c = '\f';
break;
case 'u':
assertUnreachable("Unicode escape sequences are not supported.");
break;
default:
break;
}
if(outOffset >= bufferSize) {
bufferSize *= 2;
string = memoryReallocate(string, bufferSize * sizeof(char_t));
}
string[outOffset] = c;
offset++;
outOffset++;
continue;
}
if(c == '\\') {
inEscape = true;
offset++;
continue;
}
if(c == '"') break;
if(outOffset >= bufferSize) {
bufferSize *= 2;
string = memoryReallocate(string, bufferSize * sizeof(char_t));
}
string[outOffset] = c;
offset++;
outOffset++;
}
string[outOffset] = '\0';
outOffset++;
*out = obj;
obj->string = string;
return offset + 1;// For closing string quote
}
size_t assetJsonParseAsObject(
const char_t *json,
assetjson_t **out
) {
assetjson_t *obj = memoryAllocate(sizeof(assetjson_t));
obj->type = ASSET_JSON_DATA_TYPE_OBJECT;
size_t bufferSize = 2;
char_t **keys = memoryAllocate(bufferSize * sizeof(char_t*));
assetjson_t **values = memoryAllocate(bufferSize * sizeof(assetjson_t*));
size_t length = 0;
// Skip whitespace
size_t offset = 1;// Skip opening bracket
char_t c;
while(true) {
while((c = json[offset]) == ' ' || c == '\t' || c == '\n' || c == '\r') {
if(c == '\0') assertUnreachable("Unexpected end of JSON data.");
offset++;
}
// Only expect opening string or closing brace
if(c == '}') break;
assertTrue(c == '"', "Expected opening string for JSON object key.");
char_t *bufferKey;
// Skip "
offset++;
offset += assetJsonReadString(json + offset, &bufferKey);
// Skip whitespace
while((c = json[offset]) == ' ' || c == '\t' || c == '\n' || c == '\r') {
if(c == '\0') assertUnreachable("Unexpected end of JSON data.");
offset++;
}
// Only expect colon
assertTrue(c == ':', "Expected colon after JSON object key.");
offset++;
// Skip whitespace
while((c = json[offset]) == ' ' || c == '\t' || c == '\n' || c == '\r') {
if(c == '\0') assertUnreachable("Unexpected end of JSON data.");
offset++;
}
// Parse sub
assetjson_t *value;
offset += assetJsonParseSub(json + offset, &value);
// Need to resize?
if(length >= bufferSize) {
bufferSize *= 2;
keys = memoryReallocate(keys, bufferSize * sizeof(char_t*));
values = memoryReallocate(values, bufferSize * sizeof(assetjson_t*));
}
keys[length] = bufferKey;
values[length] = value;
length++;
// Skip whitespace
while((c = json[offset]) == ' ' || c == '\t' || c == '\n' || c == '\r') {
if(c == '\0') assertUnreachable("Unexpected end of JSON data.");
offset++;
}
// Expect either comma or closing bracket
assertTrue(
c == ',' || c == '}',
"Expected comma or closing bracket after JSON object value."
);
if(c == '}') break;
offset++;
}
obj->object.keys = keys;
obj->object.values = values;
obj->object.length = length;
*out = obj;
return offset + 1;// Skip closing bracket
}
size_t assetJsonParseAsArray(
const char_t *json,
assetjson_t **out
) {
assetjson_t *obj = (assetjson_t*)memoryAllocate(sizeof(assetjson_t));
obj->type = ASSET_JSON_DATA_TYPE_ARRAY;
size_t offset = 1;// Skip opening bracket
char_t c;
// Create array
size_t arraySize = 2;
obj->array.value = (assetjson_t**)memoryAllocate(
arraySize * sizeof(assetjson_t*)
);
obj->array.length = 0;
// Until closing bracket
while(true) {
c = json[offset];
if(c == '\0') assertUnreachable("Unexpected end of JSON array.");
if(c == ']') break;
// Skip whitespace ONLY
if(c == ' ' || c == '\t' || c == '\n' || c == '\r') {
offset++;
continue;
}
// Need to expand?
if(obj->array.length >= arraySize) {
arraySize *= 2;
obj->array.value = memoryReallocate(
obj->array.value, arraySize * sizeof(assetjson_t*)
);
}
// Parse sub
offset += assetJsonParseSub(
json + offset,
&obj->array.value[obj->array.length++]
);
// Skip whitespace ONLY
while((c = json[offset]) == ' ' || c == '\t' || c == '\n' || c == '\r') {
if(c == '\0') assertUnreachable("Unexpected end of JSON data.");
offset++;
}
// If comma, continue
if(c == ',') {
offset++;
continue;
}
// If closing bracket, break
if(c == ']') break;
// Else, error
assertUnreachable("Unexpected character found in JSON array.");
}
// End of array
*out = obj;
return offset + 1;// Skip closing bracket
}
size_t assetJsonParseAsNumber(
const char_t *json,
assetjson_t **out
) {
assetjson_t *obj = (assetjson_t*)memoryAllocate(sizeof(assetjson_t));
obj->type = ASSET_JSON_DATA_TYPE_NUMBER;
// For each char
size_t offset = 0;
size_t outOffset = 0;
char_t c;
size_t bufferSize = 2;
char_t *buffer = (char_t*)memoryAllocate(bufferSize * sizeof(char_t));
bool_t hasDecimal = false;
bool_t hasNumber = false;
// Read number
while(true) {
c = json[offset];
if(c == '\0') assertUnreachable("Unexpected end of number.");
if(c == '-') {
// only accepted on first input
assertTrue(outOffset == 0, "Unexpected - after first digit");
} else if(c == '.') {
// only accepted once
assertTrue(!hasDecimal, "Unexpected . after first decimal");
hasDecimal = true;
if(!hasNumber) {
// If no number before decimal, add a 0
if(outOffset >= bufferSize) {
bufferSize *= 2;
buffer = memoryReallocate(buffer, bufferSize * sizeof(char_t));
}
buffer[outOffset] = '0';
outOffset++;
}
} else if(c >= '0' && c <= '9') {
hasNumber = true;
} else {
break;
}
// Need to expand?
if(outOffset >= bufferSize) {
bufferSize *= 2;
buffer = memoryReallocate(buffer, bufferSize * sizeof(char_t));
}
buffer[outOffset] = c;
offset++;
outOffset++;
}
// Seal the buffer, parse and cleanup
buffer[outOffset] = '\0';
obj->number = strtod(buffer, NULL);
memoryFree(buffer);
*out = obj;
return offset;
}
size_t assetJsonReadString(
const char_t *json,
char_t **buffer
) {
size_t offset = 0;
size_t outOffset = 0;
char_t c;
size_t bufferSize = 32;
char_t *string = (char_t*)memoryAllocate(sizeof(char_t) * bufferSize);
bool_t inEscape = false;
// For each char
while(true) {
c = json[offset];
if(c == '\0') assertUnreachable("Unexpected end of string.");
if(inEscape) {
inEscape = false;
switch(c) {
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 't':
c = '\t';
break;
case 'b':
c = '\b';
break;
case 'f':
c = '\f';
break;
case 'u':
assertUnreachable("Unicode escape sequences are not supported.");
break;
default:
break;
}
if(outOffset >= bufferSize) {
bufferSize *= 2;
string = memoryReallocate(string, bufferSize);
}
string[outOffset] = c;
offset++;
outOffset++;
continue;
}
if(c == '\\') {
inEscape = true;
offset++;
continue;
}
if(c == '"') break;
if(outOffset >= bufferSize) {
bufferSize *= 2;
string = memoryReallocate(string, bufferSize);
}
string[outOffset] = c;
offset++;
outOffset++;
}
string[outOffset] = '\0';
outOffset++;
*buffer = string;
return offset + 1;
}
assetjson_t * assetJsonGetObjectValue(assetjson_t *json, const char_t *key) {
assertTrue(json->type == ASSET_JSON_DATA_TYPE_OBJECT, "Expected JSON object.");
for(size_t i = 0; i < json->object.length; i++) {
if(strcmp(json->object.keys[i], key) == 0) {
return json->object.values[i];
}
}
return NULL;
}
void assetJsonDispose(assetjson_t *json) {
switch(json->type) {
case ASSET_JSON_DATA_TYPE_OBJECT:
for(size_t i = 0; i < json->object.length; i++) {
memoryFree(json->object.keys[i]);
assetJsonDispose(json->object.values[i]);
}
memoryFree(json->object.keys);
memoryFree(json->object.values);
break;
case ASSET_JSON_DATA_TYPE_ARRAY:
for(size_t i = 0; i < json->array.length; i++) {
assetJsonDispose(json->array.value[i]);
}
memoryFree(json->array.value);
break;
case ASSET_JSON_DATA_TYPE_STRING:
memoryFree(json->string);
break;
case ASSET_JSON_DATA_TYPE_NUMBER:
break;
case ASSET_JSON_DATA_TYPE_BOOLEAN:
break;
case ASSET_JSON_DATA_TYPE_NULL:
break;
}
memoryFree(json);
}

140
src/dawn/asset/assetjson.h Normal file
View File

@@ -0,0 +1,140 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawn.h"
typedef enum {
ASSET_JSON_DATA_TYPE_OBJECT,
ASSET_JSON_DATA_TYPE_ARRAY,
ASSET_JSON_DATA_TYPE_STRING,
ASSET_JSON_DATA_TYPE_NUMBER,
ASSET_JSON_DATA_TYPE_BOOLEAN,
ASSET_JSON_DATA_TYPE_NULL
} assetjsondatatype_t;
typedef struct _assetjson_t assetjson_t;
typedef struct _assetjson_t {
assetjsondatatype_t type;
union {
struct {
char_t **keys;
assetjson_t **values;
size_t length;
} object;
struct {
assetjson_t **value;
size_t length;
} array;
double_t number;
char_t *string;
bool_t boolean;
};
} assetjson_t;
/**
* Parses a JSON string.
*
* @param json JSON string to parse.
* @param out Pointer to store the parsed JSON object.
* @return The number of characters parsed.
*/
size_t assetJsonParse(const char_t *json, assetjson_t **out);
/**
* Parses a JSON string as a sub-object.
*
* @param json JSON string to parse.
* @param out Pointer to store the parsed JSON object.
* @return The number of characters parsed.
*/
size_t assetJsonParseSub(const char_t *json, assetjson_t **out);
/**
* Parses a JSON string as a NULL value.
*
* @param json JSON string to parse.
* @param out Pointer to store the parsed JSON object.
* @return The number of characters parsed.
*/
size_t assetJsonParseAsNull(const char_t *json, assetjson_t **out);
/**
* Parses a JSON string as a boolean value.
*
* @param json JSON string to parse.
* @param out Pointer to store the parsed JSON object.
* @return The number of characters parsed.
*/
size_t assetJsonParseAsBoolean(const char_t *json, assetjson_t **out);
/**
* Parses a JSON string as an object.
*
* @param json JSON string to parse.
* @param out Pointer to store the parsed JSON object.
* @return The number of characters parsed.
*/
size_t assetJsonParseAsObject(const char_t *json, assetjson_t **out);
/**
* Parses a JSON string as an array.
*
* @param json JSON string to parse.
* @param out Pointer to store the parsed JSON object.
* @return The number of characters parsed.
*/
size_t assetJsonParseAsArray(const char_t *json, assetjson_t **out);
/**
* Parses a JSON string as a string.
*
* @param json JSON string to parse.
* @param out Pointer to store the parsed JSON object.
* @return The number of characters parsed.
*/
size_t assetJsonParseAsString(const char_t *json, assetjson_t **out);
/**
* Parses a JSON string as a number.
*
* @param json JSON string to parse.
* @param out Pointer to store the parsed JSON object.
* @return The number of characters parsed.
*/
size_t assetJsonParseAsNumber(const char_t *json, assetjson_t **out);
/**
* Parses a JSON string as a string.
*
* @param json JSON string to parse.
* @param out Pointer to store the parsed JSON object.
* @return The number of characters parsed.
*/
size_t assetJsonReadString(const char_t *json, char_t **out);
/**
* Parses a JSON string as a number.
*
* @param json JSON string to parse.
* @param out Pointer to store the parsed JSON object.
* @return The number of characters parsed.
*/
assetjson_t *assetJsonGetObjectValue(assetjson_t *object, const char_t *key);
/**
* Parses a JSON string as a number.
*
* @param json JSON string to parse.
* @param out Pointer to store the parsed JSON object.
* @return The number of characters parsed.
*/
void assetJsonDispose(assetjson_t *json);

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assetlanguage.h"
#include "asset/asset.h"
#include "assert/assert.h"
#include "locale/language.h"
#include "util/memory.h"
void assetLanguageObjectLoad(
const char_t *key,
assetjson_t *json
) {
char_t tKey[LANGUAGE_STRING_KEY_LENGTH_MAX];
size_t keyLength = strlen(key);
strcpy(tKey, key);
if(keyLength > 0) {
tKey[keyLength] = '.';
keyLength++;
}
for(int32_t i = 0; i < json->object.length; i++) {
const char_t *subKey = json->object.keys[i];
assetjson_t *value = json->object.values[i];
strcpy(tKey + keyLength, subKey);
if(json->object.values[i]->type == ASSET_JSON_DATA_TYPE_OBJECT) {
assetLanguageObjectLoad(tKey, value);
continue;
}
if(json->object.values[i]->type == ASSET_JSON_DATA_TYPE_STRING) {
strcpy(LANGUAGE.keys[LANGUAGE.count], tKey);
strcpy(LANGUAGE.strings[LANGUAGE.count], value->string);
LANGUAGE.count++;
continue;
}
assertUnreachable("Language value is not a string or object!");
}
}
void assetLanguageLoad(const char_t *path) {
assertNotNull(path, "Path cannot be NULL.");
assetOpen(path);
size_t length = assetGetSize();
char_t *buffer = memoryAllocate(sizeof(char_t) * (length + 1));
buffer[length] = '\0';
size_t read = assetRead((uint8_t*)buffer, length);
assertTrue(read == length, "Failed to read language file!");
assetClose();
assetjson_t *json;
read = assetJsonParse(buffer, &json);
memoryFree(buffer);
assertTrue(
json->type == ASSET_JSON_DATA_TYPE_OBJECT,
"Language file is not an object!"
);
languageInit();
assetLanguageObjectLoad("", json);
assetJsonDispose(json);
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "asset/assetjson.h"
/**
* Loads a JSON object from the asset file.
*
* @param key The key to load.
* @param json The value to load into.
*/
void assetLanguageObjectLoad(
const char_t *key,
assetjson_t *json
);
/**
* Loads the language file from the specified path.
*
* @param path Path to the language file.
*/
void assetLanguageLoad(const char_t *path);

236
src/dawn/asset/assetmap.c Normal file
View File

@@ -0,0 +1,236 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assetmap.h"
#include "asset/asset.h"
#include "assert/assert.h"
#include "util/memory.h"
void assetMapLoadEntity(
assetjson_t *jEnt,
map_t *map
) {
int32_t x = (int32_t)assetJsonGetObjectValue(jEnt, "x")->number;
int32_t y = (int32_t)assetJsonGetObjectValue(jEnt, "y")->number;
entitytype_t type = (entitytype_t)assetJsonGetObjectValue(
jEnt, "type"
)->number;
entity_t *ent = mapEntityAdd(map);
entityInit(ent, type, map);
entityPositionSet(ent, x, y);
assetjson_t *val;
switch(type) {
case ENTITY_TYPE_NPC:
val = assetJsonGetObjectValue(jEnt, "name");
if(val != NULL) {
assertTrue(
val->type == ASSET_JSON_DATA_TYPE_STRING,
"assetMapLoad: NPC name is not a string!"
);
npcNameSet(&ent->npc, val->string);
}
val = assetJsonGetObjectValue(jEnt, "text");
if(val != NULL) {
assertTrue(
val->type == ASSET_JSON_DATA_TYPE_STRING,
"assetMapLoad: NPC text is not a string!"
);
npcTextSet(&ent->npc, val->string);
}
break;
case ENTITY_TYPE_SIGN:
val = assetJsonGetObjectValue(jEnt, "text");
if(val != NULL) {
assertTrue(
val->type == ASSET_JSON_DATA_TYPE_STRING,
"assetMapLoad: Sign text is not a string!"
);
signTextAppend(&ent->sign, val->string);
}
val = assetJsonGetObjectValue(jEnt, "texts");
if(val != NULL) {
assertTrue(
val->type == ASSET_JSON_DATA_TYPE_ARRAY,
"assetMapLoad: Sign texts is not an array!"
);
assertTrue(
val->array.length <= SIGN_TEXT_COUNT_MAX,
"assetMapLoad: Too many sign texts!"
);
for(int32_t i = 0; i < val->array.length; i++) {
assetjson_t *subVal = val->array.value[i];
assertTrue(
subVal->type == ASSET_JSON_DATA_TYPE_STRING,
"assetMapLoad: Sign text is not a string!"
);
signTextAppend(&ent->sign, subVal->string);
}
}
break;
case ENTITY_TYPE_DOOR:
val = assetJsonGetObjectValue(jEnt, "door");
if(val != NULL) {
assetjson_t *door = assetJsonGetObjectValue(val, "x");
x = (int32_t)door->number;
assertTrue(
door->type == ASSET_JSON_DATA_TYPE_NUMBER,
"assetMapLoad: Door x is not a number!"
);
door = assetJsonGetObjectValue(val, "y");
y = (int32_t)door->number;
assertTrue(
door->type == ASSET_JSON_DATA_TYPE_NUMBER,
"assetMapLoad: Door y is not a number!"
);
door = assetJsonGetObjectValue(val, "direction");
entitydirection_t direction = ENTITY_DIRECTION_SOUTH;
if(door != NULL) {
direction = (entitydirection_t)(
assetJsonGetObjectValue(val, "direction")->number
);
}
door = assetJsonGetObjectValue(val, "map");
maplist_t list;
if(door == NULL) {
list = map->list;
} else if(door->type == ASSET_JSON_DATA_TYPE_STRING) {
list = mapListGet(door->string);
} else if(door->type == ASSET_JSON_DATA_TYPE_NUMBER) {
list = (maplist_t)door->number;
} else {
assertUnreachable("assetMapLoad: Door map not string or number!");
}
doorDestinationSet(&ent->door, x, y, direction, list);
}
break;
default:
break;
}
}
void assetMapLoad(
const char_t *path,
map_t *map
) {
assertNotNull(map, "assetMapLoad: Map is NULL!");
// Read in the string data.
assetOpen(path);
size_t length = assetGetSize();
char_t *buffer = memoryAllocate(sizeof(char_t) * (length + 1));
size_t read = assetRead((uint8_t*)buffer, length);
buffer[length] = '\0';
assertTrue(read == length, "assetMapLoad: Failed to read map data!");
assetClose();
// Begin parsing JSON data.
assetjson_t *json;
read = assetJsonParse(buffer, &json);
memoryFree(buffer);
assertTrue(
json->type == ASSET_JSON_DATA_TYPE_OBJECT,
"assetMapLoad: Map data is not an object!"
);
int32_t width = (int32_t)assetJsonGetObjectValue(json, "width")->number;
int32_t height = (int32_t)assetJsonGetObjectValue(json, "height")->number;
assetjson_t *layers = assetJsonGetObjectValue(json, "layers");
assertTrue(
layers->type == ASSET_JSON_DATA_TYPE_ARRAY,
"assetMapLoad: Layers is not an array!"
);
int32_t layerCount = layers->array.length;
assertTrue(layerCount == MAP_LAYERS_MAX, "assetMapLoad: No layers found!");
mapInit(map, mapListGet(path), width, height, layerCount);
// Load tile data.
for(int32_t i = 0; i < layerCount; i++) {
assetjson_t *layer = layers->array.value[i];
assertTrue(
layer->type == ASSET_JSON_DATA_TYPE_OBJECT,
"assetMapLoad: Layer is not an object!"
);
assetjson_t *tiles = assetJsonGetObjectValue(layer, "tiles");
assertTrue(
tiles->type == ASSET_JSON_DATA_TYPE_ARRAY,
"assetMapLoad: Tiles is not an array!"
);
assertTrue(
tiles->array.length == width * height,
"assetMapLoad: Tile count does not match map size!"
);
for(int32_t j = 0; j < width * height; j++) {
map->tiles[j] = (tile_t)tiles->array.value[j]->number;
}
}
// Load entity data
assetjson_t *entities = assetJsonGetObjectValue(json, "entities");
if(entities != NULL) {
assertTrue(
entities->type == ASSET_JSON_DATA_TYPE_ARRAY,
"assetMapLoad: Entities is not an array!"
);
for(int32_t i = 0; i < entities->array.length; i++) {
assetjson_t *jEnt = entities->array.value[i];
assertTrue(
jEnt->type == ASSET_JSON_DATA_TYPE_OBJECT,
"assetMapLoad: Entity is not an object!"
);
assetMapLoadEntity(jEnt, map);
}
}
// Load trigger data
assetjson_t *triggers = assetJsonGetObjectValue(json, "triggers");
if(triggers != NULL) {
assertTrue(
triggers->type == ASSET_JSON_DATA_TYPE_ARRAY,
"assetMapLoad: Triggers is not an array!"
);
for(int32_t i = 0; i < triggers->array.length; i++) {
assetjson_t *jTrig = triggers->array.value[i];
assertTrue(
jTrig->type == ASSET_JSON_DATA_TYPE_OBJECT,
"assetMapLoad: Trigger is not an object!"
);
int32_t x = (int32_t)assetJsonGetObjectValue(jTrig, "x")->number;
int32_t y = (int32_t)assetJsonGetObjectValue(jTrig, "y")->number;
int32_t width = (int32_t)assetJsonGetObjectValue(jTrig, "width")->number;
int32_t height = (int32_t)assetJsonGetObjectValue(
jTrig, "height"
)->number;
triggertype_t type = (triggertype_t)assetJsonGetObjectValue(
jTrig, "type"
)->number;
trigger_t *trigger = mapTriggerAdd(map);
triggerInit(trigger, type, x, y, width, height);
}
}
assetJsonDispose(json);
}

32
src/dawn/asset/assetmap.h Normal file
View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "rpg/world/map.h"
#include "asset/assetjson.h"
/**
* Loads an entity from the specified JSON object.
*
* @param jEnt JSON object to load the entity from.
* @param map Map to load the entity into.
*/
void assetMapLoadEntity(
assetjson_t *jEnt,
map_t *map
);
/**
* Loads a map from the specified path.
*
* @param path Path to the map file.
* @param map Map to load the data into.
*/
void assetMapLoad(
const char_t *path,
map_t *map
);

22
src/dawn/dawn.h Normal file
View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <assert.h>
#include <float.h>
#include <errno.h>
typedef bool bool_t;
typedef char char_t;
typedef unsigned char uchar_t;
typedef uint_fast32_t flag_t;

View File

@@ -1,14 +1,15 @@
# Copyright (c) 2025 Dominic Masters
# Copyright (c) 2024 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Subdirs
add_subdirectory(draw)
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
Display.cpp
color.c
frame.c
symbol.c
)
# Subdirs
add_subdirectory(color)
add_subdirectory(mesh)

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "animationitem.h"
#define ANIMATION_ITEM_COUNT_MAX 64
typedef struct _animation_t {
float_t time;
bool_t loop;
float_t duration;
animationitem_t items[ANIMATION_ITEM_COUNT_MAX];
uint8_t itemCount;
} animation_t;
/**
* Initializes an animation.
* @param anim The animation to initialize.
*/
void animationInit(animation_t *anim);
/**
* Updates an animation.
* @param anim The animation to update.
* @param delta The time since the last update.
*/
void animationUpdate(animation_t *anim, const float_t delta);
/**
* Returns the total duration of the animation.
*
* @param anim The animation to get the duration of.
* @return The total duration of the animation.
*/
float_t animationDurationGet(const animation_t *anim);

View File

@@ -0,0 +1,20 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawn.h"
typedef struct _animation_t animation_t;
typedef struct _animationitem_t {
float_t time;
float_t duration;
void *user;
void (*start)(animation_t *anim, animationitem_t *item);
void (*update)(animation_t *anim,animationitem_t *item,const float_t rDelta);
} animationitem_t;

26
src/dawn/display/color.c Normal file
View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "color.h"
#include "assert/assert.h"
const color4f_t COLOR4F_COLOR_MAP[COLOR_COUNT] = {
COLOR4F_BLACK,
COLOR4F_WHITE,
COLOR4F(0.8f, 0.0f, 0.0f, 1.0f),
COLOR4F(0.0f, 0.5f, 0.0f, 1.0f),
COLOR4F(0.0f, 0.0f, 0.8f, 1.0f),
COLOR4F(0.8f, 0.8f, 0.0f, 1.0f),
COLOR4F(0.8f, 0.0f, 0.8f, 1.0f),
COLOR4F(0.0f, 0.8f, 0.8f, 1.0f),
COLOR4F(0.5f, 0.5f, 0.5f, 1.0f),
COLOR4F(0.5f, 0.25f, 0.0f, 1.0f)
};
void color4fCopy(const color4f_t src, color4f_t dest) {
memcpy(dest, src, sizeof(color4f_t));
}

81
src/dawn/display/color.h Normal file
View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawn.h"
typedef float_t color3f_t[3];
typedef float_t color4f_t[4];
// Simple colors, used by the frame system.
#define COLOR_BLACK 0x00
#define COLOR_WHITE 0x01
#define COLOR_RED 0x02
#define COLOR_GREEN 0x03
#define COLOR_BLUE 0x04
#define COLOR_YELLOW 0x05
#define COLOR_MAGENTA 0x06
#define COLOR_CYAN 0x07
#define COLOR_GRAY 0x08
#define COLOR_BROWN 0x09
#define COLOR_COUNT COLOR_BROWN+1
#define COLOR3F(r,g,b) ((color3f_t){ r, g, b })
#define COLOR3F_RED COLOR3F(1, 0, 0)
#define COLOR3F_GREEN COLOR3F(0, 1, 0)
#define COLOR3F_BLUE COLOR3F(0, 0, 1)
#define COLOR3F_BLACK COLOR3F(0, 0, 0)
#define COLOR3F_WHITE COLOR3F(1, 1, 1)
#define COLOR3F_MAGENTA COLOR3F(1, 0, 1)
#define COLOR3F_YELLOW COLOR3F(1, 1, 0)
#define COLOR3F_CYAN COLOR3F(0, 1, 1)
#define COLOR3F_GRAY COLOR3F(0.5f, 0.5f, 0.5f)
#define COLOR3F_DARKGRAY COLOR3F(0.25f, 0.25f, 0.25f)
#define COLOR3F_LIGHTGRAY COLOR3F(0.75f, 0.75f, 0.75f)
#define COLOR3F_ORANGE COLOR3F(1, 0.5f, 0)
#define COLOR3F_PURPLE COLOR3F(0.5f, 0, 1)
#define COLOR3F_PINK COLOR3F(1, 0, 0.5f)
#define COLOR3F_BROWN COLOR3F(0.5f, 0.25f, 0)
#define COLOR3F_GOLD COLOR3F(1, 0.75f, 0)
#define COLOR3F_SILVER COLOR3F(0.75f, 0.75f, 0.75f)
#define COLOR3F_BRONZE COLOR3F(0.75f, 0.5f, 0.25f)
#define COLOR3F_CORNFLOWERBLUE COLOR3F(0.4f, 0.6f, 0.9f)
#define COLOR4F(r,g,b,a) ((color4f_t){ r, g, b, a })
#define COLOR4F_RED COLOR4F(1, 0, 0, 1)
#define COLOR4F_GREEN COLOR4F(0, 1, 0, 1)
#define COLOR4F_BLUE COLOR4F(0, 0, 1, 1)
#define COLOR4F_BLACK COLOR4F(0, 0, 0, 1)
#define COLOR4F_WHITE COLOR4F(1, 1, 1, 1)
#define COLOR4F_MAGENTA COLOR4F(1, 0, 1, 1)
#define COLOR4F_YELLOW COLOR4F(1, 1, 0, 1)
#define COLOR4F_CYAN COLOR4F(0, 1, 1, 1)
#define COLOR4F_GRAY COLOR4F(0.5f, 0.5f, 0.5f, 1)
#define COLOR4F_DARKGRAY COLOR4F(0.25f, 0.25f, 0.25f, 1)
#define COLOR4F_LIGHTGRAY COLOR4F(0.75f, 0.75f, 0.75f, 1)
#define COLOR4F_ORANGE COLOR4F(1, 0.5f, 0, 1)
#define COLOR4F_PURPLE COLOR4F(0.5f, 0, 1, 1)
#define COLOR4F_PINK COLOR4F(1, 0, 0.5f, 1)
#define COLOR4F_BROWN COLOR4F(0.5f, 0.25f, 0, 1)
#define COLOR4F_GOLD COLOR4F(1, 0.75f, 0, 1)
#define COLOR4F_SILVER COLOR4F(0.75f, 0.75f, 0.75f, 1)
#define COLOR4F_BRONZE COLOR4F(0.75f, 0.5f, 0.25f, 1)
#define COLOR4F_CORNFLOWERBLUE COLOR4F(0.4f, 0.6f, 0.9f, 1)
#define COLOR4F_TRANSPARENT_BLACK COLOR4F(0, 0, 0, 0)
#define COLOR4F_TRANSPARENT_WHITE COLOR4F(1, 1, 1, 0)
#define COLOR4F_TRANSPARENT COLOR4F_TRANSPARENT_BLACK
extern const color4f_t COLOR4F_COLOR_MAP[COLOR_COUNT];
/**
* Copies a color.
*
* @param src Source color.
* @param dest Destination color.
*/
void color4fCopy(const color4f_t src, color4f_t dest);

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawn.h"
/**
* Initializes the display subsystem.
*/
void displayInit();
/**
* Updates the display subsystem, this is basically asking the display renderer
* to perform a render.
*/
void displayUpdate();
/**
* Cleans up the display subsystem.
*/
void displayDispose();

View File

@@ -0,0 +1,18 @@
# Copyright (c) 2024 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Subdirs
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
drawbattle.c
drawstatemainmenu.c
drawstateoverworld.c
drawmap.c
drawtext.c
drawshape.c
drawui.c
)

View File

@@ -0,0 +1,17 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "drawbattle.h"
#include "display/draw/drawshape.h"
#include "display/draw/drawui.h"
void drawBattle() {
drawClear(' ', COLOR_WHITE);
// Draw UI
drawUITextbox();
}

View File

@@ -0,0 +1,13 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
/**
* Draws the battle state.
*/
void drawBattle();

View File

@@ -0,0 +1,91 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "drawmap.h"
#include "assert/assert.h"
#include "display/symbol.h"
void drawMap(
const map_t *map,
const uint16_t cameraX,
const uint16_t cameraY,
const uint16_t drawX,
const uint16_t drawY,
const uint16_t drawWidth,
const uint16_t drawHeight
) {
assertNotNull(map, "Map cannot be NULL.");
assertTrue(drawX < FRAME_WIDTH, "Map is too far right.");
assertTrue(drawY < FRAME_HEIGHT, "Map is too far down.");
assertTrue(drawWidth > 0, "Map is too narrow.");
assertTrue(drawHeight > 0, "Map is too short.");
assertTrue(drawX + drawWidth <= FRAME_WIDTH, "Map is too wide.");
assertTrue(drawY + drawHeight <= FRAME_HEIGHT, "Map is too tall.");
entity_t *ent;
tile_t tile;
uint32_t i;
char_t *bufferDest;
uint8_t *colorDest;
// If the size of the map's smaller than the frame, center it, otherwise use
// the cameraPosition as the center point.
int16_t offsetX = 0, offsetY = 0;
if(map->width < drawWidth) {
offsetX = (drawWidth - map->width) / 2;
} else {
// Clamp to map bounds
if(cameraX < drawWidth / 2) {
offsetX = 0;
} else if(cameraX >= map->width - (drawWidth / 2)) {
offsetX = -(map->width - drawWidth);
} else {
offsetX = -(cameraX - (drawWidth / 2));
}
}
if(map->height < drawHeight) {
offsetY = (drawHeight - map->height) / 2;
} else {
// Clamp to map bounds
if(cameraY < drawHeight / 2) {
offsetY = 0;
} else if(cameraY >= map->height - (drawHeight / 2)) {
offsetY = -(map->height - drawHeight);
} else {
offsetY = -(cameraY - (drawHeight / 2));
}
}
// Draw the map
i = 0;
for(uint16_t y = 0; y < drawHeight; y++) {
bufferDest = &FRAME_BUFFER[(drawY + y) * FRAME_WIDTH + drawX];
colorDest = &FRAME_COLOR[(drawY + y) * FRAME_WIDTH + drawX];
for(uint16_t x = 0; x < drawWidth; x++) {
if(x < offsetX || y < offsetY || x >= map->width + offsetX || y >= map->height + offsetY) {
colorDest[x] = COLOR_BLACK;
bufferDest[x] = ' ';
continue;
}
// Entity?
ent = mapEntityGetByPosition(map, x - offsetX, y - offsetY);
if(ent != NULL) {
colorDest[x] = symbolGetColorByEntity(ent);
bufferDest[x] = symbolGetCharByEntity(ent);
continue;
}
// Tile?
tile = mapTileGetByPosition(map, x - offsetX, y - offsetY, 0);
colorDest[x] = tileColorGet(tile);
bufferDest[x] = tileSymbolGet(tile);
}
}
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/frame.h"
#include "rpg/world/map.h"
/**
* Draws a map to a region of the screen.
*
* @param map Map to draw.
* @param cameraX X position of the camera.
* @param cameraY Y position of the camera.
* @param drawX X position to draw the map.
* @param drawY Y position to draw the map.
* @param drawWidth Width of the map to draw.
* @param drawHeight Height of the map to draw.
*/
void drawMap(
const map_t *map,
const uint16_t cameraX,
const uint16_t cameraY,
const uint16_t drawX,
const uint16_t drawY,
const uint16_t drawWidth,
const uint16_t drawHeight
);

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "drawshape.h"
#include "assert/assert.h"
#include "util/memory.h"
void drawClear(
const char_t c,
const uint8_t color
) {
memorySet(FRAME_BUFFER, c, FRAME_HEIGHT * FRAME_WIDTH);
memorySet(FRAME_COLOR, color, FRAME_HEIGHT * FRAME_WIDTH);
}
void drawBox(
const uint16_t x,
const uint16_t y,
const uint16_t width,
const uint16_t height,
const char_t c,
const uint8_t color
) {
assertTrue(x < FRAME_WIDTH, "Box is too far right.");
assertTrue(y < FRAME_HEIGHT, "Box is too far down.");
if(width == 0 || height == 0) return;
assertTrue(x + width <= FRAME_WIDTH, "Box is too wide.");
assertTrue(y + height <= FRAME_HEIGHT, "Box is too tall.");
if(width == FRAME_WIDTH) {
memorySet(&FRAME_BUFFER[y * FRAME_WIDTH + x], c, height * width);
memorySet(&FRAME_COLOR[y * FRAME_WIDTH + x], color, height * width);
return;
}
for(uint16_t iy = 0; iy < height; iy++) {
memorySet(&FRAME_BUFFER[(y + iy) * FRAME_WIDTH + x], c, width);
memorySet(&FRAME_COLOR[(y + iy) * FRAME_WIDTH + x], color, width);
}
}

View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/frame.h"
/**
* Clears the frame buffer with a character and color.
*
* @param c Character to clear the frame buffer with.
* @param color Color to clear the frame buffer with.
*/
void drawClear(
const char_t c,
const uint8_t color
);
/**
* Draws a box to the frame buffer.
*
* @param x The x position to draw the box.
* @param y The y position to draw the box.
* @param width The width of the box.
* @param height The height of the box.
* @param c The character to draw the box with.
* @param color The color to draw the box.
*/
void drawBox(
const uint16_t x,
const uint16_t y,
const uint16_t width,
const uint16_t height,
const char_t c,
const uint8_t color
);

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "drawstatemainmenu.h"
#include "display/draw/drawtext.h"
#include "display/draw/drawui.h"
#include "ui/mainmenu.h"
void drawStateMainMenu() {
// Draw the logo
drawText("Dawn", 4, 0, 0, COLOR_WHITE);
// Draw the menu
uint16_t width = menuGetLongestStringLength(&MAIN_MENU) + 3;
uint16_t height = MAIN_MENU.rows + 2;
drawUIMenuBox(
&MAIN_MENU,
(FRAME_WIDTH - width) / 2, FRAME_HEIGHT - height - 4,
width, height,
COLOR_WHITE, COLOR_WHITE, COLOR_WHITE
);
}

View File

@@ -0,0 +1,14 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/frame.h"
/**
* Draws the main menu state.
*/
void drawStateMainMenu();

View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "drawstateoverworld.h"
#include "ui/textbox.h"
#include "rpg/world/map.h"
#include "game/game.h"
#include "display/draw/drawmap.h"
#include "display/draw/drawui.h"
void drawStateOverworld() {
map_t *map = GAME.currentMap;
if(map == NULL) return;
// Draw the map, based on player position
entity_t *player = mapEntityGetByType(map, ENTITY_TYPE_PLAYER);
uint16_t cameraPositionX, cameraPositionY;
if(player == NULL) {
cameraPositionX = 0;
cameraPositionY = 0;
} else {
cameraPositionX = player->x;
cameraPositionY = player->y;
}
drawMap(
GAME.currentMap,
cameraPositionX, cameraPositionY,
0, 0,
FRAME_WIDTH, FRAME_HEIGHT
);
// Draw UI
drawUITextbox();
}

View File

@@ -0,0 +1,14 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/frame.h"
/**
* Draws the overworld state.
*/
void drawStateOverworld();

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "drawtext.h"
#include "assert/assert.h"
#include "util/math.h"
void drawText(
const char_t *text,
const size_t len,
const uint16_t x,
const uint16_t y,
const uint8_t color
) {
if(len == 0) return;
size_t rLen = mathMin(len, strlen(text));
if(rLen == 0) return;
assertTrue(x < FRAME_WIDTH, "Text is too far right.");
assertTrue(y < FRAME_HEIGHT, "Text is too low.");
char_t c;
uint16_t xPos = x;
uint16_t yPos = y * FRAME_WIDTH;
size_t i = 0;
do {
c = text[i++];
if(c == '\n') {
yPos += FRAME_WIDTH;
xPos = x;
continue;
}
assertTrue(xPos < FRAME_WIDTH, "Text overflows right.");
FRAME_BUFFER[yPos + xPos] = c;
FRAME_COLOR[yPos + xPos] = color;
xPos++;
} while(i < rLen);
}

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/frame.h"
/**
* Draws some text to the frame buffer.
*
* @param text The text to draw.
* @param len The length of the text to draw.
* @param x The x position to draw the text.
* @param y The y position to draw the text.
* @param color The color to draw the text.
*/
void drawText(
const char_t *text,
const size_t len,
const uint16_t x,
const uint16_t y,
const uint8_t color
);

View File

@@ -0,0 +1,229 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "drawui.h"
#include "drawshape.h"
#include "assert/assert.h"
#include "game/time.h"
#include "display/draw/drawtext.h"
#include "ui/textbox.h"
#include "ui/testmenu.h"
#include "util/math.h"
#include "util/memory.h"
void drawUIBox(
const uint16_t x,
const uint16_t y,
const uint16_t width,
const uint16_t height,
const uint8_t color,
const bool_t fill
) {
assertTrue(x < FRAME_WIDTH, "Box is too far right.");
assertTrue(y < FRAME_HEIGHT, "Box is too far down.");
assertTrue(width > 2, "Box is too narrow.");
assertTrue(height > 2, "Box is too short.");
assertTrue(x + width <= FRAME_WIDTH, "Box is too wide.");
assertTrue(y + height <= FRAME_HEIGHT, "Box is too tall.");
// Draw the top and bottom borders
for(uint16_t i = 0; i < width; i++) {
FRAME_BUFFER[y * FRAME_WIDTH + x + i] = '-';
FRAME_BUFFER[(y + height - 1) * FRAME_WIDTH + x + i] = '-';
FRAME_COLOR[y * FRAME_WIDTH + x + i] = color;
FRAME_COLOR[(y + height - 1) * FRAME_WIDTH + x + i] = color;
}
// Draw the left and right borders
for(uint16_t i = 0; i < height; i++) {
FRAME_BUFFER[(y + i) * FRAME_WIDTH + x] = '|';
FRAME_BUFFER[(y + i) * FRAME_WIDTH + x + width - 1] = '|';
FRAME_COLOR[(y + i) * FRAME_WIDTH + x] = color;
FRAME_COLOR[(y + i) * FRAME_WIDTH + x + width - 1] = color;
}
// Draw the corners
FRAME_BUFFER[y * FRAME_WIDTH + x] = '+';
FRAME_BUFFER[y * FRAME_WIDTH + x + width - 1] = '+';
FRAME_BUFFER[(y + height - 1) * FRAME_WIDTH + x] = '+';
FRAME_BUFFER[(y + height - 1) * FRAME_WIDTH + x + width - 1] = '+';
FRAME_COLOR[y * FRAME_WIDTH + x] = color;
FRAME_COLOR[y * FRAME_WIDTH + x + width - 1] = color;
FRAME_COLOR[(y + height - 1) * FRAME_WIDTH + x] = color;
FRAME_COLOR[(y + height - 1) * FRAME_WIDTH + x + width - 1] = color;
// Fill the inside
if(!fill) return;
drawBox(x + 1, y + 1, width - 2, height - 2, ' ', COLOR_BLACK);
}
void drawUIMenu(
menu_t *menu,
const uint16_t x,
const uint16_t y,
const uint16_t width,
const uint16_t height,
const uint8_t cursorColor,
const uint8_t textColor
) {
assertTrue(menu, "Menu cannot be NULL.");
assertTrue(menu->rows > 0, "Rows must be greater than 0.");
assertTrue(menu->columns > 0, "Columns must be greater than 0.");
assertTrue(width*2 > menu->columns, "Width must be greater than columns.");
uint16_t colWidth;
uint16_t startingCol;
uint16_t startingRow;
uint16_t rowCount;
uint16_t colCount;
char_t *str;
size_t i;
// Determine the width of each column. This must include space for the cursor
// and the text.
if(menu->columns == 1 || rowCount == 0) {
colWidth = width;
colCount = 1;
} else {
size_t longestString = 0;
i = 0;
while(true) {
str = menu->strings[i++];
if(str == NULL) break;
size_t len = strlen(str);
if(len > longestString) longestString = len;
// If we overrun the size of the box, we can stop looking.
if((len+1) > width) {
longestString = width;
break;
}
}
// How many columns can we fit?
uint16_t possibleColumnsAtThisWidth = width / (longestString + 1);
if(possibleColumnsAtThisWidth < menu->columns) {
// We need to take a subset of the columns
colWidth = width / possibleColumnsAtThisWidth;
colCount = possibleColumnsAtThisWidth;
} else {
// We can fit all columns in
colWidth = width / menu->columns;
colCount = menu->columns;
}
assertTrue(longestString != 0, "Longest string has length 0?");
}
// Determine count of rows to render
if(rowCount == 1) {
rowCount = 1;
} else {
rowCount = mathMin(rowCount, height);
}
// Where are we rendering from?
startingCol = menu->renderOffsetX;
if(menu->x >= startingCol + colCount) {
startingCol = (menu->x + 1) - colCount;
} else if(menu->x < startingCol) {
startingCol = menu->x;
}
startingRow = menu->renderOffsetY;
if(menu->y >= startingRow + rowCount) {
startingRow = (menu->y + 1) - rowCount;
} else if(menu->y < startingRow) {
startingRow = menu->y;
}
// Update back to the menu
menu->renderOffsetX = startingCol;
menu->renderOffsetY = startingRow;
for(uint16_t col = startingCol; col < startingCol+colCount; col++) {
for(uint16_t row = startingRow; row < startingRow+rowCount; row++) {
uint16_t index = (row * menu->columns) + col;
if(menu->strings[index] == NULL) continue;
drawText(
menu->strings[index],
colWidth,
x + (colWidth * (col - startingCol)) + 1,
y + (row - startingRow),
textColor
);
}
}
// Draw the cursor
i = (
x + ((menu->x - startingCol) * colWidth)
) + (
(y + (menu->y - startingRow)) * FRAME_WIDTH
);
FRAME_BUFFER[i] = '>';
FRAME_COLOR[i] = cursorColor;
}
void drawUIMenuBox(
menu_t *menu,
const uint16_t x,
const uint16_t y,
const uint16_t width,
const uint16_t height,
const uint8_t cursorColor,
const uint8_t textColor,
const uint8_t boxColor
) {
drawUIBox(x, y, width, height, boxColor, true);
drawUIMenu(
menu,
x + 1, y + 1,
width - 2, height - 2,
cursorColor, textColor
);
}
void drawUITextbox() {
if(!textboxIsOpen()) return;
// Border
drawUIBox(
0, FRAME_HEIGHT - DRAW_UI_TEXTBOX_HEIGHT,
DRAW_UI_TEXTBOX_WIDTH, DRAW_UI_TEXTBOX_HEIGHT,
COLOR_MAGENTA, true
);
// Title
drawText(
TEXTBOX.title,
-1,
2, FRAME_HEIGHT - DRAW_UI_TEXTBOX_HEIGHT,
COLOR_WHITE
);
// Text
drawText(
TEXTBOX.text,
TEXTBOX.textIndex,
1, FRAME_HEIGHT - DRAW_UI_TEXTBOX_HEIGHT + 1,
COLOR_WHITE
);
// Blinking cursor
memorySet(&FRAME_BUFFER[DRAW_UI_TEXTBOX_CURSOR_POS], ' ', 3);
memorySet(&FRAME_COLOR[DRAW_UI_TEXTBOX_CURSOR_POS], COLOR_WHITE, 3);
if(TEXTBOX.textIndex < TEXTBOX.textLength) return;
int32_t blink = (int32_t)(TIME.time * DRAW_UI_TEXTBOX_BLINKS_PER_SECOND) % 2;
FRAME_BUFFER[DRAW_UI_TEXTBOX_CURSOR_POS + 1] = blink ? '>' : ' ';
FRAME_BUFFER[DRAW_UI_TEXTBOX_CURSOR_POS + 2] = blink ? ' ' : '>';
}

View File

@@ -0,0 +1,83 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/frame.h"
#include "ui/menu.h"
#define DRAW_UI_TEXTBOX_WIDTH FRAME_WIDTH
#define DRAW_UI_TEXTBOX_HEIGHT 8
#define DRAW_UI_TEXTBOX_CURSOR_POS ((FRAME_HEIGHT * FRAME_WIDTH) - 4)
#define DRAW_UI_TEXTBOX_BLINKS_PER_SECOND 2
/**
* Draws a UI box to the frame buffer.
*
* @param x The x position to draw the border.
* @param y The y position to draw the border.
* @param width The width of the border.
* @param height The height of the border.
* @param color The color to draw the border.
* @param fill Whether or not to fill the inside of the border.
*/
void drawUIBox(
const uint16_t x,
const uint16_t y,
const uint16_t width,
const uint16_t height,
const uint8_t color,
const bool_t fill
);
/**
* Draws a UI menu to the frame buffer.
*
* @param menu The menu to draw.
* @param x The x position to draw the menu.
* @param y The y position to draw the menu.
* @param width The width of the menu.
* @param height The height of the menu.
* @param cursorColor The color of the cursor.
* @param textColor The color of the text.
*/
void drawUIMenu(
menu_t *menu,
const uint16_t x,
const uint16_t y,
const uint16_t width,
const uint16_t height,
const uint8_t cursorColor,
const uint8_t textColor
);
/**
* Draws a UI menu box to the frame buffer.
*
* @param menu The menu to draw.
* @param x The x position to draw the menu.
* @param y The y position to draw the menu.
* @param width The width of the menu.
* @param height The height of the menu.
* @param cursorColor The color of the cursor.
* @param textColor The color of the text.
* @param boxColor The color of the box.
*/
void drawUIMenuBox(
menu_t *menu,
const uint16_t x,
const uint16_t y,
const uint16_t width,
const uint16_t height,
const uint8_t cursorColor,
const uint8_t textColor,
const uint8_t boxColor
);
/**
* Draws the UI textbox to the frame buffer.
*/
void drawUITextbox();

54
src/dawn/display/frame.c Normal file
View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "frame.h"
#include "game/game.h"
#include "display/draw/drawshape.h"
#include "display/draw/drawtext.h"
#include "display/draw/drawstateoverworld.h"
#include "display/draw/drawstatemainmenu.h"
#include "display/draw/drawbattle.h"
char_t FRAME_BUFFER[FRAME_HEIGHT * FRAME_WIDTH];
uint8_t FRAME_COLOR[FRAME_HEIGHT * FRAME_WIDTH];
void frameInit() {
drawClear(' ', COLOR_BLACK);
}
void frameUpdate() {
switch(GAME.state) {
case GAME_STATE_PAUSED:
const char_t *str = "PAUSED";
size_t len = strlen(str);
drawText(
str,
-1,
(FRAME_WIDTH - len) / 2,
(FRAME_HEIGHT - 1) / 2,
COLOR_WHITE
);
break;
case GAME_STATE_OVERWORLD:
drawStateOverworld();
break;
case GAME_STATE_MAIN_MENU:
drawStateMainMenu();
break;
case GAME_STATE_BATTLE:
drawBattle();
break;
default:
printf("Rendering unknown game state: %d\n", GAME.state);
break;
}
}

33
src/dawn/display/frame.h Normal file
View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/color.h"
#define FRAME_CHAR_SIZE 8
#define FRAME_PIXEL_WIDTH 320
#define FRAME_PIXEL_HEIGHT 240
#define FRAME_WIDTH FRAME_PIXEL_WIDTH/FRAME_CHAR_SIZE
#define FRAME_HEIGHT FRAME_PIXEL_HEIGHT/FRAME_CHAR_SIZE
extern char_t FRAME_BUFFER[FRAME_HEIGHT * FRAME_WIDTH];
extern uint8_t FRAME_COLOR[FRAME_HEIGHT * FRAME_WIDTH];
/**
* Initializes the frame buffer.
*/
void frameInit();
/**
* Updates the terminal frame.
*/
void frameUpdate();
/**
* Clears the frame buffer.
*/
void frameClear();

55
src/dawn/display/symbol.c Normal file
View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert/assert.h"
#include "rpg/entity/entitydirection.h"
#include "symbol.h"
#include "game/time.h"
char_t symbolGetCharByEntity(const entity_t *ent) {
assertNotNull(ent, "Entity cannot be NULL.");
switch(ent->type) {
case ENTITY_TYPE_SIGN:
return '+';
case ENTITY_TYPE_DOOR:
return 'D';
default:
break;
}
switch(ent->direction) {
case ENTITY_DIRECTION_EAST: return '>';
case ENTITY_DIRECTION_WEST: return '<';
case ENTITY_DIRECTION_NORTH: return '^';
case ENTITY_DIRECTION_SOUTH: return 'v';
default:
assertUnreachable("Invalid entity direction.");
}
}
uint8_t symbolGetColorByEntity(const entity_t *ent) {
assertNotNull(ent, "Entity cannot be NULL.");
switch(ent->type) {
case ENTITY_TYPE_PLAYER:
return COLOR_RED;
case ENTITY_TYPE_NPC:
return COLOR_MAGENTA;
case ENTITY_TYPE_SIGN:
return COLOR_YELLOW;
case ENTITY_TYPE_DOOR:
return COLOR_BROWN;
default:
assertUnreachable("Invalid entity type.");
}
}

26
src/dawn/display/symbol.h Normal file
View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "rpg/entity/entity.h"
#include "display/color.h"
/**
* Returns the symbol for the given entity.
*
* @param ent Entity to get the symbol for.
* @return The symbol for the entity.
*/
char_t symbolGetCharByEntity(const entity_t *ent);
/**
* Returns the color for the given entity.
*
* @param ent Entity to get the color for.
* @return The color for the entity.
*/
uint8_t symbolGetColorByEntity(const entity_t *ent);

View File

@@ -0,0 +1,14 @@
# Copyright (c) 2024 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Subdirs
add_subdirectory(state)
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
time.c
game.c
)

94
src/dawn/game/game.c Normal file
View File

@@ -0,0 +1,94 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "game/game.h"
#include "game/time.h"
#include "input.h"
#include "display/display.h"
#include "asset/asset.h"
#include "ui/textbox.h"
#include "ui/testmenu.h"
#include "ui/mainmenu.h"
#include "game/state/mainmenu.h"
#include "game/state/mapchange.h"
#include "game/state/overworld.h"
#include "game/state/battle.h"
#include "rpg/conversation/conversation.h"
#include "asset/assetlanguage.h"
#include "util/memory.h"
game_t GAME;
void gameInit() {
memoryInit();
memorySet(&GAME, 0, sizeof(game_t));
timeInit();
inputInit();
displayInit();
assetInit();
assetLanguageLoad("en.json");
textboxInit();
testMenuInit();
mainMenuInit();
conversationInit();
GAME.state = GAME_STATE_INITIAL;
}
gameupdateresult_t gameUpdate(const float_t delta) {
timeUpdate(delta);
inputUpdate();
switch(GAME.state) {
case GAME_STATE_INITIAL:
GAME.state = GAME_STATE_MAIN_MENU;
break;
case GAME_STATE_OVERWORLD:
gameStateOverworldUpdate();
break;
case GAME_STATE_PAUSED:
if(inputWasPressed(INPUT_BIND_PAUSE)) GAME.state = GAME_STATE_OVERWORLD;
break;
case GAME_STATE_MAP_CHANGE:
gameStateMapChangeUpdate();
break;
case GAME_STATE_MAIN_MENU:
gameStateMainMenuUpdate();
break;
case GAME_STATE_BATTLE:
gameStateBattleUpdate();
break;
default:
printf("Updating unknown state %d\n", GAME.state);
}
if(inputWasPressed(INPUT_BIND_FORCE_QUIT)) GAME.shouldExit = true;
// Perform render.
displayUpdate();
if(GAME.shouldExit) return GAME_UPDATE_RESULT_EXIT;
return GAME_UPDATE_RESULT_CONTINUE;
}
void gameDispose() {
assetDispose();
displayDispose();
memoryTestZero();
}

50
src/dawn/game/game.h Normal file
View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "rpg/world/map.h"
typedef enum {
GAME_UPDATE_RESULT_CONTINUE = 0,
GAME_UPDATE_RESULT_EXIT = 1
} gameupdateresult_t;
typedef enum {
GAME_STATE_INITIAL = 0,
GAME_STATE_OVERWORLD = 1,
GAME_STATE_PAUSED = 2,
GAME_STATE_MAP_CHANGE = 3,
GAME_STATE_MAIN_MENU = 4,
GAME_STATE_BATTLE = 5
} gamestate_t;
typedef struct {
map_t *currentMap;
gamestate_t state;
bool_t shouldExit;
maplist_t mapNext;
} game_t;
extern game_t GAME;
/**
* Initializes the game state.
*/
void gameInit();
/**
* Updates the game state.
*
* @param delta Time since last update.
* @return Game update result, 0 for continue, 1 for exit, else for failure.
*/
gameupdateresult_t gameUpdate(const float_t delta);
/**
* Cleans up the game state.
*/
void gameDispose();

View File

@@ -0,0 +1,15 @@
# Copyright (c) 2024 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Subdirs
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
battle.c
mainmenu.c
mapchange.c
overworld.c
)

View File

@@ -0,0 +1,17 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "battle.h"
#include "game/game.h"
#include "ui/textbox.h"
#include "rpg/battle/battle.h"
void gameStateBattleUpdate() {
battleUpdate();
textboxUpdate();
}

View File

@@ -0,0 +1,13 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
/**
* Updates the battle state.
*/
void gameStateBattleUpdate();

View File

@@ -0,0 +1,14 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "mainmenu.h"
#include "ui/mainmenu.h"
void gameStateMainMenuUpdate() {
// Update the main menu
mainMenuUpdate();
}

View File

@@ -0,0 +1,14 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawn.h"
/**
* Updates the main menu state.
*/
void gameStateMainMenuUpdate();

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "mapchange.h"
#include "asset/assetmap.h"
#include "game/game.h"
#include "util/memory.h"
void gameStateMapChangeUpdate() {
// First try and find the player object
entity_t *player = NULL;
player_t oldPlayerData;
entitydirection_t dir = ENTITY_DIRECTION_SOUTH;
if(GAME.currentMap != NULL) {
player = mapEntityGetByType(GAME.currentMap, ENTITY_TYPE_PLAYER);
dir = player->direction;
oldPlayerData = player->player;
}
assetMapLoad(MAP_LIST_PATHS[GAME.mapNext], &MAP_MAIN);
GAME.state = GAME_STATE_OVERWORLD;
GAME.currentMap = &MAP_MAIN;
// Do not reference player since its invalid, just check if it DID exist
if(GAME.currentMap != NULL && player != NULL) {
player = mapEntityGetByType(GAME.currentMap, ENTITY_TYPE_PLAYER);
if(player == NULL) return;
player->player = oldPlayerData;
player->direction = dir;
}
}

View File

@@ -0,0 +1,14 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawn.h"
/**
* Updates the map change game state.
*/
void gameStateMapChangeUpdate();

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "overworld.h"
#include "game/game.h"
#include "input.h"
#include "ui/textbox.h"
#include "ui/testmenu.h"
#include "rpg/conversation/conversation.h"
void gameStateOverworldUpdate() {
textboxUpdate();
testMenuUpdate();
conversationUpdate();
if(GAME.currentMap) mapUpdate(GAME.currentMap);
if(inputWasPressed(INPUT_BIND_PAUSE)) GAME.state = GAME_STATE_PAUSED;
}

View File

@@ -0,0 +1,14 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawn.h"
/**
* Updates the overworld game state.
*/
void gameStateOverworldUpdate();

23
src/dawn/game/time.c Normal file
View File

@@ -0,0 +1,23 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "game/time.h"
#include "assert/assert.h"
dawntime_t TIME;
void timeInit() {
TIME.delta = 0.0f;
TIME.time = 0.0f;
}
void timeUpdate(const float_t delta) {
assertTrue(delta > 0, "time delta must be greater than 0.");
TIME.delta = delta;
TIME.time += delta;
}

28
src/dawn/game/time.h Normal file
View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawn.h"
typedef struct {
float_t delta;
float_t time;
} dawntime_t;
extern dawntime_t TIME;
/**
* Initializes the time system.
*/
void timeInit();
/**
* Updates the time system.
*
* @param delta Time since last update.
*/
void timeUpdate(const float_t delta);

41
src/dawn/input.c Normal file
View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "input.h"
#include "util/memory.h"
input_t INPUT;
void inputInit() {
memorySet(&INPUT, 0, sizeof(input_t));
}
void inputUpdate() {
// Update the previous state
memoryCopy(
INPUT.previousState,
INPUT.currentState,
sizeof(INPUT.previousState)
);
// Get the new state
for(inputbind_t i = 0; i < INPUT_BIND_COUNT; i++) {
INPUT.currentState[i] = inputGetState(i);
}
}
inputstate_t inputIsDown(const inputbind_t bind) {
return INPUT.currentState[bind];
}
inputstate_t inputWasPressed(const inputbind_t bind) {
return INPUT.currentState[bind] && !INPUT.previousState[bind];
}
inputstate_t inputWasReleased(const inputbind_t bind) {
return !INPUT.currentState[bind] && INPUT.previousState[bind];
}

68
src/dawn/input.h Normal file
View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawn.h"
typedef uint8_t inputbind_t;
typedef bool_t inputstate_t;
#define INPUT_BIND_UP 0x00
#define INPUT_BIND_DOWN 0x01
#define INPUT_BIND_LEFT 0x02
#define INPUT_BIND_RIGHT 0x03
#define INPUT_BIND_ACCEPT 0x04
#define INPUT_BIND_CANCEL 0x05
#define INPUT_BIND_PAUSE 0x06
#define INPUT_BIND_FORCE_QUIT 0x07
#define INPUT_BIND_COUNT (INPUT_BIND_FORCE_QUIT + 1)
typedef struct {
inputstate_t currentState[INPUT_BIND_COUNT];
inputstate_t previousState[INPUT_BIND_COUNT];
} input_t;
extern input_t INPUT;
/**
* Initializes the input system.
*/
void inputInit();
/**
* Updates the input system.
*/
void inputUpdate();
/**
* Returns whether or not the given input is down.
*
* @param bind The input to check.
*/
inputstate_t inputIsDown(const inputbind_t bind);
/**
* Returns whether or not the given input was pressed this update tick.
*
* @param bind The input to check.
*/
inputstate_t inputWasPressed(const inputbind_t bind);
/**
* Returns whether or not the given input was released this update tick.
*
* @param bind The input to check.
*/
inputstate_t inputWasReleased(const inputbind_t bind);
/**
* Returns the current state of the given input. Overridden per platform.
*
* @param bind The input to check.
*/
inputstate_t inputGetState(const inputbind_t bind);

View File

@@ -0,0 +1,12 @@
# Copyright (c) 2024 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Subdirs
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
language.c
)

View File

@@ -0,0 +1,43 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "language.h"
#include "assert/assert.h"
#include "util/memory.h"
language_t LANGUAGE;
void languageInit() {
memorySet(&LANGUAGE, 0, sizeof(language_t));
}
const char_t * languageGetPointer(const char_t *key) {
assertNotNull(key, "Key cannot be NULL.");
language_t *lang = &LANGUAGE;
lang->count;
int32_t i = 0;
while(i < LANGUAGE.count) {
if(strcmp(key, LANGUAGE.keys[i]) != 0) {
i++;
continue;
}
return LANGUAGE.strings[i];
}
return NULL;
}
int32_t languageGet(char_t *buffer, const char_t *key) {
const char_t *str = languageGetPointer(key);
if(str == NULL) return -1;
if(buffer == NULL) return strlen(str);
strcpy(buffer, str);
return strlen(str);
}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawn.h"
#define LANGUAGE_STRING_COUNT_MAX 128
#define LANGUAGE_STRING_KEY_LENGTH_MAX 64
#define LANGUAGE_STRING_LENGTH_MAX 1024
typedef struct {
const char_t keys[LANGUAGE_STRING_COUNT_MAX][LANGUAGE_STRING_KEY_LENGTH_MAX];
const char_t strings[LANGUAGE_STRING_COUNT_MAX][LANGUAGE_STRING_LENGTH_MAX];
int32_t count;
} language_t;
extern language_t LANGUAGE;
/**
* Initializes the language system.
*/
void languageInit();
/**
* Gets the pointer to the language string for the given key. Pointer should not
* be modified or freed.
*
* @param key The key to get the string for.
* @return The pointer to the string.
*/
const char_t * languageGetPointer(const char_t *key);
/**
* Returns the language string for the given key.
*
* @param buffer The buffer to write the string to, or NULL to get length.
* @param key The key to get the string for.
* @return The length of the string.
*/
int32_t languageGet(char_t *buffer, const char_t *key);

View File

@@ -0,0 +1,12 @@
# Copyright (c) 2024 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Subdirs
add_subdirectory(battle)
add_subdirectory(conversation)
add_subdirectory(entity)
add_subdirectory(world)
# Sources

View File

@@ -1,10 +1,12 @@
# Copyright (c) 2025 Dominic Masters
# Copyright (c) 2024 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Subdirs
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
Console.cpp
battle.c
)

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert/assert.h"
#include "battle.h"
#include "util/memory.h"
#include "game/game.h"
#include "ui/textbox.h"
battle_t BATTLE;
void battleInit() {
memorySet(&BATTLE, 0, sizeof(battle_t));
}
void battleStart() {
GAME.state = GAME_STATE_BATTLE;
BATTLE.state = BATTLE_STATE_INITIAL;
textboxSetText(NULL, "battle.start");
}
void battleUpdate() {
switch(BATTLE.state) {
case BATTLE_STATE_INITIAL:
if(textboxIsOpen()) return;
break;
default:
assertUnreachable("Unknown battle state.");
break;
}
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawn.h"
typedef enum {
BATTLE_STATE_INITIAL
} battlestate_t;
typedef struct {
} battlefighter_t;
typedef struct {
battlestate_t state;
} battle_t;
extern battle_t BATTLE;
/**
* Initializes the battle system.
*/
void battleInit();
/**
* Starts a battle.
*/
void battleStart(
);
/**
* Updates the battle system.
*/
void battleUpdate();

View File

@@ -0,0 +1,14 @@
# Copyright (c) 2024 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Subdirs
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
conversation.c
conversationinteractentity.c
conversationinteracttile.c
)

View File

@@ -0,0 +1,85 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "conversation.h"
#include "assert/assert.h"
#include "util/memory.h"
conversation_t CONVERSATION;
void conversationInit() {
memorySet(&CONVERSATION, 0, sizeof(conversation_t));
}
void conversationSet(
const conversationcallback_t init,
const conversationcallback_t update,
const conversationdata_t data
) {
CONVERSATION.init = init;
CONVERSATION.update = update;
CONVERSATION.index = CONVERSATION_NOT_INITIALIZED;
CONVERSATION.data = data;
}
void conversationUpdate() {
if(CONVERSATION.init == NULL || CONVERSATION.update == NULL) return;
uint16_t ret;
bool_t recheck = true;
// Init or update.
while(recheck) {
switch(CONVERSATION.index) {
case CONVERSATION_NOT_INITIALIZED:
CONVERSATION.index = 0;
ret = CONVERSATION.init(
&CONVERSATION, CONVERSATION.index
);
break;
default:
ret = CONVERSATION.update(&CONVERSATION, CONVERSATION.index);
break;
}
// Check ret value and update conversation.
switch(ret) {
case CONVERSATION_NEXT:
CONVERSATION.index++;
CONVERSATION.index = CONVERSATION.init(
&CONVERSATION, CONVERSATION.index
);
break;
case CONVERSATION_CONTINUE:
recheck = false;
break;
case CONVERSATION_RESTART:
CONVERSATION.index = 0;
CONVERSATION.init(&CONVERSATION, CONVERSATION.index);
break;
case CONVERSATION_INVALID:
assertUnreachable("Invalid converstaion retval");
recheck = false;
break;
case CONVERSATION_DONE:
CONVERSATION.init = NULL;
CONVERSATION.update = NULL;
recheck = false;
break;
default:
CONVERSATION.index = ret;
break;
}
}
}

Some files were not shown because too many files have changed in this diff Show More