straywild/ui/decor_shop.gd

323 lines
9.2 KiB
GDScript

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)