251 lines
7.4 KiB
C
251 lines
7.4 KiB
C
/**
|
|
* 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;
|
|
|
|
// 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)) * 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);
|
|
} |