diff --git a/cmake/targets/linux.cmake b/cmake/targets/linux.cmake index a6242fb7..1d67e95b 100644 --- a/cmake/targets/linux.cmake +++ b/cmake/targets/linux.cmake @@ -36,7 +36,7 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC # DUSK_OPENGL_LEGACY DUSK_LINUX DUSK_DISPLAY_SIZE_DYNAMIC - DUSK_DISPLAY_WIDTH_DEFAULT=640 + DUSK_DISPLAY_WIDTH_DEFAULT=854 DUSK_DISPLAY_HEIGHT_DEFAULT=480 DUSK_DISPLAY_SCREEN_HEIGHT=240 DUSK_INPUT_KEYBOARD diff --git a/src/dusk/rpg/cutscene/item/item/cutsceneitemgive.c b/src/dusk/rpg/cutscene/item/item/cutsceneitemgive.c index d65d0b36..c23a868e 100644 --- a/src/dusk/rpg/cutscene/item/item/cutsceneitemgive.c +++ b/src/dusk/rpg/cutscene/item/item/cutsceneitemgive.c @@ -6,25 +6,14 @@ */ #include "rpg/cutscene/item/cutsceneitem.h" -#include "rpg/item/backpack.h" +#include "rpg/item/itemgive.h" #include "ui/rpg/uitextboxmain.h" -#include "util/string.h" void cutsceneItemGiveStart( const cutsceneitem_t *item, cutsceneitemdata_t *data ) { - backpackAdd(item->itemGive.item, item->itemGive.quantity); - - char_t msg[CUTSCENE_TEXT_MAX_CHARS]; - stringFormat( - msg, - CUTSCENE_TEXT_MAX_CHARS - 1, - "Received %s x%u", - ITEMS[item->itemGive.item].name, - (uint32_t)item->itemGive.quantity - ); - uiTextboxMainSetText(msg); + itemGive(item->itemGive.item, item->itemGive.quantity); } bool_t cutsceneItemGiveUpdate( diff --git a/src/dusk/rpg/entity/CMakeLists.txt b/src/dusk/rpg/entity/CMakeLists.txt index b6e03b19..da55a228 100644 --- a/src/dusk/rpg/entity/CMakeLists.txt +++ b/src/dusk/rpg/entity/CMakeLists.txt @@ -15,4 +15,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} add_subdirectory(anim) 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/entitytype.h b/src/dusk/rpg/entity/entitytype.h index e30c236b..6b7d2769 100644 --- a/src/dusk/rpg/entity/entitytype.h +++ b/src/dusk/rpg/entity/entitytype.h @@ -8,12 +8,14 @@ #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; @@ -23,6 +25,7 @@ typedef uint8_t entitytype_t; typedef union { player_t player; npc_t npc; + entityitem_t item; } entitytypedata_t; typedef struct { @@ -50,5 +53,10 @@ static const entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = { [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/interact/entityinteract.c b/src/dusk/rpg/entity/interact/entityinteract.c index 45db53bc..6601b411 100644 --- a/src/dusk/rpg/entity/interact/entityinteract.c +++ b/src/dusk/rpg/entity/interact/entityinteract.c @@ -40,6 +40,14 @@ void entityInteractWith(entity_t *player, entity_t *target) { // 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; diff --git a/src/dusk/rpg/entity/item/CMakeLists.txt b/src/dusk/rpg/entity/item/CMakeLists.txt new file mode 100644 index 00000000..6834d19b --- /dev/null +++ b/src/dusk/rpg/entity/item/CMakeLists.txt @@ -0,0 +1,10 @@ +# 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 new file mode 100644 index 00000000..dfcbec9a --- /dev/null +++ b/src/dusk/rpg/entity/item/entityitem.c @@ -0,0 +1,44 @@ +/** + * 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/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 new file mode 100644 index 00000000..1485f1fa --- /dev/null +++ b/src/dusk/rpg/entity/item/entityitem.h @@ -0,0 +1,56 @@ +/** + * 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/item/CMakeLists.txt b/src/dusk/rpg/item/CMakeLists.txt index b8303a06..a5b03b66 100644 --- a/src/dusk/rpg/item/CMakeLists.txt +++ b/src/dusk/rpg/item/CMakeLists.txt @@ -8,6 +8,7 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC inventory.c backpack.c + itemgive.c ) # Item Definitions diff --git a/src/dusk/rpg/item/itemgive.c b/src/dusk/rpg/item/itemgive.c new file mode 100644 index 00000000..ac8eec1a --- /dev/null +++ b/src/dusk/rpg/item/itemgive.c @@ -0,0 +1,25 @@ +/** + * 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 "ui/rpg/uitextboxmain.h" +#include "util/string.h" + +void itemGive(const itemid_t item, const uint8_t quantity) { + backpackAdd(item, quantity); + + char_t msg[ITEM_GIVE_MESSAGE_MAX_CHARS]; + stringFormat( + msg, + ITEM_GIVE_MESSAGE_MAX_CHARS - 1, + "Received %s x%u", + ITEMS[item].name, + (uint32_t)quantity + ); + uiTextboxMainSetText(msg); +} diff --git a/src/dusk/rpg/item/itemgive.h b/src/dusk/rpg/item/itemgive.h new file mode 100644 index 00000000..5fa02b91 --- /dev/null +++ b/src/dusk/rpg/item/itemgive.h @@ -0,0 +1,20 @@ +/** + * 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/rpg.c b/src/dusk/rpg/rpg.c index 37c68c54..351b4a6b 100644 --- a/src/dusk/rpg/rpg.c +++ b/src/dusk/rpg/rpg.c @@ -8,6 +8,7 @@ #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" @@ -48,6 +49,14 @@ errorret_t rpgInit(void) { 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: Create a test map area. uint8_t areaIndex = mapAreaAdd( (worldpos_t){ 11, 3, 0 },