Polish inventory shop and utility interfaces
Split inventory items and tackle views, standardize bait supply presentation, add status-effect icons, and align shop, logbook, art-kit, and shared utility layouts.
This commit is contained in:
parent
84695c473b
commit
a60925bf7b
21 changed files with 1150 additions and 385 deletions
|
|
@ -151,9 +151,6 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
_service.cancel_local_interaction(_request_id)
|
_service.cancel_local_interaction(_request_id)
|
||||||
status_changed.emit(
|
|
||||||
"Sneak close, pull the net all the way back, and line up the marker."
|
|
||||||
)
|
|
||||||
_reset_charge_state()
|
_reset_charge_state()
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,12 @@ const ENERGY_DRINK_ID: StringName = &"energy_drink"
|
||||||
const SNACK_ID: StringName = &"snack"
|
const SNACK_ID: StringName = &"snack"
|
||||||
const FISH_FINDER_ID: StringName = &"fish_finder"
|
const FISH_FINDER_ID: StringName = &"fish_finder"
|
||||||
const BATTERIES_ID: StringName = &"batteries"
|
const BATTERIES_ID: StringName = &"batteries"
|
||||||
|
const EFFECT_IDS: Array[StringName] = [
|
||||||
|
COFFEE_ID,
|
||||||
|
ENERGY_DRINK_ID,
|
||||||
|
SNACK_ID,
|
||||||
|
FISH_FINDER_ID,
|
||||||
|
]
|
||||||
const FISH_FINDER_DEAD_MESSAGE: String = "hmm... batteries are dead..."
|
const FISH_FINDER_DEAD_MESSAGE: String = "hmm... batteries are dead..."
|
||||||
const COFFEE_DURATION: float = 90.0
|
const COFFEE_DURATION: float = 90.0
|
||||||
const ENERGY_DRINK_DURATION: float = 90.0
|
const ENERGY_DRINK_DURATION: float = 90.0
|
||||||
|
|
@ -100,6 +106,10 @@ func get_remaining_snapshot() -> Dictionary[StringName, float]:
|
||||||
return _remaining.duplicate()
|
return _remaining.duplicate()
|
||||||
|
|
||||||
|
|
||||||
|
func get_registered_effect_ids() -> Array[StringName]:
|
||||||
|
return EFFECT_IDS.duplicate()
|
||||||
|
|
||||||
|
|
||||||
func get_effect_duration(item_id: StringName) -> float:
|
func get_effect_duration(item_id: StringName) -> float:
|
||||||
return _get_duration(item_id)
|
return _get_duration(item_id)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -399,6 +399,33 @@ func _run() -> void:
|
||||||
assert(hide_button.button_group.allow_unpress)
|
assert(hide_button.button_group.allow_unpress)
|
||||||
assert(brush_option.item_count == 4)
|
assert(brush_option.item_count == 4)
|
||||||
assert(grid_option.item_count == 4)
|
assert(grid_option.item_count == 4)
|
||||||
|
var expected_brush_icons: Array[String] = [
|
||||||
|
"art_kit_marker_tip_fine.png",
|
||||||
|
"art_kit_marker_tip_thin.png",
|
||||||
|
"art_kit_marker_tip_mid.png",
|
||||||
|
"art_kit_marker_tip_thick.png",
|
||||||
|
]
|
||||||
|
var expected_grid_icons: Array[String] = [
|
||||||
|
"art_kit_grid_small_light.png",
|
||||||
|
"art_kit_grid_medium_light.png",
|
||||||
|
"art_kit_grid_large_light.png",
|
||||||
|
"art_kit_grid_xl_light.png",
|
||||||
|
]
|
||||||
|
for index: int in 4:
|
||||||
|
assert(brush_option.get_item_text(index).is_empty())
|
||||||
|
assert(grid_option.get_item_text(index).is_empty())
|
||||||
|
assert(brush_option.get_item_icon(index) != null)
|
||||||
|
assert(grid_option.get_item_icon(index) != null)
|
||||||
|
assert(
|
||||||
|
brush_option.get_item_icon(index).resource_path.ends_with(
|
||||||
|
expected_brush_icons[index]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
grid_option.get_item_icon(index).resource_path.ends_with(
|
||||||
|
expected_grid_icons[index]
|
||||||
|
)
|
||||||
|
)
|
||||||
assert(not brush_option.get_popup().is_item_disabled(0))
|
assert(not brush_option.get_popup().is_item_disabled(0))
|
||||||
assert(brush_option.get_popup().is_item_disabled(1))
|
assert(brush_option.get_popup().is_item_disabled(1))
|
||||||
assert(not grid_option.get_popup().is_item_disabled(0))
|
assert(not grid_option.get_popup().is_item_disabled(0))
|
||||||
|
|
|
||||||
|
|
@ -570,6 +570,53 @@ func _test_fishing_shop_sale_ui(
|
||||||
and shop_backdrop != null
|
and shop_backdrop != null
|
||||||
and ui_viewport != null
|
and ui_viewport != null
|
||||||
)
|
)
|
||||||
|
var item_catalog := main.get("item_catalog") as ItemCatalog
|
||||||
|
var worms: ItemData = item_catalog.get_item_by_id(&"worms")
|
||||||
|
assert(worms != null and worms.max_stack == 10)
|
||||||
|
if player.bag.get_quantity(worms.item_id) == 0:
|
||||||
|
assert(player.bag.add_item(worms.item_id, 4))
|
||||||
|
assert(player.equip_bait(worms))
|
||||||
|
await process_frame
|
||||||
|
var expected_bait_supply := UtilityPageStyle.supply_quantity_text(
|
||||||
|
player.bag.get_quantity(worms.item_id),
|
||||||
|
worms.max_stack,
|
||||||
|
)
|
||||||
|
var hud_bait_quantity := game_ui.get_node(
|
||||||
|
"%ActiveBaitQuantity"
|
||||||
|
) as Label
|
||||||
|
assert(hud_bait_quantity.text == expected_bait_supply)
|
||||||
|
var hud_bait_badge := hud_bait_quantity.get_parent() as Panel
|
||||||
|
assert(
|
||||||
|
is_equal_approx(
|
||||||
|
hud_bait_badge.rotation_degrees,
|
||||||
|
UtilityPageStyle.SUPPLY_BADGE_ROTATION_DEGREES,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
player_menu.call("_refresh_tackle_box")
|
||||||
|
await process_frame
|
||||||
|
var tackle_bait_button: Button
|
||||||
|
for child: Node in player_menu.get_node("%BaitItemList").get_children():
|
||||||
|
var candidate := child as Button
|
||||||
|
if (
|
||||||
|
candidate != null
|
||||||
|
and candidate.get_meta(&"controller_tackle_item_id", StringName())
|
||||||
|
== worms.item_id
|
||||||
|
):
|
||||||
|
tackle_bait_button = candidate
|
||||||
|
break
|
||||||
|
assert(tackle_bait_button != null)
|
||||||
|
var tackle_quantity := tackle_bait_button.get_node(
|
||||||
|
"QuantityBadge/Quantity"
|
||||||
|
) as Label
|
||||||
|
assert(tackle_quantity.text == expected_bait_supply)
|
||||||
|
assert(tackle_bait_button.tooltip_text.contains(expected_bait_supply))
|
||||||
|
var tackle_badge := tackle_quantity.get_parent() as Panel
|
||||||
|
assert(
|
||||||
|
is_equal_approx(
|
||||||
|
tackle_badge.rotation_degrees,
|
||||||
|
UtilityPageStyle.SUPPLY_BADGE_ROTATION_DEGREES,
|
||||||
|
)
|
||||||
|
)
|
||||||
player.global_position = interaction.global_position
|
player.global_position = interaction.global_position
|
||||||
for _frame: int in 4:
|
for _frame: int in 4:
|
||||||
await physics_frame
|
await physics_frame
|
||||||
|
|
@ -596,6 +643,14 @@ func _test_fishing_shop_sale_ui(
|
||||||
assert(shop_panel_style.corner_radius_top_left == 24)
|
assert(shop_panel_style.corner_radius_top_left == 24)
|
||||||
var upgrade_grid := shop.get_node("%UpgradeGrid") as GridContainer
|
var upgrade_grid := shop.get_node("%UpgradeGrid") as GridContainer
|
||||||
assert(upgrade_grid != null and upgrade_grid.columns == 3)
|
assert(upgrade_grid != null and upgrade_grid.columns == 3)
|
||||||
|
assert(
|
||||||
|
upgrade_grid.get_theme_constant("h_separation")
|
||||||
|
== FishingShop.SHOP_SLOT_SEPARATION
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
upgrade_grid.get_theme_constant("v_separation")
|
||||||
|
== FishingShop.SHOP_SLOT_SEPARATION
|
||||||
|
)
|
||||||
assert((shop.get_node("%Upgrades") as Control).visible)
|
assert((shop.get_node("%Upgrades") as Control).visible)
|
||||||
assert(not (shop.get_node("%Supplies") as Control).visible)
|
assert(not (shop.get_node("%Supplies") as Control).visible)
|
||||||
for upgrade_name: String in [
|
for upgrade_name: String in [
|
||||||
|
|
@ -638,6 +693,89 @@ func _test_fishing_shop_sale_ui(
|
||||||
assert(sell_mode != null and sell_mode.text == "Sell Fish")
|
assert(sell_mode != null and sell_mode.text == "Sell Fish")
|
||||||
var equipment_tab := shop_tabs[3] as Button
|
var equipment_tab := shop_tabs[3] as Button
|
||||||
assert(equipment_tab != null and equipment_tab.text == "Equipment")
|
assert(equipment_tab != null and equipment_tab.text == "Equipment")
|
||||||
|
var supplies_list := shop.get_node("%SuppliesList") as VBoxContainer
|
||||||
|
var bait_tab := shop_tabs[1] as Button
|
||||||
|
assert(bait_tab != null and bait_tab.text == "Bait and Lures")
|
||||||
|
await _activate_pointer_control(bait_tab, ui_viewport)
|
||||||
|
await process_frame
|
||||||
|
var worms_button := supplies_list.find_child(
|
||||||
|
"Supply_worms", true, false
|
||||||
|
) as Button
|
||||||
|
var snails_button := supplies_list.find_child(
|
||||||
|
"Supply_snails", true, false
|
||||||
|
) as Button
|
||||||
|
assert(worms_button != null and snails_button != null)
|
||||||
|
var bait_grid := worms_button.get_parent().get_parent() as GridContainer
|
||||||
|
assert(bait_grid != null)
|
||||||
|
assert(
|
||||||
|
bait_grid.get_theme_constant("h_separation")
|
||||||
|
== FishingShop.SHOP_SLOT_SEPARATION
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
bait_grid.get_theme_constant("v_separation")
|
||||||
|
== FishingShop.SHOP_SLOT_SEPARATION
|
||||||
|
)
|
||||||
|
var shop_bait_quantity := worms_button.get_node(
|
||||||
|
"SupplyQuantityBadge/Quantity"
|
||||||
|
) as Label
|
||||||
|
assert(shop_bait_quantity.text == expected_bait_supply)
|
||||||
|
var shop_bait_badge := shop_bait_quantity.get_parent() as Panel
|
||||||
|
assert(shop_bait_badge.position.y == FishingShop.BAIT_SUPPLY_BADGE_Y)
|
||||||
|
assert(
|
||||||
|
is_equal_approx(
|
||||||
|
shop_bait_badge.rotation_degrees,
|
||||||
|
UtilityPageStyle.SUPPLY_BADGE_ROTATION_DEGREES,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
shop_bait_badge.position.y + shop_bait_badge.size.y
|
||||||
|
<= FishingShop.SUPPLY_PRICE_Y
|
||||||
|
)
|
||||||
|
var worms_unit_price: int = FishingShopStock.get_price(worms.item_id)
|
||||||
|
assert(
|
||||||
|
worms_button.tooltip_text.contains(
|
||||||
|
"%d fish coin%s per bait piece" % [
|
||||||
|
worms_unit_price,
|
||||||
|
"" if worms_unit_price == 1 else "s",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert(not worms_button.tooltip_text.contains("owned"))
|
||||||
|
var worms_restock_quantity: int = FishingShopStock.get_purchase_quantity(
|
||||||
|
worms.item_id,
|
||||||
|
player.bag.get_quantity(worms.item_id),
|
||||||
|
worms.max_stack,
|
||||||
|
)
|
||||||
|
var worms_restock_cost: int = FishingShopStock.get_purchase_cost(
|
||||||
|
worms.item_id,
|
||||||
|
worms_restock_quantity,
|
||||||
|
player.bag.is_bait_unlocked(worms.item_id),
|
||||||
|
)
|
||||||
|
var worms_price := worms_button.get_parent().get_node(
|
||||||
|
"PriceBubble/CurrencyAmount/Price"
|
||||||
|
) as Label
|
||||||
|
assert(worms_price.text == str(maxi(worms_restock_cost, 0)))
|
||||||
|
var bait_receipt: String = shop.call(
|
||||||
|
"_purchase_success_feedback",
|
||||||
|
worms.item_id,
|
||||||
|
NetworkShopProtocol.ProductCategory.SUPPLY,
|
||||||
|
maxi(worms_restock_quantity, 1),
|
||||||
|
maxi(worms_restock_cost, worms_unit_price),
|
||||||
|
)
|
||||||
|
assert(bait_receipt.begins_with("Bait Restocked\n"))
|
||||||
|
assert(bait_receipt.contains(CurrencyPresentation.ICON_PATH))
|
||||||
|
assert(bait_receipt.contains(" spent on "))
|
||||||
|
assert(bait_receipt.ends_with(" worms"))
|
||||||
|
assert(snails_button.get_node_or_null("SupplyQuantityBadge") == null)
|
||||||
|
assert(worms_button.get_node_or_null("UnlockStateIcon") == null)
|
||||||
|
var snails_lock_icon := snails_button.get_node(
|
||||||
|
"UnlockStateIcon"
|
||||||
|
) as TextureRect
|
||||||
|
assert(not bool(snails_lock_icon.get_meta(&"unlocked")))
|
||||||
|
assert(snails_lock_icon.texture.resource_path.ends_with(
|
||||||
|
"/lock_light.png"
|
||||||
|
))
|
||||||
|
assert(is_equal_approx(snails_lock_icon.modulate.a, 0.75))
|
||||||
await _activate_pointer_control(equipment_tab, ui_viewport)
|
await _activate_pointer_control(equipment_tab, ui_viewport)
|
||||||
await process_frame
|
await process_frame
|
||||||
var equipment_sections: Array[String] = []
|
var equipment_sections: Array[String] = []
|
||||||
|
|
@ -645,17 +783,21 @@ func _test_fishing_shop_sale_ui(
|
||||||
if child is Label:
|
if child is Label:
|
||||||
equipment_sections.append((child as Label).text)
|
equipment_sections.append((child as Label).text)
|
||||||
assert(equipment_sections == ["rods", "equipment"])
|
assert(equipment_sections == ["rods", "equipment"])
|
||||||
var supplies_list := shop.get_node("%SuppliesList") as VBoxContainer
|
|
||||||
var rod_carousel := supplies_list.get_node("RodCarousel") as HBoxContainer
|
var rod_carousel := supplies_list.get_node("RodCarousel") as HBoxContainer
|
||||||
assert(rod_carousel != null)
|
assert(rod_carousel != null)
|
||||||
var rod_cards := rod_carousel.get_node("RodScroll/RodCards") as HBoxContainer
|
var rod_cards := rod_carousel.get_node("RodScroll/RodCards") as HBoxContainer
|
||||||
assert(rod_cards != null and rod_cards.get_child_count() == 1)
|
assert(rod_cards != null and rod_cards.get_child_count() == 1)
|
||||||
|
assert(
|
||||||
|
rod_cards.get_theme_constant("separation")
|
||||||
|
== FishingShop.SHOP_SLOT_SEPARATION
|
||||||
|
)
|
||||||
var basic_rod_button := rod_cards.find_child(
|
var basic_rod_button := rod_cards.find_child(
|
||||||
"Rod_basic_fishing_rod", true, false
|
"Rod_basic_fishing_rod", true, false
|
||||||
) as Button
|
) as Button
|
||||||
assert(basic_rod_button != null)
|
assert(basic_rod_button != null)
|
||||||
assert(basic_rod_button.custom_minimum_size == Vector2(144.0, 144.0))
|
assert(basic_rod_button.custom_minimum_size == Vector2(144.0, 144.0))
|
||||||
assert(basic_rod_button.icon != null and basic_rod_button.disabled)
|
assert(basic_rod_button.icon != null and basic_rod_button.disabled)
|
||||||
|
assert(basic_rod_button.get_node_or_null("UnlockStateIcon") == null)
|
||||||
assert(rod_cards.find_child("Rod_aurora_rod", true, false) == null)
|
assert(rod_cards.find_child("Rod_aurora_rod", true, false) == null)
|
||||||
await _activate_pointer_control(art_supplies_tab, ui_viewport)
|
await _activate_pointer_control(art_supplies_tab, ui_viewport)
|
||||||
await process_frame
|
await process_frame
|
||||||
|
|
@ -681,6 +823,42 @@ func _test_fishing_shop_sale_ui(
|
||||||
marker_material.get_shader_parameter("marker_color")
|
marker_material.get_shader_parameter("marker_color")
|
||||||
== SurfaceDrawingPalette.get_color(color_id)
|
== SurfaceDrawingPalette.get_color(color_id)
|
||||||
)
|
)
|
||||||
|
var expected_art_upgrade_icons: Dictionary[StringName, String] = {
|
||||||
|
&"brush_2x": "art_kit_marker_tip_thin.png",
|
||||||
|
&"brush_3x": "art_kit_marker_tip_mid.png",
|
||||||
|
&"brush_4x": "art_kit_marker_tip_thick.png",
|
||||||
|
&"grid_32x": "art_kit_grid_medium_light.png",
|
||||||
|
&"grid_64x": "art_kit_grid_large_light.png",
|
||||||
|
&"grid_128x": "art_kit_grid_xl_light.png",
|
||||||
|
}
|
||||||
|
var found_art_upgrade_icons: int = 0
|
||||||
|
for upgrade_node: Node in shop.find_children("*", "Button", true, false):
|
||||||
|
var upgrade_button := upgrade_node as Button
|
||||||
|
if not upgrade_button.has_meta(&"art_product_id"):
|
||||||
|
continue
|
||||||
|
var product_id := StringName(
|
||||||
|
str(upgrade_button.get_meta(&"art_product_id"))
|
||||||
|
)
|
||||||
|
if not expected_art_upgrade_icons.has(product_id):
|
||||||
|
continue
|
||||||
|
found_art_upgrade_icons += 1
|
||||||
|
assert(upgrade_button.text.is_empty())
|
||||||
|
assert(upgrade_button.icon != null)
|
||||||
|
var product_unlocked: bool = (
|
||||||
|
player.art_unlocks.owns_product(product_id)
|
||||||
|
)
|
||||||
|
var state_icon := upgrade_button.get_node_or_null(
|
||||||
|
"UnlockStateIcon"
|
||||||
|
) as TextureRect
|
||||||
|
assert((state_icon == null) == product_unlocked)
|
||||||
|
if state_icon != null:
|
||||||
|
assert(state_icon.texture.resource_path.ends_with("/lock_light.png"))
|
||||||
|
assert(
|
||||||
|
upgrade_button.icon.resource_path.ends_with(
|
||||||
|
expected_art_upgrade_icons[product_id]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert(found_art_upgrade_icons == expected_art_upgrade_icons.size())
|
||||||
var price_bubbles := shop.find_children(
|
var price_bubbles := shop.find_children(
|
||||||
"PriceBubble", "PanelContainer", true, false
|
"PriceBubble", "PanelContainer", true, false
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,25 @@ func _initialize() -> void:
|
||||||
var shop := FishingShopType.new()
|
var shop := FishingShopType.new()
|
||||||
assert(shop.call("_unlock_status", false) == "locked")
|
assert(shop.call("_unlock_status", false) == "locked")
|
||||||
assert(shop.call("_unlock_status", true) == "unlocked")
|
assert(shop.call("_unlock_status", true) == "unlocked")
|
||||||
|
var locked_button := Button.new()
|
||||||
|
shop.call(
|
||||||
|
"_add_unlock_state_icon", locked_button, false, Vector2(72.0, 72.0)
|
||||||
|
)
|
||||||
|
var locked_icon := locked_button.get_node(
|
||||||
|
"UnlockStateIcon"
|
||||||
|
) as TextureRect
|
||||||
|
assert(locked_icon.texture.resource_path.ends_with("/lock_light.png"))
|
||||||
|
assert(locked_icon.size == Vector2(48.0, 48.0))
|
||||||
|
assert(locked_icon.position == Vector2(12.0, 6.0))
|
||||||
|
assert(is_equal_approx(locked_icon.modulate.a, 0.75))
|
||||||
|
assert(not bool(locked_icon.get_meta(&"unlocked")))
|
||||||
|
locked_button.free()
|
||||||
|
var unlocked_button := Button.new()
|
||||||
|
shop.call(
|
||||||
|
"_add_unlock_state_icon", unlocked_button, true, Vector2(72.0, 72.0)
|
||||||
|
)
|
||||||
|
assert(unlocked_button.get_node_or_null("UnlockStateIcon") == null)
|
||||||
|
unlocked_button.free()
|
||||||
shop.free()
|
shop.free()
|
||||||
|
|
||||||
var magnet: ItemDataType = ItemCatalogResource.get_available_item_by_id(
|
var magnet: ItemDataType = ItemCatalogResource.get_available_item_by_id(
|
||||||
|
|
@ -30,6 +49,12 @@ func _initialize() -> void:
|
||||||
assert(FishingShopStockType.get_price(&"magnet") == 250)
|
assert(FishingShopStockType.get_price(&"magnet") == 250)
|
||||||
assert(FishingShopStockType.get_stock_item_ids().has(&"magnet"))
|
assert(FishingShopStockType.get_stock_item_ids().has(&"magnet"))
|
||||||
assert(FishingShopStockType.is_permanent_unlock(&"magnet", magnet))
|
assert(FishingShopStockType.is_permanent_unlock(&"magnet", magnet))
|
||||||
|
var crab_net: ItemDataType = ItemCatalogResource.get_available_item_by_id(
|
||||||
|
&"crab_net"
|
||||||
|
)
|
||||||
|
assert(crab_net != null)
|
||||||
|
assert(crab_net.icon != null)
|
||||||
|
assert(crab_net.icon.resource_path.ends_with("/equipment/temp_net.png"))
|
||||||
|
|
||||||
var wallet := PlayerWalletType.new()
|
var wallet := PlayerWalletType.new()
|
||||||
wallet.current_balance = 250
|
wallet.current_balance = 250
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ func _run() -> void:
|
||||||
))
|
))
|
||||||
_validate_shared_inventory_geometry(player_menu)
|
_validate_shared_inventory_geometry(player_menu)
|
||||||
_validate_inventory_layering(player_menu)
|
_validate_inventory_layering(player_menu)
|
||||||
|
_validate_tackle_to_items_transition(player_menu)
|
||||||
_validate_cooler_notepad_typography(player_menu)
|
_validate_cooler_notepad_typography(player_menu)
|
||||||
_validate_resolution_matrix()
|
_validate_resolution_matrix()
|
||||||
await _capture_inventory_pages(player_menu)
|
await _capture_inventory_pages(player_menu)
|
||||||
|
|
@ -101,6 +102,15 @@ func _apply_stage_layout(stage: Control, display_size: Vector2) -> void:
|
||||||
|
|
||||||
|
|
||||||
func _validate_shared_inventory_geometry(player_menu: PlayerMenu) -> void:
|
func _validate_shared_inventory_geometry(player_menu: PlayerMenu) -> void:
|
||||||
|
for node_name: StringName in [
|
||||||
|
&"CoolerOuterWall",
|
||||||
|
&"BagOuterWall",
|
||||||
|
&"TackleMainPanel",
|
||||||
|
]:
|
||||||
|
var main_panel := player_menu.get_node("%%%s" % node_name) as Control
|
||||||
|
assert(main_panel != null)
|
||||||
|
assert(main_panel.position.is_equal_approx(INVENTORY_PANEL_RECT.position))
|
||||||
|
assert(main_panel.size.is_equal_approx(INVENTORY_PANEL_RECT.size))
|
||||||
for node_name: StringName in [
|
for node_name: StringName in [
|
||||||
&"DetailConstellation",
|
&"DetailConstellation",
|
||||||
&"BagDetailConstellation",
|
&"BagDetailConstellation",
|
||||||
|
|
@ -113,17 +123,24 @@ func _validate_shared_inventory_geometry(player_menu: PlayerMenu) -> void:
|
||||||
|
|
||||||
|
|
||||||
func _validate_inventory_layering(player_menu: PlayerMenu) -> void:
|
func _validate_inventory_layering(player_menu: PlayerMenu) -> void:
|
||||||
var bag_filters := player_menu.get_node("%BagFilterTabs") as Control
|
|
||||||
var bag_panel := player_menu.get_node("%BagOuterWall") as Control
|
var bag_panel := player_menu.get_node("%BagOuterWall") as Control
|
||||||
var sale_confirmation := player_menu.get_node("%SaleConfirmation") as Control
|
var sale_confirmation := player_menu.get_node("%SaleConfirmation") as Control
|
||||||
var cooler_panel := player_menu.get_node("%CoolerOuterWall") as Control
|
var cooler_panel := player_menu.get_node("%CoolerOuterWall") as Control
|
||||||
var tackle_panel := player_menu.get_node("%TackleMainPanel") as Control
|
var tackle_panel := player_menu.get_node("%TackleMainPanel") as Control
|
||||||
assert(bag_filters != null)
|
var inventory_tabs := player_menu.get_node("%InventorySubTabs") as Control
|
||||||
|
var items_tab := player_menu.get_node("%ItemsSubTab") as Button
|
||||||
|
var bait_list := player_menu.get_node("%BaitItemList") as GridContainer
|
||||||
|
var lure_list := player_menu.get_node("%LureItemList") as GridContainer
|
||||||
assert(bag_panel != null)
|
assert(bag_panel != null)
|
||||||
assert(sale_confirmation != null)
|
assert(sale_confirmation != null)
|
||||||
assert(cooler_panel != null)
|
assert(cooler_panel != null)
|
||||||
assert(tackle_panel != null)
|
assert(tackle_panel != null)
|
||||||
assert(bag_filters.z_index > bag_panel.z_index)
|
assert(inventory_tabs != null)
|
||||||
|
assert(items_tab != null and items_tab.text == "Items")
|
||||||
|
assert(bait_list != null and bait_list.columns == 3)
|
||||||
|
assert(lure_list != null and lure_list.columns == 3)
|
||||||
|
assert(bait_list.get_theme_constant("h_separation") == 28)
|
||||||
|
assert(lure_list.get_theme_constant("h_separation") == 28)
|
||||||
assert(sale_confirmation.z_index > cooler_panel.z_index)
|
assert(sale_confirmation.z_index > cooler_panel.z_index)
|
||||||
assert(sale_confirmation.z_index > tackle_panel.z_index)
|
assert(sale_confirmation.z_index > tackle_panel.z_index)
|
||||||
# The contextual Hotbar uses z=90 while the Player Menu is open.
|
# The contextual Hotbar uses z=90 while the Player Menu is open.
|
||||||
|
|
@ -141,6 +158,28 @@ func _validate_inventory_layering(player_menu: PlayerMenu) -> void:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _validate_tackle_to_items_transition(player_menu: PlayerMenu) -> void:
|
||||||
|
var empty_state := player_menu.get_node("%BagEmptyState") as Label
|
||||||
|
player_menu.set("_bag_view", PlayerMenu.BagView.EQUIPMENT)
|
||||||
|
player_menu.call("_refresh_bag")
|
||||||
|
assert(empty_state.text == "No equipment in your Bag.")
|
||||||
|
player_menu.call(
|
||||||
|
"_show_section_immediate", PlayerMenu.Section.TACKLE_BOX
|
||||||
|
)
|
||||||
|
player_menu.call("_show_bag_view", PlayerMenu.BagView.CONSUMABLES)
|
||||||
|
assert(
|
||||||
|
player_menu.get("_current_section") == PlayerMenu.Section.BAG
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
player_menu.get("_bag_view") == PlayerMenu.BagView.CONSUMABLES
|
||||||
|
)
|
||||||
|
assert(empty_state.text == "No items in your Bag.")
|
||||||
|
player_menu.call("_cancel_page_tween")
|
||||||
|
player_menu.call(
|
||||||
|
"_show_section_immediate", PlayerMenu.Section.COOLER
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _validate_cooler_notepad_typography(player_menu: PlayerMenu) -> void:
|
func _validate_cooler_notepad_typography(player_menu: PlayerMenu) -> void:
|
||||||
var sort_choice := player_menu.get_node("%CoolerSortOption") as Control
|
var sort_choice := player_menu.get_node("%CoolerSortOption") as Control
|
||||||
var sort_direction := player_menu.get_node(
|
var sort_direction := player_menu.get_node(
|
||||||
|
|
@ -276,10 +315,10 @@ func _capture_inventory_pages(player_menu: PlayerMenu) -> void:
|
||||||
player_menu.visible = true
|
player_menu.visible = true
|
||||||
var sections: Array[PlayerMenu.Section] = [
|
var sections: Array[PlayerMenu.Section] = [
|
||||||
PlayerMenu.Section.COOLER,
|
PlayerMenu.Section.COOLER,
|
||||||
PlayerMenu.Section.TACKLE_BOX,
|
|
||||||
PlayerMenu.Section.BAG,
|
PlayerMenu.Section.BAG,
|
||||||
|
PlayerMenu.Section.TACKLE_BOX,
|
||||||
]
|
]
|
||||||
var suffixes: Array[String] = ["cooler", "tackle", "equipment"]
|
var suffixes: Array[String] = ["cooler", "equipment", "tackle"]
|
||||||
for index: int in sections.size():
|
for index: int in sections.size():
|
||||||
player_menu.call("_show_section_immediate", sections[index])
|
player_menu.call("_show_section_immediate", sections[index])
|
||||||
await process_frame
|
await process_frame
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,34 @@ func _validate_page() -> void:
|
||||||
page.get("_category") == LogbookCatalog.Category.FRESH_WATER
|
page.get("_category") == LogbookCatalog.Category.FRESH_WATER
|
||||||
)
|
)
|
||||||
assert((page.get("_catalog_grid") as GridContainer).columns == 4)
|
assert((page.get("_catalog_grid") as GridContainer).columns == 4)
|
||||||
|
var category_tabs: Array = page.get("_category_tabs") as Array
|
||||||
|
var category_tab_categories: Array = (
|
||||||
|
page.get("_category_tab_categories") as Array
|
||||||
|
)
|
||||||
|
assert(category_tab_categories == [
|
||||||
|
LogbookCatalog.Category.FRESH_WATER,
|
||||||
|
LogbookCatalog.Category.SALT_WATER,
|
||||||
|
LogbookCatalog.Category.SHELLFISH,
|
||||||
|
LogbookCatalog.Category.OTHER,
|
||||||
|
])
|
||||||
|
assert(category_tabs.size() == category_tab_categories.size())
|
||||||
|
for tab_node: Variant in category_tabs:
|
||||||
|
var category_tab := tab_node as Button
|
||||||
|
assert(category_tab.size == LogbookPage.LOGBOOK_TAB_SIZE)
|
||||||
|
var text_width: float = UtilityPageStyle.TuffyFont.get_string_size(
|
||||||
|
category_tab.text,
|
||||||
|
HORIZONTAL_ALIGNMENT_LEFT,
|
||||||
|
-1.0,
|
||||||
|
OrganizerTab.FONT_SIZE,
|
||||||
|
).x
|
||||||
|
assert(
|
||||||
|
text_width + LogbookPage.LOGBOOK_TAB_TEXT_SIDE_INSET * 2.0
|
||||||
|
<= category_tab.size.x
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
(category_tabs[0] as Button).get_parent().position.x
|
||||||
|
== LogbookPage.LOGBOOK_TAB_LEFT_INSET
|
||||||
|
)
|
||||||
var initial_detail_body := page.get("_detail_body") as VBoxContainer
|
var initial_detail_body := page.get("_detail_body") as VBoxContainer
|
||||||
assert(not initial_detail_body.get_parent() is ScrollContainer)
|
assert(not initial_detail_body.get_parent() is ScrollContainer)
|
||||||
assert(
|
assert(
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,9 @@ func _run() -> void:
|
||||||
var hotbar := game_ui.get_node(
|
var hotbar := game_ui.get_node(
|
||||||
"UIRoot/CanonicalStage/Hotbar"
|
"UIRoot/CanonicalStage/Hotbar"
|
||||||
) as HotbarUI
|
) as HotbarUI
|
||||||
|
assert(game_ui.get_node_or_null(
|
||||||
|
"UIRoot/CanonicalStage/GameplayTransientHUD/EffectStatus"
|
||||||
|
) == null)
|
||||||
var screen_fade := game_ui.get_node("UIRoot/ScreenFade") as Control
|
var screen_fade := game_ui.get_node("UIRoot/ScreenFade") as Control
|
||||||
var title_screen := game_ui.get_node("UIRoot/TitleScreen") as TitleScreen
|
var title_screen := game_ui.get_node("UIRoot/TitleScreen") as TitleScreen
|
||||||
var title_content_stage := title_screen.get_node(
|
var title_content_stage := title_screen.get_node(
|
||||||
|
|
@ -154,9 +157,13 @@ func _run() -> void:
|
||||||
assert(presenter.size.is_equal_approx(Vector2(expected_viewport_size)))
|
assert(presenter.size.is_equal_approx(Vector2(expected_viewport_size)))
|
||||||
var chat_panel := chat_ui.get_node("ChatPanel") as Control
|
var chat_panel := chat_ui.get_node("ChatPanel") as Control
|
||||||
var clock_panel := chat_ui.get_node("WorldClockPanel") as Control
|
var clock_panel := chat_ui.get_node("WorldClockPanel") as Control
|
||||||
|
var status_effect_column := chat_ui.get_node(
|
||||||
|
"StatusEffectColumn"
|
||||||
|
) as Control
|
||||||
var weather_icon := chat_ui.get_node("WorldWeatherIcon") as Control
|
var weather_icon := chat_ui.get_node("WorldWeatherIcon") as Control
|
||||||
assert(chat_panel != null)
|
assert(chat_panel != null)
|
||||||
assert(clock_panel != null)
|
assert(clock_panel != null)
|
||||||
|
assert(status_effect_column != null)
|
||||||
assert(weather_icon != null)
|
assert(weather_icon != null)
|
||||||
assert(chat_panel.visible)
|
assert(chat_panel.visible)
|
||||||
assert(is_equal_approx(chat_panel.position.x, 0.0))
|
assert(is_equal_approx(chat_panel.position.x, 0.0))
|
||||||
|
|
@ -168,6 +175,17 @@ func _run() -> void:
|
||||||
ChatUI.CLOCK_EDGE_MARGIN,
|
ChatUI.CLOCK_EDGE_MARGIN,
|
||||||
ChatUI.CLOCK_EDGE_MARGIN,
|
ChatUI.CLOCK_EDGE_MARGIN,
|
||||||
)))
|
)))
|
||||||
|
assert(is_equal_approx(
|
||||||
|
status_effect_column.position.x,
|
||||||
|
clock_panel.position.x
|
||||||
|
+ (ChatUI.CLOCK_SIZE.x - ChatUI.STATUS_EFFECT_ICON_SIZE.x) * 0.5,
|
||||||
|
))
|
||||||
|
assert(is_equal_approx(
|
||||||
|
status_effect_column.position.y,
|
||||||
|
clock_panel.position.y
|
||||||
|
+ ChatUI.CLOCK_SIZE.y
|
||||||
|
+ ChatUI.STATUS_EFFECT_TOP_GAP,
|
||||||
|
))
|
||||||
assert(is_equal_approx(
|
assert(is_equal_approx(
|
||||||
weather_icon.position.y, ChatUI.CLOCK_EDGE_MARGIN
|
weather_icon.position.y, ChatUI.CLOCK_EDGE_MARGIN
|
||||||
))
|
))
|
||||||
|
|
|
||||||
128
ui/chat_ui.gd
128
ui/chat_ui.gd
|
|
@ -36,10 +36,12 @@ const CLOCK_FONT_SIZE: int = 27
|
||||||
const CLOCK_EDGE_MARGIN: float = 10.0
|
const CLOCK_EDGE_MARGIN: float = 10.0
|
||||||
const WEATHER_ICON_SIZE := Vector2(51.0, 51.0)
|
const WEATHER_ICON_SIZE := Vector2(51.0, 51.0)
|
||||||
const WEATHER_ICON_GAP: float = 6.0
|
const WEATHER_ICON_GAP: float = 6.0
|
||||||
const FISH_FINDER_EFFECT_ICON_SIZE := Vector2(18.0, 18.0)
|
const STATUS_EFFECT_ICON_SIZE := Vector2(40.0, 40.0)
|
||||||
const FISH_FINDER_EFFECT_ICON: Texture2D = preload(
|
const STATUS_EFFECT_TOP_GAP: float = 6.0
|
||||||
"res://items/icons/consumables/64_consumable_fishfinder.png"
|
const STATUS_EFFECT_ICON_GAP: int = 4
|
||||||
)
|
const STATUS_EFFECT_WARNING_RATIO: float = 0.10
|
||||||
|
const STATUS_EFFECT_PULSE_PERIOD: float = 1.0
|
||||||
|
const STATUS_EFFECT_PULSE_MIN_ALPHA: float = 0.25
|
||||||
const CHAT_SHOW_ICON: Texture2D = preload(
|
const CHAT_SHOW_ICON: Texture2D = preload(
|
||||||
"res://ui/icons/pictograms/arrow_light_right_more.png"
|
"res://ui/icons/pictograms/arrow_light_right_more.png"
|
||||||
)
|
)
|
||||||
|
|
@ -57,6 +59,8 @@ const WorldWeatherServiceType = preload(
|
||||||
"res://world/world_weather_service.gd"
|
"res://world/world_weather_service.gd"
|
||||||
)
|
)
|
||||||
const WeatherIconType = preload("res://ui/weather_icon.gd")
|
const WeatherIconType = preload("res://ui/weather_icon.gd")
|
||||||
|
const ItemCatalogType = preload("res://items/item_catalog.gd")
|
||||||
|
const ItemDataType = preload("res://items/item_data.gd")
|
||||||
|
|
||||||
const TypewriterRevealType = preload("res://ui/typewriter_reveal.gd")
|
const TypewriterRevealType = preload("res://ui/typewriter_reveal.gd")
|
||||||
const AnimaleseVoiceType = preload("res://ui/animalese_voice.gd")
|
const AnimaleseVoiceType = preload("res://ui/animalese_voice.gd")
|
||||||
|
|
@ -92,7 +96,8 @@ var _animalese_voice: AnimaleseVoiceType
|
||||||
var _clock_panel: PanelContainer
|
var _clock_panel: PanelContainer
|
||||||
var _clock_label: Label
|
var _clock_label: Label
|
||||||
var _weather_icon: WeatherIconType
|
var _weather_icon: WeatherIconType
|
||||||
var _fish_finder_effect_icon: TextureRect
|
var _status_effect_column: VBoxContainer
|
||||||
|
var _status_effect_icons: Dictionary[StringName, TextureRect] = {}
|
||||||
var _speech: Dictionary[int, Dictionary] = {}
|
var _speech: Dictionary[int, Dictionary] = {}
|
||||||
var _draft_save_timer: Timer
|
var _draft_save_timer: Timer
|
||||||
var _opacity_tween: Tween
|
var _opacity_tween: Tween
|
||||||
|
|
@ -117,6 +122,7 @@ var _mobile_mode: bool = false
|
||||||
var _world_time: WorldTimeServiceType
|
var _world_time: WorldTimeServiceType
|
||||||
var _world_weather: WorldWeatherServiceType
|
var _world_weather: WorldWeatherServiceType
|
||||||
var _item_effects: PlayerItemEffects
|
var _item_effects: PlayerItemEffects
|
||||||
|
var _item_catalog: ItemCatalogType
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
|
@ -137,6 +143,7 @@ func setup(
|
||||||
world_time: WorldTimeServiceType,
|
world_time: WorldTimeServiceType,
|
||||||
world_weather: WorldWeatherServiceType,
|
world_weather: WorldWeatherServiceType,
|
||||||
item_effects: PlayerItemEffects,
|
item_effects: PlayerItemEffects,
|
||||||
|
item_catalog: ItemCatalogType,
|
||||||
) -> void:
|
) -> void:
|
||||||
_service = service
|
_service = service
|
||||||
_session = session
|
_session = session
|
||||||
|
|
@ -147,6 +154,8 @@ func setup(
|
||||||
_world_time = world_time
|
_world_time = world_time
|
||||||
_world_weather = world_weather
|
_world_weather = world_weather
|
||||||
_item_effects = item_effects
|
_item_effects = item_effects
|
||||||
|
_item_catalog = item_catalog
|
||||||
|
_rebuild_status_effect_icons()
|
||||||
if (
|
if (
|
||||||
_fishing_spot != null
|
_fishing_spot != null
|
||||||
and not _fishing_spot.local_speech_requested.is_connected(
|
and not _fishing_spot.local_speech_requested.is_connected(
|
||||||
|
|
@ -195,6 +204,77 @@ func setup(
|
||||||
_refresh_history()
|
_refresh_history()
|
||||||
|
|
||||||
|
|
||||||
|
func _rebuild_status_effect_icons() -> void:
|
||||||
|
for child: Node in _status_effect_column.get_children():
|
||||||
|
_status_effect_column.remove_child(child)
|
||||||
|
child.queue_free()
|
||||||
|
_status_effect_icons.clear()
|
||||||
|
if _item_effects == null or _item_catalog == null:
|
||||||
|
_status_effect_column.hide()
|
||||||
|
return
|
||||||
|
for item_id: StringName in _item_effects.get_registered_effect_ids():
|
||||||
|
var item: ItemDataType = _item_catalog.get_item_by_id(item_id)
|
||||||
|
if item == null or item.icon == null:
|
||||||
|
continue
|
||||||
|
var icon := TextureRect.new()
|
||||||
|
icon.name = "StatusEffect_%s" % String(item_id)
|
||||||
|
icon.texture = item.icon
|
||||||
|
icon.custom_minimum_size = STATUS_EFFECT_ICON_SIZE
|
||||||
|
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
|
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||||
|
icon.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||||
|
icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
icon.hide()
|
||||||
|
_status_effect_column.add_child(icon)
|
||||||
|
_status_effect_icons[item_id] = icon
|
||||||
|
var icon_count: int = _status_effect_icons.size()
|
||||||
|
_status_effect_column.size = Vector2(
|
||||||
|
STATUS_EFFECT_ICON_SIZE.x,
|
||||||
|
STATUS_EFFECT_ICON_SIZE.y * icon_count
|
||||||
|
+ STATUS_EFFECT_ICON_GAP * maxi(icon_count - 1, 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _update_status_effect_icons() -> void:
|
||||||
|
if _status_effect_column == null:
|
||||||
|
return
|
||||||
|
var has_active_effect: bool = false
|
||||||
|
for item_id: StringName in _status_effect_icons:
|
||||||
|
var icon: TextureRect = _status_effect_icons[item_id]
|
||||||
|
var remaining: float = (
|
||||||
|
_item_effects.get_remaining(item_id)
|
||||||
|
if _item_effects != null
|
||||||
|
else 0.0
|
||||||
|
)
|
||||||
|
icon.visible = remaining > 0.0
|
||||||
|
icon.modulate = Color.WHITE
|
||||||
|
if remaining <= 0.0:
|
||||||
|
continue
|
||||||
|
has_active_effect = true
|
||||||
|
var warning_duration: float = (
|
||||||
|
_item_effects.get_effect_duration(item_id)
|
||||||
|
* STATUS_EFFECT_WARNING_RATIO
|
||||||
|
)
|
||||||
|
if warning_duration <= 0.0 or remaining > warning_duration:
|
||||||
|
continue
|
||||||
|
var warning_elapsed: float = maxf(
|
||||||
|
warning_duration - remaining, 0.0
|
||||||
|
)
|
||||||
|
var pulse_phase: float = (
|
||||||
|
fmod(warning_elapsed, STATUS_EFFECT_PULSE_PERIOD)
|
||||||
|
/ STATUS_EFFECT_PULSE_PERIOD
|
||||||
|
)
|
||||||
|
var pulse_weight: float = 0.5 + 0.5 * cos(pulse_phase * TAU)
|
||||||
|
icon.modulate.a = lerpf(
|
||||||
|
STATUS_EFFECT_PULSE_MIN_ALPHA, 1.0, pulse_weight
|
||||||
|
)
|
||||||
|
_status_effect_column.visible = (
|
||||||
|
has_active_effect
|
||||||
|
and _available
|
||||||
|
and (not _hud_hidden or _opened)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func open_chat() -> void:
|
func open_chat() -> void:
|
||||||
if (
|
if (
|
||||||
_opened or not _available or _service == null
|
_opened or not _available or _service == null
|
||||||
|
|
@ -359,6 +439,7 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
func _process(_delta: float) -> void:
|
||||||
_refresh_visibility()
|
_refresh_visibility()
|
||||||
|
_update_status_effect_icons()
|
||||||
_update_panel_opacity()
|
_update_panel_opacity()
|
||||||
_update_speech()
|
_update_speech()
|
||||||
|
|
||||||
|
|
@ -391,15 +472,13 @@ func _build_ui() -> void:
|
||||||
"font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY
|
"font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY
|
||||||
)
|
)
|
||||||
_clock_panel.add_child(_clock_label)
|
_clock_panel.add_child(_clock_label)
|
||||||
_fish_finder_effect_icon = TextureRect.new()
|
_status_effect_column = VBoxContainer.new()
|
||||||
_fish_finder_effect_icon.name = "FishFinderEffectIcon"
|
_status_effect_column.name = "StatusEffectColumn"
|
||||||
_fish_finder_effect_icon.texture = FISH_FINDER_EFFECT_ICON
|
_status_effect_column.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
_fish_finder_effect_icon.size = FISH_FINDER_EFFECT_ICON_SIZE
|
_status_effect_column.add_theme_constant_override(
|
||||||
_fish_finder_effect_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
"separation", STATUS_EFFECT_ICON_GAP
|
||||||
_fish_finder_effect_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
)
|
||||||
_fish_finder_effect_icon.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
add_child(_status_effect_column)
|
||||||
_fish_finder_effect_icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
||||||
add_child(_fish_finder_effect_icon)
|
|
||||||
_weather_icon = WeatherIconType.new()
|
_weather_icon = WeatherIconType.new()
|
||||||
_weather_icon.name = "WorldWeatherIcon"
|
_weather_icon.name = "WorldWeatherIcon"
|
||||||
_weather_icon.size = WEATHER_ICON_SIZE
|
_weather_icon.size = WEATHER_ICON_SIZE
|
||||||
|
|
@ -968,7 +1047,7 @@ func _refresh_visibility() -> void:
|
||||||
_panel.hide()
|
_panel.hide()
|
||||||
_clock_panel.hide()
|
_clock_panel.hide()
|
||||||
_weather_icon.hide()
|
_weather_icon.hide()
|
||||||
_fish_finder_effect_icon.hide()
|
_status_effect_column.hide()
|
||||||
_collapse_button.hide()
|
_collapse_button.hide()
|
||||||
_height_button.hide()
|
_height_button.hide()
|
||||||
_unread_indicator.hide()
|
_unread_indicator.hide()
|
||||||
|
|
@ -978,7 +1057,7 @@ func _refresh_visibility() -> void:
|
||||||
_panel.hide()
|
_panel.hide()
|
||||||
_clock_panel.hide()
|
_clock_panel.hide()
|
||||||
_weather_icon.hide()
|
_weather_icon.hide()
|
||||||
_fish_finder_effect_icon.hide()
|
_status_effect_column.hide()
|
||||||
_collapse_button.hide()
|
_collapse_button.hide()
|
||||||
_height_button.hide()
|
_height_button.hide()
|
||||||
_unread_indicator.hide()
|
_unread_indicator.hide()
|
||||||
|
|
@ -986,10 +1065,6 @@ func _refresh_visibility() -> void:
|
||||||
return
|
return
|
||||||
_clock_panel.show()
|
_clock_panel.show()
|
||||||
_weather_icon.show()
|
_weather_icon.show()
|
||||||
_fish_finder_effect_icon.visible = (
|
|
||||||
_item_effects != null
|
|
||||||
and _item_effects.is_active(PlayerItemEffects.FISH_FINDER_ID)
|
|
||||||
)
|
|
||||||
_collapse_button.show()
|
_collapse_button.show()
|
||||||
var collapsed := _presentation_state == PresentationState.COLLAPSED
|
var collapsed := _presentation_state == PresentationState.COLLAPSED
|
||||||
_panel.show()
|
_panel.show()
|
||||||
|
|
@ -1365,10 +1440,10 @@ func _layout_presentation(animate: bool) -> void:
|
||||||
clock_x,
|
clock_x,
|
||||||
CLOCK_EDGE_MARGIN,
|
CLOCK_EDGE_MARGIN,
|
||||||
)
|
)
|
||||||
var fish_finder_effect_position := Vector2(
|
var status_effect_column_position := Vector2(
|
||||||
clock_position.x
|
clock_position.x
|
||||||
+ (CLOCK_SIZE.x - FISH_FINDER_EFFECT_ICON_SIZE.x) * 0.5,
|
+ (CLOCK_SIZE.x - STATUS_EFFECT_ICON_SIZE.x) * 0.5,
|
||||||
clock_position.y + CLOCK_SIZE.y + 2.0,
|
clock_position.y + CLOCK_SIZE.y + STATUS_EFFECT_TOP_GAP,
|
||||||
)
|
)
|
||||||
var weather_icon_x: float = (
|
var weather_icon_x: float = (
|
||||||
clock_position.x - WEATHER_ICON_GAP - WEATHER_ICON_SIZE.x
|
clock_position.x - WEATHER_ICON_GAP - WEATHER_ICON_SIZE.x
|
||||||
|
|
@ -1393,8 +1468,7 @@ func _layout_presentation(animate: bool) -> void:
|
||||||
_panel.size = target_size
|
_panel.size = target_size
|
||||||
_clock_panel.position = clock_position
|
_clock_panel.position = clock_position
|
||||||
_clock_panel.size = CLOCK_SIZE
|
_clock_panel.size = CLOCK_SIZE
|
||||||
_fish_finder_effect_icon.position = fish_finder_effect_position
|
_status_effect_column.position = status_effect_column_position
|
||||||
_fish_finder_effect_icon.size = FISH_FINDER_EFFECT_ICON_SIZE
|
|
||||||
_weather_icon.position = weather_icon_position
|
_weather_icon.position = weather_icon_position
|
||||||
_weather_icon.size = WEATHER_ICON_SIZE
|
_weather_icon.size = WEATHER_ICON_SIZE
|
||||||
_collapse_button.position = collapse_position
|
_collapse_button.position = collapse_position
|
||||||
|
|
@ -1416,9 +1490,9 @@ func _layout_presentation(animate: bool) -> void:
|
||||||
UIMotion.CHAT_RESIZE_DURATION,
|
UIMotion.CHAT_RESIZE_DURATION,
|
||||||
).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
||||||
_height_tween.tween_property(
|
_height_tween.tween_property(
|
||||||
_fish_finder_effect_icon,
|
_status_effect_column,
|
||||||
"position",
|
"position",
|
||||||
fish_finder_effect_position,
|
status_effect_column_position,
|
||||||
UIMotion.CHAT_RESIZE_DURATION,
|
UIMotion.CHAT_RESIZE_DURATION,
|
||||||
).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
||||||
_height_tween.tween_property(
|
_height_tween.tween_property(
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,32 @@ const ART_KIT_MARKER_ICON: Texture2D = preload(
|
||||||
const ART_KIT_MARKER_SHADER: Shader = preload(
|
const ART_KIT_MARKER_SHADER: Shader = preload(
|
||||||
"res://ui/art_kit_marker_icon.gdshader"
|
"res://ui/art_kit_marker_icon.gdshader"
|
||||||
)
|
)
|
||||||
|
const ART_UPGRADE_ICONS: Dictionary[StringName, Texture2D] = {
|
||||||
|
&"brush_2x": preload(
|
||||||
|
"res://items/icons/art/art_kit_marker_tip_thin.png"
|
||||||
|
),
|
||||||
|
&"brush_3x": preload(
|
||||||
|
"res://items/icons/art/art_kit_marker_tip_mid.png"
|
||||||
|
),
|
||||||
|
&"brush_4x": preload(
|
||||||
|
"res://items/icons/art/art_kit_marker_tip_thick.png"
|
||||||
|
),
|
||||||
|
&"grid_32x": preload(
|
||||||
|
"res://items/icons/art/art_kit_grid_medium_light.png"
|
||||||
|
),
|
||||||
|
&"grid_64x": preload(
|
||||||
|
"res://items/icons/art/art_kit_grid_large_light.png"
|
||||||
|
),
|
||||||
|
&"grid_128x": preload(
|
||||||
|
"res://items/icons/art/art_kit_grid_xl_light.png"
|
||||||
|
),
|
||||||
|
}
|
||||||
const CURRENCY_ICON: Texture2D = preload(
|
const CURRENCY_ICON: Texture2D = preload(
|
||||||
"res://items/icons/shop/32_currency.png"
|
"res://items/icons/shop/32_currency.png"
|
||||||
)
|
)
|
||||||
|
const LOCKED_ITEM_ICON: Texture2D = preload(
|
||||||
|
"res://ui/icons/pictograms/lock_light.png"
|
||||||
|
)
|
||||||
|
|
||||||
signal menu_visibility_changed(is_open: bool)
|
signal menu_visibility_changed(is_open: bool)
|
||||||
signal menu_exit_started
|
signal menu_exit_started
|
||||||
|
|
@ -75,7 +98,7 @@ const SHOP_SECTION_LABELS: Array[String] = [
|
||||||
const SUPPLY_ICON_GRID_COLUMNS: int = 9
|
const SUPPLY_ICON_GRID_COLUMNS: int = 9
|
||||||
const SUPPLY_ICON_TILE_SIZE := Vector2(72.0, 72.0)
|
const SUPPLY_ICON_TILE_SIZE := Vector2(72.0, 72.0)
|
||||||
const SUPPLY_ICON_HOST_SIZE := Vector2(72.0, 84.0)
|
const SUPPLY_ICON_HOST_SIZE := Vector2(72.0, 84.0)
|
||||||
const SUPPLY_ICON_GRID_SEPARATION: int = 8
|
const SHOP_SLOT_SEPARATION: int = 28
|
||||||
const SUPPLY_PRICE_COLOR := Color("c3dfe6")
|
const SUPPLY_PRICE_COLOR := Color("c3dfe6")
|
||||||
const SUPPLY_PRICE_FONT_SIZE: int = 15
|
const SUPPLY_PRICE_FONT_SIZE: int = 15
|
||||||
const SUPPLY_PRICE_Y: float = 60.0
|
const SUPPLY_PRICE_Y: float = 60.0
|
||||||
|
|
@ -83,10 +106,15 @@ const SUPPLY_PRICE_HEIGHT: float = 24.0
|
||||||
const SUPPLY_PRICE_HORIZONTAL_PADDING: float = 12.0
|
const SUPPLY_PRICE_HORIZONTAL_PADDING: float = 12.0
|
||||||
const SUPPLY_PRICE_ICON_SIZE: float = 18.0
|
const SUPPLY_PRICE_ICON_SIZE: float = 18.0
|
||||||
const SUPPLY_PRICE_ICON_GAP: float = 3.0
|
const SUPPLY_PRICE_ICON_GAP: float = 3.0
|
||||||
|
const BAIT_SUPPLY_BADGE_Y: float = (
|
||||||
|
UtilityPageStyleType.SUPPLY_BADGE_EDGE_MARGIN
|
||||||
|
)
|
||||||
|
const LOCKED_ITEM_ICON_SIZE := Vector2(48.0, 48.0)
|
||||||
|
const LOCKED_ITEM_ICON_ALPHA: float = 0.75
|
||||||
const ROD_CARD_TILE_SIZE := Vector2(144.0, 144.0)
|
const ROD_CARD_TILE_SIZE := Vector2(144.0, 144.0)
|
||||||
const ROD_CARD_HOST_SIZE := Vector2(152.0, 198.0)
|
const ROD_CARD_HOST_SIZE := Vector2(152.0, 198.0)
|
||||||
const ROD_CAROUSEL_HEIGHT: float = 206.0
|
const ROD_CAROUSEL_HEIGHT: float = 206.0
|
||||||
const ROD_CAROUSEL_SEPARATION: int = 12
|
const ROD_CAROUSEL_SEPARATION: int = SHOP_SLOT_SEPARATION
|
||||||
const ROD_CAROUSEL_STEP: float = 328.0
|
const ROD_CAROUSEL_STEP: float = 328.0
|
||||||
const ROD_PRICE_Y: float = 128.0
|
const ROD_PRICE_Y: float = 128.0
|
||||||
const ROD_PRICE_HEIGHT: float = 30.0
|
const ROD_PRICE_HEIGHT: float = 30.0
|
||||||
|
|
@ -680,6 +708,8 @@ func _refresh_supplies() -> void:
|
||||||
current_stock_grid = _add_stock_icon_grid()
|
current_stock_grid = _add_stock_icon_grid()
|
||||||
current_stock_group = stock_group
|
current_stock_group = stock_group
|
||||||
var button := Button.new()
|
var button := Button.new()
|
||||||
|
button.name = "Supply_%s" % str(item_id)
|
||||||
|
button.set_meta(&"item_id", item_id)
|
||||||
button.custom_minimum_size = Vector2(195, 54)
|
button.custom_minimum_size = Vector2(195, 54)
|
||||||
button.icon = item.icon if item.icon != null else FALLBACK_SUPPLY_ICON
|
button.icon = item.icon if item.icon != null else FALLBACK_SUPPLY_ICON
|
||||||
button.expand_icon = true
|
button.expand_icon = true
|
||||||
|
|
@ -709,6 +739,11 @@ func _refresh_supplies() -> void:
|
||||||
)
|
)
|
||||||
var item_tooltip_text: String = item.description
|
var item_tooltip_text: String = item.description
|
||||||
if bait_topoff:
|
if bait_topoff:
|
||||||
|
var unit_price: int = FishingShopStockType.get_price(item_id)
|
||||||
|
var unit_price_description: String = (
|
||||||
|
"%d fish coin%s per bait piece"
|
||||||
|
% [unit_price, "" if unit_price == 1 else "s"]
|
||||||
|
)
|
||||||
if use_icon_tile:
|
if use_icon_tile:
|
||||||
button.custom_minimum_size = SUPPLY_ICON_TILE_SIZE
|
button.custom_minimum_size = SUPPLY_ICON_TILE_SIZE
|
||||||
button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||||
|
|
@ -725,17 +760,15 @@ func _refresh_supplies() -> void:
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
item_tooltip_text = (
|
item_tooltip_text = (
|
||||||
"%s\nunlock and fill to %d/%d\n%s" % [
|
"%s\nunlock and fill\n%s\n%s" % [
|
||||||
item.display_name,
|
item.display_name,
|
||||||
item.max_stack,
|
unit_price_description,
|
||||||
item.max_stack,
|
|
||||||
item.description,
|
item.description,
|
||||||
]
|
]
|
||||||
if not bait_unlocked
|
if not bait_unlocked
|
||||||
else "%s\n%d/%d owned\n%s" % [
|
else "%s\n%s\n%s" % [
|
||||||
item.display_name,
|
item.display_name,
|
||||||
owned,
|
unit_price_description,
|
||||||
item.max_stack,
|
|
||||||
item.description,
|
item.description,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
@ -773,14 +806,32 @@ func _refresh_supplies() -> void:
|
||||||
else item_tooltip_text
|
else item_tooltip_text
|
||||||
)
|
)
|
||||||
UtilityPageStyleType.apply_ocean_button(button)
|
UtilityPageStyleType.apply_ocean_button(button)
|
||||||
|
if permanent_unlock or bait_topoff:
|
||||||
|
_add_unlock_state_icon(
|
||||||
|
button,
|
||||||
|
bait_unlocked if bait_topoff else owned > 0,
|
||||||
|
(
|
||||||
|
SUPPLY_ICON_TILE_SIZE
|
||||||
|
if use_icon_tile
|
||||||
|
else button.custom_minimum_size
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if bait_topoff and bait_unlocked:
|
||||||
|
UtilityPageStyleType.add_supply_quantity_badge(
|
||||||
|
button,
|
||||||
|
owned,
|
||||||
|
item.max_stack,
|
||||||
|
&"SupplyQuantityBadge",
|
||||||
|
BAIT_SUPPLY_BADGE_Y,
|
||||||
|
)
|
||||||
button.pressed.connect(_purchase_supply.bind(item_id))
|
button.pressed.connect(_purchase_supply.bind(item_id))
|
||||||
if current_stock_grid != null:
|
if current_stock_grid != null:
|
||||||
_add_supply_icon_tile(
|
_add_supply_icon_tile(
|
||||||
current_stock_grid,
|
current_stock_grid,
|
||||||
button,
|
button,
|
||||||
(
|
(
|
||||||
total_cost
|
maxi(total_cost, 0)
|
||||||
if bait_topoff and not bait_unlocked
|
if bait_topoff
|
||||||
else FishingShopStockType.get_price(item_id)
|
else FishingShopStockType.get_price(item_id)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -836,10 +887,10 @@ func _add_stock_icon_grid() -> GridContainer:
|
||||||
grid.columns = SUPPLY_ICON_GRID_COLUMNS
|
grid.columns = SUPPLY_ICON_GRID_COLUMNS
|
||||||
grid.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
grid.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
grid.add_theme_constant_override(
|
grid.add_theme_constant_override(
|
||||||
"h_separation", SUPPLY_ICON_GRID_SEPARATION
|
"h_separation", SHOP_SLOT_SEPARATION
|
||||||
)
|
)
|
||||||
grid.add_theme_constant_override(
|
grid.add_theme_constant_override(
|
||||||
"v_separation", SUPPLY_ICON_GRID_SEPARATION
|
"v_separation", SHOP_SLOT_SEPARATION
|
||||||
)
|
)
|
||||||
_supplies_list.add_child(grid)
|
_supplies_list.add_child(grid)
|
||||||
return grid
|
return grid
|
||||||
|
|
@ -919,6 +970,9 @@ func _add_rod_card(parent: HBoxContainer, rod: FishingRodDataType) -> void:
|
||||||
button.expand_icon = true
|
button.expand_icon = true
|
||||||
button.alignment = HORIZONTAL_ALIGNMENT_CENTER
|
button.alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
button.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
button.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||||
|
_add_unlock_state_icon(
|
||||||
|
button, owned, ROD_CARD_TILE_SIZE, ROD_PRICE_Y
|
||||||
|
)
|
||||||
button.disabled = (
|
button.disabled = (
|
||||||
owned
|
owned
|
||||||
or level_locked
|
or level_locked
|
||||||
|
|
@ -999,6 +1053,7 @@ func _add_art_kit_button(parent: GridContainer) -> void:
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
_configure_supply_icon_tile(button, item.icon)
|
_configure_supply_icon_tile(button, item.icon)
|
||||||
|
_add_unlock_state_icon(button, owned, SUPPLY_ICON_TILE_SIZE)
|
||||||
_add_supply_icon_tile(
|
_add_supply_icon_tile(
|
||||||
parent,
|
parent,
|
||||||
button,
|
button,
|
||||||
|
|
@ -1035,16 +1090,23 @@ func _add_art_upgrade_button(
|
||||||
ArtShopStockType.get_description(product_id),
|
ArtShopStockType.get_description(product_id),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
button.accessibility_name = ArtShopStockType.get_display_name(product_id)
|
||||||
|
button.set_meta(&"art_product_id", product_id)
|
||||||
var marker_color_id: StringName = (
|
var marker_color_id: StringName = (
|
||||||
PlayerArtUnlocksType.color_id_for_product(product_id)
|
PlayerArtUnlocksType.color_id_for_product(product_id)
|
||||||
)
|
)
|
||||||
if marker_color_id.is_empty():
|
if marker_color_id.is_empty():
|
||||||
_configure_supply_icon_tile(button, FALLBACK_SUPPLY_ICON)
|
var upgrade_icon: Texture2D = ART_UPGRADE_ICONS.get(
|
||||||
|
product_id,
|
||||||
|
FALLBACK_SUPPLY_ICON,
|
||||||
|
)
|
||||||
|
_configure_supply_icon_tile(button, upgrade_icon)
|
||||||
else:
|
else:
|
||||||
_configure_marker_icon_tile(
|
_configure_marker_icon_tile(
|
||||||
button,
|
button,
|
||||||
SurfaceDrawingPalette.get_color(marker_color_id),
|
SurfaceDrawingPalette.get_color(marker_color_id),
|
||||||
)
|
)
|
||||||
|
_add_unlock_state_icon(button, unlocked, SUPPLY_ICON_TILE_SIZE)
|
||||||
_add_supply_icon_tile(
|
_add_supply_icon_tile(
|
||||||
parent,
|
parent,
|
||||||
button,
|
button,
|
||||||
|
|
@ -1110,6 +1172,32 @@ func _configure_marker_icon_tile(
|
||||||
button.add_child(icon)
|
button.add_child(icon)
|
||||||
|
|
||||||
|
|
||||||
|
func _add_unlock_state_icon(
|
||||||
|
button: Button,
|
||||||
|
unlocked: bool,
|
||||||
|
tile_size: Vector2,
|
||||||
|
price_bubble_y: float = SUPPLY_PRICE_Y,
|
||||||
|
) -> void:
|
||||||
|
if unlocked:
|
||||||
|
return
|
||||||
|
var icon := TextureRect.new()
|
||||||
|
icon.name = "UnlockStateIcon"
|
||||||
|
icon.texture = LOCKED_ITEM_ICON
|
||||||
|
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
|
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||||
|
icon.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||||
|
icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
icon.z_index = 3
|
||||||
|
icon.set_meta(&"unlocked", false)
|
||||||
|
icon.size = LOCKED_ITEM_ICON_SIZE
|
||||||
|
icon.position = Vector2(
|
||||||
|
(tile_size.x - LOCKED_ITEM_ICON_SIZE.x) * 0.5,
|
||||||
|
(price_bubble_y - LOCKED_ITEM_ICON_SIZE.y) * 0.5,
|
||||||
|
)
|
||||||
|
icon.modulate.a = LOCKED_ITEM_ICON_ALPHA
|
||||||
|
button.add_child(icon)
|
||||||
|
|
||||||
|
|
||||||
func _add_supply_icon_tile(
|
func _add_supply_icon_tile(
|
||||||
parent: Container,
|
parent: Container,
|
||||||
button: Button,
|
button: Button,
|
||||||
|
|
@ -1353,18 +1441,20 @@ func _on_network_purchase_finished(
|
||||||
_request_id: String,
|
_request_id: String,
|
||||||
accepted: bool,
|
accepted: bool,
|
||||||
message: String,
|
message: String,
|
||||||
_product_id: StringName,
|
product_id: StringName,
|
||||||
_category: int,
|
category: int,
|
||||||
_quantity: int,
|
quantity: int,
|
||||||
total_cost: int,
|
total_cost: int,
|
||||||
) -> void:
|
) -> void:
|
||||||
_transaction_in_progress = false
|
_transaction_in_progress = false
|
||||||
if not visible:
|
if not visible:
|
||||||
return
|
return
|
||||||
_set_feedback(
|
_set_feedback(
|
||||||
(
|
_purchase_success_feedback(
|
||||||
"Purchase complete • %s spent."
|
product_id,
|
||||||
% CurrencyPresentation.bbcode_amount(total_cost, 18)
|
category,
|
||||||
|
quantity,
|
||||||
|
total_cost,
|
||||||
)
|
)
|
||||||
if accepted
|
if accepted
|
||||||
else message
|
else message
|
||||||
|
|
@ -1372,6 +1462,31 @@ func _on_network_purchase_finished(
|
||||||
_refresh_all()
|
_refresh_all()
|
||||||
|
|
||||||
|
|
||||||
|
func _purchase_success_feedback(
|
||||||
|
product_id: StringName,
|
||||||
|
category: int,
|
||||||
|
quantity: int,
|
||||||
|
total_cost: int,
|
||||||
|
) -> String:
|
||||||
|
if (
|
||||||
|
category == NetworkShopProtocol.ProductCategory.SUPPLY
|
||||||
|
and FishingShopStockType.is_bait_topoff(product_id)
|
||||||
|
):
|
||||||
|
var item: ItemDataType = _item_catalog.get_item_by_id(product_id)
|
||||||
|
var item_name: String = (
|
||||||
|
item.display_name if item != null else str(product_id).capitalize()
|
||||||
|
)
|
||||||
|
return "Bait Restocked\n%s spent on %d %s" % [
|
||||||
|
CurrencyPresentation.bbcode_amount(total_cost, 18),
|
||||||
|
quantity,
|
||||||
|
item_name,
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
"Purchase complete • %s spent."
|
||||||
|
% CurrencyPresentation.bbcode_amount(total_cost, 18)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _set_feedback(message: String) -> void:
|
func _set_feedback(message: String) -> void:
|
||||||
_feedback.text = "[center]%s[/center]" % message
|
_feedback.text = "[center]%s[/center]" % message
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -198,8 +198,8 @@ horizontal_scroll_mode = 0
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
theme_override_constants/h_separation = 20
|
theme_override_constants/h_separation = 28
|
||||||
theme_override_constants/v_separation = 20
|
theme_override_constants/v_separation = 28
|
||||||
columns = 3
|
columns = 3
|
||||||
|
|
||||||
[node name="ReelTile" type="Control" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid"]
|
[node name="ReelTile" type="Control" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid"]
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,6 @@ var _shop_animalese_voice: AnimaleseVoiceType
|
||||||
var _shop_npc_player_in_range: bool = false
|
var _shop_npc_player_in_range: bool = false
|
||||||
var _shop_npc_spoken_for_current_visit: bool = false
|
var _shop_npc_spoken_for_current_visit: bool = false
|
||||||
var _shop_npc_next_speech_msec: int = 0
|
var _shop_npc_next_speech_msec: int = 0
|
||||||
@onready var _effect_status: Label = %EffectStatus
|
|
||||||
@onready var _chat_ui: ChatUIType = %ChatUI
|
@onready var _chat_ui: ChatUIType = %ChatUI
|
||||||
@onready var _emote_radial_menu: EmoteRadialMenuType = %EmoteRadialMenu
|
@onready var _emote_radial_menu: EmoteRadialMenuType = %EmoteRadialMenu
|
||||||
@onready var _quick_radial_menu: QuickRadialMenuType = %QuickRadialMenu
|
@onready var _quick_radial_menu: QuickRadialMenuType = %QuickRadialMenu
|
||||||
|
|
@ -174,7 +173,6 @@ var _system_menu_open: bool = false
|
||||||
var _shop_open: bool = false
|
var _shop_open: bool = false
|
||||||
var _chat_input_open: bool = false
|
var _chat_input_open: bool = false
|
||||||
var _player_menu_hotbar_visible: bool = false
|
var _player_menu_hotbar_visible: bool = false
|
||||||
var _item_effects: PlayerItemEffectsType
|
|
||||||
var _main_shop_buyer: FishBuyerProfileType
|
var _main_shop_buyer: FishBuyerProfileType
|
||||||
var _shop_interaction: ShopInteractionType
|
var _shop_interaction: ShopInteractionType
|
||||||
var _surface_drawing: NetworkSurfaceDrawingService
|
var _surface_drawing: NetworkSurfaceDrawingService
|
||||||
|
|
@ -304,7 +302,6 @@ func setup(
|
||||||
_item_catalog = item_catalog
|
_item_catalog = item_catalog
|
||||||
_settings_manager = settings_manager
|
_settings_manager = settings_manager
|
||||||
_fishing_spot = fishing_spot
|
_fishing_spot = fishing_spot
|
||||||
_item_effects = item_effects
|
|
||||||
_experience = experience
|
_experience = experience
|
||||||
if (
|
if (
|
||||||
_player != null
|
_player != null
|
||||||
|
|
@ -339,7 +336,8 @@ func setup(
|
||||||
_experience.experience_awarded.connect(_on_experience_awarded)
|
_experience.experience_awarded.connect(_on_experience_awarded)
|
||||||
_chat_ui.setup(
|
_chat_ui.setup(
|
||||||
network_chat_service, network_session, spawn_service, player,
|
network_chat_service, network_session, spawn_service, player,
|
||||||
fishing_spot, settings_manager, world_time, world_weather, item_effects
|
fishing_spot, settings_manager, world_time, world_weather, item_effects,
|
||||||
|
item_catalog,
|
||||||
)
|
)
|
||||||
_title_settings_panel.setup_network_profile(
|
_title_settings_panel.setup_network_profile(
|
||||||
network_profile, network_session
|
network_profile, network_session
|
||||||
|
|
@ -1195,31 +1193,6 @@ func _process(delta: float) -> void:
|
||||||
_update_virtual_mouse(delta)
|
_update_virtual_mouse(delta)
|
||||||
_update_controller_menu_scroll(delta)
|
_update_controller_menu_scroll(delta)
|
||||||
_update_experience_bubble_position()
|
_update_experience_bubble_position()
|
||||||
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, floori(remaining / 60.0), int(remaining) % 60]
|
|
||||||
)
|
|
||||||
_effect_status.text = " ".join(parts)
|
|
||||||
_effect_status.visible = not parts.is_empty()
|
|
||||||
|
|
||||||
|
|
||||||
func _update_controller_menu_scroll(delta: float) -> void:
|
func _update_controller_menu_scroll(delta: float) -> void:
|
||||||
|
|
@ -1363,14 +1336,6 @@ func _apply_active_bait_indicator_style() -> void:
|
||||||
&"icon_pressed_color",
|
&"icon_pressed_color",
|
||||||
]:
|
]:
|
||||||
_active_bait_button.add_theme_color_override(state, Color.WHITE)
|
_active_bait_button.add_theme_color_override(state, Color.WHITE)
|
||||||
var badge_style := StyleBoxFlat.new()
|
|
||||||
badge_style.bg_color = Color("0b5558")
|
|
||||||
badge_style.set_corner_radius_all(12)
|
|
||||||
badge_style.anti_aliasing = false
|
|
||||||
_active_bait_quantity_badge.add_theme_stylebox_override(
|
|
||||||
"panel",
|
|
||||||
badge_style,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func _refresh_active_bait_indicator() -> void:
|
func _refresh_active_bait_indicator() -> void:
|
||||||
|
|
@ -1399,14 +1364,16 @@ func _refresh_active_bait_indicator() -> void:
|
||||||
if _bag != null
|
if _bag != null
|
||||||
else 0
|
else 0
|
||||||
)
|
)
|
||||||
_active_bait_quantity.text = str(quantity)
|
UtilityPageStyle.configure_supply_quantity_badge(
|
||||||
_active_bait_quantity.add_theme_font_size_override(
|
_active_bait_quantity_badge,
|
||||||
"font_size",
|
_active_bait_quantity,
|
||||||
13 if quantity >= 100 else 15,
|
quantity,
|
||||||
|
item.max_stack,
|
||||||
)
|
)
|
||||||
_active_bait_button.tooltip_text = "%s ×%d" % [
|
_active_bait_button.tooltip_text = "%s • %d/%d" % [
|
||||||
item.display_name,
|
item.display_name,
|
||||||
quantity,
|
quantity,
|
||||||
|
item.max_stack,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ layout_mode = 1
|
||||||
anchors_preset = 1
|
anchors_preset = 1
|
||||||
anchor_left = 1.0
|
anchor_left = 1.0
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
offset_left = -27.0
|
offset_left = -49.0
|
||||||
offset_top = 3.0
|
offset_top = 3.0
|
||||||
offset_right = -3.0
|
offset_right = -3.0
|
||||||
offset_bottom = 27.0
|
offset_bottom = 27.0
|
||||||
|
|
@ -136,14 +136,12 @@ layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
offset_left = 1.0
|
|
||||||
offset_right = 1.0
|
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
theme_override_colors/font_color = Color(0.905882, 0.960784, 0.956863, 1)
|
theme_override_colors/font_color = Color(0.905882, 0.960784, 0.956863, 1)
|
||||||
theme_override_font_sizes/font_size = 15
|
theme_override_font_sizes/font_size = 13
|
||||||
text = "0"
|
text = "0/10"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
|
@ -473,20 +471,6 @@ polygon = PackedVector2Array(-8, 0, 8, 0, 0, 10)
|
||||||
[node name="FishingShop" parent="UIRoot/CanonicalStage" instance=ExtResource("8_shop")]
|
[node name="FishingShop" parent="UIRoot/CanonicalStage" instance=ExtResource("8_shop")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
||||||
[node name="EffectStatus" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD"]
|
|
||||||
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="ChatUI" type="Control" parent="UIRoot"]
|
[node name="ChatUI" type="Control" parent="UIRoot"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,10 @@ const SCROLL_DOWN_TEXTURE: Texture2D = preload(
|
||||||
const LOGBOOK_ARTWORK_SOURCE_SIZE := Vector2(512.0, 247.0)
|
const LOGBOOK_ARTWORK_SOURCE_SIZE := Vector2(512.0, 247.0)
|
||||||
const LEFT_PAGE_CONTENT_RECT := Rect2(56.0, 26.0, 190.0, 198.0)
|
const LEFT_PAGE_CONTENT_RECT := Rect2(56.0, 26.0, 190.0, 198.0)
|
||||||
const RIGHT_PAGE_CONTENT_RECT := Rect2(267.0, 26.0, 210.0, 198.0)
|
const RIGHT_PAGE_CONTENT_RECT := Rect2(267.0, 26.0, 210.0, 198.0)
|
||||||
const LOGBOOK_TAB_LEFT_INSET: float = 122.0
|
const LOGBOOK_TAB_LEFT_INSET: float = 92.0
|
||||||
|
const LOGBOOK_TAB_SIZE := Vector2(108.0, 38.0)
|
||||||
|
const LOGBOOK_TAB_SEPARATION: float = 6.0
|
||||||
|
const LOGBOOK_TAB_TEXT_SIDE_INSET: float = 10.0
|
||||||
const PAGE_CONTENT_SCALE: float = 0.97
|
const PAGE_CONTENT_SCALE: float = 0.97
|
||||||
const CATALOG_PORTRAIT_SIZE := Vector2(78.0, 36.0)
|
const CATALOG_PORTRAIT_SIZE := Vector2(78.0, 36.0)
|
||||||
const CATALOG_ENTRY_SIZE := Vector2(92.0, 92.0)
|
const CATALOG_ENTRY_SIZE := Vector2(92.0, 92.0)
|
||||||
|
|
@ -67,6 +70,7 @@ var _snapping_catalog_scroll: bool = false
|
||||||
var _catalog_scroll_snap_timer: Timer
|
var _catalog_scroll_snap_timer: Timer
|
||||||
|
|
||||||
var _category_tabs: Array[Button] = []
|
var _category_tabs: Array[Button] = []
|
||||||
|
var _category_tab_categories: Array[LogbookCatalog.Category] = []
|
||||||
var _catalog_scroll: ScrollContainer
|
var _catalog_scroll: ScrollContainer
|
||||||
var _catalog_grid: GridContainer
|
var _catalog_grid: GridContainer
|
||||||
var _catalog_scroll_up_indicator: TextureRect
|
var _catalog_scroll_up_indicator: TextureRect
|
||||||
|
|
@ -163,7 +167,9 @@ func focus_initial() -> void:
|
||||||
var first := _entry_buttons.values().front() as Button
|
var first := _entry_buttons.values().front() as Button
|
||||||
first.grab_focus()
|
first.grab_focus()
|
||||||
else:
|
else:
|
||||||
_category_tabs[int(_category)].grab_focus()
|
var category_tab_index: int = _category_tab_categories.find(_category)
|
||||||
|
if category_tab_index >= 0:
|
||||||
|
_category_tabs[category_tab_index].grab_focus()
|
||||||
|
|
||||||
|
|
||||||
func _build_interface() -> void:
|
func _build_interface() -> void:
|
||||||
|
|
@ -183,19 +189,26 @@ func _build_interface() -> void:
|
||||||
var tabs := HBoxContainer.new()
|
var tabs := HBoxContainer.new()
|
||||||
# Align the first flange with the authored left paper edge.
|
# Align the first flange with the authored left paper edge.
|
||||||
tabs.position = Vector2(LOGBOOK_TAB_LEFT_INSET, 37.0)
|
tabs.position = Vector2(LOGBOOK_TAB_LEFT_INSET, 37.0)
|
||||||
tabs.size = Vector2(384.0, 38.0)
|
var category_order: Array[LogbookCatalog.Category] = [
|
||||||
tabs.z_index = 10
|
|
||||||
tabs.mouse_filter = Control.MOUSE_FILTER_PASS
|
|
||||||
tabs.add_theme_constant_override("separation", 6)
|
|
||||||
stack.add_child(tabs)
|
|
||||||
for category: LogbookCatalog.Category in [
|
|
||||||
LogbookCatalog.Category.FRESH_WATER,
|
LogbookCatalog.Category.FRESH_WATER,
|
||||||
LogbookCatalog.Category.SALT_WATER,
|
LogbookCatalog.Category.SALT_WATER,
|
||||||
LogbookCatalog.Category.OTHER,
|
|
||||||
LogbookCatalog.Category.SHELLFISH,
|
LogbookCatalog.Category.SHELLFISH,
|
||||||
]:
|
LogbookCatalog.Category.OTHER,
|
||||||
|
]
|
||||||
|
tabs.size = Vector2(
|
||||||
|
LOGBOOK_TAB_SIZE.x * float(category_order.size())
|
||||||
|
+ LOGBOOK_TAB_SEPARATION * float(category_order.size() - 1),
|
||||||
|
LOGBOOK_TAB_SIZE.y,
|
||||||
|
)
|
||||||
|
tabs.z_index = 10
|
||||||
|
tabs.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||||
|
tabs.add_theme_constant_override(
|
||||||
|
"separation", roundi(LOGBOOK_TAB_SEPARATION)
|
||||||
|
)
|
||||||
|
stack.add_child(tabs)
|
||||||
|
for category: LogbookCatalog.Category in category_order:
|
||||||
var tab := OrganizerTabType.new()
|
var tab := OrganizerTabType.new()
|
||||||
tab.custom_minimum_size = Vector2(90, 38)
|
tab.custom_minimum_size = LOGBOOK_TAB_SIZE
|
||||||
tab.toggle_mode = true
|
tab.toggle_mode = true
|
||||||
tab.text = LogbookCatalog.category_label(category)
|
tab.text = LogbookCatalog.category_label(category)
|
||||||
tab.palette_index = int(category)
|
tab.palette_index = int(category)
|
||||||
|
|
@ -203,6 +216,7 @@ func _build_interface() -> void:
|
||||||
tab.pressed.connect(_select_category.bind(category))
|
tab.pressed.connect(_select_category.bind(category))
|
||||||
tabs.add_child(tab)
|
tabs.add_child(tab)
|
||||||
_category_tabs.append(tab)
|
_category_tabs.append(tab)
|
||||||
|
_category_tab_categories.append(category)
|
||||||
_configure_category_focus()
|
_configure_category_focus()
|
||||||
|
|
||||||
var book := Control.new()
|
var book := Control.new()
|
||||||
|
|
@ -529,7 +543,7 @@ func _select_category(category: LogbookCatalog.Category) -> void:
|
||||||
_selected_entry_key = StringName()
|
_selected_entry_key = StringName()
|
||||||
for index: int in _category_tabs.size():
|
for index: int in _category_tabs.size():
|
||||||
(_category_tabs[index] as OrganizerTabType).set_selected(
|
(_category_tabs[index] as OrganizerTabType).set_selected(
|
||||||
index == int(_category)
|
_category_tab_categories[index] == _category
|
||||||
)
|
)
|
||||||
_begin_category_transition()
|
_begin_category_transition()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,6 @@ func setup(
|
||||||
|
|
||||||
func activate() -> void:
|
func activate() -> void:
|
||||||
_show_inbox()
|
_show_inbox()
|
||||||
UtilityPageStyle.animate_in(self)
|
|
||||||
call_deferred("_focus_first")
|
call_deferred("_focus_first")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -217,7 +216,7 @@ func _build_compose() -> Control:
|
||||||
_attachment_kind = OptionButton.new()
|
_attachment_kind = OptionButton.new()
|
||||||
_attachment_kind.position = Vector2(688, 48)
|
_attachment_kind.position = Vector2(688, 48)
|
||||||
_attachment_kind.size = Vector2(280, 46)
|
_attachment_kind.size = Vector2(280, 46)
|
||||||
for label: String in ["No gift", "Currency", "Fish", "Consumable"]:
|
for label: String in ["No gift", "Currency", "Fish", "Item"]:
|
||||||
_attachment_kind.add_item(label)
|
_attachment_kind.add_item(label)
|
||||||
_attachment_kind.item_selected.connect(_refresh_attachment_choices)
|
_attachment_kind.item_selected.connect(_refresh_attachment_choices)
|
||||||
page.add_child(_attachment_kind)
|
page.add_child(_attachment_kind)
|
||||||
|
|
@ -770,7 +769,7 @@ func _attachment_text(attachment: Dictionary) -> String:
|
||||||
StringName(str(attachment.get("item_id", "")))
|
StringName(str(attachment.get("item_id", "")))
|
||||||
)
|
)
|
||||||
return "%s ×%d" % [
|
return "%s ×%d" % [
|
||||||
item.display_name if item != null else "Consumable",
|
item.display_name if item != null else "Item",
|
||||||
int(attachment.get("quantity", 0)),
|
int(attachment.get("quantity", 0)),
|
||||||
]
|
]
|
||||||
return "Invalid gift."
|
return "Invalid gift."
|
||||||
|
|
|
||||||
|
|
@ -115,11 +115,6 @@ enum BagView {
|
||||||
CONSUMABLES,
|
CONSUMABLES,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum TackleView {
|
|
||||||
BAIT,
|
|
||||||
LURES,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SortMode {
|
enum SortMode {
|
||||||
CATCH_ORDER,
|
CATCH_ORDER,
|
||||||
NAME,
|
NAME,
|
||||||
|
|
@ -150,10 +145,9 @@ const INVENTORY_NOTEPAD_POSITION := Vector2(
|
||||||
INVENTORY_MAIN_POSITION.y,
|
INVENTORY_MAIN_POSITION.y,
|
||||||
)
|
)
|
||||||
const INVENTORY_NOTEPAD_SIZE := Vector2(278.0, 484.0)
|
const INVENTORY_NOTEPAD_SIZE := Vector2(278.0, 484.0)
|
||||||
const INVENTORY_HEADER_INSET := Vector2(24.0, 20.0)
|
|
||||||
const INVENTORY_HEADER_HEIGHT := 40.0
|
|
||||||
const INVENTORY_MAIN_CORNER_RADIUS: int = 58
|
const INVENTORY_MAIN_CORNER_RADIUS: int = 58
|
||||||
const INVENTORY_INNER_CORNER_RADIUS: int = 45
|
const INVENTORY_INNER_CORNER_RADIUS: int = 45
|
||||||
|
const TACKLE_GRID_COLUMNS: int = 3
|
||||||
const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0)
|
const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0)
|
||||||
|
|
||||||
@onready var _navigation_cluster: BubbleClusterType = %NavigationCluster
|
@onready var _navigation_cluster: BubbleClusterType = %NavigationCluster
|
||||||
|
|
@ -198,18 +192,18 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0)
|
||||||
@onready var _inventory_sub_tabs: HBoxContainer = %InventorySubTabs
|
@onready var _inventory_sub_tabs: HBoxContainer = %InventorySubTabs
|
||||||
@onready var _cooler_sub_tab: Button = %CoolerSubTab
|
@onready var _cooler_sub_tab: Button = %CoolerSubTab
|
||||||
@onready var _bag_sub_tab: Button = %BagSubTab
|
@onready var _bag_sub_tab: Button = %BagSubTab
|
||||||
|
@onready var _items_sub_tab: Button = %ItemsSubTab
|
||||||
@onready var _tackle_sub_tab: Button = %TackleSubTab
|
@onready var _tackle_sub_tab: Button = %TackleSubTab
|
||||||
@onready var _bag_filter_tabs: HBoxContainer = %BagFilterTabs
|
|
||||||
@onready var _equipment_filter: Button = %EquipmentFilter
|
|
||||||
@onready var _consumables_filter: Button = %ConsumablesFilter
|
|
||||||
@onready var _tackle_main_panel: PanelContainer = %TackleMainPanel
|
@onready var _tackle_main_panel: PanelContainer = %TackleMainPanel
|
||||||
@onready var _tackle_detail_panel: PanelContainer = %TackleDetailPanel
|
@onready var _tackle_detail_panel: PanelContainer = %TackleDetailPanel
|
||||||
@onready var _bait_filter: Button = %BaitFilter
|
@onready var _bait_heading: Label = %BaitHeading
|
||||||
@onready var _lures_filter: Button = %LuresFilter
|
@onready var _lure_heading: Label = %LureHeading
|
||||||
@onready var _tackle_empty: Label = %TackleEmpty
|
@onready var _bait_empty: Label = %BaitEmpty
|
||||||
|
@onready var _lure_empty: Label = %LureEmpty
|
||||||
@onready var _tackle_detail_text: Label = %TackleDetailText
|
@onready var _tackle_detail_text: Label = %TackleDetailText
|
||||||
@onready var _tackle_equip_button: NotepadInkActionType = %TackleEquipButton
|
@onready var _tackle_equip_button: NotepadInkActionType = %TackleEquipButton
|
||||||
@onready var _tackle_item_list: VBoxContainer = %TackleItemList
|
@onready var _bait_item_list: GridContainer = %BaitItemList
|
||||||
|
@onready var _lure_item_list: GridContainer = %LureItemList
|
||||||
@onready var _bag_outer_wall: PanelContainer = %BagOuterWall
|
@onready var _bag_outer_wall: PanelContainer = %BagOuterWall
|
||||||
@onready var _bag_inner_liner: PanelContainer = %BagInnerLiner
|
@onready var _bag_inner_liner: PanelContainer = %BagInnerLiner
|
||||||
@onready var _bag_scroll: ScrollContainer = %BagScroll
|
@onready var _bag_scroll: ScrollContainer = %BagScroll
|
||||||
|
|
@ -312,8 +306,8 @@ var _cooler_capacity: PlayerCoolerCapacityType
|
||||||
var _current_section: Section = Section.COOLER
|
var _current_section: Section = Section.COOLER
|
||||||
var _last_inventory_section: Section = Section.COOLER
|
var _last_inventory_section: Section = Section.COOLER
|
||||||
var _bag_view: BagView = BagView.EQUIPMENT
|
var _bag_view: BagView = BagView.EQUIPMENT
|
||||||
var _tackle_view: TackleView = TackleView.BAIT
|
|
||||||
var _selected_tackle_item_id: StringName
|
var _selected_tackle_item_id: StringName
|
||||||
|
var _tackle_item_buttons: Dictionary[StringName, Button] = {}
|
||||||
var _controller_ownership: ControllerOwnership = ControllerOwnership.ITEM_LIST
|
var _controller_ownership: ControllerOwnership = ControllerOwnership.ITEM_LIST
|
||||||
var _controller_source_section: Section = Section.COOLER
|
var _controller_source_section: Section = Section.COOLER
|
||||||
var _controller_source_identity: StringName
|
var _controller_source_identity: StringName
|
||||||
|
|
@ -377,18 +371,11 @@ func _ready() -> void:
|
||||||
_cooler_original_index = _cooler_page.get_index()
|
_cooler_original_index = _cooler_page.get_index()
|
||||||
_confirmation_original_parent = _sale_confirmation.get_parent()
|
_confirmation_original_parent = _sale_confirmation.get_parent()
|
||||||
_confirmation_original_index = _sale_confirmation.get_index()
|
_confirmation_original_index = _sale_confirmation.get_index()
|
||||||
_inventory_sub_tabs.move_child(_tackle_sub_tab, 1)
|
|
||||||
_inventory_sub_tabs.move_child(_bag_sub_tab, 2)
|
|
||||||
_inventory_tab.pressed.connect(_show_last_inventory_section)
|
_inventory_tab.pressed.connect(_show_last_inventory_section)
|
||||||
_cooler_sub_tab.pressed.connect(_show_section.bind(Section.COOLER))
|
_cooler_sub_tab.pressed.connect(_show_section.bind(Section.COOLER))
|
||||||
_bag_sub_tab.pressed.connect(_show_section.bind(Section.BAG))
|
_bag_sub_tab.pressed.connect(_show_bag_view.bind(BagView.EQUIPMENT))
|
||||||
|
_items_sub_tab.pressed.connect(_show_bag_view.bind(BagView.CONSUMABLES))
|
||||||
_tackle_sub_tab.pressed.connect(_show_section.bind(Section.TACKLE_BOX))
|
_tackle_sub_tab.pressed.connect(_show_section.bind(Section.TACKLE_BOX))
|
||||||
_equipment_filter.pressed.connect(_set_bag_view.bind(BagView.EQUIPMENT))
|
|
||||||
_consumables_filter.pressed.connect(
|
|
||||||
_set_bag_view.bind(BagView.CONSUMABLES)
|
|
||||||
)
|
|
||||||
_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_tackle)
|
_tackle_equip_button.pressed.connect(_toggle_active_tackle)
|
||||||
_logbook_tab.pressed.connect(
|
_logbook_tab.pressed.connect(
|
||||||
_show_section.bind(Section.LOGBOOK)
|
_show_section.bind(Section.LOGBOOK)
|
||||||
|
|
@ -434,7 +421,6 @@ func _ready() -> void:
|
||||||
# Keep the exposed tab hitboxes above the full-page roots. The individual
|
# Keep the exposed tab hitboxes above the full-page roots. The individual
|
||||||
# main panels retain a higher z-index and cover the tab bodies below y=166.
|
# main panels retain a higher z-index and cover the tab bodies below y=166.
|
||||||
_inventory_sub_tabs.move_to_front()
|
_inventory_sub_tabs.move_to_front()
|
||||||
_bag_filter_tabs.move_to_front()
|
|
||||||
_apply_cooler_control_styles()
|
_apply_cooler_control_styles()
|
||||||
_apply_cooler_wall_styles()
|
_apply_cooler_wall_styles()
|
||||||
_apply_cooler_notepad_style()
|
_apply_cooler_notepad_style()
|
||||||
|
|
@ -947,17 +933,7 @@ func _restore_controller_item_focus(
|
||||||
elif section == Section.BAG:
|
elif section == Section.BAG:
|
||||||
target = _bag_item_nodes.get(identity) as BagItemSpriteType
|
target = _bag_item_nodes.get(identity) as BagItemSpriteType
|
||||||
elif section == Section.TACKLE_BOX:
|
elif section == Section.TACKLE_BOX:
|
||||||
for child: Node in _tackle_item_list.get_children():
|
target = _tackle_item_buttons.get(identity) as Button
|
||||||
var button := child as BaseButton
|
|
||||||
if (
|
|
||||||
button != null
|
|
||||||
and button.has_meta(&"controller_tackle_item_id")
|
|
||||||
and StringName(str(button.get_meta(
|
|
||||||
&"controller_tackle_item_id"
|
|
||||||
))) == identity
|
|
||||||
):
|
|
||||||
target = button
|
|
||||||
break
|
|
||||||
if (
|
if (
|
||||||
target != null
|
target != null
|
||||||
and is_instance_valid(target)
|
and is_instance_valid(target)
|
||||||
|
|
@ -1087,23 +1063,14 @@ func _handle_controller_secondary_switch(event: InputEvent) -> bool:
|
||||||
):
|
):
|
||||||
return true
|
return true
|
||||||
if _is_inventory_section(_current_section):
|
if _is_inventory_section(_current_section):
|
||||||
|
var inventory_index: int = _get_inventory_tab_index()
|
||||||
if (
|
if (
|
||||||
(direction > 0 and _current_section == Section.BAG)
|
(direction > 0 and inventory_index == 3)
|
||||||
or (direction < 0 and _current_section == Section.COOLER)
|
or (direction < 0 and inventory_index == 0)
|
||||||
):
|
):
|
||||||
_begin_controller_hotbar_management()
|
_begin_controller_hotbar_management()
|
||||||
return true
|
return true
|
||||||
var inventory_sections: Array[Section] = [
|
_show_inventory_tab(inventory_index + direction)
|
||||||
Section.COOLER,
|
|
||||||
Section.TACKLE_BOX,
|
|
||||||
Section.BAG,
|
|
||||||
]
|
|
||||||
var inventory_index: int = inventory_sections.find(_current_section)
|
|
||||||
_show_section(inventory_sections[wrapi(
|
|
||||||
inventory_index + direction,
|
|
||||||
0,
|
|
||||||
inventory_sections.size(),
|
|
||||||
)])
|
|
||||||
return true
|
return true
|
||||||
_cycle_visible_secondary_tabs(direction)
|
_cycle_visible_secondary_tabs(direction)
|
||||||
return true
|
return true
|
||||||
|
|
@ -1129,13 +1096,31 @@ func _begin_controller_hotbar_management() -> void:
|
||||||
|
|
||||||
func _cycle_from_controller_hotbar_management(direction: int) -> void:
|
func _cycle_from_controller_hotbar_management(direction: int) -> void:
|
||||||
_release_controller_ownership(false, false)
|
_release_controller_ownership(false, false)
|
||||||
var target_section: Section = (
|
var target_index: int = 0 if direction > 0 else 3
|
||||||
Section.COOLER if direction > 0 else Section.BAG
|
if target_index == _get_inventory_tab_index():
|
||||||
)
|
|
||||||
if target_section == _current_section:
|
|
||||||
call_deferred("_focus_current_section")
|
call_deferred("_focus_current_section")
|
||||||
else:
|
else:
|
||||||
_show_section(target_section)
|
_show_inventory_tab(target_index)
|
||||||
|
|
||||||
|
|
||||||
|
func _get_inventory_tab_index() -> int:
|
||||||
|
if _current_section == Section.COOLER:
|
||||||
|
return 0
|
||||||
|
if _current_section == Section.BAG:
|
||||||
|
return 2 if _bag_view == BagView.CONSUMABLES else 1
|
||||||
|
return 3
|
||||||
|
|
||||||
|
|
||||||
|
func _show_inventory_tab(index: int) -> void:
|
||||||
|
match clampi(index, 0, 3):
|
||||||
|
0:
|
||||||
|
_show_section(Section.COOLER)
|
||||||
|
1:
|
||||||
|
_show_bag_view(BagView.EQUIPMENT)
|
||||||
|
2:
|
||||||
|
_show_bag_view(BagView.CONSUMABLES)
|
||||||
|
_:
|
||||||
|
_show_section(Section.TACKLE_BOX)
|
||||||
|
|
||||||
|
|
||||||
func _cycle_visible_secondary_tabs(direction: int) -> bool:
|
func _cycle_visible_secondary_tabs(direction: int) -> bool:
|
||||||
|
|
@ -1575,7 +1560,9 @@ func _show_section_immediate(section: Section) -> void:
|
||||||
if section != Section.BAG:
|
if section != Section.BAG:
|
||||||
_close_bag_detail()
|
_close_bag_detail()
|
||||||
else:
|
else:
|
||||||
_update_bag_detail()
|
# BagView can change while entering from another Inventory page. Rebuild
|
||||||
|
# the filtered contents here so the visible list always matches its tab.
|
||||||
|
_refresh_bag()
|
||||||
_refresh_tackle_box()
|
_refresh_tackle_box()
|
||||||
if section != Section.LOGBOOK:
|
if section != Section.LOGBOOK:
|
||||||
_cancel_logbook_page_transition(true)
|
_cancel_logbook_page_transition(true)
|
||||||
|
|
@ -1600,7 +1587,12 @@ func _show_section_immediate(section: Section) -> void:
|
||||||
_players_page.deactivate()
|
_players_page.deactivate()
|
||||||
_inventory_tab.button_pressed = _is_inventory_section(section)
|
_inventory_tab.button_pressed = _is_inventory_section(section)
|
||||||
_cooler_sub_tab.set_selected(section == Section.COOLER)
|
_cooler_sub_tab.set_selected(section == Section.COOLER)
|
||||||
_bag_sub_tab.set_selected(section == Section.BAG)
|
_bag_sub_tab.set_selected(
|
||||||
|
section == Section.BAG and _bag_view == BagView.EQUIPMENT
|
||||||
|
)
|
||||||
|
_items_sub_tab.set_selected(
|
||||||
|
section == Section.BAG and _bag_view == BagView.CONSUMABLES
|
||||||
|
)
|
||||||
_tackle_sub_tab.set_selected(section == Section.TACKLE_BOX)
|
_tackle_sub_tab.set_selected(section == Section.TACKLE_BOX)
|
||||||
_refresh_inventory_organizer_tabs()
|
_refresh_inventory_organizer_tabs()
|
||||||
_logbook_tab.button_pressed = section == Section.LOGBOOK
|
_logbook_tab.button_pressed = section == Section.LOGBOOK
|
||||||
|
|
@ -1622,8 +1614,9 @@ func _is_inventory_section(section: Section) -> bool:
|
||||||
func _refresh_inventory_organizer_tabs() -> void:
|
func _refresh_inventory_organizer_tabs() -> void:
|
||||||
for tab: OrganizerTabType in [
|
for tab: OrganizerTabType in [
|
||||||
_cooler_sub_tab,
|
_cooler_sub_tab,
|
||||||
_tackle_sub_tab,
|
|
||||||
_bag_sub_tab,
|
_bag_sub_tab,
|
||||||
|
_items_sub_tab,
|
||||||
|
_tackle_sub_tab,
|
||||||
]:
|
]:
|
||||||
tab.refresh_state()
|
tab.refresh_state()
|
||||||
|
|
||||||
|
|
@ -1633,8 +1626,9 @@ func _animate_inventory_tab_entry() -> void:
|
||||||
return
|
return
|
||||||
var tabs: Array[OrganizerTabType] = [
|
var tabs: Array[OrganizerTabType] = [
|
||||||
_cooler_sub_tab,
|
_cooler_sub_tab,
|
||||||
_tackle_sub_tab,
|
|
||||||
_bag_sub_tab,
|
_bag_sub_tab,
|
||||||
|
_items_sub_tab,
|
||||||
|
_tackle_sub_tab,
|
||||||
]
|
]
|
||||||
for index: int in tabs.size():
|
for index: int in tabs.size():
|
||||||
tabs[index].animate_entrance(float(index) * 0.025)
|
tabs[index].animate_entrance(float(index) * 0.025)
|
||||||
|
|
@ -1643,8 +1637,9 @@ func _animate_inventory_tab_entry() -> void:
|
||||||
func _settle_inventory_tabs_for_close() -> void:
|
func _settle_inventory_tabs_for_close() -> void:
|
||||||
for tab: OrganizerTabType in [
|
for tab: OrganizerTabType in [
|
||||||
_cooler_sub_tab,
|
_cooler_sub_tab,
|
||||||
_tackle_sub_tab,
|
|
||||||
_bag_sub_tab,
|
_bag_sub_tab,
|
||||||
|
_items_sub_tab,
|
||||||
|
_tackle_sub_tab,
|
||||||
]:
|
]:
|
||||||
tab.settle_for_close()
|
tab.settle_for_close()
|
||||||
|
|
||||||
|
|
@ -1779,8 +1774,9 @@ func _reserve_visible_secondary_navigation() -> void:
|
||||||
call_deferred("_focus_current_section")
|
call_deferred("_focus_current_section")
|
||||||
var inventory_tabs: Array[Button] = [
|
var inventory_tabs: Array[Button] = [
|
||||||
_cooler_sub_tab,
|
_cooler_sub_tab,
|
||||||
_tackle_sub_tab,
|
|
||||||
_bag_sub_tab,
|
_bag_sub_tab,
|
||||||
|
_items_sub_tab,
|
||||||
|
_tackle_sub_tab,
|
||||||
]
|
]
|
||||||
for index: int in inventory_tabs.size():
|
for index: int in inventory_tabs.size():
|
||||||
var tab: Button = inventory_tabs[index]
|
var tab: Button = inventory_tabs[index]
|
||||||
|
|
@ -1794,9 +1790,7 @@ func _reserve_visible_secondary_navigation() -> void:
|
||||||
_inventory_tab.focus_neighbor_bottom = _inventory_tab.get_path_to(
|
_inventory_tab.focus_neighbor_bottom = _inventory_tab.get_path_to(
|
||||||
_inventory_tab_for_section(_last_inventory_section)
|
_inventory_tab_for_section(_last_inventory_section)
|
||||||
)
|
)
|
||||||
_tackle_sub_tab.focus_neighbor_bottom = _tackle_sub_tab.get_path_to(
|
_configure_tackle_item_focus()
|
||||||
_bait_filter
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func _inventory_tab_for_section(section: Section) -> Button:
|
func _inventory_tab_for_section(section: Section) -> Button:
|
||||||
|
|
@ -1804,7 +1798,11 @@ func _inventory_tab_for_section(section: Section) -> Button:
|
||||||
Section.COOLER:
|
Section.COOLER:
|
||||||
return _cooler_sub_tab
|
return _cooler_sub_tab
|
||||||
Section.BAG:
|
Section.BAG:
|
||||||
return _bag_sub_tab
|
return (
|
||||||
|
_items_sub_tab
|
||||||
|
if _bag_view == BagView.CONSUMABLES
|
||||||
|
else _bag_sub_tab
|
||||||
|
)
|
||||||
_:
|
_:
|
||||||
return _tackle_sub_tab
|
return _tackle_sub_tab
|
||||||
|
|
||||||
|
|
@ -1847,7 +1845,7 @@ func _configure_active_page_focus() -> void:
|
||||||
Section.BAG:
|
Section.BAG:
|
||||||
_configure_bag_item_focus()
|
_configure_bag_item_focus()
|
||||||
Section.TACKLE_BOX:
|
Section.TACKLE_BOX:
|
||||||
_bait_filter.grab_focus()
|
_configure_tackle_item_focus()
|
||||||
Section.LOGBOOK:
|
Section.LOGBOOK:
|
||||||
_catalog_logbook.focus_initial()
|
_catalog_logbook.focus_initial()
|
||||||
Section.NET:
|
Section.NET:
|
||||||
|
|
@ -1859,13 +1857,6 @@ func _configure_active_page_focus() -> void:
|
||||||
|
|
||||||
|
|
||||||
func _apply_inventory_styles() -> void:
|
func _apply_inventory_styles() -> void:
|
||||||
for button: Button in [
|
|
||||||
_equipment_filter,
|
|
||||||
_consumables_filter,
|
|
||||||
_bait_filter,
|
|
||||||
_lures_filter,
|
|
||||||
]:
|
|
||||||
UtilityPageStyle.apply_ocean_button(button)
|
|
||||||
for panel: PanelContainer in [_tackle_main_panel]:
|
for panel: PanelContainer in [_tackle_main_panel]:
|
||||||
var panel_style := UtilityPageStyle.panel_style()
|
var panel_style := UtilityPageStyle.panel_style()
|
||||||
panel_style.set_corner_radius_all(INVENTORY_MAIN_CORNER_RADIUS)
|
panel_style.set_corner_radius_all(INVENTORY_MAIN_CORNER_RADIUS)
|
||||||
|
|
@ -1873,7 +1864,12 @@ func _apply_inventory_styles() -> void:
|
||||||
"panel",
|
"panel",
|
||||||
panel_style,
|
panel_style,
|
||||||
)
|
)
|
||||||
for label: Label in [_tackle_empty]:
|
for label: Label in [
|
||||||
|
_bait_heading,
|
||||||
|
_lure_heading,
|
||||||
|
_bait_empty,
|
||||||
|
_lure_empty,
|
||||||
|
]:
|
||||||
label.add_theme_font_override("font", UtilityPageStyle.TuffyFont)
|
label.add_theme_font_override("font", UtilityPageStyle.TuffyFont)
|
||||||
label.add_theme_color_override(
|
label.add_theme_color_override(
|
||||||
"font_color",
|
"font_color",
|
||||||
|
|
@ -1895,124 +1891,180 @@ func _apply_inventory_styles() -> void:
|
||||||
UtilityPageStyle.apply_ocean_button(_cancel_sale_button)
|
UtilityPageStyle.apply_ocean_button(_cancel_sale_button)
|
||||||
|
|
||||||
|
|
||||||
func _set_bag_view(view: BagView) -> void:
|
func _show_bag_view(view: BagView) -> void:
|
||||||
|
if (
|
||||||
|
_transitioning
|
||||||
|
or _page_transitioning
|
||||||
|
or _sale_confirmation.visible
|
||||||
|
or get_viewport().gui_is_dragging()
|
||||||
|
):
|
||||||
|
return
|
||||||
|
if _current_section != Section.BAG:
|
||||||
|
_bag_view = view
|
||||||
|
_selected_bag_item_id = StringName()
|
||||||
|
_show_section(Section.BAG)
|
||||||
|
return
|
||||||
if _bag_view == view:
|
if _bag_view == view:
|
||||||
return
|
return
|
||||||
|
_release_controller_ownership(false, true)
|
||||||
_bag_view = view
|
_bag_view = view
|
||||||
_selected_bag_item_id = StringName()
|
_selected_bag_item_id = StringName()
|
||||||
_refresh_bag()
|
_refresh_bag()
|
||||||
|
_bag_sub_tab.set_selected(view == BagView.EQUIPMENT)
|
||||||
|
_items_sub_tab.set_selected(view == BagView.CONSUMABLES)
|
||||||
func _set_tackle_view(view: TackleView) -> void:
|
_refresh_inventory_organizer_tabs()
|
||||||
_tackle_view = view
|
_reserve_visible_secondary_navigation()
|
||||||
_selected_tackle_item_id = StringName()
|
_set_content_interactive(true)
|
||||||
_refresh_tackle_box()
|
call_deferred("_focus_current_section")
|
||||||
|
|
||||||
|
|
||||||
func _refresh_tackle_box() -> void:
|
func _refresh_tackle_box() -> void:
|
||||||
if not is_node_ready():
|
if not is_node_ready():
|
||||||
return
|
return
|
||||||
_bait_filter.button_pressed = _tackle_view == TackleView.BAIT
|
for item_list: GridContainer in [_bait_item_list, _lure_item_list]:
|
||||||
_lures_filter.button_pressed = _tackle_view == TackleView.LURES
|
for child: Node in item_list.get_children():
|
||||||
for child: Node in _tackle_item_list.get_children():
|
child.queue_free()
|
||||||
child.queue_free()
|
_tackle_item_buttons.clear()
|
||||||
var matching_items: Array[OwnedItemType] = []
|
var bait_items: Array[OwnedItemType] = []
|
||||||
|
var lure_items: Array[OwnedItemType] = []
|
||||||
if _bag != null and _item_catalog != null:
|
if _bag != null and _item_catalog != null:
|
||||||
var available_items: Array[OwnedItemType] = (
|
for owned: OwnedItemType in _bag.get_unlocked_bait_items():
|
||||||
_bag.get_unlocked_bait_items()
|
|
||||||
if _tackle_view == TackleView.BAIT
|
|
||||||
else _bag.get_all_items()
|
|
||||||
)
|
|
||||||
for owned: OwnedItemType in available_items:
|
|
||||||
var item: ItemDataType = _item_catalog.get_item_by_id(
|
var item: ItemDataType = _item_catalog.get_item_by_id(
|
||||||
owned.item_id
|
owned.item_id
|
||||||
)
|
)
|
||||||
if item == null:
|
if item != null and item.category == ItemDataType.Category.BAIT:
|
||||||
continue
|
bait_items.append(owned)
|
||||||
if (
|
for owned: OwnedItemType in _bag.get_all_items():
|
||||||
_tackle_view == TackleView.BAIT
|
var item: ItemDataType = _item_catalog.get_item_by_id(
|
||||||
and item.category == ItemDataType.Category.BAIT
|
owned.item_id
|
||||||
):
|
)
|
||||||
matching_items.append(owned)
|
if item != null and item.category == ItemDataType.Category.LURE:
|
||||||
elif (
|
lure_items.append(owned)
|
||||||
_tackle_view == TackleView.LURES
|
bait_items.sort_custom(_sort_bag_items)
|
||||||
and item.category == ItemDataType.Category.LURE
|
lure_items.sort_custom(_sort_bag_items)
|
||||||
):
|
_populate_tackle_column(bait_items, _bait_item_list)
|
||||||
matching_items.append(owned)
|
_populate_tackle_column(lure_items, _lure_item_list)
|
||||||
matching_items.sort_custom(_sort_bag_items)
|
if (
|
||||||
for owned: OwnedItemType in matching_items:
|
not _selected_tackle_item_id.is_empty()
|
||||||
|
and not _tackle_item_buttons.has(_selected_tackle_item_id)
|
||||||
|
):
|
||||||
|
_selected_tackle_item_id = StringName()
|
||||||
|
_bait_empty.visible = bait_items.is_empty()
|
||||||
|
_lure_empty.visible = lure_items.is_empty()
|
||||||
|
_configure_tackle_item_focus()
|
||||||
|
_update_tackle_detail()
|
||||||
|
_apply_tackle_interactivity(_tackle_is_interactive())
|
||||||
|
|
||||||
|
|
||||||
|
func _populate_tackle_column(
|
||||||
|
owned_items: Array[OwnedItemType],
|
||||||
|
item_list: GridContainer,
|
||||||
|
) -> void:
|
||||||
|
for owned: OwnedItemType in owned_items:
|
||||||
var item: ItemDataType = _item_catalog.get_item_by_id(owned.item_id)
|
var item: ItemDataType = _item_catalog.get_item_by_id(owned.item_id)
|
||||||
var row := Button.new()
|
if item == null:
|
||||||
row.icon = item.icon
|
continue
|
||||||
row.tooltip_text = (
|
var button := Button.new()
|
||||||
"%s ×%d" % [item.display_name, owned.quantity]
|
button.icon = item.icon
|
||||||
|
button.tooltip_text = (
|
||||||
|
"%s • %d/%d" % [
|
||||||
|
item.display_name,
|
||||||
|
owned.quantity,
|
||||||
|
item.max_stack,
|
||||||
|
]
|
||||||
if item.is_bait()
|
if item.is_bait()
|
||||||
else item.display_name
|
else item.display_name
|
||||||
)
|
)
|
||||||
var compact_tackle_item: bool = item.is_bait() or item.is_lure()
|
button.custom_minimum_size = Vector2(72, 72)
|
||||||
if compact_tackle_item:
|
button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||||
row.custom_minimum_size = Vector2(72, 72)
|
button.expand_icon = item.icon != null
|
||||||
row.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
button.alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
row.expand_icon = item.icon != null
|
button.text = "" if item.icon != null else item.display_name
|
||||||
row.alignment = HORIZONTAL_ALIGNMENT_CENTER
|
if item.icon == null:
|
||||||
row.text = "" if item.icon != null else item.display_name
|
button.add_theme_font_size_override("font_size", 12)
|
||||||
if item.icon == null:
|
button.toggle_mode = true
|
||||||
row.add_theme_font_size_override("font_size", 12)
|
button.set_meta(&"controller_tackle_item_id", owned.item_id)
|
||||||
else:
|
button.button_pressed = owned.item_id == _selected_tackle_item_id
|
||||||
row.text = "%s ×%d" % [item.display_name, owned.quantity]
|
_apply_tackle_bait_button_style(button)
|
||||||
row.alignment = HORIZONTAL_ALIGNMENT_LEFT
|
if item.is_bait():
|
||||||
row.toggle_mode = true
|
UtilityPageStyle.add_supply_quantity_badge(
|
||||||
row.set_meta(&"controller_tackle_item_id", owned.item_id)
|
button,
|
||||||
row.button_pressed = owned.item_id == _selected_tackle_item_id
|
owned.quantity,
|
||||||
if compact_tackle_item:
|
item.max_stack,
|
||||||
_apply_tackle_bait_button_style(row)
|
&"QuantityBadge",
|
||||||
if item.is_bait():
|
)
|
||||||
_add_tackle_quantity_badge(row, owned.quantity)
|
button.pressed.connect(_select_tackle_item.bind(owned.item_id))
|
||||||
else:
|
item_list.add_child(button)
|
||||||
UtilityPageStyle.apply_ocean_button(row)
|
_tackle_item_buttons[owned.item_id] = button
|
||||||
row.pressed.connect(_select_tackle_item.bind(owned.item_id))
|
|
||||||
_tackle_item_list.add_child(row)
|
|
||||||
_tackle_empty.text = (
|
|
||||||
"No bait collected."
|
|
||||||
if _tackle_view == TackleView.BAIT
|
|
||||||
else "No lures collected."
|
|
||||||
)
|
|
||||||
_tackle_empty.visible = matching_items.is_empty()
|
|
||||||
_update_tackle_detail()
|
|
||||||
|
|
||||||
|
|
||||||
func _add_tackle_quantity_badge(row: Button, quantity: int) -> void:
|
func _configure_tackle_item_focus() -> void:
|
||||||
var badge := Panel.new()
|
if not is_node_ready():
|
||||||
badge.name = "QuantityBadge"
|
return
|
||||||
badge.anchor_left = 1.0
|
var bait_buttons: Array[Button] = _get_tackle_column_buttons(
|
||||||
badge.anchor_right = 1.0
|
_bait_item_list
|
||||||
badge.offset_left = -27.0
|
|
||||||
badge.offset_top = 3.0
|
|
||||||
badge.offset_right = -3.0
|
|
||||||
badge.offset_bottom = 27.0
|
|
||||||
badge.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
||||||
badge.z_index = 2
|
|
||||||
var badge_style := StyleBoxFlat.new()
|
|
||||||
badge_style.bg_color = Color("0b5558")
|
|
||||||
badge_style.set_corner_radius_all(12)
|
|
||||||
badge_style.anti_aliasing = false
|
|
||||||
badge.add_theme_stylebox_override("panel", badge_style)
|
|
||||||
row.add_child(badge)
|
|
||||||
var quantity_label := Label.new()
|
|
||||||
quantity_label.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
|
||||||
quantity_label.offset_left = 1.0
|
|
||||||
quantity_label.offset_right = 1.0
|
|
||||||
quantity_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
||||||
quantity_label.text = str(quantity)
|
|
||||||
quantity_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
||||||
quantity_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
|
||||||
quantity_label.add_theme_font_override("font", UtilityPageStyle.TuffyFont)
|
|
||||||
quantity_label.add_theme_font_size_override(
|
|
||||||
"font_size",
|
|
||||||
13 if quantity >= 100 else 15,
|
|
||||||
)
|
)
|
||||||
quantity_label.add_theme_color_override("font_color", Color("e7f5f4"))
|
var lure_buttons: Array[Button] = _get_tackle_column_buttons(
|
||||||
badge.add_child(quantity_label)
|
_lure_item_list
|
||||||
|
)
|
||||||
|
var first_button: Button = (
|
||||||
|
bait_buttons.front()
|
||||||
|
if not bait_buttons.is_empty()
|
||||||
|
else (lure_buttons.front() if not lure_buttons.is_empty() else null)
|
||||||
|
)
|
||||||
|
_tackle_sub_tab.focus_neighbor_bottom = (
|
||||||
|
_tackle_sub_tab.get_path_to(first_button)
|
||||||
|
if first_button != null
|
||||||
|
else NodePath()
|
||||||
|
)
|
||||||
|
_configure_tackle_column_focus(bait_buttons, lure_buttons, true)
|
||||||
|
_configure_tackle_column_focus(lure_buttons, bait_buttons, false)
|
||||||
|
|
||||||
|
|
||||||
|
func _get_tackle_column_buttons(item_list: GridContainer) -> Array[Button]:
|
||||||
|
var buttons: Array[Button] = []
|
||||||
|
for child: Node in item_list.get_children():
|
||||||
|
var button := child as Button
|
||||||
|
if button != null and not button.is_queued_for_deletion():
|
||||||
|
buttons.append(button)
|
||||||
|
return buttons
|
||||||
|
|
||||||
|
|
||||||
|
func _configure_tackle_column_focus(
|
||||||
|
buttons: Array[Button],
|
||||||
|
other_buttons: Array[Button],
|
||||||
|
is_left_column: bool,
|
||||||
|
) -> void:
|
||||||
|
for index: int in buttons.size():
|
||||||
|
var button: Button = buttons[index]
|
||||||
|
var column: int = index % TACKLE_GRID_COLUMNS
|
||||||
|
var row: int = floori(float(index) / float(TACKLE_GRID_COLUMNS))
|
||||||
|
var left_target: Button = button
|
||||||
|
var right_target: Button = button
|
||||||
|
if column > 0:
|
||||||
|
left_target = buttons[index - 1]
|
||||||
|
elif not is_left_column and not other_buttons.is_empty():
|
||||||
|
left_target = other_buttons[mini(
|
||||||
|
row * TACKLE_GRID_COLUMNS + TACKLE_GRID_COLUMNS - 1,
|
||||||
|
other_buttons.size() - 1,
|
||||||
|
)]
|
||||||
|
if column < TACKLE_GRID_COLUMNS - 1 and index + 1 < buttons.size():
|
||||||
|
right_target = buttons[index + 1]
|
||||||
|
elif is_left_column and not other_buttons.is_empty():
|
||||||
|
right_target = other_buttons[mini(
|
||||||
|
row * TACKLE_GRID_COLUMNS,
|
||||||
|
other_buttons.size() - 1,
|
||||||
|
)]
|
||||||
|
button.focus_neighbor_left = button.get_path_to(left_target)
|
||||||
|
button.focus_neighbor_right = button.get_path_to(right_target)
|
||||||
|
button.focus_neighbor_top = (
|
||||||
|
button.get_path_to(_tackle_sub_tab)
|
||||||
|
if index < TACKLE_GRID_COLUMNS
|
||||||
|
else button.get_path_to(buttons[index - TACKLE_GRID_COLUMNS])
|
||||||
|
)
|
||||||
|
button.focus_neighbor_bottom = button.get_path_to(
|
||||||
|
buttons[mini(index + TACKLE_GRID_COLUMNS, buttons.size() - 1)]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _apply_tackle_bait_button_style(button: Button) -> void:
|
func _apply_tackle_bait_button_style(button: Button) -> void:
|
||||||
|
|
@ -2045,7 +2097,10 @@ func _apply_tackle_bait_button_style(button: Button) -> void:
|
||||||
|
|
||||||
func _select_tackle_item(item_id: StringName) -> void:
|
func _select_tackle_item(item_id: StringName) -> void:
|
||||||
_selected_tackle_item_id = item_id
|
_selected_tackle_item_id = item_id
|
||||||
_refresh_tackle_box()
|
for candidate_id: StringName in _tackle_item_buttons:
|
||||||
|
var button: Button = _tackle_item_buttons[candidate_id]
|
||||||
|
button.button_pressed = candidate_id == item_id
|
||||||
|
_update_tackle_detail()
|
||||||
|
|
||||||
|
|
||||||
func _update_tackle_detail() -> void:
|
func _update_tackle_detail() -> void:
|
||||||
|
|
@ -2057,11 +2112,8 @@ func _update_tackle_detail() -> void:
|
||||||
if item == null:
|
if item == null:
|
||||||
_tackle_equip_button.visible = false
|
_tackle_equip_button.visible = false
|
||||||
_tackle_equip_button.persistent_mark = false
|
_tackle_equip_button.persistent_mark = false
|
||||||
_tackle_detail_text.text = (
|
_tackle_detail_text.text = "Select bait or a lure for details."
|
||||||
"Select bait for details."
|
_apply_tackle_interactivity(_tackle_is_interactive())
|
||||||
if _tackle_view == TackleView.BAIT
|
|
||||||
else "Select a lure for details."
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
_tackle_equip_button.visible = item.is_bait() or item.is_lure()
|
_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 quantity: int = _bag.get_quantity(item.item_id) if _bag != null else 0
|
||||||
|
|
@ -2073,7 +2125,7 @@ func _update_tackle_detail() -> void:
|
||||||
break
|
break
|
||||||
var detail_lines: Array[String] = [item.display_name, ""]
|
var detail_lines: Array[String] = [item.display_name, ""]
|
||||||
if item.is_bait():
|
if item.is_bait():
|
||||||
detail_lines.append("quantity: %d" % quantity)
|
detail_lines.append("quantity: %d/%d" % [quantity, item.max_stack])
|
||||||
if assigned_slot >= 0:
|
if assigned_slot >= 0:
|
||||||
detail_lines.append("hotbar slot %d" % (assigned_slot + 1))
|
detail_lines.append("hotbar slot %d" % (assigned_slot + 1))
|
||||||
detail_lines.append("")
|
detail_lines.append("")
|
||||||
|
|
@ -2096,6 +2148,42 @@ func _update_tackle_detail() -> void:
|
||||||
_tackle_equip_button.persistent_mark = is_equipped
|
_tackle_equip_button.persistent_mark = is_equipped
|
||||||
_tackle_equip_button.disabled = quantity <= 0
|
_tackle_equip_button.disabled = quantity <= 0
|
||||||
_tackle_equip_button.refresh_ink_state()
|
_tackle_equip_button.refresh_ink_state()
|
||||||
|
_apply_tackle_interactivity(_tackle_is_interactive())
|
||||||
|
|
||||||
|
|
||||||
|
func _tackle_is_interactive() -> bool:
|
||||||
|
return (
|
||||||
|
visible
|
||||||
|
and _current_section == Section.TACKLE_BOX
|
||||||
|
and not _transitioning
|
||||||
|
and not _page_transitioning
|
||||||
|
and not _sale_confirmation.visible
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _apply_tackle_interactivity(interactive: bool) -> void:
|
||||||
|
for button: Button in _tackle_item_buttons.values():
|
||||||
|
button.focus_mode = (
|
||||||
|
Control.FOCUS_ALL if interactive else Control.FOCUS_NONE
|
||||||
|
)
|
||||||
|
button.mouse_filter = (
|
||||||
|
Control.MOUSE_FILTER_STOP
|
||||||
|
if interactive
|
||||||
|
else Control.MOUSE_FILTER_IGNORE
|
||||||
|
)
|
||||||
|
var action_interactive: bool = (
|
||||||
|
interactive
|
||||||
|
and _tackle_equip_button.visible
|
||||||
|
and not _tackle_equip_button.disabled
|
||||||
|
)
|
||||||
|
_tackle_equip_button.focus_mode = (
|
||||||
|
Control.FOCUS_ALL if action_interactive else Control.FOCUS_NONE
|
||||||
|
)
|
||||||
|
_tackle_equip_button.mouse_filter = (
|
||||||
|
Control.MOUSE_FILTER_STOP
|
||||||
|
if action_interactive
|
||||||
|
else Control.MOUSE_FILTER_IGNORE
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _toggle_active_tackle() -> void:
|
func _toggle_active_tackle() -> void:
|
||||||
|
|
@ -2489,10 +2577,6 @@ func _update_shell_layout() -> void:
|
||||||
_tackle_rest_position = Vector2.ZERO
|
_tackle_rest_position = Vector2.ZERO
|
||||||
_bag_outer_wall.position = INVENTORY_MAIN_POSITION
|
_bag_outer_wall.position = INVENTORY_MAIN_POSITION
|
||||||
_bag_outer_wall.size = INVENTORY_MAIN_SIZE
|
_bag_outer_wall.size = INVENTORY_MAIN_SIZE
|
||||||
_bag_filter_tabs.position = (
|
|
||||||
INVENTORY_MAIN_POSITION + INVENTORY_HEADER_INSET
|
|
||||||
)
|
|
||||||
_bag_filter_tabs.size = Vector2(320.0, INVENTORY_HEADER_HEIGHT)
|
|
||||||
_bag_host.custom_minimum_size = (
|
_bag_host.custom_minimum_size = (
|
||||||
Vector2(520.0, 152.0) if compact else Vector2(788.0, 358.0)
|
Vector2(520.0, 152.0) if compact else Vector2(788.0, 358.0)
|
||||||
)
|
)
|
||||||
|
|
@ -2932,7 +3016,10 @@ func _set_content_interactive(interactive: bool) -> void:
|
||||||
else Control.MOUSE_FILTER_IGNORE
|
else Control.MOUSE_FILTER_IGNORE
|
||||||
)
|
)
|
||||||
for tab: Button in [
|
for tab: Button in [
|
||||||
_cooler_sub_tab, _bag_sub_tab, _tackle_sub_tab,
|
_cooler_sub_tab,
|
||||||
|
_bag_sub_tab,
|
||||||
|
_items_sub_tab,
|
||||||
|
_tackle_sub_tab,
|
||||||
]:
|
]:
|
||||||
var tab_interactive: bool = (
|
var tab_interactive: bool = (
|
||||||
interactive and _is_inventory_section(_current_section)
|
interactive and _is_inventory_section(_current_section)
|
||||||
|
|
@ -3014,6 +3101,10 @@ func _set_content_interactive(interactive: bool) -> void:
|
||||||
if bag_interactive
|
if bag_interactive
|
||||||
else Control.MOUSE_FILTER_IGNORE
|
else Control.MOUSE_FILTER_IGNORE
|
||||||
)
|
)
|
||||||
|
var tackle_interactive: bool = (
|
||||||
|
interactive and _current_section == Section.TACKLE_BOX
|
||||||
|
)
|
||||||
|
_apply_tackle_interactivity(tackle_interactive)
|
||||||
|
|
||||||
|
|
||||||
func _cancel_presentation_tween() -> void:
|
func _cancel_presentation_tween() -> void:
|
||||||
|
|
@ -3117,9 +3208,9 @@ func _on_hotbar_changed() -> void:
|
||||||
func _refresh_bag() -> void:
|
func _refresh_bag() -> void:
|
||||||
if not is_node_ready():
|
if not is_node_ready():
|
||||||
return
|
return
|
||||||
var owned_items: Array[OwnedItemType] = (
|
var owned_items: Array[OwnedItemType] = []
|
||||||
_bag.get_all_items() if _bag != null else []
|
if _bag != null:
|
||||||
)
|
owned_items = _bag.get_all_items()
|
||||||
var filtered_items: Array[OwnedItemType] = []
|
var filtered_items: Array[OwnedItemType] = []
|
||||||
for owned: OwnedItemType in owned_items:
|
for owned: OwnedItemType in owned_items:
|
||||||
var item: ItemDataType = (
|
var item: ItemDataType = (
|
||||||
|
|
@ -3145,16 +3236,12 @@ func _refresh_bag() -> void:
|
||||||
owned_items = filtered_items
|
owned_items = filtered_items
|
||||||
owned_items.sort_custom(_sort_bag_items)
|
owned_items.sort_custom(_sort_bag_items)
|
||||||
_sorted_bag_items = owned_items
|
_sorted_bag_items = owned_items
|
||||||
_equipment_filter.button_pressed = _bag_view == BagView.EQUIPMENT
|
|
||||||
_consumables_filter.button_pressed = (
|
|
||||||
_bag_view == BagView.CONSUMABLES
|
|
||||||
)
|
|
||||||
_bag_empty.visible = owned_items.is_empty()
|
_bag_empty.visible = owned_items.is_empty()
|
||||||
_bag_empty_state.visible = owned_items.is_empty()
|
_bag_empty_state.visible = owned_items.is_empty()
|
||||||
_bag_empty_state.text = (
|
_bag_empty_state.text = (
|
||||||
"No equipment in your Bag."
|
"No equipment in your Bag."
|
||||||
if _bag_view == BagView.EQUIPMENT
|
if _bag_view == BagView.EQUIPMENT
|
||||||
else "No consumables in your Bag."
|
else "No items in your Bag."
|
||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
not _selected_bag_item_id.is_empty()
|
not _selected_bag_item_id.is_empty()
|
||||||
|
|
@ -3209,7 +3296,7 @@ func _sync_bag_item_nodes(owned_items: Array[OwnedItemType]) -> void:
|
||||||
continue
|
continue
|
||||||
var removed := _bag_item_nodes[item_id] as BagItemSpriteType
|
var removed := _bag_item_nodes[item_id] as BagItemSpriteType
|
||||||
_bag_item_nodes.erase(item_id)
|
_bag_item_nodes.erase(item_id)
|
||||||
_release_focus_from(removed, _bag_sub_tab)
|
_release_focus_from(removed, _active_bag_tab())
|
||||||
removed.queue_free()
|
removed.queue_free()
|
||||||
_layout_bag_items()
|
_layout_bag_items()
|
||||||
_configure_bag_item_focus()
|
_configure_bag_item_focus()
|
||||||
|
|
@ -3270,6 +3357,7 @@ func _layout_bag_items() -> void:
|
||||||
func _configure_bag_item_focus() -> void:
|
func _configure_bag_item_focus() -> void:
|
||||||
if _current_section != Section.BAG:
|
if _current_section != Section.BAG:
|
||||||
return
|
return
|
||||||
|
var active_tab: Button = _active_bag_tab()
|
||||||
var controls: Array[BagItemSpriteType] = []
|
var controls: Array[BagItemSpriteType] = []
|
||||||
for owned: OwnedItemType in _sorted_bag_items:
|
for owned: OwnedItemType in _sorted_bag_items:
|
||||||
var item_node := _bag_item_nodes.get(
|
var item_node := _bag_item_nodes.get(
|
||||||
|
|
@ -3278,9 +3366,9 @@ func _configure_bag_item_focus() -> void:
|
||||||
if item_node != null:
|
if item_node != null:
|
||||||
controls.append(item_node)
|
controls.append(item_node)
|
||||||
if controls.is_empty():
|
if controls.is_empty():
|
||||||
_bag_sub_tab.focus_neighbor_bottom = NodePath()
|
active_tab.focus_neighbor_bottom = NodePath()
|
||||||
return
|
return
|
||||||
_bag_sub_tab.focus_neighbor_bottom = _bag_sub_tab.get_path_to(
|
active_tab.focus_neighbor_bottom = active_tab.get_path_to(
|
||||||
controls.front()
|
controls.front()
|
||||||
)
|
)
|
||||||
for index: int in controls.size():
|
for index: int in controls.size():
|
||||||
|
|
@ -3296,7 +3384,7 @@ func _configure_bag_item_focus() -> void:
|
||||||
controls[right_index]
|
controls[right_index]
|
||||||
)
|
)
|
||||||
control.focus_neighbor_top = (
|
control.focus_neighbor_top = (
|
||||||
control.get_path_to(_bag_sub_tab)
|
control.get_path_to(active_tab)
|
||||||
if index < 3
|
if index < 3
|
||||||
else control.get_path_to(controls[index - 3])
|
else control.get_path_to(controls[index - 3])
|
||||||
)
|
)
|
||||||
|
|
@ -3305,6 +3393,14 @@ func _configure_bag_item_focus() -> void:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _active_bag_tab() -> Button:
|
||||||
|
return (
|
||||||
|
_items_sub_tab
|
||||||
|
if _bag_view == BagView.CONSUMABLES
|
||||||
|
else _bag_sub_tab
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _sort_bag_items(a: OwnedItemType, b: OwnedItemType) -> bool:
|
func _sort_bag_items(a: OwnedItemType, b: OwnedItemType) -> bool:
|
||||||
var item_a: ItemDataType = _item_catalog.get_item_by_id(a.item_id)
|
var item_a: ItemDataType = _item_catalog.get_item_by_id(a.item_id)
|
||||||
var item_b: ItemDataType = _item_catalog.get_item_by_id(b.item_id)
|
var item_b: ItemDataType = _item_catalog.get_item_by_id(b.item_id)
|
||||||
|
|
@ -3355,7 +3451,7 @@ func _update_bag_detail() -> void:
|
||||||
_bag_detail_data.text = (
|
_bag_detail_data.text = (
|
||||||
"%s\nquantity: %d\n%s\n%s\n%s"
|
"%s\nquantity: %d\n%s\n%s\n%s"
|
||||||
% [
|
% [
|
||||||
item.get_category_name(),
|
_item_category_display_name(item),
|
||||||
quantity,
|
quantity,
|
||||||
item_state,
|
item_state,
|
||||||
hotbar_assignment,
|
hotbar_assignment,
|
||||||
|
|
@ -3374,7 +3470,7 @@ func _update_bag_detail() -> void:
|
||||||
_bag_sprite_detail_data.text = (
|
_bag_sprite_detail_data.text = (
|
||||||
"%s • quantity: %d\n%s • %s\n%s"
|
"%s • quantity: %d\n%s • %s\n%s"
|
||||||
% [
|
% [
|
||||||
item.get_category_name(),
|
_item_category_display_name(item),
|
||||||
quantity,
|
quantity,
|
||||||
item_state,
|
item_state,
|
||||||
hotbar_assignment,
|
hotbar_assignment,
|
||||||
|
|
@ -3388,6 +3484,12 @@ func _update_bag_detail() -> void:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _item_category_display_name(item: ItemDataType) -> String:
|
||||||
|
if item.category == ItemDataType.Category.CONSUMABLE:
|
||||||
|
return "item"
|
||||||
|
return item.get_category_name()
|
||||||
|
|
||||||
|
|
||||||
func _close_bag_detail() -> void:
|
func _close_bag_detail() -> void:
|
||||||
_bag_detail_constellation.visible = false
|
_bag_detail_constellation.visible = false
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ z_index = 40
|
||||||
layout_mode = 0
|
layout_mode = 0
|
||||||
offset_left = 150.0
|
offset_left = 150.0
|
||||||
offset_top = 136.0
|
offset_top = 136.0
|
||||||
offset_right = 534.0
|
offset_right = 664.0
|
||||||
offset_bottom = 174.0
|
offset_bottom = 174.0
|
||||||
mouse_filter = 1
|
mouse_filter = 1
|
||||||
theme_override_constants/separation = 6
|
theme_override_constants/separation = 6
|
||||||
|
|
@ -103,6 +103,16 @@ text = "Equipment"
|
||||||
script = ExtResource("23_organizer_tab")
|
script = ExtResource("23_organizer_tab")
|
||||||
palette_index = 2
|
palette_index = 2
|
||||||
|
|
||||||
|
[node name="ItemsSubTab" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/InventorySubTabs"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
custom_minimum_size = Vector2(124, 38)
|
||||||
|
layout_mode = 2
|
||||||
|
toggle_mode = true
|
||||||
|
button_group = null
|
||||||
|
text = "Items"
|
||||||
|
script = ExtResource("23_organizer_tab")
|
||||||
|
palette_index = 2
|
||||||
|
|
||||||
[node name="TackleSubTab" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/InventorySubTabs"]
|
[node name="TackleSubTab" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/InventorySubTabs"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(124, 38)
|
custom_minimum_size = Vector2(124, 38)
|
||||||
|
|
@ -485,30 +495,6 @@ offset_right = 1280.0
|
||||||
offset_bottom = 720.0
|
offset_bottom = 720.0
|
||||||
mouse_filter = 1
|
mouse_filter = 1
|
||||||
|
|
||||||
[node name="BagFilterTabs" type="HBoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/BagPage"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
z_index = 55
|
|
||||||
layout_mode = 0
|
|
||||||
offset_left = 122.0
|
|
||||||
offset_top = 166.0
|
|
||||||
offset_right = 442.0
|
|
||||||
offset_bottom = 206.0
|
|
||||||
theme_override_constants/separation = 8
|
|
||||||
|
|
||||||
[node name="EquipmentFilter" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/BagPage/BagFilterTabs"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
custom_minimum_size = Vector2(150, 40)
|
|
||||||
layout_mode = 2
|
|
||||||
toggle_mode = true
|
|
||||||
text = "Equipment"
|
|
||||||
|
|
||||||
[node name="ConsumablesFilter" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/BagPage/BagFilterTabs"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
custom_minimum_size = Vector2(150, 40)
|
|
||||||
layout_mode = 2
|
|
||||||
toggle_mode = true
|
|
||||||
text = "Consumables"
|
|
||||||
|
|
||||||
[node name="BagOuterWall" type="PanelContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/BagPage"]
|
[node name="BagOuterWall" type="PanelContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/BagPage"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
z_index = 50
|
z_index = 50
|
||||||
|
|
@ -532,7 +518,7 @@ layout_mode = 2
|
||||||
[node name="BagInnerMargin" type="MarginContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/BagPage/BagOuterWall/BagOuterMargin/BagInnerLiner"]
|
[node name="BagInnerMargin" type="MarginContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/BagPage/BagOuterWall/BagOuterMargin/BagInnerLiner"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/margin_left = 18
|
theme_override_constants/margin_left = 18
|
||||||
theme_override_constants/margin_top = 58
|
theme_override_constants/margin_top = 18
|
||||||
theme_override_constants/margin_right = 18
|
theme_override_constants/margin_right = 18
|
||||||
theme_override_constants/margin_bottom = 18
|
theme_override_constants/margin_bottom = 18
|
||||||
|
|
||||||
|
|
@ -646,47 +632,114 @@ theme_override_constants/margin_bottom = 20
|
||||||
|
|
||||||
[node name="Layout" type="VBoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin"]
|
[node name="Layout" type="VBoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/separation = 14
|
|
||||||
|
|
||||||
[node name="TackleFilterTabs" type="HBoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
theme_override_constants/separation = 8
|
theme_override_constants/separation = 8
|
||||||
|
|
||||||
[node name="BaitFilter" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout/TackleFilterTabs"]
|
[node name="TackleColumns" type="HBoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout"]
|
||||||
unique_name_in_owner = true
|
|
||||||
custom_minimum_size = Vector2(150, 40)
|
|
||||||
layout_mode = 2
|
|
||||||
toggle_mode = true
|
|
||||||
text = "Bait"
|
|
||||||
|
|
||||||
[node name="LuresFilter" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout/TackleFilterTabs"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
custom_minimum_size = Vector2(150, 40)
|
|
||||||
layout_mode = 2
|
|
||||||
toggle_mode = true
|
|
||||||
text = "Lures"
|
|
||||||
|
|
||||||
[node name="TackleScroll" type="ScrollContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout"]
|
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
horizontal_scroll_mode = 0
|
theme_override_constants/separation = 18
|
||||||
|
|
||||||
[node name="TackleItemList" type="VBoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout/TackleScroll"]
|
[node name="BaitColumn" type="VBoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout/TackleColumns"]
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
theme_override_constants/separation = 8
|
theme_override_constants/separation = 8
|
||||||
|
|
||||||
[node name="TackleEmpty" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout"]
|
[node name="BaitHeading" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout/TackleColumns/BaitColumn"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 26
|
||||||
|
text = "bait"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="BaitBody" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout/TackleColumns/BaitColumn"]
|
||||||
|
layout_mode = 2
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="BaitScroll" type="ScrollContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout/TackleColumns/BaitColumn/BaitBody"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
horizontal_scroll_mode = 0
|
||||||
|
|
||||||
|
[node name="BaitItemList" type="GridContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout/TackleColumns/BaitColumn/BaitBody/BaitScroll"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_constants/h_separation = 28
|
||||||
|
theme_override_constants/v_separation = 12
|
||||||
|
columns = 3
|
||||||
|
|
||||||
|
[node name="BaitEmpty" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout/TackleColumns/BaitColumn/BaitBody"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
text = "No bait collected."
|
text = "No bait collected."
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="TackleDivider" type="ColorRect" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout/TackleColumns"]
|
||||||
|
custom_minimum_size = Vector2(2, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
color = Color(0.204, 0.482, 0.553, 0.65)
|
||||||
|
|
||||||
|
[node name="LureColumn" type="VBoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout/TackleColumns"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_constants/separation = 8
|
||||||
|
|
||||||
|
[node name="LureHeading" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout/TackleColumns/LureColumn"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 26
|
||||||
|
text = "lures"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="LureBody" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout/TackleColumns/LureColumn"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="LureScroll" type="ScrollContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout/TackleColumns/LureColumn/LureBody"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
horizontal_scroll_mode = 0
|
||||||
|
|
||||||
|
[node name="LureItemList" type="GridContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout/TackleColumns/LureColumn/LureBody/LureScroll"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_constants/h_separation = 28
|
||||||
|
theme_override_constants/v_separation = 12
|
||||||
|
columns = 3
|
||||||
|
|
||||||
|
[node name="LureEmpty" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleMainPanel/Margin/Layout/TackleColumns/LureColumn/LureBody"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
text = "No lures collected."
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
[node name="TackleDetailPanel" type="PanelContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage"]
|
[node name="TackleDetailPanel" type="PanelContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 0
|
layout_mode = 0
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,6 @@ func setup(
|
||||||
|
|
||||||
func activate() -> void:
|
func activate() -> void:
|
||||||
_refresh()
|
_refresh()
|
||||||
UtilityPageStyle.animate_in(self)
|
|
||||||
_focus_first()
|
_focus_first()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -82,7 +81,6 @@ func _build() -> void:
|
||||||
tab_row.add_child(_count_label)
|
tab_row.add_child(_count_label)
|
||||||
_build_host_settings(root)
|
_build_host_settings(root)
|
||||||
var scroll := ScrollContainer.new()
|
var scroll := ScrollContainer.new()
|
||||||
scroll.custom_minimum_size = Vector2(0, 310)
|
|
||||||
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||||
root.add_child(scroll)
|
root.add_child(scroll)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,18 @@ const MARKER_MODE_SHADER: Shader = preload(
|
||||||
const GRID_MODE_ICON: Texture2D = preload(
|
const GRID_MODE_ICON: Texture2D = preload(
|
||||||
"res://items/icons/art/art_kit_grid_light.png"
|
"res://items/icons/art/art_kit_grid_light.png"
|
||||||
)
|
)
|
||||||
|
const BRUSH_SIZE_ICONS: Dictionary[int, Texture2D] = {
|
||||||
|
1: preload("res://items/icons/art/art_kit_marker_tip_fine.png"),
|
||||||
|
2: preload("res://items/icons/art/art_kit_marker_tip_thin.png"),
|
||||||
|
3: preload("res://items/icons/art/art_kit_marker_tip_mid.png"),
|
||||||
|
4: preload("res://items/icons/art/art_kit_marker_tip_thick.png"),
|
||||||
|
}
|
||||||
|
const GRID_SIZE_ICONS: Dictionary[int, Texture2D] = {
|
||||||
|
16: preload("res://items/icons/art/art_kit_grid_small_light.png"),
|
||||||
|
32: preload("res://items/icons/art/art_kit_grid_medium_light.png"),
|
||||||
|
64: preload("res://items/icons/art/art_kit_grid_large_light.png"),
|
||||||
|
128: preload("res://items/icons/art/art_kit_grid_xl_light.png"),
|
||||||
|
}
|
||||||
const TOOLBAR_WIDTH: float = 510.0
|
const TOOLBAR_WIDTH: float = 510.0
|
||||||
const TOOLBAR_HEIGHT: float = 373.0
|
const TOOLBAR_HEIGHT: float = 373.0
|
||||||
const COLOR_RAIL_WIDTH: float = 46.0
|
const COLOR_RAIL_WIDTH: float = 46.0
|
||||||
|
|
@ -41,6 +53,12 @@ func _ready() -> void:
|
||||||
UtilityPageStyleType.apply_ocean_button(_mode_button)
|
UtilityPageStyleType.apply_ocean_button(_mode_button)
|
||||||
UtilityPageStyleType.apply_ocean_button(_brush_option)
|
UtilityPageStyleType.apply_ocean_button(_brush_option)
|
||||||
UtilityPageStyleType.apply_ocean_button(_grid_option)
|
UtilityPageStyleType.apply_ocean_button(_grid_option)
|
||||||
|
for size_option: OptionButton in [_brush_option, _grid_option]:
|
||||||
|
size_option.expand_icon = true
|
||||||
|
size_option.add_theme_constant_override("icon_max_width", 40)
|
||||||
|
size_option.get_popup().add_theme_constant_override(
|
||||||
|
"icon_max_width", 40
|
||||||
|
)
|
||||||
for button: Button in _action_buttons():
|
for button: Button in _action_buttons():
|
||||||
UtilityPageStyleType.apply_ocean_button(button)
|
UtilityPageStyleType.apply_ocean_button(button)
|
||||||
button.custom_minimum_size = Vector2(48, 44)
|
button.custom_minimum_size = Vector2(48, 44)
|
||||||
|
|
@ -188,10 +206,26 @@ func owns_pointer_event(event: InputEvent) -> bool:
|
||||||
func _build_options() -> void:
|
func _build_options() -> void:
|
||||||
_brush_option.clear()
|
_brush_option.clear()
|
||||||
for brush_size: int in PlayerArtUnlocks.BRUSH_SIZES:
|
for brush_size: int in PlayerArtUnlocks.BRUSH_SIZES:
|
||||||
_brush_option.add_item("%d×" % brush_size, brush_size)
|
_brush_option.add_icon_item(
|
||||||
|
BRUSH_SIZE_ICONS[brush_size],
|
||||||
|
"",
|
||||||
|
brush_size,
|
||||||
|
)
|
||||||
|
_brush_option.set_item_tooltip(
|
||||||
|
_brush_option.item_count - 1,
|
||||||
|
"%d× marker size" % brush_size,
|
||||||
|
)
|
||||||
_grid_option.clear()
|
_grid_option.clear()
|
||||||
for grid_size: int in PlayerArtUnlocks.GRID_SIZES:
|
for grid_size: int in PlayerArtUnlocks.GRID_SIZES:
|
||||||
_grid_option.add_item("%d×" % grid_size, grid_size)
|
_grid_option.add_icon_item(
|
||||||
|
GRID_SIZE_ICONS[grid_size],
|
||||||
|
"",
|
||||||
|
grid_size,
|
||||||
|
)
|
||||||
|
_grid_option.set_item_tooltip(
|
||||||
|
_grid_option.item_count - 1,
|
||||||
|
"%d×%d grid" % [grid_size, grid_size],
|
||||||
|
)
|
||||||
for child: Node in _color_list.get_children():
|
for child: Node in _color_list.get_children():
|
||||||
child.queue_free()
|
child.queue_free()
|
||||||
_color_buttons.clear()
|
_color_buttons.clear()
|
||||||
|
|
|
||||||
|
|
@ -41,13 +41,21 @@ theme_override_constants/separation = 5
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(80, 48)
|
custom_minimum_size = Vector2(80, 48)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
tooltip_text = "brush size"
|
texture_filter = 1
|
||||||
|
tooltip_text = "marker size"
|
||||||
|
accessibility_name = "marker size"
|
||||||
|
expand_icon = true
|
||||||
|
fit_to_longest_item = false
|
||||||
|
|
||||||
[node name="GridOption" type="OptionButton" parent="TopPanel/Top"]
|
[node name="GridOption" type="OptionButton" parent="TopPanel/Top"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(92, 48)
|
custom_minimum_size = Vector2(92, 48)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
texture_filter = 1
|
||||||
tooltip_text = "grid size"
|
tooltip_text = "grid size"
|
||||||
|
accessibility_name = "grid size"
|
||||||
|
expand_icon = true
|
||||||
|
fit_to_longest_item = false
|
||||||
|
|
||||||
[node name="EraserButton" type="Button" parent="TopPanel/Top"]
|
[node name="EraserButton" type="Button" parent="TopPanel/Top"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
|
||||||
|
|
@ -27,12 +27,97 @@ const LIGHT_TEXT: Color = Color("f5eed9")
|
||||||
const DISABLED_TEXT: Color = Color(0.33, 0.36, 0.35, 0.72)
|
const DISABLED_TEXT: Color = Color(0.33, 0.36, 0.35, 0.72)
|
||||||
const MOTION_TWEEN_META: StringName = &"utility_page_motion_tween"
|
const MOTION_TWEEN_META: StringName = &"utility_page_motion_tween"
|
||||||
const LAPTOP_RECT: Rect2 = Rect2(66.0, 132.0, 1148.0, 520.0)
|
const LAPTOP_RECT: Rect2 = Rect2(66.0, 132.0, 1148.0, 520.0)
|
||||||
|
const SUPPLY_BADGE_MIN_WIDTH: float = 46.0
|
||||||
|
const SUPPLY_BADGE_HEIGHT: float = 24.0
|
||||||
|
const SUPPLY_BADGE_EDGE_MARGIN: float = 3.0
|
||||||
|
const SUPPLY_BADGE_X_OFFSET: float = 18.0
|
||||||
|
const SUPPLY_BADGE_ROTATION_DEGREES: float = 15.0
|
||||||
|
const SUPPLY_BADGE_HORIZONTAL_PADDING: float = 10.0
|
||||||
|
const SUPPLY_BADGE_FONT_SIZE: int = 13
|
||||||
|
const SUPPLY_BADGE_COLOR: Color = Color("0b5558")
|
||||||
|
const SUPPLY_BADGE_TEXT_COLOR: Color = Color("e7f5f4")
|
||||||
|
|
||||||
|
|
||||||
static func apply_page(root: Control) -> void:
|
static func apply_page(root: Control) -> void:
|
||||||
root.add_theme_font_override("font", TuffyFont)
|
root.add_theme_font_override("font", TuffyFont)
|
||||||
|
|
||||||
|
|
||||||
|
static func supply_quantity_text(current: int, total: int) -> String:
|
||||||
|
return "%d/%d" % [maxi(current, 0), maxi(total, 0)]
|
||||||
|
|
||||||
|
|
||||||
|
static func add_supply_quantity_badge(
|
||||||
|
parent: Control,
|
||||||
|
current: int,
|
||||||
|
total: int,
|
||||||
|
badge_name: StringName = &"SupplyQuantityBadge",
|
||||||
|
top_margin: float = SUPPLY_BADGE_EDGE_MARGIN,
|
||||||
|
) -> Panel:
|
||||||
|
var badge := Panel.new()
|
||||||
|
badge.name = badge_name
|
||||||
|
badge.set_anchors_preset(Control.PRESET_TOP_RIGHT)
|
||||||
|
badge.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
badge.z_index = 3
|
||||||
|
parent.add_child(badge)
|
||||||
|
var quantity_label := Label.new()
|
||||||
|
quantity_label.name = "Quantity"
|
||||||
|
quantity_label.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||||
|
quantity_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
quantity_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
quantity_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||||
|
badge.add_child(quantity_label)
|
||||||
|
configure_supply_quantity_badge(
|
||||||
|
badge, quantity_label, current, total, top_margin
|
||||||
|
)
|
||||||
|
return badge
|
||||||
|
|
||||||
|
|
||||||
|
static func configure_supply_quantity_badge(
|
||||||
|
badge: Panel,
|
||||||
|
quantity_label: Label,
|
||||||
|
current: int,
|
||||||
|
total: int,
|
||||||
|
top_margin: float = SUPPLY_BADGE_EDGE_MARGIN,
|
||||||
|
) -> void:
|
||||||
|
var supply_text := supply_quantity_text(current, total)
|
||||||
|
var text_width: float = TuffyFont.get_string_size(
|
||||||
|
supply_text,
|
||||||
|
HORIZONTAL_ALIGNMENT_LEFT,
|
||||||
|
-1.0,
|
||||||
|
SUPPLY_BADGE_FONT_SIZE,
|
||||||
|
).x
|
||||||
|
var badge_width: float = maxf(
|
||||||
|
SUPPLY_BADGE_MIN_WIDTH,
|
||||||
|
ceilf(text_width + SUPPLY_BADGE_HORIZONTAL_PADDING),
|
||||||
|
)
|
||||||
|
badge.offset_left = (
|
||||||
|
-badge_width - SUPPLY_BADGE_EDGE_MARGIN + SUPPLY_BADGE_X_OFFSET
|
||||||
|
)
|
||||||
|
badge.offset_top = top_margin
|
||||||
|
badge.offset_right = -SUPPLY_BADGE_EDGE_MARGIN + SUPPLY_BADGE_X_OFFSET
|
||||||
|
badge.offset_bottom = top_margin + SUPPLY_BADGE_HEIGHT
|
||||||
|
badge.pivot_offset = Vector2(
|
||||||
|
badge_width * 0.5,
|
||||||
|
SUPPLY_BADGE_HEIGHT * 0.5,
|
||||||
|
)
|
||||||
|
badge.rotation_degrees = SUPPLY_BADGE_ROTATION_DEGREES
|
||||||
|
var badge_style := rounded_style(
|
||||||
|
SUPPLY_BADGE_COLOR,
|
||||||
|
roundi(SUPPLY_BADGE_HEIGHT * 0.5),
|
||||||
|
)
|
||||||
|
badge_style.anti_aliasing = false
|
||||||
|
badge.add_theme_stylebox_override("panel", badge_style)
|
||||||
|
quantity_label.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||||
|
quantity_label.text = supply_text
|
||||||
|
quantity_label.add_theme_font_override("font", TuffyFont)
|
||||||
|
quantity_label.add_theme_font_size_override(
|
||||||
|
"font_size", SUPPLY_BADGE_FONT_SIZE
|
||||||
|
)
|
||||||
|
quantity_label.add_theme_color_override(
|
||||||
|
"font_color", SUPPLY_BADGE_TEXT_COLOR
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
static func rounded_style(color: Color, radius: int) -> StyleBoxFlat:
|
static func rounded_style(color: Color, radius: int) -> StyleBoxFlat:
|
||||||
var style := StyleBoxFlat.new()
|
var style := StyleBoxFlat.new()
|
||||||
style.bg_color = color
|
style.bg_color = color
|
||||||
|
|
@ -46,6 +131,7 @@ static func build_laptop_screen(
|
||||||
laptop_rect: Rect2 = LAPTOP_RECT,
|
laptop_rect: Rect2 = LAPTOP_RECT,
|
||||||
) -> MarginContainer:
|
) -> MarginContainer:
|
||||||
var laptop := PanelContainer.new()
|
var laptop := PanelContainer.new()
|
||||||
|
laptop.name = "UtilityMainBox"
|
||||||
laptop.position = laptop_rect.position
|
laptop.position = laptop_rect.position
|
||||||
laptop.size = laptop_rect.size
|
laptop.size = laptop_rect.size
|
||||||
laptop.add_theme_stylebox_override(
|
laptop.add_theme_stylebox_override(
|
||||||
|
|
@ -53,12 +139,21 @@ static func build_laptop_screen(
|
||||||
)
|
)
|
||||||
root.add_child(laptop)
|
root.add_child(laptop)
|
||||||
|
|
||||||
|
# Page-specific minimum sizes must not resize the shared outer artwork.
|
||||||
|
# Keep the shell in a fixed Control so every utility page retains the same
|
||||||
|
# canonical rectangle even as its internal controls evolve.
|
||||||
|
var fixed_layout := Control.new()
|
||||||
|
fixed_layout.name = "UtilityMainBoxLayout"
|
||||||
|
fixed_layout.clip_contents = true
|
||||||
|
laptop.add_child(fixed_layout)
|
||||||
|
|
||||||
var shell_margin := MarginContainer.new()
|
var shell_margin := MarginContainer.new()
|
||||||
|
shell_margin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||||
shell_margin.add_theme_constant_override("margin_left", 20)
|
shell_margin.add_theme_constant_override("margin_left", 20)
|
||||||
shell_margin.add_theme_constant_override("margin_top", 18)
|
shell_margin.add_theme_constant_override("margin_top", 18)
|
||||||
shell_margin.add_theme_constant_override("margin_right", 20)
|
shell_margin.add_theme_constant_override("margin_right", 20)
|
||||||
shell_margin.add_theme_constant_override("margin_bottom", 24)
|
shell_margin.add_theme_constant_override("margin_bottom", 24)
|
||||||
laptop.add_child(shell_margin)
|
fixed_layout.add_child(shell_margin)
|
||||||
|
|
||||||
var screen := PanelContainer.new()
|
var screen := PanelContainer.new()
|
||||||
screen.add_theme_stylebox_override(
|
screen.add_theme_stylebox_override(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue