268 lines
7.3 KiB
GDScript
268 lines
7.3 KiB
GDScript
class_name PlayerInventory
|
|
extends RefCounted
|
|
|
|
const BASE_INVENTORY_SIZE := 16
|
|
const BACKPACK_BONUS_SLOTS := 4
|
|
const MAX_INVENTORY_SIZE := BASE_INVENTORY_SIZE + BACKPACK_BONUS_SLOTS
|
|
var slots: Array[InventorySlot] = []
|
|
var equipped_weapon: InventorySlot = InventorySlot.new()
|
|
var equipped_hat: InventorySlot = InventorySlot.new()
|
|
var equipped_backpack: InventorySlot = InventorySlot.new()
|
|
|
|
|
|
func _init():
|
|
_initialize_slots()
|
|
|
|
|
|
func _initialize_slots():
|
|
slots.clear()
|
|
for i in range(MAX_INVENTORY_SIZE):
|
|
slots.append(InventorySlot.new())
|
|
|
|
|
|
func get_active_slot_count() -> int:
|
|
return MAX_INVENTORY_SIZE if not equipped_backpack.is_empty() else BASE_INVENTORY_SIZE
|
|
|
|
|
|
func is_slot_active(index: int) -> bool:
|
|
return index >= 0 and index < get_active_slot_count()
|
|
|
|
|
|
func get_slot(index: int) -> InventorySlot:
|
|
if index >= 0 and index < slots.size():
|
|
return slots[index]
|
|
return null
|
|
|
|
|
|
func get_equipped_slot(item_type: Item.ItemType) -> InventorySlot:
|
|
match item_type:
|
|
Item.ItemType.WEAPON:
|
|
return equipped_weapon
|
|
Item.ItemType.HAT:
|
|
return equipped_hat
|
|
Item.ItemType.BACKPACK:
|
|
return equipped_backpack
|
|
_:
|
|
return null
|
|
|
|
|
|
func equip_from_slot(index: int, item_type: Item.ItemType) -> bool:
|
|
if not is_slot_active(index):
|
|
return false
|
|
|
|
var backpack_slot: InventorySlot = get_slot(index)
|
|
var equipped_slot: InventorySlot = get_equipped_slot(item_type)
|
|
if not backpack_slot or backpack_slot.is_empty() or not equipped_slot:
|
|
return false
|
|
|
|
var item: Item = ItemDatabase.get_item(backpack_slot.item_id)
|
|
if not item or item.item_type != item_type:
|
|
return false
|
|
|
|
var new_item_id: String = backpack_slot.item_id
|
|
var new_quantity: int = backpack_slot.quantity
|
|
backpack_slot.item_id = equipped_slot.item_id
|
|
backpack_slot.quantity = equipped_slot.quantity
|
|
equipped_slot.item_id = new_item_id
|
|
equipped_slot.quantity = new_quantity
|
|
if backpack_slot.is_empty():
|
|
backpack_slot.clear()
|
|
return true
|
|
|
|
|
|
func unequip_to_slot(item_type: Item.ItemType, destination_index: int = -1) -> bool:
|
|
var equipped_slot: InventorySlot = get_equipped_slot(item_type)
|
|
if not equipped_slot or equipped_slot.is_empty():
|
|
return false
|
|
if item_type == Item.ItemType.BACKPACK:
|
|
return _unequip_backpack_to_slot(equipped_slot, destination_index)
|
|
|
|
if destination_index < 0:
|
|
destination_index = get_first_empty_slot()
|
|
if not is_slot_active(destination_index):
|
|
return false
|
|
var destination: InventorySlot = get_slot(destination_index)
|
|
if not destination or not destination.is_empty():
|
|
return false
|
|
|
|
destination.item_id = equipped_slot.item_id
|
|
destination.quantity = equipped_slot.quantity
|
|
equipped_slot.clear()
|
|
return true
|
|
|
|
|
|
func _unequip_backpack_to_slot(equipped_slot: InventorySlot, destination_index: int) -> bool:
|
|
if destination_index < 0:
|
|
destination_index = _get_first_empty_base_slot()
|
|
if destination_index < 0 or destination_index >= BASE_INVENTORY_SIZE:
|
|
return false
|
|
|
|
var destination := get_slot(destination_index)
|
|
if not destination or not destination.is_empty():
|
|
return false
|
|
|
|
var occupied_bonus_slots: Array[int] = []
|
|
for i in range(BASE_INVENTORY_SIZE, MAX_INVENTORY_SIZE):
|
|
if not slots[i].is_empty():
|
|
occupied_bonus_slots.append(i)
|
|
|
|
var available_base_slots: Array[int] = []
|
|
for i in range(BASE_INVENTORY_SIZE):
|
|
if i != destination_index and slots[i].is_empty():
|
|
available_base_slots.append(i)
|
|
if occupied_bonus_slots.size() > available_base_slots.size():
|
|
return false
|
|
|
|
for i in range(occupied_bonus_slots.size()):
|
|
var source := slots[occupied_bonus_slots[i]]
|
|
var target := slots[available_base_slots[i]]
|
|
target.item_id = source.item_id
|
|
target.quantity = source.quantity
|
|
source.clear()
|
|
|
|
destination.item_id = equipped_slot.item_id
|
|
destination.quantity = equipped_slot.quantity
|
|
equipped_slot.clear()
|
|
return true
|
|
|
|
|
|
func _get_first_empty_base_slot() -> int:
|
|
for i in range(BASE_INVENTORY_SIZE):
|
|
if slots[i].is_empty():
|
|
return i
|
|
return -1
|
|
|
|
|
|
func add_item(item: Item, quantity: int = 1) -> int:
|
|
var remaining = quantity
|
|
var active_slot_count := get_active_slot_count()
|
|
|
|
if item.stackable:
|
|
for i in range(active_slot_count):
|
|
var slot := slots[i]
|
|
if slot.item_id == item.id:
|
|
remaining = slot.add_item(item, remaining)
|
|
if remaining <= 0:
|
|
break
|
|
|
|
if remaining > 0:
|
|
for i in range(active_slot_count):
|
|
var slot := slots[i]
|
|
if slot.is_empty():
|
|
remaining = slot.add_item(item, remaining)
|
|
if remaining <= 0:
|
|
break
|
|
|
|
return remaining
|
|
|
|
|
|
func remove_item(item_id: String, quantity: int = 1) -> int:
|
|
var removed = 0
|
|
for i in range(get_active_slot_count()):
|
|
var slot := slots[i]
|
|
if slot.item_id == item_id:
|
|
var slot_removed = slot.remove_item(quantity - removed)
|
|
removed += slot_removed
|
|
if removed >= quantity:
|
|
break
|
|
return removed
|
|
|
|
|
|
func move_item(from_index: int, to_index: int, quantity: int = -1) -> bool:
|
|
if (
|
|
from_index == to_index
|
|
or not is_slot_active(from_index)
|
|
or not is_slot_active(to_index)
|
|
or quantity == 0
|
|
or quantity < -1
|
|
):
|
|
return false
|
|
|
|
var from_slot = get_slot(from_index)
|
|
var to_slot = get_slot(to_index)
|
|
|
|
if not from_slot or not to_slot or from_slot.is_empty():
|
|
return false
|
|
|
|
var move_amount = from_slot.quantity if quantity == -1 else quantity
|
|
move_amount = min(move_amount, from_slot.quantity)
|
|
|
|
var item = ItemDatabase.get_item(from_slot.item_id)
|
|
if not item:
|
|
return false
|
|
|
|
var remaining = to_slot.add_item(item, move_amount)
|
|
var moved_amount = move_amount - remaining
|
|
if moved_amount > 0:
|
|
from_slot.remove_item(moved_amount)
|
|
|
|
return moved_amount > 0
|
|
|
|
|
|
func swap_items(from_index: int, to_index: int) -> bool:
|
|
if not is_slot_active(from_index) or not is_slot_active(to_index):
|
|
return false
|
|
|
|
var from_slot = get_slot(from_index)
|
|
var to_slot = get_slot(to_index)
|
|
|
|
if not from_slot or not to_slot:
|
|
return false
|
|
|
|
if from_slot.item_id == to_slot.item_id and not from_slot.is_empty() and not to_slot.is_empty():
|
|
var item = ItemDatabase.get_item(from_slot.item_id)
|
|
if item and item.stackable:
|
|
var total_quantity = from_slot.quantity + to_slot.quantity
|
|
if total_quantity <= item.max_stack:
|
|
to_slot.quantity = total_quantity
|
|
from_slot.clear()
|
|
return true
|
|
var space_available = item.max_stack - to_slot.quantity
|
|
var amount_to_move = min(space_available, from_slot.quantity)
|
|
to_slot.quantity += amount_to_move
|
|
from_slot.quantity -= amount_to_move
|
|
if from_slot.quantity <= 0:
|
|
from_slot.clear()
|
|
return true
|
|
|
|
var temp_item_id = from_slot.item_id
|
|
var temp_quantity = from_slot.quantity
|
|
|
|
from_slot.item_id = to_slot.item_id
|
|
from_slot.quantity = to_slot.quantity
|
|
|
|
to_slot.item_id = temp_item_id
|
|
to_slot.quantity = temp_quantity
|
|
|
|
return true
|
|
|
|
|
|
func get_first_empty_slot() -> int:
|
|
for i in range(get_active_slot_count()):
|
|
if slots[i].is_empty():
|
|
return i
|
|
return -1
|
|
|
|
|
|
func to_dict() -> Dictionary:
|
|
var data = []
|
|
for slot in slots:
|
|
data.append(slot.to_dict())
|
|
return {
|
|
"slots": data,
|
|
"equipped_weapon": equipped_weapon.to_dict(),
|
|
"equipped_hat": equipped_hat.to_dict(),
|
|
"equipped_backpack": equipped_backpack.to_dict()
|
|
}
|
|
|
|
|
|
func from_dict(data: Dictionary) -> void:
|
|
var slots_data = data.get("slots", [])
|
|
for i in range(min(slots_data.size(), slots.size())):
|
|
slots[i].from_dict(slots_data[i])
|
|
for i in range(slots_data.size(), slots.size()):
|
|
slots[i].clear()
|
|
equipped_weapon.from_dict(data.get("equipped_weapon", {}))
|
|
equipped_hat.from_dict(data.get("equipped_hat", {}))
|
|
equipped_backpack.from_dict(data.get("equipped_backpack", {}))
|