A Minecraft style clone in Godot
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

218 lines
7.7 KiB

extends Node
signal item_dropped(item: DBItemResource)
signal item_picked_up(item: DBItemResource)
signal creative_mode_disabled
signal creative_mode_enabled
signal toggle_creative_mode(enabled: bool)
#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 creative_mode: bool = false
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)
self.toggle_creative_mode.connect(_on_toggle_creative_mode)
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:
if item_slot >= inventory.size(): return ""
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 creative_mode: return
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 creative_mode: return
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 and not creative_mode:
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:
if creative_mode: return
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)
func _on_toggle_creative_mode(enabled: bool) -> void:
creative_mode = enabled
if creative_mode:
creative_mode_enabled.emit()
else:
creative_mode_disabled.emit()