archivemuh

This commit is contained in:
2025-08-20 19:18:38 -05:00
parent 1411c2e96b
commit fbfcbe9578
173 changed files with 2802 additions and 13 deletions

View File

@@ -1,15 +1,34 @@
# Copyright (c) 2025 Dominic Masters
#
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
add_subdirectory(dusk)
# Libs
target_link_libraries(${DUSK_TARGET_NAME}
PUBLIC
m
)
if(DUSK_TARGET_SYSTEM STREQUAL "linux")
add_subdirectory(dusksdl2)
elseif(DUSK_TARGET_SYSTEM STREQUAL "psp")
add_subdirectory(duskpsp)
add_subdirectory(dusksdl2)
else()
message(FATAL_ERROR "Unsupported target system: ${DUSK_TARGET_SYSTEM}")
endif()
# Includes
target_include_directories(${DUSK_TARGET_NAME}
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
main.c
)
# Subdirs
add_subdirectory(assert)
add_subdirectory(console)
add_subdirectory(display)
add_subdirectory(engine)
add_subdirectory(error)
add_subdirectory(input)
add_subdirectory(locale)
add_subdirectory(thread)
add_subdirectory(time)
add_subdirectory(util)

View File

@@ -12,4 +12,12 @@ target_sources(${DUSK_TARGET_NAME}
)
# Subdirectories
add_subdirectory(cmd)
add_subdirectory(cmd)
# Compiler flags.
if(DUSK_TARGET_SYSTEM STREQUAL "linux")
target_compile_definitions(${DUSK_TARGET_NAME}
PRIVATE
DUSK_CONSOLE_TERMIOS=1
)
endif()

View File

@@ -7,9 +7,9 @@
#pragma once
#include "console/console.h"
#include "game.h"
#include "engine/engine.h"
void cmdQuit(const consolecmdexec_t *exec) {
consolePrint("Quitting application...");
GAME.running = false;
consolePrint("Quitting...");
ENGINE.running = false;
}

View File

@@ -14,8 +14,6 @@
#include "console/cmd/cmdset.h"
#include "console/cmd/cmdget.h"
#include "input.h"
console_t CONSOLE;
void consoleInit() {
@@ -28,6 +26,17 @@ void consoleInit() {
consoleRegCmd("echo", cmdEcho);
consolePrint(" = Dawn Console = ");
#if DUSK_CONSOLE_TERMIOS
// Create termios session.
struct termios newTermios;
tcgetattr(STDIN_FILENO, &CONSOLE.originalTermios);
// Disable canonical mode & echo
newTermios.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newTermios);
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);// Set stdin to non-blocking
#endif
}
consolecmd_t * consoleRegCmd(const char_t *name, consolecmdfunc_t function) {
@@ -274,14 +283,13 @@ void consoleExec(const char_t *line) {
// May move these later
void consoleUpdate() {
if(inputPressed(INPUT_BIND_CONSOLE)) {
CONSOLE.visible = !CONSOLE.visible;
if(CONSOLE.visible) {
consolePrint("Console opened.");
} else {
consolePrint("Console closed.");
#if DUSK_CONSOLE_TERMIOS
char_t c;
for(;;) {
if(!read(STDIN_FILENO, &c, 1)) break;
putchar(c);
}
}
#endif
for(uint32_t i = 0; i < CONSOLE.execBufferCount; i++) {
consolecmdexec_t *exec = &CONSOLE.execBuffer[i];
@@ -326,4 +334,11 @@ void consoleUpdate() {
// Clear the exec buffer
CONSOLE.execBufferCount = 0;
}
void consoleDispose(void) {
// Reset termios
#if DUSK_CONSOLE_TERMIOS
tcsetattr(STDIN_FILENO, TCSANOW, &CONSOLE.originalTermios);
#endif
}

View File

@@ -9,6 +9,12 @@
#include "consolevar.h"
#include "consolecmd.h"
#if DUSK_CONSOLE_TERMIOS
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>
#endif
typedef enum {
CONSOLE_EXEC_STATE_INITIAL,
CONSOLE_EXEC_STATE_PARSE_CMD,
@@ -39,12 +45,12 @@ typedef struct {
consolecmd_t *cmdSet;
bool_t visible;
// May move these later
// #if DUSK_KEYBOARD_SUPPORT == 1
// char_t inputBuffer[CONSOLE_LINE_MAX];
// int32_t inputIndex;
// #endif
#if DUSK_CONSOLE_TERMIOS
struct termios originalTermios;
char_t inputBuffer[CONSOLE_LINE_MAX];
int32_t inputBufferLength;
#endif
} console_t;
extern console_t CONSOLE;
@@ -100,7 +106,7 @@ void consoleExec(const char_t *line);
*/
void consoleUpdate();
void cmdGet(const consolecmdexec_t *exec);
void cmdSet(const consolecmdexec_t *exec);
void cmdEcho(const consolecmdexec_t *exec);
void cmdQuit(const consolecmdexec_t *exec);
/**
* Disposes of the console.
*/
void consoleDispose(void);

View File

@@ -6,5 +6,5 @@
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
camera.c
render.c
)

View File

@@ -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)

View File

@@ -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
)

View File

@@ -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();
}
}
}

View File

@@ -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);

View File

@@ -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
)

View File

@@ -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;
}
}

View File

@@ -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
);

View File

@@ -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;
}

View File

@@ -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
);

View File

@@ -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");
}
}

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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
)

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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;

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 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

View File

@@ -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();
}
}

View File

@@ -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);

View File

@@ -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 >= DUSK_TIME_STEP) {
sceneUpdate();
uiTextboxUpdate();
eventUpdate();
inputUpdate();
timeSinceLastTick -= DUSK_TIME_STEP;
TIME.lastTick = TIME.time;
}
if(inputPressed(INPUT_BIND_QUIT)) consoleExec("quit");
consoleUpdate();
}
void gameDispose(void) {
sceneDispose();
}

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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
);

View File

@@ -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;

View File

@@ -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"

View File

@@ -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.");
}
}

View File

@@ -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
);

View File

@@ -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
chunk.c
overworld.c
)

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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);

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"
#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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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>

View File

@@ -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)

View File

@@ -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)

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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
mesh.c
quad.c
)

View File

@@ -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));
}

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 "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);

View File

@@ -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
};
}

View File

@@ -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
);

View File

@@ -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
)

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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)

View File

@@ -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();
}
}

View File

@@ -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);

View File

@@ -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
spritebatch.c
)
# Subdirs
# add_subdirectory(draw)

View File

@@ -1,75 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "spritebatch.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "console/console.h"
spritebatch_t SPRITEBATCH;
void spriteBatchInit() {
SPRITEBATCH.spriteCount = 0;
SPRITEBATCH.currentTexture = NULL;
meshInit(
&SPRITEBATCH.mesh,
GL_TRIANGLES,
SPRITEBATCH_VERTEX_COUNT,
&SPRITEBATCH.vertices[0]
);
}
void spriteBatchPush(
texture_t *texture,
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
) {
// Need to flush?
if(
SPRITEBATCH.currentTexture != texture ||
SPRITEBATCH.spriteCount >= SPRITEBATCH_SPRITES_MAX
) {
spriteBatchFlush();
SPRITEBATCH.currentTexture = texture;
}
quadBuffer(
&SPRITEBATCH.vertices[SPRITEBATCH.spriteCount * QUAD_VERTEX_COUNT],
minX, minY, maxX, maxY,
r, g, b, a,
u0, v0, u1, v1
);
SPRITEBATCH.spriteCount++;
}
void spriteBatchClear() {
SPRITEBATCH.spriteCount = 0;
SPRITEBATCH.currentTexture = NULL;
}
void spriteBatchFlush() {
if(SPRITEBATCH.spriteCount == 0) return;
textureBind(SPRITEBATCH.currentTexture);
meshDraw(&SPRITEBATCH.mesh, 0, QUAD_VERTEX_COUNT * SPRITEBATCH.spriteCount);
spriteBatchClear();
}
void spriteBatchDispose() {
meshDispose(&SPRITEBATCH.mesh);
}

View File

@@ -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 "display/mesh/quad.h"
#include "display/texture/texture.h"
#define SPRITEBATCH_SPRITES_MAX 1
#define SPRITEBATCH_VERTEX_COUNT (SPRITEBATCH_SPRITES_MAX * QUAD_VERTEX_COUNT)
typedef struct {
mesh_t mesh;
int32_t spriteCount;
texture_t *currentTexture;
meshvertex_t vertices[SPRITEBATCH_VERTEX_COUNT];
} spritebatch_t;
extern spritebatch_t SPRITEBATCH;
/**
* Initializes a sprite batch.
*
* @param spriteBatch The sprite batch to initialize.
*/
void spriteBatchInit();
void spriteBatchPush(
texture_t *texture,
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
);
void spriteBatchClear();
void spriteBatchFlush();
void spriteBatchDispose();

View File

@@ -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
texture.c
)
# Subdirs
# add_subdirectory(draw)

View File

@@ -1,78 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "texture.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/math.h"
void textureInit(
texture_t *texture,
const int32_t width,
const int32_t height,
const GLenum format,
const uint8_t *data
) {
assertNotNull(texture, "Texture cannot be NULL");
assertTrue(width > 0 && height > 0, "Width and height must be greater than 0");
#if PSP
assertTrue(
width == mathNextPowTwo(width),
"Width must be powers of 2 for PSP"
);
assertTrue(
height == mathNextPowTwo(height),
"Height must be powers of 2 for PSP"
);
#endif
memoryZero(texture, sizeof(texture_t));
texture->width = width;
texture->height = height;
glGenTextures(1, &texture->id);
glBindTexture(GL_TEXTURE_2D, texture->id);
glTexImage2D(
GL_TEXTURE_2D, 0, format, width, height, 0,
format, GL_UNSIGNED_BYTE, data
);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
}
void textureBind(const texture_t *texture) {
if(texture == NULL) {
glDisable(GL_TEXTURE_2D);
// glBindTexture(GL_TEXTURE_2D, 0);
return;
}
assertTrue(
texture->id != 0,
"Texture ID must not be 0"
);
assertTrue(
texture->width > 0 && texture->height > 0,
"Texture width and height must be greater than 0"
);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture->id);
}
void textureDispose(texture_t *texture) {
assertNotNull(texture, "Texture cannot be NULL");
assertTrue(texture->id != 0, "Texture ID must not be 0");
glDeleteTextures(1, &texture->id);
}

View File

@@ -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 "dusksdl2.h"
typedef struct {
GLuint id;
int32_t width;
int32_t height;
} texture_t;
/**
* Initializes a texture.
*
* @param texture The texture to initialize.
* @param width The width of the texture.
* @param height The height of the texture.
* @param format The format of the texture (e.g., GL_RGBA, GL_ALPHA).
* @param data The pixel data for the texture.
*/
void textureInit(
texture_t *texture,
const int32_t width,
const int32_t height,
const GLenum format,
const uint8_t *data
);
/**
* Binds a texture for rendering.
*
* @param texture The texture to bind.
*/
void textureBind(const texture_t *texture);
/**
* Disposes a texture.
*
* @param texture The texture to dispose.
*/
void textureDispose(texture_t *texture);

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
renderconsole.c
renderfps.c
rendertext.c
renderui.c
rendertextbox.c
)

View File

@@ -1,29 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "renderconsole.h"
#include "console/console.h"
#include "display/ui/rendertext.h"
void renderConsoleDraw(void) {
if(!CONSOLE.visible) return;
int32_t i = 0;
char_t *line;
do {
line = CONSOLE.line[i];
if(line[0] == '\0') {
i++;
continue;
}
renderTextDraw(
0, (CONSOLE_HISTORY_MAX - i - 1) * FONT_TILE_HEIGHT, line,
0xFF, 0xFF, 0xFF
);
i++;
} while(i < CONSOLE_HISTORY_MAX);
}

View File

@@ -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 "dusksdl2.h"
/**
* Draws the console overlay.
*/
void renderConsoleDraw(void);

View File

@@ -1,46 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "renderfps.h"
#include "display/render.h"
#include "display/ui/rendertext.h"
#include "time.h"
#include "game.h"
float_t RENDER_FPS_AVG = -1.0f;
float_t RENDER_TPS_AVG = -1.0f;
void renderFPSDraw(void) {
if(TIME.delta > 0) {
float_t fps = 1.0f / TIME.realDelta;
if(RENDER_FPS_AVG == -1.0f) {
RENDER_FPS_AVG = fps;
} else {
RENDER_FPS_AVG = (RENDER_FPS_AVG + fps) / 2.0f;
}
}
if(TIME.time != TIME.lastTick) {
float_t timeSinceLastTick = TIME.realTime - TIME.lastTick;
float_t tps = 1.0f / timeSinceLastTick;
if(RENDER_TPS_AVG == -1.0f) {
RENDER_TPS_AVG = tps;
} else {
RENDER_TPS_AVG = (RENDER_TPS_AVG + tps) / 2.0f;
}
}
char_t buffer[64];
snprintf(buffer, sizeof(buffer), "%.1f/%.1f", RENDER_FPS_AVG, RENDER_TPS_AVG);
int32_t width, height;
renderTextMeasure(buffer, &width, &height);
renderTextDraw(RENDER_WIDTH - width, 0, buffer, 0x00, 0xFF, 0x00);
}

View File

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

View File

@@ -1,159 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "rendertext.h"
#include "display/render.h"
#include "assert/assert.h"
#include "display/spritebatch/spritebatch.h"
#include "util/memory.h"
#include "util/math.h"
texture_t RENDER_TEXT_TEXTURE;
static mesh_t RENDER_TEXT_QUAD_MESH;
void renderTextInit(void) {
const int32_t cols = FONT_COLUMN_COUNT;
const int32_t rows = (FONT_TILE_COUNT + cols - 1) / cols;
const int32_t inputFontWidth = cols * FONT_TILE_WIDTH;
const int32_t inputFontHeight = rows * FONT_TILE_HEIGHT;
int32_t outputFontWidth = inputFontWidth;
int32_t outputFontHeight = inputFontHeight;
// Round up to nearest power of 2
#if PSP
outputFontWidth = mathNextPowTwo(inputFontWidth);
outputFontHeight = mathNextPowTwo(inputFontHeight);
#endif
uint8_t *pixels = (uint8_t *)memoryAllocate(
outputFontWidth * outputFontHeight *
sizeof(uint8_t)
);
// Buffer the pixels.
for(int tileIndex = 0; tileIndex < FONT_TILE_COUNT; ++tileIndex) {
const int32_t tileX = (tileIndex % FONT_COLUMN_COUNT) * FONT_TILE_WIDTH;
const int32_t tileY = (tileIndex / FONT_COLUMN_COUNT) * FONT_TILE_HEIGHT;
const uint8_t* tile = TILE_PIXEL_DATA[tileIndex];
for (int y = 0; y < FONT_TILE_HEIGHT; ++y) {
for (int x = 0; x < FONT_TILE_WIDTH; ++x) {
const int32_t pixel = (tileY + y) * outputFontWidth + (tileX + x);
const int32_t pixelOffset = pixel;
uint8_t value = tile[y * FONT_TILE_WIDTH + x];
pixels[pixel] = value ? 0xFF : 0x00; // Alpha channel
}
}
}
textureInit(
&RENDER_TEXT_TEXTURE,
outputFontWidth, outputFontHeight,
GL_ALPHA, pixels
);
memoryFree(pixels);
}
void renderTextDrawChar(
const float_t x,
const float_t y,
const char_t c,
const uint8_t r,
const uint8_t g,
const uint8_t b
) {
int32_t tileIndex = (int32_t)(c) - FONT_CHAR_START;
assertTrue(
tileIndex >= 0 && tileIndex < FONT_TILE_COUNT,
"Character is out of bounds for font tiles"
);
const float_t w = (float)RENDER_TEXT_TEXTURE.width;
const float_t h = (float)RENDER_TEXT_TEXTURE.height;
const int32_t tileX = (tileIndex % FONT_COLUMN_COUNT);
const int32_t tileY = (tileIndex / FONT_COLUMN_COUNT);
spriteBatchPush(
&RENDER_TEXT_TEXTURE,
x, y,
x + FONT_TILE_WIDTH, y + FONT_TILE_HEIGHT,
r, g, b, 0xFF,
(tileX * FONT_TILE_WIDTH) / w,
(tileY * FONT_TILE_HEIGHT) / h,
((tileX + 1) * FONT_TILE_WIDTH) / w,
((tileY + 1) * FONT_TILE_HEIGHT) / h
);
}
void renderTextDraw(
const float_t x,
const float_t y,
const char_t *text,
const uint8_t r,
const uint8_t g,
const uint8_t b
) {
assertNotNull(text, "Text cannot be NULL");
float_t posX = x;
float_t posY = y;
char_t c;
int32_t i = 0;
while((c = text[i++]) != '\0') {
if(c == '\n') {
posX = x;
posY += FONT_TILE_HEIGHT;
continue;
}
renderTextDrawChar(posX, posY, c, r, g, b);
posX += FONT_TILE_WIDTH;
}
}
void renderTextMeasure(
const char_t *text,
int32_t *outWidth,
int32_t *outHeight
) {
assertNotNull(text, "Text cannot be NULL");
assertNotNull(outWidth, "Output width pointer cannot be NULL");
assertNotNull(outHeight, "Output height pointer cannot be NULL");
int32_t width = 0;
int32_t height = FONT_TILE_HEIGHT;
int32_t lineWidth = 0;
char_t c;
int32_t i = 0;
while((c = text[i++]) != '\0') {
if(c == '\n') {
if(lineWidth > width) {
width = lineWidth;
}
lineWidth = 0;
height += FONT_TILE_HEIGHT;
continue;
}
lineWidth += FONT_TILE_WIDTH;
}
if(lineWidth > width) {
width = lineWidth;
}
*outWidth = width;
*outHeight = height;
}
void renderTextDispose(void) {
textureDispose(&RENDER_TEXT_TEXTURE);
}

View File

@@ -1,74 +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 "ui/font.h"
#include "display/texture/texture.h"
extern texture_t RENDER_TEXT_TEXTURE;
/**
* Initializes the text rendering system.
*/
void renderTextInit(void);
/**
* Draws a single character at the specified position.
*
* @param x The x-coordinate to draw the character at.
* @param y The y-coordinate to draw the character at.
* @param c The character to draw.
* @param r The red component of the color (0-255).
* @param g The green component of the color (0-255).
* @param b The blue component of the color (0-255).
*/
void renderTextDrawChar(
const float_t x,
const float_t y,
const char_t c,
const uint8_t r,
const uint8_t g,
const uint8_t b
);
/**
* Draws a string of text at the specified position.
*
* @param x The x-coordinate to draw the text at.
* @param y The y-coordinate to draw the text at.
* @param text The null-terminated string of text to draw.
* @param r The red component of the color (0-255).
* @param g The green component of the color (0-255).
* @param b The blue component of the color (0-255).
*/
void renderTextDraw(
const float_t x,
const float_t y,
const char_t *text,
const uint8_t r,
const uint8_t g,
const uint8_t b
);
/**
* Measures the width and height of the given text string when rendered.
*
* @param text The null-terminated string of text to measure.
* @param outWidth Pointer to store the measured width in pixels.
* @param outHeight Pointer to store the measured height in pixels.
*/
void renderTextMeasure(
const char_t *text,
int32_t *outWidth,
int32_t *outHeight
);
/**
* Disposes of the text rendering system, freeing any allocated resources.
*/
void renderTextDispose(void);

View File

@@ -1,70 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "rendertextbox.h"
#include "ui/uitextbox.h"
#include "display/ui/rendertext.h"
#include "display/spritebatch/spritebatch.h"
#include "assert/assert.h"
void renderTextboxDraw(void) {
if(!UI_TEXTBOX.visible) return;
// Background
spriteBatchPush(
NULL,
0, RENDER_HEIGHT - UI_TEXTBOX_HEIGHT,
RENDER_WIDTH, RENDER_HEIGHT,
0x00, 0x00, 0x00, 0xFF,
0.0f, 0.0f, 1.0f, 1.0f
);
uint32_t x = 0;
uint32_t y = RENDER_HEIGHT - UI_TEXTBOX_HEIGHT;
if(UI_TEXTBOX.charsRevealed > 0) {
uint8_t charsRendered = 0;
// For each line
for(uint8_t i = 0; i < UI_TEXTBOX_LINES_PER_PAGE; i++) {
// Get count of chars in the line
uint8_t lineLength = UI_TEXTBOX.lineLengths[
i + (UI_TEXTBOX.page * UI_TEXTBOX_LINES_PER_PAGE)
];
if(lineLength == 0) continue;
// Determine how many chars left to render
uint8_t lineChars = UI_TEXTBOX.charsRevealed - charsRendered;
// Don't render more than in line
if(lineChars > lineLength) lineChars = lineLength;
assertTrue(lineChars > 0, "Line chars must be greater than 0");
// Update how many rendered
charsRendered += lineChars;
for(uint8_t j = 0; j < lineChars; j++) {
renderTextDrawChar(
x + UI_TEXTBOX_PADDING_X + UI_TEXTBOX_BORDER_WIDTH + (
j * FONT_TILE_WIDTH
),
y + UI_TEXTBOX_PADDING_Y + UI_TEXTBOX_BORDER_HEIGHT + (
i * FONT_TILE_HEIGHT
),
UI_TEXTBOX.text[
(i * UI_TEXTBOX_CHARS_PER_LINE) + j +
(UI_TEXTBOX.page * UI_TEXTBOX_CHARS_PER_PAGE)
],
0xFF, 0xFF, 0xFF
);
}
// Check if we're done rendering text
if(UI_TEXTBOX.charsRevealed - charsRendered == 0) break;
}
}
}

View File

@@ -1,10 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
void renderTextboxDraw(void);

View File

@@ -1,63 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "renderui.h"
#include "display/ui/rendertext.h"
#include "display/ui/renderconsole.h"
#include "display/ui/renderfps.h"
#include "display/ui/rendertextbox.h"
#include "display/spritebatch/spritebatch.h"
#include "display/camera/camera.h"
renderuicallback_t RENDER_UI_CALLBACKS[] = {
{
.init = renderTextInit,
.dispose = renderTextDispose
},
{
.draw = renderTextboxDraw,
},
{
.draw = renderConsoleDraw,
},
{
.draw = renderFPSDraw,
},
};
#define RENDER_UI_CALLBACKS_COUNT ( \
sizeof(RENDER_UI_CALLBACKS) / sizeof(RENDER_UI_CALLBACKS[0]) \
)
void renderUIInit(void) {
for (int32_t i = 0; i < RENDER_UI_CALLBACKS_COUNT; i++) {
if(!RENDER_UI_CALLBACKS[i].init) continue;
RENDER_UI_CALLBACKS[i].init();
}
}
void renderUIDraw(void) {
cameraUIPush();
for (int32_t i = 0; i < RENDER_UI_CALLBACKS_COUNT; i++) {
if(!RENDER_UI_CALLBACKS[i].draw) continue;
RENDER_UI_CALLBACKS[i].draw();
}
spriteBatchFlush();
cameraUIPop();
}
void renderUIDispose(void) {
for (int32_t i = 0; i < RENDER_UI_CALLBACKS_COUNT; i++) {
if(!RENDER_UI_CALLBACKS[i].dispose) continue;
RENDER_UI_CALLBACKS[i].dispose();
}
}

View File

@@ -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 "dusksdl2.h"
typedef struct {
void (*init)(void);
void (*draw)(void);
void (*dispose)(void);
} renderuicallback_t;
extern renderuicallback_t RENDER_UI_CALLBACKS[];
/**
* Initialize the UI rendering system.
*/
void renderUIInit(void);
/**
* Draw the UI elements.
*/
void renderUIDraw(void);
/**
* Dispose of the UI rendering system.
*/
void renderUIDispose(void);

View File

@@ -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 "dusk.h"
#include <SDL2/SDL.h>
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#include <cglm/cglm.h>

View File

@@ -1,44 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "dusksdl2input.h"
inputstate_t inputStateGet() {
inputstate_t state = 0;
#if INPUT_SUPPORT_GAMEPAD
// Get gamepad state.
for(int32_t i = 0; i < SDL_NumJoysticks(); i++) {
if(!SDL_IsGameController(i)) continue;
SDL_GameController *controller = SDL_GameControllerOpen(i);
if(!controller) continue;
inputsdlbuttonmap_t *map = INPUT_SDL_BUTTON_MAP;
do {
if(SDL_GameControllerGetButton(controller, map->button)) {
state |= map->bind;
}
map++;
} while(map->bind != 0);
}
#endif
// Get keyboard state.
#if INPUT_SUPPORT_KEYBOARD
const uint8_t *keyboardState = SDL_GetKeyboardState(NULL);
inputsdlkbmap_t *kbmap = INPUT_SDL_KEYBOARD_MAP;
do {
if(keyboardState[kbmap->code]) {
state |= kbmap->bind;
}
kbmap++;
} while(kbmap->bind != 0);
#endif
return state;
}

View File

@@ -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 "dusksdl2.h"
#include "input.h"
#ifndef INPUT_SUPPORT_GAMEPAD
#define INPUT_SUPPORT_GAMEPAD 1
#endif
#ifndef INPUT_SUPPORT_KEYBOARD
#define INPUT_SUPPORT_KEYBOARD 1
#endif
#if INPUT_SUPPORT_GAMEPAD
typedef struct {
const SDL_GameControllerButton button;
const inputbind_t bind;
} inputsdlbuttonmap_t;
static const inputsdlbuttonmap_t INPUT_SDL_BUTTON_MAP[] = {
{ SDL_CONTROLLER_BUTTON_DPAD_UP, INPUT_BIND_UP },
{ SDL_CONTROLLER_BUTTON_DPAD_DOWN, INPUT_BIND_DOWN },
{ SDL_CONTROLLER_BUTTON_DPAD_LEFT, INPUT_BIND_LEFT },
{ SDL_CONTROLLER_BUTTON_DPAD_RIGHT, INPUT_BIND_RIGHT },
{ SDL_CONTROLLER_BUTTON_A, INPUT_BIND_ACTION },
{ SDL_CONTROLLER_BUTTON_B, INPUT_BIND_CANCEL },
{ SDL_CONTROLLER_BUTTON_BACK, INPUT_BIND_CONSOLE },
{ 0, 0 }
};
#endif
#if INPUT_SUPPORT_KEYBOARD
typedef struct {
SDL_Scancode code;
inputbind_t bind;
} inputsdlkbmap_t;
static const inputsdlkbmap_t INPUT_SDL_KEYBOARD_MAP[] = {
{ SDL_SCANCODE_W, INPUT_BIND_UP },
{ SDL_SCANCODE_S, INPUT_BIND_DOWN },
{ SDL_SCANCODE_A, INPUT_BIND_LEFT },
{ SDL_SCANCODE_D, INPUT_BIND_RIGHT },
{ SDL_SCANCODE_LEFT, INPUT_BIND_LEFT },
{ SDL_SCANCODE_RIGHT, INPUT_BIND_RIGHT },
{ SDL_SCANCODE_UP, INPUT_BIND_UP },
{ SDL_SCANCODE_DOWN, INPUT_BIND_DOWN },
{ SDL_SCANCODE_RETURN, INPUT_BIND_ACTION },
{ SDL_SCANCODE_SPACE, INPUT_BIND_ACTION },
{ SDL_SCANCODE_E, INPUT_BIND_ACTION },
{ SDL_SCANCODE_ESCAPE, INPUT_BIND_CANCEL },
{ SDL_SCANCODE_BACKSPACE, INPUT_BIND_CANCEL },
{ SDL_SCANCODE_TAB, INPUT_BIND_CONSOLE },
{ SDL_SCANCODE_GRAVE, INPUT_BIND_CONSOLE },
{ SDL_SCANCODE_Q, INPUT_BIND_QUIT },
{ 0, 0 }
};
#endif

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