Add inventory.
Build Dusk / run-tests (push) Successful in 2m12s
Build Dusk / build-linux (push) Successful in 1m49s
Build Dusk / build-psp (push) Successful in 1m52s

This commit is contained in:
YourWishes committed 2026-01-06 07:47:16 -06:00
1 parent 024ace1078
commit af5bf987c8
91 files changed
+1108 -139

No files matched your search

+2 -2
View File
@@ -13,8 +13,8 @@ option(ENABLE_TESTS "Enable tests" ON)
# Set target system # Set target system
if(NOT DEFINED DUSK_TARGET_SYSTEM) if(NOT DEFINED DUSK_TARGET_SYSTEM)
# set(DUSK_TARGET_SYSTEM "linux") set(DUSK_TARGET_SYSTEM "linux")
set(DUSK_TARGET_SYSTEM "psp") # set(DUSK_TARGET_SYSTEM "psp")
endif() endif()
# Prep cache # Prep cache
File renamed without changes.
@@ -7,7 +7,7 @@
#pragma once #pragma once
#include "error/error.h" #include "error/error.h"
#include "rpg/world/chunk.h" #include "rpg/overworld/chunk.h"
typedef struct assetcustom_s assetcustom_t; typedef struct assetcustom_s assetcustom_t;
File renamed without changes.
@@ -7,7 +7,7 @@
#pragma once #pragma once
#include "error/error.h" #include "error/error.h"
#include "rpg/world/map.h" #include "rpg/overworld/map.h"
#include "display/mesh/mesh.h" #include "display/mesh/mesh.h"
/** /**
@@ -9,7 +9,7 @@
#include "script/scriptcontext.h" #include "script/scriptcontext.h"
#include "debug/debug.h" #include "debug/debug.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "rpg/world/map.h" #include "rpg/overworld/map.h"
int moduleMapLoad(lua_State *L) { int moduleMapLoad(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL"); assertNotNull(L, "Lua state cannot be NULL");
@@ -14,4 +14,4 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
# Subdirs # Subdirs
add_subdirectory(cutscene) add_subdirectory(cutscene)
add_subdirectory(entity) add_subdirectory(entity)
add_subdirectory(world) add_subdirectory(overworld)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -11,7 +11,7 @@
#include "time/time.h" #include "time/time.h"
#include "util/math.h" #include "util/math.h"
#include "rpg/cutscene/cutscenemode.h" #include "rpg/cutscene/cutscenemode.h"
#include "rpg/world/map.h" #include "rpg/overworld/map.h"
entity_t ENTITIES[ENTITY_COUNT]; entity_t ENTITIES[ENTITY_COUNT];
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -6,7 +6,7 @@
*/ */
#pragma once #pragma once
#include "rpg/world/worldpos.h" #include "rpg/overworld/worldpos.h"
typedef enum { typedef enum {
ENTITY_DIR_UP = ENTITY_DIR_NORTH, ENTITY_DIR_UP = ENTITY_DIR_NORTH,
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -6,7 +6,7 @@
*/ */
#pragma once #pragma once
#include "rpg/world/tile.h" #include "rpg/overworld/tile.h"
#include "worldpos.h" #include "worldpos.h"
#include "display/mesh/quad.h" #include "display/mesh/quad.h"
File renamed without changes.
@@ -6,7 +6,7 @@
*/ */
#pragma once #pragma once
#include "rpg/world/chunk.h" #include "rpg/overworld/chunk.h"
#define MAP_FILE_PATH_MAX 128 #define MAP_FILE_PATH_MAX 128
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1 -1
View File
@@ -7,7 +7,7 @@
#include "rpg.h" #include "rpg.h"
#include "entity/entity.h" #include "entity/entity.h"
#include "rpg/world/map.h" #include "rpg/overworld/map.h"
#include "rpg/cutscene/cutscenesystem.h" #include "rpg/cutscene/cutscenesystem.h"
#include "time/time.h" #include "time/time.h"
#include "rpgcamera.h" #include "rpgcamera.h"
View File
File renamed without changes.
@@ -8,7 +8,7 @@
#include "rpgcamera.h" #include "rpgcamera.h"
#include "util/memory.h" #include "util/memory.h"
#include "rpg/entity/entity.h" #include "rpg/entity/entity.h"
#include "rpg/world/map.h" #include "rpg/overworld/map.h"
#include "assert/assert.h" #include "assert/assert.h"
rpgcamera_t RPG_CAMERA; rpgcamera_t RPG_CAMERA;
@@ -6,7 +6,7 @@
*/ */
#pragma once #pragma once
#include "rpg/world/worldpos.h" #include "rpg/overworld/worldpos.h"
#include "error/error.h" #include "error/error.h"
typedef enum { typedef enum {
File renamed without changes.
File renamed without changes.
+13
View File
@@ -0,0 +1,13 @@
# 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
scenemanager.c
)
# Subdirs
add_subdirectory(scene)
+24
View File
@@ -0,0 +1,24 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include "error/error.h"
#include "display/color.h"
#define SCENE_FLAG_INITIALIZED (1 << 0)
typedef struct scenedata_s scenedata_t;
typedef struct {
const char_t *name;
errorret_t (*init)(scenedata_t *data);
void (*update)(scenedata_t *data);
void (*render)(scenedata_t *data);
void (*dispose)(scenedata_t *data);
uint8_t flags;
} scene_t;
File renamed without changes.
@@ -11,7 +11,7 @@
#include "assert/assert.h" #include "assert/assert.h"
#include "asset/asset.h" #include "asset/asset.h"
#include "rpg/entity/entity.h" #include "rpg/entity/entity.h"
#include "rpg/world/map.h" #include "rpg/overworld/map.h"
#include "display/screen.h" #include "display/screen.h"
#include "rpg/rpgcamera.h" #include "rpg/rpgcamera.h"
#include "util/memory.h" #include "util/memory.h"
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -9,11 +9,4 @@
#include "scene/scene.h" #include "scene/scene.h"
#include "scene/scene/scenetest.h" #include "scene/scene/scenetest.h"
#include "scene/scene/scenemap.h" #include "scene/scene/scenemap.h"
typedef struct scenedata_s {
union {
scenetest_t sceneTest;
scenemap_t sceneMap;
};
} scenedata_t;
File renamed without changes.
@@ -15,7 +15,6 @@ typedef struct {
scene_t *current; scene_t *current;
scene_t *scenes[SCENE_MANAGER_SCENE_COUNT_MAX]; scene_t *scenes[SCENE_MANAGER_SCENE_COUNT_MAX];
uint8_t sceneCount; uint8_t sceneCount;
scenedata_t sceneData;
} scenemanager_t; } scenemanager_t;
extern scenemanager_t SCENE_MANAGER; extern scenemanager_t SCENE_MANAGER;
File renamed without changes.
+1 -2
View File
@@ -1,7 +1,6 @@
module('platform') module('platform')
module('input') module('input')
module('scene') module('scene')
module('map')
-- Default Input bindings. -- Default Input bindings.
if PLATFORM == "psp" then if PLATFORM == "psp" then
@@ -39,4 +38,4 @@ else
end end
sceneSet('map') sceneSet('map')
mapLoad('map/testmap/testmap.dmf') -- mapLoad('map/testmap/testmap.dmf')
+2 -1
View File
@@ -56,8 +56,9 @@ add_subdirectory(display)
add_subdirectory(engine) add_subdirectory(engine)
add_subdirectory(error) add_subdirectory(error)
add_subdirectory(input) add_subdirectory(input)
add_subdirectory(item)
add_subdirectory(locale) add_subdirectory(locale)
add_subdirectory(rpg) # add_subdirectory(rpg)
add_subdirectory(scene) add_subdirectory(scene)
add_subdirectory(script) add_subdirectory(script)
add_subdirectory(thread) add_subdirectory(thread)
-17
View File
@@ -9,8 +9,6 @@
#include "type/assetpaletteimage.h" #include "type/assetpaletteimage.h"
#include "type/assetalphaimage.h" #include "type/assetalphaimage.h"
#include "type/assetlanguage.h" #include "type/assetlanguage.h"
#include "type/assetmap.h"
#include "type/assetchunk.h"
#include "type/assetscript.h" #include "type/assetscript.h"
#include <zip.h> #include <zip.h>
@@ -20,8 +18,6 @@ typedef enum {
ASSET_TYPE_PALETTE_IMAGE, ASSET_TYPE_PALETTE_IMAGE,
ASSET_TYPE_ALPHA_IMAGE, ASSET_TYPE_ALPHA_IMAGE,
ASSET_TYPE_LANGUAGE, ASSET_TYPE_LANGUAGE,
ASSET_TYPE_MAP,
ASSET_TYPE_CHUNK,
ASSET_TYPE_SCRIPT, ASSET_TYPE_SCRIPT,
ASSET_TYPE_COUNT, ASSET_TYPE_COUNT,
@@ -72,19 +68,6 @@ static const assettypedef_t ASSET_TYPE_DEFINITIONS[ASSET_TYPE_COUNT] = {
.custom = assetLanguageHandler .custom = assetLanguageHandler
}, },
[ASSET_TYPE_MAP] = {
.header = "DMF",
.loadStrategy = ASSET_LOAD_STRAT_ENTIRE,
.dataSize = sizeof(1),
.entire = assetMapLoad
},
[ASSET_TYPE_CHUNK] = {
.header = "DCF",
.loadStrategy = ASSET_LOAD_STRAT_CUSTOM,
.custom = assetChunkLoad
},
[ASSET_TYPE_SCRIPT] = { [ASSET_TYPE_SCRIPT] = {
.header = "DSF", .header = "DSF",
.loadStrategy = ASSET_LOAD_STRAT_CUSTOM, .loadStrategy = ASSET_LOAD_STRAT_CUSTOM,
-2
View File
@@ -9,7 +9,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
assetalphaimage.c assetalphaimage.c
assetpaletteimage.c assetpaletteimage.c
assetlanguage.c assetlanguage.c
assetmap.c
assetchunk.c
assetscript.c assetscript.c
) )
+1 -1
View File
@@ -45,7 +45,7 @@ typedef color4b_t color_t;
#define color4b(r, g, b, a) ((color4b_t){r, g, b, a}) #define color4b(r, g, b, a) ((color4b_t){r, g, b, a})
#define color(r, g, b, a) ((color_t){r, g, b, a}) #define color(r, g, b, a) ((color_t){r, g, b, a})
#define color_hex(hex) color( \ #define colorHex(hex) color( \
((hex >> 24) & 0xFF), \ ((hex >> 24) & 0xFF), \
((hex >> 16) & 0xFF), \ ((hex >> 16) & 0xFF), \
((hex >> 8) & 0xFF), \ ((hex >> 8) & 0xFF), \
+4 -2
View File
@@ -8,7 +8,7 @@
#include "display/display.h" #include "display/display.h"
#include "engine/engine.h" #include "engine/engine.h"
#include "display/framebuffer.h" #include "display/framebuffer.h"
#include "scene/scenemanager.h" #include "scene/scene.h"
#include "display/spritebatch.h" #include "display/spritebatch.h"
#include "display/mesh/quad.h" #include "display/mesh/quad.h"
#include "display/screen.h" #include "display/screen.h"
@@ -120,7 +120,9 @@ errorret_t displayUpdate(void) {
FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH, FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH,
COLOR_CORNFLOWER_BLUE COLOR_CORNFLOWER_BLUE
); );
sceneManagerRender(); sceneRender();
// Render UI
uiRender(); uiRender();
// Finish up // Finish up
+8 -8
View File
@@ -11,10 +11,10 @@
#include "input/input.h" #include "input/input.h"
#include "locale/localemanager.h" #include "locale/localemanager.h"
#include "display/display.h" #include "display/display.h"
#include "scene/scenemanager.h" #include "scene/scene.h"
#include "asset/asset.h" #include "asset/asset.h"
#include "ui/ui.h" #include "ui/ui.h"
#include "rpg/rpg.h" // #include "rpg/rpg.h"
#include "script/scriptmanager.h" #include "script/scriptmanager.h"
#include "debug/debug.h" #include "debug/debug.h"
@@ -35,8 +35,8 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
errorChain(scriptManagerInit()); errorChain(scriptManagerInit());
errorChain(displayInit()); errorChain(displayInit());
errorChain(uiInit()); errorChain(uiInit());
errorChain(rpgInit()); // errorChain(rpgInit());
errorChain(sceneManagerInit()); errorChain(sceneInit());
// Run the initial script. // Run the initial script.
scriptcontext_t ctx; scriptcontext_t ctx;
@@ -51,9 +51,9 @@ errorret_t engineUpdate(void) {
timeUpdate(); timeUpdate();
inputUpdate(); inputUpdate();
errorChain(rpgUpdate()); // errorChain(rpgUpdate());
uiUpdate(); uiUpdate();
sceneManagerUpdate(); sceneUpdate();
errorChain(displayUpdate()); errorChain(displayUpdate());
if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false; if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false;
@@ -67,8 +67,8 @@ void engineExit(void) {
errorret_t engineDispose(void) { errorret_t engineDispose(void) {
localeManagerDispose(); localeManagerDispose();
sceneManagerDispose(); // sceneManagerDispose();
rpgDispose(); // rpgDispose();
uiDispose(); uiDispose();
errorChain(displayDispose()); errorChain(displayDispose());
assetDispose(); assetDispose();
+11
View File
@@ -0,0 +1,11 @@
# 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
)
+165
View File
@@ -0,0 +1,165 @@
/**
* 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 "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;
// Zero item ids.
memoryZero(inventory->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));
// 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;
}
+103
View File
@@ -0,0 +1,103 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "item.h"
#define ITEM_STACK_QUANTITY_MAX UINT8_MAX
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);
+19
View File
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "item.h"
const item_t ITEMS[] = {
[ITEM_ID_NULL] = {
.type = ITEM_TYPE_NULL
},
// Potion
[ITEM_ID_POTION] = {
.type = ITEM_TYPE_MEDICINE
},
};
+21
View File
@@ -0,0 +1,21 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "itemtype.h"
typedef struct {
itemtype_t type;
} item_t;
typedef uint8_t itemid_t;
#define ITEM_ID_NULL 0
#define ITEM_ID_POTION 1
#define ITEM_ID_POTATO 2
extern const item_t ITEMS[];
+19
View File
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef enum {
ITEM_TYPE_NULL,
ITEM_TYPE_MEDICINE,
ITEM_TYPE_INGREDIENT,
ITEM_TYPE_KEYITEM,
ITEM_TYPE_COUNT
} itemtype_t;
+2 -5
View File
@@ -6,8 +6,5 @@
# Sources # Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME} target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC PUBLIC
scenemanager.c scene.c
) )
# Subdirs
add_subdirectory(scene)
+84
View File
@@ -0,0 +1,84 @@
// Copyright (c) 2026 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "scene.h"
#include "assert/assert.h"
#include "util/string.h"
const scene_t SCENES[] = {
};
uint_fast8_t SCENE_CURRENT = 0xFF;
errorret_t sceneInit(void) {
// Initialize the scene subsystem here
errorOk();
}
void sceneUpdate(void) {
const scene_t *current = sceneGetCurrent();
if(current && current->update) {
current->update();
}
}
void sceneRender(void) {
const scene_t *current = sceneGetCurrent();
if(current && current->render) {
current->render();
}
}
void sceneDispose(void) {
const scene_t *current = sceneGetCurrent();
if(current && current->dispose) {
current->dispose();
}
}
errorret_t sceneSet(const scene_t *scene) {
sceneDispose();
if(scene) {
SCENE_CURRENT = (uint_fast8_t)(scene - SCENES);
assertTrue(
SCENE_CURRENT < sizeof(SCENES) / sizeof(scene_t),
"Invalid scene index."
);
if(scene->init) {
errorret_t err = scene->init();
if(err.code != ERROR_OK) SCENE_CURRENT = 0xFF;
errorChain(err);
}
} else {
SCENE_CURRENT = 0xFF;
}
errorOk();
}
const scene_t* sceneGetCurrent(void) {
if(SCENE_CURRENT == 0xFF) return NULL;
assertTrue(
SCENE_CURRENT < sizeof(SCENES) / sizeof(scene_t),
"Invalid current scene index."
);
return &SCENES[SCENE_CURRENT];
}
const scene_t* sceneGetByName(const char_t *name) {
for(uint_fast8_t i = 0; i < sizeof(SCENES) / sizeof(scene_t); i++) {
if(stringCompare(SCENES[i].name, name) == 0) {
return &SCENES[i];
}
}
return NULL;
}
+52 -13
View File
@@ -1,24 +1,63 @@
/** /**
* Copyright (c) 2025 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#pragma once #pragma once
#include "dusk.h"
#include "error/error.h" #include "error/error.h"
#include "display/color.h"
#define SCENE_FLAG_INITIALIZED (1 << 0)
typedef struct scenedata_s scenedata_t;
typedef struct { typedef struct {
const char_t *name; const char_t *name;
errorret_t (*init)(scenedata_t *data); errorret_t (*init)(void);
void (*update)(scenedata_t *data); void (*update)(void);
void (*render)(scenedata_t *data); void (*render)(void);
void (*dispose)(scenedata_t *data); void (*dispose)(void);
uint8_t flags; } scene_t;
} scene_t;
extern const scene_t SCENES[];
extern uint_fast8_t SCENE_CURRENT;
/**
* Initialize the scene subsystem.
*/
errorret_t sceneInit(void);
/**
* Update the current scene.
*/
void sceneUpdate(void);
/**
* Render the current scene.
*/
void sceneRender(void);
/**
* Dispose of the scene subsystem.
*/
void sceneDispose(void);
/**
* Set the current scene.
*
* @param sceneIndex The index of the scene to set.
* @return An error code indicating success or failure.
*/
errorret_t sceneSet(const scene_t *scene);
/**
* Get the current scene.
*
* @return The current scene.
*/
const scene_t* sceneGetCurrent(void);
/**
* Get a scene by its name.
*
* @param name The name of the scene.
* @return The scene with the given name, or NULL if not found.
*/
const scene_t* sceneGetByName(const char_t *name);
+3 -3
View File
@@ -7,7 +7,7 @@
#pragma once #pragma once
#include "script/scriptcontext.h" #include "script/scriptcontext.h"
#include "scene/scenemanager.h" #include "scene/scene.h"
int moduleSceneSetScene(lua_State *L) { int moduleSceneSetScene(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL"); assertNotNull(L, "Lua state cannot be NULL");
@@ -20,13 +20,13 @@ int moduleSceneSetScene(lua_State *L) {
return 0; return 0;
} }
scene_t *scene = sceneManagerGetSceneByName(sceneName); const scene_t *scene = sceneGetByName(sceneName);
if(scene == NULL) { if(scene == NULL) {
luaL_error(L, "Scene '%s' not found", sceneName); luaL_error(L, "Scene '%s' not found", sceneName);
return 0; return 0;
} }
errorret_t err = sceneManagerSetScene(scene); errorret_t err = sceneSet(scene);
if(err.code != ERROR_OK) { if(err.code != ERROR_OK) {
luaL_error(L, "Failed to set scene '%s'", sceneName); luaL_error(L, "Failed to set scene '%s'", sceneName);
return 0; return 0;
-2
View File
@@ -10,14 +10,12 @@
#include "script/module/moduleinput.h" #include "script/module/moduleinput.h"
#include "script/module/moduleplatform.h" #include "script/module/moduleplatform.h"
#include "script/module/modulescene.h" #include "script/module/modulescene.h"
#include "script/module/modulemap.h"
const scriptmodule_t SCRIPT_MODULE_LIST[] = { const scriptmodule_t SCRIPT_MODULE_LIST[] = {
{ .name = "system", .callback = moduleSystem }, { .name = "system", .callback = moduleSystem },
{ .name = "input", .callback = moduleInput }, { .name = "input", .callback = moduleInput },
{ .name = "platform", .callback = modulePlatform }, { .name = "platform", .callback = modulePlatform },
{ .name = "scene", .callback = moduleScene }, { .name = "scene", .callback = moduleScene },
{ .name = "map", .callback = moduleMapSystem },
}; };
#define SCRIPT_MODULE_COUNT ( \ #define SCRIPT_MODULE_COUNT ( \
+3 -1
View File
@@ -26,6 +26,7 @@ void timeInit(void) {
TIME.dynamicTime = TIME_STEP; TIME.dynamicTime = TIME_STEP;
TIME.dynamicDelta = TIME_STEP; TIME.dynamicDelta = TIME_STEP;
TIME.dynamicUpdate = false; TIME.dynamicUpdate = false;
TIME.lastNonDynamic = TIME.dynamicTime;
#endif #endif
} }
@@ -44,8 +45,9 @@ void timeUpdate(void) {
assertTrue(TIME.dynamicDelta >= 0.0f, "Time delta is negative"); assertTrue(TIME.dynamicDelta >= 0.0f, "Time delta is negative");
// Is within 1ms of a full step? // Is within 1ms of a full step?
if(TIME.dynamicTime - TIME.time >= TIME_STEP * 0.999f) { if(TIME.dynamicTime - TIME.lastNonDynamic >= TIME_STEP * 0.999f) {
TIME.dynamicUpdate = false; TIME.dynamicUpdate = false;
TIME.lastNonDynamic = TIME.dynamicTime;
TIME.delta = TIME_STEP; TIME.delta = TIME_STEP;
TIME.time += TIME_STEP; TIME.time += TIME_STEP;
} }
+1
View File
@@ -18,6 +18,7 @@ typedef struct {
float_t time; float_t time;
#if TIME_FIXED == 0 #if TIME_FIXED == 0
float_t lastNonDynamic;
bool_t dynamicUpdate; bool_t dynamicUpdate;
float_t dynamicDelta; float_t dynamicDelta;
float_t dynamicTime; float_t dynamicTime;
+3 -3
View File
@@ -11,7 +11,7 @@
#include "util/memory.h" #include "util/memory.h"
#include "display/tileset/tileset_minogram.h" #include "display/tileset/tileset_minogram.h"
#include "display/screen.h" #include "display/screen.h"
#include "ui/uitextbox.h" // #include "ui/uitextbox.h"
ui_t UI; ui_t UI;
@@ -33,7 +33,7 @@ void uiUpdate(void) {
UI.camera.orthographic.top = 0; UI.camera.orthographic.top = 0;
UI.camera.orthographic.bottom = SCREEN.height; UI.camera.orthographic.bottom = SCREEN.height;
uiTextboxUpdate(); // uiTextboxUpdate();
} }
void uiRender(void) { void uiRender(void) {
@@ -42,7 +42,7 @@ void uiRender(void) {
// Render UI elements here // Render UI elements here
if(UI.fontTexture.width > 0) { if(UI.fontTexture.width > 0) {
uiDebugRender(UI.fontTileset, &UI.fontTexture); uiDebugRender(UI.fontTileset, &UI.fontTexture);
uiTextboxRender(); // uiTextboxRender();
} }
cameraPopMatrix(); cameraPopMatrix();
} }
+27 -27
View File
@@ -11,7 +11,7 @@
#include "ui/uitext.h" #include "ui/uitext.h"
#include "display/screen.h" #include "display/screen.h"
#include "display/spritebatch.h" #include "display/spritebatch.h"
#include "rpg/entity/entity.h" // #include "rpg/entity/entity.h"
bool_t UI_DEBUG_DRAW = true; bool_t UI_DEBUG_DRAW = true;
@@ -67,32 +67,32 @@ void uiDebugRender(const tileset_t *tileset, texture_t *texture) {
// Player position // Player position
entity_t *player = NULL; // entity_t *player = NULL;
for(uint8_t i = 0; i < ENTITY_COUNT; i++) { // for(uint8_t i = 0; i < ENTITY_COUNT; i++) {
if(ENTITIES[i].type != ENTITY_TYPE_PLAYER) continue; // if(ENTITIES[i].type != ENTITY_TYPE_PLAYER) continue;
player = &ENTITIES[i]; // player = &ENTITIES[i];
break; // break;
} // }
if(player == NULL) { // if(player == NULL) {
snprintf(buffer, sizeof(buffer), "Player: N/A"); // snprintf(buffer, sizeof(buffer), "Player: N/A");
} else { // } else {
snprintf( // snprintf(
buffer, // buffer,
sizeof(buffer), // sizeof(buffer),
"%d,%d,%d/%d/%d", // "%d,%d,%d/%d/%d",
player->position.x, // player->position.x,
player->position.y, // player->position.y,
player->position.z, // player->position.z,
(int32_t)player->direction, // (int32_t)player->direction,
(int32_t)player->animation // (int32_t)player->animation
); // );
} // }
uiTextMeasure(buffer, tileset, &w, &h); // uiTextMeasure(buffer, tileset, &w, &h);
uiTextDraw( // uiTextDraw(
SCREEN.width - w, hOffset, // SCREEN.width - w, hOffset,
buffer, COLOR_GREEN, tileset, texture // buffer, COLOR_GREEN, tileset, texture
); // );
hOffset += h; // hOffset += h;
spriteBatchFlush(); spriteBatchFlush();
} }
+25 -25
View File
@@ -8,38 +8,38 @@
#include "uitextbox.h" #include "uitextbox.h"
#include "ui/ui.h" #include "ui/ui.h"
#include "ui/uitext.h" #include "ui/uitext.h"
#include "rpg/rpgtextbox.h" // #include "rpg/rpgtextbox.h"
#include "display/screen.h" #include "display/screen.h"
#include "display/spritebatch.h" #include "display/spritebatch.h"
#include "input/input.h" #include "input/input.h"
void uiTextboxUpdate() { // void uiTextboxUpdate() {
if(!rpgTextboxIsVisible()) return; // if(!rpgTextboxIsVisible()) return;
if(inputPressed(INPUT_ACTION_ACCEPT)) { // if(inputPressed(INPUT_ACTION_ACCEPT)) {
rpgTextboxHide(); // rpgTextboxHide();
} // }
} // }
void uiTextboxRender() { // void uiTextboxRender() {
if(!rpgTextboxIsVisible()) return; // if(!rpgTextboxIsVisible()) return;
const char_t *text = RPG_TEXTBOX.text; // const char_t *text = RPG_TEXTBOX.text;
int32_t textWidth, textHeight; // int32_t textWidth, textHeight;
uiTextMeasure(text, UI.fontTileset, &textWidth, &textHeight); // uiTextMeasure(text, UI.fontTileset, &textWidth, &textHeight);
float_t y = 0; // float_t y = 0;
if(RPG_TEXTBOX.position == RPG_TEXTBOX_POS_BOTTOM) { // if(RPG_TEXTBOX.position == RPG_TEXTBOX_POS_BOTTOM) {
y = SCREEN.height - (float_t)textHeight; // y = SCREEN.height - (float_t)textHeight;
} // }
spriteBatchPush( // spriteBatchPush(
NULL, // NULL,
0.0f, y, // 0.0f, y,
(float_t)SCREEN.width, (float_t)(y + textHeight), // (float_t)SCREEN.width, (float_t)(y + textHeight),
COLOR_BLACK, // COLOR_BLACK,
0.0f, 0.0f, 1.0f, 1.0f // 0.0f, 0.0f, 1.0f, 1.0f
); // );
uiTextDraw(0, y, text, COLOR_RED, UI.fontTileset, &UI.fontTexture); // uiTextDraw(0, y, text, COLOR_RED, UI.fontTileset, &UI.fontTexture);
} // }
+3
View File
@@ -3,5 +3,8 @@
# This software is released under the MIT License. # This software is released under the MIT License.
# https://opensource.org/licenses/MIT # https://opensource.org/licenses/MIT
add_subdirectory(display)
# add_subdirectory(rpg)
add_subdirectory(item)
add_subdirectory(time) add_subdirectory(time)
add_subdirectory(util) add_subdirectory(util)
+9
View File
@@ -0,0 +1,9 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
include(dusktest)
# Tests
dusktest(test_color.c)
+98
View File
@@ -0,0 +1,98 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "dusktest.h"
#include "display/color.h"
static void test_color3f_create(void **state) {
(void)state;
color3f_t color = color3f(0.1f, 0.2f, 0.3f);
assert_float_equal(color.r, 0.1f, 0.0001f);
assert_float_equal(color.g, 0.2f, 0.0001f);
assert_float_equal(color.b, 0.3f, 0.0001f);
}
static void test_color4f_create(void **state) {
(void)state;
color4f_t color = color4f(0.1f, 0.2f, 0.3f, 0.4f);
assert_float_equal(color.r, 0.1f, 0.0001f);
assert_float_equal(color.g, 0.2f, 0.0001f);
assert_float_equal(color.b, 0.3f, 0.0001f);
assert_float_equal(color.a, 0.4f, 0.0001f);
}
static void test_color3b_create(void **state) {
(void)state;
color3b_t color = color3b(10, 20, 30);
assert_int_equal(color.r, 10);
assert_int_equal(color.g, 20);
assert_int_equal(color.b, 30);
}
static void test_color4b_create(void **state) {
(void)state;
color4b_t color = color4b(10, 20, 30, 40);
assert_int_equal(color.r, 10);
assert_int_equal(color.g, 20);
assert_int_equal(color.b, 30);
assert_int_equal(color.a, 40);
}
static void test_color_create(void **state) {
(void)state;
color_t color = color(10, 20, 30, 40);
assert_int_equal(color.r, 10);
assert_int_equal(color.g, 20);
assert_int_equal(color.b, 30);
assert_int_equal(color.a, 40);
}
static void test_colorHex_create(void **state) {
(void)state;
color_t color = colorHex(0x11223344);
assert_int_equal(color.r, 0x11);
assert_int_equal(color.g, 0x22);
assert_int_equal(color.b, 0x33);
assert_int_equal(color.a, 0x44);
color = colorHex(0xFF00FF00);
assert_int_equal(color.r, 0xFF);
assert_int_equal(color.g, 0x00);
assert_int_equal(color.b, 0xFF);
assert_int_equal(color.a, 0x00);
color_t comp = color(255, 0, 255, 0);
assert_int_equal(color.r, comp.r);
assert_int_equal(color.g, comp.g);
assert_int_equal(color.b, comp.b);
assert_int_equal(color.a, comp.a);
color = colorHex(0xFFFFFFFF);
assert_int_equal(color.r, COLOR_WHITE.r);
assert_int_equal(color.g, COLOR_WHITE.g);
assert_int_equal(color.b, COLOR_WHITE.b);
assert_int_equal(color.a, COLOR_WHITE.a);
}
int main(int argc, char **argv) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_color3f_create),
cmocka_unit_test(test_color4f_create),
cmocka_unit_test(test_color3b_create),
cmocka_unit_test(test_color4b_create),
cmocka_unit_test(test_color_create),
cmocka_unit_test(test_colorHex_create),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}
+9
View File
@@ -0,0 +1,9 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
include(dusktest)
# Tests
dusktest(test_inventory.c)
+341
View File
@@ -0,0 +1,341 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "dusktest.h"
#include "item/inventory.h"
static void test_inventoryInit(void **state) {
(void)state;
inventorystack_t storage[5];
inventory_t inventory;
inventoryInit(&inventory, storage, 5);
// Should setup struct
assert_non_null(inventory.storage);
assert_int_equal(inventory.storageSize, 5);
// Should zero the item ids.
for(size_t i = 0; i < inventory.storageSize; i++) {
assert_int_equal(inventory.storage[i].item, ITEM_ID_NULL);
assert_int_equal(inventory.storage[i].quantity, 0);
}
// Should fail when given NULL inventory pointer
expect_assert_failure(inventoryInit(NULL, storage, 5));
// Should fail when given NULL storage pointer
expect_assert_failure(inventoryInit(&inventory, NULL, 5));
// Should fail when given zero storage size
expect_assert_failure(inventoryInit(&inventory, storage, 0));
}
static void test_inventoryItemExists(void **state) {
(void)state;
inventorystack_t storage[5];
inventory_t inventory;
inventoryInit(&inventory, storage, 5);
// Initially should not exist
assert_false(inventoryItemExists(&inventory, ITEM_ID_POTION));
// Add item
inventorySet(&inventory, ITEM_ID_POTION, 3);
assert_true(inventoryItemExists(&inventory, ITEM_ID_POTION));
// Remove item
inventorySet(&inventory, ITEM_ID_POTION, 0);
assert_false(inventoryItemExists(&inventory, ITEM_ID_POTION));
// Should fail when given NULL inventory pointer
expect_assert_failure(inventoryItemExists(NULL, ITEM_ID_POTION));
// Should fail when given NULL storage pointer
inventory.storage = NULL;
expect_assert_failure(inventoryItemExists(&inventory, ITEM_ID_POTION));
inventory.storage = storage;
// Should fail when given zero storage size
inventory.storageSize = 0;
expect_assert_failure(inventoryItemExists(&inventory, ITEM_ID_POTION));
inventory.storageSize = 5;
// Should fail when given ITEM_ID_NULL
expect_assert_failure(inventoryItemExists(&inventory, ITEM_ID_NULL));
// Should fail if item has zero quantity somehow
inventorySet(&inventory, ITEM_ID_POTION, 3);
inventory.storage[0].quantity = 0;
expect_assert_failure(inventoryItemExists(&inventory, ITEM_ID_POTION));
}
static void test_inventorySet(void **state) {
(void)state;
inventorystack_t storage[5];
inventory_t inventory;
inventoryInit(&inventory, storage, 5);
// Set item quantity
inventorySet(&inventory, ITEM_ID_POTION, 4);
assert_int_equal(inventory.storage[0].item, ITEM_ID_POTION);
assert_int_equal(inventory.storage[0].quantity, 4);
// Update item quantity
inventorySet(&inventory, ITEM_ID_POTION, 2);
assert_int_equal(inventory.storage[0].item, ITEM_ID_POTION);
assert_int_equal(inventory.storage[0].quantity, 2);
// Remove item by setting quantity to 0
inventorySet(&inventory, ITEM_ID_POTION, 0);
assert_int_equal(inventory.storage[0].item, ITEM_ID_NULL);
// Setting multiple items should not cause issues
inventorySet(&inventory, ITEM_ID_POTION, 1);
inventorySet(&inventory, ITEM_ID_POTATO, 5);
assert_int_equal(inventory.storage[0].item, ITEM_ID_POTION);
assert_int_equal(inventory.storage[0].quantity, 1);
assert_int_equal(inventory.storage[1].item, ITEM_ID_POTATO);
assert_int_equal(inventory.storage[1].quantity, 5);
// Setting early item to 0 should shift others down
inventorySet(&inventory, ITEM_ID_POTION, 0);
assert_int_equal(inventory.storage[0].item, ITEM_ID_POTATO);
assert_int_equal(inventory.storage[0].quantity, 5);
assert_int_equal(inventory.storage[1].item, ITEM_ID_NULL);
// Setting non-existing item should add it
inventorySet(&inventory, ITEM_ID_POTION, 3);
assert_int_equal(inventory.storage[1].item, ITEM_ID_POTION);
assert_int_equal(inventory.storage[1].quantity, 3);
// Should fail when given NULL inventory pointer
expect_assert_failure(inventorySet(NULL, ITEM_ID_POTION, 3));
// Should fail when given NULL storage pointer
inventory.storage = NULL;
expect_assert_failure(inventorySet(&inventory, ITEM_ID_POTION, 3));
inventory.storage = storage;
// Should fail when given zero storage size
inventory.storageSize = 0;
expect_assert_failure(inventorySet(&inventory, ITEM_ID_POTION, 3));
inventory.storageSize = 5;
// Should fail when given ITEM_ID_NULL
expect_assert_failure(inventorySet(&inventory, ITEM_ID_NULL, 3));
}
static void test_inventoryAdd(void **state) {
(void)state;
inventorystack_t storage[5];
inventory_t inventory;
inventoryInit(&inventory, storage, 5);
// Add item quantity
inventoryAdd(&inventory, ITEM_ID_POTION, 4);
assert_int_equal(inventory.storage[0].item, ITEM_ID_POTION);
assert_int_equal(inventory.storage[0].quantity, 4);
// Add more to existing item
inventoryAdd(&inventory, ITEM_ID_POTION, 3);
assert_int_equal(inventory.storage[0].item, ITEM_ID_POTION);
assert_int_equal(inventory.storage[0].quantity, 7);
// Adding item that would overflow should assert
expect_assert_failure(inventoryAdd(&inventory, ITEM_ID_POTION, 250));
// Should fail when given NULL inventory pointer
expect_assert_failure(inventoryAdd(NULL, ITEM_ID_POTION, 3));
// Should fail when given NULL storage pointer
inventory.storage = NULL;
expect_assert_failure(inventoryAdd(&inventory, ITEM_ID_POTION, 3));
inventory.storage = storage;
// Should fail when given zero storage size
inventory.storageSize = 0;
expect_assert_failure(inventoryAdd(&inventory, ITEM_ID_POTION, 3));
inventory.storageSize = 5;
// Should fail when given ITEM_ID_NULL
expect_assert_failure(inventoryAdd(&inventory, ITEM_ID_NULL, 3));
}
static void test_inventoryRemove(void **state) {
(void)state;
inventorystack_t storage[5];
inventory_t inventory;
inventoryInit(&inventory, storage, 5);
// Add item and then remove it
inventorySet(&inventory, ITEM_ID_POTION, 5);
inventoryRemove(&inventory, ITEM_ID_POTION);
assert_int_equal(inventory.storage[0].item, ITEM_ID_NULL);
// Removing non-existing item should do nothing
inventoryRemove(&inventory, ITEM_ID_POTION);
assert_int_equal(inventory.storage[0].item, ITEM_ID_NULL);
// Should fail when given NULL inventory pointer
expect_assert_failure(inventoryRemove(NULL, ITEM_ID_POTION));
// Should fail when given NULL storage pointer
inventory.storage = NULL;
expect_assert_failure(inventoryRemove(&inventory, ITEM_ID_POTION));
inventory.storage = storage;
// Should fail when given zero storage size
inventory.storageSize = 0;
expect_assert_failure(inventoryRemove(&inventory, ITEM_ID_POTION));
inventory.storageSize = 5;
// Should fail when given ITEM_ID_NULL
expect_assert_failure(inventoryRemove(&inventory, ITEM_ID_NULL));
}
static void test_inventoryGetCount(void **state) {
(void)state;
inventorystack_t storage[5];
inventory_t inventory;
inventoryInit(&inventory, storage, 5);
// Initially should be zero
assert_int_equal(inventoryGetCount(&inventory, ITEM_ID_POTION), 0);
// Add item and check count
inventorySet(&inventory, ITEM_ID_POTION, 4);
assert_int_equal(inventoryGetCount(&inventory, ITEM_ID_POTION), 4);
// Update item quantity and check count
inventorySet(&inventory, ITEM_ID_POTION, 2);
assert_int_equal(inventoryGetCount(&inventory, ITEM_ID_POTION), 2);
// Remove item and check count
inventorySet(&inventory, ITEM_ID_POTION, 0);
assert_int_equal(inventoryGetCount(&inventory, ITEM_ID_POTION), 0);
// Should fail when given NULL inventory pointer
expect_assert_failure(inventoryGetCount(NULL, ITEM_ID_POTION));
// Should fail when given NULL storage pointer
inventory.storage = NULL;
expect_assert_failure(inventoryGetCount(&inventory, ITEM_ID_POTION));
inventory.storage = storage;
// Should fail when given zero storage size
inventory.storageSize = 0;
expect_assert_failure(inventoryGetCount(&inventory, ITEM_ID_POTION));
inventory.storageSize = 5;
// Should fail when given ITEM_ID_NULL
expect_assert_failure(inventoryGetCount(&inventory, ITEM_ID_NULL));
}
static void test_inventoryIsFull(void **state) {
(void)state;
inventorystack_t storage[2];
inventory_t inventory;
inventoryInit(&inventory, storage, 2);
// Initially should not be full
assert_false(inventoryIsFull(&inventory));
// Fill inventory
inventorySet(&inventory, ITEM_ID_POTION, 1);
inventorySet(&inventory, ITEM_ID_POTATO, 1);
assert_true(inventoryIsFull(&inventory));
// Remove one item
inventoryRemove(&inventory, ITEM_ID_POTION);
assert_false(inventoryIsFull(&inventory));
// Add one item back
inventorySet(&inventory, ITEM_ID_POTION, 1);
assert_true(inventoryIsFull(&inventory));
// Should fail when given NULL inventory pointer
expect_assert_failure(inventoryIsFull(NULL));
// Should fail when given NULL storage pointer
inventory.storage = NULL;
expect_assert_failure(inventoryIsFull(&inventory));
inventory.storage = storage;
// Should fail when given zero storage size
inventory.storageSize = 0;
expect_assert_failure(inventoryIsFull(&inventory));
inventory.storageSize = 2;
}
static void test_inventoryItemFull(void **state) {
(void)state;
inventorystack_t storage[2];
inventory_t inventory;
inventoryInit(&inventory, storage, 2);
// Initially should not be full
assert_false(inventoryItemFull(&inventory, ITEM_ID_POTION));
// Add some potions, but not too many.
inventorySet(&inventory, ITEM_ID_POTION, 100);
assert_false(inventoryItemFull(&inventory, ITEM_ID_POTION));
// Fill with potions
inventorySet(&inventory, ITEM_ID_POTION, ITEM_STACK_QUANTITY_MAX);
assert_true(inventoryItemFull(&inventory, ITEM_ID_POTION));
// Add potatoes, should not affect potions
inventorySet(&inventory, ITEM_ID_POTATO, 50);
assert_true(inventoryItemFull(&inventory, ITEM_ID_POTION));
assert_false(inventoryItemFull(&inventory, ITEM_ID_POTATO));
// Remove some potions
inventorySet(&inventory, ITEM_ID_POTION, 200);
assert_false(inventoryItemFull(&inventory, ITEM_ID_POTION));
// Fill with potatoes
inventorySet(&inventory, ITEM_ID_POTATO, ITEM_STACK_QUANTITY_MAX);
assert_false(inventoryItemFull(&inventory, ITEM_ID_POTION));
assert_true(inventoryItemFull(&inventory, ITEM_ID_POTATO));
// Should fail when given NULL inventory pointer
expect_assert_failure(inventoryItemFull(NULL, ITEM_ID_POTION));
// Should fail when given NULL storage pointer
inventory.storage = NULL;
expect_assert_failure(inventoryItemFull(&inventory, ITEM_ID_POTION));
inventory.storage = storage;
// Should fail when given zero storage size
inventory.storageSize = 0;
expect_assert_failure(inventoryItemFull(&inventory, ITEM_ID_POTION));
inventory.storageSize = 2;
}
int main(int argc, char** argv) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_inventoryInit),
cmocka_unit_test(test_inventoryItemExists),
cmocka_unit_test(test_inventorySet),
cmocka_unit_test(test_inventoryAdd),
cmocka_unit_test(test_inventoryRemove),
cmocka_unit_test(test_inventoryGetCount),
cmocka_unit_test(test_inventoryIsFull),
cmocka_unit_test(test_inventoryItemFull),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}
+12
View File
@@ -0,0 +1,12 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
include(dusktest)
# Tests
dusktest(test_rpg.c)
# Subdirs
add_subdirectory(overworld)
+10
View File
@@ -0,0 +1,10 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
include(dusktest)
# Tests
# Subdirs
+16
View File
@@ -0,0 +1,16 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "dusktest.h"
int main(int argc, char** argv) {
const struct CMUnitTest tests[] = {
// Add RPG tests here in the future
};
return cmocka_run_group_tests(tests, NULL, NULL);
}
+4 -4
View File
@@ -100,17 +100,17 @@ static void test_timeUpdate(void **state) {
assert_float_equal(TIME.delta, TIME_STEP, 0.0001f); assert_float_equal(TIME.delta, TIME_STEP, 0.0001f);
assert_float_equal(TIME.time, TIME_STEP * 4, 0.0001f); assert_float_equal(TIME.time, TIME_STEP * 4, 0.0001f);
// Fifth test, despite a small time passing the game should be trying to catch // Fifth test, despite a small time passing the game should not start
// and running extra ticks // trying to run ahead
SDL_GETTICKS_TICKS = 104; // Simulate 4ms elapsed SDL_GETTICKS_TICKS = 104; // Simulate 4ms elapsed
SDL_GETTICKS_CALLED = false; SDL_GETTICKS_CALLED = false;
timeUpdate(); timeUpdate();
assert_true(SDL_GETTICKS_CALLED); assert_true(SDL_GETTICKS_CALLED);
assert_false(TIME.dynamicUpdate); assert_true(TIME.dynamicUpdate);
assert_float_equal(TIME.dynamicDelta, 0.004f, 0.0001f); assert_float_equal(TIME.dynamicDelta, 0.004f, 0.0001f);
assert_float_equal(TIME.dynamicTime, TIME_STEP + 0.104f, 0.0001f); assert_float_equal(TIME.dynamicTime, TIME_STEP + 0.104f, 0.0001f);
assert_float_equal(TIME.delta, TIME_STEP, 0.0001f); assert_float_equal(TIME.delta, TIME_STEP, 0.0001f);
assert_float_equal(TIME.time, TIME_STEP * 5, 0.0001f); assert_float_equal(TIME.time, TIME_STEP * 4, 0.0001f);
// Time can stand still if needed // Time can stand still if needed
SDL_GETTICKS_CALLED = false; SDL_GETTICKS_CALLED = false;