diff --git a/src/dusk/rpg/entity/CMakeLists.txt b/src/dusk/rpg/entity/CMakeLists.txt index 7aa0960c..b6e03b19 100644 --- a/src/dusk/rpg/entity/CMakeLists.txt +++ b/src/dusk/rpg/entity/CMakeLists.txt @@ -14,4 +14,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} add_subdirectory(anim) add_subdirectory(interact) -add_subdirectory(npc) \ No newline at end of file +add_subdirectory(npc) +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 index d7a35abd..2106ac99 100644 --- a/src/dusk/rpg/entity/entity.c +++ b/src/dusk/rpg/entity/entity.c @@ -10,7 +10,6 @@ #include "util/memory.h" #include "time/time.h" #include "util/math.h" -#include "util/random.h" #include "rpg/overworld/map.h" #include "rpg/overworld/chunk.h" #include "rpg/overworld/tile.h" @@ -28,7 +27,7 @@ void entityInit(entity_t *entity, const entitytype_t type) { memoryZero(entity, sizeof(entity_t)); entity->id = (uint8_t)(entity - ENTITIES); - entity->globalId = (entityglobalid_t)randomInt(0, ENTITY_GLOBAL_ID_START); + entity->globalId = ENTITY_GLOBAL_ID_NULL; entity->type = type; entity->chunkIndex = 0xFF; diff --git a/src/dusk/rpg/entity/entity.h b/src/dusk/rpg/entity/entity.h index 500b73ff..8cbfee13 100644 --- a/src/dusk/rpg/entity/entity.h +++ b/src/dusk/rpg/entity/entity.h @@ -16,8 +16,9 @@ typedef struct map_s map_t; typedef uint16_t entityglobalid_t; -#define ENTITY_GLOBAL_ID_START 1000 -#define ENTITY_GLOBAL_ID_PLAYER UINT16_MAX +#define ENTITY_GLOBAL_ID_NULL 0 +#define ENTITY_GLOBAL_ID_START 1 +#define ENTITY_GLOBAL_ID_PLAYER 1 typedef struct entity_s { uint8_t id; diff --git a/src/dusk/rpg/entity/global/CMakeLists.txt b/src/dusk/rpg/entity/global/CMakeLists.txt new file mode 100644 index 00000000..9b35b01d --- /dev/null +++ b/src/dusk/rpg/entity/global/CMakeLists.txt @@ -0,0 +1,9 @@ +# 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 new file mode 100644 index 00000000..10d51d60 --- /dev/null +++ b/src/dusk/rpg/entity/global/entityglobal.h @@ -0,0 +1,17 @@ +/** + * 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 new file mode 100644 index 00000000..73913e45 --- /dev/null +++ b/src/dusk/rpg/entity/global/entityglobaldefs.h @@ -0,0 +1,44 @@ +/** + * 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 new file mode 100644 index 00000000..2207763c --- /dev/null +++ b/src/dusk/rpg/entity/global/entitygloballist.h @@ -0,0 +1,30 @@ +/** + * 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/overworld/map.c b/src/dusk/rpg/overworld/map.c index ea803ca0..ba302deb 100644 --- a/src/dusk/rpg/overworld/map.c +++ b/src/dusk/rpg/overworld/map.c @@ -12,8 +12,8 @@ #include "asset/loader/assetloader.h" #include "console/console.h" #include "event/event.h" -#include "rpg/entity/entity.h" #include "util/string.h" +#include "rpg/entity/global/entityglobal.h" map_t MAP; @@ -268,6 +268,49 @@ bool_t mapGetWalkableZNear( 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" + ); + + // 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); + + // Assign to the local chunk. + chunkpos_t cp; + worldPosToChunkPos(&entity->position, &cp); + chunkindex_t ci = mapGetChunkIndexAt(cp); + if(ci != -1) entitySetChunk(entity, (uint8_t)ci); + + // 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_LOADED_CHUNK_COUNT; i++) { diff --git a/src/dusk/rpg/overworld/map.h b/src/dusk/rpg/overworld/map.h index 5a4cf0c1..c56cecd3 100644 --- a/src/dusk/rpg/overworld/map.h +++ b/src/dusk/rpg/overworld/map.h @@ -8,6 +8,7 @@ #pragma once #include "error/error.h" #include "rpg/overworld/chunk.h" +#include "rpg/entity/entity.h" #define MAP_FILE_PATH_MAX 128 @@ -36,18 +37,6 @@ errorret_t mapInit(); */ bool_t mapIsLoaded(); -/** - * Loads a map from the given file path. - * - * @param path The file path. - * @param position The initial chunk position. - * @return An error code. - */ -// errorret_t mapLoad( -// const char_t *path, -// const chunkpos_t position -// ); - /** * Updates the map. * @@ -150,4 +139,19 @@ bool_t mapGetWalkableZNear( 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/rpg.c b/src/dusk/rpg/rpg.c index fe0869ef..279215c3 100644 --- a/src/dusk/rpg/rpg.c +++ b/src/dusk/rpg/rpg.c @@ -45,25 +45,7 @@ errorret_t rpgInit(void) { if(ci != -1) entitySetChunk(ent, (uint8_t)ci); } - uint8_t npcIndex = entityGetAvailable(); - entity_t *npc = &ENTITIES[npcIndex]; - entityInit(npc, ENTITY_TYPE_NPC); - npc->globalId = ENTITY_GLOBAL_ID_START; - npcSetMoveType(npc, NPC_MOVE_TYPE_PATH); - entityPositionSet(npc, (worldpos_t){ 8, 8, 1 }); - npc->interact.type = ENTITY_INTERACT_CUTSCENE; - npc->interact.data.cutscene = CUTSCENE_REFERENCE(TEST_TWO); - { - chunkpos_t cp; - worldPosToChunkPos(&npc->position, &cp); - chunkindex_t ci = mapGetChunkIndexAt(cp); - if(ci != -1) entitySetChunk(npc, (uint8_t)ci); - } - - npcPathAddNode(&npc->data.npc, (worldpos_t){ 4, 4, 0 }); - npcPathAddNode(&npc->data.npc, (worldpos_t){ 10, 10, 1 }); - npcPathAddNode(&npc->data.npc, (worldpos_t){ 4, 4, 0 }); - npcPathAddNode(&npc->data.npc, (worldpos_t){ 10, 10, 1 }); + mapSpawnEntity(3, (worldpos_t){ 8, 8, 1 }); // All Good! errorOk();