Mercurial > molko
view librpg/rpg/inventory.c @ 169:eb0a7ab71023
misc: extreme cleanup, closes #2506
While here, remove unneeded stuff.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 20 Oct 2020 17:39:13 +0200 |
parents | c577c15df07f |
children | ce789473567e |
line wrap: on
line source
/* * inventory.c -- inventory of items * * Copyright (c) 2020 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <assert.h> #include <stddef.h> #include <stdlib.h> #include <string.h> #include "inventory.h" #include "item.h" #define INVENTORY_TOTAL (INVENTORY_ROWS_MAX * INVENTORY_COLS_MAX) static bool can_be_used(struct inventory_slot *slot, const struct item *item) { assert(item); /* Empty slot. */ if (!slot->item) return false; /* Not same object. */ if (strcmp(slot->item->name, item->name) != 0) return false; /* No space in this slot. */ if (slot->amount >= slot->item->stackable) return false; return true; } static struct inventory_slot * find(struct inventory *iv, const struct item *item) { assert(iv); assert(item); /* First pass: find an entry with the same item. */ for (unsigned int r = 0; r < INVENTORY_ROWS_MAX; ++r) for (unsigned int c = 0; c < INVENTORY_COLS_MAX; ++c) if (can_be_used(&iv->items[r][c], item)) return &iv->items[r][c]; /* Second pass: try to find an empty slot. */ for (unsigned int r = 0; r < INVENTORY_ROWS_MAX; ++r) for (unsigned int c = 0; c < INVENTORY_COLS_MAX; ++c) if (!iv->items[r][c].item) return &iv->items[r][c]; return NULL; } static unsigned provide(struct inventory_slot *slot, struct item *item, unsigned int amount) { assert(slot); unsigned int avail; /* The slot may be empty, make sure it contains this item. */ slot->item = item; /* * Example: * * The slot has already 10 items. * The slot item is stackble up to 64 items. * * When pushing: * * 80: 54 pushed, 26 left * 30: 30 pushed, 0 left. */ avail = slot->item->stackable - slot->amount; if (amount > avail) { slot->amount += avail; amount -= avail; } else { slot->amount += amount; amount = 0; } return amount; } static bool merge(struct inventory_slot *slot, struct inventory_slot *other) { assert(slot); assert(slot->item); assert(other); /* Not compatible, return false to let the sorting continue. */ if (slot->item != other->item) return false; while (slot->amount < slot->item->stackable && other->amount) { slot->amount++; other->amount--; } /* No more amount in the other slot, empty it. */ if (other->amount == 0U) memset(other, 0, sizeof (*other)); return slot->amount >= slot->item->stackable; } static void sort(struct inventory *iv, struct inventory_slot *slot, int r, int c) { assert(slot); assert(slot->item); /* Merge until the end of thiw row. */ for (c = c + 1; c < INVENTORY_COLS_MAX; ++c) if (merge(slot, &iv->items[r][c])) return; /* Merge the next rows. */ for (r = r + 1; r < INVENTORY_ROWS_MAX; ++r) for (c = 0; c < INVENTORY_COLS_MAX; ++c) if (merge(slot, &iv->items[r][c])) return; } unsigned int inventory_push(struct inventory *iv, struct item *item, unsigned int amount) { assert(iv); assert(item); while (amount) { struct inventory_slot *slot; if (!(slot = find(iv, item))) break; /* Add as much as we can in this slot. */ amount = provide(slot, item, amount); } return amount; } static int compare_slot(const void *v1, const void *v2) { const struct inventory_slot *slot1 = v1; const struct inventory_slot *slot2 = v2; int cmp; /* Two null slots compare equal. */ if (!slot1->item && !slot2->item) return 0; /* Null left should be moved after. */ if (!slot1->item) return 1; /* Null right slots should be moved after. */ if (!slot2->item) return -1; /* If they are identical, use amount to sort. */ if ((cmp = strcmp(slot1->item->name, slot2->item->name)) == 0) return (long long int)slot2->amount - (long long int)slot1->amount; return cmp; } void inventory_sort(struct inventory *iv) { assert(iv); for (int r = 0; r < INVENTORY_ROWS_MAX; ++r) for (int c = 0; c < INVENTORY_COLS_MAX; ++c) if (iv->items[r][c].item) sort(iv, &iv->items[r][c], r, c); /* Sort by names AND by amount. */ qsort(iv->items, INVENTORY_TOTAL, sizeof (struct inventory_slot), compare_slot); } void inventory_clear(struct inventory *iv) { assert(iv); memset(iv, 0, sizeof (*iv)); }