Refresh menus chat and player expression
123
ui/animalese_voice.gd
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
class_name AnimaleseVoice
|
||||
extends Node
|
||||
|
||||
const SAMPLE_DIRECTORY := "res://sound/dialogue/animalese/placeholder"
|
||||
const SUPPORTED_CHARACTERS := "abcdefghijklmnopqrstuvwxyz"
|
||||
const DEFAULT_CHARACTERS_PER_SECOND: float = 28.0
|
||||
const POLYPHONY: int = 8
|
||||
const VoiceProfilesType = preload(
|
||||
"res://player/animalese_voice_profiles.gd"
|
||||
)
|
||||
const TypewriterRevealType = preload("res://ui/typewriter_reveal.gd")
|
||||
|
||||
var base_pitch: float = 1.0
|
||||
var volume_db: float = -13.0
|
||||
|
||||
var _samples: Dictionary[String, AudioStream] = {}
|
||||
var _player: AudioStreamPlayer
|
||||
var _playback: AudioStreamPlaybackPolyphonic
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_load_samples()
|
||||
var polyphonic_stream := AudioStreamPolyphonic.new()
|
||||
polyphonic_stream.polyphony = POLYPHONY
|
||||
_player = AudioStreamPlayer.new()
|
||||
_player.name = "AnimaleseAudio"
|
||||
_player.stream = polyphonic_stream
|
||||
add_child(_player)
|
||||
_player.play()
|
||||
_playback = (
|
||||
_player.get_stream_playback() as AudioStreamPlaybackPolyphonic
|
||||
)
|
||||
|
||||
|
||||
func speak_text(
|
||||
owner: Node,
|
||||
text: String,
|
||||
voice_key: String,
|
||||
voice_profile_id: String = VoiceProfilesType.DEFAULT_ID,
|
||||
characters_per_second: float = -1.0,
|
||||
) -> Tween:
|
||||
var speech_tween := owner.create_tween()
|
||||
var resolved_characters_per_second := (
|
||||
characters_per_second
|
||||
if characters_per_second > 0.0
|
||||
else TypewriterRevealType.get_characters_per_second()
|
||||
)
|
||||
var character_seconds := 1.0 / maxf(
|
||||
resolved_characters_per_second,
|
||||
1.0,
|
||||
)
|
||||
var voice_pitch := (
|
||||
base_pitch * VoiceProfilesType.pitch_for(voice_profile_id)
|
||||
)
|
||||
for character_index: int in range(text.length()):
|
||||
speech_tween.tween_interval(character_seconds)
|
||||
speech_tween.tween_callback(
|
||||
_play_character.bind(
|
||||
text.substr(character_index, 1),
|
||||
character_index,
|
||||
text,
|
||||
voice_key,
|
||||
voice_pitch,
|
||||
)
|
||||
)
|
||||
return speech_tween
|
||||
|
||||
|
||||
func _load_samples() -> void:
|
||||
for character_index: int in range(SUPPORTED_CHARACTERS.length()):
|
||||
var character := SUPPORTED_CHARACTERS.substr(character_index, 1)
|
||||
_load_sample(character)
|
||||
_load_sample("fallback")
|
||||
|
||||
|
||||
func _load_sample(sample_name: String) -> void:
|
||||
var sample_path := "%s/%s.wav" % [SAMPLE_DIRECTORY, sample_name]
|
||||
var sample := load(sample_path) as AudioStream
|
||||
if sample != null:
|
||||
_samples[sample_name] = sample
|
||||
|
||||
|
||||
func _play_character(
|
||||
character: String,
|
||||
character_index: int,
|
||||
full_text: String,
|
||||
voice_key: String,
|
||||
voice_pitch: float,
|
||||
) -> void:
|
||||
if _playback == null or character.strip_edges().is_empty():
|
||||
return
|
||||
var normalized := character.to_lower()
|
||||
if normalized in [".", ",", "!", "?", ":", ";", "-", "_"]:
|
||||
return
|
||||
var sample_name := (
|
||||
normalized if SUPPORTED_CHARACTERS.contains(normalized) else "fallback"
|
||||
)
|
||||
var sample: AudioStream = _samples.get(sample_name)
|
||||
if sample == null:
|
||||
return
|
||||
var speaker_variation := (
|
||||
float(posmod(hash(voice_key), 1001)) / 1000.0 - 0.5
|
||||
) * 0.16
|
||||
var character_variation := (
|
||||
float(posmod(hash("%s:%d" % [voice_key, character_index]), 1001))
|
||||
/ 1000.0
|
||||
- 0.5
|
||||
) * 0.10
|
||||
var question_lift := 0.0
|
||||
if full_text.ends_with("?") and character_index >= full_text.length() * 3 / 4:
|
||||
var final_progress := (
|
||||
float(character_index) / maxf(float(full_text.length() - 1), 1.0)
|
||||
)
|
||||
question_lift = lerpf(0.0, 0.14, final_progress)
|
||||
var pitch := clampf(
|
||||
voice_pitch
|
||||
+ speaker_variation
|
||||
+ character_variation
|
||||
+ question_lift,
|
||||
0.72,
|
||||
1.45,
|
||||
)
|
||||
_playback.play_stream(sample, 0.0, volume_db, pitch)
|
||||
1
ui/animalese_voice.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://djst00wf8yntj
|
||||
BIN
ui/assets/logbook/ui_logbook.png
Normal file
|
After Width: | Height: | Size: 113 KiB |
40
ui/assets/logbook/ui_logbook.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bnwbcopsjmi3s"
|
||||
path="res://.godot/imported/ui_logbook.png-e45558834671114c04857bb4a6091575.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/assets/logbook/ui_logbook.png"
|
||||
dest_files=["res://.godot/imported/ui_logbook.png-e45558834671114c04857bb4a6091575.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
|
||||
167
ui/chat_ui.gd
|
|
@ -6,6 +6,7 @@ const RECENT_SECONDS: float = 8.0
|
|||
const SPEECH_SECONDS: float = 6.0
|
||||
const DRAFT_SAVE_DELAY: float = 0.4
|
||||
const IDLE_ALPHA: float = 0.58
|
||||
const CHAT_SURFACE_COLOR := Color(0.025, 0.13, 0.19, 0.94)
|
||||
const ORIGINAL_PANEL_WIDTH: float = 390.0
|
||||
const PANEL_WIDTH: float = ORIGINAL_PANEL_WIDTH * 0.85
|
||||
const COMPACT_HEIGHT: float = 250.0
|
||||
|
|
@ -33,12 +34,31 @@ const CLOCK_SIZE := Vector2(116.0, 34.0)
|
|||
const CLOCK_EDGE_MARGIN: float = 10.0
|
||||
const WEATHER_ICON_SIZE := Vector2(34.0, 34.0)
|
||||
const WEATHER_ICON_GAP: float = 6.0
|
||||
const CHAT_SHOW_ICON: Texture2D = preload(
|
||||
"res://ui/icons/pictograms/arrow_light_right_more.png"
|
||||
)
|
||||
const CHAT_HIDE_ICON: Texture2D = preload(
|
||||
"res://ui/icons/pictograms/arrow_light_left_more.png"
|
||||
)
|
||||
const CHAT_EXPAND_ICON: Texture2D = preload(
|
||||
"res://ui/icons/pictograms/arrow_light_up_more.png"
|
||||
)
|
||||
const CHAT_COMPACT_ICON: Texture2D = preload(
|
||||
"res://ui/icons/pictograms/arrow_light_down_more.png"
|
||||
)
|
||||
const WorldTimeServiceType = preload("res://world/world_time_service.gd")
|
||||
const WorldWeatherServiceType = preload(
|
||||
"res://world/world_weather_service.gd"
|
||||
)
|
||||
const WeatherIconType = preload("res://ui/weather_icon.gd")
|
||||
|
||||
const TypewriterRevealType = preload("res://ui/typewriter_reveal.gd")
|
||||
const AnimaleseVoiceType = preload("res://ui/animalese_voice.gd")
|
||||
const VoiceProfilesType = preload(
|
||||
"res://player/animalese_voice_profiles.gd"
|
||||
)
|
||||
|
||||
|
||||
enum PresentationState {
|
||||
COLLAPSED,
|
||||
COMPACT,
|
||||
|
|
@ -62,6 +82,7 @@ var _height_button: Button
|
|||
var _unread_indicator: Label
|
||||
var _hint: Label
|
||||
var _speech_layer: Control
|
||||
var _animalese_voice: AnimaleseVoiceType
|
||||
var _clock_panel: PanelContainer
|
||||
var _clock_label: Label
|
||||
var _weather_icon: WeatherIconType
|
||||
|
|
@ -379,6 +400,15 @@ func _build_ui() -> void:
|
|||
_collapse_button.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
_collapse_button.focus_mode = Control.FOCUS_ALL
|
||||
_collapse_button.z_index = 3
|
||||
_collapse_button.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
_collapse_button.expand_icon = true
|
||||
_collapse_button.alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
_collapse_button.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
_collapse_button.vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
_collapse_button.add_theme_constant_override(
|
||||
"icon_max_width",
|
||||
roundi(minf(HANDLE_SIZE.x, HANDLE_SIZE.y) * 0.5),
|
||||
)
|
||||
_collapse_button.tooltip_text = "Collapse chat"
|
||||
_collapse_button.pressed.connect(func() -> void:
|
||||
if _presentation_state == PresentationState.COLLAPSED:
|
||||
|
|
@ -405,6 +435,15 @@ func _build_ui() -> void:
|
|||
_height_button.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
_height_button.focus_mode = Control.FOCUS_ALL
|
||||
_height_button.z_index = 3
|
||||
_height_button.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
_height_button.expand_icon = true
|
||||
_height_button.alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
_height_button.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
_height_button.vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
_height_button.add_theme_constant_override(
|
||||
"icon_max_width",
|
||||
roundi(minf(HANDLE_SIZE.x, HANDLE_SIZE.y) * 0.5),
|
||||
)
|
||||
_height_button.pressed.connect(func() -> void:
|
||||
if _presentation_state == PresentationState.COLLAPSED:
|
||||
_visible_state_before_collapse = (
|
||||
|
|
@ -461,6 +500,9 @@ func _build_ui() -> void:
|
|||
_speech_layer.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
_speech_layer.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
add_child(_speech_layer)
|
||||
_animalese_voice = AnimaleseVoiceType.new()
|
||||
_animalese_voice.name = "PlayerAnimaleseVoice"
|
||||
add_child(_animalese_voice)
|
||||
_draft_save_timer = Timer.new()
|
||||
_draft_save_timer.one_shot = true
|
||||
_draft_save_timer.wait_time = DRAFT_SAVE_DELAY
|
||||
|
|
@ -478,7 +520,7 @@ func _build_ui() -> void:
|
|||
|
||||
|
||||
func _chat_panel_style() -> StyleBoxFlat:
|
||||
var style := _borderless_style(Color(0.025, 0.13, 0.19, 0.94))
|
||||
var style := _borderless_style(CHAT_SURFACE_COLOR)
|
||||
if _mobile_mode:
|
||||
style.corner_radius_top_left = 0
|
||||
style.corner_radius_top_right = 0
|
||||
|
|
@ -509,7 +551,7 @@ func _refresh_input_ownership() -> void:
|
|||
|
||||
|
||||
func _clock_panel_style() -> StyleBoxFlat:
|
||||
var style := _borderless_style(Color(0.025, 0.13, 0.19, 0.94))
|
||||
var style := _borderless_style(CHAT_SURFACE_COLOR)
|
||||
style.set_corner_radius_all(12)
|
||||
style.content_margin_left = 10
|
||||
style.content_margin_right = 10
|
||||
|
|
@ -522,6 +564,10 @@ func _borderless_style(color: Color) -> StyleBoxFlat:
|
|||
var style := StyleBoxFlat.new()
|
||||
style.bg_color = color
|
||||
style.set_border_width_all(0)
|
||||
style.anti_aliasing = false
|
||||
style.shadow_size = 0
|
||||
style.shadow_color = Color.TRANSPARENT
|
||||
style.shadow_offset = Vector2.ZERO
|
||||
return style
|
||||
|
||||
|
||||
|
|
@ -530,6 +576,12 @@ func _flat_chat_button_style(color: Color) -> StyleBoxFlat:
|
|||
style.bg_color = color
|
||||
style.set_border_width_all(0)
|
||||
style.set_corner_radius_all(0)
|
||||
if _dock_right:
|
||||
style.corner_radius_top_left = 10
|
||||
style.corner_radius_bottom_left = 10
|
||||
else:
|
||||
style.corner_radius_top_right = 10
|
||||
style.corner_radius_bottom_right = 10
|
||||
style.anti_aliasing = false
|
||||
style.shadow_size = 0
|
||||
style.shadow_color = Color.TRANSPARENT
|
||||
|
|
@ -572,6 +624,18 @@ func _apply_flat_chat_button(button: Button) -> void:
|
|||
button.add_theme_stylebox_override(
|
||||
"disabled", _flat_chat_button_style(UtilityPageStyle.OCEAN_DISABLED)
|
||||
)
|
||||
for state: StringName in [
|
||||
&"normal",
|
||||
&"hover",
|
||||
&"pressed",
|
||||
&"hover_pressed",
|
||||
&"focus",
|
||||
&"disabled",
|
||||
]:
|
||||
button.add_theme_stylebox_override(
|
||||
state,
|
||||
_flat_chat_button_style(CHAT_SURFACE_COLOR),
|
||||
)
|
||||
|
||||
|
||||
func _speech_panel_style() -> StyleBoxFlat:
|
||||
|
|
@ -605,6 +669,8 @@ func _send() -> void:
|
|||
if _send_pending:
|
||||
return
|
||||
var body := _entry.text
|
||||
if _handle_chat_command(body):
|
||||
return
|
||||
_send_pending = true
|
||||
_pending_send_body = NetworkChatProtocol.sanitize_body(body)
|
||||
_entry.editable = false
|
||||
|
|
@ -616,6 +682,59 @@ func _send() -> void:
|
|||
_set_status("Sending…")
|
||||
|
||||
|
||||
func _handle_chat_command(body: String) -> bool:
|
||||
var command_text := body.strip_edges()
|
||||
if not command_text.begins_with("/"):
|
||||
return false
|
||||
var parts: PackedStringArray = command_text.split(" ", false)
|
||||
var command := String(parts[0]).trim_prefix("/").to_lower()
|
||||
match command:
|
||||
"time":
|
||||
_handle_time_command(parts)
|
||||
"":
|
||||
_set_status("Enter a command after /.")
|
||||
_:
|
||||
_set_status("Unknown command: /%s" % command)
|
||||
_entry.clear()
|
||||
_flush_draft()
|
||||
return true
|
||||
|
||||
|
||||
func _handle_time_command(parts: PackedStringArray) -> void:
|
||||
if parts.size() != 2:
|
||||
_set_status("Usage: /time [dawn, day, dusk, night]")
|
||||
return
|
||||
if _session == null or not _session.is_host():
|
||||
_set_status("Only the host can change world time.")
|
||||
return
|
||||
if _world_time == null:
|
||||
_set_status("World time is unavailable.")
|
||||
return
|
||||
var phase_name := String(parts[1]).to_lower()
|
||||
var target_hour: float
|
||||
match phase_name:
|
||||
"dawn":
|
||||
target_hour = WorldTimeServiceType.DAWN_START_HOUR
|
||||
"day":
|
||||
target_hour = WorldTimeServiceType.DAWN_END_HOUR
|
||||
"dusk":
|
||||
target_hour = WorldTimeServiceType.DUSK_START_HOUR
|
||||
"night":
|
||||
target_hour = WorldTimeServiceType.DUSK_END_HOUR
|
||||
_:
|
||||
_set_status("Usage: /time [dawn, day, dusk, night]")
|
||||
return
|
||||
if not _world_time.set_authoritative_time(target_hour):
|
||||
_set_status("World time could not be changed.")
|
||||
return
|
||||
_set_status("")
|
||||
_service.broadcast_system_message(
|
||||
"World time set to %s (%s)."
|
||||
% [phase_name, _world_time.get_clock_text()]
|
||||
)
|
||||
close_chat()
|
||||
|
||||
|
||||
func _on_local_message_confirmed(message: Dictionary) -> void:
|
||||
if (
|
||||
not _send_pending
|
||||
|
|
@ -664,10 +783,27 @@ func _on_message(message: Dictionary) -> void:
|
|||
)
|
||||
bubble.add_child(label)
|
||||
_speech_layer.add_child(bubble)
|
||||
var reveal_seconds := TypewriterRevealType.start(label)
|
||||
var voice_profile_id: String = VoiceProfilesType.DEFAULT_ID
|
||||
var speaker_avatar := _spawn.get_avatar(peer_id)
|
||||
if speaker_avatar != null:
|
||||
voice_profile_id = VoiceProfilesType.sanitized_id(
|
||||
speaker_avatar.get_animalese_voice_id()
|
||||
)
|
||||
_animalese_voice.speak_text(
|
||||
label,
|
||||
label.text,
|
||||
str(peer_id),
|
||||
voice_profile_id,
|
||||
)
|
||||
_speech[peer_id] = {
|
||||
"bubble": bubble,
|
||||
"pointer": pointer,
|
||||
"expires": Time.get_ticks_msec() / 1000.0 + SPEECH_SECONDS,
|
||||
"expires": (
|
||||
Time.get_ticks_msec() / 1000.0
|
||||
+ reveal_seconds
|
||||
+ SPEECH_SECONDS
|
||||
),
|
||||
"fingerprint": str(message.get("sender_fingerprint", "")),
|
||||
}
|
||||
|
||||
|
|
@ -770,6 +906,8 @@ func set_dock_right(should_dock_right: bool) -> void:
|
|||
if not is_node_ready() or _panel == null:
|
||||
return
|
||||
_panel.add_theme_stylebox_override("panel", _chat_panel_style())
|
||||
_apply_flat_chat_button(_collapse_button)
|
||||
_apply_flat_chat_button(_height_button)
|
||||
_refresh_handle_labels(_presentation_state)
|
||||
_layout_presentation(false)
|
||||
|
||||
|
|
@ -800,21 +938,24 @@ func is_mobile_mode() -> bool:
|
|||
func _refresh_handle_labels(state: PresentationState) -> void:
|
||||
var collapsed := state == PresentationState.COLLAPSED
|
||||
var height_state := _visible_state_before_collapse if collapsed else state
|
||||
if _mobile_mode:
|
||||
_collapse_button.text = "v" if collapsed else "^"
|
||||
elif _dock_right:
|
||||
_collapse_button.text = "<" if collapsed else ">"
|
||||
else:
|
||||
_collapse_button.text = ">" if collapsed else "<"
|
||||
_collapse_button.text = ""
|
||||
_collapse_button.icon = CHAT_SHOW_ICON if collapsed else CHAT_HIDE_ICON
|
||||
_collapse_button.tooltip_text = (
|
||||
"Show chat" if collapsed else "Hide chat"
|
||||
)
|
||||
_height_button.text = "v" if height_state == PresentationState.EXPANDED else "^"
|
||||
_collapse_button.accessibility_name = _collapse_button.tooltip_text
|
||||
_height_button.text = ""
|
||||
_height_button.icon = (
|
||||
CHAT_COMPACT_ICON
|
||||
if height_state == PresentationState.EXPANDED
|
||||
else CHAT_EXPAND_ICON
|
||||
)
|
||||
_height_button.tooltip_text = (
|
||||
"Compact chat"
|
||||
if height_state == PresentationState.EXPANDED
|
||||
else "Expand chat history"
|
||||
)
|
||||
_height_button.accessibility_name = _height_button.tooltip_text
|
||||
|
||||
|
||||
func _update_panel_opacity(immediate_recheck: bool = false) -> void:
|
||||
|
|
@ -823,10 +964,8 @@ func _update_panel_opacity(immediate_recheck: bool = false) -> void:
|
|||
if _presentation_state == PresentationState.COLLAPSED:
|
||||
if _height_tween == null or not _height_tween.is_running():
|
||||
_panel.modulate.a = IDLE_ALPHA
|
||||
_collapse_button.modulate.a = (
|
||||
1.0 if _collapsed_has_unread else 0.82
|
||||
)
|
||||
_height_button.modulate.a = 0.82
|
||||
_collapse_button.modulate.a = IDLE_ALPHA
|
||||
_height_button.modulate.a = IDLE_ALPHA
|
||||
return
|
||||
var recent := (
|
||||
Time.get_ticks_msec() / 1000.0 - _last_message_time < RECENT_SECONDS
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
class_name BubbleButton
|
||||
extends Button
|
||||
|
||||
const ICON_FILL_RATIO: float = 0.76
|
||||
|
||||
@export var profile: BubbleMenuProfile
|
||||
|
||||
@export_group("Authored Layout")
|
||||
|
|
@ -37,6 +39,7 @@ func _ready() -> void:
|
|||
resized.connect(_update_pivot)
|
||||
_update_pivot()
|
||||
apply_profile()
|
||||
_apply_icon_presentation(neutral_size)
|
||||
|
||||
|
||||
func apply_layout(
|
||||
|
|
@ -49,6 +52,7 @@ func apply_layout(
|
|||
neutral_position = position
|
||||
presented_size = bubble_size
|
||||
_update_pivot()
|
||||
_apply_icon_presentation(bubble_size)
|
||||
var label_control: Control = get_label_control()
|
||||
var font_size := clampi(
|
||||
roundi(minf(bubble_size.x, bubble_size.y) * font_size_ratio),
|
||||
|
|
@ -63,6 +67,9 @@ func get_layout_size(layout_scale: float, compact: bool) -> Vector2:
|
|||
if compact:
|
||||
bubble_size.x = maxf(bubble_size.x, compact_minimum_size.x)
|
||||
bubble_size.y = maxf(bubble_size.y, compact_minimum_size.y)
|
||||
if _uses_icon_only_presentation():
|
||||
var diameter: float = maxf(bubble_size.x, bubble_size.y)
|
||||
bubble_size = Vector2(diameter, diameter)
|
||||
return bubble_size
|
||||
|
||||
|
||||
|
|
@ -78,6 +85,24 @@ func get_label_control() -> Control:
|
|||
return self
|
||||
|
||||
|
||||
func _uses_icon_only_presentation() -> bool:
|
||||
return icon != null and text.is_empty() and label_control_path.is_empty()
|
||||
|
||||
|
||||
func _apply_icon_presentation(bubble_size: Vector2) -> void:
|
||||
if not _uses_icon_only_presentation():
|
||||
return
|
||||
alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
expand_icon = true
|
||||
texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
add_theme_constant_override(
|
||||
"icon_max_width",
|
||||
maxi(1, roundi(minf(bubble_size.x, bubble_size.y) * ICON_FILL_RATIO)),
|
||||
)
|
||||
|
||||
|
||||
func advance_emphasis(delta: float) -> void:
|
||||
var target: float = 1.0 if _hovered or _focused else 0.0
|
||||
emphasis = move_toward(
|
||||
|
|
@ -122,6 +147,8 @@ func calculate_visual_scale(
|
|||
* motion_scale
|
||||
* lerpf(1.0, profile.emphasized_deformation_scale, emphasis)
|
||||
)
|
||||
if _uses_icon_only_presentation():
|
||||
deformation_amount = 0.0
|
||||
var hover_scale: float = lerpf(
|
||||
1.0,
|
||||
profile.hover_focus_scale,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
[ext_resource type="Texture2D" path="res://items/icons/placeholder/reel_speed_upgrade.svg" id="3_reel"]
|
||||
[ext_resource type="Texture2D" path="res://items/icons/placeholder/barrier_power_upgrade.svg" id="4_barrier"]
|
||||
[ext_resource type="Texture2D" path="res://items/icons/placeholder/fish_coin.svg" id="5_coin"]
|
||||
[ext_resource type="Texture2D" path="res://items/icons/placeholder/cooler_expansion.svg" id="6_cooler"]
|
||||
[ext_resource type="Texture2D" path="res://items/icons/equipment/64_cooler_plus.png" id="6_cooler"]
|
||||
|
||||
[node name="FishingShop" type="Control"]
|
||||
unique_name_in_owner = true
|
||||
|
|
@ -196,7 +196,7 @@ layout_mode = 2
|
|||
theme_override_constants/separation = 9
|
||||
|
||||
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row"]
|
||||
custom_minimum_size = Vector2(44, 44)
|
||||
custom_minimum_size = Vector2(88, 88)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("3_reel")
|
||||
expand_mode = 1
|
||||
|
|
@ -250,7 +250,7 @@ layout_mode = 2
|
|||
theme_override_constants/separation = 9
|
||||
|
||||
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row"]
|
||||
custom_minimum_size = Vector2(44, 44)
|
||||
custom_minimum_size = Vector2(88, 88)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("4_barrier")
|
||||
expand_mode = 1
|
||||
|
|
@ -304,7 +304,7 @@ layout_mode = 2
|
|||
theme_override_constants/separation = 9
|
||||
|
||||
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"]
|
||||
custom_minimum_size = Vector2(44, 44)
|
||||
custom_minimum_size = Vector2(88, 88)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("6_cooler")
|
||||
expand_mode = 1
|
||||
|
|
|
|||
241
ui/game_ui.gd
|
|
@ -12,6 +12,7 @@ const PlayerMenuType = preload("res://ui/player_menu.gd")
|
|||
const PlayerType = preload("res://player/player.gd")
|
||||
const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
||||
const ItemCatalogType = preload("res://items/item_catalog.gd")
|
||||
const ItemDataType = preload("res://items/item_data.gd")
|
||||
const PlayerBagType = preload("res://inventory/player_bag.gd")
|
||||
const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
|
||||
const HotbarUIType = preload("res://ui/hotbar.gd")
|
||||
|
|
@ -57,6 +58,12 @@ const PlayerExperienceType = preload(
|
|||
const UIReferencePresentationType = preload(
|
||||
"res://ui/ui_reference_presentation.gd"
|
||||
)
|
||||
const MAIN_BUBBLE_PROFILE: BubbleMenuProfile = preload(
|
||||
"res://ui/components/bubble_menu/bubble_menu_profile.tres"
|
||||
)
|
||||
const ACTIVE_BAIT_EMPTY_ICON: Texture2D = preload(
|
||||
"res://ui/icons/pictograms/x_dark.png"
|
||||
)
|
||||
|
||||
signal pixelation_settings_visibility_changed(is_visible: bool)
|
||||
signal crisp_reset_focus_requested
|
||||
|
|
@ -85,6 +92,9 @@ const FISHING_PANEL_SHOWCASE_BOTTOM_OFFSET: float = -104.0
|
|||
|
||||
@onready var _status_label: Label = %StatusLabel
|
||||
@onready var _gameplay_transient_hud: Control = %GameplayTransientHUD
|
||||
@onready var _active_bait_button: Button = %ActiveBaitButton
|
||||
@onready var _active_bait_quantity_badge: Panel = %ActiveBaitQuantityBadge
|
||||
@onready var _active_bait_quantity: Label = %ActiveBaitQuantity
|
||||
@onready var _experience_presentation: Control = %ExperiencePresentation
|
||||
@onready var _catch_track: Control = %CatchTrack
|
||||
@onready var _green_catch_progress: ProgressBar = %GreenCatchProgress
|
||||
|
|
@ -101,6 +111,13 @@ const FISHING_PANEL_SHOWCASE_BOTTOM_OFFSET: float = -104.0
|
|||
@onready var _experience_progress: ProgressBar = %ExperienceProgress
|
||||
@onready var _experience_bubble: PanelContainer = %ExperienceBubble
|
||||
@onready var _experience_bubble_label: Label = %ExperienceBubbleLabel
|
||||
const TypewriterRevealType = preload("res://ui/typewriter_reveal.gd")
|
||||
const AnimaleseVoiceType = preload("res://ui/animalese_voice.gd")
|
||||
const SHOP_ANIMALESE_VOICE_ID: String = "natural"
|
||||
const SHOP_ANIMALESE_BASE_PITCH: float = 1.08
|
||||
const SHOP_SPEECH_CHARACTERS_PER_SECOND: float = 28.0
|
||||
|
||||
|
||||
@onready var _canonical_stage: Control = %CanonicalStage
|
||||
@onready var _ui_root: Control = %UIRoot
|
||||
@onready var _player_menu: PlayerMenuType = %PlayerMenu
|
||||
|
|
@ -109,7 +126,13 @@ const FISHING_PANEL_SHOWCASE_BOTTOM_OFFSET: float = -104.0
|
|||
@onready var _pause_menu: PauseMenuType = %PauseMenu
|
||||
@onready var _hotbar_ui: HotbarUIType = %Hotbar
|
||||
@onready var _fishing_shop: FishingShopType = %FishingShop
|
||||
@onready var _shop_prompt: PanelContainer = %ShopPrompt
|
||||
@onready var _shop_prompt: Control = %ShopPrompt
|
||||
@onready var _shop_prompt_bubble: PanelContainer = %ShopPromptBubble
|
||||
@onready var _shop_prompt_message: Label = %ShopPromptMessage
|
||||
@onready var _shop_prompt_key_badge: Panel = %ShopPromptKeyBadge
|
||||
@onready var _shop_prompt_key: Label = %ShopPromptKey
|
||||
@onready var _shop_prompt_pointer: Polygon2D = %ShopPromptPointer
|
||||
var _shop_animalese_voice: AnimaleseVoiceType
|
||||
@onready var _effect_status: Label = %EffectStatus
|
||||
@onready var _chat_ui: ChatUIType = %ChatUI
|
||||
@onready var _emote_radial_menu: EmoteRadialMenuType = %EmoteRadialMenu
|
||||
|
|
@ -129,6 +152,8 @@ const FISHING_PANEL_SHOWCASE_BOTTOM_OFFSET: float = -104.0
|
|||
|
||||
var _showcase_active: bool = false
|
||||
var _player: PlayerType
|
||||
var _bag: PlayerBagType
|
||||
var _item_catalog: ItemCatalogType
|
||||
var _player_menu_open: bool = false
|
||||
var _gameplay_ui_enabled: bool = false
|
||||
var _fishing_spot: FishingSpotType
|
||||
|
|
@ -163,6 +188,8 @@ var _settings_manager: PlayerSettingsManagerType
|
|||
|
||||
|
||||
func _ready() -> void:
|
||||
_apply_active_bait_indicator_style()
|
||||
_refresh_active_bait_indicator()
|
||||
# Reward feedback must remain above full-screen canonical menus. Keeping the
|
||||
# overlay as the final stage child makes that ownership explicit instead of
|
||||
# relying on scene declaration order when another menu adds high-z children.
|
||||
|
|
@ -236,10 +263,27 @@ func setup(
|
|||
world_sun: DirectionalLight3D,
|
||||
) -> void:
|
||||
_player = player
|
||||
_bag = bag
|
||||
_item_catalog = item_catalog
|
||||
_settings_manager = settings_manager
|
||||
_fishing_spot = fishing_spot
|
||||
_item_effects = item_effects
|
||||
_experience = experience
|
||||
if (
|
||||
_player != null
|
||||
and not _player.active_bait_changed.is_connected(
|
||||
_on_hud_active_bait_changed
|
||||
)
|
||||
):
|
||||
_player.active_bait_changed.connect(_on_hud_active_bait_changed)
|
||||
if (
|
||||
_bag != null
|
||||
and not _bag.contents_changed.is_connected(
|
||||
_on_hud_bait_inventory_changed
|
||||
)
|
||||
):
|
||||
_bag.contents_changed.connect(_on_hud_bait_inventory_changed)
|
||||
_refresh_active_bait_indicator()
|
||||
if (
|
||||
_experience != null
|
||||
and not _experience.experience_awarded.is_connected(
|
||||
|
|
@ -1174,6 +1218,88 @@ func get_pause_menu() -> PauseMenuType:
|
|||
return _pause_menu
|
||||
|
||||
|
||||
func _apply_active_bait_indicator_style() -> void:
|
||||
var normal_style: StyleBoxFlat = MAIN_BUBBLE_PROFILE.make_normal_style()
|
||||
var hover_style: StyleBoxFlat = MAIN_BUBBLE_PROFILE.make_hover_style()
|
||||
var pressed_style: StyleBoxFlat = MAIN_BUBBLE_PROFILE.make_pressed_style()
|
||||
var disabled_style: StyleBoxFlat = MAIN_BUBBLE_PROFILE.make_disabled_style()
|
||||
for style: StyleBoxFlat in [
|
||||
normal_style,
|
||||
hover_style,
|
||||
pressed_style,
|
||||
disabled_style,
|
||||
]:
|
||||
style.set_corner_radius_all(36)
|
||||
style.content_margin_left = 13.5
|
||||
style.content_margin_top = 13.5
|
||||
style.content_margin_right = 13.5
|
||||
style.content_margin_bottom = 13.5
|
||||
_active_bait_button.add_theme_stylebox_override("normal", normal_style)
|
||||
_active_bait_button.add_theme_stylebox_override("hover", hover_style)
|
||||
_active_bait_button.add_theme_stylebox_override("focus", hover_style)
|
||||
_active_bait_button.add_theme_stylebox_override("pressed", pressed_style)
|
||||
_active_bait_button.add_theme_stylebox_override("disabled", disabled_style)
|
||||
for state: StringName in [
|
||||
&"icon_normal_color",
|
||||
&"icon_hover_color",
|
||||
&"icon_focus_color",
|
||||
&"icon_pressed_color",
|
||||
]:
|
||||
_active_bait_button.add_theme_color_override(state, Color.WHITE)
|
||||
var badge_style := StyleBoxFlat.new()
|
||||
badge_style.bg_color = Color("0b5558")
|
||||
badge_style.set_corner_radius_all(12)
|
||||
badge_style.anti_aliasing = false
|
||||
_active_bait_quantity_badge.add_theme_stylebox_override(
|
||||
"panel",
|
||||
badge_style,
|
||||
)
|
||||
|
||||
|
||||
func _refresh_active_bait_indicator() -> void:
|
||||
if not is_node_ready():
|
||||
return
|
||||
var item: ItemDataType
|
||||
if (
|
||||
_player != null
|
||||
and _item_catalog != null
|
||||
and not _player.active_bait_id.is_empty()
|
||||
):
|
||||
item = _item_catalog.get_item_by_id(_player.active_bait_id)
|
||||
var has_active_bait: bool = item != null and item.is_bait()
|
||||
_active_bait_button.icon = (
|
||||
item.icon
|
||||
if has_active_bait and item.icon != null
|
||||
else ACTIVE_BAIT_EMPTY_ICON
|
||||
)
|
||||
_active_bait_quantity_badge.visible = has_active_bait
|
||||
if not has_active_bait:
|
||||
_active_bait_button.tooltip_text = "no bait selected"
|
||||
return
|
||||
var quantity: int = (
|
||||
_bag.get_quantity(item.item_id)
|
||||
if _bag != null
|
||||
else 0
|
||||
)
|
||||
_active_bait_quantity.text = str(quantity)
|
||||
_active_bait_quantity.add_theme_font_size_override(
|
||||
"font_size",
|
||||
13 if quantity >= 100 else 15,
|
||||
)
|
||||
_active_bait_button.tooltip_text = "%s ×%d" % [
|
||||
item.display_name,
|
||||
quantity,
|
||||
]
|
||||
|
||||
|
||||
func _on_hud_active_bait_changed(_item_id: StringName) -> void:
|
||||
_refresh_active_bait_indicator()
|
||||
|
||||
|
||||
func _on_hud_bait_inventory_changed() -> void:
|
||||
_refresh_active_bait_indicator()
|
||||
|
||||
|
||||
func set_gameplay_ui_enabled(enabled: bool) -> void:
|
||||
_gameplay_ui_enabled = enabled
|
||||
_gameplay_transient_hud.visible = enabled and not _player_menu_open
|
||||
|
|
@ -1231,7 +1357,12 @@ func get_fishing_shop() -> FishingShopType:
|
|||
return _fishing_shop
|
||||
|
||||
|
||||
func set_shop_prompt_visible(is_visible: bool) -> void:
|
||||
func set_shop_prompt_visible(
|
||||
is_visible: bool,
|
||||
world_anchor: Vector3 = Vector3(0.0, INF, 0.0),
|
||||
) -> void:
|
||||
_apply_shop_prompt_style()
|
||||
var was_visible := _shop_prompt.visible
|
||||
_shop_prompt.visible = (
|
||||
is_visible
|
||||
and _gameplay_ui_enabled
|
||||
|
|
@ -1239,6 +1370,112 @@ func set_shop_prompt_visible(is_visible: bool) -> void:
|
|||
and not _player_menu_open
|
||||
and not _shop_open
|
||||
)
|
||||
if _shop_prompt.visible:
|
||||
if not was_visible:
|
||||
_ensure_shop_animalese_voice()
|
||||
TypewriterRevealType.start(
|
||||
_shop_prompt_message,
|
||||
SHOP_SPEECH_CHARACTERS_PER_SECOND,
|
||||
)
|
||||
_shop_animalese_voice.speak_text(
|
||||
_shop_prompt_message,
|
||||
_shop_prompt_message.text,
|
||||
"shopkeeper",
|
||||
SHOP_ANIMALESE_VOICE_ID,
|
||||
SHOP_SPEECH_CHARACTERS_PER_SECOND,
|
||||
)
|
||||
if world_anchor.is_finite():
|
||||
_position_shop_prompt(world_anchor)
|
||||
|
||||
|
||||
func _ensure_shop_animalese_voice() -> void:
|
||||
if _shop_animalese_voice != null:
|
||||
return
|
||||
_shop_animalese_voice = AnimaleseVoiceType.new()
|
||||
_shop_animalese_voice.name = "ShopAnimaleseVoice"
|
||||
_shop_animalese_voice.base_pitch = SHOP_ANIMALESE_BASE_PITCH
|
||||
add_child(_shop_animalese_voice)
|
||||
|
||||
|
||||
func _apply_shop_prompt_style() -> void:
|
||||
if _shop_prompt.has_meta(&"shop_prompt_styled"):
|
||||
return
|
||||
_shop_prompt.set_meta(&"shop_prompt_styled", true)
|
||||
var bubble_style := StyleBoxFlat.new()
|
||||
bubble_style.bg_color = Color(UtilityPageStyle.OCEAN_PANEL_MID, 0.96)
|
||||
bubble_style.set_border_width_all(0)
|
||||
bubble_style.set_corner_radius_all(12)
|
||||
bubble_style.anti_aliasing = false
|
||||
_shop_prompt_bubble.add_theme_stylebox_override("panel", bubble_style)
|
||||
var badge_style := StyleBoxFlat.new()
|
||||
badge_style.bg_color = Color("0b5558")
|
||||
badge_style.set_border_width_all(0)
|
||||
badge_style.set_corner_radius_all(12)
|
||||
badge_style.anti_aliasing = false
|
||||
_shop_prompt_key_badge.add_theme_stylebox_override("panel", badge_style)
|
||||
for label: Label in [_shop_prompt_message, _shop_prompt_key]:
|
||||
label.add_theme_font_override("font", UtilityPageStyle.TuffyFont)
|
||||
label.add_theme_color_override(
|
||||
"font_color",
|
||||
UtilityPageStyle.OCEAN_TEXT_PRIMARY,
|
||||
)
|
||||
_shop_prompt_pointer.color = Color(
|
||||
UtilityPageStyle.OCEAN_PANEL_MID,
|
||||
0.96,
|
||||
)
|
||||
|
||||
|
||||
func _position_shop_prompt(world_anchor: Vector3) -> void:
|
||||
if _player == null:
|
||||
_shop_prompt.hide()
|
||||
return
|
||||
var camera: Camera3D = _player.get_gameplay_camera()
|
||||
if camera == null or camera.is_position_behind(world_anchor):
|
||||
_shop_prompt.hide()
|
||||
return
|
||||
var camera_viewport_size := camera.get_viewport().get_visible_rect().size
|
||||
var ui_viewport_size: Vector2 = _canonical_stage.size
|
||||
if camera_viewport_size.x <= 0.0 or camera_viewport_size.y <= 0.0:
|
||||
_shop_prompt.hide()
|
||||
return
|
||||
var screen_position := (
|
||||
camera.unproject_position(world_anchor)
|
||||
* ui_viewport_size
|
||||
/ camera_viewport_size
|
||||
)
|
||||
if (
|
||||
screen_position.x < -80.0
|
||||
or screen_position.x > ui_viewport_size.x + 80.0
|
||||
or screen_position.y < -80.0
|
||||
or screen_position.y > ui_viewport_size.y + 80.0
|
||||
):
|
||||
_shop_prompt.hide()
|
||||
return
|
||||
var pointer_height := 10.0
|
||||
var desired := screen_position - Vector2(
|
||||
_shop_prompt.size.x * 0.5,
|
||||
_shop_prompt.size.y + pointer_height,
|
||||
)
|
||||
_shop_prompt.position = Vector2(
|
||||
clampf(
|
||||
desired.x,
|
||||
8.0,
|
||||
ui_viewport_size.x - _shop_prompt.size.x - 8.0,
|
||||
),
|
||||
clampf(
|
||||
desired.y,
|
||||
8.0,
|
||||
ui_viewport_size.y - _shop_prompt.size.y - pointer_height - 8.0,
|
||||
),
|
||||
)
|
||||
_shop_prompt_pointer.position = Vector2(
|
||||
clampf(
|
||||
screen_position.x - _shop_prompt.position.x,
|
||||
20.0,
|
||||
_shop_prompt.size.x - 20.0,
|
||||
),
|
||||
_shop_prompt.size.y - 1.0,
|
||||
)
|
||||
|
||||
|
||||
func get_screen_fade() -> ScreenFade:
|
||||
|
|
|
|||
131
ui/game_ui.tscn
|
|
@ -82,6 +82,71 @@ grow_horizontal = 2
|
|||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="ActiveBaitIndicator" type="Control" parent="UIRoot/CanonicalStage/GameplayTransientHUD"]
|
||||
unique_name_in_owner = true
|
||||
z_index = 54
|
||||
layout_mode = 1
|
||||
anchors_preset = 3
|
||||
anchor_left = 1.0
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -88.0
|
||||
offset_top = -88.0
|
||||
offset_right = -16.0
|
||||
offset_bottom = -16.0
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 0
|
||||
mouse_filter = 2
|
||||
theme = ExtResource("3_theme")
|
||||
|
||||
[node name="ActiveBaitButton" type="Button" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ActiveBaitIndicator"]
|
||||
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
|
||||
focus_mode = 0
|
||||
texture_filter = 1
|
||||
expand_icon = true
|
||||
alignment = 1
|
||||
icon_alignment = 1
|
||||
vertical_icon_alignment = 1
|
||||
|
||||
[node name="ActiveBaitQuantityBadge" type="Panel" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ActiveBaitIndicator"]
|
||||
unique_name_in_owner = true
|
||||
z_index = 2
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -27.0
|
||||
offset_top = 3.0
|
||||
offset_right = -3.0
|
||||
offset_bottom = 27.0
|
||||
grow_horizontal = 0
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="ActiveBaitQuantity" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ActiveBaitIndicator/ActiveBaitQuantityBadge"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 1.0
|
||||
offset_right = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
theme_override_colors/font_color = Color(0.905882, 0.960784, 0.956863, 1)
|
||||
theme_override_font_sizes/font_size = 15
|
||||
text = "0"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="ExperiencePresentation" type="Control" parent="UIRoot/CanonicalStage"]
|
||||
unique_name_in_owner = true
|
||||
z_index = 200
|
||||
|
|
@ -333,35 +398,67 @@ unique_name_in_owner = true
|
|||
[node name="QuickRadialMenu" parent="UIRoot/CanonicalStage" instance=ExtResource("12_quick")]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="ShopPrompt" type="PanelContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD"]
|
||||
[node name="ShopPrompt" type="Control" parent="UIRoot/CanonicalStage/GameplayTransientHUD"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
z_index = 55
|
||||
anchors_preset = 7
|
||||
anchor_left = 0.5
|
||||
anchor_top = 1.0
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -150.0
|
||||
offset_top = -284.0
|
||||
offset_right = 150.0
|
||||
offset_bottom = -246.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
offset_right = 300.0
|
||||
offset_bottom = 72.0
|
||||
mouse_filter = 2
|
||||
theme = ExtResource("3_theme")
|
||||
|
||||
[node name="Margin" type="MarginContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt"]
|
||||
[node name="ShopPromptBubble" type="PanelContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt"]
|
||||
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
|
||||
|
||||
[node name="Margin" type="MarginContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt/ShopPromptBubble"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 12
|
||||
theme_override_constants/margin_top = 7
|
||||
theme_override_constants/margin_top = 8
|
||||
theme_override_constants/margin_right = 12
|
||||
theme_override_constants/margin_bottom = 7
|
||||
theme_override_constants/margin_bottom = 8
|
||||
|
||||
[node name="Label" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt/Margin"]
|
||||
[node name="ShopPromptMessage" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt/ShopPromptBubble/Margin"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "press e to open fishing shop"
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "Hey, if you've got any fish I've got cash! OwO"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="ShopPromptKeyBadge" type="Panel" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 0
|
||||
offset_left = 282.0
|
||||
offset_top = -8.0
|
||||
offset_right = 306.0
|
||||
offset_bottom = 16.0
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="ShopPromptKey" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt/ShopPromptKeyBadge"]
|
||||
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_font_sizes/font_size = 15
|
||||
text = "E"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="ShopPromptPointer" type="Polygon2D" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt"]
|
||||
unique_name_in_owner = true
|
||||
polygon = PackedVector2Array(-8, 0, 8, 0, 0, 10)
|
||||
|
||||
[node name="FishingShop" parent="UIRoot/CanonicalStage" instance=ExtResource("8_shop")]
|
||||
unique_name_in_owner = true
|
||||
|
|
|
|||
BIN
ui/icons/pictograms/arrow_dark_down_full.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
40
ui/icons/pictograms/arrow_dark_down_full.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://d4aah3j5brkij"
|
||||
path="res://.godot/imported/arrow_dark_down_full.png-0e7bb59c41a81c0bd02261d6a7b319ef.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/pictograms/arrow_dark_down_full.png"
|
||||
dest_files=["res://.godot/imported/arrow_dark_down_full.png-0e7bb59c41a81c0bd02261d6a7b319ef.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/pictograms/arrow_dark_up_full.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
40
ui/icons/pictograms/arrow_dark_up_full.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://d34x31jx5p5ll"
|
||||
path="res://.godot/imported/arrow_dark_up_full.png-907f3a8653cf6bf8b6ba95ad31b18e0d.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/pictograms/arrow_dark_up_full.png"
|
||||
dest_files=["res://.godot/imported/arrow_dark_up_full.png-907f3a8653cf6bf8b6ba95ad31b18e0d.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/pictograms/arrow_light_down_more.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
40
ui/icons/pictograms/arrow_light_down_more.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cmc7sguttrl2o"
|
||||
path="res://.godot/imported/arrow_light_down_more.png-a02fb5d09f2022507ddb6c06e3f22732.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/pictograms/arrow_light_down_more.png"
|
||||
dest_files=["res://.godot/imported/arrow_light_down_more.png-a02fb5d09f2022507ddb6c06e3f22732.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/pictograms/arrow_light_left_more.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
40
ui/icons/pictograms/arrow_light_left_more.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b3lx64prsccyk"
|
||||
path="res://.godot/imported/arrow_light_left_more.png-a92f7c96dbcafd9c1b2cb612e94fef44.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/pictograms/arrow_light_left_more.png"
|
||||
dest_files=["res://.godot/imported/arrow_light_left_more.png-a92f7c96dbcafd9c1b2cb612e94fef44.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/pictograms/arrow_light_right_more.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
40
ui/icons/pictograms/arrow_light_right_more.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cedstrt57et53"
|
||||
path="res://.godot/imported/arrow_light_right_more.png-56d3df49848f9fee268e5c4b6c279828.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/pictograms/arrow_light_right_more.png"
|
||||
dest_files=["res://.godot/imported/arrow_light_right_more.png-56d3df49848f9fee268e5c4b6c279828.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/pictograms/arrow_light_up_more.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
40
ui/icons/pictograms/arrow_light_up_more.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dqu71oklbvofk"
|
||||
path="res://.godot/imported/arrow_light_up_more.png-7e8472cc9f410835ab24173229e09c9b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/pictograms/arrow_light_up_more.png"
|
||||
dest_files=["res://.godot/imported/arrow_light_up_more.png-7e8472cc9f410835ab24173229e09c9b.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/pictograms/check_mark_dark.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
40
ui/icons/pictograms/check_mark_dark.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://3x5tryb721th"
|
||||
path="res://.godot/imported/check_mark_dark.png-d9abda15f33a6a8f8c29c5f148cda086.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/pictograms/check_mark_dark.png"
|
||||
dest_files=["res://.godot/imported/check_mark_dark.png-d9abda15f33a6a8f8c29c5f148cda086.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/pictograms/check_mark_light.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
40
ui/icons/pictograms/check_mark_light.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dtibvrik7ug0j"
|
||||
path="res://.godot/imported/check_mark_light.png-83417416d6580b7b5eb41987b7af5ccf.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/pictograms/check_mark_light.png"
|
||||
dest_files=["res://.godot/imported/check_mark_light.png-83417416d6580b7b5eb41987b7af5ccf.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/pictograms/online_dark.png
Normal file
|
After Width: | Height: | Size: 2 KiB |
40
ui/icons/pictograms/online_dark.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://uxkgiwd7py03"
|
||||
path="res://.godot/imported/online_dark.png-081123db8a385b31ed626b7c95b20c3c.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/pictograms/online_dark.png"
|
||||
dest_files=["res://.godot/imported/online_dark.png-081123db8a385b31ed626b7c95b20c3c.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/pictograms/save_dark.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
40
ui/icons/pictograms/save_dark.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b71gds1q8ny61"
|
||||
path="res://.godot/imported/save_dark.png-6dbadbfe5b8057ede23f77629e3c1226.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/pictograms/save_dark.png"
|
||||
dest_files=["res://.godot/imported/save_dark.png-6dbadbfe5b8057ede23f77629e3c1226.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/pictograms/settings_dark.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
40
ui/icons/pictograms/settings_dark.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://ul28xl5qed55"
|
||||
path="res://.godot/imported/settings_dark.png-7196550e6c3f5a58c1402ab0ccc9c9d0.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/pictograms/settings_dark.png"
|
||||
dest_files=["res://.godot/imported/settings_dark.png-7196550e6c3f5a58c1402ab0ccc9c9d0.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/pictograms/undo_dark.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
40
ui/icons/pictograms/undo_dark.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://deux1h722j33u"
|
||||
path="res://.godot/imported/undo_dark.png-652c08d6b38e19b70b66927ecfc6222c.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/pictograms/undo_dark.png"
|
||||
dest_files=["res://.godot/imported/undo_dark.png-652c08d6b38e19b70b66927ecfc6222c.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/pictograms/undo_light.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
40
ui/icons/pictograms/undo_light.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://1qr5t7gx1eag"
|
||||
path="res://.godot/imported/undo_light.png-83dd161fb13faa93326dee6cb01e205c.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/pictograms/undo_light.png"
|
||||
dest_files=["res://.godot/imported/undo_light.png-83dd161fb13faa93326dee6cb01e205c.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/pictograms/x_dark.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
40
ui/icons/pictograms/x_dark.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cg8vi6sxnpl2t"
|
||||
path="res://.godot/imported/x_dark.png-5430cf57b7772f059ee1fac9842c6898.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/pictograms/x_dark.png"
|
||||
dest_files=["res://.godot/imported/x_dark.png-5430cf57b7772f059ee1fac9842c6898.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/pictograms/x_light.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
40
ui/icons/pictograms/x_light.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cvb228ypykqaq"
|
||||
path="res://.godot/imported/x_light.png-b099203c17614ca6f36b269beabfcbab.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/pictograms/x_light.png"
|
||||
dest_files=["res://.godot/imported/x_light.png-b099203c17614ca6f36b269beabfcbab.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/weather/weather_clear_day.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
40
ui/icons/weather/weather_clear_day.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://ce6ge5yypnnpf"
|
||||
path="res://.godot/imported/weather_clear_day.png-1cd1140830664281637d5589a748ef58.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/weather/weather_clear_day.png"
|
||||
dest_files=["res://.godot/imported/weather_clear_day.png-1cd1140830664281637d5589a748ef58.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/weather/weather_clear_night_full.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
40
ui/icons/weather/weather_clear_night_full.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://78l0keyrvuiw"
|
||||
path="res://.godot/imported/weather_clear_night_full.png-3640fcf886e9bd01942e21cc0212bc45.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/weather/weather_clear_night_full.png"
|
||||
dest_files=["res://.godot/imported/weather_clear_night_full.png-3640fcf886e9bd01942e21cc0212bc45.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/weather/weather_cloudy.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
40
ui/icons/weather/weather_cloudy.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b17frlbta1p7f"
|
||||
path="res://.godot/imported/weather_cloudy.png-c41e4d98007294310395f752b39ddaa6.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/weather/weather_cloudy.png"
|
||||
dest_files=["res://.godot/imported/weather_cloudy.png-c41e4d98007294310395f752b39ddaa6.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/weather/weather_fog.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
40
ui/icons/weather/weather_fog.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://wpaj2l01upt6"
|
||||
path="res://.godot/imported/weather_fog.png-0fe4352261121bbcc8da10ebc1995663.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/weather/weather_fog.png"
|
||||
dest_files=["res://.godot/imported/weather_fog.png-0fe4352261121bbcc8da10ebc1995663.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/weather/weather_rain.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
40
ui/icons/weather/weather_rain.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cfqvlqp3nrfok"
|
||||
path="res://.godot/imported/weather_rain.png-affbb7091de81103538e2740c5b2c762.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://ui/icons/weather/weather_rain.png"
|
||||
dest_files=["res://.godot/imported/weather_rain.png-affbb7091de81103538e2740c5b2c762.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
|
||||
|
|
@ -23,9 +23,26 @@ const HANDWRITTEN_NUMERIC_SCALE: float = 0.8
|
|||
const PORTRAIT_VIEW_MAX_SIZE := Vector2(860.0, 480.0)
|
||||
const INK := Color("251b10")
|
||||
const MUTED_INK := Color("6d5b45")
|
||||
const PAPER := Color("f2e6c9")
|
||||
const PAPER_DARK := Color("e8d7b4")
|
||||
const LOGBOOK_TAB_LEFT_INSET: float = 34.0
|
||||
const LOGBOOK_ARTWORK: Texture2D = preload(
|
||||
"res://ui/assets/logbook/ui_logbook.png"
|
||||
)
|
||||
const SCROLL_UP_TEXTURE: Texture2D = preload(
|
||||
"res://ui/icons/pictograms/arrow_dark_up_full.png"
|
||||
)
|
||||
const SCROLL_DOWN_TEXTURE: Texture2D = preload(
|
||||
"res://ui/icons/pictograms/arrow_dark_down_full.png"
|
||||
)
|
||||
const LOGBOOK_ARTWORK_SOURCE_SIZE := Vector2(512.0, 247.0)
|
||||
const LEFT_PAGE_CONTENT_RECT := Rect2(56.0, 26.0, 190.0, 198.0)
|
||||
const RIGHT_PAGE_CONTENT_RECT := Rect2(267.0, 26.0, 210.0, 198.0)
|
||||
const LOGBOOK_TAB_LEFT_INSET: float = 122.0
|
||||
const PAGE_CONTENT_SCALE: float = 0.97
|
||||
const CATALOG_PORTRAIT_SIZE := Vector2(78.0, 36.0)
|
||||
const CATALOG_ENTRY_SIZE := Vector2(92.0, 92.0)
|
||||
const CATALOG_ROW_STEP: float = 98.0
|
||||
const CATALOG_SNAP_DELAY: float = 0.12
|
||||
const DETAIL_PORTRAIT_SIZE := Vector2(160.0, 88.0)
|
||||
const DETAIL_BOTTOM_INSET: float = 35.0
|
||||
|
||||
var _collection_log: CollectionLogType
|
||||
var _inventory: FishInventoryType
|
||||
|
|
@ -41,10 +58,14 @@ var _detail_tween: Tween
|
|||
var _category_generation: int = 0
|
||||
var _category_tween: Tween
|
||||
var _silhouette_material: ShaderMaterial
|
||||
var _snapping_catalog_scroll: bool = false
|
||||
var _catalog_scroll_snap_timer: Timer
|
||||
|
||||
var _category_tabs: Array[Button] = []
|
||||
var _catalog_scroll: ScrollContainer
|
||||
var _catalog_grid: GridContainer
|
||||
var _catalog_scroll_up_indicator: TextureRect
|
||||
var _catalog_scroll_down_indicator: TextureRect
|
||||
var _empty_state: Label
|
||||
var _detail_body: VBoxContainer
|
||||
var _detail_portrait_button: Button
|
||||
|
|
@ -145,7 +166,7 @@ func _build_interface() -> void:
|
|||
var outer := MarginContainer.new()
|
||||
outer.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
outer.add_theme_constant_override("margin_left", 52)
|
||||
outer.add_theme_constant_override("margin_top", 116)
|
||||
outer.add_theme_constant_override("margin_top", 85)
|
||||
outer.add_theme_constant_override("margin_right", 52)
|
||||
outer.add_theme_constant_override("margin_bottom", 18)
|
||||
add_child(outer)
|
||||
|
|
@ -155,9 +176,8 @@ func _build_interface() -> void:
|
|||
outer.add_child(stack)
|
||||
|
||||
var tabs := HBoxContainer.new()
|
||||
# The 26 px page corner plus 8 px clearance keeps the flange behind
|
||||
# the straight portion of the spread.
|
||||
tabs.position = Vector2(LOGBOOK_TAB_LEFT_INSET, 20.0)
|
||||
# Align the first flange with the authored left paper edge.
|
||||
tabs.position = Vector2(LOGBOOK_TAB_LEFT_INSET, 37.0)
|
||||
tabs.size = Vector2(384.0, 38.0)
|
||||
tabs.z_index = 10
|
||||
tabs.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||
|
|
@ -179,71 +199,206 @@ func _build_interface() -> void:
|
|||
_category_tabs.append(tab)
|
||||
_configure_category_focus()
|
||||
|
||||
var book := HBoxContainer.new()
|
||||
var book := Control.new()
|
||||
book.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
book.offset_top = 50.0
|
||||
book.z_index = 20
|
||||
book.add_theme_constant_override("separation", 12)
|
||||
book.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
stack.add_child(book)
|
||||
|
||||
var left_page := PanelContainer.new()
|
||||
left_page.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
left_page.size_flags_stretch_ratio = 1.12
|
||||
left_page.add_theme_stylebox_override(
|
||||
"panel", _paper_style(PAPER, true)
|
||||
)
|
||||
var artwork := TextureRect.new()
|
||||
artwork.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
artwork.texture = LOGBOOK_ARTWORK
|
||||
artwork.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
artwork.stretch_mode = TextureRect.STRETCH_SCALE
|
||||
artwork.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
artwork.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
book.add_child(artwork)
|
||||
|
||||
var left_page := Control.new()
|
||||
left_page.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
_apply_artwork_rect(left_page, LEFT_PAGE_CONTENT_RECT)
|
||||
book.add_child(left_page)
|
||||
var left_margin := MarginContainer.new()
|
||||
_set_margins(left_margin, 18, 16, 18, 16)
|
||||
left_page.add_child(left_margin)
|
||||
var left_layout := VBoxContainer.new()
|
||||
left_layout.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
left_layout.add_theme_constant_override("separation", 8)
|
||||
left_margin.add_child(left_layout)
|
||||
var heading := _label("catch catalog", 25)
|
||||
heading.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
left_layout.add_child(heading)
|
||||
left_page.add_child(left_layout)
|
||||
_apply_page_content_scale(left_layout)
|
||||
var scroll_indicator_top_lane := Control.new()
|
||||
scroll_indicator_top_lane.custom_minimum_size.y = 20.0
|
||||
scroll_indicator_top_lane.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
left_layout.add_child(scroll_indicator_top_lane)
|
||||
_empty_state = _label("", 18)
|
||||
_empty_state.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
_empty_state.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
_empty_state.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
left_layout.add_child(_empty_state)
|
||||
var catalog_scroll_margin := MarginContainer.new()
|
||||
catalog_scroll_margin.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
catalog_scroll_margin.add_theme_constant_override("margin_left", 20)
|
||||
left_layout.add_child(catalog_scroll_margin)
|
||||
_catalog_scroll = ScrollContainer.new()
|
||||
_catalog_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
_catalog_scroll.horizontal_scroll_mode = (
|
||||
ScrollContainer.SCROLL_MODE_DISABLED
|
||||
)
|
||||
left_layout.add_child(_catalog_scroll)
|
||||
_catalog_scroll.scroll_vertical_custom_step = CATALOG_ROW_STEP
|
||||
catalog_scroll_margin.add_child(_catalog_scroll)
|
||||
_catalog_grid = GridContainer.new()
|
||||
_catalog_grid.columns = 5
|
||||
_catalog_grid.columns = 4
|
||||
_catalog_grid.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_catalog_grid.add_theme_constant_override("h_separation", 8)
|
||||
_catalog_grid.add_theme_constant_override("v_separation", 8)
|
||||
_catalog_grid.add_theme_constant_override("h_separation", 6)
|
||||
_catalog_grid.add_theme_constant_override("v_separation", 6)
|
||||
_catalog_scroll.add_child(_catalog_grid)
|
||||
|
||||
var gutter := ColorRect.new()
|
||||
gutter.custom_minimum_size.x = 10
|
||||
gutter.color = Color("795f3f")
|
||||
gutter.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
book.add_child(gutter)
|
||||
|
||||
var right_page := PanelContainer.new()
|
||||
right_page.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
right_page.add_theme_stylebox_override(
|
||||
"panel", _paper_style(PAPER_DARK, false)
|
||||
var scroll_indicator_bottom_lane := Control.new()
|
||||
scroll_indicator_bottom_lane.custom_minimum_size.y = 32.0
|
||||
scroll_indicator_bottom_lane.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
left_layout.add_child(scroll_indicator_bottom_lane)
|
||||
_configure_catalog_scroll_bar()
|
||||
_catalog_scroll_up_indicator = _make_scroll_indicator(
|
||||
"CatalogScrollUpIndicator",
|
||||
SCROLL_UP_TEXTURE,
|
||||
true,
|
||||
)
|
||||
left_page.add_child(_catalog_scroll_up_indicator)
|
||||
_catalog_scroll_down_indicator = _make_scroll_indicator(
|
||||
"CatalogScrollDownIndicator",
|
||||
SCROLL_DOWN_TEXTURE,
|
||||
false,
|
||||
)
|
||||
left_page.add_child(_catalog_scroll_down_indicator)
|
||||
|
||||
var right_page := Control.new()
|
||||
right_page.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
_apply_artwork_rect(right_page, RIGHT_PAGE_CONTENT_RECT)
|
||||
book.add_child(right_page)
|
||||
var right_margin := MarginContainer.new()
|
||||
_set_margins(right_margin, 22, 16, 22, 16)
|
||||
right_page.add_child(right_margin)
|
||||
_detail_body = VBoxContainer.new()
|
||||
_detail_body.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
_detail_body.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_detail_body.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
_detail_body.add_theme_constant_override("separation", 16)
|
||||
right_margin.add_child(_detail_body)
|
||||
right_page.add_child(_detail_body)
|
||||
_apply_page_content_scale(_detail_body)
|
||||
_show_no_selection()
|
||||
_build_portrait_overlay()
|
||||
|
||||
|
||||
func _apply_artwork_rect(control: Control, source_rect: Rect2) -> void:
|
||||
control.anchor_left = source_rect.position.x / LOGBOOK_ARTWORK_SOURCE_SIZE.x
|
||||
control.anchor_top = source_rect.position.y / LOGBOOK_ARTWORK_SOURCE_SIZE.y
|
||||
control.anchor_right = source_rect.end.x / LOGBOOK_ARTWORK_SOURCE_SIZE.x
|
||||
control.anchor_bottom = source_rect.end.y / LOGBOOK_ARTWORK_SOURCE_SIZE.y
|
||||
control.offset_left = 0.0
|
||||
control.offset_top = 0.0
|
||||
control.offset_right = 0.0
|
||||
control.offset_bottom = 0.0
|
||||
|
||||
|
||||
func _apply_page_content_scale(control: Control) -> void:
|
||||
control.scale = Vector2.ONE * PAGE_CONTENT_SCALE
|
||||
control.resized.connect(_center_scaled_content.bind(control))
|
||||
_center_scaled_content.call_deferred(control)
|
||||
|
||||
|
||||
func _center_scaled_content(control: Control) -> void:
|
||||
if is_instance_valid(control):
|
||||
control.pivot_offset = control.size * 0.5
|
||||
|
||||
|
||||
func _configure_catalog_scroll_bar() -> void:
|
||||
_catalog_scroll_snap_timer = Timer.new()
|
||||
_catalog_scroll_snap_timer.one_shot = true
|
||||
_catalog_scroll_snap_timer.wait_time = CATALOG_SNAP_DELAY
|
||||
_catalog_scroll_snap_timer.timeout.connect(_snap_catalog_scroll_to_row)
|
||||
_catalog_scroll.add_child(_catalog_scroll_snap_timer)
|
||||
var scroll_bar: VScrollBar = _catalog_scroll.get_v_scroll_bar()
|
||||
scroll_bar.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
scroll_bar.focus_mode = Control.FOCUS_NONE
|
||||
scroll_bar.self_modulate = Color.TRANSPARENT
|
||||
scroll_bar.custom_minimum_size.x = 0.0
|
||||
scroll_bar.add_theme_constant_override("scroll_size", 0)
|
||||
var empty_style := StyleBoxEmpty.new()
|
||||
for style_name: StringName in [
|
||||
&"scroll",
|
||||
&"scroll_focus",
|
||||
&"grabber",
|
||||
&"grabber_highlight",
|
||||
&"grabber_pressed",
|
||||
]:
|
||||
scroll_bar.add_theme_stylebox_override(style_name, empty_style)
|
||||
scroll_bar.changed.connect(_refresh_catalog_scroll_indicators)
|
||||
scroll_bar.value_changed.connect(_on_catalog_scroll_value_changed)
|
||||
|
||||
|
||||
func _make_scroll_indicator(
|
||||
indicator_name: String,
|
||||
indicator_texture: Texture2D,
|
||||
at_top: bool,
|
||||
) -> TextureRect:
|
||||
var indicator := TextureRect.new()
|
||||
indicator.name = indicator_name
|
||||
indicator.anchor_left = 0.5
|
||||
indicator.anchor_right = 0.5
|
||||
indicator.anchor_top = 0.0 if at_top else 1.0
|
||||
indicator.anchor_bottom = indicator.anchor_top
|
||||
indicator.offset_left = -14.0
|
||||
indicator.offset_right = 14.0
|
||||
indicator.offset_top = -8.0 if at_top else -35.0
|
||||
indicator.offset_bottom = 20.0 if at_top else -7.0
|
||||
indicator.z_index = 30
|
||||
indicator.texture = indicator_texture
|
||||
indicator.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
indicator.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
indicator.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
indicator.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
indicator.visible = false
|
||||
return indicator
|
||||
|
||||
|
||||
func _on_catalog_scroll_value_changed(value: float) -> void:
|
||||
if _snapping_catalog_scroll:
|
||||
return
|
||||
_catalog_scroll_snap_timer.start()
|
||||
_refresh_catalog_scroll_indicators()
|
||||
|
||||
|
||||
func _snap_catalog_scroll_to_row() -> void:
|
||||
var scroll_bar: VScrollBar = _catalog_scroll.get_v_scroll_bar()
|
||||
var value: float = scroll_bar.value
|
||||
var maximum_scroll: float = scroll_bar.max_value - scroll_bar.page
|
||||
var snapped_value: float = clampf(
|
||||
roundf(value / CATALOG_ROW_STEP) * CATALOG_ROW_STEP,
|
||||
scroll_bar.min_value,
|
||||
maximum_scroll,
|
||||
)
|
||||
if not is_equal_approx(value, snapped_value):
|
||||
_snapping_catalog_scroll = true
|
||||
_catalog_scroll.scroll_vertical = roundi(snapped_value)
|
||||
_snapping_catalog_scroll = false
|
||||
_refresh_catalog_scroll_indicators()
|
||||
|
||||
|
||||
func _refresh_catalog_scroll_indicators() -> void:
|
||||
if (
|
||||
_catalog_scroll == null
|
||||
or _catalog_scroll_up_indicator == null
|
||||
or _catalog_scroll_down_indicator == null
|
||||
):
|
||||
return
|
||||
var scroll_bar: VScrollBar = _catalog_scroll.get_v_scroll_bar()
|
||||
var maximum_scroll: float = scroll_bar.max_value - scroll_bar.page
|
||||
var has_overflow: bool = (
|
||||
_catalog_scroll.visible
|
||||
and maximum_scroll > scroll_bar.min_value + 0.5
|
||||
)
|
||||
_catalog_scroll_up_indicator.visible = (
|
||||
has_overflow and scroll_bar.value > scroll_bar.min_value + 0.5
|
||||
)
|
||||
_catalog_scroll_down_indicator.visible = (
|
||||
has_overflow and scroll_bar.value < maximum_scroll - 0.5
|
||||
)
|
||||
|
||||
|
||||
func _refresh_catalog() -> void:
|
||||
if not is_node_ready():
|
||||
return
|
||||
|
|
@ -293,11 +448,12 @@ func _refresh_catalog() -> void:
|
|||
_refresh_selection_styles()
|
||||
_refresh_details(false)
|
||||
set_interactive(_interactive)
|
||||
_refresh_catalog_scroll_indicators.call_deferred()
|
||||
|
||||
|
||||
func _make_entry(fish: FishDataType, discovered: bool) -> Button:
|
||||
var entry := Button.new()
|
||||
entry.custom_minimum_size = Vector2(102, 102)
|
||||
entry.custom_minimum_size = CATALOG_ENTRY_SIZE
|
||||
entry.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
|
||||
entry.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
entry.toggle_mode = true
|
||||
|
|
@ -344,7 +500,7 @@ func _add_entry_content(
|
|||
var portrait_view := LogbookPortraitType.new()
|
||||
portrait_view.configure(
|
||||
portrait,
|
||||
LogbookPortraitType.ENTRY_FRAME_SIZE,
|
||||
CATALOG_PORTRAIT_SIZE,
|
||||
_silhouette_material if unknown else null,
|
||||
)
|
||||
content.add_child(portrait_view)
|
||||
|
|
@ -495,10 +651,14 @@ func _refresh_details(animate: bool) -> void:
|
|||
func _build_known_details(fish: FishDataType) -> void:
|
||||
_clear_details()
|
||||
var catalog_number: int = LogbookCatalog.catalog_number(fish.id)
|
||||
var summary_margin := MarginContainer.new()
|
||||
summary_margin.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_set_margins(summary_margin, 40, 0, 40, 0)
|
||||
_detail_body.add_child(summary_margin)
|
||||
var summary_columns := HBoxContainer.new()
|
||||
summary_columns.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
summary_columns.add_theme_constant_override("separation", 24)
|
||||
_detail_body.add_child(summary_columns)
|
||||
summary_margin.add_child(summary_columns)
|
||||
|
||||
var portrait_column := VBoxContainer.new()
|
||||
portrait_column.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
|
|
@ -510,12 +670,11 @@ func _build_known_details(fish: FishDataType) -> void:
|
|||
portrait_column.add_child(title)
|
||||
if fish.display_texture != null:
|
||||
_detail_portrait_button = Button.new()
|
||||
_detail_portrait_button.custom_minimum_size = (
|
||||
LogbookPortraitType.DETAIL_FRAME_SIZE
|
||||
)
|
||||
_detail_portrait_button.custom_minimum_size = DETAIL_PORTRAIT_SIZE
|
||||
_detail_portrait_button.size_flags_horizontal = (
|
||||
Control.SIZE_SHRINK_CENTER
|
||||
)
|
||||
_detail_portrait_button.clip_contents = true
|
||||
_detail_portrait_button.tooltip_text = "View larger artwork"
|
||||
_detail_portrait_button.accessibility_name = (
|
||||
"View larger artwork for %s" % fish.display_name
|
||||
|
|
@ -545,7 +704,7 @@ func _build_known_details(fish: FishDataType) -> void:
|
|||
var artwork := LogbookPortraitType.new()
|
||||
artwork.configure(
|
||||
fish.display_texture,
|
||||
LogbookPortraitType.DETAIL_FRAME_SIZE,
|
||||
DETAIL_PORTRAIT_SIZE,
|
||||
)
|
||||
artwork_center.add_child(artwork)
|
||||
|
||||
|
|
@ -562,11 +721,15 @@ func _build_known_details(fish: FishDataType) -> void:
|
|||
facts_column.add_child(facts)
|
||||
_detail_body.add_child(_build_quality_progress(fish.id))
|
||||
|
||||
var stats_anchor := VBoxContainer.new()
|
||||
stats_anchor.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
stats_anchor.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
stats_anchor.alignment = BoxContainer.ALIGNMENT_END
|
||||
_detail_body.add_child(stats_anchor)
|
||||
var stats_columns := HBoxContainer.new()
|
||||
stats_columns.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
stats_columns.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
stats_columns.add_theme_constant_override("separation", 28)
|
||||
_detail_body.add_child(stats_columns)
|
||||
stats_anchor.add_child(stats_columns)
|
||||
var left_stats := _make_stats_column()
|
||||
var right_stats := _make_stats_column()
|
||||
stats_columns.add_child(left_stats)
|
||||
|
|
@ -611,11 +774,16 @@ func _build_known_details(fish: FishDataType) -> void:
|
|||
"number owned",
|
||||
str(_inventory.get_count(fish.id) if _inventory != null else 0),
|
||||
)
|
||||
var bottom_inset := Control.new()
|
||||
bottom_inset.custom_minimum_size.y = DETAIL_BOTTOM_INSET
|
||||
bottom_inset.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
stats_anchor.add_child(bottom_inset)
|
||||
|
||||
|
||||
func _build_quality_progress(fish_id: StringName) -> VBoxContainer:
|
||||
var quality_section := VBoxContainer.new()
|
||||
quality_section.add_theme_constant_override("separation", 3)
|
||||
quality_section.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
quality_section.add_theme_constant_override("separation", 6)
|
||||
var discovered_count: int = 0
|
||||
for quality: int in FishQualityType.TIER_COUNT:
|
||||
if _collection_log.has_discovered_quality(fish_id, quality):
|
||||
|
|
@ -625,29 +793,36 @@ func _build_quality_progress(fish_id: StringName) -> VBoxContainer:
|
|||
discovered_count,
|
||||
FishQualityType.TIER_COUNT,
|
||||
],
|
||||
14,
|
||||
17,
|
||||
)
|
||||
heading.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
heading.add_theme_color_override("font_color", MUTED_INK)
|
||||
quality_section.add_child(heading)
|
||||
var tiers := HBoxContainer.new()
|
||||
tiers.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
tiers.add_theme_constant_override("separation", 10)
|
||||
var tiers := VBoxContainer.new()
|
||||
tiers.add_theme_constant_override("separation", 6)
|
||||
quality_section.add_child(tiers)
|
||||
for quality: int in FishQualityType.TIER_COUNT:
|
||||
var row: HBoxContainer
|
||||
if quality % 2 == 0:
|
||||
row = HBoxContainer.new()
|
||||
row.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
row.add_theme_constant_override("separation", 28)
|
||||
tiers.add_child(row)
|
||||
else:
|
||||
row = tiers.get_child(tiers.get_child_count() - 1) as HBoxContainer
|
||||
var discovered: bool = _collection_log.has_discovered_quality(
|
||||
fish_id,
|
||||
quality,
|
||||
)
|
||||
var tier := HBoxContainer.new()
|
||||
tier.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
tier.add_theme_constant_override("separation", 3)
|
||||
tier.add_theme_constant_override("separation", 5)
|
||||
var quality_color: Color = UIPalette.get_quality_color(quality)
|
||||
var dot := _label("●" if discovered else "○", 13)
|
||||
var dot := _label("●" if discovered else "○", 17)
|
||||
dot.add_theme_color_override("font_color", quality_color)
|
||||
dot.modulate.a = 1.0 if discovered else 0.48
|
||||
tier.add_child(dot)
|
||||
var tier_label := _label(FishQualityType.display_name(quality), 13)
|
||||
var tier_label := _label(FishQualityType.display_name(quality), 16)
|
||||
tier_label.add_theme_color_override(
|
||||
"font_color",
|
||||
INK if discovered else MUTED_INK,
|
||||
|
|
@ -659,7 +834,7 @@ func _build_quality_progress(fish_id: StringName) -> VBoxContainer:
|
|||
else "%s quality not yet collected"
|
||||
) % FishQualityType.display_name(quality)
|
||||
tier.add_child(tier_label)
|
||||
tiers.add_child(tier)
|
||||
row.add_child(tier)
|
||||
return quality_section
|
||||
|
||||
|
||||
|
|
@ -848,18 +1023,6 @@ func _entry_label_text(entry_name: String) -> String:
|
|||
)
|
||||
|
||||
|
||||
func _paper_style(color: Color, left: bool) -> StyleBoxFlat:
|
||||
var style := StyleBoxFlat.new()
|
||||
style.bg_color = color
|
||||
style.border_color = Color("9b7a4f")
|
||||
style.set_border_width_all(3)
|
||||
style.corner_radius_top_left = 26 if left else 6
|
||||
style.corner_radius_bottom_left = 32 if left else 6
|
||||
style.corner_radius_top_right = 6 if left else 28
|
||||
style.corner_radius_bottom_right = 6 if left else 34
|
||||
return style
|
||||
|
||||
|
||||
func _build_portrait_overlay() -> void:
|
||||
_portrait_overlay = Control.new()
|
||||
_portrait_overlay.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=11 format=3]
|
||||
[gd_scene load_steps=16 format=3]
|
||||
|
||||
[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"]
|
||||
|
|
@ -10,6 +10,11 @@
|
|||
[ext_resource type="Script" path="res://ui/components/bubble_menu/bubble_transition_flurry.gd" id="8_flurry"]
|
||||
[ext_resource type="PackedScene" path="res://ui/components/bubble_menu/bubble_confirmation_page.tscn" id="9_confirmation"]
|
||||
[ext_resource type="PackedScene" path="res://ui/network/join_game_page.tscn" id="10_join_page"]
|
||||
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/online_dark.png" id="11_online_dark"]
|
||||
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/save_dark.png" id="12_save_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/pictograms/undo_dark.png" id="14_undo_dark"]
|
||||
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/x_dark.png" id="15_x_dark"]
|
||||
|
||||
[node name="PauseMenu" type="Control"]
|
||||
unique_name_in_owner = true
|
||||
|
|
@ -87,7 +92,9 @@ compact_reference_size = Vector2(608, 448)
|
|||
[node name="ResumeButton" parent="ResponsivePauseStage/PausePresentationScaleRoot/RootPage/BubbleCluster" instance=ExtResource("5_bubble")]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 0)
|
||||
text = "return\nto game"
|
||||
texture_filter = 1
|
||||
icon = ExtResource("14_undo_dark")
|
||||
tooltip_text = "return to game"
|
||||
accessibility_name = "return to game"
|
||||
neutral_size = Vector2(174, 164)
|
||||
desktop_anchor = Vector2(335, 220)
|
||||
|
|
@ -100,7 +107,9 @@ motion_phase = 0.3
|
|||
[node name="SaveButton" parent="ResponsivePauseStage/PausePresentationScaleRoot/RootPage/BubbleCluster" instance=ExtResource("5_bubble")]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 0)
|
||||
text = "save\nnow"
|
||||
texture_filter = 1
|
||||
icon = ExtResource("12_save_dark")
|
||||
tooltip_text = "save now"
|
||||
accessibility_name = "save now"
|
||||
neutral_size = Vector2(126, 120)
|
||||
desktop_anchor = Vector2(185, 185)
|
||||
|
|
@ -113,7 +122,10 @@ motion_phase = 1.15
|
|||
[node name="SettingsButton" parent="ResponsivePauseStage/PausePresentationScaleRoot/RootPage/BubbleCluster" instance=ExtResource("5_bubble")]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 0)
|
||||
text = "settings"
|
||||
texture_filter = 1
|
||||
icon = ExtResource("13_settings_dark")
|
||||
tooltip_text = "settings"
|
||||
accessibility_name = "settings"
|
||||
neutral_size = Vector2(144, 136)
|
||||
desktop_anchor = Vector2(485, 170)
|
||||
compact_anchor = Vector2(515, 175)
|
||||
|
|
@ -125,7 +137,9 @@ motion_phase = 2.05
|
|||
[node name="JoinGameButton" parent="ResponsivePauseStage/PausePresentationScaleRoot/RootPage/BubbleCluster" instance=ExtResource("5_bubble")]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 0)
|
||||
text = "join\ngame"
|
||||
texture_filter = 1
|
||||
icon = ExtResource("11_online_dark")
|
||||
tooltip_text = "join game"
|
||||
accessibility_name = "join game"
|
||||
neutral_size = Vector2(126, 120)
|
||||
desktop_anchor = Vector2(105, 320)
|
||||
|
|
@ -164,7 +178,10 @@ motion_phase = 3.9
|
|||
[node name="QuitButton" parent="ResponsivePauseStage/PausePresentationScaleRoot/RootPage/BubbleCluster" instance=ExtResource("5_bubble")]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 0)
|
||||
text = "quit"
|
||||
texture_filter = 1
|
||||
icon = ExtResource("15_x_dark")
|
||||
tooltip_text = "quit"
|
||||
accessibility_name = "quit"
|
||||
neutral_size = Vector2(102, 98)
|
||||
desktop_anchor = Vector2(390, 395)
|
||||
compact_anchor = Vector2(390, 410)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=26 format=3]
|
||||
[gd_scene load_steps=27 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/player_menu.gd" id="1_menu"]
|
||||
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
[ext_resource type="Script" path="res://ui/components/inventory_notepad.gd" id="22_inventory_notepad"]
|
||||
[ext_resource type="Script" path="res://ui/components/organizer_tab.gd" id="23_organizer_tab"]
|
||||
[ext_resource type="PackedScene" path="res://ui/the_net_page.tscn" id="24_the_net_page"]
|
||||
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/online_dark.png" id="25_online_dark"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_collection"]
|
||||
|
||||
|
|
@ -700,11 +701,13 @@ layout_mode = 2
|
|||
text = "Select bait or a lure for details."
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="TackleEquipButton" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleDetailPanel/Margin/Layout"]
|
||||
[node name="TackleEquipButton" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/TackleBoxPage/TackleDetailPanel/Margin/Layout" instance=ExtResource("12_ink_action")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
custom_minimum_size = Vector2(0, 44)
|
||||
custom_minimum_size = Vector2(150, 68)
|
||||
size_flags_horizontal = 4
|
||||
text = "equip worms"
|
||||
allow_persistent_mark = true
|
||||
|
||||
[node name="LogbookPage" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot"]
|
||||
unique_name_in_owner = true
|
||||
|
|
@ -1506,7 +1509,9 @@ deformation_period = 6.5
|
|||
unique_name_in_owner = true
|
||||
layout_mode = 0
|
||||
toggle_mode = true
|
||||
text = "online"
|
||||
texture_filter = 1
|
||||
icon = ExtResource("25_online_dark")
|
||||
tooltip_text = "online"
|
||||
accessibility_name = "online"
|
||||
script = ExtResource("6_bubble")
|
||||
profile = ExtResource("4_profile")
|
||||
|
|
|
|||
|
|
@ -6,6 +6,12 @@ const OPTION_GRID_COLUMNS: int = 6
|
|||
const ControllerMappingManagerType = preload(
|
||||
"res://settings/controller_mapping_manager.gd"
|
||||
)
|
||||
const AnimaleseVoiceType = preload("res://ui/animalese_voice.gd")
|
||||
const TypewriterRevealType = preload("res://ui/typewriter_reveal.gd")
|
||||
const VoiceProfilesType = preload(
|
||||
"res://player/animalese_voice_profiles.gd"
|
||||
)
|
||||
const VOICE_CATEGORY_ID: String = "voice"
|
||||
|
||||
var _service: NetworkProfileService
|
||||
var _experience: PlayerExperience
|
||||
|
|
@ -13,6 +19,10 @@ var _draft_name: String = ""
|
|||
var _draft_appearance: Dictionary = {}
|
||||
var _persisted_name: String = ""
|
||||
var _persisted_appearance: Dictionary = {}
|
||||
var _draft_voice_id: String = VoiceProfilesType.DEFAULT_ID
|
||||
var _persisted_voice_id: String = VoiceProfilesType.DEFAULT_ID
|
||||
var _draft_speech_speed_id: String = VoiceProfilesType.DEFAULT_SPEED_ID
|
||||
var _persisted_speech_speed_id: String = VoiceProfilesType.DEFAULT_SPEED_ID
|
||||
var _category_id: String = "species"
|
||||
var _dirty: bool = false
|
||||
var _allow_duplicate: bool = false
|
||||
|
|
@ -36,6 +46,8 @@ var _experience_progress: ProgressBar
|
|||
var _experience_value: Label
|
||||
var _feature_preview_cache: Dictionary = {}
|
||||
var _scale_value_label: Label
|
||||
var _voice_preview: AnimaleseVoiceType
|
||||
var _voice_preview_tween: Tween
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
|
@ -104,6 +116,8 @@ func activate() -> void:
|
|||
func deactivate() -> void:
|
||||
visible = false
|
||||
_debounce.stop()
|
||||
if _voice_preview_tween != null and _voice_preview_tween.is_valid():
|
||||
_voice_preview_tween.kill()
|
||||
_preview.reset_view()
|
||||
|
||||
|
||||
|
|
@ -368,15 +382,22 @@ func _build_ui() -> void:
|
|||
UtilityPageStyle.apply_ocean_button(_keep_editing_button)
|
||||
confirm_buttons.add_child(_keep_editing_button)
|
||||
|
||||
_voice_preview = AnimaleseVoiceType.new()
|
||||
_voice_preview.name = "ProfileVoicePreview"
|
||||
add_child(_voice_preview)
|
||||
_build_categories()
|
||||
|
||||
|
||||
func _build_categories() -> void:
|
||||
for child: Node in _category_list.get_children():
|
||||
child.queue_free()
|
||||
for category_id: String in CharacterCustomizationCatalog.CATEGORY_IDS:
|
||||
var category_ids: Array[String] = []
|
||||
for appearance_category: String in CharacterCustomizationCatalog.CATEGORY_IDS:
|
||||
category_ids.append(appearance_category)
|
||||
category_ids.append(VOICE_CATEGORY_ID)
|
||||
for category_id: String in category_ids:
|
||||
var button := Button.new()
|
||||
button.text = CharacterCustomizationCatalog.category_label(category_id)
|
||||
button.text = _category_label(category_id)
|
||||
button.toggle_mode = true
|
||||
button.custom_minimum_size.x = 120.0
|
||||
button.button_pressed = category_id == _category_id
|
||||
|
|
@ -392,7 +413,7 @@ func _select_category(category_id: String) -> void:
|
|||
var button := child as Button
|
||||
if button != null:
|
||||
button.button_pressed = button.text == (
|
||||
CharacterCustomizationCatalog.category_label(category_id)
|
||||
_category_label(category_id)
|
||||
)
|
||||
_refresh_options()
|
||||
|
||||
|
|
@ -401,12 +422,15 @@ func _refresh_options() -> void:
|
|||
for child: Node in _option_list.get_children():
|
||||
child.queue_free()
|
||||
var title := Label.new()
|
||||
title.text = CharacterCustomizationCatalog.category_label(_category_id)
|
||||
title.text = _category_label(_category_id)
|
||||
title.add_theme_font_size_override("font_size", 18)
|
||||
title.add_theme_color_override(
|
||||
"font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY
|
||||
)
|
||||
_option_list.add_child(title)
|
||||
if _category_id == VOICE_CATEGORY_ID:
|
||||
_build_voice_options()
|
||||
return
|
||||
if _category_id == CharacterCustomizationCatalog.SCALE_CATEGORY_ID:
|
||||
_build_scale_option()
|
||||
return
|
||||
|
|
@ -440,6 +464,106 @@ func _refresh_options() -> void:
|
|||
_option_list.add_child(button)
|
||||
|
||||
|
||||
func _category_label(category_id: String) -> String:
|
||||
return (
|
||||
"voice"
|
||||
if category_id == VOICE_CATEGORY_ID
|
||||
else CharacterCustomizationCatalog.category_label(category_id)
|
||||
)
|
||||
|
||||
|
||||
func _build_voice_options() -> void:
|
||||
var description := Label.new()
|
||||
description.text = "Choose how your character sounds in chat."
|
||||
description.add_theme_color_override(
|
||||
"font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY
|
||||
)
|
||||
_option_list.add_child(description)
|
||||
var grid := GridContainer.new()
|
||||
grid.columns = 2
|
||||
grid.add_theme_constant_override("h_separation", 8)
|
||||
grid.add_theme_constant_override("v_separation", 8)
|
||||
_option_list.add_child(grid)
|
||||
for option: Dictionary in VoiceProfilesType.OPTIONS:
|
||||
var option_id := str(option.get("id", ""))
|
||||
var button := Button.new()
|
||||
button.text = str(option.get("label", option_id))
|
||||
button.toggle_mode = true
|
||||
button.custom_minimum_size = Vector2(170.0, 42.0)
|
||||
button.button_pressed = _draft_voice_id == option_id
|
||||
button.pressed.connect(_select_voice_option.bind(option_id))
|
||||
UtilityPageStyle.apply_compact_ocean_button(button)
|
||||
grid.add_child(button)
|
||||
var divider := HSeparator.new()
|
||||
divider.custom_minimum_size.y = 8.0
|
||||
_option_list.add_child(divider)
|
||||
var speed_title := Label.new()
|
||||
speed_title.text = "speech speed"
|
||||
speed_title.add_theme_font_size_override("font_size", 16)
|
||||
speed_title.add_theme_color_override(
|
||||
"font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY
|
||||
)
|
||||
_option_list.add_child(speed_title)
|
||||
var speed_description := Label.new()
|
||||
speed_description.text = "Controls player chat speech on this device only."
|
||||
speed_description.add_theme_color_override(
|
||||
"font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY
|
||||
)
|
||||
_option_list.add_child(speed_description)
|
||||
var speed_grid := GridContainer.new()
|
||||
speed_grid.columns = 2
|
||||
speed_grid.add_theme_constant_override("h_separation", 8)
|
||||
speed_grid.add_theme_constant_override("v_separation", 8)
|
||||
_option_list.add_child(speed_grid)
|
||||
for option: Dictionary in VoiceProfilesType.SPEED_OPTIONS:
|
||||
var speed_id := str(option.get("id", ""))
|
||||
var speed_button := Button.new()
|
||||
speed_button.text = str(option.get("label", speed_id))
|
||||
speed_button.tooltip_text = "%d characters per second" % roundi(
|
||||
float(option.get("characters_per_second", 28.0))
|
||||
)
|
||||
speed_button.toggle_mode = true
|
||||
speed_button.custom_minimum_size = Vector2(170.0, 38.0)
|
||||
speed_button.button_pressed = _draft_speech_speed_id == speed_id
|
||||
speed_button.pressed.connect(
|
||||
_select_speech_speed_option.bind(speed_id)
|
||||
)
|
||||
UtilityPageStyle.apply_compact_ocean_button(speed_button)
|
||||
speed_grid.add_child(speed_button)
|
||||
|
||||
|
||||
func _select_voice_option(voice_id: String) -> void:
|
||||
if not VoiceProfilesType.is_valid(voice_id):
|
||||
return
|
||||
_draft_voice_id = voice_id
|
||||
_dirty = _draft_differs()
|
||||
_refresh_options()
|
||||
_refresh_actions()
|
||||
_play_voice_preview()
|
||||
|
||||
|
||||
func _select_speech_speed_option(speed_id: String) -> void:
|
||||
if not VoiceProfilesType.is_valid_speed(speed_id):
|
||||
return
|
||||
_draft_speech_speed_id = speed_id
|
||||
_dirty = _draft_differs()
|
||||
_refresh_options()
|
||||
_refresh_actions()
|
||||
_play_voice_preview()
|
||||
|
||||
|
||||
func _play_voice_preview() -> void:
|
||||
if _voice_preview_tween != null and _voice_preview_tween.is_valid():
|
||||
_voice_preview_tween.kill()
|
||||
_voice_preview_tween = _voice_preview.speak_text(
|
||||
self,
|
||||
"hello there!",
|
||||
"profile-preview",
|
||||
_draft_voice_id,
|
||||
VoiceProfilesType.speed_for(_draft_speech_speed_id),
|
||||
)
|
||||
|
||||
|
||||
func _build_scale_option() -> void:
|
||||
var description := Label.new()
|
||||
description.text = "Adjust your character's visual size."
|
||||
|
|
@ -741,7 +865,13 @@ func _apply() -> void:
|
|||
if _service == null:
|
||||
return
|
||||
_apply_button.disabled = true
|
||||
_service.apply_profile(_draft_name, _draft_appearance, _allow_duplicate)
|
||||
_service.apply_profile(
|
||||
_draft_name,
|
||||
_draft_appearance,
|
||||
_allow_duplicate,
|
||||
_draft_voice_id,
|
||||
_draft_speech_speed_id,
|
||||
)
|
||||
|
||||
|
||||
func _on_apply_finished(accepted: bool, message: String) -> void:
|
||||
|
|
@ -756,8 +886,17 @@ func _load_persisted() -> void:
|
|||
return
|
||||
_persisted_name = _service.get_persisted_name()
|
||||
_persisted_appearance = _service.get_persisted_appearance()
|
||||
_persisted_voice_id = _service.get_persisted_voice_id()
|
||||
_persisted_speech_speed_id = (
|
||||
_service.get_persisted_speech_speed_id()
|
||||
)
|
||||
_draft_name = _persisted_name
|
||||
_draft_appearance = _persisted_appearance.duplicate(true)
|
||||
_draft_voice_id = _persisted_voice_id
|
||||
_draft_speech_speed_id = _persisted_speech_speed_id
|
||||
TypewriterRevealType.set_characters_per_second(
|
||||
VoiceProfilesType.speed_for(_persisted_speech_speed_id)
|
||||
)
|
||||
_name_edit.text = _draft_name
|
||||
_preview.apply_appearance_profile(_draft_appearance)
|
||||
_dirty = false
|
||||
|
|
@ -793,6 +932,8 @@ func _confirm_pending_action() -> void:
|
|||
_discard_confirmation.visible = false
|
||||
if _confirmation_action == "defaults":
|
||||
_draft_appearance = CharacterCustomizationCatalog.default_snapshot()
|
||||
_draft_voice_id = VoiceProfilesType.DEFAULT_ID
|
||||
_draft_speech_speed_id = VoiceProfilesType.DEFAULT_SPEED_ID
|
||||
_preview.apply_appearance_profile(_draft_appearance)
|
||||
_dirty = _draft_differs()
|
||||
_refresh_options()
|
||||
|
|
@ -806,6 +947,8 @@ func _draft_differs() -> bool:
|
|||
return (
|
||||
_draft_name.strip_edges() != _persisted_name
|
||||
or _draft_appearance != _persisted_appearance
|
||||
or _draft_voice_id != _persisted_voice_id
|
||||
or _draft_speech_speed_id != _persisted_speech_speed_id
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=7 format=3]
|
||||
[gd_scene load_steps=9 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/settings_panel.gd" id="1_panel"]
|
||||
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
|
||||
|
|
@ -6,6 +6,8 @@
|
|||
[ext_resource type="PackedScene" path="res://ui/components/bubble_menu/bubble_button.tscn" id="4_bubble"]
|
||||
[ext_resource type="Script" path="res://ui/components/bubble_menu/bubble_cluster.gd" id="5_cluster"]
|
||||
[ext_resource type="Resource" path="res://ui/components/bubble_menu/bubble_menu_profile.tres" id="6_profile"]
|
||||
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/undo_dark.png" id="7_undo_dark"]
|
||||
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/check_mark_dark.png" id="8_check_mark_dark"]
|
||||
|
||||
[node name="SettingsPanel" type="Control"]
|
||||
visible = false
|
||||
|
|
@ -109,7 +111,9 @@ offset_left = 474.0
|
|||
offset_top = 308.0
|
||||
offset_right = 586.0
|
||||
offset_bottom = 412.0
|
||||
text = "apply"
|
||||
icon = ExtResource("8_check_mark_dark")
|
||||
tooltip_text = "apply"
|
||||
accessibility_name = "apply"
|
||||
neutral_size = Vector2(112, 104)
|
||||
desktop_anchor = Vector2(511, 332)
|
||||
compact_anchor = Vector2(470, 330)
|
||||
|
|
@ -137,7 +141,9 @@ offset_left = 70.0
|
|||
offset_top = 343.0
|
||||
offset_right = 170.0
|
||||
offset_bottom = 437.0
|
||||
text = "back"
|
||||
icon = ExtResource("7_undo_dark")
|
||||
tooltip_text = "back"
|
||||
accessibility_name = "back"
|
||||
neutral_size = Vector2(100, 94)
|
||||
desktop_anchor = Vector2(220, 300)
|
||||
compact_anchor = Vector2(155, 275)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
[gd_scene load_steps=3 format=3]
|
||||
[gd_scene load_steps=6 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/surface_drawing_toolbar.gd" id="1_script"]
|
||||
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
|
||||
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/undo_light.png" id="3_undo_light"]
|
||||
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/check_mark_light.png" id="4_check_mark_light"]
|
||||
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/x_light.png" id="5_x_light"]
|
||||
|
||||
[node name="SurfaceDrawingToolbar" type="Control"]
|
||||
visible = false
|
||||
|
|
@ -55,8 +58,11 @@ text = "⌫"
|
|||
[node name="UndoButton" type="Button" parent="TopPanel/Top"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
texture_filter = 1
|
||||
tooltip_text = "undo last stroke"
|
||||
text = "↶"
|
||||
accessibility_name = "undo last stroke"
|
||||
icon = ExtResource("3_undo_light")
|
||||
expand_icon = true
|
||||
|
||||
[node name="HideGuideButton" type="Button" parent="TopPanel/Top"]
|
||||
unique_name_in_owner = true
|
||||
|
|
@ -75,15 +81,21 @@ text = "▤"
|
|||
[node name="FinalizeGuideButton" type="Button" parent="TopPanel/Top"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
texture_filter = 1
|
||||
tooltip_text = "finish grid on next click"
|
||||
toggle_mode = true
|
||||
text = "✓"
|
||||
accessibility_name = "finish grid on next click"
|
||||
icon = ExtResource("4_check_mark_light")
|
||||
expand_icon = true
|
||||
|
||||
[node name="CloseButton" type="Button" parent="TopPanel/Top"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
texture_filter = 1
|
||||
tooltip_text = "close art tools"
|
||||
text = "×"
|
||||
accessibility_name = "close art tools"
|
||||
icon = ExtResource("5_x_light")
|
||||
expand_icon = true
|
||||
|
||||
[node name="ColorPanel" type="PanelContainer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
|
|
|
|||
|
|
@ -407,9 +407,9 @@ func _refresh_forecast(force: bool) -> void:
|
|||
slot.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
_forecast_list.add_child(slot)
|
||||
var icon := WeatherIcon.new()
|
||||
icon.position = Vector2(1.0, 7.0)
|
||||
icon.size = Vector2(56.0, 56.0)
|
||||
icon.custom_minimum_size = Vector2(56.0, 56.0)
|
||||
icon.position = Vector2(10.5, 17.0)
|
||||
icon.size = Vector2(42.0, 42.0)
|
||||
icon.custom_minimum_size = Vector2(42.0, 42.0)
|
||||
icon.set_weather(weather)
|
||||
icon.set_nighttime(
|
||||
WorldTimeService.phase_for_hour(start_hour)
|
||||
|
|
@ -417,13 +417,13 @@ func _refresh_forecast(force: bool) -> void:
|
|||
)
|
||||
slot.add_child(icon)
|
||||
var time_label := Label.new()
|
||||
time_label.position = Vector2(34.0, 0.0)
|
||||
time_label.size = Vector2(29.0, 18.0)
|
||||
time_label.position = Vector2(27.0, 0.0)
|
||||
time_label.size = Vector2(36.0, 22.0)
|
||||
time_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
time_label.text = _forecast_exponent_time(start_hour)
|
||||
time_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
time_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
time_label.add_theme_font_size_override("font_size", 10)
|
||||
time_label.add_theme_font_size_override("font_size", 13)
|
||||
time_label.add_theme_constant_override("outline_size", 3)
|
||||
time_label.add_theme_color_override(
|
||||
"font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=14 format=3]
|
||||
[gd_scene load_steps=17 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/title_screen.gd" id="1_script"]
|
||||
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
|
||||
|
|
@ -11,6 +11,9 @@
|
|||
[ext_resource type="Resource" path="res://ui/components/bubble_menu/bubble_menu_profile.tres" id="9_bubble_profile"]
|
||||
[ext_resource type="PackedScene" path="res://ui/title_confirmation_bubble_page.tscn" id="10_confirmation_page"]
|
||||
[ext_resource type="PackedScene" path="res://ui/network/join_game_page.tscn" id="11_join_page"]
|
||||
[ext_resource type="Texture2D" path="res://ui/icons/pictograms/online_dark.png" id="12_online_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/pictograms/x_dark.png" id="14_x_dark"]
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_title_water"]
|
||||
shader = ExtResource("4_water_shader")
|
||||
|
|
@ -303,7 +306,10 @@ offset_left = 264.0
|
|||
offset_top = 17.0
|
||||
offset_right = 384.0
|
||||
offset_bottom = 133.0
|
||||
text = "settings"
|
||||
texture_filter = 1
|
||||
icon = ExtResource("13_settings_dark")
|
||||
tooltip_text = "settings"
|
||||
accessibility_name = "settings"
|
||||
script = ExtResource("7_bubble_button")
|
||||
profile = ExtResource("9_bubble_profile")
|
||||
desktop_anchor = Vector2(324, 75)
|
||||
|
|
@ -322,7 +328,10 @@ offset_left = 126.0
|
|||
offset_top = 224.0
|
||||
offset_right = 248.0
|
||||
offset_bottom = 342.0
|
||||
text = "join\ngame"
|
||||
texture_filter = 1
|
||||
icon = ExtResource("12_online_dark")
|
||||
tooltip_text = "join game"
|
||||
accessibility_name = "join game"
|
||||
script = ExtResource("7_bubble_button")
|
||||
profile = ExtResource("9_bubble_profile")
|
||||
neutral_size = Vector2(122, 118)
|
||||
|
|
@ -380,7 +389,10 @@ offset_left = 275.0
|
|||
offset_top = 217.0
|
||||
offset_right = 369.0
|
||||
offset_bottom = 309.0
|
||||
text = "quit"
|
||||
texture_filter = 1
|
||||
icon = ExtResource("14_x_dark")
|
||||
tooltip_text = "quit"
|
||||
accessibility_name = "quit"
|
||||
script = ExtResource("7_bubble_button")
|
||||
profile = ExtResource("9_bubble_profile")
|
||||
neutral_size = Vector2(94, 92)
|
||||
|
|
|
|||
47
ui/typewriter_reveal.gd
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
class_name TypewriterReveal
|
||||
extends RefCounted
|
||||
|
||||
const DEFAULT_CHARACTERS_PER_SECOND: float = 28.0
|
||||
const MIN_CHARACTERS_PER_SECOND: float = 12.0
|
||||
const MAX_CHARACTERS_PER_SECOND: float = 60.0
|
||||
|
||||
static var _characters_per_second: float = DEFAULT_CHARACTERS_PER_SECOND
|
||||
|
||||
|
||||
static func set_characters_per_second(value: float) -> void:
|
||||
_characters_per_second = clampf(
|
||||
value,
|
||||
MIN_CHARACTERS_PER_SECOND,
|
||||
MAX_CHARACTERS_PER_SECOND,
|
||||
)
|
||||
|
||||
|
||||
static func get_characters_per_second() -> float:
|
||||
return _characters_per_second
|
||||
|
||||
|
||||
static func start(
|
||||
label: Label,
|
||||
characters_per_second: float = -1.0,
|
||||
) -> float:
|
||||
if label == null:
|
||||
return 0.0
|
||||
var character_count: int = label.get_total_character_count()
|
||||
if character_count <= 0:
|
||||
label.visible_characters = -1
|
||||
return 0.0
|
||||
label.visible_characters = 0
|
||||
var resolved_characters_per_second := (
|
||||
characters_per_second
|
||||
if characters_per_second > 0.0
|
||||
else _characters_per_second
|
||||
)
|
||||
var duration := float(character_count) / resolved_characters_per_second
|
||||
var reveal_tween := label.create_tween()
|
||||
reveal_tween.tween_property(
|
||||
label,
|
||||
"visible_characters",
|
||||
character_count,
|
||||
duration,
|
||||
).from(0).set_trans(Tween.TRANS_LINEAR)
|
||||
return duration
|
||||
1
ui/typewriter_reveal.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://c1as5qlog6ijy
|
||||
|
|
@ -1,11 +1,21 @@
|
|||
class_name WeatherIcon
|
||||
extends Control
|
||||
|
||||
const BUBBLE_COLOR := Color(0.025, 0.13, 0.19, 0.94)
|
||||
const ICON_COLOR := Color(0.78, 0.91, 0.95)
|
||||
const SUN_COLOR := Color(0.98, 0.82, 0.34)
|
||||
const MOON_COLOR := Color(0.78, 0.88, 1.0)
|
||||
const RAIN_COLOR := Color(0.28, 0.73, 0.82)
|
||||
const CLEAR_DAY_TEXTURE: Texture2D = preload(
|
||||
"res://ui/icons/weather/weather_clear_day.png"
|
||||
)
|
||||
const CLEAR_NIGHT_TEXTURE: Texture2D = preload(
|
||||
"res://ui/icons/weather/weather_clear_night_full.png"
|
||||
)
|
||||
const CLOUDY_TEXTURE: Texture2D = preload(
|
||||
"res://ui/icons/weather/weather_cloudy.png"
|
||||
)
|
||||
const FOG_TEXTURE: Texture2D = preload(
|
||||
"res://ui/icons/weather/weather_fog.png"
|
||||
)
|
||||
const RAIN_TEXTURE: Texture2D = preload(
|
||||
"res://ui/icons/weather/weather_rain.png"
|
||||
)
|
||||
|
||||
var _weather: WorldWeatherService.Weather = WorldWeatherService.Weather.SUNNY
|
||||
var _is_nighttime: bool = false
|
||||
|
|
@ -14,6 +24,7 @@ var _is_nighttime: bool = false
|
|||
func _ready() -> void:
|
||||
mouse_filter = Control.MOUSE_FILTER_PASS
|
||||
focus_mode = Control.FOCUS_NONE
|
||||
texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
custom_minimum_size = Vector2(34.0, 34.0)
|
||||
_update_tooltip()
|
||||
queue_redraw()
|
||||
|
|
@ -44,55 +55,22 @@ func get_weather() -> WorldWeatherService.Weather:
|
|||
|
||||
|
||||
func _draw() -> void:
|
||||
var center: Vector2 = size * 0.5
|
||||
var radius: float = minf(size.x, size.y) * 0.5
|
||||
draw_circle(center, radius, BUBBLE_COLOR)
|
||||
var icon_size := Vector2.ONE * minf(size.x, size.y)
|
||||
var icon_rect := Rect2((size - icon_size) * 0.5, icon_size)
|
||||
draw_texture_rect(_get_weather_texture(), icon_rect, false)
|
||||
|
||||
|
||||
func _get_weather_texture() -> Texture2D:
|
||||
match _weather:
|
||||
WorldWeatherService.Weather.SUNNY:
|
||||
if _is_nighttime:
|
||||
_draw_moon(center)
|
||||
else:
|
||||
_draw_sun(center)
|
||||
return CLEAR_NIGHT_TEXTURE if _is_nighttime else CLEAR_DAY_TEXTURE
|
||||
WorldWeatherService.Weather.CLOUDY:
|
||||
_draw_cloud(center + Vector2(0.0, 1.0))
|
||||
return CLOUDY_TEXTURE
|
||||
WorldWeatherService.Weather.RAINY:
|
||||
_draw_cloud(center + Vector2(0.0, -2.0))
|
||||
for x_offset: float in PackedFloat32Array([-6.0, 0.0, 6.0]):
|
||||
draw_line(
|
||||
center + Vector2(x_offset + 1.0, 5.0),
|
||||
center + Vector2(x_offset - 1.0, 9.0),
|
||||
RAIN_COLOR,
|
||||
2.0,
|
||||
true,
|
||||
)
|
||||
return RAIN_TEXTURE
|
||||
WorldWeatherService.Weather.FOGGY:
|
||||
for y_offset: float in PackedFloat32Array([-6.0, 0.0, 6.0]):
|
||||
draw_line(
|
||||
center + Vector2(-9.0, y_offset),
|
||||
center + Vector2(9.0, y_offset),
|
||||
ICON_COLOR,
|
||||
2.0,
|
||||
true,
|
||||
)
|
||||
|
||||
|
||||
func _draw_sun(center: Vector2) -> void:
|
||||
draw_circle(center, 5.0, SUN_COLOR)
|
||||
for index: int in 8:
|
||||
var angle: float = TAU * float(index) / 8.0
|
||||
var direction := Vector2(cos(angle), sin(angle))
|
||||
draw_line(
|
||||
center + direction * 8.0,
|
||||
center + direction * 11.0,
|
||||
SUN_COLOR,
|
||||
2.0,
|
||||
true,
|
||||
)
|
||||
|
||||
|
||||
func _draw_moon(center: Vector2) -> void:
|
||||
draw_circle(center, 8.0, MOON_COLOR)
|
||||
draw_circle(center + Vector2(4.0, -2.0), 7.0, BUBBLE_COLOR)
|
||||
return FOG_TEXTURE
|
||||
return CLEAR_DAY_TEXTURE
|
||||
|
||||
|
||||
func _update_tooltip() -> void:
|
||||
|
|
@ -101,10 +79,3 @@ func _update_tooltip() -> void:
|
|||
if _weather == WorldWeatherService.Weather.SUNNY
|
||||
else WorldWeatherService.weather_name(_weather)
|
||||
)
|
||||
|
||||
|
||||
func _draw_cloud(center: Vector2) -> void:
|
||||
draw_circle(center + Vector2(-5.0, 0.0), 5.0, ICON_COLOR)
|
||||
draw_circle(center + Vector2(0.0, -3.0), 6.0, ICON_COLOR)
|
||||
draw_circle(center + Vector2(6.0, 0.0), 5.0, ICON_COLOR)
|
||||
draw_rect(Rect2(center + Vector2(-9.0, 0.0), Vector2(18.0, 5.0)), ICON_COLOR)
|
||||
|
|
|
|||