Compare commits
72 Commits
Author | SHA1 | Date | |
---|---|---|---|
57995a0e00 | |||
53dc496f2f | |||
6a0ffd4a45 | |||
ef84c85527 | |||
39d74cc375 | |||
ddbef64f43 | |||
b2a3ca4411 | |||
a74f285cb2 | |||
5fb36aad35 | |||
258976b76c | |||
7867076bbe | |||
3baafec9cb | |||
6c062df9bb | |||
825cc88e17 | |||
9f4cb283c1 | |||
86feb7e56a | |||
a7dc7fdcf8 | |||
cb3a58e456 | |||
5334f0944e | |||
a1d4b0a1d7 | |||
ecb3b9c5d1 | |||
bf3912bb7f | |||
b5d7b7e229 | |||
b7987401af | |||
ffc46c677c | |||
7a8ca2fca1 | |||
5751f7c83c | |||
c69d0ec1cc | |||
e14445680f | |||
5256b32055 | |||
5444b0b8c7 | |||
45b3cf9478 | |||
0224ddd36b | |||
40b0395552 | |||
b7829fda5c | |||
bdef59bbe1 | |||
27ce6526e6 | |||
704002e671 | |||
cfe4cf6cad | |||
69672cf8a6 | |||
c1345218a3 | |||
be27408cf4 | |||
4205b919d4 | |||
e14083b4b4 | |||
ad317da97e | |||
2ff1a159bc | |||
c770953b2e | |||
2cb1d745b2 | |||
8ce0e8f9f6 | |||
5101a856be | |||
303d0c0a6f | |||
e3a4368d1e | |||
7c34127900 | |||
49d90b3362 | |||
935398d45e | |||
4065556d4a | |||
fb8f6e3f95 | |||
b4c2ce16a0 | |||
b309478922 | |||
ad3974bde5 | |||
e5349cc093 | |||
01c56477aa | |||
916396e175 | |||
0e5b85633c | |||
e5f3f69120 | |||
ca240bc180 | |||
a3a891ddb2 | |||
5fae94c722 | |||
d5b3b6d619 | |||
b4e261d954 | |||
856bc306fe | |||
cffe7f73a2 |
56
.github/workflows/build-helloworld-vita.yml
vendored
Normal file
56
.github/workflows/build-helloworld-vita.yml
vendored
Normal 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/
|
26
.github/workflows/build-liminal-glfw-linux64.yml
vendored
Normal file
26
.github/workflows/build-liminal-glfw-linux64.yml
vendored
Normal 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
2
.gitignore
vendored
@ -65,7 +65,7 @@ CTestTestfile.cmake
|
||||
_deps
|
||||
|
||||
# Custom
|
||||
build
|
||||
/build/*
|
||||
.vscode
|
||||
|
||||
assets/testworld/tileset.png
|
||||
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "lib/font8x8"]
|
||||
path = lib/font8x8
|
||||
url = https://github.com/dhepper/font8x8
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2022 Dominic Masters
|
||||
# Copyright (c) 2024 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
@ -6,11 +6,37 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_library(dawn STATIC
|
||||
src/funcs.c
|
||||
# Variable Caches
|
||||
set(DAWN_CACHE_TARGET "dawn-target")
|
||||
set(DAWN_TARGET_NAME "Dawn")
|
||||
|
||||
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()
|
||||
|
||||
# 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_TOOLS_DIR "${DAWN_ROOT_DIR}/tools")
|
||||
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")
|
||||
|
||||
# Initialize Project.
|
||||
project(Dawn
|
||||
VERSION 1.0.0
|
||||
LANGUAGES C
|
||||
)
|
||||
|
||||
target_include_directories(dawn PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
# Add tools
|
||||
add_subdirectory(tools)
|
||||
|
||||
# Add Libraries
|
||||
add_subdirectory(lib)
|
||||
|
||||
# Add Project Files
|
||||
add_subdirectory(src)
|
@ -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
42
assets/en.json
Normal 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
46
assets/maps/testmap.tmx
Normal 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
17
assets/maps/testmap2.tmx
Normal 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>
|
14
assets/tiled_project.tiled-project
Normal file
14
assets/tiled_project.tiled-project
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"automappingRulesFile": "",
|
||||
"commands": [
|
||||
],
|
||||
"compatibilityVersion": 1100,
|
||||
"extensionsPath": "extensions",
|
||||
"folders": [
|
||||
"."
|
||||
],
|
||||
"properties": [
|
||||
],
|
||||
"propertyTypes": [
|
||||
]
|
||||
}
|
114
assets/tiled_project.tiled-session
Normal file
114
assets/tiled_project.tiled-session
Normal 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
|
||||
}
|
||||
}
|
BIN
assets/tilemaps/entities.png
Normal file
BIN
assets/tilemaps/entities.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 326 B |
8
assets/tilemaps/entities.tsx
Normal file
8
assets/tilemaps/entities.tsx
Normal 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
BIN
assets/tilemaps/tilemap.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 408 B |
4
assets/tilemaps/tilemap.tsx
Normal file
4
assets/tilemaps/tilemap.tsx
Normal 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>
|
234
gentileset.py
Normal file
234
gentileset.py
Normal 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
31
lib/CMakeLists.txt
Normal 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
1
lib/font8x8
Submodule
Submodule lib/font8x8 added at 8e279d2d86
12
lib/glad/CMakeLists.txt
Normal file
12
lib/glad/CMakeLists.txt
Normal 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
|
||||
)
|
290
lib/glad/include/KHR/khrplatform.h
Normal file
290
lib/glad/include/KHR/khrplatform.h
Normal 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
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
2522
lib/glad/src/glad.c
Normal file
File diff suppressed because it is too large
Load Diff
22
src/CMakeLists.txt
Normal file
22
src/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright (c) 2024 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Build Project
|
||||
add_executable(${DAWN_TARGET_NAME})
|
||||
|
||||
# Add base
|
||||
add_subdirectory(dawn)
|
||||
|
||||
# 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()
|
39
src/dawn/CMakeLists.txt
Normal file
39
src/dawn/CMakeLists.txt
Normal 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)
|
12
src/dawn/assert/CMakeLists.txt
Normal file
12
src/dawn/assert/CMakeLists.txt
Normal 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
|
||||
assert.c
|
||||
)
|
37
src/dawn/assert/assert.c
Normal file
37
src/dawn/assert/assert.c
Normal 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
117
src/dawn/assert/assert.h
Normal 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__)
|
14
src/dawn/asset/CMakeLists.txt
Normal file
14
src/dawn/asset/CMakeLists.txt
Normal 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
159
src/dawn/asset/asset.c
Normal 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
73
src/dawn/asset/asset.h
Normal 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();
|
55
src/dawn/asset/assetarchive.c
Normal file
55
src/dawn/asset/assetarchive.c
Normal 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);
|
||||
}
|
68
src/dawn/asset/assetarchive.h
Normal file
68
src/dawn/asset/assetarchive.h
Normal 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
560
src/dawn/asset/assetjson.c
Normal 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
140
src/dawn/asset/assetjson.h
Normal 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);
|
70
src/dawn/asset/assetlanguage.c
Normal file
70
src/dawn/asset/assetlanguage.c
Normal 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);
|
||||
}
|
27
src/dawn/asset/assetlanguage.h
Normal file
27
src/dawn/asset/assetlanguage.h
Normal 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
236
src/dawn/asset/assetmap.c
Normal 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
32
src/dawn/asset/assetmap.h
Normal 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
22
src/dawn/dawn.h
Normal 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;
|
15
src/dawn/display/CMakeLists.txt
Normal file
15
src/dawn/display/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
# 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
|
||||
color.c
|
||||
frame.c
|
||||
symbol.c
|
||||
)
|
41
src/dawn/display/animation/animation.h
Normal file
41
src/dawn/display/animation/animation.h
Normal 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);
|
20
src/dawn/display/animation/animationitem.h
Normal file
20
src/dawn/display/animation/animationitem.h
Normal 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
26
src/dawn/display/color.c
Normal 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
81
src/dawn/display/color.h
Normal 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);
|
25
src/dawn/display/display.h
Normal file
25
src/dawn/display/display.h
Normal 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();
|
18
src/dawn/display/draw/CMakeLists.txt
Normal file
18
src/dawn/display/draw/CMakeLists.txt
Normal 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
|
||||
)
|
17
src/dawn/display/draw/drawbattle.c
Normal file
17
src/dawn/display/draw/drawbattle.c
Normal 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();
|
||||
}
|
13
src/dawn/display/draw/drawbattle.h
Normal file
13
src/dawn/display/draw/drawbattle.h
Normal 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();
|
91
src/dawn/display/draw/drawmap.c
Normal file
91
src/dawn/display/draw/drawmap.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
31
src/dawn/display/draw/drawmap.h
Normal file
31
src/dawn/display/draw/drawmap.h
Normal 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
|
||||
);
|
44
src/dawn/display/draw/drawshape.c
Normal file
44
src/dawn/display/draw/drawshape.c
Normal 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);
|
||||
}
|
||||
}
|
39
src/dawn/display/draw/drawshape.h
Normal file
39
src/dawn/display/draw/drawshape.h
Normal 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
|
||||
);
|
26
src/dawn/display/draw/drawstatemainmenu.c
Normal file
26
src/dawn/display/draw/drawstatemainmenu.c
Normal 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
|
||||
);
|
||||
}
|
14
src/dawn/display/draw/drawstatemainmenu.h
Normal file
14
src/dawn/display/draw/drawstatemainmenu.h
Normal 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();
|
39
src/dawn/display/draw/drawstateoverworld.c
Normal file
39
src/dawn/display/draw/drawstateoverworld.c
Normal 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();
|
||||
}
|
14
src/dawn/display/draw/drawstateoverworld.h
Normal file
14
src/dawn/display/draw/drawstateoverworld.h
Normal 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();
|
44
src/dawn/display/draw/drawtext.c
Normal file
44
src/dawn/display/draw/drawtext.c
Normal 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);
|
||||
}
|
26
src/dawn/display/draw/drawtext.h
Normal file
26
src/dawn/display/draw/drawtext.h
Normal 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
|
||||
);
|
229
src/dawn/display/draw/drawui.c
Normal file
229
src/dawn/display/draw/drawui.c
Normal 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 ? ' ' : '>';
|
||||
}
|
83
src/dawn/display/draw/drawui.h
Normal file
83
src/dawn/display/draw/drawui.h
Normal 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
54
src/dawn/display/frame.c
Normal 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
33
src/dawn/display/frame.h
Normal 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
55
src/dawn/display/symbol.c
Normal 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
26
src/dawn/display/symbol.h
Normal 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);
|
14
src/dawn/game/CMakeLists.txt
Normal file
14
src/dawn/game/CMakeLists.txt
Normal 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
94
src/dawn/game/game.c
Normal 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
50
src/dawn/game/game.h
Normal 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();
|
15
src/dawn/game/state/CMakeLists.txt
Normal file
15
src/dawn/game/state/CMakeLists.txt
Normal 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
|
||||
)
|
17
src/dawn/game/state/battle.c
Normal file
17
src/dawn/game/state/battle.c
Normal 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();
|
||||
}
|
13
src/dawn/game/state/battle.h
Normal file
13
src/dawn/game/state/battle.h
Normal 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();
|
14
src/dawn/game/state/mainmenu.c
Normal file
14
src/dawn/game/state/mainmenu.c
Normal 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();
|
||||
}
|
14
src/dawn/game/state/mainmenu.h
Normal file
14
src/dawn/game/state/mainmenu.h
Normal 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();
|
36
src/dawn/game/state/mapchange.c
Normal file
36
src/dawn/game/state/mapchange.c
Normal 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;
|
||||
}
|
||||
}
|
14
src/dawn/game/state/mapchange.h
Normal file
14
src/dawn/game/state/mapchange.h
Normal 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();
|
22
src/dawn/game/state/overworld.c
Normal file
22
src/dawn/game/state/overworld.c
Normal 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;
|
||||
}
|
14
src/dawn/game/state/overworld.h
Normal file
14
src/dawn/game/state/overworld.h
Normal 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
23
src/dawn/game/time.c
Normal 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
28
src/dawn/game/time.h
Normal 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
41
src/dawn/input.c
Normal 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
68
src/dawn/input.h
Normal 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);
|
12
src/dawn/locale/CMakeLists.txt
Normal file
12
src/dawn/locale/CMakeLists.txt
Normal 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
|
||||
)
|
43
src/dawn/locale/language.c
Normal file
43
src/dawn/locale/language.c
Normal 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);
|
||||
}
|
44
src/dawn/locale/language.h
Normal file
44
src/dawn/locale/language.h
Normal 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);
|
12
src/dawn/rpg/CMakeLists.txt
Normal file
12
src/dawn/rpg/CMakeLists.txt
Normal 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
|
12
src/dawn/rpg/battle/CMakeLists.txt
Normal file
12
src/dawn/rpg/battle/CMakeLists.txt
Normal 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
|
||||
battle.c
|
||||
)
|
37
src/dawn/rpg/battle/battle.c
Normal file
37
src/dawn/rpg/battle/battle.c
Normal 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;
|
||||
}
|
||||
}
|
40
src/dawn/rpg/battle/battle.h
Normal file
40
src/dawn/rpg/battle/battle.h
Normal 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();
|
14
src/dawn/rpg/conversation/CMakeLists.txt
Normal file
14
src/dawn/rpg/conversation/CMakeLists.txt
Normal 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
|
||||
)
|
85
src/dawn/rpg/conversation/conversation.c
Normal file
85
src/dawn/rpg/conversation/conversation.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
65
src/dawn/rpg/conversation/conversation.h
Normal file
65
src/dawn/rpg/conversation/conversation.h
Normal file
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 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 "rpg/world/tile.h"
|
||||
|
||||
#define CONVERSATION_NEXT ((uint16_t)-1)
|
||||
#define CONVERSATION_CONTINUE ((uint16_t)-2)
|
||||
#define CONVERSATION_NOT_INITIALIZED ((uint16_t)-3)
|
||||
#define CONVERSATION_RESTART CONVERSATION_NOT_INITIALIZED
|
||||
#define CONVERSATION_DONE ((uint16_t)-4)
|
||||
#define CONVERSATION_INVALID ((uint16_t)-5)
|
||||
|
||||
typedef struct _conversation_t conversation_t;
|
||||
|
||||
typedef uint16_t (*conversationcallback_t) (
|
||||
conversation_t *convo, const uint16_t
|
||||
);
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
entity_t *entity;
|
||||
} entityInteract;
|
||||
|
||||
struct {
|
||||
tile_t tile;
|
||||
} tileInteract;
|
||||
} conversationdata_t;
|
||||
|
||||
typedef struct _conversation_t {
|
||||
uint16_t index;
|
||||
conversationcallback_t init;
|
||||
conversationcallback_t update;
|
||||
conversationdata_t data;
|
||||
} conversation_t;
|
||||
|
||||
extern conversation_t CONVERSATION;
|
||||
|
||||
/**
|
||||
* Initializes the conversation object.
|
||||
*/
|
||||
void conversationInit();
|
||||
|
||||
/**
|
||||
* Sets the conversation object.
|
||||
*
|
||||
* @param init The initialization callback.
|
||||
* @param update The update callback.
|
||||
* @param data The user data.
|
||||
*/
|
||||
void conversationSet(
|
||||
const conversationcallback_t init,
|
||||
const conversationcallback_t update,
|
||||
const conversationdata_t data
|
||||
);
|
||||
|
||||
/**
|
||||
* Update a given conversation.
|
||||
*/
|
||||
void conversationUpdate();
|
61
src/dawn/rpg/conversation/conversationinteractentity.c
Normal file
61
src/dawn/rpg/conversation/conversationinteractentity.c
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "conversationinteractentity.h"
|
||||
#include "assert/assert.h"
|
||||
#include "rpg/entity/entity.h"
|
||||
#include "locale/language.h"
|
||||
#include "ui/textbox.h"
|
||||
|
||||
uint16_t conversationInteractEntityInit(
|
||||
conversation_t *convo,
|
||||
const uint16_t i
|
||||
) {
|
||||
assertNotNull(convo, "Conversation is NULL!");
|
||||
assertNotNull(convo->data.entityInteract.entity, "Conversation user is NULL!");
|
||||
|
||||
entity_t *e = (entity_t*)convo->data.entityInteract.entity;
|
||||
switch(e->type) {
|
||||
case ENTITY_TYPE_SIGN:
|
||||
textboxSetText("entities.sign.name", e->sign.texts[i]);
|
||||
return i;
|
||||
|
||||
case ENTITY_TYPE_NPC:
|
||||
textboxSetText(e->npc.text.name, e->npc.text.text);
|
||||
return i;
|
||||
|
||||
default:
|
||||
assertUnreachable("Invalid entity type for conversation.");
|
||||
return CONVERSATION_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t conversationInteractEntityUpdate(
|
||||
conversation_t *convo,
|
||||
const uint16_t i
|
||||
) {
|
||||
assertNotNull(convo, "Conversation is NULL!");
|
||||
assertNotNull(convo->data.entityInteract.entity, "Conversation user is NULL!");
|
||||
|
||||
entity_t *e = (entity_t*)convo->data.entityInteract.entity;
|
||||
switch(e->type) {
|
||||
case ENTITY_TYPE_SIGN:
|
||||
if(textboxIsOpen()) return CONVERSATION_CONTINUE;
|
||||
if(i == SIGN_TEXT_COUNT_MAX-1) return CONVERSATION_DONE;
|
||||
if(e->sign.texts[i+1][0] == '\0') return CONVERSATION_DONE;
|
||||
return CONVERSATION_NEXT;
|
||||
|
||||
default:
|
||||
switch(i) {
|
||||
case 0:
|
||||
return textboxIsOpen() ? CONVERSATION_CONTINUE : CONVERSATION_DONE;
|
||||
|
||||
default:
|
||||
return CONVERSATION_INVALID;
|
||||
}
|
||||
}
|
||||
}
|
31
src/dawn/rpg/conversation/conversationinteractentity.h
Normal file
31
src/dawn/rpg/conversation/conversationinteractentity.h
Normal 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 "conversation.h"
|
||||
|
||||
/**
|
||||
* Initializes the conversation object for an entity interaction.
|
||||
*
|
||||
* @param convo The conversation object to initialize.
|
||||
* @param i The index to initialize the conversation at.
|
||||
* @return The next index to run.
|
||||
*/
|
||||
uint16_t conversationInteractEntityInit(
|
||||
conversation_t *convo, const uint16_t i
|
||||
);
|
||||
|
||||
/**
|
||||
* Updates the conversation object for an entity interaction.
|
||||
*
|
||||
* @param convo The conversation object to update.
|
||||
* @param i The index to update the conversation at.
|
||||
* @return The next index to run.
|
||||
*/
|
||||
uint16_t conversationInteractEntityUpdate(
|
||||
conversation_t *convo, const uint16_t i
|
||||
);
|
53
src/dawn/rpg/conversation/conversationinteracttile.c
Normal file
53
src/dawn/rpg/conversation/conversationinteracttile.c
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "conversationinteractentity.h"
|
||||
#include "assert/assert.h"
|
||||
#include "rpg/entity/entity.h"
|
||||
#include "locale/language.h"
|
||||
#include "ui/textbox.h"
|
||||
|
||||
uint16_t conversationInteractTileInit(
|
||||
conversation_t *convo,
|
||||
const uint16_t i
|
||||
) {
|
||||
assertNotNull(convo, "Conversation is NULL!");
|
||||
|
||||
switch(convo->data.tileInteract.tile) {
|
||||
case TILE_WATER:
|
||||
textboxSetText(NULL, "tiles.water.interact");
|
||||
break;
|
||||
|
||||
case TILE_LAMP:
|
||||
textboxSetText(NULL, "tiles.lamp.interact");
|
||||
break;
|
||||
|
||||
case TILE_RAIL_SLEEPER:
|
||||
case TILE_RAIL_TRACK:
|
||||
textboxSetText(NULL, "tiles.rail.interact");
|
||||
break;
|
||||
|
||||
default:
|
||||
assertUnreachable("Invalid tile interaction!");
|
||||
return CONVERSATION_INVALID;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t conversationInteractTileUpdate(
|
||||
conversation_t *convo,
|
||||
const uint16_t i
|
||||
) {
|
||||
switch(i) {
|
||||
case 0:
|
||||
return textboxIsOpen() ? CONVERSATION_CONTINUE : CONVERSATION_DONE;
|
||||
|
||||
default:
|
||||
return CONVERSATION_INVALID;
|
||||
}
|
||||
}
|
31
src/dawn/rpg/conversation/conversationinteracttile.h
Normal file
31
src/dawn/rpg/conversation/conversationinteracttile.h
Normal 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 "conversation.h"
|
||||
|
||||
/**
|
||||
* Initializes the conversation object for a tile interaction.
|
||||
*
|
||||
* @param convo The conversation object to initialize.
|
||||
* @param i The index to initialize the conversation at.
|
||||
* @return The next index to run.
|
||||
*/
|
||||
uint16_t conversationInteractTileInit(
|
||||
conversation_t *convo, const uint16_t i
|
||||
);
|
||||
|
||||
/**
|
||||
* Updates the conversation object for a tile interaction.
|
||||
*
|
||||
* @param convo The conversation object to update.
|
||||
* @param i The index to update the conversation at.
|
||||
* @return The next index to run.
|
||||
*/
|
||||
uint16_t conversationInteractTileUpdate(
|
||||
conversation_t *convo, const uint16_t i
|
||||
);
|
18
src/dawn/rpg/entity/CMakeLists.txt
Normal file
18
src/dawn/rpg/entity/CMakeLists.txt
Normal 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
|
||||
entity.c
|
||||
entitydirection.c
|
||||
player.c
|
||||
sign.c
|
||||
interact.c
|
||||
npc.c
|
||||
door.c
|
||||
)
|
27
src/dawn/rpg/entity/door.c
Normal file
27
src/dawn/rpg/entity/door.c
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "door.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
void doorInit(door_t *door) {
|
||||
assertNotNull(door, "Door cannot be NULL.");
|
||||
}
|
||||
|
||||
void doorDestinationSet(
|
||||
door_t *door,
|
||||
const int32_t x,
|
||||
const int32_t y,
|
||||
const entitydirection_t direction,
|
||||
const maplist_t map
|
||||
) {
|
||||
assertNotNull(door, "Door cannot be NULL.");
|
||||
door->x = x;
|
||||
door->y = y;
|
||||
door->direction = direction;
|
||||
door->map = map;
|
||||
}
|
41
src/dawn/rpg/entity/door.h
Normal file
41
src/dawn/rpg/entity/door.h
Normal 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 "entitydirection.h"
|
||||
#include "rpg/world/maplist.h"
|
||||
|
||||
typedef struct {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
entitydirection_t direction;
|
||||
maplist_t map;
|
||||
} door_t;
|
||||
|
||||
/**
|
||||
* Initializes a door.
|
||||
*
|
||||
* @param door Door to initialize.
|
||||
*/
|
||||
void doorInit(door_t *door);
|
||||
|
||||
/**
|
||||
* Sets the destination of a door.
|
||||
*
|
||||
* @param door Door to set the destination of.
|
||||
* @param x X coordinate of the destination.
|
||||
* @param y Y coordinate of the destination.
|
||||
* @param direction Direction the player should face when they arrive.
|
||||
* @param map Map the player should arrive in.
|
||||
*/
|
||||
void doorDestinationSet(
|
||||
door_t *door,
|
||||
const int32_t x,
|
||||
const int32_t y,
|
||||
const entitydirection_t direction,
|
||||
const maplist_t map
|
||||
);
|
133
src/dawn/rpg/entity/entity.c
Normal file
133
src/dawn/rpg/entity/entity.c
Normal file
@ -0,0 +1,133 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "entity.h"
|
||||
#include "entitydirection.h"
|
||||
#include "rpg/world/map.h"
|
||||
#include "assert/assert.h"
|
||||
#include "game/time.h"
|
||||
#include "ui/textbox.h"
|
||||
#include "util/memory.h"
|
||||
|
||||
void entityInit(
|
||||
entity_t *entity,
|
||||
const entitytype_t type,
|
||||
map_t *map
|
||||
) {
|
||||
assertNotNull(entity, "Entity cannot be NULL.");
|
||||
assertNotNull(map, "Map cannot be NULL.");
|
||||
|
||||
memorySet(entity, 0, sizeof(entity_t));
|
||||
entity->type = type;
|
||||
entity->map = map;
|
||||
|
||||
switch(type) {
|
||||
case ENTITY_TYPE_NULL:
|
||||
assertUnreachable("Should not be initializing a NULL entity.");
|
||||
break;
|
||||
|
||||
case ENTITY_TYPE_NPC:
|
||||
npcInit(&entity->npc);
|
||||
break;
|
||||
|
||||
case ENTITY_TYPE_SIGN:
|
||||
signInit(&entity->sign);
|
||||
break;
|
||||
|
||||
case ENTITY_TYPE_DOOR:
|
||||
doorInit(&entity->door);
|
||||
break;
|
||||
|
||||
case ENTITY_TYPE_PLAYER:
|
||||
playerInit(entity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void entityUpdate(entity_t *entity) {
|
||||
assertNotNull(entity, "Entity cannot be NULL.");
|
||||
|
||||
switch(entity->state) {
|
||||
case ENTITY_STATE_IDLE:
|
||||
switch(entity->type) {
|
||||
case ENTITY_TYPE_NULL:
|
||||
assertUnreachable("Should not be updating a NULL entity.");
|
||||
break;
|
||||
|
||||
case ENTITY_TYPE_PLAYER:
|
||||
playerUpdate(entity);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ENTITY_STATE_WALKING:
|
||||
entity->walk.time -= TIME.delta;
|
||||
if(entity->walk.time > 0.0f) return;
|
||||
entity->state = ENTITY_STATE_IDLE;
|
||||
break;
|
||||
|
||||
case ENTITY_STATE_TALKING:
|
||||
if(!textboxIsOpen()) entity->state = ENTITY_STATE_IDLE;
|
||||
break;
|
||||
|
||||
default:
|
||||
assertUnreachable("Unknown entity state.");
|
||||
}
|
||||
}
|
||||
|
||||
void entityWalk(entity_t *entity, const entitydirection_t dir) {
|
||||
assertNotNull(entity, "Entity cannot be NULL.");
|
||||
|
||||
uint16_t newX = entity->x, newY = entity->y;
|
||||
entityDirectionOffsetAdd(dir, &newX, &newY);
|
||||
|
||||
// Update direction, this happens regardless of if the player can move
|
||||
entity->direction = dir;
|
||||
|
||||
// Walking OOB?
|
||||
if(newX >= entity->map->width || newY >= entity->map->height) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get tile at position
|
||||
tile_t tileAtPosition = mapTileGetByPosition(
|
||||
entity->map,
|
||||
newX,
|
||||
newY,
|
||||
entity->layer
|
||||
);
|
||||
if(tileIsSolid(tileAtPosition)) return;
|
||||
|
||||
// Get entity at position
|
||||
entity_t *entityAtPosition = mapEntityGetByPosition(
|
||||
entity->map,
|
||||
newX,
|
||||
newY
|
||||
);
|
||||
if(entityAtPosition != NULL) return;
|
||||
|
||||
// Commit to move
|
||||
entity->x = newX;
|
||||
entity->y = newY;
|
||||
|
||||
entity->state = ENTITY_STATE_WALKING;
|
||||
entity->walk.time = 0.1f;
|
||||
|
||||
// Check for triggers
|
||||
trigger_t *trigger = mapTriggerGetByPosition(
|
||||
entity->map,
|
||||
entity->x,
|
||||
entity->y
|
||||
);
|
||||
if(trigger != NULL) triggerOnStep(trigger, entity);
|
||||
}
|
||||
|
||||
void entityPositionSet(entity_t *entity, const uint16_t x, const uint16_t y) {
|
||||
assertNotNull(entity, "Entity cannot be NULL.");
|
||||
entity->x = x;
|
||||
entity->y = y;
|
||||
}
|
92
src/dawn/rpg/entity/entity.h
Normal file
92
src/dawn/rpg/entity/entity.h
Normal file
@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "player.h"
|
||||
#include "sign.h"
|
||||
#include "entitydirection.h"
|
||||
#include "npc.h"
|
||||
#include "door.h"
|
||||
|
||||
typedef struct _map_t map_t;
|
||||
|
||||
typedef enum {
|
||||
ENTITY_TYPE_NULL = 0,
|
||||
ENTITY_TYPE_PLAYER = 1,
|
||||
ENTITY_TYPE_NPC = 2,
|
||||
ENTITY_TYPE_SIGN = 3,
|
||||
ENTITY_TYPE_DOOR = 4
|
||||
} entitytype_t;
|
||||
|
||||
typedef enum {
|
||||
ENTITY_STATE_IDLE = 0,
|
||||
ENTITY_STATE_WALKING = 1,
|
||||
ENTITY_STATE_TALKING = 2
|
||||
} entitystate_t;
|
||||
|
||||
typedef struct {
|
||||
float_t time;
|
||||
} entitywalkstate_t;
|
||||
|
||||
typedef struct _entity_t {
|
||||
map_t *map;
|
||||
entitytype_t type;
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
uint8_t layer;
|
||||
entitydirection_t direction;
|
||||
entitystate_t state;
|
||||
|
||||
// State
|
||||
union {
|
||||
entitywalkstate_t walk;
|
||||
};
|
||||
|
||||
// Type data
|
||||
union {
|
||||
player_t player;
|
||||
npc_t npc;
|
||||
sign_t sign;
|
||||
door_t door;
|
||||
};
|
||||
} entity_t;
|
||||
|
||||
/**
|
||||
* Initializes an entity.
|
||||
*
|
||||
* @param entity Entity to initialize.
|
||||
* @param type Type of entity to initialize.
|
||||
*/
|
||||
void entityInit(
|
||||
entity_t *entity,
|
||||
const entitytype_t type,
|
||||
map_t *map
|
||||
);
|
||||
|
||||
/**
|
||||
* Updates an entity.
|
||||
*
|
||||
* @param entity Entity to update.
|
||||
*/
|
||||
void entityUpdate(entity_t *entity);
|
||||
|
||||
/**
|
||||
* Moves an entity.
|
||||
*
|
||||
* @param entity Entity to move.
|
||||
* @param dir Direction to move.
|
||||
*/
|
||||
void entityWalk(entity_t *entity, const entitydirection_t dir);
|
||||
|
||||
/**
|
||||
* Sets the position of an entity. Performs an immediate move.
|
||||
*
|
||||
* @param entity Entity to set position of.
|
||||
* @param x X position to set.
|
||||
* @param y Y position to set.
|
||||
*/
|
||||
void entityPositionSet(entity_t *entity, const uint16_t x, const uint16_t y);
|
70
src/dawn/rpg/entity/entitydirection.c
Normal file
70
src/dawn/rpg/entity/entitydirection.c
Normal file
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "entitydirection.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
void entityDirectionOffsetGet(
|
||||
const entitydirection_t dir,
|
||||
uint16_t *x,
|
||||
uint16_t *y
|
||||
) {
|
||||
switch(dir) {
|
||||
case ENTITY_DIRECTION_SOUTH:
|
||||
*x = 0;
|
||||
*y = 1;
|
||||
break;
|
||||
case ENTITY_DIRECTION_NORTH:
|
||||
*x = 0;
|
||||
*y = -1;
|
||||
break;
|
||||
case ENTITY_DIRECTION_WEST:
|
||||
*x = -1;
|
||||
*y = 0;
|
||||
break;
|
||||
case ENTITY_DIRECTION_EAST:
|
||||
*x = 1;
|
||||
*y = 0;
|
||||
break;
|
||||
default:
|
||||
assertUnreachable("Invalid entity direction.");
|
||||
}
|
||||
}
|
||||
|
||||
void entityDirectionOffsetAdd(
|
||||
const entitydirection_t dir,
|
||||
uint16_t *x,
|
||||
uint16_t *y
|
||||
) {
|
||||
switch(dir) {
|
||||
case ENTITY_DIRECTION_SOUTH:
|
||||
*y += 1;
|
||||
break;
|
||||
case ENTITY_DIRECTION_NORTH:
|
||||
*y -= 1;
|
||||
break;
|
||||
case ENTITY_DIRECTION_WEST:
|
||||
*x -= 1;
|
||||
break;
|
||||
case ENTITY_DIRECTION_EAST:
|
||||
*x += 1;
|
||||
break;
|
||||
default:
|
||||
assertUnreachable("Invalid entity direction.");
|
||||
}
|
||||
}
|
||||
|
||||
entitydirection_t entityDirectionLookAt(
|
||||
const uint16_t srcX, const uint16_t srcY,
|
||||
const uint16_t trgX, const uint16_t trgY
|
||||
) {
|
||||
if(srcX < trgX) return ENTITY_DIRECTION_EAST;
|
||||
if(srcX > trgX) return ENTITY_DIRECTION_WEST;
|
||||
if(srcY < trgY) return ENTITY_DIRECTION_SOUTH;
|
||||
if(srcY > trgY) return ENTITY_DIRECTION_NORTH;
|
||||
return ENTITY_DIRECTION_SOUTH;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user