New basic console

This commit is contained in:
2025-03-28 11:23:13 -05:00
parent b9de46a16b
commit 2135217843
55 changed files with 838 additions and 1496 deletions

View File

@ -3,6 +3,5 @@
# This software is released under the MIT License. # This software is released under the MIT License.
# https://opensource.org/licenses/MIT # https://opensource.org/licenses/MIT
add_subdirectory(dusktest)
add_subdirectory(dusk) add_subdirectory(dusk)
add_subdirectory(duskgl)
add_subdirectory(duskglfw)

View File

@ -17,12 +17,10 @@ target_include_directories(${DUSK_TARGET_NAME}
# Sources # Sources
target_sources(${DUSK_TARGET_NAME} target_sources(${DUSK_TARGET_NAME}
PRIVATE PRIVATE
game.c
input.c
) )
# Subdirs # Subdirs
add_subdirectory(assert) add_subdirectory(assert)
add_subdirectory(display) add_subdirectory(console)
add_subdirectory(overworld) add_subdirectory(error)
add_subdirectory(util) add_subdirectory(util)

View File

@ -122,7 +122,7 @@ void assertMemoryRangeMatchesImpl(
#define assertDeprecated(message) \ #define assertDeprecated(message) \
assertDeprecatedImpl(__FILE__, __LINE__, message) assertDeprecatedImpl(__FILE__, __LINE__, message)
#define assertStrLen(str, len, message) \ #define assertStrLenMax(str, len, message) \
assertTrue(strlen(str) <= len, message) assertTrue(strlen(str) <= len, message)
#define assertStrLenMin(str, len, message) \ #define assertStrLenMin(str, len, message) \

View File

@ -6,8 +6,7 @@
# Sources # Sources
target_sources(${DUSK_TARGET_NAME} target_sources(${DUSK_TARGET_NAME}
PRIVATE PRIVATE
scene.c console.c
tileset.c consolecmd.c
consolevar.c
) )
# Subdirs

224
src/dusk/console/console.c Normal file
View File

@ -0,0 +1,224 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "console.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
console_t CONSOLE;
void consoleInit() {
memoryZero(&CONSOLE, sizeof(console_t));
// Register the get and set command.
CONSOLE.cmdGet = consoleRegCmd("get", consoleCmdGet);
CONSOLE.cmdSet = consoleRegCmd("set", consoleCmdSet);
}
consolecmd_t * consoleRegCmd(const char_t *name, consolecmdfunc_t function) {
consolecmd_t *cmd = &CONSOLE.commands[CONSOLE.commandCount++];
consoleCmdInit(cmd, name, function);
return cmd;
}
consolevar_t * consoleRegVar(
const char_t *name,
const char_t *value,
consolevarchanged_t event
) {
consolevar_t *var = &CONSOLE.variables[CONSOLE.variableCount++];
consoleVarInitListener(var, name, value, event);
return var;
}
void consolePrint(const char_t *message, ...) {
char_t buffer[CONSOLE_LINE_MAX];
va_list args;
va_start(args, message);
size_t len = vsnprintf(buffer, CONSOLE_LINE_MAX, message, args);
va_end(args);
assertTrue(len < CONSOLE_LINE_MAX, "Message is too long.");
// Move all lines back
memoryMove(
CONSOLE.line[0],
CONSOLE.line[1],
(CONSOLE_HISTORY_MAX - 1) * CONSOLE_LINE_MAX
);
// Copy the new line
memoryCopy(
CONSOLE.line[CONSOLE_HISTORY_MAX - 1],
buffer,
len + 1
);
printf("%s\n", buffer);
}
void consoleExec(const char_t *line) {
assertNotNull(line, "line must not be NULL");
assertTrue(
CONSOLE.execBufferCount < CONSOLE_EXEC_BUFFER_MAX,
"Too many commands in the buffer."
);
if(line[0] == '\0') return;
char_t buffer[CONSOLE_LINE_MAX + 1];
size_t i = 0, j = 0;
char_t c;
do {
c = line[i++];
// Handle command separation by semicolon or end of line
if(c == ';' || c == '\0') {
// Null-terminate the current command and trim whitespace
buffer[j] = '\0';
stringTrim(buffer);
// Skip empty commands
if(buffer[0] == '\0') {
j = 0;
continue;
}
// Ensure the exec buffer is not full
assertTrue(
CONSOLE.execBufferCount < CONSOLE_EXEC_BUFFER_MAX,
"Too many commands in the buffer."
);
// Create a new command execution
consolecmdexec_t *exec = &CONSOLE.execBuffer[CONSOLE.execBufferCount++];
memoryZero(exec, sizeof(consolecmdexec_t));
// Parse command and arguments
char_t *token = strtok(buffer, " ");
while(token != NULL) {
assertTrue(
exec->argc < CONSOLE_CMD_ARGC_MAX,
"Too many arguments in the command."
);
stringCopy(exec->argv[exec->argc++], token, CONSOLE_LINE_MAX);
token = strtok(NULL, " ");
}
// First, see if there's a matching command.
for(uint32_t k = 0; k < CONSOLE.commandCount; k++) {
if(stringCompare(CONSOLE.commands[k].name, exec->argv[0]) != 0) {
continue;
}
exec->cmd = &CONSOLE.commands[k];
}
// If match found continue
if(exec->cmd != NULL) {
j = 0;
continue;
}
// If no match found, see if there's a matching variable.
consolevar_t *var = NULL;
for(uint32_t k = 0; k < CONSOLE.variableCount; k++) {
if(stringCompare(CONSOLE.variables[k].name, exec->argv[0]) != 0) {
continue;
}
var = &CONSOLE.variables[k];
break;
}
// If no variable found, error out.
if(!var) {
consolePrint("Error: Command/Variable '%s' not found.", exec->argv[0]);
CONSOLE.execBufferCount--;
j = 0;
continue;
}
// Found a matching variable, we basically change this entire command
// to a get or a set. Arguments are reversed so they aren't stepped over
if(exec->argc == 1) {
exec->cmd = CONSOLE.cmdGet;
exec->argc = 2;
stringCopy(exec->argv[1], var->name, CONSOLE_LINE_MAX);
stringCopy(exec->argv[0], CONSOLE.cmdGet->name, CONSOLE_LINE_MAX);
} else {
exec->cmd = CONSOLE.cmdSet;
exec->argc = 3;
stringCopy(exec->argv[2], exec->argv[1], CONSOLE_LINE_MAX);
stringCopy(exec->argv[1], var->name, CONSOLE_LINE_MAX);
stringCopy(exec->argv[0], CONSOLE.cmdSet->name, CONSOLE_LINE_MAX);
}
j = 0;
continue;
}
if(j >= CONSOLE_LINE_MAX) {
assertTrue(false, "Command exceeds maximum length.");
}
buffer[j++] = c; // Accumulate characters into the buffer
} while(c != '\0');
}
void consoleProcess() {
for(uint32_t i = 0; i < CONSOLE.execBufferCount; i++) {
consolecmdexec_t *exec = &CONSOLE.execBuffer[i];
assertTrue(
exec->argc > 0,
"Command execution has no arguments."
);
assertNotNull(
exec->cmd,
"Command execution has no command."
);
exec->cmd->function(exec);
}
// Clear the exec buffer
CONSOLE.execBufferCount = 0;
}
void consoleCmdGet(const consolecmdexec_t *exec) {
assertTrue(
exec->argc >= 2,
"get command requires 1 argument."
);
for(uint32_t i = 0; i < CONSOLE.variableCount; i++) {
consolevar_t *var = &CONSOLE.variables[i];
if(stringCompare(var->name, exec->argv[1]) != 0) continue;
consolePrint("%s", var->value);
return;
}
consolePrint("Error: Variable '%s' not found.", exec->argv[1]);
}
void consoleCmdSet(const consolecmdexec_t *exec) {
assertTrue(
exec->argc >= 3,
"set command requires 2 arguments."
);
for(uint32_t i = 0; i < CONSOLE.variableCount; i++) {
consolevar_t *var = &CONSOLE.variables[i];
if(stringCompare(var->name, exec->argv[1]) != 0) continue;
consoleVarSetValue(var, exec->argv[2]);
return;
}
consolePrint("Error: Variable '%s' not found.", exec->argv[1]);
}

View File

@ -0,0 +1,95 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "consolevar.h"
#include "consolecmd.h"
typedef struct {
consolecmd_t commands[CONSOLE_COMMANDS_MAX];
uint32_t commandCount;
consolevar_t variables[CONSOLE_VARIABLES_MAX];
uint32_t variableCount;
char_t line[CONSOLE_HISTORY_MAX][CONSOLE_LINE_MAX];
consolecmdexec_t execBuffer[CONSOLE_EXEC_BUFFER_MAX];
uint32_t execBufferCount;
consolecmd_t *cmdGet;
consolecmd_t *cmdSet;
} console_t;
extern console_t CONSOLE;
/**
* Initializes the console.
*/
void consoleInit();
/**
* Registers a console command.
*
* @param name The name of the command.
* @param function The function to execute when the command is called.
* @return The registered command.
*/
consolecmd_t * consoleRegCmd(const char_t *name, consolecmdfunc_t function);
/**
* Registers a console variable.
*
* @param name The name of the variable.
* @param value The initial value of the variable.
* @param event The event to register.
* @return The registered variable.
*/
consolevar_t * consoleRegVar(
const char_t *name,
const char_t *value,
consolevarchanged_t event
);
/**
* Sets the value of a console variable.
*
* @param name The name of the variable.
* @param value The new value of the variable.
*/
void consolePrint(
const char_t *message,
...
);
/**
* Executes a console command.
*
* @param line The line to execute.
*/
void consoleExec(const char_t *line);
/**
* Processes the console's pending commands.
*/
void consoleProcess();
/**
* Gets the value of a console variable.
*
* @param name The name of the variable.
* @return The value of the variable.
*/
void consoleCmdGet(const consolecmdexec_t *exec);
/**
* Sets the value of a console variable.
*
* @param name The name of the variable.
* @param value The new value of the variable.
*/
void consoleCmdSet(const consolecmdexec_t *exec);

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "consolecmd.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
void consoleCmdInit(
consolecmd_t *cmd,
const char_t *name,
consolecmdfunc_t function
) {
assertNotNull(cmd, "Command is NULL.");
assertNotNull(name, "Name is NULL.");
assertNotNull(function, "Function is NULL.");
assertStrLenMin(name, 1, "Name is empty.");
assertStrLenMax(name, CONSOLE_CMD_NAME_MAX, "Name is too long.");
memoryZero(cmd, sizeof(consolecmd_t));
stringCopy(cmd->name, name, CONSOLE_CMD_NAME_MAX);
cmd->function = function;
}

View File

@ -0,0 +1,38 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include "consoledefs.h"
typedef struct consolecmd_s consolecmd_t;
typedef struct {
const consolecmd_t *cmd;
uint32_t argc;
char_t argv[CONSOLE_CMD_ARGC_MAX][CONSOLE_LINE_MAX];
} consolecmdexec_t;
typedef void (*consolecmdfunc_t)(const consolecmdexec_t *exec);
typedef struct consolecmd_s {
char_t name[CONSOLE_CMD_NAME_MAX + 1];
consolecmdfunc_t function;
} consolecmd_t;
/**
* Initializes a console command.
*
* @param cmd Pointer to the console command.
* @param name The name of the command.
* @param function The function to execute when the command is called.
*/
void consoleCmdInit(
consolecmd_t *cmd,
const char_t *name,
consolecmdfunc_t function
);

View File

@ -0,0 +1,21 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#define CONSOLE_CMD_NAME_MAX 32
#define CONSOLE_CMD_ARGC_MAX 16
#define CONSOLE_COMMANDS_MAX 128
#define CONSOLE_VARIABLES_MAX 128
#define CONSOLE_LINE_MAX 256
#define CONSOLE_HISTORY_MAX 32
#define CONSOLE_EXEC_BUFFER_MAX 16
#define CONSOLE_VAR_NAME_MAX 32
#define CONSOLE_VAR_VALUE_MAX 128
#define CONSOLE_VAR_EVENTS_MAX 8

View File

@ -0,0 +1,64 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "consolevar.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
void consoleVarInit(
consolevar_t *var,
const char_t *name,
const char_t *value
) {
assertNotNull(var, "var must not be NULL");
assertNotNull(name, "name must not be NULL");
assertNotNull(value, "value must not be NULL");
assertStrLenMin(name, 1, "name must not be empty");
assertStrLenMax(name, CONSOLE_VAR_NAME_MAX, "name is too long");
assertStrLenMax(value, CONSOLE_VAR_VALUE_MAX, "value is too long");
memoryZero(var, sizeof(consolevar_t));
stringCopy(var->name, name, CONSOLE_VAR_NAME_MAX);
stringCopy(var->value, value, CONSOLE_VAR_VALUE_MAX);
}
void consoleVarInitListener(
consolevar_t *var,
const char_t *name,
const char_t *value,
consolevarchanged_t event
) {
consoleVarInit(var, name, value);
if(event) consoleVarListen(var, event);
}
void consoleVarSetValue(consolevar_t *var, const char_t *value) {
assertNotNull(var, "var must not be NULL");
assertNotNull(value, "value must not be NULL");
assertStrLenMax(value, CONSOLE_VAR_VALUE_MAX, "value is too long");
stringCopy(var->value, value, CONSOLE_VAR_VALUE_MAX);
uint8_t i = 0;
while (i < var->eventCount) {
var->events[i](var);
i++;
}
}
void consoleVarListen(consolevar_t *var, consolevarchanged_t event) {
assertNotNull(var, "var must not be NULL");
assertNotNull(event, "event must not be NULL");
assertTrue(
var->eventCount < CONSOLE_VAR_EVENTS_MAX,
"Event count is too high"
);
var->events[var->eventCount++] = event;
}

View File

@ -0,0 +1,65 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include "consoledefs.h"
typedef struct consolevar_s consolevar_t;
typedef void (*consolevarchanged_t)(const consolevar_t *var);
typedef struct consolevar_s {
char_t name[CONSOLE_VAR_NAME_MAX + 1];
char_t value[CONSOLE_VAR_VALUE_MAX + 1];
consolevarchanged_t events[CONSOLE_VAR_EVENTS_MAX];
uint8_t eventCount;
} consolevar_t;
/**
* Initializes a console variable.
*
* @param var Pointer to the console variable.
* @param name The name of the variable.
* @param value The initial value of the variable.
*/
void consoleVarInit(
consolevar_t *var,
const char_t *name,
const char_t *value
);
/**
* Initializes a console variable with a listener.
*
* @param var Pointer to the console variable.
* @param name The name of the variable.
* @param value The initial value of the variable.
* @param event The event to register.
*/
void consoleVarInitListener(
consolevar_t *var,
const char_t *name,
const char_t *value,
consolevarchanged_t event
);
/**
* Sets the value of a console variable.
*
* @param var Pointer to the console variable.
* @param value The new value of the variable.
*/
void consoleVarSetValue(consolevar_t *var, const char_t *value);
/**
* Registers an event to be called when the value of a console variable changes.
*
* @param var Pointer to the console variable.
* @param event The event to register.
*/
void consoleVarListen(consolevar_t *var, consolevarchanged_t event);

View File

@ -1,9 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240

View File

@ -1,65 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "scene.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "overworld/overworld.h"
scenetypecallback_t SCENE_CALLBACKS[] = {
// SCENE_TYPE_INITIAL
{ NULL, NULL, NULL, NULL, NULL },
// SCENE_TYPE_OVERWORLD
{
overworldInit,
overworldSceneInit,
overworldSceneDeinit,
overworldUpdate,
overworldRender
},
};
scene_t SCENE;
void sceneInit() {
memoryZero(&SCENE, sizeof(scene_t));
for(uint8_t i = 0; i < SCENE_TYPE_COUNT; i++) {
if(SCENE_CALLBACKS[i].onInit) SCENE_CALLBACKS[i].onInit();
}
sceneSet(SCENE_TYPE_OVERWORLD);// Testing
}
void sceneUpdate() {
assertTrue(SCENE.current < SCENE_TYPE_COUNT, "Invalid Scene Type");
if(SCENE.next != SCENE.current) {
SCENE.current = SCENE.next;
if(SCENE_CALLBACKS[SCENE.current].onActive) {
SCENE_CALLBACKS[SCENE.current].onActive();
}
}
if(SCENE_CALLBACKS[SCENE.current].onUpdate) {
SCENE_CALLBACKS[SCENE.current].onUpdate();
}
}
void sceneRender() {
assertTrue(SCENE.current < SCENE_TYPE_COUNT, "Invalid Scene Type");
if(SCENE_CALLBACKS[SCENE.current].onRender) {
SCENE_CALLBACKS[SCENE.current].onRender();
}
}
void sceneSet(const scenetype_t scene) {
assertTrue(SCENE.next < SCENE_TYPE_COUNT, "Invalid Scene Type");
SCENE.next = scene;
}

View File

@ -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 "dusk.h"
typedef enum {
SCENE_TYPE_INITIAL,
SCENE_TYPE_OVERWORLD,
} scenetype_t;
#define SCENE_TYPE_COUNT 2
typedef struct {
scenetype_t current;
scenetype_t next;
} scene_t;
typedef struct {
void (*onInit)();
void (*onActive)();
void (*onInactive)();
void (*onUpdate)();
void (*onRender)();
} scenetypecallback_t;
extern scene_t SCENE;
/**
* Initializes the scene system.
*/
void sceneInit();
/**
* Updates the scene system.
*/
void sceneUpdate();
/**
* Renders the scene system.
*
* Scene rendering is really just an abstraction meant to simplify things for
* the render host. It is not REQUIRED to be called at all and is not actually
* implemented in the dusk dir itself, it is overriden somewhere within the
* render host.
*/
void sceneRender();
/**
* Sets the current scene. This will happen at the start of the next tick.
*
* @param scene The scene to set.
*/
void sceneSet(const scenetype_t scene);

View File

@ -1,34 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "tileset.h"
tileset_t TILESETS[TILESET_COUNT] = {
// TILESET_NULL
{ 0, 0 },
// TILESET_FONT
{ 16, 14 }
};
tilesetid_t TILESET_SLOTS[TILESET_SLOT_COUNT] = {
TILESET_NULL,
TILESET_NULL,
TILESET_NULL,
TILESET_NULL
};
uint8_t tilesetGetSlot(const tilesetid_t id) {
uint8_t i = 0;
do {
if(TILESET_SLOTS[i] == id) return i;
} while(++i < TILESET_SLOT_COUNT);
return 0xFF;
}
bool_t tilesetIsBound(const tilesetid_t id) {
return tilesetGetSlot(id) != 0xFF;
}

View File

@ -1,53 +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 "tilesetdefs.h"
typedef enum {
TILESET_NULL,
TILESET_FONT
} tilesetid_t;
#define TILESET_COUNT 2
typedef struct {
uint8_t columns;
uint8_t rows;
} tileset_t;
extern tileset_t TILESETS[];
extern tilesetid_t TILESET_SLOTS[];
/**
* Binds a tileset to a slot. The host can also use this to say load a texture,
* or upload to VRAM.
*
* This does not guarantee anything on the game side, rendering is responsible
* for handling anything visual.
*
* @param id The tileset to bind.
* @param slot The slot to bind the tileset to.
*/
void tilesetBind(const tilesetid_t id, const uint8_t slot);
/**
* Gets the slot that a tileset is bound to, or 0xFF if not bound.
*
* @param id The tileset to check.
* @return The slot the tileset is bound to, or 0xFF if not bound.
*/
uint8_t tilesetGetSlot(const tilesetid_t id);
/**
* Checks if a tileset is bound to a slot.
*
* @param id The tileset to check.
* @return TRUE if the tileset is bound, FALSE otherwise.
*/
bool_t tilesetIsBound(const tilesetid_t id);

View File

@ -1,17 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#define TILESET_SLOT_0 0
#define TILESET_SLOT_1 1
#define TILESET_SLOT_2 2
#define TILESET_SLOT_3 3
#define TILESET_SLOT_UI TILESET_SLOT_0
#define TILESET_SLOT_ENTITIES TILESET_SLOT_1
#define TILESET_SLOT_MAP TILESET_SLOT_2
#define TILESET_SLOT_COUNT 4

View File

@ -14,6 +14,7 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <ctype.h>
typedef bool bool_t; typedef bool bool_t;
typedef char char_t; typedef char char_t;

View File

@ -6,8 +6,5 @@
# Sources # Sources
target_sources(${DUSK_TARGET_NAME} target_sources(${DUSK_TARGET_NAME}
PRIVATE PRIVATE
tile.c error.c
map.c
) )
# Subdirs

45
src/dusk/error/error.c Normal file
View File

@ -0,0 +1,45 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "error.h"
errorstack_t ERROR_STACK;
errorret_t error(const char_t *message, ...) {
return errorCode(1, message);
}
errorret_t errorCode(const errorret_t code, const char_t *message, ...) {
if(ERROR_STACK.code != ERROR_OK) {
snprintf(
ERROR_STACK.message,
ERROR_STACK_SIZE,
"Multiple errors encountered."
);
errorPrint();
}
va_list args;
va_start(args, message);
vsnprintf(ERROR_STACK.message, ERROR_STACK_SIZE, message, args);
va_end(args);
return ERROR_STACK.code = code;
}
bool_t errorCheck() {
return ERROR_STACK.code != ERROR_OK;
}
errorret_t errorPrint() {
if(ERROR_STACK.code == ERROR_OK) return ERROR_OK;
printf("Error: %s\n", ERROR_STACK.message);
errorret_t code = ERROR_STACK.code;
ERROR_STACK.code = ERROR_OK;
return code;
}

50
src/dusk/error/error.h Normal file
View File

@ -0,0 +1,50 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef int32_t errorret_t;
#define ERROR_OK 0
#define ERROR_STACK_SIZE 256
typedef struct {
char_t message[ERROR_STACK_SIZE + 1];
errorret_t code;
} errorstack_t;
extern errorstack_t ERROR_STACK;
/**
* Pushes an error message to the error stack.
*
* @param message Message to push to the error stack.
* @param ... Arguments to format the message with.
*/
errorret_t error(const char_t *message, ...);
/**
* Pushes an error message to the error stack with a given error code.
*
* @param code Error code to push to the error stack.
* @param message Message to push to the error stack.
* @param ... Arguments to format the message with.
*/
errorret_t errorCode(const errorret_t code, const char_t *message, ...);
/**
* Checks if an error has been pushed to the error stack.
*
* @return True if an error has been pushed to the error stack.
*/
bool_t errorCheck();
/**
* Prints the error stack to the console. This also clears the error stack.
*/
errorret_t errorPrint();

View File

@ -1,31 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "game.h"
#include "input.h"
#include "display/scene.h"
#include "util/memory.h"
game_t GAME;
void gameInit() {
memoryZero(&GAME, sizeof(game_t));
inputInit();
sceneInit();
}
void gameUpdate() {
GAME.tick++;
inputUpdate();
sceneUpdate();
}
void gameDispose() {
}

View File

@ -1,35 +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 GAME_TICK_RATE 60
typedef struct {
uint32_t tick;
} game_t;
extern game_t GAME;
/**
* Initializes the game.
*/
void gameInit();
/**
* Updates the game. The game is setup to handle "frames", aka 1/60th of a
* second. Typically more modern engines would use floats to tick game engine
* by a semi random delta time, but this engine is intended to work on really
* weak machines so we take the simple approach.
*/
void gameUpdate();
/**
* Disposes the game.
*/
void gameDispose();

View File

@ -1,37 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "input.h"
inputstate_t INPUT_CURRENT;
inputstate_t INPUT_LAST_FRAME;
void inputInit() {
INPUT_CURRENT = 0;
INPUT_LAST_FRAME = 0;
}
void inputUpdate() {
INPUT_LAST_FRAME = INPUT_CURRENT;
INPUT_CURRENT = inputPlatformState();
}
bool_t inputWasPressed(const inputstate_t state) {
return (INPUT_LAST_FRAME & state) == 0 && (INPUT_CURRENT & state) != 0;
}
bool_t inputWasReleased(const inputstate_t state) {
return (INPUT_LAST_FRAME & state) != 0 && (INPUT_CURRENT & state) == 0;
}
bool_t inputIsDown(const inputstate_t state) {
return (INPUT_CURRENT & state) != 0;
}
bool_t inputIsUp(const inputstate_t state) {
return (INPUT_CURRENT & state) == 0;
}

View File

@ -1,71 +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 uint16_t inputstate_t;
#define INPUT_MENU (1 << 0)
#define INPUT_UP (1 << 1)
#define INPUT_DOWN (1 << 2)
#define INPUT_LEFT (1 << 3)
#define INPUT_RIGHT (1 << 4)
#define INPUT_ACCEPT (1 << 5)
#define INPUT_BACK (1 << 6)
extern inputstate_t INPUT_CURRENT;
extern inputstate_t INPUT_LAST_FRAME;
/**
* Initializes the input system
*/
void inputInit();
/**
* Updates the input system for this tick.
*/
void inputUpdate();
/**
* Requests the platform let us know the current state of each input.
*
* @return Current input state.
*/
inputstate_t inputPlatformState();
/**
* Returns true if the input was pressed this frame, but not last frame.
*
* @param state Inputs to check, typically a single input.
* @return True if input was pressed this frame but not last frame.
*/
bool_t inputWasPressed(const inputstate_t state);
/**
* Returns true if the input was released this frame, but pressed last frame.
*
* @param state Inputs to check, typically a single input.
* @return True if input was released this frame but was pressed last frame.
*/
bool_t inputWasReleased(const inputstate_t state);
/**
* Returns true if the input is currently pressed.
*
* @param state Inputs to check, typically a single input.
* @return True if input is currently pressed.
*/
bool_t inputIsDown(const inputstate_t state);
/**
* Returns true if the input is currently released.
*
* @param state Inputs to check, typically a single input.
* @return True if input is currently released.
*/
bool_t inputIsUp(const inputstate_t state);

View File

@ -1,14 +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
overworld.c
)
# Subdirs
add_subdirectory(entity)
add_subdirectory(map)

View File

@ -1,150 +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 "overworld/overworld.h"
entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = {
{ NULL, NULL, NULL },
{ playerInit, playerUpdate, NULL },
{ npcInit, npcUpdate, npcInteract }
};
entity_t ENTITY_TEST;
void entityInit(
entity_t *ent,
const entitytype_t type
) {
assertNotNull(ent, "Entity cannot be NULL");
assertTrue(type < ENTITY_TYPE_COUNT, "Invalid entity type");
// Init values
memoryZero(ent, sizeof(entity_t));
ent->type = type;
// Call init
assertNotNull(ENTITY_CALLBACKS[type].init, "Entity type init callback err.");
ENTITY_CALLBACKS[type].init(ent);
entityTurn(ent, FACING_DIRECTION_SOUTH);
}
void entityUpdate(entity_t *ent) {
assertNotNull(ent, "Entity cannot be NULL");
assertTrue(ent->type < ENTITY_TYPE_COUNT, "Invalid entity type");
assertNotNull(ENTITY_CALLBACKS[ent->type].update, "Entity type update err.");
ENTITY_CALLBACKS[ent->type].update(ent);
// Handle moving
if(ent->subX > 0) {
ent->subX -= ENTITY_MOVE_SPEED;
if(ent->subX < 0) ent->subX = 0;
} else if(ent->subX < 0) {
ent->subX += ENTITY_MOVE_SPEED;
if(ent->subX > 0) ent->subX = 0;
} else if(ent->subY > 0) {
ent->subY -= ENTITY_MOVE_SPEED;
if(ent->subY < 0) ent->subY = 0;
} else if(ent->subY < 0) {
ent->subY += ENTITY_MOVE_SPEED;
if(ent->subY > 0) ent->subY = 0;
}
}
void entityTurn(entity_t *ent, const facingdir_t dir) {
assertNotNull(ent, "Entity cannot be NULL");
assertTrue(dir < FACING_DIRECTION_COUNT, "Invalid facing direction");
ent->direction = dir;
switch(dir) {
case FACING_DIRECTION_SOUTH:
ent->frame = 25;
break;
case FACING_DIRECTION_NORTH:
ent->frame = 24;
break;
case FACING_DIRECTION_EAST:
ent->frame = 26;
break;
case FACING_DIRECTION_WEST:
ent->frame = 27;
break;
}
}
void entityMove(entity_t *ent, const facingdir_t dir) {
assertNotNull(ent, "Entity cannot be NULL");
if(entityIsMoving(ent)) return;
entityTurn(ent, dir);
uint8_t targetX = ent->x, targetY = ent->y;
facingDirAdd(ent->direction, &targetX, &targetY);
// Check oob
if(targetX < 0 || targetX >= OVERWORLD.map.width) return;
if(targetY < 0 || targetY >= OVERWORLD.map.height) return;
// Check tile at target
uint8_t i = 0;
tileid_t tileId;
for(i = 0; i < OVERWORLD.map.layerCount; i++) {
tileId = mapGetTileIdAtPosition(&OVERWORLD.map, i, targetX, targetY);
if(tileIsSolid(tileId)) return;
}
// Check for entity at target
entity_t *atPos = overworldEntityGetAtPosition(targetX, targetY);
if(atPos != NULL) return;
// Commit to move
ent->x = targetX;
ent->y = targetY;
switch(dir) {
case FACING_DIRECTION_EAST:
ent->subX = -OVERWORLD_ENTITY_WIDTH;
break;
case FACING_DIRECTION_WEST:
ent->subX = OVERWORLD_ENTITY_WIDTH;
break;
case FACING_DIRECTION_NORTH:
ent->subY = -OVERWORLD_ENTITY_HEIGHT;
break;
case FACING_DIRECTION_SOUTH:
ent->subY = OVERWORLD_ENTITY_HEIGHT;
break;
}
}
bool_t entityIsMoving(const entity_t *ent) {
assertNotNull(ent, "Entity cannot be NULL");
return ent->subX != 0 || ent->subY != 0;
}
bool_t entityInteract(entity_t *ent) {
assertNotNull(ent, "Entity cannot be NULL.");
assertTrue(ent->type == ENTITY_TYPE_PLAYER, "Entity should be player.");
if(entityIsMoving(ent)) return false;
uint8_t targetX = ent->x, targetY = ent->y;
facingDirAdd(ent->direction, &targetX, &targetY);
entity_t *target = overworldEntityGetAtPosition(targetX, targetY);
assertFalse(target == ent, "Cannot interact with self.");
if(target == NULL) return false;
assertTrue(target->type < ENTITY_TYPE_COUNT, "Invalid entity type.");
if(ENTITY_CALLBACKS[target->type].interact == NULL) return false;
ENTITY_CALLBACKS[target->type].interact(ent, target);
return true;
}

View File

@ -1,89 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "facing.h"
#include "player.h"
#include "npc.h"
typedef enum {
ENTITY_TYPE_NULL,
ENTITY_TYPE_PLAYER,
ENTITY_TYPE_NPC
} entitytype_t;
#define ENTITY_TYPE_COUNT 3
typedef struct {
void (*init)(entity_t *);
void (*update)(entity_t *);
void (*interact)(entity_t *, entity_t *);
} entitycallback_t;
typedef struct _entity_t {
uint8_t x, y;
int8_t subX, subY;
uint8_t frame;
facingdir_t direction;
entitytype_t type;
union {
player_t player;
};
} entity_t;
#define ENTITY_MOVE_SPEED 3
/**
* Initializes an entity.
*
* @param entity The entity to initialize.
* @param type The type of entity to initialize.
*/
void entityInit(
entity_t *entity,
const entitytype_t type
);
/**
* Updates an entity.
*
* @param entity The entity to update.
*/
void entityUpdate(entity_t *entity);
/**
* Turns an entity in a given direction.
*
* @param entity The entity to turn.
* @param dir The direction to turn the entity.
*/
void entityTurn(entity_t *entity, const facingdir_t dir);
/**
* Moves an entity in a given direction.
*
* @param entity The entity to move.
* @param dir The direction to move the entity.
*/
void entityMove(entity_t *entity, const facingdir_t dir);
/**
* Checks if an entity is moving.
*
* @param entity The entity to check.
* @return TRUE if the entity is moving, FALSE otherwise.
*/
bool_t entityIsMoving(const entity_t *entity);
/**
* Make this entity attempt to interact with the world.
*
* @param entity The entity that is doing the interaction.
* @return TRUE if an entity was interacted with, FALSE otherwise.
*/
bool_t entityInteract(entity_t *entity);

View File

@ -1,65 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "facing.h"
#include "assert/assert.h"
void facingDirGetRelative(const facingdir_t dir, int8_t *x, int8_t *y) {
assertNotNull(x, "X cannot be NULL");
assertNotNull(y, "Y cannot be NULL");
switch(dir) {
case FACING_DIRECTION_SOUTH:
*x = 0;
*y = -1;
break;
case FACING_DIRECTION_EAST:
*x = 1;
*y = 0;
break;
case FACING_DIRECTION_NORTH:
*x = 0;
*y = 1;
break;
case FACING_DIRECTION_WEST:
*x = -1;
*y = 0;
break;
default:
assertUnreachable("Invalid facing direction.");
}
}
void facingDirAdd(const facingdir_t dir, uint8_t *x, uint8_t *y) {
assertNotNull(x, "X cannot be NULL");
assertNotNull(y, "Y cannot be NULL");
int8_t dx, dy;
facingDirGetRelative(dir, &dx, &dy);
*x += dx;
*y += dy;
}
facingdir_t facingDirFace(
const uint8_t x1, const uint8_t y1,
const uint8_t x2, const uint8_t y2
) {
if(x1 == x2) {
if(y1 < y2) return FACING_DIRECTION_SOUTH;
if(y1 > y2) return FACING_DIRECTION_NORTH;
} else if(y1 == y2) {
if(x1 < x2) return FACING_DIRECTION_WEST;
if(x1 > x2) return FACING_DIRECTION_EAST;
}
return FACING_DIRECTION_SOUTH;
}

View File

@ -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 "dusk.h"
typedef enum {
FACING_DIRECTION_SOUTH,
FACING_DIRECTION_EAST,
FACING_DIRECTION_NORTH,
FACING_DIRECTION_WEST
} facingdir_t;
#define FACING_DIRECTION_COUNT 4
/**
* Converts a facing direction to a directional vector.
*
* @param dir The direction to convert.
* @param x The x component of the vector.
* @param y The y component of the vector.
*/
void facingDirGetRelative(const facingdir_t dir, int8_t *x, int8_t *y);
/**
* Adds a facing direction to a position.
*
* @param dir The direction to add.
* @param x The x position to add to.
* @param y The y position to add to.
*/
void facingDirAdd(const facingdir_t dir, uint8_t *x, uint8_t *y);
/**
* Returns the facing direction to face from one position to another.
*
* @param x1 The x position to face from.
* @param y1 The y position to face from.
* @param x2 The x position to face to.
* @param y2 The y position to face to.
* @return The facing direction to face from one position to another.
*/
facingdir_t facingDirFace(
const uint8_t x1, const uint8_t y1,
const uint8_t x2, const uint8_t y2
);

View File

@ -1,19 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entity.h"
void npcInit(entity_t *ent) {
}
void npcUpdate(entity_t *ent) {
}
void npcInteract(entity_t *player, entity_t *ent) {
printf("Interact\n");
entityTurn(ent, facingDirFace(player->x, player->y, ent->x, ent->y));
}

View File

@ -1,37 +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 _entity_t entity_t;
typedef struct {
int32_t nothing;
} npc_t;
/**
* Handles initialization of an NPC.
*
* @param ent The entity to initialize as an NPC.
*/
void npcInit(entity_t *ent);
/**
* Handles updating an NPC.
*
* @param ent The entity to update as an NPC.
*/
void npcUpdate(entity_t *ent);
/**
* Handles interaction with an NPC.
*
* @param player The player entity interacting.
* @param ent The entity to interact with.
*/
void npcInteract(entity_t *player, entity_t *ent);

View File

@ -1,33 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "overworld/overworld.h"
#include "assert/assert.h"
#include "input.h"
void playerInit(entity_t *ent) {
assertNotNull(ent, "Entity cannot be NULL");
printf("Initializing player entity\n");
}
void playerUpdate(entity_t *ent) {
assertNotNull(ent, "Entity cannot be NULL");
if(inputIsDown(INPUT_RIGHT)) {
entityMove(ent, FACING_DIRECTION_EAST);
} else if(inputIsDown(INPUT_LEFT)) {
entityMove(ent, FACING_DIRECTION_WEST);
} else if(inputIsDown(INPUT_UP)) {
entityMove(ent, FACING_DIRECTION_NORTH);
} else if(inputIsDown(INPUT_DOWN)) {
entityMove(ent, FACING_DIRECTION_SOUTH);
} else if(inputWasPressed(INPUT_ACCEPT)) {
entityInteract(ent);
}
}

View File

@ -1,29 +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 _entity_t entity_t;
typedef struct {
int32_t nothing;
} player_t;
/**
* Handles initialization of the player.
*
* @param ent The entity to initialize as a player.
*/
void playerInit(entity_t *ent);
/**
* Handles updating the player.
*
* @param ent The entity to update as a player.
*/
void playerUpdate(entity_t *ent);

View File

@ -1,69 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "map.h"
#include "assert/assert.h"
#include "util/memory.h"
void mapInit(map_t *map) {
memoryZero(map, sizeof(map_t));
// Test
map->width = 16;
map->height = 16;
map->layerCount = 1;
memorySet(&map->tileIds, 0x01, sizeof(map->tileIds));
// Test size
assertTrue(
(map->width * map->height * map->layerCount) <= OVERWORLD_TILE_COUNT_MAX,
"Map size exceeds tile count."
);
}
uint32_t mapGetTileIndex(
const map_t *map,
const uint8_t layer,
const uint8_t x,
const uint8_t y
) {
assertNotNull(map, "Map cannot be NULL");
assertTrue(layer < map->layerCount, "Invalid layer");
assertTrue(x < map->width, "Invalid x");
assertTrue(y < map->height, "Invalid y");
return (layer * map->width * map->height) + (y * map->width) + x;
}
tileid_t mapGetTileId(
const map_t *map,
const uint32_t tileIndex
) {
assertNotNull(map, "Map cannot be NULL");
assertMapIndexValid(map, tileIndex);
return map->tileIds[tileIndex];
}
tileid_t mapGetTileIdAtPosition(
const map_t *map,
const uint8_t layer,
const uint8_t x,
const uint8_t y
) {
return mapGetTileId(map, mapGetTileIndex(map, layer, x, y));
}
tiledata_t mapGetTileData(
const map_t *map,
const uint32_t tileIndex
) {
assertNotNull(map, "Map cannot be NULL");
assertMapIndexValid(map, tileIndex);
return map->tileData[tileIndex];
}

View File

@ -1,82 +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 "overworld/overworlddefs.h"
#include "assert/assert.h"
#define assertMapIndexValid(map, index) \
assertTrue( \
index < (map->layerCount * map->width * map->height), \
"Invalid tile index" \
);
typedef struct {
tileid_t tileIds[OVERWORLD_TILE_COUNT_MAX];
tiledata_t tileData[OVERWORLD_TILE_COUNT_MAX];
uint8_t width, height, layerCount;
} map_t;
void mapInit(map_t *map);
/**
* Returns the index of the tile at the given position.
*
* @param map The map to get the tile index from.
* @param layer The layer to get the tile from.
* @param x The x position of the tile.
* @param y The y position of the tile.
* @return The index of the tile at the given position.
*/
uint32_t mapGetTileIndex(
const map_t *map,
const uint8_t layer,
const uint8_t x,
const uint8_t y
);
/**
* Returns the tile ID at the given index.
*
* @param map The map to get the tile ID from.
* @param tileIndex The index of the tile to get.
* @return The tile ID at the given index.
*/
tileid_t mapGetTileId(
const map_t *map,
const uint32_t tileIndex
);
/**
* Returns the tile ID at the given position. Shorthand for both getting the
* map index and the tile ID.
*
* @param map The map to get the tile ID from.
* @param layer The layer to get the tile from.
* @param x The x position of the tile.
* @param y The y position of the tile.
* @return The tile ID at the given position.
*/
tileid_t mapGetTileIdAtPosition(
const map_t *map,
const uint8_t layer,
const uint8_t x,
const uint8_t y
);
/**
* Returns the tile data at the given index.
*
* @param map The map to get the tile data from.
* @param tileIndex The index of the tile to get.
* @return The tile data at the given index.
*/
tiledata_t mapGetTileData(
const map_t *map,
const uint32_t tileIndex
);

View File

@ -1,19 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "tile.h"
bool_t tileIsSolid(const tileid_t id) {
switch(id) {
case TILE_ID_NULL:
case TILE_ID_GRASS:
return false;
default:
return true;
}
}

View File

@ -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"
typedef uint8_t tileid_t;
typedef struct {
uint32_t nothing;
} tiledata_t;
#define TILE_ID_NULL 0
#define TILE_ID_GRASS 1
/**
* Returns whether or not the tile is solid.
*
* @param id The tile id to check.
* @return Whether or not the tile is solid.
*/
bool_t tileIsSolid(const tileid_t id);

View File

@ -1,49 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "overworld.h"
#include "util/memory.h"
#include "assert/assert.h"
#include "display/tileset.h"
overworld_t OVERWORLD;
void overworldInit() {
memoryZero(&OVERWORLD, sizeof(overworld_t));
mapInit(&OVERWORLD.map);
entityInit(OVERWORLD.entities + OVERWORLD.entityCount++, ENTITY_TYPE_PLAYER);
entityInit(OVERWORLD.entities + OVERWORLD.entityCount++, ENTITY_TYPE_NPC);
OVERWORLD.entities[1].x = 2;
OVERWORLD.entities[1].y = 2;
}
void overworldSceneInit() {
tilesetBind(TILESET_FONT, TILESET_SLOT_ENTITIES);
}
void overworldSceneDeinit() {
}
void overworldUpdate() {
uint8_t i = 0;
while(i < OVERWORLD.entityCount) {
entityUpdate(OVERWORLD.entities + i++);
}
}
entity_t * overworldEntityGetAtPosition(const uint8_t x, const uint8_t y) {
uint8_t i = 0;
while(i < OVERWORLD.entityCount) {
entity_t * entity = OVERWORLD.entities + i++;
if(entity->x == x && entity->y == y) return entity;
}
return NULL;
}

View File

@ -1,57 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "overworlddefs.h"
#include "overworld/entity/entity.h"
#include "overworld/map/map.h"
typedef struct {
entity_t entities[OVERWORLD_ENTITY_COUNT_MAX];
uint8_t entityCount;
map_t map;
} overworld_t;
extern overworld_t OVERWORLD;
/**
* Initializes the overworld.
*/
void overworldInit();
/**
* Called by the scene system when the overworld scene is made to be the
* active scene.
*/
void overworldSceneInit();
/**
* Called by the scene system when the overworld scene is made to be inactive.
*/
void overworldSceneDeinit();
/**
* Called by the scene system when the overworld scene is to perform a tick.
*/
void overworldUpdate();
/**
* Platform level render method. Refer to sceneRender for information.
*/
void overworldRender();
/**
* Returns the entity at the given position.
*
* @param x The x position of the entity.
* @param y The y position of the entity.
* @return The entity at the given position.
*/
entity_t * overworldEntityGetAtPosition(
const uint8_t x,
const uint8_t y
);

View File

@ -1,16 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#define OVERWORLD_ENTITY_COUNT_MAX 8
#define OVERWORLD_ENTITY_WIDTH 32
#define OVERWORLD_ENTITY_HEIGHT 32
#define OVERWORLD_TILE_LAYER_COUNT_MAX 2
#define OVERWORLD_TILE_COUNT_PER_LAYER 256
#define OVERWORLD_TILE_COUNT_MAX (OVERWORLD_TILE_COUNT_PER_LAYER * OVERWORLD_TILE_LAYER_COUNT_MAX)
#define OVERWORLD_TILE_WIDTH OVERWORLD_ENTITY_WIDTH
#define OVERWORLD_TILE_HEIGHT OVERWORLD_ENTITY_HEIGHT

19
src/dusk/server/server.h Normal file
View File

@ -0,0 +1,19 @@
/**
* 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 {
} server_t;
extern server_t SERVER;
void serverInit();
void serverStart();
void serverDispose();

View File

@ -1,55 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "uimenu.h"
#include "util/memory.h"
#include "assert/assert.h"
#include "input.h"
void uiMenuInit(
uimenu_t *menu,
const uint8_t columns,
const uint8_t rows
) {
assertNotNull(menu, "Menu cannot be NULL.");
memoryZero(menu, sizeof(uimenu_t));
menu->columns = columns;
menu->rows = rows;
}
void uiMenuSetCursor(
uimenu_t *menu,
const uint8_t x,
const uint8_t y
) {
assertNotNull(menu, "Menu cannot be NULL.");
assert(x < menu->columns, "X position out of bounds.");
assert(y < menu->rows, "Y position out of bounds.");
menu->x = x;
menu->y = y;
}
void uiMenuMoveCursor(
uimenu_t *menu,
const int8_t x,
const int8_t y
) {
assertNotNull(menu, "Menu cannot be NULL.");
uiMenuSetCursor(
menu,
(menu->x + x) % menu->columns,
(menu->y + y) % menu->rows
);
}
uimenuaction_t uiMenuUpdate(uimenu_t *menu) {
assertNotNull(menu, "Menu cannot be NULL.");
}

View File

@ -1,69 +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 {
UI_MENU_ACTION_NOTHING,
UI_MENU_ACTION_CURSOR_MOVE,
UI_MENU_ACTION_CURSOR_ACCEPT,
UI_MENU_ACTION_CURSOR_CANCEL
} uimenuaction_t;
typedef struct {
uint8_t columns;
uint8_t rows;
uint8_t x;
uint8_t y;
} uimenu_t;
/**
* Initializes a menu.
*
* @param menu The menu to initialize.
* @param columns The amount of columns in the menu.
* @param rows The amount of rows in the menu.
*/
void uiMenuInit(
uimenu_t *menu,
const uint8_t columns,
const uint8_t rows
);
/**
* Sets the cursor of a menu.
*
* @param menu The menu to set the cursor of.
* @param x The x position of the cursor.
* @param y The y position of the cursor.
*/
void uiMenuSetCursor(
uimenu_t *menu,
const uint8_t x,
const uint8_t y
);
/**
* Moves the cursor of a menu.
*
* @param menu The menu to move the cursor of.
* @param x The amount to move the cursor on the x axis.
* @param y The amount to move the cursor on the y axis.
*/
void uiMenuMoveCursor(
uimenu_t *menu,
const int8_t x,
const int8_t y
);
/**
* Handles updating of a menu, this will accept input basically.
*
* @param menu The menu to update.
*/
uimenuaction_t uiMenuUpdate(uimenu_t *menu);

View File

@ -1,12 +1,11 @@
# Copyright (c) 2023 Dominic Masters # Copyright (c) 2025 Dominic Masters
# #
# This software is released under the MIT License. # This software is released under the MIT License.
# https:#opensource.org/licenses/MIT # https://opensource.org/licenses/MIT
# Sources # Sources
target_sources(${DUSK_TARGET_NAME} target_sources(${DUSK_TARGET_NAME}
PRIVATE PRIVATE
math.c
random.c
memory.c memory.c
string.c
) )

View File

@ -1,24 +0,0 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "math.h"
#include "assert/assert.h"
int32_t mathClampi32(
const int32_t value,
const int32_t min,
const int32_t max
) {
assertTrue(max >= min, "mathClampi32: Max must be >= Min");
if(value < min) return min;
if(value > max) return max;
return value;
}
int32_t mathModi32(int32_t value, int32_t modulo) {
return ((value % modulo) + modulo) % modulo;
}

View File

@ -1,48 +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"
/**
* Returns the minimum of two values.
* @param a First value.
* @param b Second value.
* @return The minimum of the two values.
*/
#define mathMin(a, b) ((a) < (b) ? (a) : (b))
/**
* Returns the maximum of two values.
* @param a First value.
* @param b Second value.
* @return The maximum of the two values.
*/
#define mathMax(a, b) ((a) > (b) ? (a) : (b))
/**
* Clamps a value between a min and max.
*
* @param value Value to clamp.
* @param min Minimum value to clamp value to.
* @param max Maximum value to clamp value to.
* @return The clamped value.
*/
int32_t mathClampi32(
const int32_t value,
const int32_t min,
const int32_t max
);
/**
* Returns the modulo of a value. For 32 bit integers.
*
* @param value Value to modulo.
* @param modulo Modulo value.
* @return The modulo of the value.
*/
int32_t mathModi32(int32_t value, int32_t modulo);

View File

@ -50,3 +50,11 @@ void memoryCopyRangeSafe(
assertTrue(copy <= sizeMax, "Size of memory to copy is too large."); assertTrue(copy <= sizeMax, "Size of memory to copy is too large.");
memoryCopy(dest, start, copy); 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);
}

View File

@ -65,3 +65,12 @@ void memoryCopyRangeSafe(
const void *end, const void *end,
const size_t sizeMax 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);

View File

@ -1,26 +0,0 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "random.h"
#include "assert/assert.h"
bool_t RANDOM_IS_INITIALIZED = false;
void randomInit() {
assertFalse(RANDOM_IS_INITIALIZED, "Random already initialized.");
srand(1234567890);
RANDOM_IS_INITIALIZED = true;
}
int32_t randomInt32() {
assertTrue(RANDOM_IS_INITIALIZED, "Random not initialized.");
return rand();
}
int32_t randomInt32Range(int32_t min, int32_t max) {
return min + (rand() % (max - min));
}

View File

@ -1,32 +0,0 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
extern bool_t RANDOM_IS_INITIALIZED;
/**
* Initializes the random number generator, and seeds it.
*/
void randomInit();
/**
* Returns a random 32-bit integer.
*
* @return Random 32-bit integer.
*/
int32_t randomInt32();
/**
* Returns a random 32-bit integer within a given range.
*
* @param min Minimum value.
* @param max Maximum value (exclusive).
* @return Random 32-bit integer within the given range.
*/
int32_t randomInt32Range(int32_t min, int32_t max);

50
src/dusk/util/string.c Normal file
View File

@ -0,0 +1,50 @@
/**
* 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"
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");
assertStrLenMax(src, destSize, "src is too long");
strncpy(dest, src, destSize);
}
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);
}

55
src/dusk/util/string.h Normal file
View File

@ -0,0 +1,55 @@
/**
* 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);

View File

@ -3,13 +3,21 @@
# This software is released under the MIT License. # This software is released under the MIT License.
# https://opensource.org/licenses/MIT # https://opensource.org/licenses/MIT
# Libs
target_link_libraries(${DUSK_TARGET_NAME}
PUBLIC
)
# Includes
target_include_directories(${DUSK_TARGET_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
# Sources # Sources
target_sources(${DUSK_TARGET_NAME} target_sources(${DUSK_TARGET_NAME}
PRIVATE PRIVATE
entity.c main.c
facing.c
player.c
npc.c
) )
# Subdirs # Subdirs

39
src/dusktest/main.c Normal file
View File

@ -0,0 +1,39 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "dusk.h"
#include "error/error.h"
#include "console/console.h"
#include "util/string.h"
bool_t exitRequested = false;
void cmdExit(const consolecmdexec_t *exec) {
exitRequested = true;
}
void testChange(const consolevar_t *var) {
consolePrint("Variable '%s' changed to '%s'.", var->name, var->value);
}
int main() {
consoleInit();
consoleRegCmd("exit", cmdExit);
consoleRegVar("test", "Hello", testChange);
consolePrint(" = Dusk Console = ");
char_t buffer[CONSOLE_LINE_MAX];
while(fgets(buffer, sizeof(buffer), stdin)) {
consoleExec(buffer);
consoleProcess();
if(exitRequested) break;
}
printf("Goodbye!\n");
return 0;
}