Add Cooler, Bag, and compact hotbar systems
This commit is contained in:
parent
8cd3e83285
commit
49e7faaf84
32 changed files with 1613 additions and 99 deletions
104
inventory/player_bag.gd
Normal file
104
inventory/player_bag.gd
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
class_name PlayerBag
|
||||
extends Node
|
||||
|
||||
const ItemCatalogType = preload("res://items/item_catalog.gd")
|
||||
const ItemDataType = preload("res://items/item_data.gd")
|
||||
const OwnedItemType = preload("res://items/owned_item.gd")
|
||||
|
||||
signal contents_changed
|
||||
|
||||
var _catalog: ItemCatalogType
|
||||
var _items: Array[OwnedItemType] = []
|
||||
|
||||
|
||||
func setup(catalog: ItemCatalogType) -> void:
|
||||
_catalog = catalog
|
||||
|
||||
|
||||
func add_item(item_id: StringName, quantity: int = 1) -> bool:
|
||||
var item: ItemDataType = _resolve_valid_item(item_id)
|
||||
if item == null or quantity <= 0:
|
||||
return false
|
||||
var existing: OwnedItemType = get_owned_item(item_id)
|
||||
if existing != null:
|
||||
if not item.stackable:
|
||||
return false
|
||||
if existing.quantity + quantity > item.max_stack:
|
||||
return false
|
||||
existing.quantity += quantity
|
||||
else:
|
||||
if quantity > (item.max_stack if item.stackable else 1):
|
||||
return false
|
||||
var owned := OwnedItemType.new()
|
||||
owned.item_id = item_id
|
||||
owned.quantity = quantity
|
||||
_items.append(owned)
|
||||
contents_changed.emit()
|
||||
return true
|
||||
|
||||
|
||||
func remove_item(item_id: StringName, quantity: int = 1) -> bool:
|
||||
if quantity <= 0:
|
||||
return false
|
||||
for index: int in range(_items.size()):
|
||||
var owned: OwnedItemType = _items[index]
|
||||
if owned.item_id != item_id or owned.quantity < quantity:
|
||||
continue
|
||||
owned.quantity -= quantity
|
||||
if owned.quantity == 0:
|
||||
_items.remove_at(index)
|
||||
contents_changed.emit()
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func get_quantity(item_id: StringName) -> int:
|
||||
var owned: OwnedItemType = get_owned_item(item_id)
|
||||
return owned.quantity if owned != null else 0
|
||||
|
||||
|
||||
func owns_item(item_id: StringName) -> bool:
|
||||
return get_quantity(item_id) > 0
|
||||
|
||||
|
||||
func get_owned_item(item_id: StringName) -> OwnedItemType:
|
||||
if item_id.is_empty():
|
||||
return null
|
||||
for owned: OwnedItemType in _items:
|
||||
if owned != null and owned.item_id == item_id:
|
||||
return owned
|
||||
return null
|
||||
|
||||
|
||||
func get_all_items() -> Array[OwnedItemType]:
|
||||
var result: Array[OwnedItemType] = []
|
||||
for owned: OwnedItemType in _items:
|
||||
if owned != null:
|
||||
result.append(owned.duplicate_record())
|
||||
return result
|
||||
|
||||
|
||||
func replace_all_items(items: Array[OwnedItemType]) -> bool:
|
||||
var validated: Array[OwnedItemType] = []
|
||||
var seen: Dictionary[StringName, bool] = {}
|
||||
for owned: OwnedItemType in items:
|
||||
if owned == null or not owned.is_valid() or seen.has(owned.item_id):
|
||||
return false
|
||||
var item: ItemDataType = _resolve_valid_item(owned.item_id)
|
||||
if item == null:
|
||||
return false
|
||||
var maximum: int = item.max_stack if item.stackable else 1
|
||||
if owned.quantity > maximum:
|
||||
return false
|
||||
seen[owned.item_id] = true
|
||||
validated.append(owned.duplicate_record())
|
||||
_items = validated
|
||||
contents_changed.emit()
|
||||
return true
|
||||
|
||||
|
||||
func _resolve_valid_item(item_id: StringName) -> ItemDataType:
|
||||
if _catalog == null:
|
||||
return null
|
||||
var item: ItemDataType = _catalog.get_item_by_id(item_id)
|
||||
return item if item != null and item.is_valid() else null
|
||||
1
inventory/player_bag.gd.uid
Normal file
1
inventory/player_bag.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://c2033c62stp2d
|
||||
171
inventory/player_hotbar.gd
Normal file
171
inventory/player_hotbar.gd
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
class_name PlayerHotbar
|
||||
extends Node
|
||||
|
||||
const SLOT_COUNT: int = 9
|
||||
const ItemCatalogType = preload("res://items/item_catalog.gd")
|
||||
const ItemDataType = preload("res://items/item_data.gd")
|
||||
const PlayerBagType = preload("res://inventory/player_bag.gd")
|
||||
|
||||
signal slots_changed
|
||||
signal selected_slot_changed(slot_index: int, item_id: StringName)
|
||||
|
||||
var _bag: PlayerBagType
|
||||
var _catalog: ItemCatalogType
|
||||
var _slots: Array[StringName] = []
|
||||
var _selected_slot: int = 0
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_slots.resize(SLOT_COUNT)
|
||||
_slots.fill(StringName())
|
||||
|
||||
|
||||
func setup(bag: PlayerBagType, catalog: ItemCatalogType) -> void:
|
||||
_bag = bag
|
||||
_catalog = catalog
|
||||
if _bag != null and not _bag.contents_changed.is_connected(
|
||||
_on_bag_contents_changed
|
||||
):
|
||||
_bag.contents_changed.connect(_on_bag_contents_changed)
|
||||
_validate_assignments()
|
||||
|
||||
|
||||
func assign_item(slot_index: int, item_id: StringName) -> bool:
|
||||
if not _is_slot_valid(slot_index) or not _can_assign(item_id):
|
||||
return false
|
||||
if _slots[slot_index] == item_id:
|
||||
return true
|
||||
for index: int in range(SLOT_COUNT):
|
||||
if index != slot_index and _slots[index] == item_id:
|
||||
_slots[index] = StringName()
|
||||
_slots[slot_index] = item_id
|
||||
slots_changed.emit()
|
||||
if slot_index == _selected_slot:
|
||||
selected_slot_changed.emit(_selected_slot, item_id)
|
||||
return true
|
||||
|
||||
|
||||
func clear_slot(slot_index: int) -> bool:
|
||||
if not _is_slot_valid(slot_index) or _slots[slot_index].is_empty():
|
||||
return false
|
||||
_slots[slot_index] = StringName()
|
||||
slots_changed.emit()
|
||||
if slot_index == _selected_slot:
|
||||
selected_slot_changed.emit(_selected_slot, StringName())
|
||||
return true
|
||||
|
||||
|
||||
func swap_slots(from_index: int, to_index: int) -> bool:
|
||||
if (
|
||||
not _is_slot_valid(from_index)
|
||||
or not _is_slot_valid(to_index)
|
||||
or from_index == to_index
|
||||
):
|
||||
return false
|
||||
var temporary: StringName = _slots[from_index]
|
||||
_slots[from_index] = _slots[to_index]
|
||||
_slots[to_index] = temporary
|
||||
slots_changed.emit()
|
||||
if from_index == _selected_slot or to_index == _selected_slot:
|
||||
selected_slot_changed.emit(
|
||||
_selected_slot,
|
||||
_slots[_selected_slot]
|
||||
)
|
||||
return true
|
||||
|
||||
|
||||
func select_slot(slot_index: int) -> bool:
|
||||
if not _is_slot_valid(slot_index):
|
||||
return false
|
||||
if _selected_slot == slot_index:
|
||||
return true
|
||||
_selected_slot = slot_index
|
||||
selected_slot_changed.emit(_selected_slot, _slots[_selected_slot])
|
||||
return true
|
||||
|
||||
|
||||
func cycle_selection(direction: int) -> bool:
|
||||
if direction == 0:
|
||||
return false
|
||||
return select_slot(
|
||||
posmod(_selected_slot + signi(direction), SLOT_COUNT)
|
||||
)
|
||||
|
||||
|
||||
func get_selected_slot() -> int:
|
||||
return _selected_slot
|
||||
|
||||
|
||||
func get_selected_item_id() -> StringName:
|
||||
return _slots[_selected_slot]
|
||||
|
||||
|
||||
func get_item_id(slot_index: int) -> StringName:
|
||||
return _slots[slot_index] if _is_slot_valid(slot_index) else StringName()
|
||||
|
||||
|
||||
func get_slots() -> Array[StringName]:
|
||||
return _slots.duplicate()
|
||||
|
||||
|
||||
func replace_state(
|
||||
slots: Array[StringName],
|
||||
selected_slot: int,
|
||||
) -> bool:
|
||||
var normalized: Array[StringName] = []
|
||||
normalized.resize(SLOT_COUNT)
|
||||
normalized.fill(StringName())
|
||||
var seen_assignments: Dictionary[StringName, bool] = {}
|
||||
for index: int in range(mini(slots.size(), SLOT_COUNT)):
|
||||
var item_id: StringName = slots[index]
|
||||
if item_id.is_empty():
|
||||
continue
|
||||
if _can_assign(item_id) and not seen_assignments.has(item_id):
|
||||
normalized[index] = item_id
|
||||
seen_assignments[item_id] = true
|
||||
_slots = normalized
|
||||
_selected_slot = clampi(selected_slot, 0, SLOT_COUNT - 1)
|
||||
slots_changed.emit()
|
||||
selected_slot_changed.emit(
|
||||
_selected_slot,
|
||||
_slots[_selected_slot]
|
||||
)
|
||||
return true
|
||||
|
||||
|
||||
func has_any_assignment() -> bool:
|
||||
for item_id: StringName in _slots:
|
||||
if not item_id.is_empty():
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func _can_assign(item_id: StringName) -> bool:
|
||||
if item_id.is_empty() or _bag == null or not _bag.owns_item(item_id):
|
||||
return false
|
||||
if _catalog == null:
|
||||
return false
|
||||
var item: ItemDataType = _catalog.get_item_by_id(item_id)
|
||||
return item != null and item.is_valid() and item.hotbar_allowed
|
||||
|
||||
|
||||
func _validate_assignments() -> void:
|
||||
var changed: bool = false
|
||||
for index: int in range(SLOT_COUNT):
|
||||
if not _slots[index].is_empty() and not _can_assign(_slots[index]):
|
||||
_slots[index] = StringName()
|
||||
changed = true
|
||||
if changed:
|
||||
slots_changed.emit()
|
||||
selected_slot_changed.emit(
|
||||
_selected_slot,
|
||||
_slots[_selected_slot]
|
||||
)
|
||||
|
||||
|
||||
func _on_bag_contents_changed() -> void:
|
||||
_validate_assignments()
|
||||
|
||||
|
||||
func _is_slot_valid(slot_index: int) -> bool:
|
||||
return slot_index >= 0 and slot_index < SLOT_COUNT
|
||||
1
inventory/player_hotbar.gd.uid
Normal file
1
inventory/player_hotbar.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://bl60rgr4hqxdc
|
||||
Loading…
Add table
Add a link
Reference in a new issue