diff --git a/economy/buyers/pelicans.tres b/economy/buyers/pelicans.tres
index d126545..a9867c3 100644
--- a/economy/buyers/pelicans.tres
+++ b/economy/buyers/pelicans.tres
@@ -8,5 +8,5 @@ id = &"pelicans"
display_name = "Pelicans"
animal_name_singular = "pelican"
animal_name_plural = "pelicans"
-payout_multiplier = 0.7
+payout_multiplier = 0.25
sale_message_template = "You sold your {fish} to the pelicans for ${payout}."
diff --git a/economy/fishing_shop_stock.gd b/economy/fishing_shop_stock.gd
new file mode 100644
index 0000000..3d5e867
--- /dev/null
+++ b/economy/fishing_shop_stock.gd
@@ -0,0 +1,58 @@
+class_name FishingShopStock
+extends RefCounted
+
+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 PlayerWalletType = preload("res://economy/player_wallet.gd")
+
+const ITEM_PRICES: Dictionary[StringName, int] = {
+ &"coffee": 20,
+ &"energy_drink": 35,
+ &"snack": 30,
+ &"fish_finder": 60,
+}
+const ITEM_ORDER: Array[StringName] = [
+ &"coffee",
+ &"energy_drink",
+ &"snack",
+ &"fish_finder",
+]
+
+
+static func get_price(item_id: StringName) -> int:
+ return ITEM_PRICES.get(item_id, -1)
+
+
+static func get_stock_item_ids() -> Array[StringName]:
+ return ITEM_ORDER.duplicate()
+
+
+static func purchase_one(
+ item_id: StringName,
+ wallet: PlayerWalletType,
+ bag: PlayerBagType,
+ catalog: ItemCatalogType,
+) -> bool:
+ if wallet == null or bag == null or catalog == null:
+ return false
+ var price: int = get_price(item_id)
+ var item: ItemDataType = catalog.get_item_by_id(item_id)
+ if (
+ price < 0
+ or item == null
+ or not item.is_valid()
+ or item.category != ItemDataType.Category.CONSUMABLE
+ or not item.stackable
+ or not item.usable
+ or not bag.can_add_item(item_id, 1)
+ or not wallet.can_afford(price)
+ ):
+ return false
+ if not wallet.debit(price):
+ return false
+ if bag.add_item(item_id, 1):
+ return true
+ if not wallet.credit(price):
+ push_error("Fishing Shop failed to roll back an item purchase.")
+ return false
diff --git a/economy/fishing_shop_stock.gd.uid b/economy/fishing_shop_stock.gd.uid
new file mode 100644
index 0000000..951bd91
--- /dev/null
+++ b/economy/fishing_shop_stock.gd.uid
@@ -0,0 +1 @@
+uid://dvvvjfah26pp3
diff --git a/fish/fish_selector.gd b/fish/fish_selector.gd
index 7764ad3..3c1656b 100644
--- a/fish/fish_selector.gd
+++ b/fish/fish_selector.gd
@@ -8,6 +8,7 @@ const FishPoolType = preload("res://fish/fish_pool.gd")
const FishingContextType = preload("res://fishing/fishing_context.gd")
var undiscovered_weight_multiplier: float = 1.5
+var rarity_weight_multipliers: Array[float] = []
var use_deterministic_test_seed: bool = false
var deterministic_test_seed: int = 24680
var selection_seed: int = 0
@@ -45,6 +46,8 @@ func select_fish(
final_weight *= fish.availability.get_bait_weight_multiplier(context)
if not collection_log.has_discovered(fish.id):
final_weight *= maxf(undiscovered_weight_multiplier, 0.0)
+ if fish.rarity >= 0 and fish.rarity < rarity_weight_multipliers.size():
+ final_weight *= maxf(rarity_weight_multipliers[fish.rarity], 0.0)
if final_weight <= 0.0:
continue
eligible_fish.append(fish)
diff --git a/fish/species/bass/bass.tres b/fish/species/bass/bass.tres
index 65c2801..d9ffd1c 100644
--- a/fish/species/bass/bass.tres
+++ b/fish/species/bass/bass.tres
@@ -25,7 +25,7 @@ weight_max_lb = 8.0
display_scale_min = 0.9
display_scale_max = 1.5
display_scale_curve = 0.9
-sell_value_min = 12
-sell_value_max = 45
+sell_value_min = 7
+sell_value_max = 7
sell_value_curve = 0.9
display_texture = ExtResource("4_texture")
diff --git a/fish/species/bluegill/bluegill.tres b/fish/species/bluegill/bluegill.tres
index 0f47849..f3eb81d 100644
--- a/fish/species/bluegill/bluegill.tres
+++ b/fish/species/bluegill/bluegill.tres
@@ -19,6 +19,6 @@ display_scale_min = 0.7
display_scale_max = 1.1
display_scale_curve = 1.0
sell_value_min = 3
-sell_value_max = 12
+sell_value_max = 3
sell_value_curve = 1.0
display_texture = ExtResource("4_texture")
diff --git a/fish/species/carp/carp.tres b/fish/species/carp/carp.tres
index e6794f5..571cf69 100644
--- a/fish/species/carp/carp.tres
+++ b/fish/species/carp/carp.tres
@@ -25,7 +25,7 @@ weight_max_lb = 18.0
display_scale_min = 1.0
display_scale_max = 1.8
display_scale_curve = 0.85
-sell_value_min = 25
-sell_value_max = 90
+sell_value_min = 10
+sell_value_max = 10
sell_value_curve = 0.85
display_texture = ExtResource("4_texture")
diff --git a/fishing/catch_controller.gd b/fishing/catch_controller.gd
index 7043508..350bafa 100644
--- a/fishing/catch_controller.gd
+++ b/fishing/catch_controller.gd
@@ -128,6 +128,13 @@ func set_reel_input(held: bool) -> void:
_auto_click_accumulator = 0.0
+func set_effective_stats(reel_speed: float, click_power: int) -> void:
+ if state == CatchState.IDLE:
+ return
+ _reel_speed = maxf(reel_speed, 0.0)
+ _click_power = maxi(click_power, 1)
+
+
func handle_primary_pressed() -> void:
if state not in [CatchState.REELING, CatchState.BLOCKED_BY_BARRIER]:
return
diff --git a/fishing/fishing_spot.gd b/fishing/fishing_spot.gd
index ca82d34..3198fb0 100644
--- a/fishing/fishing_spot.gd
+++ b/fishing/fishing_spot.gd
@@ -16,6 +16,12 @@ const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
const PlayerFishingUpgradesType = preload(
"res://progression/player_fishing_upgrades.gd"
)
+const PlayerItemEffectsType = preload(
+ "res://progression/player_item_effects.gd"
+)
+const PlayerCoolerCapacityType = preload(
+ "res://progression/player_cooler_capacity.gd"
+)
const PlayerType = preload("res://player/player.gd")
const FishableWaterRegionType = preload(
"res://world/fishable_water_region.gd"
@@ -96,6 +102,8 @@ var _local_bag: PlayerBagType
var _local_hotbar: PlayerHotbarType
var _item_catalog: ItemCatalogType
var _fishing_upgrades: PlayerFishingUpgradesType
+var _item_effects: PlayerItemEffectsType
+var _cooler_capacity: PlayerCoolerCapacityType
var _active_player: PlayerType
var _state_time_remaining: float = 0.0
var _cast_charge: float = 0.0
@@ -137,6 +145,8 @@ func setup(
local_hotbar: PlayerHotbarType,
item_catalog: ItemCatalogType,
fishing_upgrades: PlayerFishingUpgradesType,
+ item_effects: PlayerItemEffectsType,
+ cooler_capacity: PlayerCoolerCapacityType,
) -> void:
_local_player = local_player
_local_inventory = local_inventory
@@ -145,6 +155,18 @@ func setup(
_local_hotbar = local_hotbar
_item_catalog = item_catalog
_fishing_upgrades = fishing_upgrades
+ _item_effects = item_effects
+ _cooler_capacity = cooler_capacity
+ if not _local_inventory.catches_changed.is_connected(
+ _on_cooler_availability_changed
+ ):
+ _local_inventory.catches_changed.connect(
+ _on_cooler_availability_changed
+ )
+ if not _cooler_capacity.capacity_changed.is_connected(
+ _on_cooler_capacity_changed
+ ):
+ _cooler_capacity.capacity_changed.connect(_on_cooler_capacity_changed)
func can_open_player_menu() -> bool:
@@ -322,6 +344,10 @@ func _process(delta: float) -> void:
FishingState.WAITING_FOR_BITE:
_update_waiting_for_bite(delta)
FishingState.FIGHTING:
+ _catch_controller.set_effective_stats(
+ _get_effective_reel_speed(),
+ _get_effective_barrier_damage()
+ )
if not Input.is_action_pressed("fish_primary"):
_catch_controller.set_reel_input(false)
FishingState.COOLDOWN:
@@ -347,10 +373,29 @@ func _unhandled_input(event: InputEvent) -> void:
FishingState.READY:
if not _new_cast_press_armed:
return
+ var active_item: ItemDataType = _get_active_item()
+ if (
+ active_item != null
+ and active_item.category == ItemDataType.Category.CONSUMABLE
+ ):
+ if _item_effects.use_consumable(active_item, _local_bag):
+ status_changed.emit(
+ _item_effects.get_feedback(active_item.item_id)
+ )
+ get_viewport().set_input_as_handled()
+ return
if not has_active_fishing_rod():
status_changed.emit("Select a fishing rod to cast.")
get_viewport().set_input_as_handled()
return
+ if (
+ _is_cooler_full()
+ ):
+ status_changed.emit(
+ "Cooler full. Sell fish before casting again."
+ )
+ get_viewport().set_input_as_handled()
+ return
_begin_aiming(_local_player)
FishingState.WAITING_FOR_BITE:
_withdrawal_input_held = true
@@ -411,10 +456,7 @@ func has_active_fishing_rod() -> bool:
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)
+ var item: ItemDataType = _get_active_item()
return (
item != null
and item.is_valid()
@@ -424,6 +466,38 @@ func has_active_fishing_rod() -> bool:
)
+func _get_active_item() -> ItemDataType:
+ if (
+ _local_bag == null
+ or _local_hotbar == null
+ or _item_catalog == null
+ ):
+ return null
+ var item_id: StringName = _local_hotbar.get_selected_item_id()
+ if item_id.is_empty() or not _local_bag.owns_item(item_id):
+ return null
+ var item: ItemDataType = _item_catalog.get_item_by_id(item_id)
+ return item if item != null and item.is_valid() else null
+
+
+func _is_cooler_full() -> bool:
+ return (
+ _cooler_capacity != null
+ and _local_inventory != null
+ and _local_inventory.get_all_catches().size()
+ >= _cooler_capacity.get_capacity()
+ )
+
+
+func _on_cooler_availability_changed() -> void:
+ if state == FishingState.READY and not _is_cooler_full():
+ refresh_active_item_status()
+
+
+func _on_cooler_capacity_changed(_level: int, _capacity: int) -> void:
+ _on_cooler_availability_changed()
+
+
func is_fighting() -> bool:
return state == FishingState.FIGHTING
@@ -472,6 +546,13 @@ func _on_cast_completed() -> void:
return
_selection_context = _build_fishing_context(_selected_water_region)
_fish_selector.undiscovered_weight_multiplier = undiscovered_weight_multiplier
+ _fish_selector.rarity_weight_multipliers = []
+ for rarity: int in range(FishDataType.Rarity.size()):
+ _fish_selector.rarity_weight_multipliers.append(
+ _item_effects.get_rarity_weight_multiplier(rarity)
+ if _item_effects != null
+ else 1.0
+ )
_fish_selector.use_deterministic_test_seed = use_deterministic_selection_seed
_fish_selector.deterministic_test_seed = deterministic_selection_seed
_fish_selector.begin_roll()
@@ -485,7 +566,11 @@ func _on_cast_completed() -> void:
return
state = FishingState.WAITING_FOR_BITE
- _state_time_remaining = wait_time
+ _state_time_remaining = wait_time * (
+ _item_effects.get_bite_time_multiplier()
+ if _item_effects != null
+ else 1.0
+ )
_withdrawal_progress = 0.0
_withdrawal_input_held = false
_bobber_water_position = _cast_target
@@ -618,16 +703,8 @@ func _activate_bite() -> void:
_presentation.begin_reeling()
_catch_controller.start_encounter(
_selected_fish.catch_profile,
- _active_player.reel_speed * (
- _fishing_upgrades.get_reel_speed_multiplier()
- if _fishing_upgrades != null
- else 1.0
- ),
- (
- _fishing_upgrades.get_barrier_damage()
- if _fishing_upgrades != null
- else _active_player.click_power
- )
+ _get_effective_reel_speed(),
+ _get_effective_barrier_damage()
)
_catch_controller.set_reel_input(
Input.is_action_pressed("fish_primary")
@@ -635,6 +712,34 @@ func _activate_bite() -> void:
_presentation.show_bite()
+func _get_effective_reel_speed() -> float:
+ if _active_player == null:
+ return 0.0
+ return _active_player.reel_speed * (
+ _fishing_upgrades.get_reel_speed_multiplier()
+ if _fishing_upgrades != null
+ else 1.0
+ ) * (
+ _item_effects.get_reel_multiplier()
+ if _item_effects != null
+ else 1.0
+ )
+
+
+func _get_effective_barrier_damage() -> int:
+ if _active_player == null:
+ return 1
+ return (
+ _fishing_upgrades.get_barrier_damage()
+ if _fishing_upgrades != null
+ else _active_player.click_power
+ ) + (
+ _item_effects.get_barrier_bonus()
+ if _item_effects != null
+ else 0
+ )
+
+
func _on_catch_encounter_updated(
progress: float,
chase_progress: float,
diff --git a/inventory/player_bag.gd b/inventory/player_bag.gd
index 0d6112a..1e885ee 100644
--- a/inventory/player_bag.gd
+++ b/inventory/player_bag.gd
@@ -16,19 +16,13 @@ func setup(catalog: ItemCatalogType) -> void:
func add_item(item_id: StringName, quantity: int = 1) -> bool:
- var item: ItemDataType = _resolve_valid_item(item_id)
- if item == null or quantity <= 0:
+ if not can_add_item(item_id, quantity):
return false
+ var item: ItemDataType = _resolve_valid_item(item_id)
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
@@ -37,6 +31,19 @@ func add_item(item_id: StringName, quantity: int = 1) -> bool:
return true
+func can_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:
+ return (
+ item.stackable
+ and existing.quantity + quantity <= item.max_stack
+ )
+ return quantity <= (item.max_stack if item.stackable else 1)
+
+
func remove_item(item_id: StringName, quantity: int = 1) -> bool:
if quantity <= 0:
return false
diff --git a/items/catalog/coffee.tres b/items/catalog/coffee.tres
new file mode 100644
index 0000000..4e7d343
--- /dev/null
+++ b/items/catalog/coffee.tres
@@ -0,0 +1,16 @@
+[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3]
+
+[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
+[ext_resource type="Texture2D" path="res://items/icons/placeholder/coffee.svg" id="2"]
+
+[resource]
+script = ExtResource("1")
+item_id = &"coffee"
+display_name = "Coffee"
+description = "Temporarily increases ground movement speed."
+category = 3
+icon = ExtResource("2")
+stackable = true
+max_stack = 99
+usable = true
+hotbar_allowed = true
diff --git a/items/catalog/cooler_expansion.tres b/items/catalog/cooler_expansion.tres
new file mode 100644
index 0000000..28114f6
--- /dev/null
+++ b/items/catalog/cooler_expansion.tres
@@ -0,0 +1,17 @@
+[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3]
+
+[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
+[ext_resource type="Texture2D" path="res://items/icons/placeholder/cooler_expansion.svg" id="2"]
+
+[resource]
+script = ExtResource("1")
+item_id = &"cooler_expansion"
+display_name = "Cooler Expansion"
+description = "Permanently increases the number of fish the Cooler can hold."
+category = 4
+icon = ExtResource("2")
+stackable = false
+max_stack = 1
+usable = false
+equippable = false
+hotbar_allowed = false
diff --git a/items/catalog/energy_drink.tres b/items/catalog/energy_drink.tres
new file mode 100644
index 0000000..6594dbe
--- /dev/null
+++ b/items/catalog/energy_drink.tres
@@ -0,0 +1,16 @@
+[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3]
+
+[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
+[ext_resource type="Texture2D" path="res://items/icons/placeholder/energy_drink.svg" id="2"]
+
+[resource]
+script = ExtResource("1")
+item_id = &"energy_drink"
+display_name = "Energy Drink"
+description = "Temporarily increases reeling progress."
+category = 3
+icon = ExtResource("2")
+stackable = true
+max_stack = 99
+usable = true
+hotbar_allowed = true
diff --git a/items/catalog/fish_finder.tres b/items/catalog/fish_finder.tres
new file mode 100644
index 0000000..310b16a
--- /dev/null
+++ b/items/catalog/fish_finder.tres
@@ -0,0 +1,16 @@
+[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3]
+
+[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
+[ext_resource type="Texture2D" path="res://items/icons/placeholder/fish_finder.svg" id="2"]
+
+[resource]
+script = ExtResource("1")
+item_id = &"fish_finder"
+display_name = "Fish Finder"
+description = "Temporarily shortens bite waits and improves rarity weights."
+category = 3
+icon = ExtResource("2")
+stackable = true
+max_stack = 99
+usable = true
+hotbar_allowed = true
diff --git a/items/catalog/item_catalog.tres b/items/catalog/item_catalog.tres
index f4e3986..3cccb42 100644
--- a/items/catalog/item_catalog.tres
+++ b/items/catalog/item_catalog.tres
@@ -1,8 +1,13 @@
-[gd_resource type="Resource" script_class="ItemCatalog" load_steps=3 format=3]
+[gd_resource type="Resource" script_class="ItemCatalog" load_steps=8 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"]
+[ext_resource type="Resource" path="res://items/catalog/coffee.tres" id="3_coffee"]
+[ext_resource type="Resource" path="res://items/catalog/energy_drink.tres" id="4_energy"]
+[ext_resource type="Resource" path="res://items/catalog/snack.tres" id="5_snack"]
+[ext_resource type="Resource" path="res://items/catalog/fish_finder.tres" id="6_finder"]
+[ext_resource type="Resource" path="res://items/catalog/cooler_expansion.tres" id="7_cooler"]
[resource]
script = ExtResource("1_script")
-items = [ExtResource("2_rod")]
+items = [ExtResource("2_rod"), ExtResource("3_coffee"), ExtResource("4_energy"), ExtResource("5_snack"), ExtResource("6_finder"), ExtResource("7_cooler")]
diff --git a/items/catalog/snack.tres b/items/catalog/snack.tres
new file mode 100644
index 0000000..f81edca
--- /dev/null
+++ b/items/catalog/snack.tres
@@ -0,0 +1,16 @@
+[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3]
+
+[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
+[ext_resource type="Texture2D" path="res://items/icons/placeholder/snack.svg" id="2"]
+
+[resource]
+script = ExtResource("1")
+item_id = &"snack"
+display_name = "Snack"
+description = "Temporarily adds one barrier damage."
+category = 3
+icon = ExtResource("2")
+stackable = true
+max_stack = 99
+usable = true
+hotbar_allowed = true
diff --git a/items/icons/placeholder/coffee.svg b/items/icons/placeholder/coffee.svg
new file mode 100644
index 0000000..d30c96a
--- /dev/null
+++ b/items/icons/placeholder/coffee.svg
@@ -0,0 +1,5 @@
+
diff --git a/items/icons/placeholder/coffee.svg.import b/items/icons/placeholder/coffee.svg.import
new file mode 100644
index 0000000..f8cbaa9
--- /dev/null
+++ b/items/icons/placeholder/coffee.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://ddx3pbgh2ele5"
+path="res://.godot/imported/coffee.svg-aecf16cc8c38e5e0f432cd5395949bcf.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/placeholder/coffee.svg"
+dest_files=["res://.godot/imported/coffee.svg-aecf16cc8c38e5e0f432cd5395949bcf.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/items/icons/placeholder/cooler_expansion.svg b/items/icons/placeholder/cooler_expansion.svg
new file mode 100644
index 0000000..105bff7
--- /dev/null
+++ b/items/icons/placeholder/cooler_expansion.svg
@@ -0,0 +1,5 @@
+
diff --git a/items/icons/placeholder/cooler_expansion.svg.import b/items/icons/placeholder/cooler_expansion.svg.import
new file mode 100644
index 0000000..fbcc85c
--- /dev/null
+++ b/items/icons/placeholder/cooler_expansion.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://d115jiqughf40"
+path="res://.godot/imported/cooler_expansion.svg-a3d63bc21ba230007f1039372b518c8f.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/placeholder/cooler_expansion.svg"
+dest_files=["res://.godot/imported/cooler_expansion.svg-a3d63bc21ba230007f1039372b518c8f.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/items/icons/placeholder/energy_drink.svg b/items/icons/placeholder/energy_drink.svg
new file mode 100644
index 0000000..da04ac7
--- /dev/null
+++ b/items/icons/placeholder/energy_drink.svg
@@ -0,0 +1,4 @@
+
diff --git a/items/icons/placeholder/energy_drink.svg.import b/items/icons/placeholder/energy_drink.svg.import
new file mode 100644
index 0000000..0e79f6a
--- /dev/null
+++ b/items/icons/placeholder/energy_drink.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://d3elrkttv1f1h"
+path="res://.godot/imported/energy_drink.svg-8d7bc90bd15610c7852e4cf522a5b1ca.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/placeholder/energy_drink.svg"
+dest_files=["res://.godot/imported/energy_drink.svg-8d7bc90bd15610c7852e4cf522a5b1ca.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/items/icons/placeholder/fish_finder.svg b/items/icons/placeholder/fish_finder.svg
new file mode 100644
index 0000000..7bbc0b1
--- /dev/null
+++ b/items/icons/placeholder/fish_finder.svg
@@ -0,0 +1,6 @@
+
diff --git a/items/icons/placeholder/fish_finder.svg.import b/items/icons/placeholder/fish_finder.svg.import
new file mode 100644
index 0000000..df63a31
--- /dev/null
+++ b/items/icons/placeholder/fish_finder.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cxsaix6vp7t6u"
+path="res://.godot/imported/fish_finder.svg-33ab7d731566e03c2f84e185d3503b7d.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/placeholder/fish_finder.svg"
+dest_files=["res://.godot/imported/fish_finder.svg-33ab7d731566e03c2f84e185d3503b7d.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/items/icons/placeholder/snack.svg b/items/icons/placeholder/snack.svg
new file mode 100644
index 0000000..edff1ec
--- /dev/null
+++ b/items/icons/placeholder/snack.svg
@@ -0,0 +1,5 @@
+
diff --git a/items/icons/placeholder/snack.svg.import b/items/icons/placeholder/snack.svg.import
new file mode 100644
index 0000000..5ad06b4
--- /dev/null
+++ b/items/icons/placeholder/snack.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://i611n7iwr8i6"
+path="res://.godot/imported/snack.svg-dfbab25d47c1c79c46c1d7fcf9b21cfe.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/placeholder/snack.svg"
+dest_files=["res://.godot/imported/snack.svg-dfbab25d47c1c79c46c1d7fcf9b21cfe.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/main/main.gd b/main/main.gd
index f781ef4..0deb188 100644
--- a/main/main.gd
+++ b/main/main.gd
@@ -62,7 +62,8 @@ func _ready() -> void:
_player.bag,
_player.hotbar,
item_catalog,
- _player.fishing_upgrades
+ _player.fishing_upgrades,
+ _player.cooler_capacity
)
_save_manager.set_autosave_enabled(false)
_fishing_spot.setup(
@@ -72,7 +73,9 @@ func _ready() -> void:
_player.bag,
_player.hotbar,
item_catalog,
- _player.fishing_upgrades
+ _player.fishing_upgrades,
+ _player.item_effects,
+ _player.cooler_capacity
)
_game_ui.setup(
_player,
@@ -88,7 +91,9 @@ func _ready() -> void:
item_catalog,
main_shop_buyer_profile,
_player.fishing_upgrades,
- _shop_interaction
+ _shop_interaction,
+ _player.item_effects,
+ _player.cooler_capacity
)
_water_recovery.setup(
_player,
@@ -210,6 +215,8 @@ func _apply_runtime_settings(settings: PlayerSettingsType) -> void:
func _set_gameplay_active(active: bool) -> void:
_gameplay_started = active
+ if not active:
+ _player.item_effects.reset_all()
_player.set_movement_enabled(active)
_player.set_camera_input_enabled(active)
_fishing_spot.set_gameplay_input_enabled(active)
diff --git a/player/player.gd b/player/player.gd
index 4022b12..847492c 100644
--- a/player/player.gd
+++ b/player/player.gd
@@ -11,6 +11,12 @@ const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
const PlayerFishingUpgradesType = preload(
"res://progression/player_fishing_upgrades.gd"
)
+const PlayerItemEffectsType = preload(
+ "res://progression/player_item_effects.gd"
+)
+const PlayerCoolerCapacityType = preload(
+ "res://progression/player_cooler_capacity.gd"
+)
class ShowcaseCameraSnapshot:
extends RefCounted
@@ -75,6 +81,8 @@ class ShowcaseCameraSnapshot:
@onready var bag: PlayerBagType = %Bag
@onready var hotbar: PlayerHotbarType = %Hotbar
@onready var fishing_upgrades: PlayerFishingUpgradesType = %FishingUpgrades
+@onready var item_effects: PlayerItemEffectsType = %ItemEffects
+@onready var cooler_capacity: PlayerCoolerCapacityType = %CoolerCapacity
@onready var _cast_origin: Marker3D = %CastOrigin
@onready var _fishing_rod: Node3D = %FishingRod
@onready var _fishing_rod_tip: Marker3D = %FishingRodTip
@@ -141,7 +149,9 @@ func _physics_process(delta: float) -> void:
var input_strength: float = minf(input_vector.length(), 1.0)
move_direction = move_direction.normalized()
- var speed: float = _get_current_speed()
+ var speed: float = (
+ _get_current_speed() * item_effects.get_movement_multiplier()
+ )
velocity.x = move_direction.x * speed * input_strength
velocity.z = move_direction.z * speed * input_strength
diff --git a/player/player.tscn b/player/player.tscn
index ead43d0..977040d 100644
--- a/player/player.tscn
+++ b/player/player.tscn
@@ -1,4 +1,4 @@
-[gd_scene load_steps=16 format=3]
+[gd_scene load_steps=18 format=3]
[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"]
@@ -8,6 +8,8 @@
[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"]
[ext_resource type="Script" path="res://progression/player_fishing_upgrades.gd" id="8_upgrades"]
+[ext_resource type="Script" path="res://progression/player_item_effects.gd" id="9_effects"]
+[ext_resource type="Script" path="res://progression/player_cooler_capacity.gd" id="10_capacity"]
[sub_resource type="CapsuleShape3D" id="PlayerShape"]
radius = 0.45
@@ -103,6 +105,14 @@ script = ExtResource("7_hotbar")
unique_name_in_owner = true
script = ExtResource("8_upgrades")
+[node name="ItemEffects" type="Node" parent="."]
+unique_name_in_owner = true
+script = ExtResource("9_effects")
+
+[node name="CoolerCapacity" type="Node" parent="."]
+unique_name_in_owner = true
+script = ExtResource("10_capacity")
+
[node name="CatchDisplayAnchor" type="Marker3D" parent="Visuals"]
unique_name_in_owner = true
position = Vector3(0, 1.45, -1.15)
diff --git a/playtest/README-PLAYTEST.txt b/playtest/README-PLAYTEST.txt
index da39761..060f1cf 100644
--- a/playtest/README-PLAYTEST.txt
+++ b/playtest/README-PLAYTEST.txt
@@ -49,6 +49,10 @@ FISHING
- Accessibility auto-click can be enabled in Settings.
- Approach the bright Fishing Shop booth and press E to open it.
- The Fishing Shop buys one Cooler fish at a time for full base value.
+- Buy Coffee, Energy Drinks, Snacks, and Fish Finders as Bag supplies.
+- Drag supplies from Bag to the hotbar and left-click in READY to use one.
+- Coffee improves movement; other supplies temporarily improve fishing.
+- The Cooler starts at 12 fish; buy permanent capacity expansions at the shop.
- Reel Speed upgrades increase authoritative green reeling progress.
- Barrier Power upgrades increase damage per valid barrier action.
- Shop upgrades persist in the local progression save.
diff --git a/progression/player_cooler_capacity.gd b/progression/player_cooler_capacity.gd
new file mode 100644
index 0000000..1e77784
--- /dev/null
+++ b/progression/player_cooler_capacity.gd
@@ -0,0 +1,65 @@
+class_name PlayerCoolerCapacity
+extends Node
+
+const PlayerWalletType = preload("res://economy/player_wallet.gd")
+
+signal capacity_changed(level: int, capacity: int)
+
+const CAPACITIES: Array[int] = [12, 18, 24, 32, 40]
+const EXPANSION_COSTS: Array[int] = [75, 175, 400, 850]
+const MAX_LEVEL: int = 4
+
+var _capacity_level: int = 0
+
+
+func get_level() -> int:
+ return _capacity_level
+
+
+func get_capacity() -> int:
+ return CAPACITIES[_capacity_level]
+
+
+func get_next_capacity() -> int:
+ if _capacity_level >= MAX_LEVEL:
+ return -1
+ return CAPACITIES[_capacity_level + 1]
+
+
+func get_next_cost() -> int:
+ if _capacity_level >= MAX_LEVEL:
+ return -1
+ return EXPANSION_COSTS[_capacity_level]
+
+
+func can_purchase(wallet: PlayerWalletType) -> bool:
+ var cost: int = get_next_cost()
+ return wallet != null and cost >= 0 and wallet.can_afford(cost)
+
+
+func purchase(wallet: PlayerWalletType) -> bool:
+ if not can_purchase(wallet):
+ return false
+ var cost: int = get_next_cost()
+ if not wallet.debit(cost):
+ return false
+ _capacity_level += 1
+ capacity_changed.emit(_capacity_level, get_capacity())
+ return true
+
+
+func restore_level(level: int) -> bool:
+ var validated: int = clampi(level, 0, MAX_LEVEL)
+ var changed: bool = _capacity_level != validated
+ _capacity_level = validated
+ if changed:
+ capacity_changed.emit(_capacity_level, get_capacity())
+ return true
+
+
+func reset_to_defaults() -> void:
+ restore_level(0)
+
+
+func to_save_data() -> Dictionary:
+ return {"capacity_level": _capacity_level}
diff --git a/progression/player_cooler_capacity.gd.uid b/progression/player_cooler_capacity.gd.uid
new file mode 100644
index 0000000..2dda316
--- /dev/null
+++ b/progression/player_cooler_capacity.gd.uid
@@ -0,0 +1 @@
+uid://c85ofvdqxyacg
diff --git a/progression/player_item_effects.gd b/progression/player_item_effects.gd
new file mode 100644
index 0000000..f0ee063
--- /dev/null
+++ b/progression/player_item_effects.gd
@@ -0,0 +1,142 @@
+class_name PlayerItemEffects
+extends Node
+
+const ItemDataType = preload("res://items/item_data.gd")
+const PlayerBagType = preload("res://inventory/player_bag.gd")
+
+signal effects_changed
+
+const COFFEE_ID: StringName = &"coffee"
+const ENERGY_DRINK_ID: StringName = &"energy_drink"
+const SNACK_ID: StringName = &"snack"
+const FISH_FINDER_ID: StringName = &"fish_finder"
+const COFFEE_DURATION: float = 90.0
+const ENERGY_DRINK_DURATION: float = 90.0
+const SNACK_DURATION: float = 60.0
+const FISH_FINDER_DURATION: float = 120.0
+const COFFEE_MOVEMENT_MULTIPLIER: float = 1.10
+const ENERGY_REEL_MULTIPLIER: float = 1.15
+const SNACK_BARRIER_BONUS: int = 1
+const FISH_FINDER_BITE_MULTIPLIER: float = 0.80
+
+var _remaining: Dictionary[StringName, float] = {
+ COFFEE_ID: 0.0,
+ ENERGY_DRINK_ID: 0.0,
+ SNACK_ID: 0.0,
+ FISH_FINDER_ID: 0.0,
+}
+
+
+func _process(delta: float) -> void:
+ var changed: bool = false
+ for item_id: StringName in _remaining:
+ var previous: float = _remaining[item_id]
+ if previous <= 0.0:
+ continue
+ var next: float = maxf(previous - delta, 0.0)
+ _remaining[item_id] = next
+ if next == 0.0:
+ changed = true
+ if changed:
+ effects_changed.emit()
+
+
+func use_consumable(
+ item: ItemDataType,
+ bag: PlayerBagType,
+) -> bool:
+ if (
+ item == null
+ or bag == null
+ or item.category != ItemDataType.Category.CONSUMABLE
+ or not item.usable
+ or not _remaining.has(item.item_id)
+ or not bag.owns_item(item.item_id)
+ ):
+ return false
+ if not bag.remove_item(item.item_id, 1):
+ return false
+ _remaining[item.item_id] = _get_duration(item.item_id)
+ effects_changed.emit()
+ return true
+
+
+func reset_all() -> void:
+ var changed: bool = false
+ for item_id: StringName in _remaining:
+ changed = changed or _remaining[item_id] > 0.0
+ _remaining[item_id] = 0.0
+ if changed:
+ effects_changed.emit()
+
+
+func get_remaining(item_id: StringName) -> float:
+ return maxf(_remaining.get(item_id, 0.0), 0.0)
+
+
+func is_active(item_id: StringName) -> bool:
+ return get_remaining(item_id) > 0.0
+
+
+func get_movement_multiplier() -> float:
+ return COFFEE_MOVEMENT_MULTIPLIER if is_active(COFFEE_ID) else 1.0
+
+
+func get_reel_multiplier() -> float:
+ return ENERGY_REEL_MULTIPLIER if is_active(ENERGY_DRINK_ID) else 1.0
+
+
+func get_barrier_bonus() -> int:
+ return SNACK_BARRIER_BONUS if is_active(SNACK_ID) else 0
+
+
+func get_bite_time_multiplier() -> float:
+ return (
+ FISH_FINDER_BITE_MULTIPLIER
+ if is_active(FISH_FINDER_ID)
+ else 1.0
+ )
+
+
+func get_rarity_weight_multiplier(rarity: int) -> float:
+ if not is_active(FISH_FINDER_ID):
+ return 1.0
+ match rarity:
+ 0:
+ return 1.0
+ 1:
+ return 1.10
+ 2:
+ return 1.15
+ 3:
+ return 1.20
+ _:
+ return 1.25
+
+
+func get_feedback(item_id: StringName) -> String:
+ match item_id:
+ COFFEE_ID:
+ return "Coffee active: movement speed increased."
+ ENERGY_DRINK_ID:
+ return "Energy Drink active: reeling speed increased."
+ SNACK_ID:
+ return "Snack active: barrier damage increased."
+ FISH_FINDER_ID:
+ return "Fish Finder active: bites and uncommon catches improved."
+ _:
+ return ""
+
+
+func _get_duration(item_id: StringName) -> float:
+ match item_id:
+ COFFEE_ID:
+ return COFFEE_DURATION
+ ENERGY_DRINK_ID:
+ return ENERGY_DRINK_DURATION
+ SNACK_ID:
+ return SNACK_DURATION
+ FISH_FINDER_ID:
+ return FISH_FINDER_DURATION
+ _:
+ return 0.0
diff --git a/progression/player_item_effects.gd.uid b/progression/player_item_effects.gd.uid
new file mode 100644
index 0000000..2866bb2
--- /dev/null
+++ b/progression/player_item_effects.gd.uid
@@ -0,0 +1 @@
+uid://cvd5jj5wbab1k
diff --git a/save/player_save_manager.gd b/save/player_save_manager.gd
index 2acab87..41a5428 100644
--- a/save/player_save_manager.gd
+++ b/save/player_save_manager.gd
@@ -14,8 +14,11 @@ const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
const PlayerFishingUpgradesType = preload(
"res://progression/player_fishing_upgrades.gd"
)
+const PlayerCoolerCapacityType = preload(
+ "res://progression/player_cooler_capacity.gd"
+)
-const SAVE_VERSION: int = 3
+const SAVE_VERSION: int = 4
const BASIC_ROD_ID: StringName = &"basic_fishing_rod"
const SAVE_PATH: String = "user://player_save.json"
const TEMP_PATH: String = "user://player_save.json.tmp"
@@ -34,6 +37,7 @@ class LoadSnapshot:
var selected_hotbar_slot: int = 0
var reel_speed_level: int = 0
var barrier_power_level: int = 0
+ var cooler_capacity_level: int = 0
@export_range(0.05, 5.0, 0.05) var autosave_delay: float = 0.5
@@ -46,6 +50,7 @@ var _bag: PlayerBagType
var _hotbar: PlayerHotbarType
var _item_catalog: ItemCatalogType
var _fishing_upgrades: PlayerFishingUpgradesType
+var _cooler_capacity: PlayerCoolerCapacityType
var _autosave_timer: Timer
var _is_configured: bool = false
var _is_restoring: bool = false
@@ -70,6 +75,7 @@ func setup(
hotbar: PlayerHotbarType,
item_catalog: ItemCatalogType,
fishing_upgrades: PlayerFishingUpgradesType,
+ cooler_capacity: PlayerCoolerCapacityType,
) -> void:
_inventory = inventory
_collection_log = collection_log
@@ -79,6 +85,7 @@ func setup(
_hotbar = hotbar
_item_catalog = item_catalog
_fishing_upgrades = fishing_upgrades
+ _cooler_capacity = cooler_capacity
_is_configured = (
_inventory != null
and _collection_log != null
@@ -88,6 +95,7 @@ func setup(
and _hotbar != null
and _item_catalog != null
and _fishing_upgrades != null
+ and _cooler_capacity != null
)
if not _is_configured:
push_error("PlayerSaveManager setup is missing required references.")
@@ -114,6 +122,12 @@ func setup(
_on_upgrades_changed
):
_fishing_upgrades.upgrades_changed.connect(_on_upgrades_changed)
+ if not _cooler_capacity.capacity_changed.is_connected(
+ _on_cooler_capacity_changed
+ ):
+ _cooler_capacity.capacity_changed.connect(
+ _on_cooler_capacity_changed
+ )
func load_player_data() -> bool:
@@ -183,6 +197,9 @@ func load_player_data() -> bool:
snapshot.reel_speed_level,
snapshot.barrier_power_level
)
+ var cooler_restored: bool = _cooler_capacity.restore_level(
+ snapshot.cooler_capacity_level
+ )
_is_restoring = false
if (
not inventory_restored
@@ -191,6 +208,7 @@ func load_player_data() -> bool:
or not bag_restored
or not hotbar_restored
or not upgrades_restored
+ or not cooler_restored
):
push_error("Validated player save could not be restored.")
return false
@@ -400,6 +418,7 @@ func _build_save_dictionary() -> Dictionary:
"slots": serialized_slots,
},
"upgrades": _fishing_upgrades.to_save_data(),
+ "cooler": _cooler_capacity.to_save_data(),
}
@@ -418,12 +437,17 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot:
var bag_data: Dictionary = save_data["bag"]
var hotbar_data: Dictionary = save_data["hotbar"]
var upgrades_data: Dictionary = {}
+ var cooler_data: Dictionary = {}
if typeof(save_data.get("upgrades")) == TYPE_DICTIONARY:
upgrades_data = save_data["upgrades"]
else:
push_warning(
"Saved fishing upgrades were missing or invalid; using defaults."
)
+ if typeof(save_data.get("cooler")) == TYPE_DICTIONARY:
+ cooler_data = save_data["cooler"]
+ else:
+ push_warning("Saved Cooler data was missing or invalid; using defaults.")
if (
not wallet_data.has("balance")
or typeof(collection_data.get("discovered_fish_ids")) != TYPE_ARRAY
@@ -600,6 +624,11 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot:
PlayerFishingUpgradesType.MAX_BARRIER_POWER_LEVEL,
"barrier_power_level"
)
+ snapshot.cooler_capacity_level = _read_upgrade_level(
+ cooler_data.get("capacity_level"),
+ PlayerCoolerCapacityType.MAX_LEVEL,
+ "cooler.capacity_level"
+ )
return snapshot
@@ -619,6 +648,8 @@ func _migrate_save(
migrated = _migrate_version_1_to_2(migrated)
2:
migrated = _migrate_version_2_to_3(migrated)
+ 3:
+ migrated = _migrate_version_3_to_4(migrated)
_:
return {}
if migrated.is_empty():
@@ -659,6 +690,13 @@ func _migrate_version_2_to_3(data: Dictionary) -> Dictionary:
return migrated
+func _migrate_version_3_to_4(data: Dictionary) -> Dictionary:
+ var migrated: Dictionary = data.duplicate(true)
+ migrated["save_version"] = 4
+ migrated["cooler"] = {"capacity_level": 0}
+ return migrated
+
+
func _mark_dirty() -> void:
if (
_is_restoring
@@ -693,6 +731,13 @@ func _on_upgrades_changed(
_mark_dirty()
+func _on_cooler_capacity_changed(
+ _level: int,
+ _capacity: int,
+) -> void:
+ _mark_dirty()
+
+
func _on_autosave_timeout() -> void:
if _is_dirty:
save_now()
@@ -748,6 +793,7 @@ func _restore_defaults() -> void:
_bag.replace_all_items(default_items)
_hotbar.replace_state(default_slots, 0)
_fishing_upgrades.reset_to_defaults()
+ _cooler_capacity.reset_to_defaults()
_is_restoring = false
_is_dirty = false
diff --git a/ui/fishing_shop.gd b/ui/fishing_shop.gd
index 6220950..16774a3 100644
--- a/ui/fishing_shop.gd
+++ b/ui/fishing_shop.gd
@@ -9,11 +9,18 @@ const FishInventoryType = preload("res://inventory/fish_inventory.gd")
const FishSaleResultType = preload("res://economy/fish_sale_result.gd")
const FishSaleServiceType = preload("res://economy/fish_sale_service.gd")
const PlayerWalletType = preload("res://economy/player_wallet.gd")
+const FishingShopStockType = preload("res://economy/fishing_shop_stock.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 FishingSpotType = preload("res://fishing/fishing_spot.gd")
const PlayerFishingUpgradesType = preload(
"res://progression/player_fishing_upgrades.gd"
)
const PlayerType = preload("res://player/player.gd")
+const PlayerCoolerCapacityType = preload(
+ "res://progression/player_cooler_capacity.gd"
+)
const ShopInteractionType = preload(
"res://world/fishing_shop_interaction.gd"
)
@@ -30,6 +37,7 @@ enum CloseReason {
@onready var _wallet_label: Label = %WalletLabel
@onready var _fish_list: ItemList = %FishList
+@onready var _sales_title: Label = %SalesTitle
@onready var _fish_empty: Label = %FishEmpty
@onready var _fish_texture: TextureRect = %FishTexture
@onready var _fish_name: Label = %FishName
@@ -44,6 +52,11 @@ enum CloseReason {
@onready var _barrier_effect: Label = %BarrierEffect
@onready var _barrier_cost: Label = %BarrierCost
@onready var _barrier_purchase: Button = %BarrierPurchase
+@onready var _supplies_list: VBoxContainer = %SuppliesList
+@onready var _cooler_level: Label = %CoolerLevel
+@onready var _cooler_effect: Label = %CoolerEffect
+@onready var _cooler_cost: Label = %CoolerCost
+@onready var _cooler_purchase: Button = %CoolerPurchase
@onready var _confirmation: PanelContainer = %SaleConfirmation
@onready var _confirmation_text: Label = %ConfirmationText
@onready var _confirm_sale: Button = %ConfirmSale
@@ -57,6 +70,9 @@ var _buyer: FishBuyerProfileType
var _upgrades: PlayerFishingUpgradesType
var _fishing_spot: FishingSpotType
var _interaction: ShopInteractionType
+var _bag: PlayerBagType
+var _item_catalog: ItemCatalogType
+var _cooler_capacity: PlayerCoolerCapacityType
var _selected_catch_id: StringName
var _confirmation_catch_id: StringName
var _prior_movement_enabled: bool = true
@@ -77,6 +93,7 @@ func _ready() -> void:
_cancel_sale.pressed.connect(_close_sale_confirmation)
_reel_purchase.pressed.connect(_purchase_reel_speed)
_barrier_purchase.pressed.connect(_purchase_barrier_power)
+ _cooler_purchase.pressed.connect(_purchase_cooler_capacity)
func setup(
@@ -88,6 +105,9 @@ func setup(
upgrades: PlayerFishingUpgradesType,
fishing_spot: FishingSpotType,
interaction: ShopInteractionType,
+ bag: PlayerBagType,
+ item_catalog: ItemCatalogType,
+ cooler_capacity: PlayerCoolerCapacityType,
) -> void:
_player = player
_inventory = inventory
@@ -97,12 +117,21 @@ func setup(
_upgrades = upgrades
_fishing_spot = fishing_spot
_interaction = interaction
+ _bag = bag
+ _item_catalog = item_catalog
+ _cooler_capacity = cooler_capacity
if not _inventory.catches_changed.is_connected(_on_inventory_changed):
_inventory.catches_changed.connect(_on_inventory_changed)
if not _wallet.balance_changed.is_connected(_on_wallet_changed):
_wallet.balance_changed.connect(_on_wallet_changed)
if not _upgrades.upgrades_changed.is_connected(_on_upgrades_changed):
_upgrades.upgrades_changed.connect(_on_upgrades_changed)
+ if not _bag.contents_changed.is_connected(_on_bag_changed):
+ _bag.contents_changed.connect(_on_bag_changed)
+ if not _cooler_capacity.capacity_changed.is_connected(
+ _on_cooler_capacity_changed
+ ):
+ _cooler_capacity.capacity_changed.connect(_on_cooler_capacity_changed)
_refresh_all()
@@ -207,6 +236,8 @@ func _refresh_all() -> void:
_refresh_fish_list()
_refresh_selected_fish()
_refresh_upgrades()
+ _refresh_supplies()
+ _refresh_cooler_capacity()
func _refresh_wallet() -> void:
@@ -222,6 +253,10 @@ func _refresh_fish_list() -> void:
var catches: Array[FishCatchType] = (
_inventory.get_all_catches() if _inventory != null else []
)
+ _sales_title.text = "Cooler %d / %d • Full base value" % [
+ catches.size(),
+ _cooler_capacity.get_capacity() if _cooler_capacity != null else 0,
+ ]
catches.sort_custom(
func(a: FishCatchType, b: FishCatchType) -> bool:
return a.catch_sequence > b.catch_sequence
@@ -335,6 +370,65 @@ func _refresh_upgrades() -> void:
_barrier_purchase.text = "MAX" if barrier_cost < 0 else "Purchase"
+func _refresh_supplies() -> void:
+ if _supplies_list == null:
+ return
+ for child: Node in _supplies_list.get_children():
+ _supplies_list.remove_child(child)
+ child.queue_free()
+ for item_id: StringName in FishingShopStockType.get_stock_item_ids():
+ var item: ItemDataType = _item_catalog.get_item_by_id(item_id)
+ if item == null:
+ continue
+ var button := Button.new()
+ button.custom_minimum_size = Vector2(195, 54)
+ button.icon = item.icon
+ button.expand_icon = true
+ button.alignment = HORIZONTAL_ALIGNMENT_LEFT
+ button.text = "%s\n$%d • Owned %d" % [
+ item.display_name,
+ FishingShopStockType.get_price(item_id),
+ _bag.get_quantity(item_id),
+ ]
+ button.tooltip_text = item.description
+ button.disabled = (
+ _transaction_in_progress
+ or _closing
+ or not _bag.can_add_item(item_id, 1)
+ or not _wallet.can_afford(
+ FishingShopStockType.get_price(item_id)
+ )
+ )
+ button.pressed.connect(_purchase_supply.bind(item_id))
+ _supplies_list.add_child(button)
+
+
+func _refresh_cooler_capacity() -> void:
+ if _cooler_capacity == null:
+ return
+ var level: int = _cooler_capacity.get_level()
+ var cost: int = _cooler_capacity.get_next_cost()
+ var next_capacity: int = _cooler_capacity.get_next_capacity()
+ _cooler_level.text = "Level %d" % level
+ _cooler_purchase.disabled = (
+ cost < 0
+ or _transaction_in_progress
+ or _closing
+ or not _cooler_capacity.can_purchase(_wallet)
+ )
+ if cost < 0:
+ _cooler_effect.text = "%d fish" % _cooler_capacity.get_capacity()
+ _cooler_cost.text = "MAX"
+ _cooler_purchase.text = "MAX"
+ else:
+ _cooler_effect.text = "%d → %d fish" % [
+ _cooler_capacity.get_capacity(),
+ next_capacity,
+ ]
+ _cooler_cost.text = "$%d" % cost
+ _cooler_purchase.text = "Purchase"
+
+
func _on_fish_selected(index: int) -> void:
if index < 0 or index >= _fish_list.item_count:
return
@@ -405,6 +499,59 @@ func _purchase_barrier_power() -> void:
_purchase_upgrade(false)
+func _purchase_supply(item_id: StringName) -> void:
+ if _transaction_in_progress or not _is_transaction_context_valid():
+ _feedback.text = "Unable to complete purchase."
+ return
+ var price: int = FishingShopStockType.get_price(item_id)
+ if not _bag.can_add_item(item_id, 1):
+ _feedback.text = "Bag cannot accept this item."
+ return
+ if not _wallet.can_afford(price):
+ _feedback.text = "Not enough money."
+ return
+ var transaction_generation: int = _generation
+ _transaction_in_progress = true
+ var purchased: bool = FishingShopStockType.purchase_one(
+ item_id,
+ _wallet,
+ _bag,
+ _item_catalog
+ )
+ _transaction_in_progress = false
+ if transaction_generation != _generation or not visible:
+ return
+ _feedback.text = (
+ "Item purchased." if purchased else "Unable to complete purchase."
+ )
+ _refresh_all()
+
+
+func _purchase_cooler_capacity() -> void:
+ if _transaction_in_progress or not _is_transaction_context_valid():
+ _feedback.text = "Unable to complete purchase."
+ return
+ var cost: int = _cooler_capacity.get_next_cost()
+ if cost < 0:
+ _feedback.text = "Maximum level reached."
+ return
+ if not _wallet.can_afford(cost):
+ _feedback.text = "Not enough money."
+ return
+ var transaction_generation: int = _generation
+ _transaction_in_progress = true
+ var purchased: bool = _cooler_capacity.purchase(_wallet)
+ _transaction_in_progress = false
+ if transaction_generation != _generation or not visible:
+ return
+ _feedback.text = (
+ "Upgrade purchased."
+ if purchased
+ else "Unable to complete purchase."
+ )
+ _refresh_all()
+
+
func _purchase_upgrade(is_reel_speed: bool) -> void:
if _transaction_in_progress or not _is_transaction_context_valid():
_feedback.text = "Unable to complete purchase."
@@ -472,6 +619,8 @@ func _on_inventory_changed() -> void:
func _on_wallet_changed(_balance: int, _delta: int) -> void:
_refresh_wallet()
_refresh_upgrades()
+ _refresh_supplies()
+ _refresh_cooler_capacity()
func _on_upgrades_changed(
@@ -481,6 +630,15 @@ func _on_upgrades_changed(
_refresh_upgrades()
+func _on_bag_changed() -> void:
+ _refresh_supplies()
+
+
+func _on_cooler_capacity_changed(_level: int, _capacity: int) -> void:
+ _refresh_cooler_capacity()
+ _refresh_fish_list()
+
+
func _apply_mouse_close_policy(reason: CloseReason) -> void:
if not _mouse_snapshot_stored:
return
diff --git a/ui/fishing_shop.tscn b/ui/fishing_shop.tscn
index 8561d75..4ecc8b5 100644
--- a/ui/fishing_shop.tscn
+++ b/ui/fishing_shop.tscn
@@ -1,10 +1,11 @@
-[gd_scene load_steps=6 format=3]
+[gd_scene load_steps=7 format=3]
[ext_resource type="Script" path="res://ui/fishing_shop.gd" id="1_script"]
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
[ext_resource type="Texture2D" path="res://items/icons/placeholder/reel_speed_upgrade.svg" id="3_reel"]
[ext_resource type="Texture2D" path="res://items/icons/placeholder/barrier_power_upgrade.svg" id="4_barrier"]
[ext_resource type="Texture2D" path="res://items/icons/placeholder/fish_coin.svg" id="5_coin"]
+[ext_resource type="Texture2D" path="res://items/icons/placeholder/cooler_expansion.svg" id="6_cooler"]
[node name="FishingShop" type="Control"]
unique_name_in_owner = true
@@ -37,10 +38,10 @@ anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
-offset_left = -450.0
-offset_top = -260.0
-offset_right = 450.0
-offset_bottom = 260.0
+offset_left = -480.0
+offset_top = -310.0
+offset_right = 480.0
+offset_bottom = 310.0
grow_horizontal = 2
grow_vertical = 2
@@ -98,12 +99,13 @@ size_flags_vertical = 3
theme_override_constants/separation = 12
[node name="FishSales" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body"]
-custom_minimum_size = Vector2(500, 0)
+custom_minimum_size = Vector2(420, 0)
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 6
[node name="SalesTitle" type="Label" parent="ShopPanel/Margin/Layout/Body/FishSales"]
+unique_name_in_owner = true
layout_mode = 2
text = "Sell individual Cooler fish • Full base value"
theme_override_font_sizes/font_size = 17
@@ -111,11 +113,11 @@ theme_override_font_sizes/font_size = 17
[node name="SalesBody" type="HSplitContainer" parent="ShopPanel/Margin/Layout/Body/FishSales"]
layout_mode = 2
size_flags_vertical = 3
-split_offset = 265
+split_offset = 220
theme_override_constants/separation = 8
[node name="ListPanel" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody"]
-custom_minimum_size = Vector2(250, 0)
+custom_minimum_size = Vector2(205, 0)
layout_mode = 2
[node name="ListMargin" type="MarginContainer" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/ListPanel"]
@@ -143,7 +145,7 @@ icon_mode = 1
same_column_width = true
[node name="FishDetail" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody"]
-custom_minimum_size = Vector2(220, 0)
+custom_minimum_size = Vector2(185, 0)
layout_mode = 2
[node name="DetailMargin" type="MarginContainer" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail"]
@@ -159,7 +161,7 @@ theme_override_constants/separation = 6
[node name="FishTexture" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail/DetailMargin/DetailStack"]
unique_name_in_owner = true
-custom_minimum_size = Vector2(190, 100)
+custom_minimum_size = Vector2(160, 90)
layout_mode = 2
expand_mode = 1
stretch_mode = 5
@@ -186,7 +188,7 @@ disabled = true
text = "Sell Selected"
[node name="Upgrades" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body"]
-custom_minimum_size = Vector2(310, 0)
+custom_minimum_size = Vector2(270, 0)
layout_mode = 2
theme_override_constants/separation = 8
@@ -195,6 +197,27 @@ layout_mode = 2
text = "Fishing Upgrades"
theme_override_font_sizes/font_size = 17
+[node name="Supplies" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body"]
+custom_minimum_size = Vector2(210, 0)
+layout_mode = 2
+theme_override_constants/separation = 6
+
+[node name="Title" type="Label" parent="ShopPanel/Margin/Layout/Body/Supplies"]
+layout_mode = 2
+text = "Supplies"
+theme_override_font_sizes/font_size = 17
+
+[node name="Scroll" type="ScrollContainer" parent="ShopPanel/Margin/Layout/Body/Supplies"]
+layout_mode = 2
+size_flags_vertical = 3
+horizontal_scroll_mode = 0
+
+[node name="SuppliesList" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body/Supplies/Scroll"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 3
+theme_override_constants/separation = 5
+
[node name="ReelCard" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades"]
layout_mode = 2
@@ -210,7 +233,7 @@ layout_mode = 2
theme_override_constants/separation = 9
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row"]
-custom_minimum_size = Vector2(60, 60)
+custom_minimum_size = Vector2(48, 48)
layout_mode = 2
texture = ExtResource("3_reel")
expand_mode = 1
@@ -261,7 +284,7 @@ layout_mode = 2
theme_override_constants/separation = 9
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row"]
-custom_minimum_size = Vector2(60, 60)
+custom_minimum_size = Vector2(48, 48)
layout_mode = 2
texture = ExtResource("4_barrier")
expand_mode = 1
@@ -297,6 +320,57 @@ unique_name_in_owner = true
layout_mode = 2
text = "Purchase"
+[node name="CoolerCard" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades"]
+layout_mode = 2
+
+[node name="Margin" type="MarginContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard"]
+layout_mode = 2
+theme_override_constants/margin_left = 9
+theme_override_constants/margin_top = 9
+theme_override_constants/margin_right = 9
+theme_override_constants/margin_bottom = 9
+
+[node name="Row" type="HBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin"]
+layout_mode = 2
+theme_override_constants/separation = 9
+
+[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"]
+custom_minimum_size = Vector2(48, 48)
+layout_mode = 2
+texture = ExtResource("6_cooler")
+expand_mode = 1
+stretch_mode = 5
+texture_filter = 1
+
+[node name="Data" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"]
+layout_mode = 2
+size_flags_horizontal = 3
+
+[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
+layout_mode = 2
+text = "Cooler Capacity"
+theme_override_font_sizes/font_size = 17
+
+[node name="CoolerLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
+unique_name_in_owner = true
+layout_mode = 2
+text = "Level 0"
+
+[node name="CoolerEffect" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
+unique_name_in_owner = true
+layout_mode = 2
+text = "12 → 18 fish"
+
+[node name="CoolerCost" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
+unique_name_in_owner = true
+layout_mode = 2
+text = "$75"
+
+[node name="CoolerPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"]
+unique_name_in_owner = true
+layout_mode = 2
+text = "Purchase"
+
[node name="SaleConfirmation" type="PanelContainer" parent="."]
unique_name_in_owner = true
visible = false
diff --git a/ui/game_ui.gd b/ui/game_ui.gd
index bb5e715..883d746 100644
--- a/ui/game_ui.gd
+++ b/ui/game_ui.gd
@@ -23,6 +23,12 @@ const PlayerFishingUpgradesType = preload(
const ShopInteractionType = preload(
"res://world/fishing_shop_interaction.gd"
)
+const PlayerItemEffectsType = preload(
+ "res://progression/player_item_effects.gd"
+)
+const PlayerCoolerCapacityType = preload(
+ "res://progression/player_cooler_capacity.gd"
+)
@onready var _status_label: Label = %StatusLabel
@onready var _catch_track: Control = %CatchTrack
@@ -40,6 +46,7 @@ const ShopInteractionType = preload(
@onready var _hotbar_ui: HotbarUIType = %Hotbar
@onready var _fishing_shop: FishingShopType = %FishingShop
@onready var _shop_prompt: PanelContainer = %ShopPrompt
+@onready var _effect_status: Label = %EffectStatus
var _showcase_active: bool = false
var _player_menu_open: bool = false
@@ -47,6 +54,7 @@ var _gameplay_ui_enabled: bool = false
var _fishing_spot: FishingSpotType
var _system_menu_open: bool = false
var _shop_open: bool = false
+var _item_effects: PlayerItemEffectsType
func setup(
@@ -64,8 +72,11 @@ func setup(
main_shop_buyer: FishBuyerProfileType,
fishing_upgrades: PlayerFishingUpgradesType,
shop_interaction: ShopInteractionType,
+ item_effects: PlayerItemEffectsType,
+ cooler_capacity: PlayerCoolerCapacityType,
) -> void:
_fishing_spot = fishing_spot
+ _item_effects = item_effects
fishing_spot.status_changed.connect(_on_fishing_status_changed)
fishing_spot.catch_display_changed.connect(_on_catch_display_changed)
fishing_spot.showcase_changed.connect(_on_showcase_changed)
@@ -83,7 +94,8 @@ func setup(
fishing_spot,
bag,
hotbar,
- item_catalog
+ item_catalog,
+ cooler_capacity
)
_hotbar_ui.setup(hotbar, bag, item_catalog, fishing_spot)
_fishing_shop.setup(
@@ -94,11 +106,42 @@ func setup(
main_shop_buyer,
fishing_upgrades,
fishing_spot,
- shop_interaction
+ shop_interaction,
+ bag,
+ item_catalog,
+ cooler_capacity
)
_fishing_shop.menu_visibility_changed.connect(_on_shop_visibility_changed)
+func _process(_delta: float) -> void:
+ if _item_effects == null or not _gameplay_ui_enabled:
+ _effect_status.hide()
+ return
+ var parts: Array[String] = []
+ for item_id: StringName in [
+ PlayerItemEffectsType.COFFEE_ID,
+ PlayerItemEffectsType.ENERGY_DRINK_ID,
+ PlayerItemEffectsType.SNACK_ID,
+ PlayerItemEffectsType.FISH_FINDER_ID,
+ ]:
+ var remaining: float = _item_effects.get_remaining(item_id)
+ if remaining <= 0.0:
+ continue
+ var label: String = {
+ PlayerItemEffectsType.COFFEE_ID: "Coffee",
+ PlayerItemEffectsType.ENERGY_DRINK_ID: "Energy",
+ PlayerItemEffectsType.SNACK_ID: "Snack",
+ PlayerItemEffectsType.FISH_FINDER_ID: "Finder",
+ }.get(item_id, "")
+ parts.append(
+ "%s %d:%02d"
+ % [label, int(remaining) / 60, int(remaining) % 60]
+ )
+ _effect_status.text = " ".join(parts)
+ _effect_status.visible = not parts.is_empty()
+
+
func close_player_menu() -> void:
_player_menu.close_menu()
diff --git a/ui/game_ui.tscn b/ui/game_ui.tscn
index 3c4eabe..16171df 100644
--- a/ui/game_ui.tscn
+++ b/ui/game_ui.tscn
@@ -167,6 +167,20 @@ horizontal_alignment = 1
[node name="FishingShop" parent="." instance=ExtResource("8_shop")]
unique_name_in_owner = true
+[node name="EffectStatus" type="Label" parent="."]
+unique_name_in_owner = true
+visible = false
+z_index = 54
+offset_left = 18.0
+offset_top = 18.0
+offset_right = 500.0
+offset_bottom = 44.0
+mouse_filter = 2
+theme = ExtResource("3_theme")
+theme_override_colors/font_shadow_color = Color(0, 0, 0, 0.9)
+theme_override_constants/shadow_offset_x = 2
+theme_override_constants/shadow_offset_y = 2
+
[node name="ScreenFade" type="ColorRect" parent="."]
unique_name_in_owner = true
visible = false
diff --git a/ui/player_menu.gd b/ui/player_menu.gd
index c5fb8a5..ceaee4f 100644
--- a/ui/player_menu.gd
+++ b/ui/player_menu.gd
@@ -19,6 +19,9 @@ 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")
+const PlayerCoolerCapacityType = preload(
+ "res://progression/player_cooler_capacity.gd"
+)
signal menu_visibility_changed(is_open: bool)
@@ -59,6 +62,7 @@ enum CloseReason {
@onready var _sort_option: OptionButton = %SortOption
@onready var _sort_direction: Button = %SortDirection
@onready var _held_value: Label = %HeldValue
+@onready var _cooler_count: Label = %CoolerCount
@onready var _inventory_empty: Label = %InventoryEmpty
@onready var _inventory_grid: GridContainer = %InventoryGrid
@onready var _detail_texture: TextureRect = %DetailTexture
@@ -86,6 +90,7 @@ var _fishing_spot: FishingSpotType
var _bag: PlayerBagType
var _hotbar: PlayerHotbarType
var _item_catalog: ItemCatalogType
+var _cooler_capacity: PlayerCoolerCapacityType
var _current_section: Section = Section.COOLER
var _sort_mode: SortMode = SortMode.CATCH_ORDER
var _sort_descending: bool = true
@@ -138,6 +143,7 @@ func setup(
bag: PlayerBagType,
hotbar: PlayerHotbarType,
item_catalog: ItemCatalogType,
+ cooler_capacity: PlayerCoolerCapacityType,
) -> void:
_player = player
_inventory = inventory
@@ -150,6 +156,7 @@ func setup(
_bag = bag
_hotbar = hotbar
_item_catalog = item_catalog
+ _cooler_capacity = cooler_capacity
if not _inventory.catches_changed.is_connected(_on_inventory_changed):
_inventory.catches_changed.connect(_on_inventory_changed)
if not _collection_log.fish_discovered.is_connected(_on_fish_discovered):
@@ -160,6 +167,10 @@ func setup(
_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)
+ if not _cooler_capacity.capacity_changed.is_connected(
+ _on_cooler_capacity_changed
+ ):
+ _cooler_capacity.capacity_changed.connect(_on_cooler_capacity_changed)
_refresh_all()
@@ -461,6 +472,14 @@ func _refresh_economy_summary() -> void:
)
_wallet_balance.text = "Wallet: $%d" % balance
_held_value.text = "Held fish base value: $%d" % held_total
+ _cooler_count.text = "%d / %d" % [
+ _inventory.get_all_catches().size() if _inventory != null else 0,
+ _cooler_capacity.get_capacity() if _cooler_capacity != null else 0,
+ ]
+
+
+func _on_cooler_capacity_changed(_level: int, _capacity: int) -> void:
+ _refresh_economy_summary()
func _refresh_inventory() -> void:
diff --git a/ui/player_menu.tscn b/ui/player_menu.tscn
index dbb1ea3..1fe08dc 100644
--- a/ui/player_menu.tscn
+++ b/ui/player_menu.tscn
@@ -140,6 +140,12 @@ text = "Newest first"
layout_mode = 2
size_flags_horizontal = 3
+[node name="CoolerCount" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
+unique_name_in_owner = true
+layout_mode = 2
+text = "0 / 12"
+theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
+
[node name="HeldValue" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
unique_name_in_owner = true
layout_mode = 2