Add portable RV homes and decor prototype
This commit is contained in:
parent
81decf2b71
commit
fe099f47dd
56 changed files with 5459 additions and 112 deletions
323
ui/decor_shop.gd
Normal file
323
ui/decor_shop.gd
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
class_name DecorShop
|
||||
extends Control
|
||||
|
||||
const INPUT_OWNER: StringName = &"decor_shop"
|
||||
const DecorCatalogType = preload("res://homes/decor_catalog.gd")
|
||||
const UtilityPageStyleType = preload("res://ui/utility_page_style.gd")
|
||||
|
||||
signal menu_visibility_changed(is_open: bool)
|
||||
signal menu_exit_started
|
||||
|
||||
var _player: Player
|
||||
var _wallet: PlayerWallet
|
||||
var _fishing_spot: FishingSpot
|
||||
var _interaction: DecorShopInteraction
|
||||
var _bag: PlayerBag
|
||||
var _home_state: PlayerHomeState
|
||||
var _network_shop: NetworkShopService
|
||||
var _wallet_label: Label
|
||||
var _owned_label: Label
|
||||
var _feedback: Label
|
||||
var _buy_button: Button
|
||||
var _close_button: Button
|
||||
var _transaction_pending: bool = false
|
||||
var _prior_movement_enabled: bool = true
|
||||
var _prior_camera_enabled: bool = true
|
||||
var _prior_mouse_mode: Input.MouseMode = Input.MOUSE_MODE_VISIBLE
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
UtilityPageStyleType.apply_page(self)
|
||||
_build_ui()
|
||||
hide()
|
||||
|
||||
|
||||
func setup(
|
||||
player: Player,
|
||||
wallet: PlayerWallet,
|
||||
fishing_spot: FishingSpot,
|
||||
interaction: DecorShopInteraction,
|
||||
bag: PlayerBag,
|
||||
home_state: PlayerHomeState,
|
||||
network_shop: NetworkShopService,
|
||||
) -> void:
|
||||
_player = player
|
||||
_wallet = wallet
|
||||
_fishing_spot = fishing_spot
|
||||
_interaction = interaction
|
||||
_bag = bag
|
||||
_home_state = home_state
|
||||
_network_shop = network_shop
|
||||
if not _wallet.balance_changed.is_connected(_on_wallet_changed):
|
||||
_wallet.balance_changed.connect(_on_wallet_changed)
|
||||
if not _bag.contents_changed.is_connected(_refresh):
|
||||
_bag.contents_changed.connect(_refresh)
|
||||
if not _home_state.changed.is_connected(_refresh):
|
||||
_home_state.changed.connect(_refresh)
|
||||
if (
|
||||
_network_shop != null
|
||||
and not _network_shop.local_purchase_pending.is_connected(
|
||||
_on_purchase_pending
|
||||
)
|
||||
):
|
||||
_network_shop.local_purchase_pending.connect(_on_purchase_pending)
|
||||
_network_shop.local_purchase_finished.connect(_on_purchase_finished)
|
||||
_refresh()
|
||||
|
||||
|
||||
func set_world_interaction(interaction: DecorShopInteraction) -> void:
|
||||
_interaction = interaction
|
||||
|
||||
|
||||
func open_shop() -> bool:
|
||||
if (
|
||||
visible
|
||||
or _player == null
|
||||
or _wallet == null
|
||||
or _fishing_spot == null
|
||||
or _interaction == null
|
||||
or not _interaction.is_local_player_in_range()
|
||||
or not _fishing_spot.can_open_fishing_shop()
|
||||
):
|
||||
return false
|
||||
_prior_movement_enabled = _player.is_movement_enabled()
|
||||
_prior_camera_enabled = _player.is_camera_input_enabled()
|
||||
_prior_mouse_mode = Input.mouse_mode
|
||||
_player.set_movement_enabled(false)
|
||||
_player.set_camera_input_enabled(false)
|
||||
_fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, true)
|
||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||
_transaction_pending = (
|
||||
_network_shop != null
|
||||
and _network_shop.is_local_purchase_pending()
|
||||
)
|
||||
_feedback.text = ""
|
||||
_refresh()
|
||||
show()
|
||||
_buy_button.call_deferred("grab_focus")
|
||||
menu_visibility_changed.emit(true)
|
||||
return true
|
||||
|
||||
|
||||
func close_shop(restore_controls: bool = true) -> void:
|
||||
if not visible:
|
||||
return
|
||||
menu_exit_started.emit()
|
||||
get_viewport().gui_release_focus()
|
||||
hide()
|
||||
if _fishing_spot != null:
|
||||
_fishing_spot.set_local_menu_input_suppressed(INPUT_OWNER, false)
|
||||
if restore_controls and _player != null:
|
||||
_player.set_movement_enabled(_prior_movement_enabled)
|
||||
_player.set_camera_input_enabled(_prior_camera_enabled)
|
||||
Input.mouse_mode = _prior_mouse_mode
|
||||
menu_visibility_changed.emit(false)
|
||||
|
||||
|
||||
func consume_escape() -> bool:
|
||||
if not visible:
|
||||
return false
|
||||
close_shop()
|
||||
return true
|
||||
|
||||
|
||||
func close_for_range_exit() -> void:
|
||||
close_shop()
|
||||
|
||||
|
||||
func close_for_water_recovery() -> void:
|
||||
close_shop(false)
|
||||
|
||||
|
||||
func close_for_session_end() -> void:
|
||||
close_shop(false)
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if not visible or not event.is_action_pressed("ui_cancel"):
|
||||
return
|
||||
close_shop()
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func _purchase_cube() -> void:
|
||||
if _network_shop == null or _transaction_pending:
|
||||
return
|
||||
var request_id: String = _network_shop.request_decor(
|
||||
DecorCatalogType.BASIC_CUBE_ID
|
||||
)
|
||||
if request_id.is_empty():
|
||||
_refresh()
|
||||
|
||||
|
||||
func _on_purchase_pending(_request_id: String) -> void:
|
||||
_transaction_pending = true
|
||||
_feedback.text = "purchasing…"
|
||||
_refresh()
|
||||
|
||||
|
||||
func _on_purchase_finished(
|
||||
_request_id: String,
|
||||
accepted: bool,
|
||||
message: String,
|
||||
product_id: StringName,
|
||||
category: int,
|
||||
_quantity: int,
|
||||
total_cost: int,
|
||||
) -> void:
|
||||
if (
|
||||
category != NetworkShopProtocol.ProductCategory.DECOR
|
||||
or product_id != DecorCatalogType.BASIC_CUBE_ID
|
||||
):
|
||||
return
|
||||
_transaction_pending = false
|
||||
_feedback.text = (
|
||||
"basic cube purchased · %d fishcoin spent" % total_cost
|
||||
if accepted
|
||||
else message.to_lower()
|
||||
)
|
||||
_refresh()
|
||||
|
||||
|
||||
func _on_wallet_changed(_balance: int, _delta: int) -> void:
|
||||
_refresh()
|
||||
|
||||
|
||||
func _refresh() -> void:
|
||||
if _wallet_label == null:
|
||||
return
|
||||
var balance: int = _wallet.get_balance() if _wallet != null else 0
|
||||
var owned: int = (
|
||||
_home_state.get_owned_count(DecorCatalogType.BASIC_CUBE_ID)
|
||||
if _home_state != null else 0
|
||||
)
|
||||
var carried: int = (
|
||||
_bag.get_quantity(DecorCatalogType.BASIC_CUBE_ID)
|
||||
if _bag != null else 0
|
||||
)
|
||||
_wallet_label.text = "%d fishcoins" % balance
|
||||
_owned_label.text = "owned %d · packed %d" % [owned, carried]
|
||||
_buy_button.disabled = (
|
||||
_transaction_pending
|
||||
or _wallet == null
|
||||
or not _wallet.can_afford(
|
||||
DecorCatalogType.get_price(DecorCatalogType.BASIC_CUBE_ID)
|
||||
)
|
||||
or _bag == null
|
||||
or not _bag.can_add_item(DecorCatalogType.BASIC_CUBE_ID, 1)
|
||||
or owned >= DecorCatalogType.MAX_OWNED_PER_PRODUCT
|
||||
)
|
||||
|
||||
|
||||
func _build_ui() -> void:
|
||||
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
z_index = 83
|
||||
mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
var blocker := ColorRect.new()
|
||||
blocker.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
blocker.color = Color(0.02, 0.08, 0.10, 0.55)
|
||||
blocker.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
add_child(blocker)
|
||||
|
||||
var panel := PanelContainer.new()
|
||||
panel.set_anchors_preset(Control.PRESET_CENTER)
|
||||
panel.position = Vector2(-440.0, -250.0)
|
||||
panel.size = Vector2(880.0, 500.0)
|
||||
panel.add_theme_stylebox_override(
|
||||
"panel",
|
||||
UtilityPageStyleType.rounded_style(
|
||||
UtilityPageStyleType.OCEAN_PANEL_DEEP, 24
|
||||
),
|
||||
)
|
||||
add_child(panel)
|
||||
var margin := MarginContainer.new()
|
||||
for side: StringName in [
|
||||
&"margin_left", &"margin_top", &"margin_right", &"margin_bottom",
|
||||
]:
|
||||
margin.add_theme_constant_override(side, 28)
|
||||
panel.add_child(margin)
|
||||
var layout := VBoxContainer.new()
|
||||
layout.add_theme_constant_override("separation", 18)
|
||||
margin.add_child(layout)
|
||||
|
||||
var header := HBoxContainer.new()
|
||||
header.add_theme_constant_override("separation", 12)
|
||||
layout.add_child(header)
|
||||
var title := Label.new()
|
||||
title.text = "decor"
|
||||
title.add_theme_font_size_override("font_size", 38)
|
||||
title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
header.add_child(title)
|
||||
_wallet_label = Label.new()
|
||||
_wallet_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||
_wallet_label.add_theme_font_size_override("font_size", 22)
|
||||
header.add_child(_wallet_label)
|
||||
|
||||
var item_panel := PanelContainer.new()
|
||||
item_panel.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
item_panel.add_theme_stylebox_override(
|
||||
"panel",
|
||||
UtilityPageStyleType.rounded_style(
|
||||
UtilityPageStyleType.OCEAN_FIELD, 18
|
||||
),
|
||||
)
|
||||
layout.add_child(item_panel)
|
||||
var item_margin := MarginContainer.new()
|
||||
for side: StringName in [
|
||||
&"margin_left", &"margin_top", &"margin_right", &"margin_bottom",
|
||||
]:
|
||||
item_margin.add_theme_constant_override(side, 24)
|
||||
item_panel.add_child(item_margin)
|
||||
var item_row := HBoxContainer.new()
|
||||
item_row.add_theme_constant_override("separation", 28)
|
||||
item_margin.add_child(item_row)
|
||||
var preview := Panel.new()
|
||||
preview.custom_minimum_size = Vector2(150.0, 150.0)
|
||||
preview.add_theme_stylebox_override(
|
||||
"panel",
|
||||
UtilityPageStyleType.rounded_style(Color("b77a55"), 12),
|
||||
)
|
||||
item_row.add_child(preview)
|
||||
var details := VBoxContainer.new()
|
||||
details.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
details.add_theme_constant_override("separation", 10)
|
||||
item_row.add_child(details)
|
||||
var item_name := Label.new()
|
||||
item_name.text = DecorCatalogType.get_display_name(
|
||||
DecorCatalogType.BASIC_CUBE_ID
|
||||
)
|
||||
item_name.add_theme_font_size_override("font_size", 32)
|
||||
details.add_child(item_name)
|
||||
var description := Label.new()
|
||||
description.text = DecorCatalogType.get_description(
|
||||
DecorCatalogType.BASIC_CUBE_ID
|
||||
)
|
||||
description.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
description.add_theme_font_size_override("font_size", 20)
|
||||
description.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
details.add_child(description)
|
||||
_owned_label = Label.new()
|
||||
_owned_label.add_theme_font_size_override("font_size", 18)
|
||||
details.add_child(_owned_label)
|
||||
_buy_button = Button.new()
|
||||
_buy_button.text = "buy · 1 fishcoin"
|
||||
_buy_button.custom_minimum_size = Vector2(260.0, 58.0)
|
||||
UtilityPageStyleType.apply_ocean_button(_buy_button)
|
||||
_buy_button.pressed.connect(_purchase_cube)
|
||||
details.add_child(_buy_button)
|
||||
|
||||
var footer := HBoxContainer.new()
|
||||
footer.add_theme_constant_override("separation", 12)
|
||||
layout.add_child(footer)
|
||||
_feedback = Label.new()
|
||||
_feedback.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_feedback.add_theme_font_size_override("font_size", 18)
|
||||
footer.add_child(_feedback)
|
||||
_close_button = Button.new()
|
||||
_close_button.text = "close"
|
||||
_close_button.custom_minimum_size = Vector2(150.0, 50.0)
|
||||
UtilityPageStyleType.apply_ocean_button(_close_button)
|
||||
_close_button.pressed.connect(close_shop)
|
||||
footer.add_child(_close_button)
|
||||
_buy_button.focus_neighbor_bottom = _buy_button.get_path_to(_close_button)
|
||||
_close_button.focus_neighbor_top = _close_button.get_path_to(_buy_button)
|
||||
1
ui/decor_shop.gd.uid
Normal file
1
ui/decor_shop.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://810ck3mvbn5v
|
||||
14
ui/decor_shop.tscn
Normal file
14
ui/decor_shop.tscn
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/decor_shop.gd" id="1_script"]
|
||||
|
||||
[node name="DecorShop" type="Control"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_script")
|
||||
|
|
@ -20,6 +20,7 @@ const TitleScreenType = preload("res://ui/title_screen.gd")
|
|||
const PauseMenuType = preload("res://ui/pause_menu.gd")
|
||||
const NetworkSessionType = preload("res://network/network_session.gd")
|
||||
const FishingShopType = preload("res://ui/fishing_shop.gd")
|
||||
const DecorShopType = preload("res://ui/decor_shop.gd")
|
||||
const SettingsPanelType = preload("res://ui/settings_panel.gd")
|
||||
const PlayerFishingUpgradesType = preload(
|
||||
"res://progression/player_fishing_upgrades.gd"
|
||||
|
|
@ -27,7 +28,11 @@ const PlayerFishingUpgradesType = preload(
|
|||
const ShopInteractionType = preload(
|
||||
"res://world/fishing_shop_interaction.gd"
|
||||
)
|
||||
const DecorShopInteractionType = preload(
|
||||
"res://world/decor_shop_interaction.gd"
|
||||
)
|
||||
const PlayerStorageType = preload("res://ui/player_storage.gd")
|
||||
const HomeDecorToolbarType = preload("res://ui/home_decor_toolbar.gd")
|
||||
const PlayerStorageInteractionType = preload(
|
||||
"res://world/player_storage_interaction.gd"
|
||||
)
|
||||
|
|
@ -71,6 +76,9 @@ const MAIN_BUBBLE_PROFILE: BubbleMenuProfile = preload(
|
|||
const ACTIVE_BAIT_EMPTY_ICON: Texture2D = preload(
|
||||
"res://ui/icons/pictograms/x_dark.png"
|
||||
)
|
||||
const RV_STORAGE_ICON: Texture2D = preload(
|
||||
"res://ui/icons/tab_menu/inventory_simple.png"
|
||||
)
|
||||
|
||||
signal pixelation_settings_visibility_changed(is_visible: bool)
|
||||
signal crisp_reset_focus_requested
|
||||
|
|
@ -82,6 +90,7 @@ signal virtual_pointer_mode_changed(is_active: bool)
|
|||
signal social_prompt_accepted
|
||||
signal social_prompt_declined
|
||||
signal data_root_changed
|
||||
signal rv_storage_requested
|
||||
|
||||
const VIRTUAL_MOUSE_INPUT_OWNER: StringName = &"controller_virtual_mouse"
|
||||
const EMOTE_RADIAL_CAMERA_OWNER: StringName = &"emote_radial_menu"
|
||||
|
|
@ -145,7 +154,10 @@ const SHOP_NPC_SPEECH_COOLDOWN_MILLISECONDS: int = 5000
|
|||
@onready var _social_prompt: BubbleConfirmationPage = %SocialPrompt
|
||||
@onready var _hotbar_ui: HotbarUIType = %Hotbar
|
||||
@onready var _fishing_shop: FishingShopType = %FishingShop
|
||||
@onready var _decor_shop: DecorShopType = %DecorShop
|
||||
@onready var _player_storage: PlayerStorageType = %PlayerStorage
|
||||
@onready var _home_decor_toolbar: HomeDecorToolbarType = %HomeDecorToolbar
|
||||
@onready var _rv_storage_button: Button = %RVStorageButton
|
||||
@onready var _storage_prompt: PanelContainer = %StoragePrompt
|
||||
@onready var _storage_prompt_message: Label = %StoragePromptMessage
|
||||
@onready var _shop_prompt: Control = %ShopPrompt
|
||||
|
|
@ -195,6 +207,7 @@ var _chat_input_open: bool = false
|
|||
var _player_menu_hotbar_visible: bool = false
|
||||
var _main_shop_buyer: FishBuyerProfileType
|
||||
var _shop_interaction: ShopInteractionType
|
||||
var _decor_shop_interaction: DecorShopInteractionType
|
||||
var _storage_interaction: PlayerStorageInteractionType
|
||||
var _surface_drawing: NetworkSurfaceDrawingService
|
||||
var _surface_drawing_hotbar_selected: bool = false
|
||||
|
|
@ -221,14 +234,17 @@ var _controller_text_entry_request: Callable
|
|||
var _controller_text_entry_is_open: Callable
|
||||
var _controller_text_entry_close: Callable
|
||||
var _restore_chat_keyboard_after_fishing: bool = false
|
||||
var _rv_storage_button_requested_visible: bool = false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_prioritize_surface_drawing_pointer_input()
|
||||
_bite_prompt_button.pressed.connect(_on_bite_prompt_pressed)
|
||||
_rv_storage_button.pressed.connect(_on_rv_storage_button_pressed)
|
||||
_social_prompt.confirmed.connect(_accept_social_prompt)
|
||||
_social_prompt.cancelled.connect(_decline_social_prompt)
|
||||
_apply_active_bait_indicator_style()
|
||||
_apply_rv_storage_button_style()
|
||||
_refresh_active_bait_indicator()
|
||||
_refresh_interaction_prompt_bindings()
|
||||
# Reward feedback must remain above full-screen canonical menus. Keeping the
|
||||
|
|
@ -346,11 +362,13 @@ func setup(
|
|||
fishing_upgrades: PlayerFishingUpgradesType,
|
||||
shop_interaction: ShopInteractionType,
|
||||
storage_interaction: PlayerStorageInteractionType,
|
||||
decor_shop_interaction: DecorShopInteractionType,
|
||||
item_effects: PlayerItemEffectsType,
|
||||
cooler_capacity: PlayerCoolerCapacityType,
|
||||
network_session: NetworkSessionType,
|
||||
network_sale_service: NetworkSaleService,
|
||||
network_shop_service: NetworkShopService,
|
||||
home_state: PlayerHomeState,
|
||||
network_chat_service: NetworkChatService,
|
||||
network_profile: NetworkProfilePreferences,
|
||||
spawn_service: PlayerSpawnService,
|
||||
|
|
@ -494,6 +512,15 @@ func setup(
|
|||
network_sale_service,
|
||||
reservations,
|
||||
)
|
||||
_decor_shop.setup(
|
||||
player,
|
||||
wallet,
|
||||
fishing_spot,
|
||||
decor_shop_interaction,
|
||||
bag,
|
||||
home_state,
|
||||
network_shop_service,
|
||||
)
|
||||
_player_storage.setup(
|
||||
player,
|
||||
fishing_spot,
|
||||
|
|
@ -509,6 +536,8 @@ func setup(
|
|||
)
|
||||
_fishing_shop.menu_visibility_changed.connect(_on_shop_visibility_changed)
|
||||
_fishing_shop.menu_exit_started.connect(_on_shop_exit_started)
|
||||
_decor_shop.menu_visibility_changed.connect(_on_shop_visibility_changed)
|
||||
_decor_shop.menu_exit_started.connect(_on_shop_exit_started)
|
||||
_fishing_shop.sell_fish_requested.connect(_on_shop_sell_fish_requested)
|
||||
_fishing_shop.shop_cooler_return_requested.connect(
|
||||
_on_shop_cooler_return_requested
|
||||
|
|
@ -518,6 +547,7 @@ func setup(
|
|||
)
|
||||
_main_shop_buyer = main_shop_buyer
|
||||
_shop_interaction = shop_interaction
|
||||
_decor_shop_interaction = decor_shop_interaction
|
||||
_storage_interaction = storage_interaction
|
||||
_surface_drawing = surface_drawing
|
||||
if (
|
||||
|
|
@ -540,11 +570,14 @@ func setup(
|
|||
func set_world_interactions(
|
||||
shop_interaction: ShopInteractionType,
|
||||
storage_interaction: PlayerStorageInteractionType,
|
||||
decor_shop_interaction: DecorShopInteractionType,
|
||||
) -> void:
|
||||
_shop_interaction = shop_interaction
|
||||
_storage_interaction = storage_interaction
|
||||
_decor_shop_interaction = decor_shop_interaction
|
||||
_fishing_shop.set_world_interaction(shop_interaction)
|
||||
_player_storage.set_world_interaction(storage_interaction)
|
||||
_decor_shop.set_world_interaction(decor_shop_interaction)
|
||||
|
||||
|
||||
func _prioritize_surface_drawing_pointer_input() -> void:
|
||||
|
|
@ -1655,6 +1688,34 @@ func _apply_active_bait_indicator_style() -> void:
|
|||
_active_bait_button.add_theme_color_override(state, Color.WHITE)
|
||||
|
||||
|
||||
func _apply_rv_storage_button_style() -> void:
|
||||
var normal_style: StyleBoxFlat = MAIN_BUBBLE_PROFILE.make_normal_style()
|
||||
var hover_style: StyleBoxFlat = MAIN_BUBBLE_PROFILE.make_hover_style()
|
||||
var pressed_style: StyleBoxFlat = MAIN_BUBBLE_PROFILE.make_pressed_style()
|
||||
for style: StyleBoxFlat in [normal_style, hover_style, pressed_style]:
|
||||
style.set_corner_radius_all(29)
|
||||
style.content_margin_left = 9.0
|
||||
style.content_margin_top = 9.0
|
||||
style.content_margin_right = 9.0
|
||||
style.content_margin_bottom = 9.0
|
||||
_rv_storage_button.icon = RV_STORAGE_ICON
|
||||
_rv_storage_button.add_theme_stylebox_override("normal", normal_style)
|
||||
_rv_storage_button.add_theme_stylebox_override("hover", hover_style)
|
||||
_rv_storage_button.add_theme_stylebox_override("focus", hover_style)
|
||||
_rv_storage_button.add_theme_stylebox_override("pressed", pressed_style)
|
||||
for state: StringName in [
|
||||
&"icon_normal_color",
|
||||
&"icon_hover_color",
|
||||
&"icon_focus_color",
|
||||
&"icon_pressed_color",
|
||||
]:
|
||||
_rv_storage_button.add_theme_color_override(state, Color.WHITE)
|
||||
|
||||
|
||||
func _on_rv_storage_button_pressed() -> void:
|
||||
rv_storage_requested.emit()
|
||||
|
||||
|
||||
func _refresh_active_bait_indicator() -> void:
|
||||
_refresh_active_bait_indicator_visibility()
|
||||
if not is_node_ready():
|
||||
|
|
@ -1718,17 +1779,20 @@ func set_gameplay_ui_enabled(enabled: bool) -> void:
|
|||
if not enabled:
|
||||
_gameplay_hud_hidden = false
|
||||
_refresh_active_bait_indicator_visibility()
|
||||
_refresh_rv_storage_button_visibility()
|
||||
if enabled:
|
||||
_try_start_shop_npc_speech()
|
||||
_refresh_gameplay_hud_visibility()
|
||||
_refresh_chat_availability()
|
||||
if not enabled:
|
||||
_home_decor_toolbar.set_active(false)
|
||||
_end_virtual_mouse()
|
||||
_close_radial_menus()
|
||||
if _surface_drawing != null:
|
||||
_surface_drawing.deactivate()
|
||||
close_player_menu_for_session_end()
|
||||
_fishing_shop.close_for_session_end()
|
||||
_decor_shop.close_for_session_end()
|
||||
_player_storage.close_for_session_end()
|
||||
_fishing_panel.visible = false
|
||||
_shop_prompt.hide()
|
||||
|
|
@ -1749,6 +1813,7 @@ func set_gameplay_hud_hidden(hidden: bool) -> void:
|
|||
_quick_radial_menu.set_hud_hidden(hidden)
|
||||
_refresh_gameplay_hud_visibility()
|
||||
_refresh_active_bait_indicator_visibility()
|
||||
_refresh_rv_storage_button_visibility()
|
||||
_refresh_hotbar_visibility()
|
||||
_refresh_chat_availability()
|
||||
|
||||
|
|
@ -1864,13 +1929,43 @@ func get_fishing_shop() -> FishingShopType:
|
|||
return _fishing_shop
|
||||
|
||||
|
||||
func get_decor_shop() -> DecorShopType:
|
||||
return _decor_shop
|
||||
|
||||
|
||||
func get_player_storage() -> PlayerStorageType:
|
||||
return _player_storage
|
||||
|
||||
|
||||
func get_home_decor_toolbar() -> HomeDecorToolbarType:
|
||||
return _home_decor_toolbar
|
||||
|
||||
|
||||
func set_rv_interior_camera_mode(enabled: bool) -> void:
|
||||
_hotbar_ui.set_rv_interior_camera_mode(enabled)
|
||||
|
||||
|
||||
func set_rv_storage_button_visible(requested_visible: bool) -> void:
|
||||
_rv_storage_button_requested_visible = requested_visible
|
||||
_refresh_rv_storage_button_visibility()
|
||||
|
||||
|
||||
func _refresh_rv_storage_button_visibility() -> void:
|
||||
_rv_storage_button.visible = (
|
||||
_rv_storage_button_requested_visible
|
||||
and _gameplay_ui_enabled
|
||||
and not _gameplay_hud_hidden
|
||||
and not _system_menu_open
|
||||
and not _player_menu_open
|
||||
and not _shop_open
|
||||
and not _storage_open
|
||||
)
|
||||
|
||||
|
||||
func set_storage_prompt_visible(
|
||||
requested_visible: bool,
|
||||
world_anchor: Vector3 = Vector3(0.0, INF, 0.0),
|
||||
action_text: String = "open storage",
|
||||
) -> void:
|
||||
if not _storage_prompt.has_meta(&"styled"):
|
||||
_storage_prompt.set_meta(&"styled", true)
|
||||
|
|
@ -1879,6 +1974,7 @@ func set_storage_prompt_visible(
|
|||
)
|
||||
style.anti_aliasing = false
|
||||
_storage_prompt.add_theme_stylebox_override("panel", style)
|
||||
_storage_prompt_message.text = action_text
|
||||
_storage_prompt.visible = (
|
||||
requested_visible
|
||||
and _gameplay_ui_enabled
|
||||
|
|
@ -2762,6 +2858,7 @@ func _refresh_hotbar_visibility() -> void:
|
|||
|
||||
func _emit_interactive_pointer_ui_changed() -> void:
|
||||
_refresh_active_bait_indicator_visibility()
|
||||
_refresh_rv_storage_button_visibility()
|
||||
interactive_pointer_ui_changed.emit(
|
||||
_system_menu_open
|
||||
or _player_menu_open
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=22 format=3]
|
||||
[gd_scene load_steps=24 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/game_ui.gd" id="1_ui"]
|
||||
[ext_resource type="PackedScene" path="res://ui/player_menu.tscn" id="2_menu"]
|
||||
|
|
@ -15,6 +15,8 @@
|
|||
[ext_resource type="Script" path="res://ui/controller_virtual_cursor.gd" id="13_cursor"]
|
||||
[ext_resource type="PackedScene" path="res://ui/player_storage.tscn" id="14_storage"]
|
||||
[ext_resource type="PackedScene" path="res://ui/components/bubble_menu/bubble_confirmation_page.tscn" id="15_social_prompt"]
|
||||
[ext_resource type="PackedScene" path="res://ui/decor_shop.tscn" id="16_decor_shop"]
|
||||
[ext_resource type="PackedScene" path="res://ui/home_decor_toolbar.tscn" id="17_decor_toolbar"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBox_chase_background"]
|
||||
bg_color = Color(0.032, 0.118, 0.15, 1)
|
||||
|
|
@ -472,9 +474,37 @@ polygon = PackedVector2Array(-8, 0, 8, 0, 0, 10)
|
|||
[node name="FishingShop" parent="UIRoot/CanonicalStage" instance=ExtResource("8_shop")]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="DecorShop" parent="UIRoot/CanonicalStage" instance=ExtResource("16_decor_shop")]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="HomeDecorToolbar" parent="UIRoot/CanonicalStage" instance=ExtResource("17_decor_toolbar")]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="PlayerStorage" parent="UIRoot/CanonicalStage" instance=ExtResource("14_storage")]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="RVStorageButton" type="Button" parent="UIRoot/CanonicalStage/GameplayTransientHUD"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
z_index = 57
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -74.0
|
||||
offset_top = 16.0
|
||||
offset_right = -16.0
|
||||
offset_bottom = 74.0
|
||||
grow_horizontal = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
focus_mode = 0
|
||||
tooltip_text = "open RV storage"
|
||||
theme = ExtResource("3_theme")
|
||||
texture_filter = 1
|
||||
expand_icon = true
|
||||
icon_alignment = 1
|
||||
vertical_icon_alignment = 1
|
||||
|
||||
[node name="StoragePrompt" type="PanelContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
|
|
|
|||
126
ui/home_decor_toolbar.gd
Normal file
126
ui/home_decor_toolbar.gd
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
class_name HomeDecorToolbar
|
||||
extends PanelContainer
|
||||
|
||||
signal placement_mode_requested(mode: int)
|
||||
signal rotate_requested
|
||||
signal store_requested
|
||||
signal cancel_requested
|
||||
|
||||
var _mode_button: Button
|
||||
var _selection_label: Label
|
||||
var _rotate_button: Button
|
||||
var _store_button: Button
|
||||
var _mode: PlayerHomeState.PlacementMode = PlayerHomeState.PlacementMode.GRID
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_build_ui()
|
||||
hide()
|
||||
|
||||
|
||||
func set_active(active: bool) -> void:
|
||||
if visible == active:
|
||||
return
|
||||
visible = active
|
||||
if active and _mode_button != null:
|
||||
_mode_button.call_deferred("grab_focus")
|
||||
|
||||
|
||||
func set_mode(mode: PlayerHomeState.PlacementMode) -> void:
|
||||
_mode = mode
|
||||
if _mode_button != null:
|
||||
_mode_button.text = (
|
||||
"placement: grid"
|
||||
if mode == PlayerHomeState.PlacementMode.GRID
|
||||
else "placement: free"
|
||||
)
|
||||
|
||||
|
||||
func set_selection(instance_id: String, display_name: String = "") -> void:
|
||||
var selected: bool = not instance_id.is_empty()
|
||||
if _selection_label != null:
|
||||
_selection_label.text = (
|
||||
"selected: %s" % display_name
|
||||
if selected else "click decor to select; drag to move"
|
||||
)
|
||||
if _rotate_button != null:
|
||||
_rotate_button.disabled = not selected
|
||||
if _store_button != null:
|
||||
_store_button.disabled = not selected
|
||||
|
||||
|
||||
func report_status(message: String) -> void:
|
||||
if _selection_label != null:
|
||||
_selection_label.text = message
|
||||
|
||||
|
||||
func _build_ui() -> void:
|
||||
custom_minimum_size = Vector2(330.0, 0.0)
|
||||
position = Vector2(925.0, 540.0)
|
||||
z_index = 58
|
||||
mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
add_theme_stylebox_override(
|
||||
"panel",
|
||||
UtilityPageStyle.rounded_style(
|
||||
Color(UtilityPageStyle.OCEAN_PANEL_DEEP, 0.96), 16
|
||||
),
|
||||
)
|
||||
var margin := MarginContainer.new()
|
||||
for side: StringName in [
|
||||
&"margin_left", &"margin_top", &"margin_right", &"margin_bottom",
|
||||
]:
|
||||
margin.add_theme_constant_override(side, 12)
|
||||
add_child(margin)
|
||||
var layout := VBoxContainer.new()
|
||||
layout.add_theme_constant_override("separation", 8)
|
||||
margin.add_child(layout)
|
||||
var title := Label.new()
|
||||
title.text = "RV decor"
|
||||
title.add_theme_font_size_override("font_size", 24)
|
||||
layout.add_child(title)
|
||||
_selection_label = Label.new()
|
||||
_selection_label.text = "click decor to select; drag to move"
|
||||
_selection_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
_selection_label.add_theme_font_size_override("font_size", 17)
|
||||
layout.add_child(_selection_label)
|
||||
var row := HBoxContainer.new()
|
||||
row.add_theme_constant_override("separation", 8)
|
||||
layout.add_child(row)
|
||||
_mode_button = Button.new()
|
||||
_mode_button.text = "placement: grid"
|
||||
_mode_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
UtilityPageStyle.apply_ocean_button(_mode_button)
|
||||
_mode_button.pressed.connect(_toggle_mode)
|
||||
row.add_child(_mode_button)
|
||||
_rotate_button = Button.new()
|
||||
_rotate_button.text = "rotate"
|
||||
_rotate_button.disabled = true
|
||||
UtilityPageStyle.apply_ocean_button(_rotate_button)
|
||||
_rotate_button.pressed.connect(rotate_requested.emit)
|
||||
row.add_child(_rotate_button)
|
||||
var action_row := HBoxContainer.new()
|
||||
action_row.add_theme_constant_override("separation", 8)
|
||||
layout.add_child(action_row)
|
||||
_store_button = Button.new()
|
||||
_store_button.text = "pack up"
|
||||
_store_button.disabled = true
|
||||
_store_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
UtilityPageStyle.apply_ocean_button(_store_button)
|
||||
_store_button.pressed.connect(store_requested.emit)
|
||||
action_row.add_child(_store_button)
|
||||
var cancel_button := Button.new()
|
||||
cancel_button.text = "clear selection"
|
||||
cancel_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
UtilityPageStyle.apply_ocean_button(cancel_button)
|
||||
cancel_button.pressed.connect(cancel_requested.emit)
|
||||
action_row.add_child(cancel_button)
|
||||
|
||||
|
||||
func _toggle_mode() -> void:
|
||||
_mode = (
|
||||
PlayerHomeState.PlacementMode.FREE
|
||||
if _mode == PlayerHomeState.PlacementMode.GRID
|
||||
else PlayerHomeState.PlacementMode.GRID
|
||||
)
|
||||
set_mode(_mode)
|
||||
placement_mode_requested.emit(int(_mode))
|
||||
1
ui/home_decor_toolbar.gd.uid
Normal file
1
ui/home_decor_toolbar.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://bmtlixw3wief3
|
||||
8
ui/home_decor_toolbar.tscn
Normal file
8
ui/home_decor_toolbar.tscn
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/home_decor_toolbar.gd" id="1_script"]
|
||||
|
||||
[node name="HomeDecorToolbar" type="PanelContainer"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
script = ExtResource("1_script")
|
||||
11
ui/hotbar.gd
11
ui/hotbar.gd
|
|
@ -37,6 +37,7 @@ var _slots: Array[BubbleHotbarSlotType] = []
|
|||
var _gameplay_input_enabled: bool = false
|
||||
var _drag_enabled: bool = false
|
||||
var _swap_hotbar_camera_scroll: bool = false
|
||||
var _rv_interior_camera_mode: bool = false
|
||||
var _motion_elapsed: float = 0.0
|
||||
var _compact_layout: bool = false
|
||||
var _player_menu_context: bool = false
|
||||
|
|
@ -99,6 +100,10 @@ func set_swap_hotbar_camera_scroll(enabled: bool) -> void:
|
|||
_swap_hotbar_camera_scroll = enabled
|
||||
|
||||
|
||||
func set_rv_interior_camera_mode(enabled: bool) -> void:
|
||||
_rv_interior_camera_mode = enabled
|
||||
|
||||
|
||||
func set_drag_enabled(enabled: bool) -> void:
|
||||
_drag_enabled = enabled
|
||||
for slot: BubbleHotbarSlotType in _slots:
|
||||
|
|
@ -308,7 +313,11 @@ func _unhandled_input(event: InputEvent) -> void:
|
|||
if (
|
||||
event is InputEventMouseButton
|
||||
and event.pressed
|
||||
and event.shift_pressed == _swap_hotbar_camera_scroll
|
||||
and (
|
||||
event.shift_pressed
|
||||
if _rv_interior_camera_mode
|
||||
else event.shift_pressed == _swap_hotbar_camera_scroll
|
||||
)
|
||||
):
|
||||
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
|
||||
_hotbar.cycle_selection(-1)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ extends Control
|
|||
const INPUT_OWNER: StringName = &"player_storage"
|
||||
|
||||
signal menu_visibility_changed(is_open: bool)
|
||||
signal decorate_requested
|
||||
|
||||
var _player: Player
|
||||
var _fishing_spot: FishingSpot
|
||||
|
|
@ -14,6 +15,9 @@ var _storage_grid: GeneralInventoryGrid
|
|||
var _inventory_count: Label
|
||||
var _storage_count: Label
|
||||
var _feedback: Label
|
||||
var _title: Label
|
||||
var _decorate_button: Button
|
||||
var _home_access: bool = false
|
||||
var _prior_movement_enabled: bool = true
|
||||
var _prior_camera_enabled: bool = true
|
||||
var _prior_mouse_mode: Input.MouseMode = Input.MOUSE_MODE_VISIBLE
|
||||
|
|
@ -75,15 +79,34 @@ func set_world_interaction(interaction: PlayerStorageInteraction) -> void:
|
|||
|
||||
|
||||
func open_storage() -> bool:
|
||||
return _open_storage(false)
|
||||
|
||||
|
||||
func open_storage_from_home(allow_decorating: bool = true) -> bool:
|
||||
return _open_storage(true, allow_decorating)
|
||||
|
||||
|
||||
func _open_storage(
|
||||
home_access: bool,
|
||||
allow_decorating: bool = false,
|
||||
) -> bool:
|
||||
if (
|
||||
visible
|
||||
or _player == null
|
||||
or _interaction == null
|
||||
or not _interaction.is_local_player_in_range()
|
||||
or _fishing_spot == null
|
||||
or not _fishing_spot.can_open_fishing_shop()
|
||||
or (
|
||||
not home_access
|
||||
and (
|
||||
_interaction == null
|
||||
or not _interaction.is_local_player_in_range()
|
||||
)
|
||||
)
|
||||
):
|
||||
return false
|
||||
_home_access = home_access
|
||||
_title.text = "RV storage" if home_access else "private storage"
|
||||
_decorate_button.visible = home_access and allow_decorating
|
||||
_prior_movement_enabled = _player.is_movement_enabled()
|
||||
_prior_camera_enabled = _player.is_camera_input_enabled()
|
||||
_prior_mouse_mode = Input.mouse_mode
|
||||
|
|
@ -112,10 +135,13 @@ func close_storage(restore_controls: bool = true) -> void:
|
|||
_player.set_movement_enabled(_prior_movement_enabled)
|
||||
_player.set_camera_input_enabled(_prior_camera_enabled)
|
||||
Input.mouse_mode = _prior_mouse_mode
|
||||
_home_access = false
|
||||
menu_visibility_changed.emit(false)
|
||||
|
||||
|
||||
func close_for_range_exit() -> void:
|
||||
if _home_access:
|
||||
return
|
||||
close_storage()
|
||||
|
||||
|
||||
|
|
@ -248,12 +274,20 @@ func _build_ui() -> void:
|
|||
|
||||
var header := HBoxContainer.new()
|
||||
layout.add_child(header)
|
||||
var title := Label.new()
|
||||
title.text = "private storage"
|
||||
title.add_theme_font_size_override("font_size", 32)
|
||||
title.add_theme_color_override("font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY)
|
||||
title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
header.add_child(title)
|
||||
_title = Label.new()
|
||||
_title.text = "private storage"
|
||||
_title.add_theme_font_size_override("font_size", 32)
|
||||
_title.add_theme_color_override("font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY)
|
||||
_title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
header.add_child(_title)
|
||||
_decorate_button = Button.new()
|
||||
_decorate_button.text = "decorate RV"
|
||||
_decorate_button.tooltip_text = "Place decor inside your RV"
|
||||
_decorate_button.custom_minimum_size = Vector2(160.0, 48.0)
|
||||
UtilityPageStyle.apply_ocean_button(_decorate_button)
|
||||
_decorate_button.hide()
|
||||
_decorate_button.pressed.connect(_request_decorating)
|
||||
header.add_child(_decorate_button)
|
||||
var close := Button.new()
|
||||
close.text = "×"
|
||||
close.tooltip_text = "Close storage"
|
||||
|
|
@ -285,6 +319,13 @@ func _build_ui() -> void:
|
|||
layout.add_child(_feedback)
|
||||
|
||||
|
||||
func _request_decorating() -> void:
|
||||
if not _home_access:
|
||||
return
|
||||
close_storage()
|
||||
decorate_requested.emit()
|
||||
|
||||
|
||||
func _build_column(parent: HBoxContainer, title_text: String) -> Dictionary:
|
||||
var panel := PanelContainer.new()
|
||||
panel.custom_minimum_size = Vector2(550.0, 0.0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue