/** * 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); }