Polish interface artwork and controller behavior
|
|
@ -1,7 +1,7 @@
|
||||||
[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3]
|
[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://items/item_data.gd" id="1_item"]
|
[ext_resource type="Script" path="res://items/item_data.gd" id="1_item"]
|
||||||
[ext_resource type="Texture2D" path="res://items/icons/equipment/temp_net.png" id="2_icon"]
|
[ext_resource type="Texture2D" path="res://items/icons/equipment/net.png" id="2_icon"]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
script = ExtResource("1_item")
|
script = ExtResource("1_item")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3]
|
[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://items/item_data.gd" id="1_item"]
|
[ext_resource type="Script" path="res://items/item_data.gd" id="1_item"]
|
||||||
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/x_light.png" id="2_icon"]
|
[ext_resource type="Texture2D" path="res://items/icons/equipment/shovel.png" id="2_icon"]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
script = ExtResource("1_item")
|
script = ExtResource("1_item")
|
||||||
|
|
|
||||||
BIN
items/icons/equipment/net.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
40
items/icons/equipment/net.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://c026rqygllmst"
|
||||||
|
path="res://.godot/imported/net.png-695e6b1511b4d1d2f6c885df7ccf0412.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://items/icons/equipment/net.png"
|
||||||
|
dest_files=["res://.godot/imported/net.png-695e6b1511b4d1d2f6c885df7ccf0412.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
|
||||||
BIN
items/icons/equipment/shovel.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
40
items/icons/equipment/shovel.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cabeaw2e0glu"
|
||||||
|
path="res://.godot/imported/shovel.png-cdfb898e33414742110b8f36a05aceb3.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://items/icons/equipment/shovel.png"
|
||||||
|
dest_files=["res://.godot/imported/shovel.png-cdfb898e33414742110b8f36a05aceb3.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
|
||||||
|
|
@ -15,6 +15,9 @@ const DEFAULT_WORLD_PIXEL_SIZE: int = 3
|
||||||
const MIN_UI_PIXEL_SIZE: int = 1
|
const MIN_UI_PIXEL_SIZE: int = 1
|
||||||
const MAX_UI_PIXEL_SIZE: int = 5
|
const MAX_UI_PIXEL_SIZE: int = 5
|
||||||
const DEFAULT_UI_PIXEL_SIZE: int = 3
|
const DEFAULT_UI_PIXEL_SIZE: int = 3
|
||||||
|
const DEFAULT_CHAT_DOCK_RIGHT: bool = false
|
||||||
|
const DEFAULT_CHAT_MOBILE_MODE: bool = false
|
||||||
|
const DEFAULT_PAINT_DOCK_RIGHT: bool = true
|
||||||
const MAX_CHAT_DRAFT_CHARACTERS: int = 256
|
const MAX_CHAT_DRAFT_CHARACTERS: int = 256
|
||||||
const COMPACT_DISPLAY_HEIGHT: int = 560
|
const COMPACT_DISPLAY_HEIGHT: int = 560
|
||||||
const WORLD_DESKTOP_GRID_HEIGHTS: Array[int] = [0, 540, 360, 216, 96]
|
const WORLD_DESKTOP_GRID_HEIGHTS: Array[int] = [0, 540, 360, 216, 96]
|
||||||
|
|
@ -32,9 +35,10 @@ const UI_COMPACT_RENDER_HEIGHTS: Array[int] = [0, 408, 336, 264, 192]
|
||||||
@export var on_screen_keyboard_enabled: bool = false
|
@export var on_screen_keyboard_enabled: bool = false
|
||||||
@export var chat_draft: String = ""
|
@export var chat_draft: String = ""
|
||||||
@export var chat_collapsed: bool = false
|
@export var chat_collapsed: bool = false
|
||||||
@export var chat_dock_right: bool = false
|
@export var chat_dock_right: bool = DEFAULT_CHAT_DOCK_RIGHT
|
||||||
@export var chat_mobile_mode: bool = false
|
@export var chat_mobile_mode: bool = DEFAULT_CHAT_MOBILE_MODE
|
||||||
@export var paint_dock_right: bool = true
|
@export var paint_dock_right: bool = DEFAULT_PAINT_DOCK_RIGHT
|
||||||
|
@export var presentation_layout_customized: bool = false
|
||||||
@export var fullscreen_enabled: bool = false
|
@export var fullscreen_enabled: bool = false
|
||||||
@export_range(0.0, 1.0, 0.01) var master_volume: float = 1.0
|
@export_range(0.0, 1.0, 0.01) var master_volume: float = 1.0
|
||||||
@export_range(0.0, 1.0, 0.01) var music_volume: float = 1.0
|
@export_range(0.0, 1.0, 0.01) var music_volume: float = 1.0
|
||||||
|
|
@ -81,6 +85,7 @@ func copy() -> PlayerSettings:
|
||||||
result.chat_dock_right = chat_dock_right
|
result.chat_dock_right = chat_dock_right
|
||||||
result.chat_mobile_mode = chat_mobile_mode
|
result.chat_mobile_mode = chat_mobile_mode
|
||||||
result.paint_dock_right = paint_dock_right
|
result.paint_dock_right = paint_dock_right
|
||||||
|
result.presentation_layout_customized = presentation_layout_customized
|
||||||
result.fullscreen_enabled = fullscreen_enabled
|
result.fullscreen_enabled = fullscreen_enabled
|
||||||
result.master_volume = master_volume
|
result.master_volume = master_volume
|
||||||
result.music_volume = music_volume
|
result.music_volume = music_volume
|
||||||
|
|
@ -91,6 +96,20 @@ func copy() -> PlayerSettings:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
func normalize_implicit_presentation_layout() -> bool:
|
||||||
|
if presentation_layout_customized:
|
||||||
|
return false
|
||||||
|
var changed: bool = (
|
||||||
|
chat_dock_right != DEFAULT_CHAT_DOCK_RIGHT
|
||||||
|
or chat_mobile_mode != DEFAULT_CHAT_MOBILE_MODE
|
||||||
|
or paint_dock_right != DEFAULT_PAINT_DOCK_RIGHT
|
||||||
|
)
|
||||||
|
chat_dock_right = DEFAULT_CHAT_DOCK_RIGHT
|
||||||
|
chat_mobile_mode = DEFAULT_CHAT_MOBILE_MODE
|
||||||
|
paint_dock_right = DEFAULT_PAINT_DOCK_RIGHT
|
||||||
|
return changed
|
||||||
|
|
||||||
|
|
||||||
static func _is_valid_audio_volume(value: float) -> bool:
|
static func _is_valid_audio_volume(value: float) -> bool:
|
||||||
return (
|
return (
|
||||||
is_finite(value)
|
is_finite(value)
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,10 @@ func load_settings() -> bool:
|
||||||
presentation.has("paint_dock_right")
|
presentation.has("paint_dock_right")
|
||||||
and typeof(presentation["paint_dock_right"]) != TYPE_BOOL
|
and typeof(presentation["paint_dock_right"]) != TYPE_BOOL
|
||||||
)
|
)
|
||||||
|
or (
|
||||||
|
presentation.has("layout_customized")
|
||||||
|
and typeof(presentation["layout_customized"]) != TYPE_BOOL
|
||||||
|
)
|
||||||
or (
|
or (
|
||||||
presentation.has("fullscreen_enabled")
|
presentation.has("fullscreen_enabled")
|
||||||
and typeof(presentation["fullscreen_enabled"]) != TYPE_BOOL
|
and typeof(presentation["fullscreen_enabled"]) != TYPE_BOOL
|
||||||
|
|
@ -135,6 +139,12 @@ func load_settings() -> bool:
|
||||||
presentation.get("chat_mobile_mode", false)
|
presentation.get("chat_mobile_mode", false)
|
||||||
)
|
)
|
||||||
loaded.paint_dock_right = bool(presentation.get("paint_dock_right", true))
|
loaded.paint_dock_right = bool(presentation.get("paint_dock_right", true))
|
||||||
|
loaded.presentation_layout_customized = bool(
|
||||||
|
presentation.get("layout_customized", false)
|
||||||
|
)
|
||||||
|
var normalized_implicit_layout: bool = (
|
||||||
|
loaded.normalize_implicit_presentation_layout()
|
||||||
|
)
|
||||||
loaded.fullscreen_enabled = bool(
|
loaded.fullscreen_enabled = bool(
|
||||||
presentation.get("fullscreen_enabled", false)
|
presentation.get("fullscreen_enabled", false)
|
||||||
)
|
)
|
||||||
|
|
@ -147,7 +157,7 @@ func load_settings() -> bool:
|
||||||
if not loaded.is_valid():
|
if not loaded.is_valid():
|
||||||
return _use_defaults_after_corruption("Player settings values are invalid.")
|
return _use_defaults_after_corruption("Player settings values are invalid.")
|
||||||
current_settings = loaded
|
current_settings = loaded
|
||||||
_is_dirty = false
|
_is_dirty = normalized_implicit_layout
|
||||||
_apply_audio_settings(current_settings)
|
_apply_audio_settings(current_settings)
|
||||||
emit_signal("settings_changed", current_settings)
|
emit_signal("settings_changed", current_settings)
|
||||||
return true
|
return true
|
||||||
|
|
@ -157,7 +167,9 @@ func apply_settings(settings: PlayerSettings) -> bool:
|
||||||
if settings == null or not settings.is_valid():
|
if settings == null or not settings.is_valid():
|
||||||
return false
|
return false
|
||||||
var previous: PlayerSettings = current_settings
|
var previous: PlayerSettings = current_settings
|
||||||
current_settings = settings.copy()
|
var normalized_settings := settings.copy()
|
||||||
|
normalized_settings.normalize_implicit_presentation_layout()
|
||||||
|
current_settings = normalized_settings
|
||||||
_is_dirty = true
|
_is_dirty = true
|
||||||
if not save_now():
|
if not save_now():
|
||||||
current_settings = previous
|
current_settings = previous
|
||||||
|
|
@ -240,6 +252,9 @@ func save_now() -> bool:
|
||||||
"chat_dock_right": current_settings.chat_dock_right,
|
"chat_dock_right": current_settings.chat_dock_right,
|
||||||
"chat_mobile_mode": current_settings.chat_mobile_mode,
|
"chat_mobile_mode": current_settings.chat_mobile_mode,
|
||||||
"paint_dock_right": current_settings.paint_dock_right,
|
"paint_dock_right": current_settings.paint_dock_right,
|
||||||
|
"layout_customized": (
|
||||||
|
current_settings.presentation_layout_customized
|
||||||
|
),
|
||||||
"fullscreen_enabled": current_settings.fullscreen_enabled,
|
"fullscreen_enabled": current_settings.fullscreen_enabled,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,14 +87,25 @@ func _run() -> void:
|
||||||
assert(player != null and service != null and toolbar != null)
|
assert(player != null and service != null and toolbar != null)
|
||||||
assert(chat_ui != null)
|
assert(chat_ui != null)
|
||||||
assert(bool(game_ui.call("_can_start_virtual_mouse")))
|
assert(bool(game_ui.call("_can_start_virtual_mouse")))
|
||||||
|
var virtual_pointer_states: Array[bool] = []
|
||||||
|
game_ui.virtual_pointer_mode_changed.connect(
|
||||||
|
func(active: bool) -> void: virtual_pointer_states.append(active)
|
||||||
|
)
|
||||||
game_ui.call("_begin_virtual_mouse", 0)
|
game_ui.call("_begin_virtual_mouse", 0)
|
||||||
var virtual_cursor := game_ui.get_node(
|
var virtual_cursor := game_ui.get_node(
|
||||||
"%ControllerVirtualCursor"
|
"%ControllerVirtualCursor"
|
||||||
) as ControllerVirtualCursor
|
) as ControllerVirtualCursor
|
||||||
assert(virtual_cursor.visible)
|
assert(virtual_cursor.visible)
|
||||||
|
assert(virtual_pointer_states == [true])
|
||||||
|
# PopupMenu subwindows can consume the physical shoulder release. The
|
||||||
|
# polled primary-button state must still release the synthetic mouse click.
|
||||||
|
game_ui.set("_virtual_mouse_button_mask", MOUSE_BUTTON_MASK_LEFT)
|
||||||
|
game_ui.call("_sync_virtual_mouse_primary_button", false)
|
||||||
|
assert(int(game_ui.get("_virtual_mouse_button_mask")) == 0)
|
||||||
assert(not player.is_camera_input_enabled())
|
assert(not player.is_camera_input_enabled())
|
||||||
game_ui.call("_end_virtual_mouse")
|
game_ui.call("_end_virtual_mouse")
|
||||||
assert(not virtual_cursor.visible)
|
assert(not virtual_cursor.visible)
|
||||||
|
assert(virtual_pointer_states == [true, false])
|
||||||
assert(player.is_camera_input_enabled())
|
assert(player.is_camera_input_enabled())
|
||||||
var settings_panel := game_ui.get(
|
var settings_panel := game_ui.get(
|
||||||
"_pause_settings_panel"
|
"_pause_settings_panel"
|
||||||
|
|
@ -131,6 +142,12 @@ func _run() -> void:
|
||||||
changed_docks.paint_dock_right = false
|
changed_docks.paint_dock_right = false
|
||||||
assert(settings_manager.apply_settings(changed_docks))
|
assert(settings_manager.apply_settings(changed_docks))
|
||||||
await process_frame
|
await process_frame
|
||||||
|
assert(not chat_ui.is_docked_right())
|
||||||
|
assert(not chat_ui.is_mobile_mode())
|
||||||
|
assert(toolbar.is_docked_right())
|
||||||
|
changed_docks.presentation_layout_customized = true
|
||||||
|
assert(settings_manager.apply_settings(changed_docks))
|
||||||
|
await process_frame
|
||||||
assert(chat_ui.is_docked_right())
|
assert(chat_ui.is_docked_right())
|
||||||
assert(chat_ui.is_mobile_mode())
|
assert(chat_ui.is_mobile_mode())
|
||||||
assert(not toolbar.is_docked_right())
|
assert(not toolbar.is_docked_right())
|
||||||
|
|
@ -153,7 +170,31 @@ func _run() -> void:
|
||||||
assert(reloaded_settings.current_settings.chat_dock_right)
|
assert(reloaded_settings.current_settings.chat_dock_right)
|
||||||
assert(reloaded_settings.current_settings.chat_mobile_mode)
|
assert(reloaded_settings.current_settings.chat_mobile_mode)
|
||||||
assert(not reloaded_settings.current_settings.paint_dock_right)
|
assert(not reloaded_settings.current_settings.paint_dock_right)
|
||||||
|
assert(reloaded_settings.current_settings.presentation_layout_customized)
|
||||||
reloaded_settings.queue_free()
|
reloaded_settings.queue_free()
|
||||||
|
var legacy_data: Dictionary = JSON.parse_string(
|
||||||
|
FileAccess.get_file_as_string(PlayerSettingsManager.SETTINGS_PATH)
|
||||||
|
) as Dictionary
|
||||||
|
var legacy_presentation := legacy_data.get("presentation", {}) as Dictionary
|
||||||
|
legacy_presentation.erase("layout_customized")
|
||||||
|
legacy_data["presentation"] = legacy_presentation
|
||||||
|
var legacy_file := FileAccess.open(
|
||||||
|
PlayerSettingsManager.SETTINGS_PATH,
|
||||||
|
FileAccess.WRITE,
|
||||||
|
)
|
||||||
|
assert(legacy_file != null)
|
||||||
|
legacy_file.store_string(JSON.stringify(legacy_data, "\t"))
|
||||||
|
legacy_file.close()
|
||||||
|
var migrated_legacy_settings := PlayerSettingsManager.new()
|
||||||
|
root.add_child(migrated_legacy_settings)
|
||||||
|
assert(migrated_legacy_settings.load_settings())
|
||||||
|
assert(not migrated_legacy_settings.current_settings.chat_dock_right)
|
||||||
|
assert(not migrated_legacy_settings.current_settings.chat_mobile_mode)
|
||||||
|
assert(migrated_legacy_settings.current_settings.paint_dock_right)
|
||||||
|
assert(
|
||||||
|
not migrated_legacy_settings.current_settings.presentation_layout_customized
|
||||||
|
)
|
||||||
|
migrated_legacy_settings.queue_free()
|
||||||
var default_docks: PlayerSettings = settings_manager.current_settings.copy()
|
var default_docks: PlayerSettings = settings_manager.current_settings.copy()
|
||||||
default_docks.chat_dock_right = false
|
default_docks.chat_dock_right = false
|
||||||
default_docks.chat_mobile_mode = false
|
default_docks.chat_mobile_mode = false
|
||||||
|
|
@ -456,18 +497,39 @@ func _run() -> void:
|
||||||
assert(grid_option.get_item_text(index).is_empty())
|
assert(grid_option.get_item_text(index).is_empty())
|
||||||
assert(brush_option.get_item_icon(index) != null)
|
assert(brush_option.get_item_icon(index) != null)
|
||||||
assert(grid_option.get_item_icon(index) != null)
|
assert(grid_option.get_item_icon(index) != null)
|
||||||
assert(
|
var brush_unlocked: bool = index == 0
|
||||||
str(brush_option.get_item_icon(index).get_meta(
|
var grid_unlocked: bool = index == 0
|
||||||
&"channel_mask_source", ""
|
if brush_unlocked:
|
||||||
)).ends_with(
|
assert(
|
||||||
expected_brush_icons[index]
|
str(brush_option.get_item_icon(index).get_meta(
|
||||||
|
&"channel_mask_source", ""
|
||||||
|
)).ends_with(
|
||||||
|
expected_brush_icons[index]
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
else:
|
||||||
assert(
|
assert(bool(brush_option.get_item_icon(index).get_meta(
|
||||||
grid_option.get_item_icon(index).resource_path.ends_with(
|
&"locked_content_icon", false
|
||||||
expected_grid_icons[index]
|
)))
|
||||||
|
if grid_unlocked:
|
||||||
|
assert(
|
||||||
|
grid_option.get_item_icon(index).resource_path.ends_with(
|
||||||
|
expected_grid_icons[index]
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
else:
|
||||||
|
assert(bool(grid_option.get_item_icon(index).get_meta(
|
||||||
|
&"locked_content_icon", false
|
||||||
|
)))
|
||||||
|
var expected_lock_alpha: float = 1.0 if index == 0 else 0.72
|
||||||
|
assert(is_equal_approx(
|
||||||
|
brush_option.get_popup().get_item_icon_modulate(index).a,
|
||||||
|
expected_lock_alpha,
|
||||||
|
))
|
||||||
|
assert(is_equal_approx(
|
||||||
|
grid_option.get_popup().get_item_icon_modulate(index).a,
|
||||||
|
expected_lock_alpha,
|
||||||
|
))
|
||||||
assert(not brush_option.get_popup().is_item_disabled(0))
|
assert(not brush_option.get_popup().is_item_disabled(0))
|
||||||
assert(brush_option.get_popup().is_item_disabled(1))
|
assert(brush_option.get_popup().is_item_disabled(1))
|
||||||
assert(not grid_option.get_popup().is_item_disabled(0))
|
assert(not grid_option.get_popup().is_item_disabled(0))
|
||||||
|
|
@ -478,6 +540,26 @@ func _run() -> void:
|
||||||
assert(color_button.focus_mode == Control.FOCUS_NONE)
|
assert(color_button.focus_mode == Control.FOCUS_NONE)
|
||||||
assert(not (color_buttons[&"chalk_white"] as Button).disabled)
|
assert(not (color_buttons[&"chalk_white"] as Button).disabled)
|
||||||
assert((color_buttons[&"ocean_teal"] as Button).disabled)
|
assert((color_buttons[&"ocean_teal"] as Button).disabled)
|
||||||
|
var base_color_lock := (
|
||||||
|
(color_buttons[&"chalk_white"] as Button).get_node(
|
||||||
|
"UnlockStateIcon"
|
||||||
|
) as TextureRect
|
||||||
|
)
|
||||||
|
var locked_color_lock := (
|
||||||
|
(color_buttons[&"ocean_teal"] as Button).get_node(
|
||||||
|
"UnlockStateIcon"
|
||||||
|
) as TextureRect
|
||||||
|
)
|
||||||
|
assert(not base_color_lock.visible)
|
||||||
|
assert(locked_color_lock.visible)
|
||||||
|
assert(locked_color_lock.size == Vector2(18.0, 18.0))
|
||||||
|
assert(is_equal_approx(locked_color_lock.modulate.a, 0.34))
|
||||||
|
var popup_lock_icon := toolbar.get("_popup_lock_icon") as Texture2D
|
||||||
|
assert(popup_lock_icon.get_width() == 40)
|
||||||
|
assert(popup_lock_icon.get_height() == 40)
|
||||||
|
assert(int(popup_lock_icon.get_meta(
|
||||||
|
&"locked_content_icon_size", 0
|
||||||
|
)) == 24)
|
||||||
|
|
||||||
for product_id: StringName in [
|
for product_id: StringName in [
|
||||||
&"marker_ocean_teal", &"brush_4x", &"grid_128x",
|
&"marker_ocean_teal", &"brush_4x", &"grid_128x",
|
||||||
|
|
@ -487,6 +569,19 @@ func _run() -> void:
|
||||||
assert(not (color_buttons[&"ocean_teal"] as Button).disabled)
|
assert(not (color_buttons[&"ocean_teal"] as Button).disabled)
|
||||||
assert(not brush_option.get_popup().is_item_disabled(3))
|
assert(not brush_option.get_popup().is_item_disabled(3))
|
||||||
assert(not grid_option.get_popup().is_item_disabled(3))
|
assert(not grid_option.get_popup().is_item_disabled(3))
|
||||||
|
assert(not locked_color_lock.visible)
|
||||||
|
assert(str(brush_option.get_item_icon(3).get_meta(
|
||||||
|
&"channel_mask_source", ""
|
||||||
|
)).ends_with(expected_brush_icons[3]))
|
||||||
|
assert(grid_option.get_item_icon(3).resource_path.ends_with(
|
||||||
|
expected_grid_icons[3]
|
||||||
|
))
|
||||||
|
assert(is_equal_approx(
|
||||||
|
brush_option.get_popup().get_item_icon_modulate(3).a, 1.0
|
||||||
|
))
|
||||||
|
assert(is_equal_approx(
|
||||||
|
grid_option.get_popup().get_item_icon_modulate(3).a, 1.0
|
||||||
|
))
|
||||||
assert(service.set_color_id(&"ocean_teal"))
|
assert(service.set_color_id(&"ocean_teal"))
|
||||||
assert(service.set_brush_size(4))
|
assert(service.set_brush_size(4))
|
||||||
assert(service.set_grid_size(128))
|
assert(service.set_grid_size(128))
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,30 @@ func _run() -> void:
|
||||||
stage.add_child(second_button)
|
stage.add_child(second_button)
|
||||||
await process_frame
|
await process_frame
|
||||||
|
|
||||||
|
standard_button.grab_focus()
|
||||||
|
await process_frame
|
||||||
|
_expect(
|
||||||
|
root.gui_get_focus_owner() == null,
|
||||||
|
"a menu's programmatic first focus is visible before navigation input",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
presentation.get("_neutral_focus_seed") == standard_button,
|
||||||
|
"the neutral menu focus seed did not remember the first action",
|
||||||
|
)
|
||||||
|
var first_navigation := InputEventJoypadButton.new()
|
||||||
|
first_navigation.button_index = JOY_BUTTON_DPAD_DOWN
|
||||||
|
first_navigation.pressed = true
|
||||||
|
presentation._input(first_navigation)
|
||||||
|
await process_frame
|
||||||
|
_expect(
|
||||||
|
root.gui_get_focus_owner() == standard_button,
|
||||||
|
"the first directional input did not activate the neutral focus seed",
|
||||||
|
)
|
||||||
|
|
||||||
var controller_event := InputEventJoypadButton.new()
|
var controller_event := InputEventJoypadButton.new()
|
||||||
controller_event.button_index = JOY_BUTTON_A
|
controller_event.button_index = JOY_BUTTON_A
|
||||||
controller_event.pressed = true
|
controller_event.pressed = true
|
||||||
presentation._input(controller_event)
|
presentation._input(controller_event)
|
||||||
standard_button.grab_focus()
|
|
||||||
await process_frame
|
await process_frame
|
||||||
var focus_arrow := presentation.get("_focus_arrow") as TextureRect
|
var focus_arrow := presentation.get("_focus_arrow") as TextureRect
|
||||||
var focus_shadow := presentation.get("_focus_arrow_shadow") as TextureRect
|
var focus_shadow := presentation.get("_focus_arrow_shadow") as TextureRect
|
||||||
|
|
@ -111,8 +130,8 @@ func _run() -> void:
|
||||||
root.gui_get_hovered_control() == standard_button,
|
root.gui_get_hovered_control() == standard_button,
|
||||||
"the pointer did not reach the hover-suppression test button",
|
"the pointer did not reach the hover-suppression test button",
|
||||||
)
|
)
|
||||||
|
presentation._input(first_navigation)
|
||||||
presentation._input(controller_event)
|
presentation._input(controller_event)
|
||||||
standard_button.grab_focus()
|
|
||||||
await process_frame
|
await process_frame
|
||||||
_expect(
|
_expect(
|
||||||
standard_button.get_theme_stylebox("hover") == normal_style,
|
standard_button.get_theme_stylebox("hover") == normal_style,
|
||||||
|
|
@ -154,6 +173,10 @@ func _run() -> void:
|
||||||
not focus_shadow.visible,
|
not focus_shadow.visible,
|
||||||
"mouse motion leaves the controller focus shadow visible",
|
"mouse motion leaves the controller focus shadow visible",
|
||||||
)
|
)
|
||||||
|
_expect(
|
||||||
|
root.gui_get_focus_owner() == null,
|
||||||
|
"mouse motion leaves a stale menu action focused",
|
||||||
|
)
|
||||||
_expect(
|
_expect(
|
||||||
standard_button.get_theme_stylebox("focus") == old_focus_style,
|
standard_button.get_theme_stylebox("focus") == old_focus_style,
|
||||||
"native theme overrides were not restored after controller use",
|
"native theme overrides were not restored after controller use",
|
||||||
|
|
@ -162,8 +185,6 @@ func _run() -> void:
|
||||||
standard_button.get_theme_stylebox("hover") == old_hover_style,
|
standard_button.get_theme_stylebox("hover") == old_hover_style,
|
||||||
"mouse hover styling was not restored after controller use",
|
"mouse hover styling was not restored after controller use",
|
||||||
)
|
)
|
||||||
presentation._input(controller_event)
|
|
||||||
|
|
||||||
var item_list := ItemList.new()
|
var item_list := ItemList.new()
|
||||||
item_list.position = Vector2(80.0, 160.0)
|
item_list.position = Vector2(80.0, 160.0)
|
||||||
item_list.size = Vector2(240.0, 150.0)
|
item_list.size = Vector2(240.0, 150.0)
|
||||||
|
|
@ -175,6 +196,9 @@ func _run() -> void:
|
||||||
item_list.select(0)
|
item_list.select(0)
|
||||||
item_list.grab_focus()
|
item_list.grab_focus()
|
||||||
await process_frame
|
await process_frame
|
||||||
|
presentation._input(first_navigation)
|
||||||
|
presentation._input(controller_event)
|
||||||
|
await process_frame
|
||||||
var first_item_arrow_y: float = focus_arrow.position.y
|
var first_item_arrow_y: float = focus_arrow.position.y
|
||||||
item_list.select(2)
|
item_list.select(2)
|
||||||
await process_frame
|
await process_frame
|
||||||
|
|
@ -329,6 +353,42 @@ func _run() -> void:
|
||||||
not focus_arrow.visible,
|
not focus_arrow.visible,
|
||||||
"focus arrow remains after its control becomes unfocusable",
|
"focus arrow remains after its control becomes unfocusable",
|
||||||
)
|
)
|
||||||
|
var event_focused_button := Button.new()
|
||||||
|
event_focused_button.text = "event focused"
|
||||||
|
event_focused_button.position = Vector2(700.0, 360.0)
|
||||||
|
event_focused_button.custom_minimum_size = Vector2(160.0, 50.0)
|
||||||
|
stage.add_child(event_focused_button)
|
||||||
|
await process_frame
|
||||||
|
presentation._input(first_navigation)
|
||||||
|
event_focused_button.grab_focus()
|
||||||
|
await process_frame
|
||||||
|
_expect(
|
||||||
|
root.gui_get_focus_owner() == event_focused_button,
|
||||||
|
(
|
||||||
|
"a menu focus established by the current directional event was "
|
||||||
|
+ "mistaken for an automatic default"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
presentation.set_virtual_pointer_active(true)
|
||||||
|
await process_frame
|
||||||
|
_expect(
|
||||||
|
not focus_arrow.visible,
|
||||||
|
"focus arrow competes with active virtual-pointer presentation",
|
||||||
|
)
|
||||||
|
presentation._input(controller_event)
|
||||||
|
await process_frame
|
||||||
|
_expect(
|
||||||
|
not focus_arrow.visible,
|
||||||
|
"controller input stole presentation from the held virtual pointer",
|
||||||
|
)
|
||||||
|
presentation.set_virtual_pointer_active(false)
|
||||||
|
presentation._input(controller_event)
|
||||||
|
await process_frame
|
||||||
|
_expect(
|
||||||
|
focus_arrow.visible,
|
||||||
|
"controller focus presentation did not resume after virtual-pointer use",
|
||||||
|
)
|
||||||
|
|
||||||
stage.queue_free()
|
stage.queue_free()
|
||||||
if _failures.is_empty():
|
if _failures.is_empty():
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ extends SceneTree
|
||||||
const JoinGamePageScene = preload(
|
const JoinGamePageScene = preload(
|
||||||
"res://ui/network/join_game_page.tscn"
|
"res://ui/network/join_game_page.tscn"
|
||||||
)
|
)
|
||||||
|
const TitleScreenScene = preload("res://ui/title_screen.tscn")
|
||||||
|
const PauseMenuScene = preload("res://ui/pause_menu.tscn")
|
||||||
const SettingsPanelScene = preload("res://ui/settings_panel.tscn")
|
const SettingsPanelScene = preload("res://ui/settings_panel.tscn")
|
||||||
const BubbleConfirmationScene = preload(
|
const BubbleConfirmationScene = preload(
|
||||||
"res://ui/components/bubble_menu/bubble_confirmation_page.tscn"
|
"res://ui/components/bubble_menu/bubble_confirmation_page.tscn"
|
||||||
|
|
@ -40,6 +42,7 @@ func _initialize() -> void:
|
||||||
|
|
||||||
func _run() -> void:
|
func _run() -> void:
|
||||||
root.size = Vector2i(1280, 720)
|
root.size = Vector2i(1280, 720)
|
||||||
|
await _validate_primary_menu_navigation()
|
||||||
await _validate_join_game_navigation()
|
await _validate_join_game_navigation()
|
||||||
await _validate_data_settings_navigation()
|
await _validate_data_settings_navigation()
|
||||||
await _validate_settings_adjustment_navigation()
|
await _validate_settings_adjustment_navigation()
|
||||||
|
|
@ -61,6 +64,49 @@ func _run() -> void:
|
||||||
quit(1)
|
quit(1)
|
||||||
|
|
||||||
|
|
||||||
|
func _validate_primary_menu_navigation() -> void:
|
||||||
|
var title := TitleScreenScene.instantiate() as Control
|
||||||
|
root.add_child(title)
|
||||||
|
await process_frame
|
||||||
|
(title.get_node("%ButtonCenter") as Control).show()
|
||||||
|
(title.get_node("%BubbleField") as Control).show()
|
||||||
|
title.call("_update_title_layout")
|
||||||
|
title.call("_set_title_bubbles_interactive", true)
|
||||||
|
var title_controls: Array[Control] = []
|
||||||
|
for candidate: Variant in title.call("_get_title_buttons"):
|
||||||
|
var control := candidate as Control
|
||||||
|
if control is BaseButton:
|
||||||
|
(control as BaseButton).disabled = false
|
||||||
|
title_controls.append(control)
|
||||||
|
_expect(
|
||||||
|
title_controls.size() == 7,
|
||||||
|
"Title menu does not expose all seven primary actions.",
|
||||||
|
)
|
||||||
|
_assert_directionally_reachable(title_controls.front(), title_controls)
|
||||||
|
title.queue_free()
|
||||||
|
await process_frame
|
||||||
|
|
||||||
|
var pause := PauseMenuScene.instantiate() as Control
|
||||||
|
root.add_child(pause)
|
||||||
|
pause.show()
|
||||||
|
var root_page := pause.get_node("%RootPage") as Control
|
||||||
|
root_page.call("show_page", false)
|
||||||
|
for _frame: int in 2:
|
||||||
|
await process_frame
|
||||||
|
var pause_controls: Array[Control] = [
|
||||||
|
pause.get_node("%ResumeButton") as Control,
|
||||||
|
pause.get_node("%SaveButton") as Control,
|
||||||
|
pause.get_node("%JoinGameButton") as Control,
|
||||||
|
pause.get_node("%SettingsButton") as Control,
|
||||||
|
pause.get_node("%ReturnToTitleButton") as Control,
|
||||||
|
pause.get_node("%ResetProgressButton") as Control,
|
||||||
|
pause.get_node("%QuitButton") as Control,
|
||||||
|
]
|
||||||
|
_assert_directionally_reachable(pause_controls.front(), pause_controls)
|
||||||
|
pause.queue_free()
|
||||||
|
await process_frame
|
||||||
|
|
||||||
|
|
||||||
func _validate_join_game_navigation() -> void:
|
func _validate_join_game_navigation() -> void:
|
||||||
var page := JoinGamePageScene.instantiate() as Control
|
var page := JoinGamePageScene.instantiate() as Control
|
||||||
root.add_child(page)
|
root.add_child(page)
|
||||||
|
|
@ -541,12 +587,21 @@ func _validate_settings_presentation_requires_apply() -> void:
|
||||||
_expect(
|
_expect(
|
||||||
not settings_manager.current_settings.chat_dock_right
|
not settings_manager.current_settings.chat_dock_right
|
||||||
and not settings_manager.current_settings.chat_mobile_mode
|
and not settings_manager.current_settings.chat_mobile_mode
|
||||||
and settings_manager.current_settings.paint_dock_right,
|
and settings_manager.current_settings.paint_dock_right
|
||||||
|
and not settings_manager.current_settings.presentation_layout_customized,
|
||||||
(
|
(
|
||||||
"Navigating presentation choices changed saved docking settings "
|
"Navigating presentation choices changed saved docking settings "
|
||||||
+ "before Apply."
|
+ "before Apply."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
panel.call("_apply_settings")
|
||||||
|
_expect(
|
||||||
|
settings_manager.current_settings.chat_dock_right
|
||||||
|
and settings_manager.current_settings.chat_mobile_mode
|
||||||
|
and not settings_manager.current_settings.paint_dock_right
|
||||||
|
and settings_manager.current_settings.presentation_layout_customized,
|
||||||
|
"Applying deliberate presentation choices did not retain them.",
|
||||||
|
)
|
||||||
panel.close_panel(true)
|
panel.close_panel(true)
|
||||||
panel.queue_free()
|
panel.queue_free()
|
||||||
settings_manager.queue_free()
|
settings_manager.queue_free()
|
||||||
|
|
@ -622,6 +677,7 @@ func _validate_profile_confirmation_focus() -> void:
|
||||||
account_controls.append(item as Control)
|
account_controls.append(item as Control)
|
||||||
var preview := page.get("_preview") as Control
|
var preview := page.get("_preview") as Control
|
||||||
var reset_view := page.get("_reset_view_button") as Button
|
var reset_view := page.get("_reset_view_button") as Button
|
||||||
|
var reset_view_hint := page.get("_reset_view_controller_hint") as Label
|
||||||
_expect(
|
_expect(
|
||||||
suggestion in account_controls,
|
suggestion in account_controls,
|
||||||
"Profile name-conflict choices are outside the account zone.",
|
"Profile name-conflict choices are outside the account zone.",
|
||||||
|
|
@ -638,6 +694,34 @@ func _validate_profile_confirmation_focus() -> void:
|
||||||
reset_view.focus_mode == Control.FOCUS_NONE,
|
reset_view.focus_mode == Control.FOCUS_NONE,
|
||||||
"The profile reset-view button must not take controller focus.",
|
"The profile reset-view button must not take controller focus.",
|
||||||
)
|
)
|
||||||
|
_expect(
|
||||||
|
reset_view_hint != null
|
||||||
|
and reset_view_hint.text == "Reset View: RS"
|
||||||
|
and reset_view_hint.focus_mode == Control.FOCUS_NONE,
|
||||||
|
"The profile preview does not present its right-stick reset hint.",
|
||||||
|
)
|
||||||
|
preview.set("_camera_yaw", 1.0)
|
||||||
|
preview.set("_camera_pitch", 0.4)
|
||||||
|
preview.set("_camera_distance", 2.5)
|
||||||
|
var reset_view_event := InputEventJoypadButton.new()
|
||||||
|
reset_view_event.button_index = JOY_BUTTON_RIGHT_STICK
|
||||||
|
reset_view_event.pressed = true
|
||||||
|
_expect(
|
||||||
|
bool(page.call("handle_controller_input", reset_view_event)),
|
||||||
|
"Profile customization did not consume right-stick reset.",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
is_equal_approx(float(preview.get("_camera_yaw")), 0.0)
|
||||||
|
and is_equal_approx(
|
||||||
|
float(preview.get("_camera_pitch")),
|
||||||
|
ProfilePreview.DEFAULT_CAMERA_PITCH,
|
||||||
|
)
|
||||||
|
and is_equal_approx(
|
||||||
|
float(preview.get("_camera_distance")),
|
||||||
|
ProfilePreview.DEFAULT_CAMERA_DISTANCE,
|
||||||
|
),
|
||||||
|
"Right-stick click did not restore the default profile preview view.",
|
||||||
|
)
|
||||||
page.call("_show_confirmation", "defaults")
|
page.call("_show_confirmation", "defaults")
|
||||||
var confirmation := page.get("_discard_confirmation") as Control
|
var confirmation := page.get("_discard_confirmation") as Control
|
||||||
var confirm := page.get("_confirmation_confirm") as Button
|
var confirm := page.get("_confirmation_confirm") as Button
|
||||||
|
|
@ -1087,11 +1171,13 @@ func _validate_inventory_tab_zone_transitions() -> void:
|
||||||
inventory_state.add_child(node)
|
inventory_state.add_child(node)
|
||||||
bag.setup(ItemCatalogResource)
|
bag.setup(ItemCatalogResource)
|
||||||
layout.setup(bag, catches, ItemCatalogResource, storage_capacity)
|
layout.setup(bag, catches, ItemCatalogResource, storage_capacity)
|
||||||
|
assert(layout.restore_backpack_level(1))
|
||||||
bag.set_inventory_layout(layout)
|
bag.set_inventory_layout(layout)
|
||||||
catches.set_inventory_layout(layout)
|
catches.set_inventory_layout(layout)
|
||||||
hotbar.setup(bag, ItemCatalogResource, catches, layout)
|
hotbar.setup(bag, ItemCatalogResource, catches, layout)
|
||||||
assert(bag.add_item(&"basic_fishing_rod", 1))
|
assert(bag.add_item(&"basic_fishing_rod", 1))
|
||||||
assert(bag.add_item(&"coffee", 1))
|
assert(bag.add_item(&"coffee", 1))
|
||||||
|
assert(bag.add_item(&"worms", 1))
|
||||||
assert(layout.move_entry(
|
assert(layout.move_entry(
|
||||||
PlayerInventoryLayout.EntryKind.ITEM,
|
PlayerInventoryLayout.EntryKind.ITEM,
|
||||||
&"basic_fishing_rod",
|
&"basic_fishing_rod",
|
||||||
|
|
@ -1415,6 +1501,47 @@ func _validate_inventory_tab_zone_transitions() -> void:
|
||||||
)
|
)
|
||||||
for _frame: int in 2:
|
for _frame: int in 2:
|
||||||
await process_frame
|
await process_frame
|
||||||
|
|
||||||
|
menu.call("_show_section_immediate", PlayerMenuType.Section.TACKLE_BOX)
|
||||||
|
menu.call("_set_content_interactive", true)
|
||||||
|
menu.set(
|
||||||
|
"_controller_ownership",
|
||||||
|
PlayerMenuType.ControllerOwnership.ITEM_LIST,
|
||||||
|
)
|
||||||
|
menu.call("_apply_inventory_controller_zone_focus_modes")
|
||||||
|
menu.call("_configure_tackle_item_focus")
|
||||||
|
for _frame: int in 2:
|
||||||
|
await process_frame
|
||||||
|
var tackle_buttons: Dictionary = menu.get("_tackle_item_buttons")
|
||||||
|
var worms_button := tackle_buttons.get(&"worms") as Button
|
||||||
|
_expect(worms_button != null, "Tackle test could not find unlocked worms.")
|
||||||
|
if worms_button != null:
|
||||||
|
worms_button.grab_focus()
|
||||||
|
menu.call("_handle_controller_ownership_input", accept)
|
||||||
|
menu.call("_handle_controller_ownership_input", accept_release)
|
||||||
|
_expect(
|
||||||
|
StringName(menu.get("_selected_tackle_item_id")).is_empty(),
|
||||||
|
"Controller A still activates a bait or lure inventory action.",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
not (menu.get_node("%TackleDetailPanel") as Control).visible,
|
||||||
|
"Controller A opened the tackle notepad reserved for Y.",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
bool(menu.call(
|
||||||
|
"_handle_controller_ownership_input", context_press
|
||||||
|
)),
|
||||||
|
"Controller Y did not open the bait/lure equip notepad.",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
menu.get("_controller_ownership")
|
||||||
|
== PlayerMenuType.ControllerOwnership.NOTEPAD_ACTIONS,
|
||||||
|
"Tackle notepad did not take controller ownership after Y.",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
(menu.get_node("%TackleDetailPanel") as Control).visible,
|
||||||
|
"Controller Y left the bait/lure equip notepad hidden.",
|
||||||
|
)
|
||||||
menu.queue_free()
|
menu.queue_free()
|
||||||
inventory_state.queue_free()
|
inventory_state.queue_free()
|
||||||
await process_frame
|
await process_frame
|
||||||
|
|
|
||||||
|
|
@ -215,6 +215,13 @@ func _validate_settings_navigation_contract() -> void:
|
||||||
settings.crisp_reset_focus_requested.connect(
|
settings.crisp_reset_focus_requested.connect(
|
||||||
reset_overlay.focus_reset_button
|
reset_overlay.focus_reset_button
|
||||||
)
|
)
|
||||||
|
var reset_cursor_presentation := reset_overlay.get_node(
|
||||||
|
"ControllerFocusPresentation"
|
||||||
|
) as ControllerFocusPresentation
|
||||||
|
var controller_right := InputEventJoypadButton.new()
|
||||||
|
controller_right.button_index = JOY_BUTTON_DPAD_RIGHT
|
||||||
|
controller_right.pressed = true
|
||||||
|
reset_cursor_presentation._input(controller_right)
|
||||||
var focus_reset_event := InputEventAction.new()
|
var focus_reset_event := InputEventAction.new()
|
||||||
focus_reset_event.action = &"ui_right"
|
focus_reset_event.action = &"ui_right"
|
||||||
focus_reset_event.pressed = true
|
focus_reset_event.pressed = true
|
||||||
|
|
@ -224,6 +231,9 @@ func _validate_settings_navigation_contract() -> void:
|
||||||
root.gui_get_focus_owner()
|
root.gui_get_focus_owner()
|
||||||
== reset_overlay.get_node("%ResetPixelationButton")
|
== reset_overlay.get_node("%ResetPixelationButton")
|
||||||
)
|
)
|
||||||
|
assert(
|
||||||
|
(reset_cursor_presentation.get("_focus_arrow") as TextureRect).visible
|
||||||
|
)
|
||||||
reset_overlay.return_to_settings_requested.connect(
|
reset_overlay.return_to_settings_requested.connect(
|
||||||
settings.focus_back_button
|
settings.focus_back_button
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -806,15 +806,15 @@ func _test_fishing_shop_sale_ui(
|
||||||
assert(bait_receipt.contains(" spent on "))
|
assert(bait_receipt.contains(" spent on "))
|
||||||
assert(bait_receipt.ends_with(" worms"))
|
assert(bait_receipt.ends_with(" worms"))
|
||||||
assert(snails_button.get_node_or_null("SupplyQuantityBadge") == null)
|
assert(snails_button.get_node_or_null("SupplyQuantityBadge") == null)
|
||||||
assert(worms_button.get_node_or_null("UnlockStateIcon") == null)
|
assert(worms_button.get_node_or_null("UnlockStateBadge") == null)
|
||||||
var snails_lock_icon := snails_button.get_node(
|
var snails_lock_icon := snails_button.get_node(
|
||||||
"UnlockStateIcon"
|
"UnlockStateBadge/UnlockStateIcon"
|
||||||
) as TextureRect
|
) as TextureRect
|
||||||
assert(not bool(snails_lock_icon.get_meta(&"unlocked")))
|
assert(not bool(snails_lock_icon.get_meta(&"unlocked")))
|
||||||
assert(snails_lock_icon.texture.resource_path.ends_with(
|
assert(snails_lock_icon.texture.resource_path.ends_with(
|
||||||
"/lock_light.png"
|
"/lock_light.png"
|
||||||
))
|
))
|
||||||
assert(is_equal_approx(snails_lock_icon.modulate.a, 0.75))
|
assert(is_equal_approx(snails_lock_icon.modulate.a, 0.88))
|
||||||
await _activate_pointer_control(equipment_tab, ui_viewport)
|
await _activate_pointer_control(equipment_tab, ui_viewport)
|
||||||
await process_frame
|
await process_frame
|
||||||
var equipment_sections: Array[String] = []
|
var equipment_sections: Array[String] = []
|
||||||
|
|
@ -836,7 +836,7 @@ func _test_fishing_shop_sale_ui(
|
||||||
assert(basic_rod_button != null)
|
assert(basic_rod_button != null)
|
||||||
assert(basic_rod_button.custom_minimum_size == Vector2(144.0, 144.0))
|
assert(basic_rod_button.custom_minimum_size == Vector2(144.0, 144.0))
|
||||||
assert(basic_rod_button.icon != null and basic_rod_button.disabled)
|
assert(basic_rod_button.icon != null and basic_rod_button.disabled)
|
||||||
assert(basic_rod_button.get_node_or_null("UnlockStateIcon") == null)
|
assert(basic_rod_button.get_node_or_null("UnlockStateBadge") == null)
|
||||||
assert(rod_cards.find_child("Rod_aurora_rod", true, false) == null)
|
assert(rod_cards.find_child("Rod_aurora_rod", true, false) == null)
|
||||||
await _activate_pointer_control(art_supplies_tab, ui_viewport)
|
await _activate_pointer_control(art_supplies_tab, ui_viewport)
|
||||||
await process_frame
|
await process_frame
|
||||||
|
|
@ -886,8 +886,13 @@ func _test_fishing_shop_sale_ui(
|
||||||
var product_unlocked: bool = (
|
var product_unlocked: bool = (
|
||||||
player.art_unlocks.owns_product(product_id)
|
player.art_unlocks.owns_product(product_id)
|
||||||
)
|
)
|
||||||
var state_icon := upgrade_button.get_node_or_null(
|
var state_badge := upgrade_button.get_node_or_null(
|
||||||
"UnlockStateIcon"
|
"UnlockStateBadge"
|
||||||
|
) as Panel
|
||||||
|
var state_icon := (
|
||||||
|
state_badge.get_node("UnlockStateIcon") as TextureRect
|
||||||
|
if state_badge != null
|
||||||
|
else null
|
||||||
) as TextureRect
|
) as TextureRect
|
||||||
assert((state_icon == null) == product_unlocked)
|
assert((state_icon == null) == product_unlocked)
|
||||||
if state_icon != null:
|
if state_icon != null:
|
||||||
|
|
|
||||||
|
|
@ -20,20 +20,25 @@ func _initialize() -> void:
|
||||||
shop.call(
|
shop.call(
|
||||||
"_add_unlock_state_icon", locked_button, false, Vector2(72.0, 72.0)
|
"_add_unlock_state_icon", locked_button, false, Vector2(72.0, 72.0)
|
||||||
)
|
)
|
||||||
var locked_icon := locked_button.get_node(
|
var locked_badge := locked_button.get_node(
|
||||||
|
"UnlockStateBadge"
|
||||||
|
) as Panel
|
||||||
|
var locked_icon := locked_badge.get_node(
|
||||||
"UnlockStateIcon"
|
"UnlockStateIcon"
|
||||||
) as TextureRect
|
) as TextureRect
|
||||||
|
assert(locked_badge.position == Vector2(4.0, 4.0))
|
||||||
|
assert(locked_badge.size == Vector2(30.0, 30.0))
|
||||||
assert(locked_icon.texture.resource_path.ends_with("/lock_light.png"))
|
assert(locked_icon.texture.resource_path.ends_with("/lock_light.png"))
|
||||||
assert(locked_icon.size == Vector2(48.0, 48.0))
|
assert(locked_icon.size == Vector2(18.0, 18.0))
|
||||||
assert(locked_icon.position == Vector2(12.0, 6.0))
|
assert(locked_icon.position == Vector2(6.0, 6.0))
|
||||||
assert(is_equal_approx(locked_icon.modulate.a, 0.75))
|
assert(is_equal_approx(locked_icon.modulate.a, 0.88))
|
||||||
assert(not bool(locked_icon.get_meta(&"unlocked")))
|
assert(not bool(locked_icon.get_meta(&"unlocked")))
|
||||||
locked_button.free()
|
locked_button.free()
|
||||||
var unlocked_button := Button.new()
|
var unlocked_button := Button.new()
|
||||||
shop.call(
|
shop.call(
|
||||||
"_add_unlock_state_icon", unlocked_button, true, Vector2(72.0, 72.0)
|
"_add_unlock_state_icon", unlocked_button, true, Vector2(72.0, 72.0)
|
||||||
)
|
)
|
||||||
assert(unlocked_button.get_node_or_null("UnlockStateIcon") == null)
|
assert(unlocked_button.get_node_or_null("UnlockStateBadge") == null)
|
||||||
unlocked_button.free()
|
unlocked_button.free()
|
||||||
shop.free()
|
shop.free()
|
||||||
|
|
||||||
|
|
@ -54,13 +59,14 @@ func _initialize() -> void:
|
||||||
)
|
)
|
||||||
assert(crab_net != null)
|
assert(crab_net != null)
|
||||||
assert(crab_net.icon != null)
|
assert(crab_net.icon != null)
|
||||||
assert(crab_net.icon.resource_path.ends_with("/equipment/temp_net.png"))
|
assert(crab_net.icon.resource_path.ends_with("/equipment/net.png"))
|
||||||
var shovel: ItemDataType = ItemCatalogResource.get_available_item_by_id(
|
var shovel: ItemDataType = ItemCatalogResource.get_available_item_by_id(
|
||||||
&"standard_shovel"
|
&"standard_shovel"
|
||||||
)
|
)
|
||||||
assert(shovel != null)
|
assert(shovel != null)
|
||||||
assert(shovel.category == ItemDataType.Category.TOOL)
|
assert(shovel.category == ItemDataType.Category.TOOL)
|
||||||
assert(shovel.icon != null)
|
assert(shovel.icon != null)
|
||||||
|
assert(shovel.icon.resource_path.ends_with("/equipment/shovel.png"))
|
||||||
assert(shovel.equippable)
|
assert(shovel.equippable)
|
||||||
assert(shovel.hotbar_allowed)
|
assert(shovel.hotbar_allowed)
|
||||||
assert(FishingShopStockType.get_price(&"standard_shovel") == 75)
|
assert(FishingShopStockType.get_price(&"standard_shovel") == 75)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@ signal context_requested(slot: GeneralInventorySlot)
|
||||||
signal context_changed(slot: GeneralInventorySlot, text: String, active: bool)
|
signal context_changed(slot: GeneralInventorySlot, text: String, active: bool)
|
||||||
|
|
||||||
const ItemDataType = preload("res://items/item_data.gd")
|
const ItemDataType = preload("res://items/item_data.gd")
|
||||||
const LOCK_ICON: Texture2D = preload("res://ui/icons/pictograms/lock_light.png")
|
const LockedContentPresentationType = preload(
|
||||||
|
"res://ui/components/locked_content_presentation.gd"
|
||||||
|
)
|
||||||
|
|
||||||
var slot_index: int = -1
|
var slot_index: int = -1
|
||||||
var container: int = -1
|
var container: int = -1
|
||||||
|
|
@ -104,8 +106,8 @@ func refresh() -> void:
|
||||||
disabled = _locked
|
disabled = _locked
|
||||||
_apply_icon_geometry()
|
_apply_icon_geometry()
|
||||||
if _locked:
|
if _locked:
|
||||||
_icon.texture = LOCK_ICON
|
_icon.texture = LockedContentPresentationType.ICON
|
||||||
_icon.modulate = Color(UtilityPageStyle.OCEAN_DISABLED, 0.18)
|
_icon.modulate = LockedContentPresentationType.icon_modulate()
|
||||||
var storage_slot := (
|
var storage_slot := (
|
||||||
container == PlayerInventoryLayout.InventoryContainer.STORAGE
|
container == PlayerInventoryLayout.InventoryContainer.STORAGE
|
||||||
)
|
)
|
||||||
|
|
@ -305,7 +307,7 @@ func _apply_style() -> void:
|
||||||
Color(UtilityPageStyle.OCEAN_SELECTED, 0.92), radius
|
Color(UtilityPageStyle.OCEAN_SELECTED, 0.92), radius
|
||||||
)
|
)
|
||||||
var locked := UtilityPageStyle.rounded_style(
|
var locked := UtilityPageStyle.rounded_style(
|
||||||
Color(UtilityPageStyle.OCEAN_FIELD, 0.38), radius
|
LockedContentPresentationType.disabled_background_color(), radius
|
||||||
)
|
)
|
||||||
for state: StringName in [&"normal", &"pressed"]:
|
for state: StringName in [&"normal", &"pressed"]:
|
||||||
add_theme_stylebox_override(state, normal)
|
add_theme_stylebox_override(state, normal)
|
||||||
|
|
@ -332,7 +334,9 @@ func _apply_icon_geometry() -> void:
|
||||||
var is_large: bool = _presentation_size.x >= 70.0
|
var is_large: bool = _presentation_size.x >= 70.0
|
||||||
var margin: float
|
var margin: float
|
||||||
if _locked:
|
if _locked:
|
||||||
var lock_size: float = 24.0 if is_large else 18.0
|
var lock_size: float = LockedContentPresentationType.icon_size_for(
|
||||||
|
_presentation_size
|
||||||
|
).x
|
||||||
margin = maxf(
|
margin = maxf(
|
||||||
(_presentation_size.x - lock_size) * 0.5,
|
(_presentation_size.x - lock_size) * 0.5,
|
||||||
0.0,
|
0.0,
|
||||||
|
|
|
||||||
67
ui/components/locked_content_presentation.gd
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
class_name LockedContentPresentation
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
const UtilityPageStyleType = preload("res://ui/utility_page_style.gd")
|
||||||
|
const ICON: Texture2D = preload("res://ui/icons/pictograms/lock_light.png")
|
||||||
|
|
||||||
|
const LARGE_ICON_SIZE: float = 24.0
|
||||||
|
const COMPACT_ICON_SIZE: float = 18.0
|
||||||
|
const LARGE_PRESENTATION_THRESHOLD: float = 70.0
|
||||||
|
const ICON_ALPHA: float = 0.18
|
||||||
|
const BACKGROUND_ALPHA: float = 0.38
|
||||||
|
|
||||||
|
|
||||||
|
static func icon_size_for(presentation_size: Vector2) -> Vector2:
|
||||||
|
var edge: float = (
|
||||||
|
LARGE_ICON_SIZE
|
||||||
|
if minf(presentation_size.x, presentation_size.y)
|
||||||
|
>= LARGE_PRESENTATION_THRESHOLD
|
||||||
|
else COMPACT_ICON_SIZE
|
||||||
|
)
|
||||||
|
return Vector2(edge, edge)
|
||||||
|
|
||||||
|
|
||||||
|
static func icon_modulate(alpha: float = ICON_ALPHA) -> Color:
|
||||||
|
return Color(UtilityPageStyleType.OCEAN_DISABLED, alpha)
|
||||||
|
|
||||||
|
|
||||||
|
static func disabled_background_color() -> Color:
|
||||||
|
return Color(UtilityPageStyleType.OCEAN_FIELD, BACKGROUND_ALPHA)
|
||||||
|
|
||||||
|
|
||||||
|
static func make_icon_texture(
|
||||||
|
icon_size: int = int(COMPACT_ICON_SIZE),
|
||||||
|
canvas_size: int = icon_size,
|
||||||
|
) -> Texture2D:
|
||||||
|
var source_image: Image = ICON.get_image()
|
||||||
|
if source_image == null or source_image.is_empty():
|
||||||
|
return ICON
|
||||||
|
var image := source_image.duplicate() as Image
|
||||||
|
if image.is_compressed() and image.decompress() != OK:
|
||||||
|
return ICON
|
||||||
|
image.convert(Image.FORMAT_RGBA8)
|
||||||
|
image.resize(
|
||||||
|
icon_size,
|
||||||
|
icon_size,
|
||||||
|
Image.INTERPOLATE_NEAREST,
|
||||||
|
)
|
||||||
|
if canvas_size > icon_size:
|
||||||
|
var canvas := Image.create(
|
||||||
|
canvas_size,
|
||||||
|
canvas_size,
|
||||||
|
false,
|
||||||
|
Image.FORMAT_RGBA8,
|
||||||
|
)
|
||||||
|
canvas.fill(Color.TRANSPARENT)
|
||||||
|
var inset := Vector2i.ONE * ((canvas_size - icon_size) / 2)
|
||||||
|
canvas.blend_rect(
|
||||||
|
image,
|
||||||
|
Rect2i(Vector2i.ZERO, Vector2i(icon_size, icon_size)),
|
||||||
|
inset,
|
||||||
|
)
|
||||||
|
image = canvas
|
||||||
|
var texture := ImageTexture.create_from_image(image)
|
||||||
|
texture.set_meta(&"locked_content_icon", true)
|
||||||
|
texture.set_meta(&"locked_content_icon_size", icon_size)
|
||||||
|
texture.set_meta(&"locked_content_canvas_size", canvas_size)
|
||||||
|
return texture
|
||||||
1
ui/components/locked_content_presentation.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://c0188g32tfjk3
|
||||||
|
|
@ -43,6 +43,13 @@ const HOVER_COLOR_REPLACEMENTS: Dictionary[StringName, StringName] = {
|
||||||
}
|
}
|
||||||
|
|
||||||
var _controller_active: bool = false
|
var _controller_active: bool = false
|
||||||
|
var _virtual_pointer_active: bool = false
|
||||||
|
var _navigation_focus_active: bool = false
|
||||||
|
var _neutral_focus_seed: Control
|
||||||
|
var _releasing_neutral_focus: bool = false
|
||||||
|
var _focus_clear_generation: int = 0
|
||||||
|
var _directional_input_generation: int = 0
|
||||||
|
var _directional_input_in_flight: bool = false
|
||||||
var _focused_control: Control
|
var _focused_control: Control
|
||||||
var _focused_popup: PopupMenu
|
var _focused_popup: PopupMenu
|
||||||
var _popup_scroll_offset: float = 0.0
|
var _popup_scroll_offset: float = 0.0
|
||||||
|
|
@ -64,6 +71,9 @@ func _ready() -> void:
|
||||||
get_viewport().gui_focus_changed.connect(_on_focus_changed)
|
get_viewport().gui_focus_changed.connect(_on_focus_changed)
|
||||||
set_process_input(true)
|
set_process_input(true)
|
||||||
set_process(true)
|
set_process(true)
|
||||||
|
var existing_focus: Control = _active_focus_owner()
|
||||||
|
if existing_focus != null:
|
||||||
|
_capture_neutral_focus_seed(existing_focus)
|
||||||
|
|
||||||
|
|
||||||
func _exit_tree() -> void:
|
func _exit_tree() -> void:
|
||||||
|
|
@ -72,6 +82,17 @@ func _exit_tree() -> void:
|
||||||
|
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
|
if _virtual_pointer_active and (
|
||||||
|
event is InputEventJoypadButton or event is InputEventJoypadMotion
|
||||||
|
):
|
||||||
|
return
|
||||||
|
var directional_navigation: bool = _is_directional_navigation(event)
|
||||||
|
if directional_navigation:
|
||||||
|
_directional_input_generation += 1
|
||||||
|
_directional_input_in_flight = true
|
||||||
|
_clear_directional_input_in_flight.call_deferred(
|
||||||
|
_directional_input_generation
|
||||||
|
)
|
||||||
if event is InputEventJoypadButton:
|
if event is InputEventJoypadButton:
|
||||||
if (event as InputEventJoypadButton).pressed:
|
if (event as InputEventJoypadButton).pressed:
|
||||||
_set_controller_active(true)
|
_set_controller_active(true)
|
||||||
|
|
@ -82,15 +103,32 @@ func _input(event: InputEvent) -> void:
|
||||||
_set_controller_active(true)
|
_set_controller_active(true)
|
||||||
elif event is InputEventMouseMotion:
|
elif event is InputEventMouseMotion:
|
||||||
_set_controller_active(false)
|
_set_controller_active(false)
|
||||||
|
_neutralize_current_navigation_focus()
|
||||||
elif event is InputEventMouseButton:
|
elif event is InputEventMouseButton:
|
||||||
if (event as InputEventMouseButton).pressed:
|
if (event as InputEventMouseButton).pressed:
|
||||||
_set_controller_active(false)
|
_set_controller_active(false)
|
||||||
|
_neutralize_current_navigation_focus()
|
||||||
elif event is InputEventKey:
|
elif event is InputEventKey:
|
||||||
if (event as InputEventKey).pressed:
|
if (event as InputEventKey).pressed:
|
||||||
_set_controller_active(false)
|
_set_controller_active(false)
|
||||||
|
if directional_navigation and _restore_neutral_focus_seed():
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
|
||||||
|
func set_virtual_pointer_active(active: bool) -> void:
|
||||||
|
if _virtual_pointer_active == active:
|
||||||
|
return
|
||||||
|
_virtual_pointer_active = active
|
||||||
|
if active:
|
||||||
|
_set_controller_active(false)
|
||||||
|
_clear_focus_presentation()
|
||||||
|
_restore_native_hover_highlight()
|
||||||
|
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
func _process(_delta: float) -> void:
|
||||||
|
if _virtual_pointer_active:
|
||||||
|
_set_controller_active(false)
|
||||||
|
return
|
||||||
_update_hover_suppression()
|
_update_hover_suppression()
|
||||||
if _controller_active:
|
if _controller_active:
|
||||||
var focused_popup: PopupMenu = _active_focused_popup(get_viewport())
|
var focused_popup: PopupMenu = _active_focused_popup(get_viewport())
|
||||||
|
|
@ -164,6 +202,24 @@ func _set_controller_active(active: bool) -> void:
|
||||||
|
|
||||||
|
|
||||||
func _on_focus_changed(control: Control) -> void:
|
func _on_focus_changed(control: Control) -> void:
|
||||||
|
if _releasing_neutral_focus and control == null:
|
||||||
|
return
|
||||||
|
if control == null:
|
||||||
|
_focus_clear_generation += 1
|
||||||
|
_clear_focus_presentation()
|
||||||
|
_reset_navigation_session_after_focus_clear.call_deferred(
|
||||||
|
_focus_clear_generation
|
||||||
|
)
|
||||||
|
return
|
||||||
|
# A normal transfer between controls briefly reports a null owner. Cancel
|
||||||
|
# that deferred reset so active directional navigation remains continuous.
|
||||||
|
_focus_clear_generation += 1
|
||||||
|
if _directional_input_in_flight and _is_navigation_focus(control):
|
||||||
|
_navigation_focus_active = true
|
||||||
|
_neutral_focus_seed = null
|
||||||
|
elif not _navigation_focus_active and _is_navigation_focus(control):
|
||||||
|
_capture_neutral_focus_seed(control)
|
||||||
|
return
|
||||||
if _controller_active:
|
if _controller_active:
|
||||||
var focused_popup: PopupMenu = _active_focused_popup(get_viewport())
|
var focused_popup: PopupMenu = _active_focused_popup(get_viewport())
|
||||||
if focused_popup != null:
|
if focused_popup != null:
|
||||||
|
|
@ -174,6 +230,68 @@ func _on_focus_changed(control: Control) -> void:
|
||||||
_queue_focus_visibility_update(control)
|
_queue_focus_visibility_update(control)
|
||||||
|
|
||||||
|
|
||||||
|
func _reset_navigation_session_after_focus_clear(generation: int) -> void:
|
||||||
|
if generation != _focus_clear_generation or _active_focus_owner() != null:
|
||||||
|
return
|
||||||
|
_navigation_focus_active = false
|
||||||
|
_neutral_focus_seed = null
|
||||||
|
|
||||||
|
|
||||||
|
func _clear_directional_input_in_flight(generation: int) -> void:
|
||||||
|
if generation == _directional_input_generation:
|
||||||
|
_directional_input_in_flight = false
|
||||||
|
|
||||||
|
|
||||||
|
func _is_directional_navigation(event: InputEvent) -> bool:
|
||||||
|
if event is InputEventKey and (event as InputEventKey).echo:
|
||||||
|
return false
|
||||||
|
return (
|
||||||
|
event.is_action_pressed(&"ui_left")
|
||||||
|
or event.is_action_pressed(&"ui_right")
|
||||||
|
or event.is_action_pressed(&"ui_up")
|
||||||
|
or event.is_action_pressed(&"ui_down")
|
||||||
|
or event.is_action_pressed(&"ui_focus_next")
|
||||||
|
or event.is_action_pressed(&"ui_focus_prev")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _is_navigation_focus(control: Control) -> bool:
|
||||||
|
# Text entry is an explicit interaction rather than menu navigation. It must
|
||||||
|
# retain direct focus for typing, including Chat and dialog fields.
|
||||||
|
return not (control is LineEdit or control is TextEdit)
|
||||||
|
|
||||||
|
|
||||||
|
func _capture_neutral_focus_seed(control: Control) -> void:
|
||||||
|
if not _focus_is_presentable(control) or not _is_navigation_focus(control):
|
||||||
|
return
|
||||||
|
_navigation_focus_active = false
|
||||||
|
_neutral_focus_seed = control
|
||||||
|
_clear_focus_presentation()
|
||||||
|
_releasing_neutral_focus = true
|
||||||
|
control.release_focus()
|
||||||
|
_releasing_neutral_focus = false
|
||||||
|
|
||||||
|
|
||||||
|
func _neutralize_current_navigation_focus() -> void:
|
||||||
|
var focus_owner: Control = _active_focus_owner()
|
||||||
|
if focus_owner != null and _is_navigation_focus(focus_owner):
|
||||||
|
_capture_neutral_focus_seed(focus_owner)
|
||||||
|
else:
|
||||||
|
_navigation_focus_active = false
|
||||||
|
_clear_focus_presentation()
|
||||||
|
|
||||||
|
|
||||||
|
func _restore_neutral_focus_seed() -> bool:
|
||||||
|
if not _focus_is_presentable(_neutral_focus_seed):
|
||||||
|
_neutral_focus_seed = null
|
||||||
|
return false
|
||||||
|
var target: Control = _neutral_focus_seed
|
||||||
|
_neutral_focus_seed = null
|
||||||
|
_navigation_focus_active = true
|
||||||
|
target.grab_focus()
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
func _queue_focus_visibility_update(control: Control) -> void:
|
func _queue_focus_visibility_update(control: Control) -> void:
|
||||||
if control == null:
|
if control == null:
|
||||||
return
|
return
|
||||||
|
|
@ -314,15 +432,23 @@ func _bubble_focus_target_position(control: Control) -> Vector2:
|
||||||
|
|
||||||
|
|
||||||
func _presentation_canvas_scale() -> Vector2:
|
func _presentation_canvas_scale() -> Vector2:
|
||||||
var host := get_parent() as Control
|
var target: Control = _focused_control
|
||||||
if host == null:
|
var target_scale := Vector2.ONE
|
||||||
return Vector2.ONE
|
if target != null and is_instance_valid(target):
|
||||||
var host_scale: Vector2 = (
|
target_scale = (
|
||||||
host.get_global_transform_with_canvas().get_scale().abs()
|
target.get_global_transform_with_canvas().get_scale().abs()
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
# PopupMenu is a Window rather than a Control. Its cursor retains the
|
||||||
|
# canonical host scale used before cross-viewport focus support.
|
||||||
|
var host := get_parent() as Control
|
||||||
|
if host != null:
|
||||||
|
target_scale = (
|
||||||
|
host.get_global_transform_with_canvas().get_scale().abs()
|
||||||
|
)
|
||||||
return Vector2(
|
return Vector2(
|
||||||
maxf(host_scale.x, 0.001),
|
maxf(target_scale.x, 0.001),
|
||||||
maxf(host_scale.y, 0.001),
|
maxf(target_scale.y, 0.001),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -535,8 +661,6 @@ func _update_hover_suppression() -> void:
|
||||||
_restore_native_hover_highlight()
|
_restore_native_hover_highlight()
|
||||||
if hovered != null:
|
if hovered != null:
|
||||||
_suppress_native_hover_highlight(hovered)
|
_suppress_native_hover_highlight(hovered)
|
||||||
|
|
||||||
|
|
||||||
func _hovered_control_in_viewport(viewport: Viewport) -> Control:
|
func _hovered_control_in_viewport(viewport: Viewport) -> Control:
|
||||||
var embedded_windows: Array[Window] = viewport.get_embedded_subwindows()
|
var embedded_windows: Array[Window] = viewport.get_embedded_subwindows()
|
||||||
for index: int in range(embedded_windows.size() - 1, -1, -1):
|
for index: int in range(embedded_windows.size() - 1, -1, -1):
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@ const ShopInteractionType = preload(
|
||||||
)
|
)
|
||||||
const UtilityPageStyleType = preload("res://ui/utility_page_style.gd")
|
const UtilityPageStyleType = preload("res://ui/utility_page_style.gd")
|
||||||
const OrganizerTabType = preload("res://ui/components/organizer_tab.gd")
|
const OrganizerTabType = preload("res://ui/components/organizer_tab.gd")
|
||||||
|
const LockedContentPresentationType = preload(
|
||||||
|
"res://ui/components/locked_content_presentation.gd"
|
||||||
|
)
|
||||||
const UIMotionType = preload("res://ui/ui_motion.gd")
|
const UIMotionType = preload("res://ui/ui_motion.gd")
|
||||||
const ControllerMappingManagerType = preload(
|
const ControllerMappingManagerType = preload(
|
||||||
"res://settings/controller_mapping_manager.gd"
|
"res://settings/controller_mapping_manager.gd"
|
||||||
|
|
@ -66,9 +69,6 @@ const ART_UPGRADE_ICONS: Dictionary[StringName, Texture2D] = {
|
||||||
const CURRENCY_ICON: Texture2D = preload(
|
const CURRENCY_ICON: Texture2D = preload(
|
||||||
"res://items/icons/shop/32_currency.png"
|
"res://items/icons/shop/32_currency.png"
|
||||||
)
|
)
|
||||||
const LOCKED_ITEM_ICON: Texture2D = preload(
|
|
||||||
"res://ui/icons/pictograms/lock_light.png"
|
|
||||||
)
|
|
||||||
|
|
||||||
signal menu_visibility_changed(is_open: bool)
|
signal menu_visibility_changed(is_open: bool)
|
||||||
signal menu_exit_started
|
signal menu_exit_started
|
||||||
|
|
@ -120,8 +120,11 @@ const SUPPLY_PRICE_ICON_GAP: float = 3.0
|
||||||
const BAIT_SUPPLY_BADGE_Y: float = (
|
const BAIT_SUPPLY_BADGE_Y: float = (
|
||||||
UtilityPageStyleType.SUPPLY_BADGE_EDGE_MARGIN
|
UtilityPageStyleType.SUPPLY_BADGE_EDGE_MARGIN
|
||||||
)
|
)
|
||||||
const LOCKED_ITEM_ICON_SIZE := Vector2(48.0, 48.0)
|
const LOCK_BADGE_SIZE := Vector2(30.0, 30.0)
|
||||||
const LOCKED_ITEM_ICON_ALPHA: float = 0.75
|
const LOCK_BADGE_MARGIN := Vector2(4.0, 4.0)
|
||||||
|
const LOCK_BADGE_ICON_SIZE := Vector2(18.0, 18.0)
|
||||||
|
const LOCK_BADGE_ALPHA: float = 0.9
|
||||||
|
const LOCK_BADGE_ICON_ALPHA: float = 0.88
|
||||||
const ROD_CARD_TILE_SIZE := Vector2(144.0, 144.0)
|
const ROD_CARD_TILE_SIZE := Vector2(144.0, 144.0)
|
||||||
const ROD_CARD_HOST_SIZE := Vector2(152.0, 198.0)
|
const ROD_CARD_HOST_SIZE := Vector2(152.0, 198.0)
|
||||||
const ROD_CAROUSEL_HEIGHT: float = 206.0
|
const ROD_CAROUSEL_HEIGHT: float = 206.0
|
||||||
|
|
@ -1434,27 +1437,41 @@ func _configure_marker_icon_tile(
|
||||||
func _add_unlock_state_icon(
|
func _add_unlock_state_icon(
|
||||||
button: Button,
|
button: Button,
|
||||||
unlocked: bool,
|
unlocked: bool,
|
||||||
tile_size: Vector2,
|
_tile_size: Vector2,
|
||||||
price_bubble_y: float = SUPPLY_PRICE_Y,
|
_price_bubble_y: float = SUPPLY_PRICE_Y,
|
||||||
) -> void:
|
) -> void:
|
||||||
if unlocked:
|
if unlocked:
|
||||||
return
|
return
|
||||||
|
var badge := Panel.new()
|
||||||
|
badge.name = "UnlockStateBadge"
|
||||||
|
badge.position = LOCK_BADGE_MARGIN
|
||||||
|
badge.size = LOCK_BADGE_SIZE
|
||||||
|
badge.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
badge.z_index = 3
|
||||||
|
var badge_style := UtilityPageStyleType.rounded_style(
|
||||||
|
Color(UtilityPageStyleType.OCEAN_FIELD, LOCK_BADGE_ALPHA),
|
||||||
|
int(LOCK_BADGE_SIZE.x * 0.5),
|
||||||
|
)
|
||||||
|
badge.add_theme_stylebox_override("panel", badge_style)
|
||||||
|
button.add_child(badge)
|
||||||
var icon := TextureRect.new()
|
var icon := TextureRect.new()
|
||||||
icon.name = "UnlockStateIcon"
|
icon.name = "UnlockStateIcon"
|
||||||
icon.texture = LOCKED_ITEM_ICON
|
icon.texture = LockedContentPresentationType.ICON
|
||||||
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||||
icon.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
icon.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||||
icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
icon.z_index = 3
|
|
||||||
icon.set_meta(&"unlocked", false)
|
icon.set_meta(&"unlocked", false)
|
||||||
icon.size = LOCKED_ITEM_ICON_SIZE
|
icon.size = LOCK_BADGE_ICON_SIZE
|
||||||
icon.position = Vector2(
|
icon.position = Vector2(
|
||||||
(tile_size.x - LOCKED_ITEM_ICON_SIZE.x) * 0.5,
|
(LOCK_BADGE_SIZE.x - LOCK_BADGE_ICON_SIZE.x) * 0.5,
|
||||||
(price_bubble_y - LOCKED_ITEM_ICON_SIZE.y) * 0.5,
|
(LOCK_BADGE_SIZE.y - LOCK_BADGE_ICON_SIZE.y) * 0.5,
|
||||||
)
|
)
|
||||||
icon.modulate.a = LOCKED_ITEM_ICON_ALPHA
|
icon.modulate = Color(
|
||||||
button.add_child(icon)
|
UtilityPageStyleType.OCEAN_TEXT_PRIMARY,
|
||||||
|
LOCK_BADGE_ICON_ALPHA,
|
||||||
|
)
|
||||||
|
badge.add_child(icon)
|
||||||
|
|
||||||
|
|
||||||
func _add_supply_icon_tile(
|
func _add_supply_icon_tile(
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ 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)
|
signal shop_backdrop_visibility_changed(is_visible: bool)
|
||||||
|
signal virtual_pointer_mode_changed(is_active: bool)
|
||||||
|
|
||||||
const VIRTUAL_MOUSE_INPUT_OWNER: StringName = &"controller_virtual_mouse"
|
const VIRTUAL_MOUSE_INPUT_OWNER: StringName = &"controller_virtual_mouse"
|
||||||
const VIRTUAL_MOUSE_TRIGGER_THRESHOLD: float = 0.55
|
const VIRTUAL_MOUSE_TRIGGER_THRESHOLD: float = 0.55
|
||||||
|
|
@ -881,6 +882,7 @@ func _begin_virtual_mouse(device_id: int) -> void:
|
||||||
if _virtual_mouse_active:
|
if _virtual_mouse_active:
|
||||||
return
|
return
|
||||||
_virtual_mouse_active = true
|
_virtual_mouse_active = true
|
||||||
|
virtual_pointer_mode_changed.emit(true)
|
||||||
_virtual_mouse_device_id = maxi(device_id, 0)
|
_virtual_mouse_device_id = maxi(device_id, 0)
|
||||||
_virtual_mouse_stick = (
|
_virtual_mouse_stick = (
|
||||||
_mapped_virtual_mouse_stick()
|
_mapped_virtual_mouse_stick()
|
||||||
|
|
@ -921,6 +923,7 @@ func _end_virtual_mouse() -> void:
|
||||||
if _virtual_mouse_right_pressed:
|
if _virtual_mouse_right_pressed:
|
||||||
_set_virtual_mouse_button(MOUSE_BUTTON_RIGHT, false)
|
_set_virtual_mouse_button(MOUSE_BUTTON_RIGHT, false)
|
||||||
_virtual_mouse_active = false
|
_virtual_mouse_active = false
|
||||||
|
virtual_pointer_mode_changed.emit(false)
|
||||||
_virtual_mouse_trigger_strength = 0.0
|
_virtual_mouse_trigger_strength = 0.0
|
||||||
_virtual_mouse_stick = Vector2.ZERO
|
_virtual_mouse_stick = Vector2.ZERO
|
||||||
_controller_virtual_cursor.visible = false
|
_controller_virtual_cursor.visible = false
|
||||||
|
|
@ -994,6 +997,12 @@ func _poll_virtual_mouse_controller_state() -> void:
|
||||||
_virtual_mouse_trigger_strength = _virtual_mouse_strength_for_device(
|
_virtual_mouse_trigger_strength = _virtual_mouse_strength_for_device(
|
||||||
_virtual_mouse_device_id
|
_virtual_mouse_device_id
|
||||||
)
|
)
|
||||||
|
_sync_virtual_mouse_primary_button(
|
||||||
|
Input.is_joy_button_pressed(
|
||||||
|
_virtual_mouse_device_id,
|
||||||
|
JOY_BUTTON_RIGHT_SHOULDER,
|
||||||
|
)
|
||||||
|
)
|
||||||
_set_virtual_mouse_button(
|
_set_virtual_mouse_button(
|
||||||
MOUSE_BUTTON_RIGHT,
|
MOUSE_BUTTON_RIGHT,
|
||||||
_secondary_click_strength_for_device(
|
_secondary_click_strength_for_device(
|
||||||
|
|
@ -1034,6 +1043,11 @@ func _poll_mapped_virtual_mouse_controller_state() -> void:
|
||||||
_end_virtual_mouse()
|
_end_virtual_mouse()
|
||||||
return
|
return
|
||||||
_virtual_mouse_stick = _mapped_virtual_mouse_stick()
|
_virtual_mouse_stick = _mapped_virtual_mouse_stick()
|
||||||
|
_sync_virtual_mouse_primary_button(
|
||||||
|
_controller_mapping_manager.get_role_strength(
|
||||||
|
ControllerMappingManagerType.ROLE_RB
|
||||||
|
) >= VIRTUAL_MOUSE_TRIGGER_THRESHOLD
|
||||||
|
)
|
||||||
_set_virtual_mouse_button(
|
_set_virtual_mouse_button(
|
||||||
MOUSE_BUTTON_RIGHT,
|
MOUSE_BUTTON_RIGHT,
|
||||||
_controller_mapping_manager.get_role_strength(
|
_controller_mapping_manager.get_role_strength(
|
||||||
|
|
@ -1151,6 +1165,13 @@ func _set_virtual_mouse_button(button: MouseButton, pressed: bool) -> void:
|
||||||
_update_virtual_cursor_position(_virtual_mouse_window_position)
|
_update_virtual_cursor_position(_virtual_mouse_window_position)
|
||||||
|
|
||||||
|
|
||||||
|
func _sync_virtual_mouse_primary_button(pressed: bool) -> void:
|
||||||
|
# Embedded PopupMenu windows can consume the controller release event before
|
||||||
|
# GameUI._input receives it. Polling the physical role while virtual-pointer
|
||||||
|
# mode is held guarantees the synthetic left button cannot remain latched.
|
||||||
|
_set_virtual_mouse_button(MOUSE_BUTTON_LEFT, pressed)
|
||||||
|
|
||||||
|
|
||||||
func _emit_virtual_mouse_motion(relative_motion: Vector2) -> void:
|
func _emit_virtual_mouse_motion(relative_motion: Vector2) -> void:
|
||||||
var motion_event := InputEventMouseMotion.new()
|
var motion_event := InputEventMouseMotion.new()
|
||||||
motion_event.position = _virtual_mouse_window_position
|
motion_event.position = _virtual_mouse_window_position
|
||||||
|
|
|
||||||
BIN
ui/icons/main_menu/delete_save.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
40
ui/icons/main_menu/delete_save.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cujpq4ow85uvo"
|
||||||
|
path="res://.godot/imported/delete_save.png-109b364ded207c94c5c900cd8f4a9bcb.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/icons/main_menu/delete_save.png"
|
||||||
|
dest_files=["res://.godot/imported/delete_save.png-109b364ded207c94c5c900cd8f4a9bcb.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
|
||||||
BIN
ui/icons/moderation_options/moderation_options_ban_light.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bjcoxu6kr8to0"
|
||||||
|
path="res://.godot/imported/moderation_options_ban_light.png-664b6bd4fe95df65f41d3a12c9ee6a40.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/icons/moderation_options/moderation_options_ban_light.png"
|
||||||
|
dest_files=["res://.godot/imported/moderation_options_ban_light.png-664b6bd4fe95df65f41d3a12c9ee6a40.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
|
||||||
BIN
ui/icons/moderation_options/moderation_options_block_light.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://htxi2g3qy1bh"
|
||||||
|
path="res://.godot/imported/moderation_options_block_light.png-48d6ddc186905a599655e8b153fa5b6c.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/icons/moderation_options/moderation_options_block_light.png"
|
||||||
|
dest_files=["res://.godot/imported/moderation_options_block_light.png-48d6ddc186905a599655e8b153fa5b6c.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
|
||||||
BIN
ui/icons/moderation_options/moderation_options_kick_light.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bkey68v7byxky"
|
||||||
|
path="res://.godot/imported/moderation_options_kick_light.png-3dc1d5aa8e3312c17f3a81edd965a73b.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/icons/moderation_options/moderation_options_kick_light.png"
|
||||||
|
dest_files=["res://.godot/imported/moderation_options_kick_light.png-3dc1d5aa8e3312c17f3a81edd965a73b.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
|
||||||
BIN
ui/icons/moderation_options/moderation_options_mute_light.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://f1cjsxeylc72"
|
||||||
|
path="res://.godot/imported/moderation_options_mute_light.png-10dd65374b10f6456b5d4ab813da36a0.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/icons/moderation_options/moderation_options_mute_light.png"
|
||||||
|
dest_files=["res://.godot/imported/moderation_options_mute_light.png-10dd65374b10f6456b5d4ab813da36a0.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
|
||||||
|
|
@ -519,14 +519,17 @@ func _build_compose() -> Control:
|
||||||
page.add_child(_body)
|
page.add_child(_body)
|
||||||
_salutation = OptionButton.new()
|
_salutation = OptionButton.new()
|
||||||
_salutation.position = Vector2(12, 336)
|
_salutation.position = Vector2(12, 336)
|
||||||
_salutation.size = Vector2(180, 46)
|
_salutation.size = Vector2(230, 46)
|
||||||
|
_salutation.alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
for id: String in NetworkMailProtocol.SALUTATIONS:
|
for id: String in NetworkMailProtocol.SALUTATIONS:
|
||||||
_salutation.add_item(SALUTATION_LABELS[id])
|
_salutation.add_item(SALUTATION_LABELS[id])
|
||||||
_salutation.set_item_metadata(_salutation.item_count - 1, id)
|
_salutation.set_item_metadata(_salutation.item_count - 1, id)
|
||||||
page.add_child(_salutation)
|
page.add_child(_salutation)
|
||||||
_signature = Label.new()
|
_signature = Label.new()
|
||||||
_signature.position = Vector2(204, 342)
|
_signature.position = Vector2(262, 336)
|
||||||
_signature.size = Vector2(288, 36)
|
_signature.size = Vector2(230, 46)
|
||||||
|
_signature.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
_signature.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||||
page.add_child(_signature)
|
page.add_child(_signature)
|
||||||
_attachment_kind = OptionButton.new()
|
_attachment_kind = OptionButton.new()
|
||||||
_attachment_kind.position = Vector2(ATTACHMENT_COLUMN_X, 48)
|
_attachment_kind.position = Vector2(ATTACHMENT_COLUMN_X, 48)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_scene load_steps=16 format=3]
|
[gd_scene load_steps=17 format=3]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://ui/pause_menu.gd" id="1_script"]
|
[ext_resource type="Script" path="res://ui/pause_menu.gd" id="1_script"]
|
||||||
[ext_resource type="PackedScene" path="res://ui/settings_panel.tscn" id="2_settings"]
|
[ext_resource type="PackedScene" path="res://ui/settings_panel.tscn" id="2_settings"]
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/settings_dark.png" id="13_settings_dark"]
|
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/settings_dark.png" id="13_settings_dark"]
|
||||||
[ext_resource type="Texture2D" path="res://ui/icons/main_menu/continue.png" id="14_continue"]
|
[ext_resource type="Texture2D" path="res://ui/icons/main_menu/continue.png" id="14_continue"]
|
||||||
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/x_dark.png" id="15_x_dark"]
|
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/x_dark.png" id="15_x_dark"]
|
||||||
|
[ext_resource type="Texture2D" path="res://ui/icons/main_menu/delete_save.png" id="16_delete_save"]
|
||||||
|
|
||||||
[node name="PauseMenu" type="Control"]
|
[node name="PauseMenu" type="Control"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
@ -162,7 +163,9 @@ motion_phase = 3.0
|
||||||
[node name="ResetProgressButton" parent="ResponsivePauseStage/PausePresentationScaleRoot/RootPage/BubbleCluster" instance=ExtResource("5_bubble")]
|
[node name="ResetProgressButton" parent="ResponsivePauseStage/PausePresentationScaleRoot/RootPage/BubbleCluster" instance=ExtResource("5_bubble")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(0, 0)
|
custom_minimum_size = Vector2(0, 0)
|
||||||
text = "reset\nprogress"
|
texture_filter = 1
|
||||||
|
icon = ExtResource("16_delete_save")
|
||||||
|
tooltip_text = "reset progress"
|
||||||
accessibility_name = "reset progress"
|
accessibility_name = "reset progress"
|
||||||
neutral_size = Vector2(126, 120)
|
neutral_size = Vector2(126, 120)
|
||||||
desktop_anchor = Vector2(245, 365)
|
desktop_anchor = Vector2(245, 365)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@ const FADE_DURATION: float = 0.55
|
||||||
const UIReferencePresentationType = preload(
|
const UIReferencePresentationType = preload(
|
||||||
"res://ui/ui_reference_presentation.gd"
|
"res://ui/ui_reference_presentation.gd"
|
||||||
)
|
)
|
||||||
|
const ControllerFocusPresentationType = preload(
|
||||||
|
"res://ui/controller_focus_presentation.gd"
|
||||||
|
)
|
||||||
|
|
||||||
signal reset_requested
|
signal reset_requested
|
||||||
signal return_to_settings_requested
|
signal return_to_settings_requested
|
||||||
|
|
@ -16,9 +19,16 @@ signal return_to_settings_requested
|
||||||
var _fade_tween: Tween
|
var _fade_tween: Tween
|
||||||
var _fade_generation: int = 0
|
var _fade_generation: int = 0
|
||||||
var _settings_open: bool = false
|
var _settings_open: bool = false
|
||||||
|
var _controller_focus_presentation: ControllerFocusPresentationType
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
# This control intentionally lives outside the pixelated UI SubViewport, so
|
||||||
|
# it needs a cursor presenter in its own viewport. Reusing the shared class
|
||||||
|
# keeps its paw geometry and input policy identical to every other menu.
|
||||||
|
_controller_focus_presentation = ControllerFocusPresentationType.new()
|
||||||
|
_controller_focus_presentation.name = "ControllerFocusPresentation"
|
||||||
|
add_child(_controller_focus_presentation)
|
||||||
_reset_button.pressed.connect(reset_requested.emit)
|
_reset_button.pressed.connect(reset_requested.emit)
|
||||||
_reset_button.gui_input.connect(_on_reset_button_gui_input)
|
_reset_button.gui_input.connect(_on_reset_button_gui_input)
|
||||||
get_viewport().size_changed.connect(_update_responsive_layout)
|
get_viewport().size_changed.connect(_update_responsive_layout)
|
||||||
|
|
|
||||||
|
|
@ -357,7 +357,6 @@ var _selected_inventory_kind: int = -1
|
||||||
var _selected_inventory_identity: StringName
|
var _selected_inventory_identity: StringName
|
||||||
var _inventory_move_kind: int = -1
|
var _inventory_move_kind: int = -1
|
||||||
var _inventory_move_identity: StringName
|
var _inventory_move_identity: StringName
|
||||||
var _tackle_move_identity: StringName
|
|
||||||
var _context_tooltip: PanelContainer
|
var _context_tooltip: PanelContainer
|
||||||
var _context_tooltip_label: Label
|
var _context_tooltip_label: Label
|
||||||
var _context_tooltip_source: Control
|
var _context_tooltip_source: Control
|
||||||
|
|
@ -796,6 +795,12 @@ func _handle_controller_ownership_input(event: InputEvent) -> bool:
|
||||||
):
|
):
|
||||||
_cancel_active_item_move()
|
_cancel_active_item_move()
|
||||||
return _try_enter_notepad_controller_ownership()
|
return _try_enter_notepad_controller_ownership()
|
||||||
|
if _current_section == Section.TACKLE_BOX and accept_event:
|
||||||
|
# Bait and lures are unlock collections, not movable inventory slots.
|
||||||
|
# Controller Y owns their contextual equip/notepad action; A is
|
||||||
|
# intentionally inert and consumed so it cannot activate the Button.
|
||||||
|
_cancel_controller_accept_hold()
|
||||||
|
return true
|
||||||
if (
|
if (
|
||||||
event.is_action_pressed("ui_down")
|
event.is_action_pressed("ui_down")
|
||||||
and _controller_focus_is_on_last_inventory_row()
|
and _controller_focus_is_on_last_inventory_row()
|
||||||
|
|
@ -862,13 +867,6 @@ func _activate_inventory_selection() -> void:
|
||||||
var slot := focus_owner as GeneralInventorySlotType
|
var slot := focus_owner as GeneralInventorySlotType
|
||||||
if slot != null:
|
if slot != null:
|
||||||
_on_general_inventory_slot_activated(slot)
|
_on_general_inventory_slot_activated(slot)
|
||||||
elif _current_section == Section.TACKLE_BOX:
|
|
||||||
if focus_owner != null and focus_owner.has_meta(
|
|
||||||
&"controller_tackle_item_id"
|
|
||||||
):
|
|
||||||
_activate_tackle_move(StringName(str(
|
|
||||||
focus_owner.get_meta(&"controller_tackle_item_id")
|
|
||||||
)))
|
|
||||||
|
|
||||||
|
|
||||||
func _cancel_controller_accept_hold() -> void:
|
func _cancel_controller_accept_hold() -> void:
|
||||||
|
|
@ -2500,7 +2498,7 @@ func _populate_tackle_column(
|
||||||
item.max_stack,
|
item.max_stack,
|
||||||
&"QuantityBadge",
|
&"QuantityBadge",
|
||||||
)
|
)
|
||||||
button.pressed.connect(_activate_tackle_move.bind(owned.item_id))
|
button.pressed.connect(_select_tackle_item.bind(owned.item_id))
|
||||||
button.gui_input.connect(
|
button.gui_input.connect(
|
||||||
_on_tackle_button_gui_input.bind(owned.item_id, button)
|
_on_tackle_button_gui_input.bind(owned.item_id, button)
|
||||||
)
|
)
|
||||||
|
|
@ -2521,7 +2519,6 @@ func _populate_tackle_column(
|
||||||
)
|
)
|
||||||
item_list.add_child(button)
|
item_list.add_child(button)
|
||||||
_tackle_item_buttons[owned.item_id] = button
|
_tackle_item_buttons[owned.item_id] = button
|
||||||
_refresh_tackle_move_presentation()
|
|
||||||
|
|
||||||
|
|
||||||
func _tackle_context_text(item: ItemDataType, quantity: int) -> String:
|
func _tackle_context_text(item: ItemDataType, quantity: int) -> String:
|
||||||
|
|
@ -2537,51 +2534,6 @@ func _tackle_context_text(item: ItemDataType, quantity: int) -> String:
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
func _activate_tackle_move(item_id: StringName) -> void:
|
|
||||||
if _bag == null or item_id.is_empty():
|
|
||||||
return
|
|
||||||
var target_slot: int = _bag.get_storage_slot(item_id)
|
|
||||||
if _tackle_move_identity.is_empty():
|
|
||||||
if target_slot < 0:
|
|
||||||
return
|
|
||||||
_tackle_move_identity = item_id
|
|
||||||
_selected_tackle_item_id = item_id
|
|
||||||
_update_tackle_detail()
|
|
||||||
_refresh_tackle_move_presentation()
|
|
||||||
var button := _tackle_item_buttons.get(item_id) as Button
|
|
||||||
if button != null:
|
|
||||||
_show_inventory_context_tooltip(
|
|
||||||
button,
|
|
||||||
"moving %s\nchoose another %s • B cancels" % [
|
|
||||||
button.accessibility_name
|
|
||||||
if not button.accessibility_name.is_empty()
|
|
||||||
else str(
|
|
||||||
button.get_meta(&"inventory_context_text", "item")
|
|
||||||
).get_slice("\n", 0),
|
|
||||||
"bait" if _item_catalog.get_item_by_id(item_id).is_bait()
|
|
||||||
else "lure",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
return
|
|
||||||
var source_id: StringName = _tackle_move_identity
|
|
||||||
_tackle_move_identity = StringName()
|
|
||||||
_refresh_tackle_move_presentation()
|
|
||||||
_hide_inventory_context_tooltip()
|
|
||||||
if source_id == item_id or target_slot < 0:
|
|
||||||
return
|
|
||||||
_bag.move_item_to_storage_slot(source_id, target_slot)
|
|
||||||
|
|
||||||
|
|
||||||
func _refresh_tackle_move_presentation() -> void:
|
|
||||||
for item_id: StringName in _tackle_item_buttons:
|
|
||||||
var button: Button = _tackle_item_buttons[item_id]
|
|
||||||
button.modulate = (
|
|
||||||
Color(1.0, 1.0, 1.0, 0.42)
|
|
||||||
if item_id == _tackle_move_identity
|
|
||||||
else Color.WHITE
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_tackle_button_gui_input(
|
func _on_tackle_button_gui_input(
|
||||||
event: InputEvent,
|
event: InputEvent,
|
||||||
item_id: StringName,
|
item_id: StringName,
|
||||||
|
|
@ -3912,13 +3864,7 @@ func _cancel_inventory_move(hide_tooltip: bool = true) -> bool:
|
||||||
|
|
||||||
|
|
||||||
func _cancel_active_item_move() -> bool:
|
func _cancel_active_item_move() -> bool:
|
||||||
var canceled: bool = _cancel_inventory_move()
|
return _cancel_inventory_move()
|
||||||
if not _tackle_move_identity.is_empty():
|
|
||||||
_tackle_move_identity = StringName()
|
|
||||||
_refresh_tackle_move_presentation()
|
|
||||||
_hide_inventory_context_tooltip()
|
|
||||||
canceled = true
|
|
||||||
return canceled
|
|
||||||
|
|
||||||
|
|
||||||
func _open_inventory_notepad(
|
func _open_inventory_notepad(
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,20 @@ const TOGGLE_STATE_COLOR := Color("c3dfe6")
|
||||||
const DialogControllerNavigationType = preload(
|
const DialogControllerNavigationType = preload(
|
||||||
"res://ui/file_dialog_controller_navigation.gd"
|
"res://ui/file_dialog_controller_navigation.gd"
|
||||||
)
|
)
|
||||||
|
const MODERATION_BAN_ICON: Texture2D = preload(
|
||||||
|
"res://ui/icons/moderation_options/moderation_options_ban_light.png"
|
||||||
|
)
|
||||||
|
const MODERATION_BLOCK_ICON: Texture2D = preload(
|
||||||
|
"res://ui/icons/moderation_options/moderation_options_block_light.png"
|
||||||
|
)
|
||||||
|
const MODERATION_KICK_ICON: Texture2D = preload(
|
||||||
|
"res://ui/icons/moderation_options/moderation_options_kick_light.png"
|
||||||
|
)
|
||||||
|
const MODERATION_MUTE_ICON: Texture2D = preload(
|
||||||
|
"res://ui/icons/moderation_options/moderation_options_mute_light.png"
|
||||||
|
)
|
||||||
|
const MODERATION_BUTTON_SIZE := Vector2(52.0, 40.0)
|
||||||
|
const MODERATION_ICON_SIZE: int = 40
|
||||||
|
|
||||||
enum ControllerZone {
|
enum ControllerZone {
|
||||||
TABS,
|
TABS,
|
||||||
|
|
@ -492,16 +506,15 @@ func _build_active_rows() -> void:
|
||||||
)
|
)
|
||||||
row.add_child(ping)
|
row.add_child(ping)
|
||||||
var mute := Button.new()
|
var mute := Button.new()
|
||||||
mute.text = "unmute" if entry.muted else "mute"
|
var mute_action: String = "unmute" if entry.muted else "mute"
|
||||||
mute.disabled = entry.is_local_player
|
mute.disabled = entry.is_local_player
|
||||||
mute.pressed.connect(_toggle_mute.bind(entry))
|
mute.pressed.connect(_toggle_mute.bind(entry))
|
||||||
UtilityPageStyle.apply_compact_ocean_button(mute)
|
_configure_moderation_button(mute, MODERATION_MUTE_ICON, mute_action)
|
||||||
row.add_child(mute)
|
row.add_child(mute)
|
||||||
var block := Button.new()
|
var block := Button.new()
|
||||||
block.text = "block"
|
|
||||||
block.disabled = entry.is_local_player
|
block.disabled = entry.is_local_player
|
||||||
block.pressed.connect(_confirm_block.bind(entry))
|
block.pressed.connect(_confirm_block.bind(entry))
|
||||||
UtilityPageStyle.apply_compact_ocean_button(block)
|
_configure_moderation_button(block, MODERATION_BLOCK_ICON, "block")
|
||||||
row.add_child(block)
|
row.add_child(block)
|
||||||
if entry.can_manage_operator:
|
if entry.can_manage_operator:
|
||||||
var operator := Button.new()
|
var operator := Button.new()
|
||||||
|
|
@ -510,19 +523,50 @@ func _build_active_rows() -> void:
|
||||||
UtilityPageStyle.apply_compact_ocean_button(operator)
|
UtilityPageStyle.apply_compact_ocean_button(operator)
|
||||||
row.add_child(operator)
|
row.add_child(operator)
|
||||||
var kick := Button.new()
|
var kick := Button.new()
|
||||||
kick.text = "kick"
|
|
||||||
kick.disabled = not entry.can_kick
|
kick.disabled = not entry.can_kick
|
||||||
kick.pressed.connect(_confirm_kick.bind(entry))
|
kick.pressed.connect(_confirm_kick.bind(entry))
|
||||||
UtilityPageStyle.apply_compact_ocean_button(kick)
|
_configure_moderation_button(kick, MODERATION_KICK_ICON, "kick")
|
||||||
row.add_child(kick)
|
row.add_child(kick)
|
||||||
var ban := Button.new()
|
var ban := Button.new()
|
||||||
ban.text = "ban"
|
|
||||||
ban.disabled = not entry.can_ban
|
ban.disabled = not entry.can_ban
|
||||||
ban.pressed.connect(_confirm_ban.bind(entry))
|
ban.pressed.connect(_confirm_ban.bind(entry))
|
||||||
UtilityPageStyle.apply_compact_ocean_button(ban)
|
_configure_moderation_button(ban, MODERATION_BAN_ICON, "ban")
|
||||||
row.add_child(ban)
|
row.add_child(ban)
|
||||||
|
|
||||||
|
|
||||||
|
func _configure_moderation_button(
|
||||||
|
button: Button,
|
||||||
|
button_icon: Texture2D,
|
||||||
|
action_label: String,
|
||||||
|
) -> void:
|
||||||
|
button.text = ""
|
||||||
|
button.icon = button_icon
|
||||||
|
button.tooltip_text = action_label
|
||||||
|
button.accessibility_name = action_label
|
||||||
|
button.expand_icon = true
|
||||||
|
button.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
button.vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||||
|
button.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||||
|
UtilityPageStyle.apply_compact_ocean_button(button)
|
||||||
|
button.custom_minimum_size = MODERATION_BUTTON_SIZE
|
||||||
|
button.add_theme_constant_override("icon_max_width", MODERATION_ICON_SIZE)
|
||||||
|
button.add_theme_color_override(
|
||||||
|
"icon_disabled_color",
|
||||||
|
Color(1.0, 1.0, 1.0, 0.78),
|
||||||
|
)
|
||||||
|
for state: StringName in [
|
||||||
|
&"normal", &"hover", &"pressed", &"focus", &"disabled",
|
||||||
|
]:
|
||||||
|
var style := button.get_theme_stylebox(state).duplicate() as StyleBoxFlat
|
||||||
|
if style == null:
|
||||||
|
continue
|
||||||
|
style.content_margin_left = 6.0
|
||||||
|
style.content_margin_right = 6.0
|
||||||
|
style.content_margin_top = 0.0
|
||||||
|
style.content_margin_bottom = 0.0
|
||||||
|
button.add_theme_stylebox_override(state, style)
|
||||||
|
|
||||||
|
|
||||||
func _build_session_artwork_controls() -> void:
|
func _build_session_artwork_controls() -> void:
|
||||||
var counts: Vector2i = _service.get_session_artwork_counts()
|
var counts: Vector2i = _service.get_session_artwork_counts()
|
||||||
var row := _make_row()
|
var row := _make_row()
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ var _apply_button: Button
|
||||||
var _revert_button: Button
|
var _revert_button: Button
|
||||||
var _defaults_button: Button
|
var _defaults_button: Button
|
||||||
var _reset_view_button: Button
|
var _reset_view_button: Button
|
||||||
|
var _reset_view_controller_hint: Label
|
||||||
var _discard_confirmation: PanelContainer
|
var _discard_confirmation: PanelContainer
|
||||||
var _confirmation_label: Label
|
var _confirmation_label: Label
|
||||||
var _confirmation_confirm: Button
|
var _confirmation_confirm: Button
|
||||||
|
|
@ -226,6 +227,15 @@ func consume_escape() -> bool:
|
||||||
func handle_controller_input(event: InputEvent) -> bool:
|
func handle_controller_input(event: InputEvent) -> bool:
|
||||||
if not _profile_active or not _profile_interactive:
|
if not _profile_active or not _profile_interactive:
|
||||||
return false
|
return false
|
||||||
|
var reset_view_pressed := _event_matches_controller_press(
|
||||||
|
event,
|
||||||
|
&"sneak",
|
||||||
|
ControllerMappingManagerType.ROLE_RIGHT_STICK_CLICK,
|
||||||
|
JOY_BUTTON_RIGHT_STICK,
|
||||||
|
)
|
||||||
|
if reset_view_pressed:
|
||||||
|
_preview.reset_view()
|
||||||
|
return true
|
||||||
if (
|
if (
|
||||||
event.is_action_pressed(&"ui_up")
|
event.is_action_pressed(&"ui_up")
|
||||||
or event.is_action_pressed(&"ui_down")
|
or event.is_action_pressed(&"ui_down")
|
||||||
|
|
@ -794,6 +804,17 @@ func _build_ui() -> void:
|
||||||
UtilityPageStyle.apply_compact_ocean_button(_reset_view_button)
|
UtilityPageStyle.apply_compact_ocean_button(_reset_view_button)
|
||||||
_reset_view_button.add_theme_font_size_override("font_size", 22)
|
_reset_view_button.add_theme_font_size_override("font_size", 22)
|
||||||
preview_layer.add_child(_reset_view_button)
|
preview_layer.add_child(_reset_view_button)
|
||||||
|
_reset_view_controller_hint = Label.new()
|
||||||
|
_reset_view_controller_hint.name = "ResetViewControllerHint"
|
||||||
|
_reset_view_controller_hint.text = "Reset View: RS"
|
||||||
|
_reset_view_controller_hint.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
_reset_view_controller_hint.focus_mode = Control.FOCUS_NONE
|
||||||
|
_reset_view_controller_hint.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
_reset_view_controller_hint.add_theme_font_size_override("font_size", 14)
|
||||||
|
_reset_view_controller_hint.add_theme_color_override(
|
||||||
|
"font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY
|
||||||
|
)
|
||||||
|
preview_stack.add_child(_reset_view_controller_hint)
|
||||||
|
|
||||||
_discard_confirmation = PanelContainer.new()
|
_discard_confirmation = PanelContainer.new()
|
||||||
_discard_confirmation.visible = false
|
_discard_confirmation.visible = false
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,7 @@ var _on_screen_keyboard_enabled: bool = false
|
||||||
var _chat_dock_right: bool = false
|
var _chat_dock_right: bool = false
|
||||||
var _chat_mobile_mode: bool = false
|
var _chat_mobile_mode: bool = false
|
||||||
var _paint_dock_right: bool = true
|
var _paint_dock_right: bool = true
|
||||||
|
var _presentation_layout_edited: bool = false
|
||||||
var _fullscreen_enabled: bool = false
|
var _fullscreen_enabled: bool = false
|
||||||
var _master_volume: float = 1.0
|
var _master_volume: float = 1.0
|
||||||
var _music_volume: float = 1.0
|
var _music_volume: float = 1.0
|
||||||
|
|
@ -941,6 +942,8 @@ func _apply_settings() -> void:
|
||||||
edited.chat_dock_right = _chat_dock_right
|
edited.chat_dock_right = _chat_dock_right
|
||||||
edited.chat_mobile_mode = _chat_mobile_mode
|
edited.chat_mobile_mode = _chat_mobile_mode
|
||||||
edited.paint_dock_right = _paint_dock_right
|
edited.paint_dock_right = _paint_dock_right
|
||||||
|
if _presentation_layout_edited:
|
||||||
|
edited.presentation_layout_customized = true
|
||||||
edited.fullscreen_enabled = _fullscreen_enabled
|
edited.fullscreen_enabled = _fullscreen_enabled
|
||||||
edited.master_volume = _master_volume
|
edited.master_volume = _master_volume
|
||||||
edited.music_volume = _music_volume
|
edited.music_volume = _music_volume
|
||||||
|
|
@ -1000,6 +1003,7 @@ func _load_controls() -> void:
|
||||||
_auto_click_interval_slider.set_value_no_signal(
|
_auto_click_interval_slider.set_value_no_signal(
|
||||||
_auto_click_interval_value
|
_auto_click_interval_value
|
||||||
)
|
)
|
||||||
|
_presentation_layout_edited = false
|
||||||
_settings_manager.restore_audio_levels()
|
_settings_manager.restore_audio_levels()
|
||||||
_refresh_value_labels()
|
_refresh_value_labels()
|
||||||
|
|
||||||
|
|
@ -1081,14 +1085,17 @@ func _set_ui_pixelation(pixel_size: int) -> void:
|
||||||
|
|
||||||
func _on_chat_dock_selected(index: int) -> void:
|
func _on_chat_dock_selected(index: int) -> void:
|
||||||
_chat_dock_right = _chat_dock.get_item_id(index) == 1
|
_chat_dock_right = _chat_dock.get_item_id(index) == 1
|
||||||
|
_presentation_layout_edited = true
|
||||||
|
|
||||||
|
|
||||||
func _on_chat_mode_selected(index: int) -> void:
|
func _on_chat_mode_selected(index: int) -> void:
|
||||||
_chat_mobile_mode = _chat_mode.get_item_id(index) == 1
|
_chat_mobile_mode = _chat_mode.get_item_id(index) == 1
|
||||||
|
_presentation_layout_edited = true
|
||||||
|
|
||||||
|
|
||||||
func _on_paint_dock_selected(index: int) -> void:
|
func _on_paint_dock_selected(index: int) -> void:
|
||||||
_paint_dock_right = _paint_dock.get_item_id(index) == 1
|
_paint_dock_right = _paint_dock.get_item_id(index) == 1
|
||||||
|
_presentation_layout_edited = true
|
||||||
|
|
||||||
|
|
||||||
func _set_fullscreen(enabled: bool) -> void:
|
func _set_fullscreen(enabled: bool) -> void:
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ const UtilityPageStyleType = preload("res://ui/utility_page_style.gd")
|
||||||
const ControllerVirtualCursorType = preload(
|
const ControllerVirtualCursorType = preload(
|
||||||
"res://ui/controller_virtual_cursor.gd"
|
"res://ui/controller_virtual_cursor.gd"
|
||||||
)
|
)
|
||||||
|
const LockedContentPresentationType = preload(
|
||||||
|
"res://ui/components/locked_content_presentation.gd"
|
||||||
|
)
|
||||||
const MARKER_MODE_ICON: Texture2D = preload(
|
const MARKER_MODE_ICON: Texture2D = preload(
|
||||||
"res://items/icons/art/art_kit_marker.png"
|
"res://items/icons/art/art_kit_marker.png"
|
||||||
)
|
)
|
||||||
|
|
@ -29,6 +32,10 @@ const GRID_SIZE_ICONS: Dictionary[int, Texture2D] = {
|
||||||
const TOOLBAR_WIDTH: float = 510.0
|
const TOOLBAR_WIDTH: float = 510.0
|
||||||
const TOOLBAR_HEIGHT: float = 373.0
|
const TOOLBAR_HEIGHT: float = 373.0
|
||||||
const COLOR_RAIL_WIDTH: float = 46.0
|
const COLOR_RAIL_WIDTH: float = 46.0
|
||||||
|
const POPUP_LOCK_ICON_SIZE: int = 24
|
||||||
|
const POPUP_LOCK_CANVAS_SIZE: int = 40
|
||||||
|
const POPUP_LOCK_ICON_ALPHA: float = 0.72
|
||||||
|
const COLOR_LOCK_ICON_ALPHA: float = 0.34
|
||||||
|
|
||||||
@onready var _mode_button: Button = %ModeButton
|
@onready var _mode_button: Button = %ModeButton
|
||||||
@onready var _brush_option: OptionButton = %BrushOption
|
@onready var _brush_option: OptionButton = %BrushOption
|
||||||
|
|
@ -50,6 +57,8 @@ var _marker_mode_material: ShaderMaterial
|
||||||
var _brush_popup_cursor: ControllerVirtualCursorType
|
var _brush_popup_cursor: ControllerVirtualCursorType
|
||||||
var _grid_popup_cursor: ControllerVirtualCursorType
|
var _grid_popup_cursor: ControllerVirtualCursorType
|
||||||
var _applied_marker_icon_color: Color = Color(-1.0, -1.0, -1.0, -1.0)
|
var _applied_marker_icon_color: Color = Color(-1.0, -1.0, -1.0, -1.0)
|
||||||
|
var _brush_option_icons: Dictionary[int, Texture2D] = {}
|
||||||
|
var _popup_lock_icon: Texture2D
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
|
@ -98,6 +107,10 @@ func _ready() -> void:
|
||||||
_configure_pointer_only_controls()
|
_configure_pointer_only_controls()
|
||||||
_brush_popup_cursor = _add_popup_cursor(_brush_option.get_popup())
|
_brush_popup_cursor = _add_popup_cursor(_brush_option.get_popup())
|
||||||
_grid_popup_cursor = _add_popup_cursor(_grid_option.get_popup())
|
_grid_popup_cursor = _add_popup_cursor(_grid_option.get_popup())
|
||||||
|
_popup_lock_icon = LockedContentPresentationType.make_icon_texture(
|
||||||
|
POPUP_LOCK_ICON_SIZE,
|
||||||
|
POPUP_LOCK_CANVAS_SIZE,
|
||||||
|
)
|
||||||
_build_options()
|
_build_options()
|
||||||
_apply_marker_color_to_brush_icons(
|
_apply_marker_color_to_brush_icons(
|
||||||
SurfaceDrawingPalette.get_color(SurfaceDrawingPalette.DEFAULT_COLOR_ID)
|
SurfaceDrawingPalette.get_color(SurfaceDrawingPalette.DEFAULT_COLOR_ID)
|
||||||
|
|
@ -307,19 +320,49 @@ func _build_options() -> void:
|
||||||
button.pressed.connect(_select_color.bind(color_id))
|
button.pressed.connect(_select_color.bind(color_id))
|
||||||
_color_list.add_child(button)
|
_color_list.add_child(button)
|
||||||
_color_buttons[color_id] = button
|
_color_buttons[color_id] = button
|
||||||
|
_add_color_lock_icon(button)
|
||||||
_apply_color_button_style(button, color_id, false)
|
_apply_color_button_style(button, color_id, false)
|
||||||
|
|
||||||
|
|
||||||
|
func _add_color_lock_icon(button: Button) -> void:
|
||||||
|
var icon_size := Vector2.ONE * (
|
||||||
|
LockedContentPresentationType.COMPACT_ICON_SIZE
|
||||||
|
)
|
||||||
|
var icon := TextureRect.new()
|
||||||
|
icon.name = "UnlockStateIcon"
|
||||||
|
icon.texture = LockedContentPresentationType.ICON
|
||||||
|
icon.set_anchors_preset(Control.PRESET_CENTER)
|
||||||
|
icon.offset_left = -icon_size.x * 0.5
|
||||||
|
icon.offset_top = -icon_size.y * 0.5
|
||||||
|
icon.offset_right = icon_size.x * 0.5
|
||||||
|
icon.offset_bottom = icon_size.y * 0.5
|
||||||
|
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
|
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||||
|
icon.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||||
|
icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
icon.modulate = Color(
|
||||||
|
UtilityPageStyleType.OCEAN_TEXT_SECONDARY,
|
||||||
|
COLOR_LOCK_ICON_ALPHA,
|
||||||
|
)
|
||||||
|
icon.z_index = 1
|
||||||
|
icon.visible = false
|
||||||
|
button.add_child(icon)
|
||||||
|
|
||||||
|
|
||||||
func _apply_marker_color_to_brush_icons(marker_color: Color) -> void:
|
func _apply_marker_color_to_brush_icons(marker_color: Color) -> void:
|
||||||
if _applied_marker_icon_color.is_equal_approx(marker_color):
|
if _applied_marker_icon_color.is_equal_approx(marker_color):
|
||||||
return
|
return
|
||||||
_applied_marker_icon_color = marker_color
|
_applied_marker_icon_color = marker_color
|
||||||
|
_brush_option_icons.clear()
|
||||||
for index: int in range(_brush_option.item_count):
|
for index: int in range(_brush_option.item_count):
|
||||||
var brush_size: int = _brush_option.get_item_id(index)
|
var brush_size: int = _brush_option.get_item_id(index)
|
||||||
_brush_option.set_item_icon(
|
_brush_option_icons[brush_size] = _channel_masked_icon(
|
||||||
index,
|
BRUSH_SIZE_ICONS[brush_size], marker_color
|
||||||
_channel_masked_icon(BRUSH_SIZE_ICONS[brush_size], marker_color),
|
|
||||||
)
|
)
|
||||||
|
if _unlocks == null or _unlocks.is_brush_size_unlocked(brush_size):
|
||||||
|
_brush_option.set_item_icon(
|
||||||
|
index, _brush_option_icons[brush_size]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _channel_masked_icon(source: Texture2D, marker_color: Color) -> Texture2D:
|
func _channel_masked_icon(source: Texture2D, marker_color: Color) -> Texture2D:
|
||||||
|
|
@ -371,18 +414,26 @@ func _refresh_unlocks() -> void:
|
||||||
var brush_popup: PopupMenu = _brush_option.get_popup()
|
var brush_popup: PopupMenu = _brush_option.get_popup()
|
||||||
for index: int in range(_brush_option.item_count):
|
for index: int in range(_brush_option.item_count):
|
||||||
var brush_size: int = _brush_option.get_item_id(index)
|
var brush_size: int = _brush_option.get_item_id(index)
|
||||||
brush_popup.set_item_disabled(
|
_apply_popup_unlock_state(
|
||||||
index, not _unlocks.is_brush_size_unlocked(brush_size)
|
brush_popup,
|
||||||
|
index,
|
||||||
|
_unlocks.is_brush_size_unlocked(brush_size),
|
||||||
|
_brush_option_icons.get(brush_size, BRUSH_SIZE_ICONS[brush_size]),
|
||||||
)
|
)
|
||||||
var grid_popup: PopupMenu = _grid_option.get_popup()
|
var grid_popup: PopupMenu = _grid_option.get_popup()
|
||||||
for index: int in range(_grid_option.item_count):
|
for index: int in range(_grid_option.item_count):
|
||||||
var grid_size: int = _grid_option.get_item_id(index)
|
var grid_size: int = _grid_option.get_item_id(index)
|
||||||
grid_popup.set_item_disabled(
|
_apply_popup_unlock_state(
|
||||||
index, not _unlocks.is_grid_size_unlocked(grid_size)
|
grid_popup,
|
||||||
|
index,
|
||||||
|
_unlocks.is_grid_size_unlocked(grid_size),
|
||||||
|
GRID_SIZE_ICONS[grid_size],
|
||||||
)
|
)
|
||||||
for color_id: StringName in _color_buttons:
|
for color_id: StringName in _color_buttons:
|
||||||
var button: Button = _color_buttons[color_id]
|
var button: Button = _color_buttons[color_id]
|
||||||
button.disabled = not _unlocks.is_color_unlocked(color_id)
|
button.disabled = not _unlocks.is_color_unlocked(color_id)
|
||||||
|
var lock_icon := button.get_node("UnlockStateIcon") as TextureRect
|
||||||
|
lock_icon.visible = button.disabled
|
||||||
_apply_color_button_style(
|
_apply_color_button_style(
|
||||||
button,
|
button,
|
||||||
color_id,
|
color_id,
|
||||||
|
|
@ -394,6 +445,25 @@ func _refresh_unlocks() -> void:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _apply_popup_unlock_state(
|
||||||
|
popup: PopupMenu,
|
||||||
|
index: int,
|
||||||
|
unlocked: bool,
|
||||||
|
unlocked_icon: Texture2D,
|
||||||
|
) -> void:
|
||||||
|
popup.set_item_disabled(index, not unlocked)
|
||||||
|
popup.set_item_icon(index, unlocked_icon if unlocked else _popup_lock_icon)
|
||||||
|
popup.set_item_icon_modulate(
|
||||||
|
index,
|
||||||
|
Color.WHITE
|
||||||
|
if unlocked
|
||||||
|
else Color(
|
||||||
|
UtilityPageStyleType.OCEAN_TEXT_SECONDARY,
|
||||||
|
POPUP_LOCK_ICON_ALPHA,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _apply_color_button_style(
|
func _apply_color_button_style(
|
||||||
button: Button,
|
button: Button,
|
||||||
color_id: StringName,
|
color_id: StringName,
|
||||||
|
|
@ -412,7 +482,9 @@ func _apply_color_button_style(
|
||||||
style.set_corner_radius_all(17)
|
style.set_corner_radius_all(17)
|
||||||
button.add_theme_stylebox_override(style_name, style)
|
button.add_theme_stylebox_override(style_name, style)
|
||||||
var disabled_style := StyleBoxFlat.new()
|
var disabled_style := StyleBoxFlat.new()
|
||||||
disabled_style.bg_color = UtilityPageStyleType.OCEAN_DISABLED
|
disabled_style.bg_color = (
|
||||||
|
LockedContentPresentationType.disabled_background_color()
|
||||||
|
)
|
||||||
disabled_style.set_corner_radius_all(17)
|
disabled_style.set_corner_radius_all(17)
|
||||||
button.add_theme_stylebox_override("disabled", disabled_style)
|
button.add_theme_stylebox_override("disabled", disabled_style)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@ const SettingsPanelType = preload("res://ui/settings_panel.gd")
|
||||||
const FullscreenMenuPresentationType = preload(
|
const FullscreenMenuPresentationType = preload(
|
||||||
"res://ui/fullscreen_menu_presentation.gd"
|
"res://ui/fullscreen_menu_presentation.gd"
|
||||||
)
|
)
|
||||||
|
const ControllerFocusNavigationType = preload(
|
||||||
|
"res://ui/controller_focus_navigation.gd"
|
||||||
|
)
|
||||||
const BubbleButtonType = preload(
|
const BubbleButtonType = preload(
|
||||||
"res://ui/components/bubble_menu/bubble_button.gd"
|
"res://ui/components/bubble_menu/bubble_button.gd"
|
||||||
)
|
)
|
||||||
|
|
@ -85,7 +88,6 @@ const BUBBLE_COMPACT_HEIGHT_THRESHOLD: float = 560.0
|
||||||
const START_PROMPT_MIN_SCALE: float = 0.985
|
const START_PROMPT_MIN_SCALE: float = 0.985
|
||||||
const START_PROMPT_MAX_SCALE: float = 1.015
|
const START_PROMPT_MAX_SCALE: float = 1.015
|
||||||
const START_PROMPT_CYCLE_SECONDS: float = 3.0
|
const START_PROMPT_CYCLE_SECONDS: float = 3.0
|
||||||
const DISABLED_BUBBLE_LABEL_ALPHA: float = 0.55
|
|
||||||
const INTRO_PROMPT_FADE_DURATION: float = 0.55
|
const INTRO_PROMPT_FADE_DURATION: float = 0.55
|
||||||
const INTRO_BUBBLE_TRAVEL_DURATION: float = 2.40
|
const INTRO_BUBBLE_TRAVEL_DURATION: float = 2.40
|
||||||
const INTRO_BRANDING_TRAVEL_DURATION: float = 2.0
|
const INTRO_BRANDING_TRAVEL_DURATION: float = 2.0
|
||||||
|
|
@ -110,7 +112,6 @@ enum ConfirmationAction {
|
||||||
@onready var _delete_button: BubbleButtonType = %DeleteSaveButton
|
@onready var _delete_button: BubbleButtonType = %DeleteSaveButton
|
||||||
@onready var _quit_button: BubbleButtonType = %QuitButton
|
@onready var _quit_button: BubbleButtonType = %QuitButton
|
||||||
@onready var _join_game_button: BubbleButtonType = %JoinGameButton
|
@onready var _join_game_button: BubbleButtonType = %JoinGameButton
|
||||||
@onready var _delete_save_label: Label = %DeleteSaveLabel
|
|
||||||
@onready var _feedback_label: RichTextLabel = %FeedbackLabel
|
@onready var _feedback_label: RichTextLabel = %FeedbackLabel
|
||||||
@onready var _confirmation_page: TitleConfirmationBubblePageType = (
|
@onready var _confirmation_page: TitleConfirmationBubblePageType = (
|
||||||
%ConfirmationPage
|
%ConfirmationPage
|
||||||
|
|
@ -446,9 +447,6 @@ func _update_title_layout() -> void:
|
||||||
)
|
)
|
||||||
if not _title_settings_transition_active:
|
if not _title_settings_transition_active:
|
||||||
call_deferred("_capture_title_bubble_rest_position")
|
call_deferred("_capture_title_bubble_rest_position")
|
||||||
_delete_save_label.text = (
|
|
||||||
"delete\nsave" if compact_layout else "delete save"
|
|
||||||
)
|
|
||||||
if _awaiting_start_input and not _title_entry_transition_active:
|
if _awaiting_start_input and not _title_entry_transition_active:
|
||||||
_schedule_intro_presentation()
|
_schedule_intro_presentation()
|
||||||
_update_world_preview_resolution()
|
_update_world_preview_resolution()
|
||||||
|
|
@ -1415,6 +1413,10 @@ func _set_title_bubbles_interactive(interactive: bool) -> void:
|
||||||
if interactive
|
if interactive
|
||||||
else Control.MOUSE_FILTER_IGNORE
|
else Control.MOUSE_FILTER_IGNORE
|
||||||
)
|
)
|
||||||
|
if interactive:
|
||||||
|
ControllerFocusNavigationType.configure_spatial_neighbors(
|
||||||
|
_get_title_buttons()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _capture_title_bubble_rest_position() -> void:
|
func _capture_title_bubble_rest_position() -> void:
|
||||||
|
|
@ -1470,9 +1472,6 @@ func _refresh_save_inspection() -> void:
|
||||||
_inspection = _save_manager.inspect_save()
|
_inspection = _save_manager.inspect_save()
|
||||||
_continue_button.disabled = not _inspection.can_continue()
|
_continue_button.disabled = not _inspection.can_continue()
|
||||||
_delete_button.disabled = not _inspection.can_delete()
|
_delete_button.disabled = not _inspection.can_delete()
|
||||||
_delete_save_label.modulate.a = (
|
|
||||||
DISABLED_BUBBLE_LABEL_ALPHA if _delete_button.disabled else 1.0
|
|
||||||
)
|
|
||||||
_feedback_label.text = _center_feedback_text(_inspection.message)
|
_feedback_label.text = _center_feedback_text(_inspection.message)
|
||||||
if _inspection.status == SaveInspectionType.Status.VALID_SUPPORTED:
|
if _inspection.status == SaveInspectionType.Status.VALID_SUPPORTED:
|
||||||
_feedback_label.text = _get_continue_stats_text()
|
_feedback_label.text = _get_continue_stats_text()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_scene load_steps=22 format=3]
|
[gd_scene load_steps=23 format=3]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://ui/title_screen.gd" id="1_script"]
|
[ext_resource type="Script" path="res://ui/title_screen.gd" id="1_script"]
|
||||||
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
|
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
[ext_resource type="Texture2D" path="res://ui/icons/main_menu/continue.png" id="16_continue"]
|
[ext_resource type="Texture2D" path="res://ui/icons/main_menu/continue.png" id="16_continue"]
|
||||||
[ext_resource type="Texture2D" path="res://ui/icons/main_menu/new_game.png" id="17_new_game"]
|
[ext_resource type="Texture2D" path="res://ui/icons/main_menu/new_game.png" id="17_new_game"]
|
||||||
[ext_resource type="Texture2D" path="res://ui/icons/main_menu/credits.png" id="18_credits"]
|
[ext_resource type="Texture2D" path="res://ui/icons/main_menu/credits.png" id="18_credits"]
|
||||||
|
[ext_resource type="Texture2D" path="res://ui/icons/main_menu/delete_save.png" id="19_delete_save"]
|
||||||
|
|
||||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_title_water"]
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_title_water"]
|
||||||
shader = ExtResource("4_water_shader")
|
shader = ExtResource("4_water_shader")
|
||||||
|
|
@ -384,13 +385,16 @@ offset_left = -11.0
|
||||||
offset_top = 122.0
|
offset_top = 122.0
|
||||||
offset_right = 123.0
|
offset_right = 123.0
|
||||||
offset_bottom = 248.0
|
offset_bottom = 248.0
|
||||||
|
texture_filter = 1
|
||||||
|
icon = ExtResource("19_delete_save")
|
||||||
|
tooltip_text = "delete save"
|
||||||
|
accessibility_name = "delete save"
|
||||||
script = ExtResource("7_bubble_button")
|
script = ExtResource("7_bubble_button")
|
||||||
profile = ExtResource("9_bubble_profile")
|
profile = ExtResource("9_bubble_profile")
|
||||||
neutral_size = Vector2(134, 126)
|
neutral_size = Vector2(134, 126)
|
||||||
desktop_anchor = Vector2(56, 185)
|
desktop_anchor = Vector2(56, 185)
|
||||||
compact_anchor = Vector2(53, 196)
|
compact_anchor = Vector2(53, 196)
|
||||||
compact_minimum_size = Vector2(86, 86)
|
compact_minimum_size = Vector2(86, 86)
|
||||||
label_control_path = NodePath("DeleteSaveLabel")
|
|
||||||
minimum_font_size = 15
|
minimum_font_size = 15
|
||||||
maximum_font_size = 23
|
maximum_font_size = 23
|
||||||
vertical_amplitude = 5.1
|
vertical_amplitude = 5.1
|
||||||
|
|
@ -399,21 +403,6 @@ motion_phase = 3.7
|
||||||
deformation_amplitude = 0.018
|
deformation_amplitude = 0.018
|
||||||
deformation_period = 5.4
|
deformation_period = 5.4
|
||||||
|
|
||||||
[node name="DeleteSaveLabel" type="Label" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter/BubbleLayoutSlot/BubbleMotionRoot/BubbleField/DeleteSaveButton"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
mouse_filter = 2
|
|
||||||
theme_override_colors/font_color = Color(0.035, 0.145, 0.22, 1)
|
|
||||||
theme_override_constants/line_spacing = -3
|
|
||||||
text = "delete save"
|
|
||||||
horizontal_alignment = 1
|
|
||||||
vertical_alignment = 1
|
|
||||||
|
|
||||||
[node name="QuitButton" type="Button" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter/BubbleLayoutSlot/BubbleMotionRoot/BubbleField"]
|
[node name="QuitButton" type="Button" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter/BubbleLayoutSlot/BubbleMotionRoot/BubbleField"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
offset_left = 275.0
|
offset_left = 275.0
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@ func _ready() -> void:
|
||||||
)
|
)
|
||||||
var controller_focus_presentation := ControllerFocusPresentationType.new()
|
var controller_focus_presentation := ControllerFocusPresentationType.new()
|
||||||
_ui_root.add_child(controller_focus_presentation)
|
_ui_root.add_child(controller_focus_presentation)
|
||||||
|
_game_ui.virtual_pointer_mode_changed.connect(
|
||||||
|
controller_focus_presentation.set_virtual_pointer_active
|
||||||
|
)
|
||||||
var root_viewport: Viewport = get_viewport()
|
var root_viewport: Viewport = get_viewport()
|
||||||
root_viewport.size_changed.connect(_resize_presentation)
|
root_viewport.size_changed.connect(_resize_presentation)
|
||||||
_resize_presentation()
|
_resize_presentation()
|
||||||
|
|
|
||||||