Add synchronized time and weather systems

This commit is contained in:
Alexander Sellite 2026-08-02 12:48:33 -04:00
parent 30ad9215ca
commit 58fb3d6605
37 changed files with 1900 additions and 17 deletions

View file

@ -20,6 +20,16 @@ const HANDLE_SIZE := Vector2(34, 42)
const HANDLE_GAP: float = 6.0
const COLLAPSED_REVEAL_WIDTH: float = 8.0
const HINT_EDGE_MARGIN: float = 4.0
const CLOCK_SIZE := Vector2(116.0, 34.0)
const CLOCK_EDGE_MARGIN: float = 10.0
const CLOCK_PANEL_GAP: float = 8.0
const WEATHER_ICON_SIZE := Vector2(34.0, 34.0)
const WEATHER_ICON_GAP: float = 6.0
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")
enum PresentationState {
COLLAPSED,
@ -44,6 +54,9 @@ var _height_button: Button
var _unread_indicator: Label
var _hint: Label
var _speech_layer: Control
var _clock_panel: PanelContainer
var _clock_label: Label
var _weather_icon: WeatherIconType
var _speech: Dictionary[int, Dictionary] = {}
var _draft_save_timer: Timer
var _opacity_tween: Tween
@ -62,6 +75,8 @@ var _prior_movement: bool = true
var _prior_camera: bool = true
var _output_scale: float = 1.0
var _dock_right: bool = false
var _world_time: WorldTimeServiceType
var _world_weather: WorldWeatherServiceType
func _ready() -> void:
@ -79,6 +94,8 @@ func setup(
player: Player,
fishing_spot: FishingSpot,
settings: PlayerSettingsManager,
world_time: WorldTimeServiceType,
world_weather: WorldWeatherServiceType,
) -> void:
_service = service
_session = session
@ -86,6 +103,29 @@ func setup(
_player = player
_fishing_spot = fishing_spot
_settings = settings
_world_time = world_time
_world_weather = world_weather
if (
_world_time != null
and not _world_time.time_changed.is_connected(
_on_world_time_changed
)
):
_world_time.time_changed.connect(_on_world_time_changed)
_on_world_time_changed(
_world_time.get_time_hours(), _world_time.get_phase()
)
if (
_world_weather != null
and not _world_weather.weather_changed.is_connected(
_on_world_weather_changed
)
):
_world_weather.weather_changed.connect(_on_world_weather_changed)
_on_world_weather_changed(
_world_weather.get_weather(),
_world_weather.get_seconds_remaining(),
)
set_dock_right(_settings.current_settings.chat_dock_right)
_service.message_received.connect(_on_message)
_service.local_message_confirmed.connect(_on_local_message_confirmed)
@ -184,6 +224,31 @@ func _exit_tree() -> void:
func _build_ui() -> void:
_clock_panel = PanelContainer.new()
_clock_panel.name = "WorldClockPanel"
_clock_panel.size = CLOCK_SIZE
_clock_panel.mouse_filter = Control.MOUSE_FILTER_IGNORE
_clock_panel.add_theme_stylebox_override(
"panel", _clock_panel_style()
)
add_child(_clock_panel)
_clock_label = Label.new()
_clock_label.name = "WorldClockLabel"
_clock_label.text = "8:00 am"
_clock_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
_clock_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
_clock_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
_clock_label.add_theme_font_override("font", UtilityPageStyle.TuffyFont)
_clock_label.add_theme_font_size_override("font_size", 18)
_clock_label.add_theme_color_override(
"font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY
)
_clock_panel.add_child(_clock_label)
_weather_icon = WeatherIconType.new()
_weather_icon.name = "WorldWeatherIcon"
_weather_icon.size = WEATHER_ICON_SIZE
_weather_icon.mouse_filter = Control.MOUSE_FILTER_STOP
add_child(_weather_icon)
_panel = PanelContainer.new()
_panel.name = "ChatPanel"
_panel.mouse_filter = Control.MOUSE_FILTER_STOP
@ -326,6 +391,8 @@ func _build_ui() -> void:
_draft_save_timer.timeout.connect(_flush_draft)
add_child(_draft_save_timer)
_panel.hide()
_clock_panel.hide()
_weather_icon.hide()
_collapse_button.hide()
_height_button.hide()
_unread_indicator.hide()
@ -346,6 +413,16 @@ func _chat_panel_style() -> StyleBoxFlat:
return style
func _clock_panel_style() -> StyleBoxFlat:
var style := _borderless_style(Color(0.025, 0.13, 0.19, 0.94))
style.set_corner_radius_all(12)
style.content_margin_left = 10
style.content_margin_right = 10
style.content_margin_top = 4
style.content_margin_bottom = 4
return style
func _borderless_style(color: Color) -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = color
@ -525,11 +602,15 @@ func _refresh_history() -> void:
func _refresh_visibility() -> void:
if not _available:
_panel.hide()
_clock_panel.hide()
_weather_icon.hide()
_collapse_button.hide()
_height_button.hide()
_unread_indicator.hide()
_hint.hide()
return
_clock_panel.show()
_weather_icon.show()
_collapse_button.show()
var collapsed := _presentation_state == PresentationState.COLLAPSED
_panel.show()
@ -737,6 +818,25 @@ func _layout_presentation(animate: bool) -> void:
panel_x = -(PANEL_WIDTH - COLLAPSED_REVEAL_WIDTH) if collapsed else 0.0
var target_position := Vector2(panel_x, bottom - height)
var target_size := Vector2(PANEL_WIDTH, height)
var clock_x: float = (
viewport_width - CLOCK_SIZE.x - CLOCK_EDGE_MARGIN
if _dock_right else CLOCK_EDGE_MARGIN
)
var clock_position := Vector2(
clock_x,
maxf(
CLOCK_EDGE_MARGIN,
target_position.y - CLOCK_SIZE.y - CLOCK_PANEL_GAP,
),
)
var weather_icon_x: float = (
clock_position.x - WEATHER_ICON_GAP - WEATHER_ICON_SIZE.x
if _dock_right
else clock_position.x + CLOCK_SIZE.x + WEATHER_ICON_GAP
)
var weather_icon_position := Vector2(
weather_icon_x, clock_position.y
)
var handle_stack_height := HANDLE_SIZE.y * 2.0 + HANDLE_GAP
var handle_stack_top := (
bottom - COMPACT_HEIGHT * 0.5 - handle_stack_height * 0.5
@ -766,6 +866,10 @@ func _layout_presentation(animate: bool) -> void:
if not animate:
_panel.position = target_position
_panel.size = target_size
_clock_panel.position = clock_position
_clock_panel.size = CLOCK_SIZE
_weather_icon.position = weather_icon_position
_weather_icon.size = WEATHER_ICON_SIZE
_collapse_button.position = collapse_position
_height_button.position = height_position
_unread_indicator.position = unread_position
@ -779,6 +883,18 @@ func _layout_presentation(animate: bool) -> void:
_height_tween.tween_property(
_panel, "size", target_size, UIMotion.CHAT_RESIZE_DURATION
).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
_height_tween.tween_property(
_clock_panel,
"position",
clock_position,
UIMotion.CHAT_RESIZE_DURATION,
).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
_height_tween.tween_property(
_weather_icon,
"position",
weather_icon_position,
UIMotion.CHAT_RESIZE_DURATION,
).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
_height_tween.tween_property(
_collapse_button,
"position",
@ -836,6 +952,22 @@ func _on_viewport_resized() -> void:
_layout_presentation(false)
func _on_world_time_changed(
_time_hours: float,
_phase: WorldTimeService.Phase,
) -> void:
if _clock_label != null and _world_time != null:
_clock_label.text = _world_time.get_clock_text()
func _on_world_weather_changed(
weather: WorldWeatherService.Weather,
_seconds_remaining: float,
) -> void:
if _weather_icon != null:
_weather_icon.set_weather(weather)
func _on_exterior_handle_entered() -> void:
_panel_hovered = true
_update_panel_opacity(true)

View file

@ -39,6 +39,10 @@ const SurfaceDrawingToolbarType = preload(
const PlayerSettingsManagerType = preload(
"res://settings/player_settings_manager.gd"
)
const WorldTimeServiceType = preload("res://world/world_time_service.gd")
const WorldWeatherServiceType = preload(
"res://world/world_weather_service.gd"
)
signal pixelation_settings_visibility_changed(is_visible: bool)
signal crisp_reset_focus_requested
@ -145,13 +149,15 @@ func setup(
settings_manager: PlayerSettingsManagerType,
surface_drawing: NetworkSurfaceDrawingService,
art_unlocks: PlayerArtUnlocks,
world_time: WorldTimeServiceType,
world_weather: WorldWeatherServiceType,
) -> void:
_player = player
_fishing_spot = fishing_spot
_item_effects = item_effects
_chat_ui.setup(
network_chat_service, network_session, spawn_service, player,
fishing_spot, settings_manager
fishing_spot, settings_manager, world_time, world_weather
)
_title_settings_panel.setup_network_profile(
network_profile, network_session

80
ui/weather_icon.gd Normal file
View file

@ -0,0 +1,80 @@
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 RAIN_COLOR := Color(0.28, 0.73, 0.82)
var _weather: WorldWeatherService.Weather = WorldWeatherService.Weather.SUNNY
func _ready() -> void:
mouse_filter = Control.MOUSE_FILTER_PASS
focus_mode = Control.FOCUS_NONE
custom_minimum_size = Vector2(34.0, 34.0)
tooltip_text = "sunny"
queue_redraw()
func set_weather(weather: WorldWeatherService.Weather) -> void:
if _weather == weather:
return
_weather = weather
tooltip_text = WorldWeatherService.weather_name(_weather)
queue_redraw()
func get_weather() -> WorldWeatherService.Weather:
return _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)
match _weather:
WorldWeatherService.Weather.SUNNY:
_draw_sun(center)
WorldWeatherService.Weather.CLOUDY:
_draw_cloud(center + Vector2(0.0, 1.0))
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,
)
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_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)

1
ui/weather_icon.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://crcognj0s8ntq