Game code start
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Includes
|
||||
target_include_directories(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
)
|
||||
|
||||
add_subdirectory(game)
|
||||
add_subdirectory(input)
|
||||
add_subdirectory(item)
|
||||
@@ -0,0 +1,9 @@
|
||||
# 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
|
||||
game.c
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "game/game.h"
|
||||
|
||||
errorret_t gameInit(void) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t gameUpdate(void) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t gameDispose(void) {
|
||||
errorOk();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Header-only: inputbind.h has no implementation.
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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 {
|
||||
INPUT_BIND_NULL,
|
||||
|
||||
INPUT_BIND_UP,
|
||||
INPUT_BIND_DOWN,
|
||||
INPUT_BIND_LEFT,
|
||||
INPUT_BIND_RIGHT,
|
||||
INPUT_BIND_ACCEPT,
|
||||
INPUT_BIND_CANCEL,
|
||||
INPUT_BIND_PAUSE,
|
||||
INPUT_BIND_RAGEQUIT,
|
||||
INPUT_BIND_CONSOLE,
|
||||
INPUT_BIND_POINTERX,
|
||||
INPUT_BIND_POINTERY,
|
||||
|
||||
INPUT_BIND_COUNT
|
||||
} inputbind_t;
|
||||
|
||||
static const char_t *INPUT_BIND_IDS[] = {
|
||||
[INPUT_BIND_UP] = "UP",
|
||||
[INPUT_BIND_DOWN] = "DOWN",
|
||||
[INPUT_BIND_LEFT] = "LEFT",
|
||||
[INPUT_BIND_RIGHT] = "RIGHT",
|
||||
[INPUT_BIND_ACCEPT] = "ACCEPT",
|
||||
[INPUT_BIND_CANCEL] = "CANCEL",
|
||||
[INPUT_BIND_PAUSE] = "PAUSE",
|
||||
[INPUT_BIND_RAGEQUIT] = "RAGEQUIT",
|
||||
[INPUT_BIND_CONSOLE] = "CONSOLE",
|
||||
[INPUT_BIND_POINTERX] = "POINTERX",
|
||||
[INPUT_BIND_POINTERY] = "POINTERY",
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "input/inputbind.h"
|
||||
|
||||
typedef struct {
|
||||
const char_t *buttonName;
|
||||
inputbind_t bind;
|
||||
} inputbindmapentry_t;
|
||||
|
||||
#ifdef DUSK_LINUX
|
||||
static const inputbindmapentry_t INPUT_BIND_MAP[] = {
|
||||
#ifdef DUSK_INPUT_KEYBOARD
|
||||
{ .buttonName = "w", .bind = INPUT_BIND_UP },
|
||||
{ .buttonName = "s", .bind = INPUT_BIND_DOWN },
|
||||
{ .buttonName = "a", .bind = INPUT_BIND_LEFT },
|
||||
{ .buttonName = "d", .bind = INPUT_BIND_RIGHT },
|
||||
{ .buttonName = "left", .bind = INPUT_BIND_LEFT },
|
||||
{ .buttonName = "right", .bind = INPUT_BIND_RIGHT },
|
||||
{ .buttonName = "up", .bind = INPUT_BIND_UP },
|
||||
{ .buttonName = "down", .bind = INPUT_BIND_DOWN },
|
||||
{ .buttonName = "enter", .bind = INPUT_BIND_ACCEPT },
|
||||
{ .buttonName = "e", .bind = INPUT_BIND_ACCEPT },
|
||||
{ .buttonName = "space", .bind = INPUT_BIND_ACCEPT },
|
||||
{ .buttonName = "tab", .bind = INPUT_BIND_CANCEL },
|
||||
{ .buttonName = "q", .bind = INPUT_BIND_CANCEL },
|
||||
{ .buttonName = "escape", .bind = INPUT_BIND_RAGEQUIT },
|
||||
{ .buttonName = "enter", .bind = INPUT_BIND_PAUSE },
|
||||
{ .buttonName = "`", .bind = INPUT_BIND_CONSOLE },
|
||||
#endif
|
||||
|
||||
#ifdef DUSK_INPUT_GAMEPAD
|
||||
{ .buttonName = "gamepad_up", .bind = INPUT_BIND_UP },
|
||||
{ .buttonName = "gamepad_down", .bind = INPUT_BIND_DOWN },
|
||||
{ .buttonName = "gamepad_left", .bind = INPUT_BIND_LEFT },
|
||||
{ .buttonName = "gamepad_right", .bind = INPUT_BIND_RIGHT },
|
||||
{ .buttonName = "gamepad_a", .bind = INPUT_BIND_ACCEPT },
|
||||
{ .buttonName = "gamepad_b", .bind = INPUT_BIND_CANCEL },
|
||||
{ .buttonName = "gamepad_back", .bind = INPUT_BIND_RAGEQUIT },
|
||||
{ .buttonName = "gamepad_start", .bind = INPUT_BIND_PAUSE },
|
||||
{ .buttonName = "gamepad_lstick_up", .bind = INPUT_BIND_UP },
|
||||
{ .buttonName = "gamepad_lstick_down", .bind = INPUT_BIND_DOWN },
|
||||
{ .buttonName = "gamepad_lstick_left", .bind = INPUT_BIND_LEFT },
|
||||
{ .buttonName = "gamepad_lstick_right", .bind = INPUT_BIND_RIGHT },
|
||||
#endif
|
||||
|
||||
#ifdef DUSK_INPUT_POINTER
|
||||
{ .buttonName = "mouse_x", .bind = INPUT_BIND_POINTERX },
|
||||
{ .buttonName = "mouse_y", .bind = INPUT_BIND_POINTERY },
|
||||
#endif
|
||||
|
||||
{ .buttonName = NULL, .bind = INPUT_BIND_NULL }
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef DUSK_PSP
|
||||
static const inputbindmapentry_t INPUT_BIND_MAP[] = {
|
||||
{ .buttonName = "up", .bind = INPUT_BIND_UP },
|
||||
{ .buttonName = "down", .bind = INPUT_BIND_DOWN },
|
||||
{ .buttonName = "left", .bind = INPUT_BIND_LEFT },
|
||||
{ .buttonName = "right", .bind = INPUT_BIND_RIGHT },
|
||||
{ .buttonName = "accept", .bind = INPUT_BIND_ACCEPT },
|
||||
{ .buttonName = "cancel", .bind = INPUT_BIND_CANCEL },
|
||||
{ .buttonName = "triangle", .bind = INPUT_BIND_CONSOLE },
|
||||
{ .buttonName = "select", .bind = INPUT_BIND_RAGEQUIT },
|
||||
{ .buttonName = "start", .bind = INPUT_BIND_PAUSE },
|
||||
{ .buttonName = "lstick_up", .bind = INPUT_BIND_UP },
|
||||
{ .buttonName = "lstick_down", .bind = INPUT_BIND_DOWN },
|
||||
{ .buttonName = "lstick_left", .bind = INPUT_BIND_LEFT },
|
||||
{ .buttonName = "lstick_right", .bind = INPUT_BIND_RIGHT },
|
||||
{ .buttonName = NULL, .bind = INPUT_BIND_NULL }
|
||||
};
|
||||
#endif
|
||||
|
||||
// GameCube and Wii share the same pad layout for now.
|
||||
// TODO: Wiimote, USB Keyboard, probably more.
|
||||
#if defined(DUSK_GAMECUBE) || defined(DUSK_WII)
|
||||
static const inputbindmapentry_t INPUT_BIND_MAP[] = {
|
||||
{ .buttonName = "up", .bind = INPUT_BIND_UP },
|
||||
{ .buttonName = "down", .bind = INPUT_BIND_DOWN },
|
||||
{ .buttonName = "left", .bind = INPUT_BIND_LEFT },
|
||||
{ .buttonName = "right", .bind = INPUT_BIND_RIGHT },
|
||||
{ .buttonName = "lstick_up", .bind = INPUT_BIND_UP },
|
||||
{ .buttonName = "lstick_down", .bind = INPUT_BIND_DOWN },
|
||||
{ .buttonName = "lstick_left", .bind = INPUT_BIND_LEFT },
|
||||
{ .buttonName = "lstick_right", .bind = INPUT_BIND_RIGHT },
|
||||
{ .buttonName = "a", .bind = INPUT_BIND_ACCEPT },
|
||||
{ .buttonName = "b", .bind = INPUT_BIND_CANCEL },
|
||||
{ .buttonName = "z", .bind = INPUT_BIND_CONSOLE },
|
||||
{ .buttonName = "start", .bind = INPUT_BIND_RAGEQUIT },
|
||||
{ .buttonName = "start", .bind = INPUT_BIND_PAUSE },
|
||||
{ .buttonName = NULL, .bind = INPUT_BIND_NULL }
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Sources
|
||||
# itemgive.c/h are not built yet -- they depend on the RPG textbox UI
|
||||
# (ui/rpg/textbox/uitextboxmain.h), which hasn't been restored. Add
|
||||
# itemgive.c back here once that's back.
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
item.c
|
||||
inventory.c
|
||||
backpack.c
|
||||
)
|
||||
|
||||
# Item Definitions
|
||||
dusk_run_python(
|
||||
dusk_item_json_defs
|
||||
tools.item
|
||||
--json ${CMAKE_CURRENT_SOURCE_DIR}/item.json
|
||||
--output ${DUSK_GENERATED_HEADERS_DIR}/item/itemdef.h
|
||||
)
|
||||
add_dependencies(${DUSK_LIBRARY_TARGET_NAME} dusk_item_json_defs)
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* 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
|
||||
);
|
||||
@@ -0,0 +1,250 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "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
|
||||
);
|
||||
|
||||
/**
|
||||
* Comparator for sorting inventory stacks by item ID, ascending.
|
||||
*
|
||||
* @param a First inventorystack_t to compare.
|
||||
* @param b Second inventorystack_t to compare.
|
||||
* @return Comparison result for use with sort().
|
||||
*/
|
||||
int_t inventorySortById(const void *a, const void *b);
|
||||
|
||||
/**
|
||||
* Comparator for sorting inventory stacks by item ID, descending.
|
||||
*
|
||||
* @param a First inventorystack_t to compare.
|
||||
* @param b Second inventorystack_t to compare.
|
||||
* @return Comparison result for use with sort().
|
||||
*/
|
||||
int_t inventorySortByIdReverse(const void *a, const void *b);
|
||||
|
||||
/**
|
||||
* Comparator for sorting inventory stacks by item type, ascending.
|
||||
*
|
||||
* @param a First inventorystack_t to compare.
|
||||
* @param b Second inventorystack_t to compare.
|
||||
* @return Comparison result for use with sort().
|
||||
*/
|
||||
int_t inventorySortByType(const void *a, const void *b);
|
||||
|
||||
/**
|
||||
* Comparator for sorting inventory stacks by item type, descending.
|
||||
*
|
||||
* @param a First inventorystack_t to compare.
|
||||
* @param b Second inventorystack_t to compare.
|
||||
* @return Comparison result for use with sort().
|
||||
*/
|
||||
int_t inventorySortByTypeReverse(const void *a, const void *b);
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* 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 "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
|
||||
);
|
||||
@@ -0,0 +1,5 @@
|
||||
[
|
||||
{ "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" }
|
||||
]
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "itemgive.h"
|
||||
#include "item/backpack.h"
|
||||
#include "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);
|
||||
}
|
||||
@@ -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 "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 <item> x<quantity>" 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);
|
||||
Reference in New Issue
Block a user