Polish world, shop, and artwork presentation

This commit is contained in:
Alexander Sellite 2026-08-13 14:53:39 -04:00
parent 7d38bc962b
commit 117fdbfdaa
94 changed files with 1771 additions and 579 deletions

View file

@ -0,0 +1,15 @@
shader_type canvas_item;
uniform vec4 marker_color : source_color = vec4(1.0, 0.0, 0.0, 1.0);
void fragment() {
vec4 source = texture(TEXTURE, UV) * COLOR;
float red_dominance = source.r - max(source.g, source.b);
float marker_mask = smoothstep(0.04, 0.18, red_dominance);
vec3 colored_marker = marker_color.rgb * source.r;
COLOR = vec4(
mix(source.rgb, colored_marker, marker_mask),
source.a * marker_color.a
);
}

View file

@ -0,0 +1 @@
uid://bsg3cwrnjq6ev

View file

@ -5,6 +5,7 @@ extends PanelContainer
@onready var _heading: Label = %Heading
@onready var _value: Label = %Value
@onready var _currency_icon: TextureRect = %CurrencyIcon
func _ready() -> void:
@ -16,3 +17,10 @@ func _ready() -> void:
func set_content(heading: String, value: String) -> void:
_heading.text = heading
_value.text = value
_currency_icon.visible = false
func set_currency_amount(heading: String, amount: int) -> void:
_heading.text = heading
_value.text = str(amount)
_currency_icon.visible = true

View file

@ -1,7 +1,8 @@
[gd_scene load_steps=3 format=3]
[gd_scene load_steps=4 format=3]
[ext_resource type="Script" path="res://ui/components/bubble_menu/bubble_status_bubble.gd" id="1_status"]
[ext_resource type="Resource" path="res://ui/components/bubble_menu/bubble_menu_profile.tres" id="2_profile"]
[ext_resource type="Texture2D" path="res://items/icons/shop/32_currency.png" id="3_currency"]
[node name="BubbleStatusBubble" type="PanelContainer"]
custom_minimum_size = Vector2(112, 62)
@ -23,7 +24,25 @@ theme_override_font_sizes/font_size = 13
text = "status"
horizontal_alignment = 1
[node name="Value" type="Label" parent="Labels"]
[node name="ValueRow" type="HBoxContainer" parent="Labels"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/separation = 3
alignment = 1
[node name="CurrencyIcon" type="TextureRect" parent="Labels/ValueRow"]
unique_name_in_owner = true
visible = false
custom_minimum_size = Vector2(18, 18)
layout_mode = 2
size_flags_vertical = 4
mouse_filter = 2
texture = ExtResource("3_currency")
expand_mode = 1
stretch_mode = 5
texture_filter = 1
[node name="Value" type="Label" parent="Labels/ValueRow"]
unique_name_in_owner = true
layout_mode = 2
mouse_filter = 2

View file

@ -0,0 +1,48 @@
class_name CurrencyAmount
extends HBoxContainer
@export var amount: int = 0:
set(value):
amount = value
_display_text = str(value)
_refresh_amount()
@export_range(8.0, 64.0, 1.0) var icon_size: float = 18.0:
set(value):
icon_size = value
_refresh_icon_size()
@onready var _icon: TextureRect = %Icon
@onready var _amount_label: Label = %Amount
var _display_text: String = "0"
func _ready() -> void:
mouse_filter = Control.MOUSE_FILTER_IGNORE
_refresh_icon_size()
_refresh_amount()
func set_amount(value: int) -> void:
amount = value
func set_amount_text(value: String) -> void:
_display_text = value
_refresh_amount()
func get_amount_label() -> Label:
if _amount_label != null:
return _amount_label
return get_node_or_null("Amount") as Label
func _refresh_amount() -> void:
if _amount_label != null:
_amount_label.text = _display_text
func _refresh_icon_size() -> void:
if _icon != null:
_icon.custom_minimum_size = Vector2.ONE * icon_size

View file

@ -0,0 +1 @@
uid://b7jx040ujcol

View file

@ -0,0 +1,29 @@
[gd_scene load_steps=3 format=3]
[ext_resource type="Script" path="res://ui/components/currency_amount.gd" id="1_script"]
[ext_resource type="Texture2D" path="res://items/icons/shop/32_currency.png" id="2_coin"]
[node name="CurrencyAmount" type="HBoxContainer"]
mouse_filter = 2
theme_override_constants/separation = 3
alignment = 1
script = ExtResource("1_script")
[node name="Icon" type="TextureRect" parent="."]
unique_name_in_owner = true
custom_minimum_size = Vector2(18, 18)
layout_mode = 2
size_flags_vertical = 4
mouse_filter = 2
texture = ExtResource("2_coin")
expand_mode = 1
stretch_mode = 5
texture_filter = 1
[node name="Amount" type="Label" parent="."]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 4
mouse_filter = 2
text = "0"
vertical_alignment = 1

View file

@ -0,0 +1,28 @@
class_name CurrencyPresentation
extends RefCounted
const ICON_PATH: String = (
"res://items/icons/shop/32_currency.png"
)
const AMOUNT_SCENE: PackedScene = preload(
"res://ui/components/currency_amount.tscn"
)
static func instantiate_amount(
value: int,
icon_size: float = 18.0,
) -> CurrencyAmount:
var display := AMOUNT_SCENE.instantiate() as CurrencyAmount
display.icon_size = icon_size
display.amount = value
return display
static func bbcode_amount(value: int, icon_size: int = 18) -> String:
return "[img=%dx%d]%s[/img] %d" % [
icon_size,
icon_size,
ICON_PATH,
value,
]

View file

@ -0,0 +1 @@
uid://bcvy1l2jqqbmt

View file

@ -30,6 +30,15 @@ const UIMotionType = preload("res://ui/ui_motion.gd")
const FALLBACK_SUPPLY_ICON: Texture2D = preload(
"res://ui/icons/pictograms/x_light.png"
)
const ART_KIT_MARKER_ICON: Texture2D = preload(
"res://items/icons/art/art_kit_marker.png"
)
const ART_KIT_MARKER_SHADER: Shader = preload(
"res://ui/art_kit_marker_icon.gdshader"
)
const CURRENCY_ICON: Texture2D = preload(
"res://items/icons/shop/32_currency.png"
)
signal menu_visibility_changed(is_open: bool)
signal menu_exit_started
@ -71,6 +80,8 @@ const SUPPLY_PRICE_FONT_SIZE: int = 15
const SUPPLY_PRICE_Y: float = 60.0
const SUPPLY_PRICE_HEIGHT: float = 24.0
const SUPPLY_PRICE_HORIZONTAL_PADDING: float = 12.0
const SUPPLY_PRICE_ICON_SIZE: float = 18.0
const SUPPLY_PRICE_ICON_GAP: float = 3.0
@onready var _wallet_label: Label = %WalletLabel
@onready var _shop_panel: PanelContainer = %ShopPanel
@ -79,23 +90,20 @@ const SUPPLY_PRICE_HORIZONTAL_PADDING: float = 12.0
@onready var _shop_cooler_page: Control = %ShopCoolerPage
@onready var _shop_cooler_mount: Control = %ShopCoolerMount
@onready var _shop_body: HBoxContainer = $ShopPanel/Margin/Layout/Body
@onready var _feedback: Label = %Feedback
@onready var _feedback: RichTextLabel = %Feedback
@onready var _shop_tab_bar: HBoxContainer = %ShopTabBar
@onready var _upgrades_content: VBoxContainer = %Upgrades
@onready var _supplies_content: VBoxContainer = %Supplies
@onready var _stock_title: Label = %StockTitle
@onready var _reel_level: Label = %ReelLevel
@onready var _reel_effect: Label = %ReelEffect
@onready var _reel_cost: Label = %ReelCost
@onready var _reel_cost: CurrencyAmount = %ReelCost
@onready var _reel_price_bubble: PanelContainer = %ReelPriceBubble
@onready var _reel_purchase: Button = %ReelPurchase
@onready var _barrier_level: Label = %BarrierLevel
@onready var _barrier_effect: Label = %BarrierEffect
@onready var _barrier_cost: Label = %BarrierCost
@onready var _barrier_cost: CurrencyAmount = %BarrierCost
@onready var _barrier_price_bubble: PanelContainer = %BarrierPriceBubble
@onready var _barrier_purchase: Button = %BarrierPurchase
@onready var _supplies_list: VBoxContainer = %SuppliesList
@onready var _cooler_level: Label = %CoolerLevel
@onready var _cooler_effect: Label = %CoolerEffect
@onready var _cooler_cost: Label = %CoolerCost
@onready var _cooler_cost: CurrencyAmount = %CoolerCost
@onready var _cooler_price_bubble: PanelContainer = %CoolerPriceBubble
@onready var _cooler_purchase: Button = %CoolerPurchase
var _player: PlayerType
@ -136,17 +144,17 @@ func _ready() -> void:
func _request_shop_cooler() -> bool:
if _transaction_in_progress:
_feedback.text = "Finish the current transaction first."
_set_feedback("Finish the current transaction first.")
return false
if not _is_transaction_context_valid():
_feedback.text = "The fishing shop is no longer available."
_set_feedback("The fishing shop is no longer available.")
return false
sell_fish_requested.emit()
return _cooler_page_active
func _focus_shop_section() -> void:
_feedback.text = ""
_set_feedback("")
if _shop_section == ShopSection.UPGRADES:
_reel_purchase.grab_focus()
return
@ -193,6 +201,10 @@ func _select_shop_section(section_index: int, focus_content: bool) -> void:
if _closing:
return
if section_index == int(_shop_section):
if section_index != ShopSection.SELL_FISH:
var showing_upgrades := section_index == ShopSection.UPGRADES
_upgrades_content.visible = showing_upgrades
_supplies_content.visible = not showing_upgrades
_update_shop_tab_selection()
if focus_content:
_focus_shop_section()
@ -236,13 +248,31 @@ func _apply_shop_styles() -> void:
"panel", shop_panel_style
)
for panel: PanelContainer in [
$ShopPanel/Margin/Layout/Body/Upgrades/ReelCard,
$ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard,
$ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard,
_reel_price_bubble,
_barrier_price_bubble,
_cooler_price_bubble,
]:
panel.add_theme_stylebox_override(
"panel", UtilityPageStyleType.row_style()
var price_style := UtilityPageStyleType.rounded_style(
UtilityPageStyleType.OCEAN_FIELD,
18,
)
price_style.content_margin_left = 8.0
price_style.content_margin_right = 8.0
price_style.content_margin_top = 2.0
price_style.content_margin_bottom = 2.0
panel.add_theme_stylebox_override(
"panel", price_style
)
for price: CurrencyAmount in [
_reel_cost,
_barrier_cost,
_cooler_cost,
]:
var amount_label := price.get_amount_label()
amount_label.add_theme_color_override(
"font_color", SUPPLY_PRICE_COLOR
)
amount_label.add_theme_font_size_override("font_size", 18)
for button: BaseButton in [
%CloseButton,
_reel_purchase,
@ -336,7 +366,7 @@ func open_shop() -> bool:
_player.set_camera_input_enabled(false)
_fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, true)
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
_feedback.text = ""
_set_feedback("")
deactivate_shop_cooler_page()
show()
_shop_tab_bar.show()
@ -520,7 +550,6 @@ func _refresh_upgrades() -> void:
return
var reel_level: int = _upgrades.get_reel_speed_level()
var reel_cost: int = _upgrades.get_next_reel_speed_cost()
_reel_level.text = "level %d" % reel_level
_reel_purchase.disabled = (
reel_cost < 0
or _transaction_in_progress
@ -529,27 +558,30 @@ func _refresh_upgrades() -> void:
or not _network_shop.can_request_purchase()
or not _upgrades.can_purchase_reel_speed(_wallet)
)
_reel_purchase.tooltip_text = (
"Purchases are unavailable in this session."
if _network_shop == null or not _network_shop.can_request_purchase()
else ""
)
var reel_effect: String
if reel_cost < 0:
_reel_effect.text = "%.2f×" % _upgrades.get_reel_speed_multiplier()
_reel_cost.text = "max"
reel_effect = "%.2f× reel speed · maximum level" % (
_upgrades.get_reel_speed_multiplier()
)
_reel_price_bubble.hide()
else:
_reel_effect.text = (
_reel_price_bubble.show()
reel_effect = (
"%.2f×%.2f×"
% [
_upgrades.get_reel_speed_multiplier(),
1.0 + float(reel_level + 1) * 0.10,
]
)
_reel_cost.text = "$%d" % reel_cost
_reel_purchase.text = "max" if reel_cost < 0 else "purchase"
_reel_cost.set_amount(reel_cost)
_reel_purchase.tooltip_text = _upgrade_tooltip(
"reel speed",
reel_level,
reel_effect,
)
_reel_purchase.accessibility_name = _reel_purchase.tooltip_text
var barrier_level: int = _upgrades.get_barrier_power_level()
var barrier_cost: int = _upgrades.get_next_barrier_power_cost()
_barrier_level.text = "level %d" % barrier_level
_barrier_purchase.disabled = (
barrier_cost < 0
or _transaction_in_progress
@ -558,20 +590,39 @@ func _refresh_upgrades() -> void:
or not _network_shop.can_request_purchase()
or not _upgrades.can_purchase_barrier_power(_wallet)
)
_barrier_purchase.tooltip_text = _reel_purchase.tooltip_text
var barrier_effect: String
if barrier_cost < 0:
_barrier_effect.text = "%d damage" % _upgrades.get_barrier_damage()
_barrier_cost.text = "max"
barrier_effect = "%d damage · maximum level" % (
_upgrades.get_barrier_damage()
)
_barrier_price_bubble.hide()
else:
_barrier_effect.text = (
_barrier_price_bubble.show()
barrier_effect = (
"%d damage → %d damage"
% [
_upgrades.get_barrier_damage(),
barrier_level + 2,
]
)
_barrier_cost.text = "$%d" % barrier_cost
_barrier_purchase.text = "max" if barrier_cost < 0 else "purchase"
_barrier_cost.set_amount(barrier_cost)
_barrier_purchase.tooltip_text = _upgrade_tooltip(
"rod power",
barrier_level,
barrier_effect,
)
_barrier_purchase.accessibility_name = _barrier_purchase.tooltip_text
func _upgrade_tooltip(
upgrade_name: String,
level: int,
effect: String,
) -> String:
var tooltip := "%s\nlevel %d\n%s" % [upgrade_name, level, effect]
if _network_shop == null or not _network_shop.can_request_purchase():
tooltip += "\nPurchases are unavailable in this session."
return tooltip
func _refresh_supplies() -> void:
@ -632,9 +683,8 @@ func _refresh_supplies() -> void:
var use_icon_tile: bool = (
current_stock_grid != null
)
button.text = "%s\n$%downed %d" % [
button.text = "%s\nowned %d" % [
item.display_name,
FishingShopStockType.get_price(item_id),
owned,
]
var item_tooltip_text: String = item.description
@ -646,27 +696,24 @@ func _refresh_supplies() -> void:
button.text = ""
else:
button.text = (
"%s\nunlock $%d" % [item.display_name, total_cost]
"%s\nunlock and fill" % item.display_name
if not bait_unlocked
else "%s\n$%d each • %d/%d" % [
else "%s\n%d/%d owned" % [
item.display_name,
FishingShopStockType.get_price(item_id),
owned,
item.max_stack,
]
)
item_tooltip_text = (
"%s\nunlock $%d • fills to %d/%d\n%s" % [
"%s\nunlock and fill to %d/%d\n%s" % [
item.display_name,
total_cost,
item.max_stack,
item.max_stack,
item.description,
]
if not bait_unlocked
else "%s\n$%d each • %d/%d\n%s" % [
else "%s\n%d/%d owned\n%s" % [
item.display_name,
FishingShopStockType.get_price(item_id),
owned,
item.max_stack,
item.description,
@ -677,9 +724,8 @@ func _refresh_supplies() -> void:
button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
button.alignment = HORIZONTAL_ALIGNMENT_CENTER
button.text = ""
item_tooltip_text = "%s\n$%downed %d\n%s" % [
item_tooltip_text = "%s\nowned %d\n%s" % [
item.display_name,
FishingShopStockType.get_price(item_id),
owned,
item.description,
]
@ -704,7 +750,11 @@ func _refresh_supplies() -> void:
_add_supply_icon_tile(
current_stock_grid,
button,
FishingShopStockType.get_price(item_id),
(
total_cost
if bait_topoff and not bait_unlocked
else FishingShopStockType.get_price(item_id)
),
)
else:
_supplies_list.add_child(button)
@ -769,14 +819,12 @@ func _add_art_kit_button(parent: GridContainer) -> void:
return
var owned: bool = _bag.get_quantity(ArtShopStockType.ART_KIT_ITEM_ID) > 0
var button: Button = _make_stock_button(
"%s\n$%d%s" % [
"%s\n%s" % [
item.display_name,
ArtShopStockType.ART_KIT_PRICE,
"owned" if owned else "not owned",
],
"%s\n$%d%s\n%s" % [
"%s\n%s\n%s" % [
item.display_name,
ArtShopStockType.ART_KIT_PRICE,
"owned" if owned else "not owned",
item.description,
],
@ -808,19 +856,26 @@ func _add_art_upgrade_button(
)
var unlocked: bool = _art_unlocks.owns_product(product_id)
var button: Button = _make_stock_button(
"%s\n$%d%s" % [
"%s\n%s" % [
ArtShopStockType.get_display_name(product_id),
ArtShopStockType.UPGRADE_PRICE,
"unlocked" if unlocked else "locked",
],
"%s\n$%d%s\n%s" % [
"%s\n%s\n%s" % [
ArtShopStockType.get_display_name(product_id),
ArtShopStockType.UPGRADE_PRICE,
"unlocked" if unlocked else "locked",
ArtShopStockType.get_description(product_id),
],
)
_configure_supply_icon_tile(button, FALLBACK_SUPPLY_ICON)
var marker_color_id: StringName = (
PlayerArtUnlocksType.color_id_for_product(product_id)
)
if marker_color_id.is_empty():
_configure_supply_icon_tile(button, FALLBACK_SUPPLY_ICON)
else:
_configure_marker_icon_tile(
button,
SurfaceDrawingPalette.get_color(marker_color_id),
)
_add_supply_icon_tile(
parent,
button,
@ -863,6 +918,29 @@ func _configure_supply_icon_tile(
button.text = ""
func _configure_marker_icon_tile(
button: Button,
marker_color: Color,
) -> void:
button.custom_minimum_size = SUPPLY_ICON_TILE_SIZE
button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
button.icon = null
button.text = ""
var icon := TextureRect.new()
icon.name = "MarkerIcon"
icon.position = Vector2(4.0, 4.0)
icon.size = Vector2(64.0, 64.0)
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
var marker_material := ShaderMaterial.new()
marker_material.shader = ART_KIT_MARKER_SHADER
marker_material.set_shader_parameter("marker_color", marker_color)
icon.material = marker_material
icon.texture = ART_KIT_MARKER_ICON
button.add_child(icon)
func _add_supply_icon_tile(
parent: GridContainer,
button: Button,
@ -875,7 +953,7 @@ func _add_supply_icon_tile(
button.position = Vector2.ZERO
button.size = SUPPLY_ICON_TILE_SIZE
tile_host.add_child(button)
var price_text := "$%d" % price
var price_text := str(price)
var price_text_width: float = UtilityPageStyleType.TuffyFont.get_string_size(
price_text,
HORIZONTAL_ALIGNMENT_LEFT,
@ -884,7 +962,12 @@ func _add_supply_icon_tile(
).x
var price_width: float = minf(
SUPPLY_ICON_TILE_SIZE.x,
ceilf(price_text_width + SUPPLY_PRICE_HORIZONTAL_PADDING),
ceilf(
price_text_width
+ SUPPLY_PRICE_ICON_SIZE
+ SUPPLY_PRICE_ICON_GAP
+ SUPPLY_PRICE_HORIZONTAL_PADDING
),
)
var price_bubble := PanelContainer.new()
price_bubble.name = "PriceBubble"
@ -905,6 +988,23 @@ func _add_supply_icon_tile(
price_style.content_margin_bottom = 0.0
price_bubble.add_theme_stylebox_override("panel", price_style)
tile_host.add_child(price_bubble)
var price_row := HBoxContainer.new()
price_row.name = "CurrencyAmount"
price_row.mouse_filter = Control.MOUSE_FILTER_IGNORE
price_row.alignment = BoxContainer.ALIGNMENT_CENTER
price_row.add_theme_constant_override(
"separation", int(SUPPLY_PRICE_ICON_GAP)
)
price_bubble.add_child(price_row)
var price_icon := TextureRect.new()
price_icon.name = "CurrencyIcon"
price_icon.custom_minimum_size = Vector2.ONE * SUPPLY_PRICE_ICON_SIZE
price_icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
price_icon.texture = CURRENCY_ICON
price_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
price_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
price_icon.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
price_row.add_child(price_icon)
var price_label := Label.new()
price_label.name = "Price"
price_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
@ -921,7 +1021,7 @@ func _add_supply_icon_tile(
"font_color", SUPPLY_PRICE_COLOR
)
price_label.add_theme_constant_override("outline_size", 0)
price_bubble.add_child(price_label)
price_row.add_child(price_label)
func _refresh_cooler_capacity() -> void:
@ -930,7 +1030,6 @@ func _refresh_cooler_capacity() -> void:
var level: int = _cooler_capacity.get_level()
var cost: int = _cooler_capacity.get_next_cost()
var next_capacity: int = _cooler_capacity.get_next_capacity()
_cooler_level.text = "level %d" % level
_cooler_purchase.disabled = (
cost < 0
or _transaction_in_progress
@ -939,18 +1038,25 @@ func _refresh_cooler_capacity() -> void:
or not _network_shop.can_request_purchase()
or not _cooler_capacity.can_purchase(_wallet)
)
_cooler_purchase.tooltip_text = _reel_purchase.tooltip_text
var cooler_effect: String
if cost < 0:
_cooler_effect.text = "%d fish" % _cooler_capacity.get_capacity()
_cooler_cost.text = "max"
_cooler_purchase.text = "max"
cooler_effect = "%d fish · maximum level" % (
_cooler_capacity.get_capacity()
)
_cooler_price_bubble.hide()
else:
_cooler_effect.text = "%d%d fish" % [
_cooler_price_bubble.show()
cooler_effect = "%d%d fish" % [
_cooler_capacity.get_capacity(),
next_capacity,
]
_cooler_cost.text = "$%d" % cost
_cooler_purchase.text = "purchase"
_cooler_cost.set_amount(cost)
_cooler_purchase.tooltip_text = _upgrade_tooltip(
"cooler capacity",
level,
cooler_effect,
)
_cooler_purchase.accessibility_name = _cooler_purchase.tooltip_text
func _purchase_reel_speed() -> void:
@ -963,65 +1069,65 @@ func _purchase_barrier_power() -> void:
func _purchase_supply(item_id: StringName) -> void:
if _transaction_in_progress or not _is_transaction_context_valid():
_feedback.text = "unable to complete purchase."
_set_feedback("unable to complete purchase.")
return
var owned: int = _bag.get_quantity(item_id)
var item: ItemDataType = _item_catalog.get_item_by_id(item_id)
if item == null:
_feedback.text = "Purchase could not be completed."
_set_feedback("Purchase could not be completed.")
return
var quantity: int = FishingShopStockType.get_purchase_quantity(
item_id, owned, item.max_stack
)
if quantity <= 0 or not _bag.can_add_item(item_id, quantity):
_feedback.text = "Your Bag is full."
_set_feedback("Your Bag is full.")
return
var total_cost: int = FishingShopStockType.get_purchase_cost(
item_id, quantity, _bag.is_bait_unlocked(item_id)
)
if total_cost < 0 or not _wallet.can_afford(total_cost):
_feedback.text = "Not enough fish coin."
_set_feedback("Insufficient funds.")
return
if _network_shop == null:
_feedback.text = "Purchase could not be completed."
_set_feedback("Purchase could not be completed.")
return
_network_shop.request_supply(item_id)
func _purchase_art_kit() -> void:
if _network_shop == null or not _is_transaction_context_valid():
_feedback.text = "Purchase could not be completed."
_set_feedback("Purchase could not be completed.")
return
_network_shop.request_art_kit()
func _purchase_art_upgrade(product_id: StringName) -> void:
if _network_shop == null or not _is_transaction_context_valid():
_feedback.text = "Purchase could not be completed."
_set_feedback("Purchase could not be completed.")
return
_network_shop.request_art_upgrade(product_id)
func _purchase_cooler_capacity() -> void:
if _transaction_in_progress or not _is_transaction_context_valid():
_feedback.text = "unable to complete purchase."
_set_feedback("unable to complete purchase.")
return
var cost: int = _cooler_capacity.get_next_cost()
if cost < 0:
_feedback.text = "Upgrade is already at maximum."
_set_feedback("Upgrade is already at maximum.")
return
if not _wallet.can_afford(cost):
_feedback.text = "Not enough fish coin."
_set_feedback("Insufficient funds.")
return
if _network_shop == null:
_feedback.text = "Purchase could not be completed."
_set_feedback("Purchase could not be completed.")
return
_network_shop.request_cooler_capacity_upgrade()
func _purchase_upgrade(is_reel_speed: bool) -> void:
if _transaction_in_progress or not _is_transaction_context_valid():
_feedback.text = "unable to complete purchase."
_set_feedback("unable to complete purchase.")
return
var cost: int = (
_upgrades.get_next_reel_speed_cost()
@ -1029,13 +1135,13 @@ func _purchase_upgrade(is_reel_speed: bool) -> void:
else _upgrades.get_next_barrier_power_cost()
)
if cost < 0:
_feedback.text = "Upgrade is already at maximum."
_set_feedback("Upgrade is already at maximum.")
return
if not _wallet.can_afford(cost):
_feedback.text = "Not enough fish coin."
_set_feedback("Insufficient funds.")
return
if _network_shop == null:
_feedback.text = "Purchase could not be completed."
_set_feedback("Purchase could not be completed.")
return
if is_reel_speed:
_network_shop.request_reel_speed_upgrade()
@ -1046,7 +1152,7 @@ func _purchase_upgrade(is_reel_speed: bool) -> void:
func _on_network_purchase_pending(_request_id: String) -> void:
_transaction_in_progress = true
if visible:
_feedback.text = "Purchasing…"
_set_feedback("Purchasing…")
_refresh_all()
@ -1062,14 +1168,21 @@ func _on_network_purchase_finished(
_transaction_in_progress = false
if not visible:
return
_feedback.text = (
"Purchase complete. $%d spent." % total_cost
_set_feedback(
(
"Purchase complete • %s spent."
% CurrencyPresentation.bbcode_amount(total_cost, 18)
)
if accepted
else message
)
_refresh_all()
func _set_feedback(message: String) -> void:
_feedback.text = "[center]%s[/center]" % message
func _is_transaction_context_valid() -> bool:
return (
visible

View file

@ -1,12 +1,13 @@
[gd_scene load_steps=8 format=3]
[gd_scene load_steps=9 format=3]
[ext_resource type="Script" path="res://ui/fishing_shop.gd" id="1_script"]
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
[ext_resource type="Texture2D" path="res://items/icons/shop/64_speed_plus.png" id="3_reel"]
[ext_resource type="Texture2D" path="res://items/icons/shop/64_power_plus.png" id="4_barrier"]
[ext_resource type="Texture2D" path="res://items/icons/placeholder/fish_coin.svg" id="5_coin"]
[ext_resource type="Texture2D" path="res://items/icons/shop/32_currency.png" id="5_coin"]
[ext_resource type="Texture2D" path="res://items/icons/equipment/64_cooler_plus.png" id="6_cooler"]
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/x_light.png" id="7_close"]
[ext_resource type="PackedScene" path="res://ui/components/currency_amount.tscn" id="8_currency"]
[node name="FishingShop" type="Control"]
unique_name_in_owner = true
@ -126,12 +127,16 @@ icon = ExtResource("7_close")
expand_icon = true
icon_max_width = 40
[node name="Feedback" type="Label" parent="ShopPanel/Margin/Layout"]
[node name="Feedback" type="RichTextLabel" parent="ShopPanel/Margin/Layout"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 28)
layout_mode = 2
text = ""
horizontal_alignment = 1
theme_override_colors/font_color = Color(1, 0.82, 0.4, 1)
bbcode_enabled = true
fit_content = true
scroll_active = false
theme_override_colors/default_color = Color(1, 0.82, 0.4, 1)
theme_override_font_sizes/normal_font_size = 20
[node name="ShopTabBar" type="HBoxContainer" parent="."]
unique_name_in_owner = true
@ -184,164 +189,102 @@ layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 5
[node name="ReelCard" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades"]
[node name="UpgradeScroll" type="ScrollContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades"]
layout_mode = 2
size_flags_vertical = 3
horizontal_scroll_mode = 0
[node name="Margin" type="MarginContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard"]
layout_mode = 2
theme_override_constants/margin_left = 9
theme_override_constants/margin_top = 7
theme_override_constants/margin_right = 9
theme_override_constants/margin_bottom = 7
[node name="Row" type="HBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin"]
layout_mode = 2
theme_override_constants/separation = 9
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row"]
custom_minimum_size = Vector2(88, 88)
layout_mode = 2
texture = ExtResource("3_reel")
expand_mode = 1
stretch_mode = 5
texture_filter = 1
[node name="Data" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row"]
[node name="UpgradeGrid" type="GridContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/h_separation = 20
theme_override_constants/v_separation = 20
columns = 3
[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
[node name="ReelTile" type="Control" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid"]
custom_minimum_size = Vector2(144, 168)
layout_mode = 2
text = "reel speed"
theme_override_font_sizes/font_size = 22
mouse_filter = 2
[node name="ReelLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
[node name="ReelPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/ReelTile"]
unique_name_in_owner = true
offset_right = 144.0
offset_bottom = 144.0
tooltip_text = "reel speed"
icon = ExtResource("3_reel")
expand_icon = true
icon_max_width = 128
[node name="ReelPriceBubble" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/ReelTile"]
unique_name_in_owner = true
z_index = 2
offset_left = 22.0
offset_top = 128.0
offset_right = 122.0
offset_bottom = 160.0
mouse_filter = 2
[node name="ReelCost" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/ReelTile/ReelPriceBubble" instance=ExtResource("8_currency")]
unique_name_in_owner = true
layout_mode = 2
text = "level 0"
theme_override_font_sizes/font_size = 18
amount = 50
icon_size = 24.0
[node name="ReelEffect" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
[node name="BarrierTile" type="Control" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid"]
custom_minimum_size = Vector2(144, 168)
layout_mode = 2
mouse_filter = 2
[node name="BarrierPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/BarrierTile"]
unique_name_in_owner = true
offset_right = 144.0
offset_bottom = 144.0
tooltip_text = "rod power"
icon = ExtResource("4_barrier")
expand_icon = true
icon_max_width = 128
[node name="BarrierPriceBubble" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/BarrierTile"]
unique_name_in_owner = true
z_index = 2
offset_left = 22.0
offset_top = 128.0
offset_right = 122.0
offset_bottom = 160.0
mouse_filter = 2
[node name="BarrierCost" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/BarrierTile/BarrierPriceBubble" instance=ExtResource("8_currency")]
unique_name_in_owner = true
layout_mode = 2
text = "1.00× → 1.10×"
theme_override_font_sizes/font_size = 18
amount = 100
icon_size = 24.0
[node name="ReelCost" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
[node name="CoolerTile" type="Control" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid"]
custom_minimum_size = Vector2(144, 168)
layout_mode = 2
mouse_filter = 2
[node name="CoolerPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/CoolerTile"]
unique_name_in_owner = true
offset_right = 144.0
offset_bottom = 144.0
tooltip_text = "cooler capacity"
icon = ExtResource("6_cooler")
expand_icon = true
icon_max_width = 128
[node name="CoolerPriceBubble" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/CoolerTile"]
unique_name_in_owner = true
z_index = 2
offset_left = 22.0
offset_top = 128.0
offset_right = 122.0
offset_bottom = 160.0
mouse_filter = 2
[node name="CoolerCost" parent="ShopPanel/Margin/Layout/Body/Upgrades/UpgradeScroll/UpgradeGrid/CoolerTile/CoolerPriceBubble" instance=ExtResource("8_currency")]
unique_name_in_owner = true
layout_mode = 2
text = "$50"
theme_override_font_sizes/font_size = 18
[node name="ReelPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row"]
unique_name_in_owner = true
layout_mode = 2
text = "purchase"
[node name="BarrierCard" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades"]
layout_mode = 2
[node name="Margin" type="MarginContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard"]
layout_mode = 2
theme_override_constants/margin_left = 9
theme_override_constants/margin_top = 7
theme_override_constants/margin_right = 9
theme_override_constants/margin_bottom = 7
[node name="Row" type="HBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin"]
layout_mode = 2
theme_override_constants/separation = 9
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row"]
custom_minimum_size = Vector2(88, 88)
layout_mode = 2
texture = ExtResource("4_barrier")
expand_mode = 1
stretch_mode = 5
texture_filter = 1
[node name="Data" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row"]
layout_mode = 2
size_flags_horizontal = 3
[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"]
layout_mode = 2
text = "rod power"
theme_override_font_sizes/font_size = 22
[node name="BarrierLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"]
unique_name_in_owner = true
layout_mode = 2
text = "level 0"
theme_override_font_sizes/font_size = 18
[node name="BarrierEffect" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"]
unique_name_in_owner = true
layout_mode = 2
text = "1 damage → 2 damage"
theme_override_font_sizes/font_size = 18
[node name="BarrierCost" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"]
unique_name_in_owner = true
layout_mode = 2
text = "$100"
theme_override_font_sizes/font_size = 18
[node name="BarrierPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row"]
unique_name_in_owner = true
layout_mode = 2
text = "purchase"
[node name="CoolerCard" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades"]
layout_mode = 2
[node name="Margin" type="MarginContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard"]
layout_mode = 2
theme_override_constants/margin_left = 9
theme_override_constants/margin_top = 7
theme_override_constants/margin_right = 9
theme_override_constants/margin_bottom = 7
[node name="Row" type="HBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin"]
layout_mode = 2
theme_override_constants/separation = 9
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"]
custom_minimum_size = Vector2(88, 88)
layout_mode = 2
texture = ExtResource("6_cooler")
expand_mode = 1
stretch_mode = 5
texture_filter = 1
[node name="Data" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"]
layout_mode = 2
size_flags_horizontal = 3
[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
layout_mode = 2
text = "cooler capacity"
theme_override_font_sizes/font_size = 22
[node name="CoolerLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
unique_name_in_owner = true
layout_mode = 2
text = "level 0"
theme_override_font_sizes/font_size = 18
[node name="CoolerEffect" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
unique_name_in_owner = true
layout_mode = 2
text = "12 → 18 fish"
theme_override_font_sizes/font_size = 18
[node name="CoolerCost" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
unique_name_in_owner = true
layout_mode = 2
text = "$75"
theme_override_font_sizes/font_size = 18
[node name="CoolerPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"]
unique_name_in_owner = true
layout_mode = 2
text = "purchase"
amount = 75
icon_size = 24.0

View file

@ -16,6 +16,9 @@ const InventoryNotepadType = preload(
const LogbookPortraitType = preload(
"res://ui/components/logbook_portrait.gd"
)
const CurrencyPresentationType = preload(
"res://ui/currency_presentation.gd"
)
const DETAIL_FADE_DURATION: float = 0.12
const CATEGORY_FADE_DURATION: float = UIMotion.UTILITY_EXIT_DURATION
@ -756,19 +759,17 @@ func _build_known_details(fish: FishDataType) -> void:
_add_detail_row(left_stats, "number caught", "unknown")
_add_detail_row(right_stats, "rarity", fish.get_rarity_name().to_lower())
_add_detail_row(right_stats, "time of day", _availability_text(fish))
_add_detail_row(
_add_currency_detail_row(
right_stats,
"value range",
"%d%d fish coin" % [
FishQualityType.apply_sale_value(
fish.sell_value_min,
FishQualityType.Tier.BORING,
),
FishQualityType.apply_sale_value(
fish.sell_value_max,
FishQualityType.Tier.SHINY,
),
],
FishQualityType.apply_sale_value(
fish.sell_value_min,
FishQualityType.Tier.BORING,
),
FishQualityType.apply_sale_value(
fish.sell_value_max,
FishQualityType.Tier.SHINY,
),
)
_add_detail_row(
right_stats,
@ -878,6 +879,31 @@ func _add_detail_row(
parent.add_child(row)
func _add_currency_detail_row(
parent: VBoxContainer,
row_name: String,
minimum_value: int,
maximum_value: int,
) -> void:
var row := VBoxContainer.new()
row.add_theme_constant_override("separation", 2)
var heading := _field_label(row_name, 14)
heading.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
heading.add_theme_color_override("font_color", MUTED_INK)
row.add_child(heading)
var value: CurrencyAmount = (
CurrencyPresentationType.instantiate_amount(minimum_value, 16.0)
)
value.set_amount_text("%d%d" % [minimum_value, maximum_value])
value.get_amount_label().add_theme_font_override(
"font", InventoryNotepadType.HANDWRITTEN_FONT
)
value.get_amount_label().add_theme_font_size_override("font_size", 16)
value.get_amount_label().add_theme_color_override("font_color", INK)
row.add_child(value)
parent.add_child(row)
func _make_stats_column() -> VBoxContainer:
var column := VBoxContainer.new()
column.size_flags_horizontal = Control.SIZE_EXPAND_FILL

View file

@ -15,6 +15,9 @@ const SALUTATION_LABELS := {
"good_luck_have_fun": "Good Luck Have Fun",
}
const FishQualityType = preload("res://fish/fish_quality.gd")
const CurrencyPresentationType = preload(
"res://ui/currency_presentation.gd"
)
var _service: NetworkMailService
var _reservations: PlayerAssetReservationService
@ -36,14 +39,16 @@ var _signature: Label
var _attachment_kind: OptionButton
var _attachment_choice: OptionButton
var _attachment_amount: LineEdit
var _coin_available: Label
var _coin_available_heading: Label
var _coin_available: CurrencyAmount
var _attachment_amount_currency_icon: TextureRect
var _amount_minus: Button
var _amount_plus: Button
var _attachment_summary: Label
var _attachment_summary: RichTextLabel
var _send_button: Button
var _status: Label
var _letter_text: Label
var _letter_gift: Label
var _letter_gift: RichTextLabel
var _accept: Button
var _decline: Button
var _archive: Button
@ -212,7 +217,7 @@ func _build_compose() -> Control:
_attachment_kind = OptionButton.new()
_attachment_kind.position = Vector2(688, 48)
_attachment_kind.size = Vector2(280, 46)
for label: String in ["No gift", "Fish coin", "Fish", "Consumable"]:
for label: String in ["No gift", "Currency", "Fish", "Consumable"]:
_attachment_kind.add_item(label)
_attachment_kind.item_selected.connect(_refresh_attachment_choices)
page.add_child(_attachment_kind)
@ -225,13 +230,34 @@ func _build_compose() -> Control:
_update_attachment_summary()
)
page.add_child(_attachment_choice)
_coin_available = Label.new()
_coin_available.position = Vector2(688, 158)
_coin_available.size = Vector2(280, 28)
_coin_available_heading = Label.new()
_coin_available_heading.text = "Available"
_coin_available_heading.position = Vector2(688, 158)
_coin_available_heading.size = Vector2(80, 28)
page.add_child(_coin_available_heading)
_coin_available = CurrencyPresentationType.instantiate_amount(0, 18.0)
_coin_available.position = Vector2(770, 158)
_coin_available.size = Vector2(198, 28)
_coin_available.alignment = BoxContainer.ALIGNMENT_BEGIN
page.add_child(_coin_available)
_attachment_amount_currency_icon = TextureRect.new()
_attachment_amount_currency_icon.position = Vector2(746, 204)
_attachment_amount_currency_icon.size = Vector2(18, 18)
_attachment_amount_currency_icon.texture = preload(
"res://items/icons/shop/32_currency.png"
)
_attachment_amount_currency_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
_attachment_amount_currency_icon.stretch_mode = (
TextureRect.STRETCH_KEEP_ASPECT_CENTERED
)
_attachment_amount_currency_icon.texture_filter = (
CanvasItem.TEXTURE_FILTER_NEAREST
)
_attachment_amount_currency_icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
page.add_child(_attachment_amount_currency_icon)
_attachment_amount = LineEdit.new()
_attachment_amount.position = Vector2(746, 190)
_attachment_amount.size = Vector2(164, 46)
_attachment_amount.position = Vector2(770, 190)
_attachment_amount.size = Vector2(140, 46)
_attachment_amount.placeholder_text = "0"
_attachment_amount.text = "0"
_attachment_amount.text_changed.connect(_on_attachment_amount_changed)
@ -248,9 +274,12 @@ func _build_compose() -> Control:
_amount_plus.size = Vector2(48, 46)
_amount_plus.pressed.connect(_step_attachment_amount.bind(1))
page.add_child(_amount_plus)
_attachment_summary = Label.new()
_attachment_summary = RichTextLabel.new()
_attachment_summary.position = Vector2(688, 246)
_attachment_summary.size = Vector2(280, 78)
_attachment_summary.bbcode_enabled = true
_attachment_summary.fit_content = true
_attachment_summary.scroll_active = false
_attachment_summary.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
page.add_child(_attachment_summary)
var cancel := Button.new()
@ -281,9 +310,12 @@ func _build_letter() -> Control:
_letter_text.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
_letter_text.vertical_alignment = VERTICAL_ALIGNMENT_TOP
page.add_child(_letter_text)
_letter_gift = Label.new()
_letter_gift = RichTextLabel.new()
_letter_gift.position = Vector2(730, 46)
_letter_gift.size = Vector2(230, 180)
_letter_gift.bbcode_enabled = true
_letter_gift.fit_content = true
_letter_gift.scroll_active = false
_letter_gift.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
page.add_child(_letter_gift)
_accept = Button.new()
@ -479,13 +511,18 @@ func _refresh_attachment_choices(_index: int) -> void:
_attachment_amount.visible = amount_visible
_amount_minus.visible = amount_visible
_amount_plus.visible = amount_visible
_coin_available.visible = _attachment_kind.selected == 1
var currency_selected: bool = _attachment_kind.selected == 1
_coin_available_heading.visible = currency_selected
_coin_available.visible = currency_selected
_attachment_amount_currency_icon.visible = currency_selected
_attachment_amount.position.x = 770.0 if currency_selected else 746.0
_attachment_amount.size.x = 140.0 if currency_selected else 164.0
match _attachment_kind.selected:
0:
_attachment_choice.add_item("No attachment")
1:
_attachment_choice.add_item("Fish coin")
_coin_available.text = "Available: %d fish coin" % (
_attachment_choice.add_item("Currency")
_coin_available.set_amount(
_reservations.get_available_fish_coin()
)
2:
@ -705,7 +742,9 @@ func _attachment_text(attachment: Dictionary) -> String:
PlayerAssetReservationService.AttachmentType.NONE:
return "No gift attached."
PlayerAssetReservationService.AttachmentType.FISH_COIN:
return "%d fish coin" % int(attachment.get("amount", 0))
return CurrencyPresentationType.bbcode_amount(
int(attachment.get("amount", 0)), 18
)
PlayerAssetReservationService.AttachmentType.FISH:
var fish_catch := _inventory.get_catch_by_id(
StringName(str(attachment.get("catch_id", "")))

View file

@ -14,6 +14,9 @@ const FishInventoryType = preload("res://inventory/fish_inventory.gd")
const InventoryNotepadType = preload(
"res://ui/components/inventory_notepad.gd"
)
const CurrencyPresentationType = preload(
"res://ui/currency_presentation.gd"
)
const OrganizerTabType = preload("res://ui/components/organizer_tab.gd")
const FishPoolType = preload("res://fish/fish_pool.gd")
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
@ -168,7 +171,7 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0)
@onready var _notepad_binding: Label = %BindingDecoration
@onready var _notepad_title: Label = %NotepadTitle
@onready var _notepad_rule: ColorRect = %NotepadRule
@onready var _notepad_wallet_value: Label = %NotepadWalletValue
@onready var _notepad_wallet_value: CurrencyAmount = %NotepadWalletValue
@onready var _notepad_capacity_value: Label = %NotepadCapacityValue
@onready var _cooler_detail_texture: TextureRect = %CoolerDetailTexture
@onready var _cooler_detail_name: Label = %CoolerDetailName
@ -178,7 +181,7 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0)
@onready var _cooler_weight_unit: Label = %CoolerWeightUnit
@onready var _cooler_offer_row: HBoxContainer = %CoolerOfferRow
@onready var _cooler_offer_label: Label = %CoolerOfferLabel
@onready var _cooler_offer_value: Label = %CoolerOfferValue
@onready var _cooler_offer_value: CurrencyAmount = %CoolerOfferValue
@onready var _cooler_selection_summary: Control = %CoolerSelectionSummary
@onready var _cooler_selection_empty: Label = %CoolerSelectionEmpty
@onready var _cooler_selected_count_row: HBoxContainer = %CoolerSelectedCountRow
@ -186,7 +189,7 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0)
@onready var _cooler_selected_count_label: Label = %CoolerSelectedCountLabel
@onready var _cooler_combined_offer_row: HBoxContainer = %CoolerCombinedOfferRow
@onready var _cooler_combined_offer_label: Label = %CoolerCombinedOfferLabel
@onready var _cooler_combined_offer_value: Label = %CoolerCombinedOfferValue
@onready var _cooler_combined_offer_value: CurrencyAmount = %CoolerCombinedOfferValue
@onready var _favorite_bubble: NotepadInkActionType = %FavoriteBubble
@onready var _sell_bubble: NotepadInkActionType = %SellBubble
@onready var _sell_all_bubble: NotepadInkActionType = %SellAllBubble
@ -247,7 +250,7 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0)
@onready var _content_stage: Control = %Content
@onready var _cooler_scroll: ScrollContainer = %CoolerScroll
@onready var _cooler_host: Control = %CoolerHost
@onready var _wallet_balance: Label = %WalletBalance
@onready var _wallet_balance: CurrencyAmount = %WalletBalance
@onready var _header: Control = %Header
@onready var _separator: Control = %Separator
@onready var _wallet_status: BubbleStatusBubbleType = %WalletStatus
@ -266,16 +269,16 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0)
@onready var _bag_detail_data: Label = %BagDetailData
@onready var _sort_option: OptionButton = %SortOption
@onready var _sort_direction: Button = %SortDirection
@onready var _held_value: Label = %HeldValue
@onready var _held_value: CurrencyAmount = %HeldValue
@onready var _cooler_count: Label = %CoolerCount
@onready var _inventory_empty: Label = %InventoryEmpty
@onready var _selection_summary: Label = %SelectionSummary
@onready var _favorite_button: Button = %FavoriteButton
@onready var _sell_button: Button = %SellButton
@onready var _sale_unavailable: Label = %SaleUnavailable
@onready var _transaction_feedback: Label = %TransactionFeedback
@onready var _transaction_feedback: RichTextLabel = %TransactionFeedback
@onready var _sale_confirmation: PanelContainer = %SaleConfirmation
@onready var _confirmation_message: Label = %ConfirmationMessage
@onready var _confirmation_message: RichTextLabel = %ConfirmationMessage
@onready var _confirm_sale_button: Button = %ConfirmSaleButton
@onready var _cancel_sale_button: Button = %CancelSaleButton
@onready var _logbook_empty: Label = %LogbookEmpty
@ -1881,11 +1884,11 @@ func _apply_inventory_styles() -> void:
UtilityPageStyle.panel_style(),
)
_confirmation_message.add_theme_font_override(
"font",
"normal_font",
UtilityPageStyle.TuffyFont,
)
_confirmation_message.add_theme_color_override(
"font_color",
"default_color",
UtilityPageStyle.OCEAN_TEXT_PRIMARY,
)
UtilityPageStyle.apply_ocean_button(_confirm_sale_button)
@ -2405,14 +2408,13 @@ func _update_shell_layout() -> void:
_notepad_capacity_value.size = (
Vector2(142.0, 17.0) if compact else Vector2(124.0, 23.0)
)
for status_value: Label in [
_notepad_wallet_value,
_notepad_capacity_value,
]:
status_value.add_theme_font_size_override(
"font_size",
10 if compact else 14,
)
_notepad_wallet_value.icon_size = 10.0 if compact else 14.0
_notepad_wallet_value.get_amount_label().add_theme_font_size_override(
"font_size", 10 if compact else 14
)
_notepad_capacity_value.add_theme_font_size_override(
"font_size", 10 if compact else 14
)
_cooler_detail_texture.position = (
Vector2(5.0, 84.0) if compact else Vector2(80.0, 137.0)
)
@ -2594,22 +2596,21 @@ func _layout_cooler_detail_text(compact: bool) -> void:
"font_size",
13 if compact else 18,
)
_cooler_offer_value.add_theme_font_size_override(
"font_size",
10 if compact else 15,
_cooler_offer_value.icon_size = 10.0 if compact else 15.0
_cooler_offer_value.get_amount_label().add_theme_font_size_override(
"font_size", 10 if compact else 15
)
func _layout_cooler_selection_text(compact: bool) -> void:
var row_height: float = 17.5 if compact else 20.0
for numeric_label: Label in [
_cooler_selected_count_value,
_cooler_combined_offer_value,
]:
numeric_label.add_theme_font_size_override(
"font_size",
10 if compact else 14,
)
_cooler_selected_count_value.add_theme_font_size_override(
"font_size", 10 if compact else 14
)
_cooler_combined_offer_value.icon_size = 10.0 if compact else 14.0
_cooler_combined_offer_value.get_amount_label().add_theme_font_size_override(
"font_size", 10 if compact else 14
)
for word_label: Label in [
_cooler_selection_empty,
_cooler_selected_count_label,
@ -3438,9 +3439,9 @@ func _refresh_economy_summary() -> void:
if _inventory != null
else 0
)
_wallet_balance.text = "wallet: $%d" % balance
_wallet_status.set_content("wallet", "$%d" % balance)
_notepad_wallet_value.text = "$%d" % balance
_wallet_balance.set_amount(balance)
_wallet_status.set_currency_amount("wallet", balance)
_notepad_wallet_value.set_amount(balance)
_capacity_status.set_content("cooler", "%d / %d" % [
_inventory.get_all_catches().size() if _inventory != null else 0,
_cooler_capacity.get_capacity() if _cooler_capacity != null else 0,
@ -3449,8 +3450,8 @@ func _refresh_economy_summary() -> void:
_inventory.get_all_catches().size() if _inventory != null else 0,
_cooler_capacity.get_capacity() if _cooler_capacity != null else 0,
]
_held_value_status.set_content("held value", "$%d" % held_total)
_held_value.text = "held fish base value: $%d" % held_total
_held_value_status.set_currency_amount("held value", held_total)
_held_value.set_amount(held_total)
_cooler_count.text = "%d / %d" % [
_inventory.get_all_catches().size() if _inventory != null else 0,
_cooler_capacity.get_capacity() if _cooler_capacity != null else 0,
@ -3707,7 +3708,7 @@ func _on_catch_card_pressed(catch_id: StringName) -> void:
Input.is_key_pressed(KEY_CTRL),
Input.is_key_pressed(KEY_SHIFT)
)
_transaction_feedback.text = ""
_set_transaction_feedback("")
_refresh_inventory()
@ -3719,7 +3720,7 @@ func _on_fish_field_gui_input(event: InputEvent) -> void:
and not get_viewport().gui_is_dragging()
):
_fish_selection.clear()
_transaction_feedback.text = ""
_set_transaction_feedback("")
_refresh_inventory()
@ -3751,9 +3752,9 @@ func _clear_cooler_detail_stats() -> void:
_cooler_weight_value,
_cooler_weight_unit,
_cooler_offer_label,
_cooler_offer_value,
]:
label.text = ""
_cooler_offer_value.visible = false
func _set_cooler_selection_summary_empty() -> void:
@ -3764,10 +3765,10 @@ func _set_cooler_selection_summary_empty() -> void:
_cooler_selected_count_value,
_cooler_selected_count_label,
_cooler_combined_offer_label,
_cooler_combined_offer_value,
]:
label.visible = false
label.text = ""
_cooler_combined_offer_value.visible = false
func _set_cooler_selection_summary(
@ -3785,10 +3786,9 @@ func _set_cooler_selection_summary(
_cooler_selected_count_label.text = "fish selected"
if offer_value >= 0:
_cooler_combined_offer_label.text = "combined offer"
_cooler_combined_offer_value.text = "$%d" % offer_value
_cooler_combined_offer_value.set_amount(offer_value)
else:
_cooler_combined_offer_label.text = "offer unavailable"
_cooler_combined_offer_value.text = ""
func _update_inventory_detail(fish_catch: FishCatchType) -> void:
@ -3838,10 +3838,11 @@ func _update_inventory_detail(fish_catch: FishCatchType) -> void:
]
if buyer_offer >= 0 and active_buyer != null:
_cooler_offer_label.text = _get_offer_label(active_buyer)
_cooler_offer_value.text = "$%d" % buyer_offer
_cooler_offer_value.visible = true
_cooler_offer_value.set_amount(buyer_offer)
else:
_cooler_offer_label.text = "buyer unavailable"
_cooler_offer_value.text = ""
_cooler_offer_value.visible = false
_favorite_button.disabled = false
_favorite_button.text = (
"unfavorite" if fish_catch.is_favorited else "favorite"
@ -3970,12 +3971,8 @@ func _update_sale_summary() -> void:
or preview.status == FishSaleResultType.Status.FAVORITED
)
):
_selection_summary.text += "\n%s total offer: $%d" % [
active_buyer.display_name,
preview.payout,
]
_set_cooler_selection_summary(selected_count, preview.payout)
_offer_status.set_content(offer_label, "$%d" % preview.payout)
_offer_status.set_currency_amount(offer_label, preview.payout)
else:
_set_cooler_selection_summary(selected_count, -1)
_offer_status.set_content(offer_label, "unavailable")
@ -4058,7 +4055,7 @@ func _on_favorite_pressed() -> void:
fish_catch.catch_id,
not fish_catch.is_favorited
):
_transaction_feedback.text = (
_set_transaction_feedback(
"%s %s."
% [
fish_catch.fish.display_name,
@ -4074,7 +4071,7 @@ func _on_sell_pressed() -> void:
func _on_sell_all_pressed() -> void:
var catch_ids: Array[StringName] = _get_sell_all_catch_ids()
if catch_ids.is_empty():
_transaction_feedback.text = "No sellable fish in the cooler."
_set_transaction_feedback("No sellable fish in the cooler.")
return
_begin_sale_confirmation(catch_ids)
@ -4091,14 +4088,14 @@ func _begin_sale_confirmation(catch_ids: Array[StringName]) -> void:
and _network_sale_service.is_local_sale_pending()
)
):
_transaction_feedback.text = "Selling…"
_set_transaction_feedback("Selling…")
return
if (
_network_sale_service == null
or buyer_id.is_empty()
or not _network_sale_service.can_request_sale(buyer_id)
):
_transaction_feedback.text = (
_set_transaction_feedback(
"Selling is not supported by this server."
)
return
@ -4114,7 +4111,7 @@ func _begin_sale_confirmation(catch_ids: Array[StringName]) -> void:
active_buyer
)
if not preview.is_success():
_transaction_feedback.text = (
_set_transaction_feedback(
"favorited fish cannot be sold. "
+ "remove them from the selection first."
if preview.status == FishSaleResultType.Status.FAVORITED
@ -4128,15 +4125,15 @@ func _begin_sale_confirmation(catch_ids: Array[StringName]) -> void:
_confirmation_generation = _menu_generation
var fish_label: String = "fish"
_confirmation_message.text = (
"sell %d %s to the %s for $%d?\ncombined base value: $%d"
% [
preview.fish_count,
fish_label,
_get_buyer_display_group(active_buyer),
preview.payout,
preview.base_value,
]
)
"[center]sell %d %s to the %s for %s?\n"
+ "combined base value: %s[/center]"
) % [
preview.fish_count,
fish_label,
_get_buyer_display_group(active_buyer),
CurrencyPresentationType.bbcode_amount(preview.payout, 22),
CurrencyPresentationType.bbcode_amount(preview.base_value, 22),
]
_sale_confirmation.visible = true
if _shop_cooler_context_active:
shop_cooler_modal_changed.emit(true)
@ -4182,7 +4179,7 @@ func _on_confirm_sale_pressed() -> void:
and transaction_generation == _menu_generation
and (visible or _shop_cooler_context_active)
):
_transaction_feedback.text = "Selling…"
_set_transaction_feedback("Selling…")
func _can_use_shared_world_actions() -> bool:
@ -4195,7 +4192,7 @@ func _can_use_shared_world_actions() -> bool:
func _on_network_sale_pending(_request_id: String) -> void:
_sale_in_progress = true
if visible or _shop_cooler_context_active:
_transaction_feedback.text = "Selling…"
_set_transaction_feedback("Selling…")
_update_sale_summary()
@ -4213,11 +4210,12 @@ func _on_network_sale_finished(
return
_refresh_all()
var feedback_message: String = (
"Sale complete. $%d received." % payout
"Sale complete • %s received."
% CurrencyPresentationType.bbcode_amount(payout, 20)
if accepted
else message
)
_transaction_feedback.text = feedback_message
_set_transaction_feedback(feedback_message)
if not accepted:
_sale_unavailable.text = feedback_message
_sale_unavailable.visible = true
@ -4273,7 +4271,7 @@ func _revalidate_confirmation() -> void:
or _confirmation_buyer.id != _confirmation_buyer_id
):
_close_sale_confirmation()
_transaction_feedback.text = "sale selection is no longer available."
_set_transaction_feedback("sale selection is no longer available.")
return
var preview: FishSaleResultType = _sale_service.preview_batch(
_confirmation_catch_ids,
@ -4288,7 +4286,7 @@ func _revalidate_confirmation() -> void:
)
):
_close_sale_confirmation()
_transaction_feedback.text = (
_set_transaction_feedback(
"favorited fish cannot be sold. "
+ "remove them from the selection first."
if preview.status == FishSaleResultType.Status.FAVORITED
@ -4308,6 +4306,10 @@ func _get_buyer_display_group(
return "buyer"
func _set_transaction_feedback(message: String) -> void:
_transaction_feedback.text = "[center]%s[/center]" % message
func _get_active_sale_buyer() -> FishBuyerProfileType:
if _sale_buyer_override != null and _sale_buyer_override.is_valid():
return _sale_buyer_override

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=33 format=3]
[gd_scene load_steps=34 format=3]
[ext_resource type="Script" path="res://ui/player_menu.gd" id="1_menu"]
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
@ -31,6 +31,7 @@
[ext_resource type="Texture2D" path="res://ui/icons/tab_menu/profile_simple.png" id="29_profile_simple"]
[ext_resource type="Texture2D" path="res://ui/icons/tab_menu/online_simple.png" id="30_online_simple"]
[ext_resource type="Texture2D" path="res://ui/icons/tab_menu/close.png" id="31_close"]
[ext_resource type="PackedScene" path="res://ui/components/currency_amount.tscn" id="32_currency"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_collection"]
@ -224,7 +225,7 @@ theme_override_font_sizes/font_size = 19
text = "cooler notes"
horizontal_alignment = 1
[node name="NotepadWalletValue" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation"]
[node name="NotepadWalletValue" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation" instance=ExtResource("32_currency")]
unique_name_in_owner = true
layout_mode = 0
offset_left = 18.0
@ -232,9 +233,7 @@ offset_top = 61.0
offset_right = 138.0
offset_bottom = 84.0
mouse_filter = 2
theme_override_colors/font_color = Color(0.16, 0.16, 0.14, 1)
theme_override_font_sizes/font_size = 14
text = "$0"
icon_size = 14.0
[node name="NotepadCapacityValue" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation"]
unique_name_in_owner = true
@ -367,12 +366,11 @@ mouse_filter = 2
theme_override_colors/font_color = Color(0.16, 0.16, 0.14, 1)
theme_override_font_sizes/font_size = 18
[node name="CoolerOfferValue" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation/CoolerDetailStats/CoolerOfferRow"]
[node name="CoolerOfferValue" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation/CoolerDetailStats/CoolerOfferRow" instance=ExtResource("32_currency")]
unique_name_in_owner = true
layout_mode = 2
mouse_filter = 2
theme_override_colors/font_color = Color(0.16, 0.16, 0.14, 1)
theme_override_font_sizes/font_size = 15
icon_size = 15.0
[node name="CoolerSelectionSummary" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation"]
unique_name_in_owner = true
@ -442,12 +440,11 @@ theme_override_colors/font_color = Color(0.2, 0.18, 0.15, 0.86)
theme_override_font_sizes/font_size = 18
text = "combined offer"
[node name="CoolerCombinedOfferValue" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation/CoolerSelectionSummary/CoolerCombinedOfferRow"]
[node name="CoolerCombinedOfferValue" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation/CoolerSelectionSummary/CoolerCombinedOfferRow" instance=ExtResource("32_currency")]
unique_name_in_owner = true
layout_mode = 2
mouse_filter = 2
theme_override_colors/font_color = Color(0.2, 0.18, 0.15, 0.86)
theme_override_font_sizes/font_size = 14
icon_size = 14.0
[node name="FavoriteBubble" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/CoolerPage/DetailConstellation" instance=ExtResource("12_ink_action")]
unique_name_in_owner = true
@ -965,24 +962,31 @@ unique_name_in_owner = true
layout_mode = 2
theme_override_constants/separation = 8
[node name="WalletBalance" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout/Header"]
[node name="WalletHeading" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout/Header"]
layout_mode = 2
text = "wallet"
theme_override_colors/font_color = Color(1, 0.82, 0.4, 1)
[node name="WalletBalance" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout/Header" instance=ExtResource("32_currency")]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
text = "wallet: $0"
theme_override_colors/font_color = Color(1, 0.82, 0.4, 1)
alignment = 0
[node name="Separator" type="HSeparator" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout"]
unique_name_in_owner = true
layout_mode = 2
[node name="TransactionFeedback" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout"]
[node name="TransactionFeedback" type="RichTextLabel" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 30)
layout_mode = 2
text = ""
horizontal_alignment = 1
theme_override_colors/font_color = Color(1, 0.82, 0.4, 1)
theme_override_font_sizes/font_size = 21
bbcode_enabled = true
fit_content = true
scroll_active = false
theme_override_colors/default_color = Color(1, 0.82, 0.4, 1)
theme_override_font_sizes/normal_font_size = 21
[node name="Content" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout"]
unique_name_in_owner = true
@ -1057,12 +1061,16 @@ layout_mode = 2
text = "0 / 12"
theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
[node name="HeldValue" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
[node name="HeldValueHeading" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
visible = false
layout_mode = 2
text = "held fish base value"
theme_override_colors/font_color = Color(0.682, 0.733, 0.761, 1)
[node name="HeldValue" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout/Content/InventorySection/SortBar" instance=ExtResource("32_currency")]
unique_name_in_owner = true
visible = false
layout_mode = 2
text = "held fish base value: $0"
theme_override_colors/font_color = Color(0.682, 0.733, 0.761, 1)
[node name="InventoryBody" type="BoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/MenuPanel/Margin/Layout/Content/InventorySection"]
unique_name_in_owner = true
@ -1366,12 +1374,15 @@ layout_mode = 2
theme_override_constants/separation = 12
alignment = 1
[node name="ConfirmationMessage" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation/ConfirmationMargin/ConfirmationLayout"]
[node name="ConfirmationMessage" type="RichTextLabel" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation/ConfirmationMargin/ConfirmationLayout"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 64)
layout_mode = 2
theme_override_font_sizes/font_size = 22
bbcode_enabled = true
fit_content = true
scroll_active = false
theme_override_font_sizes/normal_font_size = 22
autowrap_mode = 2
horizontal_alignment = 1
[node name="ConfirmationButtons" type="HBoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation/ConfirmationMargin/ConfirmationLayout"]
layout_mode = 2

View file

@ -36,6 +36,8 @@ func _ready() -> void:
UtilityPageStyleType.apply_ocean_button(button)
button.custom_minimum_size = Vector2(48, 44)
_make_button_round(button, 22)
_enlarge_action_icon(_eraser_button)
_enlarge_action_icon(_undo_button)
_mode_button.custom_minimum_size = Vector2(48, 48)
_make_button_round(_mode_button, 24)
_mode_button.pressed.connect(_toggle_mode)
@ -91,6 +93,25 @@ func _make_button_round(button: Button, radius: int) -> void:
button.add_theme_stylebox_override(style_name, style)
func _enlarge_action_icon(button: Button) -> void:
# The shared ocean button uses generous text padding, leaving only a
# 20-pixel icon area in these compact bubbles. Keep the 48x44 control and
# hitbox intact while giving icon-only actions a full 40x40 presentation.
button.add_theme_constant_override("icon_max_width", 40)
for style_name: StringName in [
&"normal", &"hover", &"pressed", &"focus", &"disabled",
]:
var existing: StyleBox = button.get_theme_stylebox(style_name)
var style := existing.duplicate() as StyleBoxFlat
if style == null:
continue
style.content_margin_left = 4.0
style.content_margin_right = 4.0
style.content_margin_top = 2.0
style.content_margin_bottom = 2.0
button.add_theme_stylebox_override(style_name, style)
func setup(
service: NetworkSurfaceDrawingService,
unlocks: PlayerArtUnlocks,

View file

@ -1,10 +1,14 @@
[gd_scene load_steps=6 format=3]
[gd_scene load_steps=8 format=3]
[ext_resource type="Script" path="res://ui/surface_drawing_toolbar.gd" id="1_script"]
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/undo_light.png" id="3_undo_light"]
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/check_mark_light.png" id="4_check_mark_light"]
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/x_light.png" id="5_x_light"]
[ext_resource type="Texture2D" path="res://items/icons/art/art_kit_eraser.png" id="6_art_kit_eraser"]
[sub_resource type="ButtonGroup" id="GuideActionGroup"]
allow_unpress = true
[node name="SurfaceDrawingToolbar" type="Control"]
visible = false
@ -51,9 +55,12 @@ tooltip_text = "grid size"
[node name="EraserButton" type="Button" parent="TopPanel/Top"]
unique_name_in_owner = true
layout_mode = 2
texture_filter = 1
tooltip_text = "toggle eraser"
toggle_mode = true
text = "⌫"
accessibility_name = "toggle eraser"
icon = ExtResource("6_art_kit_eraser")
expand_icon = true
[node name="UndoButton" type="Button" parent="TopPanel/Top"]
unique_name_in_owner = true
@ -67,24 +74,27 @@ expand_icon = true
[node name="HideGuideButton" type="Button" parent="TopPanel/Top"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "hide grid on next click"
tooltip_text = "toggle hide grid mode"
toggle_mode = true
button_group = SubResource("GuideActionGroup")
text = "◌"
[node name="RestoreGuideButton" type="Button" parent="TopPanel/Top"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "restore hidden grid on next click"
tooltip_text = "toggle show grid mode"
toggle_mode = true
button_group = SubResource("GuideActionGroup")
text = "▤"
[node name="FinalizeGuideButton" type="Button" parent="TopPanel/Top"]
unique_name_in_owner = true
layout_mode = 2
texture_filter = 1
tooltip_text = "finish grid on next click"
tooltip_text = "toggle finalize grid mode"
toggle_mode = true
accessibility_name = "finish grid on next click"
button_group = SubResource("GuideActionGroup")
accessibility_name = "toggle finalize grid mode"
icon = ExtResource("4_check_mark_light")
expand_icon = true

View file

@ -1,6 +1,10 @@
class_name TheNetPage
extends Control
const CurrencyPresentationType = preload(
"res://ui/currency_presentation.gd"
)
enum View {
DAILY,
LIFETIME,
@ -278,15 +282,11 @@ func _build_job_rows(jobs: Array[Dictionary], empty_text: String) -> void:
)
content.add_child(count)
var reward := Label.new()
reward.custom_minimum_size.x = 98.0
reward.text = "$%d%d xp" % [
int(job.get("fish_coin", 0)), int(job.get("experience", 0)),
]
reward.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
reward.add_theme_color_override(
"font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY
var reward := _make_reward_display(
int(job.get("fish_coin", 0)),
int(job.get("experience", 0)),
)
reward.custom_minimum_size.x = 128.0
content.add_child(reward)
var claim := Button.new()
@ -326,17 +326,19 @@ func _build_payment_rows() -> void:
var content := HBoxContainer.new()
content.add_theme_constant_override("separation", 14)
row.add_child(content)
var reward_details := VBoxContainer.new()
reward_details.size_flags_horizontal = Control.SIZE_EXPAND_FILL
var label := Label.new()
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
label.text = "%s\n$%d%d xp" % [
str(reward.get("title", "completed job")),
int(reward.get("fish_coin", 0)),
int(reward.get("experience", 0)),
]
label.text = str(reward.get("title", "completed job"))
label.add_theme_color_override(
"font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY
)
content.add_child(label)
reward_details.add_child(label)
reward_details.add_child(_make_reward_display(
int(reward.get("fish_coin", 0)),
int(reward.get("experience", 0)),
))
content.add_child(reward_details)
var claim := Button.new()
claim.text = "claim"
claim.custom_minimum_size = Vector2(110.0, 42.0)
@ -363,6 +365,27 @@ func _claim(claim_id: String) -> void:
_refresh()
func _make_reward_display(fish_coin: int, experience: int) -> HBoxContainer:
var reward := HBoxContainer.new()
reward.mouse_filter = Control.MOUSE_FILTER_IGNORE
reward.add_theme_constant_override("separation", 7)
var currency: CurrencyAmount = (
CurrencyPresentationType.instantiate_amount(fish_coin, 18.0)
)
currency.get_amount_label().add_theme_color_override(
"font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY
)
reward.add_child(currency)
var experience_label := Label.new()
experience_label.text = "%d xp" % experience
experience_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
experience_label.add_theme_color_override(
"font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY
)
reward.add_child(experience_label)
return reward
func _add_empty(message: String) -> void:
var empty := Label.new()
empty.text = message

View file

@ -20,6 +20,9 @@ const TitleCreditsPageType = preload("res://ui/title_credits_page.gd")
const NetworkSessionType = preload("res://network/network_session.gd")
const SavedServerStoreType = preload("res://network/saved_server_store.gd")
const JoinGamePageType = preload("res://ui/network/join_game_page.gd")
const CurrencyPresentationType = preload(
"res://ui/currency_presentation.gd"
)
const DECORATIVE_FISH_TEXTURES: Array[Texture2D] = [
preload("res://fish/species/bass/fish_bass_striped.png"),
preload("res://fish/species/bluegill/fish_bluegill.png"),
@ -106,7 +109,7 @@ enum ConfirmationAction {
@onready var _join_game_button: BubbleButtonType = %JoinGameButton
@onready var _new_game_label: Label = %NewGameLabel
@onready var _delete_save_label: Label = %DeleteSaveLabel
@onready var _feedback_label: Label = %FeedbackLabel
@onready var _feedback_label: RichTextLabel = %FeedbackLabel
@onready var _confirmation_page: TitleConfirmationBubblePageType = (
%ConfirmationPage
)
@ -287,7 +290,7 @@ func open_join_game_page(endpoint: String = "") -> void:
func report_network_error(message: String) -> void:
_join_game_page.set_status(message)
if not _join_game_page.visible:
_feedback_label.text = message
_feedback_label.text = _center_feedback_text(message)
_feedback_label.show()
_feedback_label.modulate.a = 1.0
@ -953,16 +956,23 @@ func _get_continue_stats_text() -> String:
or _inspection.status != SaveInspectionType.Status.VALID_SUPPORTED
):
return ""
return (
"%d fish • $%d%d discovered"
% [
return _center_feedback_text(
"%d fish • %s%d discovered" % [
_inspection.catch_count,
_inspection.wallet_balance,
CurrencyPresentationType.bbcode_amount(
_inspection.wallet_balance, 18
),
_inspection.discovered_species_count,
]
)
func _center_feedback_text(message: String) -> String:
if message.begins_with("[center]"):
return message
return "[center]%s[/center]" % message
func _on_continue_pressed() -> void:
if (
_action_in_progress
@ -991,13 +1001,15 @@ func _on_new_game_pressed() -> void:
new_game_requested.emit()
return
if _inspection.status == SaveInspectionType.Status.UNSUPPORTED_VERSION:
_feedback_label.text = (
_feedback_label.text = _center_feedback_text(
"this save is from a newer game version. "
+ "use delete save before starting over."
)
return
if _inspection.status == SaveInspectionType.Status.IO_ERROR:
_feedback_label.text = "the existing save cannot be accessed safely."
_feedback_label.text = _center_feedback_text(
"the existing save cannot be accessed safely."
)
return
_open_confirmation(
ConfirmationAction.NEW_GAME,
@ -1043,14 +1055,16 @@ func _on_confirmation_accepted() -> void:
_action_in_progress = false
return
if not _save_manager.delete_progression_save():
_feedback_label.text = "failed to delete saved progression."
_feedback_label.text = _center_feedback_text(
"failed to delete saved progression."
)
_action_in_progress = false
_refresh_save_inspection()
_begin_confirmation_return()
return
_save_manager.initialize_new_game()
_refresh_save_inspection()
_feedback_label.text = "saved progression deleted."
_feedback_label.text = _center_feedback_text("saved progression deleted.")
_begin_confirmation_return()
_action_in_progress = false
@ -1325,8 +1339,9 @@ func _begin_title_cluster_return(feedback_text: String) -> void:
_title_settings_transition_generation += 1
_cancel_title_settings_tween()
_title_settings_transition_active = true
if _feedback_label.text != feedback_text:
_feedback_label.text = feedback_text
var centered_feedback: String = _center_feedback_text(feedback_text)
if _feedback_label.text != centered_feedback:
_feedback_label.text = centered_feedback
_feedback_label.visible = _feedback_visible_before_settings
_hide_continue_stats_context()
_set_title_bubbles_interactive(false)
@ -1434,7 +1449,7 @@ func _refresh_save_inspection() -> void:
_delete_save_label.modulate.a = (
DISABLED_BUBBLE_LABEL_ALPHA if _delete_button.disabled else 1.0
)
_feedback_label.text = _inspection.message
_feedback_label.text = _center_feedback_text(_inspection.message)
if _inspection.status == SaveInspectionType.Status.VALID_SUPPORTED:
_feedback_label.text = _get_continue_stats_text()
if _continue_button.disabled:

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=18 format=3]
[gd_scene load_steps=19 format=3]
[ext_resource type="Script" path="res://ui/title_screen.gd" id="1_script"]
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
@ -22,6 +22,17 @@ shader = ExtResource("4_water_shader")
[sub_resource type="ShaderMaterial" id="ShaderMaterial_title_logo"]
shader = ExtResource("6_logo_shader")
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_continue_stats"]
content_margin_left = 14.0
content_margin_top = 6.0
content_margin_right = 14.0
content_margin_bottom = 6.0
bg_color = Color(0.031, 0.122, 0.169, 1)
corner_radius_top_left = 14
corner_radius_top_right = 14
corner_radius_bottom_right = 14
corner_radius_bottom_left = 14
[node name="TitleScreen" type="Control"]
unique_name_in_owner = true
z_index = 200
@ -433,15 +444,18 @@ motion_phase = 4.8
deformation_amplitude = 0.02
deformation_period = 4.8
[node name="FeedbackLabel" type="Label" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent"]
[node name="FeedbackLabel" type="RichTextLabel" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent"]
unique_name_in_owner = true
visible = false
custom_minimum_size = Vector2(0, 44)
custom_minimum_size = Vector2(430, 44)
layout_mode = 2
size_flags_horizontal = 4
text = ""
horizontal_alignment = 1
vertical_alignment = 1
bbcode_enabled = true
scroll_active = false
autowrap_mode = 2
vertical_alignment = 1
theme_override_styles/normal = SubResource("StyleBoxFlat_continue_stats")
[node name="ConfirmationPage" parent="ResponsiveTitleStage/TitlePresentationScaleRoot" instance=ExtResource("10_confirmation_page")]
unique_name_in_owner = true