This commit is contained in:
2025-08-11 13:29:29 -05:00
parent 26ecf67472
commit e9d2f4904a
39 changed files with 569 additions and 829 deletions

View File

@@ -94,6 +94,10 @@ if(NOT TARGET pspsdk)
pspdisplay
pspge
pspctrl
pspgu
pspaudio
pspaudiolib
psputility
)
endif()
endif()

View File

@@ -1,20 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
include(FetchContent)
FetchContent_Declare(
raylib
URL https://github.com/raysan5/raylib/archive/refs/tags/5.5.tar.gz
URL_HASH MD5=61638c4c2c097fbca1d6a71e4da36c16
)
FetchContent_GetProperties(raylib)
FetchContent_MakeAvailable(raylib)
# Define expected target if not already defined
if(NOT TARGET raylib)
set(raylib_FOUND FALSE CACHE INTERNAL "Whether raylib was found")
else()
set(raylib_FOUND TRUE CACHE INTERNAL "Whether raylib was found")
endif()

View File

@@ -6,9 +6,10 @@
add_subdirectory(dusk)
if(DUSK_TARGET_SYSTEM STREQUAL "linux")
add_subdirectory(duskraylib)
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()

View File

@@ -6,6 +6,7 @@
# Libs
target_link_libraries(${DUSK_TARGET_NAME}
PUBLIC
m
)
# Includes
@@ -25,6 +26,7 @@ target_sources(${DUSK_TARGET_NAME}
add_subdirectory(assert)
add_subdirectory(console)
add_subdirectory(display)
add_subdirectory(error)
add_subdirectory(entity)
add_subdirectory(event)
add_subdirectory(item)

View File

@@ -6,24 +6,28 @@
*/
#pragma once
#include "dusk.h"
#include "error/error.h"
#define RENDER_WIDTH 320
#define RENDER_HEIGHT 240
#ifndef RENDER_WIDTH
#define RENDER_WIDTH 320
#endif
#ifndef RENDER_HEIGHT
#define RENDER_HEIGHT 240
#endif
extern uint8_t RENDER_FRAME;
/**
* Initializes the rendering system.
*/
void renderInit(void);
errorret_t renderInit(void);
/**
* Tells the rendering system to actually draw the frame.
*/
void renderDraw(void);
errorret_t renderDraw(void);
/**
* Disposes of the rendering system.
*/
void renderDispose(void);
errorret_t renderDispose(void);

View File

@@ -17,7 +17,7 @@ typedef struct {
#define PLAYER_ENTITY_ID (UINT32_MAX-1)
#define PLAYER_MOVE_SPEED 1.0f
#define PLAYER_MOVE_SPEED_XY 0.70710678118f
#define PLAYER_MOVE_SPEED_XY 0.7f
#define PLAYER_INTERACT_RANGE (TILE_WIDTH_HEIGHT + (TILE_WIDTH_HEIGHT / 3))
#define PLAYER_INTERACT_ANGLE 0.68359375f

View File

@@ -1,12 +1,10 @@
# 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
)
# Subdirs
error.c
)

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

@@ -0,0 +1,125 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert/assert.h"
#include "error.h"
#include "util/memory.h"
#include "util/string.h"
errorret_t errorThrowImpl(
errorstate_t *state,
errorcode_t code,
const char_t *file,
const char_t *function,
const int32_t line,
const char_t *message,
...
) {
assertNotNull(state, "Error state cannot be NULL");
assertTrue(code != ERROR_OK, "Error code must not be OK");
assertNotNull(file, "File cannot be NULL");
assertNotNull(function, "Function cannot be NULL");
assertTrue(line >= 0, "File pointer must be valid");
assertNotNull(message, "Message cannot be NULL");
memoryZero(state, sizeof(errorstate_t));
state->code = code;
// Format args.
va_list args;
va_start(args, message);
// Get length of formatted message
va_list argsCopy;
va_copy(argsCopy, args);
int32_t len = stringFormatVA(NULL, 0, message, argsCopy);
va_end(argsCopy);
// Create string to hold the formatted message
state->message = (char_t *)memoryAllocate(len + 1);
stringFormatVA(state->message, len + 1, message, args);
va_end(args);
// Format lines
len = stringFormat(NULL, 0, ERROR_LINE_FORMAT, file, line, function);
assertTrue(len >= 0, "Line formatting failed");
state->lines = (char_t *)memoryAllocate(len + 1);
stringFormat(state->lines, len + 1, ERROR_LINE_FORMAT, file, line, function);
return (errorret_t) {
.code = code,
.state = state
};
}
errorret_t errorOkImpl() {
return (errorret_t) {
.code = ERROR_OK,
.state = NULL
};
}
errorret_t errorChainImpl(
const errorret_t retval,
const char_t *file,
const char_t *function,
const int32_t line
) {
if (retval.code == ERROR_OK) return retval;
assertNotNull(retval.state, "Error state cannot be NULL");
assertNotNull(retval.state->message, "Message cannot be NULL");
// Create a new line string.
int32_t newLineLen = snprintf(NULL, 0, ERROR_LINE_FORMAT, file, line, function);
assertTrue(newLineLen >= 0, "Line formatting failed");
char_t *newLine = (char_t *)memoryAllocate(newLineLen + 1);
snprintf(newLine, newLineLen + 1, ERROR_LINE_FORMAT, file, line, function);
// Resize the existing lines to accommodate the new line
size_t existingLen = strlen(retval.state->lines);
memoryResize(
(void**)&retval.state->lines,
existingLen,
existingLen + newLineLen + 1
);
// Now append the new line to the existing lines
memoryCopy(
retval.state->lines + existingLen,
newLine,
newLineLen + 1
);
// Cleanup the temporary new line
memoryFree(newLine);
return retval;
}
void errorCatch(const errorret_t retval) {
if (retval.code == ERROR_OK) return;
assertNotNull(retval.state, "Error state cannot be NULL");
assertNotNull(retval.state->message, "Message cannot be NULL");
memoryFree((void*)retval.state->message);
}
errorret_t errorPrint(const errorret_t retval) {
if (retval.code == ERROR_OK) return retval;
assertNotNull(retval.state, "Error state cannot be NULL");
assertNotNull(retval.state->message, "Message cannot be NULL");
printf(
ERROR_PRINT_FORMAT,
retval.state->code,
retval.state->message,
retval.state->lines
);
return retval;
}

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

@@ -0,0 +1,137 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef uint8_t errorcode_t;
typedef struct {
errorcode_t code;
char_t *message;
char_t *lines;
} errorstate_t;
typedef struct {
errorcode_t code;
errorstate_t *state;
} errorret_t;
static const errorcode_t ERROR_OK = 0;
static const errorcode_t ERROR_NOT_OK = 1;
static const char_t *ERROR_PRINT_FORMAT = "Error (%d): %s\n%s";
static const char_t *ERROR_LINE_FORMAT = " at %s:%d in function %s\n";
static errorstate_t ERROR_STATE = {
.code = ERROR_OK,
.message = NULL,
.lines = NULL
};
/**
* Sets the error state with the provided code and message.
*
* @param state The error state to initialize.
* @param code The error code to set.
* @param file The file where the error occurred.
* @param function The function where the error occurred.
* @param line The line number where the error occurred.
* @param message The error message.
* @param args The arguments for the error message.
* @return The error code.
*/
errorret_t errorThrowImpl(
errorstate_t *state,
errorcode_t code,
const char_t *file,
const char_t *function,
const int32_t line,
const char_t *message,
...
);
/**
* Returns an error state with no error.
*
* @return An error state with code ERROR_OK.
*/
errorret_t errorOkImpl();
/**
* Chains an error state, allowing for error propagation.
*
* @param retval The return value containing the error state.
* @param file The file where the error occurred.
* @param function The function where the error occurred.
* @param line The line number where the error occurred.
* @return The error code if an error occurred, otherwise continues execution.
*/
errorret_t errorChainImpl(
const errorret_t retval,
const char_t *file,
const char_t *function,
const int32_t line
);
/**
* Catches an error and handles it.
*
* @param retval The return value containing the error state.
*/
void errorCatch(const errorret_t retval);
/**
* Prints the error state to the console.
*
* @param retval The return value containing the error state.
* @return Passed retval for chaining.
*/
errorret_t errorPrint(const errorret_t retval);
/**
* Throws an error with a formatted message.
*
* @param code The error code to throw.
* @param message The format string for the error message.
* @param ... Additional arguments for the format string.
* @return The error code.
*/
#define errorThrowWithCode(code, message, ... ) \
return errorThrowImpl(\
&ERROR_STATE, (code), __FILE__, __func__, __LINE__, (message), \
__VA_ARGS__ \
)
/**
* Throws an error with a default error code of ERROR_NOT_OK.
*
* @param message The format string for the error message.
* @param ... Additional arguments for the format string.
* @return The error code.
*/
#define errorThrow(message, ...) \
return errorThrowImpl(\
&ERROR_STATE, ERROR_NOT_OK, __FILE__, __func__, __LINE__, (message), \
__VA_ARGS__ \
)
/**
* Checks if a child method errored, and if it did, then send it up the chain.
* @param retval The return value containing the error state.
* @return The error code if an error occurred, otherwise continues execution.
*/
#define errorChain(retval) \
if ((retval).code != ERROR_OK) { \
return errorChainImpl(retval, __FILE__, __func__, __LINE__); \
}
/**
* Returns without an error.
*/
#define errorOk() \
return errorOkImpl()
// EOF

View File

@@ -8,6 +8,19 @@
#include "memory.h"
#include "assert/assert.h"
void * memoryAllocate(const size_t size) {
assertTrue(size > 0, "Cannot allocate 0 bytes of memory.");
void *ptr = malloc(size);
assertNotNull(ptr, "Memory allocation failed.");
return ptr;
}
void memoryFree(void *ptr) {
assertNotNull(ptr, "Cannot free NULL memory.");
free(ptr);
}
void memoryCopy(void *dest, const void *src, const size_t size) {
assertNotNull(dest, "Cannot copy to NULL memory.");
assertNotNull(src, "Cannot copy from NULL memory.");
@@ -57,4 +70,27 @@ int_t memoryCompare(
assertNotNull(b, "Cannot compare NULL memory.");
assertTrue(size > 0, "Cannot compare 0 bytes of memory.");
return memcmp(a, b, size);
}
void memoryReallocate(void **ptr, const size_t size) {
assertNotNull(ptr, "Cannot reallocate NULL pointer.");
assertTrue(size > 0, "Cannot reallocate to 0 bytes of memory.");
void *newPointer = memoryAllocate(size);
assertNotNull(newPointer, "Memory reallocation failed.");
memoryFree(*ptr);
*ptr = newPointer;
}
void memoryResize(void **ptr, const size_t oldSize, const size_t newSize) {
assertNotNull(ptr, "Cannot resize NULL pointer.");
if(newSize == oldSize) return;
if(oldSize == 0) return memoryReallocate(ptr, newSize);
assertTrue(newSize > oldSize, "New size must be greater than old size.");
void *newPointer = memoryAllocate(newSize);
assertNotNull(newPointer, "Memory resizing failed.");
memoryCopy(newPointer, *ptr, oldSize);
memoryFree(*ptr);
*ptr = newPointer;
}

View File

@@ -87,4 +87,21 @@ int_t memoryCompare(
const void *a,
const void *b,
const size_t size
);
);
/**
* Reallocates memory.
*
* @param ptr The pointer to the memory to reallocate.
* @param size The new size of the memory.
*/
void memoryReallocate(void **ptr, const size_t size);
/**
* Reallocates memory, but copies existing data to the new memory.
*
* @param ptr The pointer to the memory to resize.
* @param oldSize The old size of the memory.
* @param newSize The new size of the memory.
*/
void memoryResize(void **ptr, const size_t oldSize, const size_t newSize);

View File

@@ -54,29 +54,30 @@ char_t * stringToken(char_t *str, const char_t *delim) {
int32_t stringFormat(
char_t *dest,
const size_t destSize,
char_t *format,
const char_t *format,
...
) {
assertNotNull(dest, "dest must not be NULL");
assertNotNull(format, "format must not be NULL");
assertTrue(destSize > 0, "destSize must be greater than 0");
va_list args;
va_start(args, format);
int32_t result = stringFormatVA(dest, destSize, format, args);
int32_t len = stringFormatVA(dest, destSize, format, args);
va_end(args);
return result;
return len;
}
int32_t stringFormatVA(
char_t *dest,
const size_t destSize,
const char_t *format,
va_list args
const va_list args
) {
assertNotNull(dest, "dest must not be NULL");
assertNotNull(format, "format must not be NULL");
if(dest == NULL) {
int32_t ret = vsnprintf(NULL, 0, format, args);
assertTrue(ret >= 0, "Failed to format string.");
return ret;
}
assertTrue(destSize > 0, "destSize must be greater than 0");
int32_t ret = vsnprintf(dest, destSize, format, args);
assertTrue(ret >= 0, "Failed to format string.");

View File

@@ -57,19 +57,28 @@ char_t * stringToken(char_t *str, const char_t *delim);
/**
* Formats a string.
*
* @param dest The destination string.
* @param destSize The size of the destination string exc. null terminator.
* @param dest The destination string, or NULL to get the length of the
* formatted string.
* @param destSize The size of the destination string exc. null terminator, can
* be anything if dest is NULL.
* @param format The format string.
* @param ... The arguments to format.
* @return The number of characters written.
*/
int32_t stringFormat(char_t *dest, const size_t destSize, char_t *format, ...);
int32_t stringFormat(
char_t *dest,
const size_t destSize,
const char_t *format,
...
);
/**
* Formats a string using a va_list.
*
* @param dest The destination string.
* @param destSize The size of the destination string exc. null terminator.
* @param dest The destination string, or NULL to get the length of the
* formatted string.
* @param destSize The size of the destination string exc. null terminator, can
* be anything if dest is NULL.
* @param format The format string.
* @param args The va_list of arguments.
* @return The number of characters written.
@@ -78,7 +87,7 @@ int32_t stringFormatVA(
char_t *dest,
const size_t destSize,
const char_t *format,
va_list args
const va_list args
);
/**

View File

@@ -5,25 +5,34 @@
# Libs
find_package(pspsdk REQUIRED)
find_package(SDL2 REQUIRED)
target_link_libraries(${DUSK_TARGET_NAME}
PRIVATE
pspsdk
# 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
)
# Includes
target_include_directories(${DUSK_TARGET_NAME}
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
${SDL2_INCLUDE_DIRS}
)
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
input.c
main.c
duskpsp.c
)
# Subdirs
add_subdirectory(display)
# Subdirs

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 "render.h"
#include "input.h"
void renderInit(void) {
pspDebugScreenInit();
}
void renderDraw(void) {
sceDisplayWaitVblankStart();
pspDebugScreenSetXY(0, 2);
pspDebugScreenPrintf("Checking buttons \n");
if(inputIsDown(INPUT_BIND_UP)) {
pspDebugScreenPrintf("Up pressed\n");
}
if(inputIsDown(INPUT_BIND_DOWN)) {
pspDebugScreenPrintf("Down pressed\n");
}
if(inputIsDown(INPUT_BIND_LEFT)) {
pspDebugScreenPrintf("Left pressed\n");
}
if(inputIsDown(INPUT_BIND_RIGHT)) {
pspDebugScreenPrintf("Right pressed\n");
}
if(inputIsDown(INPUT_BIND_ACTION)) {
pspDebugScreenPrintf("Action pressed\n");
}
if(inputIsDown(INPUT_BIND_CANCEL)) {
pspDebugScreenPrintf("Cancel pressed\n");
}
}
void renderDispose(void) {
}

View File

@@ -7,5 +7,5 @@
#include "duskpsp.h"
PSP_MODULE_INFO("Dusk", 0, 1, 0);
PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER | THREAD_ATTR_VFPU);
// PSP_MODULE_INFO("Dusk", 0, 1, 0);
// PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER | THREAD_ATTR_VFPU);

View File

@@ -1,27 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "duskpsp.h"
#include "input.h"
uint8_t inputStateGet() {
SceCtrlData pad;
sceCtrlReadBufferPositive(&pad, 1);
if(pad.Buttons == 0) return 0;
uint8_t state = 0;
if(pad.Buttons & PSP_CTRL_UP) state |= INPUT_BIND_UP;
if(pad.Buttons & PSP_CTRL_DOWN) state |= INPUT_BIND_DOWN;
if(pad.Buttons & PSP_CTRL_LEFT) state |= INPUT_BIND_LEFT;
if(pad.Buttons & PSP_CTRL_RIGHT) state |= INPUT_BIND_RIGHT;
if(pad.Buttons & PSP_CTRL_CROSS) state |= INPUT_BIND_ACTION;
if(pad.Buttons & PSP_CTRL_CIRCLE) state |= INPUT_BIND_CANCEL;
return state;
}

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 "display/render.h"
#include "game.h"
int_t exit_callback(int arg1, int arg2, void *common) {
sceKernelExitGame();
return 0;
}
int_t callback_thread(SceSize args, void *argp) {
int cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
sceKernelRegisterExitCallback(cbid);
sceKernelSleepThreadCB();
return 0;
}
int_t setup_callbacks(void) {
int thid = sceKernelCreateThread("update_thread", callback_thread, 0x11, 0xFA0, 0, 0);
if(thid >= 0) {
sceKernelStartThread(thid, 0, 0);
}
return thid;
}
void main(void) {
setup_callbacks();
renderInit();
gameInit();
sceCtrlSetSamplingCycle(0);
sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);
while(true) {
gameUpdate();
renderDraw();
}
gameDispose();
renderDispose();
}

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
drawscene.c
drawoverworld.c
drawui.c
drawconsole.c
)

View File

@@ -1,33 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "drawconsole.h"
#include "console/console.h"
void drawConsoleInit(void) {
}
void drawConsoleDraw(void) {
CONSOLE.visible = true; // Set console to visible by default
if(!CONSOLE.visible) return;
BeginBlendMode(BLEND_ALPHA);
size_t i = 0;
char_t *line;
int32_t fontSize = 10;
do {
line = CONSOLE.line[i];
if(line[0] == '\0') {
i++;
continue;
}
DrawText(line, 0, i * fontSize, fontSize, ORANGE);
i++;
} while(i < CONSOLE_HISTORY_MAX);
EndBlendMode();
}

View File

@@ -1,19 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskraylib.h"
/**
* Initializes the console drawing system.
*/
void drawConsoleInit(void);
/**
* Draws the console to the screen.
*/
void drawConsoleDraw(void);

View File

@@ -1,132 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "drawoverworld.h"
#include "world/chunk.h"
#include "world/overworld.h"
#include "display/render.h"
#include "assert/assert.h"
Camera2D DRAW_OVERWORLD_CAMERA = { 0 };
void drawOverworldInit(void) {
DRAW_OVERWORLD_CAMERA = (Camera2D){
.offset = { 0, 0 },
.target = { 0, 0 },
.rotation = 0.0f,
.zoom = 1.0f
};
}
void drawOverworldDraw(void) {
if(OVERWORLD_CAMERA_X < RENDER_WIDTH / 2) {
DRAW_OVERWORLD_CAMERA.target.x = 0;
} else {
DRAW_OVERWORLD_CAMERA.target.x = OVERWORLD_CAMERA_X - (RENDER_WIDTH / 2);
}
if(OVERWORLD_CAMERA_Y < RENDER_HEIGHT / 2) {
DRAW_OVERWORLD_CAMERA.target.y = 0;
} else {
DRAW_OVERWORLD_CAMERA.target.y = OVERWORLD_CAMERA_Y - (RENDER_HEIGHT / 2);
}
BeginMode2D(DRAW_OVERWORLD_CAMERA);
// Bottom layer
chunk_t *chunk = CHUNK_MAP.chunks;
Color colors[] = {
RED, GREEN, BLUE, YELLOW, PURPLE, ORANGE, PINK, BROWN,
DARKGRAY, LIGHTGRAY, DARKBLUE, DARKGREEN, DARKPURPLE,
DARKBROWN
};
uint8_t colorCount = sizeof(colors) / sizeof(Color);
do {
// Base layer
for(uint8_t i = 0; i < CHUNK_TILE_COUNT; i++) {
tile_t tile = chunk->tilesBase[i];
if(tile == 0) continue; // Skip empty tiles
uint32_t tilemapIndex = tile - 1; // Convert to zero-based index
uint32_t x = (uint32_t)chunk->x * CHUNK_WIDTH * TILE_WIDTH_HEIGHT + (i % CHUNK_WIDTH) * TILE_WIDTH_HEIGHT;
uint32_t y = (uint32_t)chunk->y * CHUNK_HEIGHT * TILE_WIDTH_HEIGHT + (i / CHUNK_WIDTH) * TILE_WIDTH_HEIGHT;
uint32_t tilemapX = (tilemapIndex % (RENDER_TILEMAP_TEXTURE.width / TILE_WIDTH_HEIGHT)) * TILE_WIDTH_HEIGHT;
uint32_t tilemapY = (tilemapIndex / (RENDER_TILEMAP_TEXTURE.width / TILE_WIDTH_HEIGHT)) * TILE_WIDTH_HEIGHT;
DrawTextureRec(
RENDER_TILEMAP_TEXTURE,
(Rectangle){
tilemapX,
tilemapY,
TILE_WIDTH_HEIGHT,
TILE_WIDTH_HEIGHT
},
(Vector2){ x, y },
WHITE
);
}
// Base overlay layer
for(uint8_t i = 0; i < CHUNK_TILE_COUNT; i++) {
tile_t tile = chunk->tilesBaseOverlay[i];
if(tile == 0) continue; // Skip empty tiles
uint32_t tilemapIndex = tile - 1; // Convert to zero-based index
uint32_t x = (uint32_t)chunk->x * CHUNK_WIDTH * TILE_WIDTH_HEIGHT + (i % CHUNK_WIDTH) * TILE_WIDTH_HEIGHT;
uint32_t y = (uint32_t)chunk->y * CHUNK_HEIGHT * TILE_WIDTH_HEIGHT + (i / CHUNK_WIDTH) * TILE_WIDTH_HEIGHT;
uint32_t tilemapX = (tilemapIndex % (RENDER_TILEMAP_TEXTURE.width / TILE_WIDTH_HEIGHT)) * TILE_WIDTH_HEIGHT;
uint32_t tilemapY = (tilemapIndex / (RENDER_TILEMAP_TEXTURE.width / TILE_WIDTH_HEIGHT)) * TILE_WIDTH_HEIGHT;
DrawTextureRec(
RENDER_TILEMAP_TEXTURE,
(Rectangle){
tilemapX,
tilemapY,
TILE_WIDTH_HEIGHT,
TILE_WIDTH_HEIGHT
},
(Vector2){ x, y },
WHITE
);
}
chunk++;
} while(chunk < CHUNK_MAP.chunks + CHUNK_MAP_COUNT);
// Entities
entity_t *entity = ENTITIES;
do {
drawOverworldDrawEntity(entity++);
} while(entity < ENTITIES + ENTITY_COUNT_MAX);
EndMode2D();
}
void drawOverworldDrawEntity(const entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL");
if(entity->type == ENTITY_TYPE_NULL) return; // Skip null entities
uint32_t x = (uint32_t)(entity->x);
uint32_t y = (uint32_t)(entity->y);
uint32_t row = 0;
uint32_t col = entity->dir;
DrawTextureRec(
RENDER_ENTITIES_TEXTURE,
(Rectangle){
col * TILE_WIDTH_HEIGHT,
row * TILE_WIDTH_HEIGHT,
TILE_WIDTH_HEIGHT,
TILE_WIDTH_HEIGHT
},
(Vector2){ x, y },
WHITE
);
}

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 "duskraylib.h"
#include "entity/entity.h"
/**
* Initializes the overworld drawing system.
*/
void drawOverworldInit(void);
/**
* Renders the overworld, including map and characters.
*/
void drawOverworldDraw(void);
/**
* Renders a specific entity in the overworld.
*
* @param entity The entity to render.
*/
void drawOverworldDrawEntity(const entity_t *entity);

View File

@@ -1,25 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "drawscene.h"
#include "display/scene.h"
#include "assert/assert.h"
#include "drawoverworld.h"
void drawScene(void) {
switch(SCENE_CURRENT){
case SCENE_INITIAL:
break;
case SCENE_OVERWORLD:
drawOverworldDraw();
break;
default:
assertUnreachable("Unknown scene in drawScene");
}
}

View File

@@ -1,122 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "drawui.h"
#include "ui/uitextbox.h"
#include "util/memory.h"
#include "assert/assert.h"
#include "display/render.h"
void drawUIInit(void) {
}
void drawUI() {
drawUITextbox();
}
void drawUITextbox() {
if(!UI_TEXTBOX.visible) return;
// Semi-transparent dark blue
Color background = (Color){ 0, 0, 128, 128 };
uint32_t x = 0;
uint32_t y = RENDER_HEIGHT - UI_TEXTBOX_HEIGHT;
DrawRectangle(
x, y,
UI_TEXTBOX_WIDTH, UI_TEXTBOX_HEIGHT,
background
);
BeginBlendMode(BLEND_ALPHA);
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++) {
drawUIChar(
UI_TEXTBOX.text[
(i * UI_TEXTBOX_CHARS_PER_LINE) + j +
(UI_TEXTBOX.page * UI_TEXTBOX_CHARS_PER_PAGE)
],
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
),
WHITE
);
}
// Check if we're done rendering text
if(UI_TEXTBOX.charsRevealed - charsRendered == 0) break;
}
}
EndBlendMode();
}
void drawUIText(
const uint8_t *text,
const uint16_t length,
const int32_t x,
const int32_t y,
const Color color
) {
assertNotNull(text, "Text to draw cannot be NULL");
if(length == 0) return;
BeginBlendMode(BLEND_ALPHA);
for(uint16_t i = 0; i < length; i++) {
drawUIChar(text[i], x + (i * FONT_TILE_WIDTH), y, color);
}
EndBlendMode();
}
void drawUIChar(
const char_t c,
const int32_t x,
const int32_t y,
const Color color
) {
if(c < ' ' || c > '~') return; // Only print printable ASCII characters
int32_t charIndex = TILE_INDEXES[c - FONT_CHAR_START];
int32_t charX = (charIndex % FONT_COLUMN_COUNT) * FONT_TILE_WIDTH;
int32_t charY = (charIndex / FONT_COLUMN_COUNT) * FONT_TILE_HEIGHT;
// FLip Y coordinate for raylib texture coordinates
DrawTextureRec(
RENDER_FONT_TEXTURE,
(Rectangle) {
(float) charX,
(float) charY, // Flip Y by adding height
(float) FONT_TILE_WIDTH,
(float) FONT_TILE_HEIGHT // Negative height to flip the texture
},
(Vector2) { x, y }, // Add FONT_HEIGHT to flip Y
color
);
}

View File

@@ -1,56 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskraylib.h"
/**
* Initializes the UI drawing system.
*/
void drawUIInit(void);
/**
* Draws all the UI elements to the screen in the correct order.
*/
void drawUI(void);
/**
* Draws the UI textbox to the screen.
*/
void drawUITextbox(void);
/**
* Draws text to the screen at the specified position with the given color.
*
* @param text The text to draw.
* @param length The length of the text to draw.
* @param x The x-coordinate to draw the text at.
* @param y The y-coordinate to draw the text at.
* @param color The color of the text.
*/
void drawUIText(
const uint8_t *text,
const uint16_t length,
const int32_t x,
const int32_t y,
const Color color
);
/**
* Draws a single character to the screen at the specified position with the given color.
*
* @param c The character to draw.
* @param x The x-coordinate to draw the character at.
* @param y The y-coordinate to draw the character at.
* @param color The color of the character.
*/
void drawUIChar(
const char_t c,
const int32_t x,
const int32_t y,
const Color color
);

View File

@@ -1,92 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "render.h"
#include "display/draw/drawscene.h"
#include "display/draw/drawoverworld.h"
#include "display/draw/drawui.h"
#include "display/draw/drawconsole.h"
#include "assert/assert.h"
RenderTexture2D RENDER_SCREEN_TEXTURE;
Texture2D RENDER_TILEMAP_TEXTURE;
Texture2D RENDER_ENTITIES_TEXTURE;
Texture2D RENDER_FONT_TEXTURE;
void renderInit(void) {
InitWindow(
RENDER_WIDTH * 3,
RENDER_HEIGHT * 3,
"Dusk Raylib Render"
);
SetTargetFPS(60);
SetWindowState(
FLAG_WINDOW_RESIZABLE |
FLAG_WINDOW_HIGHDPI |
FLAG_VSYNC_HINT
);
RENDER_SCREEN_TEXTURE = LoadRenderTexture(RENDER_WIDTH, RENDER_HEIGHT);
RENDER_TILEMAP_TEXTURE = LoadTexture("../data/tilemap.png");
RENDER_ENTITIES_TEXTURE = LoadTexture("../data/entities.png");
RENDER_FONT_TEXTURE = LoadTexture("../data/minogram_6x10.png");
drawOverworldInit();
drawUIInit();
drawConsoleInit();
}
void renderDraw(void) {
// Draw the actual game.
BeginBlendMode(BLEND_ALPHA_PREMULTIPLY);
BeginTextureMode(RENDER_SCREEN_TEXTURE);
ClearBackground(BLACK);
drawScene();
drawUI();
EndTextureMode();
// Draw the render texture to the screen.
BeginDrawing();
ClearBackground(RED);
// Keep aspect and center the render
int32_t renderWidth, renderHeight, renderX, renderY;
const int32_t width = GetScreenWidth();
const int32_t height = GetScreenHeight();
if(RENDER_WIDTH * height > RENDER_HEIGHT * width) {
renderWidth = width;
renderHeight = (RENDER_HEIGHT * width) / RENDER_WIDTH;
renderX = 0;
renderY = (height - renderHeight) / 2;
} else {
renderWidth = (RENDER_WIDTH * height) / RENDER_HEIGHT;
renderHeight = height;
renderX = (width - renderWidth) / 2;
renderY = 0;
}
DrawTexturePro(
RENDER_SCREEN_TEXTURE.texture,
(Rectangle) { 0, 0, RENDER_WIDTH, -RENDER_HEIGHT },
(Rectangle) { renderX, renderY, renderWidth, renderHeight },
(Vector2) { 0, 0 },
0.0f,
WHITE
);
drawConsoleDraw();
EndDrawing();
EndBlendMode();
}
void renderDispose(void) {
UnloadRenderTexture(RENDER_SCREEN_TEXTURE);
UnloadTexture(RENDER_TILEMAP_TEXTURE);
UnloadTexture(RENDER_ENTITIES_TEXTURE);
CloseWindow();
}

View File

@@ -1,15 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskraylib.h"
#include "display/renderbase.h"
extern RenderTexture2D RENDER_SCREEN_TEXTURE;
extern Texture2D RENDER_TILEMAP_TEXTURE;
extern Texture2D RENDER_ENTITIES_TEXTURE;
extern Texture2D RENDER_FONT_TEXTURE;

View File

@@ -1,89 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "duskraylib.h"
#include "input.h"
typedef struct {
int32_t key;
uint8_t bind;
} inputkbmap_t;
typedef struct {
GamepadButton button;
uint8_t bind;
} inputgpmap_t;
typedef struct {
int32_t key;
uint32_t bind;
} inputkeyboardmap_t;
inputkbmap_t INPUT_KB_MAP[] = {
{ KEY_UP, INPUT_BIND_UP },
{ KEY_W, INPUT_BIND_UP },
{ KEY_DOWN, INPUT_BIND_DOWN },
{ KEY_S, INPUT_BIND_DOWN },
{ KEY_LEFT, INPUT_BIND_LEFT },
{ KEY_A, INPUT_BIND_LEFT },
{ KEY_RIGHT, INPUT_BIND_RIGHT },
{ KEY_D, INPUT_BIND_RIGHT },
{ KEY_SPACE, INPUT_BIND_ACTION },
{ KEY_E, INPUT_BIND_ACTION },
{ KEY_ENTER, INPUT_BIND_ACTION },
{ KEY_ESCAPE, INPUT_BIND_CANCEL },
{ KEY_Q, INPUT_BIND_CANCEL },
{ KEY_BACKSPACE, INPUT_BIND_CANCEL },
{ 0, 0 }
};
inputgpmap_t INPUT_GP_MAP[] = {
{ GAMEPAD_BUTTON_LEFT_FACE_UP, INPUT_BIND_UP },
{ GAMEPAD_BUTTON_LEFT_FACE_DOWN, INPUT_BIND_DOWN },
{ GAMEPAD_BUTTON_LEFT_FACE_LEFT, INPUT_BIND_LEFT },
{ GAMEPAD_BUTTON_LEFT_FACE_RIGHT, INPUT_BIND_RIGHT },
{ GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, INPUT_BIND_ACTION },
{ GAMEPAD_BUTTON_RIGHT_FACE_DOWN, INPUT_BIND_CANCEL },
{ 0, 0 }
};
uint8_t inputStateGet() {
uint8_t state = 0;
inputkbmap_t *kbMap = INPUT_KB_MAP;
do {
if(IsKeyDown(kbMap->key)) state |= kbMap->bind;
kbMap++;
} while(kbMap->key != 0);
for(uint32_t i = 0; i < 32; i++) {
if(!IsGamepadAvailable(i)) continue;
inputgpmap_t *gpMap = INPUT_GP_MAP;
do {
if(IsGamepadButtonDown(i, gpMap->button)) state |= gpMap->bind;
gpMap++;
} while(gpMap->button != 0);
}
return state;
}
// uint32_t inputKeyboardPop(void) {
// while(true) {
// int32_t key = GetKeyPressed();
// if(key == 0) return 0;
// printf("Got key: %d\n", key);
// return (uint32_t)key;
// }
// return 0;
// }

View File

@@ -1,22 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "display/render.h"
#include "game.h"
void main(void) {
renderInit();
gameInit();
while(!WindowShouldClose()) {
gameUpdate();
renderDraw();
}
gameDispose();
renderDispose();
}

View File

@@ -10,11 +10,11 @@ target_compile_definitions(${DUSK_TARGET_NAME}
)
# Libs
find_package(raylib REQUIRED)
find_package(SDL2 REQUIRED)
target_link_libraries(${DUSK_TARGET_NAME}
PUBLIC
raylib
SDL2::SDL2
)

View File

@@ -10,4 +10,4 @@ target_sources(${DUSK_TARGET_NAME}
)
# Subdirs
add_subdirectory(draw)
# add_subdirectory(draw)

View File

@@ -0,0 +1,83 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "render.h"
#include "assert/assert.h"
SDL_Window *RENDER_WINDOW;
SDL_Renderer *RENDER_RENDERER;
bool_t RENDER_RUNNING;
errorret_t renderInit(void) {
if(SDL_Init(SDL_INIT_VIDEO) != 0) {
errorThrow(
"SDL Failed to Initialize: %s",
SDL_GetError()
);
}
RENDER_WINDOW = SDL_CreateWindow(
"DuskSDL2",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
RENDER_WINDOW_WIDTH_DEFAULT,
RENDER_WINDOW_HEIGHT_DEFAULT,
SDL_WINDOW_SHOWN
);
#if RENDER_USE_FRAMEBUFFER
printf("Using framebuffer for rendering\n");
#else
printf("Using window for rendering\n");
#endif
RENDER_RENDERER = SDL_CreateRenderer(
RENDER_WINDOW,
-1,
SDL_RENDERER_ACCELERATED
);
if(!RENDER_RENDERER) {
errorThrow("SDL_CreateRenderer failed: %s", SDL_GetError());
}
RENDER_RUNNING = true;
errorOk();
}
errorret_t renderDraw(void) {
SDL_Event event;
while(SDL_PollEvent(&event)) {
if(event.type == SDL_QUIT) {
RENDER_RUNNING = false;
}
}
SDL_SetRenderDrawColor(RENDER_RENDERER, 0, 0, 0, 255);
SDL_RenderClear(RENDER_RENDERER);
// Draw red triangle
SDL_SetRenderDrawColor(RENDER_RENDERER, 255, 0, 0, 255);
SDL_RenderDrawLine(RENDER_RENDERER, 100, 100, 200, 100);
SDL_RenderDrawLine(RENDER_RENDERER, 200, 100, 150, 50);
SDL_RenderDrawLine(RENDER_RENDERER, 150, 50, 100, 100);
SDL_RenderPresent(RENDER_RENDERER);
RENDER_FRAME++;
errorOk();
}
errorret_t renderDispose(void) {
SDL_DestroyRenderer(RENDER_RENDERER);
SDL_DestroyWindow(RENDER_WINDOW);
SDL_Quit();
errorOk();
}

View File

@@ -0,0 +1,27 @@
/**
* 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

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "renderbackbuffer.h"
#if RENDER_USE_FRAMEBUFFER
SDL_Texture *RENDER_BACKBUFFER;
#else
#endif
void renderBackBufferInit(void) {
#if RENDER_USE_FRAMEBUFFER
RENDER_BACKBUFFER = SDL_CreateTexture(
RENDER_RENDERER,
SDL_PIXELFORMAT_RGBA8888,
SDL_TEXTUREACCESS_TARGET,
RENDER_WINDOW_WIDTH_DEFAULT,
RENDER_WINDOW_HEIGHT_DEFAULT
);
if(!RENDER_BACKBUFFER) {
assertUnreachable("SDL_CreateTexture failed\n");
return;
}
#else
// No back buffer needed for window rendering
#endif
}

View File

@@ -6,5 +6,4 @@
*/
#pragma once
#include "duskpsp.h"
#include "display/renderbase.h"
#include "dusksdl2.h"

View File

@@ -7,4 +7,4 @@
#pragma once
#include "dusk.h"
#include <raylib.h>
#include <SDL2/SDL.h>

View File

@@ -5,10 +5,11 @@
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskraylib.h"
#include "dusksdl2.h"
#include "input.h"
/**
* Draws the current scene to the screen.
*/
void drawScene(void);
uint8_t inputStateGet() {
uint8_t state = 0;
return state;
}

32
src/dusksdl2/main.c Normal file
View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "display/render.h"
#include "game.h"
#define mainError(ret) \
if((ret).code != ERROR_OK) { \
errorPrint(ret); \
return (ret).code; \
}
int main(int argc, char *argv[]) {
errorret_t ret;
mainError(renderInit());
gameInit();
while(RENDER_RUNNING) {
gameUpdate();
mainError(renderDraw());
}
gameDispose();
mainError(renderDispose());
return 0;
}