Add expanded fishing gear and presentation

This commit is contained in:
Alexander Sellite 2026-08-08 22:39:50 -04:00
parent 551db3ccc6
commit 72d3a303f5
29 changed files with 783 additions and 86 deletions

View file

@ -34,6 +34,10 @@ const CLOCK_SIZE := Vector2(116.0, 34.0)
const CLOCK_EDGE_MARGIN: float = 10.0
const WEATHER_ICON_SIZE := Vector2(34.0, 34.0)
const WEATHER_ICON_GAP: float = 6.0
const FISH_FINDER_EFFECT_ICON_SIZE := Vector2(18.0, 18.0)
const FISH_FINDER_EFFECT_ICON: Texture2D = preload(
"res://items/icons/consumables/64_consumable_fishfinder.png"
)
const CHAT_SHOW_ICON: Texture2D = preload(
"res://ui/icons/pictograms/arrow_light_right_more.png"
)
@ -86,6 +90,7 @@ var _animalese_voice: AnimaleseVoiceType
var _clock_panel: PanelContainer
var _clock_label: Label
var _weather_icon: WeatherIconType
var _fish_finder_effect_icon: TextureRect
var _speech: Dictionary[int, Dictionary] = {}
var _draft_save_timer: Timer
var _opacity_tween: Tween
@ -110,6 +115,7 @@ var _dock_right: bool = false
var _mobile_mode: bool = false
var _world_time: WorldTimeServiceType
var _world_weather: WorldWeatherServiceType
var _item_effects: PlayerItemEffects
func _ready() -> void:
@ -129,6 +135,7 @@ func setup(
settings: PlayerSettingsManager,
world_time: WorldTimeServiceType,
world_weather: WorldWeatherServiceType,
item_effects: PlayerItemEffects,
) -> void:
_service = service
_session = session
@ -138,6 +145,14 @@ func setup(
_settings = settings
_world_time = world_time
_world_weather = world_weather
_item_effects = item_effects
if (
_fishing_spot != null
and not _fishing_spot.local_speech_requested.is_connected(
show_local_speech
)
):
_fishing_spot.local_speech_requested.connect(show_local_speech)
if (
_world_time != null
and not _world_time.time_changed.is_connected(
@ -361,6 +376,15 @@ func _build_ui() -> void:
"font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY
)
_clock_panel.add_child(_clock_label)
_fish_finder_effect_icon = TextureRect.new()
_fish_finder_effect_icon.name = "FishFinderEffectIcon"
_fish_finder_effect_icon.texture = FISH_FINDER_EFFECT_ICON
_fish_finder_effect_icon.size = FISH_FINDER_EFFECT_ICON_SIZE
_fish_finder_effect_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
_fish_finder_effect_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
_fish_finder_effect_icon.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
_fish_finder_effect_icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(_fish_finder_effect_icon)
_weather_icon = WeatherIconType.new()
_weather_icon.name = "WorldWeatherIcon"
_weather_icon.size = WEATHER_ICON_SIZE
@ -800,6 +824,24 @@ func _on_message(message: Dictionary) -> void:
if int(message["kind"]) != NetworkChatProtocol.Kind.PLAYER:
return
var peer_id: int = message["sender_peer_id"]
_show_speech_bubble(
peer_id,
str(message["body"]),
str(message.get("sender_fingerprint", "")),
)
func show_local_speech(body: String) -> void:
if body.strip_edges().is_empty() or _session == null:
return
_show_speech_bubble(_session.get_local_peer_id(), body, "")
func _show_speech_bubble(
peer_id: int,
body: String,
fingerprint: String,
) -> void:
_on_peer_removed(peer_id)
var bubble := PanelContainer.new()
bubble.mouse_filter = Control.MOUSE_FILTER_IGNORE
@ -808,7 +850,7 @@ func _on_message(message: Dictionary) -> void:
var pointer := _create_speech_pointer()
bubble.add_child(pointer)
var label := Label.new()
label.text = str(message["body"])
label.text = body
label.custom_minimum_size = Vector2(
SPEECH_BUBBLE_WIDTH - 20.0,
0.0,
@ -844,7 +886,7 @@ func _on_message(message: Dictionary) -> void:
+ reveal_seconds
+ SPEECH_SECONDS
),
"fingerprint": str(message.get("sender_fingerprint", "")),
"fingerprint": fingerprint,
}
@ -891,6 +933,7 @@ func _refresh_visibility() -> void:
_panel.hide()
_clock_panel.hide()
_weather_icon.hide()
_fish_finder_effect_icon.hide()
_collapse_button.hide()
_height_button.hide()
_unread_indicator.hide()
@ -898,6 +941,10 @@ func _refresh_visibility() -> void:
return
_clock_panel.show()
_weather_icon.show()
_fish_finder_effect_icon.visible = (
_item_effects != null
and _item_effects.is_active(PlayerItemEffects.FISH_FINDER_ID)
)
_collapse_button.show()
var collapsed := _presentation_state == PresentationState.COLLAPSED
_panel.show()
@ -1232,6 +1279,11 @@ func _layout_presentation(animate: bool) -> void:
clock_x,
CLOCK_EDGE_MARGIN,
)
var fish_finder_effect_position := Vector2(
clock_position.x
+ (CLOCK_SIZE.x - FISH_FINDER_EFFECT_ICON_SIZE.x) * 0.5,
clock_position.y + CLOCK_SIZE.y + 2.0,
)
var weather_icon_x: float = (
clock_position.x - WEATHER_ICON_GAP - WEATHER_ICON_SIZE.x
if _dock_right
@ -1255,6 +1307,8 @@ func _layout_presentation(animate: bool) -> void:
_panel.size = target_size
_clock_panel.position = clock_position
_clock_panel.size = CLOCK_SIZE
_fish_finder_effect_icon.position = fish_finder_effect_position
_fish_finder_effect_icon.size = FISH_FINDER_EFFECT_ICON_SIZE
_weather_icon.position = weather_icon_position
_weather_icon.size = WEATHER_ICON_SIZE
_collapse_button.position = collapse_position
@ -1275,6 +1329,12 @@ func _layout_presentation(animate: bool) -> void:
clock_position,
UIMotion.CHAT_RESIZE_DURATION,
).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
_height_tween.tween_property(
_fish_finder_effect_icon,
"position",
fish_finder_effect_position,
UIMotion.CHAT_RESIZE_DURATION,
).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
_height_tween.tween_property(
_weather_icon,
"position",

View file

@ -25,6 +25,7 @@ const ShopInteractionType = preload(
"res://world/fishing_shop_interaction.gd"
)
const UtilityPageStyleType = preload("res://ui/utility_page_style.gd")
const OrganizerTabType = preload("res://ui/components/organizer_tab.gd")
signal menu_visibility_changed(is_open: bool)
signal sell_fish_requested
@ -39,6 +40,22 @@ enum CloseReason {
TEARDOWN,
}
enum ShopSection {
UPGRADES,
BAIT,
SNACKS,
EQUIPMENT,
ART_SUPPLIES,
}
const SHOP_SECTION_LABELS: Array[String] = [
"Upgrades",
"Bait",
"Snacks",
"Equipment",
"Art Supplies",
]
@onready var _wallet_label: Label = %WalletLabel
@onready var _shop_panel: PanelContainer = %ShopPanel
@onready var _shop_cooler_page: Control = %ShopCoolerPage
@ -47,6 +64,10 @@ enum CloseReason {
@onready var _buy_mode_button: Button = %BuyModeButton
@onready var _sell_mode_button: Button = %SellModeButton
@onready var _feedback: Label = %Feedback
@onready var _shop_tab_bar: HBoxContainer = %ShopTabBar
@onready var _upgrades_content: VBoxContainer = %Upgrades
@onready var _supplies_content: VBoxContainer = %Supplies
@onready var _stock_title: Label = %StockTitle
@onready var _reel_level: Label = %ReelLevel
@onready var _reel_effect: Label = %ReelEffect
@onready var _reel_cost: Label = %ReelCost
@ -81,11 +102,14 @@ var _transaction_in_progress: bool = false
var _closing: bool = false
var _cooler_page_active: bool = false
var _cooler_modal_open: bool = false
var _shop_section: ShopSection = ShopSection.UPGRADES
var _shop_tabs: Array[OrganizerTab] = []
func _ready() -> void:
UtilityPageStyleType.apply_page(self)
_apply_shop_styles()
_build_shop_tabs()
%CloseButton.pressed.connect(close_shop)
_buy_mode_button.pressed.connect(_focus_buy_page)
_sell_mode_button.pressed.connect(_request_shop_cooler)
@ -107,12 +131,39 @@ func _request_shop_cooler() -> void:
func _focus_buy_page() -> void:
_feedback.text = ""
if _shop_section == ShopSection.UPGRADES:
_reel_purchase.grab_focus()
return
for child: Node in _supplies_list.get_children():
var stock_button := child as Button
if stock_button != null and not stock_button.disabled:
stock_button.grab_focus()
return
_reel_purchase.grab_focus()
_buy_mode_button.grab_focus()
func _build_shop_tabs() -> void:
for section_index: int in range(ShopSection.size()):
var tab: OrganizerTab = OrganizerTabType.new()
tab.text = SHOP_SECTION_LABELS[section_index]
tab.palette_index = section_index
tab.size_flags_horizontal = Control.SIZE_EXPAND_FILL
tab.pressed.connect(_select_shop_section.bind(section_index, true))
_shop_tab_bar.add_child(tab)
_shop_tabs.append(tab)
_select_shop_section(ShopSection.UPGRADES, false)
func _select_shop_section(section_index: int, focus_content: bool) -> void:
_shop_section = section_index as ShopSection
var showing_upgrades: bool = _shop_section == ShopSection.UPGRADES
_upgrades_content.visible = showing_upgrades
_supplies_content.visible = not showing_upgrades
for tab_index: int in range(_shop_tabs.size()):
_shop_tabs[tab_index].set_selected(tab_index == section_index, true)
_refresh_supplies()
if focus_content and visible:
_focus_buy_page()
func _apply_shop_styles() -> void:
@ -236,6 +287,7 @@ func open_shop() -> bool:
_feedback.text = ""
deactivate_shop_cooler_page()
show()
_select_shop_section(ShopSection.UPGRADES, false)
_refresh_all()
_buy_mode_button.grab_focus()
menu_visibility_changed.emit(true)
@ -413,10 +465,12 @@ func _refresh_supplies() -> void:
for child: Node in _supplies_list.get_children():
_supplies_list.remove_child(child)
child.queue_free()
_add_stock_section("supplies")
if _shop_section == ShopSection.UPGRADES:
return
_stock_title.text = SHOP_SECTION_LABELS[int(_shop_section)]
for item_id: StringName in FishingShopStockType.get_stock_item_ids():
var item: ItemDataType = _item_catalog.get_item_by_id(item_id)
if item == null:
if item == null or not _item_belongs_in_current_section(item):
continue
var button := Button.new()
button.custom_minimum_size = Vector2(195, 54)
@ -492,17 +546,32 @@ func _refresh_supplies() -> void:
UtilityPageStyleType.apply_ocean_button(button)
button.pressed.connect(_purchase_supply.bind(item_id))
_supplies_list.add_child(button)
_add_stock_section("art kit")
_add_art_kit_button()
_add_stock_section("markers")
for product_id: StringName in ArtShopStockType.MARKER_PRODUCTS:
_add_art_upgrade_button(product_id)
_add_stock_section("brushes")
for product_id: StringName in ArtShopStockType.BRUSH_PRODUCTS:
_add_art_upgrade_button(product_id)
_add_stock_section("grids")
for product_id: StringName in ArtShopStockType.GRID_PRODUCTS:
_add_art_upgrade_button(product_id)
if _shop_section == ShopSection.ART_SUPPLIES:
_add_stock_section("art kit")
_add_art_kit_button()
_add_stock_section("markers")
for product_id: StringName in ArtShopStockType.MARKER_PRODUCTS:
_add_art_upgrade_button(product_id)
_add_stock_section("brushes")
for product_id: StringName in ArtShopStockType.BRUSH_PRODUCTS:
_add_art_upgrade_button(product_id)
_add_stock_section("grids")
for product_id: StringName in ArtShopStockType.GRID_PRODUCTS:
_add_art_upgrade_button(product_id)
func _item_belongs_in_current_section(item: ItemDataType) -> bool:
match _shop_section:
ShopSection.BAIT:
return item.is_bait()
ShopSection.SNACKS:
return item.category == ItemDataType.Category.CONSUMABLE
ShopSection.EQUIPMENT:
return item.category in [
ItemDataType.Category.TOOL,
ItemDataType.Category.LURE,
]
return false
func _add_stock_section(title: String) -> void:

View file

@ -143,12 +143,19 @@ text = ""
horizontal_alignment = 1
theme_override_colors/font_color = Color(1, 0.82, 0.4, 1)
[node name="ShopTabBar" type="HBoxContainer" parent="ShopPanel/Margin/Layout"]
unique_name_in_owner = true
layout_mode = 2
alignment = 1
theme_override_constants/separation = 6
[node name="Body" type="HBoxContainer" parent="ShopPanel/Margin/Layout"]
layout_mode = 2
size_flags_vertical = 3
theme_override_constants/separation = 12
theme_override_constants/separation = 0
[node name="Upgrades" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body"]
unique_name_in_owner = true
custom_minimum_size = Vector2(270, 0)
layout_mode = 2
size_flags_horizontal = 3
@ -160,12 +167,14 @@ text = "fishing upgrades"
theme_override_font_sizes/font_size = 23
[node name="Supplies" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body"]
unique_name_in_owner = true
custom_minimum_size = Vector2(210, 0)
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 6
[node name="Title" type="Label" parent="ShopPanel/Margin/Layout/Body/Supplies"]
[node name="StockTitle" type="Label" parent="ShopPanel/Margin/Layout/Body/Supplies"]
unique_name_in_owner = true
layout_mode = 2
text = "shop stock"
theme_override_font_sizes/font_size = 23

View file

@ -91,6 +91,7 @@ const FISHING_PANEL_SHOWCASE_TOP_OFFSET: float = -174.0
const FISHING_PANEL_SHOWCASE_BOTTOM_OFFSET: float = -104.0
@onready var _status_label: Label = %StatusLabel
@onready var _bite_prompt_button: Button = %BitePromptButton
@onready var _gameplay_transient_hud: Control = %GameplayTransientHUD
@onready var _active_bait_button: Button = %ActiveBaitButton
@onready var _active_bait_quantity_badge: Panel = %ActiveBaitQuantityBadge
@ -190,6 +191,7 @@ var _settings_manager: PlayerSettingsManagerType
func _ready() -> void:
_bite_prompt_button.pressed.connect(_on_bite_prompt_pressed)
_apply_active_bait_indicator_style()
_refresh_active_bait_indicator()
# Reward feedback must remain above full-screen canonical menus. Keeping the
@ -307,7 +309,7 @@ func setup(
_experience.experience_awarded.connect(_on_experience_awarded)
_chat_ui.setup(
network_chat_service, network_session, spawn_service, player,
fishing_spot, settings_manager, world_time, world_weather
fishing_spot, settings_manager, world_time, world_weather, item_effects
)
_title_settings_panel.setup_network_profile(
network_profile, network_session
@ -316,6 +318,7 @@ func setup(
network_profile, network_session
)
fishing_spot.status_changed.connect(_on_fishing_status_changed)
fishing_spot.bite_prompt_changed.connect(_on_bite_prompt_changed)
fishing_spot.catch_display_changed.connect(_on_catch_display_changed)
fishing_spot.showcase_changed.connect(_on_showcase_changed)
fishing_spot.art_ui_toggle_requested.connect(_toggle_surface_drawing)
@ -1590,6 +1593,7 @@ func _refresh_fishing_panel_visibility() -> void:
var has_content: bool = (
not _status_label.text.strip_edges().is_empty()
or not _showcase_details.text.strip_edges().is_empty()
or _bite_prompt_button.visible
)
_fishing_panel.visible = (
_gameplay_ui_enabled
@ -1597,6 +1601,16 @@ func _refresh_fishing_panel_visibility() -> void:
)
func _on_bite_prompt_changed(is_visible: bool) -> void:
_bite_prompt_button.visible = is_visible
_refresh_fishing_panel_visibility()
func _on_bite_prompt_pressed() -> void:
if _fishing_spot != null:
_fishing_spot.confirm_pending_bite()
func _on_catch_display_changed(
progress: float,
chase_progress: float,

View file

@ -279,6 +279,16 @@ text = ""
horizontal_alignment = 1
theme_override_font_sizes/font_size = 16
[node name="BitePromptButton" type="Button" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"]
unique_name_in_owner = true
visible = false
custom_minimum_size = Vector2(0, 42)
layout_mode = 2
focus_mode = 0
mouse_default_cursor_shape = 2
text = "you got a bite!"
theme_override_font_sizes/font_size = 22
[node name="ShowcaseDetails" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"]
unique_name_in_owner = true
visible = false

View file

@ -189,6 +189,7 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0)
@onready var _cooler_combined_offer_value: Label = %CoolerCombinedOfferValue
@onready var _favorite_bubble: NotepadInkActionType = %FavoriteBubble
@onready var _sell_bubble: NotepadInkActionType = %SellBubble
@onready var _sell_all_bubble: NotepadInkActionType = %SellAllBubble
@onready var _bag_page: Control = %BagPage
@onready var _tackle_box_page: Control = %TackleBoxPage
@onready var _inventory_sub_tabs: HBoxContainer = %InventorySubTabs
@ -392,7 +393,7 @@ func _ready() -> void:
)
_bait_filter.pressed.connect(_set_tackle_view.bind(TackleView.BAIT))
_lures_filter.pressed.connect(_set_tackle_view.bind(TackleView.LURES))
_tackle_equip_button.pressed.connect(_toggle_active_bait)
_tackle_equip_button.pressed.connect(_toggle_active_tackle)
_logbook_tab.pressed.connect(
_show_section.bind(Section.LOGBOOK)
)
@ -409,6 +410,7 @@ func _ready() -> void:
_sell_button.pressed.connect(_on_sell_pressed)
_favorite_bubble.pressed.connect(_on_favorite_pressed)
_sell_bubble.pressed.connect(_on_sell_pressed)
_sell_all_bubble.pressed.connect(_on_sell_all_pressed)
_confirm_sale_button.pressed.connect(_on_confirm_sale_pressed)
_cancel_sale_button.pressed.connect(_close_sale_confirmation)
_configure_sale_confirmation_focus()
@ -593,6 +595,8 @@ func setup(
_bag.contents_changed.connect(_on_bag_changed)
if not _player.active_bait_changed.is_connected(_on_active_bait_changed):
_player.active_bait_changed.connect(_on_active_bait_changed)
if not _player.active_lure_changed.is_connected(_on_active_lure_changed):
_player.active_lure_changed.connect(_on_active_lure_changed)
if not _hotbar.slots_changed.is_connected(_on_hotbar_changed):
_hotbar.slots_changed.connect(_on_hotbar_changed)
if not _cooler_capacity.capacity_changed.is_connected(
@ -754,6 +758,8 @@ func _try_enter_notepad_controller_ownership() -> bool:
actions.append(_favorite_bubble)
if not _sell_bubble.disabled:
actions.append(_sell_bubble)
if not _sell_all_bubble.disabled:
actions.append(_sell_all_bubble)
elif _current_section == Section.TACKLE_BOX:
if not focus_owner.has_meta(&"controller_tackle_item_id"):
return false
@ -1943,21 +1949,30 @@ func _refresh_tackle_box() -> void:
var item: ItemDataType = _item_catalog.get_item_by_id(owned.item_id)
var row := Button.new()
row.icon = item.icon
row.tooltip_text = "%s ×%d" % [item.display_name, owned.quantity]
if item.is_bait() and item.icon != null:
row.tooltip_text = (
"%s ×%d" % [item.display_name, owned.quantity]
if item.is_bait()
else item.display_name
)
var compact_tackle_item: bool = item.is_bait() or item.is_lure()
if compact_tackle_item:
row.custom_minimum_size = Vector2(72, 72)
row.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
row.expand_icon = true
row.expand_icon = item.icon != null
row.alignment = HORIZONTAL_ALIGNMENT_CENTER
row.text = "" if item.icon != null else item.display_name
if item.icon == null:
row.add_theme_font_size_override("font_size", 12)
else:
row.text = "%s ×%d" % [item.display_name, owned.quantity]
row.alignment = HORIZONTAL_ALIGNMENT_LEFT
row.toggle_mode = true
row.set_meta(&"controller_tackle_item_id", owned.item_id)
row.button_pressed = owned.item_id == _selected_tackle_item_id
if item.is_bait() and item.icon != null:
if compact_tackle_item:
_apply_tackle_bait_button_style(row)
_add_tackle_quantity_badge(row, owned.quantity)
if item.is_bait():
_add_tackle_quantity_badge(row, owned.quantity)
else:
UtilityPageStyle.apply_ocean_button(row)
row.pressed.connect(_select_tackle_item.bind(owned.item_id))
@ -2053,7 +2068,7 @@ func _update_tackle_detail() -> void:
else "Select a lure for details."
)
return
_tackle_equip_button.visible = item.is_bait()
_tackle_equip_button.visible = item.is_bait() or item.is_lure()
var quantity: int = _bag.get_quantity(item.item_id) if _bag != null else 0
var assigned_slot: int = -1
if _hotbar != null:
@ -2061,19 +2076,22 @@ func _update_tackle_detail() -> void:
if _hotbar.get_item_id(slot_index) == item.item_id:
assigned_slot = slot_index
break
var detail_lines: Array[String] = [
item.display_name,
"",
"quantity: %d" % quantity,
]
var detail_lines: Array[String] = [item.display_name, ""]
if item.is_bait():
detail_lines.append("quantity: %d" % quantity)
if assigned_slot >= 0:
detail_lines.append("hotbar slot %d" % (assigned_slot + 1))
detail_lines.append("")
detail_lines.append(item.description)
_tackle_detail_text.text = "\n".join(detail_lines)
if item.is_bait():
if item.is_bait() or item.is_lure():
var is_equipped: bool = (
_player != null and _player.active_bait_id == item.item_id
_player != null
and (
_player.active_bait_id == item.item_id
if item.is_bait()
else _player.active_lure_id == item.item_id
)
)
_tackle_equip_button.text = (
"dequip %s" % item.display_name
@ -2085,16 +2103,24 @@ func _update_tackle_detail() -> void:
_tackle_equip_button.refresh_ink_state()
func _toggle_active_bait() -> void:
func _toggle_active_tackle() -> void:
if _player == null or _item_catalog == null:
return
var item: ItemDataType = _item_catalog.get_item_by_id(_selected_tackle_item_id)
if item == null or not item.is_bait():
if item == null:
return
if _player.active_bait_id == item.item_id:
_player.unequip_bait()
if item.is_bait():
if _player.active_bait_id == item.item_id:
_player.unequip_bait()
else:
_player.equip_bait(item)
elif item.is_lure():
if _player.active_lure_id == item.item_id:
_player.unequip_lure()
else:
_player.equip_lure(item)
else:
_player.equip_bait(item)
return
_update_tackle_detail()
@ -2102,6 +2128,10 @@ func _on_active_bait_changed(_item_id: StringName) -> void:
_update_tackle_detail()
func _on_active_lure_changed(_item_id: StringName) -> void:
_update_tackle_detail()
func _apply_cooler_control_styles() -> void:
var profile: BubbleMenuProfile = _inventory_tab.profile
if profile == null:
@ -2426,6 +2456,9 @@ func _update_shell_layout() -> void:
_sell_bubble.custom_minimum_size = (
Vector2(74.0, 42.0) if compact else Vector2(118.0, 50.0)
)
_sell_all_bubble.custom_minimum_size = (
Vector2(96.0, 38.0) if compact else Vector2(122.0, 44.0)
)
_favorite_bubble.position = (
Vector2(144.0, 148.0) if compact else Vector2(26.0, 378.0)
)
@ -2438,12 +2471,21 @@ func _update_shell_layout() -> void:
_sell_bubble.size = (
Vector2(74.0, 42.0) if compact else Vector2(118.0, 50.0)
)
_sell_all_bubble.position = (
Vector2(102.0, 190.0) if compact else Vector2(78.0, 426.0)
)
_sell_all_bubble.size = (
Vector2(96.0, 38.0) if compact else Vector2(122.0, 44.0)
)
_favorite_bubble.add_theme_font_size_override(
"font_size", 11 if compact else 17,
)
_sell_bubble.add_theme_font_size_override(
"font_size", 11 if compact else 17,
)
_sell_all_bubble.add_theme_font_size_override(
"font_size", 11 if compact else 15,
)
_update_sort_direction_text()
_bag_page.size = reference_size
_bag_page.position = Vector2.ZERO
@ -2946,7 +2988,11 @@ func _set_content_interactive(interactive: bool) -> void:
var detail_interactive: bool = (
cooler_interactive and _detail_constellation.visible
)
for action: NotepadInkActionType in [_favorite_bubble, _sell_bubble]:
for action: NotepadInkActionType in [
_favorite_bubble,
_sell_bubble,
_sell_all_bubble,
]:
var action_interactive: bool = detail_interactive and not action.disabled
action.focus_mode = (
Control.FOCUS_ALL
@ -3595,6 +3641,11 @@ func _configure_cooler_fish_focus() -> void:
not _favorite_bubble.disabled
and index + columns >= controls.size()
)
else control.get_path_to(_sell_all_bubble)
if (
not _sell_all_bubble.disabled
and index + columns >= controls.size()
)
else control.get_path_to(
controls[mini(index + columns, controls.size() - 1)]
)
@ -3620,6 +3671,18 @@ func _configure_cooler_fish_focus() -> void:
_sell_bubble.get_path_to(_close_button)
)
_sell_bubble.focus_neighbor_top = _sell_bubble.get_path_to(focused_node)
_sell_bubble.focus_neighbor_bottom = (
_sell_bubble.get_path_to(_sell_all_bubble)
)
_sell_all_bubble.focus_neighbor_left = (
_sell_all_bubble.get_path_to(_favorite_bubble)
)
_sell_all_bubble.focus_neighbor_right = (
_sell_all_bubble.get_path_to(_close_button)
)
_sell_all_bubble.focus_neighbor_top = (
_sell_all_bubble.get_path_to(_sell_bubble)
)
func _compare_catches(left: FishCatchType, right: FishCatchType) -> bool:
@ -3684,6 +3747,7 @@ func _close_detail_constellation() -> void:
_sell_bubble.mouse_filter = Control.MOUSE_FILTER_IGNORE
_favorite_bubble.refresh_ink_state()
_sell_bubble.refresh_ink_state()
_refresh_cooler_notepad_action_interactivity()
_configure_cooler_fish_focus()
@ -3750,6 +3814,7 @@ func _update_inventory_detail(fish_catch: FishCatchType) -> void:
_sell_bubble.mouse_filter = Control.MOUSE_FILTER_IGNORE
_favorite_bubble.refresh_ink_state()
_sell_bubble.refresh_ink_state()
_refresh_cooler_notepad_action_interactivity()
_configure_cooler_fish_focus()
return
_cooler_detail_texture.texture = fish_catch.fish.display_texture
@ -3797,7 +3862,24 @@ func _update_inventory_detail(fish_catch: FishCatchType) -> void:
and not _transitioning
and not _page_transitioning
)
for action: NotepadInkActionType in [_favorite_bubble, _sell_bubble]:
_refresh_cooler_notepad_action_interactivity()
_configure_cooler_fish_focus()
func _refresh_cooler_notepad_action_interactivity() -> void:
var detail_interactive: bool = (
_detail_constellation.visible
and _current_section == Section.COOLER
and (visible or _shop_cooler_context_active)
and not _transitioning
and not _page_transitioning
and not _sale_confirmation.visible
)
for action: NotepadInkActionType in [
_favorite_bubble,
_sell_bubble,
_sell_all_bubble,
]:
var action_interactive: bool = detail_interactive and not action.disabled
action.focus_mode = (
Control.FOCUS_ALL
@ -3810,7 +3892,6 @@ func _update_inventory_detail(fish_catch: FishCatchType) -> void:
else Control.MOUSE_FILTER_IGNORE
)
action.refresh_ink_state()
_configure_cooler_fish_focus()
func _update_sale_summary() -> void:
@ -3818,6 +3899,7 @@ func _update_sale_summary() -> void:
var buyer_id: StringName = (
active_buyer.id if active_buyer != null else StringName()
)
_update_sell_all_action(active_buyer, buyer_id)
var offer_label: String = _get_offer_label(active_buyer)
var selected_ids: Array[StringName] = _fish_selection.get_selected_ids()
var selected_count: int = selected_ids.size()
@ -3927,6 +4009,55 @@ func _update_sale_summary() -> void:
_sale_unavailable.visible = not _sale_unavailable.text.is_empty()
func _update_sell_all_action(
active_buyer: FishBuyerProfileType,
buyer_id: StringName,
) -> void:
var sell_all_ids: Array[StringName] = _get_sell_all_catch_ids()
var sale_available: bool = (
not _sale_in_progress
and (
_network_sale_service == null
or not _network_sale_service.is_local_sale_pending()
)
and _network_sale_service != null
and not buyer_id.is_empty()
and _network_sale_service.can_request_sale(buyer_id)
and _sale_service != null
and active_buyer != null
and not sell_all_ids.is_empty()
)
if sale_available:
var preview: FishSaleResultType = _sale_service.preview_batch(
sell_all_ids,
active_buyer,
)
sale_available = preview.is_success()
_sell_all_bubble.text = "sell all fish"
_sell_all_bubble.disabled = not sale_available
_sell_all_bubble.persistent_mark = false
_sell_all_bubble.refresh_ink_state()
func _get_sell_all_catch_ids() -> Array[StringName]:
var catch_ids: Array[StringName] = []
if _inventory == null:
return catch_ids
for fish_catch: FishCatchType in _inventory.get_all_catches():
if (
fish_catch == null
or not fish_catch.is_valid()
or fish_catch.is_favorited
or (
_reservations != null
and _reservations.is_fish_reserved(fish_catch.catch_id)
)
):
continue
catch_ids.append(fish_catch.catch_id)
return catch_ids
func _on_favorite_pressed() -> void:
var focused_id: StringName = _fish_selection.get_focused_id()
if _inventory == null or focused_id.is_empty():
@ -3951,6 +4082,18 @@ func _on_favorite_pressed() -> void:
func _on_sell_pressed() -> void:
_begin_sale_confirmation(_fish_selection.get_selected_ids())
func _on_sell_all_pressed() -> void:
var catch_ids: Array[StringName] = _get_sell_all_catch_ids()
if catch_ids.is_empty():
_transaction_feedback.text = "No sellable fish in the cooler."
return
_begin_sale_confirmation(catch_ids)
func _begin_sale_confirmation(catch_ids: Array[StringName]) -> void:
var active_buyer: FishBuyerProfileType = _get_active_sale_buyer()
var buyer_id: StringName = (
active_buyer.id if active_buyer != null else StringName()
@ -3973,16 +4116,15 @@ func _on_sell_pressed() -> void:
"Selling is not supported by this server."
)
return
var selected_ids: Array[StringName] = _fish_selection.get_selected_ids()
if (
_inventory == null
or _sale_service == null
or active_buyer == null
or selected_ids.is_empty()
or catch_ids.is_empty()
):
return
var preview: FishSaleResultType = _sale_service.preview_batch(
selected_ids,
catch_ids,
active_buyer
)
if not preview.is_success():
@ -3994,7 +4136,7 @@ func _on_sell_pressed() -> void:
)
_refresh_inventory()
return
_confirmation_catch_ids = selected_ids.duplicate()
_confirmation_catch_ids = catch_ids.duplicate()
_confirmation_buyer = active_buyer
_confirmation_buyer_id = active_buyer.id
_confirmation_generation = _menu_generation

View file

@ -470,6 +470,16 @@ offset_right = 256.0
offset_bottom = 464.0
text = "sell fish"
[node name="SellAllBubble" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation" instance=ExtResource("12_ink_action")]
unique_name_in_owner = true
custom_minimum_size = Vector2(122, 44)
layout_mode = 0
offset_left = 78.0
offset_top = 426.0
offset_right = 200.0
offset_bottom = 470.0
text = "sell all fish"
[node name="BagPage" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot"]
unique_name_in_owner = true
visible = false