diff --git a/src/dusk/CMakeLists.txt b/src/dusk/CMakeLists.txt index d99f093c..727a756e 100644 --- a/src/dusk/CMakeLists.txt +++ b/src/dusk/CMakeLists.txt @@ -63,7 +63,6 @@ add_subdirectory(engine) add_subdirectory(error) add_subdirectory(input) add_subdirectory(locale) -add_subdirectory(rpg) add_subdirectory(scene) add_subdirectory(system) add_subdirectory(time) diff --git a/src/dusk/rpg/CMakeLists.txt b/src/dusk/rpg/CMakeLists.txt deleted file mode 100644 index e17955c4..00000000 --- a/src/dusk/rpg/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2025 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - rpg.c - rpgcamera.c -) - -# Subdirs -add_subdirectory(cutscene) -add_subdirectory(entity) -add_subdirectory(overworld) -add_subdirectory(item) -add_subdirectory(physics) \ No newline at end of file diff --git a/src/dusk/rpg/cutscene/CMakeLists.txt b/src/dusk/rpg/cutscene/CMakeLists.txt deleted file mode 100644 index ccecedc5..00000000 --- a/src/dusk/rpg/cutscene/CMakeLists.txt +++ /dev/null @@ -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_LIBRARY_TARGET_NAME} - PUBLIC - cutscenesystem.c -) - -# Subdirs -add_subdirectory(item) \ No newline at end of file diff --git a/src/dusk/rpg/cutscene/cutscene.h b/src/dusk/rpg/cutscene/cutscene.h deleted file mode 100644 index f954455e..00000000 --- a/src/dusk/rpg/cutscene/cutscene.h +++ /dev/null @@ -1,232 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "rpg/cutscene/item/cutsceneitem.h" -#include "rpg/cutscene/cutscenepause.h" - -typedef struct cutscene_s { - const cutsceneitem_t *items; - uint8_t itemCount; - cutscenepause_t pause; - - // Size in bytes of this cutscene's custom user data, carved out of - // CUTSCENE_SYSTEM.data while the cutscene is running. - size_t dataSize; -} cutscene_t; - -#define CUTSCENE(NAME, SIZE, PAUSE_TYPE, ...) \ - static const cutsceneitem_t CUTSCENE_##NAME##_ITEMS[] = { __VA_ARGS__ }; \ - static const cutscene_t CUTSCENE_##NAME = { \ - .items = CUTSCENE_##NAME##_ITEMS, \ - .itemCount = sizeof(CUTSCENE_##NAME##_ITEMS) / sizeof(cutsceneitem_t), \ - .pause = CUTSCENE_PAUSE_##PAUSE_TYPE, \ - .dataSize = SIZE \ - }; - -#define CUTSCENE_REFERENCE(CUTSCENE) \ - &CUTSCENE_##CUTSCENE - -#define CUTSCENE_TEXT(TEXT) \ - { .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = TEXT } } - -#define CUTSCENE_TEXT_MINI(TEXT, X, Y, Z, DURATION) \ - { \ - .type = CUTSCENE_ITEM_TYPE_TEXT_MINI, \ - .textMini = { \ - .text = TEXT, \ - .position = { X, Y, Z }, \ - .duration = DURATION \ - } \ - } - -#define CUTSCENE_TEXT_MINI_HIDE(INDEX) \ - { \ - .type = CUTSCENE_ITEM_TYPE_TEXT_MINI_HIDE, \ - .textMiniHide = { .index = INDEX } \ - } - -#define CUTSCENE_WAIT(WAIT) \ - { .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = WAIT } - -#define CUTSCENE_CUTSCENE(CUTSCENE) \ - { \ - .type = CUTSCENE_ITEM_TYPE_CUTSCENE, \ - .cutscene = CUTSCENE_REFERENCE(CUTSCENE) \ - } - -#define CUTSCENE_CALLBACK(CALLBACK) \ - { .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = CALLBACK } - -#define CUTSCENE_ENTITY_WALK_TO(ENTITY_INDEX, X, Y, Z) \ - { \ - .type = CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO, \ - .entityWalkTo = { \ - .entityIndex = ENTITY_INDEX, \ - .positions = (const worldpos_t[]){ { X, Y, Z } }, \ - .count = 1, \ - .walkAround = true \ - } \ - } - -#define CUTSCENE_ENTITY_WALK_PATH(NAME, ENTITY_INDEX, ...) \ - static const worldpos_t CUTSCENE_##NAME##_POSITIONS[] = { __VA_ARGS__ }; \ - static const cutsceneitem_t CUTSCENE_##NAME = { \ - .type = CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO, \ - .entityWalkTo = { \ - .entityIndex = ENTITY_INDEX, \ - .positions = CUTSCENE_##NAME##_POSITIONS, \ - .count = sizeof(CUTSCENE_##NAME##_POSITIONS) / sizeof(worldpos_t), \ - .walkAround = true \ - } \ - } - -#define CUTSCENE_ENTITY_REMOVE(ENTITY_INDEX) \ - { \ - .type = CUTSCENE_ITEM_TYPE_ENTITY_REMOVE, \ - .entityRemove = { .entityIndex = ENTITY_INDEX } \ - } - -#define CUTSCENE_ENTITY_ADD(TYPE, X, Y, Z) \ - { \ - .type = CUTSCENE_ITEM_TYPE_ENTITY_ADD, \ - .entityAdd = { .entityType = TYPE, .position = { X, Y, Z } } \ - } - -#define CUTSCENE_ENTITY_TURN(ENTITY_INDEX, DIRECTION) \ - { \ - .type = CUTSCENE_ITEM_TYPE_ENTITY_TURN, \ - .entityTurn = { .entityIndex = ENTITY_INDEX, .direction = DIRECTION } \ - } - -// Walks ENTITY_INDEX to stand beside TARGET_ENTITY_INDEX, offset by -// (OFFSET_X, OFFSET_Y) on the 2D plane. The destination Z is resolved -// from nearby terrain each frame, so ramps between the two entities are -// accounted for automatically. -#define CUTSCENE_ENTITY_WALK_TO_ENTITY( \ - ENTITY_INDEX, TARGET_ENTITY_INDEX, OFFSET_X, OFFSET_Y \ -) \ - { \ - .type = CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO_ENTITY, \ - .entityWalkToEntity = { \ - .entityIndex = ENTITY_INDEX, \ - .targetEntityIndex = TARGET_ENTITY_INDEX, \ - .offsetX = OFFSET_X, \ - .offsetY = OFFSET_Y \ - } \ - } - -#define CUTSCENE_ENTITY_TELEPORT(ENTITY_INDEX, X, Y, Z) \ - { \ - .type = CUTSCENE_ITEM_TYPE_ENTITY_TELEPORT, \ - .entityTeleport = { .entityIndex = ENTITY_INDEX, .target = { X, Y, Z } } \ - } - -#define CUTSCENE_FADE(FROM, TO, DURATION, EASING) \ - { \ - .type = CUTSCENE_ITEM_TYPE_FADE, \ - .fade = { .from = FROM, .to = TO, .duration = DURATION, .easing = EASING } \ - } - -#define CUTSCENE_FADE_TO_BLACK(DURATION) \ - CUTSCENE_FADE(COLOR_TRANSPARENT_BLACK, COLOR_BLACK, DURATION, EASING_LINEAR) - -#define CUTSCENE_FADE_FROM_BLACK(DURATION) \ - CUTSCENE_FADE(COLOR_BLACK, COLOR_TRANSPARENT_BLACK, DURATION, EASING_LINEAR) - -#define CUTSCENE_FADE_TO_WHITE(DURATION) \ - CUTSCENE_FADE(COLOR_TRANSPARENT_WHITE, COLOR_WHITE, DURATION, EASING_LINEAR) - -#define CUTSCENE_FADE_FROM_WHITE(DURATION) \ - CUTSCENE_FADE(COLOR_WHITE, COLOR_TRANSPARENT_WHITE, DURATION, EASING_LINEAR) - -#define CUTSCENE_EMOJI(ENTITY_INDEX, EMOJI_TYPE, DURATION) \ - { \ - .type = CUTSCENE_ITEM_TYPE_EMOJI, \ - .emoji = { \ - .entityIndex = ENTITY_INDEX, \ - .emojiType = EMOJI_TYPE, \ - .duration = DURATION \ - } \ - } - -// AMOUNT ranges 0 (no shake) to 4 (three tiles): 1 is half a tile, 2 is -// a full tile, 3 is two tiles, and 4 is three tiles. -#define CUTSCENE_SHAKE(AMOUNT, DURATION) \ - { \ - .type = CUTSCENE_ITEM_TYPE_SHAKE, \ - .shake = { .amount = AMOUNT, .duration = DURATION } \ - } - -#define CUTSCENE_SET_PAUSE(FLAGS) \ - { .type = CUTSCENE_ITEM_TYPE_SET_PAUSE, .setPause = (FLAGS) } - -#define CUTSCENE_ITEM_GIVE(ITEM_ID, QUANTITY) \ - { \ - .type = CUTSCENE_ITEM_TYPE_ITEM_GIVE, \ - .itemGive = { .item = ITEM_ID, .quantity = QUANTITY } \ - } - -// Runs all listed items simultaneously and waits until all are done. -// Concurrent items cannot be nested inside another CUTSCENE_CONCURRENT. -#define CUTSCENE_CONCURRENT(...) \ - { \ - .type = CUTSCENE_ITEM_TYPE_CONCURRENT, \ - .concurrent = { \ - .items = (const cutsceneitem_t[]){ __VA_ARGS__ }, \ - .count = (uint8_t)( \ - sizeof((cutsceneitem_t[]){ __VA_ARGS__ }) / \ - sizeof(cutsceneitem_t) \ - ) \ - } \ - } - -#define CUTSCENE_MAP_AREA_ADD( \ - MIN_X, MIN_Y, MIN_Z, MAX_X, MAX_Y, MAX_Z, CALLBACK, NOTIFY, TRIGGER \ -) \ - { \ - .type = CUTSCENE_ITEM_TYPE_MAP_AREA_ADD, \ - .mapAreaAdd = { \ - .min = { MIN_X, MIN_Y, MIN_Z }, \ - .max = { MAX_X, MAX_Y, MAX_Z }, \ - .callback = CALLBACK, \ - .notify = NOTIFY, \ - .trigger = TRIGGER \ - } \ - } - -#define CUTSCENE_MAP_AREA_REMOVE(AREA_ID) \ - { \ - .type = CUTSCENE_ITEM_TYPE_MAP_AREA_REMOVE, \ - .mapAreaRemove = { .areaId = AREA_ID } \ - } - -// Waits until any one of the given map area IDs has its callback invoked. -// Accepts CUTSCENE_AREA_LAST_CREATED in place of a literal area ID. -#define CUTSCENE_MAP_AREA_WAIT(...) \ - { \ - .type = CUTSCENE_ITEM_TYPE_MAP_AREA_WAIT, \ - .mapAreaWait = { \ - .areaIds = (const uint8_t[]){ __VA_ARGS__ }, \ - .count = (uint8_t)( \ - sizeof((const uint8_t[]){ __VA_ARGS__ }) / sizeof(uint8_t) \ - ) \ - } \ - } - -// Adds a map area, waits for it to be triggered once, then removes it -// before the cutscene continues. Uses a no-op callback since the wait is -// driven by the area's trigger count rather than callback logic. -#define CUTSCENE_MAP_AREA_TRIGGER_ONCE( \ - MIN_X, MIN_Y, MIN_Z, MAX_X, MAX_Y, MAX_Z, NOTIFY, TRIGGER \ -) \ - CUTSCENE_MAP_AREA_ADD( \ - MIN_X, MIN_Y, MIN_Z, MAX_X, MAX_Y, MAX_Z, \ - mapAreaNoopCallback, NOTIFY, TRIGGER \ - ), \ - CUTSCENE_MAP_AREA_WAIT(CUTSCENE_AREA_LAST_CREATED), \ - CUTSCENE_MAP_AREA_REMOVE(CUTSCENE_AREA_LAST_CREATED) diff --git a/src/dusk/rpg/cutscene/cutscenepause.h b/src/dusk/rpg/cutscene/cutscenepause.h deleted file mode 100644 index 222d28c0..00000000 --- a/src/dusk/rpg/cutscene/cutscenepause.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" - -typedef uint8_t cutscenepause_t; - -#define CUTSCENE_PAUSE_NONE ((cutscenepause_t)0) -#define CUTSCENE_PAUSE_NPC ((cutscenepause_t)(1 << 0)) -#define CUTSCENE_PAUSE_PLAYER ((cutscenepause_t)(1 << 1)) -#define CUTSCENE_PAUSE_WORLD ((cutscenepause_t)(1 << 2)) - -#define CUTSCENE_PAUSE_DEFAULT ((cutscenepause_t)( \ - CUTSCENE_PAUSE_NPC | CUTSCENE_PAUSE_PLAYER \ -)) - -#define CUTSCENE_PAUSE_ALL ((cutscenepause_t)( \ - CUTSCENE_PAUSE_NPC | CUTSCENE_PAUSE_PLAYER | CUTSCENE_PAUSE_WORLD \ -)) diff --git a/src/dusk/rpg/cutscene/cutscenesystem.c b/src/dusk/rpg/cutscene/cutscenesystem.c deleted file mode 100644 index 933dbc0b..00000000 --- a/src/dusk/rpg/cutscene/cutscenesystem.c +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "cutscenesystem.h" -#include "rpg/entity/entity.h" -#include "util/memory.h" -#include "assert/assert.h" - -cutscenesystem_t CUTSCENE_SYSTEM; - -void cutsceneSystemInit() { - memoryZero(&CUTSCENE_SYSTEM, sizeof(cutscenesystem_t)); -} - -void cutsceneSystemStartCutscene(const cutscene_t *cutscene) { - cutsceneSystemStartCutsceneWith(cutscene, NULL, NULL); -} - -void cutsceneSystemStartCutsceneWith( - const cutscene_t *cutscene, - entity_t *interact, - entity_t *interacted -) { - assertTrue( - cutscene->dataSize < CUTSCENE_SYSTEM_SIZE_MAX, - "Cutscene data size exceeds CUTSCENE_SYSTEM_SIZE_MAX" - ); - - CUTSCENE_SYSTEM.scene = cutscene; - CUTSCENE_SYSTEM.pause = cutscene->pause; - CUTSCENE_SYSTEM.entityInteract = interact; - CUTSCENE_SYSTEM.entityInteracted = interacted; - CUTSCENE_SYSTEM.entityLastCreated = NULL; - CUTSCENE_SYSTEM.entityLastRef = NULL; - CUTSCENE_SYSTEM.areaLastCreated = CUTSCENE_AREA_LAST_CREATED; - CUTSCENE_SYSTEM.textMiniLastCreated = CUTSCENE_TEXT_MINI_LAST_CREATED; - CUTSCENE_SYSTEM.currentItem = 0xFF;// Set to 0xFF so Next wraps to 0. - cutsceneSystemNext(); -} - -void cutsceneSystemUpdate() { - if(CUTSCENE_SYSTEM.scene == NULL) return; - - const cutsceneitem_t *item = cutsceneSystemGetCurrentItem(); - if(cutsceneItemUpdate(item, &CUTSCENE_SYSTEM.data)) cutsceneSystemNext(); -} - -void cutsceneSystemNext() { - if(CUTSCENE_SYSTEM.scene == NULL) return; - - CUTSCENE_SYSTEM.currentItem++; - - // End of the cutscene? - if( - CUTSCENE_SYSTEM.currentItem >= CUTSCENE_SYSTEM.scene->itemCount - ) { - CUTSCENE_SYSTEM.scene = NULL; - CUTSCENE_SYSTEM.currentItem = 0xFF; - CUTSCENE_SYSTEM.pause = CUTSCENE_PAUSE_NONE; - CUTSCENE_SYSTEM.entityInteract = NULL; - CUTSCENE_SYSTEM.entityInteracted = NULL; - CUTSCENE_SYSTEM.entityLastCreated = NULL; - CUTSCENE_SYSTEM.entityLastRef = NULL; - CUTSCENE_SYSTEM.areaLastCreated = CUTSCENE_AREA_LAST_CREATED; - CUTSCENE_SYSTEM.textMiniLastCreated = CUTSCENE_TEXT_MINI_LAST_CREATED; - return; - } - - // Start item. - const cutsceneitem_t *item = cutsceneSystemGetCurrentItem(); - memset(&CUTSCENE_SYSTEM.data, 0, sizeof(CUTSCENE_SYSTEM.data)); - cutsceneItemStart(item, &CUTSCENE_SYSTEM.data); -} - -const cutsceneitem_t * cutsceneSystemGetCurrentItem() { - if(CUTSCENE_SYSTEM.scene == NULL) return NULL; - - return &CUTSCENE_SYSTEM.scene->items[CUTSCENE_SYSTEM.currentItem]; -} - -entity_t * cutsceneSystemGetEntity(const uint8_t entityIndex) { - entity_t *entity; - - if(entityIndex == CUTSCENE_ENTITY_INTERACT) { - assertNotNull( - CUTSCENE_SYSTEM.entityInteract, - "CUTSCENE_ENTITY_INTERACT used but no interact entity is set" - ); - entity = CUTSCENE_SYSTEM.entityInteract; - } else if(entityIndex == CUTSCENE_ENTITY_INTERACTED) { - assertNotNull( - CUTSCENE_SYSTEM.entityInteracted, - "CUTSCENE_ENTITY_INTERACTED used but no interacted entity is set" - ); - entity = CUTSCENE_SYSTEM.entityInteracted; - } else if(entityIndex == CUTSCENE_ENTITY_LAST_CREATED) { - assertNotNull( - CUTSCENE_SYSTEM.entityLastCreated, - "CUTSCENE_ENTITY_LAST_CREATED used but no entity has been created" - ); - entity = CUTSCENE_SYSTEM.entityLastCreated; - } else if(entityIndex == CUTSCENE_ENTITY_LAST_REF) { - assertNotNull( - CUTSCENE_SYSTEM.entityLastRef, - "CUTSCENE_ENTITY_LAST_REF used but no entity has been referenced" - ); - entity = CUTSCENE_SYSTEM.entityLastRef; - } else { - assertTrue( - entityIndex < ENTITY_COUNT, - "Entity index is out of range" - ); - entity = &ENTITIES[entityIndex]; - } - - CUTSCENE_SYSTEM.entityLastRef = entity; - return entity; -} - -uint8_t cutsceneSystemGetAreaId(const uint8_t areaId) { - if(areaId == CUTSCENE_AREA_LAST_CREATED) { - assertTrue( - CUTSCENE_SYSTEM.areaLastCreated != CUTSCENE_AREA_LAST_CREATED, - "CUTSCENE_AREA_LAST_CREATED used but no map area has been created" - ); - return CUTSCENE_SYSTEM.areaLastCreated; - } - return areaId; -} - -uint8_t cutsceneSystemGetTextMiniId(const uint8_t index) { - if(index == CUTSCENE_TEXT_MINI_LAST_CREATED) { - assertTrue( - CUTSCENE_SYSTEM.textMiniLastCreated != CUTSCENE_TEXT_MINI_LAST_CREATED, - "CUTSCENE_TEXT_MINI_LAST_CREATED used but no mini textbox has been " - "shown" - ); - return CUTSCENE_SYSTEM.textMiniLastCreated; - } - return index; -} - -void cutsceneSystemDispose() { - CUTSCENE_SYSTEM.scene = NULL; - CUTSCENE_SYSTEM.currentItem = 0xFF; - CUTSCENE_SYSTEM.pause = CUTSCENE_PAUSE_NONE; - CUTSCENE_SYSTEM.entityInteract = NULL; - CUTSCENE_SYSTEM.entityInteracted = NULL; - CUTSCENE_SYSTEM.entityLastCreated = NULL; - CUTSCENE_SYSTEM.entityLastRef = NULL; - CUTSCENE_SYSTEM.areaLastCreated = CUTSCENE_AREA_LAST_CREATED; - CUTSCENE_SYSTEM.textMiniLastCreated = CUTSCENE_TEXT_MINI_LAST_CREATED; -} diff --git a/src/dusk/rpg/cutscene/cutscenesystem.h b/src/dusk/rpg/cutscene/cutscenesystem.h deleted file mode 100644 index 6f95a2a7..00000000 --- a/src/dusk/rpg/cutscene/cutscenesystem.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "cutscene.h" - -typedef struct entity_s entity_t; - -#define CUTSCENE_ENTITY_INTERACT ((uint8_t)0xFE) -#define CUTSCENE_ENTITY_INTERACTED ((uint8_t)0xFD) -#define CUTSCENE_ENTITY_LAST_CREATED ((uint8_t)0xFC) -#define CUTSCENE_ENTITY_LAST_REF ((uint8_t)0xFB) -#define CUTSCENE_AREA_LAST_CREATED ((uint8_t)0xFF) -#define CUTSCENE_TEXT_MINI_LAST_CREATED ((uint8_t)0xFA) - -// Maximum number of bytes a running cutscene may request via -// cutscene_t.dataSize. -#define CUTSCENE_SYSTEM_SIZE_MAX 8192 - -typedef struct { - const cutscene_t *scene; - uint8_t currentItem; - cutscenepause_t pause; - entity_t *entityInteract; - entity_t *entityInteracted; - entity_t *entityLastCreated; - entity_t *entityLastRef; - uint8_t areaLastCreated; - uint8_t textMiniLastCreated; - - // Data (used by the current item). - cutsceneitemdata_t data; - - // Custom user data for the running cutscene, sized per-scene by - // cutscene_t.dataSize. - uint8_t userData[CUTSCENE_SYSTEM_SIZE_MAX]; -} cutscenesystem_t; - -extern cutscenesystem_t CUTSCENE_SYSTEM; - -/** - * Initialize the cutscene system. - */ -void cutsceneSystemInit(); - -/** - * Start a cutscene with no bound entities. - * - * @param cutscene Pointer to the cutscene to start. - */ -void cutsceneSystemStartCutscene(const cutscene_t *cutscene); - -/** - * Start a cutscene with the two entities that triggered it. - * - * @param cutscene Pointer to the cutscene to start. - * @param interact The entity that initiated the interaction (player). - * @param interacted The entity that was interacted with (NPC). - */ -void cutsceneSystemStartCutsceneWith( - const cutscene_t *cutscene, - entity_t *interact, - entity_t *interacted -); - -/** - * Resolves a raw entity index (or sentinel) to an entity pointer. - * Handles CUTSCENE_ENTITY_INTERACT, CUTSCENE_ENTITY_INTERACTED, - * CUTSCENE_ENTITY_LAST_CREATED and CUTSCENE_ENTITY_LAST_REF. - * Updates CUTSCENE_SYSTEM.entityLastRef to the resolved entity. - * Asserts the resolved entity is within bounds. - * - * @param entityIndex Raw entity index or sentinel value. - * @returns Pointer to the resolved entity. - */ -entity_t * cutsceneSystemGetEntity(const uint8_t entityIndex); - -/** - * Resolves a raw map area ID (or CUTSCENE_AREA_LAST_CREATED sentinel) to - * a concrete map area ID. - * - * @param areaId Raw map area ID or sentinel value. - * @returns The resolved map area ID. - */ -uint8_t cutsceneSystemGetAreaId(const uint8_t areaId); - -/** - * Resolves a raw mini textbox slot index (or CUTSCENE_TEXT_MINI_LAST_CREATED - * sentinel) to a concrete UI_TEXTBOX_MINI_LIST slot index. - * - * @param index Raw slot index or sentinel value. - * @returns The resolved slot index. - */ -uint8_t cutsceneSystemGetTextMiniId(const uint8_t index); - -/** - * Advance to the next item in the cutscene. - */ -void cutsceneSystemNext(); - -/** - * Update the cutscene system for one frame. - */ -void cutsceneSystemUpdate(); - -/** - * Get the current cutscene item. - * - * @return Pointer to the current cutscene item. - */ -const cutsceneitem_t * cutsceneSystemGetCurrentItem(); - -/** - * Disposes of the cutscene system, stopping any active cutscene. - */ -void cutsceneSystemDispose(); \ No newline at end of file diff --git a/src/dusk/rpg/cutscene/item/CMakeLists.txt b/src/dusk/rpg/cutscene/item/CMakeLists.txt deleted file mode 100755 index a3ce80fe..00000000 --- a/src/dusk/rpg/cutscene/item/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2025 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - cutsceneitem.c - cutscenecallback.c -) - -add_subdirectory(control) -add_subdirectory(entity) -add_subdirectory(item) -add_subdirectory(maparea) -add_subdirectory(ui) diff --git a/src/dusk/rpg/cutscene/item/control/CMakeLists.txt b/src/dusk/rpg/cutscene/item/control/CMakeLists.txt deleted file mode 100644 index d0e492d6..00000000 --- a/src/dusk/rpg/cutscene/item/control/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - cutscenewait.c - cutscenesetpause.c - cutsceneconcurrent.c -) diff --git a/src/dusk/rpg/cutscene/item/control/cutsceneconcurrent.c b/src/dusk/rpg/cutscene/item/control/cutsceneconcurrent.c deleted file mode 100644 index 7145b383..00000000 --- a/src/dusk/rpg/cutscene/item/control/cutsceneconcurrent.c +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "assert/assert.h" - -void cutsceneConcurrentStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - assertTrue( - item->concurrent.count <= CUTSCENE_CONCURRENT_MAX, - "Too many items in CUTSCENE_CONCURRENT" - ); - for(uint8_t i = 0; i < item->concurrent.count; i++) { - assertTrue( - item->concurrent.items[i].type != CUTSCENE_ITEM_TYPE_CONCURRENT, - "Concurrent items cannot be nested" - ); - cutsceneItemStart( - &item->concurrent.items[i], - (cutsceneitemdata_t *)&data->concurrent.childData[i] - ); - } -} - -bool_t cutsceneConcurrentUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - for(uint8_t i = 0; i < item->concurrent.count; i++) { - if(data->concurrent.doneMask & (1u << i)) continue; - if(cutsceneItemUpdate( - &item->concurrent.items[i], - (cutsceneitemdata_t *)&data->concurrent.childData[i] - )) { - data->concurrent.doneMask |= (uint8_t)(1u << i); - } - } - uint8_t allDone = (uint8_t)((1u << item->concurrent.count) - 1u); - return data->concurrent.doneMask == allDone; -} diff --git a/src/dusk/rpg/cutscene/item/control/cutsceneconcurrent.h b/src/dusk/rpg/cutscene/item/control/cutsceneconcurrent.h deleted file mode 100644 index c61b8d2e..00000000 --- a/src/dusk/rpg/cutscene/item/control/cutsceneconcurrent.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "cutscenewait.h" -#include "rpg/cutscene/item/entity/cutsceneentitywalkto.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -/** Maximum number of items that may run inside a CUTSCENE_CONCURRENT. */ -#define CUTSCENE_CONCURRENT_MAX 8 - -/** - * Static (const) data for a concurrent cutscene item. - */ -typedef struct { - const cutsceneitem_t *items; - uint8_t count; -} cutsceneconcurrent_t; - -/** - * Runtime data for one non-concurrent child item. - * Concurrent items cannot be nested. - */ -typedef union { - cutscenewaitdata_t wait; - cutsceneentitywalktodata_t entityWalkTo; -} cutsceneconcurrentchilddata_t; - -/** Runtime data for a running concurrent item. */ -typedef struct { - cutsceneconcurrentchilddata_t childData[CUTSCENE_CONCURRENT_MAX]; - uint8_t doneMask; -} cutsceneconcurrentdata_t; - -/** - * Starts a concurrent item (starts all child items simultaneously). - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneConcurrentStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates a concurrent item (ticks all unfinished children). - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true once every child has completed. - */ -bool_t cutsceneConcurrentUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/item/control/cutscenesetpause.c b/src/dusk/rpg/cutscene/item/control/cutscenesetpause.c deleted file mode 100644 index 1a22b47a..00000000 --- a/src/dusk/rpg/cutscene/item/control/cutscenesetpause.c +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "rpg/cutscene/cutscenesystem.h" - -void cutsceneSetPauseStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - CUTSCENE_SYSTEM.pause = item->setPause; -} - -bool_t cutsceneSetPauseUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - return true; -} diff --git a/src/dusk/rpg/cutscene/item/control/cutscenesetpause.h b/src/dusk/rpg/cutscene/item/control/cutscenesetpause.h deleted file mode 100644 index bb04cf6a..00000000 --- a/src/dusk/rpg/cutscene/item/control/cutscenesetpause.h +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "rpg/cutscene/cutscenepause.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -/** - * Starts a set-pause item (applies the new pause flags immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneSetPauseStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates a set-pause item (always completes immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true always. - */ -bool_t cutsceneSetPauseUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/item/control/cutscenewait.c b/src/dusk/rpg/cutscene/item/control/cutscenewait.c deleted file mode 100644 index f85f1d2c..00000000 --- a/src/dusk/rpg/cutscene/item/control/cutscenewait.c +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "time/time.h" - -void cutsceneWaitStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - data->wait = item->wait; -} - -bool_t cutsceneWaitUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - data->wait -= TIME.delta; - return data->wait <= 0; -} diff --git a/src/dusk/rpg/cutscene/item/control/cutscenewait.h b/src/dusk/rpg/cutscene/item/control/cutscenewait.h deleted file mode 100644 index 985dca67..00000000 --- a/src/dusk/rpg/cutscene/item/control/cutscenewait.h +++ /dev/null @@ -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 struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -typedef float_t cutscenewait_t; -typedef float_t cutscenewaitdata_t; - -/** - * Starts a wait item (stores the duration in data). - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneWaitStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates a wait item. - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true when the wait has elapsed. - */ -bool_t cutsceneWaitUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); \ No newline at end of file diff --git a/src/dusk/rpg/cutscene/item/cutscenecallback.c b/src/dusk/rpg/cutscene/item/cutscenecallback.c deleted file mode 100644 index 29ee598f..00000000 --- a/src/dusk/rpg/cutscene/item/cutscenecallback.c +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "cutsceneitem.h" -#include "rpg/cutscene/cutscenesystem.h" - -void cutsceneCallbackStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - if(item->callback != NULL) item->callback(CUTSCENE_SYSTEM.userData); -} - -bool_t cutsceneCallbackUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - return true; -} diff --git a/src/dusk/rpg/cutscene/item/cutscenecallback.h b/src/dusk/rpg/cutscene/item/cutscenecallback.h deleted file mode 100644 index ab65d68a..00000000 --- a/src/dusk/rpg/cutscene/item/cutscenecallback.h +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -typedef void (*cutscenecallback_t)(void *userData); - -/** - * Starts a callback item (invokes the callback immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneCallbackStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates a callback item (always completes immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true always. - */ -bool_t cutsceneCallbackUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); \ No newline at end of file diff --git a/src/dusk/rpg/cutscene/item/cutsceneitem.c b/src/dusk/rpg/cutscene/item/cutsceneitem.c deleted file mode 100644 index 120b37ae..00000000 --- a/src/dusk/rpg/cutscene/item/cutsceneitem.c +++ /dev/null @@ -1,147 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/cutscenesystem.h" - -cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = { - [CUTSCENE_ITEM_TYPE_NULL] = { 0 }, - - [CUTSCENE_ITEM_TYPE_TEXT] = { - .init = cutsceneTextStart, - .update = cutsceneTextUpdate - }, - - [CUTSCENE_ITEM_TYPE_TEXT_MINI] = { - .init = cutsceneTextMiniStart, - .update = cutsceneTextMiniUpdate - }, - - [CUTSCENE_ITEM_TYPE_TEXT_MINI_HIDE] = { - .init = cutsceneTextMiniHideStart, - .update = cutsceneTextMiniHideUpdate - }, - - [CUTSCENE_ITEM_TYPE_CALLBACK] = { - .init = cutsceneCallbackStart, - .update = cutsceneCallbackUpdate - }, - - [CUTSCENE_ITEM_TYPE_WAIT] = { - .init = cutsceneWaitStart, - .update = cutsceneWaitUpdate - }, - - [CUTSCENE_ITEM_TYPE_CUTSCENE] = { - .init = cutsceneCutsceneStart, - .update = cutsceneCutsceneUpdate - }, - - [CUTSCENE_ITEM_TYPE_ENTITY_TELEPORT] = { - .init = cutsceneEntityTeleportStart, - .update = cutsceneEntityTeleportUpdate - }, - - [CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO] = { - .init = cutsceneEntityWalkToStart, - .update = cutsceneEntityWalkToUpdate - }, - - [CUTSCENE_ITEM_TYPE_FADE] = { - .init = cutsceneFadeStart, - .update = cutsceneFadeUpdate - }, - - [CUTSCENE_ITEM_TYPE_SET_PAUSE] = { - .init = cutsceneSetPauseStart, - .update = cutsceneSetPauseUpdate - }, - - [CUTSCENE_ITEM_TYPE_CONCURRENT] = { - .init = cutsceneConcurrentStart, - .update = cutsceneConcurrentUpdate - }, - - [CUTSCENE_ITEM_TYPE_ITEM_GIVE] = { - .init = cutsceneItemGiveStart, - .update = cutsceneItemGiveUpdate - }, - - [CUTSCENE_ITEM_TYPE_ENTITY_REMOVE] = { - .init = cutsceneEntityRemoveStart, - .update = cutsceneEntityRemoveUpdate - }, - - [CUTSCENE_ITEM_TYPE_ENTITY_ADD] = { - .init = cutsceneEntityAddStart, - .update = cutsceneEntityAddUpdate - }, - - [CUTSCENE_ITEM_TYPE_ENTITY_TURN] = { - .init = cutsceneEntityTurnStart, - .update = cutsceneEntityTurnUpdate - }, - - [CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO_ENTITY] = { - .init = cutsceneEntityWalkToEntityStart, - .update = cutsceneEntityWalkToEntityUpdate - }, - - [CUTSCENE_ITEM_TYPE_MAP_AREA_ADD] = { - .init = cutsceneMapAreaAddStart, - .update = cutsceneMapAreaAddUpdate - }, - - [CUTSCENE_ITEM_TYPE_MAP_AREA_REMOVE] = { - .init = cutsceneMapAreaRemoveStart, - .update = cutsceneMapAreaRemoveUpdate - }, - - [CUTSCENE_ITEM_TYPE_MAP_AREA_WAIT] = { - .init = cutsceneMapAreaWaitStart, - .update = cutsceneMapAreaWaitUpdate - }, - - [CUTSCENE_ITEM_TYPE_EMOJI] = { - .init = cutsceneEmojiStart, - .update = cutsceneEmojiUpdate - }, - - [CUTSCENE_ITEM_TYPE_SHAKE] = { - .init = cutsceneShakeStart, - .update = cutsceneShakeUpdate - } -}; - -void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data) { - cutsceneiteminitcallback_t *init = CUTSCENE_ITEM_CALLBACKS[item->type].init; - if(init != NULL) init(item, data); -} - -bool_t cutsceneItemUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - cutsceneitemupdatecallback_t *update = - CUTSCENE_ITEM_CALLBACKS[item->type].update; - if(update == NULL) return false; - - return update(item, data); -} - -void cutsceneCutsceneStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - if(item->cutscene != NULL) cutsceneSystemStartCutscene(item->cutscene); -} - -bool_t cutsceneCutsceneUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - return false; -} diff --git a/src/dusk/rpg/cutscene/item/cutsceneitem.h b/src/dusk/rpg/cutscene/item/cutsceneitem.h deleted file mode 100644 index 6f1ab772..00000000 --- a/src/dusk/rpg/cutscene/item/cutsceneitem.h +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "cutscenecallback.h" -#include "control/cutscenewait.h" -#include "control/cutscenesetpause.h" -#include "control/cutsceneconcurrent.h" -#include "entity/cutsceneentityteleport.h" -#include "entity/cutsceneentitywalkto.h" -#include "entity/cutsceneentityremove.h" -#include "entity/cutsceneentityadd.h" -#include "entity/cutsceneentityturn.h" -#include "entity/cutsceneentitywalktoentity.h" -#include "ui/cutscenetext.h" -#include "ui/cutscenetextmini.h" -#include "ui/cutscenetextminihide.h" -#include "ui/cutscenefade.h" -#include "ui/cutsceneemoji.h" -#include "ui/cutsceneshake.h" -#include "item/cutsceneitemgive.h" -#include "maparea/cutscenemapareaadd.h" -#include "maparea/cutscenemaparearemove.h" -#include "maparea/cutscenemapareawait.h" - -typedef struct cutscene_s cutscene_t; - -typedef enum { - CUTSCENE_ITEM_TYPE_NULL, - - CUTSCENE_ITEM_TYPE_TEXT, - CUTSCENE_ITEM_TYPE_TEXT_MINI, - CUTSCENE_ITEM_TYPE_TEXT_MINI_HIDE, - CUTSCENE_ITEM_TYPE_CALLBACK, - CUTSCENE_ITEM_TYPE_WAIT, - CUTSCENE_ITEM_TYPE_CUTSCENE, - CUTSCENE_ITEM_TYPE_ENTITY_TELEPORT, - CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO, - CUTSCENE_ITEM_TYPE_FADE, - CUTSCENE_ITEM_TYPE_SET_PAUSE, - CUTSCENE_ITEM_TYPE_CONCURRENT, - CUTSCENE_ITEM_TYPE_ITEM_GIVE, - CUTSCENE_ITEM_TYPE_ENTITY_REMOVE, - CUTSCENE_ITEM_TYPE_ENTITY_ADD, - CUTSCENE_ITEM_TYPE_ENTITY_TURN, - CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO_ENTITY, - CUTSCENE_ITEM_TYPE_MAP_AREA_ADD, - CUTSCENE_ITEM_TYPE_MAP_AREA_REMOVE, - CUTSCENE_ITEM_TYPE_MAP_AREA_WAIT, - CUTSCENE_ITEM_TYPE_EMOJI, - CUTSCENE_ITEM_TYPE_SHAKE, - - CUTSCENE_ITEM_TYPE_COUNT -} cutsceneitemtype_t; - -struct cutsceneitem_s { - cutsceneitemtype_t type; - - union { - cutscenetext_t text; - cutscenetextmini_t textMini; - cutscenetextminihide_t textMiniHide; - cutscenecallback_t callback; - cutscenewait_t wait; - const cutscene_t *cutscene; - cutsceneentityteleport_t entityTeleport; - cutsceneentitywalkto_t entityWalkTo; - cutscenefade_t fade; - cutscenepause_t setPause; - cutsceneconcurrent_t concurrent; - cutsceneitemgive_t itemGive; - cutsceneentityremove_t entityRemove; - cutsceneentityadd_t entityAdd; - cutsceneentityturn_t entityTurn; - cutsceneentitywalktoentity_t entityWalkToEntity; - cutscenemapareaadd_t mapAreaAdd; - cutscenemaparearemove_t mapAreaRemove; - cutscenemapareawait_t mapAreaWait; - cutsceneemoji_t emoji; - cutsceneshake_t shake; - }; -}; - -typedef union cutsceneitemdata_u { - cutscenewaitdata_t wait; - cutsceneentitywalktodata_t entityWalkTo; - cutsceneconcurrentdata_t concurrent; - cutscenemapareawaitdata_t mapAreaWait; -} cutsceneitemdata_t; - -typedef void (cutsceneiteminitcallback_t)( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -typedef bool_t (cutsceneitemupdatecallback_t)( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -typedef struct { - cutsceneiteminitcallback_t *init; - cutsceneitemupdatecallback_t *update; -} cutsceneitemcallbacks_t; - -extern cutsceneitemcallbacks_t - CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT]; - -/** - * Start the given cutscene item. - * - * @param item The cutscene item to start. - * @param data Runtime data storage (pre-zeroed by caller). - */ -void cutsceneItemStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Tick the given cutscene item (one frame). - * - * @param item The cutscene item to tick. - * @param data Runtime data storage. - * @returns true if the item is complete and the cutscene should advance. - */ -bool_t cutsceneItemUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Starts a nested-cutscene item, handing control over to the - * referenced cutscene. - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneCutsceneStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates a nested-cutscene item. By the time this would run, control - * has already moved on to the referenced cutscene, so this always - * reports incomplete. - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns false always. - */ -bool_t cutsceneCutsceneUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/item/entity/CMakeLists.txt b/src/dusk/rpg/cutscene/item/entity/CMakeLists.txt deleted file mode 100644 index e6dfbd2d..00000000 --- a/src/dusk/rpg/cutscene/item/entity/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - cutsceneentityteleport.c - cutsceneentitywalkto.c - cutsceneentityremove.c - cutsceneentityadd.c - cutsceneentityturn.c - cutsceneentitywalktoentity.c -) diff --git a/src/dusk/rpg/cutscene/item/entity/cutsceneentityadd.c b/src/dusk/rpg/cutscene/item/entity/cutsceneentityadd.c deleted file mode 100644 index 0ab8c36e..00000000 --- a/src/dusk/rpg/cutscene/item/entity/cutsceneentityadd.c +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "rpg/cutscene/cutscenesystem.h" -#include "rpg/entity/entity.h" -#include "assert/assert.h" - -void cutsceneEntityAddStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - uint8_t entIndex = entityGetAvailable(); - assertTrue(entIndex != 0xFF, "No available entity slots for CUTSCENE_ENTITY_ADD"); - - entity_t *entity = &ENTITIES[entIndex]; - entityInit(entity, item->entityAdd.entityType); - entityPositionSet(entity, item->entityAdd.position);// Also assigns chunk. - - CUTSCENE_SYSTEM.entityLastCreated = entity; - CUTSCENE_SYSTEM.entityLastRef = entity; -} - -bool_t cutsceneEntityAddUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - return true; -} diff --git a/src/dusk/rpg/cutscene/item/entity/cutsceneentityadd.h b/src/dusk/rpg/cutscene/item/entity/cutsceneentityadd.h deleted file mode 100644 index 5e0944b1..00000000 --- a/src/dusk/rpg/cutscene/item/entity/cutsceneentityadd.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "rpg/entity/entitytype.h" -#include "rpg/overworld/worldpos.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -typedef struct { - entitytype_t entityType; - worldpos_t position; -} cutsceneentityadd_t; - -/** - * Starts an entity add step (spawns the entity into the world immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneEntityAddStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates an entity add step (always completes immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true always. - */ -bool_t cutsceneEntityAddUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/item/entity/cutsceneentityremove.c b/src/dusk/rpg/cutscene/item/entity/cutsceneentityremove.c deleted file mode 100644 index a358fb91..00000000 --- a/src/dusk/rpg/cutscene/item/entity/cutsceneentityremove.c +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "rpg/cutscene/cutscenesystem.h" -#include "rpg/entity/entity.h" - -void cutsceneEntityRemoveStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - cutsceneSystemGetEntity(item->entityRemove.entityIndex)->type = \ - ENTITY_TYPE_NULL; -} - -bool_t cutsceneEntityRemoveUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - return true; -} diff --git a/src/dusk/rpg/cutscene/item/entity/cutsceneentityremove.h b/src/dusk/rpg/cutscene/item/entity/cutsceneentityremove.h deleted file mode 100644 index 2c7c4950..00000000 --- a/src/dusk/rpg/cutscene/item/entity/cutsceneentityremove.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -typedef struct { - uint8_t entityIndex; -} cutsceneentityremove_t; - -/** - * Starts an entity remove step (removes the entity from the world immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneEntityRemoveStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates an entity remove step (always completes immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true always. - */ -bool_t cutsceneEntityRemoveUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/item/entity/cutsceneentityteleport.c b/src/dusk/rpg/cutscene/item/entity/cutsceneentityteleport.c deleted file mode 100644 index 789e9e37..00000000 --- a/src/dusk/rpg/cutscene/item/entity/cutsceneentityteleport.c +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "rpg/cutscene/cutscenesystem.h" -#include "rpg/entity/entity.h" - -void cutsceneEntityTeleportStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - entityPositionSet( - cutsceneSystemGetEntity(item->entityTeleport.entityIndex), - item->entityTeleport.target - ); -} - -bool_t cutsceneEntityTeleportUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - return true; -} diff --git a/src/dusk/rpg/cutscene/item/entity/cutsceneentityteleport.h b/src/dusk/rpg/cutscene/item/entity/cutsceneentityteleport.h deleted file mode 100644 index 3237f89e..00000000 --- a/src/dusk/rpg/cutscene/item/entity/cutsceneentityteleport.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "rpg/overworld/worldpos.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -typedef struct { - uint8_t entityIndex; - worldpos_t target; -} cutsceneentityteleport_t; - -/** - * Starts an entity teleport item (teleports the entity immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneEntityTeleportStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates an entity teleport item (always completes immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true always. - */ -bool_t cutsceneEntityTeleportUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/item/entity/cutsceneentityturn.c b/src/dusk/rpg/cutscene/item/entity/cutsceneentityturn.c deleted file mode 100644 index 707389b4..00000000 --- a/src/dusk/rpg/cutscene/item/entity/cutsceneentityturn.c +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "rpg/cutscene/cutscenesystem.h" -#include "rpg/entity/entity.h" - -void cutsceneEntityTurnStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { -} - -bool_t cutsceneEntityTurnUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - entity_t *entity = cutsceneSystemGetEntity(item->entityTurn.entityIndex); - if( - entity->direction == item->entityTurn.direction && - entity->animation == ENTITY_ANIM_IDLE - ) return true; - - entityTurn(entity, item->entityTurn.direction); - return false; -} diff --git a/src/dusk/rpg/cutscene/item/entity/cutsceneentityturn.h b/src/dusk/rpg/cutscene/item/entity/cutsceneentityturn.h deleted file mode 100644 index a14bb31b..00000000 --- a/src/dusk/rpg/cutscene/item/entity/cutsceneentityturn.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "rpg/entity/entitydir.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -typedef struct { - uint8_t entityIndex; - entitydir_t direction; -} cutsceneentityturn_t; - -/** - * Starts an entity turn step. The turn itself is driven from Update, since - * the entity may still be finishing a previous action. - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneEntityTurnStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates an entity turn step, retrying entityTurn until it takes effect - * and its animation completes. - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true once the entity is idle and facing the target direction. - */ -bool_t cutsceneEntityTurnUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/item/entity/cutsceneentitywalkto.c b/src/dusk/rpg/cutscene/item/entity/cutsceneentitywalkto.c deleted file mode 100644 index d629df15..00000000 --- a/src/dusk/rpg/cutscene/item/entity/cutsceneentitywalkto.c +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "rpg/cutscene/cutscenesystem.h" -#include "rpg/entity/entitypathstep.h" - -void cutsceneEntityWalkToStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - data->entityWalkTo.currentIndex = 0; -} - -bool_t cutsceneEntityWalkToUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - uint8_t i = data->entityWalkTo.currentIndex; - entity_t *e = cutsceneSystemGetEntity(item->entityWalkTo.entityIndex); - if(!entityPathStep( - e, - item->entityWalkTo.positions[i], - item->entityWalkTo.walkAround - )) return false; - i++; - if(i < item->entityWalkTo.count) { - data->entityWalkTo.currentIndex = i; - return false; - } - return true; -} diff --git a/src/dusk/rpg/cutscene/item/entity/cutsceneentitywalkto.h b/src/dusk/rpg/cutscene/item/entity/cutsceneentitywalkto.h deleted file mode 100644 index ecf4677c..00000000 --- a/src/dusk/rpg/cutscene/item/entity/cutsceneentitywalkto.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "rpg/overworld/worldpos.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -typedef struct { - uint8_t entityIndex; - const worldpos_t *positions; - uint8_t count; - bool_t walkAround; -} cutsceneentitywalkto_t; - -typedef struct { - uint8_t currentIndex; -} cutsceneentitywalktodata_t; - -/** - * Starts an entity walk-to item (resets the waypoint index). - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneEntityWalkToStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates an entity walk-to item (steps the entity toward the next waypoint). - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true once all waypoints have been reached. - */ -bool_t cutsceneEntityWalkToUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/item/entity/cutsceneentitywalktoentity.c b/src/dusk/rpg/cutscene/item/entity/cutsceneentitywalktoentity.c deleted file mode 100644 index 2b695545..00000000 --- a/src/dusk/rpg/cutscene/item/entity/cutsceneentitywalktoentity.c +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "rpg/cutscene/cutscenesystem.h" -#include "rpg/entity/entity.h" -#include "rpg/entity/entitypathstep.h" -#include "rpg/overworld/map.h" - -void cutsceneEntityWalkToEntityStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { -} - -bool_t cutsceneEntityWalkToEntityUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - entity_t *entity = cutsceneSystemGetEntity( - item->entityWalkToEntity.entityIndex - ); - entity_t *target = cutsceneSystemGetEntity( - item->entityWalkToEntity.targetEntityIndex - ); - - worldpos_t dest = { - .x = (worldunit_t)(target->position.x + item->entityWalkToEntity.offsetX), - .y = (worldunit_t)(target->position.y + item->entityWalkToEntity.offsetY), - .z = target->position.z - }; - - worldunit_t z; - if(mapGetWalkableZNear(dest.x, dest.y, target->position.z, &z)) dest.z = z; - - return entityPathStep(entity, dest, true); -} diff --git a/src/dusk/rpg/cutscene/item/entity/cutsceneentitywalktoentity.h b/src/dusk/rpg/cutscene/item/entity/cutsceneentitywalktoentity.h deleted file mode 100644 index 0014ed4b..00000000 --- a/src/dusk/rpg/cutscene/item/entity/cutsceneentitywalktoentity.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "rpg/overworld/worldpos.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -typedef struct { - uint8_t entityIndex; - uint8_t targetEntityIndex; - worldunit_t offsetX; - worldunit_t offsetY; -} cutsceneentitywalktoentity_t; - -/** - * Starts an entity walk-to-entity item. No setup is needed, the destination - * is recomputed from the target's live position every Update. - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneEntityWalkToEntityStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates an entity walk-to-entity item. Re-reads the target entity's - * current position each frame, applies the X/Y offset, resolves the - * destination Z from nearby terrain (to account for ramps), and steps - * the entity toward it. - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true once the entity has reached the target's side. - */ -bool_t cutsceneEntityWalkToEntityUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/item/item/CMakeLists.txt b/src/dusk/rpg/cutscene/item/item/CMakeLists.txt deleted file mode 100644 index df5ebc21..00000000 --- a/src/dusk/rpg/cutscene/item/item/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - cutsceneitemgive.c -) diff --git a/src/dusk/rpg/cutscene/item/item/cutsceneitemgive.c b/src/dusk/rpg/cutscene/item/item/cutsceneitemgive.c deleted file mode 100644 index 69c27136..00000000 --- a/src/dusk/rpg/cutscene/item/item/cutsceneitemgive.c +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "rpg/item/itemgive.h" -#include "ui/rpg/textbox/uitextboxmain.h" - -void cutsceneItemGiveStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - itemGive(item->itemGive.item, item->itemGive.quantity); -} - -bool_t cutsceneItemGiveUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - return !uiTextboxMainIsActive(); -} diff --git a/src/dusk/rpg/cutscene/item/item/cutsceneitemgive.h b/src/dusk/rpg/cutscene/item/item/cutsceneitemgive.h deleted file mode 100644 index 12a53c01..00000000 --- a/src/dusk/rpg/cutscene/item/item/cutsceneitemgive.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "rpg/item/item.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -typedef struct { - itemid_t item; - uint8_t quantity; -} cutsceneitemgive_t; - -/** - * Starts a give-item step (adds the item to the player's backpack immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneItemGiveStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates a give-item step (always completes immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true always. - */ -bool_t cutsceneItemGiveUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/item/maparea/CMakeLists.txt b/src/dusk/rpg/cutscene/item/maparea/CMakeLists.txt deleted file mode 100644 index cd32d7f6..00000000 --- a/src/dusk/rpg/cutscene/item/maparea/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - cutscenemapareaadd.c - cutscenemaparearemove.c - cutscenemapareawait.c -) diff --git a/src/dusk/rpg/cutscene/item/maparea/cutscenemapareaadd.c b/src/dusk/rpg/cutscene/item/maparea/cutscenemapareaadd.c deleted file mode 100644 index 7cbd2f19..00000000 --- a/src/dusk/rpg/cutscene/item/maparea/cutscenemapareaadd.c +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "rpg/cutscene/cutscenesystem.h" - -void cutsceneMapAreaAddStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - CUTSCENE_SYSTEM.areaLastCreated = mapAreaAdd( - item->mapAreaAdd.min, - item->mapAreaAdd.max, - item->mapAreaAdd.callback, - item->mapAreaAdd.notify, - item->mapAreaAdd.trigger - ); -} - -bool_t cutsceneMapAreaAddUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - return true; -} diff --git a/src/dusk/rpg/cutscene/item/maparea/cutscenemapareaadd.h b/src/dusk/rpg/cutscene/item/maparea/cutscenemapareaadd.h deleted file mode 100644 index b1614335..00000000 --- a/src/dusk/rpg/cutscene/item/maparea/cutscenemapareaadd.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "rpg/overworld/maparea.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -typedef struct { - worldpos_t min; - worldpos_t max; - mapareacallback_t callback; - uint8_t notify; - uint8_t trigger; -} cutscenemapareaadd_t; - -/** - * Starts a map area add step (adds the area immediately, storing its ID - * in CUTSCENE_SYSTEM.areaLastCreated). - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneMapAreaAddStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates a map area add step (always completes immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true always. - */ -bool_t cutsceneMapAreaAddUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/item/maparea/cutscenemaparearemove.c b/src/dusk/rpg/cutscene/item/maparea/cutscenemaparearemove.c deleted file mode 100644 index 58045ba9..00000000 --- a/src/dusk/rpg/cutscene/item/maparea/cutscenemaparearemove.c +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "rpg/cutscene/cutscenesystem.h" -#include "rpg/overworld/maparea.h" - -void cutsceneMapAreaRemoveStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - mapAreaRemove(cutsceneSystemGetAreaId(item->mapAreaRemove.areaId)); -} - -bool_t cutsceneMapAreaRemoveUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - return true; -} diff --git a/src/dusk/rpg/cutscene/item/maparea/cutscenemaparearemove.h b/src/dusk/rpg/cutscene/item/maparea/cutscenemaparearemove.h deleted file mode 100644 index 3d1550dd..00000000 --- a/src/dusk/rpg/cutscene/item/maparea/cutscenemaparearemove.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -typedef struct { - uint8_t areaId; -} cutscenemaparearemove_t; - -/** - * Starts a map area remove step (removes the area immediately). Accepts - * CUTSCENE_AREA_LAST_CREATED in place of a literal area ID. - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneMapAreaRemoveStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates a map area remove step (always completes immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true always. - */ -bool_t cutsceneMapAreaRemoveUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/item/maparea/cutscenemapareawait.c b/src/dusk/rpg/cutscene/item/maparea/cutscenemapareawait.c deleted file mode 100644 index 7b0e3f2e..00000000 --- a/src/dusk/rpg/cutscene/item/maparea/cutscenemapareawait.c +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "rpg/cutscene/cutscenesystem.h" -#include "rpg/overworld/maparea.h" -#include "assert/assert.h" - -void cutsceneMapAreaWaitStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - assertTrue( - item->mapAreaWait.count <= CUTSCENE_MAP_AREA_WAIT_MAX, - "Too many areas in CUTSCENE_MAP_AREA_WAIT" - ); - - for(uint8_t i = 0; i < item->mapAreaWait.count; i++) { - uint8_t areaId = cutsceneSystemGetAreaId(item->mapAreaWait.areaIds[i]); - data->mapAreaWait.baseline[i] = MAP_AREAS[areaId].triggerCount; - } -} - -bool_t cutsceneMapAreaWaitUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - for(uint8_t i = 0; i < item->mapAreaWait.count; i++) { - uint8_t areaId = cutsceneSystemGetAreaId(item->mapAreaWait.areaIds[i]); - if(MAP_AREAS[areaId].triggerCount != data->mapAreaWait.baseline[i]) { - return true; - } - } - - return false; -} diff --git a/src/dusk/rpg/cutscene/item/maparea/cutscenemapareawait.h b/src/dusk/rpg/cutscene/item/maparea/cutscenemapareawait.h deleted file mode 100644 index 039b4b76..00000000 --- a/src/dusk/rpg/cutscene/item/maparea/cutscenemapareawait.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -/** Maximum number of areas a single CUTSCENE_MAP_AREA_WAIT may watch. */ -#define CUTSCENE_MAP_AREA_WAIT_MAX 4 - -typedef struct { - const uint8_t *areaIds; - uint8_t count; -} cutscenemapareawait_t; - -typedef struct { - uint32_t baseline[CUTSCENE_MAP_AREA_WAIT_MAX]; -} cutscenemapareawaitdata_t; - -/** - * Starts a map area wait step, snapshotting each watched area's current - * trigger count. - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneMapAreaWaitStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates a map area wait step, completing once any watched area's - * trigger count has changed since Start (i.e. its callback fired). - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true once any watched area has been triggered. - */ -bool_t cutsceneMapAreaWaitUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/item/ui/CMakeLists.txt b/src/dusk/rpg/cutscene/item/ui/CMakeLists.txt deleted file mode 100644 index 8f2e44fc..00000000 --- a/src/dusk/rpg/cutscene/item/ui/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - cutscenetext.c - cutscenetextmini.c - cutscenetextminihide.c - cutscenefade.c - cutsceneemoji.c - cutsceneshake.c -) diff --git a/src/dusk/rpg/cutscene/item/ui/cutsceneemoji.c b/src/dusk/rpg/cutscene/item/ui/cutsceneemoji.c deleted file mode 100644 index 2cd52bb7..00000000 --- a/src/dusk/rpg/cutscene/item/ui/cutsceneemoji.c +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "rpg/cutscene/cutscenesystem.h" -#include "rpg/entity/entity.h" - -void cutsceneEmojiStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - entity_t *entity = cutsceneSystemGetEntity(item->emoji.entityIndex); - uiEmojiAdd(entity->id, item->emoji.duration, item->emoji.emojiType); -} - -bool_t cutsceneEmojiUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - return true; -} diff --git a/src/dusk/rpg/cutscene/item/ui/cutsceneemoji.h b/src/dusk/rpg/cutscene/item/ui/cutsceneemoji.h deleted file mode 100644 index 1c9deeca..00000000 --- a/src/dusk/rpg/cutscene/item/ui/cutsceneemoji.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" -#include "ui/rpg/uiemoji.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -typedef struct { - uint8_t entityIndex; - float_t duration; - uiemojitype_t emojiType; -} cutsceneemoji_t; - -/** - * Starts an emoji step (shows an emoji above the entity for the given - * duration, then completes immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneEmojiStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates an emoji step (always completes immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true always. - */ -bool_t cutsceneEmojiUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/item/ui/cutscenefade.c b/src/dusk/rpg/cutscene/item/ui/cutscenefade.c deleted file mode 100644 index 8ffa4656..00000000 --- a/src/dusk/rpg/cutscene/item/ui/cutscenefade.c +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "ui/overlay/uifullbox.h" - -void cutsceneFadeStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - uiFullboxTransition( - &UI_FULLBOX_OVER, - item->fade.from, - item->fade.to, - item->fade.duration, - item->fade.easing - ); -} - -bool_t cutsceneFadeUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - return !( - UI_FULLBOX_OVER.duration > 0.0f && - UI_FULLBOX_OVER.time < UI_FULLBOX_OVER.duration - ); -} diff --git a/src/dusk/rpg/cutscene/item/ui/cutscenefade.h b/src/dusk/rpg/cutscene/item/ui/cutscenefade.h deleted file mode 100644 index c23b12f9..00000000 --- a/src/dusk/rpg/cutscene/item/ui/cutscenefade.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "display/color.h" -#include "animation/easing.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -typedef struct { - color_t from; - color_t to; - float_t duration; - easingtype_t easing; -} cutscenefade_t; - -/** - * Starts a fade item (begins the overlay transition). - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneFadeStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates a fade item. - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true once the overlay transition has completed. - */ -bool_t cutsceneFadeUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/item/ui/cutsceneshake.c b/src/dusk/rpg/cutscene/item/ui/cutsceneshake.c deleted file mode 100644 index eb1eca92..00000000 --- a/src/dusk/rpg/cutscene/item/ui/cutsceneshake.c +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "rpg/rpgcamera.h" - -void cutsceneShakeStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - rpgCameraShake(item->shake.amount, item->shake.duration); -} - -bool_t cutsceneShakeUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - return true; -} diff --git a/src/dusk/rpg/cutscene/item/ui/cutsceneshake.h b/src/dusk/rpg/cutscene/item/ui/cutsceneshake.h deleted file mode 100644 index cc0c0f24..00000000 --- a/src/dusk/rpg/cutscene/item/ui/cutsceneshake.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -typedef struct { - uint8_t amount; - float_t duration; -} cutsceneshake_t; - -/** - * Starts a camera shake item (kicks off the shake on the RPG camera). - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneShakeStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates a camera shake item. The shake itself runs asynchronously on - * the RPG camera, so this always completes immediately. - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true always. - */ -bool_t cutsceneShakeUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/item/ui/cutscenetext.c b/src/dusk/rpg/cutscene/item/ui/cutscenetext.c deleted file mode 100644 index 9ad60c75..00000000 --- a/src/dusk/rpg/cutscene/item/ui/cutscenetext.c +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "ui/rpg/textbox/uitextboxmain.h" - -void cutsceneTextStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - uiTextboxMainSetText(item->text.text); -} - -bool_t cutsceneTextUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - return !uiTextboxMainIsActive(); -} diff --git a/src/dusk/rpg/cutscene/item/ui/cutscenetext.h b/src/dusk/rpg/cutscene/item/ui/cutscenetext.h deleted file mode 100644 index 61017806..00000000 --- a/src/dusk/rpg/cutscene/item/ui/cutscenetext.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -#define CUTSCENE_TEXT_MAX_CHARS 256 - -typedef struct { - char_t text[CUTSCENE_TEXT_MAX_CHARS]; -} cutscenetext_t; - -/** - * Starts a text item (shows the textbox with the item's text). - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneTextStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates a text item. - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true once the textbox has been dismissed. - */ -bool_t cutsceneTextUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); \ No newline at end of file diff --git a/src/dusk/rpg/cutscene/item/ui/cutscenetextmini.c b/src/dusk/rpg/cutscene/item/ui/cutscenetextmini.c deleted file mode 100644 index f4d7f750..00000000 --- a/src/dusk/rpg/cutscene/item/ui/cutscenetextmini.c +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "rpg/cutscene/cutscenesystem.h" -#include "ui/rpg/textbox/uitextboxminilist.h" - -void cutsceneTextMiniStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - uint8_t index = uiTextboxMiniListGetNext(); - uiTextboxMiniShow( - &UI_TEXTBOX_MINI_LIST[index], - item->textMini.text, - item->textMini.position, - item->textMini.duration, - NULL, - NULL - ); - CUTSCENE_SYSTEM.textMiniLastCreated = index; -} - -bool_t cutsceneTextMiniUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - return true; -} diff --git a/src/dusk/rpg/cutscene/item/ui/cutscenetextmini.h b/src/dusk/rpg/cutscene/item/ui/cutscenetextmini.h deleted file mode 100644 index fb2628e7..00000000 --- a/src/dusk/rpg/cutscene/item/ui/cutscenetextmini.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -#define CUTSCENE_TEXT_MINI_MAX_CHARS 128 - -typedef struct { - char_t text[CUTSCENE_TEXT_MINI_MAX_CHARS]; - vec3 position; - float_t duration; -} cutscenetextmini_t; - -/** - * Starts a mini text item (shows a mini textbox at the given world - * position for the given duration, then completes immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneTextMiniStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates a mini text item (always completes immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true always. - */ -bool_t cutsceneTextMiniUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/item/ui/cutscenetextminihide.c b/src/dusk/rpg/cutscene/item/ui/cutscenetextminihide.c deleted file mode 100644 index c02da2be..00000000 --- a/src/dusk/rpg/cutscene/item/ui/cutscenetextminihide.c +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/cutscene/item/cutsceneitem.h" -#include "rpg/cutscene/cutscenesystem.h" -#include "ui/rpg/textbox/uitextboxminilist.h" - -void cutsceneTextMiniHideStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - uint8_t index = cutsceneSystemGetTextMiniId(item->textMiniHide.index); - uiTextboxMiniClose(&UI_TEXTBOX_MINI_LIST[index]); -} - -bool_t cutsceneTextMiniHideUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -) { - return true; -} diff --git a/src/dusk/rpg/cutscene/item/ui/cutscenetextminihide.h b/src/dusk/rpg/cutscene/item/ui/cutscenetextminihide.h deleted file mode 100644 index 9dbd7f8e..00000000 --- a/src/dusk/rpg/cutscene/item/ui/cutscenetextminihide.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" - -typedef struct cutsceneitem_s cutsceneitem_t; -typedef union cutsceneitemdata_u cutsceneitemdata_t; - -typedef struct { - uint8_t index; -} cutscenetextminihide_t; - -/** - * Starts a mini text hide step (closes the mini textbox immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - */ -void cutsceneTextMiniHideStart( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); - -/** - * Updates a mini text hide step (always completes immediately). - * - * @param item The cutscene item. - * @param data Runtime data storage. - * @returns true always. - */ -bool_t cutsceneTextMiniHideUpdate( - const cutsceneitem_t *item, - cutsceneitemdata_t *data -); diff --git a/src/dusk/rpg/cutscene/scene/testcutscene.h b/src/dusk/rpg/cutscene/scene/testcutscene.h deleted file mode 100755 index 10f7ba5f..00000000 --- a/src/dusk/rpg/cutscene/scene/testcutscene.h +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "rpg/cutscene/cutscene.h" -#include "rpg/cutscene/cutscenesystem.h" - -CUTSCENE(TEST_ONE, 0, DEFAULT, - CUTSCENE_TEXT("Test One."), -); - -CUTSCENE(TEST_TWO, 0, DEFAULT, - CUTSCENE_TEXT("Test Two."), - CUTSCENE_ENTITY_ADD(ENTITY_TYPE_NPC, 4, 4, 0), - CUTSCENE_TEXT_MINI("Hello!", 4, 4, 0, 3.0f), - CUTSCENE_EMOJI( - CUTSCENE_ENTITY_LAST_CREATED, UI_EMOJI_EXCLAMATION_MARK, 2.0f - ), - CUTSCENE_ENTITY_WALK_TO(CUTSCENE_ENTITY_LAST_CREATED, 8, 2, 0), - // CUTSCENE_CONCURRENT( - // CUTSCENE_ENTITY_WALK_TO(CUTSCENE_ENTITY_INTERACT, 4, 4, 0), - // CUTSCENE_ENTITY_WALK_TO(CUTSCENE_ENTITY_INTERACTED, 8, 2, 0), - // ), - // CUTSCENE_ITEM_GIVE(ITEM_ID_POTATO, 3), - // CUTSCENE_ENTITY_REMOVE(CUTSCENE_ENTITY_INTERACT), - CUTSCENE_TEXT("Done."), -); \ No newline at end of file diff --git a/src/dusk/rpg/entity/CMakeLists.txt b/src/dusk/rpg/entity/CMakeLists.txt deleted file mode 100644 index 5af2ce99..00000000 --- a/src/dusk/rpg/entity/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2025 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - entity.c - entitydir.c - entitypathstep.c - player.c -) - -add_subdirectory(interact) -add_subdirectory(npc) -add_subdirectory(item) -add_subdirectory(global) \ No newline at end of file diff --git a/src/dusk/rpg/entity/entity.c b/src/dusk/rpg/entity/entity.c deleted file mode 100644 index 7f21bab8..00000000 --- a/src/dusk/rpg/entity/entity.c +++ /dev/null @@ -1,244 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "entity.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "util/math.h" -#include "time/time.h" -#include "rpg/overworld/map.h" -#include "rpg/overworld/maparea.h" -#include "rpg/overworld/chunk.h" - -entity_t ENTITIES[ENTITY_COUNT]; -physicsworld_t ENTITY_PHYSICS_WORLD; - -void entityInit(entity_t *entity, const entitytype_t type) { - assertNotNull(entity, "Entity pointer cannot be NULL"); - assertTrue(type < ENTITY_TYPE_COUNT, "Invalid entity type"); - assertTrue(type != ENTITY_TYPE_NULL, "Cannot have NULL entity type"); - assertTrue( - entity >= ENTITIES && entity < ENTITIES + ENTITY_COUNT, - "Entity pointer is out of bounds" - ); - - memoryZero(entity, sizeof(entity_t)); - entity->id = (uint8_t)(entity - ENTITIES); - entity->globalId = ENTITY_GLOBAL_ID_NULL; - entity->type = type; - entity->chunkIndex = 0xFF; - - const vec3 extents = ENTITY_PHYSICS_EXTENTS_DEFAULT; - physicsBodyInit(&entity->body, (vec3){ 0.0f, 0.0f, 0.0f }, extents); - - if(ENTITY_CALLBACKS[type].init != NULL) ENTITY_CALLBACKS[type].init(entity); -} - -void entityUpdate(entity_t *entity) { - assertNotNull(entity, "Entity pointer cannot be NULL"); - assertTrue(entity->type < ENTITY_TYPE_COUNT, "Invalid entity type"); - assertTrue(entity->type != ENTITY_TYPE_NULL, "Cannot have NULL entity type"); - - if(ENTITY_CALLBACKS[entity->type].movement != NULL) { - ENTITY_CALLBACKS[entity->type].movement(entity); - } - - physicsbody_t *others[ENTITY_COUNT]; - uint32_t othersCount = 0; - for(uint8_t i = 0; i < ENTITY_COUNT; i++) { - if(ENTITIES[i].type == ENTITY_TYPE_NULL) continue; - if(&ENTITIES[i] == entity) continue; - others[othersCount++] = &ENTITIES[i].body; - } - - physicsWorldStep( - &ENTITY_PHYSICS_WORLD, &entity->body, TIME.delta, others, othersCount - ); - entitySyncFromPhysics(entity); -} - -bool_t entityCanUnload(entity_t *entity) { - return entity->globalId < ENTITY_GLOBAL_ID_START; -} - -void entityMove( - entity_t *entity, const vec2 direction, const bool_t running -) { - assertNotNull(entity, "Entity pointer cannot be NULL"); - - const float_t magSq = - direction[0] * direction[0] + direction[1] * direction[1]; - if(magSq <= ENTITY_MOVE_DEADZONE * ENTITY_MOVE_DEADZONE) { - entity->body.velocity[0] = 0.0f; - entity->body.velocity[1] = 0.0f; - entity->animation = ENTITY_ANIM_IDLE; - return; - } - - const float_t mag = sqrtf(magSq); - const float_t clampedMag = mathMin(mag, 1.0f); - const float_t speed = running ? ENTITY_MOVE_RUN_SPEED : ENTITY_MOVE_WALK_SPEED; - - entity->body.velocity[0] = (direction[0] / mag) * clampedMag * speed; - entity->body.velocity[1] = (direction[1] / mag) * clampedMag * speed; - entity->direction = entityDirFromVec2(direction); - entity->animation = running ? ENTITY_ANIM_RUN : ENTITY_ANIM_WALK; -} - -void entityStop(entity_t *entity) { - const vec2 zero = { 0.0f, 0.0f }; - entityMove(entity, zero, false); -} - -void entityTurn(entity_t *entity, const entitydir_t direction) { - assertNotNull(entity, "Entity pointer cannot be NULL"); - entity->direction = direction; -} - -void entityWalk(entity_t *entity, const entitydir_t direction) { - vec2 dirVec; - entityDirToVec2(direction, dirVec); - entityMove(entity, dirVec, false); -} - -void entityRun(entity_t *entity, const entitydir_t direction) { - vec2 dirVec; - entityDirToVec2(direction, dirVec); - entityMove(entity, dirVec, true); -} - -entity_t * entityGetFacing(entity_t *entity, const float_t range) { - assertNotNull(entity, "Entity pointer cannot be NULL"); - - vec2 dir; - entityDirToVec2(entity->direction, dir); - - vec3 min, max; - physicsBodyGetBounds(&entity->body, min, max); - - const vec3 probeMin = { - min[0] + dir[0] * range, min[1] + dir[1] * range, min[2] - }; - const vec3 probeMax = { - max[0] + dir[0] * range, max[1] + dir[1] * range, max[2] - }; - - entity_t *best = NULL; - float_t bestDistSq = 0.0f; - - entity_t *ent = ENTITIES; - do { - if(ent->type == ENTITY_TYPE_NULL) continue; - if(ent == entity) continue; - - vec3 oMin, oMax; - physicsBodyGetBounds(&ent->body, oMin, oMax); - - if(probeMin[0] >= oMax[0] || probeMax[0] <= oMin[0]) continue; - if(probeMin[1] >= oMax[1] || probeMax[1] <= oMin[1]) continue; - if(probeMin[2] >= oMax[2] || probeMax[2] <= oMin[2]) continue; - - const float_t dx = ent->body.position[0] - entity->body.position[0]; - const float_t dy = ent->body.position[1] - entity->body.position[1]; - const float_t distSq = dx * dx + dy * dy; - - if(best != NULL && distSq >= bestDistSq) continue; - best = ent; - bestDistSq = distSq; - } while(++ent, ent < &ENTITIES[ENTITY_COUNT]); - - return best; -} - -entity_t * entityGetByGlobalId(const entityglobalid_t globalId) { - entity_t *ent = ENTITIES; - do { - if(ent->type == ENTITY_TYPE_NULL) continue; - if(ent->globalId != globalId) continue; - return ent; - } while(++ent, ent < &ENTITIES[ENTITY_COUNT]); - - return NULL; -} - -uint8_t entityGetAvailable() { - entity_t *ent = ENTITIES; - do { - if(ent->type == ENTITY_TYPE_NULL) return ent - ENTITIES; - } while(++ent, ent < &ENTITIES[ENTITY_COUNT]); - - return 0xFF; -} - -void entityPositionSet(entity_t *entity, const worldpos_t pos) { - assertNotNull(entity, "Entity pointer cannot be NULL"); - - const vec3 floatPos = { - (float_t)pos.x, (float_t)pos.y, (float_t)pos.z - }; - const vec3 extents = ENTITY_PHYSICS_EXTENTS_DEFAULT; - physicsBodyInit(&entity->body, floatPos, extents); - entity->animation = ENTITY_ANIM_IDLE; - entitySyncFromPhysics(entity); -} - -void entitySetChunk(entity_t *entity, const uint8_t chunkIndex) { - assertNotNull(entity, "Entity pointer cannot be NULL"); - - if(entity->chunkIndex != 0xFF) { - chunk_t *old = mapGetChunk(entity->chunkIndex); - if(old != NULL) { - for(uint8_t i = 0; i < CHUNK_ENTITY_COUNT_MAX; i++) { - if(old->entities[i] != entity->id) continue; - old->entities[i] = 0xFF; - break; - } - } - } - - entity->chunkIndex = chunkIndex; - - if(chunkIndex != 0xFF) { - chunk_t *next = mapGetChunk(chunkIndex); - if(next != NULL) { - bool_t inserted = false; - for(uint8_t i = 0; i < CHUNK_ENTITY_COUNT_MAX; i++) { - if(next->entities[i] != 0xFF) continue; - next->entities[i] = entity->id; - inserted = true; - break; - } - assertTrue(inserted, "Chunk entity slot overflow"); - } - } -} - -void entityUpdateChunk(entity_t *entity) { - assertNotNull(entity, "Entity pointer cannot be NULL"); - - chunkpos_t cp; - worldPosToChunkPos(&entity->position, &cp); - chunkindex_t ci = mapGetChunkIndexAt(cp); - if(ci == -1 || ci == entity->chunkIndex) return; - entitySetChunk(entity, (uint8_t)ci); -} - -void entitySyncFromPhysics(entity_t *entity) { - assertNotNull(entity, "Entity pointer cannot be NULL"); - - entity->position = (worldpos_t){ - (worldunit_t)floorf(entity->body.position[0]), - (worldunit_t)floorf(entity->body.position[1]), - (worldunit_t)floorf(entity->body.position[2]) - }; - - glm_vec3_copy(entity->body.position, entity->renderPosition); - entity->renderPosition[2] *= WORLD_LAYER_HEIGHT; - - entityUpdateChunk(entity); - mapAreaCheckEntity(entity); -} diff --git a/src/dusk/rpg/entity/entity.h b/src/dusk/rpg/entity/entity.h deleted file mode 100644 index af24adde..00000000 --- a/src/dusk/rpg/entity/entity.h +++ /dev/null @@ -1,213 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "entitydir.h" -#include "interact/entityinteract.h" -#include "entitytype.h" -#include "npc/npc.h" -#include "rpg/physics/physicsbody.h" -#include "rpg/physics/physicsworld.h" - -typedef struct map_s map_t; - -typedef uint16_t entityglobalid_t; - -#define ENTITY_GLOBAL_ID_NULL 0 -#define ENTITY_GLOBAL_ID_START 1 -#define ENTITY_GLOBAL_ID_PLAYER 1 - -// Default collision box for every entity's physics body - matches the old -// system's exact one-tile footprint. -#define ENTITY_PHYSICS_EXTENTS_DEFAULT { 1.0f, 1.0f, 1.0f } - -// Movement speeds, in grid units per second - chosen to match the feel of -// the old fixed-duration one-tile-per-step system (12 ticks/tile walking, -// 6 ticks/tile running, at DUSK_TIME_STEP = 16ms). -#define ENTITY_MOVE_WALK_SPEED 5.2083f -#define ENTITY_MOVE_RUN_SPEED 10.4167f - -// Movement vectors below this magnitude are treated as no movement. -#define ENTITY_MOVE_DEADZONE 0.1f - -// How far in front of an entity entityGetFacing probes for a target. -#define ENTITY_INTERACT_RANGE 1.0f - -typedef enum { - ENTITY_ANIM_IDLE, - ENTITY_ANIM_WALK, - ENTITY_ANIM_RUN, - ENTITY_ANIM_COUNT -} entityanim_t; - -typedef struct entity_s { - uint8_t id; - entityglobalid_t globalId; - entitytype_t type; - entitytypedata_t data; - - // Movement - entitydir_t direction; - physicsbody_t body; - - // Derived each frame from body.position (floored) - kept for systems - // that still assume an integer grid position (chunk membership, map - // area triggers, entity-at-position queries). - worldpos_t position; - - // Derived each frame from body.position - mirrors the physics position - // into render/world-float space (z scaled by WORLD_LAYER_HEIGHT). - vec3 renderPosition; - - entityanim_t animation; - - entityinteract_t interact; - - uint8_t chunkIndex; -} entity_t; - -extern entity_t ENTITIES[ENTITY_COUNT]; - -// Shared physics world every entity's body steps against. -extern physicsworld_t ENTITY_PHYSICS_WORLD; - -/** - * Initializes an entity structure. - * - * @param entity Pointer to the entity structure to initialize. - * @param type The type of the entity. - */ -void entityInit(entity_t *entity, const entitytype_t type); - -/** - * Updates an entity. - * - * @param entity Pointer to the entity structure to update. - */ -void entityUpdate(entity_t *entity); - -/** - * Returns true if the entity is allowed to be unloaded. By default this is - * true for entities whose global ID falls within the randomly assigned - * range below ENTITY_GLOBAL_ID_START. - * - * @param entity Pointer to the entity to check. - * @returns True if the entity can be unloaded. - */ -bool_t entityCanUnload(entity_t *entity); - -/** - * Moves an entity continuously in a direction, at walking or running - * speed. Sets the entity's facing to the nearest cardinal direction that - * matches the movement vector. Must be called every tick the entity - * should keep moving - unlike the old tile-stepping system this does not - * complete a move on its own; call entityStop to stop. - * - * @param entity Pointer to the entity to move. - * @param direction Movement vector, magnitude 0-1 (values longer than 1 - * are clamped to 1, so diagonals aren't faster than cardinals). - * @param running Whether to move at running speed instead of walking. - */ -void entityMove(entity_t *entity, const vec2 direction, const bool_t running); - -/** - * Stops an entity's horizontal movement (equivalent to - * entityMove(entity, {0, 0}, false)). - * - * @param entity Pointer to the entity to stop. - */ -void entityStop(entity_t *entity); - -/** - * Turn an entity to face a new direction, instantly, without moving it. - * - * @param entity Pointer to the entity to turn. - * @param direction The direction to face. - */ -void entityTurn(entity_t *entity, const entitydir_t direction); - -/** - * Makes an entity walk continuously in a cardinal direction, at walking - * speed. Convenience wrapper over entityMove for callers that only think - * in terms of the 4 cardinal directions. Must be called every tick the - * entity should keep moving. - * - * @param entity Pointer to the entity to make walk. - * @param direction The direction to walk in. - */ -void entityWalk(entity_t *entity, const entitydir_t direction); - -/** - * Makes an entity walk continuously in a cardinal direction, at running - * speed. See entityWalk. - * - * @param entity Pointer to the entity to make run. - * @param direction The direction to run in. - */ -void entityRun(entity_t *entity, const entitydir_t direction); - -/** - * Finds the closest other entity whose bounds overlap a probe box - * projected out from the given entity's own bounds, along its current - * facing direction. Used for interaction targeting - continuous-position - * aware, unlike an exact tile match. - * - * @param entity Pointer to the entity to probe from. - * @param range Distance, in grid units, to project the probe box. - * @return Pointer to the closest overlapping entity, or NULL if none. - */ -entity_t *entityGetFacing(entity_t *entity, const float_t range); - -/** - * Gets the entity with the given global ID, if one is currently loaded. - * - * @param globalId The global ID to search for. - * @return Pointer to the matching entity, or NULL if none is loaded. - */ -entity_t *entityGetByGlobalId(const entityglobalid_t globalId); - -/** - * Gets an available entity index. - * - * @return The index of an available entity, or 0xFF if none are available. - */ -uint8_t entityGetAvailable(); - -/** - * Assigns an entity to a chunk, removing it from its current chunk first. - * Pass 0xFF as chunkIndex to detach the entity from any chunk. - * - * @param entity Pointer to the entity. - * @param chunkIndex Index of the chunk to assign to, or 0xFF for none. - */ -void entitySetChunk(entity_t *entity, const uint8_t chunkIndex); - -/** - * Resolves the chunk that an entity's current position falls into and - * assigns the entity to it via entitySetChunk. Leaves the entity's chunk - * unchanged if its position doesn't fall within any loaded chunk. - * - * @param entity Pointer to the entity to update. - */ -void entityUpdateChunk(entity_t *entity); - -/** - * Instantly moves an entity to a world position, resetting movement state. - * - * @param entity Pointer to the entity to move. - * @param pos The world position to place the entity at. - */ -void entityPositionSet(entity_t *entity, const worldpos_t pos); - -/** - * Derives position and renderPosition from the entity's physics body, and - * refreshes chunk membership and map area triggers to match. Called once - * per entity per frame, after the physics step. - * - * @param entity Pointer to the entity to sync. - */ -void entitySyncFromPhysics(entity_t *entity); \ No newline at end of file diff --git a/src/dusk/rpg/entity/entitydir.c b/src/dusk/rpg/entity/entitydir.c deleted file mode 100644 index da1de144..00000000 --- a/src/dusk/rpg/entity/entitydir.c +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "entitydir.h" -#include "assert/assert.h" - -entitydir_t entityDirGetOpposite(const entitydir_t dir) { - switch(dir) { - case ENTITY_DIR_NORTH: return ENTITY_DIR_SOUTH; - case ENTITY_DIR_SOUTH: return ENTITY_DIR_NORTH; - case ENTITY_DIR_EAST: return ENTITY_DIR_WEST; - case ENTITY_DIR_WEST: return ENTITY_DIR_EAST; - default: return dir; - } -} - -void entityDirGetRelative( - const entitydir_t from, - worldunits_t *outX, - worldunits_t *outY -) { - assertValidEntityDir(from, "Invalid direction provided"); - assertNotNull(outX, "Output X pointer cannot be NULL"); - assertNotNull(outY, "Output Y pointer cannot be NULL"); - - switch(from) { - case ENTITY_DIR_NORTH: - *outX = 0; - *outY = 1; - break; - - case ENTITY_DIR_EAST: - *outX = 1; - *outY = 0; - break; - - case ENTITY_DIR_SOUTH: - *outX = 0; - *outY = -1; - break; - - case ENTITY_DIR_WEST: - *outX = -1; - *outY = 0; - break; - } -} - -void entityDirToVec2(const entitydir_t dir, vec2 out) { - assertValidEntityDir(dir, "Invalid direction provided"); - assertNotNull(out, "Output vector cannot be NULL"); - - worldunits_t relX, relY; - entityDirGetRelative(dir, &relX, &relY); - out[0] = (float_t)relX; - out[1] = (float_t)relY; -} - -entitydir_t entityDirFromVec2(const vec2 direction) { - assertNotNull(direction, "Direction vector cannot be NULL"); - assertTrue( - direction[0] != 0.0f || direction[1] != 0.0f, - "Direction vector cannot be zero" - ); - - if(fabsf(direction[0]) > fabsf(direction[1])) { - return direction[0] > 0.0f ? ENTITY_DIR_EAST : ENTITY_DIR_WEST; - } - return direction[1] > 0.0f ? ENTITY_DIR_NORTH : ENTITY_DIR_SOUTH; -} \ No newline at end of file diff --git a/src/dusk/rpg/entity/entitydir.h b/src/dusk/rpg/entity/entitydir.h deleted file mode 100644 index 19d2fda3..00000000 --- a/src/dusk/rpg/entity/entitydir.h +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "rpg/overworld/worldpos.h" - -typedef enum { - ENTITY_DIR_NORTH, - ENTITY_DIR_EAST, - ENTITY_DIR_SOUTH, - ENTITY_DIR_WEST, - ENTITY_DIR_UP = ENTITY_DIR_NORTH, - ENTITY_DIR_DOWN = ENTITY_DIR_SOUTH, - ENTITY_DIR_LEFT = ENTITY_DIR_WEST, - ENTITY_DIR_RIGHT = ENTITY_DIR_EAST, -} entitydir_t; - -/** - * Gets the opposite direction of a given direction. - * - * @param dir The direction to get the opposite of. - * @return entitydir_t The opposite direction. - */ -entitydir_t entityDirGetOpposite(const entitydir_t dir); - -/** - * Asserts a given direction is valid. - * - * @param dir The direction to validate. - * @param msg The message to display if the assertion fails. - */ -#define assertValidEntityDir(dir, msg) \ - assertTrue( \ - (dir) == ENTITY_DIR_NORTH || \ - (dir) == ENTITY_DIR_EAST || \ - (dir) == ENTITY_DIR_SOUTH || \ - (dir) == ENTITY_DIR_WEST, \ - msg \ - ) - -/** - * Gets the relative x and y offsets for a given direction. - * - * @param dir The direction to get offsets for. - * @param relX Pointer to store the relative x offset. - * @param relY Pointer to store the relative y offset. - */ -void entityDirGetRelative( - const entitydir_t dir, worldunits_t *relX, worldunits_t *relY -); - -/** - * Converts a cardinal direction to a unit 2D vector, using the same axis - * convention as entityDirGetRelative (north = +y, east = +x). - * - * @param dir The direction to convert. - * @param out Output unit vector for the direction. - */ -void entityDirToVec2(const entitydir_t dir, vec2 out); - -/** - * Quantizes a 2D direction vector to the nearest cardinal direction, by - * comparing the magnitude of its x and y components. - * - * @param direction The direction vector to quantize. Must not be a zero - * vector. - * @return The nearest cardinal direction. - */ -entitydir_t entityDirFromVec2(const vec2 direction); \ No newline at end of file diff --git a/src/dusk/rpg/entity/entitypathstep.c b/src/dusk/rpg/entity/entitypathstep.c deleted file mode 100644 index fbbd69be..00000000 --- a/src/dusk/rpg/entity/entitypathstep.c +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "entitypathstep.h" -#include "entitydir.h" -#include "assert/assert.h" - -// Distance, in grid units, within which the entity is considered to have -// arrived at its path target. -#define ENTITY_PATH_ARRIVE_EPSILON 0.05f - -bool_t entityPathStep( - entity_t *entity, - const worldpos_t target, - bool_t walkAround -) { - assertNotNull(entity, "Entity must not be NULL"); - assertTrue( - entity->type != ENTITY_TYPE_NULL, - "Cannot path step a NULL entity type" - ); - assertTrue( - entity >= ENTITIES && entity < ENTITIES + ENTITY_COUNT, - "Entity pointer is out of bounds" - ); - - const float_t dx = (float_t)target.x - entity->body.position[0]; - const float_t dy = (float_t)target.y - entity->body.position[1]; - - if( - fabsf(dx) <= ENTITY_PATH_ARRIVE_EPSILON && - fabsf(dy) <= ENTITY_PATH_ARRIVE_EPSILON - ) { - entityStop(entity); - return true; - } - - entitydir_t dir; - bool_t horizontal; - if(fabsf(dx) > fabsf(dy)) { - dir = dx > 0.0f ? ENTITY_DIR_EAST : ENTITY_DIR_WEST; - horizontal = true; - } else { - dir = dy > 0.0f ? ENTITY_DIR_NORTH : ENTITY_DIR_SOUTH; - horizontal = false; - } - - // Was the entity trying to walk on this axis last tick, but ended up not - // moving (velocity clamped by a collision)? If so, treat it as blocked. - if(walkAround) { - const uint8_t axis = horizontal ? 0 : 1; - const bool_t blockedLastTick = - entity->animation != ENTITY_ANIM_IDLE && - fabsf(entity->body.velocity[axis]) <= ENTITY_MOVE_DEADZONE; - - if(blockedLastTick) { - const entitydir_t alt = horizontal - ? (dy >= 0.0f ? ENTITY_DIR_NORTH : ENTITY_DIR_SOUTH) - : (dx >= 0.0f ? ENTITY_DIR_EAST : ENTITY_DIR_WEST); - entityWalk(entity, alt); - return false; - } - } - - entityWalk(entity, dir); - return false; -} diff --git a/src/dusk/rpg/entity/entitypathstep.h b/src/dusk/rpg/entity/entitypathstep.h deleted file mode 100644 index ca8041b9..00000000 --- a/src/dusk/rpg/entity/entitypathstep.h +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "entity.h" - -/** - * Continuously walks the entity toward target's X/Y position (Z is left to - * gravity/ground collision, not deliberate path-stepping). Prefers closing - * whichever of X/Y currently differs most. Must be called every tick until - * it returns true. When walkAround is true and the last tick's movement on - * the current axis was blocked (its velocity on that axis reads as zero - * despite currently walking), tries a perpendicular direction instead. - * - * @param entity Pointer to the entity to move. - * @param target The world position to move toward (Z is ignored). - * @param walkAround Whether to try a perpendicular direction when blocked. - * @returns true once the entity is within arrival distance of target. - */ -bool_t entityPathStep( - entity_t *entity, - const worldpos_t target, - bool_t walkAround -); diff --git a/src/dusk/rpg/entity/entitytype.h b/src/dusk/rpg/entity/entitytype.h deleted file mode 100644 index 6b7d2769..00000000 --- a/src/dusk/rpg/entity/entitytype.h +++ /dev/null @@ -1,62 +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/player.h" -#include "npc/npc.h" -#include "item/entityitem.h" - -typedef enum { - ENTITY_TYPE_NULL, - - ENTITY_TYPE_PLAYER, - ENTITY_TYPE_NPC, - ENTITY_TYPE_ITEM, - - ENTITY_TYPE_COUNT -} entitytype_enum_t; - -typedef uint8_t entitytype_t; - -typedef union { - player_t player; - npc_t npc; - entityitem_t item; -} entitytypedata_t; - -typedef struct { - /** - * Initialization callback for the entity type. - * @param entity Pointer to the entity to initialize. - */ - void (*init)(entity_t *entity); - - /** - * Movement callback for the entity type. Gated by cutscene input. - * @param entity Pointer to the entity to move. - */ - void (*movement)(entity_t *entity); -} entitycallback_t; - -static const entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = { - [ENTITY_TYPE_NULL] = { NULL }, - - [ENTITY_TYPE_PLAYER] = { - .init = playerInit, - .movement = playerInput - }, - - [ENTITY_TYPE_NPC] = { - .init = npcInit, - .movement = npcMovement, - }, - - [ENTITY_TYPE_ITEM] = { - .init = entityItemInit, - .movement = entityItemMovement, - } -}; \ No newline at end of file diff --git a/src/dusk/rpg/entity/global/CMakeLists.txt b/src/dusk/rpg/entity/global/CMakeLists.txt deleted file mode 100644 index 9b35b01d..00000000 --- a/src/dusk/rpg/entity/global/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC -) \ No newline at end of file diff --git a/src/dusk/rpg/entity/global/entityglobal.h b/src/dusk/rpg/entity/global/entityglobal.h deleted file mode 100644 index 10d51d60..00000000 --- a/src/dusk/rpg/entity/global/entityglobal.h +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "entityglobaldefs.h" -#include "entitygloballist.h" - -#define ENTITY_GLOBAL_LIST_COUNT ( \ - sizeof(ENTITY_GLOBAL_LIST) / \ - sizeof(ENTITY_GLOBAL_LIST[0]) \ -) - -//EOF diff --git a/src/dusk/rpg/entity/global/entityglobaldefs.h b/src/dusk/rpg/entity/global/entityglobaldefs.h deleted file mode 100644 index 73913e45..00000000 --- a/src/dusk/rpg/entity/global/entityglobaldefs.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" -#include "rpg/overworld/worldpos.h" -#include "rpg/entity/entity.h" - -typedef struct entity_s entity_t; - -typedef struct { - entity_t *entity; - worldpos_t position; -} entityglobalcreate_t; - -/** - * Callback invoked to initialize a global entity. - * - * @param create Pointer to the entity/position being initialized. - * @returns An error code. - */ -typedef void (*entityglobalinitcallback_t)( - entityglobalcreate_t *create -); - -typedef struct { - entitytype_t type; - entityglobalinitcallback_t callback; -} entityglobaldef_t; - -#define ENTITY_GLOBAL(id, entType, callbackFn) \ - [id] = { .type = entType, .callback = callbackFn } - -#define ENTITY_GLOBAL_CALLBACK(id) \ - static void ENTTIYT_GLOBAL_CALLBACK_##id(entityglobalcreate_t *create) - -#define ENTITY_GLOBAL_REF(id) \ - ENTTIYT_GLOBAL_CALLBACK_##id - -//EOF \ No newline at end of file diff --git a/src/dusk/rpg/entity/global/entitygloballist.h b/src/dusk/rpg/entity/global/entitygloballist.h deleted file mode 100644 index 2207763c..00000000 --- a/src/dusk/rpg/entity/global/entitygloballist.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "entityglobaldefs.h" -#include "rpg/cutscene/scene/testcutscene.h" - -ENTITY_GLOBAL_CALLBACK(3) { - create->entity->data.npc.moveType = NPC_MOVE_TYPE_PATH; - npcPathAddNode(&create->entity->data.npc, (worldpos_t){ 4, 4, 0 }); - npcPathAddNode(&create->entity->data.npc, (worldpos_t){ 10, 10, 1 }); - npcPathAddNode(&create->entity->data.npc, (worldpos_t){ 4, 4, 0 }); - npcPathAddNode(&create->entity->data.npc, (worldpos_t){ 10, 10, 1 }); - - create->entity->interact.type = ENTITY_INTERACT_CUTSCENE; - create->entity->interact.data.cutscene = CUTSCENE_REFERENCE(TEST_TWO); -} - -static const entityglobaldef_t ENTITY_GLOBAL_LIST[] = { - ENTITY_GLOBAL(ENTITY_GLOBAL_ID_NULL, ENTITY_TYPE_NULL, NULL), - ENTITY_GLOBAL(ENTITY_GLOBAL_ID_PLAYER, ENTITY_TYPE_PLAYER, NULL), - - ENTITY_GLOBAL(3, ENTITY_TYPE_NPC, ENTITY_GLOBAL_REF(3)), -}; - -//EOF diff --git a/src/dusk/rpg/entity/interact/CMakeLists.txt b/src/dusk/rpg/entity/interact/CMakeLists.txt deleted file mode 100644 index f7e07e35..00000000 --- a/src/dusk/rpg/entity/interact/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - entityinteract.c -) diff --git a/src/dusk/rpg/entity/interact/entityinteract.c b/src/dusk/rpg/entity/interact/entityinteract.c deleted file mode 100644 index d7a8b224..00000000 --- a/src/dusk/rpg/entity/interact/entityinteract.c +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/entity/entity.h" -#include "assert/assert.h" -#include "rpg/cutscene/cutscenesystem.h" -#include "ui/rpg/textbox/uitextboxmain.h" - -void entityInteractWith(entity_t *player, entity_t *target) { - assertNotNull(player, "Player entity pointer cannot be NULL"); - assertNotNull(target, "Target entity pointer cannot be NULL"); - - switch(target->interact.type) { - case ENTITY_INTERACT_CUTSCENE: - assertNotNull( - target->interact.data.cutscene, - "Interact cutscene pointer cannot be NULL" - ); - cutsceneSystemStartCutsceneWith( - target->interact.data.cutscene, - player, - target - ); - break; - - case ENTITY_INTERACT_PRINT: - uiTextboxMainSetText(target->interact.data.message); - - // If NPC turn to face player. - if(target->type == ENTITY_TYPE_NPC) { - target->data.npc.interactState = NPC_INTERACT_STATE_CONVERSING; - target->animation = ENTITY_ANIM_IDLE; - entityTurn(target, entityDirGetOpposite(player->direction)); - } - - // entityTurn(player, player->direction); // Redundant (for now) - break; - - case ENTITY_INTERACT_CALLBACK: - assertNotNull( - target->interact.data.callback, - "Interact callback pointer cannot be NULL" - ); - target->interact.data.callback(player, target); - break; - - case ENTITY_INTERACT_NULL: - break; - - default: - assertUnreachable("Unknown entity interact type"); - break; - } -} \ No newline at end of file diff --git a/src/dusk/rpg/entity/interact/entityinteract.h b/src/dusk/rpg/entity/interact/entityinteract.h deleted file mode 100644 index 4f1f39d4..00000000 --- a/src/dusk/rpg/entity/interact/entityinteract.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" - -typedef struct entity_s entity_t; -typedef struct cutscene_s cutscene_t; - -/** - * Describes the type of interaction an entity supports. - */ -typedef enum { - ENTITY_INTERACT_NULL = 0, - - ENTITY_INTERACT_CUTSCENE, - ENTITY_INTERACT_PRINT, - ENTITY_INTERACT_CALLBACK, - - ENTITY_INTERACT_COUNT -} entityinteracttype_t; - -/** - * Per-type data for an entity's interact component. - */ -typedef union { - const cutscene_t *cutscene; - const char_t *message; - void (*callback)(entity_t *player, entity_t *target); -} entityinteractdata_t; - -/** - * Interact component attached to any entity that can be interacted with. - * Set type to ENTITY_INTERACT_NULL to mark the entity as non-interactable. - */ -typedef struct { - entityinteracttype_t type; - entityinteractdata_t data; -} entityinteract_t; - -/** - * Attempts to interact with the target entity on behalf of the player. - * Dispatches via the interact component; falls back to the entity type - * callback if no component is set. - * - * @param player Pointer to the player entity. - * @param target Pointer to the entity to interact with. - */ -void entityInteractWith(entity_t *player, entity_t *target); diff --git a/src/dusk/rpg/entity/item/CMakeLists.txt b/src/dusk/rpg/entity/item/CMakeLists.txt deleted file mode 100644 index 6834d19b..00000000 --- a/src/dusk/rpg/entity/item/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - entityitem.c -) diff --git a/src/dusk/rpg/entity/item/entityitem.c b/src/dusk/rpg/entity/item/entityitem.c deleted file mode 100644 index eccfd0f6..00000000 --- a/src/dusk/rpg/entity/item/entityitem.c +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "entityitem.h" -#include "rpg/entity/entity.h" -#include "assert/assert.h" -#include "rpg/item/itemgive.h" -#include "ui/rpg/textbox/uitextboxmain.h" - -void entityItemInit(entity_t *entity) { - assertNotNull(entity, "Entity pointer cannot be NULL"); - entity->interact.type = ENTITY_INTERACT_CALLBACK; - entity->interact.data.callback = entityItemInteract; -} - -void entityItemSet( - entity_t *entity, - const itemid_t item, - const uint8_t quantity -) { - assertNotNull(entity, "Entity pointer cannot be NULL"); - entity->data.item.item = item; - entity->data.item.quantity = quantity; -} - -void entityItemInteract(entity_t *player, entity_t *target) { - assertNotNull(player, "Player entity pointer cannot be NULL"); - assertNotNull(target, "Target entity pointer cannot be NULL"); - - itemGive(target->data.item.item, target->data.item.quantity); - target->data.item.collected = true; -} - -void entityItemMovement(entity_t *entity) { - assertNotNull(entity, "Entity pointer cannot be NULL"); - if(!entity->data.item.collected) return; - if(uiTextboxMainIsActive()) return; - - entity->type = ENTITY_TYPE_NULL; -} diff --git a/src/dusk/rpg/entity/item/entityitem.h b/src/dusk/rpg/entity/item/entityitem.h deleted file mode 100644 index 1485f1fa..00000000 --- a/src/dusk/rpg/entity/item/entityitem.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "rpg/item/item.h" - -typedef struct entity_s entity_t; - -typedef struct { - itemid_t item; - uint8_t quantity; - bool_t collected; -} entityitem_t; - -/** - * Initializes an item entity. Wires up the interact component so that - * interacting with the entity gives its item and removes it. - * - * @param entity Pointer to the entity structure to initialize. - */ -void entityItemInit(entity_t *entity); - -/** - * Sets the item and quantity represented by an item entity. - * - * @param entity Pointer to the entity structure. - * @param item The item ID to give when picked up. - * @param quantity The quantity to give when picked up. - */ -void entityItemSet( - entity_t *entity, - const itemid_t item, - const uint8_t quantity -); - -/** - * Interact callback for item entities. Gives the entity's item to the - * player and shows the pickup message. The entity itself is removed once - * the message has been dismissed, via entityItemMovement. - * - * @param player Pointer to the player entity. - * @param target Pointer to the item entity being picked up. - */ -void entityItemInteract(entity_t *player, entity_t *target); - -/** - * Update callback for item entities. Removes the entity once its pickup - * message has been dismissed. No-op if the item hasn't been collected. - * - * @param entity Pointer to the entity structure to update. - */ -void entityItemMovement(entity_t *entity); diff --git a/src/dusk/rpg/entity/npc/CMakeLists.txt b/src/dusk/rpg/entity/npc/CMakeLists.txt deleted file mode 100644 index 922a4e1d..00000000 --- a/src/dusk/rpg/entity/npc/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - npc.c - npcturn.c - npcwalk.c - npcpath.c -) diff --git a/src/dusk/rpg/entity/npc/npc.c b/src/dusk/rpg/entity/npc/npc.c deleted file mode 100644 index 05175828..00000000 --- a/src/dusk/rpg/entity/npc/npc.c +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg/entity/entity.h" -#include "assert/assert.h" -#include "rpg/cutscene/cutscenesystem.h" - -#include "rpg/cutscene/scene/testcutscene.h" - -const npcmovecallback_t NPC_MOVE_CALLBACKS[NPC_MOVE_TYPE_COUNT] = { - [NPC_MOVE_TYPE_NULL] = { 0 }, - - [NPC_MOVE_TYPE_RANDOM_TURN] = { - npcRandomTurnInit, - npcRandomTurnMovement - }, - - [NPC_MOVE_TYPE_RANDOM_WALK] = { - npcRandomWalkInit, - npcRandomWalkMovement - }, - - [NPC_MOVE_TYPE_RANDOM_TURN_AND_WALK] = { - npcRandomTurnAndWalkInit, - npcRandomTurnAndWalkMovement - }, - - [NPC_MOVE_TYPE_PATH] = { - npcPathInit, - npcPathMovement - }, -}; - -void npcInit(entity_t *entity) { - assertNotNull(entity, "Entity pointer cannot be NULL"); -} - -void npcSetMoveType(entity_t *entity, const npcmovetype_t moveType) { - assertNotNull(entity, "Entity pointer cannot be NULL"); - npc_t *npc = &entity->data.npc; - npc->moveType = moveType; - if(NPC_MOVE_CALLBACKS[moveType].init != NULL) { - NPC_MOVE_CALLBACKS[moveType].init(npc); - } -} - -void npcMovement(entity_t *entity) { - assertNotNull(entity, "Entity pointer cannot be NULL"); - if(CUTSCENE_SYSTEM.pause & CUTSCENE_PAUSE_NPC) return; - - npc_t *npc = &entity->data.npc; - if(npc->interactState != NPC_INTERACT_STATE_NONE) return; - - const npcmovecallback_t *cb = &NPC_MOVE_CALLBACKS[npc->moveType]; - if(cb->movement != NULL) cb->movement(entity); -} \ No newline at end of file diff --git a/src/dusk/rpg/entity/npc/npc.h b/src/dusk/rpg/entity/npc/npc.h deleted file mode 100644 index be16ee9b..00000000 --- a/src/dusk/rpg/entity/npc/npc.h +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" -#include "npcturn.h" -#include "npcwalk.h" -#include "npcpath.h" - -typedef struct entity_s entity_t; - -typedef enum { - NPC_INTERACT_STATE_NONE, - NPC_INTERACT_STATE_CONVERSING, - NPC_INTERACT_STATE_COUNT -} npcinteractstate_t; - -typedef enum { - NPC_MOVE_TYPE_NULL, - NPC_MOVE_TYPE_RANDOM_TURN, - NPC_MOVE_TYPE_RANDOM_WALK, - NPC_MOVE_TYPE_RANDOM_TURN_AND_WALK, - NPC_MOVE_TYPE_PATH, - NPC_MOVE_TYPE_COUNT -} npcmovetype_t; - -typedef union { - npcrandomturn_t randomTurn; - npcrandomwalk_t randomWalk; - npcrandomturnandwalk_t randomTurnAndWalk; - npcpath_t path; -} npcmovedata_t; - -typedef struct npc_s { - npcinteractstate_t interactState; - npcmovetype_t moveType; - npcmovedata_t moveData; -} npc_t; - -typedef struct { - /** Called once when the move type is set. */ - void (*init)(npc_t *npc); - /** Called each movement tick. */ - void (*movement)(entity_t *entity); -} npcmovecallback_t; - -extern const npcmovecallback_t NPC_MOVE_CALLBACKS[NPC_MOVE_TYPE_COUNT]; - -/** - * Initializes an NPC entity. - * - * @param entity Pointer to the entity structure to initialize. - */ -void npcInit(entity_t *entity); - -/** - * Sets the movement type for an NPC entity. - * - * @param entity Pointer to the entity structure. - * @param moveType The movement type to set. - */ -void npcSetMoveType(entity_t *entity, const npcmovetype_t moveType); - -/** - * Movement callback for an NPC entity. Gated by cutscene input. - * - * @param entity Pointer to the entity structure to update. - */ -void npcMovement(entity_t *entity); - -/** - * Free movement callback for an NPC entity. Runs always-run move types - * regardless of cutscene state. - * - * @param entity Pointer to the entity structure to update. - */ -void npcFreeMovement(entity_t *entity); \ No newline at end of file diff --git a/src/dusk/rpg/entity/npc/npcpath.c b/src/dusk/rpg/entity/npc/npcpath.c deleted file mode 100644 index e55740db..00000000 --- a/src/dusk/rpg/entity/npc/npcpath.c +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "npc.h" -#include "rpg/entity/entity.h" -#include "rpg/entity/entitypathstep.h" -#include "rpg/overworld/worldpos.h" -#include "assert/assert.h" - -void npcPathInit(npc_t *npc) { - npcpath_t *path = &npc->moveData.path; - path->count = 0; - path->index = 0; -} - -void npcPathAddNode(npc_t *npc, const worldpos_t pos) { - assertNotNull(npc, "NPC must not be NULL"); - assertTrue( - npc->moveData.path.count < NPC_PATH_COUNT_MAX, - "NPC path is full" - ); - npc->moveData.path.positions[npc->moveData.path.count++] = pos; -} - -void npcPathMovement(entity_t *entity) { - npcpath_t *path = &entity->data.npc.moveData.path; - if(path->count == 0) return; - - const worldpos_t target = path->positions[path->index]; - if(entityPathStep(entity, target, false)) { - path->index = (path->index + 1) % path->count; - } -} diff --git a/src/dusk/rpg/entity/npc/npcpath.h b/src/dusk/rpg/entity/npc/npcpath.h deleted file mode 100644 index d6469168..00000000 --- a/src/dusk/rpg/entity/npc/npcpath.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" -#include "rpg/overworld/worldpos.h" - -typedef struct npc_s npc_t; -typedef struct entity_s entity_t; - -/** Maximum number of waypoints in an NPC path. */ -#define NPC_PATH_COUNT_MAX 8 - -typedef struct { - worldpos_t positions[NPC_PATH_COUNT_MAX]; - uint8_t count; - uint8_t index; -} npcpath_t; - -/** - * Initializes the path movement data for an NPC. - * - * @param npc Pointer to the NPC to initialize. - */ -void npcPathInit(npc_t *npc); - -/** - * Appends a waypoint to the NPC's path. Has no effect if the path is full. - * - * @param npc Pointer to the NPC. - * @param pos The world position to append. - */ -void npcPathAddNode(npc_t *npc, const worldpos_t pos); - -/** - * Movement tick for an NPC following a path. - * - * @param entity Pointer to the entity to update. - */ -void npcPathMovement(entity_t *entity); diff --git a/src/dusk/rpg/entity/npc/npcturn.c b/src/dusk/rpg/entity/npc/npcturn.c deleted file mode 100644 index cdbaccc9..00000000 --- a/src/dusk/rpg/entity/npc/npcturn.c +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "npc.h" -#include "rpg/entity/entity.h" -#include "util/random.h" -#include "time/time.h" -#include - -void npcRandomTurnInit(npc_t *npc) { - npcrandomturn_t *turn = &npc->moveData.randomTurn; - turn->frequencyMin = NPC_RANDOM_TURN_FREQUENCY_MIN_DEFAULT; - turn->frequencyMax = NPC_RANDOM_TURN_FREQUENCY_MAX_DEFAULT; - turn->timer = randomFloat(turn->frequencyMin, turn->frequencyMax); -} - -void npcRandomTurnMovement(entity_t *entity) { - npcrandomturn_t *turn = &entity->data.npc.moveData.randomTurn; - turn->timer -= TIME.delta; - if(turn->timer > 0.0f) return; - turn->timer = randomFloat(turn->frequencyMin, turn->frequencyMax); - entityTurn(entity, (entitydir_t)(rand() % 4)); -} diff --git a/src/dusk/rpg/entity/npc/npcturn.h b/src/dusk/rpg/entity/npc/npcturn.h deleted file mode 100644 index 3568bbdb..00000000 --- a/src/dusk/rpg/entity/npc/npcturn.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" - -typedef struct npc_s npc_t; -typedef struct entity_s entity_t; - -/** Default min/max seconds between NPC random-turn ticks. */ -#define NPC_RANDOM_TURN_FREQUENCY_MIN_DEFAULT 2.0f -#define NPC_RANDOM_TURN_FREQUENCY_MAX_DEFAULT 4.0f - -typedef struct { - float_t frequencyMin; - float_t frequencyMax; - float_t timer; -} npcrandomturn_t; - -/** - * Initializes the random-turn movement data for an NPC. - * - * @param npc Pointer to the NPC to initialize. - */ -void npcRandomTurnInit(npc_t *npc); - -/** - * Movement tick for an NPC using random-turn movement. - * - * @param entity Pointer to the entity to update. - */ -void npcRandomTurnMovement(entity_t *entity); diff --git a/src/dusk/rpg/entity/npc/npcwalk.c b/src/dusk/rpg/entity/npc/npcwalk.c deleted file mode 100644 index 7d70feb7..00000000 --- a/src/dusk/rpg/entity/npc/npcwalk.c +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "npc.h" -#include "rpg/entity/entity.h" -#include "util/random.h" -#include "time/time.h" -#include - -void npcRandomWalkInit(npc_t *npc) { - npcrandomwalk_t *walk = &npc->moveData.randomWalk; - walk->frequencyMin = NPC_RANDOM_WALK_FREQUENCY_MIN_DEFAULT; - walk->frequencyMax = NPC_RANDOM_WALK_FREQUENCY_MAX_DEFAULT; - walk->timer = randomFloat(walk->frequencyMin, walk->frequencyMax); - walk->moveDuration = 0.0f; -} - -void npcRandomWalkMovement(entity_t *entity) { - npcrandomwalk_t *walk = &entity->data.npc.moveData.randomWalk; - - if(walk->moveDuration > 0.0f) { - walk->moveDuration -= TIME.delta; - entityWalk(entity, walk->direction); - if(walk->moveDuration <= 0.0f) entityStop(entity); - return; - } - - walk->timer -= TIME.delta; - if(walk->timer > 0.0f) return; - walk->timer = randomFloat(walk->frequencyMin, walk->frequencyMax); - walk->direction = (entitydir_t)(rand() % 4); - walk->moveDuration = NPC_RANDOM_WALK_MOVE_DURATION_DEFAULT; -} - -void npcRandomTurnAndWalkInit(npc_t *npc) { - npcRandomTurnInit(npc); - npcrandomturnandwalk_t *tw = &npc->moveData.randomTurnAndWalk; - tw->walk.frequencyMin = NPC_RANDOM_WALK_FREQUENCY_MIN_DEFAULT; - tw->walk.frequencyMax = NPC_RANDOM_WALK_FREQUENCY_MAX_DEFAULT; - tw->walk.timer = randomFloat(tw->walk.frequencyMin, tw->walk.frequencyMax); - tw->walk.moveDuration = 0.0f; -} - -void npcRandomTurnAndWalkMovement(entity_t *entity) { - npcrandomturnandwalk_t *tw = &entity->data.npc.moveData.randomTurnAndWalk; - - if(tw->walk.moveDuration > 0.0f) { - tw->walk.moveDuration -= TIME.delta; - entityWalk(entity, tw->walk.direction); - if(tw->walk.moveDuration <= 0.0f) entityStop(entity); - return; - } - - tw->turn.timer -= TIME.delta; - if(tw->turn.timer <= 0.0f) { - tw->turn.timer = randomFloat(tw->turn.frequencyMin, tw->turn.frequencyMax); - entityTurn(entity, (entitydir_t)(rand() % 4)); - } - - tw->walk.timer -= TIME.delta; - if(tw->walk.timer <= 0.0f) { - tw->walk.timer = randomFloat(tw->walk.frequencyMin, tw->walk.frequencyMax); - tw->walk.direction = (entitydir_t)(rand() % 4); - tw->walk.moveDuration = NPC_RANDOM_WALK_MOVE_DURATION_DEFAULT; - } -} - diff --git a/src/dusk/rpg/entity/npc/npcwalk.h b/src/dusk/rpg/entity/npc/npcwalk.h deleted file mode 100644 index 36dbb0f9..00000000 --- a/src/dusk/rpg/entity/npc/npcwalk.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" -#include "npcturn.h" -#include "rpg/entity/entitydir.h" - -/** Default min/max seconds between NPC random-walk ticks. */ -#define NPC_RANDOM_WALK_FREQUENCY_MIN_DEFAULT 2.0f -#define NPC_RANDOM_WALK_FREQUENCY_MAX_DEFAULT 5.0f - -/** How long a random-walk move lasts once triggered, roughly one tile's - * worth of travel at walking speed. */ -#define NPC_RANDOM_WALK_MOVE_DURATION_DEFAULT 0.2f - -typedef struct { - float_t frequencyMin; - float_t frequencyMax; - float_t timer; - - // In-progress move state - while moveDuration > 0, movement keeps - // walking in direction every tick. - float_t moveDuration; - entitydir_t direction; -} npcrandomwalk_t; - -typedef struct { - npcrandomturn_t turn; - npcrandomwalk_t walk; -} npcrandomturnandwalk_t; - -/** - * Initializes the random-walk movement data for an NPC. - * - * @param npc Pointer to the NPC to initialize. - */ -void npcRandomWalkInit(npc_t *npc); - -/** - * Movement tick for an NPC using random-walk movement. - * - * @param entity Pointer to the entity to update. - */ -void npcRandomWalkMovement(entity_t *entity); - -/** - * Initializes the random-turn-and-walk movement data for an NPC. - * - * @param npc Pointer to the NPC to initialize. - */ -void npcRandomTurnAndWalkInit(npc_t *npc); - -/** - * Movement tick for an NPC using random-turn-and-walk movement. - * - * @param entity Pointer to the entity to update. - */ -void npcRandomTurnAndWalkMovement(entity_t *entity); - diff --git a/src/dusk/rpg/entity/player.c b/src/dusk/rpg/entity/player.c deleted file mode 100644 index fff0360c..00000000 --- a/src/dusk/rpg/entity/player.c +++ /dev/null @@ -1,68 +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 "rpg/rpgcamera.h" -#include "util/memory.h" -#include "time/time.h" -#include "ui/focus/uifocus.h" -#include "ui/frame/game/uigamemenu.h" -#include "rpg/cutscene/cutscenesystem.h" - -void playerInit(entity_t *entity) { - assertNotNull(entity, "Entity pointer cannot be NULL"); - entity->globalId = ENTITY_GLOBAL_ID_PLAYER; -} - -bool_t playerCanInteract(entity_t *entity) { - return entity->animation == ENTITY_ANIM_IDLE; -} - -void playerInput(entity_t *entity) { - assertNotNull(entity, "Entity pointer cannot be NULL"); - if(CUTSCENE_SYSTEM.pause & CUTSCENE_PAUSE_PLAYER) { - entityStop(entity); - return; - } - - // Toggle game menu on pause - if(uiGameMenuIsOpen() && inputPressed(INPUT_ACTION_PAUSE)) { - uiGameMenuClose(); - } else if( - inputPressed(INPUT_ACTION_PAUSE) && - entity->animation == ENTITY_ANIM_IDLE - ) { - uiGameMenuOpen(); - } - - // Can player act? - if(UI_FOCUS.count > 0) { - entityStop(entity); - return; - } - - // Analog/free-angle movement vector from the 4 directional actions - - // already normalized so diagonals aren't faster than cardinals, and - // preserves real analog magnitude on platforms that bind an actual - // gamepad stick to these actions. - vec2 moveDir; - inputAngle2D( - INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT, - INPUT_ACTION_DOWN, INPUT_ACTION_UP, - moveDir - ); - - entityMove(entity, moveDir, inputIsDown(INPUT_ACTION_CANCEL)); - - // Interaction - if(inputPressed(INPUT_ACTION_ACCEPT) && playerCanInteract(entity)) { - entity_t *target = entityGetFacing(entity, ENTITY_INTERACT_RANGE); - if(target == NULL) return; - entityInteractWith(entity, target); - } -} \ No newline at end of file diff --git a/src/dusk/rpg/entity/player.h b/src/dusk/rpg/entity/player.h deleted file mode 100644 index 24fc683e..00000000 --- a/src/dusk/rpg/entity/player.h +++ /dev/null @@ -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 "input/input.h" -#include "entitydir.h" - -typedef struct entity_s entity_t; - -typedef struct { - void *nothing; -} player_t; - -/** - * Initializes a player entity. - * - * @param entity Pointer to the entity structure to initialize. - */ -void playerInit(entity_t *entity); - -/** - * Returns true if the player entity is in a state where it can interact. - * - * @param entity Pointer to the player entity to check. - * @returns True if the entity can interact. - */ -bool_t playerCanInteract(entity_t *entity); - -/** - * Handles movement logic for the player entity. - * - * @param entity Pointer to the player entity structure. - */ -void playerInput(entity_t *entity); \ No newline at end of file diff --git a/src/dusk/rpg/item/CMakeLists.txt b/src/dusk/rpg/item/CMakeLists.txt deleted file mode 100644 index cbd4416d..00000000 --- a/src/dusk/rpg/item/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) 2025 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - item.c - inventory.c - backpack.c - itemgive.c -) - -# Item Definitions -dusk_run_python( - dusk_item_json_defs - tools.item - --json ${CMAKE_CURRENT_SOURCE_DIR}/item.json - --output ${DUSK_GENERATED_HEADERS_DIR}/rpg/item/itemdef.h -) -add_dependencies(${DUSK_LIBRARY_TARGET_NAME} dusk_item_json_defs) \ No newline at end of file diff --git a/src/dusk/rpg/item/backpack.c b/src/dusk/rpg/item/backpack.c deleted file mode 100644 index 03bd91ae..00000000 --- a/src/dusk/rpg/item/backpack.c +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "backpack.h" -#include "assert/assert.h" - -backpack_t BACKPACK; - -void backpackInit() { - for(uint8_t i = 0; i < ITEM_TYPE_COUNT; i++) { - inventoryInit( - &BACKPACK.inventories[i], - BACKPACK.storage[i], - ITEM_TYPE_COUNT_MAX - ); - } -} - -inventory_t *backpackGetInventory(const itemtype_t type) { - assertTrue(type > ITEM_TYPE_NULL, "Item type must not be null"); - assertTrue(type < ITEM_TYPE_COUNT, "Item type out of range"); - return &BACKPACK.inventories[type]; -} - -void backpackAdd(const itemid_t item, const uint8_t quantity) { - assertTrue(item > ITEM_ID_NULL, "Item ID must not be null"); - assertTrue(item < ITEM_ID_COUNT, "Item ID out of range"); - inventoryAdd(backpackGetInventory(ITEMS[item].type), item, quantity); -} - -void backpackRemove(const itemid_t item) { - assertTrue(item > ITEM_ID_NULL, "Item ID must not be null"); - assertTrue(item < ITEM_ID_COUNT, "Item ID out of range"); - inventoryRemove(backpackGetInventory(ITEMS[item].type), item); -} - -void backpackSet(const itemid_t item, const uint8_t quantity) { - assertTrue(item > ITEM_ID_NULL, "Item ID must not be null"); - assertTrue(item < ITEM_ID_COUNT, "Item ID out of range"); - inventorySet(backpackGetInventory(ITEMS[item].type), item, quantity); -} - -uint8_t backpackGetCount(const itemid_t item) { - assertTrue(item > ITEM_ID_NULL, "Item ID must not be null"); - assertTrue(item < ITEM_ID_COUNT, "Item ID out of range"); - return inventoryGetCount(backpackGetInventory(ITEMS[item].type), item); -} - -bool_t backpackItemExists(const itemid_t item) { - assertTrue(item > ITEM_ID_NULL, "Item ID must not be null"); - assertTrue(item < ITEM_ID_COUNT, "Item ID out of range"); - return inventoryItemExists(backpackGetInventory(ITEMS[item].type), item); -} - -bool_t backpackIsFull(const itemtype_t type) { - assertTrue(type > ITEM_TYPE_NULL, "Item type must not be null"); - assertTrue(type < ITEM_TYPE_COUNT, "Item type out of range"); - return inventoryIsFull(backpackGetInventory(type)); -} - -bool_t backpackItemFull(const itemid_t item) { - assertTrue(item > ITEM_ID_NULL, "Item ID must not be null"); - assertTrue(item < ITEM_ID_COUNT, "Item ID out of range"); - return inventoryItemFull(backpackGetInventory(ITEMS[item].type), item); -} - -void backpackSort( - const itemtype_t type, - const inventorysort_t sortBy, - const bool_t reverse -) { - assertTrue(type > ITEM_TYPE_NULL, "Item type must not be null"); - assertTrue(type < ITEM_TYPE_COUNT, "Item type out of range"); - inventorySort(backpackGetInventory(type), sortBy, reverse); -} \ No newline at end of file diff --git a/src/dusk/rpg/item/backpack.h b/src/dusk/rpg/item/backpack.h deleted file mode 100644 index 5812befa..00000000 --- a/src/dusk/rpg/item/backpack.h +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "inventory.h" - -typedef struct { - inventorystack_t storage[ITEM_TYPE_COUNT][ITEM_TYPE_COUNT_MAX]; - inventory_t inventories[ITEM_TYPE_COUNT]; -} backpack_t; - -extern backpack_t BACKPACK; - -/** - * Initializes the backpack inventory for the player. - */ -void backpackInit(); - -/** - * Returns the inventory for a given item type. - * - * @param type The item type. - * @returns Pointer to the inventory for that type. - */ -inventory_t *backpackGetInventory(const itemtype_t type); - -/** - * Adds a quantity of an item to the backpack. - * - * @param item The item ID to add. - * @param quantity The quantity to add. - */ -void backpackAdd(const itemid_t item, const uint8_t quantity); - -/** - * Removes an item from the backpack. - * - * @param item The item ID to remove. - */ -void backpackRemove(const itemid_t item); - -/** - * Sets the quantity of an item in the backpack. - * - * @param item The item ID to set. - * @param quantity The quantity to set. - */ -void backpackSet(const itemid_t item, const uint8_t quantity); - -/** - * Gets the quantity of an item in the backpack. - * - * @param item The item ID to check. - * @returns The quantity held. - */ -uint8_t backpackGetCount(const itemid_t item); - -/** - * Checks if an item exists in the backpack (quantity > 0). - * - * @param item The item ID to check. - * @returns true if the item exists. - */ -bool_t backpackItemExists(const itemid_t item); - -/** - * Checks if the inventory for a given type is full. - * - * @param type The item type to check. - * @returns true if the type's inventory is full. - */ -bool_t backpackIsFull(const itemtype_t type); - -/** - * Checks if an item's stack is full in the backpack. - * - * @param item The item ID to check. - * @returns true if the item stack is full. - */ -bool_t backpackItemFull(const itemid_t item); - -/** - * Sorts the inventory for a given item type. - * - * @param type The item type whose inventory to sort. - * @param sortBy The sorting criteria. - * @param reverse Whether to sort in reverse order. - */ -void backpackSort( - const itemtype_t type, - const inventorysort_t sortBy, - const bool_t reverse -); \ No newline at end of file diff --git a/src/dusk/rpg/item/inventory.c b/src/dusk/rpg/item/inventory.c deleted file mode 100644 index b3579ccb..00000000 --- a/src/dusk/rpg/item/inventory.c +++ /dev/null @@ -1,250 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "inventory.h" -#include "util/memory.h" -#include "util/sort.h" -#include "assert/assert.h" - -void inventoryInit( - inventory_t* inventory, - inventorystack_t* storage, - uint8_t storageSize -) { - assertNotNull(inventory, "Inventory pointer is NULL."); - assertNotNull(storage, "Storage pointer is NULL."); - assertTrue(storageSize > 0, "Storage size must be greater than zero."); - - inventory->storage = storage; - inventory->storageSize = storageSize; - - memoryZero(storage, sizeof(inventorystack_t) * storageSize); -} - -bool_t inventoryItemExists(const inventory_t *inventory, const itemid_t item) { - assertNotNull(inventory, "Inventory pointer is NULL."); - assertNotNull(inventory->storage, "Storage pointer is NULL."); - assertTrue(inventory->storageSize > 0, "Storage too small."); - assertTrue(item != ITEM_ID_NULL, "Item ID cannot be ITEM_ID_NULL."); - - inventorystack_t *stack = inventory->storage; - inventorystack_t *end = stack + inventory->storageSize; - do { - if(stack->item == ITEM_ID_NULL) break; - if(stack->item != item) continue; - assertTrue(stack->quantity > 0, "Item has quantity zero."); - return true; - } while(++stack < end); - - return false; -} - -void inventorySet( - inventory_t *inventory, - const itemid_t item, - const uint8_t quantity -) { - assertNotNull(inventory, "Inventory pointer is NULL."); - assertNotNull(inventory->storage, "Storage pointer is NULL."); - assertTrue(inventory->storageSize > 0, "Storage too small."); - assertTrue(item != ITEM_ID_NULL, "Item ID cannot be ITEM_ID_NULL."); - - // If quantity 0, remove. - if(quantity == 0) return inventoryRemove(inventory, item); - - // Search for existing stack. - inventorystack_t *stack = inventory->storage; - inventorystack_t *end = stack + inventory->storageSize; - do { - // Not in inventory yet, add as new stack. - if(stack->item == ITEM_ID_NULL) { - stack->item = item; - stack->quantity = quantity; - return; - } - - // Not the stack we're looking for. - if(stack->item != item) continue; - - // Update existing stack. - stack->quantity = quantity; - return; - } while(++stack < end); - - // No space in the inventory. - assertUnreachable("Inventory is full, cannot set more items."); -} - -void inventoryAdd( - inventory_t *inventory, - const itemid_t item, - const uint8_t quantity -) { - uint8_t current = inventoryGetCount(inventory, item); - uint16_t newQuantity = (uint16_t)current + (uint16_t)quantity; - - assertTrue( - newQuantity <= UINT8_MAX, - "Cannot add item, would overflow maximum quantity." - ); - - inventorySet(inventory, item, (uint8_t)newQuantity); -} - -void inventoryRemove(inventory_t *inventory, const itemid_t item) { - assertNotNull(inventory, "Inventory pointer is NULL."); - assertNotNull(inventory->storage, "Storage pointer is NULL."); - assertTrue(inventory->storageSize > 0, "Storage too small."); - assertTrue(item != ITEM_ID_NULL, "Item ID cannot be ITEM_ID_NULL."); - - inventorystack_t *stack = inventory->storage; - inventorystack_t *end = stack + inventory->storageSize; - - // Search for existing stack. - do { - // End of inventory, item not present. - if(stack->item == ITEM_ID_NULL) break; - - // Not matching stack. - if(stack->item != item) continue; - - // Match found, shift everything else down - memoryMove( - stack, - stack + 1, - (end - (stack + 1)) * sizeof(inventorystack_t) - ); - - // Clear last stack. - inventorystack_t *last = end - 1; - last->item = ITEM_ID_NULL; - - break; - } while(++stack < end); -} - -uint8_t inventoryGetCount(const inventory_t *inventory, const itemid_t item) { - assertNotNull(inventory, "Inventory pointer is NULL."); - assertNotNull(inventory->storage, "Storage pointer is NULL."); - assertTrue(inventory->storageSize > 0, "Storage too small."); - assertTrue(item != ITEM_ID_NULL, "Item ID cannot be ITEM_ID_NULL."); - - inventorystack_t *stack = inventory->storage; - inventorystack_t *end = stack + inventory->storageSize; - do { - // End of inventory, item not present. - if(stack->item == ITEM_ID_NULL) break; - - // Not matching stack. - if(stack->item != item) continue; - - // Match found, return quantity. - return stack->quantity; - } while(++stack < end); - - return 0; -} - -bool_t inventoryIsFull(const inventory_t *inventory) { - assertNotNull(inventory, "Inventory pointer is NULL."); - assertNotNull(inventory->storage, "Storage pointer is NULL."); - assertTrue(inventory->storageSize > 0, "Storage too small."); - - inventorystack_t *stack = inventory->storage; - inventorystack_t *end = stack + inventory->storageSize; - do { - // Found empty stack, not full. - if(stack->item == ITEM_ID_NULL) return false; - } while(++stack < end); - - return true; -} - -bool_t inventoryItemFull(const inventory_t *inventory, const itemid_t item) { - return inventoryGetCount(inventory, item) == ITEM_STACK_QUANTITY_MAX; -} - -// Sorters -int_t inventorySortById(const void *a, const void *b) { - const inventorystack_t *stackA = (const inventorystack_t*)a; - const inventorystack_t *stackB = (const inventorystack_t*)b; - if(stackA->item < stackB->item) return -1; - if(stackA->item > stackB->item) return 1; - return 0; -} - -int_t inventorySortByIdReverse(const void *a, const void *b) { - const inventorystack_t *stackA = (const inventorystack_t*)a; - const inventorystack_t *stackB = (const inventorystack_t*)b; - if(stackA->item < stackB->item) return 1; - if(stackA->item > stackB->item) return -1; - return 0; -} - -int_t inventorySortByType(const void *a, const void *b) { - const inventorystack_t *stackA = (const inventorystack_t*)a; - const inventorystack_t *stackB = (const inventorystack_t*)b; - const itemtype_t typeA = ITEMS[stackA->item].type; - const itemtype_t typeB = ITEMS[stackB->item].type; - if(typeA < typeB) return -1; - if(typeA > typeB) return 1; - return 0; -} - -int_t inventorySortByTypeReverse(const void *a, const void *b) { - const inventorystack_t *stackA = (const inventorystack_t*)a; - const inventorystack_t *stackB = (const inventorystack_t*)b; - const itemtype_t typeA = ITEMS[stackA->item].type; - const itemtype_t typeB = ITEMS[stackB->item].type; - if(typeA < typeB) return 1; - if(typeA > typeB) return -1; - return 0; -} - -void inventorySort( - inventory_t *inventory, - const inventorysort_t sortBy, - const bool_t reverse -) { - assertNotNull(inventory, "Inventory pointer is NULL."); - assertNotNull(inventory->storage, "Storage pointer is NULL."); - assertTrue(inventory->storageSize > 0, "Storage too small."); - assertTrue(sortBy < INVENTORY_SORT_COUNT, "Invalid sort type."); - - // Get count of used stacks - size_t count = 0; - inventorystack_t *stack = inventory->storage; - inventorystack_t *end = stack + inventory->storageSize; - do { - if(stack->item == ITEM_ID_NULL) break; - count++; - } while(++stack < end); - - if(count == 0) return; // Nothing to sort - - // Comparator - sortcompare_t comparator = NULL; - switch(sortBy) { - case INVENTORY_SORT_BY_ID: { - comparator = reverse ? inventorySortByIdReverse : inventorySortById; - break; - }; - - case INVENTORY_SORT_BY_TYPE: { - comparator = reverse ? inventorySortByTypeReverse : inventorySortByType; - break; - }; - - default: - assertUnreachable("Invalid sort type."); - break; - } - - assertNotNull(comparator, "Comparator function is NULL."); - - sort((void*)inventory->storage, count, sizeof(inventorystack_t), comparator); -} \ No newline at end of file diff --git a/src/dusk/rpg/item/inventory.h b/src/dusk/rpg/item/inventory.h deleted file mode 100644 index ba559dfe..00000000 --- a/src/dusk/rpg/item/inventory.h +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "rpg/item/item.h" - -#define ITEM_STACK_QUANTITY_MAX 99 - -typedef enum { - INVENTORY_SORT_BY_ID, - INVENTORY_SORT_BY_TYPE, - - INVENTORY_SORT_COUNT -} inventorysort_t; - -typedef struct { - itemid_t item; - uint8_t quantity; -} inventorystack_t; - -typedef struct { - inventorystack_t *storage; - uint8_t storageSize; -} inventory_t; - -/** - * Initializes an inventory. - * - * @param inventory The inventory to initialize. - * @param storage The storage array for the inventory. - * @param storageSize The size of the storage array. - */ -void inventoryInit( - inventory_t* inventory, - inventorystack_t* storage, - uint8_t storageSize -); - -/** - * Checks if a specific item exists in the inventory (and has quantity > 0). - * - * @param inventory The inventory to check. - * @param item The item ID to check. - * @return true if the item exists, false otherwise. - */ -bool_t inventoryItemExists(const inventory_t *inventory, const itemid_t item); - -/** - * Sets the quantity of a specific item in the inventory. - * - * @param inventory The inventory to modify. - * @param item The item ID to set. - * @param quantity The quantity to set. - */ -void inventorySet( - inventory_t *inventory, - const itemid_t item, - const uint8_t quantity -); - -/** - * Adds a specific quantity of an item to the inventory. - * - * @param inventory The inventory to modify. - * @param item The item ID to add. - * @param quantity The quantity to add. - */ -void inventoryAdd( - inventory_t *inventory, - const itemid_t item, - const uint8_t quantity -); - -/** - * Removes an item from the inventory. - * - * @param inventory The inventory to modify. - * @param item The item ID to remove. - */ -void inventoryRemove(inventory_t *inventory, const itemid_t item); - -/** - * Gets the count of a specific item in the inventory. - * - * @param inventory The inventory to check. - * @param item The item ID to check. - * @return The count of the item in the inventory. - */ -uint8_t inventoryGetCount(const inventory_t *inventory, const itemid_t item); - -/** - * Checks if the inventory is full. - * - * @param inventory The inventory to check. - * @return true if full, false otherwise. - */ -bool_t inventoryIsFull(const inventory_t *inventory); - -/** - * Checks if a specific item stack is full in the inventory. - * - * @param inventory The inventory to check. - * @param item The item ID to check. - * @return true if the item stack is full, false otherwise. - */ -bool_t inventoryItemFull(const inventory_t *inventory, const itemid_t item); - -/** - * Sorts the inventory based on the specified criteria. - * - * @param inventory The inventory to sort. - * @param sortBy The sorting criteria. - * @param reverse Whether to sort in reverse order. - */ -void inventorySort( - inventory_t *inventory, - const inventorysort_t sortBy, - const bool_t reverse -); \ No newline at end of file diff --git a/src/dusk/rpg/item/item.c b/src/dusk/rpg/item/item.c deleted file mode 100644 index d80a6b67..00000000 --- a/src/dusk/rpg/item/item.c +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "item.h" -#include "assert/assert.h" -#include "locale/localemanager.h" -#include "asset/loader/locale/assetlocaleloader.h" - -errorret_t itemGetName( - const itemid_t item, - char_t *buffer, - const size_t bufferSize -) { - assertTrue(item > ITEM_ID_NULL, "Item ID must not be null"); - assertTrue(item < ITEM_ID_COUNT, "Item ID out of range"); - - errorChain(assetLocaleGetString( - &LOCALE.entry->data.locale, - ITEMS[item].name, - 0, - buffer, - bufferSize - )); - - errorOk(); -} diff --git a/src/dusk/rpg/item/item.h b/src/dusk/rpg/item/item.h deleted file mode 100644 index 1c1beda5..00000000 --- a/src/dusk/rpg/item/item.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" -#include "rpg/item/itemdef.h" - -/** - * Gets the localized display name for an item. - * - * @param item The item ID to look up. Must not be ITEM_ID_NULL. - * @param buffer Buffer to write the localized name into. - * @param bufferSize Size of the buffer. - * @return Any error that occurs. - */ -errorret_t itemGetName( - const itemid_t item, - char_t *buffer, - const size_t bufferSize -); diff --git a/src/dusk/rpg/item/item.json b/src/dusk/rpg/item/item.json deleted file mode 100644 index 35aceb4d..00000000 --- a/src/dusk/rpg/item/item.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - { "id": "POTION", "type": "MEDICINE", "weight": 1.0, "name": "potion" }, - { "id": "POTATO", "type": "FOOD", "weight": 0.5, "name": "potato" }, - { "id": "APPLE", "type": "FOOD", "weight": 0.3, "name": "apple" } -] diff --git a/src/dusk/rpg/item/itemgive.c b/src/dusk/rpg/item/itemgive.c deleted file mode 100644 index c7416dc2..00000000 --- a/src/dusk/rpg/item/itemgive.c +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "itemgive.h" -#include "rpg/item/backpack.h" -#include "rpg/item/item.h" -#include "ui/rpg/textbox/uitextboxmain.h" -#include "util/string.h" -#include "error/error.h" - -#define ITEM_GIVE_NAME_MAX_CHARS 32 - -void itemGive(const itemid_t item, const uint8_t quantity) { - backpackAdd(item, quantity); - - char_t name[ITEM_GIVE_NAME_MAX_CHARS]; - errorCatch(itemGetName(item, name, ITEM_GIVE_NAME_MAX_CHARS)); - - char_t msg[ITEM_GIVE_MESSAGE_MAX_CHARS]; - stringFormat( - msg, - ITEM_GIVE_MESSAGE_MAX_CHARS - 1, - "Received %s x%u", - name, - (uint32_t)quantity - ); - uiTextboxMainSetText(msg); -} diff --git a/src/dusk/rpg/item/itemgive.h b/src/dusk/rpg/item/itemgive.h deleted file mode 100644 index 5fa02b91..00000000 --- a/src/dusk/rpg/item/itemgive.h +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "rpg/item/item.h" - -#define ITEM_GIVE_MESSAGE_MAX_CHARS 256 - -/** - * Adds a quantity of an item to the player's backpack and shows a - * "Received x" message in the main textbox. - * - * @param item The item ID to give. - * @param quantity The quantity to give. - */ -void itemGive(const itemid_t item, const uint8_t quantity); diff --git a/src/dusk/rpg/overworld/CMakeLists.txt b/src/dusk/rpg/overworld/CMakeLists.txt deleted file mode 100644 index a6c4f934..00000000 --- a/src/dusk/rpg/overworld/CMakeLists.txt +++ /dev/null @@ -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_LIBRARY_TARGET_NAME} - PUBLIC - chunk.c - map.c - maparea.c - worldpos.c - tile.c - tileshape.c -) - diff --git a/src/dusk/rpg/overworld/chunk.c b/src/dusk/rpg/overworld/chunk.c deleted file mode 100644 index 18a5316e..00000000 --- a/src/dusk/rpg/overworld/chunk.c +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "chunk.h" - -uint32_t chunkGetTileIndex(const chunkpos_t position) { - return (position.y * CHUNK_WIDTH) + position.x; -} - -bool_t chunkPositionIsEqual(const chunkpos_t a, const chunkpos_t b) { - return (a.x == b.x) && (a.y == b.y) && (a.z == b.z); -} \ No newline at end of file diff --git a/src/dusk/rpg/overworld/chunk.h b/src/dusk/rpg/overworld/chunk.h deleted file mode 100644 index 38cc9abe..00000000 --- a/src/dusk/rpg/overworld/chunk.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "rpg/overworld/tile.h" -#include "worldpos.h" - -#define CHUNK_MESH_COUNT_MAX 10 -#define CHUNK_MESH_NAME_MAX 64 -#define CHUNK_ENTITY_COUNT_MAX 10 - -typedef struct assetentry_s assetentry_t; - -typedef struct chunk_s { - chunkpos_t position; - tile_t tiles[CHUNK_TILE_COUNT]; - - assetentry_t *dcfEntry; - - uint8_t meshCount; - char_t modelNames[CHUNK_MESH_COUNT_MAX][CHUNK_MESH_NAME_MAX]; - vec3 meshOffsets[CHUNK_MESH_COUNT_MAX]; - mat4 meshModels[CHUNK_MESH_COUNT_MAX]; - assetentry_t *modelEntries[CHUNK_MESH_COUNT_MAX]; - - uint8_t entities[CHUNK_ENTITY_COUNT_MAX]; -} chunk_t; - -/** - * Gets the tile index for a tile position within a chunk. - * - * @param position The position within the chunk. - * @return The tile index within the chunk. - */ -uint32_t chunkGetTileIndex(const chunkpos_t position); - -/** - * Checks if two chunk positions are equal. - * - * @param a The first chunk position. - * @param b The second chunk position. - * @return true if equal, false otherwise. - */ -bool_t chunkPositionIsEqual(const chunkpos_t a, const chunkpos_t b); diff --git a/src/dusk/rpg/overworld/map.c b/src/dusk/rpg/overworld/map.c deleted file mode 100644 index a64556be..00000000 --- a/src/dusk/rpg/overworld/map.c +++ /dev/null @@ -1,446 +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" -#include "asset/asset.h" -#include "asset/loader/assetloader.h" -#include "console/console.h" -#include "event/event.h" -#include "util/string.h" -#include "rpg/entity/global/entityglobal.h" - -map_t MAP; - -errorret_t mapInit() { - memoryZero(&MAP, sizeof(map_t)); - MAP.loaded = true; - - chunkindex_t i = 0; - for(chunkunit_t z = 0; z < MAP_CHUNK_DEPTH; z++) { - for(chunkunit_t y = 0; y < MAP_CHUNK_HEIGHT; y++) { - for(chunkunit_t x = 0; x < MAP_CHUNK_WIDTH; x++) { - chunk_t *chunk = &MAP.chunks[i++]; - chunk->position = (chunkpos_t){ - (chunkunit_t)x, (chunkunit_t)y, (chunkunit_t)z - }; - errorChain(mapChunkLoad(chunk)); - } - } - } - - mapRebuildChunkOrder(); - errorOk(); -} - -bool_t mapIsLoaded() { - return MAP.loaded; -} - -errorret_t mapPositionSet(const chunkpos_t newPos) { - if(!mapIsLoaded()) errorThrow("No map loaded"); - if(chunkPositionIsEqual(newPos, MAP.chunkPosition)) errorOk(); - - // Separate loaded chunks into "keep" and "free" buckets. - chunkindex_t chunksFreed[MAP_CHUNK_COUNT]; - uint32_t freedCount = 0; - - // Use a boolean grid so the inner load loop can check O(1). - bool_t posLoaded[MAP_CHUNK_WIDTH][MAP_CHUNK_HEIGHT][MAP_CHUNK_DEPTH]; - memoryZero(posLoaded, sizeof(posLoaded)); - - for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) { - chunk_t *chunk = &MAP.chunks[i]; - chunkunit_t rx = chunk->position.x - newPos.x; - chunkunit_t ry = chunk->position.y - newPos.y; - chunkunit_t rz = chunk->position.z - newPos.z; - if( - rx >= 0 && rx < MAP_CHUNK_WIDTH && - ry >= 0 && ry < MAP_CHUNK_HEIGHT && - rz >= 0 && rz < MAP_CHUNK_DEPTH - ) { - posLoaded[rx][ry][rz] = true; - } else { - mapChunkUnload(chunk); - chunksFreed[freedCount++] = i; - } - } - - for(chunkunit_t z = 0; z < MAP_CHUNK_DEPTH; z++) { - for(chunkunit_t y = 0; y < MAP_CHUNK_HEIGHT; y++) { - for(chunkunit_t x = 0; x < MAP_CHUNK_WIDTH; x++) { - if(posLoaded[x][y][z]) continue; - assertTrue(freedCount > 0, "No free chunk slot available."); - chunk_t *chunk = &MAP.chunks[chunksFreed[--freedCount]]; - chunk->position = (chunkpos_t){ - newPos.x + (chunkunit_t)x, - newPos.y + (chunkunit_t)y, - newPos.z + (chunkunit_t)z - }; - errorChain(mapChunkLoad(chunk)); - } - } - } - - MAP.chunkPosition = newPos; - mapRebuildChunkOrder(); - errorOk(); -} - -errorret_t mapUpdate() { - errorOk(); -} - -errorret_t mapDispose() { - for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) { - mapChunkUnload(&MAP.chunks[i]); - } - errorOk(); -} - -void mapChunkUnload(chunk_t *chunk) { - mapChunkLoadQueueRemove(chunk); - if(MAP.loadingChunk == chunk) MAP.loadingChunk = NULL; - - for(uint8_t i = 0; i < CHUNK_ENTITY_COUNT_MAX; i++) { - if(chunk->entities[i] == 0xFF) continue; - entity_t *entity = &ENTITIES[chunk->entities[i]]; - if(!entityCanUnload(entity)) { - entitySetChunk(entity, 0xFF); - } else { - entity->type = ENTITY_TYPE_NULL; - } - } - - memorySet(chunk->entities, 0xFF, sizeof(chunk->entities)); - - if(chunk->dcfEntry != NULL) { - eventUnsubscribe(&chunk->dcfEntry->onLoaded, mapChunkLoaded); - eventUnsubscribe(&chunk->dcfEntry->onError, mapChunkLoadError); - assetUnlockEntry(chunk->dcfEntry); - chunk->dcfEntry = NULL; - } - - // modelEntries are borrowed pointers, not independently locked - the - // chunk asset entry (released above) is what actually holds the ref on - // each model, so nothing to unlock here, just drop our own copies. - for(uint8_t m = 0; m < chunk->meshCount; m++) { - chunk->modelEntries[m] = NULL; - } - chunk->meshCount = 0; -} - -errorret_t mapChunkLoad(chunk_t *chunk) { - if(!mapIsLoaded()) errorThrow("No map loaded"); - - mapChunkLoadQueueRemove(chunk); - if(MAP.loadingChunk == chunk) MAP.loadingChunk = NULL; - - if(chunk->dcfEntry != NULL) { - eventUnsubscribe(&chunk->dcfEntry->onLoaded, mapChunkLoaded); - eventUnsubscribe(&chunk->dcfEntry->onError, mapChunkLoadError); - assetUnlockEntry(chunk->dcfEntry); - chunk->dcfEntry = NULL; - } - - memorySet(chunk->entities, 0xFF, sizeof(chunk->entities)); - chunk->meshCount = 0; - - char_t name[64]; - stringFormat( - name, sizeof(name), - "chunks/%d_%d_%d.dcf", - (int32_t)chunk->position.x, - (int32_t)chunk->position.y, - (int32_t)chunk->position.z - ); - - if(!assetFileExists(name)) { - for(uint32_t i = 0; i < CHUNK_TILE_COUNT; i++) { - // chunk->tiles[i] = (tile_t){ .shape = TILE_SHAPE_GROUND, .z = 0 }; - chunk->tiles[i] = (tile_t){ .shape = TILE_SHAPE_GROUND }; - } - errorOk(); - } - - // Placeholder-fill with flat ground while the real data streams in - // asynchronously - entities standing on this chunk (subject to gravity) - // would otherwise fall through the zeroed/empty tiles until - // mapChunkLoaded() replaces them with the real data. - for(uint32_t i = 0; i < CHUNK_TILE_COUNT; i++) { - chunk->tiles[i] = (tile_t){ .shape = TILE_SHAPE_GROUND }; - } - - assertTrue( - MAP.loadQueueCount < MAP_CHUNK_COUNT, - "Chunk load queue overflow" - ); - MAP.loadQueue[MAP.loadQueueCount++] = chunk; - mapChunkLoadNext(); - errorOk(); -} - -void mapChunkLoadNext() { - if(MAP.loadingChunk != NULL) return; - if(MAP.loadQueueCount == 0) return; - - chunk_t *chunk = MAP.loadQueue[0]; - for(uint32_t i = 1; i < MAP.loadQueueCount; i++) { - MAP.loadQueue[i - 1] = MAP.loadQueue[i]; - } - MAP.loadQueueCount--; - MAP.loadingChunk = chunk; - - char_t name[64]; - stringFormat( - name, sizeof(name), - "chunks/%d_%d_%d.dcf", - (int32_t)chunk->position.x, - (int32_t)chunk->position.y, - (int32_t)chunk->position.z - ); - - assetentry_t *entry = assetLock(name, ASSET_LOADER_TYPE_CHUNK, NULL); - assertNotNull(entry, "Failed to get chunk asset entry"); - chunk->dcfEntry = entry; - - // The entry may already be resident from an earlier load that hasn't been - // reaped yet - in that case onLoaded/onError already fired once and never - // will again, so handle the terminal state directly instead of waiting on - // a subscription that would never trigger. - if(entry->state == ASSET_ENTRY_STATE_LOADED) { - mapChunkLoaded(entry, chunk); - return; - } - if(entry->state == ASSET_ENTRY_STATE_ERROR) { - mapChunkLoadError(entry, chunk); - return; - } - - eventSubscribe(&entry->onLoaded, mapChunkLoaded, chunk); - eventSubscribe(&entry->onError, mapChunkLoadError, chunk); -} - -void mapChunkLoadQueueRemove(chunk_t *chunk) { - for(uint32_t i = 0; i < MAP.loadQueueCount; i++) { - if(MAP.loadQueue[i] != chunk) continue; - for(uint32_t j = i + 1; j < MAP.loadQueueCount; j++) { - MAP.loadQueue[j - 1] = MAP.loadQueue[j]; - } - MAP.loadQueueCount--; - return; - } -} - - -chunkindex_t mapGetChunkIndexAt(const chunkpos_t position) { - if(!mapIsLoaded()) return -1; - - chunkpos_t relPos = { - position.x - MAP.chunkPosition.x, - position.y - MAP.chunkPosition.y, - position.z - MAP.chunkPosition.z - }; - - if( - relPos.x < 0 || relPos.y < 0 || relPos.z < 0 || - relPos.x >= MAP_CHUNK_WIDTH || - relPos.y >= MAP_CHUNK_HEIGHT || - relPos.z >= MAP_CHUNK_DEPTH - ) { - return -1; - } - - return chunkPosToIndex(&relPos); -} - -chunk_t *mapGetChunk(const uint8_t index) { - if(index >= MAP_CHUNK_COUNT) return NULL; - if(!mapIsLoaded()) return NULL; - return MAP.chunkOrder[index]; -} - -tile_t mapGetTile(const worldpos_t position) { - if(!mapIsLoaded()) return TILE_NULL; - - chunkpos_t chunkPos; - worldPosToChunkPos(&position, &chunkPos); - chunkindex_t chunkIndex = mapGetChunkIndexAt(chunkPos); - if(chunkIndex == -1) return TILE_NULL; - - chunk_t *chunk = mapGetChunk(chunkIndex); - assertNotNull(chunk, "Chunk pointer cannot be NULL"); - chunktileindex_t tileIndex = worldPosToChunkTileIndex(&position); - tile_t tile = chunk->tiles[tileIndex]; - if(tile.z != worldPosToChunkLocalZ(&position)) return TILE_NULL; - return tile; -} - -bool_t mapGetWalkableZNear( - const worldunit_t x, - const worldunit_t y, - const worldunit_t nearZ, - worldunit_t *outZ -) { - assertNotNull(outZ, "Output Z pointer cannot be NULL"); - - const worldunit_t candidates[] = { - nearZ, (worldunit_t)(nearZ + 1), (worldunit_t)(nearZ - 1) - }; - for(uint8_t i = 0; i < 3; i++) { - const worldpos_t pos = { x, y, candidates[i] }; - if(!tileShapeIsWalkable(mapGetTile(pos).shape)) continue; - *outZ = candidates[i]; - return true; - } - - return false; -} - -entity_t * mapSpawnEntity( - const entityglobalid_t globalId, - const worldpos_t position -) { - assertTrue( - globalId > ENTITY_GLOBAL_ID_START, - "mapSpawnEntity requires a global ID greater than ENTITY_GLOBAL_ID_START" - ); - assertTrue( - globalId < ENTITY_GLOBAL_LIST_COUNT, - "Global ID is out of range for entity global init callbacks" - ); - - // Already spawned? Reuse the existing entity instead of making a - // duplicate - two entities must never share a global ID. - entity_t *existing = entityGetByGlobalId(globalId); - if(existing != NULL) return existing; - - // See if there is a callback for this entity first. - const entityglobaldef_t *def = &ENTITY_GLOBAL_LIST[globalId]; - assertNotNull(def, "No global entity definition for this ID"); - assertNotNull(def->callback, "No callback registered for this global ID"); - - // Get available entity. - uint8_t index = entityGetAvailable(); - assertTrue(index != 0xFF, "No available entity slots for mapSpawnEntity"); - - // Get the pointer and do the init. - entity_t *entity = &ENTITIES[index]; - entityInit(entity, def->type); - entity->globalId = globalId; - entityPositionSet(entity, position);// Also assigns the entity's chunk. - - // Invoke the callback to initialize the entity. - entityglobalcreate_t create = { - .entity = entity, - .position = position - }; - def->callback(&create); - return entity; -} - -void mapRebuildChunkOrder() { - memoryZero(MAP.chunkOrder, sizeof(MAP.chunkOrder)); - for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) { - chunk_t *chunk = &MAP.chunks[i]; - const chunkpos_t rel = { - chunk->position.x - MAP.chunkPosition.x, - chunk->position.y - MAP.chunkPosition.y, - chunk->position.z - MAP.chunkPosition.z - }; - if( - rel.x < 0 || rel.x >= MAP_CHUNK_WIDTH || - rel.y < 0 || rel.y >= MAP_CHUNK_HEIGHT || - rel.z < 0 || rel.z >= MAP_CHUNK_DEPTH - ) continue; - MAP.chunkOrder[chunkPosToIndex(&rel)] = chunk; - } -} - -void mapChunkLoadError(void *params, void *user) { - assertNotNull(params, "mapChunkLoadError: params cannot be NULL"); - assertNotNull(user, "mapChunkLoadError: user cannot be NULL"); - assetentry_t *entry = (assetentry_t *)params; - chunk_t *chunk = (chunk_t *)user; - if(chunk->dcfEntry != entry) return; - consolePrint( - "Chunk load error: %d %d %d", - (int32_t)chunk->position.x, - (int32_t)chunk->position.y, - (int32_t)chunk->position.z - ); - eventUnsubscribe(&entry->onLoaded, mapChunkLoaded); - eventUnsubscribe(&entry->onError, mapChunkLoadError); - assetUnlockEntry(chunk->dcfEntry); - chunk->dcfEntry = NULL; - memorySet(chunk->tiles, 0x00, sizeof(chunk->tiles)); - - if(MAP.loadingChunk == chunk) MAP.loadingChunk = NULL; - mapChunkLoadNext(); -} - -void mapChunkLoaded(void *params, void *user) { - assertNotNull(params, "mapChunkLoaded: params cannot be NULL"); - assertNotNull(user, "mapChunkLoaded: user cannot be NULL"); - assetentry_t *entry = (assetentry_t *)params; - chunk_t *chunk = (chunk_t *)user; - if(chunk->dcfEntry != entry) return; - // consolePrint( - // "Chunk loaded: %d %d %d", - // (int32_t)chunk->position.x, - // (int32_t)chunk->position.y, - // (int32_t)chunk->position.z - // ); - uint8_t meshCount = entry->data.chunk.meshCount; - memoryCopy( - chunk->tiles, - entry->data.chunk.tiles, - sizeof(chunk->tiles) - ); - worldpos_t wp; - chunkPosToWorldPos(&chunk->position, &wp); - vec3 wpf = { - (float_t)wp.x, (float_t)wp.y, (float_t)wp.z * WORLD_LAYER_HEIGHT - }; - for(uint8_t m = 0; m < meshCount; m++) { - stringCopy( - chunk->modelNames[m], - entry->data.chunk.modelNames[m], - CHUNK_MESH_NAME_MAX - ); - glm_vec3_copy( - entry->data.chunk.meshOffsets[m], - chunk->meshOffsets[m] - ); - vec3 scaledOffset = { - chunk->meshOffsets[m][0], - chunk->meshOffsets[m][1], - chunk->meshOffsets[m][2] * WORLD_LAYER_HEIGHT - }; - vec3 pos; - glm_vec3_add(wpf, scaledOffset, pos); - glm_translate_make(chunk->meshModels[m], pos); - // Borrow the pointer rather than stealing it - the chunk asset entry - // keeps its own lock on each model (taken once while it loaded) and we - // keep the chunk asset entry itself locked (see below), so the models - // stay valid for as long as this chunk_t is using them. The entry may - // now be reused by a later mapChunkLoad for a different chunk_t once we - // eventually unlock it in mapChunkUnload, at which point its - // modelEntries must still be intact for that next reuse to copy from. - chunk->modelEntries[m] = entry->data.chunk.modelEntries[m]; - } - eventUnsubscribe(&entry->onLoaded, mapChunkLoaded); - eventUnsubscribe(&entry->onError, mapChunkLoadError); - // Deliberately keep chunk->dcfEntry locked and set - it is what keeps the - // chunk asset entry (and therefore its model locks) alive for as long as - // this chunk_t is displaying it. Released in mapChunkUnload instead. - chunk->meshCount = meshCount; - - if(MAP.loadingChunk == chunk) MAP.loadingChunk = NULL; - mapChunkLoadNext(); -} diff --git a/src/dusk/rpg/overworld/map.h b/src/dusk/rpg/overworld/map.h deleted file mode 100644 index f8b09bf8..00000000 --- a/src/dusk/rpg/overworld/map.h +++ /dev/null @@ -1,177 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" -#include "rpg/overworld/chunk.h" -#include "rpg/entity/entity.h" - -#define MAP_FILE_PATH_MAX 128 - -typedef struct map_s { - bool_t loaded; - - chunk_t chunks[MAP_CHUNK_COUNT]; - chunk_t *chunkOrder[MAP_CHUNK_COUNT]; - chunkpos_t chunkPosition; - - // Only one chunk may be mid-load (asset locked & awaiting onLoaded/ - // onError) at any given time - everything else waits here in FIFO order. - chunk_t *loadQueue[MAP_CHUNK_COUNT]; - uint32_t loadQueueCount; - chunk_t *loadingChunk; -} map_t; - -extern map_t MAP; - -/** - * Initializes the map. - * - * @return An error code. - */ -errorret_t mapInit(); - -/** - * Checks if a map is loaded. - * - * @return true if a map is loaded, false otherwise. - */ -bool_t mapIsLoaded(); - -/** - * Updates the map. - * - * @return An error code. - */ -errorret_t mapUpdate(); - -/** - * Disposes of the map. - * - * @return An error code. - */ -errorret_t mapDispose(); - -/** - * Sets the map position and updates chunks accordingly. - * - * @param newPos The new chunk position. - * @return An error code. - */ -errorret_t mapPositionSet(const chunkpos_t newPos); - -/** - * Unloads a chunk. - * - * @param chunk The chunk to unload. - */ -void mapChunkUnload(chunk_t* chunk); - -/** - * Loads a chunk. Starts async loading without blocking. - * - * @param chunk The chunk to load. - * @return An error code. - */ -errorret_t mapChunkLoad(chunk_t* chunk); - -/** - * Starts loading the next queued chunk, if no chunk is currently mid-load. - * Called after mapChunkLoad enqueues a chunk, and again after the - * currently-loading chunk finishes (or is unloaded) to advance the queue. - */ -void mapChunkLoadNext(); - -/** - * Removes a chunk from the load queue if present. Used when a chunk is - * re-queued or unloaded before its turn to load has come up. - * - * @param chunk The chunk to remove from the load queue. - */ -void mapChunkLoadQueueRemove(chunk_t *chunk); - -/** - * Callback invoked when a chunk DCF asset fails to load. Fills the - * chunk tiles with TILE_SHAPE_GROUND as a fallback. - * Always invoked on the main thread. - * - * @param params The failed assetentry_t. - * @param user The chunk_t that owns the entry. - */ -void mapChunkLoadError(void *params, void *user); - -/** - * Callback invoked when a chunk DCF asset finishes loading. - * Always invoked on the main thread. - * - * @param params The loaded assetentry_t. - * @param user The chunk_t that owns the entry. - */ -void mapChunkLoaded(void *params, void *user); - -/** - * Rebuilds chunkOrder from the loaded chunks that fall within the - * current render window. Called whenever chunkPosition changes. - */ -void mapRebuildChunkOrder(); - -/** - * Gets the index of a chunk, within the world, at the given position. - * - * @param position The chunk position. - * @return The index of the chunk, or -1 if out of bounds. - */ -chunkindex_t mapGetChunkIndexAt(const chunkpos_t position); - -/** - * Gets a chunk by its index. - * - * @param chunkIndex The index of the chunk. - * @return A pointer to the chunk. - */ -chunk_t * mapGetChunk(const uint8_t chunkIndex); - -/** - * Gets the tile at the given world position. - * - * @param position The world position. - * @return The tile at that position, or TILE_NULL if the chunk is unloaded. - */ -tile_t mapGetTile(const worldpos_t position); - -/** - * Finds the closest walkable Z layer to nearZ at the given X/Y. Checks - * nearZ first, then nearZ + 1, then nearZ - 1, since ramps only ever - * change height by one Z layer between adjacent tiles. - * - * @param x The world X coordinate to check. - * @param y The world Y coordinate to check. - * @param nearZ The reference Z layer to search outward from. - * @param outZ Output pointer, set to the resolved Z layer on success. - * @return true if a walkable tile was found, false otherwise. - */ -bool_t mapGetWalkableZNear( - const worldunit_t x, - const worldunit_t y, - const worldunit_t nearZ, - worldunit_t *outZ -); - -/** - * Spawns a global (persistent) entity into the world at the given position. - * Asserts globalId is greater than ENTITY_GLOBAL_ID_START - use entityInit - * directly for ephemeral, non-global entities. - * - * @param globalId The global entity ID to assign, must be greater than - * ENTITY_GLOBAL_ID_START. - * @param position The world position to spawn the entity at. - * @return Pointer to the spawned entity. - */ -entity_t * mapSpawnEntity( - const entityglobalid_t globalId, - const worldpos_t position -); \ No newline at end of file diff --git a/src/dusk/rpg/overworld/maparea.c b/src/dusk/rpg/overworld/maparea.c deleted file mode 100644 index 562afaa6..00000000 --- a/src/dusk/rpg/overworld/maparea.c +++ /dev/null @@ -1,155 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "maparea.h" -#include "assert/assert.h" -#include "util/math.h" -#include "util/memory.h" -#include "rpg/overworld/map.h" - -maparea_t MAP_AREAS[MAP_AREA_COUNT_MAX]; - -void mapAreaInit( - maparea_t *area, - const worldpos_t min, - const worldpos_t max, - const mapareacallback_t callback, - const uint8_t notify, - const uint8_t trigger -) { - assertNotNull(area, "Map area pointer cannot be NULL"); - assertNotNull(callback, "Map area callback cannot be NULL"); - - area->min.x = mathMin(min.x, max.x); - area->min.y = mathMin(min.y, max.y); - area->min.z = mathMin(min.z, max.z); - - area->max.x = mathMax(min.x, max.x); - area->max.y = mathMax(min.y, max.y); - area->max.z = mathMax(min.z, max.z); - - area->callback = callback; - area->notify = notify; - area->trigger = trigger; - area->triggerCount = 0; - - memorySet(area->entities, 0, sizeof(area->entities)); - memorySet(area->lastStepPosition, 0, sizeof(area->lastStepPosition)); -} - -bool_t mapAreaIsInside(const maparea_t *area, const worldpos_t position) { - assertNotNull(area, "Map area pointer cannot be NULL"); - - return ( - position.x >= area->min.x && position.x <= area->max.x && - position.y >= area->min.y && position.y <= area->max.y && - position.z >= area->min.z && position.z <= area->max.z - ); -} - -bool_t mapAreaIsChunkOverlappingOrInside( - const maparea_t *area, - const chunk_t *chunk -) { - assertNotNull(area, "Map area pointer cannot be NULL"); - assertNotNull(chunk, "Chunk pointer cannot be NULL"); - - worldpos_t chunkMin, chunkMax; - chunkPosToWorldPos(&chunk->position, &chunkMin); - chunkMax.x = chunkMin.x + CHUNK_WIDTH - 1; - chunkMax.y = chunkMin.y + CHUNK_HEIGHT - 1; - chunkMax.z = chunkMin.z + CHUNK_DEPTH - 1; - - return ( - chunkMin.x <= area->max.x && area->min.x <= chunkMax.x && - chunkMin.y <= area->max.y && area->min.y <= chunkMax.y && - chunkMin.z <= area->max.z && area->min.z <= chunkMax.z - ); -} - -bool_t mapAreaCanUnload(const maparea_t *area) { - assertNotNull(area, "Map area pointer cannot be NULL"); - - for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) { - if(mapAreaIsChunkOverlappingOrInside(area, &MAP.chunks[i])) return false; - } - - return true; -} - -bool_t mapAreaShouldNotify(const maparea_t *area, const entity_t *entity) { - assertNotNull(area, "Map area pointer cannot be NULL"); - assertNotNull(entity, "Entity pointer cannot be NULL"); - - switch(entity->type) { - case ENTITY_TYPE_PLAYER: - return (area->notify & MAP_AREA_NOTIFY_PLAYER) != 0; - - case ENTITY_TYPE_NPC: - return (area->notify & MAP_AREA_NOTIFY_NPC) != 0; - - default: - return false; - } -} - -uint8_t mapAreaAdd( - const worldpos_t min, - const worldpos_t max, - const mapareacallback_t callback, - const uint8_t notify, - const uint8_t trigger -) { - for(uint8_t i = 0; i < MAP_AREA_COUNT_MAX; i++) { - if(MAP_AREAS[i].callback != NULL) continue; - mapAreaInit(&MAP_AREAS[i], min, max, callback, notify, trigger); - return i; - } - - assertUnreachable("No available map area slots"); - return 0xFF; -} - -void mapAreaRemove(const uint8_t id) { - assertTrue(id < MAP_AREA_COUNT_MAX, "Map area ID is out of range"); - MAP_AREAS[id].callback = NULL; -} - -void mapAreaCheckEntity(entity_t *entity) { - assertNotNull(entity, "Entity pointer cannot be NULL"); - - for(uint8_t i = 0; i < MAP_AREA_COUNT_MAX; i++) { - maparea_t *area = &MAP_AREAS[i]; - if(area->callback == NULL) continue; - if(!mapAreaShouldNotify(area, entity)) continue; - - bool_t wasInside = area->entities[entity->id] != 0; - bool_t isInside = mapAreaIsInside(area, entity->position); - uint8_t trigger = 0; - - if(isInside && !wasInside) { - area->entities[entity->id] = 1; - area->lastStepPosition[entity->id] = entity->position; - trigger = MAP_TRIGGER_ENTER; - } else if(isInside && wasInside) { - if(!worldPosIsEqual(entity->position, area->lastStepPosition[entity->id])) { - area->lastStepPosition[entity->id] = entity->position; - trigger = MAP_TRIGGER_STEP; - } - } else if(!isInside && wasInside) { - area->entities[entity->id] = 0; - trigger = MAP_TRIGGER_EXIT; - } - - if(trigger == 0 || !(area->trigger & trigger)) continue; - area->triggerCount++; - area->callback(entity, trigger); - } -} - -void mapAreaNoopCallback(entity_t *entity, const uint8_t trigger) { -} diff --git a/src/dusk/rpg/overworld/maparea.h b/src/dusk/rpg/overworld/maparea.h deleted file mode 100644 index f4d88dc9..00000000 --- a/src/dusk/rpg/overworld/maparea.h +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "worldpos.h" -#include "rpg/entity/entity.h" - -typedef struct chunk_s chunk_t; -typedef struct maparea_s maparea_t; - -#define MAP_AREA_COUNT_MAX 32 - -#define MAP_AREA_NOTIFY_PLAYER (1 << 0) -#define MAP_AREA_NOTIFY_NPC (1 << 1) -#define MAP_AREA_NOTIFY_ALL (MAP_AREA_NOTIFY_PLAYER | MAP_AREA_NOTIFY_NPC) - -// Fires once per newly-entered tile while an entity remains inside the -// area (not once per frame) - see mapAreaCheckEntity. -#define MAP_TRIGGER_STEP (1 << 0) -#define MAP_TRIGGER_ENTER (1 << 1) -#define MAP_TRIGGER_EXIT (1 << 2) -#define MAP_TRIGGER_ALL (MAP_TRIGGER_STEP | MAP_TRIGGER_ENTER | MAP_TRIGGER_EXIT) - -/** - * Callback invoked for a map area. - * - * @param entity Pointer to the entity associated with the callback. - * @param trigger Which MAP_TRIGGER_* condition invoked the callback. - */ -typedef void (*mapareacallback_t)(entity_t *entity, const uint8_t trigger); - -typedef struct maparea_s { - worldpos_t min; - worldpos_t max; - mapareacallback_t callback; - uint8_t notify; - uint8_t trigger; - uint8_t entities[ENTITY_COUNT]; - - // The floored tile position ENTER or STEP last fired at, per entity. - // Used to fire STEP only once per newly-entered tile rather than - // every frame the entity remains inside the area. - worldpos_t lastStepPosition[ENTITY_COUNT]; - - // Incremented every time this area's callback is invoked. Lets other - // systems (e.g. cutscenes) detect "has this area fired since I last - // checked" without needing to be the callback themselves. - uint32_t triggerCount; -} maparea_t; - -extern maparea_t MAP_AREAS[MAP_AREA_COUNT_MAX]; - -/** - * Initializes a map area with the given bounds, callback, notify flags - * and trigger flags. - * - * @param area Pointer to the map area to initialize. - * @param min The minimum world position of the area. - * @param max The maximum world position of the area. - * @param callback The callback to invoke for this area. Must not be NULL. - * @param notify Bitwise MAP_AREA_NOTIFY_* flags for which entity types - * should trigger the callback. - * @param trigger Bitwise MAP_TRIGGER_* flags for which conditions should - * invoke the callback. - */ -void mapAreaInit( - maparea_t *area, - const worldpos_t min, - const worldpos_t max, - const mapareacallback_t callback, - const uint8_t notify, - const uint8_t trigger -); - -/** - * Checks whether a world position falls within a map area's bounds - * (inclusive on all axes). - * - * @param area Pointer to the map area to check. - * @param position The world position to check. - * @returns true if the position is inside the area. - */ -bool_t mapAreaIsInside(const maparea_t *area, const worldpos_t position); - -/** - * Checks whether a chunk's full footprint overlaps a map area's bounds, - * rather than just testing the chunk's origin corner. - * - * @param area Pointer to the map area to check. - * @param chunk Pointer to the chunk to check. - * @returns true if the chunk overlaps the area. - */ -bool_t mapAreaIsChunkOverlappingOrInside( - const maparea_t *area, - const chunk_t *chunk -); - -/** - * Checks whether a map area is safe to unload, i.e. no entity currently - * occupies a position within its bounds. - * - * @param area Pointer to the map area to check. - * @returns true if no entity is inside the area. - */ -bool_t mapAreaCanUnload(const maparea_t *area); - -/** - * Checks whether an entity's type is included in a map area's notify flags. - * - * @param area Pointer to the map area to check. - * @param entity Pointer to the entity to check. - * @returns true if the entity's type should trigger this area's callback. - */ -bool_t mapAreaShouldNotify(const maparea_t *area, const entity_t *entity); - -/** - * Adds a map area to the global MAP_AREAS list, in the first free slot. - * A slot is considered free if its callback is NULL. - * - * @param min The minimum world position of the area. - * @param max The maximum world position of the area. - * @param callback The callback to invoke for this area. Must not be NULL. - * @param notify Bitwise MAP_AREA_NOTIFY_* flags for which entity types - * should trigger the callback. - * @param trigger Bitwise MAP_TRIGGER_* flags for which conditions should - * invoke the callback. - * @returns The ID of the newly added map area. - */ -uint8_t mapAreaAdd( - const worldpos_t min, - const worldpos_t max, - const mapareacallback_t callback, - const uint8_t notify, - const uint8_t trigger -); - -/** - * Removes a map area from the global MAP_AREAS list, freeing its slot by - * clearing its callback to NULL. - * - * @param id The ID of the map area to remove. - */ -void mapAreaRemove(const uint8_t id); - -/** - * Checks every active map area against an entity's current position, - * for areas whose notify flags include the entity's type. Invokes the - * area's callback with MAP_TRIGGER_ENTER the frame the entity first - * becomes inside, MAP_TRIGGER_STEP the frame it moves into a different - * tile while remaining inside, and MAP_TRIGGER_EXIT the frame it leaves. - * Safe (and expected) to call every frame - trigger conditions are - * edge-detected against the entity's tile position, not the frame rate. - * - * @param entity Pointer to the entity to check. - */ -void mapAreaCheckEntity(entity_t *entity); - -/** - * A map area callback that does nothing. Useful for areas whose only - * purpose is to be waited on (see CUTSCENE_MAP_AREA_WAIT), since the - * wait is driven by triggerCount rather than callback logic. - * - * @param entity Pointer to the entity associated with the callback. - * @param trigger Which MAP_TRIGGER_* condition invoked the callback. - */ -void mapAreaNoopCallback(entity_t *entity, const uint8_t trigger); \ No newline at end of file diff --git a/src/dusk/rpg/overworld/tile.c b/src/dusk/rpg/overworld/tile.c deleted file mode 100644 index 34e25cda..00000000 --- a/src/dusk/rpg/overworld/tile.c +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "tile.h" diff --git a/src/dusk/rpg/overworld/tile.h b/src/dusk/rpg/overworld/tile.h deleted file mode 100644 index 82f30850..00000000 --- a/src/dusk/rpg/overworld/tile.h +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "tileshape.h" - -typedef struct { - tileshape_t shape; - - // Local Z layer (0..CHUNK_DEPTH-1) this tile occupies within its chunk's - // depth slab. Used for entity navigation since chunk_t only stores one - // tile per X/Y column rather than one per X/Y/Z. - uint8_t z; -} tile_t; - -#define TILE_NULL ((tile_t){ .shape = TILE_SHAPE_NULL, .z = 0 }) \ No newline at end of file diff --git a/src/dusk/rpg/overworld/tileshape.c b/src/dusk/rpg/overworld/tileshape.c deleted file mode 100644 index 3493795e..00000000 --- a/src/dusk/rpg/overworld/tileshape.c +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "tileshape.h" -#include "assert/assert.h" - -// Per-shape corner heights as [sw, se, ne, nw] offsets (0 or 1) from the -// tile's base Z layer - transcribed verbatim from the mesh generator's -// ramp tables (tools/asset/chunk/__main__.py _RAMP_CORNERS, -// editor/client/public/common/chunkterrain.js RAMP_CORNERS) so the -// walkable surface always matches what's rendered. -static const float_t TILE_SHAPE_RAMP_CORNERS[TILE_SHAPE_COUNT][4] = { - [TILE_SHAPE_GROUND] = { 0.0f, 0.0f, 0.0f, 0.0f }, - [TILE_SHAPE_RAMP_NORTH] = { 0.0f, 0.0f, 1.0f, 1.0f }, - [TILE_SHAPE_RAMP_SOUTH] = { 1.0f, 1.0f, 0.0f, 0.0f }, - [TILE_SHAPE_RAMP_EAST] = { 0.0f, 1.0f, 1.0f, 0.0f }, - [TILE_SHAPE_RAMP_WEST] = { 1.0f, 0.0f, 0.0f, 1.0f }, - [TILE_SHAPE_RAMP_NORTHEAST] = { 0.0f, 0.0f, 1.0f, 0.0f }, - [TILE_SHAPE_RAMP_NORTHWEST] = { 0.0f, 0.0f, 0.0f, 1.0f }, - [TILE_SHAPE_RAMP_SOUTHEAST] = { 0.0f, 1.0f, 0.0f, 0.0f }, - [TILE_SHAPE_RAMP_SOUTHWEST] = { 1.0f, 0.0f, 0.0f, 0.0f }, - [TILE_SHAPE_RAMP_NORTHEAST_INNER] = { 0.0f, 1.0f, 1.0f, 1.0f }, - [TILE_SHAPE_RAMP_NORTHWEST_INNER] = { 1.0f, 0.0f, 1.0f, 1.0f }, - [TILE_SHAPE_RAMP_SOUTHEAST_INNER] = { 1.0f, 1.0f, 1.0f, 0.0f }, - [TILE_SHAPE_RAMP_SOUTHWEST_INNER] = { 1.0f, 1.0f, 0.0f, 1.0f }, -}; - -bool_t tileShapeIsWalkable(const tileshape_t shape) { - switch(shape) { - case TILE_SHAPE_NULL: - return false; - - default: - return true; - } -} - -bool_t tileShapeIsRamp(const tileshape_t shape) { - switch(shape) { - case TILE_SHAPE_RAMP_NORTH: - case TILE_SHAPE_RAMP_SOUTH: - case TILE_SHAPE_RAMP_EAST: - case TILE_SHAPE_RAMP_WEST: - case TILE_SHAPE_RAMP_NORTHEAST: - case TILE_SHAPE_RAMP_NORTHWEST: - case TILE_SHAPE_RAMP_SOUTHEAST: - case TILE_SHAPE_RAMP_SOUTHWEST: - case TILE_SHAPE_RAMP_NORTHEAST_INNER: - case TILE_SHAPE_RAMP_NORTHWEST_INNER: - case TILE_SHAPE_RAMP_SOUTHEAST_INNER: - case TILE_SHAPE_RAMP_SOUTHWEST_INNER: - return true; - - default: - return false; - } -} - -float_t tileShapeGetRampHeight( - const tileshape_t shape, const float_t localX, const float_t localY -) { - assertTrue(localX >= 0.0f && localX <= 1.0f, "localX must be in [0,1]"); - assertTrue(localY >= 0.0f && localY <= 1.0f, "localY must be in [0,1]"); - - const float_t sw = TILE_SHAPE_RAMP_CORNERS[shape][0]; - const float_t se = TILE_SHAPE_RAMP_CORNERS[shape][1]; - const float_t ne = TILE_SHAPE_RAMP_CORNERS[shape][2]; - const float_t nw = TILE_SHAPE_RAMP_CORNERS[shape][3]; - - // Planar interpolation per triangle, split along the SW-NE diagonal - - // matches the two-triangle quad the mesh generator emits exactly, - // rather than a smooth (but unrendered) bilinear blend. - if(localY <= localX) { - return sw + (se - sw) * localX + (ne - se) * localY; - } - return sw + (ne - nw) * localX + (nw - sw) * localY; -} \ No newline at end of file diff --git a/src/dusk/rpg/overworld/tileshape.h b/src/dusk/rpg/overworld/tileshape.h deleted file mode 100644 index 14d0fb97..00000000 --- a/src/dusk/rpg/overworld/tileshape.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "rpg/entity/entitydir.h" - -typedef enum { - TILE_SHAPE_NULL = 0, - - TILE_SHAPE_GROUND = 1, - TILE_SHAPE_RAMP_NORTH = 2, - TILE_SHAPE_RAMP_EAST = 3, - TILE_SHAPE_RAMP_SOUTH = 4, - TILE_SHAPE_RAMP_WEST = 5, - TILE_SHAPE_RAMP_NORTHEAST = 6, - TILE_SHAPE_RAMP_NORTHWEST = 7, - TILE_SHAPE_RAMP_SOUTHEAST = 8, - TILE_SHAPE_RAMP_SOUTHWEST = 9, - TILE_SHAPE_RAMP_NORTHEAST_INNER = 10, - TILE_SHAPE_RAMP_NORTHWEST_INNER = 11, - TILE_SHAPE_RAMP_SOUTHEAST_INNER = 12, - TILE_SHAPE_RAMP_SOUTHWEST_INNER = 13, - - TILE_SHAPE_COUNT -} tileshape_t; - -/** - * Returns whether or not the given tile shape is a ramp. - * - * @param shape The tile shape to check. - * @return bool_t True if ramp, false if not. - */ -bool_t tileShapeIsRamp(const tileshape_t shape); - -/** - * Returns whether or not the given tile shape is walkable. - * - * @param shape The tile shape to check. - * @return bool_t True if walkable, false if not. - */ -bool_t tileShapeIsWalkable(const tileshape_t shape); - -/** - * Returns the floor height offset (0 to 1, above the tile's base Z - * layer) for the given tile shape at a local position within the tile. - * Flat ground is always 0. Ramp shapes vary linearly across the tile, - * matching the two-triangle mesh generated for rendering (split along - * the SW-NE diagonal) exactly, so the walkable surface and the - * rendered surface never disagree. - * - * @param shape The tile shape to query. - * @param localX Local X position within the tile, in [0, 1]. - * @param localY Local Y position within the tile, in [0, 1]. - * @return float_t The height offset above the tile's base Z layer. - */ -float_t tileShapeGetRampHeight( - const tileshape_t shape, const float_t localX, const float_t localY -); \ No newline at end of file diff --git a/src/dusk/rpg/overworld/worldpos.c b/src/dusk/rpg/overworld/worldpos.c deleted file mode 100644 index 57bf5118..00000000 --- a/src/dusk/rpg/overworld/worldpos.c +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "worldpos.h" -#include "assert/assert.h" - -bool_t worldPosIsEqual(const worldpos_t a, const worldpos_t b) { - return a.x == b.x && a.y == b.y && a.z == b.z; -} - -void chunkPosToWorldPos(const chunkpos_t* chunkPos, worldpos_t* out) { - assertNotNull(chunkPos, "Chunk position pointer cannot be NULL"); - assertNotNull(out, "Output world position pointer cannot be NULL"); - - out->x = (worldunit_t)(chunkPos->x * CHUNK_WIDTH); - out->y = (worldunit_t)(chunkPos->y * CHUNK_HEIGHT); - out->z = (worldunit_t)(chunkPos->z * CHUNK_DEPTH); -} - -void worldPosToChunkPos(const worldpos_t* worldPos, chunkpos_t* out) { - assertNotNull(worldPos, "World position pointer cannot be NULL"); - assertNotNull(out, "Output chunk position pointer cannot be NULL"); - - if(worldPos->x < 0) { - out->x = (chunkunit_t)((worldPos->x - (CHUNK_WIDTH - 1)) / CHUNK_WIDTH); - } else { - out->x = (chunkunit_t)(worldPos->x / CHUNK_WIDTH); - } - - if(worldPos->y < 0) { - out->y = (chunkunit_t)((worldPos->y - (CHUNK_HEIGHT - 1)) / CHUNK_HEIGHT); - } else { - out->y = (chunkunit_t)(worldPos->y / CHUNK_HEIGHT); - } - - if(worldPos->z < 0) { - out->z = (chunkunit_t)((worldPos->z - (CHUNK_DEPTH - 1)) / CHUNK_DEPTH); - } else { - out->z = (chunkunit_t)(worldPos->z / CHUNK_DEPTH); - } -} - -chunktileindex_t worldPosToChunkTileIndex(const worldpos_t* worldPos) { - assertNotNull(worldPos, "World position pointer cannot be NULL"); - - uint8_t localX, localY; - if(worldPos->x < 0) { - localX = (uint8_t)( - (CHUNK_WIDTH - 1) - ((-worldPos->x - 1) % CHUNK_WIDTH) - ); - } else { - localX = (uint8_t)(worldPos->x % CHUNK_WIDTH); - } - - if(worldPos->y < 0) { - localY = (uint8_t)( - (CHUNK_HEIGHT - 1) - ((-worldPos->y - 1) % CHUNK_HEIGHT) - ); - } else { - localY = (uint8_t)(worldPos->y % CHUNK_HEIGHT); - } - - chunktileindex_t chunkTileIndex = (chunktileindex_t)( - (localY * CHUNK_WIDTH) + localX - ); - assertTrue( - chunkTileIndex < CHUNK_TILE_COUNT, - "Calculated chunk tile index is out of bounds" - ); - return chunkTileIndex; -} - -uint8_t worldPosToChunkLocalZ(const worldpos_t* worldPos) { - assertNotNull(worldPos, "World position pointer cannot be NULL"); - - if(worldPos->z < 0) { - return (uint8_t)( - (CHUNK_DEPTH - 1) - ((-worldPos->z - 1) % CHUNK_DEPTH) - ); - } - return (uint8_t)(worldPos->z % CHUNK_DEPTH); -} - -chunkindex_t chunkPosToIndex(const chunkpos_t* pos) { - assertNotNull(pos, "Chunk position pointer cannot be NULL"); - - chunkindex_t chunkIndex = (chunkindex_t)( - (pos->z * MAP_CHUNK_WIDTH * MAP_CHUNK_HEIGHT) + - (pos->y * MAP_CHUNK_WIDTH) + - pos->x - ); - - return chunkIndex; -} \ No newline at end of file diff --git a/src/dusk/rpg/overworld/worldpos.h b/src/dusk/rpg/overworld/worldpos.h deleted file mode 100644 index 32758288..00000000 --- a/src/dusk/rpg/overworld/worldpos.h +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "display/display.h" -#include "util/math.h" - -#define TILE_SIZE_PIXELS 24 - -// World-space Z distance of one Z-layer/story, in render/world-float -// space. A ramp rises this much over a horizontal run of 1 tile-width; -// chosen as 1/sqrt(2) so the ramp's slope, derived from a 45-45-90 -// triangle with hypotenuse 1, matches the length of a flat tile edge. -#define WORLD_LAYER_HEIGHT 0.70710678f - -#define CHUNK_WIDTH 16 -#define CHUNK_HEIGHT 16 -#define CHUNK_DEPTH 8 - -// Chunks store one tile per X/Y column, not one per X/Y/Z - each tile -// records its own local Z layer (see tile_t) instead. -#define CHUNK_TILE_COUNT (CHUNK_WIDTH * CHUNK_HEIGHT) - -#define MAP_CHUNK_WIDTH 3 -#define MAP_CHUNK_HEIGHT 3 -#define MAP_CHUNK_DEPTH 4 -#define MAP_CHUNK_COUNT (MAP_CHUNK_WIDTH * MAP_CHUNK_HEIGHT * MAP_CHUNK_DEPTH) - -#define ENTITY_COUNT 32 - -typedef int16_t worldunit_t; -typedef int16_t chunkunit_t; -typedef int16_t chunkindex_t; -typedef uint32_t chunktileindex_t; - -typedef int32_t worldunits_t; -typedef int32_t chunkunits_t; - -typedef struct worldpos2d_s { - worldunit_t x, y; -} worldpos2d_t; - -typedef struct worldpos_s { - worldunit_t x, y, z; -} worldpos_t; - -typedef struct chunkpos_t { - chunkunit_t x, y, z; -} chunkpos_t; - -/** - * Compares two world positions for equality. - * - * @param a The first world position. - * @param b The second world position. - * @return true if equal, false otherwise. - */ -bool_t worldPosIsEqual(const worldpos_t a, const worldpos_t b); - -/** - * Converts a world position to a chunk position. - * - * @param worldPos The world position. - * @param out The output chunk position. - */ -void chunkPosToWorldPos(const chunkpos_t* chunkPos, worldpos_t* out); - -/** - * Converts a chunk position to a world position. - * - * @param worldPos The world position. - * @param out The output chunk position. - */ -void worldPosToChunkPos(const worldpos_t* worldPos, chunkpos_t* out); - -/** - * Converts a position in world-space to an index inside a chunk that the tile - * resides in. - * - * @param worldPos The world position. - * @return The tile index within the chunk. - */ -chunktileindex_t worldPosToChunkTileIndex(const worldpos_t* worldPos); - -/** - * Converts a world-space Z coordinate to the local Z layer (0..CHUNK_DEPTH-1) - * within the chunk that owns it, for comparison against tile_t.z. - * - * @param worldPos The world position. - * @return The local Z layer within the owning chunk. - */ -uint8_t worldPosToChunkLocalZ(const worldpos_t* worldPos); - -/** - * Converts a chunk position to a world position. - * - * @param worldPos The world position. - * @param out The output chunk position. - */ -chunkindex_t chunkPosToIndex(const chunkpos_t* pos); \ No newline at end of file diff --git a/src/dusk/rpg/physics/CMakeLists.txt b/src/dusk/rpg/physics/CMakeLists.txt deleted file mode 100644 index 5f81ee6e..00000000 --- a/src/dusk/rpg/physics/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - physicsbody.c - physicsworld.c -) diff --git a/src/dusk/rpg/physics/physicsbody.c b/src/dusk/rpg/physics/physicsbody.c deleted file mode 100644 index 01d84649..00000000 --- a/src/dusk/rpg/physics/physicsbody.c +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "physicsbody.h" -#include "assert/assert.h" - -void physicsBodyInit( - physicsbody_t *body, const vec3 position, const vec3 extents -) { - assertNotNull(body, "body must not be null"); - assertNotNull(position, "position must not be null"); - assertNotNull(extents, "extents must not be null"); - assertTrue(extents[0] > 0.0f, "extents.x must be greater than 0"); - assertTrue(extents[1] > 0.0f, "extents.y must be greater than 0"); - assertTrue(extents[2] > 0.0f, "extents.z must be greater than 0"); - - glm_vec3_copy((float_t *)position, body->position); - glm_vec3_copy((float_t *)extents, body->extents); - glm_vec3_zero(body->velocity); - body->grounded = false; -} - -void physicsBodyGetBounds( - const physicsbody_t *body, vec3 outMin, vec3 outMax -) { - assertNotNull(body, "body must not be null"); - assertNotNull(outMin, "outMin must not be null"); - assertNotNull(outMax, "outMax must not be null"); - - glm_vec3_copy((float_t *)body->position, outMin); - glm_vec3_add((float_t *)body->position, (float_t *)body->extents, outMax); -} diff --git a/src/dusk/rpg/physics/physicsbody.h b/src/dusk/rpg/physics/physicsbody.h deleted file mode 100644 index d0837ad9..00000000 --- a/src/dusk/rpg/physics/physicsbody.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "util/math.h" - -typedef struct physicsbody_s { - // Base/foot position, in raw grid units (same convention as worldpos_t - - // 1.0 equals one tile in X/Y, one Z-layer in Z). Not pre-scaled by - // WORLD_LAYER_HEIGHT, which is a cosmetic render-space value only. - vec3 position; - - // Velocity, in grid units per second, per axis. - vec3 velocity; - - // Full box size, in grid units. The box is anchored at position on all - // three axes and extends in the positive direction - i.e. it occupies - // [position, position + extents) - matching the tile grid's own - // convention that tile n occupies [n, n + 1). - vec3 extents; - - // True if the last physicsWorldStep clamped a downward Z velocity - // against a walkable tile beneath the body. - bool_t grounded; -} physicsbody_t; - -/** - * Initializes a physics body at the given position with the given extents. - * Velocity is zeroed and grounded is set to false. - * - * @param body Pointer to the physics body to initialize. - * @param position The initial position of the body. - * @param extents The size of the body's collision box. - */ -void physicsBodyInit( - physicsbody_t *body, const vec3 position, const vec3 extents -); - -/** - * Computes the world-space min/max bounds of a physics body's collision - * box, from its position and extents. The box is anchored at position - * and extends in the positive direction on every axis, so outMin equals - * position and outMax equals position + extents. - * - * @param body Pointer to the physics body. - * @param outMin Output, set to the box's minimum corner. - * @param outMax Output, set to the box's maximum corner. - */ -void physicsBodyGetBounds( - const physicsbody_t *body, vec3 outMin, vec3 outMax -); diff --git a/src/dusk/rpg/physics/physicsworld.c b/src/dusk/rpg/physics/physicsworld.c deleted file mode 100644 index 56e148b9..00000000 --- a/src/dusk/rpg/physics/physicsworld.c +++ /dev/null @@ -1,423 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "physicsworld.h" -#include "assert/assert.h" -#include "rpg/overworld/map.h" -#include "rpg/overworld/tile.h" -#include "rpg/overworld/tileshape.h" -#include "rpg/overworld/worldpos.h" - -// Tolerance used when converting a float boundary coordinate into a tile -// column/layer index, so that a coordinate sitting exactly on a tile -// boundary is treated as belonging to the tile it is entering/leaving, -// not the neighbour on the far side of the boundary. -#define PHYSICS_EPSILON 0.0001f - -void physicsWorldInit( - physicsworld_t *world, - const float_t gravity, - const float_t terminalVelocity -) { - assertNotNull(world, "world must not be null"); - assertTrue(terminalVelocity > 0.0f, "terminalVelocity must be positive"); - - world->gravity = gravity; - world->terminalVelocity = terminalVelocity; -} - -void physicsWorldStep( - const physicsworld_t *world, - physicsbody_t *body, - const float_t dt, - physicsbody_t * const *others, - const uint32_t othersCount -) { - assertNotNull(world, "world must not be null"); - assertNotNull(body, "body must not be null"); - - body->velocity[2] -= world->gravity * dt; - body->velocity[2] = mathClamp( - body->velocity[2], -world->terminalVelocity, world->terminalVelocity - ); - - physicsWorldResolveAxisX(world, body, dt, others, othersCount); - physicsWorldResolveAxisY(world, body, dt, others, othersCount); - physicsWorldResolveAxisZ(world, body, dt, others, othersCount); -} - -void physicsWorldResolveAxisX( - const physicsworld_t *world, - physicsbody_t *body, - const float_t dt, - physicsbody_t * const *others, - const uint32_t othersCount -) { - assertNotNull(world, "world must not be null"); - assertNotNull(body, "body must not be null"); - - const float_t vx = body->velocity[0]; - if(vx == 0.0f) return; - - vec3 min, max; - physicsBodyGetBounds(body, min, max); - - const worldunit_t yStart = (worldunit_t)floorf(min[1] + PHYSICS_EPSILON); - const worldunit_t yEnd = (worldunit_t)floorf(max[1] - PHYSICS_EPSILON); - const worldunit_t layerZ = - (worldunit_t)floorf(body->position[2] + PHYSICS_EPSILON); - - if(vx > 0.0f) { - const worldunit_t oldColMax = - (worldunit_t)floorf(max[0] - PHYSICS_EPSILON); - const float_t newMax = max[0] + vx * dt; - const worldunit_t newColMax = - (worldunit_t)floorf(newMax - PHYSICS_EPSILON); - - for(worldunit_t col = oldColMax + 1; col <= newColMax; col++) { - bool_t blocked = false; - for(worldunit_t y = yStart; y <= yEnd; y++) { - worldunit_t unusedBase; - tileshape_t unusedShape; - if( - !physicsWorldFindColumnTile( - col, y, layerZ, true, &unusedBase, &unusedShape - ) - ) { - blocked = true; - break; - } - } - - if(blocked) { - body->position[0] = (float_t)col - body->extents[0]; - body->velocity[0] = 0.0f; - physicsWorldResolveBodyOverlap(body, 0, others, othersCount); - return; - } - } - - body->position[0] += vx * dt; - } else { - const worldunit_t oldColMin = - (worldunit_t)floorf(min[0] + PHYSICS_EPSILON); - const float_t newMin = min[0] + vx * dt; - const worldunit_t newColMin = - (worldunit_t)floorf(newMin + PHYSICS_EPSILON); - - for(worldunit_t col = oldColMin - 1; col >= newColMin; col--) { - bool_t blocked = false; - for(worldunit_t y = yStart; y <= yEnd; y++) { - worldunit_t unusedBase; - tileshape_t unusedShape; - if( - !physicsWorldFindColumnTile( - col, y, layerZ, true, &unusedBase, &unusedShape - ) - ) { - blocked = true; - break; - } - } - - if(blocked) { - body->position[0] = (float_t)(col + 1); - body->velocity[0] = 0.0f; - physicsWorldResolveBodyOverlap(body, 0, others, othersCount); - return; - } - } - - body->position[0] += vx * dt; - } - - physicsWorldResolveBodyOverlap(body, 0, others, othersCount); -} - -void physicsWorldResolveAxisY( - const physicsworld_t *world, - physicsbody_t *body, - const float_t dt, - physicsbody_t * const *others, - const uint32_t othersCount -) { - assertNotNull(world, "world must not be null"); - assertNotNull(body, "body must not be null"); - - const float_t vy = body->velocity[1]; - if(vy == 0.0f) return; - - vec3 min, max; - physicsBodyGetBounds(body, min, max); - - const worldunit_t xStart = (worldunit_t)floorf(min[0] + PHYSICS_EPSILON); - const worldunit_t xEnd = (worldunit_t)floorf(max[0] - PHYSICS_EPSILON); - const worldunit_t layerZ = - (worldunit_t)floorf(body->position[2] + PHYSICS_EPSILON); - - if(vy > 0.0f) { - const worldunit_t oldRowMax = - (worldunit_t)floorf(max[1] - PHYSICS_EPSILON); - const float_t newMax = max[1] + vy * dt; - const worldunit_t newRowMax = - (worldunit_t)floorf(newMax - PHYSICS_EPSILON); - - for(worldunit_t row = oldRowMax + 1; row <= newRowMax; row++) { - bool_t blocked = false; - for(worldunit_t x = xStart; x <= xEnd; x++) { - worldunit_t unusedBase; - tileshape_t unusedShape; - if( - !physicsWorldFindColumnTile( - x, row, layerZ, true, &unusedBase, &unusedShape - ) - ) { - blocked = true; - break; - } - } - - if(blocked) { - body->position[1] = (float_t)row - body->extents[1]; - body->velocity[1] = 0.0f; - physicsWorldResolveBodyOverlap(body, 1, others, othersCount); - return; - } - } - - body->position[1] += vy * dt; - } else { - const worldunit_t oldRowMin = - (worldunit_t)floorf(min[1] + PHYSICS_EPSILON); - const float_t newMin = min[1] + vy * dt; - const worldunit_t newRowMin = - (worldunit_t)floorf(newMin + PHYSICS_EPSILON); - - for(worldunit_t row = oldRowMin - 1; row >= newRowMin; row--) { - bool_t blocked = false; - for(worldunit_t x = xStart; x <= xEnd; x++) { - worldunit_t unusedBase; - tileshape_t unusedShape; - if( - !physicsWorldFindColumnTile( - x, row, layerZ, true, &unusedBase, &unusedShape - ) - ) { - blocked = true; - break; - } - } - - if(blocked) { - body->position[1] = (float_t)(row + 1); - body->velocity[1] = 0.0f; - physicsWorldResolveBodyOverlap(body, 1, others, othersCount); - return; - } - } - - body->position[1] += vy * dt; - } - - physicsWorldResolveBodyOverlap(body, 1, others, othersCount); -} - -void physicsWorldResolveAxisZ( - const physicsworld_t *world, - physicsbody_t *body, - const float_t dt, - physicsbody_t * const *others, - const uint32_t othersCount -) { - assertNotNull(world, "world must not be null"); - assertNotNull(body, "body must not be null"); - - vec3 min, max; - physicsBodyGetBounds(body, min, max); - - const worldunit_t xStart = (worldunit_t)floorf(min[0] + PHYSICS_EPSILON); - const worldunit_t xEnd = (worldunit_t)floorf(max[0] - PHYSICS_EPSILON); - const worldunit_t yStart = (worldunit_t)floorf(min[1] + PHYSICS_EPSILON); - const worldunit_t yEnd = (worldunit_t)floorf(max[1] - PHYSICS_EPSILON); - const worldunit_t layer = - (worldunit_t)floorf(body->position[2] + PHYSICS_EPSILON); - const float_t vz = body->velocity[2]; - - // Whether a column not directly at the current layer should still - // count as walkable if its own tile sits one layer above or below - - // each column only ever has one real tile, so this is what lets a - // column just crossed into horizontally (see the +/-1 layer check in - // ResolveAxisX/Y) be recognised here too, whether its tile sits one - // layer above (finishing a climb) or below (starting a descent). - // Gating on horizontal movement (rather than vz's sign) keeps this - // from ever mistaking a genuine ceiling directly above a stationary - // body - e.g. the frame right after being blocked by one, when vz has - // just been zeroed - for ground to snap up onto. - const bool_t movingHorizontally = - body->velocity[0] != 0.0f || body->velocity[1] != 0.0f; - - // Walkability across the whole footprint - every spanned column must - // have a walkable tile (at the current layer, or the +/-1 fallback - // above) for this layer to count as solid, same as before ramps. - bool_t solid = true; - for(worldunit_t x = xStart; x <= xEnd && solid; x++) { - for(worldunit_t y = yStart; y <= yEnd && solid; y++) { - worldunit_t unusedBase; - tileshape_t shape; - if( - !physicsWorldFindColumnTile( - x, y, layer, movingHorizontally, &unusedBase, &shape - ) - ) { - solid = false; - } - } - } - - // Ground/ramp height comes from a single reference column - the - // footprint's center, not the tallest of every spanned column. A 1- - // wide body sitting at a non-integer position always straddles two - // columns (e.g. [1.01, 2.01)); using the tallest of them would yank a - // body barely touching a taller neighbouring column onto its full - // height, rather than the column it's actually standing on. - float_t groundHeight = (float_t)layer; - if(solid) { - const float_t centerX = body->position[0] + body->extents[0] * 0.5f; - const float_t centerY = body->position[1] + body->extents[1] * 0.5f; - const worldunit_t centerCol = - (worldunit_t)floorf(centerX + PHYSICS_EPSILON); - const worldunit_t centerRow = - (worldunit_t)floorf(centerY + PHYSICS_EPSILON); - - worldunit_t tileBase; - tileshape_t shape; - physicsWorldFindColumnTile( - centerCol, centerRow, layer, movingHorizontally, &tileBase, &shape - ); - - const float_t localX = mathClamp(centerX - centerCol, 0.0f, 1.0f); - const float_t localY = mathClamp(centerY - centerRow, 0.0f, 1.0f); - groundHeight = - (float_t)tileBase + tileShapeGetRampHeight(shape, localX, localY); - } - - // Standing on, or embedded below, the ground/ramp surface - snap up to - // it regardless of vertical velocity direction. Gravity only ever - // pulls down, so walking onto a rising ramp needs this explicit lift - // rather than just a fall-clamp. - if(solid && body->position[2] < groundHeight) { - body->position[2] = groundHeight; - if(vz <= 0.0f) body->velocity[2] = 0.0f; - body->grounded = true; - physicsWorldResolveBodyOverlap(body, 2, others, othersCount); - return; - } - - if(vz == 0.0f) return; - - if(vz < 0.0f) { - const float_t newZ = body->position[2] + vz * dt; - if(solid && newZ < groundHeight) { - body->position[2] = groundHeight; - body->velocity[2] = 0.0f; - body->grounded = true; - } else { - body->position[2] = newZ; - body->grounded = false; - } - } else { - const float_t head = max[2]; - const worldunit_t ceilLayer = (worldunit_t)ceilf(head - PHYSICS_EPSILON); - - bool_t ceilingSolid = true; - for(worldunit_t x = xStart; x <= xEnd && ceilingSolid; x++) { - for(worldunit_t y = yStart; y <= yEnd && ceilingSolid; y++) { - const worldpos_t pos = { x, y, ceilLayer }; - if(!tileShapeIsWalkable(mapGetTile(pos).shape)) ceilingSolid = false; - } - } - - const float_t newHead = head + vz * dt; - if(ceilingSolid && newHead > (float_t)ceilLayer) { - body->position[2] = (float_t)ceilLayer - body->extents[2]; - body->velocity[2] = 0.0f; - } else { - body->position[2] += vz * dt; - } - } - - physicsWorldResolveBodyOverlap(body, 2, others, othersCount); -} - -bool_t physicsWorldFindColumnTile( - const worldunit_t x, - const worldunit_t y, - const worldunit_t layer, - const bool_t movingHorizontally, - worldunit_t *outBase, - tileshape_t *outShape -) { - assertNotNull(outBase, "outBase must not be null"); - assertNotNull(outShape, "outShape must not be null"); - - tile_t tile = mapGetTile((worldpos_t){ x, y, layer }); - worldunit_t base = layer; - - if(!tileShapeIsWalkable(tile.shape) && movingHorizontally) { - const tile_t tileAbove = - mapGetTile((worldpos_t){ x, y, (worldunit_t)(layer + 1) }); - if(tileShapeIsWalkable(tileAbove.shape)) { - tile = tileAbove; - base = (worldunit_t)(layer + 1); - } else { - const tile_t tileBelow = - mapGetTile((worldpos_t){ x, y, (worldunit_t)(layer - 1) }); - if(tileShapeIsWalkable(tileBelow.shape)) { - tile = tileBelow; - base = (worldunit_t)(layer - 1); - } - } - } - - *outBase = base; - *outShape = tile.shape; - return tileShapeIsWalkable(tile.shape); -} - -void physicsWorldResolveBodyOverlap( - physicsbody_t *body, - const uint8_t axis, - physicsbody_t * const *others, - const uint32_t othersCount -) { - assertNotNull(body, "body must not be null"); - assertTrue(axis < 3, "axis must be 0, 1 or 2"); - - const float_t v = body->velocity[axis]; - if(v == 0.0f) return; - - for(uint32_t i = 0; i < othersCount; i++) { - physicsbody_t *other = others[i]; - if(other == NULL || other == body) continue; - - vec3 min, max; - physicsBodyGetBounds(body, min, max); - vec3 oMin, oMax; - physicsBodyGetBounds(other, oMin, oMax); - - if(min[0] >= oMax[0] || max[0] <= oMin[0]) continue; - if(min[1] >= oMax[1] || max[1] <= oMin[1]) continue; - if(min[2] >= oMax[2] || max[2] <= oMin[2]) continue; - - if(v > 0.0f) { - body->position[axis] -= max[axis] - oMin[axis]; - } else { - body->position[axis] += oMax[axis] - min[axis]; - } - body->velocity[axis] = 0.0f; - } -} diff --git a/src/dusk/rpg/physics/physicsworld.h b/src/dusk/rpg/physics/physicsworld.h deleted file mode 100644 index c2b84cef..00000000 --- a/src/dusk/rpg/physics/physicsworld.h +++ /dev/null @@ -1,201 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "physicsbody.h" -#include "rpg/overworld/worldpos.h" -#include "rpg/overworld/tileshape.h" - -#define PHYSICS_WORLD_GRAVITY_DEFAULT 20.0f -#define PHYSICS_WORLD_TERMINAL_VELOCITY_DEFAULT 40.0f - -typedef struct physicsworld_s { - // Downward acceleration applied to velocity.z every step, in grid units - // per second squared. - float_t gravity; - - // Maximum magnitude velocity.z may reach while falling, in grid units - // per second. A safety clamp bounding how far a single step can move, - // to limit (not eliminate) the tunneling limitation documented on - // physicsWorldStep. - float_t terminalVelocity; -} physicsworld_t; - -/** - * Initializes a physics world with the given gravity and terminal - * velocity. - * - * @param world Pointer to the physics world to initialize. - * @param gravity Downward acceleration applied every step. - * @param terminalVelocity Maximum falling speed. - */ -void physicsWorldInit( - physicsworld_t *world, - const float_t gravity, - const float_t terminalVelocity -); - -/** - * Advances a body by one timestep: applies gravity, integrates velocity - * into position (semi-implicit Euler), and resolves collisions against - * the tile map one axis at a time (X, then Y, then Z), clamping position - * and zeroing velocity on any axis that hits a tile boundary. After each - * axis's tile resolution, also resolves overlap against any given other - * bodies (see physicsWorldResolveBodyOverlap) - so another body is - * treated as solid too, not just the tile map. - * - * Ramp tiles are walkable at a continuously-varying height (see - * tileShapeGetRampHeight) rather than a flat Z layer - walking across one - * lifts or drops the body smoothly to match the tile's sloped surface, - * snapping upward as needed since gravity alone only ever pulls down. - * - * Known limitations, deliberately out of scope for this very basic pass: - * - Ceiling/head-bump checks are still flat and ramp-unaware - only the - * walking surface accounts for slope, not the underside of a ramp - * above the body. - * - Descending a fast-dropping ramp relies on gravity to close the gap - * each step rather than an explicit "snap down" - imperceptible at - * normal walk/run speeds given the default gravity, but a real - * asymmetry with the "snap up" case. - * - A body whose footprint spans multiple tile columns (unused by any - * entity today - all default to a 1x1 footprint) queries each - * column's ramp height using its own clamped local coordinate - * independently, rather than blending a single surface across tiles. - * - Limited wall/hole distinction - horizontal movement checks the - * body's current Z layer plus one layer above and below (each column - * only ever stores a single tile at one Z layer, so this covers - * stepping onto an adjacent ascending or descending ramp/ledge - * whose own layer differs from the body's current one). A column - * with nothing walkable within one layer either way is still treated - * as a solid wall rather than a deeper edge to fall from. - * - A single step can tunnel through an intervening solid Z layer if - * velocity.z * dt exceeds one grid unit; terminalVelocity bounds this - * but does not eliminate it for very small/thin floors. - * - Body-vs-body resolution only reacts to the axis currently being - * resolved - two bodies already overlapping on every axis, with no - * velocity on any of them, are never proactively separated. - * - * @param world Physics world configuration. - * @param body The body to step. Its position/velocity/grounded fields are - * updated in place. - * @param dt Timestep, in seconds (use DUSK_TIME_STEP for the fixed step). - * @param others Array of pointers to other bodies to treat as solid. - * Pass NULL (with othersCount 0) to resolve against the tile map only. - * @param othersCount Number of entries in others. - */ -void physicsWorldStep( - const physicsworld_t *world, - physicsbody_t *body, - const float_t dt, - physicsbody_t * const *others, - const uint32_t othersCount -); - -/** - * Resolves the body's movement along the X axis for this step, clamping - * position and zeroing velocity.x if a non-walkable column, or another - * body, blocks the move. Declared publicly as an internal step helper, - * not a stable public API on its own. - * - * @param world Physics world configuration. - * @param body The body to resolve. - * @param dt Timestep, in seconds. - * @param others Array of pointers to other bodies to treat as solid. - * @param othersCount Number of entries in others. - */ -void physicsWorldResolveAxisX( - const physicsworld_t *world, - physicsbody_t *body, - const float_t dt, - physicsbody_t * const *others, - const uint32_t othersCount -); - -/** - * Resolves the body's movement along the Y axis for this step. See - * physicsWorldResolveAxisX. - * - * @param world Physics world configuration. - * @param body The body to resolve. - * @param dt Timestep, in seconds. - * @param others Array of pointers to other bodies to treat as solid. - * @param othersCount Number of entries in others. - */ -void physicsWorldResolveAxisY( - const physicsworld_t *world, - physicsbody_t *body, - const float_t dt, - physicsbody_t * const *others, - const uint32_t othersCount -); - -/** - * Resolves the body's movement along the Z axis for this step, clamping - * against walkable tile planes above and below the body and updating - * grounded. See physicsWorldResolveAxisX. - * - * @param world Physics world configuration. - * @param body The body to resolve. - * @param dt Timestep, in seconds. - * @param others Array of pointers to other bodies to treat as solid. - * @param othersCount Number of entries in others. - */ -void physicsWorldResolveAxisZ( - const physicsworld_t *world, - physicsbody_t *body, - const float_t dt, - physicsbody_t * const *others, - const uint32_t othersCount -); - -/** - * Resolves the body's overlap against a set of other bodies along a - * single axis: if the body's bounds fully overlap (on all 3 axes) - * another body's bounds, pushes the body back along the given axis by - * the overlap depth on that axis, in the opposite direction of its - * current velocity on that axis, and zeroes that velocity component. - * Does nothing if the body has no velocity on that axis, since there is - * then no direction to know which way to push it out. - * - * @param body The body to resolve. - * @param axis The axis to resolve overlap on (0 = x, 1 = y, 2 = z). - * @param others Array of pointers to other bodies to check against. - * @param othersCount Number of entries in others. - */ -void physicsWorldResolveBodyOverlap( - physicsbody_t *body, - const uint8_t axis, - physicsbody_t * const *others, - const uint32_t othersCount -); - -/** - * Finds the walkable tile for a single tile column (x, y) near the given - * layer: checks the layer itself first, and - only if movingHorizontally - * is true, so a stationary body never mistakes a genuine ceiling above - * it for ground - falls back to one layer above then one layer below. - * Each column only ever has one real tile, so this lets a column whose - * own tile sits adjacent to the current layer (e.g. finishing a climb - * onto, or starting a descent from, an adjacent ramp/ledge) be found. - * - * @param x Column X coordinate. - * @param y Column Y coordinate. - * @param layer The layer to check first. - * @param movingHorizontally Whether to also try one layer above/below. - * @param outBase Output, set to the layer the found tile actually sits - * at (layer, layer + 1, or layer - 1). - * @param outShape Output, set to the found tile's shape. - * @return true if a walkable tile was found. - */ -bool_t physicsWorldFindColumnTile( - const worldunit_t x, - const worldunit_t y, - const worldunit_t layer, - const bool_t movingHorizontally, - worldunit_t *outBase, - tileshape_t *outShape -); diff --git a/src/dusk/rpg/rpg.c b/src/dusk/rpg/rpg.c deleted file mode 100644 index 2111737a..00000000 --- a/src/dusk/rpg/rpg.c +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpg.h" -#include "entity/entity.h" -#include "rpg/entity/npc/npcpath.h" -#include "rpg/entity/item/entityitem.h" -#include "rpg/overworld/map.h" -#include "rpg/overworld/maparea.h" -#include "rpg/cutscene/cutscenesystem.h" -#include "rpg/cutscene/scene/testcutscene.h" -#include "rpg/item/backpack.h" -#include "ui/rpg/textbox/uitextboxminilist.h" -#include "time/time.h" -#include "rpgcamera.h" -#include "util/memory.h" -#include "util/string.h" -#include "assert/assert.h" -#include "console/console.h" - -#include "ui/rpg/uiemoji.h" - -void rpgTestAreaCallback(entity_t *entity, const uint8_t trigger) { - consolePrint("rpgTestAreaCallback: trigger=%u", trigger); -} - -errorret_t rpgInit(void) { - memoryZero(ENTITIES, sizeof(ENTITIES)); - memoryZero(MAP_AREAS, sizeof(MAP_AREAS)); - - physicsWorldInit( - &ENTITY_PHYSICS_WORLD, - PHYSICS_WORLD_GRAVITY_DEFAULT, - PHYSICS_WORLD_TERMINAL_VELOCITY_DEFAULT - ); - - backpackInit(); - cutsceneSystemInit(); - - errorChain(mapInit()); - - rpgCameraInit(); - // Init world - errorChain(mapPositionSet((chunkpos_t){ 0, 0, 0 })); - - // TEST: Create some entities. - uint8_t entIndex = entityGetAvailable(); - assertTrue(entIndex != 0xFF, "No available entity slots!."); - entity_t *ent = &ENTITIES[entIndex]; - entityInit(ent, ENTITY_TYPE_PLAYER); - entityPositionSet(ent, (worldpos_t){ 10, 2, 0 });// Also assigns the chunk. - RPG_CAMERA.mode = RPG_CAMERA_MODE_FOLLOW_ENTITY; - RPG_CAMERA.followEntity.followEntityId = ent->id; - - mapSpawnEntity(3, (worldpos_t){ 8, 8, 1 }); - - // TEST: Place an item entity. - uint8_t itemEntIndex = entityGetAvailable(); - assertTrue(itemEntIndex != 0xFF, "No available entity slots!."); - entity_t *itemEnt = &ENTITIES[itemEntIndex]; - entityInit(itemEnt, ENTITY_TYPE_ITEM); - entityItemSet(itemEnt, ITEM_ID_POTION, 1); - entityPositionSet(itemEnt, (worldpos_t){ 12, 2, 0 }); - - // TEST: Give the player a starting assortment of items. - backpackAdd(ITEM_ID_POTION, 5); - backpackAdd(ITEM_ID_POTATO, 3); - backpackAdd(ITEM_ID_APPLE, 8); - - // TEST: Create a test map area. - uint8_t areaIndex = mapAreaAdd( - (worldpos_t){ 11, 3, 0 }, - (worldpos_t){ 16, 9, 10 }, - rpgTestAreaCallback, - MAP_AREA_NOTIFY_ALL, - MAP_TRIGGER_ENTER | MAP_TRIGGER_EXIT - ); - assertTrue(areaIndex != 0xFF, "No available map area slots!."); - - // All Good! - errorOk(); -} - -errorret_t rpgUpdate(void) { - #ifdef DUSK_TIME_DYNAMIC - if(TIME.dynamicUpdate) { - errorOk(); - } - #endif - - // TODO: Do not update if the scene is not the map scene? - errorChain(mapUpdate()); - - // Update overworld ents. - entity_t *ent = &ENTITIES[0]; - do { - if(ent->type == ENTITY_TYPE_NULL) continue; - entityUpdate(ent); - } while(++ent < &ENTITIES[ENTITY_COUNT]); - - cutsceneSystemUpdate(); - errorChain(rpgCameraUpdate()); - errorOk(); -} - -errorret_t rpgDispose(void) { - cutsceneSystemDispose(); - errorChain(mapDispose()); - - errorOk(); -} \ No newline at end of file diff --git a/src/dusk/rpg/rpg.h b/src/dusk/rpg/rpg.h deleted file mode 100644 index 9396c326..00000000 --- a/src/dusk/rpg/rpg.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" - -typedef struct { - int32_t nothing; -} rpg_t; - -/** - * Initialize the RPG subsystem. - * - * @return An error code and state. - */ -errorret_t rpgInit(void); - -/** - * Update the RPG subsystem. - * - * @return An error code. - */ -errorret_t rpgUpdate(void); - -/** - * Dispose of the RPG subsystem. - * - * @return An error code. - */ -errorret_t rpgDispose(void); \ No newline at end of file diff --git a/src/dusk/rpg/rpgcamera.c b/src/dusk/rpg/rpgcamera.c deleted file mode 100644 index d3742d88..00000000 --- a/src/dusk/rpg/rpgcamera.c +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "rpgcamera.h" -#include "util/memory.h" -#include "util/random.h" -#include "rpg/entity/entity.h" -#include "rpg/overworld/map.h" -#include "assert/assert.h" -#include "time/time.h" - -#include "display/screen/screen.h" - -static const float_t RPG_CAMERA_SHAKE_AMOUNTS[] = { - 0.0f, 0.5f, 1.0f, 2.0f, 3.0f -}; - -rpgcamera_t RPG_CAMERA; - -void rpgCameraInit(void) { - memoryZero(&RPG_CAMERA, sizeof(rpgcamera_t)); - RPG_CAMERA.projectionDirty = true; -} - -void rpgCameraShake(uint8_t amount, float_t duration) { - assertTrue(amount <= 4, "Camera shake amount must be between 0 and 4"); - RPG_CAMERA.shakeAmount = RPG_CAMERA_SHAKE_AMOUNTS[amount]; - RPG_CAMERA.shakeDuration = duration; - RPG_CAMERA.shakeTime = 0.0f; -} - -void rpgCameraGetPosition(vec3 out) { - switch(RPG_CAMERA.mode) { - case RPG_CAMERA_MODE_FREE: - glm_vec3_copy(RPG_CAMERA.free, out); - return; - - case RPG_CAMERA_MODE_FOLLOW_ENTITY: { - entity_t *entity = &ENTITIES[RPG_CAMERA.followEntity.followEntityId]; - if(entity->type == ENTITY_TYPE_NULL) { - glm_vec3_zero(out); - return; - } - glm_vec3_copy(entity->renderPosition, out); - return; - } - - default: - assertUnreachable("Invalid RPG camera mode"); - } -} - -void rpgCameraUpdateProjection(void) { - #ifdef DUSK_DISPLAY_WIDTH - if(!RPG_CAMERA.projectionDirty) return; - RPG_CAMERA.projectionDirty = false; - #endif - - glm_perspective( - glm_rad(RPG_CAMERA_FOV), - SCREEN.aspect, - 0.1f, - 100.0f, - RPG_CAMERA.projection - ); -} - -void rpgCameraUpdateEye(void) { - float_t fov = glm_rad(RPG_CAMERA_FOV); - float_t pixelsPerUnit = TILE_SIZE_PIXELS; - float_t worldH = (float_t)(SCREEN.height / SCREEN.scale3d) / pixelsPerUnit; - float_t z = (worldH * 0.5f) / tanf(fov * 0.5f); - float_t offset = -24.0f * (worldH / TILE_SIZE_PIXELS); - - vec3 target; - rpgCameraGetPosition(target); - glm_vec3_add(target, (vec3){ 0.5f, 0.5f, 0.5f }, target); - - if(RPG_CAMERA.shakeTime < RPG_CAMERA.shakeDuration) { - float_t t = 1.0f - (RPG_CAMERA.shakeTime / RPG_CAMERA.shakeDuration); - float_t magnitude = RPG_CAMERA.shakeAmount * t; - target[0] += randomFloat(-magnitude, magnitude); - target[2] += randomFloat(-magnitude, magnitude); - } - - glm_lookat( - (vec3){ target[0], target[1] + offset, target[2] + z }, - target, - (vec3){ 0, 1, 0 }, // up - RPG_CAMERA.eye - ); -} - -void rpgCameraToScreen(vec3 worldPos, vec2 out) { - mat4 viewProj; - glm_mat4_mul(RPG_CAMERA.projection, RPG_CAMERA.eye, viewProj); - - vec4 viewport = { - 0.0f, 0.0f, (float_t)SCREEN.width, (float_t)SCREEN.height - }; - vec3 window; - glm_project(worldPos, viewProj, viewport, window); - - out[0] = window[0]; - out[1] = (float_t)SCREEN.height - window[1]; -} - -errorret_t rpgCameraUpdate(void) { - if(RPG_CAMERA.shakeTime < RPG_CAMERA.shakeDuration) { - RPG_CAMERA.shakeTime += TIME.delta; - } - - if(!mapIsLoaded()) errorOk(); - - vec3 pos; - rpgCameraGetPosition(pos); - - chunkpos_t chunkPos = { - .x = (chunkunit_t)floorf(pos[0] / CHUNK_WIDTH), - .y = (chunkunit_t)floorf(pos[1] / CHUNK_HEIGHT), - .z = (chunkunit_t)floorf(pos[2] / WORLD_LAYER_HEIGHT / CHUNK_DEPTH) - }; - - errorChain(mapPositionSet((chunkpos_t){ - .x = chunkPos.x - (MAP_CHUNK_WIDTH / 2), - .y = chunkPos.y - (MAP_CHUNK_HEIGHT / 2), - .z = chunkPos.z - (MAP_CHUNK_DEPTH / 2) - })); - errorOk(); -} \ No newline at end of file diff --git a/src/dusk/rpg/rpgcamera.h b/src/dusk/rpg/rpgcamera.h deleted file mode 100644 index 12742ff7..00000000 --- a/src/dusk/rpg/rpgcamera.h +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "rpg/overworld/worldpos.h" -#include "error/error.h" - -#define RPG_CAMERA_FOV 35.0f - -typedef enum { - RPG_CAMERA_MODE_FREE, - RPG_CAMERA_MODE_FOLLOW_ENTITY, -} rpgcameramode_t; - -typedef struct { - rpgcameramode_t mode; - - union { - vec3 free; - - struct { - uint8_t followEntityId; - } followEntity; - }; - - mat4 eye; - mat4 projection; - bool_t projectionDirty; - - float_t shakeAmount; - float_t shakeDuration; - float_t shakeTime; -} rpgcamera_t; - -extern rpgcamera_t RPG_CAMERA; - -/** - * Initializes the RPG camera. - */ -void rpgCameraInit(void); - -/** - * Gets the RPG camera's position. - * - * @param out Output vec3 filled with the camera world position. - */ -void rpgCameraGetPosition(vec3 out); - -/** - * Updates the RPG camera. - * - * @return An error code. - */ -errorret_t rpgCameraUpdate(void); - -/** - * Recomputes the camera projection matrix and stores it in - * RPG_CAMERA.projection. On platforms with a fixed display size the - * matrix is computed once and cached; on dynamic-display platforms it - * is recomputed every call. - */ -void rpgCameraUpdateProjection(void); - -/** - * Recomputes the camera eye/view matrix from the camera's current mode - * and position, and stores it in RPG_CAMERA.eye. Unlike the projection - * matrix this is never cached, since the camera position can change - * every frame. - */ -void rpgCameraUpdateEye(void); - -/** - * Shakes the RPG camera, randomly offsetting its position by a - * decreasing amount over the given duration. - * - * @param amount Shake strength from 0 (no shake) to 4 (three tiles). - * 1 is half a tile, 2 is a full tile, 3 is two tiles, and 4 is three - * tiles. - * @param duration How long the shake lasts, in seconds. - */ -void rpgCameraShake(uint8_t amount, float_t duration); - -/** - * Converts a world-space position to screen-space pixel coordinates, - * using the camera's current eye and projection matrices. - * - * @param worldPos The world-space position to convert. - * @param out Output vec2 filled with the screen-space pixel position. - */ -void rpgCameraToScreen(vec3 worldPos, vec2 out); \ No newline at end of file