Improve UI scaling and fishing commerce
This commit is contained in:
parent
2de96dd7bb
commit
6b8c3eb2f7
29 changed files with 1515 additions and 817 deletions
BIN
art/patterns/pattern_moneyfish.png
Normal file
BIN
art/patterns/pattern_moneyfish.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
40
art/patterns/pattern_moneyfish.png.import
Normal file
40
art/patterns/pattern_moneyfish.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://b6xws1d2dnbnn"
|
||||||
|
path="res://.godot/imported/pattern_moneyfish.png-c20edef3e829648b3e6aae4e190efaed.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://art/patterns/pattern_moneyfish.png"
|
||||||
|
dest_files=["res://.godot/imported/pattern_moneyfish.png-c20edef3e829648b3e6aae4e190efaed.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
|
@ -496,7 +496,7 @@ func _begin_aiming(player: PlayerType) -> void:
|
||||||
_cast_charge = 0.0
|
_cast_charge = 0.0
|
||||||
_cast_target = _calculate_cast_target(minimum_cast_distance)
|
_cast_target = _calculate_cast_target(minimum_cast_distance)
|
||||||
state = FishingState.AIMING_CAST
|
state = FishingState.AIMING_CAST
|
||||||
status_changed.emit("hold left click to aim • release to cast")
|
status_changed.emit("")
|
||||||
var target_is_fishable: bool = is_target_fishable(_cast_target)
|
var target_is_fishable: bool = is_target_fishable(_cast_target)
|
||||||
var preview_position: Vector3 = _resolve_preview_surface_position(_cast_target)
|
var preview_position: Vector3 = _resolve_preview_surface_position(_cast_target)
|
||||||
_presentation.begin_aim(
|
_presentation.begin_aim(
|
||||||
|
|
@ -603,7 +603,7 @@ func _confirm_cast() -> void:
|
||||||
return
|
return
|
||||||
|
|
||||||
state = FishingState.CASTING
|
state = FishingState.CASTING
|
||||||
status_changed.emit("casting...")
|
status_changed.emit("")
|
||||||
_presentation.begin_cast(_cast_target)
|
_presentation.begin_cast(_cast_target)
|
||||||
_presentation.set_line_mode(FishingPresentationType.LineMode.TAUT)
|
_presentation.set_line_mode(FishingPresentationType.LineMode.TAUT)
|
||||||
|
|
||||||
|
|
@ -618,7 +618,7 @@ func _on_cast_completed() -> void:
|
||||||
_cast_charge,
|
_cast_charge,
|
||||||
_build_network_evidence()
|
_build_network_evidence()
|
||||||
)
|
)
|
||||||
status_changed.emit("checking the water...")
|
status_changed.emit("")
|
||||||
return
|
return
|
||||||
|
|
||||||
_selected_water_region = get_fishable_water_region(_cast_target)
|
_selected_water_region = get_fishable_water_region(_cast_target)
|
||||||
|
|
|
||||||
62
main/main.gd
62
main/main.gd
|
|
@ -74,6 +74,7 @@ const NetworkProfileServiceType = preload(
|
||||||
const TITLE_MUSIC_SILENCE_DB: float = -80.0
|
const TITLE_MUSIC_SILENCE_DB: float = -80.0
|
||||||
const PLAYER_MENU_PATTERN_SCALE: float = 0.85
|
const PLAYER_MENU_PATTERN_SCALE: float = 0.85
|
||||||
const PLAYER_MENU_PATTERN_SCROLL_VELOCITY := Vector2(-7.0, -5.0)
|
const PLAYER_MENU_PATTERN_SCROLL_VELOCITY := Vector2(-7.0, -5.0)
|
||||||
|
const SHOP_PATTERN_SCALE: float = 1.75
|
||||||
|
|
||||||
@export var fish_catalog: FishPoolType
|
@export var fish_catalog: FishPoolType
|
||||||
@export var pelican_buyer_profile: FishBuyerProfileType
|
@export var pelican_buyer_profile: FishBuyerProfileType
|
||||||
|
|
@ -136,6 +137,7 @@ const PLAYER_MENU_PATTERN_SCROLL_VELOCITY := Vector2(-7.0, -5.0)
|
||||||
@onready var _players_root: Node3D = $Players
|
@onready var _players_root: Node3D = $Players
|
||||||
@onready var _title_background: ColorRect = %TitleBackground
|
@onready var _title_background: ColorRect = %TitleBackground
|
||||||
@onready var _player_menu_backdrop: ColorRect = %PlayerMenuBackdrop
|
@onready var _player_menu_backdrop: ColorRect = %PlayerMenuBackdrop
|
||||||
|
@onready var _shop_backdrop: ColorRect = %ShopBackdrop
|
||||||
|
|
||||||
var _gameplay_started: bool = false
|
var _gameplay_started: bool = false
|
||||||
var _shop_interaction: FishingShopInteractionType
|
var _shop_interaction: FishingShopInteractionType
|
||||||
|
|
@ -146,6 +148,7 @@ var _quit_in_progress: bool = false
|
||||||
var _join_requested_from_title: bool = false
|
var _join_requested_from_title: bool = false
|
||||||
var _join_requested_from_pause: bool = false
|
var _join_requested_from_pause: bool = false
|
||||||
var _player_menu_backdrop_tween: Tween
|
var _player_menu_backdrop_tween: Tween
|
||||||
|
var _shop_backdrop_tween: Tween
|
||||||
var _pending_join_endpoint: String = ""
|
var _pending_join_endpoint: String = ""
|
||||||
var _server_trust_dialog: ConfirmationDialog
|
var _server_trust_dialog: ConfirmationDialog
|
||||||
var _pending_trust_changed: bool = false
|
var _pending_trust_changed: bool = false
|
||||||
|
|
@ -173,6 +176,16 @@ func _ready() -> void:
|
||||||
"scroll_velocity_pixels",
|
"scroll_velocity_pixels",
|
||||||
PLAYER_MENU_PATTERN_SCROLL_VELOCITY,
|
PLAYER_MENU_PATTERN_SCROLL_VELOCITY,
|
||||||
)
|
)
|
||||||
|
var shop_pattern_material := _shop_backdrop.material as ShaderMaterial
|
||||||
|
if shop_pattern_material != null:
|
||||||
|
shop_pattern_material.set_shader_parameter(
|
||||||
|
"display_scale",
|
||||||
|
SHOP_PATTERN_SCALE,
|
||||||
|
)
|
||||||
|
shop_pattern_material.set_shader_parameter(
|
||||||
|
"scroll_velocity_pixels",
|
||||||
|
PLAYER_MENU_PATTERN_SCROLL_VELOCITY,
|
||||||
|
)
|
||||||
get_window().size_changed.connect(_resize_native_overlays)
|
get_window().size_changed.connect(_resize_native_overlays)
|
||||||
_resize_native_overlays()
|
_resize_native_overlays()
|
||||||
if not _settings_manager.settings_changed.is_connected(
|
if not _settings_manager.settings_changed.is_connected(
|
||||||
|
|
@ -338,17 +351,21 @@ func _initialize_after_data_root() -> void:
|
||||||
fish_catalog,
|
fish_catalog,
|
||||||
_network_item_use
|
_network_item_use
|
||||||
)
|
)
|
||||||
|
var sale_buyers: Array[FishBuyerProfileType] = [
|
||||||
|
pelican_buyer_profile,
|
||||||
|
main_shop_buyer_profile,
|
||||||
|
]
|
||||||
_network_sale.setup(
|
_network_sale.setup(
|
||||||
_network_session,
|
_network_session,
|
||||||
_player_spawn_service,
|
_player_spawn_service,
|
||||||
_network_fishing,
|
_network_fishing,
|
||||||
|
_shop_interaction,
|
||||||
_player.inventory,
|
_player.inventory,
|
||||||
_player.wallet,
|
_player.wallet,
|
||||||
_player.fish_sale_service,
|
_player.fish_sale_service,
|
||||||
_save_manager,
|
_save_manager,
|
||||||
fish_catalog,
|
fish_catalog,
|
||||||
pelican_buyer_profile,
|
sale_buyers,
|
||||||
_test_world.get_pelican_convenience_landmark(),
|
|
||||||
_asset_reservations
|
_asset_reservations
|
||||||
)
|
)
|
||||||
_network_shop.setup(
|
_network_shop.setup(
|
||||||
|
|
@ -455,6 +472,9 @@ func _initialize_after_data_root() -> void:
|
||||||
_game_ui.player_menu_backdrop_visibility_changed.connect(
|
_game_ui.player_menu_backdrop_visibility_changed.connect(
|
||||||
_set_player_menu_backdrop_visible
|
_set_player_menu_backdrop_visible
|
||||||
)
|
)
|
||||||
|
_game_ui.shop_backdrop_visibility_changed.connect(
|
||||||
|
_set_shop_backdrop_visible
|
||||||
|
)
|
||||||
_pixelation_reset.return_to_settings_requested.connect(
|
_pixelation_reset.return_to_settings_requested.connect(
|
||||||
_game_ui.focus_open_settings_back_button
|
_game_ui.focus_open_settings_back_button
|
||||||
)
|
)
|
||||||
|
|
@ -916,6 +936,7 @@ func _set_gameplay_active(active: bool) -> void:
|
||||||
_refresh_active_hotbar_item()
|
_refresh_active_hotbar_item()
|
||||||
else:
|
else:
|
||||||
_set_player_menu_backdrop_visible(false)
|
_set_player_menu_backdrop_visible(false)
|
||||||
|
_set_shop_backdrop_visible(false)
|
||||||
|
|
||||||
|
|
||||||
func _set_player_menu_backdrop_visible(is_visible: bool) -> void:
|
func _set_player_menu_backdrop_visible(is_visible: bool) -> void:
|
||||||
|
|
@ -953,11 +974,48 @@ func _set_player_menu_backdrop_visible(is_visible: bool) -> void:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _set_shop_backdrop_visible(is_visible: bool) -> void:
|
||||||
|
if _shop_backdrop_tween != null:
|
||||||
|
_shop_backdrop_tween.kill()
|
||||||
|
_shop_backdrop_tween = null
|
||||||
|
var should_show: bool = is_visible and _gameplay_started
|
||||||
|
if should_show:
|
||||||
|
var was_visible: bool = _shop_backdrop.visible
|
||||||
|
_shop_backdrop.visible = true
|
||||||
|
if not was_visible:
|
||||||
|
_shop_backdrop.modulate.a = 0.0
|
||||||
|
_shop_backdrop_tween = create_tween()
|
||||||
|
_shop_backdrop_tween.tween_property(
|
||||||
|
_shop_backdrop,
|
||||||
|
"modulate:a",
|
||||||
|
1.0,
|
||||||
|
UIMotion.PLAYER_MENU_ENTER_DURATION,
|
||||||
|
).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
||||||
|
return
|
||||||
|
if not _shop_backdrop.visible:
|
||||||
|
return
|
||||||
|
_shop_backdrop_tween = create_tween()
|
||||||
|
_shop_backdrop_tween.tween_property(
|
||||||
|
_shop_backdrop,
|
||||||
|
"modulate:a",
|
||||||
|
0.0,
|
||||||
|
UIMotion.PLAYER_MENU_EXIT_DURATION,
|
||||||
|
).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN)
|
||||||
|
_shop_backdrop_tween.finished.connect(
|
||||||
|
func() -> void:
|
||||||
|
_shop_backdrop.visible = false
|
||||||
|
_shop_backdrop.modulate.a = 1.0
|
||||||
|
_shop_backdrop_tween = null
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _resize_native_overlays() -> void:
|
func _resize_native_overlays() -> void:
|
||||||
if not is_node_ready():
|
if not is_node_ready():
|
||||||
return
|
return
|
||||||
_player_menu_backdrop.position = Vector2.ZERO
|
_player_menu_backdrop.position = Vector2.ZERO
|
||||||
_player_menu_backdrop.size = Vector2(get_window().size)
|
_player_menu_backdrop.size = Vector2(get_window().size)
|
||||||
|
_shop_backdrop.position = Vector2.ZERO
|
||||||
|
_shop_backdrop.size = Vector2(get_window().size)
|
||||||
|
|
||||||
|
|
||||||
func _on_new_game_requested() -> void:
|
func _on_new_game_requested() -> void:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_scene load_steps=45 format=3]
|
[gd_scene load_steps=47 format=3]
|
||||||
|
|
||||||
[ext_resource type="PackedScene" path="res://world/test_world.tscn" id="1_world"]
|
[ext_resource type="PackedScene" path="res://world/test_world.tscn" id="1_world"]
|
||||||
[ext_resource type="PackedScene" path="res://player/player.tscn" id="2_player"]
|
[ext_resource type="PackedScene" path="res://player/player.tscn" id="2_player"]
|
||||||
|
|
@ -42,6 +42,7 @@
|
||||||
[ext_resource type="Shader" path="res://ui/title_water_background.gdshader" id="40_title_water"]
|
[ext_resource type="Shader" path="res://ui/title_water_background.gdshader" id="40_title_water"]
|
||||||
[ext_resource type="Texture2D" path="res://art/patterns/pattern_tealdot.png" id="41_menu_pattern"]
|
[ext_resource type="Texture2D" path="res://art/patterns/pattern_tealdot.png" id="41_menu_pattern"]
|
||||||
[ext_resource type="Shader" path="res://ui/player_menu_pattern.gdshader" id="42_menu_pattern"]
|
[ext_resource type="Shader" path="res://ui/player_menu_pattern.gdshader" id="42_menu_pattern"]
|
||||||
|
[ext_resource type="Texture2D" path="res://art/patterns/pattern_moneyfish.png" id="43_shop_pattern"]
|
||||||
|
|
||||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_title_water_native"]
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_title_water_native"]
|
||||||
shader = ExtResource("40_title_water")
|
shader = ExtResource("40_title_water")
|
||||||
|
|
@ -53,6 +54,13 @@ shader_parameter/source_tile_size = Vector2(128, 128)
|
||||||
shader_parameter/display_scale = 0.85
|
shader_parameter/display_scale = 0.85
|
||||||
shader_parameter/scroll_velocity_pixels = Vector2(-7, -5)
|
shader_parameter/scroll_velocity_pixels = Vector2(-7, -5)
|
||||||
|
|
||||||
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_shop_pattern"]
|
||||||
|
shader = ExtResource("42_menu_pattern")
|
||||||
|
shader_parameter/pattern_texture = ExtResource("43_shop_pattern")
|
||||||
|
shader_parameter/source_tile_size = Vector2(64, 64)
|
||||||
|
shader_parameter/display_scale = 1.75
|
||||||
|
shader_parameter/scroll_velocity_pixels = Vector2(-7, -5)
|
||||||
|
|
||||||
[node name="Main" type="Node3D"]
|
[node name="Main" type="Node3D"]
|
||||||
script = ExtResource("3_main")
|
script = ExtResource("3_main")
|
||||||
fish_catalog = ExtResource("6_pool")
|
fish_catalog = ExtResource("6_pool")
|
||||||
|
|
@ -82,6 +90,14 @@ mouse_filter = 2
|
||||||
material = SubResource("ShaderMaterial_player_menu_pattern")
|
material = SubResource("ShaderMaterial_player_menu_pattern")
|
||||||
color = Color(1, 1, 1, 1)
|
color = Color(1, 1, 1, 1)
|
||||||
|
|
||||||
|
[node name="ShopBackdrop" type="ColorRect" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
visible = false
|
||||||
|
z_index = 10
|
||||||
|
mouse_filter = 2
|
||||||
|
material = SubResource("ShaderMaterial_shop_pattern")
|
||||||
|
color = Color(1, 1, 1, 1)
|
||||||
|
|
||||||
[node name="InterfaceFontController" type="Node" parent="."]
|
[node name="InterfaceFontController" type="Node" parent="."]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
script = ExtResource("39_fonts")
|
script = ExtResource("39_fonts")
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,12 @@ static func validate_result(data: Variant) -> bool:
|
||||||
and str(payload["message"]).length() <= MAX_MESSAGE_LENGTH
|
and str(payload["message"]).length() <= MAX_MESSAGE_LENGTH
|
||||||
):
|
):
|
||||||
return false
|
return false
|
||||||
|
if payload.has("buyer_id") and (
|
||||||
|
typeof(payload["buyer_id"]) not in [TYPE_STRING, TYPE_STRING_NAME]
|
||||||
|
or str(payload["buyer_id"]).is_empty()
|
||||||
|
or str(payload["buyer_id"]).length() > MAX_ID_LENGTH
|
||||||
|
):
|
||||||
|
return false
|
||||||
var seen: Dictionary[String, bool] = {}
|
var seen: Dictionary[String, bool] = {}
|
||||||
for value: Variant in payload["catch_ids"]:
|
for value: Variant in payload["catch_ids"]:
|
||||||
if typeof(value) not in [TYPE_STRING, TYPE_STRING_NAME]:
|
if typeof(value) not in [TYPE_STRING, TYPE_STRING_NAME]:
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ const FishSaleServiceType = preload("res://economy/fish_sale_service.gd")
|
||||||
const FishSaleResultType = preload("res://economy/fish_sale_result.gd")
|
const FishSaleResultType = preload("res://economy/fish_sale_result.gd")
|
||||||
|
|
||||||
const MAX_LEDGER_ENTRIES_PER_PEER: int = 64
|
const MAX_LEDGER_ENTRIES_PER_PEER: int = 64
|
||||||
const PELICAN_INTERACTION_RANGE: float = 7.5
|
const PELICAN_BUYER_ID: StringName = &"pelicans"
|
||||||
|
const MAIN_SHOP_BUYER_ID: StringName = &"main_fishing_shop"
|
||||||
|
|
||||||
signal local_sale_pending(request_id: String)
|
signal local_sale_pending(request_id: String)
|
||||||
signal local_sale_finished(
|
signal local_sale_finished(
|
||||||
|
|
@ -23,13 +24,13 @@ signal local_sale_finished(
|
||||||
var _session: NetworkSession
|
var _session: NetworkSession
|
||||||
var _spawn_service: PlayerSpawnService
|
var _spawn_service: PlayerSpawnService
|
||||||
var _network_fishing: NetworkFishingService
|
var _network_fishing: NetworkFishingService
|
||||||
|
var _shop_interaction: FishingShopInteraction
|
||||||
var _inventory: FishInventory
|
var _inventory: FishInventory
|
||||||
var _wallet: PlayerWallet
|
var _wallet: PlayerWallet
|
||||||
var _sale_service: FishSaleServiceType
|
var _sale_service: FishSaleServiceType
|
||||||
var _save_manager: PlayerSaveManager
|
var _save_manager: PlayerSaveManager
|
||||||
var _fish_catalog: FishPoolType
|
var _fish_catalog: FishPoolType
|
||||||
var _buyer: FishBuyerProfileType
|
var _buyers: Dictionary[StringName, FishBuyerProfileType] = {}
|
||||||
var _pelican_landmark: Node3D
|
|
||||||
var _request_ledgers: Dictionary[int, Dictionary] = {}
|
var _request_ledgers: Dictionary[int, Dictionary] = {}
|
||||||
var _pending_by_peer: Dictionary[int, String] = {}
|
var _pending_by_peer: Dictionary[int, String] = {}
|
||||||
var _result_owners: Dictionary[String, int] = {}
|
var _result_owners: Dictionary[String, int] = {}
|
||||||
|
|
@ -38,6 +39,7 @@ var _applied_results: Dictionary[String, bool] = {}
|
||||||
var _received_results: Dictionary[String, bool] = {}
|
var _received_results: Dictionary[String, bool] = {}
|
||||||
var _pending_local_request_id: String = ""
|
var _pending_local_request_id: String = ""
|
||||||
var _pending_local_catch_ids: Array[StringName] = []
|
var _pending_local_catch_ids: Array[StringName] = []
|
||||||
|
var _pending_local_buyer_id: StringName
|
||||||
var _reservations: PlayerAssetReservationService
|
var _reservations: PlayerAssetReservationService
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -45,25 +47,28 @@ func setup(
|
||||||
session: NetworkSession,
|
session: NetworkSession,
|
||||||
spawn_service: PlayerSpawnService,
|
spawn_service: PlayerSpawnService,
|
||||||
network_fishing: NetworkFishingService,
|
network_fishing: NetworkFishingService,
|
||||||
|
shop_interaction: FishingShopInteraction,
|
||||||
inventory: FishInventory,
|
inventory: FishInventory,
|
||||||
wallet: PlayerWallet,
|
wallet: PlayerWallet,
|
||||||
sale_service: FishSaleServiceType,
|
sale_service: FishSaleServiceType,
|
||||||
save_manager: PlayerSaveManager,
|
save_manager: PlayerSaveManager,
|
||||||
fish_catalog: FishPoolType,
|
fish_catalog: FishPoolType,
|
||||||
buyer: FishBuyerProfileType,
|
buyers: Array[FishBuyerProfileType],
|
||||||
pelican_landmark: Node3D,
|
|
||||||
reservations: PlayerAssetReservationService,
|
reservations: PlayerAssetReservationService,
|
||||||
) -> void:
|
) -> void:
|
||||||
_session = session
|
_session = session
|
||||||
_spawn_service = spawn_service
|
_spawn_service = spawn_service
|
||||||
_network_fishing = network_fishing
|
_network_fishing = network_fishing
|
||||||
|
_shop_interaction = shop_interaction
|
||||||
_inventory = inventory
|
_inventory = inventory
|
||||||
_wallet = wallet
|
_wallet = wallet
|
||||||
_sale_service = sale_service
|
_sale_service = sale_service
|
||||||
_save_manager = save_manager
|
_save_manager = save_manager
|
||||||
_fish_catalog = fish_catalog
|
_fish_catalog = fish_catalog
|
||||||
_buyer = buyer
|
_buyers.clear()
|
||||||
_pelican_landmark = pelican_landmark
|
for buyer: FishBuyerProfileType in buyers:
|
||||||
|
if buyer != null and buyer.is_valid():
|
||||||
|
_buyers[buyer.id] = buyer
|
||||||
_reservations = reservations
|
_reservations = reservations
|
||||||
if not _session.peer_removed.is_connected(_on_peer_removed):
|
if not _session.peer_removed.is_connected(_on_peer_removed):
|
||||||
_session.peer_removed.connect(_on_peer_removed)
|
_session.peer_removed.connect(_on_peer_removed)
|
||||||
|
|
@ -71,9 +76,18 @@ func setup(
|
||||||
_session.state_changed.connect(_on_session_state_changed)
|
_session.state_changed.connect(_on_session_state_changed)
|
||||||
|
|
||||||
|
|
||||||
func can_request_sale() -> bool:
|
func is_local_sale_pending() -> bool:
|
||||||
|
return not _pending_local_request_id.is_empty()
|
||||||
|
|
||||||
|
|
||||||
|
func can_request_sale(
|
||||||
|
buyer_id: StringName = PELICAN_BUYER_ID,
|
||||||
|
) -> bool:
|
||||||
|
var buyer: FishBuyerProfileType = _buyers.get(buyer_id)
|
||||||
return (
|
return (
|
||||||
_session != null
|
buyer != null
|
||||||
|
and buyer.is_valid()
|
||||||
|
and _session != null
|
||||||
and _session.is_gameplay_session_active()
|
and _session.is_gameplay_session_active()
|
||||||
and (
|
and (
|
||||||
_session.is_host()
|
_session.is_host()
|
||||||
|
|
@ -84,11 +98,10 @@ func can_request_sale() -> bool:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func is_local_sale_pending() -> bool:
|
func request_local_sale(
|
||||||
return not _pending_local_request_id.is_empty()
|
catch_ids: Array[StringName],
|
||||||
|
buyer_id: StringName = PELICAN_BUYER_ID,
|
||||||
|
) -> String:
|
||||||
func request_local_sale(catch_ids: Array[StringName]) -> String:
|
|
||||||
for catch_id: StringName in catch_ids:
|
for catch_id: StringName in catch_ids:
|
||||||
if _reservations != null and _reservations.is_fish_reserved(catch_id):
|
if _reservations != null and _reservations.is_fish_reserved(catch_id):
|
||||||
local_sale_finished.emit(
|
local_sale_finished.emit(
|
||||||
|
|
@ -100,7 +113,7 @@ func request_local_sale(catch_ids: Array[StringName]) -> String:
|
||||||
"", false, "Selling…", _empty_catch_ids(), 0
|
"", false, "Selling…", _empty_catch_ids(), 0
|
||||||
)
|
)
|
||||||
return ""
|
return ""
|
||||||
if not can_request_sale():
|
if not can_request_sale(buyer_id):
|
||||||
local_sale_finished.emit(
|
local_sale_finished.emit(
|
||||||
"",
|
"",
|
||||||
false,
|
false,
|
||||||
|
|
@ -114,6 +127,12 @@ func request_local_sale(catch_ids: Array[StringName]) -> String:
|
||||||
"", false, "Sale could not be completed.", _empty_catch_ids(), 0
|
"", false, "Sale could not be completed.", _empty_catch_ids(), 0
|
||||||
)
|
)
|
||||||
return ""
|
return ""
|
||||||
|
var buyer: FishBuyerProfileType = _buyers.get(buyer_id)
|
||||||
|
if buyer == null or not buyer.is_valid():
|
||||||
|
local_sale_finished.emit(
|
||||||
|
"", false, "The buyer is unavailable.", _empty_catch_ids(), 0
|
||||||
|
)
|
||||||
|
return ""
|
||||||
var evidence: Array[Dictionary] = []
|
var evidence: Array[Dictionary] = []
|
||||||
for catch_id: StringName in catch_ids:
|
for catch_id: StringName in catch_ids:
|
||||||
var fish_catch: FishCatch = _inventory.get_catch_by_id(catch_id)
|
var fish_catch: FishCatch = _inventory.get_catch_by_id(catch_id)
|
||||||
|
|
@ -127,11 +146,12 @@ func request_local_sale(catch_ids: Array[StringName]) -> String:
|
||||||
var request: Dictionary = {
|
var request: Dictionary = {
|
||||||
"request_id": request_id,
|
"request_id": request_id,
|
||||||
"session_id": _session.get_session_id(),
|
"session_id": _session.get_session_id(),
|
||||||
"buyer_id": str(_buyer.id) if _buyer != null else "",
|
"buyer_id": str(buyer.id),
|
||||||
"catches": evidence,
|
"catches": evidence,
|
||||||
}
|
}
|
||||||
_pending_local_request_id = request_id
|
_pending_local_request_id = request_id
|
||||||
_pending_local_catch_ids = catch_ids.duplicate()
|
_pending_local_catch_ids = catch_ids.duplicate()
|
||||||
|
_pending_local_buyer_id = buyer.id
|
||||||
local_sale_pending.emit(request_id)
|
local_sale_pending.emit(request_id)
|
||||||
if _session.is_host():
|
if _session.is_host():
|
||||||
_handle_sale_request(_session.get_local_peer_id(), request)
|
_handle_sale_request(_session.get_local_peer_id(), request)
|
||||||
|
|
@ -182,27 +202,23 @@ func _handle_sale_request(peer_id: int, data: Dictionary) -> void:
|
||||||
request_id, "A sale is already pending."
|
request_id, "A sale is already pending."
|
||||||
))
|
))
|
||||||
return
|
return
|
||||||
if not _is_buyer_available_for_peer(peer_id):
|
var buyer_id := StringName(str(data["buyer_id"]))
|
||||||
var avatar: Player = _spawn_service.get_avatar(peer_id)
|
var buyer: FishBuyerProfileType = _buyers.get(buyer_id)
|
||||||
var message: String = (
|
if buyer == null or not buyer.is_valid():
|
||||||
"The buyer is unavailable."
|
|
||||||
if avatar == null or _pelican_landmark == null
|
|
||||||
else "Move closer to the buyer."
|
|
||||||
)
|
|
||||||
_record_and_send(peer_id, _rejected_result(request_id, message))
|
|
||||||
return
|
|
||||||
if _buyer == null or not _buyer.is_valid():
|
|
||||||
_record_and_send(peer_id, _rejected_result(
|
_record_and_send(peer_id, _rejected_result(
|
||||||
request_id, "The buyer is unavailable."
|
request_id, "The buyer is unavailable."
|
||||||
))
|
))
|
||||||
return
|
return
|
||||||
if StringName(str(data["buyer_id"])) != _buyer.id:
|
if (
|
||||||
|
buyer_id == MAIN_SHOP_BUYER_ID
|
||||||
|
and not _is_shop_available_for_peer(peer_id)
|
||||||
|
):
|
||||||
_record_and_send(peer_id, _rejected_result(
|
_record_and_send(peer_id, _rejected_result(
|
||||||
request_id, "The buyer is unavailable."
|
request_id, "Move closer to the fishing shop."
|
||||||
))
|
))
|
||||||
return
|
return
|
||||||
var result: Dictionary = _build_authoritative_result(
|
var result: Dictionary = _build_authoritative_result(
|
||||||
peer_id, request_id, data["catches"]
|
peer_id, request_id, data["catches"], buyer
|
||||||
)
|
)
|
||||||
_pending_by_peer[peer_id] = request_id
|
_pending_by_peer[peer_id] = request_id
|
||||||
_record_and_send(peer_id, result)
|
_record_and_send(peer_id, result)
|
||||||
|
|
@ -212,8 +228,9 @@ func _build_authoritative_result(
|
||||||
peer_id: int,
|
peer_id: int,
|
||||||
request_id: String,
|
request_id: String,
|
||||||
evidence_values: Array,
|
evidence_values: Array,
|
||||||
|
buyer: FishBuyerProfileType,
|
||||||
) -> Dictionary:
|
) -> Dictionary:
|
||||||
if _fish_catalog == null or _buyer == null:
|
if _fish_catalog == null or buyer == null:
|
||||||
return _rejected_result(
|
return _rejected_result(
|
||||||
request_id, "The buyer is unavailable."
|
request_id, "The buyer is unavailable."
|
||||||
)
|
)
|
||||||
|
|
@ -245,7 +262,7 @@ func _build_authoritative_result(
|
||||||
return _rejected_result(
|
return _rejected_result(
|
||||||
request_id, "Favorite catches cannot be sold."
|
request_id, "Favorite catches cannot be sold."
|
||||||
)
|
)
|
||||||
var offer: int = _buyer.get_offer(decoded.sale_value)
|
var offer: int = buyer.get_offer(decoded.sale_value)
|
||||||
if (
|
if (
|
||||||
offer < 0
|
offer < 0
|
||||||
or base_value > 9223372036854775807 - decoded.sale_value
|
or base_value > 9223372036854775807 - decoded.sale_value
|
||||||
|
|
@ -262,6 +279,7 @@ func _build_authoritative_result(
|
||||||
"request_id": request_id,
|
"request_id": request_id,
|
||||||
"session_id": _session.get_session_id(),
|
"session_id": _session.get_session_id(),
|
||||||
"target_peer_id": peer_id,
|
"target_peer_id": peer_id,
|
||||||
|
"buyer_id": str(buyer.id),
|
||||||
"accepted": true,
|
"accepted": true,
|
||||||
"catch_ids": catch_ids,
|
"catch_ids": catch_ids,
|
||||||
"payout": payout,
|
"payout": payout,
|
||||||
|
|
@ -338,6 +356,12 @@ func _apply_sale_result(data: Dictionary) -> void:
|
||||||
return
|
return
|
||||||
if str(data["request_id"]) != _pending_local_request_id:
|
if str(data["request_id"]) != _pending_local_request_id:
|
||||||
return
|
return
|
||||||
|
var result_buyer_id := StringName(str(
|
||||||
|
data.get("buyer_id", _pending_local_buyer_id)
|
||||||
|
))
|
||||||
|
if result_buyer_id != _pending_local_buyer_id:
|
||||||
|
_fail_local_apply(data, "Sale could not be completed.")
|
||||||
|
return
|
||||||
if not bool(data["accepted"]):
|
if not bool(data["accepted"]):
|
||||||
_received_results[result_id] = true
|
_received_results[result_id] = true
|
||||||
_bound_dictionary(_received_results)
|
_bound_dictionary(_received_results)
|
||||||
|
|
@ -359,8 +383,12 @@ func _apply_sale_result(data: Dictionary) -> void:
|
||||||
if _reservations != null and _reservations.is_fish_reserved(catch_id):
|
if _reservations != null and _reservations.is_fish_reserved(catch_id):
|
||||||
_fail_local_apply(data, "Reserved in a letter.")
|
_fail_local_apply(data, "Reserved in a letter.")
|
||||||
return
|
return
|
||||||
|
var buyer: FishBuyerProfileType = _buyers.get(_pending_local_buyer_id)
|
||||||
|
if buyer == null or not buyer.is_valid():
|
||||||
|
_fail_local_apply(data, "The buyer is unavailable.")
|
||||||
|
return
|
||||||
var preview: FishSaleResultType = _sale_service.preview_batch(
|
var preview: FishSaleResultType = _sale_service.preview_batch(
|
||||||
catch_ids, _buyer
|
catch_ids, buyer
|
||||||
)
|
)
|
||||||
if not preview.is_success():
|
if not preview.is_success():
|
||||||
var message: String = (
|
var message: String = (
|
||||||
|
|
@ -380,7 +408,7 @@ func _apply_sale_result(data: Dictionary) -> void:
|
||||||
var sequence_snapshot: int = _inventory.get_next_catch_sequence()
|
var sequence_snapshot: int = _inventory.get_next_catch_sequence()
|
||||||
var wallet_snapshot: int = _wallet.get_balance()
|
var wallet_snapshot: int = _wallet.get_balance()
|
||||||
var local_result: FishSaleResultType = _sale_service.sell_batch(
|
var local_result: FishSaleResultType = _sale_service.sell_batch(
|
||||||
catch_ids, _buyer
|
catch_ids, buyer
|
||||||
)
|
)
|
||||||
if not local_result.is_success() or not _save_manager.save_if_dirty():
|
if not local_result.is_success() or not _save_manager.save_if_dirty():
|
||||||
_inventory.replace_all_catches(
|
_inventory.replace_all_catches(
|
||||||
|
|
@ -420,6 +448,7 @@ func _finish_local_sale(
|
||||||
) -> void:
|
) -> void:
|
||||||
_pending_local_request_id = ""
|
_pending_local_request_id = ""
|
||||||
_pending_local_catch_ids.clear()
|
_pending_local_catch_ids.clear()
|
||||||
|
_pending_local_buyer_id = StringName()
|
||||||
local_sale_finished.emit(
|
local_sale_finished.emit(
|
||||||
request_id, accepted, message, catch_ids, payout
|
request_id, accepted, message, catch_ids, payout
|
||||||
)
|
)
|
||||||
|
|
@ -494,33 +523,6 @@ func _handle_acknowledgement(
|
||||||
_pending_by_peer.erase(peer_id)
|
_pending_by_peer.erase(peer_id)
|
||||||
|
|
||||||
|
|
||||||
func _is_buyer_available_for_peer(peer_id: int) -> bool:
|
|
||||||
if (
|
|
||||||
_session == null
|
|
||||||
or not _session.is_host()
|
|
||||||
or not _session.is_gameplay_session_active()
|
|
||||||
or _spawn_service == null
|
|
||||||
or _pelican_landmark == null
|
|
||||||
or not is_instance_valid(_pelican_landmark)
|
|
||||||
):
|
|
||||||
return false
|
|
||||||
var avatar: Player = _spawn_service.get_avatar(peer_id)
|
|
||||||
if (
|
|
||||||
avatar == null
|
|
||||||
or avatar.is_water_recovery_active()
|
|
||||||
or (
|
|
||||||
_network_fishing != null
|
|
||||||
and _network_fishing.has_peer_attempt(peer_id)
|
|
||||||
)
|
|
||||||
):
|
|
||||||
return false
|
|
||||||
return (
|
|
||||||
avatar.global_position.distance_to(
|
|
||||||
_pelican_landmark.global_position
|
|
||||||
) <= PELICAN_INTERACTION_RANGE
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_peer_removed(peer_id: int) -> void:
|
func _on_peer_removed(peer_id: int) -> void:
|
||||||
_request_ledgers.erase(peer_id)
|
_request_ledgers.erase(peer_id)
|
||||||
_pending_by_peer.erase(peer_id)
|
_pending_by_peer.erase(peer_id)
|
||||||
|
|
@ -530,6 +532,28 @@ func _on_peer_removed(peer_id: int) -> void:
|
||||||
_acknowledged_results.erase(result_id)
|
_acknowledged_results.erase(result_id)
|
||||||
|
|
||||||
|
|
||||||
|
func _is_shop_available_for_peer(peer_id: int) -> bool:
|
||||||
|
if (
|
||||||
|
_session == null
|
||||||
|
or not _session.is_host()
|
||||||
|
or not _session.is_gameplay_session_active()
|
||||||
|
or _spawn_service == null
|
||||||
|
or _shop_interaction == null
|
||||||
|
or not is_instance_valid(_shop_interaction)
|
||||||
|
):
|
||||||
|
return false
|
||||||
|
var avatar: Player = _spawn_service.get_avatar(peer_id)
|
||||||
|
return (
|
||||||
|
avatar != null
|
||||||
|
and not avatar.is_water_recovery_active()
|
||||||
|
and (
|
||||||
|
_network_fishing == null
|
||||||
|
or not _network_fishing.has_peer_attempt(peer_id)
|
||||||
|
)
|
||||||
|
and _shop_interaction.is_avatar_in_range(avatar)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _on_session_state_changed(state: NetworkSession.State) -> void:
|
func _on_session_state_changed(state: NetworkSession.State) -> void:
|
||||||
if state in [
|
if state in [
|
||||||
NetworkSession.State.INACTIVE,
|
NetworkSession.State.INACTIVE,
|
||||||
|
|
@ -558,6 +582,7 @@ func _clear_session_state() -> void:
|
||||||
_received_results.clear()
|
_received_results.clear()
|
||||||
_pending_local_request_id = ""
|
_pending_local_request_id = ""
|
||||||
_pending_local_catch_ids.clear()
|
_pending_local_catch_ids.clear()
|
||||||
|
_pending_local_buyer_id = StringName()
|
||||||
|
|
||||||
|
|
||||||
func _bound_dictionary(values: Dictionary) -> void:
|
func _bound_dictionary(values: Dictionary) -> void:
|
||||||
|
|
|
||||||
|
|
@ -47,26 +47,22 @@ func _run() -> void:
|
||||||
main.get_node("%PlayerAssetReservationService")
|
main.get_node("%PlayerAssetReservationService")
|
||||||
as PlayerAssetReservationService
|
as PlayerAssetReservationService
|
||||||
)
|
)
|
||||||
var pelican := (
|
|
||||||
main.get("_test_world") as TestWorld
|
|
||||||
).get_pelican_convenience_landmark()
|
|
||||||
assert(player != null)
|
assert(player != null)
|
||||||
assert(catalog != null and catalog.candidates.size() == 8)
|
assert(catalog != null and catalog.candidates.size() == 8)
|
||||||
assert(sale_service != null)
|
assert(sale_service != null)
|
||||||
assert(shop_service != null)
|
assert(shop_service != null)
|
||||||
assert(session != null and session.is_host())
|
assert(session != null and session.is_host())
|
||||||
assert(reservations != null)
|
assert(reservations != null)
|
||||||
assert(pelican != null)
|
|
||||||
sale_service.local_sale_finished.connect(_on_sale_finished)
|
sale_service.local_sale_finished.connect(_on_sale_finished)
|
||||||
shop_service.local_purchase_finished.connect(_on_shop_finished)
|
shop_service.local_purchase_finished.connect(_on_shop_finished)
|
||||||
player.global_position = pelican.global_position
|
|
||||||
await process_frame
|
await process_frame
|
||||||
|
|
||||||
_test_each_species(player, catalog, sale_service)
|
_test_each_species(player, catalog, sale_service)
|
||||||
_test_multi_sale(player, catalog, sale_service)
|
_test_multi_sale(player, catalog, sale_service)
|
||||||
_test_reservations(player, catalog, sale_service, reservations)
|
_test_reservations(player, catalog, sale_service, reservations)
|
||||||
_test_rejection_cleanup(player, catalog, sale_service, pelican)
|
_test_anywhere_sale(player, catalog, sale_service)
|
||||||
await _test_player_menu_sale(main, player, catalog, sale_service, pelican)
|
await _test_player_menu_sale(main, player, catalog, sale_service)
|
||||||
|
await _test_host_shop_sale(main, player, catalog, sale_service)
|
||||||
|
|
||||||
assert(session.set_host_open(true))
|
assert(session.set_host_open(true))
|
||||||
assert(session.state == NetworkSession.State.OPEN_HOST)
|
assert(session.state == NetworkSession.State.OPEN_HOST)
|
||||||
|
|
@ -76,6 +72,9 @@ func _run() -> void:
|
||||||
assert(session.set_host_open(false))
|
assert(session.set_host_open(false))
|
||||||
|
|
||||||
await _test_host_shop_purchase(main, player, shop_service)
|
await _test_host_shop_purchase(main, player, shop_service)
|
||||||
|
await _test_fishing_shop_sale_ui(
|
||||||
|
main, player, catalog, sale_service, reservations
|
||||||
|
)
|
||||||
assert(not sale_service.is_local_sale_pending())
|
assert(not sale_service.is_local_sale_pending())
|
||||||
assert(not shop_service.is_local_purchase_pending())
|
assert(not shop_service.is_local_purchase_pending())
|
||||||
|
|
||||||
|
|
@ -111,14 +110,9 @@ func _run_multiplayer_host() -> void:
|
||||||
assert(remote_peer_id > 1)
|
assert(remote_peer_id > 1)
|
||||||
var spawn_service := main.get("_player_spawn_service") as PlayerSpawnService
|
var spawn_service := main.get("_player_spawn_service") as PlayerSpawnService
|
||||||
var remote_avatar := spawn_service.get_avatar(remote_peer_id)
|
var remote_avatar := spawn_service.get_avatar(remote_peer_id)
|
||||||
var pelican := (
|
assert(remote_avatar != null)
|
||||||
main.get("_test_world") as TestWorld
|
# Pelican sales are available from the Cooler anywhere. The host-owned
|
||||||
).get_pelican_convenience_landmark()
|
# avatar only moves when the later Fishing Shop purchase needs proximity.
|
||||||
assert(remote_avatar != null and pelican != null)
|
|
||||||
# The client cannot teleport its authoritative avatar. Place the host-owned
|
|
||||||
# test avatar at each interaction so this fixture validates the remote
|
|
||||||
# transaction path without conflating it with the movement suite.
|
|
||||||
remote_avatar.global_position = pelican.global_position
|
|
||||||
await create_timer(5.0).timeout
|
await create_timer(5.0).timeout
|
||||||
var interaction := main.get("_shop_interaction") as FishingShopInteraction
|
var interaction := main.get("_shop_interaction") as FishingShopInteraction
|
||||||
assert(interaction != null)
|
assert(interaction != null)
|
||||||
|
|
@ -156,10 +150,6 @@ func _run_multiplayer_client() -> void:
|
||||||
var shop_service := main.get_node("%NetworkShopService") as NetworkShopService
|
var shop_service := main.get_node("%NetworkShopService") as NetworkShopService
|
||||||
sale_service.local_sale_finished.connect(_on_sale_finished)
|
sale_service.local_sale_finished.connect(_on_sale_finished)
|
||||||
shop_service.local_purchase_finished.connect(_on_shop_finished)
|
shop_service.local_purchase_finished.connect(_on_shop_finished)
|
||||||
var pelican := (
|
|
||||||
main.get("_test_world") as TestWorld
|
|
||||||
).get_pelican_convenience_landmark()
|
|
||||||
player.global_position = pelican.global_position
|
|
||||||
var catch_ids: Array[StringName] = []
|
var catch_ids: Array[StringName] = []
|
||||||
for fish: FishData in catalog.candidates:
|
for fish: FishData in catalog.candidates:
|
||||||
var fish_catch := _make_catch(fish)
|
var fish_catch := _make_catch(fish)
|
||||||
|
|
@ -182,6 +172,28 @@ func _run_multiplayer_client() -> void:
|
||||||
var interaction := main.get("_shop_interaction") as FishingShopInteraction
|
var interaction := main.get("_shop_interaction") as FishingShopInteraction
|
||||||
player.global_position = interaction.global_position
|
player.global_position = interaction.global_position
|
||||||
await create_timer(5.5).timeout
|
await create_timer(5.5).timeout
|
||||||
|
var shop_catch := _make_catch(catalog.candidates.front())
|
||||||
|
player.inventory.add_catch(shop_catch)
|
||||||
|
var shop_sale_balance_before: int = player.wallet.get_balance()
|
||||||
|
_sale_result.clear()
|
||||||
|
assert(not sale_service.request_local_sale(
|
||||||
|
[shop_catch.catch_id],
|
||||||
|
NetworkSaleService.MAIN_SHOP_BUYER_ID,
|
||||||
|
).is_empty())
|
||||||
|
var shop_sale_deadline: int = Time.get_ticks_msec() + 10000
|
||||||
|
while Time.get_ticks_msec() < shop_sale_deadline:
|
||||||
|
await process_frame
|
||||||
|
if not _sale_result.is_empty():
|
||||||
|
break
|
||||||
|
print("Client shop sale result: ", _sale_result)
|
||||||
|
assert(not _sale_result.is_empty() and bool(_sale_result[1]))
|
||||||
|
assert(not player.inventory.contains_catch_id(shop_catch.catch_id))
|
||||||
|
assert(
|
||||||
|
player.wallet.get_balance()
|
||||||
|
== shop_sale_balance_before + shop_catch.sale_value
|
||||||
|
)
|
||||||
|
assert(not sale_service.is_local_sale_pending())
|
||||||
|
|
||||||
assert(player.wallet.credit(100))
|
assert(player.wallet.credit(100))
|
||||||
_shop_result.clear()
|
_shop_result.clear()
|
||||||
assert(not shop_service.request_supply(&"coffee").is_empty())
|
assert(not shop_service.request_supply(&"coffee").is_empty())
|
||||||
|
|
@ -277,34 +289,90 @@ func _test_reservations(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func _test_rejection_cleanup(
|
func _test_anywhere_sale(
|
||||||
player: Player,
|
player: Player,
|
||||||
catalog: FishPool,
|
catalog: FishPool,
|
||||||
sale_service: NetworkSaleService,
|
sale_service: NetworkSaleService,
|
||||||
pelican: Node3D,
|
|
||||||
) -> void:
|
) -> void:
|
||||||
var fish_catch := _make_catch(catalog.candidates[5])
|
var fish_catch := _make_catch(catalog.candidates[5])
|
||||||
player.inventory.add_catch(fish_catch)
|
player.inventory.add_catch(fish_catch)
|
||||||
player.global_position = pelican.global_position + Vector3(20.0, 0.0, 0.0)
|
# The spawn point is intentionally far from the Pelican landmark. Discounted
|
||||||
_assert_sale(player, sale_service, [fish_catch.catch_id], false)
|
# Pelican sales are a convenience action and do not require proximity.
|
||||||
assert(player.inventory.contains_catch_id(fish_catch.catch_id))
|
player.global_position = Vector3(0.0, 3.95, 13.0)
|
||||||
assert(not sale_service.is_local_sale_pending())
|
|
||||||
player.global_position = pelican.global_position
|
|
||||||
_assert_sale(player, sale_service, [fish_catch.catch_id], true)
|
_assert_sale(player, sale_service, [fish_catch.catch_id], true)
|
||||||
|
|
||||||
|
|
||||||
|
func _test_host_shop_sale(
|
||||||
|
main: Node,
|
||||||
|
player: Player,
|
||||||
|
catalog: FishPool,
|
||||||
|
sale_service: NetworkSaleService,
|
||||||
|
) -> void:
|
||||||
|
var interaction := main.get("_shop_interaction") as FishingShopInteraction
|
||||||
|
assert(interaction != null)
|
||||||
|
var out_of_range_catch := _make_catch(catalog.candidates[6])
|
||||||
|
player.inventory.add_catch(out_of_range_catch)
|
||||||
|
player.global_position = Vector3(0.0, 3.95, 13.0)
|
||||||
|
for _frame: int in 4:
|
||||||
|
await physics_frame
|
||||||
|
_assert_sale(
|
||||||
|
player,
|
||||||
|
sale_service,
|
||||||
|
[out_of_range_catch.catch_id],
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
NetworkSaleService.MAIN_SHOP_BUYER_ID,
|
||||||
|
)
|
||||||
|
player.global_position = interaction.global_position
|
||||||
|
for _frame: int in 4:
|
||||||
|
await physics_frame
|
||||||
|
assert(interaction.is_avatar_in_range(player))
|
||||||
|
var balance_before: int = player.wallet.get_balance()
|
||||||
|
_assert_sale(
|
||||||
|
player,
|
||||||
|
sale_service,
|
||||||
|
[out_of_range_catch.catch_id],
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
NetworkSaleService.MAIN_SHOP_BUYER_ID,
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
player.wallet.get_balance()
|
||||||
|
== balance_before + out_of_range_catch.sale_value
|
||||||
|
)
|
||||||
|
var species_ids: Array[StringName] = []
|
||||||
|
var expected_payout: int = 0
|
||||||
|
for fish: FishData in catalog.candidates:
|
||||||
|
var fish_catch := _make_catch(fish)
|
||||||
|
player.inventory.add_catch(fish_catch)
|
||||||
|
species_ids.append(fish_catch.catch_id)
|
||||||
|
expected_payout += fish_catch.sale_value
|
||||||
|
var species_balance_before: int = player.wallet.get_balance()
|
||||||
|
_assert_sale(
|
||||||
|
player,
|
||||||
|
sale_service,
|
||||||
|
species_ids,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
NetworkSaleService.MAIN_SHOP_BUYER_ID,
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
player.wallet.get_balance()
|
||||||
|
== species_balance_before + expected_payout
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _test_player_menu_sale(
|
func _test_player_menu_sale(
|
||||||
main: Node,
|
main: Node,
|
||||||
player: Player,
|
player: Player,
|
||||||
catalog: FishPool,
|
catalog: FishPool,
|
||||||
sale_service: NetworkSaleService,
|
sale_service: NetworkSaleService,
|
||||||
pelican: Node3D,
|
|
||||||
) -> void:
|
) -> void:
|
||||||
player.global_position = pelican.global_position
|
|
||||||
var game_ui := main.get_node("%GameUI") as GameUI
|
var game_ui := main.get_node("%GameUI") as GameUI
|
||||||
var player_menu := game_ui.get_node("%PlayerMenu") as PlayerMenu
|
var player_menu := game_ui.get_node("%PlayerMenu") as PlayerMenu
|
||||||
var fish_catch := _make_catch(catalog.candidates[6])
|
var fish_catch := _make_catch(catalog.candidates[6])
|
||||||
player.inventory.add_catch(fish_catch)
|
player.inventory.add_catch(fish_catch)
|
||||||
|
player.global_position = Vector3(0.0, 3.95, 13.0)
|
||||||
player_menu.open_menu()
|
player_menu.open_menu()
|
||||||
await create_timer(2.2).timeout
|
await create_timer(2.2).timeout
|
||||||
player_menu.call("_on_catch_card_pressed", fish_catch.catch_id)
|
player_menu.call("_on_catch_card_pressed", fish_catch.catch_id)
|
||||||
|
|
@ -312,15 +380,23 @@ func _test_player_menu_sale(
|
||||||
var sell_action := player_menu.get_node("%SellBubble") as Button
|
var sell_action := player_menu.get_node("%SellBubble") as Button
|
||||||
var confirmation := player_menu.get_node("%SaleConfirmation") as Control
|
var confirmation := player_menu.get_node("%SaleConfirmation") as Control
|
||||||
var confirm_button := player_menu.get_node("%ConfirmSaleButton") as Button
|
var confirm_button := player_menu.get_node("%ConfirmSaleButton") as Button
|
||||||
|
var ui_viewport := main.get_node(
|
||||||
|
"UIPresentation/UIViewport"
|
||||||
|
) as SubViewport
|
||||||
assert(sell_action.visible and not sell_action.disabled)
|
assert(sell_action.visible and not sell_action.disabled)
|
||||||
assert(sell_action.mouse_filter == Control.MOUSE_FILTER_STOP)
|
assert(sell_action.mouse_filter == Control.MOUSE_FILTER_STOP)
|
||||||
assert(sell_action.focus_mode == Control.FOCUS_ALL)
|
assert(sell_action.focus_mode == Control.FOCUS_ALL)
|
||||||
sell_action.pressed.emit()
|
assert(ui_viewport != null)
|
||||||
|
await _activate_focused_button(sell_action, ui_viewport)
|
||||||
await process_frame
|
await process_frame
|
||||||
assert(confirmation.visible)
|
assert(confirmation.visible)
|
||||||
assert(confirm_button.visible and not confirm_button.disabled)
|
assert(confirm_button.visible and not confirm_button.disabled)
|
||||||
|
assert(
|
||||||
|
confirmation.z_index
|
||||||
|
> (player_menu.get_node("%CoolerOuterWall") as Control).z_index
|
||||||
|
)
|
||||||
_sale_result.clear()
|
_sale_result.clear()
|
||||||
confirm_button.pressed.emit()
|
await _activate_focused_button(confirm_button, ui_viewport)
|
||||||
await process_frame
|
await process_frame
|
||||||
assert(not _sale_result.is_empty() and bool(_sale_result[1]))
|
assert(not _sale_result.is_empty() and bool(_sale_result[1]))
|
||||||
assert(not player.inventory.contains_catch_id(fish_catch.catch_id))
|
assert(not player.inventory.contains_catch_id(fish_catch.catch_id))
|
||||||
|
|
@ -339,6 +415,21 @@ func _test_player_menu_sale(
|
||||||
await create_timer(2.2).timeout
|
await create_timer(2.2).timeout
|
||||||
|
|
||||||
|
|
||||||
|
func _activate_focused_button(
|
||||||
|
button: Button,
|
||||||
|
ui_viewport: SubViewport,
|
||||||
|
) -> void:
|
||||||
|
button.grab_focus()
|
||||||
|
await process_frame
|
||||||
|
assert(button.has_focus())
|
||||||
|
for is_pressed: bool in [true, false]:
|
||||||
|
var accept := InputEventAction.new()
|
||||||
|
accept.action = &"ui_accept"
|
||||||
|
accept.pressed = is_pressed
|
||||||
|
ui_viewport.push_input(accept, false)
|
||||||
|
await process_frame
|
||||||
|
|
||||||
|
|
||||||
func _test_host_shop_purchase(
|
func _test_host_shop_purchase(
|
||||||
main: Node,
|
main: Node,
|
||||||
player: Player,
|
player: Player,
|
||||||
|
|
@ -359,16 +450,123 @@ func _test_host_shop_purchase(
|
||||||
assert(not shop_service.is_local_purchase_pending())
|
assert(not shop_service.is_local_purchase_pending())
|
||||||
|
|
||||||
|
|
||||||
|
func _test_fishing_shop_sale_ui(
|
||||||
|
main: Node,
|
||||||
|
player: Player,
|
||||||
|
catalog: FishPool,
|
||||||
|
sale_service: NetworkSaleService,
|
||||||
|
reservations: PlayerAssetReservationService,
|
||||||
|
) -> void:
|
||||||
|
var interaction := main.get("_shop_interaction") as FishingShopInteraction
|
||||||
|
var game_ui := main.get_node("%GameUI") as GameUI
|
||||||
|
var shop := game_ui.get_node("%FishingShop") as FishingShop
|
||||||
|
var player_menu := game_ui.get_node("%PlayerMenu") as PlayerMenu
|
||||||
|
var shop_backdrop := main.get_node("%ShopBackdrop") as ColorRect
|
||||||
|
var ui_viewport := main.get_node(
|
||||||
|
"UIPresentation/UIViewport"
|
||||||
|
) as SubViewport
|
||||||
|
assert(
|
||||||
|
interaction != null
|
||||||
|
and shop != null
|
||||||
|
and player_menu != null
|
||||||
|
and shop_backdrop != null
|
||||||
|
and ui_viewport != null
|
||||||
|
)
|
||||||
|
player.global_position = interaction.global_position
|
||||||
|
for _frame: int in 4:
|
||||||
|
await physics_frame
|
||||||
|
var fish_catch := _make_catch(catalog.candidates[7])
|
||||||
|
var reserved_catch := _make_catch(catalog.candidates[6])
|
||||||
|
player.inventory.add_catch(fish_catch)
|
||||||
|
player.inventory.add_catch(reserved_catch)
|
||||||
|
var reservation_id := "shop-ui-reservation"
|
||||||
|
assert(reservations.reserve(reservation_id, {
|
||||||
|
"type": PlayerAssetReservationService.AttachmentType.FISH,
|
||||||
|
"catch_id": str(reserved_catch.catch_id),
|
||||||
|
"catch": reserved_catch.to_network_dict(),
|
||||||
|
}))
|
||||||
|
var balance_before: int = player.wallet.get_balance()
|
||||||
|
assert(shop.open_shop())
|
||||||
|
await process_frame
|
||||||
|
assert(shop_backdrop.visible)
|
||||||
|
assert(shop_backdrop.material is ShaderMaterial)
|
||||||
|
assert(shop.has_node("InputBlocker") and not shop.has_node("Dimmer"))
|
||||||
|
var shop_panel := shop.get_node("%ShopPanel") as PanelContainer
|
||||||
|
assert(shop_panel != null and shop_panel.size == Vector2(980.0, 600.0))
|
||||||
|
var shop_panel_style := shop_panel.get_theme_stylebox("panel") as StyleBoxFlat
|
||||||
|
assert(shop_panel_style != null)
|
||||||
|
assert(shop_panel_style.corner_radius_top_left == 24)
|
||||||
|
var buy_mode := shop.get_node("%BuyModeButton") as Button
|
||||||
|
var sell_mode := shop.get_node("%SellModeButton") as Button
|
||||||
|
assert(buy_mode.visible and sell_mode.visible)
|
||||||
|
await _activate_focused_button(sell_mode, ui_viewport)
|
||||||
|
await process_frame
|
||||||
|
assert(shop.visible and not player_menu.visible)
|
||||||
|
assert(not shop.has_node("ShopPanel/Margin/Layout/Body/FishSales"))
|
||||||
|
assert((shop.get_node("%ShopCoolerPage") as Control).visible)
|
||||||
|
assert(not (shop.get_node("%ShopPanel") as Control).visible)
|
||||||
|
var mounted_cooler := player_menu.get("_cooler_page") as Control
|
||||||
|
assert(mounted_cooler != null and mounted_cooler.visible)
|
||||||
|
assert(
|
||||||
|
mounted_cooler.get_parent() == shop.get_node("%ShopCoolerMount")
|
||||||
|
)
|
||||||
|
var cooler_outer_wall := player_menu.get("_cooler_outer_wall") as Control
|
||||||
|
var water_surface := player_menu.get("_cooler_water_surface") as ColorRect
|
||||||
|
assert(cooler_outer_wall != null and cooler_outer_wall.visible)
|
||||||
|
assert(water_surface.visible and water_surface.material is ShaderMaterial)
|
||||||
|
assert(
|
||||||
|
StringName(
|
||||||
|
(player_menu.get("_sale_buyer_override") as FishBuyerProfile).id
|
||||||
|
) == NetworkSaleService.MAIN_SHOP_BUYER_ID
|
||||||
|
)
|
||||||
|
var fish_nodes: Dictionary = player_menu.get("_fish_nodes")
|
||||||
|
var fish_button := fish_nodes.get(fish_catch.catch_id) as Button
|
||||||
|
var reserved_button := fish_nodes.get(reserved_catch.catch_id) as Button
|
||||||
|
assert(fish_button != null and fish_button.visible)
|
||||||
|
assert(reserved_button != null and reserved_button.visible)
|
||||||
|
await _activate_focused_button(fish_button, ui_viewport)
|
||||||
|
var sell_button := player_menu.get("_sell_bubble") as Button
|
||||||
|
assert(sell_button.visible and not sell_button.disabled)
|
||||||
|
await _activate_focused_button(sell_button, ui_viewport)
|
||||||
|
await process_frame
|
||||||
|
var confirmation := player_menu.get("_sale_confirmation") as Control
|
||||||
|
var confirm_button := player_menu.get("_confirm_sale_button") as Button
|
||||||
|
assert(confirmation.visible)
|
||||||
|
assert(
|
||||||
|
confirmation.z_index
|
||||||
|
> cooler_outer_wall.z_index
|
||||||
|
)
|
||||||
|
_sale_result.clear()
|
||||||
|
await _activate_focused_button(confirm_button, ui_viewport)
|
||||||
|
await process_frame
|
||||||
|
assert(not _sale_result.is_empty() and bool(_sale_result[1]))
|
||||||
|
assert(not player.inventory.contains_catch_id(fish_catch.catch_id))
|
||||||
|
assert(player.wallet.get_balance() == balance_before + fish_catch.sale_value)
|
||||||
|
assert(not sale_service.is_local_sale_pending())
|
||||||
|
assert(reservations.release(reservation_id))
|
||||||
|
var back_button := shop.get_node("%BackToShopButton") as Button
|
||||||
|
await _activate_focused_button(back_button, ui_viewport)
|
||||||
|
await process_frame
|
||||||
|
assert(shop.visible and (shop.get_node("%ShopPanel") as Control).visible)
|
||||||
|
assert(not (shop.get_node("%ShopCoolerPage") as Control).visible)
|
||||||
|
assert(not player_menu.is_shop_cooler_mounted())
|
||||||
|
shop.close_shop()
|
||||||
|
await process_frame
|
||||||
|
|
||||||
|
|
||||||
func _assert_sale(
|
func _assert_sale(
|
||||||
player: Player,
|
player: Player,
|
||||||
sale_service: NetworkSaleService,
|
sale_service: NetworkSaleService,
|
||||||
catch_ids: Array[StringName],
|
catch_ids: Array[StringName],
|
||||||
expected_success: bool,
|
expected_success: bool,
|
||||||
expect_request_id: bool = true,
|
expect_request_id: bool = true,
|
||||||
|
buyer_id: StringName = NetworkSaleService.PELICAN_BUYER_ID,
|
||||||
) -> void:
|
) -> void:
|
||||||
var balance_before: int = player.wallet.get_balance()
|
var balance_before: int = player.wallet.get_balance()
|
||||||
_sale_result.clear()
|
_sale_result.clear()
|
||||||
var request_id: String = sale_service.request_local_sale(catch_ids)
|
var request_id: String = sale_service.request_local_sale(
|
||||||
|
catch_ids, buyer_id
|
||||||
|
)
|
||||||
assert((not request_id.is_empty()) == expect_request_id)
|
assert((not request_id.is_empty()) == expect_request_id)
|
||||||
assert(not _sale_result.is_empty())
|
assert(not _sale_result.is_empty())
|
||||||
assert(bool(_sale_result[1]) == expected_success)
|
assert(bool(_sale_result[1]) == expected_success)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ const CollectionLogType = preload("res://collection/collection_log.gd")
|
||||||
const FishDataType = preload("res://fish/fish_data.gd")
|
const FishDataType = preload("res://fish/fish_data.gd")
|
||||||
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
||||||
const FishPoolType = preload("res://fish/fish_pool.gd")
|
const FishPoolType = preload("res://fish/fish_pool.gd")
|
||||||
|
const FishBuyerProfileType = preload("res://economy/fish_buyer_profile.gd")
|
||||||
const FishSelectorType = preload("res://fish/fish_selector.gd")
|
const FishSelectorType = preload("res://fish/fish_selector.gd")
|
||||||
const FishingContextType = preload("res://fishing/fishing_context.gd")
|
const FishingContextType = preload("res://fishing/fishing_context.gd")
|
||||||
const NetworkSaleServiceType = preload(
|
const NetworkSaleServiceType = preload(
|
||||||
|
|
@ -227,7 +228,10 @@ func _validate_catches_and_authoritative_sale() -> void:
|
||||||
root.add_child(sale_service)
|
root.add_child(sale_service)
|
||||||
sale_service.set("_session", session)
|
sale_service.set("_session", session)
|
||||||
sale_service.set("_fish_catalog", Catalog)
|
sale_service.set("_fish_catalog", Catalog)
|
||||||
sale_service.set("_buyer", PelicanBuyer)
|
var sale_buyers: Dictionary[StringName, FishBuyerProfileType] = {
|
||||||
|
PelicanBuyer.id: PelicanBuyer,
|
||||||
|
}
|
||||||
|
sale_service.set("_buyers", sale_buyers)
|
||||||
|
|
||||||
for index: int in Catalog.candidates.size():
|
for index: int in Catalog.candidates.size():
|
||||||
var fish: FishDataType = Catalog.candidates[index]
|
var fish: FishDataType = Catalog.candidates[index]
|
||||||
|
|
@ -257,6 +261,7 @@ func _validate_catches_and_authoritative_sale() -> void:
|
||||||
1,
|
1,
|
||||||
"catalog_sale_%d" % index,
|
"catalog_sale_%d" % index,
|
||||||
[loaded.to_network_dict()],
|
[loaded.to_network_dict()],
|
||||||
|
PelicanBuyer,
|
||||||
)
|
)
|
||||||
assert(bool(sale_result.get("accepted", false)))
|
assert(bool(sale_result.get("accepted", false)))
|
||||||
assert(int(sale_result.get("base_value", -1)) == loaded.sale_value)
|
assert(int(sale_result.get("base_value", -1)) == loaded.sale_value)
|
||||||
|
|
|
||||||
273
tests/inventory_notepad_art_validation.gd
Normal file
273
tests/inventory_notepad_art_validation.gd
Normal file
|
|
@ -0,0 +1,273 @@
|
||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
const PlayerMenuScene = preload("res://ui/player_menu.tscn")
|
||||||
|
const UIReferencePresentationType = preload(
|
||||||
|
"res://ui/ui_reference_presentation.gd"
|
||||||
|
)
|
||||||
|
const EXPECTED_HOST_SIZE := Vector2(278.0, 484.0)
|
||||||
|
const EXPECTED_ART_SIZE := Vector2(306.28125, 580.8)
|
||||||
|
const EXPECTED_ART_POSITION := Vector2(-4.140625, -28.4)
|
||||||
|
const INVENTORY_PANEL_RECT := Rect2(54.0, 166.0, 882.0, 484.0)
|
||||||
|
const NOTEPAD_HOST_RECT := Rect2(952.0, 166.0, 278.0, 484.0)
|
||||||
|
const NOTEPAD_ART_RECT := Rect2(
|
||||||
|
NOTEPAD_HOST_RECT.position + EXPECTED_ART_POSITION,
|
||||||
|
EXPECTED_ART_SIZE,
|
||||||
|
)
|
||||||
|
const HOTBAR_BUBBLE_FIELD_RECT := Rect2(245.0, 608.0, 790.0, 104.0)
|
||||||
|
const TARGET_SIZES: Array[Vector2] = [
|
||||||
|
Vector2(1280.0, 720.0),
|
||||||
|
Vector2(1920.0, 1080.0),
|
||||||
|
Vector2(2560.0, 1440.0),
|
||||||
|
Vector2(3840.0, 2160.0),
|
||||||
|
Vector2(640.0, 480.0),
|
||||||
|
Vector2(1024.0, 768.0),
|
||||||
|
Vector2(1729.0, 973.0),
|
||||||
|
Vector2(2560.0, 1080.0),
|
||||||
|
Vector2(3440.0, 1440.0),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
func _initialize() -> void:
|
||||||
|
call_deferred("_run")
|
||||||
|
|
||||||
|
|
||||||
|
func _run() -> void:
|
||||||
|
_print_runtime_parameters()
|
||||||
|
var presentation_stage := Control.new()
|
||||||
|
presentation_stage.name = "CanonicalUIStage"
|
||||||
|
_apply_stage_layout(presentation_stage, Vector2(root.size))
|
||||||
|
root.add_child(presentation_stage)
|
||||||
|
var player_menu := PlayerMenuScene.instantiate() as PlayerMenu
|
||||||
|
presentation_stage.add_child(player_menu)
|
||||||
|
await process_frame
|
||||||
|
var notepads: Array[Node] = player_menu.find_children(
|
||||||
|
"*", "InventoryNotepad", true, false
|
||||||
|
)
|
||||||
|
assert(notepads.size() == 3)
|
||||||
|
for node: Node in notepads:
|
||||||
|
var notepad := node as InventoryNotepad
|
||||||
|
assert(notepad != null)
|
||||||
|
assert(notepad.size.is_equal_approx(EXPECTED_HOST_SIZE))
|
||||||
|
assert(notepad.mouse_filter == Control.MOUSE_FILTER_PASS)
|
||||||
|
var art_rect: Rect2 = notepad.get_art_rect()
|
||||||
|
assert(art_rect.size.is_equal_approx(EXPECTED_ART_SIZE))
|
||||||
|
assert(art_rect.position.is_equal_approx(EXPECTED_ART_POSITION))
|
||||||
|
assert(art_rect.get_center().is_equal_approx(
|
||||||
|
EXPECTED_HOST_SIZE * 0.5
|
||||||
|
+ InventoryNotepad.NOTEPAD_ART_OFFSET
|
||||||
|
))
|
||||||
|
_validate_shared_inventory_geometry(player_menu)
|
||||||
|
_validate_inventory_layering(player_menu)
|
||||||
|
_validate_resolution_matrix()
|
||||||
|
await _capture_inventory_pages(player_menu)
|
||||||
|
print("Inventory notepad artwork validation: PASS")
|
||||||
|
presentation_stage.queue_free()
|
||||||
|
await process_frame
|
||||||
|
quit()
|
||||||
|
|
||||||
|
|
||||||
|
func _print_runtime_parameters() -> void:
|
||||||
|
var window: Window = root
|
||||||
|
var screen_index: int = window.current_screen
|
||||||
|
print(
|
||||||
|
(
|
||||||
|
"Runtime UI: window=%s screen=%s screen_scale=%.3f dpi=%d "
|
||||||
|
+ "content_size=%s content_mode=%d content_aspect=%d "
|
||||||
|
+ "content_factor=%.3f"
|
||||||
|
)
|
||||||
|
% [
|
||||||
|
window.size,
|
||||||
|
DisplayServer.screen_get_size(screen_index),
|
||||||
|
DisplayServer.screen_get_scale(screen_index),
|
||||||
|
DisplayServer.screen_get_dpi(screen_index),
|
||||||
|
window.content_scale_size,
|
||||||
|
window.content_scale_mode,
|
||||||
|
window.content_scale_aspect,
|
||||||
|
window.content_scale_factor,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _apply_stage_layout(stage: Control, display_size: Vector2) -> void:
|
||||||
|
stage.set_anchors_preset(Control.PRESET_TOP_LEFT)
|
||||||
|
stage.position = UIReferencePresentationType.get_offset(display_size)
|
||||||
|
stage.size = UIReferencePresentationType.REFERENCE_SIZE
|
||||||
|
stage.scale = Vector2.ONE * UIReferencePresentationType.get_scale(
|
||||||
|
display_size
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _validate_shared_inventory_geometry(player_menu: PlayerMenu) -> void:
|
||||||
|
for node_name: StringName in [
|
||||||
|
&"DetailConstellation",
|
||||||
|
&"BagDetailConstellation",
|
||||||
|
&"TackleDetailPanel",
|
||||||
|
]:
|
||||||
|
var host := player_menu.get_node("%%%s" % node_name) as Control
|
||||||
|
assert(host != null)
|
||||||
|
assert(host.position.is_equal_approx(Vector2(952.0, 166.0)))
|
||||||
|
assert(host.size.is_equal_approx(EXPECTED_HOST_SIZE))
|
||||||
|
|
||||||
|
|
||||||
|
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 sale_confirmation := player_menu.get_node("%SaleConfirmation") as Control
|
||||||
|
var cooler_panel := player_menu.get_node("%CoolerOuterWall") as Control
|
||||||
|
var tackle_panel := player_menu.get_node("%TackleMainPanel") as Control
|
||||||
|
assert(bag_filters != null)
|
||||||
|
assert(bag_panel != null)
|
||||||
|
assert(sale_confirmation != null)
|
||||||
|
assert(cooler_panel != null)
|
||||||
|
assert(tackle_panel != null)
|
||||||
|
assert(bag_filters.z_index > bag_panel.z_index)
|
||||||
|
assert(sale_confirmation.z_index > cooler_panel.z_index)
|
||||||
|
assert(sale_confirmation.z_index > tackle_panel.z_index)
|
||||||
|
# The contextual Hotbar uses z=90 while the Player Menu is open.
|
||||||
|
assert(sale_confirmation.z_index > 90)
|
||||||
|
assert(sale_confirmation.position.is_equal_approx(Vector2(380.0, 265.0)))
|
||||||
|
assert(sale_confirmation.size.is_equal_approx(Vector2(520.0, 190.0)))
|
||||||
|
var confirmation_style := sale_confirmation.get_theme_stylebox(
|
||||||
|
"panel"
|
||||||
|
) as StyleBoxFlat
|
||||||
|
assert(confirmation_style != null)
|
||||||
|
assert(
|
||||||
|
confirmation_style.bg_color.is_equal_approx(
|
||||||
|
UtilityPageStyle.OCEAN_PANEL_DEEP
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _validate_resolution_matrix() -> void:
|
||||||
|
for target_size: Vector2 in TARGET_SIZES:
|
||||||
|
var stage_rect: Rect2 = UIReferencePresentationType.get_rect(
|
||||||
|
target_size
|
||||||
|
)
|
||||||
|
var stage_scale: float = UIReferencePresentationType.get_scale(
|
||||||
|
target_size
|
||||||
|
)
|
||||||
|
assert(is_equal_approx(
|
||||||
|
stage_rect.size.x / stage_rect.size.y,
|
||||||
|
1280.0 / 720.0,
|
||||||
|
))
|
||||||
|
assert(stage_rect.get_center().is_equal_approx(target_size * 0.5))
|
||||||
|
_validate_transformed_rect(
|
||||||
|
"Inventory panel", INVENTORY_PANEL_RECT, stage_rect, stage_scale
|
||||||
|
)
|
||||||
|
_validate_transformed_rect(
|
||||||
|
"Notepad host", NOTEPAD_HOST_RECT, stage_rect, stage_scale
|
||||||
|
)
|
||||||
|
_validate_transformed_rect(
|
||||||
|
"Notepad art", NOTEPAD_ART_RECT, stage_rect, stage_scale
|
||||||
|
)
|
||||||
|
_validate_transformed_rect(
|
||||||
|
"Hotbar", HOTBAR_BUBBLE_FIELD_RECT, stage_rect, stage_scale
|
||||||
|
)
|
||||||
|
var screen_host: Rect2 = _to_screen_rect(
|
||||||
|
NOTEPAD_HOST_RECT, stage_rect.position, stage_scale
|
||||||
|
)
|
||||||
|
var screen_art: Rect2 = _to_screen_rect(
|
||||||
|
NOTEPAD_ART_RECT, stage_rect.position, stage_scale
|
||||||
|
)
|
||||||
|
assert(is_equal_approx(
|
||||||
|
screen_art.size.y,
|
||||||
|
screen_host.size.y * InventoryNotepad.NOTEPAD_CANONICAL_OVERSCAN,
|
||||||
|
))
|
||||||
|
assert(screen_art.get_center().is_equal_approx(
|
||||||
|
screen_host.get_center()
|
||||||
|
+ InventoryNotepad.NOTEPAD_ART_OFFSET * stage_scale
|
||||||
|
))
|
||||||
|
print(
|
||||||
|
(
|
||||||
|
"UI geometry: window=%s stage=%s scale=%.6f "
|
||||||
|
+ "inventory=%s notepad=%s art=%s hotbar=%s"
|
||||||
|
)
|
||||||
|
% [
|
||||||
|
target_size,
|
||||||
|
stage_rect,
|
||||||
|
stage_scale,
|
||||||
|
_to_screen_rect(
|
||||||
|
INVENTORY_PANEL_RECT,
|
||||||
|
stage_rect.position,
|
||||||
|
stage_scale,
|
||||||
|
),
|
||||||
|
screen_host,
|
||||||
|
screen_art,
|
||||||
|
_to_screen_rect(
|
||||||
|
HOTBAR_BUBBLE_FIELD_RECT,
|
||||||
|
stage_rect.position,
|
||||||
|
stage_scale,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _validate_transformed_rect(
|
||||||
|
label: String,
|
||||||
|
canonical_rect: Rect2,
|
||||||
|
stage_rect: Rect2,
|
||||||
|
stage_scale: float,
|
||||||
|
) -> void:
|
||||||
|
var screen_rect: Rect2 = _to_screen_rect(
|
||||||
|
canonical_rect, stage_rect.position, stage_scale
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
screen_rect.position.is_equal_approx(
|
||||||
|
stage_rect.position + canonical_rect.position * stage_scale
|
||||||
|
),
|
||||||
|
"%s position does not use the single stage transform" % label,
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
screen_rect.size.is_equal_approx(canonical_rect.size * stage_scale),
|
||||||
|
"%s size does not use the single stage transform" % label,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _to_screen_rect(
|
||||||
|
canonical_rect: Rect2,
|
||||||
|
stage_offset: Vector2,
|
||||||
|
stage_scale: float,
|
||||||
|
) -> Rect2:
|
||||||
|
return Rect2(
|
||||||
|
stage_offset + canonical_rect.position * stage_scale,
|
||||||
|
canonical_rect.size * stage_scale,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _capture_inventory_pages(player_menu: PlayerMenu) -> void:
|
||||||
|
if not OS.has_environment("NETFISHING_NOTEPAD_CAPTURE"):
|
||||||
|
return
|
||||||
|
player_menu.visible = true
|
||||||
|
var sections: Array[PlayerMenu.Section] = [
|
||||||
|
PlayerMenu.Section.COOLER,
|
||||||
|
PlayerMenu.Section.TACKLE_BOX,
|
||||||
|
PlayerMenu.Section.BAG,
|
||||||
|
]
|
||||||
|
var suffixes: Array[String] = ["cooler", "tackle", "equipment"]
|
||||||
|
for index: int in sections.size():
|
||||||
|
player_menu.call("_show_section_immediate", sections[index])
|
||||||
|
await process_frame
|
||||||
|
await process_frame
|
||||||
|
await _save_capture(suffixes[index])
|
||||||
|
player_menu.call("_show_section_immediate", PlayerMenu.Section.COOLER)
|
||||||
|
var sale_confirmation := player_menu.get_node("%SaleConfirmation") as Control
|
||||||
|
var confirmation_message := player_menu.get_node(
|
||||||
|
"%ConfirmationMessage"
|
||||||
|
) as Label
|
||||||
|
assert(sale_confirmation != null)
|
||||||
|
assert(confirmation_message != null)
|
||||||
|
confirmation_message.text = "sell selected fish?"
|
||||||
|
sale_confirmation.visible = true
|
||||||
|
await process_frame
|
||||||
|
await process_frame
|
||||||
|
await _save_capture("sale-confirmation")
|
||||||
|
sale_confirmation.visible = false
|
||||||
|
|
||||||
|
|
||||||
|
func _save_capture(suffix: String) -> void:
|
||||||
|
var image: Image = root.get_texture().get_image()
|
||||||
|
var path: String = "%s-%s.png" % [
|
||||||
|
OS.get_environment("NETFISHING_NOTEPAD_CAPTURE"),
|
||||||
|
suffix,
|
||||||
|
]
|
||||||
|
assert(image.save_png(path) == OK)
|
||||||
1
tests/inventory_notepad_art_validation.gd.uid
Normal file
1
tests/inventory_notepad_art_validation.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://np6jfuf8tej8
|
||||||
195
tests/ui_scaling_runtime_validation.gd
Normal file
195
tests/ui_scaling_runtime_validation.gd
Normal file
|
|
@ -0,0 +1,195 @@
|
||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
const UIPixelationPresenterType = preload(
|
||||||
|
"res://ui/ui_pixelation_presenter.gd"
|
||||||
|
)
|
||||||
|
const UIReferencePresentationType = preload(
|
||||||
|
"res://ui/ui_reference_presentation.gd"
|
||||||
|
)
|
||||||
|
const GameUIScene := preload("res://ui/game_ui.tscn")
|
||||||
|
const TARGET_SIZES: Array[Vector2i] = [
|
||||||
|
Vector2i(1280, 720),
|
||||||
|
Vector2i(1920, 1080),
|
||||||
|
Vector2i(2560, 1440),
|
||||||
|
Vector2i(3840, 2160),
|
||||||
|
Vector2i(640, 480),
|
||||||
|
Vector2i(1024, 768),
|
||||||
|
Vector2i(1729, 973),
|
||||||
|
Vector2i(2560, 1080),
|
||||||
|
Vector2i(3440, 1440),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
func _initialize() -> void:
|
||||||
|
call_deferred("_run")
|
||||||
|
|
||||||
|
|
||||||
|
func _run() -> void:
|
||||||
|
var original_size: Vector2i = root.size
|
||||||
|
var presenter := UIPixelationPresenterType.new()
|
||||||
|
presenter.name = "UIPresentation"
|
||||||
|
presenter.stretch = true
|
||||||
|
var ui_viewport := SubViewport.new()
|
||||||
|
ui_viewport.name = "UIViewport"
|
||||||
|
var game_ui := GameUIScene.instantiate() as GameUI
|
||||||
|
var ui_root := game_ui.get_node("UIRoot") as Control
|
||||||
|
var canonical_stage := game_ui.get_node(
|
||||||
|
"UIRoot/CanonicalStage"
|
||||||
|
) as Control
|
||||||
|
var chat_ui := game_ui.get_node("UIRoot/ChatUI") as ChatUI
|
||||||
|
var player_menu := game_ui.get_node(
|
||||||
|
"UIRoot/CanonicalStage/PlayerMenu"
|
||||||
|
) as PlayerMenu
|
||||||
|
var hotbar := game_ui.get_node(
|
||||||
|
"UIRoot/CanonicalStage/Hotbar"
|
||||||
|
) as HotbarUI
|
||||||
|
var screen_fade := game_ui.get_node("UIRoot/ScreenFade") as Control
|
||||||
|
var title_screen := game_ui.get_node("UIRoot/TitleScreen") as TitleScreen
|
||||||
|
var title_content_stage := title_screen.get_node(
|
||||||
|
"ResponsiveTitleStage"
|
||||||
|
) as Control
|
||||||
|
var decorative_fish_layer := title_screen.get_node(
|
||||||
|
"DecorativeFishLayer"
|
||||||
|
) as Control
|
||||||
|
var decorative_bubble_layer := title_screen.get_node(
|
||||||
|
"DecorativeBubbleLayer"
|
||||||
|
) as Control
|
||||||
|
ui_viewport.add_child(game_ui)
|
||||||
|
presenter.add_child(ui_viewport)
|
||||||
|
root.add_child(presenter)
|
||||||
|
await process_frame
|
||||||
|
chat_ui.set_available(true)
|
||||||
|
print(
|
||||||
|
"UI viewport embedding: root=%s subviewport=%s"
|
||||||
|
% [root.gui_embed_subwindows, ui_viewport.gui_embed_subwindows]
|
||||||
|
)
|
||||||
|
|
||||||
|
for target_size: Vector2i in TARGET_SIZES:
|
||||||
|
root.size = target_size
|
||||||
|
await process_frame
|
||||||
|
await process_frame
|
||||||
|
var actual_size: Vector2i = root.size
|
||||||
|
var display_size := Vector2(actual_size)
|
||||||
|
var expected_scale: float = UIReferencePresentationType.get_scale(
|
||||||
|
display_size
|
||||||
|
)
|
||||||
|
var expected_stage_offset: Vector2 = UIReferencePresentationType.get_offset(
|
||||||
|
display_size
|
||||||
|
)
|
||||||
|
assert(presenter.position.is_equal_approx(Vector2.ZERO))
|
||||||
|
assert(ui_root.position.is_equal_approx(Vector2.ZERO))
|
||||||
|
var expected_visible_size: Vector2 = (
|
||||||
|
UIReferencePresentationType.get_visible_reference_size(display_size)
|
||||||
|
)
|
||||||
|
assert(ui_root.size.is_equal_approx(expected_visible_size))
|
||||||
|
assert(canonical_stage.size.is_equal_approx(
|
||||||
|
UIReferencePresentationType.REFERENCE_SIZE
|
||||||
|
))
|
||||||
|
assert(canonical_stage.position.is_equal_approx(
|
||||||
|
UIReferencePresentationType.get_stage_position(display_size)
|
||||||
|
))
|
||||||
|
assert(chat_ui.size.is_equal_approx(expected_visible_size))
|
||||||
|
assert(screen_fade.size.is_equal_approx(expected_visible_size))
|
||||||
|
assert(player_menu.position.is_equal_approx(Vector2.ZERO))
|
||||||
|
assert(player_menu.size.is_equal_approx(
|
||||||
|
UIReferencePresentationType.REFERENCE_SIZE
|
||||||
|
))
|
||||||
|
assert(hotbar.position.is_equal_approx(Vector2.ZERO))
|
||||||
|
assert(hotbar.size.is_equal_approx(
|
||||||
|
UIReferencePresentationType.REFERENCE_SIZE
|
||||||
|
))
|
||||||
|
assert(title_screen.size.is_equal_approx(expected_visible_size))
|
||||||
|
assert(title_content_stage.size.is_equal_approx(
|
||||||
|
UIReferencePresentationType.REFERENCE_SIZE
|
||||||
|
))
|
||||||
|
assert(title_content_stage.position.is_equal_approx(
|
||||||
|
canonical_stage.position
|
||||||
|
))
|
||||||
|
assert(decorative_fish_layer.size.is_equal_approx(
|
||||||
|
expected_visible_size
|
||||||
|
))
|
||||||
|
assert(decorative_bubble_layer.size.is_equal_approx(
|
||||||
|
expected_visible_size
|
||||||
|
))
|
||||||
|
var left_to_right_bounds: Vector2 = (
|
||||||
|
TitleScreen.get_decorative_fish_crossing_bounds(
|
||||||
|
expected_visible_size.x, 160.0, 1.0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
var right_to_left_bounds: Vector2 = (
|
||||||
|
TitleScreen.get_decorative_fish_crossing_bounds(
|
||||||
|
expected_visible_size.x, 160.0, -1.0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert(left_to_right_bounds.x < -160.0)
|
||||||
|
assert(left_to_right_bounds.y > expected_visible_size.x)
|
||||||
|
assert(right_to_left_bounds.x > expected_visible_size.x)
|
||||||
|
assert(right_to_left_bounds.y < -160.0)
|
||||||
|
assert(is_equal_approx(presenter.scale.x, presenter.scale.y))
|
||||||
|
assert(is_equal_approx(ui_root.scale.x, ui_root.scale.y))
|
||||||
|
var effective_scale: float = presenter.scale.x * ui_root.scale.x
|
||||||
|
assert(is_equal_approx(effective_scale, expected_scale))
|
||||||
|
assert((
|
||||||
|
canonical_stage.position * effective_scale
|
||||||
|
).distance_to(expected_stage_offset) <= 0.01)
|
||||||
|
var expected_render_height: int = mini(
|
||||||
|
roundi(UIReferencePresentationType.REFERENCE_SIZE.y * expected_scale),
|
||||||
|
PlayerSettings.get_ui_render_height(
|
||||||
|
PlayerSettings.DEFAULT_UI_PIXEL_SIZE,
|
||||||
|
roundi(UIReferencePresentationType.REFERENCE_SIZE.y),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
var expected_render_scale: float = (
|
||||||
|
float(expected_render_height)
|
||||||
|
/ UIReferencePresentationType.REFERENCE_SIZE.y
|
||||||
|
)
|
||||||
|
var expected_viewport_size := Vector2i(
|
||||||
|
roundi(expected_visible_size.x * expected_render_scale),
|
||||||
|
roundi(expected_visible_size.y * expected_render_scale),
|
||||||
|
)
|
||||||
|
assert(presenter.get_ui_viewport_size() == expected_viewport_size)
|
||||||
|
assert(presenter.size.is_equal_approx(Vector2(expected_viewport_size)))
|
||||||
|
var chat_panel := chat_ui.get_node("ChatPanel") as Control
|
||||||
|
assert(chat_panel != null)
|
||||||
|
assert(chat_panel.visible)
|
||||||
|
assert(is_equal_approx(chat_panel.position.x, 0.0))
|
||||||
|
assert(is_equal_approx(
|
||||||
|
(chat_panel.position.y + chat_panel.size.y) * effective_scale,
|
||||||
|
display_size.y - ChatUI.BOTTOM_MARGIN * effective_scale,
|
||||||
|
))
|
||||||
|
print(
|
||||||
|
(
|
||||||
|
"UI resize: requested=%s actual=%s viewport=%s container_scale=%.6f "
|
||||||
|
+ "root_scale=%.6f effective_scale=%.6f stage_offset=%s "
|
||||||
|
+ "chat_x=%.3f"
|
||||||
|
)
|
||||||
|
% [
|
||||||
|
target_size,
|
||||||
|
actual_size,
|
||||||
|
presenter.get_ui_viewport_size(),
|
||||||
|
presenter.scale.x,
|
||||||
|
ui_root.scale.x,
|
||||||
|
effective_scale,
|
||||||
|
expected_stage_offset,
|
||||||
|
chat_panel.position.x * effective_scale,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if OS.has_environment("NETFISHING_UI_SCALE_CAPTURE"):
|
||||||
|
await process_frame
|
||||||
|
var capture: Image = root.get_texture().get_image()
|
||||||
|
var capture_path: String = (
|
||||||
|
"%s-requested-%dx%d-actual-%dx%d.png"
|
||||||
|
) % [
|
||||||
|
OS.get_environment("NETFISHING_UI_SCALE_CAPTURE"),
|
||||||
|
target_size.x,
|
||||||
|
target_size.y,
|
||||||
|
actual_size.x,
|
||||||
|
actual_size.y,
|
||||||
|
]
|
||||||
|
assert(capture.save_png(capture_path) == OK)
|
||||||
|
|
||||||
|
presenter.queue_free()
|
||||||
|
root.size = original_size
|
||||||
|
await process_frame
|
||||||
|
print("UI scaling runtime validation: PASS")
|
||||||
|
quit()
|
||||||
1
tests/ui_scaling_runtime_validation.gd.uid
Normal file
1
tests/ui_scaling_runtime_validation.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://d1x8t4mf2kqv6
|
||||||
|
|
@ -60,6 +60,7 @@ var _send_pending: bool = false
|
||||||
var _pending_send_body: String = ""
|
var _pending_send_body: String = ""
|
||||||
var _prior_movement: bool = true
|
var _prior_movement: bool = true
|
||||||
var _prior_camera: bool = true
|
var _prior_camera: bool = true
|
||||||
|
var _output_scale: float = 1.0
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
|
@ -563,6 +564,10 @@ func _set_presentation_state(
|
||||||
_refresh_visibility()
|
_refresh_visibility()
|
||||||
|
|
||||||
|
|
||||||
|
func set_output_scale(output_scale: float) -> void:
|
||||||
|
_output_scale = maxf(output_scale, 0.001)
|
||||||
|
|
||||||
|
|
||||||
func _refresh_handle_labels(state: PresentationState) -> void:
|
func _refresh_handle_labels(state: PresentationState) -> void:
|
||||||
var collapsed := state == PresentationState.COLLAPSED
|
var collapsed := state == PresentationState.COLLAPSED
|
||||||
var height_state := _visible_state_before_collapse if collapsed else state
|
var height_state := _visible_state_before_collapse if collapsed else state
|
||||||
|
|
@ -632,7 +637,9 @@ func _update_speech() -> void:
|
||||||
if camera.is_position_behind(world_position):
|
if camera.is_position_behind(world_position):
|
||||||
bubble.hide()
|
bubble.hide()
|
||||||
continue
|
continue
|
||||||
var screen_position := camera.unproject_position(world_position)
|
var screen_position := (
|
||||||
|
camera.unproject_position(world_position) / _output_scale
|
||||||
|
)
|
||||||
var viewport_size := get_viewport_rect().size
|
var viewport_size := get_viewport_rect().size
|
||||||
if (
|
if (
|
||||||
screen_position.x < -80.0
|
screen_position.x < -80.0
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ const CONTENT_MARGIN_RIGHT := 18.0
|
||||||
const CONTENT_MARGIN_BOTTOM := 16.0
|
const CONTENT_MARGIN_BOTTOM := 16.0
|
||||||
const HANDWRITTEN_FONT: Font = preload("res://ui/fonts/seattle_avenue.otf")
|
const HANDWRITTEN_FONT: Font = preload("res://ui/fonts/seattle_avenue.otf")
|
||||||
const NOTEPAD_TEXTURE: Texture2D = preload("res://art/ui/ui_notepad.png")
|
const NOTEPAD_TEXTURE: Texture2D = preload("res://art/ui/ui_notepad.png")
|
||||||
const NOTEPAD_ART_SCALE := 1.1
|
const NOTEPAD_CANONICAL_OVERSCAN: float = 1.2
|
||||||
|
const NOTEPAD_ART_OFFSET := Vector2(10.0, 20.0)
|
||||||
|
|
||||||
@export var title_text := "notes"
|
@export var title_text := "notes"
|
||||||
@export var inset_content := true
|
@export var inset_content := true
|
||||||
|
|
@ -34,17 +35,7 @@ static func apply_handwritten_to(root: Control) -> void:
|
||||||
|
|
||||||
|
|
||||||
func _draw() -> void:
|
func _draw() -> void:
|
||||||
var texture_size := NOTEPAD_TEXTURE.get_size()
|
draw_texture_rect(NOTEPAD_TEXTURE, get_art_rect(), false)
|
||||||
var uniform_scale := minf(
|
|
||||||
size.x / texture_size.x,
|
|
||||||
size.y / texture_size.y,
|
|
||||||
) * NOTEPAD_ART_SCALE
|
|
||||||
var rendered_size := texture_size * uniform_scale
|
|
||||||
var rendered_rect := Rect2(
|
|
||||||
(size - rendered_size) * 0.5,
|
|
||||||
rendered_size,
|
|
||||||
)
|
|
||||||
draw_texture_rect(NOTEPAD_TEXTURE, rendered_rect, false)
|
|
||||||
|
|
||||||
var font: Font = HANDWRITTEN_FONT
|
var font: Font = HANDWRITTEN_FONT
|
||||||
draw_string(
|
draw_string(
|
||||||
|
|
@ -58,6 +49,34 @@ func _draw() -> void:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func get_art_rect() -> Rect2:
|
||||||
|
return get_art_rect_for_host(size)
|
||||||
|
|
||||||
|
|
||||||
|
static func get_art_rect_for_host(host_size: Vector2) -> Rect2:
|
||||||
|
# Source pixels establish the artwork aspect ratio and sampling quality. The
|
||||||
|
# canonical host rect, not the source dimensions, owns its displayed size.
|
||||||
|
var source_size: Vector2 = NOTEPAD_TEXTURE.get_size()
|
||||||
|
if (
|
||||||
|
host_size.x <= 0.0
|
||||||
|
or host_size.y <= 0.0
|
||||||
|
or source_size.x <= 0.0
|
||||||
|
or source_size.y <= 0.0
|
||||||
|
):
|
||||||
|
return Rect2()
|
||||||
|
var fit_scale: float = minf(
|
||||||
|
host_size.x / source_size.x,
|
||||||
|
host_size.y / source_size.y,
|
||||||
|
)
|
||||||
|
var rendered_size: Vector2 = (
|
||||||
|
source_size * fit_scale * NOTEPAD_CANONICAL_OVERSCAN
|
||||||
|
)
|
||||||
|
return Rect2(
|
||||||
|
(host_size - rendered_size) * 0.5 + NOTEPAD_ART_OFFSET,
|
||||||
|
rendered_size,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
static func make_layout_style(with_content_margins: bool = true) -> StyleBoxFlat:
|
static func make_layout_style(with_content_margins: bool = true) -> StyleBoxFlat:
|
||||||
var paper := StyleBoxFlat.new()
|
var paper := StyleBoxFlat.new()
|
||||||
paper.bg_color = Color.TRANSPARENT
|
paper.bg_color = Color.TRANSPARENT
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,6 @@ extends Control
|
||||||
const INPUT_OWNER: StringName = &"fishing_shop"
|
const INPUT_OWNER: StringName = &"fishing_shop"
|
||||||
const MAIN_SHOP_BUYER_ID: StringName = &"main_fishing_shop"
|
const MAIN_SHOP_BUYER_ID: StringName = &"main_fishing_shop"
|
||||||
const FishBuyerProfileType = preload("res://economy/fish_buyer_profile.gd")
|
const FishBuyerProfileType = preload("res://economy/fish_buyer_profile.gd")
|
||||||
const FishCatchType = preload("res://fish/fish_catch.gd")
|
|
||||||
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
|
||||||
const FishSaleResultType = preload("res://economy/fish_sale_result.gd")
|
|
||||||
const FishSaleServiceType = preload("res://economy/fish_sale_service.gd")
|
|
||||||
const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
||||||
const FishingShopStockType = preload("res://economy/fishing_shop_stock.gd")
|
const FishingShopStockType = preload("res://economy/fishing_shop_stock.gd")
|
||||||
const ItemCatalogType = preload("res://items/item_catalog.gd")
|
const ItemCatalogType = preload("res://items/item_catalog.gd")
|
||||||
|
|
@ -24,11 +20,12 @@ const PlayerCoolerCapacityType = preload(
|
||||||
const ShopInteractionType = preload(
|
const ShopInteractionType = preload(
|
||||||
"res://world/fishing_shop_interaction.gd"
|
"res://world/fishing_shop_interaction.gd"
|
||||||
)
|
)
|
||||||
const FishBatchSelectionType = preload(
|
const UtilityPageStyleType = preload("res://ui/utility_page_style.gd")
|
||||||
"res://ui/fish_batch_selection.gd"
|
|
||||||
)
|
|
||||||
|
|
||||||
signal menu_visibility_changed(is_open: bool)
|
signal menu_visibility_changed(is_open: bool)
|
||||||
|
signal sell_fish_requested
|
||||||
|
signal shop_cooler_return_requested
|
||||||
|
signal shop_cooler_confirmation_cancel_requested
|
||||||
|
|
||||||
enum CloseReason {
|
enum CloseReason {
|
||||||
USER,
|
USER,
|
||||||
|
|
@ -39,17 +36,12 @@ enum CloseReason {
|
||||||
}
|
}
|
||||||
|
|
||||||
@onready var _wallet_label: Label = %WalletLabel
|
@onready var _wallet_label: Label = %WalletLabel
|
||||||
@onready var _fish_sales: VBoxContainer = (
|
@onready var _shop_panel: PanelContainer = %ShopPanel
|
||||||
$ShopPanel/Margin/Layout/Body/FishSales
|
@onready var _shop_cooler_page: Control = %ShopCoolerPage
|
||||||
)
|
@onready var _shop_cooler_mount: Control = %ShopCoolerMount
|
||||||
@onready var _fish_list: ItemList = %FishList
|
@onready var _back_to_shop_button: Button = %BackToShopButton
|
||||||
@onready var _sales_title: Label = %SalesTitle
|
@onready var _buy_mode_button: Button = %BuyModeButton
|
||||||
@onready var _fish_empty: Label = %FishEmpty
|
@onready var _sell_mode_button: Button = %SellModeButton
|
||||||
@onready var _fish_texture: TextureRect = %FishTexture
|
|
||||||
@onready var _fish_name: Label = %FishName
|
|
||||||
@onready var _fish_details: Label = %FishDetails
|
|
||||||
@onready var _selection_summary: Label = %SelectionSummary
|
|
||||||
@onready var _sell_button: Button = %SellButton
|
|
||||||
@onready var _feedback: Label = %Feedback
|
@onready var _feedback: Label = %Feedback
|
||||||
@onready var _reel_level: Label = %ReelLevel
|
@onready var _reel_level: Label = %ReelLevel
|
||||||
@onready var _reel_effect: Label = %ReelEffect
|
@onready var _reel_effect: Label = %ReelEffect
|
||||||
|
|
@ -64,15 +56,9 @@ enum CloseReason {
|
||||||
@onready var _cooler_effect: Label = %CoolerEffect
|
@onready var _cooler_effect: Label = %CoolerEffect
|
||||||
@onready var _cooler_cost: Label = %CoolerCost
|
@onready var _cooler_cost: Label = %CoolerCost
|
||||||
@onready var _cooler_purchase: Button = %CoolerPurchase
|
@onready var _cooler_purchase: Button = %CoolerPurchase
|
||||||
@onready var _confirmation: PanelContainer = %SaleConfirmation
|
|
||||||
@onready var _confirmation_text: Label = %ConfirmationText
|
|
||||||
@onready var _confirm_sale: Button = %ConfirmSale
|
|
||||||
@onready var _cancel_sale: Button = %CancelSale
|
|
||||||
|
|
||||||
var _player: PlayerType
|
var _player: PlayerType
|
||||||
var _inventory: FishInventoryType
|
|
||||||
var _wallet: PlayerWalletType
|
var _wallet: PlayerWalletType
|
||||||
var _sale_service: FishSaleServiceType
|
|
||||||
var _buyer: FishBuyerProfileType
|
var _buyer: FishBuyerProfileType
|
||||||
var _upgrades: PlayerFishingUpgradesType
|
var _upgrades: PlayerFishingUpgradesType
|
||||||
var _fishing_spot: FishingSpotType
|
var _fishing_spot: FishingSpotType
|
||||||
|
|
@ -80,42 +66,93 @@ var _interaction: ShopInteractionType
|
||||||
var _bag: PlayerBagType
|
var _bag: PlayerBagType
|
||||||
var _item_catalog: ItemCatalogType
|
var _item_catalog: ItemCatalogType
|
||||||
var _cooler_capacity: PlayerCoolerCapacityType
|
var _cooler_capacity: PlayerCoolerCapacityType
|
||||||
var _network_session: NetworkSession
|
|
||||||
var _network_shop: NetworkShopService
|
var _network_shop: NetworkShopService
|
||||||
var _fish_selection := FishBatchSelectionType.new()
|
|
||||||
var _confirmation_catch_ids: Array[StringName] = []
|
|
||||||
var _confirmation_generation: int = -1
|
|
||||||
var _prior_movement_enabled: bool = true
|
var _prior_movement_enabled: bool = true
|
||||||
var _prior_camera_enabled: bool = true
|
var _prior_camera_enabled: bool = true
|
||||||
var _prior_mouse_mode: Input.MouseMode = Input.MOUSE_MODE_VISIBLE
|
var _prior_mouse_mode: Input.MouseMode = Input.MOUSE_MODE_VISIBLE
|
||||||
var _snapshot_stored: bool = false
|
var _snapshot_stored: bool = false
|
||||||
var _mouse_snapshot_stored: bool = false
|
var _mouse_snapshot_stored: bool = false
|
||||||
var _generation: int = 0
|
|
||||||
var _selection_input_generation: int = 0
|
|
||||||
var _transaction_in_progress: bool = false
|
var _transaction_in_progress: bool = false
|
||||||
var _closing: bool = false
|
var _closing: bool = false
|
||||||
|
var _cooler_page_active: bool = false
|
||||||
|
var _cooler_modal_open: bool = false
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
# Catch selling belongs to the Pelican action in the Player Menu. Keep the
|
UtilityPageStyleType.apply_page(self)
|
||||||
# legacy shop sales controls out of the active interface.
|
_apply_shop_styles()
|
||||||
_fish_sales.hide()
|
|
||||||
%CloseButton.pressed.connect(close_shop)
|
%CloseButton.pressed.connect(close_shop)
|
||||||
_fish_list.item_selected.connect(_on_fish_selected)
|
_buy_mode_button.pressed.connect(_focus_buy_page)
|
||||||
_fish_list.item_clicked.connect(_on_fish_clicked)
|
_sell_mode_button.pressed.connect(_request_shop_cooler)
|
||||||
_sell_button.pressed.connect(_open_sale_confirmation)
|
_back_to_shop_button.pressed.connect(shop_cooler_return_requested.emit)
|
||||||
_confirm_sale.pressed.connect(_on_confirm_sale)
|
|
||||||
_cancel_sale.pressed.connect(_close_sale_confirmation)
|
|
||||||
_reel_purchase.pressed.connect(_purchase_reel_speed)
|
_reel_purchase.pressed.connect(_purchase_reel_speed)
|
||||||
_barrier_purchase.pressed.connect(_purchase_barrier_power)
|
_barrier_purchase.pressed.connect(_purchase_barrier_power)
|
||||||
_cooler_purchase.pressed.connect(_purchase_cooler_capacity)
|
_cooler_purchase.pressed.connect(_purchase_cooler_capacity)
|
||||||
|
|
||||||
|
|
||||||
|
func _request_shop_cooler() -> void:
|
||||||
|
if _transaction_in_progress:
|
||||||
|
_feedback.text = "Finish the current transaction first."
|
||||||
|
return
|
||||||
|
if not _is_transaction_context_valid():
|
||||||
|
_feedback.text = "The fishing shop is no longer available."
|
||||||
|
return
|
||||||
|
sell_fish_requested.emit()
|
||||||
|
|
||||||
|
|
||||||
|
func _focus_buy_page() -> void:
|
||||||
|
_feedback.text = ""
|
||||||
|
if _supplies_list.get_child_count() > 0:
|
||||||
|
var first_supply := _supplies_list.get_child(0) as Button
|
||||||
|
if first_supply != null and not first_supply.disabled:
|
||||||
|
first_supply.grab_focus()
|
||||||
|
return
|
||||||
|
_reel_purchase.grab_focus()
|
||||||
|
|
||||||
|
|
||||||
|
func _apply_shop_styles() -> void:
|
||||||
|
var shop_panel := $ShopPanel as PanelContainer
|
||||||
|
var shop_panel_style := UtilityPageStyleType.panel_style()
|
||||||
|
shop_panel_style.set_corner_radius_all(24)
|
||||||
|
shop_panel_style.content_margin_left = 0.0
|
||||||
|
shop_panel_style.content_margin_top = 0.0
|
||||||
|
shop_panel_style.content_margin_right = 0.0
|
||||||
|
shop_panel_style.content_margin_bottom = 0.0
|
||||||
|
shop_panel.add_theme_stylebox_override(
|
||||||
|
"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,
|
||||||
|
]:
|
||||||
|
panel.add_theme_stylebox_override(
|
||||||
|
"panel", UtilityPageStyleType.row_style()
|
||||||
|
)
|
||||||
|
for button: BaseButton in [
|
||||||
|
%CloseButton,
|
||||||
|
_buy_mode_button,
|
||||||
|
_sell_mode_button,
|
||||||
|
_reel_purchase,
|
||||||
|
_barrier_purchase,
|
||||||
|
_cooler_purchase,
|
||||||
|
_back_to_shop_button,
|
||||||
|
]:
|
||||||
|
UtilityPageStyleType.apply_ocean_button(button)
|
||||||
|
_buy_mode_button.add_theme_stylebox_override(
|
||||||
|
"normal",
|
||||||
|
UtilityPageStyleType.ocean_button_style(
|
||||||
|
UtilityPageStyleType.OCEAN_SELECTED
|
||||||
|
),
|
||||||
|
)
|
||||||
|
_feedback.add_theme_color_override(
|
||||||
|
"font_color", UtilityPageStyleType.OCEAN_TEXT_SECONDARY
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func setup(
|
func setup(
|
||||||
player: PlayerType,
|
player: PlayerType,
|
||||||
inventory: FishInventoryType,
|
|
||||||
wallet: PlayerWalletType,
|
wallet: PlayerWalletType,
|
||||||
sale_service: FishSaleServiceType,
|
|
||||||
buyer: FishBuyerProfileType,
|
buyer: FishBuyerProfileType,
|
||||||
upgrades: PlayerFishingUpgradesType,
|
upgrades: PlayerFishingUpgradesType,
|
||||||
fishing_spot: FishingSpotType,
|
fishing_spot: FishingSpotType,
|
||||||
|
|
@ -123,13 +160,10 @@ func setup(
|
||||||
bag: PlayerBagType,
|
bag: PlayerBagType,
|
||||||
item_catalog: ItemCatalogType,
|
item_catalog: ItemCatalogType,
|
||||||
cooler_capacity: PlayerCoolerCapacityType,
|
cooler_capacity: PlayerCoolerCapacityType,
|
||||||
network_session: NetworkSession,
|
|
||||||
network_shop: NetworkShopService,
|
network_shop: NetworkShopService,
|
||||||
) -> void:
|
) -> void:
|
||||||
_player = player
|
_player = player
|
||||||
_inventory = inventory
|
|
||||||
_wallet = wallet
|
_wallet = wallet
|
||||||
_sale_service = sale_service
|
|
||||||
_buyer = buyer
|
_buyer = buyer
|
||||||
_upgrades = upgrades
|
_upgrades = upgrades
|
||||||
_fishing_spot = fishing_spot
|
_fishing_spot = fishing_spot
|
||||||
|
|
@ -137,7 +171,6 @@ func setup(
|
||||||
_bag = bag
|
_bag = bag
|
||||||
_item_catalog = item_catalog
|
_item_catalog = item_catalog
|
||||||
_cooler_capacity = cooler_capacity
|
_cooler_capacity = cooler_capacity
|
||||||
_network_session = network_session
|
|
||||||
_network_shop = network_shop
|
_network_shop = network_shop
|
||||||
if (
|
if (
|
||||||
_network_shop != null
|
_network_shop != null
|
||||||
|
|
@ -151,9 +184,6 @@ func setup(
|
||||||
_network_shop.local_purchase_finished.connect(
|
_network_shop.local_purchase_finished.connect(
|
||||||
_on_network_purchase_finished
|
_on_network_purchase_finished
|
||||||
)
|
)
|
||||||
_fish_selection.clear()
|
|
||||||
if not _inventory.catches_changed.is_connected(_on_inventory_changed):
|
|
||||||
_inventory.catches_changed.connect(_on_inventory_changed)
|
|
||||||
if not _wallet.balance_changed.is_connected(_on_wallet_changed):
|
if not _wallet.balance_changed.is_connected(_on_wallet_changed):
|
||||||
_wallet.balance_changed.connect(_on_wallet_changed)
|
_wallet.balance_changed.connect(_on_wallet_changed)
|
||||||
if not _upgrades.upgrades_changed.is_connected(_on_upgrades_changed):
|
if not _upgrades.upgrades_changed.is_connected(_on_upgrades_changed):
|
||||||
|
|
@ -180,7 +210,6 @@ func open_shop() -> bool:
|
||||||
or _buyer.id != MAIN_SHOP_BUYER_ID
|
or _buyer.id != MAIN_SHOP_BUYER_ID
|
||||||
):
|
):
|
||||||
return false
|
return false
|
||||||
_generation += 1
|
|
||||||
_closing = false
|
_closing = false
|
||||||
_transaction_in_progress = (
|
_transaction_in_progress = (
|
||||||
_network_shop != null
|
_network_shop != null
|
||||||
|
|
@ -196,10 +225,10 @@ func open_shop() -> bool:
|
||||||
_fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, true)
|
_fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, true)
|
||||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||||
_feedback.text = ""
|
_feedback.text = ""
|
||||||
_close_sale_confirmation()
|
deactivate_shop_cooler_page()
|
||||||
show()
|
show()
|
||||||
_refresh_all()
|
_refresh_all()
|
||||||
%CloseButton.grab_focus()
|
_buy_mode_button.grab_focus()
|
||||||
menu_visibility_changed.emit(true)
|
menu_visibility_changed.emit(true)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
@ -207,13 +236,42 @@ func open_shop() -> bool:
|
||||||
func consume_escape() -> bool:
|
func consume_escape() -> bool:
|
||||||
if not visible:
|
if not visible:
|
||||||
return false
|
return false
|
||||||
if _confirmation.visible:
|
if _cooler_page_active:
|
||||||
_close_sale_confirmation()
|
if _cooler_modal_open:
|
||||||
|
shop_cooler_confirmation_cancel_requested.emit()
|
||||||
else:
|
else:
|
||||||
|
shop_cooler_return_requested.emit()
|
||||||
|
return true
|
||||||
close_shop()
|
close_shop()
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
func get_shop_cooler_mount() -> Control:
|
||||||
|
return _shop_cooler_mount
|
||||||
|
|
||||||
|
|
||||||
|
func activate_shop_cooler_page() -> void:
|
||||||
|
_cooler_page_active = true
|
||||||
|
_shop_panel.hide()
|
||||||
|
_shop_cooler_page.show()
|
||||||
|
_back_to_shop_button.disabled = false
|
||||||
|
|
||||||
|
|
||||||
|
func deactivate_shop_cooler_page() -> void:
|
||||||
|
_cooler_page_active = false
|
||||||
|
_cooler_modal_open = false
|
||||||
|
_back_to_shop_button.disabled = false
|
||||||
|
_shop_cooler_page.hide()
|
||||||
|
_shop_panel.show()
|
||||||
|
if visible:
|
||||||
|
_buy_mode_button.grab_focus()
|
||||||
|
|
||||||
|
|
||||||
|
func set_shop_cooler_modal_open(is_open: bool) -> void:
|
||||||
|
_cooler_modal_open = is_open
|
||||||
|
_back_to_shop_button.disabled = is_open
|
||||||
|
|
||||||
|
|
||||||
func close_shop(
|
func close_shop(
|
||||||
reason: CloseReason = CloseReason.USER,
|
reason: CloseReason = CloseReason.USER,
|
||||||
restore_controls: bool = true,
|
restore_controls: bool = true,
|
||||||
|
|
@ -221,10 +279,7 @@ func close_shop(
|
||||||
if not visible:
|
if not visible:
|
||||||
return
|
return
|
||||||
_closing = true
|
_closing = true
|
||||||
_generation += 1
|
|
||||||
_transaction_in_progress = false
|
_transaction_in_progress = false
|
||||||
_close_sale_confirmation()
|
|
||||||
_fish_selection.clear()
|
|
||||||
hide()
|
hide()
|
||||||
get_viewport().gui_release_focus()
|
get_viewport().gui_release_focus()
|
||||||
if _fishing_spot != null and is_instance_valid(_fishing_spot):
|
if _fishing_spot != null and is_instance_valid(_fishing_spot):
|
||||||
|
|
@ -241,6 +296,7 @@ func close_shop(
|
||||||
_apply_mouse_close_policy(reason)
|
_apply_mouse_close_policy(reason)
|
||||||
_closing = false
|
_closing = false
|
||||||
menu_visibility_changed.emit(false)
|
menu_visibility_changed.emit(false)
|
||||||
|
deactivate_shop_cooler_page()
|
||||||
|
|
||||||
|
|
||||||
func close_for_range_exit() -> void:
|
func close_for_range_exit() -> void:
|
||||||
|
|
@ -268,8 +324,6 @@ func _refresh_all() -> void:
|
||||||
if not is_node_ready():
|
if not is_node_ready():
|
||||||
return
|
return
|
||||||
_refresh_wallet()
|
_refresh_wallet()
|
||||||
_refresh_fish_list()
|
|
||||||
_refresh_selected_fish()
|
|
||||||
_refresh_upgrades()
|
_refresh_upgrades()
|
||||||
_refresh_supplies()
|
_refresh_supplies()
|
||||||
_refresh_cooler_capacity()
|
_refresh_cooler_capacity()
|
||||||
|
|
@ -283,144 +337,6 @@ func _refresh_wallet() -> void:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func _refresh_fish_list() -> void:
|
|
||||||
_fish_list.clear()
|
|
||||||
var catches: Array[FishCatchType] = (
|
|
||||||
_inventory.get_all_catches() if _inventory != null else []
|
|
||||||
)
|
|
||||||
_sales_title.text = "cooler %d / %d • full base value" % [
|
|
||||||
catches.size(),
|
|
||||||
_cooler_capacity.get_capacity() if _cooler_capacity != null else 0,
|
|
||||||
]
|
|
||||||
catches.sort_custom(
|
|
||||||
func(a: FishCatchType, b: FishCatchType) -> bool:
|
|
||||||
return a.catch_sequence > b.catch_sequence
|
|
||||||
)
|
|
||||||
var visible_ids: Array[StringName] = []
|
|
||||||
for fish_catch: FishCatchType in catches:
|
|
||||||
if fish_catch != null and fish_catch.is_valid():
|
|
||||||
visible_ids.append(fish_catch.catch_id)
|
|
||||||
_fish_selection.set_visible_order(visible_ids)
|
|
||||||
for fish_catch: FishCatchType in catches:
|
|
||||||
if fish_catch == null or not fish_catch.is_valid():
|
|
||||||
continue
|
|
||||||
var marker: String = ""
|
|
||||||
if _fish_selection.is_selected(fish_catch.catch_id):
|
|
||||||
marker += "✓ "
|
|
||||||
if fish_catch.catch_id == _fish_selection.get_focused_id():
|
|
||||||
marker += "◆ "
|
|
||||||
if fish_catch.is_favorited:
|
|
||||||
marker += "★ "
|
|
||||||
var index: int = _fish_list.add_item(
|
|
||||||
"%s%s — %.2f lb"
|
|
||||||
% [marker, fish_catch.fish.display_name, fish_catch.weight_lb],
|
|
||||||
fish_catch.fish.display_texture
|
|
||||||
)
|
|
||||||
_fish_list.set_item_metadata(index, String(fish_catch.catch_id))
|
|
||||||
_fish_list.set_item_tooltip(
|
|
||||||
index,
|
|
||||||
"%s • %s"
|
|
||||||
% [
|
|
||||||
fish_catch.fish.get_rarity_name(),
|
|
||||||
"favorited" if fish_catch.is_favorited else "available",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
_fish_empty.visible = _fish_list.item_count == 0
|
|
||||||
_fish_list.deselect_all()
|
|
||||||
for index: int in range(_fish_list.item_count):
|
|
||||||
var catch_id := StringName(str(_fish_list.get_item_metadata(index)))
|
|
||||||
if _fish_selection.is_selected(catch_id):
|
|
||||||
_fish_list.select(index, false)
|
|
||||||
|
|
||||||
|
|
||||||
func _refresh_selected_fish() -> void:
|
|
||||||
var fish_catch: FishCatchType = _get_selected_catch()
|
|
||||||
_fish_texture.texture = (
|
|
||||||
fish_catch.fish.display_texture if fish_catch != null else null
|
|
||||||
)
|
|
||||||
_fish_name.text = (
|
|
||||||
fish_catch.fish.display_name if fish_catch != null else "select a fish"
|
|
||||||
)
|
|
||||||
if fish_catch == null:
|
|
||||||
_fish_details.text = "choose one individual fish from your cooler."
|
|
||||||
_update_sale_summary()
|
|
||||||
return
|
|
||||||
var individual_preview: FishSaleResultType = (
|
|
||||||
_sale_service.preview_batch([fish_catch.catch_id], _buyer)
|
|
||||||
if _sale_service != null
|
|
||||||
else null
|
|
||||||
)
|
|
||||||
var offer: int = (
|
|
||||||
individual_preview.payout if individual_preview != null else -1
|
|
||||||
)
|
|
||||||
_fish_details.text = (
|
|
||||||
"%.2f lb • %s\nbase value: $%d\nmain-shop offer: $%d%s"
|
|
||||||
% [
|
|
||||||
fish_catch.weight_lb,
|
|
||||||
fish_catch.fish.get_rarity_name(),
|
|
||||||
fish_catch.sale_value,
|
|
||||||
offer,
|
|
||||||
"\nfavorited fish cannot be sold."
|
|
||||||
if fish_catch.is_favorited
|
|
||||||
else "",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
_update_sale_summary()
|
|
||||||
|
|
||||||
|
|
||||||
func _update_sale_summary() -> void:
|
|
||||||
var selected_ids: Array[StringName] = _fish_selection.get_selected_ids()
|
|
||||||
var selected_count: int = selected_ids.size()
|
|
||||||
_sell_button.text = (
|
|
||||||
"sell fish"
|
|
||||||
if selected_count <= 1
|
|
||||||
else "sell %d fish" % selected_count
|
|
||||||
)
|
|
||||||
if selected_count == 0:
|
|
||||||
_selection_summary.text = "no fish selected"
|
|
||||||
_sell_button.disabled = true
|
|
||||||
return
|
|
||||||
if _network_session != null and _network_session.is_joined_client():
|
|
||||||
_selection_summary.text = (
|
|
||||||
"1 fish selected"
|
|
||||||
if selected_count == 1
|
|
||||||
else "%d fish selected" % selected_count
|
|
||||||
)
|
|
||||||
_selection_summary.text += (
|
|
||||||
"\nSell catches to the nearby pelicans."
|
|
||||||
)
|
|
||||||
_sell_button.disabled = true
|
|
||||||
return
|
|
||||||
var preview: FishSaleResultType = (
|
|
||||||
_sale_service.preview_batch(selected_ids, _buyer)
|
|
||||||
if _sale_service != null
|
|
||||||
else null
|
|
||||||
)
|
|
||||||
_selection_summary.text = (
|
|
||||||
"1 fish selected"
|
|
||||||
if selected_count == 1
|
|
||||||
else "%d fish selected" % selected_count
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
preview != null
|
|
||||||
and (
|
|
||||||
preview.is_success()
|
|
||||||
or preview.status == FishSaleResultType.Status.FAVORITED
|
|
||||||
)
|
|
||||||
):
|
|
||||||
_selection_summary.text += "\ntotal offer: $%d" % preview.payout
|
|
||||||
if preview != null and preview.status == FishSaleResultType.Status.FAVORITED:
|
|
||||||
_selection_summary.text += (
|
|
||||||
"\nfavorited fish must be removed from the selection."
|
|
||||||
)
|
|
||||||
_sell_button.disabled = (
|
|
||||||
preview == null
|
|
||||||
or not preview.is_success()
|
|
||||||
or _transaction_in_progress
|
|
||||||
or _closing
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func _refresh_upgrades() -> void:
|
func _refresh_upgrades() -> void:
|
||||||
if _upgrades == null:
|
if _upgrades == null:
|
||||||
return
|
return
|
||||||
|
|
@ -516,6 +432,7 @@ func _refresh_supplies() -> void:
|
||||||
if _network_shop == null or not _network_shop.can_request_purchase()
|
if _network_shop == null or not _network_shop.can_request_purchase()
|
||||||
else item.description
|
else item.description
|
||||||
)
|
)
|
||||||
|
UtilityPageStyleType.apply_ocean_button(button)
|
||||||
button.pressed.connect(_purchase_supply.bind(item_id))
|
button.pressed.connect(_purchase_supply.bind(item_id))
|
||||||
_supplies_list.add_child(button)
|
_supplies_list.add_child(button)
|
||||||
|
|
||||||
|
|
@ -549,142 +466,6 @@ func _refresh_cooler_capacity() -> void:
|
||||||
_cooler_purchase.text = "purchase"
|
_cooler_purchase.text = "purchase"
|
||||||
|
|
||||||
|
|
||||||
func _on_fish_selected(index: int) -> void:
|
|
||||||
if index < 0 or index >= _fish_list.item_count:
|
|
||||||
return
|
|
||||||
var input_generation: int = _selection_input_generation
|
|
||||||
call_deferred(
|
|
||||||
"_apply_keyboard_fish_selection",
|
|
||||||
index,
|
|
||||||
input_generation
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func _apply_keyboard_fish_selection(
|
|
||||||
index: int,
|
|
||||||
input_generation: int,
|
|
||||||
) -> void:
|
|
||||||
if (
|
|
||||||
input_generation != _selection_input_generation
|
|
||||||
or index < 0
|
|
||||||
or index >= _fish_list.item_count
|
|
||||||
):
|
|
||||||
return
|
|
||||||
var catch_id := StringName(str(_fish_list.get_item_metadata(index)))
|
|
||||||
_fish_selection.select_only(catch_id)
|
|
||||||
_feedback.text = ""
|
|
||||||
_refresh_fish_list()
|
|
||||||
_refresh_selected_fish()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_fish_clicked(
|
|
||||||
index: int,
|
|
||||||
_position: Vector2,
|
|
||||||
mouse_button_index: int,
|
|
||||||
) -> void:
|
|
||||||
if (
|
|
||||||
mouse_button_index != MOUSE_BUTTON_LEFT
|
|
||||||
or index < 0
|
|
||||||
or index >= _fish_list.item_count
|
|
||||||
):
|
|
||||||
return
|
|
||||||
_selection_input_generation += 1
|
|
||||||
var catch_id := StringName(str(_fish_list.get_item_metadata(index)))
|
|
||||||
_fish_selection.apply_click(
|
|
||||||
catch_id,
|
|
||||||
Input.is_key_pressed(KEY_CTRL),
|
|
||||||
Input.is_key_pressed(KEY_SHIFT)
|
|
||||||
)
|
|
||||||
_feedback.text = ""
|
|
||||||
_refresh_fish_list()
|
|
||||||
_refresh_selected_fish()
|
|
||||||
|
|
||||||
|
|
||||||
func _open_sale_confirmation() -> void:
|
|
||||||
if _network_session != null and _network_session.is_joined_client():
|
|
||||||
_feedback.text = "Sell catches to the nearby pelicans."
|
|
||||||
return
|
|
||||||
var selected_ids: Array[StringName] = _fish_selection.get_selected_ids()
|
|
||||||
if not _is_transaction_context_valid() or selected_ids.is_empty():
|
|
||||||
_feedback.text = "the fish selection is no longer available."
|
|
||||||
_refresh_all()
|
|
||||||
return
|
|
||||||
var preview: FishSaleResultType = _sale_service.preview_batch(
|
|
||||||
selected_ids,
|
|
||||||
_buyer
|
|
||||||
)
|
|
||||||
if not preview.is_success():
|
|
||||||
_feedback.text = (
|
|
||||||
"favorited fish cannot be sold. "
|
|
||||||
+ "remove them from the selection first."
|
|
||||||
if preview.status == FishSaleResultType.Status.FAVORITED
|
|
||||||
else preview.get_message()
|
|
||||||
)
|
|
||||||
return
|
|
||||||
_confirmation_catch_ids = selected_ids.duplicate()
|
|
||||||
_confirmation_generation = _generation
|
|
||||||
_confirmation_text.text = (
|
|
||||||
"sell %d fish to the fishing shop for $%d?"
|
|
||||||
% [preview.fish_count, preview.payout]
|
|
||||||
)
|
|
||||||
_confirmation.show()
|
|
||||||
_cancel_sale.grab_focus()
|
|
||||||
|
|
||||||
|
|
||||||
func _close_sale_confirmation() -> void:
|
|
||||||
_confirmation_catch_ids.clear()
|
|
||||||
_confirmation_generation = -1
|
|
||||||
_confirmation.hide()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_confirm_sale() -> void:
|
|
||||||
if (
|
|
||||||
_transaction_in_progress
|
|
||||||
or (
|
|
||||||
_network_session != null
|
|
||||||
and _network_session.is_joined_client()
|
|
||||||
)
|
|
||||||
):
|
|
||||||
return
|
|
||||||
var transaction_generation: int = _generation
|
|
||||||
var catch_ids: Array[StringName] = _confirmation_catch_ids.duplicate()
|
|
||||||
var confirmation_generation: int = _confirmation_generation
|
|
||||||
_close_sale_confirmation()
|
|
||||||
if (
|
|
||||||
catch_ids.is_empty()
|
|
||||||
or not _is_transaction_context_valid()
|
|
||||||
or transaction_generation != _generation
|
|
||||||
or confirmation_generation != transaction_generation
|
|
||||||
or _buyer.id != MAIN_SHOP_BUYER_ID
|
|
||||||
):
|
|
||||||
_feedback.text = "unable to complete sale."
|
|
||||||
return
|
|
||||||
_transaction_in_progress = true
|
|
||||||
var result: FishSaleResultType = _sale_service.sell_batch(
|
|
||||||
catch_ids,
|
|
||||||
_buyer
|
|
||||||
)
|
|
||||||
_transaction_in_progress = false
|
|
||||||
if (
|
|
||||||
transaction_generation != _generation
|
|
||||||
or not visible
|
|
||||||
):
|
|
||||||
return
|
|
||||||
if result.is_success():
|
|
||||||
_feedback.text = (
|
|
||||||
"fish sold for $%d."
|
|
||||||
if result.fish_count == 1
|
|
||||||
else "%d fish sold for $%d." % [
|
|
||||||
result.fish_count,
|
|
||||||
result.payout,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
_fish_selection.remove_ids(catch_ids)
|
|
||||||
else:
|
|
||||||
_feedback.text = result.get_message()
|
|
||||||
_refresh_all()
|
|
||||||
|
|
||||||
|
|
||||||
func _purchase_reel_speed() -> void:
|
func _purchase_reel_speed() -> void:
|
||||||
_purchase_upgrade(true)
|
_purchase_upgrade(true)
|
||||||
|
|
||||||
|
|
@ -777,13 +558,6 @@ func _on_network_purchase_finished(
|
||||||
_refresh_all()
|
_refresh_all()
|
||||||
|
|
||||||
|
|
||||||
func _get_selected_catch() -> FishCatchType:
|
|
||||||
var focused_id: StringName = _fish_selection.get_focused_id()
|
|
||||||
if _inventory == null or focused_id.is_empty():
|
|
||||||
return null
|
|
||||||
return _inventory.get_catch_by_id(focused_id)
|
|
||||||
|
|
||||||
|
|
||||||
func _is_transaction_context_valid() -> bool:
|
func _is_transaction_context_valid() -> bool:
|
||||||
return (
|
return (
|
||||||
visible
|
visible
|
||||||
|
|
@ -798,27 +572,6 @@ func _is_transaction_context_valid() -> bool:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func _on_inventory_changed() -> void:
|
|
||||||
_refresh_fish_list()
|
|
||||||
if not _confirmation_catch_ids.is_empty():
|
|
||||||
var preview: FishSaleResultType = _sale_service.preview_batch(
|
|
||||||
_confirmation_catch_ids,
|
|
||||||
_buyer
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
_confirmation_generation != _generation
|
|
||||||
or not preview.is_success()
|
|
||||||
):
|
|
||||||
_close_sale_confirmation()
|
|
||||||
_feedback.text = (
|
|
||||||
"favorited fish cannot be sold. "
|
|
||||||
+ "remove them from the selection first."
|
|
||||||
if preview.status == FishSaleResultType.Status.FAVORITED
|
|
||||||
else preview.get_message()
|
|
||||||
)
|
|
||||||
_refresh_selected_fish()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_wallet_changed(_balance: int, _delta: int) -> void:
|
func _on_wallet_changed(_balance: int, _delta: int) -> void:
|
||||||
_refresh_wallet()
|
_refresh_wallet()
|
||||||
_refresh_upgrades()
|
_refresh_upgrades()
|
||||||
|
|
@ -839,7 +592,6 @@ func _on_bag_changed() -> void:
|
||||||
|
|
||||||
func _on_cooler_capacity_changed(_level: int, _capacity: int) -> void:
|
func _on_cooler_capacity_changed(_level: int, _capacity: int) -> void:
|
||||||
_refresh_cooler_capacity()
|
_refresh_cooler_capacity()
|
||||||
_refresh_fish_list()
|
|
||||||
|
|
||||||
|
|
||||||
func _apply_mouse_close_policy(reason: CloseReason) -> void:
|
func _apply_mouse_close_policy(reason: CloseReason) -> void:
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,10 @@ grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
theme = ExtResource("2_theme")
|
theme = ExtResource("2_theme")
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
script = ExtResource("1_script")
|
script = ExtResource("1_script")
|
||||||
|
|
||||||
[node name="Dimmer" type="ColorRect" parent="."]
|
[node name="InputBlocker" type="Control" parent="."]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
|
|
@ -29,28 +30,60 @@ anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
mouse_filter = 0
|
mouse_filter = 0
|
||||||
color = Color(0.015, 0.02, 0.03, 0.64)
|
|
||||||
|
[node name="ShopCoolerPage" type="Control" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
visible = false
|
||||||
|
z_index = 90
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 1
|
||||||
|
|
||||||
|
[node name="ShopCoolerMount" type="Control" parent="ShopCoolerPage"]
|
||||||
|
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 = 1
|
||||||
|
|
||||||
|
[node name="BackToShopButton" type="Button" parent="ShopCoolerPage"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
z_index = 110
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 1052.0
|
||||||
|
offset_top = 54.0
|
||||||
|
offset_right = 1218.0
|
||||||
|
offset_bottom = 104.0
|
||||||
|
text = "back to shop"
|
||||||
|
|
||||||
[node name="ShopPanel" type="PanelContainer" parent="."]
|
[node name="ShopPanel" type="PanelContainer" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 8
|
anchors_preset = 8
|
||||||
anchor_left = 0.5
|
anchor_left = 0.5
|
||||||
anchor_top = 0.5
|
anchor_top = 0.5
|
||||||
anchor_right = 0.5
|
anchor_right = 0.5
|
||||||
anchor_bottom = 0.5
|
anchor_bottom = 0.5
|
||||||
offset_left = -560.0
|
offset_left = -490.0
|
||||||
offset_top = -320.0
|
offset_top = -300.0
|
||||||
offset_right = 560.0
|
offset_right = 490.0
|
||||||
offset_bottom = 320.0
|
offset_bottom = 300.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
|
|
||||||
[node name="Margin" type="MarginContainer" parent="ShopPanel"]
|
[node name="Margin" type="MarginContainer" parent="ShopPanel"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/margin_left = 16
|
theme_override_constants/margin_left = 16
|
||||||
theme_override_constants/margin_top = 14
|
theme_override_constants/margin_top = 12
|
||||||
theme_override_constants/margin_right = 16
|
theme_override_constants/margin_right = 16
|
||||||
theme_override_constants/margin_bottom = 14
|
theme_override_constants/margin_bottom = 12
|
||||||
|
|
||||||
[node name="Layout" type="VBoxContainer" parent="ShopPanel/Margin"]
|
[node name="Layout" type="VBoxContainer" parent="ShopPanel/Margin"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
@ -64,7 +97,7 @@ theme_override_constants/separation = 8
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
text = "fishing shop"
|
text = "fishing shop"
|
||||||
theme_override_font_sizes/font_size = 36
|
theme_override_font_sizes/font_size = 32
|
||||||
theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
|
theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
|
||||||
|
|
||||||
[node name="CoinIcon" type="TextureRect" parent="ShopPanel/Margin/Layout/Header"]
|
[node name="CoinIcon" type="TextureRect" parent="ShopPanel/Margin/Layout/Header"]
|
||||||
|
|
@ -84,7 +117,24 @@ text = "wallet: $0"
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "close"
|
text = "close"
|
||||||
custom_minimum_size = Vector2(110, 58)
|
custom_minimum_size = Vector2(104, 48)
|
||||||
|
|
||||||
|
[node name="ModeTabs" type="HBoxContainer" parent="ShopPanel/Margin/Layout"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
theme_override_constants/separation = 10
|
||||||
|
|
||||||
|
[node name="BuyModeButton" type="Button" parent="ShopPanel/Margin/Layout/ModeTabs"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
custom_minimum_size = Vector2(180, 42)
|
||||||
|
layout_mode = 2
|
||||||
|
text = "buy gear & supplies"
|
||||||
|
|
||||||
|
[node name="SellModeButton" type="Button" parent="ShopPanel/Margin/Layout/ModeTabs"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
custom_minimum_size = Vector2(180, 42)
|
||||||
|
layout_mode = 2
|
||||||
|
text = "sell fish"
|
||||||
|
|
||||||
[node name="Feedback" type="Label" parent="ShopPanel/Margin/Layout"]
|
[node name="Feedback" type="Label" parent="ShopPanel/Margin/Layout"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
@ -98,123 +148,27 @@ layout_mode = 2
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
theme_override_constants/separation = 12
|
theme_override_constants/separation = 12
|
||||||
|
|
||||||
[node name="FishSales" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body"]
|
|
||||||
custom_minimum_size = Vector2(420, 0)
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
theme_override_constants/separation = 6
|
|
||||||
|
|
||||||
[node name="SalesTitle" type="Label" parent="ShopPanel/Margin/Layout/Body/FishSales"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
text = "sell individual cooler fish • full base value"
|
|
||||||
theme_override_font_sizes/font_size = 26
|
|
||||||
|
|
||||||
[node name="SalesBody" type="HSplitContainer" parent="ShopPanel/Margin/Layout/Body/FishSales"]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_vertical = 3
|
|
||||||
split_offset = 220
|
|
||||||
theme_override_constants/separation = 8
|
|
||||||
|
|
||||||
[node name="ListPanel" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody"]
|
|
||||||
custom_minimum_size = Vector2(205, 0)
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="ListMargin" type="MarginContainer" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/ListPanel"]
|
|
||||||
layout_mode = 2
|
|
||||||
theme_override_constants/margin_left = 7
|
|
||||||
theme_override_constants/margin_top = 7
|
|
||||||
theme_override_constants/margin_right = 7
|
|
||||||
theme_override_constants/margin_bottom = 7
|
|
||||||
|
|
||||||
[node name="ListStack" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/ListPanel/ListMargin"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="FishEmpty" type="Label" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/ListPanel/ListMargin/ListStack"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
text = "your cooler is empty."
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="FishList" type="ItemList" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/ListPanel/ListMargin/ListStack"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_vertical = 3
|
|
||||||
fixed_icon_size = Vector2i(48, 36)
|
|
||||||
icon_mode = 1
|
|
||||||
select_mode = 1
|
|
||||||
same_column_width = true
|
|
||||||
|
|
||||||
[node name="FishDetail" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody"]
|
|
||||||
custom_minimum_size = Vector2(185, 0)
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="DetailMargin" type="MarginContainer" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail"]
|
|
||||||
layout_mode = 2
|
|
||||||
theme_override_constants/margin_left = 9
|
|
||||||
theme_override_constants/margin_top = 9
|
|
||||||
theme_override_constants/margin_right = 9
|
|
||||||
theme_override_constants/margin_bottom = 9
|
|
||||||
|
|
||||||
[node name="DetailStack" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail/DetailMargin"]
|
|
||||||
layout_mode = 2
|
|
||||||
theme_override_constants/separation = 6
|
|
||||||
|
|
||||||
[node name="FishTexture" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail/DetailMargin/DetailStack"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
custom_minimum_size = Vector2(160, 90)
|
|
||||||
layout_mode = 2
|
|
||||||
expand_mode = 1
|
|
||||||
stretch_mode = 5
|
|
||||||
texture_filter = 1
|
|
||||||
|
|
||||||
[node name="FishName" type="Label" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail/DetailMargin/DetailStack"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
text = "select a fish"
|
|
||||||
horizontal_alignment = 1
|
|
||||||
theme_override_font_sizes/font_size = 29
|
|
||||||
|
|
||||||
[node name="FishDetails" type="Label" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail/DetailMargin/DetailStack"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
text = "choose one individual fish from your cooler."
|
|
||||||
horizontal_alignment = 1
|
|
||||||
autowrap_mode = 2
|
|
||||||
|
|
||||||
[node name="SelectionSummary" type="Label" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail/DetailMargin/DetailStack"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
text = "no fish selected"
|
|
||||||
horizontal_alignment = 1
|
|
||||||
autowrap_mode = 2
|
|
||||||
theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
|
|
||||||
|
|
||||||
[node name="SellButton" type="Button" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail/DetailMargin/DetailStack"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
disabled = true
|
|
||||||
text = "sell selected"
|
|
||||||
|
|
||||||
[node name="Upgrades" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body"]
|
[node name="Upgrades" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body"]
|
||||||
custom_minimum_size = Vector2(270, 0)
|
custom_minimum_size = Vector2(270, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
theme_override_constants/separation = 8
|
theme_override_constants/separation = 8
|
||||||
|
|
||||||
[node name="UpgradeTitle" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades"]
|
[node name="UpgradeTitle" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "fishing upgrades"
|
text = "fishing upgrades"
|
||||||
theme_override_font_sizes/font_size = 26
|
theme_override_font_sizes/font_size = 23
|
||||||
|
|
||||||
[node name="Supplies" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body"]
|
[node name="Supplies" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body"]
|
||||||
custom_minimum_size = Vector2(210, 0)
|
custom_minimum_size = Vector2(210, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
theme_override_constants/separation = 6
|
theme_override_constants/separation = 6
|
||||||
|
|
||||||
[node name="Title" type="Label" parent="ShopPanel/Margin/Layout/Body/Supplies"]
|
[node name="Title" type="Label" parent="ShopPanel/Margin/Layout/Body/Supplies"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "supplies"
|
text = "supplies"
|
||||||
theme_override_font_sizes/font_size = 26
|
theme_override_font_sizes/font_size = 23
|
||||||
|
|
||||||
[node name="Scroll" type="ScrollContainer" parent="ShopPanel/Margin/Layout/Body/Supplies"]
|
[node name="Scroll" type="ScrollContainer" parent="ShopPanel/Margin/Layout/Body/Supplies"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
@ -233,16 +187,16 @@ layout_mode = 2
|
||||||
[node name="Margin" type="MarginContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard"]
|
[node name="Margin" type="MarginContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/margin_left = 9
|
theme_override_constants/margin_left = 9
|
||||||
theme_override_constants/margin_top = 9
|
theme_override_constants/margin_top = 7
|
||||||
theme_override_constants/margin_right = 9
|
theme_override_constants/margin_right = 9
|
||||||
theme_override_constants/margin_bottom = 9
|
theme_override_constants/margin_bottom = 7
|
||||||
|
|
||||||
[node name="Row" type="HBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin"]
|
[node name="Row" type="HBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/separation = 9
|
theme_override_constants/separation = 9
|
||||||
|
|
||||||
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row"]
|
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row"]
|
||||||
custom_minimum_size = Vector2(48, 48)
|
custom_minimum_size = Vector2(44, 44)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
texture = ExtResource("3_reel")
|
texture = ExtResource("3_reel")
|
||||||
expand_mode = 1
|
expand_mode = 1
|
||||||
|
|
@ -256,22 +210,25 @@ size_flags_horizontal = 3
|
||||||
[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
|
[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "reel speed"
|
text = "reel speed"
|
||||||
theme_override_font_sizes/font_size = 26
|
theme_override_font_sizes/font_size = 22
|
||||||
|
|
||||||
[node name="ReelLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
|
[node name="ReelLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "level 0"
|
text = "level 0"
|
||||||
|
theme_override_font_sizes/font_size = 18
|
||||||
|
|
||||||
[node name="ReelEffect" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
|
[node name="ReelEffect" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "1.00× → 1.10×"
|
text = "1.00× → 1.10×"
|
||||||
|
theme_override_font_sizes/font_size = 18
|
||||||
|
|
||||||
[node name="ReelCost" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
|
[node name="ReelCost" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "$50"
|
text = "$50"
|
||||||
|
theme_override_font_sizes/font_size = 18
|
||||||
|
|
||||||
[node name="ReelPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row"]
|
[node name="ReelPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
@ -284,16 +241,16 @@ layout_mode = 2
|
||||||
[node name="Margin" type="MarginContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard"]
|
[node name="Margin" type="MarginContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/margin_left = 9
|
theme_override_constants/margin_left = 9
|
||||||
theme_override_constants/margin_top = 9
|
theme_override_constants/margin_top = 7
|
||||||
theme_override_constants/margin_right = 9
|
theme_override_constants/margin_right = 9
|
||||||
theme_override_constants/margin_bottom = 9
|
theme_override_constants/margin_bottom = 7
|
||||||
|
|
||||||
[node name="Row" type="HBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin"]
|
[node name="Row" type="HBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/separation = 9
|
theme_override_constants/separation = 9
|
||||||
|
|
||||||
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row"]
|
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row"]
|
||||||
custom_minimum_size = Vector2(48, 48)
|
custom_minimum_size = Vector2(44, 44)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
texture = ExtResource("4_barrier")
|
texture = ExtResource("4_barrier")
|
||||||
expand_mode = 1
|
expand_mode = 1
|
||||||
|
|
@ -307,22 +264,25 @@ size_flags_horizontal = 3
|
||||||
[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"]
|
[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "barrier power"
|
text = "barrier power"
|
||||||
theme_override_font_sizes/font_size = 26
|
theme_override_font_sizes/font_size = 22
|
||||||
|
|
||||||
[node name="BarrierLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"]
|
[node name="BarrierLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "level 0"
|
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"]
|
[node name="BarrierEffect" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "1 damage → 2 damage"
|
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"]
|
[node name="BarrierCost" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "$100"
|
text = "$100"
|
||||||
|
theme_override_font_sizes/font_size = 18
|
||||||
|
|
||||||
[node name="BarrierPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row"]
|
[node name="BarrierPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
@ -335,16 +295,16 @@ layout_mode = 2
|
||||||
[node name="Margin" type="MarginContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard"]
|
[node name="Margin" type="MarginContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/margin_left = 9
|
theme_override_constants/margin_left = 9
|
||||||
theme_override_constants/margin_top = 9
|
theme_override_constants/margin_top = 7
|
||||||
theme_override_constants/margin_right = 9
|
theme_override_constants/margin_right = 9
|
||||||
theme_override_constants/margin_bottom = 9
|
theme_override_constants/margin_bottom = 7
|
||||||
|
|
||||||
[node name="Row" type="HBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin"]
|
[node name="Row" type="HBoxContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/separation = 9
|
theme_override_constants/separation = 9
|
||||||
|
|
||||||
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"]
|
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"]
|
||||||
custom_minimum_size = Vector2(48, 48)
|
custom_minimum_size = Vector2(44, 44)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
texture = ExtResource("6_cooler")
|
texture = ExtResource("6_cooler")
|
||||||
expand_mode = 1
|
expand_mode = 1
|
||||||
|
|
@ -358,74 +318,27 @@ size_flags_horizontal = 3
|
||||||
[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
|
[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "cooler capacity"
|
text = "cooler capacity"
|
||||||
theme_override_font_sizes/font_size = 26
|
theme_override_font_sizes/font_size = 22
|
||||||
|
|
||||||
[node name="CoolerLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
|
[node name="CoolerLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "level 0"
|
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"]
|
[node name="CoolerEffect" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "12 → 18 fish"
|
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"]
|
[node name="CoolerCost" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "$75"
|
text = "$75"
|
||||||
|
theme_override_font_sizes/font_size = 18
|
||||||
|
|
||||||
[node name="CoolerPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"]
|
[node name="CoolerPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "purchase"
|
text = "purchase"
|
||||||
|
|
||||||
[node name="SaleConfirmation" type="PanelContainer" parent="."]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
visible = false
|
|
||||||
z_index = 10
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 8
|
|
||||||
anchor_left = 0.5
|
|
||||||
anchor_top = 0.5
|
|
||||||
anchor_right = 0.5
|
|
||||||
anchor_bottom = 0.5
|
|
||||||
offset_left = -320.0
|
|
||||||
offset_top = -150.0
|
|
||||||
offset_right = 320.0
|
|
||||||
offset_bottom = 150.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
|
|
||||||
[node name="Margin" type="MarginContainer" parent="SaleConfirmation"]
|
|
||||||
layout_mode = 2
|
|
||||||
theme_override_constants/margin_left = 18
|
|
||||||
theme_override_constants/margin_top = 16
|
|
||||||
theme_override_constants/margin_right = 18
|
|
||||||
theme_override_constants/margin_bottom = 16
|
|
||||||
|
|
||||||
[node name="Stack" type="VBoxContainer" parent="SaleConfirmation/Margin"]
|
|
||||||
layout_mode = 2
|
|
||||||
theme_override_constants/separation = 12
|
|
||||||
|
|
||||||
[node name="ConfirmationText" type="Label" parent="SaleConfirmation/Margin/Stack"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
autowrap_mode = 2
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="Buttons" type="HBoxContainer" parent="SaleConfirmation/Margin/Stack"]
|
|
||||||
layout_mode = 2
|
|
||||||
alignment = 1
|
|
||||||
theme_override_constants/separation = 8
|
|
||||||
|
|
||||||
[node name="ConfirmSale" type="Button" parent="SaleConfirmation/Margin/Stack/Buttons"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
text = "sell fish"
|
|
||||||
theme_type_variation = &"DangerButton"
|
|
||||||
|
|
||||||
[node name="CancelSale" type="Button" parent="SaleConfirmation/Margin/Stack/Buttons"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
text = "cancel"
|
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ signal crisp_reset_focus_requested
|
||||||
signal interactive_pointer_ui_changed(is_open: bool)
|
signal interactive_pointer_ui_changed(is_open: bool)
|
||||||
signal passive_pointer_ui_changed(is_enabled: bool)
|
signal passive_pointer_ui_changed(is_enabled: bool)
|
||||||
signal player_menu_backdrop_visibility_changed(is_visible: bool)
|
signal player_menu_backdrop_visibility_changed(is_visible: bool)
|
||||||
|
signal shop_backdrop_visibility_changed(is_visible: bool)
|
||||||
|
|
||||||
@onready var _status_label: Label = %StatusLabel
|
@onready var _status_label: Label = %StatusLabel
|
||||||
@onready var _gameplay_transient_hud: Control = %GameplayTransientHUD
|
@onready var _gameplay_transient_hud: Control = %GameplayTransientHUD
|
||||||
|
|
@ -65,7 +66,7 @@ signal player_menu_backdrop_visibility_changed(is_visible: bool)
|
||||||
$UIRoot/TitleScreen/ResponsiveTitleStage/TitlePresentationScaleRoot/SettingsPanel
|
$UIRoot/TitleScreen/ResponsiveTitleStage/TitlePresentationScaleRoot/SettingsPanel
|
||||||
)
|
)
|
||||||
@onready var _pause_settings_panel: SettingsPanelType = (
|
@onready var _pause_settings_panel: SettingsPanelType = (
|
||||||
$UIRoot/PauseMenu/ResponsivePauseStage/PausePresentationScaleRoot/SettingsCenter/SettingsPanel
|
$UIRoot/CanonicalStage/PauseMenu/ResponsivePauseStage/PausePresentationScaleRoot/SettingsCenter/SettingsPanel
|
||||||
)
|
)
|
||||||
|
|
||||||
var _showcase_active: bool = false
|
var _showcase_active: bool = false
|
||||||
|
|
@ -77,6 +78,8 @@ 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 _item_effects: PlayerItemEffectsType
|
||||||
|
var _main_shop_buyer: FishBuyerProfileType
|
||||||
|
var _shop_interaction: ShopInteractionType
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
|
@ -151,6 +154,9 @@ func setup(
|
||||||
_player_menu.inventory_hotbar_context_changed.connect(
|
_player_menu.inventory_hotbar_context_changed.connect(
|
||||||
_on_inventory_hotbar_context_changed
|
_on_inventory_hotbar_context_changed
|
||||||
)
|
)
|
||||||
|
_player_menu.shop_cooler_modal_changed.connect(
|
||||||
|
_on_shop_cooler_modal_changed
|
||||||
|
)
|
||||||
_player_menu.setup(
|
_player_menu.setup(
|
||||||
player,
|
player,
|
||||||
inventory,
|
inventory,
|
||||||
|
|
@ -174,9 +180,7 @@ func setup(
|
||||||
_hotbar_ui.setup(hotbar, bag, item_catalog, fishing_spot)
|
_hotbar_ui.setup(hotbar, bag, item_catalog, fishing_spot)
|
||||||
_fishing_shop.setup(
|
_fishing_shop.setup(
|
||||||
player,
|
player,
|
||||||
inventory,
|
|
||||||
wallet,
|
wallet,
|
||||||
sale_service,
|
|
||||||
main_shop_buyer,
|
main_shop_buyer,
|
||||||
fishing_upgrades,
|
fishing_upgrades,
|
||||||
fishing_spot,
|
fishing_spot,
|
||||||
|
|
@ -184,10 +188,18 @@ func setup(
|
||||||
bag,
|
bag,
|
||||||
item_catalog,
|
item_catalog,
|
||||||
cooler_capacity,
|
cooler_capacity,
|
||||||
network_session,
|
network_shop_service,
|
||||||
network_shop_service
|
|
||||||
)
|
)
|
||||||
_fishing_shop.menu_visibility_changed.connect(_on_shop_visibility_changed)
|
_fishing_shop.menu_visibility_changed.connect(_on_shop_visibility_changed)
|
||||||
|
_fishing_shop.sell_fish_requested.connect(_on_shop_sell_fish_requested)
|
||||||
|
_fishing_shop.shop_cooler_return_requested.connect(
|
||||||
|
_on_shop_cooler_return_requested
|
||||||
|
)
|
||||||
|
_fishing_shop.shop_cooler_confirmation_cancel_requested.connect(
|
||||||
|
_player_menu.cancel_shop_cooler_confirmation
|
||||||
|
)
|
||||||
|
_main_shop_buyer = main_shop_buyer
|
||||||
|
_shop_interaction = shop_interaction
|
||||||
|
|
||||||
|
|
||||||
func setup_data_and_identity(
|
func setup_data_and_identity(
|
||||||
|
|
@ -554,6 +566,9 @@ func _on_player_menu_exit_started() -> void:
|
||||||
|
|
||||||
func _on_shop_visibility_changed(is_open: bool) -> void:
|
func _on_shop_visibility_changed(is_open: bool) -> void:
|
||||||
_shop_open = is_open
|
_shop_open = is_open
|
||||||
|
shop_backdrop_visibility_changed.emit(is_open)
|
||||||
|
if not is_open and _player_menu.is_shop_cooler_mounted():
|
||||||
|
_player_menu.unmount_shop_cooler()
|
||||||
_refresh_chat_availability()
|
_refresh_chat_availability()
|
||||||
_refresh_hotbar_visibility()
|
_refresh_hotbar_visibility()
|
||||||
_hotbar_ui.set_gameplay_input_enabled(
|
_hotbar_ui.set_gameplay_input_enabled(
|
||||||
|
|
@ -567,6 +582,33 @@ func _on_shop_visibility_changed(is_open: bool) -> void:
|
||||||
_emit_interactive_pointer_ui_changed()
|
_emit_interactive_pointer_ui_changed()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_shop_sell_fish_requested() -> void:
|
||||||
|
if (
|
||||||
|
not _fishing_shop.visible
|
||||||
|
or _main_shop_buyer == null
|
||||||
|
or not _main_shop_buyer.is_valid()
|
||||||
|
or _shop_interaction == null
|
||||||
|
or not _shop_interaction.is_local_player_in_range()
|
||||||
|
or _fishing_spot == null
|
||||||
|
or not _fishing_spot.is_ready_for_shop_transaction()
|
||||||
|
):
|
||||||
|
return
|
||||||
|
if _player_menu.mount_shop_cooler(
|
||||||
|
_fishing_shop.get_shop_cooler_mount(),
|
||||||
|
_main_shop_buyer,
|
||||||
|
):
|
||||||
|
_fishing_shop.activate_shop_cooler_page()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_shop_cooler_return_requested() -> void:
|
||||||
|
_player_menu.unmount_shop_cooler()
|
||||||
|
_fishing_shop.deactivate_shop_cooler_page()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_shop_cooler_modal_changed(is_open: bool) -> void:
|
||||||
|
_fishing_shop.set_shop_cooler_modal_open(is_open)
|
||||||
|
|
||||||
|
|
||||||
func _on_inventory_hotbar_context_changed(show_hotbar: bool) -> void:
|
func _on_inventory_hotbar_context_changed(show_hotbar: bool) -> void:
|
||||||
var context_changed: bool = _player_menu_hotbar_visible != show_hotbar
|
var context_changed: bool = _player_menu_hotbar_visible != show_hotbar
|
||||||
_player_menu_hotbar_visible = show_hotbar
|
_player_menu_hotbar_visible = show_hotbar
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,11 @@ script = ExtResource("1_ui")
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
|
|
||||||
[node name="GameplayTransientHUD" type="Control" parent="UIRoot"]
|
[node name="CanonicalStage" type="Control" parent="UIRoot"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="GameplayTransientHUD" type="Control" parent="UIRoot/CanonicalStage"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
|
|
@ -51,7 +55,7 @@ grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
|
|
||||||
[node name="FishingPanel" type="PanelContainer" parent="UIRoot/GameplayTransientHUD"]
|
[node name="FishingPanel" type="PanelContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
visible = false
|
visible = false
|
||||||
z_index = 60
|
z_index = 60
|
||||||
|
|
@ -69,33 +73,33 @@ grow_vertical = 0
|
||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
theme = ExtResource("3_theme")
|
theme = ExtResource("3_theme")
|
||||||
|
|
||||||
[node name="MarginContainer" type="MarginContainer" parent="UIRoot/GameplayTransientHUD/FishingPanel"]
|
[node name="MarginContainer" type="MarginContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel"]
|
||||||
theme_override_constants/margin_left = 14
|
theme_override_constants/margin_left = 14
|
||||||
theme_override_constants/margin_top = 8
|
theme_override_constants/margin_top = 8
|
||||||
theme_override_constants/margin_right = 14
|
theme_override_constants/margin_right = 14
|
||||||
theme_override_constants/margin_bottom = 8
|
theme_override_constants/margin_bottom = 8
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="UIRoot/GameplayTransientHUD/FishingPanel/MarginContainer"]
|
[node name="VBoxContainer" type="VBoxContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer"]
|
||||||
theme_override_constants/separation = 4
|
theme_override_constants/separation = 4
|
||||||
|
|
||||||
[node name="StatusLabel" type="Label" parent="UIRoot/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"]
|
[node name="StatusLabel" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
visible = false
|
visible = false
|
||||||
text = ""
|
text = ""
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
theme_override_font_sizes/font_size = 16
|
theme_override_font_sizes/font_size = 16
|
||||||
|
|
||||||
[node name="ShowcaseDetails" type="Label" parent="UIRoot/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"]
|
[node name="ShowcaseDetails" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
visible = false
|
visible = false
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="CatchTrack" type="Control" parent="UIRoot/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"]
|
[node name="CatchTrack" type="Control" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
visible = false
|
visible = false
|
||||||
custom_minimum_size = Vector2(0, 20)
|
custom_minimum_size = Vector2(0, 20)
|
||||||
|
|
||||||
[node name="GreenCatchProgress" type="ProgressBar" parent="UIRoot/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer/CatchTrack"]
|
[node name="GreenCatchProgress" type="ProgressBar" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer/CatchTrack"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
|
|
@ -109,7 +113,7 @@ theme_override_styles/fill = SubResource("StyleBox_catch_fill")
|
||||||
value = 0.0
|
value = 0.0
|
||||||
show_percentage = false
|
show_percentage = false
|
||||||
|
|
||||||
[node name="RedChaseProgress" type="ProgressBar" parent="UIRoot/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer/CatchTrack"]
|
[node name="RedChaseProgress" type="ProgressBar" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer/CatchTrack"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
|
|
@ -123,7 +127,7 @@ theme_override_styles/fill = SubResource("StyleBox_chase_fill")
|
||||||
value = 0.0
|
value = 0.0
|
||||||
show_percentage = false
|
show_percentage = false
|
||||||
|
|
||||||
[node name="BarrierMarkers" type="Control" parent="UIRoot/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer/CatchTrack"]
|
[node name="BarrierMarkers" type="Control" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer/CatchTrack"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
|
|
@ -133,23 +137,23 @@ grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
|
|
||||||
[node name="BarrierSummary" type="Label" parent="UIRoot/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"]
|
[node name="BarrierSummary" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
visible = false
|
visible = false
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="BarrierHealth" type="Label" parent="UIRoot/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"]
|
[node name="BarrierHealth" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
visible = false
|
visible = false
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="PlayerMenu" parent="UIRoot" instance=ExtResource("2_menu")]
|
[node name="PlayerMenu" parent="UIRoot/CanonicalStage" instance=ExtResource("2_menu")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
||||||
[node name="Hotbar" parent="UIRoot" instance=ExtResource("7_hotbar")]
|
[node name="Hotbar" parent="UIRoot/CanonicalStage" instance=ExtResource("7_hotbar")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
||||||
[node name="ShopPrompt" type="PanelContainer" parent="UIRoot/GameplayTransientHUD"]
|
[node name="ShopPrompt" type="PanelContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
visible = false
|
visible = false
|
||||||
z_index = 55
|
z_index = 55
|
||||||
|
|
@ -167,22 +171,22 @@ grow_vertical = 0
|
||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
theme = ExtResource("3_theme")
|
theme = ExtResource("3_theme")
|
||||||
|
|
||||||
[node name="Margin" type="MarginContainer" parent="UIRoot/GameplayTransientHUD/ShopPrompt"]
|
[node name="Margin" type="MarginContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/margin_left = 12
|
theme_override_constants/margin_left = 12
|
||||||
theme_override_constants/margin_top = 7
|
theme_override_constants/margin_top = 7
|
||||||
theme_override_constants/margin_right = 12
|
theme_override_constants/margin_right = 12
|
||||||
theme_override_constants/margin_bottom = 7
|
theme_override_constants/margin_bottom = 7
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="UIRoot/GameplayTransientHUD/ShopPrompt/Margin"]
|
[node name="Label" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt/Margin"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "press e to open fishing shop"
|
text = "press e to open fishing shop"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="FishingShop" parent="UIRoot" 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/GameplayTransientHUD"]
|
[node name="EffectStatus" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
visible = false
|
visible = false
|
||||||
z_index = 54
|
z_index = 54
|
||||||
|
|
@ -223,5 +227,5 @@ script = ExtResource("4_fade")
|
||||||
[node name="TitleScreen" parent="UIRoot" instance=ExtResource("5_title")]
|
[node name="TitleScreen" parent="UIRoot" instance=ExtResource("5_title")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
||||||
[node name="PauseMenu" parent="UIRoot" instance=ExtResource("6_pause")]
|
[node name="PauseMenu" parent="UIRoot/CanonicalStage" instance=ExtResource("6_pause")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ const PAUSE_DESKTOP_REFERENCE_SIZE: Vector2 = Vector2(1280.0, 720.0)
|
||||||
const PAUSE_COMPACT_REFERENCE_SIZE: Vector2 = Vector2(640.0, 480.0)
|
const PAUSE_COMPACT_REFERENCE_SIZE: Vector2 = Vector2(640.0, 480.0)
|
||||||
const COMPACT_HEIGHT_THRESHOLD: float = 560.0
|
const COMPACT_HEIGHT_THRESHOLD: float = 560.0
|
||||||
const VISIBILITY_FADE_DURATION: float = 0.55
|
const VISIBILITY_FADE_DURATION: float = 0.55
|
||||||
const BACKDROP_TARGET_ALPHA: float = 0.68
|
|
||||||
const PlayerType = preload("res://player/player.gd")
|
const PlayerType = preload("res://player/player.gd")
|
||||||
const SaveManagerType = preload("res://save/player_save_manager.gd")
|
const SaveManagerType = preload("res://save/player_save_manager.gd")
|
||||||
const SettingsManagerType = preload(
|
const SettingsManagerType = preload(
|
||||||
|
|
@ -48,7 +47,6 @@ enum CloseReason {
|
||||||
@onready var _presentation_scale_root: Control = %PausePresentationScaleRoot
|
@onready var _presentation_scale_root: Control = %PausePresentationScaleRoot
|
||||||
@onready var _root_page: SettingsBubblePage = %RootPage
|
@onready var _root_page: SettingsBubblePage = %RootPage
|
||||||
@onready var _settings_panel: SettingsPanelType = %SettingsPanel
|
@onready var _settings_panel: SettingsPanelType = %SettingsPanel
|
||||||
@onready var _dim_background: ColorRect = %DimBackground
|
|
||||||
@onready var _transition_flurry: BubbleTransitionFlurry = (
|
@onready var _transition_flurry: BubbleTransitionFlurry = (
|
||||||
%TransitionBubbleLayer
|
%TransitionBubbleLayer
|
||||||
)
|
)
|
||||||
|
|
@ -75,8 +73,6 @@ var _action_in_progress: bool = false
|
||||||
var _root_transition_active: bool = false
|
var _root_transition_active: bool = false
|
||||||
var _root_transition_generation: int = 0
|
var _root_transition_generation: int = 0
|
||||||
var _closing_menu: bool = false
|
var _closing_menu: bool = false
|
||||||
var _backdrop_fade: Tween
|
|
||||||
var _backdrop_fade_generation: int = 0
|
|
||||||
var _pending_join_endpoint: String = ""
|
var _pending_join_endpoint: String = ""
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -98,7 +94,6 @@ func _ready() -> void:
|
||||||
)
|
)
|
||||||
_join_game_page.join_requested.connect(_on_join_game_requested)
|
_join_game_page.join_requested.connect(_on_join_game_requested)
|
||||||
_join_game_page.back_requested.connect(_close_join_game)
|
_join_game_page.back_requested.connect(_close_join_game)
|
||||||
_dim_background.color.a = 0.0
|
|
||||||
_confirmation_page.hide_page()
|
_confirmation_page.hide_page()
|
||||||
resized.connect(_update_responsive_pause_stage)
|
resized.connect(_update_responsive_pause_stage)
|
||||||
call_deferred("_update_responsive_pause_stage")
|
call_deferred("_update_responsive_pause_stage")
|
||||||
|
|
@ -144,7 +139,6 @@ func open_menu() -> void:
|
||||||
_action_in_progress = false
|
_action_in_progress = false
|
||||||
_root_page.hide_page()
|
_root_page.hide_page()
|
||||||
show()
|
show()
|
||||||
_fade_backdrop(true)
|
|
||||||
_update_responsive_pause_stage()
|
_update_responsive_pause_stage()
|
||||||
_begin_root_entry(false)
|
_begin_root_entry(false)
|
||||||
menu_visibility_changed.emit(true)
|
menu_visibility_changed.emit(true)
|
||||||
|
|
@ -159,7 +153,6 @@ func resume() -> void:
|
||||||
or _confirmation_action != ConfirmationAction.NONE
|
or _confirmation_action != ConfirmationAction.NONE
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
_fade_backdrop(false)
|
|
||||||
_begin_root_exit(_finish_user_close)
|
_begin_root_exit(_finish_user_close)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -268,7 +261,6 @@ func _finish_open_settings() -> void:
|
||||||
_settings_panel.open_panel(
|
_settings_panel.open_panel(
|
||||||
_settings_manager,
|
_settings_manager,
|
||||||
SettingsPanelType.PresentationMode.GAMEPLAY_MODAL,
|
SettingsPanelType.PresentationMode.GAMEPLAY_MODAL,
|
||||||
true,
|
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -493,9 +485,6 @@ func _finish_close(reason: CloseReason, restore_controls: bool) -> void:
|
||||||
_closing_menu = false
|
_closing_menu = false
|
||||||
_root_transition_generation += 1
|
_root_transition_generation += 1
|
||||||
_root_transition_active = false
|
_root_transition_active = false
|
||||||
_cancel_backdrop_fade()
|
|
||||||
_dim_background.color.a = 0.0
|
|
||||||
_dim_background.hide()
|
|
||||||
_settings_panel.hide()
|
_settings_panel.hide()
|
||||||
_join_game_page.close_page()
|
_join_game_page.close_page()
|
||||||
_root_page.hide_page()
|
_root_page.hide_page()
|
||||||
|
|
@ -519,45 +508,6 @@ func _emit_transition_flurry() -> void:
|
||||||
_transition_flurry.emit_flurry()
|
_transition_flurry.emit_flurry()
|
||||||
|
|
||||||
|
|
||||||
func _fade_backdrop(fade_in: bool) -> void:
|
|
||||||
_backdrop_fade_generation += 1
|
|
||||||
_cancel_backdrop_fade()
|
|
||||||
_dim_background.show()
|
|
||||||
var target_alpha: float = BACKDROP_TARGET_ALPHA if fade_in else 0.0
|
|
||||||
if is_equal_approx(_dim_background.color.a, target_alpha):
|
|
||||||
_finish_backdrop_fade(_backdrop_fade_generation, fade_in)
|
|
||||||
return
|
|
||||||
var generation: int = _backdrop_fade_generation
|
|
||||||
_backdrop_fade = create_tween()
|
|
||||||
_backdrop_fade.tween_property(
|
|
||||||
_dim_background,
|
|
||||||
"color:a",
|
|
||||||
target_alpha,
|
|
||||||
VISIBILITY_FADE_DURATION
|
|
||||||
).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN_OUT)
|
|
||||||
_backdrop_fade.finished.connect(
|
|
||||||
_finish_backdrop_fade.bind(generation, fade_in),
|
|
||||||
CONNECT_ONE_SHOT
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func _finish_backdrop_fade(generation: int, faded_in: bool) -> void:
|
|
||||||
if generation != _backdrop_fade_generation:
|
|
||||||
return
|
|
||||||
_backdrop_fade = null
|
|
||||||
_dim_background.color.a = (
|
|
||||||
BACKDROP_TARGET_ALPHA if faded_in else 0.0
|
|
||||||
)
|
|
||||||
if not faded_in:
|
|
||||||
_dim_background.hide()
|
|
||||||
|
|
||||||
|
|
||||||
func _cancel_backdrop_fade() -> void:
|
|
||||||
if _backdrop_fade != null:
|
|
||||||
_backdrop_fade.kill()
|
|
||||||
_backdrop_fade = null
|
|
||||||
|
|
||||||
|
|
||||||
func _update_responsive_pause_stage() -> void:
|
func _update_responsive_pause_stage() -> void:
|
||||||
if not is_node_ready():
|
if not is_node_ready():
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -25,17 +25,6 @@ mouse_filter = 0
|
||||||
theme = ExtResource("3_theme")
|
theme = ExtResource("3_theme")
|
||||||
script = ExtResource("1_script")
|
script = ExtResource("1_script")
|
||||||
|
|
||||||
[node name="DimBackground" type="ColorRect" parent="."]
|
|
||||||
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 = 0
|
|
||||||
color = Color(0.025, 0.12, 0.19, 0.68)
|
|
||||||
|
|
||||||
[node name="TransitionBubbleLayer" type="Control" parent="."]
|
[node name="TransitionBubbleLayer" type="Control" parent="."]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
z_index = 1
|
z_index = 1
|
||||||
|
|
|
||||||
|
|
@ -79,10 +79,12 @@ const COOLER_RARITY_UNCOMMON := Color("64c87c")
|
||||||
const COOLER_RARITY_RARE := Color("6098dd")
|
const COOLER_RARITY_RARE := Color("6098dd")
|
||||||
const COOLER_RARITY_EPIC := Color("a979cf")
|
const COOLER_RARITY_EPIC := Color("a979cf")
|
||||||
const COOLER_RARITY_LEGENDARY := Color("db78a7")
|
const COOLER_RARITY_LEGENDARY := Color("db78a7")
|
||||||
|
const MAIN_SHOP_BUYER_ID: StringName = &"main_fishing_shop"
|
||||||
|
|
||||||
signal menu_visibility_changed(is_open: bool)
|
signal menu_visibility_changed(is_open: bool)
|
||||||
signal inventory_hotbar_context_changed(show_hotbar: bool)
|
signal inventory_hotbar_context_changed(show_hotbar: bool)
|
||||||
signal menu_exit_started
|
signal menu_exit_started
|
||||||
|
signal shop_cooler_modal_changed(is_open: bool)
|
||||||
|
|
||||||
enum Section {
|
enum Section {
|
||||||
COOLER,
|
COOLER,
|
||||||
|
|
@ -131,6 +133,7 @@ const INVENTORY_HEADER_INSET := Vector2(24.0, 20.0)
|
||||||
const INVENTORY_HEADER_HEIGHT := 40.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 SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0)
|
||||||
|
|
||||||
@onready var _navigation_cluster: BubbleClusterType = %NavigationCluster
|
@onready var _navigation_cluster: BubbleClusterType = %NavigationCluster
|
||||||
@onready var _presentation_scale_root: Control = %PlayerMenuPresentationScaleRoot
|
@onready var _presentation_scale_root: Control = %PlayerMenuPresentationScaleRoot
|
||||||
|
|
@ -276,6 +279,12 @@ var _reservations: PlayerAssetReservationService
|
||||||
var _network_profile_service: NetworkProfileService
|
var _network_profile_service: NetworkProfileService
|
||||||
var _network_player_list: NetworkPlayerListService
|
var _network_player_list: NetworkPlayerListService
|
||||||
var _default_buyer: FishBuyerProfileType
|
var _default_buyer: FishBuyerProfileType
|
||||||
|
var _sale_buyer_override: FishBuyerProfileType
|
||||||
|
var _shop_cooler_context_active: bool = false
|
||||||
|
var _cooler_original_parent: Node
|
||||||
|
var _cooler_original_index: int = -1
|
||||||
|
var _confirmation_original_parent: Node
|
||||||
|
var _confirmation_original_index: int = -1
|
||||||
var _catalog: FishPoolType
|
var _catalog: FishPoolType
|
||||||
var _fishing_spot: FishingSpotType
|
var _fishing_spot: FishingSpotType
|
||||||
var _bag: PlayerBagType
|
var _bag: PlayerBagType
|
||||||
|
|
@ -336,6 +345,10 @@ var _logbook_page_tween: Tween
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
_configure_inventory_transition_group()
|
_configure_inventory_transition_group()
|
||||||
|
_cooler_original_parent = _cooler_page.get_parent()
|
||||||
|
_cooler_original_index = _cooler_page.get_index()
|
||||||
|
_confirmation_original_parent = _sale_confirmation.get_parent()
|
||||||
|
_confirmation_original_index = _sale_confirmation.get_index()
|
||||||
_inventory_sub_tabs.move_child(_tackle_sub_tab, 1)
|
_inventory_sub_tabs.move_child(_tackle_sub_tab, 1)
|
||||||
_inventory_sub_tabs.move_child(_bag_sub_tab, 2)
|
_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)
|
||||||
|
|
@ -652,6 +665,7 @@ func consume_escape() -> bool:
|
||||||
func open_menu() -> void:
|
func open_menu() -> void:
|
||||||
if (
|
if (
|
||||||
visible
|
visible
|
||||||
|
or _shop_cooler_context_active
|
||||||
or _player == null
|
or _player == null
|
||||||
or _fishing_spot == null
|
or _fishing_spot == null
|
||||||
or not _fishing_spot.can_open_player_menu()
|
or not _fishing_spot.can_open_player_menu()
|
||||||
|
|
@ -678,6 +692,99 @@ func open_menu() -> void:
|
||||||
menu_visibility_changed.emit(true)
|
menu_visibility_changed.emit(true)
|
||||||
|
|
||||||
|
|
||||||
|
func mount_shop_cooler(
|
||||||
|
host: Control,
|
||||||
|
buyer: FishBuyerProfileType,
|
||||||
|
) -> bool:
|
||||||
|
if (
|
||||||
|
visible
|
||||||
|
or _shop_cooler_context_active
|
||||||
|
or host == null
|
||||||
|
or not is_instance_valid(host)
|
||||||
|
or buyer == null
|
||||||
|
or not buyer.is_valid()
|
||||||
|
or buyer.id != MAIN_SHOP_BUYER_ID
|
||||||
|
):
|
||||||
|
return false
|
||||||
|
_menu_generation += 1
|
||||||
|
_cancel_page_tween()
|
||||||
|
_transitioning = false
|
||||||
|
_page_transitioning = false
|
||||||
|
_sale_buyer_override = buyer
|
||||||
|
_shop_cooler_context_active = true
|
||||||
|
_current_section = Section.COOLER
|
||||||
|
_last_inventory_section = Section.COOLER
|
||||||
|
_cooler_page.reparent(host, false)
|
||||||
|
_sale_confirmation.reparent(host, false)
|
||||||
|
_cooler_page.position = Vector2.ZERO
|
||||||
|
_cooler_page.size = DESKTOP_REFERENCE_SIZE
|
||||||
|
_cooler_page.scale = Vector2.ONE
|
||||||
|
_cooler_page.modulate = Color.WHITE
|
||||||
|
_cooler_page.visible = true
|
||||||
|
_sale_confirmation.visible = false
|
||||||
|
_update_shell_layout()
|
||||||
|
_refresh_inventory()
|
||||||
|
_set_content_interactive(true)
|
||||||
|
_update_cooler_water_mask()
|
||||||
|
set_process(true)
|
||||||
|
call_deferred("_focus_shop_cooler")
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
func unmount_shop_cooler() -> void:
|
||||||
|
if not _shop_cooler_context_active:
|
||||||
|
return
|
||||||
|
_close_sale_confirmation()
|
||||||
|
_set_content_interactive(false)
|
||||||
|
_cooler_page.visible = false
|
||||||
|
_cooler_page.reparent(_cooler_original_parent, false)
|
||||||
|
_cooler_original_parent.move_child(
|
||||||
|
_cooler_page,
|
||||||
|
mini(_cooler_original_index, _cooler_original_parent.get_child_count() - 1),
|
||||||
|
)
|
||||||
|
_sale_confirmation.reparent(_confirmation_original_parent, false)
|
||||||
|
_confirmation_original_parent.move_child(
|
||||||
|
_sale_confirmation,
|
||||||
|
mini(
|
||||||
|
_confirmation_original_index,
|
||||||
|
_confirmation_original_parent.get_child_count() - 1,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
_sale_buyer_override = null
|
||||||
|
_shop_cooler_context_active = false
|
||||||
|
_menu_generation += 1
|
||||||
|
if not visible:
|
||||||
|
set_process(false)
|
||||||
|
|
||||||
|
|
||||||
|
func is_shop_cooler_mounted() -> bool:
|
||||||
|
return _shop_cooler_context_active
|
||||||
|
|
||||||
|
|
||||||
|
func cancel_shop_cooler_confirmation() -> void:
|
||||||
|
if _shop_cooler_context_active and _sale_confirmation.visible:
|
||||||
|
_close_sale_confirmation()
|
||||||
|
|
||||||
|
|
||||||
|
func _focus_shop_cooler() -> void:
|
||||||
|
if not _shop_cooler_context_active or not _cooler_page.is_visible_in_tree():
|
||||||
|
return
|
||||||
|
var focused_node := _fish_nodes.get(
|
||||||
|
_fish_selection.get_focused_id()
|
||||||
|
) as CoolerFishSpriteType
|
||||||
|
if focused_node != null and focused_node.focus_mode != Control.FOCUS_NONE:
|
||||||
|
focused_node.grab_focus()
|
||||||
|
return
|
||||||
|
for fish_catch: FishCatchType in _sorted_catches:
|
||||||
|
var fish_node := _fish_nodes.get(
|
||||||
|
fish_catch.catch_id
|
||||||
|
) as CoolerFishSpriteType
|
||||||
|
if fish_node != null and fish_node.focus_mode != Control.FOCUS_NONE:
|
||||||
|
fish_node.grab_focus()
|
||||||
|
return
|
||||||
|
_cooler_sort_option.grab_focus()
|
||||||
|
|
||||||
|
|
||||||
func close_menu(
|
func close_menu(
|
||||||
reason: CloseReason = CloseReason.USER,
|
reason: CloseReason = CloseReason.USER,
|
||||||
restore_controls: bool = true,
|
restore_controls: bool = true,
|
||||||
|
|
@ -886,6 +993,9 @@ func _show_last_inventory_section() -> void:
|
||||||
|
|
||||||
|
|
||||||
func _focus_current_section() -> void:
|
func _focus_current_section() -> void:
|
||||||
|
if _shop_cooler_context_active:
|
||||||
|
_focus_shop_cooler()
|
||||||
|
return
|
||||||
if _current_section == Section.COOLER:
|
if _current_section == Section.COOLER:
|
||||||
_cooler_sub_tab.grab_focus()
|
_cooler_sub_tab.grab_focus()
|
||||||
elif _current_section == Section.BAG:
|
elif _current_section == Section.BAG:
|
||||||
|
|
@ -903,12 +1013,15 @@ func _focus_current_section() -> void:
|
||||||
|
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
if visible:
|
if visible or _shop_cooler_context_active:
|
||||||
_motion_elapsed += delta
|
_motion_elapsed += delta
|
||||||
|
if visible:
|
||||||
_navigation_cluster.advance_motion(delta)
|
_navigation_cluster.advance_motion(delta)
|
||||||
_apply_navigation_selection_presentation()
|
_apply_navigation_selection_presentation()
|
||||||
for fish_node: CoolerFishSpriteType in _fish_nodes.values():
|
for fish_node: CoolerFishSpriteType in _fish_nodes.values():
|
||||||
fish_node.advance_presentation(delta, _motion_elapsed)
|
fish_node.advance_presentation(delta, _motion_elapsed)
|
||||||
|
if not visible:
|
||||||
|
return
|
||||||
var bag_motion_enabled: bool = (
|
var bag_motion_enabled: bool = (
|
||||||
not _bag_drag_active and not get_viewport().gui_is_dragging()
|
not _bag_drag_active and not get_viewport().gui_is_dragging()
|
||||||
)
|
)
|
||||||
|
|
@ -1040,6 +1153,20 @@ func _apply_inventory_styles() -> void:
|
||||||
"font_color",
|
"font_color",
|
||||||
UtilityPageStyle.OCEAN_TEXT_PRIMARY,
|
UtilityPageStyle.OCEAN_TEXT_PRIMARY,
|
||||||
)
|
)
|
||||||
|
_sale_confirmation.add_theme_stylebox_override(
|
||||||
|
"panel",
|
||||||
|
UtilityPageStyle.panel_style(),
|
||||||
|
)
|
||||||
|
_confirmation_message.add_theme_font_override(
|
||||||
|
"font",
|
||||||
|
UtilityPageStyle.TuffyFont,
|
||||||
|
)
|
||||||
|
_confirmation_message.add_theme_color_override(
|
||||||
|
"font_color",
|
||||||
|
UtilityPageStyle.OCEAN_TEXT_PRIMARY,
|
||||||
|
)
|
||||||
|
UtilityPageStyle.apply_ocean_button(_confirm_sale_button)
|
||||||
|
UtilityPageStyle.apply_ocean_button(_cancel_sale_button)
|
||||||
|
|
||||||
|
|
||||||
func _set_bag_view(view: BagView) -> void:
|
func _set_bag_view(view: BagView) -> void:
|
||||||
|
|
@ -1531,11 +1658,9 @@ func _update_shell_layout() -> void:
|
||||||
_players_page.set_anchors_preset(Control.PRESET_TOP_LEFT)
|
_players_page.set_anchors_preset(Control.PRESET_TOP_LEFT)
|
||||||
_players_page.position = Vector2.ZERO
|
_players_page.position = Vector2.ZERO
|
||||||
_players_page.size = reference_size
|
_players_page.size = reference_size
|
||||||
|
_sale_confirmation.size = SALE_CONFIRMATION_SIZE
|
||||||
_sale_confirmation.position = (
|
_sale_confirmation.position = (
|
||||||
Vector2(70.0, 128.0) if compact else Vector2(320.0, 200.0)
|
(reference_size - SALE_CONFIRMATION_SIZE) * 0.5
|
||||||
)
|
|
||||||
_sale_confirmation.size = (
|
|
||||||
Vector2(500.0, 250.0) if compact else Vector2(640.0, 320.0)
|
|
||||||
)
|
)
|
||||||
_cooler_host.custom_minimum_size = Vector2(
|
_cooler_host.custom_minimum_size = Vector2(
|
||||||
520.0 if compact else 800.0,
|
520.0 if compact else 800.0,
|
||||||
|
|
@ -2592,8 +2717,15 @@ func _configure_cooler_fish_focus() -> void:
|
||||||
controls.append(fish_node)
|
controls.append(fish_node)
|
||||||
var columns: int = 3 if _compact_layout else 9
|
var columns: int = 3 if _compact_layout else 9
|
||||||
if controls.is_empty():
|
if controls.is_empty():
|
||||||
|
if not _shop_cooler_context_active:
|
||||||
_cooler_sub_tab.focus_neighbor_bottom = NodePath()
|
_cooler_sub_tab.focus_neighbor_bottom = NodePath()
|
||||||
return
|
return
|
||||||
|
var top_control: Control = (
|
||||||
|
_cooler_sort_option
|
||||||
|
if _shop_cooler_context_active
|
||||||
|
else _cooler_sub_tab
|
||||||
|
)
|
||||||
|
if not _shop_cooler_context_active:
|
||||||
_cooler_sub_tab.focus_neighbor_bottom = _cooler_sub_tab.get_path_to(
|
_cooler_sub_tab.focus_neighbor_bottom = _cooler_sub_tab.get_path_to(
|
||||||
controls.front()
|
controls.front()
|
||||||
)
|
)
|
||||||
|
|
@ -2606,7 +2738,7 @@ func _configure_cooler_fish_focus() -> void:
|
||||||
controls[mini(index + 1, controls.size() - 1)]
|
controls[mini(index + 1, controls.size() - 1)]
|
||||||
)
|
)
|
||||||
control.focus_neighbor_top = (
|
control.focus_neighbor_top = (
|
||||||
control.get_path_to(_cooler_sub_tab)
|
control.get_path_to(top_control)
|
||||||
if index < columns
|
if index < columns
|
||||||
else control.get_path_to(controls[index - columns])
|
else control.get_path_to(controls[index - columns])
|
||||||
)
|
)
|
||||||
|
|
@ -2776,9 +2908,10 @@ func _update_inventory_detail(fish_catch: FishCatchType) -> void:
|
||||||
_cooler_detail_texture.texture = fish_catch.fish.display_texture
|
_cooler_detail_texture.texture = fish_catch.fish.display_texture
|
||||||
_cooler_detail_texture.visible = fish_catch.fish.display_texture != null
|
_cooler_detail_texture.visible = fish_catch.fish.display_texture != null
|
||||||
_cooler_detail_name.text = fish_catch.fish.display_name
|
_cooler_detail_name.text = fish_catch.fish.display_name
|
||||||
|
var active_buyer: FishBuyerProfileType = _get_active_sale_buyer()
|
||||||
var individual_preview: FishSaleResultType = (
|
var individual_preview: FishSaleResultType = (
|
||||||
_sale_service.preview_batch([fish_catch.catch_id], _default_buyer)
|
_sale_service.preview_batch([fish_catch.catch_id], active_buyer)
|
||||||
if _sale_service != null
|
if _sale_service != null and active_buyer != null
|
||||||
else null
|
else null
|
||||||
)
|
)
|
||||||
var buyer_offer: int = (
|
var buyer_offer: int = (
|
||||||
|
|
@ -2793,8 +2926,8 @@ func _update_inventory_detail(fish_catch: FishCatchType) -> void:
|
||||||
):
|
):
|
||||||
_cooler_detail_name.text += " • reserved in mail"
|
_cooler_detail_name.text += " • reserved in mail"
|
||||||
_cooler_weight_unit.text = "lb • %s" % fish_catch.fish.get_rarity_name()
|
_cooler_weight_unit.text = "lb • %s" % fish_catch.fish.get_rarity_name()
|
||||||
if buyer_offer >= 0 and _default_buyer != null:
|
if buyer_offer >= 0 and active_buyer != null:
|
||||||
_cooler_offer_label.text = "%s offer" % _default_buyer.display_name
|
_cooler_offer_label.text = _get_offer_label(active_buyer)
|
||||||
_cooler_offer_value.text = "$%d" % buyer_offer
|
_cooler_offer_value.text = "$%d" % buyer_offer
|
||||||
else:
|
else:
|
||||||
_cooler_offer_label.text = "buyer unavailable"
|
_cooler_offer_label.text = "buyer unavailable"
|
||||||
|
|
@ -2831,6 +2964,11 @@ func _update_inventory_detail(fish_catch: FishCatchType) -> void:
|
||||||
|
|
||||||
|
|
||||||
func _update_sale_summary() -> void:
|
func _update_sale_summary() -> void:
|
||||||
|
var active_buyer: FishBuyerProfileType = _get_active_sale_buyer()
|
||||||
|
var buyer_id: StringName = (
|
||||||
|
active_buyer.id if active_buyer != null else StringName()
|
||||||
|
)
|
||||||
|
var offer_label: String = _get_offer_label(active_buyer)
|
||||||
var selected_ids: Array[StringName] = _fish_selection.get_selected_ids()
|
var selected_ids: Array[StringName] = _fish_selection.get_selected_ids()
|
||||||
var selected_count: int = selected_ids.size()
|
var selected_count: int = selected_ids.size()
|
||||||
_sell_button.text = (
|
_sell_button.text = (
|
||||||
|
|
@ -2842,7 +2980,7 @@ func _update_sale_summary() -> void:
|
||||||
if selected_count == 0:
|
if selected_count == 0:
|
||||||
_selection_summary.text = "no fish selected"
|
_selection_summary.text = "no fish selected"
|
||||||
_selection_status.set_content("selected", "none")
|
_selection_status.set_content("selected", "none")
|
||||||
_offer_status.set_content("pelican offer", "—")
|
_offer_status.set_content(offer_label, "—")
|
||||||
_sell_button.text = "sell fish"
|
_sell_button.text = "sell fish"
|
||||||
_sell_button.disabled = true
|
_sell_button.disabled = true
|
||||||
_sell_bubble.disabled = true
|
_sell_bubble.disabled = true
|
||||||
|
|
@ -2866,7 +3004,7 @@ func _update_sale_summary() -> void:
|
||||||
else "%d fish selected" % selected_count
|
else "%d fish selected" % selected_count
|
||||||
)
|
)
|
||||||
_selection_status.set_content("selected", str(selected_count))
|
_selection_status.set_content("selected", str(selected_count))
|
||||||
_offer_status.set_content("pelican offer", "pending")
|
_offer_status.set_content(offer_label, "pending")
|
||||||
_sell_button.disabled = true
|
_sell_button.disabled = true
|
||||||
_sell_bubble.disabled = true
|
_sell_bubble.disabled = true
|
||||||
_sale_unavailable.text = "Selling…"
|
_sale_unavailable.text = "Selling…"
|
||||||
|
|
@ -2874,7 +3012,8 @@ func _update_sale_summary() -> void:
|
||||||
return
|
return
|
||||||
if (
|
if (
|
||||||
_network_sale_service == null
|
_network_sale_service == null
|
||||||
or not _network_sale_service.can_request_sale()
|
or buyer_id.is_empty()
|
||||||
|
or not _network_sale_service.can_request_sale(buyer_id)
|
||||||
):
|
):
|
||||||
_selection_summary.text = (
|
_selection_summary.text = (
|
||||||
"1 fish selected"
|
"1 fish selected"
|
||||||
|
|
@ -2882,7 +3021,7 @@ func _update_sale_summary() -> void:
|
||||||
else "%d fish selected" % selected_count
|
else "%d fish selected" % selected_count
|
||||||
)
|
)
|
||||||
_selection_status.set_content("selected", str(selected_count))
|
_selection_status.set_content("selected", str(selected_count))
|
||||||
_offer_status.set_content("pelican offer", "unavailable")
|
_offer_status.set_content(offer_label, "unavailable")
|
||||||
_sell_button.disabled = true
|
_sell_button.disabled = true
|
||||||
_sell_bubble.disabled = true
|
_sell_bubble.disabled = true
|
||||||
_sell_bubble.persistent_mark = false
|
_sell_bubble.persistent_mark = false
|
||||||
|
|
@ -2893,8 +3032,8 @@ func _update_sale_summary() -> void:
|
||||||
_sale_unavailable.visible = true
|
_sale_unavailable.visible = true
|
||||||
return
|
return
|
||||||
var preview: FishSaleResultType = (
|
var preview: FishSaleResultType = (
|
||||||
_sale_service.preview_batch(selected_ids, _default_buyer)
|
_sale_service.preview_batch(selected_ids, active_buyer)
|
||||||
if _sale_service != null
|
if _sale_service != null and active_buyer != null
|
||||||
else null
|
else null
|
||||||
)
|
)
|
||||||
var count_text: String = (
|
var count_text: String = (
|
||||||
|
|
@ -2907,21 +3046,21 @@ func _update_sale_summary() -> void:
|
||||||
if (
|
if (
|
||||||
preview != null
|
preview != null
|
||||||
and preview.payout >= 0
|
and preview.payout >= 0
|
||||||
and _default_buyer != null
|
and active_buyer != null
|
||||||
and (
|
and (
|
||||||
preview.is_success()
|
preview.is_success()
|
||||||
or preview.status == FishSaleResultType.Status.FAVORITED
|
or preview.status == FishSaleResultType.Status.FAVORITED
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
_selection_summary.text += "\n%s total offer: $%d" % [
|
_selection_summary.text += "\n%s total offer: $%d" % [
|
||||||
_default_buyer.display_name,
|
active_buyer.display_name,
|
||||||
preview.payout,
|
preview.payout,
|
||||||
]
|
]
|
||||||
_set_cooler_selection_summary(selected_count, preview.payout)
|
_set_cooler_selection_summary(selected_count, preview.payout)
|
||||||
_offer_status.set_content("pelican offer", "$%d" % preview.payout)
|
_offer_status.set_content(offer_label, "$%d" % preview.payout)
|
||||||
else:
|
else:
|
||||||
_set_cooler_selection_summary(selected_count, -1)
|
_set_cooler_selection_summary(selected_count, -1)
|
||||||
_offer_status.set_content("pelican offer", "unavailable")
|
_offer_status.set_content(offer_label, "unavailable")
|
||||||
_sell_button.disabled = preview == null or not preview.is_success()
|
_sell_button.disabled = preview == null or not preview.is_success()
|
||||||
_sell_bubble.disabled = _sell_button.disabled
|
_sell_bubble.disabled = _sell_button.disabled
|
||||||
_sell_bubble.persistent_mark = false
|
_sell_bubble.persistent_mark = false
|
||||||
|
|
@ -2962,6 +3101,10 @@ func _on_favorite_pressed() -> void:
|
||||||
|
|
||||||
|
|
||||||
func _on_sell_pressed() -> void:
|
func _on_sell_pressed() -> void:
|
||||||
|
var active_buyer: FishBuyerProfileType = _get_active_sale_buyer()
|
||||||
|
var buyer_id: StringName = (
|
||||||
|
active_buyer.id if active_buyer != null else StringName()
|
||||||
|
)
|
||||||
if (
|
if (
|
||||||
_sale_in_progress
|
_sale_in_progress
|
||||||
or (
|
or (
|
||||||
|
|
@ -2973,7 +3116,8 @@ func _on_sell_pressed() -> void:
|
||||||
return
|
return
|
||||||
if (
|
if (
|
||||||
_network_sale_service == null
|
_network_sale_service == null
|
||||||
or not _network_sale_service.can_request_sale()
|
or buyer_id.is_empty()
|
||||||
|
or not _network_sale_service.can_request_sale(buyer_id)
|
||||||
):
|
):
|
||||||
_transaction_feedback.text = (
|
_transaction_feedback.text = (
|
||||||
"Selling is not supported by this server."
|
"Selling is not supported by this server."
|
||||||
|
|
@ -2983,13 +3127,13 @@ func _on_sell_pressed() -> void:
|
||||||
if (
|
if (
|
||||||
_inventory == null
|
_inventory == null
|
||||||
or _sale_service == null
|
or _sale_service == null
|
||||||
or _default_buyer == null
|
or active_buyer == null
|
||||||
or selected_ids.is_empty()
|
or selected_ids.is_empty()
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
var preview: FishSaleResultType = _sale_service.preview_batch(
|
var preview: FishSaleResultType = _sale_service.preview_batch(
|
||||||
selected_ids,
|
selected_ids,
|
||||||
_default_buyer
|
active_buyer
|
||||||
)
|
)
|
||||||
if not preview.is_success():
|
if not preview.is_success():
|
||||||
_transaction_feedback.text = (
|
_transaction_feedback.text = (
|
||||||
|
|
@ -3001,8 +3145,8 @@ func _on_sell_pressed() -> void:
|
||||||
_refresh_inventory()
|
_refresh_inventory()
|
||||||
return
|
return
|
||||||
_confirmation_catch_ids = selected_ids.duplicate()
|
_confirmation_catch_ids = selected_ids.duplicate()
|
||||||
_confirmation_buyer = _default_buyer
|
_confirmation_buyer = active_buyer
|
||||||
_confirmation_buyer_id = _default_buyer.id
|
_confirmation_buyer_id = active_buyer.id
|
||||||
_confirmation_generation = _menu_generation
|
_confirmation_generation = _menu_generation
|
||||||
var fish_label: String = "fish"
|
var fish_label: String = "fish"
|
||||||
_confirmation_message.text = (
|
_confirmation_message.text = (
|
||||||
|
|
@ -3010,12 +3154,14 @@ func _on_sell_pressed() -> void:
|
||||||
% [
|
% [
|
||||||
preview.fish_count,
|
preview.fish_count,
|
||||||
fish_label,
|
fish_label,
|
||||||
_get_buyer_display_group(_default_buyer),
|
_get_buyer_display_group(active_buyer),
|
||||||
preview.payout,
|
preview.payout,
|
||||||
preview.base_value,
|
preview.base_value,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
_sale_confirmation.visible = true
|
_sale_confirmation.visible = true
|
||||||
|
if _shop_cooler_context_active:
|
||||||
|
shop_cooler_modal_changed.emit(true)
|
||||||
_set_shell_interactive(false)
|
_set_shell_interactive(false)
|
||||||
for button: Button in [_confirm_sale_button, _cancel_sale_button]:
|
for button: Button in [_confirm_sale_button, _cancel_sale_button]:
|
||||||
button.focus_mode = Control.FOCUS_ALL
|
button.focus_mode = Control.FOCUS_ALL
|
||||||
|
|
@ -3028,7 +3174,9 @@ func _on_confirm_sale_pressed() -> void:
|
||||||
if (
|
if (
|
||||||
_sale_in_progress
|
_sale_in_progress
|
||||||
or _network_sale_service == null
|
or _network_sale_service == null
|
||||||
or not _network_sale_service.can_request_sale()
|
or not _network_sale_service.can_request_sale(
|
||||||
|
_confirmation_buyer_id
|
||||||
|
)
|
||||||
or _sale_service == null
|
or _sale_service == null
|
||||||
or _confirmation_catch_ids.is_empty()
|
or _confirmation_catch_ids.is_empty()
|
||||||
or _confirmation_buyer == null
|
or _confirmation_buyer == null
|
||||||
|
|
@ -3040,11 +3188,13 @@ func _on_confirm_sale_pressed() -> void:
|
||||||
var requested_catch_ids: Array[StringName] = (
|
var requested_catch_ids: Array[StringName] = (
|
||||||
_confirmation_catch_ids.duplicate()
|
_confirmation_catch_ids.duplicate()
|
||||||
)
|
)
|
||||||
|
var requested_buyer_id: StringName = _confirmation_buyer_id
|
||||||
var transaction_generation: int = _menu_generation
|
var transaction_generation: int = _menu_generation
|
||||||
_confirm_sale_button.disabled = true
|
_confirm_sale_button.disabled = true
|
||||||
_close_sale_confirmation()
|
_close_sale_confirmation()
|
||||||
var request_id: String = _network_sale_service.request_local_sale(
|
var request_id: String = _network_sale_service.request_local_sale(
|
||||||
requested_catch_ids
|
requested_catch_ids,
|
||||||
|
requested_buyer_id,
|
||||||
)
|
)
|
||||||
if request_id.is_empty():
|
if request_id.is_empty():
|
||||||
_sale_in_progress = false
|
_sale_in_progress = false
|
||||||
|
|
@ -3052,7 +3202,7 @@ func _on_confirm_sale_pressed() -> void:
|
||||||
if (
|
if (
|
||||||
_network_sale_service.is_local_sale_pending()
|
_network_sale_service.is_local_sale_pending()
|
||||||
and transaction_generation == _menu_generation
|
and transaction_generation == _menu_generation
|
||||||
and visible
|
and (visible or _shop_cooler_context_active)
|
||||||
):
|
):
|
||||||
_transaction_feedback.text = "Selling…"
|
_transaction_feedback.text = "Selling…"
|
||||||
|
|
||||||
|
|
@ -3066,7 +3216,7 @@ func _can_use_shared_world_actions() -> bool:
|
||||||
|
|
||||||
func _on_network_sale_pending(_request_id: String) -> void:
|
func _on_network_sale_pending(_request_id: String) -> void:
|
||||||
_sale_in_progress = true
|
_sale_in_progress = true
|
||||||
if visible:
|
if visible or _shop_cooler_context_active:
|
||||||
_transaction_feedback.text = "Selling…"
|
_transaction_feedback.text = "Selling…"
|
||||||
_update_sale_summary()
|
_update_sale_summary()
|
||||||
|
|
||||||
|
|
@ -3081,19 +3231,26 @@ func _on_network_sale_finished(
|
||||||
_sale_in_progress = false
|
_sale_in_progress = false
|
||||||
if accepted:
|
if accepted:
|
||||||
_fish_selection.remove_ids(catch_ids)
|
_fish_selection.remove_ids(catch_ids)
|
||||||
if not visible:
|
if not visible and not _shop_cooler_context_active:
|
||||||
return
|
return
|
||||||
_transaction_feedback.text = (
|
_refresh_all()
|
||||||
|
var feedback_message: String = (
|
||||||
"Sale complete. $%d received." % payout
|
"Sale complete. $%d received." % payout
|
||||||
if accepted
|
if accepted
|
||||||
else message
|
else message
|
||||||
)
|
)
|
||||||
_refresh_all()
|
_transaction_feedback.text = feedback_message
|
||||||
|
if not accepted:
|
||||||
|
_sale_unavailable.text = feedback_message
|
||||||
|
_sale_unavailable.visible = true
|
||||||
if _current_section == Section.COOLER:
|
if _current_section == Section.COOLER:
|
||||||
call_deferred("_restore_inventory_tab_focus")
|
call_deferred("_restore_inventory_tab_focus")
|
||||||
|
|
||||||
|
|
||||||
func _restore_inventory_tab_focus() -> void:
|
func _restore_inventory_tab_focus() -> void:
|
||||||
|
if _shop_cooler_context_active:
|
||||||
|
_focus_shop_cooler()
|
||||||
|
return
|
||||||
if (
|
if (
|
||||||
visible
|
visible
|
||||||
and _current_section == Section.COOLER
|
and _current_section == Section.COOLER
|
||||||
|
|
@ -3110,6 +3267,8 @@ func _close_sale_confirmation() -> void:
|
||||||
_confirmation_buyer_id = StringName()
|
_confirmation_buyer_id = StringName()
|
||||||
_confirmation_generation = -1
|
_confirmation_generation = -1
|
||||||
_sale_confirmation.visible = false
|
_sale_confirmation.visible = false
|
||||||
|
if was_visible and _shop_cooler_context_active:
|
||||||
|
shop_cooler_modal_changed.emit(false)
|
||||||
_confirmation_message.text = ""
|
_confirmation_message.text = ""
|
||||||
_confirm_sale_button.disabled = false
|
_confirm_sale_button.disabled = false
|
||||||
for button: Button in [_confirm_sale_button, _cancel_sale_button]:
|
for button: Button in [_confirm_sale_button, _cancel_sale_button]:
|
||||||
|
|
@ -3117,7 +3276,7 @@ func _close_sale_confirmation() -> void:
|
||||||
button.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
button.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
if (
|
if (
|
||||||
was_visible
|
was_visible
|
||||||
and visible
|
and (visible or _shop_cooler_context_active)
|
||||||
and not _transitioning
|
and not _transitioning
|
||||||
and not _page_transitioning
|
and not _page_transitioning
|
||||||
):
|
):
|
||||||
|
|
@ -3171,6 +3330,22 @@ func _get_buyer_display_group(
|
||||||
return "buyer"
|
return "buyer"
|
||||||
|
|
||||||
|
|
||||||
|
func _get_active_sale_buyer() -> FishBuyerProfileType:
|
||||||
|
if _sale_buyer_override != null and _sale_buyer_override.is_valid():
|
||||||
|
return _sale_buyer_override
|
||||||
|
return _default_buyer
|
||||||
|
|
||||||
|
|
||||||
|
func _get_offer_label(buyer: FishBuyerProfileType) -> String:
|
||||||
|
if buyer == null:
|
||||||
|
return "buyer offer"
|
||||||
|
if buyer.id == MAIN_SHOP_BUYER_ID:
|
||||||
|
return "shop offer"
|
||||||
|
if buyer.id == NetworkSaleService.PELICAN_BUYER_ID:
|
||||||
|
return "pelican offer"
|
||||||
|
return "buyer offer"
|
||||||
|
|
||||||
|
|
||||||
func _refresh_logbook() -> void:
|
func _refresh_logbook() -> void:
|
||||||
if not is_node_ready():
|
if not is_node_ready():
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -487,7 +487,7 @@ mouse_filter = 1
|
||||||
|
|
||||||
[node name="BagFilterTabs" type="HBoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/BagPage"]
|
[node name="BagFilterTabs" type="HBoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/BagPage"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
z_index = 5
|
z_index = 55
|
||||||
layout_mode = 0
|
layout_mode = 0
|
||||||
offset_left = 122.0
|
offset_left = 122.0
|
||||||
offset_top = 166.0
|
offset_top = 166.0
|
||||||
|
|
@ -1330,12 +1330,12 @@ columns = 4
|
||||||
[node name="SaleConfirmation" type="PanelContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot"]
|
[node name="SaleConfirmation" type="PanelContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
visible = false
|
visible = false
|
||||||
z_index = 40
|
z_index = 120
|
||||||
layout_mode = 0
|
layout_mode = 0
|
||||||
offset_left = 320.0
|
offset_left = 380.0
|
||||||
offset_top = 200.0
|
offset_top = 265.0
|
||||||
offset_right = 960.0
|
offset_right = 840.0
|
||||||
offset_bottom = 520.0
|
offset_bottom = 455.0
|
||||||
|
|
||||||
[node name="ConfirmationMargin" type="MarginContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation"]
|
[node name="ConfirmationMargin" type="MarginContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
@ -1347,29 +1347,31 @@ theme_override_constants/margin_bottom = 20
|
||||||
[node name="ConfirmationLayout" type="VBoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation/ConfirmationMargin"]
|
[node name="ConfirmationLayout" type="VBoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation/ConfirmationMargin"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/separation = 12
|
theme_override_constants/separation = 12
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
[node name="ConfirmationMessage" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation/ConfirmationMargin/ConfirmationLayout"]
|
[node name="ConfirmationMessage" type="Label" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation/ConfirmationMargin/ConfirmationLayout"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 22
|
||||||
autowrap_mode = 2
|
autowrap_mode = 2
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="ConfirmationButtons" type="HBoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation/ConfirmationMargin/ConfirmationLayout"]
|
[node name="ConfirmationButtons" type="HBoxContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation/ConfirmationMargin/ConfirmationLayout"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = 10
|
||||||
alignment = 1
|
alignment = 1
|
||||||
|
|
||||||
[node name="ConfirmSaleButton" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation/ConfirmationMargin/ConfirmationLayout/ConfirmationButtons"]
|
[node name="ConfirmSaleButton" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation/ConfirmationMargin/ConfirmationLayout/ConfirmationButtons"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "confirm"
|
text = "confirm"
|
||||||
theme_type_variation = &"DangerButton"
|
custom_minimum_size = Vector2(140, 44)
|
||||||
custom_minimum_size = Vector2(170, 64)
|
|
||||||
|
|
||||||
[node name="CancelSaleButton" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation/ConfirmationMargin/ConfirmationLayout/ConfirmationButtons"]
|
[node name="CancelSaleButton" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/SaleConfirmation/ConfirmationMargin/ConfirmationLayout/ConfirmationButtons"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "cancel"
|
text = "cancel"
|
||||||
custom_minimum_size = Vector2(170, 64)
|
custom_minimum_size = Vector2(140, 44)
|
||||||
|
|
||||||
[node name="NavigationCluster" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot"]
|
[node name="NavigationCluster" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ enum PresentationMode {
|
||||||
GAMEPLAY_MODAL,
|
GAMEPLAY_MODAL,
|
||||||
}
|
}
|
||||||
|
|
||||||
@onready var _backdrop: ColorRect = $Backdrop
|
|
||||||
@onready var _root_page: SettingsBubblePage = %RootPage
|
@onready var _root_page: SettingsBubblePage = %RootPage
|
||||||
@onready var _display_page: SettingsBubblePage = %DisplayPage
|
@onready var _display_page: SettingsBubblePage = %DisplayPage
|
||||||
@onready var _controls_page: SettingsBubblePage = %ControlsPage
|
@onready var _controls_page: SettingsBubblePage = %ControlsPage
|
||||||
|
|
@ -210,15 +209,10 @@ func open_panel(
|
||||||
settings_manager: SettingsManagerType,
|
settings_manager: SettingsManagerType,
|
||||||
presentation_mode: PresentationMode = PresentationMode.GAMEPLAY_MODAL,
|
presentation_mode: PresentationMode = PresentationMode.GAMEPLAY_MODAL,
|
||||||
animate_gameplay_host_transitions: bool = false,
|
animate_gameplay_host_transitions: bool = false,
|
||||||
uses_external_backdrop: bool = false,
|
|
||||||
) -> void:
|
) -> void:
|
||||||
_cancel_page_transition()
|
_cancel_page_transition()
|
||||||
_presentation_mode = presentation_mode
|
_presentation_mode = presentation_mode
|
||||||
_animate_gameplay_host_transitions = animate_gameplay_host_transitions
|
_animate_gameplay_host_transitions = animate_gameplay_host_transitions
|
||||||
_backdrop.visible = (
|
|
||||||
presentation_mode == PresentationMode.GAMEPLAY_MODAL
|
|
||||||
and not uses_external_backdrop
|
|
||||||
)
|
|
||||||
_settings_manager = settings_manager
|
_settings_manager = settings_manager
|
||||||
_load_controls()
|
_load_controls()
|
||||||
_feedback.text = ""
|
_feedback.text = ""
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,6 @@ anchors_preset = 0
|
||||||
theme = ExtResource("2_theme")
|
theme = ExtResource("2_theme")
|
||||||
script = ExtResource("1_panel")
|
script = ExtResource("1_panel")
|
||||||
|
|
||||||
[node name="Backdrop" type="ColorRect" parent="."]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
mouse_filter = 2
|
|
||||||
color = Color(0.025, 0.085, 0.115, 0.72)
|
|
||||||
|
|
||||||
[node name="SettingsFeedback" type="Label" parent="."]
|
[node name="SettingsFeedback" type="Label" parent="."]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
|
|
|
||||||
|
|
@ -1541,12 +1541,12 @@ func _spawn_decorative_fish(generation: int) -> void:
|
||||||
CROSSING_DURATION_MIN,
|
CROSSING_DURATION_MIN,
|
||||||
CROSSING_DURATION_MAX
|
CROSSING_DURATION_MAX
|
||||||
)
|
)
|
||||||
fish_control.position = Vector2(
|
var horizontal_bounds: Vector2 = get_decorative_fish_crossing_bounds(
|
||||||
-presentation_size.x - FISH_EDGE_MARGIN
|
layer_size.x,
|
||||||
if direction > 0.0
|
presentation_size.x,
|
||||||
else layer_size.x + FISH_EDGE_MARGIN,
|
direction,
|
||||||
base_y
|
|
||||||
)
|
)
|
||||||
|
fish_control.position = Vector2(horizontal_bounds.x, base_y)
|
||||||
_decorative_fish_layer.add_child(fish_control)
|
_decorative_fish_layer.add_child(fish_control)
|
||||||
_decorative_fish = fish_control
|
_decorative_fish = fish_control
|
||||||
_decorative_fish_tween = create_tween()
|
_decorative_fish_tween = create_tween()
|
||||||
|
|
@ -1586,17 +1586,14 @@ func _update_decorative_fish(
|
||||||
or not is_instance_valid(fish_control)
|
or not is_instance_valid(fish_control)
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
var start_x: float = (
|
var horizontal_bounds: Vector2 = get_decorative_fish_crossing_bounds(
|
||||||
-fish_control.size.x - FISH_EDGE_MARGIN
|
_decorative_fish_layer.size.x,
|
||||||
if direction > 0.0
|
fish_control.size.x,
|
||||||
else _decorative_fish_layer.size.x + FISH_EDGE_MARGIN
|
direction,
|
||||||
)
|
)
|
||||||
var end_x: float = (
|
fish_control.position.x = lerpf(
|
||||||
_decorative_fish_layer.size.x + FISH_EDGE_MARGIN
|
horizontal_bounds.x, horizontal_bounds.y, progress
|
||||||
if direction > 0.0
|
|
||||||
else -fish_control.size.x - FISH_EDGE_MARGIN
|
|
||||||
)
|
)
|
||||||
fish_control.position.x = lerpf(start_x, end_x, progress)
|
|
||||||
var bobbed_y: float = (
|
var bobbed_y: float = (
|
||||||
base_y
|
base_y
|
||||||
+ sin(progress * crossing_duration * bob_speed) * bob_amplitude
|
+ sin(progress * crossing_duration * bob_speed) * bob_amplitude
|
||||||
|
|
@ -1609,6 +1606,20 @@ func _update_decorative_fish(
|
||||||
fish_control.position = _snap_world_preview(fish_control.position)
|
fish_control.position = _snap_world_preview(fish_control.position)
|
||||||
|
|
||||||
|
|
||||||
|
static func get_decorative_fish_crossing_bounds(
|
||||||
|
layer_width: float,
|
||||||
|
fish_width: float,
|
||||||
|
direction: float,
|
||||||
|
) -> Vector2:
|
||||||
|
var outside_left: float = -fish_width - FISH_EDGE_MARGIN
|
||||||
|
var outside_right: float = layer_width + FISH_EDGE_MARGIN
|
||||||
|
return (
|
||||||
|
Vector2(outside_left, outside_right)
|
||||||
|
if direction > 0.0
|
||||||
|
else Vector2(outside_right, outside_left)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _on_decorative_fish_finished(
|
func _on_decorative_fish_finished(
|
||||||
fish_control: TextureRect,
|
fish_control: TextureRect,
|
||||||
generation: int,
|
generation: int,
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,13 @@ signal effective_pixel_size_changed(
|
||||||
|
|
||||||
@onready var _ui_viewport: SubViewport = $UIViewport
|
@onready var _ui_viewport: SubViewport = $UIViewport
|
||||||
@onready var _ui_root: Control = $UIViewport/GameUI/UIRoot
|
@onready var _ui_root: Control = $UIViewport/GameUI/UIRoot
|
||||||
|
@onready var _canonical_stage: Control = (
|
||||||
|
$UIViewport/GameUI/UIRoot/CanonicalStage
|
||||||
|
)
|
||||||
|
@onready var _chat_ui: ChatUI = $UIViewport/GameUI/UIRoot/ChatUI
|
||||||
|
@onready var _title_content_stage: Control = (
|
||||||
|
$UIViewport/GameUI/UIRoot/TitleScreen/ResponsiveTitleStage
|
||||||
|
)
|
||||||
|
|
||||||
var _requested_pixel_size: int = PlayerSettings.DEFAULT_UI_PIXEL_SIZE
|
var _requested_pixel_size: int = PlayerSettings.DEFAULT_UI_PIXEL_SIZE
|
||||||
var _effective_pixel_size: int = PlayerSettings.DEFAULT_UI_PIXEL_SIZE
|
var _effective_pixel_size: int = PlayerSettings.DEFAULT_UI_PIXEL_SIZE
|
||||||
|
|
@ -94,21 +101,29 @@ func _resize_presentation() -> void:
|
||||||
float(render_height)
|
float(render_height)
|
||||||
/ UIReferencePresentationType.REFERENCE_SIZE.y
|
/ UIReferencePresentationType.REFERENCE_SIZE.y
|
||||||
)
|
)
|
||||||
|
var visible_reference_size: Vector2 = (
|
||||||
|
UIReferencePresentationType.get_visible_reference_size(display_size)
|
||||||
|
)
|
||||||
var viewport_size := Vector2i(
|
var viewport_size := Vector2i(
|
||||||
roundi(
|
roundi(visible_reference_size.x * render_scale),
|
||||||
UIReferencePresentationType.REFERENCE_SIZE.x
|
roundi(visible_reference_size.y * render_scale),
|
||||||
* render_scale
|
|
||||||
),
|
|
||||||
render_height,
|
|
||||||
)
|
)
|
||||||
set_anchors_preset(Control.PRESET_TOP_LEFT)
|
set_anchors_preset(Control.PRESET_TOP_LEFT)
|
||||||
position = UIReferencePresentationType.get_offset(display_size)
|
position = Vector2.ZERO
|
||||||
size = Vector2(viewport_size)
|
size = Vector2(viewport_size)
|
||||||
scale = Vector2.ONE * (reference_scale / render_scale)
|
scale = Vector2.ONE * (reference_scale / render_scale)
|
||||||
stretch_shrink = 1
|
stretch_shrink = 1
|
||||||
_ui_root.position = Vector2.ZERO
|
_ui_root.position = Vector2.ZERO
|
||||||
_ui_root.scale = Vector2.ONE * render_scale
|
_ui_root.scale = Vector2.ONE * render_scale
|
||||||
_ui_root.size = UIReferencePresentationType.REFERENCE_SIZE
|
_ui_root.size = visible_reference_size
|
||||||
|
_canonical_stage.position = (
|
||||||
|
UIReferencePresentationType.get_stage_position(display_size)
|
||||||
|
)
|
||||||
|
_canonical_stage.size = UIReferencePresentationType.REFERENCE_SIZE
|
||||||
|
_title_content_stage.set_anchors_preset(Control.PRESET_TOP_LEFT)
|
||||||
|
_title_content_stage.position = _canonical_stage.position
|
||||||
|
_title_content_stage.size = UIReferencePresentationType.REFERENCE_SIZE
|
||||||
|
_chat_ui.set_output_scale(reference_scale)
|
||||||
if (
|
if (
|
||||||
previous_effective_size != _effective_pixel_size
|
previous_effective_size != _effective_pixel_size
|
||||||
or _requested_pixel_size != _effective_pixel_size
|
or _requested_pixel_size != _effective_pixel_size
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,15 @@
|
||||||
class_name UIReferencePresentation
|
class_name UIReferencePresentation
|
||||||
extends RefCounted
|
extends RefCounted
|
||||||
|
|
||||||
|
# All functional UI is authored in this coordinate system and receives one
|
||||||
|
# centered, uniform transform at presentation time. Texture source dimensions
|
||||||
|
# describe sampling quality and aspect ratio; a canonical Control rect owns
|
||||||
|
# every texture's on-screen layout size.
|
||||||
|
#
|
||||||
|
# Edge-docked overlays use the same canonical units and uniform scale, but may
|
||||||
|
# use the extra logical canvas outside the centered stage on non-16:9 windows.
|
||||||
|
# This keeps deliberate window-edge attachment without letting individual
|
||||||
|
# controls calculate monitor- or DPI-specific geometry.
|
||||||
const REFERENCE_SIZE: Vector2 = Vector2(1280.0, 720.0)
|
const REFERENCE_SIZE: Vector2 = Vector2(1280.0, 720.0)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -18,6 +27,14 @@ static func get_offset(display_size: Vector2) -> Vector2:
|
||||||
) * 0.5
|
) * 0.5
|
||||||
|
|
||||||
|
|
||||||
|
static func get_visible_reference_size(display_size: Vector2) -> Vector2:
|
||||||
|
return display_size / get_scale(display_size)
|
||||||
|
|
||||||
|
|
||||||
|
static func get_stage_position(display_size: Vector2) -> Vector2:
|
||||||
|
return (get_visible_reference_size(display_size) - REFERENCE_SIZE) * 0.5
|
||||||
|
|
||||||
|
|
||||||
static func get_rect(display_size: Vector2) -> Rect2:
|
static func get_rect(display_size: Vector2) -> Rect2:
|
||||||
var presentation_scale: float = get_scale(display_size)
|
var presentation_scale: float = get_scale(display_size)
|
||||||
return Rect2(
|
return Rect2(
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ no_depth_test = true
|
||||||
|
|
||||||
[node name="ConvenienceLabel" type="Label3D" parent="Visual"]
|
[node name="ConvenienceLabel" type="Label3D" parent="Visual"]
|
||||||
position = Vector3(0, 1.65, -1.15)
|
position = Vector3(0, 1.65, -1.15)
|
||||||
text = "sell nearby • 0.25x"
|
text = "sell anytime • 0.25x"
|
||||||
font_size = 20
|
font_size = 20
|
||||||
outline_size = 6
|
outline_size = 6
|
||||||
billboard = 1
|
billboard = 1
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue