BG implemented

This commit is contained in:
2025-06-11 16:42:00 -05:00
parent e3438cae77
commit 7d8e1051fe
68 changed files with 733 additions and 2013 deletions

View File

@ -8,11 +8,13 @@ cmake_minimum_required(VERSION 3.13)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
if(NOT DEFINED DUSK_TARGET_SYSTEM)
set(DUSK_TARGET_SYSTEM "raylib")
endif()
# Prep cache
set(DUSK_CACHE_TARGET "dusk-target")
# Platform configs
# Build variables
set(DUSK_ROOT_DIR "${CMAKE_SOURCE_DIR}")
set(DUSK_BUILD_DIR "${CMAKE_BINARY_DIR}")
@ -26,6 +28,11 @@ set(DUSK_TARGET_NAME "Dusk" CACHE INTERNAL ${DUSK_CACHE_TARGET})
set(DUSK_BUILD_BINARY ${DUSK_BUILD_DIR}/Dusk CACHE INTERNAL ${DUSK_CACHE_TARGET})
set(DUSK_ASSETS "" CACHE INTERNAL ${DUSK_CACHE_TARGET})
# Toolchain
if(DUSK_TARGET_SYSTEM STREQUAL "gb")
include(${CMAKE_SOURCE_DIR}/cmake/toolchains/gbdk.cmake)
endif()
# Create directories
file(MAKE_DIRECTORY ${DUSK_GENERATED_HEADERS_DIR})
file(MAKE_DIRECTORY ${DUSK_ASSETS_BUILD_DIR})
@ -36,7 +43,7 @@ project(${DUSK_TARGET_NAME}
LANGUAGES C
)
# Executable
# Executable
add_executable(${DUSK_TARGET_NAME})
# Add tools

BIN
Dusk.gb Normal file

Binary file not shown.

View File

@ -0,0 +1,49 @@
set(GBDK_HOME "$ENV{GBDK_HOME}")
if(NOT GBDK_HOME)
set(GBDK_HOME "/opt/gbdk")
message(STATUS "GBDK_HOME not set, using default: ${GBDK_HOME}")
endif()
# Use Linux to omit any unwanted file extension in the created ROM file
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_C_COMPILER lcc)
set(CMAKE_C_COMPILER_FORCED TRUE)
set(GBDK_INCLUDE_DIR ${GBDK_HOME}/include)
set(GBDK_LIB_DIR ${GBDK_HOME}/lib)
set(CMAKE_PROGRAM_PATH ${GBDK_HOME}/bin)
set(CMAKE_INCLUDE_PATH ${GBDK_INCLUDE_DIR})
set(CMAKE_LIBRARY_PATH ${GBDK_LIB_DIR})
set(CMAKE_SYSTEM_INCLUDE_PATH ${GBDK_INCLUDE_DIR})
set(CMAKE_SYSTEM_LIBRARY_PATH ${GBDK_LIB_DIR})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE)
function(add_gb_rom target)
set(GB_MBC_TYPE 0)
if(ARGC GREATER_EQUAL 2)
set(GB_MBC_TYPE ${ARGV1})
endif()
set(GB_ROM_BANKS 2)
if(ARGC GREATER_EQUAL 3)
set(GB_ROM_BANKS ${ARGV2})
endif()
set(GB_RAM_BANKS 0)
if(ARGC GREATER_EQUAL 4)
set(GB_RAM_BANKS ${ARGV3})
endif()
set_target_properties(${target} PROPERTIES OUTPUT_NAME ${target} SUFFIX ".gb")
target_compile_options(${target} PRIVATE -Wa-l -Wl-m -Wl-j -DUSE_SFR_FOR_REG)
target_link_options(${target} PRIVATE "-Wl-yt${GB_MBC_TYPE}" "-Wl-yo${GB_ROM_BANKS}" "-Wl-ya${GB_RAM_BANKS}")
endfunction()

View File

@ -4,4 +4,9 @@
# https://opensource.org/licenses/MIT
add_subdirectory(dusk)
add_subdirectory(duskraylib)
if(DUSK_TARGET_SYSTEM STREQUAL "gb")
add_subdirectory(duskgb)
elseif(DUSK_TARGET_SYSTEM STREQUAL "raylib")
add_subdirectory(duskraylib)
endif()

View File

@ -6,7 +6,6 @@
# Libs
target_link_libraries(${DUSK_TARGET_NAME}
PUBLIC
m
)
# Includes
@ -19,12 +18,9 @@ target_include_directories(${DUSK_TARGET_NAME}
target_sources(${DUSK_TARGET_NAME}
PRIVATE
main.c
input.c
)
# Subdirs
add_subdirectory(assert)
add_subdirectory(display)
add_subdirectory(rpg)
add_subdirectory(util)
add_subdirectory(ui)
add_subdirectory(util)

View File

@ -7,118 +7,79 @@
#include "assert.h"
void assertTrueImpl(
const char *file,
const int32_t line,
const bool x,
const char *message
) {
if(x != true) {
fprintf(
stderr,
"Assertion Failed in %s:%i\n\n%s\n",
#ifndef ASSERTIONS_FAKED
void assertTrueImpl(
const char *file,
const int32_t line,
const bool x,
const char *message
) {
if(x != true) {
fprintf(
stderr,
"Assertion Failed in %s:%i\n\n%s\n",
file,
line,
message
);
abort();
}
}
void assertFalseImpl(
const char *file,
const int32_t line,
bool x,
const char *message
) {
assertTrueImpl(file, line, !x, message);
}
void assertUnreachableImpl(
const char *file,
const int32_t line,
const char *message
) {
assertTrueImpl(file, line, false, message);
}
void assertNotNullImpl(
const char *file,
const int32_t line,
const void *pointer,
const char *message
) {
assertTrueImpl(
file,
line,
pointer != NULL,
message
);
abort();
// Ensure we can touch it
volatile char temp;
temp = *((char*)pointer);
}
}
void assertFalseImpl(
const char *file,
const int32_t line,
bool x,
const char *message
) {
assertTrueImpl(file, line, !x, message);
}
void assertNullImpl(
const char *file,
const int32_t line,
const void *pointer,
const char *message
) {
assertTrueImpl(
file,
line,
pointer == NULL,
message
);
}
void assertUnreachableImpl(
const char *file,
const int32_t line,
const char *message
) {
assertTrueImpl(file, line, false, message);
}
void assertNotNullImpl(
const char *file,
const int32_t line,
const void *pointer,
const char *message
) {
assertTrueImpl(
file,
line,
pointer != NULL,
message
);
// Ensure we can touch it
volatile char temp;
temp = *((char*)pointer);
}
void assertNullImpl(
const char *file,
const int32_t line,
const void *pointer,
const char *message
) {
assertTrueImpl(
file,
line,
pointer == NULL,
message
);
}
void assertDeprecatedImpl(
const char *file,
const int32_t line,
const char *message
) {
assertUnreachableImpl(file, line, message);
}
// void assertMemoryRangeMatchesImpl(
// const char *file,
// const int32_t line,
// const void *start,
// const void *end,
// const size_t size,
// const char *message
// ) {
// assertTrueImpl(
// file,
// line,
// start != NULL,
// "Start pointer is NULL"
// );
// assertTrueImpl(
// file,
// line,
// end != NULL,
// "End pointer is NULL"
// );
// assertTrueImpl(
// file,
// line,
// size > 0,
// "Size is 0"
// );
// assertTrueImpl(
// file,
// line,
// start < end,
// "Start pointer is not before end pointer"
// );
// assertTrueImpl(
// file,
// line,
// ((uintptr_t)end - (uintptr_t)start) == size,
// message
// );
// }
void assertDeprecatedImpl(
const char *file,
const int32_t line,
const char *message
) {
assertUnreachableImpl(file, line, message);
}
#endif

View File

@ -8,124 +8,136 @@
#pragma once
#include "dusk.h"
/**
* Assert a given value to be true.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param x Value to assert as true.
* @param message Message to throw against assertion failure.
*/
void assertTrueImpl(
const char *file,
const int32_t line,
const bool x,
const char *message
);
#ifndef ASSERTIONS_FAKED
/**
* Assert a given value to be true.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param x Value to assert as true.
* @param message Message to throw against assertion failure.
*/
void assertTrueImpl(
const char *file,
const int32_t line,
const bool_t x,
const char *message
);
/**
* Asserts a given statement to be false.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param x Value to assert as false.
* @param message Message to throw against assertion failure.
*/
void assertFalseImpl(
const char *file,
const int32_t line,
const bool x,
const char *message
);
/**
* Asserts a given statement to be false.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param x Value to assert as false.
* @param message Message to throw against assertion failure.
*/
void assertFalseImpl(
const char *file,
const int32_t line,
const bool_t x,
const char *message
);
/**
* Asserts that a given line of code is unreachable. Essentially a forced
* assertion failure, good for "edge cases"
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param message Message to throw against assertion failure.
*/
void assertUnreachableImpl(
const char *file,
const int32_t line,
const char *message
);
/**
* Asserts that a given line of code is unreachable. Essentially a forced
* assertion failure, good for "edge cases"
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param message Message to throw against assertion failure.
*/
void assertUnreachableImpl(
const char *file,
const int32_t line,
const char *message
);
/**
* Assert a given pointer to not point to a null pointer.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param pointer Pointer to assert is not a null pointer.
* @param message Message to throw against assertion failure.
*/
void assertNotNullImpl(
const char *file,
const int32_t line,
const void *pointer,
const char *message
);
/**
* Assert a given pointer to not point to a null pointer.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param pointer Pointer to assert is not a null pointer.
* @param message Message to throw against assertion failure.
*/
void assertNotNullImpl(
const char *file,
const int32_t line,
const void *pointer,
const char *message
);
/**
* Asserts a given pointer to be a nullptr.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param pointer Pointer to assert is nullptr.
* @param message Message to throw against assertion failure.
*/
void assertNullImpl(
const char *file,
const int32_t line,
const void *pointer,
const char *message
);
/**
* Asserts a given pointer to be a nullptr.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param pointer Pointer to assert is nullptr.
* @param message Message to throw against assertion failure.
*/
void assertNullImpl(
const char *file,
const int32_t line,
const void *pointer,
const char *message
);
/**
* Asserts a function as being deprecated.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param message Message to throw against assertion failure.
*/
void assertDeprecatedImpl(
const char *file,
const int32_t line,
const char *message
);
/**
* Asserts a function as being deprecated.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param message Message to throw against assertion failure.
*/
void assertDeprecatedImpl(
const char *file,
const int32_t line,
const char *message
);
void assertMemoryRangeMatchesImpl(
const char *file,
const int32_t line,
const void *start,
const void *end,
const size_t size,
const char *message
);
void assertMemoryRangeMatchesImpl(
const char *file,
const int32_t line,
const void *start,
const void *end,
const size_t size,
const char *message
);
#define assertTrue(x, message) \
assertTrueImpl(__FILE__, __LINE__, x, message)
#define assertTrue(x, message) \
assertTrueImpl(__FILE__, __LINE__, x, message)
#define assertFalse(x, message) \
assertFalseImpl(__FILE__, __LINE__, x, message)
#define assertFalse(x, message) \
assertFalseImpl(__FILE__, __LINE__, x, message)
#define assertUnreachable(message) \
assertUnreachableImpl(__FILE__, __LINE__, message)
#define assertUnreachable(message) \
assertUnreachableImpl(__FILE__, __LINE__, message)
#define assertNotNull(pointer, message) \
assertNotNullImpl(__FILE__, __LINE__, pointer, message)
#define assertNotNull(pointer, message) \
assertNotNullImpl(__FILE__, __LINE__, pointer, message)
#define assertNull(pointer, message) \
assertNullImpl(__FILE__, __LINE__, pointer, message)
#define assertNull(pointer, message) \
assertNullImpl(__FILE__, __LINE__, pointer, message)
#define assertDeprecated(message) \
assertDeprecatedImpl(__FILE__, __LINE__, message)
#define assertDeprecated(message) \
assertDeprecatedImpl(__FILE__, __LINE__, message)
#define assertStrLenMax(str, len, message) \
assertTrue(strlen(str) < len, message)
#define assertStrLenMax(str, len, message) \
assertTrue(strlen(str) < len, message)
#define assertStrLenMin(str, len, message) \
assertTrue(strlen(str) >= len, message)
#define assertStrLenMin(str, len, message) \
assertTrue(strlen(str) >= len, message)
// EOF
#else
// If assertions are faked, we define the macros to do nothing.
#define assertTrue(x, message) ((void)0)
#define assertFalse(x, message) ((void)0)
#define assertUnreachable(message) ((void)0)
#define assertNotNull(pointer, message) ((void)0)
#define assertNull(pointer, message) ((void)0)
#define assertDeprecated(message) ((void)0)
#define assertStrLenMax(str, len, message) ((void)0)
#define assertStrLenMin(str, len, message) ((void)0)
#endif

View File

@ -1,9 +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
)

View File

@ -5,4 +5,4 @@
* https://opensource.org/licenses/MIT
*/
#include "quest.h"
#include "render.h"

View File

@ -6,22 +6,37 @@
*/
#pragma once
#include "dusk.h"
#include "display/renderimpl.h"
extern const uint16_t RENDER_WIDTH;
extern const uint16_t RENDER_HEIGHT;
#define RENDER_WIDTH_PIXELS 160
#define RENDER_HEIGHT_PIXELS 144
#define RENDER_WIDTH_TILES 20
#define RENDER_HEIGHT_TILES 18
extern uint8_t RENDER_STATUS;
#define RENDER_SHOULD_EXIT (1 << 0)
/**
* Init the render system.
* Initializes the rendering system.
*/
void renderInit();
void renderInit(void);
/**
* Update the render system.
* Vsyncs the frame.
*/
bool_t renderUpdate();
void renderVsync(void);
/**
* Dispose of the render system.
* Disposes of the rendering system.
*/
void renderDispose();
void renderDispose(void);
/**
* Turns off the display.
*/
void renderDisplayOff(void);
/**
* Turns on the display.
*/
void renderDisplayOn(void);

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2024 Dominic Masters
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
@ -7,20 +7,10 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
typedef bool bool_t;
typedef char char_t;
#define DUSK_NAME "Dusk"
#define DUSK_VERSION "1.0.0"
typedef int int_t;

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 "input.h"
#include "assert/assert.h"
input_t INPUT;
bool_t inputIsDown(const uint8_t key) {
assertTrue(key < INPUT_COUNT, "Invalid input key");
return (INPUT.current & key) != 0;
}
bool_t inputWasDown(const uint8_t key) {
assertTrue(key < INPUT_COUNT, "Invalid input key");
return (INPUT.previous & key) != 0;
}
bool_t inputWasPressed(const uint8_t key) {
return inputIsDown(key) && !inputWasDown(key);
}
bool_t inputWasReleased(const uint8_t key) {
return !inputIsDown(key) && inputWasDown(key);
}

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 "dusk.h"
#include <termios.h>
#define INPUT_UP (1 << 0)
#define INPUT_DOWN (1 << 1)
#define INPUT_LEFT (1 << 2)
#define INPUT_RIGHT (1 << 3)
#define INPUT_ACTION (1 << 4)
#define INPUT_CANCEL (1 << 5)
#define INPUT_COUNT (INPUT_CANCEL + 1)
#define INPUT_BUFFER_SIZE 128
typedef struct {
uint8_t current;
uint8_t previous;
} input_t;
extern input_t INPUT;
/**
* Initializes the input system.
*/
void inputInit();
/**
* Updates the input system.
*/
void inputUpdate();
/**
* Disposes of the input system, restoring terminal settings.
*/
void inputDispose();
/**
* Returns true if the specified key is currently pressed down.
*
* @param key The key to check.
* @return True if the key is down, false otherwise.
*/
bool_t inputIsDown(const uint8_t key);
/**
* Returns true if the specified key was pressed down in the previous frame.
*
* @param key The key to check.
* @return True if the key was down in the previous frame, false otherwise.
*/
bool_t inputWasDown(const uint8_t key);
/**
* Returns true if the specified key was pressed in the current frame.
*
* @param key The key to check.
* @return True if the key was pressed, false otherwise.
*/
bool_t inputWasPressed(const uint8_t key);
/**
* Returns true if the specified key was released in the current frame.
*
* @param key The key to check.
* @return True if the key was released, false otherwise.
*/
bool_t inputWasReleased(const uint8_t key);

View File

@ -1,32 +1,124 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "input.h"
#include "util/random.h"
#include "display/render.h"
#include "ui/ui.h"
#include "rpg/entity/entity.h"
#include "rpg/world/maps/testmap.h"
uint8_t backgroundtiles[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFE, 0xFE, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0xFE, 0xFE, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0xFE, 0xFE, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0xFE, 0xFE, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x7F, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x7F, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x55, 0x00, 0xAA, 0x00, 0x55, 0x00, 0xAA, 0x00,
0x55, 0x00, 0xAA, 0x00, 0x55, 0x00, 0xAA, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xAA, 0xFF, 0x00, 0x55, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00
};
int32_t main(const int32_t argc, const char **argv) {
renderInit();
inputInit();
randomInit();
#define backgroundmapWidth 40
#define backgroundmapHeight 18
#define backgroundmapBank 0
uiInit();
mapSet(TEST_MAP);
uint8_t backgroundmap[] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x03,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x02,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,
0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04
};
while(1) {
inputUpdate();
uiUpdate();
mapUpdate();
if(!renderUpdate()) break;
void pdelay(uint8_t numVbls) {
uint8_t i;
for (i = 0; i < numVbls; i++) {
renderVsync();
}
}
return EXIT_SUCCESS;
// Press F5 to compile and run the compiled game.gb in the Emulicous Emulator/Debugger
void main(void) {
renderInit();
set_bkg_data(0, 7, backgroundtiles);
set_bkg_tiles(0, 0, backgroundmapWidth, backgroundmapHeight, backgroundmap);
renderDisplayOn();
while (1) {
RENDER_BACKGROUND_X++;
pdelay(3);
if((RENDER_STATUS & RENDER_SHOULD_EXIT) != 0) break;
}
renderDispose();
}

View File

@ -1,16 +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
)
# Subdirs
add_subdirectory(entity)
add_subdirectory(event)
add_subdirectory(item)
add_subdirectory(quest)
add_subdirectory(world)

View File

@ -1,12 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
entity.c
player.c
npc.c
)

View File

@ -1,192 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert/assert.h"
#include "util/memory.h"
#include "entity.h"
#include "rpg/world/map.h"
#include "util/math.h"
entitycallbacks_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = {
[ENTITY_TYPE_NULL] = { 0 },
[ENTITY_TYPE_PLAYER] = {
.init = playerInit,
.update = playerUpdate,
.interact = NULL,
},
[ENTITY_TYPE_NPC] = {
.init = npcInit,
.update = npcUpdate,
.interact = npcInteract,
}
};
void entityInit(entity_t *entity, const entitytype_t type) {
assertNotNull(entity, "Entity is NULL");
assertTrue(type < ENTITY_TYPE_COUNT, "Invalid entity type");
assertNotNull(ENTITY_CALLBACKS[type].init, "Entity type has no init");
memoryZero(entity, sizeof(entity_t));
entity->type = type;
ENTITY_CALLBACKS[type].init(entity);
}
void entityUpdate(entity_t *entity) {
assertNotNull(entity, "Entity cannot be NULL");
assertNotNull(
ENTITY_CALLBACKS[entity->type].update,
"Entity type has no update"
);
// Handle subpixel movement
if(entity->subX < 0) {
entity->subX++;
} else if(entity->subY < 0) {
entity->subY++;
} else if(entity->subX > 0) {
entity->subX--;
} else if(entity->subY > 0) {
entity->subY--;
}
// Entity-Type handling
ENTITY_CALLBACKS[entity->type].update(entity);
}
void entityTurn(entity_t *entity, const entitydir_t dir) {
assertNotNull(entity, "Entity cannot be NULL");
assertFalse(entityIsWalking(entity), "Entity is currently walking");
entity->dir = dir;
}
void entityDirGetPosition(
const entitydir_t dir,
const uint8_t distance,
int8_t *outX,
int8_t *outY
) {
assertTrue(dir < ENTITY_DIR_COUNT, "Invalid entity direction");
assertNotNull(outX, "Output X cannot be NULL");
assertNotNull(outY, "Output Y cannot be NULL");
switch(dir) {
case ENTITY_DIR_UP:
*outX = 0;
*outY = -distance;
break;
case ENTITY_DIR_DOWN:
*outX = 0;
*outY = distance;
break;
case ENTITY_DIR_LEFT:
*outX = -distance;
*outY = 0;
break;
case ENTITY_DIR_RIGHT:
*outX = distance;
*outY = 0;
break;
default:
assertUnreachable("Invalid entity direction");
}
}
entitydir_t entityGetLookDirection(
const entity_t *entity,
const uint8_t x,
const uint8_t y
) {
assertNotNull(entity, "Entity cannot be NULL");
assertTrue(x < MAP.width, "X coordinate out of bounds");
assertTrue(y < MAP.height, "Y coordinate out of bounds");
int8_t dX = x - entity->x;
int8_t dY = y - entity->y;
// More horizontal movement or more vertical movement?
if(mathAbsI8(dX) > mathAbsI8(dY)) {
// More horizontal movement
if(dX < 0) return ENTITY_DIR_LEFT;
return ENTITY_DIR_RIGHT;
}
if(dY < 0) {
return ENTITY_DIR_UP;
}
return ENTITY_DIR_DOWN;
}
void entityWalk(entity_t *entity) {
assertNotNull(entity, "Entity cannot be NULL");
assertFalse(entityIsWalking(entity), "Entity is already walking");
assertTrue(entityCanWalk(entity, entity->dir, NULL), "Entity cannot walk");
int8_t tX, tY;
entityDirGetPosition(entity->dir, 1, &tX, &tY);
entity->y += tY;
entity->x += tX;
entity->subX = ENTITY_MOVE_SUBPIXEL * -tX;
entity->subY = ENTITY_MOVE_SUBPIXEL * -tY;
}
bool_t entityIsWalking(const entity_t *entity) {
assertNotNull(entity, "Entity cannot be NULL");
return (entity->subX != 0 || entity->subY != 0);
}
bool_t entityCanWalk(
const entity_t *entity,
const entitydir_t dir,
entity_t **entityInWay
) {
assertNotNull(entity, "Entity cannot be NULL");
assertTrue(dir < ENTITY_DIR_COUNT, "Invalid entity direction");
int8_t dX, dY;
entityDirGetPosition(dir, 1, &dX, &dY);
uint8_t tX = entity->x + dX;
if(tX < 0 || tX >= MAP.width) return false;
uint8_t tY = entity->y + dY;
if(tY < 0 || tY >= MAP.height) return false;
if(entityInWay == NULL) {
if(mapGetEntityAt(tX, tY) != NULL) return false;
} else {
*entityInWay = mapGetEntityAt(tX, tY);
if(*entityInWay != NULL) return false;
}
return true;
}
void entityPositionSet(entity_t *entity, const uint8_t x, const uint8_t y) {
assertNotNull(entity, "Entity cannot be NULL");
assertTrue(x < MAP.width, "X coordinate out of bounds");
assertTrue(y < MAP.height, "Y coordinate out of bounds");
entity->x = x;
entity->y = y;
entity->subX = 0;
entity->subY = 0;
}
bool_t entityInteract(entity_t *interacted, entity_t *player) {
assertNotNull(interacted, "Interacted entity cannot be NULL");
assertNotNull(player, "Player entity cannot be NULL");
assertTrue(interacted->type != ENTITY_TYPE_NULL, "Interacted entity invalid");
assertTrue(player->type == ENTITY_TYPE_PLAYER, "Entity is not a player");
if(ENTITY_CALLBACKS[interacted->type].interact != NULL) {
return ENTITY_CALLBACKS[interacted->type].interact(interacted, player);
}
return false;
}

View File

@ -1,158 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "player.h"
#include "npc.h"
#define ENTITY_WIDTH 16
#define ENTITY_HEIGHT 16
#define ENTITY_MOVE_SUBPIXEL ENTITY_WIDTH
typedef enum {
ENTITY_DIR_UP = 0,
ENTITY_DIR_DOWN = 1,
ENTITY_DIR_LEFT = 2,
ENTITY_DIR_RIGHT = 3,
ENTITY_DIR_NORTH = ENTITY_DIR_UP,
ENTITY_DIR_SOUTH = ENTITY_DIR_DOWN,
ENTITY_DIR_WEST = ENTITY_DIR_LEFT,
ENTITY_DIR_EAST = ENTITY_DIR_RIGHT
} entitydir_t;
#define ENTITY_DIR_COUNT (ENTITY_DIR_RIGHT + 1)
typedef enum {
ENTITY_TYPE_NULL = 0,
ENTITY_TYPE_PLAYER = 1,
ENTITY_TYPE_NPC = 2,
} entitytype_t;
#define ENTITY_TYPE_COUNT (ENTITY_TYPE_NPC + 1)
typedef struct _entity_t {
entitytype_t type;
uint8_t x, y;
int8_t subX, subY;
entitydir_t dir;
// Per type data
union {
player_t player;
npc_t npc;
};
} entity_t;
typedef struct {
void (*init)(entity_t *entity);
void (*update)(entity_t *entity);
bool_t (*interact)(entity_t *self, entity_t *player);
} entitycallbacks_t;
extern entitycallbacks_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT];
/**
* Initializes an entity with the given type.
*
* @param entity Pointer to the entity to initialize.
* @param type The type of the entity to initialize.
*/
void entityInit(entity_t *entity, const entitytype_t type);
/**
* Updates the entity's state based on its type.
*
* @param entity Pointer to the entity to update.
*/
void entityUpdate(entity_t *entity);
/**
* Turns the entity to face a specific direction.
*
* @param entity Pointer to the entity to turn.
* @param dir The direction to turn the entity towards.
*/
void entityTurn(entity_t *entity, const entitydir_t dir);
/**
* Gets the position of a specific direction at a given distance.
*
* @param dir The direction to get the position in.
* @param distance The distance to move in that direction.
* @param outX Pointer to store the resulting x-coordinate.
* @param outY Pointer to store the resulting y-coordinate.
*/
void entityDirGetPosition(
const entitydir_t dir,
const uint8_t distance,
int8_t *outX,
int8_t *outY
);
/**
* Gets the look direction for a given entity to be looking at the specified
* coordinates.
*
* @param entity Pointer to the entity to get the look direction for.
* @param x The x-coordinate to look at.
* @param Y The y-coordinate to look at.
* @return The direction the entity should look towards.
*/
entitydir_t entityGetLookDirection(
const entity_t *entity,
const uint8_t x,
const uint8_t Y
);
/**
* Makes the entity walk in the current direction.
*
* @param entity Pointer to the entity to make walk.
*/
void entityWalk(entity_t *entity);
/**
* Checks if the entity is currently mid-walking.
*
* @param entity Pointer to the entity to check.
* @return true if the entity is walking, false otherwise.
*/
bool_t entityIsWalking(const entity_t *entity);
/**
* Checks if the entity can walk in a specific direction.
*
* @param entity Pointer to the entity to check.
* @param dir The direction to check for walking capability.
* @param inWay Pointer to store any entity in the way, if applicable.
* @return true if the entity can walk in the specified direction, false otherwise.
*/
bool_t entityCanWalk(
const entity_t *entity,
const entitydir_t dir,
entity_t **inWay
);
/**
* Sets the position of the entity.
*
* @param entity Pointer to the entity to set the position for.
* @param x The x-coordinate to set.
* @param y The y-coordinate to set.
*/
void entityPositionSet(entity_t *entity, const uint8_t x, const uint8_t y);
/**
* Handles interaction between an entity and a player.
*
* @param self Pointer to the entity that is being interacted with.
* @param player Pointer to the player entity interacting with the entity.
* @return true if interaction happened.
*/
bool_t entityInteract(
entity_t *self,
entity_t *player
);

View File

@ -1,34 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entity.h"
#include "assert/assert.h"
#include "ui/uitextbox.h"
void npcInit(entity_t *ent) {
assertNotNull(ent, "NPC entity is NULL");
assertTrue(ent->type == ENTITY_TYPE_NPC, "Entity is not an NPC");
}
void npcUpdate(entity_t *ent) {
assertNotNull(ent, "Entity is NULL");
assertTrue(ent->type == ENTITY_TYPE_NPC, "Entity is not an NPC");
}
bool_t npcInteract(entity_t *self, entity_t *player) {
assertNotNull(self, "NPC entity cannot be NULL");
assertNotNull(player, "Player entity cannot be NULL");
assertTrue(self->type == ENTITY_TYPE_NPC, "Entity is not an NPC");
assertTrue(player->type == ENTITY_TYPE_PLAYER, "Entity is not a player");
// Look at the player
entityTurn(self, entityGetLookDirection(self, player->x, player->y));
eventStart(self->npc.event);
return true;
}

View File

@ -1,37 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "rpg/event/event.h"
typedef struct _entity_t entity_t;
typedef struct {
const event_t *event;
} npc_t;
/**
* Initializes an NPC entity.
*
* @param ent Pointer to the NPC entity to initialize.
*/
void npcInit(entity_t *ent);
/**
* Updates an NPC entity.
*
* @param ent Entity to update.
*/
void npcUpdate(entity_t *ent);
/**
* Handles interaction between an NPC and a player.
*
* @param self Pointer to the NPC entity.
* @param player Pointer to the player entity interacting with the NPC.
*/
bool_t npcInteract(entity_t *self, entity_t *player);

View File

@ -1,58 +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 "rpg/world/map.h"
void playerInit(entity_t *player) {
assertNotNull(player, "Player entity is NULL");
assertTrue(player->type == ENTITY_TYPE_PLAYER, "Entity is not a player");
}
void playerUpdate(entity_t *entity) {
entity_t *other;
assertNotNull(entity, "Entity is NULL");
assertTrue(entity->type == ENTITY_TYPE_PLAYER, "Entity is not a player");
// Handle movement
if(entityIsWalking(entity)) {
return;
}
entitydir_t dir = 0xFF;
if(inputIsDown(INPUT_UP)) {
dir = ENTITY_DIR_UP;
} else if(inputIsDown(INPUT_DOWN)) {
dir = ENTITY_DIR_DOWN;
} else if(inputIsDown(INPUT_LEFT)) {
dir = ENTITY_DIR_LEFT;
} else if(inputIsDown(INPUT_RIGHT)) {
dir = ENTITY_DIR_RIGHT;
}
if(dir != 0xFF) {
if(dir != entity->dir) {
entityTurn(entity, dir);
} else {
if(!entityCanWalk(entity, dir, &other)) return;
entityWalk(entity);
}
return;
}
// Handle interaction
if(inputWasPressed(INPUT_ACTION)) {
int8_t dX, dY;
entityDirGetPosition(entity->dir, 1, &dX, &dY);
other = mapGetEntityAt(entity->x + dX, entity->y + dY);
assertTrue(other != entity, "Player trying to interact with itself?");
if(other != NULL && entityInteract(other, entity)) return;
}
}

View File

@ -1,29 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef struct _entity_t entity_t;
typedef struct {
uint8_t nothing;
} player_t;
/**
* Initializes a player entity.
*
* @param ent Pointer to the player entity to initialize.
*/
void playerInit(entity_t *ent);
/**
* Updates a player entity.
*
* @param ent Entity to update.
*/
void playerUpdate(entity_t *ent);

View File

@ -1,33 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "event.h"
#include "assert/assert.h"
#include "ui/uitextbox.h"
#include "util/memory.h"
event_t eventCreate() {
event_t event;
return event;
}
void eventStart(const event_t *event) {
assertNotNull(event, "Event cannot be NULL");
assertTrue(event->count <= EVENT_ITEMS_MAX, "Event count exceeds maximum items");
assertTrue(event->count > 0, "Event must have at least one item");
switch(event->type) {
case EVENT_TYPE_CONVERSATION: {
assertTrue(event->items[0].type == EVENT_ITEM_TYPE_TEXT, "First item in conversation must be text");
uiTextboxSetText(event->items[0].text.text);
uiTextboxShow();
break;
}
default:
assertFalse(true, "Unknown event type");
}
}

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 {
EVENT_TYPE_CONVERSATION,
} eventtype_t;
typedef enum {
EVENT_ITEM_TYPE_TEXT
} eventitemtype_t;
typedef struct {
const char_t *text;
} eventtext_t;
typedef struct {
eventitemtype_t type;
union {
eventtext_t text;
};
} eventitem_t;
#define EVENT_ITEMS_MAX 16
typedef struct {
eventtype_t type;
uint8_t count;
eventitem_t items[EVENT_ITEMS_MAX];
} event_t;
/**
* Creates a new event.
*/
event_t eventCreate();
/**
* Starts a given event.
*/
void eventStart(const event_t *event);

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
itemtype.c
inventory.c
)

View File

@ -1,16 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert/assert.h"
#include "inventory.h"
#include "util/memory.h"
void inventoryInit(inventory_t *inventory) {
assertNotNull(inventory, "Inventory pointer cannot be NULL");
memoryZero(inventory, sizeof(inventory_t));
}

View File

@ -1,23 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "itemstack.h"
#define INVENTORY_ITEM_COUNT 32
typedef struct {
itemstack_t items[INVENTORY_ITEM_COUNT];
uint8_t itemCount;
} inventory_t;
/**
* Initializes the inventory.
*
* @param inventory Pointer to the inventory to initialize.
*/
void inventoryInit(inventory_t *inventory);

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 "itemtype.h"
typedef struct {
itemtype_t type;
uint8_t count;
} itemstack_t;

View File

@ -1,28 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert/assert.h"
#include "itemtype.h"
iteminfo_t ITEM_INFO[ITEM_TYPE_COUNT] = {
[ITEM_TYPE_NULL] = { 0 },
// Consumables
[ITEM_TYPE_POTION] = { .stackable = true },
// Ingredients
[ITEM_TYPE_ONION] = { .stackable = true },
[ITEM_TYPE_SWEET_POTATO] = { .stackable = true },
// Cooked Items
[ITEM_TYPE_BAKED_SWEET_POTATO] = { .stackable = true },
};
static inline bool_t itemTypeIsStackable(const itemtype_t type) {
assertTrue(type < ITEM_TYPE_COUNT, "Invalid item type");
return ITEM_INFO[type].stackable;
}

View File

@ -1,38 +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,
// Consumables
ITEM_TYPE_POTION,
// Ingredients
ITEM_TYPE_ONION,
ITEM_TYPE_SWEET_POTATO,
// Cooked Items
ITEM_TYPE_BAKED_SWEET_POTATO,
} itemtype_t;
#define ITEM_TYPE_COUNT (ITEM_TYPE_BAKED_SWEET_POTATO + 1)
typedef struct {
bool_t stackable;
} iteminfo_t;
extern iteminfo_t ITEM_INFO[ITEM_TYPE_COUNT];
/**
* Returns true if the item type is stackable.
*
* @param type The item type to check.
* @return true if the item type is stackable, false otherwise.
*/
static inline bool_t itemTypeIsStackable(const itemtype_t type);

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
quest.c
)

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
map.c
)
# Subdirs
add_subdirectory(maps)

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 "map.h"
#include "util/memory.h"
#include "assert/assert.h"
map_t MAP;
void mapSet(const mapinfo_t mapInfo) {
assertTrue(mapInfo.init != NULL, "Map initialization function cannot be NULL");
mapInfo.init();
}
void mapInit(const uint8_t width, const uint8_t height) {
memoryZero(&MAP, sizeof(map_t));
MAP.width = width;
MAP.height = height;
}
void mapUpdate() {
entity_t *ent = MAP.entities;
do {
if(ent->type != ENTITY_TYPE_NULL) {
entityUpdate(ent);
}
ent++;
} while(ent < MAP.entities + MAP_ENTITY_COUNT);
}
entity_t * mapGetEntityAt(const uint8_t x, const uint8_t y) {
if(x >= MAP.width || y >= MAP.height) return NULL;
entity_t *ent = MAP.entities;
do {
if(ent->type != ENTITY_TYPE_NULL && ent->x == x && ent->y == y) return ent;
ent++;
} while(ent < MAP.entities + MAP_ENTITY_COUNT);
return NULL;
}

View File

@ -1,59 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "rpg/entity/entity.h"
#define MAP_ENTITY_COUNT 16
typedef struct {
uint8_t width;
uint8_t height;
entity_t entities[MAP_ENTITY_COUNT];
} map_t;
typedef struct {
const char *name;
void (*init)(void);
} mapinfo_t;
extern map_t MAP;
/**
* Sets the current map to the specified map information.
*
* This function initializes the map with the provided map information,
* including its name and initialization function.
*
* @param mapInfo Map information containing the name and initialization function.
*/
void mapSet(const mapinfo_t mapInfo);
/**
* Initializes the map with the given width and height.
*
* @param width Width of the map.
* @param height Height of the map.
*/
void mapInit(const uint8_t width, const uint8_t height);
/**
* Updates the map, processing all entities.
*
* This function should be called every frame to update the state of the map
* and its entities.
*/
void mapUpdate();
/**
* Gets the entity at the specified coordinates.
*
* @param x X coordinate of the entity.
* @param y Y coordinate of the entity.
* @return Pointer to the entity at the specified coordinates or NULL.
*/
entity_t * mapGetEntityAt(const uint8_t x, const uint8_t y);

View File

@ -1,9 +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
)

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 "rpg/entity/entity.h"
#include "rpg/world/map.h"
#include "rpg/event/event.h"
const event_t TEST_MAP_NPC_TEST = {
.type = EVENT_TYPE_CONVERSATION,
.count = 1,
.items = {
{
.type = EVENT_ITEM_TYPE_TEXT,
.text = { .text = "Hello, player! How can I help you today?" }
}
},
};
/**
* Initializes the test map.
* This function sets up a test map with predefined dimensions.
*/
void testMapInit() {
mapInit(10, 10);
entity_t *ent = MAP.entities;
entityInit(ent, ENTITY_TYPE_PLAYER);
ent++;
entityInit(ent, ENTITY_TYPE_NPC);
entityPositionSet(ent, 5, 5);
ent->npc.event = &TEST_MAP_NPC_TEST;
}
mapinfo_t TEST_MAP = {
.name = "Test Map",
.init = testMapInit
};

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
ui.c
uitextbox.c
)
# Subdirs

View File

@ -1,21 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "ui.h"
#include "util/memory.h"
#include "ui/uitextbox.h"
ui_t UI;
void uiInit() {
memoryZero(&UI, sizeof(ui_t));
uiTextboxInit();
}
void uiUpdate() {
uiTextboxUpdate();
}

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 {
UI_FOCUS_TEXTBOX
} uifocus_t;
typedef struct {
uint8_t nothing;
} ui_t;
extern ui_t UI;
/**
* Initializes the UI system.
*/
void uiInit();
/**
* Updates the UI system.
* This function should be called every frame to update the UI state.
*/
void uiUpdate();

View File

@ -1,43 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "uitextbox.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "display/render.h"
#include "input.h"
uitextbox_t UI_TEXTBOX;
void uiTextboxInit() {
memoryZero(&UI_TEXTBOX, sizeof(uitextbox_t));
UI_TEXTBOX.width = RENDER_WIDTH;
UI_TEXTBOX.height = 58;
UI_TEXTBOX.x = 0;
UI_TEXTBOX.y = RENDER_HEIGHT - UI_TEXTBOX.height;
}
void uiTextboxUpdate() {
if(!UI_TEXTBOX.visible) return;
if(inputWasPressed(INPUT_ACTION)) {
UI_TEXTBOX.visible = false;
}
}
void uiTextboxSetText(const char_t *text) {
sprintf(UI_TEXTBOX.text, "%s", text);
}
void uiTextboxShow() {
UI_TEXTBOX.visible = true;
}
void uiTextboxHide() {
UI_TEXTBOX.visible = false;
}

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"
#define UI_TEXTBOX_TEXT_MAX_LENGTH 1024
typedef struct {
bool_t visible;
char_t text[UI_TEXTBOX_TEXT_MAX_LENGTH];
uint16_t width, height;
uint16_t x, y;
} uitextbox_t;
extern uitextbox_t UI_TEXTBOX;
/**
* Initializes the UI textbox.
*/
void uiTextboxInit();
/**
* Updates the UI textbox.
*/
void uiTextboxUpdate();
/**
* Sets the text of the UI textbox.
*
* @param text The text to set in the textbox.
*/
void uiTextboxSetText(const char_t *text);
/**
* Shows the UI textbox.
*/
void uiTextboxShow();
/**
* Hides the UI textbox.
*/
void uiTextboxHide();

View File

@ -7,7 +7,4 @@
target_sources(${DUSK_TARGET_NAME}
PRIVATE
memory.c
string.c
random.c
math.c
)

View File

@ -1,12 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "math.h"
int8_t mathAbsI8(const int8_t val) {
return (val > 0) ? val : -val;
}

View File

@ -1,17 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
/**
* Returns the absolute (ignoring sign) value of an 8-bit integer.
*
* @param val The 8-bit integer value to get the absolute value of.
* @return The absolute value of the input integer.
*/
int8_t mathAbsI8(const int8_t val);

View File

@ -48,7 +48,7 @@ void memoryMove(void *dest, const void *src, const size_t size) {
memmove(dest, src, size);
}
ssize_t memoryCompare(
int_t memoryCompare(
const void *a,
const void *b,
const size_t size

View File

@ -83,7 +83,7 @@ void memoryMove(void *dest, const void *src, const size_t size);
* @param size The size of the memory to compare.
* @return 0 if the memory is equal, < 0 if a < b, > 0 if a > b.
*/
ssize_t memoryCompare(
int_t memoryCompare(
const void *a,
const void *b,
const size_t size

View File

@ -1,12 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "random.h"
#include "assert/assert.h"
void randomInit() {
}

View File

@ -1,18 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
/**
* Initializes the random number generator with a random seed.
*
* This function should be called once at the start of the program to
* initialize the random number generator with a random seed. It uses
* the current time to generate a seed.
*/
void randomInit();

View File

@ -1,127 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "string.h"
#include "assert/assert.h"
#include "util/memory.h"
bool_t stringIsWhitespace(const char_t c) {
return isspace(c);
}
void stringCopy(char_t *dest, const char_t *src, const size_t destSize) {
assertNotNull(dest, "dest must not be NULL");
assertNotNull(src, "src must not be NULL");
assertTrue(destSize > 0, "destSize must be greater than 0");
assertStrLenMax(src, destSize, "src is too long");
memoryCopy(dest, src, strlen(src) + 1);
}
int stringCompare(const char_t *str1, const char_t *str2) {
assertNotNull(str1, "str1 must not be NULL");
assertNotNull(str2, "str2 must not be NULL");
return strcmp(str1, str2);
}
void stringTrim(char_t *str) {
assertNotNull(str, "str must not be NULL");
// Trim leading whitespace
char_t *start = str;
while(stringIsWhitespace(*start)) start++;
// Trim trailing whitespace
char_t *end = start + strlen(start) - 1;
while (end >= start && stringIsWhitespace(*end)) end--;
// Null-terminate the string
*(end + 1) = '\0';
// Move trimmed string to the original buffer
if (start != str) memmove(str, start, end - start + 2);
}
char_t * stringToken(char_t *str, const char_t *delim) {
assertNotNull(str, "str must not be NULL");
assertNotNull(delim, "delim must not be NULL");
return strtok(str, delim);
}
int32_t stringFormat(
char_t *dest,
const size_t destSize,
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);
va_end(args);
return result;
}
int32_t stringFormatVA(
char_t *dest,
const size_t destSize,
const char_t *format,
va_list args
) {
assertNotNull(dest, "dest must not be NULL");
assertNotNull(format, "format must not be NULL");
assertTrue(destSize > 0, "destSize must be greater than 0");
int32_t ret = vsnprintf(dest, destSize, format, args);
assertTrue(ret >= 0, "Failed to format string.");
assertTrue(ret < destSize, "Formatted string is too long.");
return ret;
}
bool_t stringToI32(const char_t *str, int32_t *out) {
assertNotNull(str, "str must not be NULL");
assertNotNull(out, "out must not be NULL");
char_t *endptr;
errno = 0;
long int result = strtol(str, &endptr, 10);
if (errno != 0 || *endptr != '\0') {
return false;
}
*out = (int32_t)result;
return true;
}
bool_t stringToI64(const char_t *str, int64_t *out) {
assertNotNull(str, "str must not be NULL");
assertNotNull(out, "out must not be NULL");
char_t *endptr;
errno = 0;
long long int result = strtoll(str, &endptr, 10);
if (errno != 0 || *endptr != '\0') {
return false;
}
*out = (int64_t)result;
return true;
}
bool_t stringToU16(const char_t *str, uint16_t *out) {
assertNotNull(str, "str must not be NULL");
assertNotNull(out, "out must not be NULL");
char_t *endptr;
errno = 0;
unsigned long int result = strtoul(str, &endptr, 10);
if (errno != 0 || *endptr != '\0' || result > UINT16_MAX) {
return false;
}
*out = (uint16_t)result;
return true;
}

View File

@ -1,109 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
/**
* Determines if a character is whitespace.
*
* @param c The character to check.
* @return TRUE if the character is whitespace, FALSE otherwise.
*/
bool_t stringIsWhitespace(const char_t c);
/**
* Copies a string from src to dest, ensuring the dest string is null-terminated
* and does not exceed the specified size.
*
* @param dest The destination string.
* @param src The source string.
* @param destSize The size of the destination string exc. null terminator.
*/
void stringCopy(char_t *dest, const char_t *src, const size_t destSize);
/**
* Compares two strings.
*
* @param str1 The first string.
* @param str2 The second string.
* @return 0 if the strings are equal, -1 if str1 is less than str2, 1 if str1
* is greater than str2.
*/
int stringCompare(const char_t *str1, const char_t *str2);
/**
* Trims whitespace from the beginning and end of a string.
*
* @param str The string to trim.
*/
void stringTrim(char_t *str);
/**
* Gets the next token in a string using a delimiter.
* e.g. input: "Hello, World, Happy Monday!" with stringToken(input, ",") will
* return "Hello" then " World" then " Happy Monday!" on each subsequent call.
*
* @param str The string to split.
* @param delim The delimiter to split by.
* @return A pointer to the next token in the string.
*/
char_t * stringToken(char_t *str, const char_t *delim);
/**
* Formats a string.
*
* @param dest The destination string.
* @param destSize The size of the destination string exc. null terminator.
* @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, ...);
/**
* Formats a string using a va_list.
*
* @param dest The destination string.
* @param destSize The size of the destination string exc. null terminator.
* @param format The format string.
* @param args The va_list of arguments.
* @return The number of characters written.
*/
int32_t stringFormatVA(
char_t *dest,
const size_t destSize,
const char_t *format,
va_list args
);
/**
* Converts a string to an integer.
*
* @param str The string to convert.
* @param out The output integer.
* @return TRUE if the conversion was successful, FALSE otherwise.
*/
bool_t stringToI32(const char_t *str, int32_t *out);
/**
* Converts a string to a signed 16-bit integer.
*
* @param str The string to convert.
* @param out The output signed integer.
* @return TRUE if the conversion was successful, FALSE otherwise.
*/
bool_t stringToI16(const char_t *str, int16_t *out);
/**
* Converts a string to an unsigned 16-bit integer.
*
* @param str The string to convert.
* @param out The output unsigned integer.
* @return TRUE if the conversion was successful, FALSE otherwise.
*/
bool_t stringToU16(const char_t *str, uint16_t *out);

31
src/duskgb/CMakeLists.txt Normal file
View File

@ -0,0 +1,31 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Definitions
target_compile_definitions(${DUSK_TARGET_NAME}
PUBLIC
ASSERTIONS_FAKED=1
)
# Libs
target_link_libraries(${DUSK_TARGET_NAME}
PUBLIC
)
# Includes
target_include_directories(${DUSK_TARGET_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
)
# Subdirs
add_subdirectory(display)
add_gb_rom(${DUSK_TARGET_NAME} 0x10 2 4)

View File

@ -1,10 +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
event.c
renderimpl.c
)

View File

@ -0,0 +1,23 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "display/render.h"
uint8_t RENDER_STATUS = 0;
void renderInit(void) {
SHOW_BKG;
DISPLAY_ON;
}
void renderVsync(void) {
vsync();
}
void renderDispose(void) {
}

View File

@ -6,7 +6,7 @@
*/
#pragma once
#include "display/render.h"
#include <raylib.h>
#include "duskgb.h"
extern Font FONT;
#define RENDER_BACKGROUND_X SCX_REG
#define RENDER_BACKGROUND_Y SCY_REG

View File

@ -7,7 +7,4 @@
#pragma once
#include "dusk.h"
typedef struct {
} quest_t;
#include <gb/gb.h>

View File

@ -27,7 +27,6 @@ target_include_directories(${DUSK_TARGET_NAME}
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
input.c
)
# Subdirs

View File

@ -1,13 +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
renderraylib.c
)
# Subdirs
add_subdirectory(draw)
renderimpl.c
)

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
drawentity.c
drawui.c
)

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 "drawentity.h"
#include "rpg/world/map.h"
void drawEntities() {
uint8_t x, y;
entity_t *ent = MAP.entities;
do {
if(ent->type == ENTITY_TYPE_NULL) {
ent++;
continue;
}
x = ent->x * ENTITY_WIDTH + ent->subX;
y = ent->y * ENTITY_HEIGHT + ent->subY;
switch(ent->dir) {
case ENTITY_DIR_UP:
DrawTriangle(
(Vector2){ x, y + ENTITY_HEIGHT },
(Vector2){ x + ENTITY_WIDTH, y + ENTITY_HEIGHT },
(Vector2){ x + ENTITY_WIDTH / 2, y },
RED
);
break;
case ENTITY_DIR_DOWN:
DrawTriangle(
(Vector2){ x, y },
(Vector2){ x + ENTITY_WIDTH / 2, y + ENTITY_HEIGHT },
(Vector2){ x + ENTITY_WIDTH, y },
RED
);
break;
case ENTITY_DIR_LEFT:
DrawTriangle(
(Vector2){ x + ENTITY_WIDTH, y },
(Vector2){ x, y + ENTITY_HEIGHT / 2 },
(Vector2){ x + ENTITY_WIDTH, y + ENTITY_HEIGHT },
RED
);
break;
case ENTITY_DIR_RIGHT:
DrawTriangle(
(Vector2){ x, y },
(Vector2){ x, y + ENTITY_HEIGHT },
(Vector2){ x + ENTITY_WIDTH, y + ENTITY_HEIGHT / 2 },
RED
);
break;
default:
break;
}
ent++;
} while(ent->type != ENTITY_TYPE_NULL);
}

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 "display/renderraylib.h"
/**
* Draws all entities on the map.
*/
void drawEntities();

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 "drawui.h"
#include "ui/ui.h"
#include "ui/uitextbox.h"
void drawUITextbox() {
if(!UI_TEXTBOX.visible) return;
DrawRectangle(
UI_TEXTBOX.x, UI_TEXTBOX.y,
UI_TEXTBOX.width, UI_TEXTBOX.height,
Fade(SKYBLUE, 0.5f)
);
DrawTextEx(
FONT,
UI_TEXTBOX.text,
(Vector2){ UI_TEXTBOX.x + 2, UI_TEXTBOX.y + 2 },
FONT.baseSize, 0, BLACK
);
}

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 "display/renderraylib.h"
/**
* Draws the UI textbox.
*/
void drawUITextbox();

View File

@ -0,0 +1,209 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "display/render.h"
#include "assert/assert.h"
#include "util/memory.h"
uint8_t RENDER_BACKGROUND_X = 0;
uint8_t RENDER_BACKGROUND_Y = 0;
uint8_t RENDER_STATUS = 0;
uint8_t RENDER_TILES[RENDER_TILE_COUNT] = { 0 };
uint8_t RENDER_BACKGROUND_TILEMAP[RENDER_BACKGROUND_TILE_COUNT] = { 0 };
Color RENDER_PALETTE[RENDER_PALETTE_COLOR_COUNT] = {
{ 102, 191, 47, 255 },
{ 78, 146, 35, 255 },
{ 48, 89, 22, 255 },
{ 18, 33, 8, 255 }
};
bool_t RENDER_DISPLAY_ON = false;
RenderTexture2D RENDER_BACKBUFFER;
RenderTexture2D RENDER_TILES_TEXTURE;
void renderInit(void) {
InitWindow(RENDER_WIDTH_PIXELS * 4, RENDER_HEIGHT_PIXELS * 4, "Dusk");
SetTargetFPS(60);
SetWindowState(
FLAG_WINDOW_RESIZABLE |
FLAG_WINDOW_HIGHDPI |
FLAG_VSYNC_HINT
);
// Create back buffer for rendering.
RENDER_BACKBUFFER = LoadRenderTexture(
RENDER_WIDTH_PIXELS,
RENDER_HEIGHT_PIXELS
);
// Create texture to hold the tile data.
RENDER_TILES_TEXTURE = LoadRenderTexture(
RENDER_TILE_COUNT * RENDER_TILE_WIDTH,
RENDER_TILE_HEIGHT
);
}
void renderVsync() {
int32_t x, y, i;
BeginDrawing();
if(RENDER_DISPLAY_ON) {
// Update the texture with the new tile data
BeginTextureMode(RENDER_TILES_TEXTURE);
i = 0;
for(i = 0; i < RENDER_TILE_COUNT; i++) {
uint8_t *tile = RENDER_TILES + (i * RENDER_TILE_BYTES_PER_TILE);
// For each pixel in the tile...
for(y = 0; y < RENDER_TILE_HEIGHT; y++) {
uint8_t low = tile[y * RENDER_TILE_BYTES_PER_ROW];
uint8_t high = tile[y * RENDER_TILE_BYTES_PER_ROW + 1];
for(x = 0; x < RENDER_TILE_WIDTH; x++) {
uint8_t loBit = (low >> (7 - x)) & 1;
uint8_t hiBit = (high >> (7 - x)) & 1;
uint8_t paletteIndex = (hiBit << 1) | loBit;
// Draw the pixel to the texture
DrawPixel(
(i * RENDER_TILE_WIDTH) + x,
y,
RENDER_PALETTE[paletteIndex]
);
}
}
}
EndTextureMode();
// Clear the back buffer
BeginTextureMode(RENDER_BACKBUFFER);
ClearBackground(RENDER_PALETTE[0]);
// Render background tiles
i = 0;
for(y = 0; y < RENDER_BACKGROUND_ROWS; y++) {
for(x = 0; x < RENDER_BACKGROUND_COLUMNS; x++) {
// Get the tile index from the tilemap
uint8_t tileIndex = RENDER_BACKGROUND_TILEMAP[i++];
DrawTexturePro(
RENDER_TILES_TEXTURE.texture,
(Rectangle){
.x = ((int32_t)tileIndex) * RENDER_TILE_WIDTH,
.y = 0,
.width = RENDER_TILE_WIDTH,
.height = -RENDER_TILE_HEIGHT
},
(Rectangle){
((int32_t)x * RENDER_TILE_WIDTH) - RENDER_BACKGROUND_X,
((int32_t)y * RENDER_TILE_HEIGHT) - RENDER_BACKGROUND_Y,
RENDER_TILE_WIDTH,
RENDER_TILE_HEIGHT
},
(Vector2){ 0, 0 },
0.0f,
WHITE
);
}
}
// Render the back buffer to the screen
EndTextureMode();
ClearBackground(WHITE);
// 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_PIXELS * height > RENDER_HEIGHT_PIXELS * width) {
renderWidth = width;
renderHeight = (RENDER_HEIGHT_PIXELS * width) / RENDER_WIDTH_PIXELS;
renderX = 0;
renderY = (height - renderHeight) / 2;
} else {
renderWidth = (RENDER_WIDTH_PIXELS * height) / RENDER_HEIGHT_PIXELS;
renderHeight = height;
renderX = (width - renderWidth) / 2;
renderY = 0;
}
DrawTexturePro(
RENDER_BACKBUFFER.texture,
(Rectangle) { 0, 0, RENDER_WIDTH_PIXELS, -RENDER_HEIGHT_PIXELS },
(Rectangle) { renderX, renderY, renderWidth, renderHeight },
(Vector2) { 0, 0 },
0.0f,
WHITE
);
}
EndDrawing();
if(WindowShouldClose()) RENDER_STATUS |= RENDER_SHOULD_EXIT;
}
void renderDispose() {
UnloadRenderTexture(RENDER_TILES_TEXTURE);
CloseWindow();
}
void renderDisplayOn(void) {
RENDER_DISPLAY_ON = true;
}
void renderDisplayOff(void) {
RENDER_DISPLAY_ON = false;
}
void set_bkg_data(
const uint8_t index,
const uint8_t count,
const uint8_t *tiles
) {
assertTrue(count > 0, "Count must be greater than zero");
assertNotNull(tiles, "Tiles pointer must not be null");
// Copy data to fake vram
memoryCopy(
&RENDER_TILES[index * RENDER_TILE_BYTES_PER_TILE],
tiles,
count * RENDER_TILE_BYTES_PER_TILE
);
}
void set_bkg_tiles(
const uint8_t xPos,
const uint8_t yPos,
const uint8_t width,
const uint8_t height,
const uint8_t *tiles
) {
uint8_t w = width, h = height, x = xPos, y = yPos;
assertNotNull(tiles, "Tiles pointer must not be null");
// Clamp x and y to tilemap bounds
if (x >= RENDER_BACKGROUND_COLUMNS) x = RENDER_BACKGROUND_COLUMNS - 1;
if (y >= RENDER_BACKGROUND_ROWS) y = RENDER_BACKGROUND_ROWS - 1;
// Clamp width and height so we don't overflow past edges
if (x + w > RENDER_BACKGROUND_COLUMNS) {
w = RENDER_BACKGROUND_COLUMNS - x;
}
if (y + h > RENDER_BACKGROUND_ROWS) {
h = RENDER_BACKGROUND_ROWS - y;
}
if (w == 0 || h == 0) return;
for (uint8_t row = 0; row < h; row++) {
memoryCopy(
&RENDER_BACKGROUND_TILEMAP[(y + row) * RENDER_BACKGROUND_COLUMNS + x],
&tiles[row * width],
w
);
}
}

View File

@ -0,0 +1,56 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskraylib.h"
#define RENDER_TILE_COUNT 384
#define RENDER_TILE_WIDTH 8
#define RENDER_TILE_BYTES_PER_ROW 2
#define RENDER_TILE_HEIGHT 8
#define RENDER_TILE_BYTES_PER_TILE (RENDER_TILE_BYTES_PER_ROW * RENDER_TILE_HEIGHT)
#define RENDER_BACKGROUND_COLUMNS 32
#define RENDER_BACKGROUND_ROWS 32
#define RENDER_BACKGROUND_TILE_COUNT (RENDER_BACKGROUND_COLUMNS * RENDER_BACKGROUND_ROWS)
extern uint8_t RENDER_BACKGROUND_X;
extern uint8_t RENDER_BACKGROUND_Y;
extern uint8_t RENDER_TILES[RENDER_TILE_COUNT];
#define RENDER_PALETTE_COLOR_COUNT 4
extern Color RENDER_PALETTE[RENDER_PALETTE_COLOR_COUNT];
extern bool_t RENDER_DISPLAY_ON;
/**
* Sets the background tile data.
*
* @param index The starting index of the tile data.
* @param count The number of tiles to set.
* @param tiles Pointer to the tile data array.
*/
void set_bkg_data(
const uint8_t index,
const uint8_t count,
const uint8_t *tiles
);
/**
* Sets the background tiles.
*
* @param x The x-coordinate of the top-left corner of the background.
* @param y The y-coordinate of the top-left corner of the background.
* @param width The width of the background in tiles.
* @param height The height of the background in tiles.
* @param tiles Pointer to the tile map data.
*/
void set_bkg_tiles(
const uint8_t x,
const uint8_t y,
const uint8_t width,
const uint8_t height,
const uint8_t *tiles
);

View File

@ -1,49 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert/assert.h"
#include "display/render.h"
#include <raylib.h>
#include "display/draw/drawentity.h"
#include "display/draw/drawui.h"
const uint16_t RENDER_WIDTH = 480;
const uint16_t RENDER_HEIGHT = 270;
Font FONT;
void renderInit() {
InitWindow(RENDER_WIDTH, RENDER_HEIGHT, "Dusk");
SetTargetFPS(60);
FONT = LoadFontEx(
"../assets/ark-pixel-font/12px/monospaced/ark-pixel-12px-monospaced-latin.otf",
12, NULL, 0
);
}
bool_t renderUpdate() {
uint8_t x, y;
char_t buffer[64];
// End rendering?
if(WindowShouldClose()) return false;
BeginDrawing();
ClearBackground(RAYWHITE);
drawEntities();
drawUITextbox();
EndDrawing();
return true;
}
void renderDispose() {
CloseWindow();
}

View File

@ -6,4 +6,5 @@
*/
#pragma once
#include "dusk.h"
#include "dusk.h"
#include <raylib.h>

View File

@ -1,60 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "input.h"
#include "raylib.h"
typedef struct {
int32_t key;
uint8_t flag;
} inputmap_t;
const inputmap_t INPUT_MAP[] = {
{ KEY_W, INPUT_UP },
{ KEY_S, INPUT_DOWN },
{ KEY_A, INPUT_LEFT },
{ KEY_D, INPUT_RIGHT },
{ KEY_SPACE, INPUT_ACTION },
{ KEY_E, INPUT_ACTION },
{ KEY_ENTER, INPUT_ACTION },
{ KEY_ESCAPE, INPUT_CANCEL },
{ KEY_Q, INPUT_CANCEL },
{ KEY_BACKSPACE, INPUT_CANCEL },
{ KEY_UP, INPUT_UP },
{ KEY_DOWN, INPUT_DOWN },
{ KEY_LEFT, INPUT_LEFT },
{ KEY_RIGHT, INPUT_RIGHT },
{ KEY_ENTER, INPUT_ACTION },
{ KEY_BACKSPACE, INPUT_CANCEL },
{ 0, 0 }
};
void inputInit() {
}
void inputUpdate() {
uint8_t state = 0;
inputmap_t *map = INPUT_MAP;
do {
if(IsKeyDown(map->key)) {
state |= map->flag;
}
map++;
} while(map->key != 0);
INPUT.previous = INPUT.current;
INPUT.current = state;
}
void inputDispose() {
}

View File

@ -5,5 +5,4 @@
# Tools
add_subdirectory(assetstool)
add_subdirectory(copytool)
add_subdirectory(glsltool)
add_subdirectory(copytool)