restore full world effects outside portmaster

This commit is contained in:
Alexander Sellite 2026-09-01 19:00:13 -04:00
parent 4fda6e97f6
commit 41b4f86ddf
15 changed files with 398 additions and 122 deletions

View file

@ -76,9 +76,11 @@ builds/v<project-version>/straywild.zip
```
`--package-only` is reserved for repackaging an already validated
PortMaster-specific ARM64 export. It does not rebuild game content. The
standard Linux ARM64 release staging directory remains separate so building
PortMaster cannot replace the normal Linux ARM64 payload with reduced assets.
PortMaster-specific ARM64 export. It does not rebuild game content, and verifies
the tagged-export marker written by a prior full build so a generic Linux ARM64
binary cannot accidentally be repackaged as PortMaster. The standard Linux
ARM64 release staging directory remains separate so building PortMaster cannot
replace the normal Linux ARM64 payload with reduced assets.
For a packaging-only correction to an already published game release, commit
only PortMaster launcher, packaging-script, or PortMaster documentation changes
@ -217,7 +219,11 @@ The light profile passes these Godot options:
- `--audio-output-latency 40`
It also passes `straywild_PERFORMANCE_PROFILE=light` and
`straywild_LOW_END=1`. The game renders the 3D world at 37.5% linear
`straywild_LOW_END=1`. Those selectors are honored only by the dedicated
PortMaster export, which carries the immutable `straywild_portmaster` Godot
feature tag; desktop, Android, macOS, generic ARM64, and dedicated-server
builds always use the full presentation even if those environment variables
are present. The game renders the 3D world at 37.5% linear
resolution with nearest-neighbor scaling, reducing 3D pixel work by about 86%
while the separately rendered UI retains its canonical resolution. The light
profile also:
@ -263,10 +269,11 @@ On a typical muOS installation, the full path is:
/mnt/mmc/ports/straywild/conf/performance_profile
```
Set the file to `light`, or remove it, to restore the default. An externally
provided `straywild_PERFORMANCE_PROFILE=normal` or `light` environment value
takes precedence over the persistent file. Invalid values safely fall back to
the light profile and are reported in `straywild/log.txt`.
Set the file to `light`, or remove it, to restore the default. Within the
PortMaster package, an externally provided
`straywild_PERFORMANCE_PROFILE=normal` or `light` environment value takes
precedence over the persistent file. Invalid values safely fall back to the
light profile and are reported in `straywild/log.txt`.
Do not use Godot's low processor mode for the light profile. It reduces idle
CPU usage by sleeping between updates and is not a game-performance

View file

@ -239,3 +239,33 @@ ssh_remote_deploy/extra_args_ssh=""
ssh_remote_deploy/extra_args_scp=""
ssh_remote_deploy/run_script="#!/usr/bin/env bash\nexport DISPLAY=:0\nunzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"\nchmod +x \"{temp_dir}/{exe_name}\"\n\"{temp_dir}/{exe_name}\" {cmd_args}"
ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash\nkill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")\nrm -rf \"{temp_dir}\""
[preset.6]
name="PortMaster ARM64"
platform="Linux"
runnable=false
advanced_options=false
dedicated_server=false
custom_features="straywild_portmaster"
export_filter="all_resources"
include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt"
exclude_filter="builds/*,scripts/*"
export_path="builds/v0.20.3-alpha/portmaster-arm64/straywild.arm64"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
seed=0
encrypt_pck=false
encrypt_directory=false
script_export_mode=2
[preset.6.options]
custom_template/debug=""
custom_template/release=""
debug/export_console_wrapper=0
binary_format/embed_pck=false
binary_format/architecture="arm64"
texture_format/s3tc_bptc=false
texture_format/etc2_astc=true

View file

@ -146,6 +146,7 @@ const TITLE_MUSIC_PATH: String = (
const NEW_GAME_MUSIC_PATH: String = "res://audio/music/world/tulips.wav"
const DUSK_MUSIC_PATH: String = "res://audio/music/world/craft.mp3"
const TIME_CROSSING_EPSILON_HOURS: float = 0.000001
const RAIN_VISUAL_MOTION_MULTIPLIER: float = 2.0
const DEDICATED_IDLE_PHYSICS_TICKS_PER_SECOND: int = 10
const DEDICATED_ACTIVE_PHYSICS_TICKS_PER_SECOND: int = 30
const HOME_ENTRY_INPUT_OWNER: StringName = &"home_entry_transition"
@ -382,6 +383,25 @@ func _configure_audio_performance_profile(light_profile: bool) -> void:
_rain_ambience.set_audio_enabled(true)
func _on_world_weather_changed(
weather: WorldWeatherService.Weather,
_seconds_remaining: float,
) -> void:
_apply_weather_visual_motion(weather)
func _apply_weather_visual_motion(
weather: WorldWeatherService.Weather,
) -> void:
var multiplier: float = (
RAIN_VISUAL_MOTION_MULTIPLIER
if weather == WorldWeatherService.Weather.RAINY
else 1.0
)
_test_world.set_foliage_wind_strength_multiplier(multiplier)
_test_world.set_water_motion_strength_multiplier(multiplier)
func _load_optional_audio_stream(path: String) -> AudioStream:
if not ResourceLoader.exists(path, "AudioStream"):
return null
@ -654,6 +674,11 @@ func _initialize_application(dedicated: bool) -> void:
Callable(_player, "get_active_gameplay_camera"),
_performance_profile.is_light(),
)
if not _world_weather.weather_changed.is_connected(
_on_world_weather_changed
):
_world_weather.weather_changed.connect(_on_world_weather_changed)
_apply_weather_visual_motion(_world_weather.get_weather())
if not _world_time.natural_time_advanced.is_connected(
_on_natural_time_advanced
):

View file

@ -9,6 +9,7 @@ const NETFISHING_PROFILE_ENVIRONMENT_VARIABLE := (
"NETFISHING_PERFORMANCE_PROFILE"
)
const NETFISHING_LIGHT_ENVIRONMENT_VARIABLE := "NETFISHING_LOW_END"
const PORTMASTER_BUILD_FEATURE: StringName = &"straywild_portmaster"
const NORMAL_PROFILE: StringName = &"normal"
const LIGHT_PROFILE: StringName = &"light"
const NORMAL_WORLD_RENDER_SCALE: float = 1.0
@ -18,6 +19,16 @@ var _profile_name: StringName = NORMAL_PROFILE
static func from_environment() -> RuntimePerformanceProfile:
return from_environment_for_portmaster_build(
OS.has_feature(PORTMASTER_BUILD_FEATURE)
)
static func from_environment_for_portmaster_build(
is_portmaster_build: bool,
) -> RuntimePerformanceProfile:
if not is_portmaster_build:
return from_name(NORMAL_PROFILE)
var profile_name: String = OS.get_environment(
PROFILE_ENVIRONMENT_VARIABLE
)

View file

@ -44,6 +44,8 @@ readonly RELEASE_ROOT="${PROJECT_ROOT}/builds/${RELEASE_TAG}"
readonly ARM64_ROOT="${RELEASE_ROOT}/portmaster-arm64"
readonly ARM64_EXECUTABLE="${ARM64_ROOT}/straywild.arm64"
readonly ARM64_PCK="${ARM64_ROOT}/straywild.pck"
readonly PORTMASTER_FEATURE_TAG="straywild_portmaster"
readonly PORTMASTER_FEATURE_MARKER="${ARM64_ROOT}/.portmaster-feature-tag"
readonly STAGE_ROOT="${RELEASE_ROOT}/portmaster-stage"
readonly GAME_ROOT="${STAGE_ROOT}/straywild"
readonly ARCHIVE="${RELEASE_ROOT}/straywild.zip"
@ -103,7 +105,10 @@ if [[ ${PACKAGE_ONLY} -eq 0 ]]; then
rm -rf -- "${EXPORT_PROJECT_ROOT}"
}
trap cleanup_export_project EXIT INT TERM
rm -f -- "${ARM64_EXECUTABLE}" "${ARM64_PCK}"
rm -f -- \
"${ARM64_EXECUTABLE}" \
"${ARM64_PCK}" \
"${PORTMASTER_FEATURE_MARKER}"
rsync -a \
--exclude '/.git/' \
--exclude '/.godot/' \
@ -124,14 +129,24 @@ if [[ ${PACKAGE_ONLY} -eq 0 ]]; then
"${GODOT_BIN}" \
--headless \
--path "${EXPORT_PROJECT_ROOT}" \
--export-release "Linux ARM64" \
--export-release "PortMaster ARM64" \
"${ARM64_EXECUTABLE}"
printf '%s\n%s\n' \
"${PORTMASTER_FEATURE_TAG}" \
"${TAG_COMMIT}" >"${PORTMASTER_FEATURE_MARKER}"
cleanup_export_project
trap - EXIT INT TERM
fi
test -s "${ARM64_EXECUTABLE}"
test -s "${ARM64_PCK}"
if [[ ! -f "${PORTMASTER_FEATURE_MARKER}" ]] \
|| ! grep -Fxq "${PORTMASTER_FEATURE_TAG}" "${PORTMASTER_FEATURE_MARKER}" \
|| ! grep -Fxq "${TAG_COMMIT}" "${PORTMASTER_FEATURE_MARKER}"; then
echo "PortMaster export is missing its tagged-preset marker." >&2
echo "Run a full PortMaster build before using --package-only." >&2
exit 1
fi
file "${ARM64_EXECUTABLE}" | grep -q "ARM aarch64"
rm -rf -- "${STAGE_ROOT}"

View file

@ -282,6 +282,16 @@ func _validate_low_end_profile_contract() -> void:
)
assert(builder.contains("prepare_portmaster_assets.sh"))
assert(builder.contains("straywild-portmaster-export"))
assert(builder.contains('--export-release "PortMaster ARM64"'))
assert(builder.contains("PORTMASTER_FEATURE_MARKER"))
assert(builder.contains("Run a full PortMaster build before using --package-only."))
var export_presets: String = FileAccess.get_file_as_string(
"res://export_presets.cfg"
)
assert(export_presets.contains('name="PortMaster ARM64"'))
assert(export_presets.contains(
'custom_features="straywild_portmaster"'
))
var asset_profile: String = FileAccess.get_file_as_string(
"res://scripts/prepare_portmaster_assets.sh"
)

View file

@ -474,6 +474,19 @@ func _validate_generated_region(
assert(ocean != null and fresh_root != null)
assert(ocean.water_type == WaterType.Type.SALT_WATER)
assert(is_equal_approx(ocean.position.y, GeneratedWorldRegion.WATER_HEIGHT))
var ocean_motion := ocean.get_node("VisualWater") as WaterSurfaceMotion
assert(ocean_motion != null and ocean_motion.is_processing())
var normal_water_amplitude: float = ocean_motion.get_effective_amplitude()
assert(normal_water_amplitude > 0.0)
region.set_water_motion_strength_multiplier(2.0)
assert(is_equal_approx(
ocean_motion.get_effective_amplitude(),
normal_water_amplitude * 2.0,
))
region.set_water_motion_strength_multiplier(1.0)
assert(is_equal_approx(
ocean_motion.get_effective_amplitude(), normal_water_amplitude
))
var ocean_recovery := ocean.get_node("RecoveryRegion") as PlayerWaterTrigger
assert(
ocean_recovery.entry_height_reference
@ -635,6 +648,20 @@ func _validate_generated_region(
"GatherableAnchors/ReachableTreeTrunks"
) as GatherableAnchorSet3D
assert(decorations != null and decorations.get_child_count() > 0)
var normal_foliage_wind := _first_foliage_wind_material(decorations)
assert(normal_foliage_wind != null)
var normal_foliage_strength: float = float(
normal_foliage_wind.get_shader_parameter("local_wind_strength")
)
assert(normal_foliage_strength > 0.0)
region.set_foliage_wind_strength_multiplier(2.0)
var rainy_foliage_wind := _first_foliage_wind_material(decorations)
assert(rainy_foliage_wind != null)
assert(is_equal_approx(
float(rainy_foliage_wind.get_shader_parameter("local_wind_strength")),
normal_foliage_strength * 2.0,
))
region.set_foliage_wind_strength_multiplier(1.0)
assert(anchors != null)
assert(anchors.get_spawn_positions().size() > 0)
_validate_tree_gatherable_anchors(region, decorations, anchors)
@ -1567,6 +1594,22 @@ func _find_mesh_instance(root_node: Node) -> MeshInstance3D:
return null
func _first_foliage_wind_material(root_node: Node) -> ShaderMaterial:
var mesh_instance := root_node as MeshInstance3D
if mesh_instance != null and mesh_instance.mesh != null:
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
for child: Node in root_node.get_children():
var found := _first_foliage_wind_material(child)
if found != null:
return found
return null
func _has_precipitation_occlusion_layer(root_node: Node) -> bool:
var mesh_instance := root_node as MeshInstance3D
if (

View file

@ -4,6 +4,9 @@ const MainScene: PackedScene = preload("res://main/main.tscn")
const RuntimePerformanceProfileType = preload(
"res://main/runtime_performance_profile.gd"
)
const FOLIAGE_WIND_SHADER: Shader = preload(
"res://world/materials/foliage_wind.gdshader"
)
func _initialize() -> void:
call_deferred("_run")
@ -30,8 +33,11 @@ func _run() -> void:
PlayerSaveManager.DEFAULT_WORLD_SEED,
true,
)))
_validate_main_profile(main)
_validate_minimal_weather(main)
# 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)
_stop_audio_players(main)
main.queue_free()
for _frame: int in 8:
@ -39,28 +45,7 @@ func _run() -> void:
OS.unset_environment(
RuntimePerformanceProfileType.PROFILE_ENVIRONMENT_VARIABLE
)
var normal_main: Node = MainScene.instantiate()
root.add_child(normal_main)
for _frame: int in 4:
await process_frame
if not bool(normal_main.get("_application_initialized")):
normal_main.call("_activate_selected_data_path", "", true)
for _frame: int in 8:
await process_frame
assert(bool(normal_main.get("_application_initialized")))
assert(bool(normal_main.call(
"_apply_world",
WorldLayout.GENERATED,
PlayerSaveManager.DEFAULT_WORLD_SEED,
true,
)))
_validate_normal_profile(normal_main)
_validate_new_game_music_transition(normal_main)
_stop_audio_players(normal_main)
normal_main.queue_free()
for _frame: int in 8:
await process_frame
print("Light performance profile validation: PASS")
print("Runtime performance profile validation: PASS")
quit()
@ -73,53 +58,31 @@ func _validate_profile_resolution() -> void:
assert(light.is_light())
assert(is_equal_approx(light.get_world_render_scale(), 0.375))
assert(legacy.is_light())
func _validate_main_profile(main: Node) -> void:
var profile: RuntimePerformanceProfile = main.get("_performance_profile")
assert(profile != null and profile.is_light())
assert(is_equal_approx(root.scaling_3d_scale, 0.375))
var pixelation: Node = main.get_node("%WorldPixelationPostprocess")
assert(bool(pixelation.call("is_light_performance_profile")))
pixelation.call("set_gameplay_active", true)
var screen_grid := pixelation.get_node("ScreenGrid") as ColorRect
assert(screen_grid != null and not screen_grid.visible)
var pond := _first_fresh_water_visual(main)
var ocean := main.get_node(
"TestWorld/Regions/GeneratedWorldRegion/WaterBodies/OceanWater/VisualWater"
) as MeshInstance3D
assert(not pond.visible)
assert(ocean.material_override is StandardMaterial3D)
assert(not ocean.material_override is ShaderMaterial)
_validate_ocean_fishing_coverage(main, ocean)
var region := main.get_node(
"TestWorld/Regions/GeneratedWorldRegion"
) as GeneratedWorldRegion
assert(region != null)
assert(region.get_node("Decorations").get_child_count() > 0)
var title_background := main.get_node("%TitleBackground") as ColorRect
var title_material := title_background.material as ShaderMaterial
assert(title_material != null)
assert(not bool(title_material.get_shader_parameter(
"animation_enabled"
)))
_validate_no_full_screen_menu_backdrops(main)
var game_ui := main.get_node("%GameUI") as GameUI
var title_screen := game_ui.get_title_screen()
assert(title_screen != null)
var player_menu := game_ui.get("_player_menu") as PlayerMenu
assert(player_menu != null)
assert(not player_menu.is_cooler_water_effect_enabled())
assert((main.get_node("%TitleMusic") as AudioStreamPlayer).stream != null)
assert((main.get_node("%NewGameMusic") as AudioStreamPlayer).stream != null)
assert((main.get_node("%DuskMusic") as AudioStreamPlayer).stream != null)
assert(
(main.get_node("%WavesAudio") as AudioStreamPlayer).stream == null
OS.set_environment(
RuntimePerformanceProfileType.PROFILE_ENVIRONMENT_VARIABLE,
"light",
)
assert(
(main.get_node("RainAmbience") as AudioStreamPlayer).stream == null
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
)
@ -176,6 +139,54 @@ func _validate_normal_profile(main: Node) -> void:
)
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,
))
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
@ -201,6 +212,22 @@ func _first_fresh_water_visual(main: Node) -> MeshInstance3D:
return fresh_water_root.get_child(0).get_node("VisualWater") as MeshInstance3D
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
func _validate_ocean_fishing_coverage(
main: Node,
ocean: MeshInstance3D,
@ -230,41 +257,3 @@ func _stop_audio_players(root_node: Node) -> void:
(root_node as AudioStreamPlayer3D).stop()
for child: Node in root_node.get_children():
_stop_audio_players(child)
func _validate_minimal_weather(main: Node) -> void:
var visuals: WorldTimeVisualController = main.get_node(
"%WorldTimeVisualController"
)
var rain := visuals.get_node("LocalRain") as GPUParticles3D
assert(rain.amount == WorldTimeVisualController.LIGHT_RAIN_PARTICLE_AMOUNT)
assert(rain.fixed_fps == WorldTimeVisualController.LIGHT_RAIN_FIXED_FPS)
assert(rain.amount == 48)
assert(rain.fixed_fps == 8)
var clouds := visuals.get_node("LocalStormClouds") as LocalStormCloudLayer
assert(clouds.get_patch_count() == 1)
var ceiling := clouds.get_node("CloudCeiling") as MeshInstance3D
assert(ceiling != null and ceiling.mesh is PlaneMesh)
assert(ceiling.material_override == null)
assert((ceiling.mesh as PlaneMesh).material is StandardMaterial3D)
visuals.apply_weather_immediately(WorldWeatherService.Weather.RAINY)
assert(rain.emitting and rain.amount_ratio > 0.99)
assert(clouds.visible and clouds.get_storm_amount() > 0.87)
var cloud_material := (
(ceiling.mesh as PlaneMesh).material as StandardMaterial3D
)
assert(cloud_material.albedo_color.a < 0.65)
var test_world := main.get_node("TestWorld") as TestWorld
var runtime_environment := (
test_world.get_world_environment().environment as Environment
)
var sky_material := (
runtime_environment.sky.sky_material as ShaderMaterial
)
assert(is_zero_approx(float(
sky_material.get_shader_parameter("cloud_coverage")
)))
assert(is_zero_approx(float(
sky_material.get_shader_parameter("cloud_opacity")
)))

View file

@ -84,6 +84,8 @@ func _validate_world_switching() -> void:
root.add_child(world)
await process_frame
assert(world.get_world_layout() == WorldLayout.GENERATED)
world.set_foliage_wind_strength_multiplier(2.0)
world.set_water_motion_strength_multiplier(2.0)
var initial_generator := world.get_node(
"Regions/GeneratedWorldRegion/Terrain/TerrainChunkGenerator"
) as TerrainChunkGenerator
@ -97,6 +99,20 @@ func _validate_world_switching() -> void:
assert(world.get_generation_seed() == 13579)
assert(world.get_node_or_null("Regions/StarterIslandRegion") != null)
assert(world.get_node_or_null("Regions/GeneratedWorldRegion") == null)
var starter_region := world.get_node(
"Regions/StarterIslandRegion"
) as StarterIslandRegion
assert(starter_region != null)
assert(is_equal_approx(
float(starter_region.get("_foliage_wind_strength_multiplier")), 2.0
))
var starter_ocean_motion := starter_region.get_node(
"WaterBodies/Ocean/VisualWater"
) as WaterSurfaceMotion
assert(starter_ocean_motion != null)
assert(is_equal_approx(
starter_ocean_motion.get_motion_strength_multiplier(), 2.0
))
assert(world.get_fishing_shop() != null)
assert(world.get_player_storage() != null)
assert(not world.get_fishable_water_regions().is_empty())
@ -114,6 +130,20 @@ func _validate_world_switching() -> void:
assert(world.get_generation_seed() == 24680)
assert(world.get_node_or_null("Regions/GeneratedWorldRegion") != null)
assert(world.get_node_or_null("Regions/StarterIslandRegion") == null)
var generated_region := world.get_node(
"Regions/GeneratedWorldRegion"
) as GeneratedWorldRegion
assert(generated_region != null)
assert(is_equal_approx(
float(generated_region.get("_foliage_wind_strength_multiplier")), 2.0
))
var generated_ocean_motion := generated_region.get_node(
"WaterBodies/OceanWater/VisualWater"
) as WaterSurfaceMotion
assert(generated_ocean_motion != null)
assert(is_equal_approx(
generated_ocean_motion.get_motion_strength_multiplier(), 2.0
))
world.queue_free()
await process_frame

View file

@ -138,6 +138,7 @@ var _biome_assignments: Dictionary[Vector2i, StringName] = {}
var _water_recovery_triangles_by_coordinate: Dictionary[Vector2i, Array] = {}
var _home_accessible_elevations_by_coordinate: Dictionary[Vector2i, Array] = {}
var _foliage_wind_enabled: bool = false
var _foliage_wind_strength_multiplier: float = 1.0
func _ready() -> void:
@ -218,6 +219,24 @@ func set_light_performance_profile(enabled: bool) -> void:
_light_performance_profile = enabled
_set_foliage_wind_enabled(not enabled)
_apply_water_materials()
var surface_motion := _get_ocean_surface_motion()
if surface_motion != null:
surface_motion.set_motion_enabled(not enabled)
func set_foliage_wind_strength_multiplier(multiplier: float) -> void:
_foliage_wind_strength_multiplier = maxf(multiplier, 0.0)
_set_foliage_wind_enabled(_foliage_wind_enabled)
func set_water_motion_strength_multiplier(multiplier: float) -> void:
var surface_motion := _get_ocean_surface_motion()
if surface_motion != null:
surface_motion.set_motion_strength_multiplier(multiplier)
func _get_ocean_surface_motion() -> WaterSurfaceMotion:
return _ocean.get_node_or_null(^"VisualWater") as WaterSurfaceMotion
func get_spawn_surface_triangles(
@ -611,13 +630,29 @@ func _apply_foliage_wind_to_mesh(mesh_instance: MeshInstance3D) -> void:
(mesh_global_scale.x + mesh_global_scale.z) * 0.5,
0.001,
)
var local_strength: float = foliage_wind_strength / horizontal_scale
var local_strength: float = (
foliage_wind_strength * _foliage_wind_strength_multiplier
/ horizontal_scale
)
var phase: float = fposmod(
mesh_instance.global_position.x * 0.73
+ mesh_instance.global_position.z * 0.41,
TAU,
)
for surface_index: int in mesh_instance.mesh.get_surface_count():
var override_material: Material = (
mesh_instance.get_surface_override_material(surface_index)
)
var existing_wind := override_material as ShaderMaterial
if (
existing_wind != null
and existing_wind.shader == FOLIAGE_WIND_SHADER
):
existing_wind.set_shader_parameter(
"local_wind_strength",
local_strength,
)
continue
var source_material: Material = mesh_instance.get_active_material(
surface_index
)

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=16 format=3]
[gd_scene load_steps=17 format=3]
[ext_resource type="Script" path="res://world/generation/generated_world_region.gd" id="1_region"]
[ext_resource type="Script" path="res://world/generation/terrain_chunk_generator.gd" id="2_generator"]
@ -15,6 +15,7 @@
[ext_resource type="Resource" path="res://world/generation/biomes/terrain_biome_catalog.tres" id="13_biome_catalog"]
[ext_resource type="PackedScene" path="res://world/interactables/decor_shop_world.tscn" id="14_decor_shop"]
[ext_resource type="PackedScene" path="res://world/interactables/rv_upgrade_shop_world.tscn" id="15_rv_upgrade"]
[ext_resource type="Script" path="res://world/water_surface_motion.gd" id="16_water_motion"]
[node name="GeneratedWorldRegion" type="Node3D"]
script = ExtResource("1_region")
@ -60,6 +61,9 @@ fish_pool = ExtResource("10_ocean_pool")
water_type = 1
location_tags = Array[StringName]([&"coast", &"ocean", &"generated_ocean"])
[node name="VisualWater" parent="WaterBodies/OceanWater" index="0"]
script = ExtResource("16_water_motion")
[node name="FreshWaterBodies" type="Node3D" parent="WaterBodies"]
unique_name_in_owner = true

View file

@ -72,6 +72,7 @@ var rv_upgrade_shop_path: NodePath = (
@export_range(0.0, 4.0, 0.05) var foliage_wind_speed: float = 0.9
var _foliage_wind_enabled: bool = false
var _foliage_wind_strength_multiplier: float = 1.0
func _ready() -> void:
@ -141,6 +142,19 @@ func set_light_performance_profile(enabled: bool) -> void:
surface_motion.set_motion_enabled(not enabled)
func set_foliage_wind_strength_multiplier(multiplier: float) -> void:
_foliage_wind_strength_multiplier = maxf(multiplier, 0.0)
_set_foliage_wind_enabled(_foliage_wind_enabled)
func set_water_motion_strength_multiplier(multiplier: float) -> void:
var ocean := get_node_or_null(
^"WaterBodies/Ocean/VisualWater"
) as WaterSurfaceMotion
if ocean != null:
ocean.set_motion_strength_multiplier(multiplier)
func is_foliage_wind_enabled() -> bool:
return _foliage_wind_enabled
@ -342,13 +356,29 @@ func _apply_foliage_wind_to_mesh(mesh_instance: MeshInstance3D) -> void:
(mesh_global_scale.x + mesh_global_scale.z) * 0.5,
0.001,
)
var local_strength: float = foliage_wind_strength / horizontal_scale
var local_strength: float = (
foliage_wind_strength * _foliage_wind_strength_multiplier
/ horizontal_scale
)
var phase: float = fposmod(
mesh_instance.global_position.x * 0.73
+ mesh_instance.global_position.z * 0.41,
TAU,
)
for surface_index: int in mesh_instance.mesh.get_surface_count():
var override_material: Material = (
mesh_instance.get_surface_override_material(surface_index)
)
var existing_wind := override_material as ShaderMaterial
if (
existing_wind != null
and existing_wind.shader == FOLIAGE_WIND_SHADER
):
existing_wind.set_shader_parameter(
"local_wind_strength",
local_strength,
)
continue
var source_material: Material = mesh_instance.get_active_material(
surface_index
)

View file

@ -38,6 +38,14 @@ func set_light_performance_profile(_enabled: bool) -> void:
pass
func set_foliage_wind_strength_multiplier(_multiplier: float) -> void:
pass
func set_water_motion_strength_multiplier(_multiplier: float) -> void:
pass
func get_playable_half_extents() -> Vector2:
return Vector2(50.0, 50.0)

View file

@ -36,6 +36,8 @@ const WORLD_BOUNDARY_SHORELINE_CLEARANCE := 18.0
var _world_layout: StringName = WorldLayoutType.GENERATED
var _world_seed: int = PlayerSaveManager.DEFAULT_WORLD_SEED
var _light_performance_profile: bool = false
var _foliage_wind_strength_multiplier: float = 1.0
var _water_motion_strength_multiplier: float = 1.0
var _dedicated_simulation: bool = false
var _local_home_interior_presentation_active: bool = false
@ -100,6 +102,22 @@ func set_light_performance_profile(enabled: bool) -> void:
_active_region.set_light_performance_profile(enabled)
func set_foliage_wind_strength_multiplier(multiplier: float) -> void:
_foliage_wind_strength_multiplier = maxf(multiplier, 0.0)
if _active_region != null:
_active_region.set_foliage_wind_strength_multiplier(
_foliage_wind_strength_multiplier
)
func set_water_motion_strength_multiplier(multiplier: float) -> void:
_water_motion_strength_multiplier = maxf(multiplier, 0.0)
if _active_region != null:
_active_region.set_water_motion_strength_multiplier(
_water_motion_strength_multiplier
)
func set_local_home_interior_presentation_active(enabled: bool) -> void:
_local_home_interior_presentation_active = enabled
if _active_region != null and not _dedicated_simulation:
@ -310,6 +328,12 @@ func _replace_active_region(layout: StringName, world_seed: int) -> bool:
_active_region = replacement
_regions_root.add_child(_active_region)
_active_region.set_light_performance_profile(_light_performance_profile)
_active_region.set_foliage_wind_strength_multiplier(
_foliage_wind_strength_multiplier
)
_active_region.set_water_motion_strength_multiplier(
_water_motion_strength_multiplier
)
if _dedicated_simulation:
_set_region_presentation_enabled(_active_region, false)
else:

View file

@ -18,6 +18,7 @@ const DEFAULT_PHASE_OFFSET: float = 0.0
@export_range(0.0, TAU, 0.01, "radians") var phase_offset: float = 0.0
var _base_height: float
var _motion_strength_multiplier: float = 1.0
func _ready() -> void:
@ -28,7 +29,7 @@ func _ready() -> void:
func _process(_delta: float) -> void:
position.y = _base_height + calculate_height_offset(
_current_time_seconds(),
amplitude,
get_effective_amplitude(),
cycle_seconds,
secondary_swell,
phase_offset,
@ -41,6 +42,20 @@ func set_motion_enabled(enabled: bool) -> void:
position.y = _base_height
func set_motion_strength_multiplier(multiplier: float) -> void:
_motion_strength_multiplier = maxf(multiplier, 0.0)
if _motion_strength_multiplier <= 0.0:
position.y = _base_height
func get_motion_strength_multiplier() -> float:
return _motion_strength_multiplier
func get_effective_amplitude() -> float:
return amplitude * _motion_strength_multiplier
static func get_default_height_offset() -> float:
return calculate_height_offset(
_current_time_seconds(),