restore full world effects outside portmaster
This commit is contained in:
parent
4fda6e97f6
commit
41b4f86ddf
15 changed files with 398 additions and 122 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
25
main/main.gd
25
main/main.gd
|
|
@ -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
|
||||
):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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}"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
)))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue