Add weather presentation and gameplay UI improvements

This commit is contained in:
Alexander Sellite 2026-08-13 01:31:05 -04:00
parent db6074ddfa
commit 51fa6535b6
28 changed files with 1067 additions and 61 deletions

View file

@ -85,6 +85,14 @@ func _run() -> void:
assert(not toolbar.is_docked_right())
var chat_panel := chat_ui.get_node("ChatPanel") as PanelContainer
var chat_height_button := chat_ui.get_node("ChatHeightButton") as Button
var chat_status := chat_ui.find_child(
"ChatStatus", true, false
) as Label
assert(chat_status != null)
assert(
chat_status.autowrap_mode
== TextServer.AUTOWRAP_WORD_SMART
)
assert(is_zero_approx(chat_panel.position.y))
assert(is_equal_approx(chat_panel.size.x, ChatUI.MOBILE_COMPACT_WIDTH))
assert(not chat_height_button.visible)
@ -101,6 +109,17 @@ func _run() -> void:
default_docks.paint_dock_right = true
assert(settings_manager.apply_settings(default_docks))
await process_frame
chat_ui.call(
"_set_status",
(
"Unknown command: /this-command-name-is-intentionally-long-"
+ "enough-to-require-smart-character-wrapping"
),
)
await process_frame
assert(is_equal_approx(chat_panel.size.x, ChatUI.PANEL_WIDTH))
assert(chat_status.size.x <= ChatUI.PANEL_WIDTH)
chat_ui.call("_set_status", "")
assert(not chat_ui.is_docked_right())
assert(not chat_ui.is_mobile_mode())
assert(toolbar.is_docked_right())
@ -140,6 +159,42 @@ func _run() -> void:
assert(bool(game_ui.call("_handle_controller_chat_controls", select_button)))
assert(chat_ui.is_open())
chat_ui.refocus_gameplay()
var quick_menu := game_ui.get_node(
"%QuickRadialMenu"
) as QuickRadialMenu
var emote_menu := game_ui.get_node(
"%EmoteRadialMenu"
) as EmoteRadialMenu
assert(quick_menu != null and emote_menu != null)
assert(QuickRadialMenu.ACTIONS.size() == QuickRadialMenu.SECTOR_COUNT)
assert(QuickRadialMenu.ACTIONS.has(&"freecam"))
assert(QuickRadialMenu.ACTIONS.has(&"hud"))
game_ui.call("_on_quick_action_selected", &"freecam")
assert(player.is_free_camera_active())
assert(player.get_active_gameplay_camera().name == &"FreeCamera")
game_ui.call("_on_quick_action_selected", &"freecam")
assert(not player.is_free_camera_active())
game_ui.call("_on_quick_action_selected", &"hud")
await process_frame
assert(game_ui.is_gameplay_hud_hidden())
assert(chat_ui.is_hud_hidden())
assert(not (
game_ui.get_node("%GameplayTransientHUD") as Control
).visible)
assert(not (
game_ui.get_node("%ExperiencePresentation") as Control
).visible)
quick_menu.open_menu(true)
assert(quick_menu.visible and quick_menu.is_open())
quick_menu.close_menu()
emote_menu.open_menu(true)
assert(emote_menu.visible and emote_menu.is_open())
emote_menu.close_menu()
chat_ui.open_chat()
await process_frame
assert(chat_ui.is_open() and chat_panel.visible)
chat_ui.refocus_gameplay()
assert(chat_ui.is_hud_hidden())
var cancel_button := InputEventJoypadButton.new()
cancel_button.button_index = JOY_BUTTON_B
cancel_button.pressed = true
@ -152,10 +207,16 @@ func _run() -> void:
player_menu.open_section(PlayerMenu.Section.PROFILE)
for _frame: int in 12:
await process_frame
assert(game_ui.is_gameplay_hud_hidden())
assert(player_menu.visible)
assert(root.gui_get_focus_owner() is not LineEdit)
player_menu.close_menu()
for _frame: int in 12:
await process_frame
game_ui.call("_on_quick_action_selected", &"hud")
await process_frame
assert(not game_ui.is_gameplay_hud_hidden())
assert(not chat_ui.is_hud_hidden())
assert(not service.can_activate())
assert(not service.is_active() and not toolbar.visible)
assert(player.bag.add_item(ArtShopStock.ART_KIT_ITEM_ID, 1))

View file

@ -31,13 +31,15 @@ func _run_host() -> void:
main.get_node("%WorldWeatherService") as WorldWeatherService
)
assert(session.start_private_host(TEST_PORT))
var game_ui := main.get_node("%GameUI") as CanvasLayer
var chat_ui := game_ui.get_node("%ChatUI") as ChatUI
assert(chat_ui.call("_handle_chat_command", "/weather clear"))
assert(world_weather.get_weather() == WorldWeatherService.Weather.SUNNY)
world_time.synchronize_time(INITIAL_HOST_TIME)
assert(is_equal_approx(
world_time.get_persistent_time_hours(), INITIAL_HOST_TIME
))
world_weather.apply_authoritative_snapshot(
WorldWeatherService.Weather.RAINY, 300.0
)
assert(chat_ui.call("_handle_chat_command", "/weather rainy"))
assert(
world_weather.get_persistent_weather()
== WorldWeatherService.Weather.RAINY
@ -66,9 +68,7 @@ func _run_host() -> void:
assert(is_equal_approx(
world_time.get_persistent_time_hours(), UPDATED_HOST_TIME
))
world_weather.apply_authoritative_snapshot(
WorldWeatherService.Weather.FOGGY, 300.0
)
assert(chat_ui.call("_handle_chat_command", "/weather foggy"))
assert(
world_weather.get_persistent_weather()
== WorldWeatherService.Weather.FOGGY

View file

@ -249,6 +249,15 @@ func _validate_environment_presentation() -> void:
< 0.01
)
var day_ambient_energy: float = runtime_environment.ambient_light_energy
assert(not runtime_environment.fog_enabled)
assert(is_equal_approx(runtime_environment.fog_sky_affect, 0.35))
var water_shader: Shader = preload(
"res://world/materials/stylized_water.gdshader"
)
assert("fog_disabled" in water_shader.code)
assert(is_zero_approx(float(
SaltWaterMaterial.get_shader_parameter("weather_fog_amount")
)))
var day_water_brightness := float(
SaltWaterMaterial.get_shader_parameter("environment_brightness")
)
@ -292,17 +301,24 @@ func _validate_environment_presentation() -> void:
)
assert(night_water_tint.get_luminance() < 0.5)
var night_background_energy := runtime_environment.background_energy_multiplier
var night_ambient_energy := runtime_environment.ambient_light_energy
var night_fog_light_energy := runtime_environment.fog_light_energy
var night_adjustment_brightness := runtime_environment.adjustment_brightness
var night_adjustment_saturation := runtime_environment.adjustment_saturation
visuals.apply_weather_immediately(WorldWeatherService.Weather.FOGGY)
visuals.apply_time_immediately(0.0)
assert(runtime_environment.fog_enabled)
assert(float(
SaltWaterMaterial.get_shader_parameter("weather_fog_amount")
) > 0.99)
var foggy_sky_horizon := (
runtime_sky_material.get_shader_parameter("sky_horizon_color") as Color
)
assert(foggy_sky_horizon.is_equal_approx(night_horizon))
assert(is_zero_approx(runtime_environment.fog_sky_affect))
assert(is_equal_approx(runtime_environment.fog_sky_affect, 1.0))
assert(is_zero_approx(runtime_environment.fog_aerial_perspective))
assert(is_equal_approx(runtime_environment.fog_depth_begin, 3.0))
assert(is_equal_approx(runtime_environment.fog_depth_end, 28.0))
assert(is_equal_approx(runtime_environment.fog_depth_end, 18.0))
assert(is_equal_approx(
float(SaltWaterMaterial.get_shader_parameter("environment_brightness")),
night_water_brightness,
@ -311,6 +327,14 @@ func _validate_environment_presentation() -> void:
runtime_environment.background_energy_multiplier,
night_background_energy,
))
assert(is_equal_approx(
runtime_environment.ambient_light_energy,
night_ambient_energy,
))
assert(is_equal_approx(
runtime_environment.fog_light_energy,
night_fog_light_energy,
))
assert(is_equal_approx(
runtime_environment.adjustment_brightness,
night_adjustment_brightness,
@ -321,6 +345,10 @@ func _validate_environment_presentation() -> void:
))
visuals.apply_weather_immediately(WorldWeatherService.Weather.SUNNY)
visuals.apply_time_immediately(20.0)
assert(not runtime_environment.fog_enabled)
assert(is_zero_approx(float(
SaltWaterMaterial.get_shader_parameter("weather_fog_amount")
)))
var dusk_horizon: Color = runtime_sky_material.get_shader_parameter(
"sky_horizon_color"
) as Color

View file

@ -24,6 +24,7 @@ func _initialize() -> void:
func _run() -> void:
_validate_weather_scheduler()
_validate_authoritative_weather_override()
_validate_weather_persistence()
_validate_snapshot_bounds()
_validate_fishing_weather_seams()
@ -74,6 +75,54 @@ func _duration_is_valid(weather: WorldWeatherServiceType) -> bool:
return false
func _validate_authoritative_weather_override() -> void:
var weather := WorldWeatherServiceType.new()
root.add_child(weather)
assert(not weather.set_authoritative_weather(
WorldWeatherServiceType.Weather.FOGGY
))
weather.begin_authoritative_session(20260812)
assert(weather.set_authoritative_weather(
WorldWeatherServiceType.Weather.FOGGY
))
assert(weather.is_foggy())
assert(_duration_is_valid(weather))
weather.end_session()
assert(not weather.set_authoritative_weather(
WorldWeatherServiceType.Weather.RAINY
))
var clock := WorldTimeServiceType.new()
root.add_child(clock)
clock.begin_session(WorldTimeServiceType.DAY_START_HOUR)
var schedule: Array[Dictionary] = []
for index: int in WorldWeatherServiceType.DAILY_PLAN_SEGMENT_COUNT:
schedule.append({
"start_hour": fposmod(
WorldTimeServiceType.DAY_START_HOUR
+ float(index)
* WorldWeatherServiceType.DAILY_PLAN_SEGMENT_HOURS,
WorldTimeServiceType.HOURS_PER_DAY,
),
"weather": int(WorldWeatherServiceType.Weather.SUNNY),
})
assert(weather.configure_daily_plan("command-test", schedule, clock))
weather.begin_authoritative_session(20260812)
assert(weather.set_authoritative_weather(
WorldWeatherServiceType.Weather.RAINY
))
weather.advance_weather(1.0)
assert(weather.is_raining())
clock.synchronize_time(
WorldTimeServiceType.DAY_START_HOUR
+ WorldWeatherServiceType.DAILY_PLAN_SEGMENT_HOURS
)
weather.advance_weather(0.01)
assert(weather.get_weather() == WorldWeatherServiceType.Weather.SUNNY)
weather.queue_free()
clock.queue_free()
func _validate_weather_persistence() -> void:
var weather := WorldWeatherServiceType.new()
root.add_child(weather)
@ -212,20 +261,118 @@ func _validate_weather_presentation() -> void:
weather.begin_remote_session()
var rain_target := Node3D.new()
world_root.add_child(rain_target)
var rain_camera := Camera3D.new()
world_root.add_child(rain_camera)
rain_target.global_position = Vector3(-18.0, 2.0, 14.0)
rain_camera.global_position = Vector3(24.0, 9.0, -18.0)
var visuals := WorldTimeVisualControllerType.new()
world_root.add_child(visuals)
visuals.setup(
clock, world_environment, sun, weather, rain_target
clock,
world_environment,
sun,
weather,
rain_target,
func() -> Camera3D: return rain_camera,
)
var runtime_environment: Environment = world_environment.environment
var runtime_sky_material := (
runtime_environment.sky.sky_material as ShaderMaterial
)
var clear_background_energy: float = (
runtime_environment.background_energy_multiplier
)
var clear_ambient_energy: float = runtime_environment.ambient_light_energy
var clear_fog_light_energy: float = runtime_environment.fog_light_energy
var clear_brightness: float = runtime_environment.adjustment_brightness
var clear_saturation: float = runtime_environment.adjustment_saturation
var clear_sun_energy: float = sun.light_energy
var clear_water_brightness: float = float(
WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter(
"environment_brightness"
)
)
assert(not runtime_environment.fog_enabled)
assert(is_zero_approx(float(
WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter(
"weather_fog_amount"
)
)))
assert(is_equal_approx(runtime_environment.fog_sky_affect, 0.35))
assert(is_zero_approx(float(
runtime_sky_material.get_shader_parameter("cloud_opacity")
)))
var storm_clouds := visuals.get_node("LocalStormClouds") as Node3D
assert(storm_clouds != null)
assert(storm_clouds.call("get_patch_count") == 81)
var cloud_field := storm_clouds.get_node("CloudField") as MultiMeshInstance3D
assert(cloud_field != null)
assert(cloud_field.multimesh != null)
assert(cloud_field.multimesh.instance_count == 81)
assert(cloud_field.multimesh.mesh is ArrayMesh)
var cloud_shader: Shader = preload(
"res://world/environment/local_storm_cloud.gdshader"
)
assert("cull_disabled" in cloud_shader.code)
assert(LocalStormCloudLayer.WRAP_RADIUS >= 315.0)
assert(is_equal_approx(LocalStormCloudLayer.MAXIMUM_OPACITY, 1.0))
assert(
LocalStormCloudLayer.DISTANCE_FADE_END
< LocalStormCloudLayer.WRAP_RADIUS
)
assert(is_zero_approx(float(storm_clouds.call("get_storm_amount"))))
visuals.apply_weather_immediately(
WorldWeatherServiceType.Weather.FOGGY
)
assert(runtime_environment.fog_enabled)
assert(float(
WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter(
"weather_fog_amount"
)
) > 0.99)
var water_fog_color := (
WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter(
"weather_fog_color"
) as Color
)
assert(water_fog_color.get_luminance() < 0.05)
assert(runtime_environment.fog_depth_begin <= 4.01)
assert(runtime_environment.fog_depth_end <= 42.01)
assert(is_zero_approx(runtime_environment.fog_sky_affect))
assert(runtime_environment.fog_aerial_perspective >= 0.91)
assert(is_equal_approx(runtime_environment.fog_depth_end, 18.0))
assert(is_equal_approx(runtime_environment.fog_sky_affect, 1.0))
assert(is_zero_approx(runtime_environment.fog_aerial_perspective))
assert(is_zero_approx(float(
runtime_sky_material.get_shader_parameter("cloud_opacity")
)))
assert(is_equal_approx(
runtime_environment.background_energy_multiplier,
clear_background_energy
* WorldTimeVisualControllerType.FOG_DAYLIGHT_SCENE_BRIGHTNESS,
))
assert(is_equal_approx(
runtime_environment.ambient_light_energy,
clear_ambient_energy
* WorldTimeVisualControllerType.FOG_DAYLIGHT_SCENE_BRIGHTNESS,
))
assert(is_equal_approx(
runtime_environment.fog_light_energy,
clear_fog_light_energy
* WorldTimeVisualControllerType.FOG_DAYLIGHT_FOG_LIGHT_BRIGHTNESS,
))
assert(is_equal_approx(
runtime_environment.adjustment_brightness,
clear_brightness
* WorldTimeVisualControllerType.FOG_DAYLIGHT_POST_BRIGHTNESS,
))
assert(is_equal_approx(sun.light_energy, clear_sun_energy * 0.30))
assert(is_equal_approx(
float(
WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter(
"environment_brightness"
)
),
clear_water_brightness
* WorldTimeVisualControllerType.FOG_DAYLIGHT_WATER_BRIGHTNESS,
))
assert(is_equal_approx(
runtime_environment.adjustment_saturation,
clear_saturation,
@ -233,8 +380,41 @@ func _validate_weather_presentation() -> void:
visuals.apply_weather_immediately(
WorldWeatherServiceType.Weather.RAINY
)
assert(runtime_environment.fog_enabled)
assert(float(
WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter(
"weather_fog_amount"
)
) > 0.99)
assert(is_equal_approx(runtime_environment.fog_sky_affect, 0.68))
assert(
float(runtime_sky_material.get_shader_parameter("cloud_coverage"))
> 0.98
)
assert(
float(runtime_sky_material.get_shader_parameter("cloud_opacity"))
> 0.99
)
assert(float(storm_clouds.call("get_storm_amount")) > 0.87)
assert(storm_clouds.visible)
assert(is_equal_approx(
runtime_environment.adjustment_brightness,
clear_brightness
* 0.92
* WorldTimeVisualControllerType.RAIN_DAYLIGHT_POST_BRIGHTNESS,
))
var rain := visuals.get_node("LocalRain") as GPUParticles3D
assert(rain != null)
assert(rain.global_position.is_equal_approx(
rain_camera.global_position
+ WorldTimeVisualControllerType.RAIN_EMITTER_OFFSET
))
rain_camera.global_position = Vector3(-31.0, 15.0, 42.0)
visuals.call("_update_rain_position")
assert(rain.global_position.is_equal_approx(
rain_camera.global_position
+ WorldTimeVisualControllerType.RAIN_EMITTER_OFFSET
))
assert(rain.emitting)
assert(rain.amount_ratio > 0.99)
assert(rain.amount == WorldTimeVisualControllerType.RAIN_PARTICLE_AMOUNT)
@ -253,8 +433,52 @@ func _validate_weather_presentation() -> void:
assert(rain_mesh.size.is_equal_approx(
WorldTimeVisualControllerType.RAIN_DROP_SIZE
))
visuals.call(
"_on_weather_changed",
WorldWeatherServiceType.Weather.SUNNY,
60.0,
)
visuals.call(
"_process",
WorldTimeVisualControllerType.WEATHER_TRANSITION_SECONDS * 0.5,
)
var halfway_water_fog: float = float(
WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter(
"weather_fog_amount"
)
)
assert(halfway_water_fog > 0.49 and halfway_water_fog < 0.51)
assert(is_equal_approx(
float(
WorldTimeVisualControllerType.FRESH_WATER_MATERIAL.get_shader_parameter(
"weather_fog_amount"
)
),
halfway_water_fog,
))
assert(runtime_environment.fog_enabled)
visuals.call(
"_process",
WorldTimeVisualControllerType.WEATHER_TRANSITION_SECONDS * 0.5,
)
assert(is_zero_approx(float(
WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter(
"weather_fog_amount"
)
)))
assert(not runtime_environment.fog_enabled)
visuals.apply_weather_immediately(
WorldWeatherServiceType.Weather.CLOUDY
)
assert(not runtime_environment.fog_enabled)
assert(is_zero_approx(float(
WorldTimeVisualControllerType.SALT_WATER_MATERIAL.get_shader_parameter(
"weather_fog_amount"
)
)))
visuals.apply_weather_immediately(
WorldWeatherServiceType.Weather.SUNNY
)
assert(not runtime_environment.fog_enabled)
assert(not rain.emitting)
world_root.queue_free()