Refact
This commit is contained in:
@@ -14,6 +14,11 @@ if(NOT DEFINED DUSK_TARGET_SYSTEM)
|
|||||||
# set(DUSK_TARGET_SYSTEM "psp")
|
# set(DUSK_TARGET_SYSTEM "psp")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(NOT DEFINED DUSK_TARGET_GAME)
|
||||||
|
set(DUSK_TARGET_GAME "minesweeper")
|
||||||
|
# set(DUSK_TARGET_GAME "rpg")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Prep cache
|
# Prep cache
|
||||||
set(DUSK_CACHE_TARGET "dusk-target")
|
set(DUSK_CACHE_TARGET "dusk-target")
|
||||||
|
|
||||||
@@ -98,7 +103,7 @@ target_include_directories(${DUSK_TARGET_NAME} PUBLIC
|
|||||||
add_custom_target(DUSK_ASSETS_BUILT ALL
|
add_custom_target(DUSK_ASSETS_BUILT ALL
|
||||||
COMMAND
|
COMMAND
|
||||||
${Python3_EXECUTABLE} ${DUSK_TOOLS_DIR}/assetstool/main.py
|
${Python3_EXECUTABLE} ${DUSK_TOOLS_DIR}/assetstool/main.py
|
||||||
--assets ${DUSK_ASSETS_DIR}
|
--assets ${DUSK_GAME_ASSETS_DIR}
|
||||||
--build-type wad
|
--build-type wad
|
||||||
--output-assets ${DUSK_BUILT_ASSETS_DIR}
|
--output-assets ${DUSK_BUILT_ASSETS_DIR}
|
||||||
--output-file ${DUSK_BUILD_DIR}/dusk.dsk
|
--output-file ${DUSK_BUILD_DIR}/dusk.dsk
|
||||||
|
@@ -1,42 +0,0 @@
|
|||||||
# Try to find SDL2 in common locations
|
|
||||||
find_path(SDL2_INCLUDE_DIR SDL.h
|
|
||||||
PATHS
|
|
||||||
/usr/include/SDL2
|
|
||||||
/usr/local/include/SDL2
|
|
||||||
$ENV{SDL2_DIR}/include
|
|
||||||
PATH_SUFFIXES SDL2
|
|
||||||
)
|
|
||||||
|
|
||||||
find_library(SDL2_LIBRARY
|
|
||||||
NAMES SDL2
|
|
||||||
PATHS
|
|
||||||
/usr/lib
|
|
||||||
/usr/local/lib
|
|
||||||
$ENV{SDL2_DIR}/lib
|
|
||||||
)
|
|
||||||
|
|
||||||
if(SDL2_INCLUDE_DIR AND SDL2_LIBRARY)
|
|
||||||
set(SDL2_FOUND TRUE)
|
|
||||||
set(SDL2_LIBRARIES ${SDL2_LIBRARY})
|
|
||||||
set(SDL2_INCLUDE_DIRS ${SDL2_INCLUDE_DIR})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# If not found, use FetchContent to acquire SDL2
|
|
||||||
if(NOT SDL2_FOUND)
|
|
||||||
include(FetchContent)
|
|
||||||
FetchContent_Declare(
|
|
||||||
SDL2
|
|
||||||
GIT_REPOSITORY https://github.com/libsdl-org/SDL.git
|
|
||||||
GIT_TAG release-2.28.5 # Change to desired version
|
|
||||||
)
|
|
||||||
FetchContent_MakeAvailable(SDL2)
|
|
||||||
set(SDL2_FOUND TRUE)
|
|
||||||
set(SDL2_LIBRARIES SDL2)
|
|
||||||
set(SDL2_INCLUDE_DIRS ${sdl2_SOURCE_DIR}/include)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Provide variables for downstream usage
|
|
||||||
if(SDL2_FOUND)
|
|
||||||
set(SDL2_INCLUDE_DIR ${SDL2_INCLUDE_DIRS} CACHE PATH "SDL2 include directory")
|
|
||||||
set(SDL2_LIBRARY ${SDL2_LIBRARIES} CACHE FILEPATH "SDL2 library")
|
|
||||||
endif()
|
|
@@ -1,86 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
set(LIBZIP_ALLOW_FETCH ON CACHE BOOL "Allow fetching libzip if not found")
|
|
||||||
set(LIBZIP_FETCH_GIT_REPO "https://github.com/nih-at/libzip.git" CACHE STRING "libzip Git repository")
|
|
||||||
set(LIBZIP_FETCH_GIT_TAG "v1.11.1" CACHE STRING "libzip Git tag to fetch")
|
|
||||||
set(LIBZIP_MIN_VERSION "" CACHE STRING "Required minimum libzip version (optional)")
|
|
||||||
|
|
||||||
unset(LIBZIP_FOUND CACHE)
|
|
||||||
unset(LIBZIP_TARGET CACHE)
|
|
||||||
unset(LIBZIP_INCLUDE_DIRS CACHE)
|
|
||||||
unset(LIBZIP_LIBRARIES CACHE)
|
|
||||||
|
|
||||||
set(_find_args CONFIG QUIET)
|
|
||||||
if(LIBZIP_MIN_VERSION)
|
|
||||||
list(PREPEND _find_args ${LIBZIP_MIN_VERSION})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_package(libzip ${_find_args})
|
|
||||||
|
|
||||||
if(TARGET libzip::zip)
|
|
||||||
set(LIBZIP_FOUND ON)
|
|
||||||
set(LIBZIP_TARGET libzip::zip)
|
|
||||||
elseif(TARGET zip) # some builds expose an un-namespaced "zip"
|
|
||||||
add_library(libzip::zip ALIAS zip)
|
|
||||||
set(LIBZIP_FOUND ON)
|
|
||||||
set(LIBZIP_TARGET libzip::zip)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT LIBZIP_FOUND)
|
|
||||||
find_package(PkgConfig QUIET)
|
|
||||||
if(PkgConfig_FOUND)
|
|
||||||
pkg_check_modules(LIBZIP_PC QUIET libzip)
|
|
||||||
if(LIBZIP_PC_FOUND)
|
|
||||||
# Create a consistent imported target matching libzip::zip
|
|
||||||
add_library(libzip::zip INTERFACE IMPORTED)
|
|
||||||
set_target_properties(libzip::zip PROPERTIES
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${LIBZIP_PC_INCLUDE_DIRS}"
|
|
||||||
INTERFACE_LINK_LIBRARIES "${LIBZIP_PC_LINK_LIBRARIES}"
|
|
||||||
)
|
|
||||||
set(LIBZIP_FOUND ON)
|
|
||||||
set(LIBZIP_TARGET libzip::zip)
|
|
||||||
set(LIBZIP_INCLUDE_DIRS "${LIBZIP_PC_INCLUDE_DIRS}")
|
|
||||||
set(LIBZIP_LIBRARIES "${LIBZIP_PC_LINK_LIBRARIES}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ---- 3) FetchContent as a last resort ---------------------------------------
|
|
||||||
if(NOT LIBZIP_FOUND AND LIBZIP_ALLOW_FETCH)
|
|
||||||
include(FetchContent)
|
|
||||||
|
|
||||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
|
||||||
set(LIBZIP_DO_INSTALL OFF CACHE BOOL "" FORCE)
|
|
||||||
set(ENABLE_BZIP2 OFF CACHE BOOL "" FORCE)
|
|
||||||
set(ENABLE_LZMA OFF CACHE BOOL "" FORCE)
|
|
||||||
set(ENABLE_ZSTD OFF CACHE BOOL "" FORCE)
|
|
||||||
|
|
||||||
FetchContent_Declare(
|
|
||||||
libzip_fetch
|
|
||||||
GIT_REPOSITORY "${LIBZIP_FETCH_GIT_REPO}"
|
|
||||||
GIT_TAG "${LIBZIP_FETCH_GIT_TAG}"
|
|
||||||
)
|
|
||||||
FetchContent_MakeAvailable(libzip_fetch)
|
|
||||||
|
|
||||||
if(NOT TARGET libzip::zip)
|
|
||||||
if(TARGET zip)
|
|
||||||
add_library(libzip::zip ALIAS zip)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(TARGET libzip::zip)
|
|
||||||
set(LIBZIP_FOUND ON)
|
|
||||||
set(LIBZIP_TARGET libzip::zip)
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "libzip fetch succeeded but target 'libzip::zip' (or 'zip') was not created.")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Final guard
|
|
||||||
if(NOT LIBZIP_FOUND)
|
|
||||||
message(FATAL_ERROR "libzip not found and fetching is disabled or failed. "
|
|
||||||
"Set LIBZIP_ALLOW_FETCH=ON or install libzip development files.")
|
|
||||||
endif()
|
|
@@ -1,19 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
asset.c
|
|
||||||
assetpalette.c
|
|
||||||
assettileset.c
|
|
||||||
assetpaletteimage.c
|
|
||||||
)
|
|
||||||
|
|
||||||
# Compile definitions
|
|
||||||
target_compile_definitions(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
ASSET_TYPE=wad
|
|
||||||
)
|
|
@@ -1,156 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "asset.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "console/console.h"
|
|
||||||
#include "util/string.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
#define ASSET_ASSET_FILE "dusk.dsk"
|
|
||||||
|
|
||||||
asset_t ASSET;
|
|
||||||
|
|
||||||
errorret_t assetInit(void) {
|
|
||||||
memoryZero(&ASSET, sizeof(asset_t));
|
|
||||||
|
|
||||||
// Open zip file
|
|
||||||
char_t searchPath[FILENAME_MAX];
|
|
||||||
consolevar_t *var = consoleVarGet("sys_path");
|
|
||||||
const char_t *sysPath = var ? var->value : ".";
|
|
||||||
for(int32_t i = 0; i < ASSET_SEARCH_PATHS_COUNT; i++) {
|
|
||||||
sprintf(
|
|
||||||
searchPath,
|
|
||||||
ASSET_SEARCH_PATHS[i],
|
|
||||||
sysPath,
|
|
||||||
ASSET_ASSET_FILE
|
|
||||||
);
|
|
||||||
|
|
||||||
// Try open
|
|
||||||
ASSET.zip = zip_open(searchPath, ZIP_RDONLY, NULL);
|
|
||||||
if(ASSET.zip == NULL) continue;
|
|
||||||
consolePrint("Opened asset file: %s", searchPath);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Did we open the asset?
|
|
||||||
if(ASSET.zip == NULL) errorThrow("Failed to open asset file.");
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
void assetLoad(
|
|
||||||
const char_t *filename,
|
|
||||||
void (*callback)(void* data),
|
|
||||||
void *data
|
|
||||||
) {
|
|
||||||
assertNotNull(filename, "Filename cannot be NULL.");
|
|
||||||
assertTrue(strlen(filename) < ASSET_FILENAME_MAX, "Filename too long.");
|
|
||||||
assertTrue(strlen(filename) > 0, "Filename cannot be empty.");
|
|
||||||
|
|
||||||
assertTrue(
|
|
||||||
ASSET.state != ASSET_STATE_LOADING,
|
|
||||||
"Asset system is already loading an asset."
|
|
||||||
);
|
|
||||||
|
|
||||||
// Determine the asset map type we are expecting based on the extension.
|
|
||||||
const assetmap_t *expected = &ASSET_MAP[0];
|
|
||||||
do {
|
|
||||||
if(stringEndsWith(filename, expected->extension)) break;
|
|
||||||
expected++;
|
|
||||||
} while(expected->extension);
|
|
||||||
assertNotNull(expected->extension, "Unknown asset type.");
|
|
||||||
|
|
||||||
// Pass off to the thread to begin loading.
|
|
||||||
ASSET.callback = callback;
|
|
||||||
ASSET.callbackData = data;
|
|
||||||
ASSET.errorState = ERROR_STATE_INIT;
|
|
||||||
ASSET.state = ASSET_STATE_LOADING;
|
|
||||||
stringCopy(ASSET.filename, filename, ASSET_FILENAME_MAX);
|
|
||||||
memoryZero(&ASSET.data, sizeof(ASSET.data));
|
|
||||||
|
|
||||||
// For the sake of testing I'm going to do blocking load, fun stuff.
|
|
||||||
zip_file_t *file = zip_fopen(ASSET.zip, filename, 0);
|
|
||||||
if(file == NULL) {
|
|
||||||
ASSET.state = ASSET_STATE_ERROR;
|
|
||||||
ASSET.error = errorCreate(
|
|
||||||
&ASSET.errorState,
|
|
||||||
"Failed to open asset: %s",
|
|
||||||
filename
|
|
||||||
);
|
|
||||||
if(ASSET.callback) ASSET.callback(ASSET.callbackData);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Loading asset: %s\n", filename);
|
|
||||||
|
|
||||||
// Determine length of the file (uncompressed)
|
|
||||||
struct zip_stat st;
|
|
||||||
if(zip_stat(ASSET.zip, filename, 0, &st) != 0) {
|
|
||||||
zip_fclose(file);
|
|
||||||
ASSET.state = ASSET_STATE_ERROR;
|
|
||||||
ASSET.error = errorCreate(
|
|
||||||
&ASSET.errorState,
|
|
||||||
"Failed to stat asset: %s",
|
|
||||||
filename
|
|
||||||
);
|
|
||||||
if(ASSET.callback) ASSET.callback(ASSET.callbackData);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(st.size > sizeof(ASSET.data.palette)) {
|
|
||||||
zip_fclose(file);
|
|
||||||
ASSET.state = ASSET_STATE_ERROR;
|
|
||||||
ASSET.error = errorCreate(
|
|
||||||
&ASSET.errorState,
|
|
||||||
"Asset size mismatch: %s (got %d, expected %d)",
|
|
||||||
filename,
|
|
||||||
(int)st.size,
|
|
||||||
(int)sizeof(ASSET.data.palette)
|
|
||||||
);
|
|
||||||
if(ASSET.callback) ASSET.callback(ASSET.callbackData);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read entire file into the asset data.
|
|
||||||
zip_fread(file, &ASSET.data, st.size);
|
|
||||||
zip_fclose(file);
|
|
||||||
ASSET.state = ASSET_STATE_LOADED;
|
|
||||||
|
|
||||||
if(memoryCompare(
|
|
||||||
ASSET.data.header, expected->header, ASSET_HEADER_LENGTH
|
|
||||||
) != 0) {
|
|
||||||
ASSET.state = ASSET_STATE_ERROR;
|
|
||||||
ASSET.error = errorCreate(
|
|
||||||
&ASSET.errorState,
|
|
||||||
"Asset header mismatch: %s (got %c%c%c, expected %c%c%c)",
|
|
||||||
filename,
|
|
||||||
ASSET.data.header[0],
|
|
||||||
ASSET.data.header[1],
|
|
||||||
ASSET.data.header[2],
|
|
||||||
expected->header[0],
|
|
||||||
expected->header[1],
|
|
||||||
expected->header[2]
|
|
||||||
);
|
|
||||||
if(ASSET.callback) ASSET.callback(ASSET.callbackData);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the asset.
|
|
||||||
assertNotNull(expected->parser, "Asset parser cannot be NULL.");
|
|
||||||
expected->parser();
|
|
||||||
|
|
||||||
printf("Loaded asset: %s\n", filename);
|
|
||||||
if(ASSET.callback) ASSET.callback(ASSET.callbackData);
|
|
||||||
}
|
|
||||||
|
|
||||||
void assetDispose(void) {
|
|
||||||
if(ASSET.zip != NULL) {
|
|
||||||
zip_close(ASSET.zip);
|
|
||||||
ASSET.zip = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,118 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
#include "assetpalette.h"
|
|
||||||
#include "assettileset.h"
|
|
||||||
#include "assetpalleteimage.h"
|
|
||||||
#include "error/error.h"
|
|
||||||
#include <zip.h>
|
|
||||||
#include "display/texture/texture.h"
|
|
||||||
|
|
||||||
#define ASSET_COUNT_MAX 128
|
|
||||||
#define ASSET_FILENAME_MAX 128
|
|
||||||
#define ASSET_HEADER_LENGTH 3
|
|
||||||
|
|
||||||
#if ASSET_TYPE == wad
|
|
||||||
#else
|
|
||||||
#error "Unsupported ASSET_TYPE"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
|
||||||
typedef struct {
|
|
||||||
char_t header[ASSET_HEADER_LENGTH];
|
|
||||||
union {
|
|
||||||
assetpalette_t palette;
|
|
||||||
assettileset_t tileset;
|
|
||||||
assetpaletteimage_t paletteImage;
|
|
||||||
};
|
|
||||||
} assetdata_t;
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
union {
|
|
||||||
texture_t palette;
|
|
||||||
assettileset_t *tileset;
|
|
||||||
texture_t paletteImage;
|
|
||||||
};
|
|
||||||
} assetloaded_t;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
ASSET_STATE_NONE,
|
|
||||||
ASSET_STATE_LOADING,
|
|
||||||
ASSET_STATE_LOADED,
|
|
||||||
ASSET_STATE_ERROR
|
|
||||||
} assetstate_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int32_t nothing;
|
|
||||||
zip_t *zip;
|
|
||||||
// thread_t thread;
|
|
||||||
|
|
||||||
errorstate_t errorState;
|
|
||||||
errorret_t error;
|
|
||||||
assetstate_t state;
|
|
||||||
char_t filename[ASSET_FILENAME_MAX];
|
|
||||||
assetdata_t data;
|
|
||||||
assetloaded_t loaded;
|
|
||||||
|
|
||||||
void (*callback)(void* data);
|
|
||||||
void *callbackData;
|
|
||||||
} asset_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const char_t *header;
|
|
||||||
const char_t *extension;
|
|
||||||
void (*parser)();
|
|
||||||
} assetmap_t;
|
|
||||||
|
|
||||||
static const char_t ASSET_SEARCH_PATHS[][FILENAME_MAX] = {
|
|
||||||
"%s/%s",
|
|
||||||
"./%s",
|
|
||||||
"../%s",
|
|
||||||
"../../%s",
|
|
||||||
"data/%s",
|
|
||||||
"../data/%s",
|
|
||||||
};
|
|
||||||
|
|
||||||
static const assetmap_t ASSET_MAP[] = {
|
|
||||||
{ "DPF", ".dpf", assetParsePalette },
|
|
||||||
{ "DPT", ".dpt", assetParseTileset },
|
|
||||||
{ "DPI", ".dpi", assetParsePaletteImage },
|
|
||||||
{ NULL, NULL, NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
#define ASSET_SEARCH_PATHS_COUNT (\
|
|
||||||
sizeof(ASSET_SEARCH_PATHS) / FILENAME_MAX\
|
|
||||||
)
|
|
||||||
|
|
||||||
extern asset_t ASSET;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the asset system.
|
|
||||||
*/
|
|
||||||
errorret_t assetInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads an asset by filename, blocking.
|
|
||||||
*
|
|
||||||
* @param filename The filename of the asset to get.
|
|
||||||
* @param callback The callback to call when the asset is loaded.
|
|
||||||
* @param data The data for the callback function.
|
|
||||||
* @return The asset.
|
|
||||||
*/
|
|
||||||
void assetLoad(
|
|
||||||
const char_t *filename,
|
|
||||||
void (*callback)(void* data),
|
|
||||||
void *data
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes/cleans up the asset system.
|
|
||||||
*/
|
|
||||||
void assetDispose(void);
|
|
@@ -1,18 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "asset.h"
|
|
||||||
|
|
||||||
void assetParsePalette() {
|
|
||||||
textureInit(
|
|
||||||
&ASSET.loaded.palette,
|
|
||||||
ASSET.data.palette.colorCount,
|
|
||||||
1,
|
|
||||||
TEXTURE_FORMAT_RGBA,
|
|
||||||
ASSET.data.palette.colors
|
|
||||||
);
|
|
||||||
}
|
|
@@ -1,22 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
#include "display/color.h"
|
|
||||||
|
|
||||||
#define ASSET_PALETTE_COLOR_COUNT_MAX 256
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int32_t colorCount;
|
|
||||||
color4b_t colors[ASSET_PALETTE_COLOR_COUNT_MAX];
|
|
||||||
} assetpalette_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse the palette from the loaded raw data.
|
|
||||||
*/
|
|
||||||
void assetParsePalette(void);
|
|
@@ -1,12 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "asset.h"
|
|
||||||
|
|
||||||
void assetParsePaletteImage(void) {
|
|
||||||
printf("nothin doin\n");
|
|
||||||
}
|
|
@@ -1,23 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
#define ASSET_PALETTE_IMAGE_PIXELS_MAX (256*256)
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int32_t width;
|
|
||||||
int32_t height;
|
|
||||||
uint8_t paletteIndex;
|
|
||||||
uint8_t paletteIndexes[ASSET_PALETTE_IMAGE_PIXELS_MAX];
|
|
||||||
} assetpaletteimage_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse the palette image from the loaded raw data.
|
|
||||||
*/
|
|
||||||
void assetParsePaletteImage(void);
|
|
@@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef struct assetimage_s assetimage_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int32_t tileWidth;
|
|
||||||
int32_t tileHeight;
|
|
||||||
int32_t tileCount;
|
|
||||||
int32_t columns;
|
|
||||||
char_t tilesetName[256];
|
|
||||||
} assettileset_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse the tileset from the loaded raw data.
|
|
||||||
*/
|
|
||||||
void assetParseTileset(void);
|
|
@@ -1,37 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Libs
|
|
||||||
target_link_libraries(${DUSK_TARGET_NAME}
|
|
||||||
PUBLIC
|
|
||||||
m
|
|
||||||
)
|
|
||||||
|
|
||||||
# Includes
|
|
||||||
target_include_directories(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
game.c
|
|
||||||
input.c
|
|
||||||
time.c
|
|
||||||
)
|
|
||||||
|
|
||||||
# Subdirs
|
|
||||||
add_subdirectory(assert)
|
|
||||||
add_subdirectory(console)
|
|
||||||
add_subdirectory(display)
|
|
||||||
add_subdirectory(error)
|
|
||||||
add_subdirectory(entity)
|
|
||||||
add_subdirectory(event)
|
|
||||||
add_subdirectory(item)
|
|
||||||
add_subdirectory(locale)
|
|
||||||
add_subdirectory(ui)
|
|
||||||
add_subdirectory(util)
|
|
||||||
add_subdirectory(world)
|
|
@@ -1,85 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2023 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "assert.h"
|
|
||||||
|
|
||||||
#ifndef ASSERTIONS_FAKED
|
|
||||||
void assertTrueImpl(
|
|
||||||
const char *file,
|
|
||||||
const int32_t line,
|
|
||||||
const bool x,
|
|
||||||
const char *message
|
|
||||||
) {
|
|
||||||
if(x != true) {
|
|
||||||
fprintf(
|
|
||||||
stderr,
|
|
||||||
"Assertion Failed in %s:%i\n\n%s\n",
|
|
||||||
file,
|
|
||||||
line,
|
|
||||||
message
|
|
||||||
);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void assertFalseImpl(
|
|
||||||
const char *file,
|
|
||||||
const int32_t line,
|
|
||||||
bool x,
|
|
||||||
const char *message
|
|
||||||
) {
|
|
||||||
assertTrueImpl(file, line, !x, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void assertUnreachableImpl(
|
|
||||||
const char *file,
|
|
||||||
const int32_t line,
|
|
||||||
const char *message
|
|
||||||
) {
|
|
||||||
assertTrueImpl(file, line, false, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void assertNotNullImpl(
|
|
||||||
const char *file,
|
|
||||||
const int32_t line,
|
|
||||||
const void *pointer,
|
|
||||||
const char *message
|
|
||||||
) {
|
|
||||||
assertTrueImpl(
|
|
||||||
file,
|
|
||||||
line,
|
|
||||||
pointer != NULL,
|
|
||||||
message
|
|
||||||
);
|
|
||||||
|
|
||||||
// Ensure we can touch it
|
|
||||||
volatile char temp;
|
|
||||||
temp = *((char*)pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void assertNullImpl(
|
|
||||||
const char *file,
|
|
||||||
const int32_t line,
|
|
||||||
const void *pointer,
|
|
||||||
const char *message
|
|
||||||
) {
|
|
||||||
assertTrueImpl(
|
|
||||||
file,
|
|
||||||
line,
|
|
||||||
pointer == NULL,
|
|
||||||
message
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void assertDeprecatedImpl(
|
|
||||||
const char *file,
|
|
||||||
const int32_t line,
|
|
||||||
const char *message
|
|
||||||
) {
|
|
||||||
assertUnreachableImpl(file, line, message);
|
|
||||||
}
|
|
||||||
#endif
|
|
@@ -1,143 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2023 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
#ifndef ASSERTIONS_FAKED
|
|
||||||
/**
|
|
||||||
* Assert a given value to be true.
|
|
||||||
*
|
|
||||||
* @param file File that the assertion is being made from.
|
|
||||||
* @param line Line that the assertion is being made from.
|
|
||||||
* @param x Value to assert as true.
|
|
||||||
* @param message Message to throw against assertion failure.
|
|
||||||
*/
|
|
||||||
void assertTrueImpl(
|
|
||||||
const char *file,
|
|
||||||
const int32_t line,
|
|
||||||
const bool_t x,
|
|
||||||
const char *message
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts a given statement to be false.
|
|
||||||
*
|
|
||||||
* @param file File that the assertion is being made from.
|
|
||||||
* @param line Line that the assertion is being made from.
|
|
||||||
* @param x Value to assert as false.
|
|
||||||
* @param message Message to throw against assertion failure.
|
|
||||||
*/
|
|
||||||
void assertFalseImpl(
|
|
||||||
const char *file,
|
|
||||||
const int32_t line,
|
|
||||||
const bool_t x,
|
|
||||||
const char *message
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that a given line of code is unreachable. Essentially a forced
|
|
||||||
* assertion failure, good for "edge cases"
|
|
||||||
*
|
|
||||||
* @param file File that the assertion is being made from.
|
|
||||||
* @param line Line that the assertion is being made from.
|
|
||||||
* @param message Message to throw against assertion failure.
|
|
||||||
*/
|
|
||||||
void assertUnreachableImpl(
|
|
||||||
const char *file,
|
|
||||||
const int32_t line,
|
|
||||||
const char *message
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assert a given pointer to not point to a null pointer.
|
|
||||||
*
|
|
||||||
* @param file File that the assertion is being made from.
|
|
||||||
* @param line Line that the assertion is being made from.
|
|
||||||
* @param pointer Pointer to assert is not a null pointer.
|
|
||||||
* @param message Message to throw against assertion failure.
|
|
||||||
*/
|
|
||||||
void assertNotNullImpl(
|
|
||||||
const char *file,
|
|
||||||
const int32_t line,
|
|
||||||
const void *pointer,
|
|
||||||
const char *message
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts a given pointer to be a nullptr.
|
|
||||||
*
|
|
||||||
* @param file File that the assertion is being made from.
|
|
||||||
* @param line Line that the assertion is being made from.
|
|
||||||
* @param pointer Pointer to assert is nullptr.
|
|
||||||
* @param message Message to throw against assertion failure.
|
|
||||||
*/
|
|
||||||
void assertNullImpl(
|
|
||||||
const char *file,
|
|
||||||
const int32_t line,
|
|
||||||
const void *pointer,
|
|
||||||
const char *message
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts a function as being deprecated.
|
|
||||||
*
|
|
||||||
* @param file File that the assertion is being made from.
|
|
||||||
* @param line Line that the assertion is being made from.
|
|
||||||
* @param message Message to throw against assertion failure.
|
|
||||||
*/
|
|
||||||
void assertDeprecatedImpl(
|
|
||||||
const char *file,
|
|
||||||
const int32_t line,
|
|
||||||
const char *message
|
|
||||||
);
|
|
||||||
|
|
||||||
void assertMemoryRangeMatchesImpl(
|
|
||||||
const char *file,
|
|
||||||
const int32_t line,
|
|
||||||
const void *start,
|
|
||||||
const void *end,
|
|
||||||
const size_t size,
|
|
||||||
const char *message
|
|
||||||
);
|
|
||||||
|
|
||||||
#define assertTrue(x, message) \
|
|
||||||
assertTrueImpl(__FILE__, __LINE__, x, message)
|
|
||||||
|
|
||||||
#define assertFalse(x, message) \
|
|
||||||
assertFalseImpl(__FILE__, __LINE__, x, message)
|
|
||||||
|
|
||||||
#define assertUnreachable(message) \
|
|
||||||
assertUnreachableImpl(__FILE__, __LINE__, message)
|
|
||||||
|
|
||||||
#define assertNotNull(pointer, message) \
|
|
||||||
assertNotNullImpl(__FILE__, __LINE__, pointer, message)
|
|
||||||
|
|
||||||
#define assertNull(pointer, message) \
|
|
||||||
assertNullImpl(__FILE__, __LINE__, pointer, message)
|
|
||||||
|
|
||||||
#define assertDeprecated(message) \
|
|
||||||
assertDeprecatedImpl(__FILE__, __LINE__, message)
|
|
||||||
|
|
||||||
#define assertStrLenMax(str, len, message) \
|
|
||||||
assertTrue(strlen(str) < len, message)
|
|
||||||
|
|
||||||
#define assertStrLenMin(str, len, message) \
|
|
||||||
assertTrue(strlen(str) >= len, message)
|
|
||||||
|
|
||||||
#else
|
|
||||||
// If assertions are faked, we define the macros to do nothing.
|
|
||||||
#define assertTrue(x, message) ((void)0)
|
|
||||||
#define assertFalse(x, message) ((void)0)
|
|
||||||
#define assertUnreachable(message) ((void)0)
|
|
||||||
#define assertNotNull(pointer, message) ((void)0)
|
|
||||||
#define assertNull(pointer, message) ((void)0)
|
|
||||||
#define assertDeprecated(message) ((void)0)
|
|
||||||
#define assertStrLenMax(str, len, message) ((void)0)
|
|
||||||
#define assertStrLenMin(str, len, message) ((void)0)
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,11 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
renderbase.c
|
|
||||||
scene.c
|
|
||||||
)
|
|
@@ -1,8 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "display/render.h"
|
|
@@ -1,31 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "error/error.h"
|
|
||||||
|
|
||||||
#ifndef RENDER_WIDTH
|
|
||||||
#define RENDER_WIDTH 320
|
|
||||||
#endif
|
|
||||||
#ifndef RENDER_HEIGHT
|
|
||||||
#define RENDER_HEIGHT 240
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the rendering system.
|
|
||||||
*/
|
|
||||||
errorret_t renderInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells the rendering system to actually draw the frame.
|
|
||||||
*/
|
|
||||||
errorret_t renderDraw(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes of the rendering system.
|
|
||||||
*/
|
|
||||||
errorret_t renderDispose(void);
|
|
@@ -1,51 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "scene.h"
|
|
||||||
#include "world/overworld.h"
|
|
||||||
|
|
||||||
scene_t SCENE_CURRENT;
|
|
||||||
scenecallback_t SCENE_CALLBACKS[SCENE_COUNT] = {
|
|
||||||
[SCENE_INITIAL] = {
|
|
||||||
.init = NULL,
|
|
||||||
.update = NULL
|
|
||||||
},
|
|
||||||
|
|
||||||
[SCENE_OVERWORLD] = {
|
|
||||||
.init = overworldInit,
|
|
||||||
.update = overworldUpdate,
|
|
||||||
.dispose = NULL
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void sceneInit(void) {
|
|
||||||
for(uint8_t i = 0; i < SCENE_COUNT; i++) {
|
|
||||||
if(SCENE_CALLBACKS[i].init) {
|
|
||||||
SCENE_CALLBACKS[i].init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SCENE_CURRENT = SCENE_OVERWORLD;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sceneSet(const scene_t scene) {
|
|
||||||
SCENE_CURRENT = scene;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sceneUpdate(void) {
|
|
||||||
if(SCENE_CALLBACKS[SCENE_CURRENT].update) {
|
|
||||||
SCENE_CALLBACKS[SCENE_CURRENT].update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sceneDispose(void) {
|
|
||||||
for(uint8_t i = 0; i < SCENE_COUNT; i++) {
|
|
||||||
if(SCENE_CALLBACKS[i].dispose) {
|
|
||||||
SCENE_CALLBACKS[i].dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,47 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
SCENE_INITIAL,
|
|
||||||
SCENE_OVERWORLD,
|
|
||||||
|
|
||||||
SCENE_COUNT
|
|
||||||
} scene_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
void (*init)(void);
|
|
||||||
void (*update)(void);
|
|
||||||
void (*dispose)(void);
|
|
||||||
} scenecallback_t;
|
|
||||||
|
|
||||||
extern scene_t SCENE_CURRENT;
|
|
||||||
extern scenecallback_t SCENE_CALLBACKS[SCENE_COUNT];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the scene module.
|
|
||||||
*/
|
|
||||||
void sceneInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the current scene.
|
|
||||||
*
|
|
||||||
* @param scene The scene to set.
|
|
||||||
*/
|
|
||||||
void sceneSet(const scene_t scene);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the current scene.
|
|
||||||
*/
|
|
||||||
void sceneUpdate(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes of the current scene.
|
|
||||||
*/
|
|
||||||
void sceneDispose(void);
|
|
@@ -1,23 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <float.h>
|
|
||||||
|
|
||||||
typedef bool bool_t;
|
|
||||||
typedef int int_t;
|
|
||||||
typedef float float_t;
|
|
||||||
typedef char char_t;
|
|
@@ -1,11 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
ecssystem.c
|
|
||||||
ecscomponent.c
|
|
||||||
)
|
|
@@ -1,124 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ecscomponent.h"
|
|
||||||
#include "ecssystem.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
|
|
||||||
void ecsComponentInitialize(ecscomponent_t *cmp) {
|
|
||||||
assertNotNull(cmp, "Component pointer cannot be NULL.");
|
|
||||||
assertTrue(
|
|
||||||
ECS_SYSTEM.componentCount < ECS_SYSTEM_ECS_COMPONENTS_MAX,
|
|
||||||
"ECS Component count exceeded maximum limit."
|
|
||||||
);
|
|
||||||
|
|
||||||
memoryZero(cmp->data, sizeof(ecscomponent_t));
|
|
||||||
|
|
||||||
if(cmp->callbacks.init) cmp->callbacks.init();
|
|
||||||
cmp->componentFlags |= ECS_COMPONENT_FLAG_INITIALIZED;
|
|
||||||
|
|
||||||
ECS_SYSTEM.components[ECS_SYSTEM.componentCount++] = cmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t ecsComponentIsInitialized(const ecscomponent_t *cmp) {
|
|
||||||
assertNotNull(cmp, "Component pointer cannot be NULL.");
|
|
||||||
return (cmp->componentFlags & ECS_COMPONENT_FLAG_INITIALIZED) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool_t ecsComponentDataHas(const ecscomponent_t *cmp, const ecsid_t id) {
|
|
||||||
assertTrue(
|
|
||||||
id >= 0 && id < ECS_ENTITY_COUNT_MAX,
|
|
||||||
"Invalid entity ID."
|
|
||||||
);
|
|
||||||
if(!ecsComponentIsInitialized(cmp)) return false;
|
|
||||||
|
|
||||||
return cmp->entityFlags[id] & ECS_COMPONENT_ENTITY_FLAG_USED;
|
|
||||||
}
|
|
||||||
|
|
||||||
void * ecsComponentDataGet(const ecscomponent_t *cmp, const ecsid_t id) {
|
|
||||||
assertTrue(ecsComponentDataHas(cmp, id), "No data for entity ID.");
|
|
||||||
return (void *)((uint8_t *)cmp->data + (id * cmp->dataSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
void * ecsComponentDataAdd(ecscomponent_t *cmp, const ecsid_t id) {
|
|
||||||
if(!ecsComponentIsInitialized(cmp)) ecsComponentInitialize(cmp);
|
|
||||||
|
|
||||||
assertTrue(id >= 0 && id < ECS_ENTITY_COUNT_MAX, "Invalid entity ID.");
|
|
||||||
assertFalse(ecsComponentDataHas(cmp, id), "Entity already has data.");
|
|
||||||
|
|
||||||
memoryZero(
|
|
||||||
(uint8_t *)cmp->data + (id * cmp->dataSize),
|
|
||||||
cmp->dataSize
|
|
||||||
);
|
|
||||||
cmp->entityFlags[id] |= ECS_COMPONENT_ENTITY_FLAG_USED;
|
|
||||||
if(cmp->callbacks.entityAdd) cmp->callbacks.entityAdd(id);
|
|
||||||
cmp->entitiesWithData[cmp->entitiesWithDataCount++] = id;
|
|
||||||
|
|
||||||
return ecsComponentDataGet(cmp, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ecsComponentDataRemove(ecscomponent_t *cmp, const ecsid_t id) {
|
|
||||||
assertTrue(
|
|
||||||
ecsComponentIsInitialized(cmp), "Component was never initialized."
|
|
||||||
);
|
|
||||||
assertTrue(ecsComponentDataHas(cmp, id), "Entity does not have data.");
|
|
||||||
|
|
||||||
cmp->entityFlags[id] = 0;
|
|
||||||
|
|
||||||
// Remove entity from the entitiesWithData array by finding its index and
|
|
||||||
// shifting the rest of the array down. Use memoryCopy to avoid
|
|
||||||
// unnecessary loops.
|
|
||||||
uint32_t index = 0;
|
|
||||||
for(; index < cmp->entitiesWithDataCount; index++) {
|
|
||||||
if(cmp->entitiesWithData[index] == id) break;
|
|
||||||
}
|
|
||||||
assertTrue(
|
|
||||||
index < cmp->entitiesWithDataCount,
|
|
||||||
"Entity not found in entitiesWithData?"
|
|
||||||
);
|
|
||||||
memoryCopy(
|
|
||||||
&cmp->entitiesWithData[index],
|
|
||||||
&cmp->entitiesWithData[index + 1],
|
|
||||||
sizeof(ecsid_t) * (cmp->entitiesWithDataCount - index - 1)
|
|
||||||
);
|
|
||||||
|
|
||||||
if(cmp->callbacks.entityRemove) cmp->callbacks.entityRemove(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t ecsComponentGetAll(const ecscomponent_t *cmp, ecsid_t *out) {
|
|
||||||
assertTrue(
|
|
||||||
ecsComponentIsInitialized(cmp),
|
|
||||||
"Component was never initialized."
|
|
||||||
);
|
|
||||||
|
|
||||||
if(!out) return cmp->entitiesWithDataCount;
|
|
||||||
|
|
||||||
memoryCopy(
|
|
||||||
out,
|
|
||||||
cmp->entitiesWithData,
|
|
||||||
sizeof(ecsid_t) * cmp->entitiesWithDataCount
|
|
||||||
);
|
|
||||||
return cmp->entitiesWithDataCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ecsComponentDispose(ecscomponent_t *cmp) {
|
|
||||||
assertNotNull(cmp, "Component pointer cannot be NULL.");
|
|
||||||
assertTrue(
|
|
||||||
ecsComponentIsInitialized(cmp),
|
|
||||||
"Component was never initialized."
|
|
||||||
);
|
|
||||||
|
|
||||||
for(uint32_t i = 0; i < cmp->entitiesWithDataCount; i++) {
|
|
||||||
if(cmp->callbacks.entityRemove)
|
|
||||||
cmp->callbacks.entityRemove(cmp->entitiesWithData[i]);
|
|
||||||
}
|
|
||||||
memoryZero(cmp->entityFlags, sizeof(cmp->entityFlags));
|
|
||||||
cmp->entitiesWithDataCount = 0;
|
|
||||||
cmp->componentFlags = 0;
|
|
||||||
}
|
|
@@ -1,111 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "ecs/ecsentity.h"
|
|
||||||
|
|
||||||
#define ECS_COMPONENT_ENTITY_FLAG_USED (1 << 0)
|
|
||||||
|
|
||||||
#define ECS_COMPONENT_FLAG_INITIALIZED (1 << 0)
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
void (*init)();
|
|
||||||
void (*entityAdd)(const ecsid_t id);
|
|
||||||
void (*entityRemove)(const ecsid_t id);
|
|
||||||
} ecscomponentcallbacks_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
void *data;
|
|
||||||
size_t dataSize;
|
|
||||||
uint8_t entityFlags[ECS_ENTITY_COUNT_MAX];
|
|
||||||
uint8_t componentFlags;
|
|
||||||
ecscomponentcallbacks_t callbacks;
|
|
||||||
|
|
||||||
ecsid_t entitiesWithData[ECS_ENTITY_COUNT_MAX];
|
|
||||||
uint32_t entitiesWithDataCount;;
|
|
||||||
} ecscomponent_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes an ECS Component.
|
|
||||||
*
|
|
||||||
* @param dPointer Pointer to the data that the component owns.
|
|
||||||
* @param cbs Callback functions for the component.
|
|
||||||
*/
|
|
||||||
#define ecsComponentInit(dPointer, cbs) \
|
|
||||||
(ecscomponent_t){ \
|
|
||||||
.data = dPointer, \
|
|
||||||
.dataSize = sizeof(*dPointer), \
|
|
||||||
.entityFlags = 0, \
|
|
||||||
.componentFlags = 0, \
|
|
||||||
.callbacks = (cbs) \
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes an ECS Component.
|
|
||||||
*
|
|
||||||
* @param cmp Pointer to the ecscomponent_t to initialize.
|
|
||||||
*/
|
|
||||||
void ecsComponentInitialize(ecscomponent_t *cmp);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the component is initialized.
|
|
||||||
*
|
|
||||||
* @param cmp Pointer to the ecscomponent_t.
|
|
||||||
* @return True if the component is initialized, false otherwise.
|
|
||||||
*/
|
|
||||||
bool_t ecsComponentIsInitialized(const ecscomponent_t *cmp);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the component has data for a specific entity.
|
|
||||||
*
|
|
||||||
* @param cmp Pointer to the ecscomponent_t.
|
|
||||||
* @param id The ID of the entity to check.
|
|
||||||
* @return True if the component has data for the entity, false otherwise.
|
|
||||||
*/
|
|
||||||
bool_t ecsComponentDataHas(const ecscomponent_t *cmp, const ecsid_t id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the data for a specific entity in the component.
|
|
||||||
*
|
|
||||||
* @param cmp Pointer to the ecscomponent_t.
|
|
||||||
* @param id The ID of the entity to get data for.
|
|
||||||
* @return Pointer to the data for the entity.
|
|
||||||
*/
|
|
||||||
void * ecsComponentDataGet(const ecscomponent_t *cmp, const ecsid_t id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds data for a specific entity in the component.
|
|
||||||
*
|
|
||||||
* @param cmp Pointer to the ecscomponent_t.
|
|
||||||
* @param id The ID of the entity to add data for.
|
|
||||||
*/
|
|
||||||
void * ecsComponentDataAdd(ecscomponent_t *cmp, const ecsid_t id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes data for a specific entity in the component.
|
|
||||||
*
|
|
||||||
* @param cmp Pointer to the ecscomponent_t.
|
|
||||||
* @param id The ID of the entity to remove data for.
|
|
||||||
*/
|
|
||||||
void ecsComponentDataRemove(ecscomponent_t *cmp, const ecsid_t id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all entities that have data in the component. Passing NULL for out will
|
|
||||||
* just return the count of entities.
|
|
||||||
*
|
|
||||||
* @param cmp Pointer to the ecscomponent_t.
|
|
||||||
* @param out Pointer to an array to store the entity IDs.
|
|
||||||
* @return The number of entities with data.
|
|
||||||
*/
|
|
||||||
uint32_t ecsComponentGetAll(const ecscomponent_t *cmp, ecsid_t *out);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes the component, freeing any resources it holds.
|
|
||||||
*
|
|
||||||
* @param cmp Pointer to the ecscomponent_t to dispose.
|
|
||||||
*/
|
|
||||||
void ecsComponentDispose(ecscomponent_t *cmp);
|
|
@@ -1,18 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "ecs.h"
|
|
||||||
|
|
||||||
#define ECS_ENTITY_FLAG_USED (1 << 0)
|
|
||||||
|
|
||||||
#define ECS_ENTITY_COUNT_MAX 2048
|
|
||||||
|
|
||||||
typedef struct ecsentity_s {
|
|
||||||
ecsid_t id;
|
|
||||||
uint8_t flags;
|
|
||||||
} ecsentity_t;
|
|
@@ -1,66 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ecssystem.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
ecssystem_t ECS_SYSTEM;
|
|
||||||
|
|
||||||
void ecsSystemInit() {
|
|
||||||
memoryZero(&ECS_SYSTEM, sizeof(ecssystem_t));
|
|
||||||
|
|
||||||
// Fill ECS ids
|
|
||||||
for(uint32_t i = 0; i < ECS_ENTITY_COUNT_MAX; i++) {
|
|
||||||
ECS_SYSTEM.entities[i].id = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill the available array.
|
|
||||||
for(uint32_t i = 0; i < ECS_ENTITY_COUNT_MAX; i++) {
|
|
||||||
ECS_SYSTEM.available[i] = &ECS_SYSTEM.entities[i];
|
|
||||||
}
|
|
||||||
ECS_SYSTEM.availableCount = ECS_ENTITY_COUNT_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
ecsid_t ecsEntityAdd() {
|
|
||||||
assertTrue(ECS_SYSTEM.availableCount > 0, "No available entities to create");
|
|
||||||
|
|
||||||
// Pop off the last available entity.
|
|
||||||
ecsentity_t *entity = ECS_SYSTEM.available[--ECS_SYSTEM.availableCount];
|
|
||||||
assertTrue((entity->flags & ECS_ENTITY_FLAG_USED) == 0, "Entity is used.");
|
|
||||||
assertTrue(entity->id >= 0, "Entity is invalid.");
|
|
||||||
assertTrue(entity->id < ECS_ENTITY_COUNT_MAX, "Entity ID out of bounds");
|
|
||||||
|
|
||||||
entity->flags |= ECS_ENTITY_FLAG_USED;
|
|
||||||
|
|
||||||
return entity->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ecsEntityRemove(const ecsid_t id) {
|
|
||||||
assertTrue(id < ECS_ENTITY_COUNT_MAX, "Invalid entity ID");
|
|
||||||
|
|
||||||
ecsentity_t *entity = &ECS_SYSTEM.entities[id];
|
|
||||||
assertTrue(entity->id >= 0, "Entity is invalid.");
|
|
||||||
assertTrue((entity->flags & ECS_ENTITY_FLAG_USED) != 0, "Entity is not used.");
|
|
||||||
|
|
||||||
// Mark the entity as available.
|
|
||||||
ECS_SYSTEM.available[ECS_SYSTEM.availableCount++] = entity;
|
|
||||||
assertTrue(
|
|
||||||
ECS_SYSTEM.availableCount <= ECS_ENTITY_COUNT_MAX,
|
|
||||||
"Available count exceeded maximum limit"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ecsSystemDispose() {
|
|
||||||
for(uint32_t i = 0; i < ECS_SYSTEM.componentCount; i++) {
|
|
||||||
ecscomponent_t *cmp = ECS_SYSTEM.components[i];
|
|
||||||
ecsComponentDispose(cmp);
|
|
||||||
assertTrue(cmp->entitiesWithDataCount == 0, "Component still has data.");
|
|
||||||
}
|
|
||||||
|
|
||||||
memoryZero(&ECS_SYSTEM, sizeof(ecssystem_t));
|
|
||||||
}
|
|
@@ -1,50 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "ecsentity.h"
|
|
||||||
#include "ecscomponent.h"
|
|
||||||
|
|
||||||
#define ECS_SYSTEM_ECS_COMPONENTS_MAX 64
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ecsentity_t entities[ECS_ENTITY_COUNT_MAX];
|
|
||||||
|
|
||||||
ecsentity_t *available[ECS_ENTITY_COUNT_MAX];
|
|
||||||
uint32_t availableCount;
|
|
||||||
|
|
||||||
ecscomponent_t *components[ECS_SYSTEM_ECS_COMPONENTS_MAX];
|
|
||||||
uint32_t componentCount;
|
|
||||||
} ecssystem_t;
|
|
||||||
|
|
||||||
extern ecssystem_t ECS_SYSTEM;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the ECS system.
|
|
||||||
*/
|
|
||||||
void ecsSystemInit();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new entity in the ECS. This locks an id and gives it to the caller,
|
|
||||||
* who will be responsible for managing the entity's lifecycle.
|
|
||||||
*
|
|
||||||
* @return The ID of the newly created entity.
|
|
||||||
*/
|
|
||||||
ecsid_t ecsEntityAdd();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes an entity in the ECS. This will free the entity's ID and make it
|
|
||||||
* available for reuse.
|
|
||||||
*
|
|
||||||
* @param id The ID of the entity to destroy.
|
|
||||||
*/
|
|
||||||
void ecsEntityRemove(const ecsid_t id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispose the ECS system, freeing all resources.
|
|
||||||
*/
|
|
||||||
void ecsSystemDispose();
|
|
@@ -1,13 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
direction.c
|
|
||||||
entity.c
|
|
||||||
player.c
|
|
||||||
npc.c
|
|
||||||
)
|
|
@@ -1,53 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "direction.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
float_t directionToAngle(const direction_t dir) {
|
|
||||||
switch(dir) {
|
|
||||||
case DIRECTION_NORTH: return (M_PI_2);
|
|
||||||
case DIRECTION_SOUTH: return -(M_PI_2);
|
|
||||||
case DIRECTION_EAST: return 0;
|
|
||||||
case DIRECTION_WEST: return (M_PI);
|
|
||||||
default: return 0; // Should never happen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void directionGetCoordinates(
|
|
||||||
const direction_t dir,
|
|
||||||
int8_t *x, int8_t *y
|
|
||||||
) {
|
|
||||||
assertNotNull(x, "X coordinate pointer cannot be NULL");
|
|
||||||
assertNotNull(y, "Y coordinate pointer cannot be NULL");
|
|
||||||
|
|
||||||
switch(dir) {
|
|
||||||
case DIRECTION_NORTH:
|
|
||||||
*x = 0;
|
|
||||||
*y = -1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DIRECTION_SOUTH:
|
|
||||||
*x = 0;
|
|
||||||
*y = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DIRECTION_EAST:
|
|
||||||
*x = 1;
|
|
||||||
*y = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DIRECTION_WEST:
|
|
||||||
*x = -1;
|
|
||||||
*y = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assertUnreachable("Invalid direction");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,41 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
DIRECTION_SOUTH = 0,
|
|
||||||
DIRECTION_EAST = 1,
|
|
||||||
DIRECTION_WEST = 2,
|
|
||||||
DIRECTION_NORTH = 3,
|
|
||||||
|
|
||||||
DIRECTION_UP = DIRECTION_NORTH,
|
|
||||||
DIRECTION_DOWN = DIRECTION_SOUTH,
|
|
||||||
DIRECTION_LEFT = DIRECTION_WEST,
|
|
||||||
DIRECTION_RIGHT = DIRECTION_EAST,
|
|
||||||
} direction_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a direction to an angle in float_t format.
|
|
||||||
*
|
|
||||||
* @param dir The direction to convert.
|
|
||||||
* @return The angle corresponding to the direction.
|
|
||||||
*/
|
|
||||||
float_t directionToAngle(const direction_t dir);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the relative coordinates for a given direction.
|
|
||||||
*
|
|
||||||
* @param dir The direction to get coordinates for.
|
|
||||||
* @param x Pointer to store the x coordinate.
|
|
||||||
* @param y Pointer to store the y coordinate.
|
|
||||||
*/
|
|
||||||
void directionGetCoordinates(
|
|
||||||
const direction_t dir,
|
|
||||||
int8_t *x, int8_t *y
|
|
||||||
);
|
|
@@ -1,131 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "entity.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "world/world.h"
|
|
||||||
#include "world/tiledata.h"
|
|
||||||
#include "time.h"
|
|
||||||
|
|
||||||
entity_t ENTITIES[ENTITY_COUNT_MAX] = {0};
|
|
||||||
|
|
||||||
entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = {
|
|
||||||
{NULL}, // ENTITY_TYPE_NULL
|
|
||||||
{
|
|
||||||
.load = playerEntityLoad,
|
|
||||||
.update = playerEntityUpdate,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.load = npcLoad,
|
|
||||||
.update = npcUpdate,
|
|
||||||
.interact = npcInteract,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
void entityLoad(entity_t *entity, const entity_t *source) {
|
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
|
||||||
assertNotNull(source, "Source entity pointer cannot be NULL");
|
|
||||||
assertTrue(source->type != ENTITY_TYPE_NULL, "Source entity type NULL");
|
|
||||||
assertTrue(source->type < ENTITY_TYPE_COUNT, "Source entity type bad");
|
|
||||||
assertNotNull(
|
|
||||||
ENTITY_CALLBACKS[source->type].load,
|
|
||||||
"Entity type has no i nit callback"
|
|
||||||
);
|
|
||||||
|
|
||||||
memoryZero(entity, sizeof(entity_t));
|
|
||||||
|
|
||||||
entity->type = source->type;
|
|
||||||
entity->x = source->x;
|
|
||||||
entity->y = source->y;
|
|
||||||
entity->dir = source->dir;
|
|
||||||
entity->id = source->id;
|
|
||||||
|
|
||||||
ENTITY_CALLBACKS[entity->type].load(entity, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
void entityUpdate(entity_t *entity) {
|
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
|
||||||
assertTrue(entity->type != ENTITY_TYPE_NULL, "Entity type NULL");
|
|
||||||
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
|
|
||||||
assertNotNull(
|
|
||||||
ENTITY_CALLBACKS[entity->type].update,
|
|
||||||
"Entity type has no update callback"
|
|
||||||
);
|
|
||||||
|
|
||||||
ENTITY_CALLBACKS[entity->type].update(entity);
|
|
||||||
|
|
||||||
if(entity->subX > 0) {
|
|
||||||
entity->subX -= entity->moveSpeed;
|
|
||||||
} else if(entity->subX < 0) {
|
|
||||||
entity->subX += entity->moveSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(entity->subY > 0) {
|
|
||||||
entity->subY -= entity->moveSpeed;
|
|
||||||
} else if(entity->subY < 0) {
|
|
||||||
entity->subY += entity->moveSpeed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void entityMove(entity_t *entity, const uint8_t moveSpeed) {
|
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
|
||||||
assertTrue(entity->type != ENTITY_TYPE_NULL, "Entity type NULL");
|
|
||||||
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
|
|
||||||
assertFalse(
|
|
||||||
entityIsMoving(entity),
|
|
||||||
"Entity is already moving, cannot move again"
|
|
||||||
);
|
|
||||||
|
|
||||||
int8_t x = 0, y = 0;
|
|
||||||
directionGetCoordinates(entity->dir, &x, &y);
|
|
||||||
|
|
||||||
// entity in way?
|
|
||||||
entity_t *ent = entityGetAt(entity->x + x, entity->y + y);
|
|
||||||
if(ent != NULL) return;
|
|
||||||
|
|
||||||
entity->x += x;
|
|
||||||
entity->y += y;
|
|
||||||
entity->subX = TILE_WIDTH_HEIGHT * -x;
|
|
||||||
entity->subY = TILE_WIDTH_HEIGHT * -y;
|
|
||||||
entity->moveSpeed = moveSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void entityTurn(entity_t *entity, const direction_t dir) {
|
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
|
||||||
assertTrue(entity->type != ENTITY_TYPE_NULL, "Entity type NULL");
|
|
||||||
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
|
|
||||||
assertTrue(
|
|
||||||
dir >= DIRECTION_SOUTH && dir <= DIRECTION_NORTH, "Invalid direction"
|
|
||||||
);
|
|
||||||
assertFalse(
|
|
||||||
entityIsMoving(entity), "Entity is already moving, cannot turn"
|
|
||||||
);
|
|
||||||
|
|
||||||
entity->dir = dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t entityIsMoving(const entity_t *entity) {
|
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
|
||||||
assertTrue(entity->type != ENTITY_TYPE_NULL, "Entity type NULL");
|
|
||||||
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
|
|
||||||
return entity->subX != 0 || entity->subY != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity_t * entityGetAt(
|
|
||||||
const uint32_t tileX,
|
|
||||||
const uint32_t tileY
|
|
||||||
) {
|
|
||||||
entity_t *entity = ENTITIES;
|
|
||||||
|
|
||||||
do {
|
|
||||||
if(entity->type == ENTITY_TYPE_NULL) continue;
|
|
||||||
if(entity->x == tileX && entity->y == tileY) return entity;
|
|
||||||
} while((entity++) < &ENTITIES[ENTITY_COUNT_MAX - 1]);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
@@ -1,99 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "direction.h"
|
|
||||||
#include "player.h"
|
|
||||||
#include "npc.h"
|
|
||||||
|
|
||||||
#define ENTITY_COUNT_MAX 32
|
|
||||||
#define ENTITY_TURN_DURATION 0.075f // Duration for turning in seconds
|
|
||||||
#define ENTITY_MOVE_DURATION 0.1f // Duration for moving 1 tile, in seconds.
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
ENTITY_TYPE_NULL = 0,
|
|
||||||
ENTITY_TYPE_PLAYER = 1,
|
|
||||||
ENTITY_TYPE_NPC = 2,
|
|
||||||
} entitytype_t;
|
|
||||||
#define ENTITY_TYPE_COUNT 3
|
|
||||||
|
|
||||||
typedef struct _entity_t {
|
|
||||||
uint32_t id;// Completely unique ID for this entity.
|
|
||||||
uint32_t x, y;
|
|
||||||
int8_t subX, subY;
|
|
||||||
uint8_t moveSpeed;
|
|
||||||
|
|
||||||
entitytype_t type;
|
|
||||||
direction_t dir;
|
|
||||||
|
|
||||||
|
|
||||||
union {
|
|
||||||
npc_t npc;
|
|
||||||
playerentity_t player;
|
|
||||||
};
|
|
||||||
} entity_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
void (*load) (entity_t *entity, const entity_t *source);
|
|
||||||
void (*update) (entity_t *entity);
|
|
||||||
void (*interact)(entity_t *player, entity_t *self);
|
|
||||||
} entitycallback_t;
|
|
||||||
|
|
||||||
extern entity_t ENTITIES[ENTITY_COUNT_MAX];
|
|
||||||
extern entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads an entity from the generated entity data.
|
|
||||||
*
|
|
||||||
* @param entity Pointer to the entity to initialize.
|
|
||||||
* @param source Pointer to the source entity data.
|
|
||||||
*/
|
|
||||||
void entityLoad(entity_t *entity, const entity_t *source);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the entity's state.
|
|
||||||
*
|
|
||||||
* @param entity Pointer to the entity to update.
|
|
||||||
*/
|
|
||||||
void entityUpdate(entity_t *entity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves the entity by the specified x and y offsets.
|
|
||||||
*
|
|
||||||
* @param entity Pointer to the entity to move.
|
|
||||||
* @param moveSpeed The speed at which to move the entity.
|
|
||||||
*/
|
|
||||||
void entityMove(entity_t *entity, const uint8_t moveSpeed);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turns the entity to face the specified direction.
|
|
||||||
*
|
|
||||||
* @param entity Pointer to the entity to turn.
|
|
||||||
* @param dir The direction to turn the entity to.
|
|
||||||
*/
|
|
||||||
void entityTurn(entity_t *entity, const direction_t dir);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether or not an entity is currently moving.
|
|
||||||
*
|
|
||||||
* @param entity Pointer to the entity to check.
|
|
||||||
* @return True if the entity is moving, false otherwise.
|
|
||||||
*/
|
|
||||||
bool_t entityIsMoving(const entity_t *entity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the entity at the specified tile coordinates.
|
|
||||||
*
|
|
||||||
* @param tileX The x coordinate of the tile to get the entity from.
|
|
||||||
* @param tileY The y coordinate of the tile to get the entity from.
|
|
||||||
* @return Pointer to the entity at the specified coordinates, or NULL if no
|
|
||||||
* entity exists there.
|
|
||||||
*/
|
|
||||||
entity_t *entityGetAt(
|
|
||||||
const uint32_t tileX,
|
|
||||||
const uint32_t tileY
|
|
||||||
);
|
|
@@ -1,45 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "entity.h"
|
|
||||||
#include "ui/uitextbox.h"
|
|
||||||
#include "locale/language.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
void npcLoad(entity_t *entity, const entity_t *source) {
|
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
|
||||||
assertNotNull(source, "Source entity pointer cannot be NULL");
|
|
||||||
assertTrue(source->type == ENTITY_TYPE_NPC, "Source entity type must be NPC");
|
|
||||||
|
|
||||||
entity->npc = source->npc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void npcUpdate(entity_t *entity) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void npcInteract(entity_t *player, entity_t *self) {
|
|
||||||
assertTrue(self->type == ENTITY_TYPE_NPC, "Entity must be of type NPC");
|
|
||||||
|
|
||||||
switch(self->npc.interactType) {
|
|
||||||
case NPC_INTERACT_TYPE_NONE:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NPC_INTERACT_TYPE_TEXT:
|
|
||||||
uiTextboxSetText(languageGet(self->npc.text));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NPC_INTERACT_TYPE_CONVO:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NPC_INTERACT_TYPE_EVENT:
|
|
||||||
eventSetActive(self->npc.eventData);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assertUnreachable("Unknown NPC interaction type");
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,44 +0,0 @@
|
|||||||
|
|
||||||
#pragma once
|
|
||||||
#include "event/eventlist.h"
|
|
||||||
|
|
||||||
typedef struct _entity_t entity_t;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
NPC_INTERACT_TYPE_NONE = 0,
|
|
||||||
NPC_INTERACT_TYPE_TEXT = 1,
|
|
||||||
NPC_INTERACT_TYPE_CONVO = 2,
|
|
||||||
NPC_INTERACT_TYPE_EVENT = 3,
|
|
||||||
} npcinteracttype_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
npcinteracttype_t interactType;
|
|
||||||
|
|
||||||
union {
|
|
||||||
const char_t* text;
|
|
||||||
const eventdata_t *eventData;
|
|
||||||
};
|
|
||||||
} npc_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the NPC entity.
|
|
||||||
*
|
|
||||||
* @param entity The entity to initialize.
|
|
||||||
* @param source The source entity to copy data from.
|
|
||||||
*/
|
|
||||||
void npcLoad(entity_t *entity, const entity_t *source);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the NPC entity.
|
|
||||||
*
|
|
||||||
* @param entity The entity to update.
|
|
||||||
*/
|
|
||||||
void npcUpdate(entity_t *entity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles interaction between the player and the NPC.
|
|
||||||
*
|
|
||||||
* @param player The player entity interacting with the NPC.
|
|
||||||
* @param self The NPC entity being interacted with.
|
|
||||||
*/
|
|
||||||
void npcInteract(entity_t *player, entity_t *self);
|
|
@@ -1,94 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "entity.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "input.h"
|
|
||||||
#include "display/render.h"
|
|
||||||
#include "world/world.h"
|
|
||||||
|
|
||||||
#include "ui/uitextbox.h"
|
|
||||||
|
|
||||||
inventory_t PLAYER_INVENTORY;
|
|
||||||
|
|
||||||
void playerInit() {
|
|
||||||
entity_t *ent = &ENTITIES[0];
|
|
||||||
|
|
||||||
entity_t playerEntityData = {
|
|
||||||
.id = PLAYER_ENTITY_ID,
|
|
||||||
.type = ENTITY_TYPE_PLAYER,
|
|
||||||
.x = WORLD_PLAYER_SPAWN_X,
|
|
||||||
.y = WORLD_PLAYER_SPAWN_Y,
|
|
||||||
};
|
|
||||||
|
|
||||||
entityLoad(ent, &playerEntityData);
|
|
||||||
inventoryInit(&PLAYER_INVENTORY, INVENTORY_SIZE_MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
void playerEntityLoad(entity_t *entity, const entity_t *source) {
|
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
|
||||||
assertNotNull(source, "Source entity pointer cannot be NULL");
|
|
||||||
assertTrue(entity->type == ENTITY_TYPE_PLAYER, "Entity type must be PLAYER");
|
|
||||||
assertTrue(source->type == entity->type, "Source/Entity type mismatch");
|
|
||||||
}
|
|
||||||
|
|
||||||
void playerEntityUpdate(entity_t *entity) {
|
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
|
||||||
assertTrue(entity->type == ENTITY_TYPE_PLAYER, "Entity type must be PLAYER");
|
|
||||||
|
|
||||||
// TODO: make this just a method somewhere.
|
|
||||||
if(UI_TEXTBOX.visible) return;
|
|
||||||
if(entityIsMoving(entity)) return;
|
|
||||||
|
|
||||||
const uint8_t moveSpeed = inputIsDown(INPUT_BIND_CANCEL) ? PLAYER_SPEED_RUN : PLAYER_SPEED_WALK;
|
|
||||||
|
|
||||||
if(inputIsDown(INPUT_BIND_UP)) {
|
|
||||||
if(entity->dir != DIRECTION_NORTH) {
|
|
||||||
entityTurn(entity, DIRECTION_NORTH);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
entityMove(entity, moveSpeed);
|
|
||||||
return;
|
|
||||||
|
|
||||||
} else if(inputIsDown(INPUT_BIND_DOWN)) {
|
|
||||||
if(entity->dir != DIRECTION_SOUTH) {
|
|
||||||
entityTurn(entity, DIRECTION_SOUTH);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
entityMove(entity, moveSpeed);
|
|
||||||
return;
|
|
||||||
} else if(inputIsDown(INPUT_BIND_LEFT)) {
|
|
||||||
if(entity->dir != DIRECTION_WEST) {
|
|
||||||
entityTurn(entity, DIRECTION_WEST);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
entityMove(entity, moveSpeed);
|
|
||||||
return;
|
|
||||||
|
|
||||||
} else if(inputIsDown(INPUT_BIND_RIGHT)) {
|
|
||||||
if(entity->dir != DIRECTION_EAST) {
|
|
||||||
entityTurn(entity, DIRECTION_EAST);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
entityMove(entity, moveSpeed);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interact
|
|
||||||
if(inputPressed(INPUT_BIND_ACTION)) {
|
|
||||||
int8_t x, y;
|
|
||||||
directionGetCoordinates(entity->dir, &x, &y);
|
|
||||||
entity_t *ent = entityGetAt(entity->x + x, entity->y + y);
|
|
||||||
|
|
||||||
if(ent != NULL && ENTITY_CALLBACKS[ent->type].interact != NULL) {
|
|
||||||
assertTrue(ent->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
|
|
||||||
ENTITY_CALLBACKS[ent->type].interact(entity, ent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,43 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
#include "item/inventory.h"
|
|
||||||
|
|
||||||
#define PLAYER_SPEED_WALK 1
|
|
||||||
#define PLAYER_SPEED_RUN 2
|
|
||||||
|
|
||||||
typedef struct _entity_t entity_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t nothing;
|
|
||||||
} playerentity_t;
|
|
||||||
|
|
||||||
#define PLAYER_ENTITY_ID (UINT32_MAX-1)
|
|
||||||
|
|
||||||
extern inventory_t PLAYER_INVENTORY;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the player and all player-related entities.
|
|
||||||
*/
|
|
||||||
void playerInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the player entity.
|
|
||||||
*
|
|
||||||
* @param entity The entity to initialize.
|
|
||||||
* @param source The source entity to copy data from.
|
|
||||||
*/
|
|
||||||
void playerEntityLoad(entity_t *entity, const entity_t *source);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the player entity.
|
|
||||||
*
|
|
||||||
* @param entity The entity to update.
|
|
||||||
*/
|
|
||||||
void playerEntityUpdate(entity_t *entity);
|
|
@@ -1,125 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "util/string.h"
|
|
||||||
|
|
||||||
errorret_t errorThrowImpl(
|
|
||||||
errorstate_t *state,
|
|
||||||
errorcode_t code,
|
|
||||||
const char_t *file,
|
|
||||||
const char_t *function,
|
|
||||||
const int32_t line,
|
|
||||||
const char_t *message,
|
|
||||||
...
|
|
||||||
) {
|
|
||||||
assertNotNull(state, "Error state cannot be NULL");
|
|
||||||
assertTrue(code != ERROR_OK, "Error code must not be OK");
|
|
||||||
assertNotNull(file, "File cannot be NULL");
|
|
||||||
assertNotNull(function, "Function cannot be NULL");
|
|
||||||
assertTrue(line >= 0, "File pointer must be valid");
|
|
||||||
assertNotNull(message, "Message cannot be NULL");
|
|
||||||
|
|
||||||
memoryZero(state, sizeof(errorstate_t));
|
|
||||||
state->code = code;
|
|
||||||
|
|
||||||
// Format args.
|
|
||||||
va_list args;
|
|
||||||
va_start(args, message);
|
|
||||||
|
|
||||||
// Get length of formatted message
|
|
||||||
va_list argsCopy;
|
|
||||||
va_copy(argsCopy, args);
|
|
||||||
int32_t len = stringFormatVA(NULL, 0, message, argsCopy);
|
|
||||||
va_end(argsCopy);
|
|
||||||
|
|
||||||
// Create string to hold the formatted message
|
|
||||||
state->message = (char_t *)memoryAllocate(len + 1);
|
|
||||||
stringFormatVA(state->message, len + 1, message, args);
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
// Format lines
|
|
||||||
len = stringFormat(NULL, 0, ERROR_LINE_FORMAT, file, line, function);
|
|
||||||
assertTrue(len >= 0, "Line formatting failed");
|
|
||||||
state->lines = (char_t *)memoryAllocate(len + 1);
|
|
||||||
stringFormat(state->lines, len + 1, ERROR_LINE_FORMAT, file, line, function);
|
|
||||||
|
|
||||||
return (errorret_t) {
|
|
||||||
.code = code,
|
|
||||||
.state = state
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t errorOkImpl() {
|
|
||||||
return (errorret_t) {
|
|
||||||
.code = ERROR_OK,
|
|
||||||
.state = NULL
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t errorChainImpl(
|
|
||||||
const errorret_t retval,
|
|
||||||
const char_t *file,
|
|
||||||
const char_t *function,
|
|
||||||
const int32_t line
|
|
||||||
) {
|
|
||||||
if (retval.code == ERROR_OK) return retval;
|
|
||||||
|
|
||||||
assertNotNull(retval.state, "Error state cannot be NULL");
|
|
||||||
assertNotNull(retval.state->message, "Message cannot be NULL");
|
|
||||||
|
|
||||||
// Create a new line string.
|
|
||||||
int32_t newLineLen = snprintf(NULL, 0, ERROR_LINE_FORMAT, file, line, function);
|
|
||||||
assertTrue(newLineLen >= 0, "Line formatting failed");
|
|
||||||
char_t *newLine = (char_t *)memoryAllocate(newLineLen + 1);
|
|
||||||
snprintf(newLine, newLineLen + 1, ERROR_LINE_FORMAT, file, line, function);
|
|
||||||
|
|
||||||
// Resize the existing lines to accommodate the new line
|
|
||||||
size_t existingLen = strlen(retval.state->lines);
|
|
||||||
memoryResize(
|
|
||||||
(void**)&retval.state->lines,
|
|
||||||
existingLen,
|
|
||||||
existingLen + newLineLen + 1
|
|
||||||
);
|
|
||||||
|
|
||||||
// Now append the new line to the existing lines
|
|
||||||
memoryCopy(
|
|
||||||
retval.state->lines + existingLen,
|
|
||||||
newLine,
|
|
||||||
newLineLen + 1
|
|
||||||
);
|
|
||||||
|
|
||||||
// Cleanup the temporary new line
|
|
||||||
memoryFree(newLine);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
void errorCatch(const errorret_t retval) {
|
|
||||||
if (retval.code == ERROR_OK) return;
|
|
||||||
|
|
||||||
assertNotNull(retval.state, "Error state cannot be NULL");
|
|
||||||
assertNotNull(retval.state->message, "Message cannot be NULL");
|
|
||||||
|
|
||||||
memoryFree((void*)retval.state->message);
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t errorPrint(const errorret_t retval) {
|
|
||||||
if (retval.code == ERROR_OK) return retval;
|
|
||||||
|
|
||||||
assertNotNull(retval.state, "Error state cannot be NULL");
|
|
||||||
assertNotNull(retval.state->message, "Message cannot be NULL");
|
|
||||||
|
|
||||||
printf(
|
|
||||||
ERROR_PRINT_FORMAT,
|
|
||||||
retval.state->code,
|
|
||||||
retval.state->message,
|
|
||||||
retval.state->lines
|
|
||||||
);
|
|
||||||
return retval;
|
|
||||||
}
|
|
@@ -1,137 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef uint8_t errorcode_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
errorcode_t code;
|
|
||||||
char_t *message;
|
|
||||||
char_t *lines;
|
|
||||||
} errorstate_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
errorcode_t code;
|
|
||||||
errorstate_t *state;
|
|
||||||
} errorret_t;
|
|
||||||
|
|
||||||
static const errorcode_t ERROR_OK = 0;
|
|
||||||
static const errorcode_t ERROR_NOT_OK = 1;
|
|
||||||
static const char_t *ERROR_PRINT_FORMAT = "Error (%d): %s\n%s";
|
|
||||||
static const char_t *ERROR_LINE_FORMAT = " at %s:%d in function %s\n";
|
|
||||||
static errorstate_t ERROR_STATE = {
|
|
||||||
.code = ERROR_OK,
|
|
||||||
.message = NULL,
|
|
||||||
.lines = NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the error state with the provided code and message.
|
|
||||||
*
|
|
||||||
* @param state The error state to initialize.
|
|
||||||
* @param code The error code to set.
|
|
||||||
* @param file The file where the error occurred.
|
|
||||||
* @param function The function where the error occurred.
|
|
||||||
* @param line The line number where the error occurred.
|
|
||||||
* @param message The error message.
|
|
||||||
* @param args The arguments for the error message.
|
|
||||||
* @return The error code.
|
|
||||||
*/
|
|
||||||
errorret_t errorThrowImpl(
|
|
||||||
errorstate_t *state,
|
|
||||||
errorcode_t code,
|
|
||||||
const char_t *file,
|
|
||||||
const char_t *function,
|
|
||||||
const int32_t line,
|
|
||||||
const char_t *message,
|
|
||||||
...
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an error state with no error.
|
|
||||||
*
|
|
||||||
* @return An error state with code ERROR_OK.
|
|
||||||
*/
|
|
||||||
errorret_t errorOkImpl();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Chains an error state, allowing for error propagation.
|
|
||||||
*
|
|
||||||
* @param retval The return value containing the error state.
|
|
||||||
* @param file The file where the error occurred.
|
|
||||||
* @param function The function where the error occurred.
|
|
||||||
* @param line The line number where the error occurred.
|
|
||||||
* @return The error code if an error occurred, otherwise continues execution.
|
|
||||||
*/
|
|
||||||
errorret_t errorChainImpl(
|
|
||||||
const errorret_t retval,
|
|
||||||
const char_t *file,
|
|
||||||
const char_t *function,
|
|
||||||
const int32_t line
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Catches an error and handles it.
|
|
||||||
*
|
|
||||||
* @param retval The return value containing the error state.
|
|
||||||
*/
|
|
||||||
void errorCatch(const errorret_t retval);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints the error state to the console.
|
|
||||||
*
|
|
||||||
* @param retval The return value containing the error state.
|
|
||||||
* @return Passed retval for chaining.
|
|
||||||
*/
|
|
||||||
errorret_t errorPrint(const errorret_t retval);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Throws an error with a formatted message.
|
|
||||||
*
|
|
||||||
* @param code The error code to throw.
|
|
||||||
* @param message The format string for the error message.
|
|
||||||
* @param ... Additional arguments for the format string.
|
|
||||||
* @return The error code.
|
|
||||||
*/
|
|
||||||
#define errorThrowWithCode(code, message, ... ) \
|
|
||||||
return errorThrowImpl(\
|
|
||||||
&ERROR_STATE, (code), __FILE__, __func__, __LINE__, (message), \
|
|
||||||
__VA_ARGS__ \
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Throws an error with a default error code of ERROR_NOT_OK.
|
|
||||||
*
|
|
||||||
* @param message The format string for the error message.
|
|
||||||
* @param ... Additional arguments for the format string.
|
|
||||||
* @return The error code.
|
|
||||||
*/
|
|
||||||
#define errorThrow(message, ...) \
|
|
||||||
return errorThrowImpl(\
|
|
||||||
&ERROR_STATE, ERROR_NOT_OK, __FILE__, __func__, __LINE__, (message), \
|
|
||||||
__VA_ARGS__ \
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a child method errored, and if it did, then send it up the chain.
|
|
||||||
* @param retval The return value containing the error state.
|
|
||||||
* @return The error code if an error occurred, otherwise continues execution.
|
|
||||||
*/
|
|
||||||
#define errorChain(retval) \
|
|
||||||
if ((retval).code != ERROR_OK) { \
|
|
||||||
return errorChainImpl(retval, __FILE__, __func__, __LINE__); \
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns without an error.
|
|
||||||
*/
|
|
||||||
#define errorOk() \
|
|
||||||
return errorOkImpl()
|
|
||||||
|
|
||||||
// EOF
|
|
@@ -1,11 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
event.c
|
|
||||||
eventtext.c
|
|
||||||
)
|
|
@@ -1,73 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "event.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
eventcallback_t EVENT_CALLBACKS[] = {
|
|
||||||
[EVENT_TYPE_NULL] = { NULL, NULL },
|
|
||||||
[EVENT_TYPE_TEXT] = { eventTextStart, eventTextUpdate },
|
|
||||||
};
|
|
||||||
|
|
||||||
event_t EVENT;
|
|
||||||
|
|
||||||
void eventInit() {
|
|
||||||
memoryZero(&EVENT, sizeof(event_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
void eventUpdate() {
|
|
||||||
if(EVENT.active == NULL) {
|
|
||||||
return; // No active event to update
|
|
||||||
}
|
|
||||||
|
|
||||||
const eventitem_t *item = &EVENT.active->items[EVENT.item];
|
|
||||||
assertNotNull(
|
|
||||||
EVENT_CALLBACKS[item->type].update,
|
|
||||||
"Event type does not have an update callback"
|
|
||||||
);
|
|
||||||
|
|
||||||
EVENT_CALLBACKS[item->type].update(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
void eventSetActive(const eventdata_t *event) {
|
|
||||||
assertNotNull(event, "Event data cannot be NULL");
|
|
||||||
assertTrue(
|
|
||||||
event->itemCount <= EVENT_ITEM_COUNT_MAX,
|
|
||||||
"Event count too high"
|
|
||||||
);
|
|
||||||
assertTrue(event->itemCount > 0, "Event must have at least one item");
|
|
||||||
|
|
||||||
EVENT.active = event;
|
|
||||||
EVENT.item = 0;
|
|
||||||
|
|
||||||
const eventitem_t *firstItem = &EVENT.active->items[EVENT.item];
|
|
||||||
|
|
||||||
assertNotNull(
|
|
||||||
EVENT_CALLBACKS[firstItem->type].start,
|
|
||||||
"Event type does not have a start callback"
|
|
||||||
);
|
|
||||||
EVENT_CALLBACKS[firstItem->type].start(firstItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
void eventNext() {
|
|
||||||
assertNotNull(EVENT.active, "No active event to proceed with");
|
|
||||||
assertTrue(EVENT.item < EVENT.active->itemCount, "No more items in the event");
|
|
||||||
|
|
||||||
EVENT.item++;
|
|
||||||
if (EVENT.item >= EVENT.active->itemCount) {
|
|
||||||
EVENT.active = NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const eventitem_t *nextItem = &EVENT.active->items[EVENT.item];
|
|
||||||
assertNotNull(
|
|
||||||
EVENT_CALLBACKS[nextItem->type].start,
|
|
||||||
"Event type does not have a start callback"
|
|
||||||
);
|
|
||||||
EVENT_CALLBACKS[nextItem->type].start(nextItem);
|
|
||||||
}
|
|
@@ -1,46 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "eventdata.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
eventdata_t data;
|
|
||||||
const eventdata_t *active;
|
|
||||||
uint8_t item;
|
|
||||||
} event_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
void (*start)(const eventitem_t *item);
|
|
||||||
void (*update)(const eventitem_t *item);
|
|
||||||
} eventcallback_t;
|
|
||||||
|
|
||||||
extern eventcallback_t EVENT_CALLBACKS[EVENT_TYPE_COUNT];
|
|
||||||
extern event_t EVENT;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the event system.
|
|
||||||
*/
|
|
||||||
void eventInit();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the active event.
|
|
||||||
*/
|
|
||||||
void eventUpdate();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the active event.
|
|
||||||
*
|
|
||||||
* @param event The event to set as active.
|
|
||||||
*/
|
|
||||||
void eventSetActive(const eventdata_t *eventData);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Goes to the next item in the active event. Only meant to be called by
|
|
||||||
* event items.
|
|
||||||
*/
|
|
||||||
void eventNext();
|
|
@@ -1,14 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "eventitem.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t itemCount;
|
|
||||||
eventitem_t items[EVENT_ITEM_COUNT_MAX];
|
|
||||||
} eventdata_t;
|
|
@@ -1,26 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma ocne
|
|
||||||
#include "eventtext.h"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
EVENT_TYPE_NULL = 0,
|
|
||||||
EVENT_TYPE_TEXT,
|
|
||||||
} eventtype_t;
|
|
||||||
|
|
||||||
#define EVENT_TYPE_COUNT 2
|
|
||||||
|
|
||||||
typedef struct _eventitem_t {
|
|
||||||
eventtype_t type;
|
|
||||||
|
|
||||||
union {
|
|
||||||
eventtext_t text;
|
|
||||||
};
|
|
||||||
} eventitem_t;
|
|
||||||
|
|
||||||
#define EVENT_ITEM_COUNT_MAX 32
|
|
@@ -1,26 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "event.h"
|
|
||||||
#include "ui/uitextbox.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
void eventTextStart(const eventitem_t *item) {
|
|
||||||
assertNotNull(item, "Event item cannot be NULL");
|
|
||||||
assertTrue(item->type == EVENT_TYPE_TEXT, "Event item must be of type TEXT");
|
|
||||||
assertNotNull(item->text, "Event item must have at least one text");
|
|
||||||
uiTextboxSetText(item->text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void eventTextUpdate(const eventitem_t *item) {
|
|
||||||
assertNotNull(item, "Event item cannot be NULL");
|
|
||||||
assertTrue(item->type == EVENT_TYPE_TEXT, "Event item must be of type TEXT");
|
|
||||||
|
|
||||||
if(!UI_TEXTBOX.visible) {
|
|
||||||
eventNext();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,36 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef struct _eventitem_t eventitem_t;
|
|
||||||
|
|
||||||
#define EVENT_TEXT_STRING_COUNT_MAX 8
|
|
||||||
|
|
||||||
typedef const char_t* eventtext_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the text event for the given item.
|
|
||||||
*
|
|
||||||
* @param item The event item to start.
|
|
||||||
*/
|
|
||||||
void eventTextStart(const eventitem_t *item);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the text event for the given item.
|
|
||||||
*
|
|
||||||
* @param item The event item to update.
|
|
||||||
*/
|
|
||||||
void eventTextUpdate(const eventitem_t *item);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query whether the text event is done or not.
|
|
||||||
*
|
|
||||||
* @param item The event item to check.
|
|
||||||
*/
|
|
||||||
bool_t eventTextIsDone(const eventitem_t *item);
|
|
@@ -1,57 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "game.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "world/chunk.h"
|
|
||||||
#include "display/scene.h"
|
|
||||||
#include "world/overworld.h"
|
|
||||||
#include "input.h"
|
|
||||||
#include "event/event.h"
|
|
||||||
#include "ui/uitextbox.h"
|
|
||||||
#include "console/console.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "time.h"
|
|
||||||
|
|
||||||
game_t GAME;
|
|
||||||
|
|
||||||
void gameInit(void) {
|
|
||||||
memoryZero(&GAME, sizeof(game_t));
|
|
||||||
GAME.running = true;
|
|
||||||
|
|
||||||
timeInit();
|
|
||||||
consoleInit();
|
|
||||||
inputInit();
|
|
||||||
eventInit();
|
|
||||||
uiTextboxInit();
|
|
||||||
sceneInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void gameUpdate(void) {
|
|
||||||
timeUpdate();
|
|
||||||
|
|
||||||
// Game logic is tied to 60FPS for now, saves me a lot of hassle with float
|
|
||||||
// issues
|
|
||||||
float_t timeSinceLastTick = TIME.time - TIME.lastTick;
|
|
||||||
while(timeSinceLastTick >= TIME_STEP) {
|
|
||||||
|
|
||||||
sceneUpdate();
|
|
||||||
uiTextboxUpdate();
|
|
||||||
eventUpdate();
|
|
||||||
inputUpdate();
|
|
||||||
|
|
||||||
timeSinceLastTick -= TIME_STEP;
|
|
||||||
TIME.lastTick = TIME.time;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(inputPressed(INPUT_BIND_QUIT)) consoleExec("quit");
|
|
||||||
consoleUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void gameDispose(void) {
|
|
||||||
sceneDispose();
|
|
||||||
}
|
|
@@ -1,49 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
bool_t running;
|
|
||||||
} game_t;
|
|
||||||
|
|
||||||
extern game_t GAME;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the game, this should be called before any other game functions.
|
|
||||||
* This should be called by the parent platform at a time that it deems
|
|
||||||
* appropriate. Any game systems cannot be used until this function has
|
|
||||||
* been called.
|
|
||||||
*
|
|
||||||
* By the point this is called, we expect;
|
|
||||||
* - Rendering has initialized and is ready to draw.
|
|
||||||
* - Input has been initialized and is ready to be read.
|
|
||||||
* - If your system handles time dynamically, it should be ready to be used.
|
|
||||||
*
|
|
||||||
* The systems called (in order) are;
|
|
||||||
* - Console.
|
|
||||||
* - Input system (Not the platforms input, but the game's input system).
|
|
||||||
* - Time system (if applicable).
|
|
||||||
* - Event System
|
|
||||||
* - UI Systems.
|
|
||||||
* - Gameplay systems.
|
|
||||||
*/
|
|
||||||
void gameInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asks the game to update, this will not do any drawing and should be called
|
|
||||||
* in the main loop of the system, ideally either after or before the rendering
|
|
||||||
* has occured.
|
|
||||||
*/
|
|
||||||
void gameUpdate(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cleans up resources used by the game, rendering really should still be
|
|
||||||
* available at this point because we want to cleanup nicely.
|
|
||||||
*/
|
|
||||||
void gameDispose(void);
|
|
@@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "input.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
|
|
||||||
input_t INPUT;
|
|
||||||
|
|
||||||
void inputInit(void) {
|
|
||||||
memoryZero(&INPUT, sizeof(input_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
void inputUpdate(void) {
|
|
||||||
INPUT.previous = INPUT.current;
|
|
||||||
INPUT.current = inputStateGet();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t inputIsDown(const inputbind_t bind) {
|
|
||||||
assertTrue(bind < INPUT_BIND_COUNT, "Input bind out of bounds");
|
|
||||||
return (INPUT.current & bind) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t inputWasDown(const inputbind_t bind) {
|
|
||||||
assertTrue(bind < INPUT_BIND_COUNT, "Input bind out of bounds");
|
|
||||||
return (INPUT.previous & bind) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t inputPressed(const inputbind_t bind) {
|
|
||||||
return inputIsDown(bind) && !inputWasDown(bind);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t inputReleased(const inputbind_t bind) {
|
|
||||||
return !inputIsDown(bind) && inputWasDown(bind);
|
|
||||||
}
|
|
@@ -1,80 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef uint8_t inputbind_t;
|
|
||||||
typedef inputbind_t inputstate_t;
|
|
||||||
|
|
||||||
#define INPUT_BIND_UP (1 << 0)
|
|
||||||
#define INPUT_BIND_DOWN (1 << 1)
|
|
||||||
#define INPUT_BIND_LEFT (1 << 2)
|
|
||||||
#define INPUT_BIND_RIGHT (1 << 3)
|
|
||||||
#define INPUT_BIND_ACTION (1 << 4)
|
|
||||||
#define INPUT_BIND_CANCEL (1 << 5)
|
|
||||||
#define INPUT_BIND_CONSOLE (1 << 6)
|
|
||||||
#define INPUT_BIND_QUIT (1 << 7)
|
|
||||||
|
|
||||||
#define INPUT_BIND_COUNT (INPUT_BIND_QUIT + 1)
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t current;
|
|
||||||
uint8_t previous;
|
|
||||||
} input_t;
|
|
||||||
|
|
||||||
extern input_t INPUT;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the input system.
|
|
||||||
*/
|
|
||||||
void inputInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the input state.
|
|
||||||
*/
|
|
||||||
void inputUpdate(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the current input state as a bitmask.
|
|
||||||
*
|
|
||||||
* @return The current input state as a bitmask.
|
|
||||||
*/
|
|
||||||
inputstate_t inputStateGet(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a specific input bind is currently pressed.
|
|
||||||
*
|
|
||||||
* @param bind The input bind to check.
|
|
||||||
* @return true if the bind is currently pressed, false otherwise.
|
|
||||||
*/
|
|
||||||
bool_t inputIsDown(const inputbind_t bind);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a specific input bind was pressed in the last update.
|
|
||||||
*
|
|
||||||
* @param bind The input bind to check.
|
|
||||||
* @return true if the bind was pressed in the last update, false otherwise.
|
|
||||||
*/
|
|
||||||
bool_t inputWasDown(const inputbind_t bind);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a specific input bind was down this frame but not in the the
|
|
||||||
* previous frame.
|
|
||||||
*
|
|
||||||
* @param bind The input bind to check.
|
|
||||||
* @return true if the bind is currently pressed, false otherwise.
|
|
||||||
*/
|
|
||||||
bool_t inputPressed(const inputbind_t bind);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a specific input bind was released this frame.
|
|
||||||
*
|
|
||||||
* @param bind The input bind to check.
|
|
||||||
* @return true if the bind was released this frame, false otherwise.
|
|
||||||
*/
|
|
||||||
bool_t inputReleased(const inputbind_t bind);
|
|
@@ -1,68 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "inventory.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
void inventoryInit(inventory_t *inventory, const uint8_t size) {
|
|
||||||
assertNotNull(inventory, "inventory must not be NULL");
|
|
||||||
assertTrue(size <= INVENTORY_SIZE_MAX, "size exceeding INVENTORY_SIZE_MAX");
|
|
||||||
assertTrue(size > 0, "size must be greater than 0");
|
|
||||||
|
|
||||||
memoryZero(inventory, sizeof(inventory_t));
|
|
||||||
inventory->size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t inventoryItemIndexByType(
|
|
||||||
const inventory_t *inventory,
|
|
||||||
const itemtype_t type
|
|
||||||
) {
|
|
||||||
assertNotNull(inventory, "inventory must not be NULL");
|
|
||||||
assertTrue(type > ITEM_TYPE_NULL, "type must be greater than ITEM_TYPE_NULL");
|
|
||||||
|
|
||||||
uint8_t item = inventory->itemCount;
|
|
||||||
while(item--) {
|
|
||||||
if(inventory->items[item].type == type) return item;
|
|
||||||
}
|
|
||||||
return INVENTORY_SIZE_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t inventoryItemCount(
|
|
||||||
const inventory_t *inventory,
|
|
||||||
const itemtype_t type
|
|
||||||
) {
|
|
||||||
assertNotNull(inventory, "inventory must not be NULL");
|
|
||||||
assertTrue(type > ITEM_TYPE_NULL, "type must be greater than ITEM_TYPE_NULL");
|
|
||||||
|
|
||||||
const uint8_t index = inventoryItemIndexByType(inventory, type);
|
|
||||||
if(index == INVENTORY_SIZE_MAX) return 0;
|
|
||||||
return inventory->items[index].count;
|
|
||||||
}
|
|
||||||
|
|
||||||
void inventoryItemSet(
|
|
||||||
inventory_t *inventory,
|
|
||||||
const itemtype_t type,
|
|
||||||
const uint8_t count
|
|
||||||
) {
|
|
||||||
assertNotNull(inventory, "inventory must not be NULL");
|
|
||||||
assertTrue(type > ITEM_TYPE_NULL, "type must be greater than ITEM_TYPE_NULL");
|
|
||||||
assertTrue(count > 0, "count must be greater than 0");
|
|
||||||
|
|
||||||
const uint8_t index = inventoryItemIndexByType(inventory, type);
|
|
||||||
|
|
||||||
if(index == INVENTORY_SIZE_MAX) {
|
|
||||||
// Item does not exist, add it
|
|
||||||
assertTrue(inventory->itemCount < inventory->size, "inventory is full");
|
|
||||||
inventory->items[inventory->itemCount].type = type;
|
|
||||||
inventory->items[inventory->itemCount].count = count;
|
|
||||||
inventory->itemCount++;
|
|
||||||
} else {
|
|
||||||
// Item exists, update the count
|
|
||||||
inventory->items[index].count = count;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,63 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "itemstack.h"
|
|
||||||
|
|
||||||
#define INVENTORY_SIZE_MAX UINT8_MAX
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
itemstack_t items[INVENTORY_SIZE_MAX];
|
|
||||||
uint8_t itemCount;
|
|
||||||
uint8_t size;
|
|
||||||
} inventory_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes an inventory with a specified size.
|
|
||||||
*
|
|
||||||
* @param inventory Pointer to the inventory to initialize.
|
|
||||||
* @param size The size of the inventory (maximum is INVENTORY_SIZE_MAX).
|
|
||||||
*/
|
|
||||||
void inventoryInit(inventory_t *inventory, const uint8_t size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the index of the item of a specified type in the inventory.
|
|
||||||
*
|
|
||||||
* @param inventory Pointer to the inventory to search.
|
|
||||||
* @param type The type of item to find.
|
|
||||||
* @return The index of the item, or INVENTORY_SIZE_MAX if not found.
|
|
||||||
*/
|
|
||||||
uint8_t inventoryItemIndexByType(
|
|
||||||
const inventory_t *inventory,
|
|
||||||
const itemtype_t type
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the count of items of a specified type in the inventory.
|
|
||||||
*
|
|
||||||
* @param inventory Pointer to the inventory to check.
|
|
||||||
* @param type The type of item to count.
|
|
||||||
* @return The count of items of the specified type.
|
|
||||||
*/
|
|
||||||
uint8_t inventoryItemCount(
|
|
||||||
const inventory_t *inventory,
|
|
||||||
const itemtype_t type
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the count of items of a specified type in the inventory.
|
|
||||||
* If the item does not exist, it will be added.
|
|
||||||
*
|
|
||||||
* @param inventory Pointer to the inventory to modify.
|
|
||||||
* @param type The type of item to set.
|
|
||||||
* @param count The count of items to set.
|
|
||||||
*/
|
|
||||||
void inventoryItemSet(
|
|
||||||
inventory_t *inventory,
|
|
||||||
const itemtype_t type,
|
|
||||||
const uint8_t count
|
|
||||||
);
|
|
@@ -1,14 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "itemtype.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
itemtype_t type;
|
|
||||||
uint8_t count;
|
|
||||||
} itemstack_t;
|
|
@@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
ITEM_TYPE_NULL = 0,
|
|
||||||
|
|
||||||
// MEDICINE
|
|
||||||
ITEM_TYPE_POTION,
|
|
||||||
|
|
||||||
// INGREDIENTS
|
|
||||||
ITEM_TYPE_ONION,
|
|
||||||
ITEM_TYPE_SWEET_POTATO,
|
|
||||||
ITEM_TYPE_CARROT,
|
|
||||||
|
|
||||||
// COOKED FOOD
|
|
||||||
ITEM_TYPE_BAKED_SWEET_POTATO,
|
|
||||||
} itemtype_t;
|
|
@@ -1,10 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
language.c
|
|
||||||
)
|
|
@@ -1,145 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "language.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
language_t LANGUAGE;
|
|
||||||
|
|
||||||
void languageInit(void) {
|
|
||||||
LANGUAGE.current = LANGUAGE_EN;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char_t * languageGet(const char_t *key) {
|
|
||||||
assertNotNull(key, "Key cannot be NULL");
|
|
||||||
assertTrue(LANGUAGE.current < LANGUAGE_COUNT, "Invalid language index");
|
|
||||||
|
|
||||||
int16_t keyIndex = -1;
|
|
||||||
for(uint16_t i = 0; i < LANGUAGE_COUNTS[LANGUAGE.current]; i++) {
|
|
||||||
if(strcmp(LANGUAGE_KEYS[LANGUAGE.current][i], key) != 0) continue;
|
|
||||||
keyIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
assertTrue(keyIndex != -1, "Key not found in language");
|
|
||||||
return LANGUAGE_VALUES[LANGUAGE.current][keyIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t langaugeGetLength(const char_t *key) {
|
|
||||||
assertNotNull(key, "Key cannot be NULL");
|
|
||||||
assertTrue(LANGUAGE.current < LANGUAGE_COUNT, "Invalid language index");
|
|
||||||
|
|
||||||
int16_t keyIndex = -1;
|
|
||||||
for(uint16_t i = 0; i < LANGUAGE_COUNTS[LANGUAGE.current]; i++) {
|
|
||||||
if(strcmp(LANGUAGE_KEYS[LANGUAGE.current][i], key) != 0) continue;
|
|
||||||
keyIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
assertTrue(keyIndex != -1, "Key not found in language");
|
|
||||||
return strlen(LANGUAGE_VALUES[LANGUAGE.current][keyIndex]);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t languageFormat(
|
|
||||||
const char_t *key,
|
|
||||||
char_t *buffer,
|
|
||||||
uint16_t buffSize,
|
|
||||||
const char_t **keys,
|
|
||||||
const char_t **values,
|
|
||||||
const uint16_t valueCount
|
|
||||||
) {
|
|
||||||
if(buffer != NULL) {
|
|
||||||
assertTrue(buffSize > 0, "Buffer size must be greater than 0");
|
|
||||||
} else {
|
|
||||||
assertTrue(buffSize == 0, "Buffer size must be 0 if buffer is NULL");
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNotNull(key, "Key cannot be NULL");
|
|
||||||
assertNotNull(keys, "Keys cannot be NULL");
|
|
||||||
assertNotNull(values, "Values cannot be NULL");
|
|
||||||
|
|
||||||
const char_t *val = languageGet(key);
|
|
||||||
assertNotNull(val, "Value for key cannot be NULL");
|
|
||||||
|
|
||||||
char_t c;
|
|
||||||
uint16_t i = 0;
|
|
||||||
uint16_t j = 0;
|
|
||||||
uint8_t k = 0;
|
|
||||||
bool_t inBraces = false;
|
|
||||||
char_t braceBuffer[64] = {0};
|
|
||||||
|
|
||||||
#define bufferChar(c) ( \
|
|
||||||
(buffer ? (buffer[j++] = c) : (j++)), \
|
|
||||||
assertTrue(buffer ? j < buffSize : true, "Buffer overflow") \
|
|
||||||
)
|
|
||||||
|
|
||||||
while((c = val[i++]) != '\0') {
|
|
||||||
if(c == '{' && val[i] == '{') {
|
|
||||||
goto startBraces;
|
|
||||||
} else if(c == '}' && val[i] == '}') {
|
|
||||||
goto endBraces;
|
|
||||||
} else if(inBraces) {
|
|
||||||
goto braceBuffering;
|
|
||||||
} else {
|
|
||||||
goto character;
|
|
||||||
}
|
|
||||||
|
|
||||||
character: {
|
|
||||||
bufferChar(c);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
braceBuffering: {
|
|
||||||
assertFalse(val[i] == '\0', "Unexpected end of string.");
|
|
||||||
braceBuffer[k++] = c;
|
|
||||||
assertTrue(k < sizeof(braceBuffer), "Brace buffer overflow");
|
|
||||||
if(val[i] == ' ') i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
startBraces: {
|
|
||||||
assertFalse(inBraces, "Nested braces are not allowed");
|
|
||||||
|
|
||||||
inBraces = true;
|
|
||||||
i++;
|
|
||||||
k = 0;
|
|
||||||
assertFalse(val[i] == '\0', "Unexpected end of string.");
|
|
||||||
if(val[i] == ' ') i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
endBraces: {
|
|
||||||
assertTrue(inBraces, "Unmatched closing brace found");
|
|
||||||
|
|
||||||
inBraces = false;
|
|
||||||
i++;
|
|
||||||
braceBuffer[k] = '\0';
|
|
||||||
|
|
||||||
uint16_t l;
|
|
||||||
for(l = 0; l < valueCount; l++) {
|
|
||||||
if(strcmp(braceBuffer, keys[l]) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const char_t *replacement = values[l];
|
|
||||||
|
|
||||||
uint16_t r = 0;
|
|
||||||
while((c = replacement[r++]) != '\0') {
|
|
||||||
bufferChar(c);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
assertTrue(l < valueCount, "No string replacement found!");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(buffer){
|
|
||||||
assertTrue(j < buffSize, "Buffer overflow");
|
|
||||||
buffer[j] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
assertFalse(inBraces, "Unmatched opening brace found");
|
|
||||||
return j;
|
|
||||||
}
|
|
@@ -1,65 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "locale/language/languages.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t current;
|
|
||||||
} language_t;
|
|
||||||
|
|
||||||
extern language_t LANGUAGE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the language system.
|
|
||||||
*
|
|
||||||
* This function sets up the language system, loading the default language
|
|
||||||
* and preparing any necessary resources for language handling.
|
|
||||||
*/
|
|
||||||
void languageInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a language string by its key.
|
|
||||||
*
|
|
||||||
* @param key The key for the language string.
|
|
||||||
* @return The language string associated with the key.
|
|
||||||
*/
|
|
||||||
const char_t * languageGet(const char_t *key);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the length of a language string by its key.
|
|
||||||
*
|
|
||||||
* @param key The key for the language string.
|
|
||||||
* @return The length of the language string associated with the key.
|
|
||||||
*/
|
|
||||||
uint16_t langaugeGetLength(const char_t *key);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats a language string with given keys and values.
|
|
||||||
*
|
|
||||||
* This function replaces placeholders in the language string with the provided
|
|
||||||
* values based on the keys.
|
|
||||||
*
|
|
||||||
* If buffer is NULL, the function will instead calculate the length of the
|
|
||||||
* formatted string.
|
|
||||||
*
|
|
||||||
* @param key The key for the language string to format.
|
|
||||||
* @param buffer The buffer to store the formatted string.
|
|
||||||
* @param buffSize The size of the buffer.
|
|
||||||
* @param keys An array of keys to replace in the language string.
|
|
||||||
* @param values An array of values corresponding to the keys.
|
|
||||||
* @param valueCount The number of key-value pairs.
|
|
||||||
* @return The number of characters written to the buffer.
|
|
||||||
*/
|
|
||||||
uint16_t languageFormat(
|
|
||||||
const char_t *key,
|
|
||||||
char_t *buffer,
|
|
||||||
uint16_t buffSize,
|
|
||||||
const char_t **keys,
|
|
||||||
const char_t **values,
|
|
||||||
const uint16_t valueCount
|
|
||||||
);
|
|
@@ -1,37 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "time.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
dusktime_t TIME;
|
|
||||||
|
|
||||||
void timeInit(void) {
|
|
||||||
memoryZero(&TIME, sizeof(TIME));
|
|
||||||
|
|
||||||
// Set these to something non-zero.
|
|
||||||
TIME.lastTick = TIME_STEP;
|
|
||||||
TIME.delta = TIME.realDelta = TIME_STEP;
|
|
||||||
TIME.realTime = TIME.time = TIME_STEP * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
void timeUpdate(void) {
|
|
||||||
TIME.realDelta = timeDeltaGet();
|
|
||||||
|
|
||||||
#if TIME_DYNAMIC
|
|
||||||
TIME.delta = TIME.realDelta;
|
|
||||||
#else
|
|
||||||
TIME.delta = TIME_PLATFORM_STEP;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
assertTrue(TIME.delta >= 0.0f, "Time delta is negative");
|
|
||||||
assertTrue(TIME.realDelta >= 0.0f, "Real time delta is negative");
|
|
||||||
|
|
||||||
TIME.time += TIME.delta;
|
|
||||||
TIME.realTime += TIME.realDelta;
|
|
||||||
}
|
|
@@ -1,52 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
float_t delta;
|
|
||||||
float_t lastTick;
|
|
||||||
float_t time;
|
|
||||||
float_t realDelta;
|
|
||||||
float_t realTime;
|
|
||||||
} dusktime_t;
|
|
||||||
|
|
||||||
extern dusktime_t TIME;
|
|
||||||
|
|
||||||
#define TIME_STEP (1.0f / 60.0f) // Default to 60FPS
|
|
||||||
|
|
||||||
#ifndef TIME_DYNAMIC
|
|
||||||
#define TIME_DYNAMIC 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TIME_DYNAMIC == 0
|
|
||||||
#ifndef TIME_PLATFORM_STEP
|
|
||||||
#define TIME_PLATFORM_STEP TIME_STEP
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the time system.
|
|
||||||
*/
|
|
||||||
void timeInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the time system
|
|
||||||
*/
|
|
||||||
void timeUpdate(void);
|
|
||||||
|
|
||||||
#if TIME_DYNAMIC == 1
|
|
||||||
/**
|
|
||||||
* Gets the time delta since the last frame, in seconds. Tied to the
|
|
||||||
* platform.
|
|
||||||
*
|
|
||||||
* This will only get called once per gameUpdate.
|
|
||||||
*/
|
|
||||||
float_t timeDeltaGet(void);
|
|
||||||
#endif
|
|
||||||
|
|
@@ -1,10 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
uitextbox.c
|
|
||||||
)
|
|
@@ -1,9 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "ui/fontdata.h"
|
|
@@ -1,191 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "uitextbox.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "input.h"
|
|
||||||
|
|
||||||
uitextbox_t UI_TEXTBOX;
|
|
||||||
|
|
||||||
void uiTextboxInit() {
|
|
||||||
memoryZero(&UI_TEXTBOX, sizeof(uitextbox_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiTextboxUpdate() {
|
|
||||||
if(UI_TEXTBOX.visible == false) return;
|
|
||||||
|
|
||||||
if(UI_TEXTBOX.charsRevealed < UI_TEXTBOX.pageChars[UI_TEXTBOX.page]) {
|
|
||||||
UI_TEXTBOX.charsRevealed++;
|
|
||||||
if(inputIsDown(INPUT_BIND_ACTION)) {
|
|
||||||
UI_TEXTBOX.charsRevealed++;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if(inputPressed(INPUT_BIND_ACTION)) {
|
|
||||||
if(UI_TEXTBOX.page < UI_TEXTBOX.pageCount - 1) {
|
|
||||||
UI_TEXTBOX.page++;
|
|
||||||
UI_TEXTBOX.charsRevealed = 0;
|
|
||||||
} else {
|
|
||||||
// Close the textbox
|
|
||||||
UI_TEXTBOX.visible = false;
|
|
||||||
UI_TEXTBOX.page = 0;
|
|
||||||
UI_TEXTBOX.charsRevealed = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiTextboxSetText(const char_t *text) {
|
|
||||||
assertNotNull(text, "Text pointer cannot be NULL, call uiTextboxClose()");
|
|
||||||
|
|
||||||
memoryZero(UI_TEXTBOX.text, sizeof(UI_TEXTBOX.text));
|
|
||||||
memoryZero(UI_TEXTBOX.lineLengths, sizeof(UI_TEXTBOX.lineLengths));
|
|
||||||
memoryZero(UI_TEXTBOX.pageChars, sizeof(UI_TEXTBOX.pageChars));
|
|
||||||
UI_TEXTBOX.pageCount = 1;// Always at least one page.
|
|
||||||
UI_TEXTBOX.totalChars = 0;
|
|
||||||
UI_TEXTBOX.page = 0;
|
|
||||||
UI_TEXTBOX.charsRevealed = 0;
|
|
||||||
UI_TEXTBOX.visible = true;
|
|
||||||
|
|
||||||
char_t c;// current char
|
|
||||||
uint16_t i = 0;// Index of character we are pulling
|
|
||||||
uint16_t lastWordStart = 0;// Index of the last word start (in src string).
|
|
||||||
uint8_t line = 0;// Which line we are currently writing to.
|
|
||||||
uint8_t page = 0;
|
|
||||||
bool_t startOfLine = true;// Are we at the start of a line?
|
|
||||||
|
|
||||||
while((c = text[i++]) != '\0') {
|
|
||||||
// HARD disallowed characters.
|
|
||||||
assertTrue(c != '\r', "Carriage return characters not allowed.");
|
|
||||||
assertTrue(c != '\t', "Tab characters not allowed.");
|
|
||||||
|
|
||||||
// Is this the beginning of a new line?
|
|
||||||
if(startOfLine) {
|
|
||||||
// Yes, we are at the start of a new line.
|
|
||||||
startOfLine = false;
|
|
||||||
// Is this the first line?
|
|
||||||
if(line == 0) {
|
|
||||||
// nothing to do, just continue.
|
|
||||||
} else {
|
|
||||||
// Yes, start of new line. Is this line the first line on a new page?
|
|
||||||
if((line % UI_TEXTBOX_LINES_PER_PAGE) == 0) {
|
|
||||||
// Yes, start a new page.
|
|
||||||
i--;// Rewind so that this character can go through the loop again.
|
|
||||||
goto newPage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change what we do depending on the character.
|
|
||||||
if(c == '\n') {
|
|
||||||
goto newline;
|
|
||||||
} else if(c == ' ') {
|
|
||||||
goto whitespace;
|
|
||||||
} else {
|
|
||||||
goto character;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle whitespace characters (not newlines)
|
|
||||||
whitespace: {
|
|
||||||
// Is this whitespace the last char of the line?
|
|
||||||
if(UI_TEXTBOX.lineLengths[line] == UI_TEXTBOX_CHARS_PER_LINE) {
|
|
||||||
goto newline;
|
|
||||||
} else if(UI_TEXTBOX.lineLengths[line] == 0) {
|
|
||||||
// If this is the first character of the line, we can just ignore it.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastWordStart = i;
|
|
||||||
goto appendCharacter;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle regular characters
|
|
||||||
character: {
|
|
||||||
// Is this character going to cause a wrap to occur?
|
|
||||||
if(UI_TEXTBOX.lineLengths[line] == UI_TEXTBOX_CHARS_PER_LINE) {
|
|
||||||
// How long ago was the last whitespace?
|
|
||||||
uint16_t charsSinceLastSpace = i - lastWordStart;
|
|
||||||
|
|
||||||
// Is the word longer than a line can possibly hold?
|
|
||||||
assertTrue(
|
|
||||||
charsSinceLastSpace < UI_TEXTBOX_CHARS_PER_LINE,
|
|
||||||
"Word longer than a line can hold."
|
|
||||||
);
|
|
||||||
|
|
||||||
// Undo appending of all characters since the last whitespace.
|
|
||||||
UI_TEXTBOX.totalChars -= charsSinceLastSpace;
|
|
||||||
UI_TEXTBOX.lineLengths[line] -= charsSinceLastSpace;
|
|
||||||
UI_TEXTBOX.pageChars[page] -= charsSinceLastSpace;
|
|
||||||
|
|
||||||
// Rewind the loop so that printing will begin on the new line at the
|
|
||||||
// start of the last word.
|
|
||||||
i -= charsSinceLastSpace;
|
|
||||||
|
|
||||||
// Newline.
|
|
||||||
goto newline;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append the character to the textbox.
|
|
||||||
goto appendCharacter;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle newlines
|
|
||||||
newline: {
|
|
||||||
// Ensure we don't exceed the maximum number of lines.
|
|
||||||
assertTrue(
|
|
||||||
line < UI_TEXTBOX_LINE_COUNT,
|
|
||||||
"Exceeded maximum number of lines in textbox."
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add a line to the textbox.
|
|
||||||
line++;
|
|
||||||
startOfLine = true;// Next iteration will be a start of line.
|
|
||||||
lastWordStart = i;// We also mark this as the last word start.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
newPage: {
|
|
||||||
// Make sure we don't exceed the maximum number of pages.
|
|
||||||
assertTrue(
|
|
||||||
UI_TEXTBOX.pageCount < UI_TEXTBOX_PAGE_COUNT_MAX,
|
|
||||||
"Exceeded maximum number of pages in textbox."
|
|
||||||
);
|
|
||||||
UI_TEXTBOX.pageCount++;
|
|
||||||
page++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
appendCharacter: {
|
|
||||||
assertTrue(
|
|
||||||
UI_TEXTBOX.totalChars < UI_TEXTBOX_CHARS_MAX,
|
|
||||||
"Exceeded maximum number of characters in textbox."
|
|
||||||
);
|
|
||||||
assertTrue(
|
|
||||||
line < UI_TEXTBOX_LINE_COUNT,
|
|
||||||
"Exceeded maximum number of lines in textbox."
|
|
||||||
);
|
|
||||||
assertTrue(
|
|
||||||
UI_TEXTBOX.lineLengths[line] < UI_TEXTBOX_CHARS_PER_LINE,
|
|
||||||
"Exceeded maximum number of chars per line in textbox."
|
|
||||||
);
|
|
||||||
|
|
||||||
// Push the character to the textbox.
|
|
||||||
UI_TEXTBOX.text[
|
|
||||||
line * UI_TEXTBOX_CHARS_PER_LINE + UI_TEXTBOX.lineLengths[line]
|
|
||||||
] = c;
|
|
||||||
|
|
||||||
// Increment the line length and page character count.
|
|
||||||
UI_TEXTBOX.totalChars++;
|
|
||||||
UI_TEXTBOX.lineLengths[line]++;
|
|
||||||
UI_TEXTBOX.pageChars[page]++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
assertUnreachable("Code should not reach here, all cases handled.");
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,77 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "display/renderbase.h"
|
|
||||||
#include "ui/font.h"
|
|
||||||
|
|
||||||
#define UI_TEXTBOX_LINES_PER_PAGE 4
|
|
||||||
#define UI_TEXTBOX_WIDTH RENDER_WIDTH
|
|
||||||
#define UI_TEXTBOX_HEIGHT_INNER ( \
|
|
||||||
FONT_TILE_HEIGHT * UI_TEXTBOX_LINES_PER_PAGE \
|
|
||||||
)
|
|
||||||
|
|
||||||
#define UI_TEXTBOX_BORDER_WIDTH 4
|
|
||||||
#define UI_TEXTBOX_BORDER_HEIGHT UI_TEXTBOX_BORDER_WIDTH
|
|
||||||
#define UI_TEXTBOX_PADDING_X 2
|
|
||||||
#define UI_TEXTBOX_PADDING_Y UI_TEXTBOX_PADDING_X
|
|
||||||
#define UI_TEXTBOX_WIDTH_INNER ( \
|
|
||||||
UI_TEXTBOX_WIDTH - (UI_TEXTBOX_BORDER_WIDTH * 2) - \
|
|
||||||
(UI_TEXTBOX_PADDING_X * 2) \
|
|
||||||
)
|
|
||||||
#define UI_TEXTBOX_HEIGHT ( \
|
|
||||||
UI_TEXTBOX_HEIGHT_INNER + (UI_TEXTBOX_BORDER_HEIGHT * 2) + \
|
|
||||||
(UI_TEXTBOX_PADDING_Y * 2) \
|
|
||||||
)
|
|
||||||
|
|
||||||
#define UI_TEXTBOX_CHARS_PER_LINE (UI_TEXTBOX_WIDTH_INNER / FONT_TILE_WIDTH)
|
|
||||||
#define UI_TEXTBOX_CHARS_PER_PAGE ( \
|
|
||||||
UI_TEXTBOX_CHARS_PER_LINE * UI_TEXTBOX_LINES_PER_PAGE \
|
|
||||||
)
|
|
||||||
#define UI_TEXTBOX_PAGE_COUNT_MAX 6
|
|
||||||
#define UI_TEXTBOX_LINE_COUNT ( \
|
|
||||||
UI_TEXTBOX_LINES_PER_PAGE * UI_TEXTBOX_PAGE_COUNT_MAX \
|
|
||||||
)
|
|
||||||
#define UI_TEXTBOX_CHARS_MAX ( \
|
|
||||||
UI_TEXTBOX_CHARS_PER_PAGE * UI_TEXTBOX_PAGE_COUNT_MAX \
|
|
||||||
)
|
|
||||||
|
|
||||||
#define UI_TEXTBOX_REVEAL_RATE 2
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char_t text[UI_TEXTBOX_CHARS_MAX];
|
|
||||||
uint8_t lineLengths[UI_TEXTBOX_LINE_COUNT];
|
|
||||||
uint8_t pageCount;
|
|
||||||
uint8_t pageChars[UI_TEXTBOX_PAGE_COUNT_MAX];
|
|
||||||
uint16_t totalChars;
|
|
||||||
|
|
||||||
uint8_t page;
|
|
||||||
uint8_t charsRevealed;
|
|
||||||
|
|
||||||
bool_t visible;
|
|
||||||
} uitextbox_t;
|
|
||||||
|
|
||||||
extern uitextbox_t UI_TEXTBOX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the UI textbox.
|
|
||||||
*/
|
|
||||||
void uiTextboxInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the UI textbox, handling text input and scrolling.
|
|
||||||
*/
|
|
||||||
void uiTextboxUpdate(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the text for the UI textbox.
|
|
||||||
*
|
|
||||||
* @param text The text to display in the textbox.
|
|
||||||
*/
|
|
||||||
void uiTextboxSetText(
|
|
||||||
const char_t *text
|
|
||||||
);
|
|
@@ -1,12 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
memory.c
|
|
||||||
string.c
|
|
||||||
math.c
|
|
||||||
)
|
|
@@ -1,19 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "math.h"
|
|
||||||
|
|
||||||
uint32_t mathNextPowTwo(uint32_t value) {
|
|
||||||
if (value == 0) return 1; // Handle zero case
|
|
||||||
value--;
|
|
||||||
value |= value >> 1;
|
|
||||||
value |= value >> 2;
|
|
||||||
value |= value >> 4;
|
|
||||||
value |= value >> 8;
|
|
||||||
value |= value >> 16;
|
|
||||||
return value + 1;
|
|
||||||
}
|
|
@@ -1,17 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the next power of two greater than or equal to the given value.
|
|
||||||
*
|
|
||||||
* @param value The value to find the next power of two for.
|
|
||||||
* @return The next power of two greater than or equal to the value.
|
|
||||||
*/
|
|
||||||
uint32_t mathNextPowTwo(uint32_t value);
|
|
@@ -1,96 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "memory.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
void * memoryAllocate(const size_t size) {
|
|
||||||
assertTrue(size > 0, "Cannot allocate 0 bytes of memory.");
|
|
||||||
void *ptr = malloc(size);
|
|
||||||
assertNotNull(ptr, "Memory allocation failed.");
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void memoryFree(void *ptr) {
|
|
||||||
assertNotNull(ptr, "Cannot free NULL memory.");
|
|
||||||
free(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void memoryCopy(void *dest, const void *src, const size_t size) {
|
|
||||||
assertNotNull(dest, "Cannot copy to NULL memory.");
|
|
||||||
assertNotNull(src, "Cannot copy from NULL memory.");
|
|
||||||
assertTrue(size > 0, "Cannot copy 0 bytes of memory.");
|
|
||||||
assertTrue(dest != src, "Cannot copy memory to itself.");
|
|
||||||
memcpy(dest, src, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void memorySet(void *dest, const uint8_t value, const size_t size) {
|
|
||||||
assertNotNull(dest, "Cannot set NULL memory.");
|
|
||||||
assertTrue(size > 0, "Cannot set 0 bytes of memory.");
|
|
||||||
memset(dest, value, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void memoryZero(void *dest, const size_t size) {
|
|
||||||
memorySet(dest, 0, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void memoryCopyRangeSafe(
|
|
||||||
void *dest,
|
|
||||||
const void *start,
|
|
||||||
const void *end,
|
|
||||||
const size_t sizeMax
|
|
||||||
) {
|
|
||||||
assertFalse(start == end, "Start and end pointers are the same.");
|
|
||||||
assertTrue(end > start, "End pointer is not after start pointer.");
|
|
||||||
|
|
||||||
size_t copy = (size_t)end - (size_t)start;
|
|
||||||
assertTrue(copy <= sizeMax, "Size of memory to copy is too large.");
|
|
||||||
memoryCopy(dest, start, copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
void memoryMove(void *dest, const void *src, const size_t size) {
|
|
||||||
assertNotNull(dest, "Cannot move to NULL memory.");
|
|
||||||
assertNotNull(src, "Cannot move from NULL memory.");
|
|
||||||
assertTrue(size > 0, "Cannot move 0 bytes of memory.");
|
|
||||||
assertTrue(dest != src, "Cannot move memory to itself.");
|
|
||||||
memmove(dest, src, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
int_t memoryCompare(
|
|
||||||
const void *a,
|
|
||||||
const void *b,
|
|
||||||
const size_t size
|
|
||||||
) {
|
|
||||||
assertNotNull(a, "Cannot compare NULL memory.");
|
|
||||||
assertNotNull(b, "Cannot compare NULL memory.");
|
|
||||||
assertTrue(size > 0, "Cannot compare 0 bytes of memory.");
|
|
||||||
return memcmp(a, b, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void memoryReallocate(void **ptr, const size_t size) {
|
|
||||||
assertNotNull(ptr, "Cannot reallocate NULL pointer.");
|
|
||||||
assertTrue(size > 0, "Cannot reallocate to 0 bytes of memory.");
|
|
||||||
void *newPointer = memoryAllocate(size);
|
|
||||||
assertNotNull(newPointer, "Memory reallocation failed.");
|
|
||||||
memoryFree(*ptr);
|
|
||||||
*ptr = newPointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void memoryResize(void **ptr, const size_t oldSize, const size_t newSize) {
|
|
||||||
assertNotNull(ptr, "Cannot resize NULL pointer.");
|
|
||||||
if(newSize == oldSize) return;
|
|
||||||
if(oldSize == 0) return memoryReallocate(ptr, newSize);
|
|
||||||
|
|
||||||
assertTrue(newSize > oldSize, "New size must be greater than old size.");
|
|
||||||
|
|
||||||
void *newPointer = memoryAllocate(newSize);
|
|
||||||
assertNotNull(newPointer, "Memory resizing failed.");
|
|
||||||
memoryCopy(newPointer, *ptr, oldSize);
|
|
||||||
memoryFree(*ptr);
|
|
||||||
*ptr = newPointer;
|
|
||||||
}
|
|
@@ -1,107 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allocates memory.
|
|
||||||
*
|
|
||||||
* @param size The size of the memory to allocate.
|
|
||||||
* @return The allocated memory.
|
|
||||||
*/
|
|
||||||
void * memoryAllocate(const size_t size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Frees memory.
|
|
||||||
*
|
|
||||||
* @param ptr The memory to free.
|
|
||||||
*/
|
|
||||||
void memoryFree(void *ptr);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies memory.
|
|
||||||
*
|
|
||||||
* @param dest The destination to copy to.
|
|
||||||
* @param src The source to copy from.
|
|
||||||
* @param size The size of the memory to copy.
|
|
||||||
*/
|
|
||||||
void memoryCopy(void *dest, const void *src, const size_t size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets memory.
|
|
||||||
*
|
|
||||||
* @param dest The destination to set.
|
|
||||||
* @param value The value to set.
|
|
||||||
* @param size The size of the memory to set.
|
|
||||||
*/
|
|
||||||
void memorySet(void *dest, const uint8_t value, const size_t size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Zeroes memory.
|
|
||||||
*
|
|
||||||
* @param dest The destination to zero.
|
|
||||||
* @param size The size of the memory to zero.
|
|
||||||
*/
|
|
||||||
void memoryZero(void *dest, const size_t size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies memory, ensuring that the memory range is as expected, typically this
|
|
||||||
* is done if you're trying to reshape data in a buffer. Extremely useful in
|
|
||||||
* copying data to a shader buffer.
|
|
||||||
*
|
|
||||||
* @param dest The destination to copy to.
|
|
||||||
* @param start The start of the source to copy from.
|
|
||||||
* @param end The end of the source to copy from.
|
|
||||||
* @param sizeMax The maximum size of the memory to copy.
|
|
||||||
*/
|
|
||||||
void memoryCopyRangeSafe(
|
|
||||||
void *dest,
|
|
||||||
const void *start,
|
|
||||||
const void *end,
|
|
||||||
const size_t sizeMax
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves memory.
|
|
||||||
*
|
|
||||||
* @param dest The destination to move to.
|
|
||||||
* @param src The source to move from.
|
|
||||||
* @param size The size of the memory to move.
|
|
||||||
*/
|
|
||||||
void memoryMove(void *dest, const void *src, const size_t size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares memory.
|
|
||||||
*
|
|
||||||
* @param a The first memory to compare.
|
|
||||||
* @param b The second memory to compare.
|
|
||||||
* @param size The size of the memory to compare.
|
|
||||||
* @return 0 if the memory is equal, < 0 if a < b, > 0 if a > b.
|
|
||||||
*/
|
|
||||||
int_t memoryCompare(
|
|
||||||
const void *a,
|
|
||||||
const void *b,
|
|
||||||
const size_t size
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reallocates memory.
|
|
||||||
*
|
|
||||||
* @param ptr The pointer to the memory to reallocate.
|
|
||||||
* @param size The new size of the memory.
|
|
||||||
*/
|
|
||||||
void memoryReallocate(void **ptr, const size_t size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reallocates memory, but copies existing data to the new memory.
|
|
||||||
*
|
|
||||||
* @param ptr The pointer to the memory to resize.
|
|
||||||
* @param oldSize The old size of the memory.
|
|
||||||
* @param newSize The new size of the memory.
|
|
||||||
*/
|
|
||||||
void memoryResize(void **ptr, const size_t oldSize, const size_t newSize);
|
|
@@ -1,128 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "string.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
|
|
||||||
bool_t stringIsWhitespace(const char_t c) {
|
|
||||||
return isspace(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void stringCopy(char_t *dest, const char_t *src, const size_t destSize) {
|
|
||||||
assertNotNull(dest, "dest must not be NULL");
|
|
||||||
assertNotNull(src, "src must not be NULL");
|
|
||||||
assertTrue(destSize > 0, "destSize must be greater than 0");
|
|
||||||
assertStrLenMax(src, destSize, "src is too long");
|
|
||||||
memoryCopy(dest, src, strlen(src) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stringCompare(const char_t *str1, const char_t *str2) {
|
|
||||||
assertNotNull(str1, "str1 must not be NULL");
|
|
||||||
assertNotNull(str2, "str2 must not be NULL");
|
|
||||||
return strcmp(str1, str2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void stringTrim(char_t *str) {
|
|
||||||
assertNotNull(str, "str must not be NULL");
|
|
||||||
|
|
||||||
// Trim leading whitespace
|
|
||||||
char_t *start = str;
|
|
||||||
while(stringIsWhitespace(*start)) start++;
|
|
||||||
|
|
||||||
// Trim trailing whitespace
|
|
||||||
char_t *end = start + strlen(start) - 1;
|
|
||||||
while (end >= start && stringIsWhitespace(*end)) end--;
|
|
||||||
|
|
||||||
// Null-terminate the string
|
|
||||||
*(end + 1) = '\0';
|
|
||||||
|
|
||||||
// Move trimmed string to the original buffer
|
|
||||||
if (start != str) memmove(str, start, end - start + 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
char_t * stringToken(char_t *str, const char_t *delim) {
|
|
||||||
assertNotNull(str, "str must not be NULL");
|
|
||||||
assertNotNull(delim, "delim must not be NULL");
|
|
||||||
return strtok(str, delim);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t stringFormat(
|
|
||||||
char_t *dest,
|
|
||||||
const size_t destSize,
|
|
||||||
const char_t *format,
|
|
||||||
...
|
|
||||||
) {
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
int32_t len = stringFormatVA(dest, destSize, format, args);
|
|
||||||
va_end(args);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t stringFormatVA(
|
|
||||||
char_t *dest,
|
|
||||||
const size_t destSize,
|
|
||||||
const char_t *format,
|
|
||||||
const va_list args
|
|
||||||
) {
|
|
||||||
assertNotNull(format, "format must not be NULL");
|
|
||||||
|
|
||||||
if(dest == NULL) {
|
|
||||||
int32_t ret = vsnprintf(NULL, 0, format, args);
|
|
||||||
assertTrue(ret >= 0, "Failed to format string.");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTrue(destSize > 0, "destSize must be greater than 0");
|
|
||||||
int32_t ret = vsnprintf(dest, destSize, format, args);
|
|
||||||
assertTrue(ret >= 0, "Failed to format string.");
|
|
||||||
assertTrue(ret < destSize, "Formatted string is too long.");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t stringToI32(const char_t *str, int32_t *out) {
|
|
||||||
assertNotNull(str, "str must not be NULL");
|
|
||||||
assertNotNull(out, "out must not be NULL");
|
|
||||||
|
|
||||||
char_t *endptr;
|
|
||||||
errno = 0;
|
|
||||||
long int result = strtol(str, &endptr, 10);
|
|
||||||
if (errno != 0 || *endptr != '\0') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*out = (int32_t)result;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t stringToI64(const char_t *str, int64_t *out) {
|
|
||||||
assertNotNull(str, "str must not be NULL");
|
|
||||||
assertNotNull(out, "out must not be NULL");
|
|
||||||
|
|
||||||
char_t *endptr;
|
|
||||||
errno = 0;
|
|
||||||
long long int result = strtoll(str, &endptr, 10);
|
|
||||||
if (errno != 0 || *endptr != '\0') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*out = (int64_t)result;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t stringToU16(const char_t *str, uint16_t *out) {
|
|
||||||
assertNotNull(str, "str must not be NULL");
|
|
||||||
assertNotNull(out, "out must not be NULL");
|
|
||||||
|
|
||||||
char_t *endptr;
|
|
||||||
errno = 0;
|
|
||||||
unsigned long int result = strtoul(str, &endptr, 10);
|
|
||||||
if (errno != 0 || *endptr != '\0' || result > UINT16_MAX) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*out = (uint16_t)result;
|
|
||||||
return true;
|
|
||||||
}
|
|
@@ -1,118 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if a character is whitespace.
|
|
||||||
*
|
|
||||||
* @param c The character to check.
|
|
||||||
* @return TRUE if the character is whitespace, FALSE otherwise.
|
|
||||||
*/
|
|
||||||
bool_t stringIsWhitespace(const char_t c);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies a string from src to dest, ensuring the dest string is null-terminated
|
|
||||||
* and does not exceed the specified size.
|
|
||||||
*
|
|
||||||
* @param dest The destination string.
|
|
||||||
* @param src The source string.
|
|
||||||
* @param destSize The size of the destination string exc. null terminator.
|
|
||||||
*/
|
|
||||||
void stringCopy(char_t *dest, const char_t *src, const size_t destSize);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares two strings.
|
|
||||||
*
|
|
||||||
* @param str1 The first string.
|
|
||||||
* @param str2 The second string.
|
|
||||||
* @return 0 if the strings are equal, -1 if str1 is less than str2, 1 if str1
|
|
||||||
* is greater than str2.
|
|
||||||
*/
|
|
||||||
int stringCompare(const char_t *str1, const char_t *str2);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Trims whitespace from the beginning and end of a string.
|
|
||||||
*
|
|
||||||
* @param str The string to trim.
|
|
||||||
*/
|
|
||||||
void stringTrim(char_t *str);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the next token in a string using a delimiter.
|
|
||||||
* e.g. input: "Hello, World, Happy Monday!" with stringToken(input, ",") will
|
|
||||||
* return "Hello" then " World" then " Happy Monday!" on each subsequent call.
|
|
||||||
*
|
|
||||||
* @param str The string to split.
|
|
||||||
* @param delim The delimiter to split by.
|
|
||||||
* @return A pointer to the next token in the string.
|
|
||||||
*/
|
|
||||||
char_t * stringToken(char_t *str, const char_t *delim);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats a string.
|
|
||||||
*
|
|
||||||
* @param dest The destination string, or NULL to get the length of the
|
|
||||||
* formatted string.
|
|
||||||
* @param destSize The size of the destination string exc. null terminator, can
|
|
||||||
* be anything if dest is NULL.
|
|
||||||
* @param format The format string.
|
|
||||||
* @param ... The arguments to format.
|
|
||||||
* @return The number of characters written.
|
|
||||||
*/
|
|
||||||
int32_t stringFormat(
|
|
||||||
char_t *dest,
|
|
||||||
const size_t destSize,
|
|
||||||
const char_t *format,
|
|
||||||
...
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats a string using a va_list.
|
|
||||||
*
|
|
||||||
* @param dest The destination string, or NULL to get the length of the
|
|
||||||
* formatted string.
|
|
||||||
* @param destSize The size of the destination string exc. null terminator, can
|
|
||||||
* be anything if dest is NULL.
|
|
||||||
* @param format The format string.
|
|
||||||
* @param args The va_list of arguments.
|
|
||||||
* @return The number of characters written.
|
|
||||||
*/
|
|
||||||
int32_t stringFormatVA(
|
|
||||||
char_t *dest,
|
|
||||||
const size_t destSize,
|
|
||||||
const char_t *format,
|
|
||||||
const va_list args
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a string to an integer.
|
|
||||||
*
|
|
||||||
* @param str The string to convert.
|
|
||||||
* @param out The output integer.
|
|
||||||
* @return TRUE if the conversion was successful, FALSE otherwise.
|
|
||||||
*/
|
|
||||||
bool_t stringToI32(const char_t *str, int32_t *out);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a string to a signed 16-bit integer.
|
|
||||||
*
|
|
||||||
* @param str The string to convert.
|
|
||||||
* @param out The output signed integer.
|
|
||||||
* @return TRUE if the conversion was successful, FALSE otherwise.
|
|
||||||
*/
|
|
||||||
bool_t stringToI16(const char_t *str, int16_t *out);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a string to an unsigned 16-bit integer.
|
|
||||||
*
|
|
||||||
* @param str The string to convert.
|
|
||||||
* @param out The output unsigned integer.
|
|
||||||
* @return TRUE if the conversion was successful, FALSE otherwise.
|
|
||||||
*/
|
|
||||||
bool_t stringToU16(const char_t *str, uint16_t *out);
|
|
@@ -1,321 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "chunk.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "world/world.h"
|
|
||||||
|
|
||||||
void renderChunkUpdated(chunk_t *chunk);
|
|
||||||
|
|
||||||
chunkmap_t CHUNK_MAP;
|
|
||||||
|
|
||||||
void chunkMapInit() {
|
|
||||||
memoryZero(&CHUNK_MAP, sizeof(chunkmap_t));
|
|
||||||
|
|
||||||
// Load default chunks, YX order.
|
|
||||||
uint16_t i = 0;
|
|
||||||
chunk_t *chunk;
|
|
||||||
for(uint8_t y = 0; y < CHUNK_MAP_HEIGHT; y++) {
|
|
||||||
for(uint8_t x = 0; x < CHUNK_MAP_WIDTH; x++) {
|
|
||||||
assertTrue(i < CHUNK_MAP_COUNT, "Chunk index out of bounds");
|
|
||||||
|
|
||||||
chunk = CHUNK_MAP.chunks + i;
|
|
||||||
CHUNK_MAP.chunkOrder[i] = chunk;
|
|
||||||
|
|
||||||
chunkLoad(chunk, x, y);
|
|
||||||
assertTrue(
|
|
||||||
chunk->x == x && chunk->y == y,
|
|
||||||
"Chunk coordinates do not match expected values"
|
|
||||||
);
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void chunkMapShift(const int16_t x, const int16_t y) {
|
|
||||||
if(x == 0 && y == 0) assertUnreachable("ChunkMapShift called with no shift");
|
|
||||||
|
|
||||||
chunk_t *newChunkOrder[CHUNK_MAP_COUNT];
|
|
||||||
chunk_t *unloadedChunks[CHUNK_MAP_COUNT];
|
|
||||||
chunk_t *chunk;
|
|
||||||
uint8_t i, j;
|
|
||||||
uint16_t
|
|
||||||
/** New Map Coordinates */
|
|
||||||
newX, newY,
|
|
||||||
newChunkX, newChunkY
|
|
||||||
;
|
|
||||||
|
|
||||||
// Calculate the new map coordinates
|
|
||||||
newX = CHUNK_MAP.topLeftX + x;
|
|
||||||
newY = CHUNK_MAP.topLeftY + y;
|
|
||||||
|
|
||||||
// Zero the new chunk order
|
|
||||||
memoryZero(newChunkOrder, sizeof(newChunkOrder));
|
|
||||||
|
|
||||||
// For each chunk...
|
|
||||||
j = 0;
|
|
||||||
chunk = CHUNK_MAP.chunks;
|
|
||||||
do {
|
|
||||||
// Is this chunk still going to be within the map bounds?
|
|
||||||
if(
|
|
||||||
chunk->x < newX || chunk->y < newY ||
|
|
||||||
chunk->x >= newX + CHUNK_MAP_WIDTH ||
|
|
||||||
chunk->y >= newY + CHUNK_MAP_HEIGHT
|
|
||||||
) {
|
|
||||||
// No, it's not, let's unload it and make it available for reuse.
|
|
||||||
chunkUnload(chunk);
|
|
||||||
assertTrue(
|
|
||||||
j < CHUNK_MAP_COUNT,
|
|
||||||
"Unloaded chunk index out of bounds"
|
|
||||||
);
|
|
||||||
unloadedChunks[j++] = chunk;
|
|
||||||
chunk++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Yes it is still valid, determine the new index that it will be at
|
|
||||||
i = (chunk->y - newY) * CHUNK_MAP_WIDTH + (chunk->x - newX);
|
|
||||||
assertTrue(
|
|
||||||
i < CHUNK_MAP_COUNT,
|
|
||||||
"Chunk index out of bounds after shifting"
|
|
||||||
);
|
|
||||||
assertNull(
|
|
||||||
newChunkOrder[i],
|
|
||||||
"New chunk order index is already occupied"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set the new chunk order
|
|
||||||
newChunkOrder[i] = chunk;
|
|
||||||
chunk++;
|
|
||||||
} while(chunk < CHUNK_MAP.chunks + CHUNK_MAP_COUNT);
|
|
||||||
|
|
||||||
// Now check the new chunk order list for missing chunks.
|
|
||||||
i = 0;
|
|
||||||
do {
|
|
||||||
assertTrue(
|
|
||||||
i < CHUNK_MAP_COUNT,
|
|
||||||
"New chunk order index out of bounds after shifting"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Is this chunk loaded still?
|
|
||||||
chunk = newChunkOrder[i];
|
|
||||||
if(chunk != NULL) {
|
|
||||||
i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the new chunk coordinates.
|
|
||||||
newChunkX = i % CHUNK_MAP_WIDTH + newX;
|
|
||||||
newChunkY = i / CHUNK_MAP_WIDTH + newY;
|
|
||||||
assertTrue(
|
|
||||||
newChunkX >= newX && newChunkX < newX + CHUNK_MAP_WIDTH,
|
|
||||||
"New chunk X coordinate out of bounds after shifting"
|
|
||||||
);
|
|
||||||
assertTrue(
|
|
||||||
newChunkY >= newY && newChunkY < newY + CHUNK_MAP_HEIGHT,
|
|
||||||
"New chunk Y coordinate out of bounds after shifting"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Pop a chunk from the unloaded chunks list.
|
|
||||||
assertTrue(j > 0, "No unloaded chunks available to reuse");
|
|
||||||
chunk = unloadedChunks[--j];
|
|
||||||
assertNotNull(chunk, "Unloaded chunk pointer is null");
|
|
||||||
|
|
||||||
// Load the chunk at the new coordinates.
|
|
||||||
chunkLoad(chunk, newChunkX, newChunkY);
|
|
||||||
assertTrue(
|
|
||||||
chunk->x == newChunkX && chunk->y == newChunkY,
|
|
||||||
"Chunk coordinates do not match expected values after shifting"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set it in order.
|
|
||||||
newChunkOrder[i] = chunk;
|
|
||||||
i++;
|
|
||||||
} while(i < CHUNK_MAP_COUNT);
|
|
||||||
|
|
||||||
// Update Absolutes.
|
|
||||||
CHUNK_MAP.topLeftX = newX;
|
|
||||||
CHUNK_MAP.topLeftY = newY;
|
|
||||||
|
|
||||||
// Update the chunk order.
|
|
||||||
memoryCopy(
|
|
||||||
CHUNK_MAP.chunkOrder,
|
|
||||||
newChunkOrder,
|
|
||||||
sizeof(CHUNK_MAP.chunkOrder)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void chunkMapSetPosition(const uint16_t x, const uint16_t y) {
|
|
||||||
if(x == CHUNK_MAP.topLeftX && y == CHUNK_MAP.topLeftY) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t shiftX = x - CHUNK_MAP.topLeftX;
|
|
||||||
int16_t shiftY = y - CHUNK_MAP.topLeftY;
|
|
||||||
|
|
||||||
// Are we shifting the entire map?
|
|
||||||
if(
|
|
||||||
shiftX >= CHUNK_MAP_WIDTH || shiftX < -CHUNK_MAP_WIDTH ||
|
|
||||||
shiftY >= CHUNK_MAP_HEIGHT || shiftY < -CHUNK_MAP_HEIGHT
|
|
||||||
) {
|
|
||||||
printf("Shifting chunk map to new position (%u, %u)\n", x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shift the chunk map by the specified offsets.
|
|
||||||
chunkMapShift(shiftX, shiftY);
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk_t * chunkGetChunkAt(const uint16_t chunkX, const uint16_t chunkY) {
|
|
||||||
assertTrue(
|
|
||||||
chunkX < WORLD_WIDTH && chunkY < WORLD_HEIGHT,
|
|
||||||
"Chunk coordinates out of bounds"
|
|
||||||
);
|
|
||||||
|
|
||||||
chunk_t *chunk = CHUNK_MAP.chunks;
|
|
||||||
do {
|
|
||||||
if(chunk->x == chunkX && chunk->y == chunkY) return chunk;
|
|
||||||
chunk++;
|
|
||||||
} while(chunk < CHUNK_MAP.chunks + CHUNK_MAP_COUNT);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void chunkLoad(chunk_t *chunk, const uint16_t x, const uint16_t y) {
|
|
||||||
assertNotNull(chunk, "Chunk pointer is null");
|
|
||||||
|
|
||||||
// Zero out the chunk data.
|
|
||||||
memoryZero(chunk, sizeof(chunk_t));
|
|
||||||
|
|
||||||
// Set the chunk coordinates.
|
|
||||||
chunk->x = x;
|
|
||||||
chunk->y = y;
|
|
||||||
|
|
||||||
// Only load data if the chunk is within bounds.
|
|
||||||
if(x >= WORLD_WIDTH || y >= WORLD_HEIGHT) {
|
|
||||||
memorySet(chunk->tilesBase, 0, sizeof(chunk->tilesBase));
|
|
||||||
memorySet(chunk->tilesBaseOverlay, 0, sizeof(chunk->tilesBaseOverlay));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is chunk data defined?
|
|
||||||
const chunkdata_t *chunkData = WORLD_CHUNKS[y * WORLD_WIDTH + x];
|
|
||||||
if(chunkData == NULL) {
|
|
||||||
memorySet(chunk->tilesBase, 0, sizeof(chunk->tilesBase));
|
|
||||||
memorySet(chunk->tilesBaseOverlay, 0, sizeof(chunk->tilesBaseOverlay));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load tile data into chunk
|
|
||||||
// printf("Loading chunk at (%u, %u)\n", x, y);
|
|
||||||
memoryCopy(
|
|
||||||
chunk->tilesBase,
|
|
||||||
chunkData->layerBase,
|
|
||||||
sizeof(chunk->tilesBase)
|
|
||||||
);
|
|
||||||
memoryCopy(
|
|
||||||
chunk->tilesBaseOverlay,
|
|
||||||
chunkData->layerBaseOverlay,
|
|
||||||
sizeof(chunk->tilesBaseOverlay)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Load chunk entities
|
|
||||||
const entity_t *data;
|
|
||||||
entity_t *entity;
|
|
||||||
data = chunkData->entities;
|
|
||||||
while(data < chunkData->entities + CHUNK_ENTITY_COUNT_MAX) {
|
|
||||||
if(data->type == ENTITY_TYPE_NULL) break;
|
|
||||||
|
|
||||||
// Store that this chunk owns this entity ID.
|
|
||||||
chunk->entityIDs[chunk->entityCount++] = data->id;
|
|
||||||
|
|
||||||
// Check entity isn't loaded (still).
|
|
||||||
entity = ENTITIES;
|
|
||||||
do {
|
|
||||||
if(entity->type != ENTITY_TYPE_NULL && entity->id == data->id) break;
|
|
||||||
entity++;
|
|
||||||
} while(entity < ENTITIES + ENTITY_COUNT_MAX);
|
|
||||||
|
|
||||||
if(entity != ENTITIES + ENTITY_COUNT_MAX) {
|
|
||||||
// Entity is already loaded, skip it.
|
|
||||||
printf("Entity ID %u already loaded, skipping...\n", data->id);
|
|
||||||
data++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find an empty entity slot.
|
|
||||||
entity = ENTITIES;
|
|
||||||
while(true) {
|
|
||||||
assertTrue(
|
|
||||||
entity < ENTITIES + ENTITY_COUNT_MAX,
|
|
||||||
"Out of available entities"
|
|
||||||
);
|
|
||||||
|
|
||||||
if(entity->type == ENTITY_TYPE_NULL) break;
|
|
||||||
entity++;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load this entity.
|
|
||||||
entityLoad(entity, data);
|
|
||||||
data++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow the rendering platform to know this chunk is loaded.
|
|
||||||
renderChunkUpdated(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
void chunkUnload(chunk_t *chunk) {
|
|
||||||
uint8_t i;
|
|
||||||
entity_t *entity;
|
|
||||||
uint32_t id;
|
|
||||||
assertNotNull(chunk, "Chunk pointer is null");
|
|
||||||
|
|
||||||
// Iterate over each entity this chunk owns.
|
|
||||||
i = 0;
|
|
||||||
while(i < chunk->entityCount) {
|
|
||||||
id = chunk->entityIDs[i++];
|
|
||||||
|
|
||||||
// Now, do we need to unload this entity?
|
|
||||||
bool_t shouldUnload = false;
|
|
||||||
|
|
||||||
// Now, find the entity loaded with this ID. It should be impossible for
|
|
||||||
// this entity to be unloaded (but may change in future).
|
|
||||||
entity = ENTITIES;
|
|
||||||
do {
|
|
||||||
if(entity->type != ENTITY_TYPE_NULL && entity->id == id) break;
|
|
||||||
entity++;
|
|
||||||
} while(entity < ENTITIES + ENTITY_COUNT_MAX);
|
|
||||||
|
|
||||||
assertTrue(
|
|
||||||
entity < ENTITIES + ENTITY_COUNT_MAX,
|
|
||||||
"Entity ID not found in ENTITIES array, cannot unload"
|
|
||||||
);
|
|
||||||
|
|
||||||
// If the entity is still within our chunk bounds, it's getting unloaded
|
|
||||||
if(
|
|
||||||
floorf(entity->x) >= chunk->x * CHUNK_WIDTH * TILE_WIDTH_HEIGHT &&
|
|
||||||
ceilf(entity->x) < (chunk->x + 1) * CHUNK_WIDTH * TILE_WIDTH_HEIGHT &&
|
|
||||||
floorf(entity->y) >= chunk->y * CHUNK_HEIGHT * TILE_WIDTH_HEIGHT &&
|
|
||||||
ceilf(entity->y) < (chunk->y + 1) * CHUNK_HEIGHT * TILE_WIDTH_HEIGHT
|
|
||||||
) {
|
|
||||||
shouldUnload = true;
|
|
||||||
} else {
|
|
||||||
assertUnreachable(
|
|
||||||
"Entity has left its chunk bounds, we should not be unloading it but "
|
|
||||||
"I have yet to implement that properly. It will need to self-manage "
|
|
||||||
"its own unloading somehow, and also not be in a null chunk "
|
|
||||||
"when it does so."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This entity is still in use, leave it loaded.
|
|
||||||
if(!shouldUnload) continue;
|
|
||||||
|
|
||||||
// NULL the entity type, effectively unloading it.
|
|
||||||
entity->type = ENTITY_TYPE_NULL;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,84 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "tile.h"
|
|
||||||
#include "display/render.h"
|
|
||||||
|
|
||||||
#define CHUNK_WIDTH 8
|
|
||||||
#define CHUNK_HEIGHT 8
|
|
||||||
#define CHUNK_TILE_COUNT (CHUNK_WIDTH * CHUNK_HEIGHT)
|
|
||||||
#define CHUNK_ENTITY_COUNT_MAX 8
|
|
||||||
|
|
||||||
#define CHUNK_MAP_WIDTH (((RENDER_WIDTH / TILE_WIDTH_HEIGHT)/CHUNK_WIDTH)+2)
|
|
||||||
#define CHUNK_MAP_HEIGHT (((RENDER_HEIGHT / TILE_WIDTH_HEIGHT)/CHUNK_HEIGHT)+2)
|
|
||||||
#define CHUNK_MAP_COUNT (CHUNK_MAP_WIDTH * CHUNK_MAP_HEIGHT)
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint16_t x, y;
|
|
||||||
tile_t tilesBase[CHUNK_TILE_COUNT];
|
|
||||||
tile_t tilesBaseOverlay[CHUNK_TILE_COUNT];
|
|
||||||
uint32_t entityIDs[CHUNK_ENTITY_COUNT_MAX];
|
|
||||||
uint8_t entityCount;
|
|
||||||
} chunk_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
chunk_t chunks[CHUNK_MAP_COUNT];
|
|
||||||
chunk_t *chunkOrder[CHUNK_MAP_COUNT];
|
|
||||||
|
|
||||||
uint16_t topLeftX;
|
|
||||||
uint16_t topLeftY;
|
|
||||||
} chunkmap_t;
|
|
||||||
|
|
||||||
extern chunkmap_t CHUNK_MAP;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the chunk map.
|
|
||||||
*/
|
|
||||||
void chunkMapInit();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shifts the chunk map by the specified x and y offsets.
|
|
||||||
*
|
|
||||||
* @param x The x offset to shift the chunk map.
|
|
||||||
* @param y The y offset to shift the chunk map.
|
|
||||||
*/
|
|
||||||
void chunkMapShift(const int16_t x, const int16_t y);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the position of the chunk map to the specified coordinates.
|
|
||||||
*
|
|
||||||
* @param x The x coordinate of the top-left chunk.
|
|
||||||
* @param y The y coordinate of the top-left chunk.
|
|
||||||
*/
|
|
||||||
void chunkMapSetPosition(const uint16_t x, const uint16_t y);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the chunk at the specified chunk coordinates.
|
|
||||||
*
|
|
||||||
* @param chunkX The x coordinate of the chunk.
|
|
||||||
* @param chunkY The y coordinate of the chunk.
|
|
||||||
* @return A pointer to the chunk at the specified chunk coordinates, or NULL if
|
|
||||||
* no chunk exists at those coordinates.
|
|
||||||
*/
|
|
||||||
chunk_t * chunkGetChunkAt(const uint16_t chunkX, const uint16_t chunkY);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a chunk at the specified coordinates.
|
|
||||||
*
|
|
||||||
* @param chunk The chunk to load.
|
|
||||||
* @param x The x coordinate of the chunk.
|
|
||||||
* @param y The y coordinate of the chunk.
|
|
||||||
*/
|
|
||||||
void chunkLoad(chunk_t *chunk, const uint16_t x, const uint16_t y);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unloads a chunk (that is currently loaded).
|
|
||||||
*
|
|
||||||
* @param chunk The chunk to unload.
|
|
||||||
*/
|
|
||||||
void chunkUnload(chunk_t *chunk);
|
|
@@ -1,16 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "chunk.h"
|
|
||||||
#include "entity/entity.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t layerBase[CHUNK_TILE_COUNT];
|
|
||||||
uint8_t layerBaseOverlay[CHUNK_TILE_COUNT];
|
|
||||||
entity_t entities[CHUNK_ENTITY_COUNT_MAX];
|
|
||||||
} chunkdata_t;
|
|
@@ -1,72 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "overworld.h"
|
|
||||||
#include "chunk.h"
|
|
||||||
#include "display/render.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "entity/entity.h"
|
|
||||||
|
|
||||||
uint32_t OVERWORLD_CAMERA_X;
|
|
||||||
uint32_t OVERWORLD_CAMERA_Y;
|
|
||||||
overworldcameratype_t OVERWORLD_CAMERA_TYPE;
|
|
||||||
|
|
||||||
void overworldInit(void) {
|
|
||||||
playerInit();
|
|
||||||
chunkMapInit();
|
|
||||||
|
|
||||||
OVERWORLD_CAMERA_X = 0;
|
|
||||||
OVERWORLD_CAMERA_Y = 0;
|
|
||||||
OVERWORLD_CAMERA_TYPE = OVERWORLD_CAMERA_TYPE_CENTERED_POSITION;
|
|
||||||
}
|
|
||||||
|
|
||||||
void overworldUpdate() {
|
|
||||||
entity_t *entity;
|
|
||||||
|
|
||||||
assertTrue(
|
|
||||||
OVERWORLD_CAMERA_X < OVERWORLD_CAMERA_LIMIT_X,
|
|
||||||
"Camera position limit (just because I haven't tested properly)"
|
|
||||||
);
|
|
||||||
assertTrue(
|
|
||||||
OVERWORLD_CAMERA_Y < OVERWORLD_CAMERA_LIMIT_Y,
|
|
||||||
"Camera position limit (just because I haven't tested properly)"
|
|
||||||
);
|
|
||||||
|
|
||||||
entity = ENTITIES;
|
|
||||||
do {
|
|
||||||
entityUpdate(entity++);
|
|
||||||
} while(entity->type != ENTITY_TYPE_NULL);
|
|
||||||
|
|
||||||
// Testing, follow player
|
|
||||||
entity = &ENTITIES[0]; // Player entity
|
|
||||||
assertTrue(
|
|
||||||
entity->type == ENTITY_TYPE_PLAYER,
|
|
||||||
"First entity must be player"
|
|
||||||
);
|
|
||||||
OVERWORLD_CAMERA_X = entity->x * TILE_WIDTH_HEIGHT + entity->subX;
|
|
||||||
OVERWORLD_CAMERA_Y = entity->y * TILE_WIDTH_HEIGHT + entity->subY;
|
|
||||||
|
|
||||||
uint16_t x, y;
|
|
||||||
uint16_t halfWidth, halfHeight;
|
|
||||||
halfWidth = ((CHUNK_MAP_WIDTH - 1) * CHUNK_WIDTH * TILE_WIDTH_HEIGHT) / 2;
|
|
||||||
halfHeight = ((CHUNK_MAP_HEIGHT - 1) * CHUNK_HEIGHT * TILE_WIDTH_HEIGHT) / 2;
|
|
||||||
|
|
||||||
// Calculate the chunk map position based on the camera position.
|
|
||||||
if(OVERWORLD_CAMERA_X < halfWidth) {
|
|
||||||
x = 0;
|
|
||||||
} else {
|
|
||||||
x = (OVERWORLD_CAMERA_X - halfWidth) / (CHUNK_WIDTH*TILE_WIDTH_HEIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(OVERWORLD_CAMERA_Y < halfHeight) {
|
|
||||||
y = 0;
|
|
||||||
} else {
|
|
||||||
y = (OVERWORLD_CAMERA_Y - halfHeight) / (CHUNK_HEIGHT*TILE_WIDTH_HEIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
chunkMapSetPosition(x, y);
|
|
||||||
}
|
|
@@ -1,30 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
OVERWORLD_CAMERA_TYPE_CENTERED_POSITION,
|
|
||||||
} overworldcameratype_t;
|
|
||||||
|
|
||||||
extern uint32_t OVERWORLD_CAMERA_X;
|
|
||||||
extern uint32_t OVERWORLD_CAMERA_Y;
|
|
||||||
extern overworldcameratype_t OVERWORLD_CAMERA_TYPE;
|
|
||||||
|
|
||||||
#define OVERWORLD_CAMERA_LIMIT_X (UINT32_MAX / 4)
|
|
||||||
#define OVERWORLD_CAMERA_LIMIT_Y (UINT32_MAX / 4)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the overworld.
|
|
||||||
*/
|
|
||||||
void overworldInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the overworld.
|
|
||||||
*/
|
|
||||||
void overworldUpdate(void);
|
|
@@ -1,26 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
#define TILE_WIDTH_HEIGHT 16
|
|
||||||
|
|
||||||
typedef uint8_t tile_t;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
TILE_SOLID_NONE = 0,
|
|
||||||
TILE_SOLID_FULL = 1,
|
|
||||||
TILE_SOLID_TRIANGLE_TOP_RIGHT = 2,
|
|
||||||
TILE_SOLID_TRIANGLE_TOP_LEFT = 3,
|
|
||||||
TILE_SOLID_TRIANGLE_BOTTOM_RIGHT = 4,
|
|
||||||
TILE_SOLID_TRIANGLE_BOTTOM_LEFT = 5,
|
|
||||||
} tilesolidtype_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
tilesolidtype_t solidType;
|
|
||||||
} tilemetadata_t;
|
|
@@ -1,40 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Libs
|
|
||||||
find_package(pspsdk REQUIRED)
|
|
||||||
find_package(SDL2 REQUIRED)
|
|
||||||
|
|
||||||
target_link_libraries(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
# pspsdk
|
|
||||||
${SDL2_LIBRARIES}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Compile definitions
|
|
||||||
target_compile_definitions(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
RENDER_WIDTH=480
|
|
||||||
RENDER_HEIGHT=272
|
|
||||||
RENDER_WINDOW_WIDTH_DEFAULT=480
|
|
||||||
RENDER_WINDOW_HEIGHT_DEFAULT=272
|
|
||||||
# DUSK_TIME_DYNAMIC=0
|
|
||||||
DUSK_TIME_DYNAMIC=1
|
|
||||||
)
|
|
||||||
|
|
||||||
# Includes
|
|
||||||
target_include_directories(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}
|
|
||||||
${SDL2_INCLUDE_DIRS}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
duskpsp.c
|
|
||||||
)
|
|
||||||
|
|
||||||
# Subdirs
|
|
@@ -1,11 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "duskpsp.h"
|
|
||||||
|
|
||||||
// PSP_MODULE_INFO("Dusk", 0, 1, 0);
|
|
||||||
// PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER | THREAD_ATTR_VFPU);
|
|
@@ -1,13 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
#include <pspkernel.h>
|
|
||||||
#include <pspdebug.h>
|
|
||||||
#include <pspdisplay.h>
|
|
||||||
#include <pspctrl.h>
|
|
@@ -1,41 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Compile defs
|
|
||||||
target_compile_definitions(${DUSK_TARGET_NAME}
|
|
||||||
PUBLIC
|
|
||||||
# DUSK_KEYBOARD_SUPPORT=1
|
|
||||||
)
|
|
||||||
|
|
||||||
# Libs
|
|
||||||
find_package(SDL2 REQUIRED)
|
|
||||||
find_package(OpenGL REQUIRED)
|
|
||||||
find_package(cglm REQUIRED)
|
|
||||||
|
|
||||||
target_link_libraries(${DUSK_TARGET_NAME}
|
|
||||||
PUBLIC
|
|
||||||
SDL2::SDL2
|
|
||||||
OpenGL::GL
|
|
||||||
GL
|
|
||||||
cglm
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Includes
|
|
||||||
target_include_directories(${DUSK_TARGET_NAME}
|
|
||||||
PUBLIC
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
dusksdl2input.c
|
|
||||||
main.c
|
|
||||||
time.c
|
|
||||||
)
|
|
||||||
|
|
||||||
# Subdirs
|
|
||||||
add_subdirectory(display)
|
|
@@ -1,21 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
render.c
|
|
||||||
renderbackbuffer.c
|
|
||||||
)
|
|
||||||
|
|
||||||
# Subdirs
|
|
||||||
add_subdirectory(camera)
|
|
||||||
add_subdirectory(framebuffer)
|
|
||||||
add_subdirectory(mesh)
|
|
||||||
add_subdirectory(overworld)
|
|
||||||
add_subdirectory(texture)
|
|
||||||
add_subdirectory(spritebatch)
|
|
||||||
add_subdirectory(scene)
|
|
||||||
add_subdirectory(ui)
|
|
@@ -1,10 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
camera.c
|
|
||||||
)
|
|
@@ -1,125 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "camera.h"
|
|
||||||
#include "display/render.h"
|
|
||||||
|
|
||||||
#include "world/overworld.h"
|
|
||||||
|
|
||||||
void cameraUIPush(void) {
|
|
||||||
glPushMatrix();
|
|
||||||
|
|
||||||
glMatrixMode(GL_PROJECTION);
|
|
||||||
glLoadIdentity();
|
|
||||||
|
|
||||||
glViewport(0, 0, RENDER_WIDTH, RENDER_HEIGHT);
|
|
||||||
|
|
||||||
mat4 ortho;
|
|
||||||
glm_ortho(
|
|
||||||
0.0f, (float_t)RENDER_WIDTH,
|
|
||||||
(float_t)RENDER_HEIGHT, 0.0f,
|
|
||||||
-1.0f, 1.0f,
|
|
||||||
ortho
|
|
||||||
);
|
|
||||||
glLoadMatrixf((const GLfloat*)ortho);
|
|
||||||
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
|
||||||
glLoadIdentity();
|
|
||||||
}
|
|
||||||
|
|
||||||
void cameraUIPop(void) {
|
|
||||||
glPopMatrix();
|
|
||||||
}
|
|
||||||
|
|
||||||
void cameraScreenPush(void) {
|
|
||||||
glPushMatrix();
|
|
||||||
|
|
||||||
glMatrixMode(GL_PROJECTION);
|
|
||||||
glLoadIdentity();
|
|
||||||
|
|
||||||
mat4 ortho;
|
|
||||||
#if RENDER_USE_FRAMEBUFFER
|
|
||||||
int32_t windowWidth, windowHeight;
|
|
||||||
SDL_GetWindowSize(RENDER_WINDOW, &windowWidth, &windowHeight);
|
|
||||||
|
|
||||||
glViewport(0, 0, windowWidth, windowHeight);
|
|
||||||
glm_ortho(
|
|
||||||
0.0f, (float_t) windowWidth,
|
|
||||||
(float_t)windowHeight, 0.0f,
|
|
||||||
-1.0f, 1.0f,
|
|
||||||
ortho
|
|
||||||
);
|
|
||||||
#else
|
|
||||||
glm_ortho(
|
|
||||||
0.0f, (float_t)RENDER_WIDTH,
|
|
||||||
(float_t)RENDER_HEIGHT, 0.0f,
|
|
||||||
-1.0f, 1.0f,
|
|
||||||
ortho
|
|
||||||
);
|
|
||||||
#endif
|
|
||||||
glLoadMatrixf((const GLfloat*)ortho);
|
|
||||||
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
|
||||||
glLoadIdentity();
|
|
||||||
}
|
|
||||||
|
|
||||||
void cameraScreenPop(void) {
|
|
||||||
glPopMatrix();
|
|
||||||
}
|
|
||||||
|
|
||||||
void cameraOverworldPush(void) {
|
|
||||||
glPushMatrix();
|
|
||||||
glLoadIdentity();
|
|
||||||
|
|
||||||
#if RENDER_USE_FRAMEBUFFER
|
|
||||||
glViewport(0, 0, RENDER_WIDTH, RENDER_HEIGHT);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
glMatrixMode(GL_PROJECTION);
|
|
||||||
glLoadIdentity();
|
|
||||||
|
|
||||||
const float_t fov = glm_rad(75.0f);
|
|
||||||
const float_t camOffset = 12.0f;
|
|
||||||
const float_t aspect = (float_t)RENDER_WIDTH / (float_t)RENDER_HEIGHT;
|
|
||||||
const float_t pixelPerfectOffset = (
|
|
||||||
tanf((glm_rad(180) - fov) / 2.0f) *
|
|
||||||
((float_t)RENDER_HEIGHT/ 2.0f)
|
|
||||||
);
|
|
||||||
|
|
||||||
vec3 look = {
|
|
||||||
OVERWORLD_CAMERA_X,
|
|
||||||
OVERWORLD_CAMERA_Y,
|
|
||||||
0.0f
|
|
||||||
};
|
|
||||||
vec3 eye = {
|
|
||||||
look[0],
|
|
||||||
look[1] + camOffset,
|
|
||||||
look[2] + pixelPerfectOffset
|
|
||||||
};
|
|
||||||
vec3 up = { 0.0f, 1.0f, 0.0f };
|
|
||||||
|
|
||||||
mat4 proj;
|
|
||||||
glm_perspective(fov, aspect, 0.1f, 1000.0f, proj);
|
|
||||||
|
|
||||||
// Flips rendering on the Y axis, so that it is still right-down even in 3D;
|
|
||||||
proj[1][1] = -proj[1][1];
|
|
||||||
|
|
||||||
mat4 view;
|
|
||||||
glm_lookat(eye, look, up, view);
|
|
||||||
|
|
||||||
mat4 pv;
|
|
||||||
glm_mat4_mul(proj, view, pv);
|
|
||||||
|
|
||||||
glLoadMatrixf((const GLfloat*)pv);
|
|
||||||
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
|
||||||
glLoadIdentity();
|
|
||||||
}
|
|
||||||
|
|
||||||
void cameraOverworldPop(void) {
|
|
||||||
glPopMatrix();
|
|
||||||
}
|
|
@@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusksdl2.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pushes the UI camera matrix onto the stack.
|
|
||||||
*/
|
|
||||||
void cameraUIPush(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pops the UI camera matrix from the stack.
|
|
||||||
*/
|
|
||||||
void cameraUIPop(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pushes the screen space camera matrix onto the stack.
|
|
||||||
*/
|
|
||||||
void cameraScreenPush(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pops the screen space camera matrix.
|
|
||||||
*/
|
|
||||||
void cameraScreenPop(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pushes the overworld camera matrix onto the stack.
|
|
||||||
*/
|
|
||||||
void cameraOverworldPush(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pops the overworld camera matrix.
|
|
||||||
*/
|
|
||||||
void cameraOverworldPop(void);
|
|
@@ -1,10 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
framebuffer.c
|
|
||||||
)
|
|
@@ -1,59 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "framebuffer.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
|
|
||||||
#if RENDER_USE_FRAMEBUFFER
|
|
||||||
void frameBufferInit(
|
|
||||||
framebuffer_t *framebuffer,
|
|
||||||
const uint32_t width,
|
|
||||||
const uint32_t height
|
|
||||||
) {
|
|
||||||
assertNotNull(framebuffer, "Framebuffer cannot be NULL");
|
|
||||||
assertTrue(width > 0 && height > 0, "Width & height must be greater than 0");
|
|
||||||
|
|
||||||
memoryZero(framebuffer, sizeof(framebuffer_t));
|
|
||||||
textureInit(&framebuffer->texture, width, height, GL_RGBA, NULL);
|
|
||||||
|
|
||||||
// Generate the framebuffer object using EXT
|
|
||||||
glGenFramebuffersEXT(1, &framebuffer->id);
|
|
||||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer->id);
|
|
||||||
|
|
||||||
// Attach the texture to the framebuffer
|
|
||||||
glFramebufferTexture2DEXT(
|
|
||||||
GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
|
|
||||||
GL_TEXTURE_2D, framebuffer->texture.id, 0
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check if the framebuffer is complete
|
|
||||||
if(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) {
|
|
||||||
assertUnreachable("Framebuffer is not complete");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unbind the framebuffer
|
|
||||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void frameBufferBind(const framebuffer_t *framebuffer) {
|
|
||||||
if(framebuffer == NULL) {
|
|
||||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind the framebuffer for rendering
|
|
||||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void frameBufferDispose(framebuffer_t *framebuffer) {
|
|
||||||
assertNotNull(framebuffer, "Framebuffer cannot be NULL");
|
|
||||||
|
|
||||||
glDeleteFramebuffersEXT(1, &framebuffer->id);
|
|
||||||
textureDispose(&framebuffer->texture);
|
|
||||||
}
|
|
||||||
#endif
|
|
@@ -1,45 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "display/render.h"
|
|
||||||
#include "display/texture/texture.h"
|
|
||||||
|
|
||||||
#if RENDER_USE_FRAMEBUFFER
|
|
||||||
typedef struct {
|
|
||||||
GLuint id;
|
|
||||||
texture_t texture;
|
|
||||||
} framebuffer_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a framebuffer using EXT methods.
|
|
||||||
*
|
|
||||||
* @param framebuffer The framebuffer to initialize.
|
|
||||||
* @param width The width of the framebuffer.
|
|
||||||
* @param height The height of the framebuffer.
|
|
||||||
* @return An error code indicating success or failure.
|
|
||||||
*/
|
|
||||||
void frameBufferInit(
|
|
||||||
framebuffer_t *framebuffer,
|
|
||||||
const uint32_t width,
|
|
||||||
const uint32_t height
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds the framebuffer for rendering using EXT methods.
|
|
||||||
*
|
|
||||||
* @param framebuffer The framebuffer to bind, or NULL to unbind.
|
|
||||||
*/
|
|
||||||
void frameBufferBind(const framebuffer_t *framebuffer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes of the framebuffer using EXT methods.
|
|
||||||
*
|
|
||||||
* @param framebuffer The framebuffer to dispose of.
|
|
||||||
*/
|
|
||||||
void frameBufferDispose(framebuffer_t *framebuffer);
|
|
||||||
#endif
|
|
@@ -1,12 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
mesh.c
|
|
||||||
quad.c
|
|
||||||
meshrenderer.c
|
|
||||||
)
|
|
@@ -1,82 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "mesh.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
#include "console/console.h"
|
|
||||||
|
|
||||||
void meshInit(
|
|
||||||
mesh_t *mesh,
|
|
||||||
const GLenum primitiveType,
|
|
||||||
const int32_t vertexCount,
|
|
||||||
const meshvertex_t *vertices
|
|
||||||
) {
|
|
||||||
assertNotNull(mesh, "Mesh cannot be NULL");
|
|
||||||
assertNotNull(vertices, "Vertices cannot be NULL");
|
|
||||||
assertTrue(vertexCount > 0, "Vertex count must be greater than 0");
|
|
||||||
|
|
||||||
memoryZero(mesh, sizeof(mesh_t));
|
|
||||||
|
|
||||||
mesh->primitiveType = primitiveType;
|
|
||||||
mesh->vertexCount = vertexCount;
|
|
||||||
mesh->vertices = vertices;
|
|
||||||
}
|
|
||||||
|
|
||||||
void meshDraw(
|
|
||||||
const mesh_t *mesh,
|
|
||||||
const int32_t vertexOffset,
|
|
||||||
const int32_t vertexCount
|
|
||||||
) {
|
|
||||||
const int32_t offset = vertexOffset == -1 ? 0 : vertexOffset;
|
|
||||||
const int32_t count = vertexCount == -1 ? mesh->vertexCount : vertexCount;
|
|
||||||
|
|
||||||
assertNotNull(mesh, "Mesh cannot be NULL");
|
|
||||||
assertTrue(offset >= 0, "Vertex offset must be non-negative");
|
|
||||||
assertTrue(count >= 0, "Vertex count must be non-negative");
|
|
||||||
assertTrue(offset + count <= mesh->vertexCount,
|
|
||||||
"Vertex offset + count must not exceed vertex count"
|
|
||||||
);
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
// PSP style pointer legacy OpenGL
|
|
||||||
const GLsizei stride = sizeof(meshvertex_t);
|
|
||||||
|
|
||||||
glColorPointer(
|
|
||||||
MESH_VERTEX_COLOR_SIZE,
|
|
||||||
GL_UNSIGNED_BYTE,
|
|
||||||
stride,
|
|
||||||
(const GLvoid*)&mesh->vertices[offset].color[0]
|
|
||||||
);
|
|
||||||
glTexCoordPointer(
|
|
||||||
MESH_VERTEX_UV_SIZE,
|
|
||||||
GL_FLOAT,
|
|
||||||
stride,
|
|
||||||
(const GLvoid*)&mesh->vertices[offset].uv[0]
|
|
||||||
);
|
|
||||||
glVertexPointer(
|
|
||||||
MESH_VERTEX_POS_SIZE,
|
|
||||||
GL_FLOAT,
|
|
||||||
stride,
|
|
||||||
(const GLvoid*)&mesh->vertices[offset].pos[0]
|
|
||||||
);
|
|
||||||
|
|
||||||
glDrawArrays(
|
|
||||||
mesh->primitiveType,
|
|
||||||
0,
|
|
||||||
count
|
|
||||||
);
|
|
||||||
#else
|
|
||||||
#error "Need to support modern OpenGL with VAOs and VBOs"
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void meshDispose(mesh_t *mesh) {
|
|
||||||
assertNotNull(mesh, "Mesh cannot be NULL");
|
|
||||||
memoryZero(mesh, sizeof(mesh_t));
|
|
||||||
}
|
|
@@ -1,58 +0,0 @@
|
|||||||
// Copyright (c) 2025 Dominic Masters
|
|
||||||
//
|
|
||||||
// This software is released under the MIT License.
|
|
||||||
// https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusksdl2.h"
|
|
||||||
|
|
||||||
#define MESH_VERTEX_COLOR_SIZE 4
|
|
||||||
#define MESH_VERTEX_UV_SIZE 2
|
|
||||||
#define MESH_VERTEX_POS_SIZE 3
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
GLubyte color[MESH_VERTEX_COLOR_SIZE];
|
|
||||||
GLfloat uv[MESH_VERTEX_UV_SIZE];
|
|
||||||
GLfloat pos[MESH_VERTEX_POS_SIZE];
|
|
||||||
} meshvertex_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const meshvertex_t *vertices;
|
|
||||||
int32_t vertexCount;
|
|
||||||
GLenum primitiveType;
|
|
||||||
} mesh_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a mesh.
|
|
||||||
*
|
|
||||||
* @param mesh The mesh to initialize.
|
|
||||||
* @param primitiveType The OpenGL primitive type (e.g., GL_TRIANGLES).
|
|
||||||
* @param vertexCount The number of vertices in the mesh.
|
|
||||||
* @param vertices The vertex data for the mesh.
|
|
||||||
*/
|
|
||||||
void meshInit(
|
|
||||||
mesh_t *mesh,
|
|
||||||
const GLenum primitiveType,
|
|
||||||
const int32_t vertexCount,
|
|
||||||
const meshvertex_t *vertices
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws a mesh.
|
|
||||||
*
|
|
||||||
* @param mesh The mesh to draw.
|
|
||||||
* @param vertexOffset The offset in the vertex array to start drawing from.
|
|
||||||
* @param vertexCount The number of vertices to draw. If -1, draws all vertices.
|
|
||||||
*/
|
|
||||||
void meshDraw(
|
|
||||||
const mesh_t *mesh,
|
|
||||||
const int32_t vertexOffset,
|
|
||||||
const int32_t vertexCount
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes a mesh.
|
|
||||||
*
|
|
||||||
* @param mesh The mesh to dispose.
|
|
||||||
*/
|
|
||||||
void meshDispose(mesh_t *mesh);
|
|
@@ -1,62 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "quad.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
void quadBuffer(
|
|
||||||
meshvertex_t *vertices,
|
|
||||||
const float_t minX,
|
|
||||||
const float_t minY,
|
|
||||||
const float_t maxX,
|
|
||||||
const float_t maxY,
|
|
||||||
const uint8_t r,
|
|
||||||
const uint8_t g,
|
|
||||||
const uint8_t b,
|
|
||||||
const uint8_t a,
|
|
||||||
const float_t u0,
|
|
||||||
const float_t v0,
|
|
||||||
const float_t u1,
|
|
||||||
const float_t v1
|
|
||||||
) {
|
|
||||||
const float_t z = 0.0f; // Z coordinate for 2D rendering
|
|
||||||
assertNotNull(vertices, "Vertices cannot be NULL");
|
|
||||||
|
|
||||||
// First triangle
|
|
||||||
vertices[0] = (meshvertex_t) {
|
|
||||||
{ r, g, b, a }, // Color
|
|
||||||
{ u0, v0 }, // UV
|
|
||||||
{ minX, minY, z } // Position
|
|
||||||
};
|
|
||||||
vertices[1] = (meshvertex_t) {
|
|
||||||
{ r, g, b, a }, // Color
|
|
||||||
{ u1, v0 }, // UV
|
|
||||||
{ maxX, minY, z } // Position
|
|
||||||
};
|
|
||||||
vertices[2] = (meshvertex_t) {
|
|
||||||
{ r, g, b, a }, // Color
|
|
||||||
{ u1, v1 }, // UV
|
|
||||||
{ maxX, maxY, z } // Position
|
|
||||||
};
|
|
||||||
|
|
||||||
// Second triangle
|
|
||||||
vertices[3] = (meshvertex_t) {
|
|
||||||
{ r, g, b, a }, // Color
|
|
||||||
{ u0, v0 }, // UV
|
|
||||||
{ minX, minY, z } // Position
|
|
||||||
};
|
|
||||||
vertices[4] = (meshvertex_t) {
|
|
||||||
{ r, g, b, a }, // Color
|
|
||||||
{ u1, v1 }, // UV
|
|
||||||
{ maxX, maxY, z } // Position
|
|
||||||
};
|
|
||||||
vertices[5] = (meshvertex_t) {
|
|
||||||
{ r, g, b, a }, // Color
|
|
||||||
{ u0, v1 }, // UV
|
|
||||||
{ minX, maxY, z } // Position
|
|
||||||
};
|
|
||||||
}
|
|
@@ -1,44 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "mesh.h"
|
|
||||||
|
|
||||||
#define QUAD_VERTEX_COUNT 6
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Buffers a quad into the provided vertex array.
|
|
||||||
*
|
|
||||||
* @param vertices The vertex array to buffer into.
|
|
||||||
* @param minX The minimum X coordinate of the quad.
|
|
||||||
* @param minY The minimum Y coordinate of the quad.
|
|
||||||
* @param maxX The maximum X coordinate of the quad.
|
|
||||||
* @param maxY The maximum Y coordinate of the quad.
|
|
||||||
* @param r The red color component (0-255).
|
|
||||||
* @param g The green color component (0-255).
|
|
||||||
* @param b The blue color component (0-255).
|
|
||||||
* @param a The alpha color component (0-255).
|
|
||||||
* @param u0 The U texture coordinate for the first vertex.
|
|
||||||
* @param v0 The V texture coordinate for the first vertex.
|
|
||||||
* @param u1 The U texture coordinate for the second vertex.
|
|
||||||
* @param v1 The V texture coordinate for the second vertex.
|
|
||||||
*/
|
|
||||||
void quadBuffer(
|
|
||||||
meshvertex_t *vertices,
|
|
||||||
const float_t minX,
|
|
||||||
const float_t minY,
|
|
||||||
const float_t maxX,
|
|
||||||
const float_t maxY,
|
|
||||||
const uint8_t r,
|
|
||||||
const uint8_t g,
|
|
||||||
const uint8_t b,
|
|
||||||
const uint8_t a,
|
|
||||||
const float_t u0,
|
|
||||||
const float_t v0,
|
|
||||||
const float_t u1,
|
|
||||||
const float_t v1
|
|
||||||
);
|
|
@@ -1,10 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
renderoverworld.c
|
|
||||||
)
|
|
@@ -1,125 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "renderoverworld.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "display/camera/camera.h"
|
|
||||||
#include "entity/entity.h"
|
|
||||||
#include "display/spritebatch/spritebatch.h"
|
|
||||||
|
|
||||||
renderoverworld_t RENDER_OVERWORLD;
|
|
||||||
|
|
||||||
void renderOverworldInit(void) {
|
|
||||||
memoryZero(&RENDER_OVERWORLD, sizeof(RENDER_OVERWORLD));
|
|
||||||
|
|
||||||
for(uint8_t i = 0; i < CHUNK_MAP_COUNT; i++) {
|
|
||||||
renderchunk_t *chunk = &RENDER_OVERWORLD.chunks[i];
|
|
||||||
|
|
||||||
meshInit(
|
|
||||||
&chunk->meshBase,
|
|
||||||
GL_TRIANGLES,
|
|
||||||
CHUNK_TILE_COUNT * QUAD_VERTEX_COUNT,
|
|
||||||
chunk->verticesBase
|
|
||||||
);
|
|
||||||
|
|
||||||
meshInit(
|
|
||||||
&chunk->meshBaseOverlay,
|
|
||||||
GL_TRIANGLES,
|
|
||||||
CHUNK_TILE_COUNT,
|
|
||||||
chunk->verticesBaseOverlay
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderOverworldDraw(void) {
|
|
||||||
cameraOverworldPush();
|
|
||||||
|
|
||||||
for(uint8_t i = 0; i < CHUNK_MAP_COUNT; i++) {
|
|
||||||
renderchunk_t *chunk = &RENDER_OVERWORLD.chunks[i];
|
|
||||||
meshDraw(&chunk->meshBase, -1, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(uint8_t i = 0; i < ENTITY_COUNT_MAX; i++) {
|
|
||||||
entity_t *entity = &ENTITIES[i];
|
|
||||||
if(entity->type == ENTITY_TYPE_NULL) continue;
|
|
||||||
|
|
||||||
float_t x = (entity->x * TILE_WIDTH_HEIGHT) + entity->subX;
|
|
||||||
float_t y = (entity->y * TILE_WIDTH_HEIGHT) + entity->subY;
|
|
||||||
|
|
||||||
// Draw the entity
|
|
||||||
spriteBatchPush(
|
|
||||||
NULL,
|
|
||||||
x, y,
|
|
||||||
x + TILE_WIDTH_HEIGHT, y + TILE_WIDTH_HEIGHT,
|
|
||||||
0xFF, 0x00, 0xFF, 0XFF,
|
|
||||||
0.0f, 0.0f, 1.0f, 1.0f
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
spriteBatchFlush();
|
|
||||||
cameraOverworldPop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderChunkUpdated(chunk_t *chunk) {
|
|
||||||
uint8_t r, g, b;
|
|
||||||
assertNotNull(chunk, "Chunk pointer is null");
|
|
||||||
|
|
||||||
int32_t chunkIndex = chunk - CHUNK_MAP.chunks;
|
|
||||||
assertTrue(
|
|
||||||
chunkIndex >= 0 && chunkIndex < CHUNK_MAP_COUNT,
|
|
||||||
"Chunk index out of bounds"
|
|
||||||
);
|
|
||||||
|
|
||||||
for(uint32_t i = 0; i < CHUNK_TILE_COUNT; i++) {
|
|
||||||
tile_t base = chunk->tilesBase[i];
|
|
||||||
tile_t overlay = chunk->tilesBaseOverlay[i];
|
|
||||||
|
|
||||||
float_t posX = (i % CHUNK_WIDTH) + (chunk->x * CHUNK_WIDTH);
|
|
||||||
float_t posY = (i / CHUNK_WIDTH) + (chunk->y * CHUNK_HEIGHT);
|
|
||||||
|
|
||||||
switch(base) {
|
|
||||||
case 0:
|
|
||||||
r = 0; g = 0; b = 0; // Black for empty
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
r = 34; g = 139; b = 34; // Forest Green
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
r = 0; g = 191; b = 255; // Deep Sky Blue
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
r = 139; g = 69; b = 19; // Saddle Brown
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
r = 255; g = 255; b = 0; // Yellow
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
r = 255; g = 20; b = 147; // Pink for unknown
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
quadBuffer(
|
|
||||||
&RENDER_OVERWORLD.chunks[chunkIndex].verticesBase[i * QUAD_VERTEX_COUNT],
|
|
||||||
posX * TILE_WIDTH_HEIGHT,
|
|
||||||
posY * TILE_WIDTH_HEIGHT,
|
|
||||||
(posX + 1) * TILE_WIDTH_HEIGHT,
|
|
||||||
(posY + 1) * TILE_WIDTH_HEIGHT,
|
|
||||||
r, g, b, 255,
|
|
||||||
0, 0, 1, 1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderOverworldDispose(void) {
|
|
||||||
// Clean up overworld rendering resources here
|
|
||||||
for(uint8_t i = 0; i < CHUNK_MAP_COUNT; i++) {
|
|
||||||
renderchunk_t *chunk = &RENDER_OVERWORLD.chunks[i];
|
|
||||||
meshDispose(&chunk->meshBase);
|
|
||||||
meshDispose(&chunk->meshBaseOverlay);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "world/chunk.h"
|
|
||||||
#include "display/mesh/quad.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
mesh_t meshBase;
|
|
||||||
meshvertex_t verticesBase[CHUNK_TILE_COUNT * QUAD_VERTEX_COUNT];
|
|
||||||
|
|
||||||
mesh_t meshBaseOverlay;
|
|
||||||
meshvertex_t verticesBaseOverlay[CHUNK_TILE_COUNT];
|
|
||||||
} renderchunk_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
renderchunk_t chunks[CHUNK_MAP_COUNT];
|
|
||||||
} renderoverworld_t;
|
|
||||||
|
|
||||||
extern renderoverworld_t RENDER_OVERWORLD;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the render overworld.
|
|
||||||
*/
|
|
||||||
void renderOverworldInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws the render overworld.
|
|
||||||
*/
|
|
||||||
void renderOverworldDraw(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes of the render overworld.
|
|
||||||
*/
|
|
||||||
void renderOverworldDispose(void);
|
|
@@ -1,119 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dusksdl2input.h"
|
|
||||||
#include "render.h"
|
|
||||||
#include "renderbackbuffer.h"
|
|
||||||
#include "display/scene/renderscene.h"
|
|
||||||
#include "display/spritebatch/spritebatch.h"
|
|
||||||
#include "display/ui/renderui.h"
|
|
||||||
|
|
||||||
SDL_Window *RENDER_WINDOW;
|
|
||||||
SDL_GLContext RENDER_GL_CONTEXT;
|
|
||||||
bool_t RENDER_RUNNING;
|
|
||||||
|
|
||||||
errorret_t renderInit(void) {
|
|
||||||
// Init SDL
|
|
||||||
uint32_t flags = SDL_INIT_VIDEO;
|
|
||||||
#if INPUT_SUPPORT_GAMEPAD
|
|
||||||
flags |= SDL_INIT_GAMECONTROLLER;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(SDL_Init(flags) != 0) {
|
|
||||||
errorThrow(
|
|
||||||
"SDL Failed to Initialize: %s",
|
|
||||||
SDL_GetError()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set OpenGL attributes (Needs to be done now or later?)
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
||||||
|
|
||||||
// Create window with OpenGL flag.
|
|
||||||
RENDER_WINDOW = SDL_CreateWindow(
|
|
||||||
"DuskSDL2",
|
|
||||||
SDL_WINDOWPOS_UNDEFINED,
|
|
||||||
SDL_WINDOWPOS_UNDEFINED,
|
|
||||||
RENDER_WINDOW_WIDTH_DEFAULT,
|
|
||||||
RENDER_WINDOW_HEIGHT_DEFAULT,
|
|
||||||
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI |
|
|
||||||
SDL_WINDOW_OPENGL
|
|
||||||
);
|
|
||||||
if(!RENDER_WINDOW) {
|
|
||||||
errorThrow("SDL_CreateWindow failed: %s", SDL_GetError());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create OpenGL context
|
|
||||||
RENDER_GL_CONTEXT = SDL_GL_CreateContext(RENDER_WINDOW);
|
|
||||||
if(!RENDER_GL_CONTEXT) {
|
|
||||||
errorThrow("SDL_GL_CreateContext failed: %s", SDL_GetError());
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_GL_SetSwapInterval(1);
|
|
||||||
glDisable(GL_DEPTH_TEST);
|
|
||||||
glDisable(GL_CULL_FACE);
|
|
||||||
glDisable(GL_LIGHTING);// PSP defaults this on?
|
|
||||||
glShadeModel(GL_SMOOTH); // Fixes color on PSP?
|
|
||||||
glEnable(GL_BLEND);
|
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
||||||
|
|
||||||
glEnableClientState(GL_COLOR_ARRAY);// To confirm: every frame on PSP?
|
|
||||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
||||||
glEnableClientState(GL_VERTEX_ARRAY);
|
|
||||||
|
|
||||||
spriteBatchInit();
|
|
||||||
renderBackBufferInit();
|
|
||||||
renderSceneInit();
|
|
||||||
renderUIInit();
|
|
||||||
|
|
||||||
RENDER_RUNNING = true;
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t renderDraw(void) {
|
|
||||||
SDL_Event event;
|
|
||||||
while(SDL_PollEvent(&event)) {
|
|
||||||
switch(event.type) {
|
|
||||||
case SDL_QUIT:
|
|
||||||
RENDER_RUNNING = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset the state
|
|
||||||
spriteBatchClear();
|
|
||||||
renderBackBufferBind();
|
|
||||||
|
|
||||||
renderSceneDraw();
|
|
||||||
renderUIDraw();
|
|
||||||
|
|
||||||
// Finish rendering, now render back buffer.
|
|
||||||
renderBackBufferUnbind();
|
|
||||||
renderBackBufferDraw();
|
|
||||||
textureBind(NULL);
|
|
||||||
|
|
||||||
SDL_GL_SwapWindow(RENDER_WINDOW);
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t renderDispose(void) {
|
|
||||||
renderUIDispose();
|
|
||||||
renderSceneDispose();
|
|
||||||
renderBackBufferDispose();
|
|
||||||
spriteBatchDispose();
|
|
||||||
|
|
||||||
// Destroy OpenGL context
|
|
||||||
SDL_GL_DeleteContext(RENDER_GL_CONTEXT);
|
|
||||||
SDL_DestroyWindow(RENDER_WINDOW);
|
|
||||||
SDL_Quit();
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
@@ -1,27 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusksdl2.h"
|
|
||||||
#include "display/renderbase.h"
|
|
||||||
|
|
||||||
#ifndef RENDER_WINDOW_WIDTH_DEFAULT
|
|
||||||
#define RENDER_WINDOW_WIDTH_DEFAULT RENDER_WIDTH * 3
|
|
||||||
#endif
|
|
||||||
#ifndef RENDER_WINDOW_HEIGHT_DEFAULT
|
|
||||||
#define RENDER_WINDOW_HEIGHT_DEFAULT RENDER_HEIGHT * 3
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if RENDER_WIDTH == RENDER_WINDOW_WIDTH_DEFAULT && RENDER_HEIGHT == RENDER_WINDOW_HEIGHT_DEFAULT
|
|
||||||
#define RENDER_USE_FRAMEBUFFER 0
|
|
||||||
#else
|
|
||||||
#define RENDER_USE_FRAMEBUFFER 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern SDL_Window *RENDER_WINDOW;
|
|
||||||
extern SDL_Renderer *RENDER_RENDERER;
|
|
||||||
extern bool_t RENDER_RUNNING;
|
|
@@ -1,93 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "renderbackbuffer.h"
|
|
||||||
#include "render.h"
|
|
||||||
#include "display/spritebatch/spritebatch.h"
|
|
||||||
#include "display/camera/camera.h"
|
|
||||||
|
|
||||||
#if RENDER_USE_FRAMEBUFFER
|
|
||||||
framebuffer_t RENDER_BACKBUFFER;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
errorret_t renderBackBufferInit(void) {
|
|
||||||
#if RENDER_USE_FRAMEBUFFER
|
|
||||||
frameBufferInit(
|
|
||||||
&RENDER_BACKBUFFER,
|
|
||||||
RENDER_WIDTH,
|
|
||||||
RENDER_HEIGHT
|
|
||||||
);
|
|
||||||
#else
|
|
||||||
// No back buffer needed for window rendering
|
|
||||||
#endif
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderBackBufferBind(void) {
|
|
||||||
#if RENDER_USE_FRAMEBUFFER
|
|
||||||
frameBufferBind(&RENDER_BACKBUFFER);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Fill background with cornflower blue.
|
|
||||||
glClearColor(0.392f, 0.584f, 0.929f, 1.0f);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderBackBufferUnbind(void) {
|
|
||||||
#if RENDER_USE_FRAMEBUFFER
|
|
||||||
frameBufferBind(NULL);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderBackBufferDraw(void) {
|
|
||||||
#if RENDER_USE_FRAMEBUFFER
|
|
||||||
int32_t windowWidth, windowHeight;
|
|
||||||
SDL_GetWindowSize(RENDER_WINDOW, &windowWidth, &windowHeight);
|
|
||||||
|
|
||||||
// Set viewport to match window size
|
|
||||||
cameraScreenPush();
|
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
|
|
||||||
// Create a quad that is scaled to fit but maintain original aspect ratio
|
|
||||||
int32_t renderWidth, renderHeight, renderX, renderY;
|
|
||||||
if(RENDER_WIDTH * windowHeight > RENDER_HEIGHT * windowWidth) {
|
|
||||||
renderWidth = windowWidth;
|
|
||||||
renderHeight = (RENDER_HEIGHT * windowWidth) / RENDER_WIDTH;
|
|
||||||
renderX = 0;
|
|
||||||
renderY = (windowHeight - renderHeight) / 2;
|
|
||||||
} else {
|
|
||||||
renderWidth = (RENDER_WIDTH * windowHeight) / RENDER_HEIGHT;
|
|
||||||
renderHeight = windowHeight;
|
|
||||||
renderX = (windowWidth - renderWidth) / 2;
|
|
||||||
renderY = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw the back buffer texture
|
|
||||||
spriteBatchClear();
|
|
||||||
spriteBatchPush(
|
|
||||||
&RENDER_BACKBUFFER.texture,
|
|
||||||
renderX, renderY,
|
|
||||||
renderX+renderWidth, renderY+renderHeight,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0, 1, 1, 0
|
|
||||||
);
|
|
||||||
spriteBatchFlush();
|
|
||||||
cameraScreenPop();
|
|
||||||
#else
|
|
||||||
// No back buffer to draw
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t renderBackBufferDispose(void) {
|
|
||||||
#if RENDER_USE_FRAMEBUFFER
|
|
||||||
frameBufferDispose(&RENDER_BACKBUFFER);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
@@ -1,42 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "display/renderbase.h"
|
|
||||||
#include "display/framebuffer/framebuffer.h"
|
|
||||||
|
|
||||||
#if RENDER_USE_FRAMEBUFFER
|
|
||||||
extern framebuffer_t RENDER_BACKBUFFER;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the render back buffer. May be either a framebuffer or a texture
|
|
||||||
* depending on the render settings.
|
|
||||||
*/
|
|
||||||
errorret_t renderBackBufferInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds the render back buffer as the current render target.
|
|
||||||
*/
|
|
||||||
void renderBackBufferBind(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unbinds the render back buffer, returning to the default render target.
|
|
||||||
*/
|
|
||||||
void renderBackBufferUnbind(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws the render back buffer to the screen, scaling it to fit the window.
|
|
||||||
*/
|
|
||||||
void renderBackBufferDraw(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes of the render back buffer, freeing any resources it holds.
|
|
||||||
*
|
|
||||||
* @return An error state if an error occurred, otherwise OK.
|
|
||||||
*/
|
|
||||||
errorret_t renderBackBufferDispose(void);
|
|
@@ -1,13 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
renderscene.c
|
|
||||||
)
|
|
||||||
|
|
||||||
# Subdirs
|
|
||||||
# add_subdirectory(draw)
|
|
@@ -1,42 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "renderscene.h"
|
|
||||||
#include "display/overworld/renderoverworld.h"
|
|
||||||
|
|
||||||
renderscenecallback_t RENDER_SCENE_CALLBACKS[SCENE_COUNT] = {
|
|
||||||
[SCENE_INITIAL] = {
|
|
||||||
.init = NULL,
|
|
||||||
.draw = NULL,
|
|
||||||
.dispose = NULL
|
|
||||||
},
|
|
||||||
|
|
||||||
[SCENE_OVERWORLD] = {
|
|
||||||
.init = renderOverworldInit,
|
|
||||||
.draw = renderOverworldDraw,
|
|
||||||
.dispose = renderOverworldDispose
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
void renderSceneInit(void) {
|
|
||||||
for(int32_t i = 0; i < SCENE_COUNT; i++) {
|
|
||||||
if(!RENDER_SCENE_CALLBACKS[i].init) continue;
|
|
||||||
RENDER_SCENE_CALLBACKS[i].init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderSceneDraw(void) {
|
|
||||||
if(!RENDER_SCENE_CALLBACKS[SCENE_CURRENT].draw) return;
|
|
||||||
RENDER_SCENE_CALLBACKS[SCENE_CURRENT].draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderSceneDispose(void) {
|
|
||||||
for(int32_t i = 0; i < SCENE_COUNT; i++) {
|
|
||||||
if(!RENDER_SCENE_CALLBACKS[i].dispose) continue;
|
|
||||||
RENDER_SCENE_CALLBACKS[i].dispose();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,32 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "display/scene.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
void (*init)(void);
|
|
||||||
void (*draw)(void);
|
|
||||||
void (*dispose)(void);
|
|
||||||
} renderscenecallback_t;
|
|
||||||
|
|
||||||
extern renderscenecallback_t RENDER_SCENE_CALLBACKS[SCENE_COUNT];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the render scene module.
|
|
||||||
*/
|
|
||||||
void renderSceneInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws the current scene.
|
|
||||||
*/
|
|
||||||
void renderSceneDraw(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes of the render scene module.
|
|
||||||
*/
|
|
||||||
void renderSceneDispose(void);
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user