extends Node signal item_dropped(item: DBItemResource) signal item_picked_up(item: DBItemResource) #region Inventory Specific signal add_to_inventory(item_id: String, amount: int) signal clear_inventory ## Remove all items in inventory signal item_added(item_id: String, amount: int) signal item_removed(item_id: String, amount: int) signal inventory_closed signal inventory_opened signal inventory_slot_updated(slot_index: int) signal remove_from_inventory(item_id: String, amount: int) signal remove_from_quickslot(amount: int) signal remove_from_slot(slot_index: int, amount: int) #endregion #region Quickslots signal next_quick_slot signal previous_quick_slot signal quick_slot_selected(slot_index: int) signal select_quick_slot(slot_index: int) #endregion var max_inventory_items: int = 40 # 4 rows of 10 var quick_slot_count: int = 10 var selected_quick_slot: int = 0 var inventory: Array[DBItemResource] = [] ## To ensure inventory is automatically sorted, "empty" inventory cells will be replaced with null to keep positions var _inventory_cache: Dictionary[String, Dictionary] = {} ## Used for caching certain information func _ready() -> void: self.item_picked_up.connect(_on_item_picked_up) self.item_dropped.connect(_on_item_dropped) self.add_to_inventory.connect(_on_add_to_inventory) self.clear_inventory.connect(_on_clear_inventory) self.quick_slot_selected.connect(_on_quick_slot_selected) self.remove_from_inventory.connect(_on_remove_from_inventory) self.remove_from_quickslot.connect(_on_remove_from_quickslot) self.remove_from_slot.connect(_on_remove_from_slot) func available_space(item_id: String) -> int: var full_stacks: int = floor(_inventory_cache[item_id].total / DBItems.data[item_id].max_stack_size) var space_in_stacks: int = ( DBItems.data[item_id].max_stack_size - abs( _inventory_cache[item_id].total - (DBItems.data[item_id].max_stack_size * full_stacks) ) ) # This is a bit of a hack because I'm a math/logic noob if space_in_stacks == DBItems.data[item_id].max_stack_size: space_in_stacks = 0 var open_stack_amount: int = (max_inventory_items - inventory.size()) * DBItems.data[item_id].max_stack_size return open_stack_amount + space_in_stacks func get_inventory_item(item_slot: int = selected_quick_slot) -> DBItemResource: if item_slot >= inventory.size() or item_slot < 0: return null return inventory.get(item_slot) func get_quick_slot_item_id(item_slot: int = selected_quick_slot) -> String: return inventory[item_slot].id func _find_stacks_by_id(item_resource: DBItemResource, item_id: String) -> bool: return item_resource != null and item_resource.id == item_id ## Find the stack where at least one item can be added func _find_stacks_with_space(item_resource: DBItemResource, item_id: String) -> bool: return ( item_resource == null or ( item_resource.id == item_id and item_resource.amount < item_resource.max_stack_size ) ) ## Removes an amount of items from a specific slot ## If the amount exceeds the amount of the slot, will NOT remove from other stacks func _remove_from_slot(slot_index: int, amount: int) -> void: if slot_index >= max_inventory_items: printerr("Slot Index ", slot_index, " out of inventory range") return inventory[slot_index].amount -= amount if inventory[slot_index].amount <= 0: inventory[slot_index] = null inventory_slot_updated.emit(slot_index) func _update_cache_total(item_id: String, amount: int) -> void: if not _inventory_cache.get(item_id): _inventory_cache[item_id] = {"total": 0} _inventory_cache[item_id].total += amount func _on_add_to_inventory(item_id: String, amount: int = 1) -> void: if not DBItems.data.get(item_id): printerr("Cannot add item, ", item_id, ", to inventory. Could not find item within DBItems.data.") return if amount < 1: printerr("Cannot add item, ", item_id, ", to inventory. Amount to add cannot be less then 1.") return var item_resource: DBItemResource = DBItems.data[item_id] # This logic seems overly complicated and is a mess. # Should look into fixing/simplifying this in the future var amount_remaining: int = amount while amount_remaining > 0: var first_stack_index: int = inventory.find_custom(_find_stacks_with_space.bind(item_id)) var stack_resource: DBItemResource if first_stack_index == -1 and inventory.size() < max_inventory_items: # No existing stack and empty slots available stack_resource = item_resource.duplicate() inventory.append(stack_resource) if amount_remaining <= stack_resource.max_stack_size: _update_cache_total(item_id, amount_remaining) inventory[-1].amount += amount_remaining amount_remaining = 0 else: _update_cache_total(item_id, stack_resource.max_stack_size) inventory[-1].amount = stack_resource.max_stack_size amount_remaining -= stack_resource.max_stack_size inventory_slot_updated.emit(inventory.size() - 1) elif first_stack_index > -1: if inventory[first_stack_index] == null: # Empty slot inventory[first_stack_index] = item_resource.duplicate() stack_resource = inventory[first_stack_index] var current_amount: int = stack_resource.amount var total_amount: int = current_amount + amount_remaining if total_amount < stack_resource.max_stack_size: # Stack not full and has space _update_cache_total(item_id, amount_remaining) inventory[first_stack_index].amount = total_amount amount_remaining = 0 else: _update_cache_total(item_id, stack_resource.max_stack_size - current_amount) inventory[first_stack_index].amount = stack_resource.max_stack_size amount_remaining -= stack_resource.max_stack_size inventory_slot_updated.emit(first_stack_index) item_added.emit(item_id, amount - amount_remaining) func _on_remove_from_inventory(item_id: String, amount: int = 1) -> void: var amount_remaining: int = amount while amount_remaining > 0: var last_stack_index: int = inventory.rfind_custom(_find_stacks_by_id.bind(item_id)) if last_stack_index > -1: var stack_resource: DBItemResource = inventory[last_stack_index] var current_stack_amount: int = stack_resource.amount var total_amount: int = current_stack_amount - amount_remaining if total_amount > 0: # Stack will not be empty after removing _update_cache_total(item_id, -amount_remaining) inventory[last_stack_index].amount -= amount_remaining amount_remaining = 0 else: var to_remove: int = stack_resource.max_stack_size - current_stack_amount _update_cache_total(item_id, -amount_remaining) inventory[last_stack_index] = null amount_remaining -= to_remove inventory_slot_updated.emit(last_stack_index) else: # Received more to remove than we have stacks for amount_remaining = 0 item_removed.emit(item_id, amount - amount_remaining) func _on_clear_inventory() -> void: inventory.clear() _inventory_cache.clear() func _on_item_dropped(item: DBItemResource) -> void: _on_remove_from_inventory(item.id, 1) func _on_item_picked_up(item: DBItemResource) -> void: _on_add_to_inventory(item.id, 1) func _on_quick_slot_selected(slot_index: int) -> void: selected_quick_slot = slot_index func _on_remove_from_slot(slot_index: int, amount: int) -> void: _remove_from_slot(slot_index, amount) func _on_remove_from_quickslot(amount: int) -> void: _remove_from_slot(selected_quick_slot, amount)