2026-08-14 22:12:02 -04:00
|
|
|
extends SceneTree
|
|
|
|
|
|
|
|
|
|
const MainScene: PackedScene = preload("res://main/main.tscn")
|
|
|
|
|
const RuntimePerformanceProfileType = preload(
|
|
|
|
|
"res://main/runtime_performance_profile.gd"
|
|
|
|
|
)
|
2026-09-01 19:00:13 -04:00
|
|
|
const FOLIAGE_WIND_SHADER: Shader = preload(
|
|
|
|
|
"res://world/materials/foliage_wind.gdshader"
|
|
|
|
|
)
|
2026-08-14 22:12:02 -04:00
|
|
|
func _initialize() -> void:
|
|
|
|
|
call_deferred("_run")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _run() -> void:
|
|
|
|
|
_validate_profile_resolution()
|
|
|
|
|
OS.set_environment(
|
|
|
|
|
RuntimePerformanceProfileType.PROFILE_ENVIRONMENT_VARIABLE,
|
|
|
|
|
"light",
|
|
|
|
|
)
|
|
|
|
|
root.size = Vector2i(640, 480)
|
|
|
|
|
var main: Node = MainScene.instantiate()
|
|
|
|
|
root.add_child(main)
|
|
|
|
|
for _frame: int in 4:
|
|
|
|
|
await process_frame
|
|
|
|
|
if not bool(main.get("_application_initialized")):
|
|
|
|
|
main.call("_activate_selected_data_path", "", true)
|
|
|
|
|
for _frame: int in 8:
|
|
|
|
|
await process_frame
|
|
|
|
|
assert(bool(main.get("_application_initialized")))
|
feat: expand progression and multiplayer systems
Add named save slots, progression import/export, and a unified play flow. Add live friend requests, presence, invitations, and relationship controls without durable discovery-server social storage. Advance the network protocol with isolated channels, movement reconciliation, late-join recovery, fishing replication, and animation synchronization. Preserve per-species catch totals, refine generated-world startup and water recovery, and complete the related input and interface improvements.
2026-08-23 20:48:38 -04:00
|
|
|
assert(bool(main.call(
|
|
|
|
|
"_apply_world",
|
|
|
|
|
WorldLayout.GENERATED,
|
|
|
|
|
PlayerSaveManager.DEFAULT_WORLD_SEED,
|
|
|
|
|
true,
|
|
|
|
|
)))
|
2026-09-01 19:00:13 -04:00
|
|
|
# A regular editor/desktop runtime must ignore a light-profile environment
|
|
|
|
|
# variable. Only the tagged PortMaster export is allowed to honor it.
|
|
|
|
|
_validate_normal_profile(main)
|
|
|
|
|
_validate_rain_visual_motion(main)
|
|
|
|
|
_validate_new_game_music_transition(main)
|
2026-08-15 09:53:30 -04:00
|
|
|
_stop_audio_players(main)
|
2026-08-14 22:12:02 -04:00
|
|
|
main.queue_free()
|
2026-08-15 09:53:30 -04:00
|
|
|
for _frame: int in 8:
|
2026-08-14 22:12:02 -04:00
|
|
|
await process_frame
|
|
|
|
|
OS.unset_environment(
|
|
|
|
|
RuntimePerformanceProfileType.PROFILE_ENVIRONMENT_VARIABLE
|
|
|
|
|
)
|
2026-09-01 19:00:13 -04:00
|
|
|
print("Runtime performance profile validation: PASS")
|
2026-08-14 22:12:02 -04:00
|
|
|
quit()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _validate_profile_resolution() -> void:
|
|
|
|
|
var normal := RuntimePerformanceProfileType.from_name(&"normal")
|
|
|
|
|
var light := RuntimePerformanceProfileType.from_name(&"light")
|
|
|
|
|
var legacy := RuntimePerformanceProfileType.from_name(&"", true)
|
|
|
|
|
assert(not normal.is_light())
|
|
|
|
|
assert(is_equal_approx(normal.get_world_render_scale(), 1.0))
|
|
|
|
|
assert(light.is_light())
|
2026-08-18 01:02:13 -04:00
|
|
|
assert(is_equal_approx(light.get_world_render_scale(), 0.375))
|
2026-08-14 22:12:02 -04:00
|
|
|
assert(legacy.is_light())
|
2026-09-01 19:00:13 -04:00
|
|
|
OS.set_environment(
|
|
|
|
|
RuntimePerformanceProfileType.PROFILE_ENVIRONMENT_VARIABLE,
|
|
|
|
|
"light",
|
2026-08-18 01:02:13 -04:00
|
|
|
)
|
2026-09-01 19:00:13 -04:00
|
|
|
assert(not RuntimePerformanceProfileType.from_environment_for_portmaster_build(
|
|
|
|
|
false
|
|
|
|
|
).is_light())
|
|
|
|
|
assert(RuntimePerformanceProfileType.from_environment_for_portmaster_build(
|
|
|
|
|
true
|
|
|
|
|
).is_light())
|
|
|
|
|
OS.unset_environment(
|
|
|
|
|
RuntimePerformanceProfileType.PROFILE_ENVIRONMENT_VARIABLE
|
|
|
|
|
)
|
|
|
|
|
OS.set_environment(
|
|
|
|
|
RuntimePerformanceProfileType.LEGACY_LIGHT_ENVIRONMENT_VARIABLE,
|
|
|
|
|
"1",
|
|
|
|
|
)
|
|
|
|
|
assert(not RuntimePerformanceProfileType.from_environment_for_portmaster_build(
|
|
|
|
|
false
|
|
|
|
|
).is_light())
|
|
|
|
|
assert(RuntimePerformanceProfileType.from_environment_for_portmaster_build(
|
|
|
|
|
true
|
|
|
|
|
).is_light())
|
|
|
|
|
OS.unset_environment(
|
|
|
|
|
RuntimePerformanceProfileType.LEGACY_LIGHT_ENVIRONMENT_VARIABLE
|
2026-08-18 01:02:13 -04:00
|
|
|
)
|
2026-08-15 09:53:30 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
func _validate_normal_profile(main: Node) -> void:
|
|
|
|
|
var profile: RuntimePerformanceProfile = main.get("_performance_profile")
|
|
|
|
|
assert(profile != null and not profile.is_light())
|
|
|
|
|
assert(is_equal_approx(root.scaling_3d_scale, 1.0))
|
2026-08-19 00:07:37 -04:00
|
|
|
var pixelation: Node = main.get_node("%WorldPixelationPostprocess")
|
|
|
|
|
assert(not bool(pixelation.call("is_light_performance_profile")))
|
2026-08-19 17:32:40 -04:00
|
|
|
var pond := _first_fresh_water_visual(main)
|
2026-08-15 09:53:30 -04:00
|
|
|
var ocean := main.get_node(
|
2026-08-19 17:32:40 -04:00
|
|
|
"TestWorld/Regions/GeneratedWorldRegion/WaterBodies/OceanWater/VisualWater"
|
2026-08-15 09:53:30 -04:00
|
|
|
) as MeshInstance3D
|
2026-08-19 17:32:40 -04:00
|
|
|
assert(not pond.visible)
|
2026-08-15 09:53:30 -04:00
|
|
|
assert(ocean.material_override is ShaderMaterial)
|
|
|
|
|
_validate_ocean_fishing_coverage(main, ocean)
|
2026-08-19 17:32:40 -04:00
|
|
|
var region := main.get_node(
|
|
|
|
|
"TestWorld/Regions/GeneratedWorldRegion"
|
|
|
|
|
) as GeneratedWorldRegion
|
|
|
|
|
assert(region != null)
|
|
|
|
|
assert(region.get_node("Decorations").get_child_count() > 0)
|
2026-08-18 01:02:13 -04:00
|
|
|
var game_ui := main.get_node("%GameUI") as GameUI
|
2026-08-19 00:07:37 -04:00
|
|
|
var title_screen := game_ui.get_title_screen()
|
|
|
|
|
assert(title_screen != null)
|
2026-08-18 01:02:13 -04:00
|
|
|
var player_menu := game_ui.get("_player_menu") as PlayerMenu
|
|
|
|
|
assert(player_menu != null)
|
|
|
|
|
assert(player_menu.is_cooler_water_effect_enabled())
|
2026-08-15 09:53:30 -04:00
|
|
|
|
|
|
|
|
var title_background := main.get_node("%TitleBackground") as ColorRect
|
|
|
|
|
var title_material := title_background.material as ShaderMaterial
|
|
|
|
|
assert(title_material != null)
|
|
|
|
|
assert(bool(title_material.get_shader_parameter("animation_enabled")))
|
2026-09-01 17:30:50 -04:00
|
|
|
_validate_no_full_screen_menu_backdrops(main)
|
2026-08-15 09:53:30 -04:00
|
|
|
var visuals: WorldTimeVisualController = main.get_node(
|
|
|
|
|
"%WorldTimeVisualController"
|
|
|
|
|
)
|
|
|
|
|
var rain := visuals.get_node("LocalRain") as GPUParticles3D
|
|
|
|
|
assert(rain.amount == WorldTimeVisualController.RAIN_PARTICLE_AMOUNT)
|
|
|
|
|
assert(rain.fixed_fps == 30)
|
2026-08-19 00:07:37 -04:00
|
|
|
var clouds := visuals.get_node(
|
|
|
|
|
"LocalStormClouds"
|
|
|
|
|
) as LocalStormCloudLayer
|
|
|
|
|
assert(clouds.get_patch_count() == 81)
|
|
|
|
|
assert(clouds.get_node_or_null("CloudField") is MultiMeshInstance3D)
|
|
|
|
|
assert(clouds.get_node_or_null("CloudCeiling") == null)
|
2026-08-18 01:02:13 -04:00
|
|
|
assert((main.get_node("%TitleMusic") as AudioStreamPlayer).stream != null)
|
2026-08-20 17:53:22 -04:00
|
|
|
assert((main.get_node("%NewGameMusic") as AudioStreamPlayer).stream != null)
|
2026-08-18 01:02:13 -04:00
|
|
|
assert((main.get_node("%DuskMusic") as AudioStreamPlayer).stream != null)
|
|
|
|
|
assert(
|
|
|
|
|
(main.get_node("%WavesAudio") as AudioStreamPlayer).stream != null
|
|
|
|
|
)
|
|
|
|
|
assert(
|
|
|
|
|
(main.get_node("RainAmbience") as AudioStreamPlayer).stream != null
|
|
|
|
|
)
|
2026-08-15 09:53:30 -04:00
|
|
|
|
|
|
|
|
|
2026-09-01 19:00:13 -04:00
|
|
|
func _validate_rain_visual_motion(main: Node) -> void:
|
|
|
|
|
var region := main.get_node(
|
|
|
|
|
"TestWorld/Regions/GeneratedWorldRegion"
|
|
|
|
|
) as GeneratedWorldRegion
|
|
|
|
|
assert(region != null)
|
|
|
|
|
var ocean_motion := region.get_node(
|
|
|
|
|
"WaterBodies/OceanWater/VisualWater"
|
|
|
|
|
) as WaterSurfaceMotion
|
|
|
|
|
assert(ocean_motion != null)
|
|
|
|
|
var normal_water_amplitude: float = ocean_motion.get_effective_amplitude()
|
|
|
|
|
var decorations := region.get_node("Decorations") as Node3D
|
|
|
|
|
assert(decorations != null)
|
|
|
|
|
var normal_wind := _first_foliage_wind_material(decorations)
|
|
|
|
|
assert(normal_wind != null)
|
|
|
|
|
var normal_wind_strength: float = float(
|
|
|
|
|
normal_wind.get_shader_parameter("local_wind_strength")
|
|
|
|
|
)
|
|
|
|
|
var weather := main.get_node("%WorldWeatherService") as WorldWeatherService
|
|
|
|
|
assert(weather != null)
|
|
|
|
|
weather.apply_authoritative_snapshot(
|
|
|
|
|
WorldWeatherService.Weather.RAINY,
|
|
|
|
|
60.0,
|
|
|
|
|
)
|
|
|
|
|
assert(is_equal_approx(
|
|
|
|
|
ocean_motion.get_effective_amplitude(),
|
|
|
|
|
normal_water_amplitude * 2.0,
|
|
|
|
|
))
|
|
|
|
|
var rainy_wind := _first_foliage_wind_material(decorations)
|
|
|
|
|
assert(rainy_wind != null)
|
|
|
|
|
assert(is_equal_approx(
|
|
|
|
|
float(rainy_wind.get_shader_parameter("local_wind_strength")),
|
|
|
|
|
normal_wind_strength * 2.0,
|
|
|
|
|
))
|
|
|
|
|
weather.apply_authoritative_snapshot(
|
|
|
|
|
WorldWeatherService.Weather.SUNNY,
|
|
|
|
|
60.0,
|
|
|
|
|
)
|
|
|
|
|
assert(is_equal_approx(
|
|
|
|
|
ocean_motion.get_effective_amplitude(), normal_water_amplitude
|
|
|
|
|
))
|
|
|
|
|
var restored_wind := _first_foliage_wind_material(decorations)
|
|
|
|
|
assert(restored_wind != null)
|
|
|
|
|
assert(is_equal_approx(
|
|
|
|
|
float(restored_wind.get_shader_parameter("local_wind_strength")),
|
|
|
|
|
normal_wind_strength,
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
|
2026-08-20 17:53:22 -04:00
|
|
|
func _validate_new_game_music_transition(main: Node) -> void:
|
|
|
|
|
var title_music := main.get_node("%TitleMusic") as AudioStreamPlayer
|
|
|
|
|
var new_game_music := main.get_node("%NewGameMusic") as AudioStreamPlayer
|
|
|
|
|
assert(title_music.playing)
|
|
|
|
|
main.call("_start_new_game_music")
|
|
|
|
|
assert(not title_music.playing)
|
|
|
|
|
assert(new_game_music.playing)
|
|
|
|
|
main.call("_show_title_music", true)
|
|
|
|
|
assert(not new_game_music.playing)
|
|
|
|
|
assert(title_music.playing)
|
|
|
|
|
|
|
|
|
|
|
2026-09-01 17:30:50 -04:00
|
|
|
func _validate_no_full_screen_menu_backdrops(main: Node) -> void:
|
|
|
|
|
assert(main.get_node_or_null("%PlayerMenuBackdrop") == null)
|
|
|
|
|
assert(main.get_node_or_null("%ShopBackdrop") == null)
|
|
|
|
|
|
|
|
|
|
|
2026-08-19 17:32:40 -04:00
|
|
|
func _first_fresh_water_visual(main: Node) -> MeshInstance3D:
|
2026-09-01 07:44:01 -04:00
|
|
|
var fresh_water_root := main.get_node(
|
2026-08-19 17:32:40 -04:00
|
|
|
"TestWorld/Regions/GeneratedWorldRegion/WaterBodies/FreshWaterBodies"
|
|
|
|
|
) as Node3D
|
2026-09-01 07:44:01 -04:00
|
|
|
assert(fresh_water_root != null and fresh_water_root.get_child_count() > 0)
|
|
|
|
|
return fresh_water_root.get_child(0).get_node("VisualWater") as MeshInstance3D
|
2026-08-19 17:32:40 -04:00
|
|
|
|
|
|
|
|
|
2026-09-01 19:00:13 -04:00
|
|
|
func _first_foliage_wind_material(root_node: Node) -> ShaderMaterial:
|
|
|
|
|
for node: Node in root_node.find_children(
|
|
|
|
|
"*", "MeshInstance3D", true, false
|
|
|
|
|
):
|
|
|
|
|
var mesh_instance := node as MeshInstance3D
|
|
|
|
|
if mesh_instance == null or mesh_instance.mesh == null:
|
|
|
|
|
continue
|
|
|
|
|
for surface_index: int in mesh_instance.mesh.get_surface_count():
|
|
|
|
|
var material := mesh_instance.get_surface_override_material(
|
|
|
|
|
surface_index
|
|
|
|
|
) as ShaderMaterial
|
|
|
|
|
if material != null and material.shader == FOLIAGE_WIND_SHADER:
|
|
|
|
|
return material
|
|
|
|
|
return null
|
|
|
|
|
|
|
|
|
|
|
2026-08-15 09:53:30 -04:00
|
|
|
func _validate_ocean_fishing_coverage(
|
|
|
|
|
main: Node,
|
|
|
|
|
ocean: MeshInstance3D,
|
|
|
|
|
) -> void:
|
|
|
|
|
var ocean_mesh := ocean.mesh as PlaneMesh
|
|
|
|
|
assert(ocean_mesh != null)
|
|
|
|
|
var shape_node := main.get_node(
|
2026-08-19 17:32:40 -04:00
|
|
|
"TestWorld/Regions/GeneratedWorldRegion/WaterBodies/OceanWater/"
|
|
|
|
|
+ "FishingRegion/Shape"
|
2026-08-15 09:53:30 -04:00
|
|
|
) as CollisionShape3D
|
|
|
|
|
assert(shape_node != null)
|
|
|
|
|
var fishing_shape := shape_node.shape as BoxShape3D
|
|
|
|
|
assert(fishing_shape != null)
|
|
|
|
|
assert(fishing_shape.size.is_equal_approx(Vector3(
|
|
|
|
|
ocean_mesh.size.x,
|
2026-08-19 17:32:40 -04:00
|
|
|
4.0,
|
2026-08-15 09:53:30 -04:00
|
|
|
ocean_mesh.size.y,
|
|
|
|
|
)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _stop_audio_players(root_node: Node) -> void:
|
|
|
|
|
if root_node is AudioStreamPlayer:
|
|
|
|
|
(root_node as AudioStreamPlayer).stop()
|
|
|
|
|
elif root_node is AudioStreamPlayer2D:
|
|
|
|
|
(root_node as AudioStreamPlayer2D).stop()
|
|
|
|
|
elif root_node is AudioStreamPlayer3D:
|
|
|
|
|
(root_node as AudioStreamPlayer3D).stop()
|
|
|
|
|
for child: Node in root_node.get_children():
|
|
|
|
|
_stop_audio_players(child)
|