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
|
|
@ -9,6 +9,10 @@ const CatchControllerType = preload("res://fishing/catch_controller.gd")
|
||||||
const FishingContextType = preload("res://fishing/fishing_context.gd")
|
const FishingContextType = preload("res://fishing/fishing_context.gd")
|
||||||
const FishingPresentationType = preload("res://fishing/fishing_presentation.gd")
|
const FishingPresentationType = preload("res://fishing/fishing_presentation.gd")
|
||||||
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
||||||
|
const ItemCatalogType = preload("res://items/item_catalog.gd")
|
||||||
|
const ItemDataType = preload("res://items/item_data.gd")
|
||||||
|
const PlayerBagType = preload("res://inventory/player_bag.gd")
|
||||||
|
const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
|
||||||
const PlayerType = preload("res://player/player.gd")
|
const PlayerType = preload("res://player/player.gd")
|
||||||
const FishableWaterRegionType = preload(
|
const FishableWaterRegionType = preload(
|
||||||
"res://world/fishable_water_region.gd"
|
"res://world/fishable_water_region.gd"
|
||||||
|
|
@ -31,6 +35,7 @@ signal showcase_changed(
|
||||||
visible: bool,
|
visible: bool,
|
||||||
)
|
)
|
||||||
signal bite_activated
|
signal bite_activated
|
||||||
|
signal ready_for_equipment_refresh
|
||||||
|
|
||||||
enum FishingState {
|
enum FishingState {
|
||||||
READY,
|
READY,
|
||||||
|
|
@ -84,6 +89,9 @@ var _local_menu_input_owners: Dictionary[StringName, bool] = {}
|
||||||
var _local_player: PlayerType
|
var _local_player: PlayerType
|
||||||
var _local_inventory: FishInventoryType
|
var _local_inventory: FishInventoryType
|
||||||
var _local_collection_log: CollectionLogType
|
var _local_collection_log: CollectionLogType
|
||||||
|
var _local_bag: PlayerBagType
|
||||||
|
var _local_hotbar: PlayerHotbarType
|
||||||
|
var _item_catalog: ItemCatalogType
|
||||||
var _active_player: PlayerType
|
var _active_player: PlayerType
|
||||||
var _state_time_remaining: float = 0.0
|
var _state_time_remaining: float = 0.0
|
||||||
var _cast_charge: float = 0.0
|
var _cast_charge: float = 0.0
|
||||||
|
|
@ -121,10 +129,16 @@ func setup(
|
||||||
local_player: PlayerType,
|
local_player: PlayerType,
|
||||||
local_inventory: FishInventoryType,
|
local_inventory: FishInventoryType,
|
||||||
local_collection_log: CollectionLogType,
|
local_collection_log: CollectionLogType,
|
||||||
|
local_bag: PlayerBagType,
|
||||||
|
local_hotbar: PlayerHotbarType,
|
||||||
|
item_catalog: ItemCatalogType,
|
||||||
) -> void:
|
) -> void:
|
||||||
_local_player = local_player
|
_local_player = local_player
|
||||||
_local_inventory = local_inventory
|
_local_inventory = local_inventory
|
||||||
_local_collection_log = local_collection_log
|
_local_collection_log = local_collection_log
|
||||||
|
_local_bag = local_bag
|
||||||
|
_local_hotbar = local_hotbar
|
||||||
|
_item_catalog = item_catalog
|
||||||
|
|
||||||
|
|
||||||
func can_open_player_menu() -> bool:
|
func can_open_player_menu() -> bool:
|
||||||
|
|
@ -151,6 +165,15 @@ func can_open_system_menu() -> bool:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func can_change_hotbar_selection() -> bool:
|
||||||
|
return (
|
||||||
|
_gameplay_input_enabled
|
||||||
|
and not _external_input_blocked
|
||||||
|
and _local_menu_input_owners.is_empty()
|
||||||
|
and state == FishingState.READY
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func handle_escape_action() -> bool:
|
func handle_escape_action() -> bool:
|
||||||
if not _gameplay_input_enabled or _external_input_blocked:
|
if not _gameplay_input_enabled or _external_input_blocked:
|
||||||
return false
|
return false
|
||||||
|
|
@ -301,6 +324,10 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||||
FishingState.READY:
|
FishingState.READY:
|
||||||
if not _new_cast_press_armed:
|
if not _new_cast_press_armed:
|
||||||
return
|
return
|
||||||
|
if not has_active_fishing_rod():
|
||||||
|
status_changed.emit("Select a fishing rod to cast.")
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
return
|
||||||
_begin_aiming(_local_player)
|
_begin_aiming(_local_player)
|
||||||
FishingState.WAITING_FOR_BITE:
|
FishingState.WAITING_FOR_BITE:
|
||||||
_withdrawal_input_held = true
|
_withdrawal_input_held = true
|
||||||
|
|
@ -354,6 +381,35 @@ func _begin_aiming(player: PlayerType) -> void:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func has_active_fishing_rod() -> bool:
|
||||||
|
if (
|
||||||
|
_local_bag == null
|
||||||
|
or _local_hotbar == null
|
||||||
|
or _item_catalog == null
|
||||||
|
):
|
||||||
|
return false
|
||||||
|
var item_id: StringName = _local_hotbar.get_selected_item_id()
|
||||||
|
if item_id.is_empty() or not _local_bag.owns_item(item_id):
|
||||||
|
return false
|
||||||
|
var item: ItemDataType = _item_catalog.get_item_by_id(item_id)
|
||||||
|
return (
|
||||||
|
item != null
|
||||||
|
and item.is_valid()
|
||||||
|
and item.category == ItemDataType.Category.ROD
|
||||||
|
and item.usable
|
||||||
|
and item.equippable
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func is_fighting() -> bool:
|
||||||
|
return state == FishingState.FIGHTING
|
||||||
|
|
||||||
|
|
||||||
|
func refresh_active_item_status() -> void:
|
||||||
|
if state == FishingState.READY and has_active_fishing_rod():
|
||||||
|
status_changed.emit("")
|
||||||
|
|
||||||
|
|
||||||
func _update_cast_charge(delta: float) -> void:
|
func _update_cast_charge(delta: float) -> void:
|
||||||
if state != FishingState.AIMING_CAST:
|
if state != FishingState.AIMING_CAST:
|
||||||
return
|
return
|
||||||
|
|
@ -581,20 +637,6 @@ func _on_catch_encounter_updated(
|
||||||
_bobber_water_position,
|
_bobber_water_position,
|
||||||
Input.is_action_pressed("fish_primary")
|
Input.is_action_pressed("fish_primary")
|
||||||
)
|
)
|
||||||
if (
|
|
||||||
active_barrier_index >= 0
|
|
||||||
and active_barrier_index < barrier_health.size()
|
|
||||||
and active_barrier_index < barrier_max_health.size()
|
|
||||||
):
|
|
||||||
status_changed.emit(
|
|
||||||
"Barrier: %d / %d"
|
|
||||||
% [
|
|
||||||
barrier_health[active_barrier_index],
|
|
||||||
barrier_max_health[active_barrier_index],
|
|
||||||
]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
status_changed.emit("Hold left click to reel")
|
|
||||||
|
|
||||||
|
|
||||||
func _on_catch_completed() -> void:
|
func _on_catch_completed() -> void:
|
||||||
|
|
@ -731,6 +773,7 @@ func _cleanup_attempt(
|
||||||
state = FishingState.READY
|
state = FishingState.READY
|
||||||
_cooldown_status = ""
|
_cooldown_status = ""
|
||||||
status_changed.emit("")
|
status_changed.emit("")
|
||||||
|
ready_for_equipment_refresh.emit()
|
||||||
else:
|
else:
|
||||||
state = FishingState.COOLDOWN
|
state = FishingState.COOLDOWN
|
||||||
_state_time_remaining = cooldown_duration
|
_state_time_remaining = cooldown_duration
|
||||||
|
|
@ -743,6 +786,7 @@ func _return_to_ready() -> void:
|
||||||
_state_time_remaining = 0.0
|
_state_time_remaining = 0.0
|
||||||
_cooldown_status = ""
|
_cooldown_status = ""
|
||||||
status_changed.emit("")
|
status_changed.emit("")
|
||||||
|
ready_for_equipment_refresh.emit()
|
||||||
|
|
||||||
|
|
||||||
func _cancel_attempt() -> void:
|
func _cancel_attempt() -> void:
|
||||||
|
|
|
||||||
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
|
||||||
26
items/catalog/basic_fishing_rod.tres
Normal file
26
items/catalog/basic_fishing_rod.tres
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
[gd_resource type="Resource" script_class="ItemData" load_steps=4 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://items/item_data.gd" id="1_script"]
|
||||||
|
|
||||||
|
[sub_resource type="Gradient" id="Gradient_rod"]
|
||||||
|
colors = PackedColorArray(0.302, 0.184, 0.11, 1, 0.957, 0.827, 0.369, 1, 0.439, 0.839, 0.82, 1)
|
||||||
|
|
||||||
|
[sub_resource type="GradientTexture2D" id="GradientTexture_rod"]
|
||||||
|
gradient = SubResource("Gradient_rod")
|
||||||
|
width = 128
|
||||||
|
height = 128
|
||||||
|
fill_from = Vector2(0.15, 0.85)
|
||||||
|
fill_to = Vector2(0.85, 0.15)
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1_script")
|
||||||
|
item_id = &"basic_fishing_rod"
|
||||||
|
display_name = "Basic Fishing Rod"
|
||||||
|
description = "A dependable starter rod. Select it to cast."
|
||||||
|
category = 0
|
||||||
|
icon = SubResource("GradientTexture_rod")
|
||||||
|
stackable = false
|
||||||
|
max_stack = 1
|
||||||
|
usable = true
|
||||||
|
equippable = true
|
||||||
|
hotbar_allowed = true
|
||||||
8
items/catalog/item_catalog.tres
Normal file
8
items/catalog/item_catalog.tres
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
[gd_resource type="Resource" script_class="ItemCatalog" load_steps=3 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://items/item_catalog.gd" id="1_script"]
|
||||||
|
[ext_resource type="Resource" path="res://items/catalog/basic_fishing_rod.tres" id="2_rod"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1_script")
|
||||||
|
items = [ExtResource("2_rod")]
|
||||||
30
items/item_catalog.gd
Normal file
30
items/item_catalog.gd
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
class_name ItemCatalog
|
||||||
|
extends Resource
|
||||||
|
|
||||||
|
const ItemDataType = preload("res://items/item_data.gd")
|
||||||
|
|
||||||
|
@export var items: Array[ItemDataType] = []
|
||||||
|
|
||||||
|
|
||||||
|
func get_item_by_id(item_id: StringName) -> ItemDataType:
|
||||||
|
if item_id.is_empty():
|
||||||
|
return null
|
||||||
|
for item: ItemDataType in items:
|
||||||
|
if item != null and item.item_id == item_id:
|
||||||
|
return item
|
||||||
|
return null
|
||||||
|
|
||||||
|
|
||||||
|
func get_valid_items() -> Array[ItemDataType]:
|
||||||
|
var result: Array[ItemDataType] = []
|
||||||
|
var seen: Dictionary[StringName, bool] = {}
|
||||||
|
for item: ItemDataType in items:
|
||||||
|
if (
|
||||||
|
item == null
|
||||||
|
or not item.is_valid()
|
||||||
|
or seen.has(item.item_id)
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
seen[item.item_id] = true
|
||||||
|
result.append(item)
|
||||||
|
return result
|
||||||
1
items/item_catalog.gd.uid
Normal file
1
items/item_catalog.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://citlop0eiu8rx
|
||||||
36
items/item_data.gd
Normal file
36
items/item_data.gd
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
class_name ItemData
|
||||||
|
extends Resource
|
||||||
|
|
||||||
|
enum Category {
|
||||||
|
ROD,
|
||||||
|
TOOL,
|
||||||
|
BAIT,
|
||||||
|
CONSUMABLE,
|
||||||
|
UTILITY,
|
||||||
|
QUEST,
|
||||||
|
COSMETIC,
|
||||||
|
}
|
||||||
|
|
||||||
|
@export var item_id: StringName
|
||||||
|
@export var display_name: String
|
||||||
|
@export_multiline var description: String
|
||||||
|
@export var category: Category = Category.UTILITY
|
||||||
|
@export var icon: Texture2D
|
||||||
|
@export var stackable: bool = false
|
||||||
|
@export_range(1, 999, 1) var max_stack: int = 1
|
||||||
|
@export var usable: bool = false
|
||||||
|
@export var equippable: bool = false
|
||||||
|
@export var hotbar_allowed: bool = false
|
||||||
|
|
||||||
|
|
||||||
|
func is_valid() -> bool:
|
||||||
|
return (
|
||||||
|
not item_id.is_empty()
|
||||||
|
and not display_name.strip_edges().is_empty()
|
||||||
|
and max_stack >= 1
|
||||||
|
and (stackable or max_stack == 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func get_category_name() -> String:
|
||||||
|
return Category.keys()[category].capitalize()
|
||||||
1
items/item_data.gd.uid
Normal file
1
items/item_data.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://cah5thda5aohb
|
||||||
20
items/owned_item.gd
Normal file
20
items/owned_item.gd
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
class_name OwnedItem
|
||||||
|
extends Resource
|
||||||
|
|
||||||
|
@export var item_id: StringName
|
||||||
|
@export var quantity: int = 1
|
||||||
|
|
||||||
|
|
||||||
|
func is_valid() -> bool:
|
||||||
|
return not item_id.is_empty() and quantity > 0
|
||||||
|
|
||||||
|
|
||||||
|
func duplicate_record():
|
||||||
|
return duplicate(true)
|
||||||
|
|
||||||
|
|
||||||
|
func to_save_dict() -> Dictionary:
|
||||||
|
return {
|
||||||
|
"item_id": String(item_id),
|
||||||
|
"quantity": quantity,
|
||||||
|
}
|
||||||
1
items/owned_item.gd.uid
Normal file
1
items/owned_item.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://c7julskwitfxi
|
||||||
56
main/main.gd
56
main/main.gd
|
|
@ -18,9 +18,12 @@ const PlayerSettingsManagerType = preload(
|
||||||
const PlayerSettingsType = preload("res://settings/player_settings.gd")
|
const PlayerSettingsType = preload("res://settings/player_settings.gd")
|
||||||
const TitleScreenType = preload("res://ui/title_screen.gd")
|
const TitleScreenType = preload("res://ui/title_screen.gd")
|
||||||
const PauseMenuType = preload("res://ui/pause_menu.gd")
|
const PauseMenuType = preload("res://ui/pause_menu.gd")
|
||||||
|
const ItemCatalogType = preload("res://items/item_catalog.gd")
|
||||||
|
const ItemDataType = preload("res://items/item_data.gd")
|
||||||
|
|
||||||
@export var fish_catalog: FishPoolType
|
@export var fish_catalog: FishPoolType
|
||||||
@export var pelican_buyer_profile: FishBuyerProfileType
|
@export var pelican_buyer_profile: FishBuyerProfileType
|
||||||
|
@export var item_catalog: ItemCatalogType
|
||||||
|
|
||||||
@onready var _test_world: TestWorldType = $TestWorld
|
@onready var _test_world: TestWorldType = $TestWorld
|
||||||
@onready var _player: PlayerType = %Player
|
@onready var _player: PlayerType = %Player
|
||||||
|
|
@ -38,17 +41,25 @@ func _ready() -> void:
|
||||||
_player.inventory,
|
_player.inventory,
|
||||||
_player.wallet
|
_player.wallet
|
||||||
)
|
)
|
||||||
|
_player.bag.setup(item_catalog)
|
||||||
|
_player.hotbar.setup(_player.bag, item_catalog)
|
||||||
_save_manager.setup(
|
_save_manager.setup(
|
||||||
_player.inventory,
|
_player.inventory,
|
||||||
_player.collection_log,
|
_player.collection_log,
|
||||||
_player.wallet,
|
_player.wallet,
|
||||||
fish_catalog
|
fish_catalog,
|
||||||
|
_player.bag,
|
||||||
|
_player.hotbar,
|
||||||
|
item_catalog
|
||||||
)
|
)
|
||||||
_save_manager.set_autosave_enabled(false)
|
_save_manager.set_autosave_enabled(false)
|
||||||
_fishing_spot.setup(
|
_fishing_spot.setup(
|
||||||
_player,
|
_player,
|
||||||
_player.inventory,
|
_player.inventory,
|
||||||
_player.collection_log
|
_player.collection_log,
|
||||||
|
_player.bag,
|
||||||
|
_player.hotbar,
|
||||||
|
item_catalog
|
||||||
)
|
)
|
||||||
_game_ui.setup(
|
_game_ui.setup(
|
||||||
_player,
|
_player,
|
||||||
|
|
@ -58,7 +69,10 @@ func _ready() -> void:
|
||||||
_player.fish_sale_service,
|
_player.fish_sale_service,
|
||||||
pelican_buyer_profile,
|
pelican_buyer_profile,
|
||||||
fish_catalog,
|
fish_catalog,
|
||||||
_fishing_spot
|
_fishing_spot,
|
||||||
|
_player.bag,
|
||||||
|
_player.hotbar,
|
||||||
|
item_catalog
|
||||||
)
|
)
|
||||||
_water_recovery.setup(
|
_water_recovery.setup(
|
||||||
_player,
|
_player,
|
||||||
|
|
@ -93,9 +107,22 @@ func _ready() -> void:
|
||||||
_on_reset_progress_requested
|
_on_reset_progress_requested
|
||||||
)
|
)
|
||||||
pause_menu.quit_requested.connect(_on_quit_requested)
|
pause_menu.quit_requested.connect(_on_quit_requested)
|
||||||
|
pause_menu.menu_visibility_changed.connect(
|
||||||
|
_game_ui.set_system_menu_open
|
||||||
|
)
|
||||||
|
_player.hotbar.selected_slot_changed.connect(
|
||||||
|
_on_active_hotbar_item_changed
|
||||||
|
)
|
||||||
|
_fishing_spot.ready_for_equipment_refresh.connect(
|
||||||
|
_refresh_active_hotbar_item
|
||||||
|
)
|
||||||
_water_recovery.recovery_starting.connect(
|
_water_recovery.recovery_starting.connect(
|
||||||
_on_water_recovery_starting
|
_on_water_recovery_starting
|
||||||
)
|
)
|
||||||
|
_on_active_hotbar_item_changed(
|
||||||
|
_player.hotbar.get_selected_slot(),
|
||||||
|
_player.hotbar.get_selected_item_id()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
|
|
@ -182,6 +209,29 @@ func _on_water_recovery_starting() -> void:
|
||||||
_game_ui.get_pause_menu().close_for_water_recovery()
|
_game_ui.get_pause_menu().close_for_water_recovery()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_active_hotbar_item_changed(
|
||||||
|
_slot_index: int,
|
||||||
|
item_id: StringName,
|
||||||
|
) -> void:
|
||||||
|
if _fishing_spot.state != FishingSpotType.FishingState.READY:
|
||||||
|
return
|
||||||
|
var item = item_catalog.get_item_by_id(item_id)
|
||||||
|
var active_is_rod: bool = (
|
||||||
|
item != null
|
||||||
|
and item.category == ItemDataType.Category.ROD
|
||||||
|
and _player.bag.owns_item(item_id)
|
||||||
|
)
|
||||||
|
_player.set_active_item_is_rod(active_is_rod)
|
||||||
|
_fishing_spot.refresh_active_item_status()
|
||||||
|
|
||||||
|
|
||||||
|
func _refresh_active_hotbar_item() -> void:
|
||||||
|
_on_active_hotbar_item_changed(
|
||||||
|
_player.hotbar.get_selected_slot(),
|
||||||
|
_player.hotbar.get_selected_item_id()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _on_quit_requested() -> void:
|
func _on_quit_requested() -> void:
|
||||||
_settings_manager.save_if_dirty()
|
_settings_manager.save_if_dirty()
|
||||||
if _gameplay_started:
|
if _gameplay_started:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_scene load_steps=11 format=3]
|
[gd_scene load_steps=12 format=3]
|
||||||
|
|
||||||
[ext_resource type="PackedScene" path="res://world/test_world.tscn" id="1_world"]
|
[ext_resource type="PackedScene" path="res://world/test_world.tscn" id="1_world"]
|
||||||
[ext_resource type="PackedScene" path="res://player/player.tscn" id="2_player"]
|
[ext_resource type="PackedScene" path="res://player/player.tscn" id="2_player"]
|
||||||
|
|
@ -10,11 +10,13 @@
|
||||||
[ext_resource type="Script" path="res://world/water_recovery_controller.gd" id="8_recovery"]
|
[ext_resource type="Script" path="res://world/water_recovery_controller.gd" id="8_recovery"]
|
||||||
[ext_resource type="Script" path="res://save/player_save_manager.gd" id="9_save"]
|
[ext_resource type="Script" path="res://save/player_save_manager.gd" id="9_save"]
|
||||||
[ext_resource type="Script" path="res://settings/player_settings_manager.gd" id="10_settings"]
|
[ext_resource type="Script" path="res://settings/player_settings_manager.gd" id="10_settings"]
|
||||||
|
[ext_resource type="Resource" path="res://items/catalog/item_catalog.tres" id="11_items"]
|
||||||
|
|
||||||
[node name="Main" type="Node3D"]
|
[node name="Main" type="Node3D"]
|
||||||
script = ExtResource("3_main")
|
script = ExtResource("3_main")
|
||||||
fish_catalog = ExtResource("6_pool")
|
fish_catalog = ExtResource("6_pool")
|
||||||
pelican_buyer_profile = ExtResource("7_pelicans")
|
pelican_buyer_profile = ExtResource("7_pelicans")
|
||||||
|
item_catalog = ExtResource("11_items")
|
||||||
|
|
||||||
[node name="TestWorld" parent="." instance=ExtResource("1_world")]
|
[node name="TestWorld" parent="." instance=ExtResource("1_world")]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ const CollectionLogType = preload("res://collection/collection_log.gd")
|
||||||
const FishCatchType = preload("res://fish/fish_catch.gd")
|
const FishCatchType = preload("res://fish/fish_catch.gd")
|
||||||
const FishSaleServiceType = preload("res://economy/fish_sale_service.gd")
|
const FishSaleServiceType = preload("res://economy/fish_sale_service.gd")
|
||||||
const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
||||||
|
const PlayerBagType = preload("res://inventory/player_bag.gd")
|
||||||
|
const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
|
||||||
|
|
||||||
class ShowcaseCameraSnapshot:
|
class ShowcaseCameraSnapshot:
|
||||||
extends RefCounted
|
extends RefCounted
|
||||||
|
|
@ -67,6 +69,8 @@ class ShowcaseCameraSnapshot:
|
||||||
@onready var collection_log: CollectionLogType = %CollectionLog
|
@onready var collection_log: CollectionLogType = %CollectionLog
|
||||||
@onready var wallet: PlayerWalletType = %Wallet
|
@onready var wallet: PlayerWalletType = %Wallet
|
||||||
@onready var fish_sale_service: FishSaleServiceType = %FishSaleService
|
@onready var fish_sale_service: FishSaleServiceType = %FishSaleService
|
||||||
|
@onready var bag: PlayerBagType = %Bag
|
||||||
|
@onready var hotbar: PlayerHotbarType = %Hotbar
|
||||||
@onready var _cast_origin: Marker3D = %CastOrigin
|
@onready var _cast_origin: Marker3D = %CastOrigin
|
||||||
@onready var _fishing_rod: Node3D = %FishingRod
|
@onready var _fishing_rod: Node3D = %FishingRod
|
||||||
@onready var _fishing_rod_tip: Marker3D = %FishingRodTip
|
@onready var _fishing_rod_tip: Marker3D = %FishingRodTip
|
||||||
|
|
@ -198,10 +202,18 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
return
|
return
|
||||||
|
|
||||||
if event.is_action_pressed("camera_zoom_in"):
|
if (
|
||||||
|
event is InputEventMouseButton
|
||||||
|
and event.shift_pressed
|
||||||
|
and event.is_action_pressed("camera_zoom_in")
|
||||||
|
):
|
||||||
_set_target_zoom(_target_zoom - zoom_step)
|
_set_target_zoom(_target_zoom - zoom_step)
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
elif event.is_action_pressed("camera_zoom_out"):
|
elif (
|
||||||
|
event is InputEventMouseButton
|
||||||
|
and event.shift_pressed
|
||||||
|
and event.is_action_pressed("camera_zoom_out")
|
||||||
|
):
|
||||||
_set_target_zoom(_target_zoom + zoom_step)
|
_set_target_zoom(_target_zoom + zoom_step)
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
|
@ -298,6 +310,13 @@ func get_fishing_rod() -> Node3D:
|
||||||
return _fishing_rod
|
return _fishing_rod
|
||||||
|
|
||||||
|
|
||||||
|
func set_active_item_is_rod(active_is_rod: bool) -> void:
|
||||||
|
if _showcase_rod_state_stored:
|
||||||
|
_showcase_rod_visibility = active_is_rod
|
||||||
|
else:
|
||||||
|
_fishing_rod.visible = active_is_rod
|
||||||
|
|
||||||
|
|
||||||
func get_cast_origin_position() -> Vector3:
|
func get_cast_origin_position() -> Vector3:
|
||||||
return _cast_origin.global_position
|
return _cast_origin.global_position
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
[gd_scene load_steps=13 format=3]
|
[gd_scene load_steps=15 format=3]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://player/player.gd" id="1_script"]
|
[ext_resource type="Script" path="res://player/player.gd" id="1_script"]
|
||||||
[ext_resource type="Script" path="res://inventory/fish_inventory.gd" id="2_inventory"]
|
[ext_resource type="Script" path="res://inventory/fish_inventory.gd" id="2_inventory"]
|
||||||
[ext_resource type="Script" path="res://collection/collection_log.gd" id="3_collection"]
|
[ext_resource type="Script" path="res://collection/collection_log.gd" id="3_collection"]
|
||||||
[ext_resource type="Script" path="res://economy/player_wallet.gd" id="4_wallet"]
|
[ext_resource type="Script" path="res://economy/player_wallet.gd" id="4_wallet"]
|
||||||
[ext_resource type="Script" path="res://economy/fish_sale_service.gd" id="5_sale_service"]
|
[ext_resource type="Script" path="res://economy/fish_sale_service.gd" id="5_sale_service"]
|
||||||
|
[ext_resource type="Script" path="res://inventory/player_bag.gd" id="6_bag"]
|
||||||
|
[ext_resource type="Script" path="res://inventory/player_hotbar.gd" id="7_hotbar"]
|
||||||
|
|
||||||
[sub_resource type="CapsuleShape3D" id="PlayerShape"]
|
[sub_resource type="CapsuleShape3D" id="PlayerShape"]
|
||||||
radius = 0.45
|
radius = 0.45
|
||||||
|
|
@ -88,6 +90,14 @@ script = ExtResource("4_wallet")
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
script = ExtResource("5_sale_service")
|
script = ExtResource("5_sale_service")
|
||||||
|
|
||||||
|
[node name="Bag" type="Node" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
script = ExtResource("6_bag")
|
||||||
|
|
||||||
|
[node name="Hotbar" type="Node" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
script = ExtResource("7_hotbar")
|
||||||
|
|
||||||
[node name="CatchDisplayAnchor" type="Marker3D" parent="Visuals"]
|
[node name="CatchDisplayAnchor" type="Marker3D" parent="Visuals"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
position = Vector3(0, 1.45, -1.15)
|
position = Vector3(0, 1.45, -1.15)
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,11 @@ Sprint Shift
|
||||||
Sneak Ctrl
|
Sneak Ctrl
|
||||||
Slow walk Alt
|
Slow walk Alt
|
||||||
Rotate camera Hold right mouse, or use right stick
|
Rotate camera Hold right mouse, or use right stick
|
||||||
Zoom camera Mouse wheel
|
Cycle active hotbar slot Mouse wheel
|
||||||
|
Select hotbar slot 1 through 9
|
||||||
|
Zoom camera Shift + mouse wheel
|
||||||
Cast / withdraw / reel Left mouse
|
Cast / withdraw / reel Left mouse
|
||||||
Inventory and Logbook Tab
|
Cooler, Bag, and Logbook Tab
|
||||||
Game Menu / back Escape
|
Game Menu / back Escape
|
||||||
|
|
||||||
FISHING
|
FISHING
|
||||||
|
|
@ -54,7 +56,8 @@ FEATURES TO TRY
|
||||||
- Barrier-and-chase catching
|
- Barrier-and-chase catching
|
||||||
- Accessibility auto-click settings
|
- Accessibility auto-click settings
|
||||||
- Catch showcase and fish size variation
|
- Catch showcase and fish size variation
|
||||||
- Inventory, Logbook, favorites, and sorting
|
- Cooler, Bag, Logbook, favorites, and sorting
|
||||||
|
- Basic Fishing Rod equipment and the 1–9 hotbar
|
||||||
- Pelican selling and wallet updates
|
- Pelican selling and wallet updates
|
||||||
- Save, Continue, New Game, and Delete Save
|
- Save, Continue, New Game, and Delete Save
|
||||||
- Game Menu and persistent camera settings
|
- Game Menu and persistent camera settings
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,51 @@ camera_zoom_out={
|
||||||
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":32,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":5,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":32,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":5,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
hotbar_1={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":49,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
hotbar_2={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":50,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
hotbar_3={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":51,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
hotbar_4={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":52,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
hotbar_5={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":53,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
hotbar_6={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":54,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
hotbar_7={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":55,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
hotbar_8={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":56,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
hotbar_9={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":57,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
open_backpack={
|
open_backpack={
|
||||||
"deadzone": 0.2,
|
"deadzone": 0.2,
|
||||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194306,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194306,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,13 @@ const CollectionLogType = preload("res://collection/collection_log.gd")
|
||||||
const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
||||||
const FishPoolType = preload("res://fish/fish_pool.gd")
|
const FishPoolType = preload("res://fish/fish_pool.gd")
|
||||||
const SaveInspectionType = preload("res://save/player_save_inspection.gd")
|
const SaveInspectionType = preload("res://save/player_save_inspection.gd")
|
||||||
|
const ItemCatalogType = preload("res://items/item_catalog.gd")
|
||||||
|
const OwnedItemType = preload("res://items/owned_item.gd")
|
||||||
|
const PlayerBagType = preload("res://inventory/player_bag.gd")
|
||||||
|
const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
|
||||||
|
|
||||||
const SAVE_VERSION: int = 1
|
const SAVE_VERSION: int = 2
|
||||||
|
const BASIC_ROD_ID: StringName = &"basic_fishing_rod"
|
||||||
const SAVE_PATH: String = "user://player_save.json"
|
const SAVE_PATH: String = "user://player_save.json"
|
||||||
const TEMP_PATH: String = "user://player_save.json.tmp"
|
const TEMP_PATH: String = "user://player_save.json.tmp"
|
||||||
const BACKUP_PATH: String = "user://player_save.json.backup"
|
const BACKUP_PATH: String = "user://player_save.json.backup"
|
||||||
|
|
@ -21,6 +26,9 @@ class LoadSnapshot:
|
||||||
var discovered_ids: Array[StringName] = []
|
var discovered_ids: Array[StringName] = []
|
||||||
var wallet_balance: int = 0
|
var wallet_balance: int = 0
|
||||||
var next_catch_sequence: int = 1
|
var next_catch_sequence: int = 1
|
||||||
|
var bag_items: Array[OwnedItemType] = []
|
||||||
|
var hotbar_slots: Array[StringName] = []
|
||||||
|
var selected_hotbar_slot: int = 0
|
||||||
|
|
||||||
|
|
||||||
@export_range(0.05, 5.0, 0.05) var autosave_delay: float = 0.5
|
@export_range(0.05, 5.0, 0.05) var autosave_delay: float = 0.5
|
||||||
|
|
@ -29,6 +37,9 @@ var _inventory: FishInventoryType
|
||||||
var _collection_log: CollectionLogType
|
var _collection_log: CollectionLogType
|
||||||
var _wallet: PlayerWalletType
|
var _wallet: PlayerWalletType
|
||||||
var _catalog: FishPoolType
|
var _catalog: FishPoolType
|
||||||
|
var _bag: PlayerBagType
|
||||||
|
var _hotbar: PlayerHotbarType
|
||||||
|
var _item_catalog: ItemCatalogType
|
||||||
var _autosave_timer: Timer
|
var _autosave_timer: Timer
|
||||||
var _is_configured: bool = false
|
var _is_configured: bool = false
|
||||||
var _is_restoring: bool = false
|
var _is_restoring: bool = false
|
||||||
|
|
@ -49,16 +60,25 @@ func setup(
|
||||||
collection_log: CollectionLogType,
|
collection_log: CollectionLogType,
|
||||||
wallet: PlayerWalletType,
|
wallet: PlayerWalletType,
|
||||||
catalog: FishPoolType,
|
catalog: FishPoolType,
|
||||||
|
bag: PlayerBagType,
|
||||||
|
hotbar: PlayerHotbarType,
|
||||||
|
item_catalog: ItemCatalogType,
|
||||||
) -> void:
|
) -> void:
|
||||||
_inventory = inventory
|
_inventory = inventory
|
||||||
_collection_log = collection_log
|
_collection_log = collection_log
|
||||||
_wallet = wallet
|
_wallet = wallet
|
||||||
_catalog = catalog
|
_catalog = catalog
|
||||||
|
_bag = bag
|
||||||
|
_hotbar = hotbar
|
||||||
|
_item_catalog = item_catalog
|
||||||
_is_configured = (
|
_is_configured = (
|
||||||
_inventory != null
|
_inventory != null
|
||||||
and _collection_log != null
|
and _collection_log != null
|
||||||
and _wallet != null
|
and _wallet != null
|
||||||
and _catalog != null
|
and _catalog != null
|
||||||
|
and _bag != null
|
||||||
|
and _hotbar != null
|
||||||
|
and _item_catalog != null
|
||||||
)
|
)
|
||||||
if not _is_configured:
|
if not _is_configured:
|
||||||
push_error("PlayerSaveManager setup is missing required references.")
|
push_error("PlayerSaveManager setup is missing required references.")
|
||||||
|
|
@ -71,6 +91,16 @@ func setup(
|
||||||
_collection_log.fish_discovered.connect(_on_fish_discovered)
|
_collection_log.fish_discovered.connect(_on_fish_discovered)
|
||||||
if not _wallet.balance_changed.is_connected(_on_balance_changed):
|
if not _wallet.balance_changed.is_connected(_on_balance_changed):
|
||||||
_wallet.balance_changed.connect(_on_balance_changed)
|
_wallet.balance_changed.connect(_on_balance_changed)
|
||||||
|
if not _bag.contents_changed.is_connected(_mark_dirty):
|
||||||
|
_bag.contents_changed.connect(_mark_dirty)
|
||||||
|
if not _hotbar.slots_changed.is_connected(_mark_dirty):
|
||||||
|
_hotbar.slots_changed.connect(_mark_dirty)
|
||||||
|
if not _hotbar.selected_slot_changed.is_connected(
|
||||||
|
_on_selected_hotbar_slot_changed
|
||||||
|
):
|
||||||
|
_hotbar.selected_slot_changed.connect(
|
||||||
|
_on_selected_hotbar_slot_changed
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func load_player_data() -> bool:
|
func load_player_data() -> bool:
|
||||||
|
|
@ -131,11 +161,18 @@ func load_player_data() -> bool:
|
||||||
var wallet_restored: bool = _wallet.restore_balance(
|
var wallet_restored: bool = _wallet.restore_balance(
|
||||||
snapshot.wallet_balance
|
snapshot.wallet_balance
|
||||||
)
|
)
|
||||||
|
var bag_restored: bool = _bag.replace_all_items(snapshot.bag_items)
|
||||||
|
var hotbar_restored: bool = _hotbar.replace_state(
|
||||||
|
snapshot.hotbar_slots,
|
||||||
|
snapshot.selected_hotbar_slot
|
||||||
|
)
|
||||||
_is_restoring = false
|
_is_restoring = false
|
||||||
if (
|
if (
|
||||||
not inventory_restored
|
not inventory_restored
|
||||||
or not collection_restored
|
or not collection_restored
|
||||||
or not wallet_restored
|
or not wallet_restored
|
||||||
|
or not bag_restored
|
||||||
|
or not hotbar_restored
|
||||||
):
|
):
|
||||||
push_error("Validated player save could not be restored.")
|
push_error("Validated player save could not be restored.")
|
||||||
return false
|
return false
|
||||||
|
|
@ -174,7 +211,13 @@ func inspect_save() -> SaveInspectionType:
|
||||||
result.status = SaveInspectionType.Status.UNSUPPORTED_VERSION
|
result.status = SaveInspectionType.Status.UNSUPPORTED_VERSION
|
||||||
result.message = "This save belongs to a newer game version."
|
result.message = "This save belongs to a newer game version."
|
||||||
return result
|
return result
|
||||||
|
if result.detected_version < 1:
|
||||||
|
result.status = SaveInspectionType.Status.MALFORMED
|
||||||
|
result.message = "The save version is unsupported."
|
||||||
|
return result
|
||||||
if result.detected_version != SAVE_VERSION:
|
if result.detected_version != SAVE_VERSION:
|
||||||
|
save_data = _migrate_save(save_data, result.detected_version)
|
||||||
|
if save_data.is_empty():
|
||||||
result.status = SaveInspectionType.Status.MALFORMED
|
result.status = SaveInspectionType.Status.MALFORMED
|
||||||
result.message = "The save version is unsupported."
|
result.message = "The save version is unsupported."
|
||||||
return result
|
return result
|
||||||
|
|
@ -300,6 +343,14 @@ func _build_save_dictionary() -> Dictionary:
|
||||||
if fish_id.is_empty():
|
if fish_id.is_empty():
|
||||||
return {}
|
return {}
|
||||||
discovered_strings.append(String(fish_id))
|
discovered_strings.append(String(fish_id))
|
||||||
|
var serialized_items: Array[Dictionary] = []
|
||||||
|
for owned: OwnedItemType in _bag.get_all_items():
|
||||||
|
if owned == null or not owned.is_valid():
|
||||||
|
return {}
|
||||||
|
serialized_items.append(owned.to_save_dict())
|
||||||
|
var serialized_slots: Array[String] = []
|
||||||
|
for item_id: StringName in _hotbar.get_slots():
|
||||||
|
serialized_slots.append(String(item_id))
|
||||||
if (
|
if (
|
||||||
_wallet.get_balance() < 0
|
_wallet.get_balance() < 0
|
||||||
or _wallet.get_balance() > MAX_SAFE_BALANCE
|
or _wallet.get_balance() > MAX_SAFE_BALANCE
|
||||||
|
|
@ -323,6 +374,13 @@ func _build_save_dictionary() -> Dictionary:
|
||||||
"next_catch_sequence": _inventory.get_next_catch_sequence(),
|
"next_catch_sequence": _inventory.get_next_catch_sequence(),
|
||||||
"catches": serialized_catches,
|
"catches": serialized_catches,
|
||||||
},
|
},
|
||||||
|
"bag": {
|
||||||
|
"items": serialized_items,
|
||||||
|
},
|
||||||
|
"hotbar": {
|
||||||
|
"selected_slot": _hotbar.get_selected_slot(),
|
||||||
|
"slots": serialized_slots,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -331,16 +389,23 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot:
|
||||||
typeof(save_data.get("wallet")) != TYPE_DICTIONARY
|
typeof(save_data.get("wallet")) != TYPE_DICTIONARY
|
||||||
or typeof(save_data.get("collection")) != TYPE_DICTIONARY
|
or typeof(save_data.get("collection")) != TYPE_DICTIONARY
|
||||||
or typeof(save_data.get("inventory")) != TYPE_DICTIONARY
|
or typeof(save_data.get("inventory")) != TYPE_DICTIONARY
|
||||||
|
or typeof(save_data.get("bag")) != TYPE_DICTIONARY
|
||||||
|
or typeof(save_data.get("hotbar")) != TYPE_DICTIONARY
|
||||||
):
|
):
|
||||||
return null
|
return null
|
||||||
var wallet_data: Dictionary = save_data["wallet"]
|
var wallet_data: Dictionary = save_data["wallet"]
|
||||||
var collection_data: Dictionary = save_data["collection"]
|
var collection_data: Dictionary = save_data["collection"]
|
||||||
var inventory_data: Dictionary = save_data["inventory"]
|
var inventory_data: Dictionary = save_data["inventory"]
|
||||||
|
var bag_data: Dictionary = save_data["bag"]
|
||||||
|
var hotbar_data: Dictionary = save_data["hotbar"]
|
||||||
if (
|
if (
|
||||||
not wallet_data.has("balance")
|
not wallet_data.has("balance")
|
||||||
or typeof(collection_data.get("discovered_fish_ids")) != TYPE_ARRAY
|
or typeof(collection_data.get("discovered_fish_ids")) != TYPE_ARRAY
|
||||||
or typeof(inventory_data.get("catches")) != TYPE_ARRAY
|
or typeof(inventory_data.get("catches")) != TYPE_ARRAY
|
||||||
or not inventory_data.has("next_catch_sequence")
|
or not inventory_data.has("next_catch_sequence")
|
||||||
|
or typeof(bag_data.get("items")) != TYPE_ARRAY
|
||||||
|
or typeof(hotbar_data.get("slots")) != TYPE_ARRAY
|
||||||
|
or not hotbar_data.has("selected_slot")
|
||||||
):
|
):
|
||||||
return null
|
return null
|
||||||
|
|
||||||
|
|
@ -424,6 +489,81 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot:
|
||||||
requested_next_sequence,
|
requested_next_sequence,
|
||||||
maximum_sequence + 1
|
maximum_sequence + 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var seen_items: Dictionary[StringName, bool] = {}
|
||||||
|
var item_values: Array = bag_data["items"]
|
||||||
|
for item_index: int in range(item_values.size()):
|
||||||
|
var value: Variant = item_values[item_index]
|
||||||
|
if typeof(value) != TYPE_DICTIONARY:
|
||||||
|
push_warning("Skipped invalid saved Bag item.")
|
||||||
|
continue
|
||||||
|
var item_record: Dictionary = value
|
||||||
|
var item_id: StringName = StringName(
|
||||||
|
str(item_record.get("item_id", ""))
|
||||||
|
)
|
||||||
|
var item_data = _item_catalog.get_item_by_id(item_id)
|
||||||
|
var quantity: int = _read_integer(
|
||||||
|
item_record.get("quantity"),
|
||||||
|
-1,
|
||||||
|
999
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
item_id.is_empty()
|
||||||
|
or item_data == null
|
||||||
|
or not item_data.is_valid()
|
||||||
|
or quantity <= 0
|
||||||
|
or seen_items.has(item_id)
|
||||||
|
):
|
||||||
|
push_warning(
|
||||||
|
"Skipped invalid or unknown saved Bag item '%s'."
|
||||||
|
% String(item_id)
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
var maximum: int = (
|
||||||
|
item_data.max_stack if item_data.stackable else 1
|
||||||
|
)
|
||||||
|
if quantity > maximum:
|
||||||
|
push_warning(
|
||||||
|
"Skipped saved Bag item with invalid quantity '%s'."
|
||||||
|
% String(item_id)
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
var owned := OwnedItemType.new()
|
||||||
|
owned.item_id = item_id
|
||||||
|
owned.quantity = quantity
|
||||||
|
seen_items[item_id] = true
|
||||||
|
snapshot.bag_items.append(owned)
|
||||||
|
|
||||||
|
var slot_values: Array = hotbar_data["slots"]
|
||||||
|
snapshot.hotbar_slots.resize(PlayerHotbarType.SLOT_COUNT)
|
||||||
|
snapshot.hotbar_slots.fill(StringName())
|
||||||
|
for slot_index: int in range(
|
||||||
|
mini(slot_values.size(), PlayerHotbarType.SLOT_COUNT)
|
||||||
|
):
|
||||||
|
var slot_value: Variant = slot_values[slot_index]
|
||||||
|
if typeof(slot_value) not in [TYPE_STRING, TYPE_STRING_NAME]:
|
||||||
|
continue
|
||||||
|
var slot_item_id: StringName = StringName(str(slot_value))
|
||||||
|
if slot_item_id.is_empty():
|
||||||
|
continue
|
||||||
|
var slot_item = _item_catalog.get_item_by_id(slot_item_id)
|
||||||
|
if (
|
||||||
|
slot_item != null
|
||||||
|
and slot_item.is_valid()
|
||||||
|
and slot_item.hotbar_allowed
|
||||||
|
and seen_items.has(slot_item_id)
|
||||||
|
):
|
||||||
|
snapshot.hotbar_slots[slot_index] = slot_item_id
|
||||||
|
var selected_slot: int = _read_integer(
|
||||||
|
hotbar_data["selected_slot"],
|
||||||
|
0,
|
||||||
|
PlayerHotbarType.SLOT_COUNT - 1
|
||||||
|
)
|
||||||
|
snapshot.selected_hotbar_slot = clampi(
|
||||||
|
selected_slot,
|
||||||
|
0,
|
||||||
|
PlayerHotbarType.SLOT_COUNT - 1
|
||||||
|
)
|
||||||
return snapshot
|
return snapshot
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -433,6 +573,26 @@ func _migrate_save(
|
||||||
) -> Dictionary:
|
) -> Dictionary:
|
||||||
if from_version == SAVE_VERSION:
|
if from_version == SAVE_VERSION:
|
||||||
return data
|
return data
|
||||||
|
if from_version == 1:
|
||||||
|
var migrated: Dictionary = data.duplicate(true)
|
||||||
|
migrated["save_version"] = SAVE_VERSION
|
||||||
|
migrated["bag"] = {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"item_id": String(BASIC_ROD_ID),
|
||||||
|
"quantity": 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
var slots: Array[String] = []
|
||||||
|
slots.resize(PlayerHotbarType.SLOT_COUNT)
|
||||||
|
slots.fill("")
|
||||||
|
slots[0] = String(BASIC_ROD_ID)
|
||||||
|
migrated["hotbar"] = {
|
||||||
|
"selected_slot": 0,
|
||||||
|
"slots": slots,
|
||||||
|
}
|
||||||
|
return migrated
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -456,6 +616,13 @@ func _on_balance_changed(_new_balance: int, _delta: int) -> void:
|
||||||
_mark_dirty()
|
_mark_dirty()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_selected_hotbar_slot_changed(
|
||||||
|
_slot_index: int,
|
||||||
|
_item_id: StringName,
|
||||||
|
) -> void:
|
||||||
|
_mark_dirty()
|
||||||
|
|
||||||
|
|
||||||
func _on_autosave_timeout() -> void:
|
func _on_autosave_timeout() -> void:
|
||||||
if _is_dirty:
|
if _is_dirty:
|
||||||
save_now()
|
save_now()
|
||||||
|
|
@ -496,9 +663,20 @@ func _restore_defaults() -> void:
|
||||||
_is_restoring = true
|
_is_restoring = true
|
||||||
var empty_catches: Array[FishCatchType] = []
|
var empty_catches: Array[FishCatchType] = []
|
||||||
var empty_discoveries: Array[StringName] = []
|
var empty_discoveries: Array[StringName] = []
|
||||||
|
var default_items: Array[OwnedItemType] = []
|
||||||
|
var basic_rod := OwnedItemType.new()
|
||||||
|
basic_rod.item_id = BASIC_ROD_ID
|
||||||
|
basic_rod.quantity = 1
|
||||||
|
default_items.append(basic_rod)
|
||||||
|
var default_slots: Array[StringName] = []
|
||||||
|
default_slots.resize(PlayerHotbarType.SLOT_COUNT)
|
||||||
|
default_slots.fill(StringName())
|
||||||
|
default_slots[0] = BASIC_ROD_ID
|
||||||
_inventory.replace_all_catches(empty_catches, 1)
|
_inventory.replace_all_catches(empty_catches, 1)
|
||||||
_collection_log.replace_discovered_ids(empty_discoveries)
|
_collection_log.replace_discovered_ids(empty_discoveries)
|
||||||
_wallet.restore_balance(0)
|
_wallet.restore_balance(0)
|
||||||
|
_bag.replace_all_items(default_items)
|
||||||
|
_hotbar.replace_state(default_slots, 0)
|
||||||
_is_restoring = false
|
_is_restoring = false
|
||||||
_is_dirty = false
|
_is_dirty = false
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,10 @@ const FishingSpotType = preload("res://fishing/fishing_spot.gd")
|
||||||
const PlayerMenuType = preload("res://ui/player_menu.gd")
|
const PlayerMenuType = preload("res://ui/player_menu.gd")
|
||||||
const PlayerType = preload("res://player/player.gd")
|
const PlayerType = preload("res://player/player.gd")
|
||||||
const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
||||||
|
const ItemCatalogType = preload("res://items/item_catalog.gd")
|
||||||
|
const PlayerBagType = preload("res://inventory/player_bag.gd")
|
||||||
|
const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
|
||||||
|
const HotbarUIType = preload("res://ui/hotbar.gd")
|
||||||
const TitleScreenType = preload("res://ui/title_screen.gd")
|
const TitleScreenType = preload("res://ui/title_screen.gd")
|
||||||
const PauseMenuType = preload("res://ui/pause_menu.gd")
|
const PauseMenuType = preload("res://ui/pause_menu.gd")
|
||||||
|
|
||||||
|
|
@ -26,10 +30,12 @@ const PauseMenuType = preload("res://ui/pause_menu.gd")
|
||||||
@onready var _screen_fade: ScreenFade = %ScreenFade
|
@onready var _screen_fade: ScreenFade = %ScreenFade
|
||||||
@onready var _title_screen: TitleScreenType = %TitleScreen
|
@onready var _title_screen: TitleScreenType = %TitleScreen
|
||||||
@onready var _pause_menu: PauseMenuType = %PauseMenu
|
@onready var _pause_menu: PauseMenuType = %PauseMenu
|
||||||
|
@onready var _hotbar_ui: HotbarUIType = %Hotbar
|
||||||
|
|
||||||
var _showcase_active: bool = false
|
var _showcase_active: bool = false
|
||||||
var _player_menu_open: bool = false
|
var _player_menu_open: bool = false
|
||||||
var _gameplay_ui_enabled: bool = false
|
var _gameplay_ui_enabled: bool = false
|
||||||
|
var _fishing_spot: FishingSpotType
|
||||||
|
|
||||||
|
|
||||||
func setup(
|
func setup(
|
||||||
|
|
@ -41,7 +47,11 @@ func setup(
|
||||||
default_buyer: FishBuyerProfileType,
|
default_buyer: FishBuyerProfileType,
|
||||||
catalog: FishPoolType,
|
catalog: FishPoolType,
|
||||||
fishing_spot: FishingSpotType,
|
fishing_spot: FishingSpotType,
|
||||||
|
bag: PlayerBagType,
|
||||||
|
hotbar: PlayerHotbarType,
|
||||||
|
item_catalog: ItemCatalogType,
|
||||||
) -> void:
|
) -> void:
|
||||||
|
_fishing_spot = fishing_spot
|
||||||
fishing_spot.status_changed.connect(_on_fishing_status_changed)
|
fishing_spot.status_changed.connect(_on_fishing_status_changed)
|
||||||
fishing_spot.catch_display_changed.connect(_on_catch_display_changed)
|
fishing_spot.catch_display_changed.connect(_on_catch_display_changed)
|
||||||
fishing_spot.showcase_changed.connect(_on_showcase_changed)
|
fishing_spot.showcase_changed.connect(_on_showcase_changed)
|
||||||
|
|
@ -56,8 +66,12 @@ func setup(
|
||||||
sale_service,
|
sale_service,
|
||||||
default_buyer,
|
default_buyer,
|
||||||
catalog,
|
catalog,
|
||||||
fishing_spot
|
fishing_spot,
|
||||||
|
bag,
|
||||||
|
hotbar,
|
||||||
|
item_catalog
|
||||||
)
|
)
|
||||||
|
_hotbar_ui.setup(hotbar, bag, item_catalog, fishing_spot)
|
||||||
|
|
||||||
|
|
||||||
func close_player_menu() -> void:
|
func close_player_menu() -> void:
|
||||||
|
|
@ -89,10 +103,22 @@ func set_gameplay_ui_enabled(enabled: bool) -> void:
|
||||||
if not enabled:
|
if not enabled:
|
||||||
close_player_menu_for_session_end()
|
close_player_menu_for_session_end()
|
||||||
_fishing_panel.visible = false
|
_fishing_panel.visible = false
|
||||||
|
_hotbar_ui.hide()
|
||||||
|
_hotbar_ui.set_gameplay_input_enabled(false)
|
||||||
else:
|
else:
|
||||||
|
_hotbar_ui.show()
|
||||||
|
_hotbar_ui.set_gameplay_input_enabled(true)
|
||||||
_refresh_fishing_panel_visibility()
|
_refresh_fishing_panel_visibility()
|
||||||
|
|
||||||
|
|
||||||
|
func set_system_menu_open(is_open: bool) -> void:
|
||||||
|
_hotbar_ui.visible = _gameplay_ui_enabled and not is_open
|
||||||
|
_hotbar_ui.set_gameplay_input_enabled(
|
||||||
|
_gameplay_ui_enabled and not is_open and not _player_menu_open
|
||||||
|
)
|
||||||
|
_hotbar_ui.set_drag_enabled(_player_menu_open and not is_open)
|
||||||
|
|
||||||
|
|
||||||
func get_screen_fade() -> ScreenFade:
|
func get_screen_fade() -> ScreenFade:
|
||||||
return _screen_fade
|
return _screen_fade
|
||||||
|
|
||||||
|
|
@ -104,6 +130,8 @@ func get_title_screen() -> TitleScreenType:
|
||||||
func _on_fishing_status_changed(status: String) -> void:
|
func _on_fishing_status_changed(status: String) -> void:
|
||||||
if _showcase_active:
|
if _showcase_active:
|
||||||
return
|
return
|
||||||
|
if _fishing_spot != null and _fishing_spot.is_fighting():
|
||||||
|
return
|
||||||
_set_fishing_status(status)
|
_set_fishing_status(status)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -125,7 +153,6 @@ func _refresh_fishing_panel_visibility() -> void:
|
||||||
_fishing_panel.visible = (
|
_fishing_panel.visible = (
|
||||||
_gameplay_ui_enabled
|
_gameplay_ui_enabled
|
||||||
and has_content
|
and has_content
|
||||||
and not _player_menu_open
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -138,17 +165,26 @@ func _on_catch_display_changed(
|
||||||
active_barrier_index: int,
|
active_barrier_index: int,
|
||||||
visible: bool,
|
visible: bool,
|
||||||
) -> void:
|
) -> void:
|
||||||
|
var encounter_visible: bool = (
|
||||||
|
visible
|
||||||
|
and _fishing_spot != null
|
||||||
|
and _fishing_spot.is_fighting()
|
||||||
|
)
|
||||||
|
_hotbar_ui.set_item_name_suppressed(encounter_visible)
|
||||||
_green_catch_progress.value = progress * 100.0
|
_green_catch_progress.value = progress * 100.0
|
||||||
_red_chase_progress.value = maxf(chase_progress, 0.0) * 100.0
|
_red_chase_progress.value = maxf(chase_progress, 0.0) * 100.0
|
||||||
_catch_track.visible = visible
|
_catch_track.visible = encounter_visible
|
||||||
_barrier_summary.visible = visible
|
_barrier_summary.visible = encounter_visible
|
||||||
if not visible:
|
_barrier_health.visible = encounter_visible
|
||||||
|
if not encounter_visible:
|
||||||
_barrier_health.visible = false
|
_barrier_health.visible = false
|
||||||
_clear_barrier_markers()
|
_clear_barrier_markers()
|
||||||
_barrier_summary.text = ""
|
_barrier_summary.text = ""
|
||||||
_barrier_health.text = ""
|
_barrier_health.text = ""
|
||||||
_refresh_fishing_panel_visibility()
|
_refresh_fishing_panel_visibility()
|
||||||
return
|
return
|
||||||
|
_status_label.text = ""
|
||||||
|
_status_label.visible = false
|
||||||
|
|
||||||
_update_barrier_markers(
|
_update_barrier_markers(
|
||||||
barrier_positions,
|
barrier_positions,
|
||||||
|
|
@ -183,7 +219,7 @@ func _on_catch_display_changed(
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
_barrier_health.text = ""
|
_barrier_health.text = "Hold left click to reel"
|
||||||
|
|
||||||
var chase_gap: float = progress - chase_progress
|
var chase_gap: float = progress - chase_progress
|
||||||
if chase_gap <= 0.05:
|
if chase_gap <= 0.05:
|
||||||
|
|
@ -194,10 +230,6 @@ func _on_catch_display_changed(
|
||||||
"\nRed is closing in!",
|
"\nRed is closing in!",
|
||||||
]
|
]
|
||||||
).strip_edges()
|
).strip_edges()
|
||||||
_barrier_health.visible = (
|
|
||||||
visible
|
|
||||||
and (active_barrier_index >= 0 or chase_gap <= 0.05)
|
|
||||||
)
|
|
||||||
_refresh_fishing_panel_visibility()
|
_refresh_fishing_panel_visibility()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -262,4 +294,8 @@ func _on_showcase_changed(
|
||||||
|
|
||||||
func _on_player_menu_visibility_changed(is_open: bool) -> void:
|
func _on_player_menu_visibility_changed(is_open: bool) -> void:
|
||||||
_player_menu_open = is_open
|
_player_menu_open = is_open
|
||||||
|
_hotbar_ui.set_gameplay_input_enabled(
|
||||||
|
_gameplay_ui_enabled and not is_open
|
||||||
|
)
|
||||||
|
_hotbar_ui.set_drag_enabled(is_open)
|
||||||
_refresh_fishing_panel_visibility()
|
_refresh_fishing_panel_visibility()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_scene load_steps=11 format=3]
|
[gd_scene load_steps=12 format=3]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://ui/game_ui.gd" id="1_ui"]
|
[ext_resource type="Script" path="res://ui/game_ui.gd" id="1_ui"]
|
||||||
[ext_resource type="PackedScene" path="res://ui/player_menu.tscn" id="2_menu"]
|
[ext_resource type="PackedScene" path="res://ui/player_menu.tscn" id="2_menu"]
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
[ext_resource type="Script" path="res://ui/screen_fade.gd" id="4_fade"]
|
[ext_resource type="Script" path="res://ui/screen_fade.gd" id="4_fade"]
|
||||||
[ext_resource type="PackedScene" path="res://ui/title_screen.tscn" id="5_title"]
|
[ext_resource type="PackedScene" path="res://ui/title_screen.tscn" id="5_title"]
|
||||||
[ext_resource type="PackedScene" path="res://ui/pause_menu.tscn" id="6_pause"]
|
[ext_resource type="PackedScene" path="res://ui/pause_menu.tscn" id="6_pause"]
|
||||||
|
[ext_resource type="PackedScene" path="res://ui/hotbar.tscn" id="7_hotbar"]
|
||||||
|
|
||||||
[sub_resource type="StyleBoxFlat" id="StyleBox_chase_background"]
|
[sub_resource type="StyleBoxFlat" id="StyleBox_chase_background"]
|
||||||
bg_color = Color(0.055, 0.105, 0.125, 1)
|
bg_color = Color(0.055, 0.105, 0.125, 1)
|
||||||
|
|
@ -37,35 +38,36 @@ script = ExtResource("1_ui")
|
||||||
[node name="FishingPanel" type="PanelContainer" parent="."]
|
[node name="FishingPanel" type="PanelContainer" parent="."]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
visible = false
|
visible = false
|
||||||
|
z_index = 60
|
||||||
anchors_preset = 7
|
anchors_preset = 7
|
||||||
anchor_left = 0.5
|
anchor_left = 0.5
|
||||||
anchor_top = 1.0
|
anchor_top = 1.0
|
||||||
anchor_right = 0.5
|
anchor_right = 0.5
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
offset_left = -210.0
|
offset_left = -205.0
|
||||||
offset_top = -130.0
|
offset_top = -216.0
|
||||||
offset_right = 210.0
|
offset_right = 205.0
|
||||||
offset_bottom = -24.0
|
offset_bottom = -128.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 0
|
grow_vertical = 0
|
||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
theme = ExtResource("3_theme")
|
theme = ExtResource("3_theme")
|
||||||
|
|
||||||
[node name="MarginContainer" type="MarginContainer" parent="FishingPanel"]
|
[node name="MarginContainer" type="MarginContainer" parent="FishingPanel"]
|
||||||
theme_override_constants/margin_left = 18
|
theme_override_constants/margin_left = 14
|
||||||
theme_override_constants/margin_top = 12
|
theme_override_constants/margin_top = 8
|
||||||
theme_override_constants/margin_right = 18
|
theme_override_constants/margin_right = 14
|
||||||
theme_override_constants/margin_bottom = 12
|
theme_override_constants/margin_bottom = 8
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="FishingPanel/MarginContainer"]
|
[node name="VBoxContainer" type="VBoxContainer" parent="FishingPanel/MarginContainer"]
|
||||||
theme_override_constants/separation = 6
|
theme_override_constants/separation = 4
|
||||||
|
|
||||||
[node name="StatusLabel" type="Label" parent="FishingPanel/MarginContainer/VBoxContainer"]
|
[node name="StatusLabel" type="Label" parent="FishingPanel/MarginContainer/VBoxContainer"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
visible = false
|
visible = false
|
||||||
text = ""
|
text = ""
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
theme_override_font_sizes/font_size = 17
|
theme_override_font_sizes/font_size = 16
|
||||||
|
|
||||||
[node name="ShowcaseDetails" type="Label" parent="FishingPanel/MarginContainer/VBoxContainer"]
|
[node name="ShowcaseDetails" type="Label" parent="FishingPanel/MarginContainer/VBoxContainer"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
@ -75,7 +77,7 @@ horizontal_alignment = 1
|
||||||
[node name="CatchTrack" type="Control" parent="FishingPanel/MarginContainer/VBoxContainer"]
|
[node name="CatchTrack" type="Control" parent="FishingPanel/MarginContainer/VBoxContainer"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
visible = false
|
visible = false
|
||||||
custom_minimum_size = Vector2(0, 22)
|
custom_minimum_size = Vector2(0, 20)
|
||||||
|
|
||||||
[node name="GreenCatchProgress" type="ProgressBar" parent="FishingPanel/MarginContainer/VBoxContainer/CatchTrack"]
|
[node name="GreenCatchProgress" type="ProgressBar" parent="FishingPanel/MarginContainer/VBoxContainer/CatchTrack"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
@ -128,6 +130,9 @@ horizontal_alignment = 1
|
||||||
[node name="PlayerMenu" parent="." instance=ExtResource("2_menu")]
|
[node name="PlayerMenu" parent="." instance=ExtResource("2_menu")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
||||||
|
[node name="Hotbar" parent="." instance=ExtResource("7_hotbar")]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
|
||||||
[node name="ScreenFade" type="ColorRect" parent="."]
|
[node name="ScreenFade" type="ColorRect" parent="."]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
visible = false
|
visible = false
|
||||||
|
|
|
||||||
185
ui/hotbar.gd
Normal file
185
ui/hotbar.gd
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
class_name HotbarUI
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
const ItemCatalogType = preload("res://items/item_catalog.gd")
|
||||||
|
const PlayerBagType = preload("res://inventory/player_bag.gd")
|
||||||
|
const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
|
||||||
|
const HotbarSlotType = preload("res://ui/hotbar_slot.gd")
|
||||||
|
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
|
||||||
|
|
||||||
|
@onready var _slot_row: HBoxContainer = %SlotRow
|
||||||
|
@onready var _selected_item_label: Label = %SelectedItemLabel
|
||||||
|
@onready var _item_name_timer: Timer = %ItemNameTimer
|
||||||
|
|
||||||
|
var _hotbar: PlayerHotbarType
|
||||||
|
var _bag: PlayerBagType
|
||||||
|
var _catalog: ItemCatalogType
|
||||||
|
var _fishing_spot: FishingSpotType
|
||||||
|
var _slots: Array[HotbarSlotType] = []
|
||||||
|
var _gameplay_input_enabled: bool = false
|
||||||
|
var _drag_enabled: bool = false
|
||||||
|
var _hovered_slot_index: int = -1
|
||||||
|
var _item_name_suppressed: bool = false
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_item_name_timer.timeout.connect(_on_item_name_timer_timeout)
|
||||||
|
|
||||||
|
|
||||||
|
func setup(
|
||||||
|
hotbar: PlayerHotbarType,
|
||||||
|
bag: PlayerBagType,
|
||||||
|
catalog: ItemCatalogType,
|
||||||
|
fishing_spot: FishingSpotType,
|
||||||
|
) -> void:
|
||||||
|
_hotbar = hotbar
|
||||||
|
_bag = bag
|
||||||
|
_catalog = catalog
|
||||||
|
_fishing_spot = fishing_spot
|
||||||
|
if not _hotbar.slots_changed.is_connected(_refresh):
|
||||||
|
_hotbar.slots_changed.connect(_refresh)
|
||||||
|
if not _hotbar.selected_slot_changed.is_connected(
|
||||||
|
_on_selected_slot_changed
|
||||||
|
):
|
||||||
|
_hotbar.selected_slot_changed.connect(_on_selected_slot_changed)
|
||||||
|
if not _bag.contents_changed.is_connected(_refresh):
|
||||||
|
_bag.contents_changed.connect(_refresh)
|
||||||
|
_build_slots()
|
||||||
|
_refresh()
|
||||||
|
|
||||||
|
|
||||||
|
func set_gameplay_input_enabled(enabled: bool) -> void:
|
||||||
|
_gameplay_input_enabled = enabled
|
||||||
|
|
||||||
|
|
||||||
|
func set_drag_enabled(enabled: bool) -> void:
|
||||||
|
_drag_enabled = enabled
|
||||||
|
for slot: HotbarSlotType in _slots:
|
||||||
|
slot.set_drag_enabled(enabled)
|
||||||
|
if not enabled:
|
||||||
|
_hovered_slot_index = -1
|
||||||
|
_hide_item_name()
|
||||||
|
|
||||||
|
|
||||||
|
func set_item_name_suppressed(suppressed: bool) -> void:
|
||||||
|
_item_name_suppressed = suppressed
|
||||||
|
if suppressed:
|
||||||
|
_hovered_slot_index = -1
|
||||||
|
_hide_item_name()
|
||||||
|
|
||||||
|
|
||||||
|
func _unhandled_input(event: InputEvent) -> void:
|
||||||
|
if (
|
||||||
|
not _gameplay_input_enabled
|
||||||
|
or _hotbar == null
|
||||||
|
or _fishing_spot == null
|
||||||
|
or not _fishing_spot.can_change_hotbar_selection()
|
||||||
|
):
|
||||||
|
return
|
||||||
|
if event is InputEventKey and event.pressed and not event.echo:
|
||||||
|
for index: int in range(PlayerHotbarType.SLOT_COUNT):
|
||||||
|
if event.is_action_pressed("hotbar_%d" % (index + 1)):
|
||||||
|
_hotbar.select_slot(index)
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
return
|
||||||
|
if (
|
||||||
|
event is InputEventMouseButton
|
||||||
|
and event.pressed
|
||||||
|
and not event.shift_pressed
|
||||||
|
):
|
||||||
|
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
|
||||||
|
_hotbar.cycle_selection(-1)
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
|
||||||
|
_hotbar.cycle_selection(1)
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
|
||||||
|
func _build_slots() -> void:
|
||||||
|
for child: Node in _slot_row.get_children():
|
||||||
|
child.queue_free()
|
||||||
|
_slots.clear()
|
||||||
|
for index: int in range(PlayerHotbarType.SLOT_COUNT):
|
||||||
|
var slot := HotbarSlotType.new()
|
||||||
|
slot.toggle_mode = true
|
||||||
|
slot.setup(index, _hotbar, _bag, _catalog)
|
||||||
|
slot.set_drag_enabled(_drag_enabled)
|
||||||
|
slot.item_hovered.connect(_on_slot_item_hovered)
|
||||||
|
slot.item_hover_ended.connect(_on_slot_item_hover_ended)
|
||||||
|
slot.item_drag_started.connect(_on_slot_drag_started)
|
||||||
|
slot.item_drag_finished.connect(_on_slot_drag_finished)
|
||||||
|
_slot_row.add_child(slot)
|
||||||
|
_slots.append(slot)
|
||||||
|
|
||||||
|
|
||||||
|
func _refresh() -> void:
|
||||||
|
for slot: HotbarSlotType in _slots:
|
||||||
|
slot.refresh()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_selected_slot_changed(
|
||||||
|
_slot_index: int,
|
||||||
|
_item_id: StringName,
|
||||||
|
) -> void:
|
||||||
|
_refresh()
|
||||||
|
if _hovered_slot_index < 0:
|
||||||
|
_show_selected_item_briefly()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_slot_item_hovered(
|
||||||
|
slot_index: int,
|
||||||
|
item_id: StringName,
|
||||||
|
) -> void:
|
||||||
|
if _item_name_suppressed:
|
||||||
|
return
|
||||||
|
_hovered_slot_index = slot_index
|
||||||
|
_item_name_timer.stop()
|
||||||
|
_show_item_name(item_id)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_slot_item_hover_ended(slot_index: int) -> void:
|
||||||
|
if slot_index != _hovered_slot_index:
|
||||||
|
return
|
||||||
|
_hovered_slot_index = -1
|
||||||
|
_show_selected_item_briefly()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_slot_drag_started() -> void:
|
||||||
|
_hovered_slot_index = -1
|
||||||
|
_hide_item_name()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_slot_drag_finished() -> void:
|
||||||
|
_show_selected_item_briefly()
|
||||||
|
|
||||||
|
|
||||||
|
func _show_selected_item_briefly() -> void:
|
||||||
|
if _item_name_suppressed or _hotbar == null:
|
||||||
|
_hide_item_name()
|
||||||
|
return
|
||||||
|
_show_item_name(_hotbar.get_selected_item_id())
|
||||||
|
if _selected_item_label.visible:
|
||||||
|
_item_name_timer.start()
|
||||||
|
|
||||||
|
|
||||||
|
func _show_item_name(item_id: StringName) -> void:
|
||||||
|
if item_id.is_empty() or _catalog == null:
|
||||||
|
_hide_item_name()
|
||||||
|
return
|
||||||
|
var item := _catalog.get_item_by_id(item_id)
|
||||||
|
if item == null:
|
||||||
|
_hide_item_name()
|
||||||
|
return
|
||||||
|
_selected_item_label.text = item.display_name
|
||||||
|
_selected_item_label.visible = true
|
||||||
|
|
||||||
|
|
||||||
|
func _hide_item_name() -> void:
|
||||||
|
_item_name_timer.stop()
|
||||||
|
_selected_item_label.text = ""
|
||||||
|
_selected_item_label.visible = false
|
||||||
|
|
||||||
|
|
||||||
|
func _on_item_name_timer_timeout() -> void:
|
||||||
|
if _hovered_slot_index < 0:
|
||||||
|
_hide_item_name()
|
||||||
1
ui/hotbar.gd.uid
Normal file
1
ui/hotbar.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://642w0srh4vwj
|
||||||
74
ui/hotbar.tscn
Normal file
74
ui/hotbar.tscn
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
[gd_scene load_steps=3 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://ui/hotbar.gd" id="1_script"]
|
||||||
|
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
|
||||||
|
|
||||||
|
[node name="Hotbar" type="Control"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
visible = false
|
||||||
|
z_index = 20
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 12
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_top = -84.0
|
||||||
|
offset_bottom = -8.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 0
|
||||||
|
mouse_filter = 2
|
||||||
|
theme = ExtResource("2_theme")
|
||||||
|
script = ExtResource("1_script")
|
||||||
|
|
||||||
|
[node name="Center" type="CenterContainer" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="Panel" type="PanelContainer" parent="Center"]
|
||||||
|
layout_mode = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="Margin" type="MarginContainer" parent="Center/Panel"]
|
||||||
|
layout_mode = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
theme_override_constants/margin_left = 5
|
||||||
|
theme_override_constants/margin_top = 5
|
||||||
|
theme_override_constants/margin_right = 5
|
||||||
|
theme_override_constants/margin_bottom = 5
|
||||||
|
|
||||||
|
[node name="SlotRow" type="HBoxContainer" parent="Center/Panel/Margin"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
theme_override_constants/separation = 4
|
||||||
|
|
||||||
|
[node name="SelectedItemLabel" type="Label" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
visible = false
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 10
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
offset_left = -150.0
|
||||||
|
offset_top = -28.0
|
||||||
|
offset_right = 150.0
|
||||||
|
offset_bottom = -8.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
clip_text = true
|
||||||
|
text_overrun_behavior = 3
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
theme_override_colors/font_color = Color(0.925, 0.953, 0.965, 1)
|
||||||
|
theme_override_colors/font_outline_color = Color(0.015, 0.02, 0.03, 0.95)
|
||||||
|
theme_override_constants/outline_size = 2
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
|
||||||
|
[node name="ItemNameTimer" type="Timer" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
wait_time = 1.5
|
||||||
|
one_shot = true
|
||||||
187
ui/hotbar_slot.gd
Normal file
187
ui/hotbar_slot.gd
Normal file
|
|
@ -0,0 +1,187 @@
|
||||||
|
class_name HotbarSlot
|
||||||
|
extends Button
|
||||||
|
|
||||||
|
signal item_hovered(slot_index: int, item_id: StringName)
|
||||||
|
signal item_hover_ended(slot_index: int)
|
||||||
|
signal item_drag_started
|
||||||
|
signal item_drag_finished
|
||||||
|
|
||||||
|
const ItemCatalogType = preload("res://items/item_catalog.gd")
|
||||||
|
const ItemDataType = preload("res://items/item_data.gd")
|
||||||
|
const PlayerBagType = preload("res://inventory/player_bag.gd")
|
||||||
|
const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
|
||||||
|
|
||||||
|
var slot_index: int = 0
|
||||||
|
var _hotbar: PlayerHotbarType
|
||||||
|
var _bag: PlayerBagType
|
||||||
|
var _catalog: ItemCatalogType
|
||||||
|
var _drag_enabled: bool = false
|
||||||
|
var _drag_in_progress: bool = false
|
||||||
|
var _slot_number_label: Label
|
||||||
|
var _quantity_label: Label
|
||||||
|
|
||||||
|
|
||||||
|
func setup(
|
||||||
|
index: int,
|
||||||
|
hotbar: PlayerHotbarType,
|
||||||
|
bag: PlayerBagType,
|
||||||
|
catalog: ItemCatalogType,
|
||||||
|
) -> void:
|
||||||
|
slot_index = index
|
||||||
|
_hotbar = hotbar
|
||||||
|
_bag = bag
|
||||||
|
_catalog = catalog
|
||||||
|
custom_minimum_size = Vector2(50, 50)
|
||||||
|
expand_icon = true
|
||||||
|
focus_mode = Control.FOCUS_ALL
|
||||||
|
_slot_number_label = _create_corner_label(str(slot_index + 1))
|
||||||
|
_quantity_label = _create_corner_label("", true)
|
||||||
|
pressed.connect(_select_slot)
|
||||||
|
mouse_entered.connect(_on_mouse_entered)
|
||||||
|
mouse_exited.connect(_on_mouse_exited)
|
||||||
|
refresh()
|
||||||
|
|
||||||
|
|
||||||
|
func set_drag_enabled(enabled: bool) -> void:
|
||||||
|
_drag_enabled = enabled
|
||||||
|
mouse_filter = (
|
||||||
|
Control.MOUSE_FILTER_STOP
|
||||||
|
if enabled
|
||||||
|
else Control.MOUSE_FILTER_IGNORE
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func refresh() -> void:
|
||||||
|
if _hotbar == null:
|
||||||
|
return
|
||||||
|
var item_id: StringName = _hotbar.get_item_id(slot_index)
|
||||||
|
var item: ItemDataType = (
|
||||||
|
_catalog.get_item_by_id(item_id)
|
||||||
|
if _catalog != null and not item_id.is_empty()
|
||||||
|
else null
|
||||||
|
)
|
||||||
|
icon = item.icon if item != null else null
|
||||||
|
var quantity: int = (
|
||||||
|
_bag.get_quantity(item_id)
|
||||||
|
if _bag != null and not item_id.is_empty()
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
var quantity_text: String = (
|
||||||
|
"×%d" % quantity
|
||||||
|
if item != null and item.stackable and quantity > 1
|
||||||
|
else ""
|
||||||
|
)
|
||||||
|
text = ""
|
||||||
|
_quantity_label.text = quantity_text
|
||||||
|
_quantity_label.visible = not quantity_text.is_empty()
|
||||||
|
tooltip_text = (
|
||||||
|
item.display_name if item != null else "Empty hotbar slot"
|
||||||
|
)
|
||||||
|
button_pressed = slot_index == _hotbar.get_selected_slot()
|
||||||
|
|
||||||
|
|
||||||
|
func _create_corner_label(
|
||||||
|
label_text: String,
|
||||||
|
bottom_right: bool = false,
|
||||||
|
) -> Label:
|
||||||
|
var label := Label.new()
|
||||||
|
label.text = label_text
|
||||||
|
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
label.add_theme_font_size_override("font_size", 11)
|
||||||
|
label.add_theme_color_override("font_color", UIPalette.TEXT)
|
||||||
|
if bottom_right:
|
||||||
|
label.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT)
|
||||||
|
label.position = Vector2(-22.0, -20.0)
|
||||||
|
else:
|
||||||
|
label.position = Vector2(5.0, 2.0)
|
||||||
|
add_child(label)
|
||||||
|
return label
|
||||||
|
|
||||||
|
|
||||||
|
func _gui_input(event: InputEvent) -> void:
|
||||||
|
if (
|
||||||
|
_drag_enabled
|
||||||
|
and event is InputEventMouseButton
|
||||||
|
and event.button_index == MOUSE_BUTTON_RIGHT
|
||||||
|
and event.pressed
|
||||||
|
and _hotbar != null
|
||||||
|
):
|
||||||
|
_hotbar.clear_slot(slot_index)
|
||||||
|
accept_event()
|
||||||
|
|
||||||
|
|
||||||
|
func _get_drag_data(_at_position: Vector2) -> Variant:
|
||||||
|
if not _drag_enabled or _hotbar == null:
|
||||||
|
return null
|
||||||
|
var item_id: StringName = _hotbar.get_item_id(slot_index)
|
||||||
|
if item_id.is_empty():
|
||||||
|
return null
|
||||||
|
var item: ItemDataType = _catalog.get_item_by_id(item_id)
|
||||||
|
var preview := TextureRect.new()
|
||||||
|
preview.custom_minimum_size = Vector2(44, 44)
|
||||||
|
preview.texture = item.icon if item != null else null
|
||||||
|
preview.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
|
preview.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||||
|
preview.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
set_drag_preview(preview)
|
||||||
|
_drag_in_progress = true
|
||||||
|
item_drag_started.emit()
|
||||||
|
return {
|
||||||
|
"kind": "hotbar_slot",
|
||||||
|
"slot_index": slot_index,
|
||||||
|
"item_id": String(item_id),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func _can_drop_data(_at_position: Vector2, data: Variant) -> bool:
|
||||||
|
if not _drag_enabled or typeof(data) != TYPE_DICTIONARY:
|
||||||
|
return false
|
||||||
|
var payload: Dictionary = data
|
||||||
|
var kind: String = str(payload.get("kind", ""))
|
||||||
|
if kind == "hotbar_slot":
|
||||||
|
var source_index: int = int(payload.get("slot_index", -1))
|
||||||
|
return (
|
||||||
|
source_index >= 0
|
||||||
|
and source_index < PlayerHotbarType.SLOT_COUNT
|
||||||
|
and source_index != slot_index
|
||||||
|
)
|
||||||
|
if kind != "bag_item":
|
||||||
|
return false
|
||||||
|
var item_id: StringName = StringName(str(payload.get("item_id", "")))
|
||||||
|
if _bag == null or not _bag.owns_item(item_id) or _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 _drop_data(_at_position: Vector2, data: Variant) -> void:
|
||||||
|
if not _can_drop_data(Vector2.ZERO, data) or _hotbar == null:
|
||||||
|
return
|
||||||
|
var payload: Dictionary = data
|
||||||
|
if str(payload.get("kind", "")) == "hotbar_slot":
|
||||||
|
_hotbar.swap_slots(int(payload["slot_index"]), slot_index)
|
||||||
|
else:
|
||||||
|
_hotbar.assign_item(
|
||||||
|
slot_index,
|
||||||
|
StringName(str(payload["item_id"]))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _select_slot() -> void:
|
||||||
|
if _hotbar != null:
|
||||||
|
_hotbar.select_slot(slot_index)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_mouse_entered() -> void:
|
||||||
|
if _hotbar != null:
|
||||||
|
item_hovered.emit(slot_index, _hotbar.get_item_id(slot_index))
|
||||||
|
|
||||||
|
|
||||||
|
func _on_mouse_exited() -> void:
|
||||||
|
item_hover_ended.emit(slot_index)
|
||||||
|
|
||||||
|
|
||||||
|
func _notification(what: int) -> void:
|
||||||
|
if what == NOTIFICATION_DRAG_END and _drag_in_progress:
|
||||||
|
_drag_in_progress = false
|
||||||
|
item_drag_finished.emit()
|
||||||
1
ui/hotbar_slot.gd.uid
Normal file
1
ui/hotbar_slot.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://swcob5q48f8e
|
||||||
46
ui/item_drag_source.gd
Normal file
46
ui/item_drag_source.gd
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
class_name ItemDragSource
|
||||||
|
extends Button
|
||||||
|
|
||||||
|
var item_id: StringName
|
||||||
|
var item_icon: Texture2D
|
||||||
|
var item_name: String
|
||||||
|
|
||||||
|
|
||||||
|
func setup(
|
||||||
|
new_item_id: StringName,
|
||||||
|
new_item_name: String,
|
||||||
|
new_icon: Texture2D,
|
||||||
|
) -> void:
|
||||||
|
item_id = new_item_id
|
||||||
|
item_name = new_item_name
|
||||||
|
item_icon = new_icon
|
||||||
|
|
||||||
|
|
||||||
|
func _get_drag_data(_at_position: Vector2) -> Variant:
|
||||||
|
if item_id.is_empty() or disabled:
|
||||||
|
return null
|
||||||
|
set_drag_preview(_create_drag_preview())
|
||||||
|
return {
|
||||||
|
"kind": "bag_item",
|
||||||
|
"item_id": String(item_id),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func _create_drag_preview() -> Control:
|
||||||
|
var preview := VBoxContainer.new()
|
||||||
|
preview.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
preview.add_theme_constant_override("separation", 2)
|
||||||
|
var texture := TextureRect.new()
|
||||||
|
texture.custom_minimum_size = Vector2(44, 44)
|
||||||
|
texture.texture = item_icon
|
||||||
|
texture.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
|
texture.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||||
|
texture.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
preview.add_child(texture)
|
||||||
|
var label := Label.new()
|
||||||
|
label.text = item_name
|
||||||
|
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
label.add_theme_font_size_override("font_size", 12)
|
||||||
|
preview.add_child(label)
|
||||||
|
return preview
|
||||||
1
ui/item_drag_source.gd.uid
Normal file
1
ui/item_drag_source.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://cfv418ixq3eeb
|
||||||
|
|
@ -13,6 +13,7 @@ const FishingSpotType = preload("res://fishing/fishing_spot.gd")
|
||||||
signal return_to_title_requested
|
signal return_to_title_requested
|
||||||
signal reset_progress_requested
|
signal reset_progress_requested
|
||||||
signal quit_requested
|
signal quit_requested
|
||||||
|
signal menu_visibility_changed(is_open: bool)
|
||||||
|
|
||||||
enum ConfirmationAction {
|
enum ConfirmationAction {
|
||||||
NONE,
|
NONE,
|
||||||
|
|
@ -100,6 +101,7 @@ func open_menu() -> void:
|
||||||
_confirmation_action = ConfirmationAction.NONE
|
_confirmation_action = ConfirmationAction.NONE
|
||||||
show()
|
show()
|
||||||
%ResumeButton.grab_focus()
|
%ResumeButton.grab_focus()
|
||||||
|
menu_visibility_changed.emit(true)
|
||||||
|
|
||||||
|
|
||||||
func resume() -> void:
|
func resume() -> void:
|
||||||
|
|
@ -138,6 +140,7 @@ func close_menu(
|
||||||
else:
|
else:
|
||||||
_control_snapshot_stored = false
|
_control_snapshot_stored = false
|
||||||
_apply_mouse_close_policy(reason)
|
_apply_mouse_close_policy(reason)
|
||||||
|
menu_visibility_changed.emit(false)
|
||||||
|
|
||||||
|
|
||||||
func handle_escape() -> bool:
|
func handle_escape() -> bool:
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,18 @@ const FishPoolType = preload("res://fish/fish_pool.gd")
|
||||||
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
|
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
|
||||||
const PlayerType = preload("res://player/player.gd")
|
const PlayerType = preload("res://player/player.gd")
|
||||||
const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
||||||
|
const ItemCatalogType = preload("res://items/item_catalog.gd")
|
||||||
|
const ItemDataType = preload("res://items/item_data.gd")
|
||||||
|
const OwnedItemType = preload("res://items/owned_item.gd")
|
||||||
|
const PlayerBagType = preload("res://inventory/player_bag.gd")
|
||||||
|
const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
|
||||||
|
const ItemDragSourceType = preload("res://ui/item_drag_source.gd")
|
||||||
|
|
||||||
signal menu_visibility_changed(is_open: bool)
|
signal menu_visibility_changed(is_open: bool)
|
||||||
|
|
||||||
enum Section {
|
enum Section {
|
||||||
INVENTORY,
|
COOLER,
|
||||||
|
BAG,
|
||||||
LOGBOOK,
|
LOGBOOK,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,11 +44,18 @@ enum CloseReason {
|
||||||
}
|
}
|
||||||
|
|
||||||
@onready var _inventory_tab: Button = %InventoryTab
|
@onready var _inventory_tab: Button = %InventoryTab
|
||||||
|
@onready var _bag_tab: Button = %BagTab
|
||||||
@onready var _logbook_tab: Button = %LogbookTab
|
@onready var _logbook_tab: Button = %LogbookTab
|
||||||
@onready var _close_button: Button = %CloseButton
|
@onready var _close_button: Button = %CloseButton
|
||||||
@onready var _wallet_balance: Label = %WalletBalance
|
@onready var _wallet_balance: Label = %WalletBalance
|
||||||
@onready var _inventory_section: Control = %InventorySection
|
@onready var _inventory_section: Control = %InventorySection
|
||||||
|
@onready var _bag_section: Control = %BagSection
|
||||||
@onready var _logbook_section: Control = %LogbookSection
|
@onready var _logbook_section: Control = %LogbookSection
|
||||||
|
@onready var _bag_empty: Label = %BagEmpty
|
||||||
|
@onready var _bag_grid: GridContainer = %BagGrid
|
||||||
|
@onready var _bag_detail_texture: TextureRect = %BagDetailTexture
|
||||||
|
@onready var _bag_detail_name: Label = %BagDetailName
|
||||||
|
@onready var _bag_detail_data: Label = %BagDetailData
|
||||||
@onready var _sort_option: OptionButton = %SortOption
|
@onready var _sort_option: OptionButton = %SortOption
|
||||||
@onready var _sort_direction: Button = %SortDirection
|
@onready var _sort_direction: Button = %SortDirection
|
||||||
@onready var _held_value: Label = %HeldValue
|
@onready var _held_value: Label = %HeldValue
|
||||||
|
|
@ -69,10 +83,14 @@ var _sale_service: FishSaleServiceType
|
||||||
var _default_buyer: FishBuyerProfileType
|
var _default_buyer: FishBuyerProfileType
|
||||||
var _catalog: FishPoolType
|
var _catalog: FishPoolType
|
||||||
var _fishing_spot: FishingSpotType
|
var _fishing_spot: FishingSpotType
|
||||||
var _current_section: Section = Section.INVENTORY
|
var _bag: PlayerBagType
|
||||||
|
var _hotbar: PlayerHotbarType
|
||||||
|
var _item_catalog: ItemCatalogType
|
||||||
|
var _current_section: Section = Section.COOLER
|
||||||
var _sort_mode: SortMode = SortMode.CATCH_ORDER
|
var _sort_mode: SortMode = SortMode.CATCH_ORDER
|
||||||
var _sort_descending: bool = true
|
var _sort_descending: bool = true
|
||||||
var _selected_catch_id: StringName
|
var _selected_catch_id: StringName
|
||||||
|
var _selected_bag_item_id: StringName
|
||||||
var _prior_movement_enabled: bool = true
|
var _prior_movement_enabled: bool = true
|
||||||
var _prior_camera_input_enabled: bool = true
|
var _prior_camera_input_enabled: bool = true
|
||||||
var _prior_mouse_mode: Input.MouseMode = Input.MOUSE_MODE_VISIBLE
|
var _prior_mouse_mode: Input.MouseMode = Input.MOUSE_MODE_VISIBLE
|
||||||
|
|
@ -87,8 +105,9 @@ var _sale_in_progress: bool = false
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
_inventory_tab.pressed.connect(
|
_inventory_tab.pressed.connect(
|
||||||
_show_section.bind(Section.INVENTORY)
|
_show_section.bind(Section.COOLER)
|
||||||
)
|
)
|
||||||
|
_bag_tab.pressed.connect(_show_section.bind(Section.BAG))
|
||||||
_logbook_tab.pressed.connect(
|
_logbook_tab.pressed.connect(
|
||||||
_show_section.bind(Section.LOGBOOK)
|
_show_section.bind(Section.LOGBOOK)
|
||||||
)
|
)
|
||||||
|
|
@ -116,6 +135,9 @@ func setup(
|
||||||
default_buyer: FishBuyerProfileType,
|
default_buyer: FishBuyerProfileType,
|
||||||
catalog: FishPoolType,
|
catalog: FishPoolType,
|
||||||
fishing_spot: FishingSpotType,
|
fishing_spot: FishingSpotType,
|
||||||
|
bag: PlayerBagType,
|
||||||
|
hotbar: PlayerHotbarType,
|
||||||
|
item_catalog: ItemCatalogType,
|
||||||
) -> void:
|
) -> void:
|
||||||
_player = player
|
_player = player
|
||||||
_inventory = inventory
|
_inventory = inventory
|
||||||
|
|
@ -125,6 +147,9 @@ func setup(
|
||||||
_default_buyer = default_buyer
|
_default_buyer = default_buyer
|
||||||
_catalog = catalog
|
_catalog = catalog
|
||||||
_fishing_spot = fishing_spot
|
_fishing_spot = fishing_spot
|
||||||
|
_bag = bag
|
||||||
|
_hotbar = hotbar
|
||||||
|
_item_catalog = item_catalog
|
||||||
if not _inventory.catches_changed.is_connected(_on_inventory_changed):
|
if not _inventory.catches_changed.is_connected(_on_inventory_changed):
|
||||||
_inventory.catches_changed.connect(_on_inventory_changed)
|
_inventory.catches_changed.connect(_on_inventory_changed)
|
||||||
if not _collection_log.fish_discovered.is_connected(_on_fish_discovered):
|
if not _collection_log.fish_discovered.is_connected(_on_fish_discovered):
|
||||||
|
|
@ -133,6 +158,8 @@ func setup(
|
||||||
_wallet.balance_changed.connect(_on_wallet_balance_changed)
|
_wallet.balance_changed.connect(_on_wallet_balance_changed)
|
||||||
if not _fishing_spot.bite_activated.is_connected(_on_bite_activated):
|
if not _fishing_spot.bite_activated.is_connected(_on_bite_activated):
|
||||||
_fishing_spot.bite_activated.connect(_on_bite_activated)
|
_fishing_spot.bite_activated.connect(_on_bite_activated)
|
||||||
|
if not _bag.contents_changed.is_connected(_on_bag_changed):
|
||||||
|
_bag.contents_changed.connect(_on_bag_changed)
|
||||||
_refresh_all()
|
_refresh_all()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -194,6 +221,7 @@ func close_menu(
|
||||||
) -> void:
|
) -> void:
|
||||||
if not visible:
|
if not visible:
|
||||||
return
|
return
|
||||||
|
get_viewport().gui_cancel_drag()
|
||||||
_close_sale_confirmation()
|
_close_sale_confirmation()
|
||||||
var closing_generation: int = _menu_generation
|
var closing_generation: int = _menu_generation
|
||||||
visible = false
|
visible = false
|
||||||
|
|
@ -268,17 +296,21 @@ func _show_section(section: Section) -> void:
|
||||||
_current_section = section
|
_current_section = section
|
||||||
if not is_node_ready():
|
if not is_node_ready():
|
||||||
return
|
return
|
||||||
_inventory_section.visible = section == Section.INVENTORY
|
_inventory_section.visible = section == Section.COOLER
|
||||||
|
_bag_section.visible = section == Section.BAG
|
||||||
_logbook_section.visible = section == Section.LOGBOOK
|
_logbook_section.visible = section == Section.LOGBOOK
|
||||||
_inventory_tab.button_pressed = section == Section.INVENTORY
|
_inventory_tab.button_pressed = section == Section.COOLER
|
||||||
|
_bag_tab.button_pressed = section == Section.BAG
|
||||||
_logbook_tab.button_pressed = section == Section.LOGBOOK
|
_logbook_tab.button_pressed = section == Section.LOGBOOK
|
||||||
if visible:
|
if visible:
|
||||||
_focus_current_section()
|
_focus_current_section()
|
||||||
|
|
||||||
|
|
||||||
func _focus_current_section() -> void:
|
func _focus_current_section() -> void:
|
||||||
if _current_section == Section.INVENTORY:
|
if _current_section == Section.COOLER:
|
||||||
_inventory_tab.grab_focus()
|
_inventory_tab.grab_focus()
|
||||||
|
elif _current_section == Section.BAG:
|
||||||
|
_bag_tab.grab_focus()
|
||||||
else:
|
else:
|
||||||
_logbook_tab.grab_focus()
|
_logbook_tab.grab_focus()
|
||||||
|
|
||||||
|
|
@ -307,9 +339,99 @@ func _update_sort_direction_text() -> void:
|
||||||
func _refresh_all() -> void:
|
func _refresh_all() -> void:
|
||||||
_refresh_economy_summary()
|
_refresh_economy_summary()
|
||||||
_refresh_inventory()
|
_refresh_inventory()
|
||||||
|
_refresh_bag()
|
||||||
_refresh_logbook()
|
_refresh_logbook()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_bag_changed() -> void:
|
||||||
|
_refresh_bag()
|
||||||
|
|
||||||
|
|
||||||
|
func _refresh_bag() -> void:
|
||||||
|
if not is_node_ready():
|
||||||
|
return
|
||||||
|
_clear_container(_bag_grid)
|
||||||
|
var owned_items: Array[OwnedItemType] = (
|
||||||
|
_bag.get_all_items() if _bag != null else []
|
||||||
|
)
|
||||||
|
owned_items.sort_custom(_sort_bag_items)
|
||||||
|
_bag_empty.visible = owned_items.is_empty()
|
||||||
|
if (
|
||||||
|
not _selected_bag_item_id.is_empty()
|
||||||
|
and (_bag == null or not _bag.owns_item(_selected_bag_item_id))
|
||||||
|
):
|
||||||
|
_selected_bag_item_id = StringName()
|
||||||
|
for owned: OwnedItemType in owned_items:
|
||||||
|
var item: ItemDataType = _item_catalog.get_item_by_id(owned.item_id)
|
||||||
|
if item == null:
|
||||||
|
continue
|
||||||
|
var card := ItemDragSourceType.new()
|
||||||
|
card.custom_minimum_size = Vector2(140, 88)
|
||||||
|
card.expand_icon = true
|
||||||
|
card.icon = item.icon
|
||||||
|
card.text = "%s%s\n%s" % [
|
||||||
|
item.display_name,
|
||||||
|
" ×%d" % owned.quantity if owned.quantity > 1 else "",
|
||||||
|
item.get_category_name(),
|
||||||
|
]
|
||||||
|
card.tooltip_text = (
|
||||||
|
"Drag to a hotbar slot."
|
||||||
|
if item.hotbar_allowed
|
||||||
|
else "This item cannot be assigned to the hotbar."
|
||||||
|
)
|
||||||
|
card.setup(item.item_id, item.display_name, item.icon)
|
||||||
|
card.pressed.connect(_select_bag_item.bind(item.item_id))
|
||||||
|
_bag_grid.add_child(card)
|
||||||
|
_update_bag_detail()
|
||||||
|
|
||||||
|
|
||||||
|
func _sort_bag_items(a: OwnedItemType, b: OwnedItemType) -> bool:
|
||||||
|
var item_a: ItemDataType = _item_catalog.get_item_by_id(a.item_id)
|
||||||
|
var item_b: ItemDataType = _item_catalog.get_item_by_id(b.item_id)
|
||||||
|
if item_a == null:
|
||||||
|
return false
|
||||||
|
if item_b == null:
|
||||||
|
return true
|
||||||
|
if item_a.category != item_b.category:
|
||||||
|
return item_a.category < item_b.category
|
||||||
|
return item_a.display_name.naturalnocasecmp_to(item_b.display_name) < 0
|
||||||
|
|
||||||
|
|
||||||
|
func _select_bag_item(item_id: StringName) -> void:
|
||||||
|
_selected_bag_item_id = item_id
|
||||||
|
_update_bag_detail()
|
||||||
|
|
||||||
|
|
||||||
|
func _update_bag_detail() -> void:
|
||||||
|
var item: ItemDataType = (
|
||||||
|
_item_catalog.get_item_by_id(_selected_bag_item_id)
|
||||||
|
if _item_catalog != null and not _selected_bag_item_id.is_empty()
|
||||||
|
else null
|
||||||
|
)
|
||||||
|
var quantity: int = (
|
||||||
|
_bag.get_quantity(_selected_bag_item_id)
|
||||||
|
if _bag != null and item != null
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
_bag_detail_texture.texture = item.icon if item != null else null
|
||||||
|
_bag_detail_name.text = item.display_name if item != null else ""
|
||||||
|
_bag_detail_data.text = (
|
||||||
|
"%s\nQuantity: %d\n%s\n%s"
|
||||||
|
% [
|
||||||
|
item.get_category_name(),
|
||||||
|
quantity,
|
||||||
|
item.description,
|
||||||
|
(
|
||||||
|
"Can be assigned to the hotbar."
|
||||||
|
if item.hotbar_allowed
|
||||||
|
else "Cannot be assigned to the hotbar."
|
||||||
|
),
|
||||||
|
]
|
||||||
|
if item != null
|
||||||
|
else "Select a Bag item for details."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _on_inventory_changed() -> void:
|
func _on_inventory_changed() -> void:
|
||||||
_refresh_economy_summary()
|
_refresh_economy_summary()
|
||||||
_revalidate_confirmation()
|
_revalidate_confirmation()
|
||||||
|
|
@ -384,7 +506,7 @@ func _compare_catches(left: FishCatchType, right: FishCatchType) -> bool:
|
||||||
func _create_inventory_card(fish_catch: FishCatchType) -> Button:
|
func _create_inventory_card(fish_catch: FishCatchType) -> Button:
|
||||||
var card := Button.new()
|
var card := Button.new()
|
||||||
card.theme_type_variation = &"CardButton"
|
card.theme_type_variation = &"CardButton"
|
||||||
card.custom_minimum_size = Vector2(152.0, 140.0)
|
card.custom_minimum_size = Vector2(132.0, 118.0)
|
||||||
card.toggle_mode = true
|
card.toggle_mode = true
|
||||||
card.button_pressed = fish_catch.catch_id == _selected_catch_id
|
card.button_pressed = fish_catch.catch_id == _selected_catch_id
|
||||||
card.pressed.connect(_select_catch.bind(fish_catch.catch_id))
|
card.pressed.connect(_select_catch.bind(fish_catch.catch_id))
|
||||||
|
|
@ -400,7 +522,7 @@ func _create_inventory_card(fish_catch: FishCatchType) -> Button:
|
||||||
card.add_child(content)
|
card.add_child(content)
|
||||||
content.add_child(_create_texture_frame(
|
content.add_child(_create_texture_frame(
|
||||||
fish_catch.fish.display_texture,
|
fish_catch.fish.display_texture,
|
||||||
Vector2(124.0, 78.0)
|
Vector2(108.0, 62.0)
|
||||||
))
|
))
|
||||||
var name_label := Label.new()
|
var name_label := Label.new()
|
||||||
name_label.text = fish_catch.fish.display_name
|
name_label.text = fish_catch.fish.display_name
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,19 @@
|
||||||
|
|
||||||
[node name="PlayerMenu" type="Control"]
|
[node name="PlayerMenu" type="Control"]
|
||||||
visible = false
|
visible = false
|
||||||
|
z_index = 30
|
||||||
layout_mode = 3
|
layout_mode = 3
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
mouse_filter = 0
|
mouse_filter = 2
|
||||||
theme = ExtResource("2_theme")
|
theme = ExtResource("2_theme")
|
||||||
script = ExtResource("1_menu")
|
script = ExtResource("1_menu")
|
||||||
|
|
||||||
[node name="Dimmer" type="ColorRect" parent="."]
|
[node name="Dimmer" type="ColorRect" parent="."]
|
||||||
|
z_index = -20
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
|
|
@ -27,23 +29,23 @@ color = Color(0.015, 0.02, 0.03, 0.72)
|
||||||
|
|
||||||
[node name="MenuPanel" type="PanelContainer" parent="."]
|
[node name="MenuPanel" type="PanelContainer" parent="."]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchor_left = 0.055
|
anchor_left = 0.07
|
||||||
anchor_top = 0.045
|
anchor_top = 0.055
|
||||||
anchor_right = 0.945
|
anchor_right = 0.93
|
||||||
anchor_bottom = 0.955
|
anchor_bottom = 0.72
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
|
|
||||||
[node name="Margin" type="MarginContainer" parent="MenuPanel"]
|
[node name="Margin" type="MarginContainer" parent="MenuPanel"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/margin_left = 22
|
theme_override_constants/margin_left = 14
|
||||||
theme_override_constants/margin_top = 18
|
theme_override_constants/margin_top = 12
|
||||||
theme_override_constants/margin_right = 22
|
theme_override_constants/margin_right = 14
|
||||||
theme_override_constants/margin_bottom = 20
|
theme_override_constants/margin_bottom = 12
|
||||||
|
|
||||||
[node name="Layout" type="VBoxContainer" parent="MenuPanel/Margin"]
|
[node name="Layout" type="VBoxContainer" parent="MenuPanel/Margin"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/separation = 12
|
theme_override_constants/separation = 8
|
||||||
|
|
||||||
[node name="Header" type="HBoxContainer" parent="MenuPanel/Margin/Layout"]
|
[node name="Header" type="HBoxContainer" parent="MenuPanel/Margin/Layout"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
@ -53,7 +55,7 @@ theme_override_constants/separation = 8
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
|
theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
|
||||||
theme_override_font_sizes/font_size = 26
|
theme_override_font_sizes/font_size = 22
|
||||||
text = "Player Menu"
|
text = "Player Menu"
|
||||||
|
|
||||||
[node name="WalletBalance" type="Label" parent="MenuPanel/Margin/Layout/Header"]
|
[node name="WalletBalance" type="Label" parent="MenuPanel/Margin/Layout/Header"]
|
||||||
|
|
@ -66,21 +68,28 @@ theme_override_colors/font_color = Color(1, 0.82, 0.4, 1)
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
toggle_mode = true
|
toggle_mode = true
|
||||||
text = "Inventory"
|
text = "Cooler"
|
||||||
custom_minimum_size = Vector2(104, 36)
|
custom_minimum_size = Vector2(88, 30)
|
||||||
|
|
||||||
|
[node name="BagTab" type="Button" parent="MenuPanel/Margin/Layout/Header"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
toggle_mode = true
|
||||||
|
text = "Bag"
|
||||||
|
custom_minimum_size = Vector2(76, 30)
|
||||||
|
|
||||||
[node name="LogbookTab" type="Button" parent="MenuPanel/Margin/Layout/Header"]
|
[node name="LogbookTab" type="Button" parent="MenuPanel/Margin/Layout/Header"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
toggle_mode = true
|
toggle_mode = true
|
||||||
text = "Logbook"
|
text = "Logbook"
|
||||||
custom_minimum_size = Vector2(104, 36)
|
custom_minimum_size = Vector2(88, 30)
|
||||||
|
|
||||||
[node name="CloseButton" type="Button" parent="MenuPanel/Margin/Layout/Header"]
|
[node name="CloseButton" type="Button" parent="MenuPanel/Margin/Layout/Header"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Close"
|
text = "Close"
|
||||||
custom_minimum_size = Vector2(76, 36)
|
custom_minimum_size = Vector2(68, 30)
|
||||||
|
|
||||||
[node name="Separator" type="HSeparator" parent="MenuPanel/Margin/Layout"]
|
[node name="Separator" type="HSeparator" parent="MenuPanel/Margin/Layout"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
@ -91,10 +100,10 @@ layout_mode = 2
|
||||||
text = ""
|
text = ""
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
theme_override_colors/font_color = Color(1, 0.82, 0.4, 1)
|
theme_override_colors/font_color = Color(1, 0.82, 0.4, 1)
|
||||||
theme_override_font_sizes/font_size = 16
|
theme_override_font_sizes/font_size = 14
|
||||||
|
|
||||||
[node name="Content" type="Control" parent="MenuPanel/Margin/Layout"]
|
[node name="Content" type="Control" parent="MenuPanel/Margin/Layout"]
|
||||||
custom_minimum_size = Vector2(0, 390)
|
custom_minimum_size = Vector2(0, 260)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
|
@ -106,11 +115,11 @@ anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
theme_override_constants/separation = 10
|
theme_override_constants/separation = 7
|
||||||
|
|
||||||
[node name="SortBar" type="HBoxContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection"]
|
[node name="SortBar" type="HBoxContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/separation = 8
|
theme_override_constants/separation = 6
|
||||||
|
|
||||||
[node name="SortLabel" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
|
[node name="SortLabel" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
@ -119,12 +128,12 @@ text = "Sort:"
|
||||||
[node name="SortOption" type="OptionButton" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
|
[node name="SortOption" type="OptionButton" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
custom_minimum_size = Vector2(145, 36)
|
custom_minimum_size = Vector2(130, 30)
|
||||||
|
|
||||||
[node name="SortDirection" type="Button" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
|
[node name="SortDirection" type="Button" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
custom_minimum_size = Vector2(125, 36)
|
custom_minimum_size = Vector2(112, 30)
|
||||||
text = "Newest first"
|
text = "Newest first"
|
||||||
|
|
||||||
[node name="SortSpacer" type="Control" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
|
[node name="SortSpacer" type="Control" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
|
||||||
|
|
@ -140,28 +149,28 @@ theme_override_colors/font_color = Color(0.682, 0.733, 0.761, 1)
|
||||||
[node name="InventoryBody" type="HSplitContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection"]
|
[node name="InventoryBody" type="HSplitContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
split_offset = 560
|
split_offset = 510
|
||||||
theme_override_constants/separation = 12
|
theme_override_constants/separation = 8
|
||||||
|
|
||||||
[node name="InventoryList" type="PanelContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody"]
|
[node name="InventoryList" type="PanelContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody"]
|
||||||
custom_minimum_size = Vector2(400, 0)
|
custom_minimum_size = Vector2(350, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="InventoryListMargin" type="MarginContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/InventoryList"]
|
[node name="InventoryListMargin" type="MarginContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/InventoryList"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/margin_left = 12
|
theme_override_constants/margin_left = 8
|
||||||
theme_override_constants/margin_top = 12
|
theme_override_constants/margin_top = 8
|
||||||
theme_override_constants/margin_right = 12
|
theme_override_constants/margin_right = 8
|
||||||
theme_override_constants/margin_bottom = 12
|
theme_override_constants/margin_bottom = 8
|
||||||
|
|
||||||
[node name="InventoryStack" type="VBoxContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/InventoryList/InventoryListMargin"]
|
[node name="InventoryStack" type="VBoxContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/InventoryList/InventoryListMargin"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/separation = 10
|
theme_override_constants/separation = 6
|
||||||
|
|
||||||
[node name="InventoryEmpty" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/InventoryList/InventoryListMargin/InventoryStack"]
|
[node name="InventoryEmpty" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/InventoryList/InventoryListMargin/InventoryStack"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "No fish in your inventory."
|
text = "Your Cooler is empty."
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="InventoryScroll" type="ScrollContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/InventoryList/InventoryListMargin/InventoryStack"]
|
[node name="InventoryScroll" type="ScrollContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/InventoryList/InventoryListMargin/InventoryStack"]
|
||||||
|
|
@ -173,28 +182,28 @@ horizontal_scroll_mode = 0
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
theme_override_constants/h_separation = 10
|
theme_override_constants/h_separation = 7
|
||||||
theme_override_constants/v_separation = 10
|
theme_override_constants/v_separation = 7
|
||||||
columns = 3
|
columns = 3
|
||||||
|
|
||||||
[node name="DetailPanel" type="PanelContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody"]
|
[node name="DetailPanel" type="PanelContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody"]
|
||||||
custom_minimum_size = Vector2(240, 0)
|
custom_minimum_size = Vector2(210, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="DetailMargin" type="MarginContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel"]
|
[node name="DetailMargin" type="MarginContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/margin_left = 16
|
theme_override_constants/margin_left = 10
|
||||||
theme_override_constants/margin_top = 14
|
theme_override_constants/margin_top = 9
|
||||||
theme_override_constants/margin_right = 16
|
theme_override_constants/margin_right = 10
|
||||||
theme_override_constants/margin_bottom = 14
|
theme_override_constants/margin_bottom = 9
|
||||||
|
|
||||||
[node name="DetailStack" type="VBoxContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin"]
|
[node name="DetailStack" type="VBoxContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/separation = 10
|
theme_override_constants/separation = 6
|
||||||
|
|
||||||
[node name="DetailTexture" type="TextureRect" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"]
|
[node name="DetailTexture" type="TextureRect" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(208, 140)
|
custom_minimum_size = Vector2(178, 100)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
expand_mode = 1
|
expand_mode = 1
|
||||||
stretch_mode = 5
|
stretch_mode = 5
|
||||||
|
|
@ -204,7 +213,7 @@ texture_filter = 1
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
|
theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
|
||||||
theme_override_font_sizes/font_size = 22
|
theme_override_font_sizes/font_size = 19
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="DetailData" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"]
|
[node name="DetailData" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"]
|
||||||
|
|
@ -234,6 +243,98 @@ autowrap_mode = 2
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
theme_override_colors/font_color = Color(1, 0.702, 0.278, 1)
|
theme_override_colors/font_color = Color(1, 0.702, 0.278, 1)
|
||||||
|
|
||||||
|
[node name="BagSection" type="VBoxContainer" parent="MenuPanel/Margin/Layout/Content"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
visible = false
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_constants/separation = 7
|
||||||
|
|
||||||
|
[node name="BagHint" type="Label" parent="MenuPanel/Margin/Layout/Content/BagSection"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Drag hotbar-compatible items onto a slot below. Right-click a slot to clear it."
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="BagBody" type="HSplitContainer" parent="MenuPanel/Margin/Layout/Content/BagSection"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
split_offset = 540
|
||||||
|
theme_override_constants/separation = 8
|
||||||
|
|
||||||
|
[node name="BagList" type="PanelContainer" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody"]
|
||||||
|
custom_minimum_size = Vector2(360, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Margin" type="MarginContainer" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagList"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/margin_left = 8
|
||||||
|
theme_override_constants/margin_top = 8
|
||||||
|
theme_override_constants/margin_right = 8
|
||||||
|
theme_override_constants/margin_bottom = 8
|
||||||
|
|
||||||
|
[node name="Stack" type="VBoxContainer" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagList/Margin"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = 6
|
||||||
|
|
||||||
|
[node name="BagEmpty" type="Label" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagList/Margin/Stack"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Your Bag is empty."
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="BagScroll" type="ScrollContainer" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagList/Margin/Stack"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
horizontal_scroll_mode = 0
|
||||||
|
|
||||||
|
[node name="BagGrid" type="GridContainer" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagList/Margin/Stack/BagScroll"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_constants/h_separation = 7
|
||||||
|
theme_override_constants/v_separation = 7
|
||||||
|
columns = 3
|
||||||
|
|
||||||
|
[node name="BagDetail" type="PanelContainer" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody"]
|
||||||
|
custom_minimum_size = Vector2(220, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Margin" type="MarginContainer" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagDetail"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/margin_left = 10
|
||||||
|
theme_override_constants/margin_top = 9
|
||||||
|
theme_override_constants/margin_right = 10
|
||||||
|
theme_override_constants/margin_bottom = 9
|
||||||
|
|
||||||
|
[node name="Stack" type="VBoxContainer" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagDetail/Margin"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = 6
|
||||||
|
|
||||||
|
[node name="BagDetailTexture" type="TextureRect" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagDetail/Margin/Stack"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
custom_minimum_size = Vector2(180, 96)
|
||||||
|
layout_mode = 2
|
||||||
|
expand_mode = 1
|
||||||
|
stretch_mode = 5
|
||||||
|
texture_filter = 1
|
||||||
|
|
||||||
|
[node name="BagDetailName" type="Label" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagDetail/Margin/Stack"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 19
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="BagDetailData" type="Label" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagDetail/Margin/Stack"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Select a Bag item for details."
|
||||||
|
horizontal_alignment = 1
|
||||||
|
autowrap_mode = 2
|
||||||
|
|
||||||
[node name="LogbookSection" type="VBoxContainer" parent="MenuPanel/Margin/Layout/Content"]
|
[node name="LogbookSection" type="VBoxContainer" parent="MenuPanel/Margin/Layout/Content"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
visible = false
|
visible = false
|
||||||
|
|
@ -243,7 +344,7 @@ anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
theme_override_constants/separation = 10
|
theme_override_constants/separation = 7
|
||||||
|
|
||||||
[node name="LogbookEmpty" type="Label" parent="MenuPanel/Margin/Layout/Content/LogbookSection"]
|
[node name="LogbookEmpty" type="Label" parent="MenuPanel/Margin/Layout/Content/LogbookSection"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
@ -260,13 +361,14 @@ horizontal_scroll_mode = 0
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
theme_override_constants/h_separation = 10
|
theme_override_constants/h_separation = 7
|
||||||
theme_override_constants/v_separation = 10
|
theme_override_constants/v_separation = 7
|
||||||
columns = 4
|
columns = 4
|
||||||
|
|
||||||
[node name="SaleConfirmation" type="PanelContainer" parent="MenuPanel/Margin/Layout/Content"]
|
[node name="SaleConfirmation" type="PanelContainer" parent="MenuPanel/Margin/Layout/Content"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
visible = false
|
visible = false
|
||||||
|
z_index = 40
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 8
|
anchors_preset = 8
|
||||||
anchor_left = 0.5
|
anchor_left = 0.5
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue