perf: harden runtime for weaker hardware
This commit is contained in:
@@ -32,11 +32,7 @@ func _ready() -> void:
|
||||
_all.append(self)
|
||||
add_to_group("activity_sites")
|
||||
_update_presentation()
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if debug_label_enabled:
|
||||
_update_presentation()
|
||||
set_process(false)
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
|
||||
@@ -5,6 +5,7 @@ var combatant: CombatantStateRecord
|
||||
var simulation_manager: Node
|
||||
var _flash_tween: Tween
|
||||
var _spawn_tween: Tween
|
||||
var _last_health := 0.0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -24,22 +25,21 @@ func bind(combatant_id_value: StringName, manager: Node) -> void:
|
||||
global_position = combatant.get_position()
|
||||
sim_position = combatant.get_position()
|
||||
_build_from_definition()
|
||||
_last_health = combatant.get_health()
|
||||
combatant.changed.connect(_on_combatant_changed)
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
func _on_combatant_changed(_state: CombatantStateRecord) -> void:
|
||||
if is_dead_visual or combatant == null or not is_instance_valid(combatant):
|
||||
return
|
||||
if not combatant.is_alive():
|
||||
play_death()
|
||||
return
|
||||
set_sim_position(combatant.get_position())
|
||||
|
||||
|
||||
func _on_combatant_changed(_state: CombatantStateRecord) -> void:
|
||||
if combatant == null or combatant.get_health() >= combatant.get_max_health():
|
||||
return
|
||||
_flash_hit()
|
||||
var current_health := combatant.get_health()
|
||||
if current_health < _last_health:
|
||||
_flash_hit()
|
||||
_last_health = current_health
|
||||
|
||||
|
||||
func _flash_hit() -> void:
|
||||
@@ -51,6 +51,8 @@ func _flash_hit() -> void:
|
||||
|
||||
|
||||
func play_death() -> void:
|
||||
if is_dead_visual:
|
||||
return
|
||||
is_dead_visual = true
|
||||
super.play_death()
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ const HOSTILE_SCENE := preload("res://world/combat/HostileCombatant.tscn")
|
||||
@export var wolf_spawn_positions: PackedVector3Array = PackedVector3Array()
|
||||
|
||||
var hostiles: Dictionary = {}
|
||||
var _initialized := false
|
||||
var _wolves_spawned := false
|
||||
|
||||
|
||||
@@ -20,7 +19,6 @@ func _ready() -> void:
|
||||
simulation_manager.combatant_spawned.connect(_on_combatant_spawned)
|
||||
simulation_manager.combatant_died.connect(_on_combatant_died)
|
||||
simulation_manager.state_restored.connect(_on_state_restored)
|
||||
_initialized = true
|
||||
call_deferred("_spawn_wolves")
|
||||
_refresh_from_state()
|
||||
|
||||
@@ -34,14 +32,6 @@ func _spawn_wolves() -> void:
|
||||
simulation_manager.spawn_wolf(position)
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if not _initialized:
|
||||
return
|
||||
for combatant_id in _hostile_ids():
|
||||
if not hostiles.has(combatant_id):
|
||||
_spawn_visual(combatant_id)
|
||||
|
||||
|
||||
func _hostile_ids() -> Array:
|
||||
var ids: Array[StringName] = []
|
||||
var living: Array = simulation_manager.get_living_hostile_combatants()
|
||||
|
||||
@@ -6,6 +6,8 @@ signal arrived_at_sim_position(creature_id: StringName)
|
||||
|
||||
const ARRIVAL_DISTANCE := 0.5
|
||||
const PATH_RETARGET_DISTANCE := 0.8
|
||||
const PATH_RETRY_BASE_SECONDS := 0.5
|
||||
const PATH_RETRY_MAX_SECONDS := 4.0
|
||||
|
||||
@export_range(0.5, 12.0, 0.1) var move_speed := 3.5
|
||||
@export_range(0.05, 1.0, 0.05) var waypoint_reached_distance := 0.3
|
||||
@@ -20,6 +22,9 @@ var path_index := 0
|
||||
var path_target := Vector3.INF
|
||||
var path_request_id := 0
|
||||
var path_pending := false
|
||||
var path_query_count := 0
|
||||
var path_retry_remaining := 0.0
|
||||
var path_retry_delay := PATH_RETRY_BASE_SECONDS
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
@@ -30,6 +35,10 @@ func _physics_process(delta: float) -> void:
|
||||
if _horizontal_distance_to(sim_position) <= ARRIVAL_DISTANCE:
|
||||
_stop_moving()
|
||||
return
|
||||
if path_retry_remaining > 0.0:
|
||||
path_retry_remaining = maxf(path_retry_remaining - delta, 0.0)
|
||||
if path_retry_remaining > 0.0:
|
||||
return
|
||||
_ensure_path()
|
||||
if path_pending:
|
||||
return
|
||||
@@ -51,12 +60,17 @@ func _physics_process(delta: float) -> void:
|
||||
func set_sim_position(position: Vector3) -> void:
|
||||
if not position.is_finite():
|
||||
return
|
||||
var moved := (
|
||||
not path_target.is_finite()
|
||||
var target_changed := (
|
||||
not sim_position.is_finite()
|
||||
or _horizontal_distance_to_position(sim_position, position) > PATH_RETARGET_DISTANCE
|
||||
)
|
||||
sim_position = position
|
||||
if moved and not is_dead_visual:
|
||||
if target_changed and not is_dead_visual:
|
||||
path_retry_remaining = 0.0
|
||||
path_retry_delay = PATH_RETRY_BASE_SECONDS
|
||||
if _horizontal_distance_to(sim_position) <= ARRIVAL_DISTANCE:
|
||||
_stop_moving()
|
||||
return
|
||||
_request_path()
|
||||
|
||||
|
||||
@@ -104,17 +118,41 @@ func _request_path() -> void:
|
||||
current_path = NavigationServer3D.map_get_path(
|
||||
get_world_3d().navigation_map, global_position, sim_position, true
|
||||
)
|
||||
path_query_count += 1
|
||||
path_pending = false
|
||||
if current_path.is_empty():
|
||||
_stop_moving()
|
||||
_schedule_path_retry()
|
||||
return
|
||||
path_retry_remaining = 0.0
|
||||
path_retry_delay = PATH_RETRY_BASE_SECONDS
|
||||
|
||||
|
||||
func _schedule_path_retry() -> void:
|
||||
current_path = PackedVector3Array()
|
||||
path_index = 0
|
||||
path_pending = false
|
||||
path_target = sim_position
|
||||
path_retry_remaining = path_retry_delay
|
||||
path_retry_delay = minf(path_retry_delay * 2.0, PATH_RETRY_MAX_SECONDS)
|
||||
|
||||
|
||||
func _stop_moving() -> void:
|
||||
if (
|
||||
current_path.is_empty()
|
||||
and not path_pending
|
||||
and path_retry_remaining <= 0.0
|
||||
and path_target.is_finite()
|
||||
and sim_position.is_finite()
|
||||
and _horizontal_distance_to_position(path_target, sim_position) <= PATH_RETARGET_DISTANCE
|
||||
):
|
||||
return
|
||||
path_request_id += 1
|
||||
current_path = PackedVector3Array()
|
||||
path_index = 0
|
||||
path_pending = false
|
||||
path_target = Vector3.INF
|
||||
path_target = sim_position
|
||||
path_retry_remaining = 0.0
|
||||
path_retry_delay = PATH_RETRY_BASE_SECONDS
|
||||
|
||||
|
||||
func _horizontal_distance_to(target_position: Vector3) -> float:
|
||||
|
||||
@@ -173,6 +173,7 @@ material = SubResource("Material_site_canvas")
|
||||
size = Vector3(2.9, 0.08, 1.4)
|
||||
|
||||
[sub_resource type="ProceduralSkyMaterial" id="SkyMaterial_jajce"]
|
||||
resource_local_to_scene = true
|
||||
sky_top_color = Color(0.28, 0.52, 0.72, 1)
|
||||
sky_horizon_color = Color(0.88, 0.74, 0.52, 1)
|
||||
ground_bottom_color = Color(0.18, 0.24, 0.14, 1)
|
||||
@@ -180,9 +181,11 @@ ground_horizon_color = Color(0.68, 0.6, 0.44, 1)
|
||||
sun_angle_max = 18.0
|
||||
|
||||
[sub_resource type="Sky" id="Sky_jajce"]
|
||||
resource_local_to_scene = true
|
||||
sky_material = SubResource("SkyMaterial_jajce")
|
||||
|
||||
[sub_resource type="Environment" id="Environment_greybox"]
|
||||
resource_local_to_scene = true
|
||||
background_mode = 2
|
||||
sky = SubResource("Sky_jajce")
|
||||
background_energy_multiplier = 0.9
|
||||
@@ -204,7 +207,7 @@ fog_height = -2.0
|
||||
fog_height_density = 0.08
|
||||
fog_aerial_perspective = 0.35
|
||||
fog_sky_affect = 0.28
|
||||
volumetric_fog_enabled = true
|
||||
volumetric_fog_enabled = false
|
||||
volumetric_fog_density = 0.0035
|
||||
volumetric_fog_albedo = Color(0.82, 0.9, 0.82, 1)
|
||||
volumetric_fog_emission = Color(0.06, 0.09, 0.07, 1)
|
||||
|
||||
@@ -34,9 +34,12 @@ const NIGHT_LIGHT_ENERGY := 0.3
|
||||
const SUNSET_LIGHT_COLOR := Color(1, 0.55, 0.3, 1)
|
||||
const SUNRISE_LIGHT_COLOR := Color(1, 0.6, 0.45, 1)
|
||||
const SUNRISE_SUNSET_ENERGY := 0.8
|
||||
const PRESENTATION_UPDATE_INTERVAL_SECONDS := 0.1
|
||||
|
||||
var elapsed := 0.0
|
||||
var _simulation_node: Node
|
||||
var _presentation_elapsed := 0.0
|
||||
var _last_applied_cycle := -1.0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -47,17 +50,25 @@ func _ready() -> void:
|
||||
_apply_environment(initial_cycle)
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
func _process(delta: float) -> void:
|
||||
_presentation_elapsed += delta
|
||||
if _presentation_elapsed < PRESENTATION_UPDATE_INTERVAL_SECONDS:
|
||||
return
|
||||
var update_delta := _presentation_elapsed
|
||||
_presentation_elapsed = 0.0
|
||||
if _simulation_node == null or not is_instance_valid(_simulation_node):
|
||||
_find_simulation_node()
|
||||
var cycle: float
|
||||
if _simulation_node != null and "clock" in _simulation_node and _simulation_node.clock != null:
|
||||
cycle = _simulation_node.clock.time_of_day()
|
||||
else:
|
||||
elapsed += _delta
|
||||
elapsed += update_delta
|
||||
if elapsed >= cycle_duration_seconds:
|
||||
elapsed = fmod(elapsed, cycle_duration_seconds)
|
||||
cycle = elapsed / cycle_duration_seconds
|
||||
if is_equal_approx(cycle, _last_applied_cycle):
|
||||
return
|
||||
_last_applied_cycle = cycle
|
||||
_apply_light_rotation(cycle)
|
||||
_apply_environment(cycle)
|
||||
|
||||
|
||||
@@ -1,12 +1,348 @@
|
||||
class_name JajceWorld
|
||||
extends Node3D
|
||||
|
||||
enum PresentationQuality {
|
||||
HIGH,
|
||||
BALANCED,
|
||||
LOW,
|
||||
}
|
||||
|
||||
const BALANCED_RENDER_SCALE := 0.85
|
||||
const LOW_RENDER_SCALE := 0.7
|
||||
const BALANCED_SHADOW_DISTANCE := 120.0
|
||||
const LOW_SHADOW_DISTANCE := 80.0
|
||||
const BALANCED_GRASS_SPACING := 1.0
|
||||
const LOW_GRASS_SPACING := 2.0
|
||||
const BALANCED_GRASS_FIXED_FPS := 18
|
||||
const LOW_GRASS_FIXED_FPS := 12
|
||||
const BALANCED_GRASS_INTERACTORS := 4
|
||||
const BALANCED_GRASS_UPDATE_INTERVAL := 0.12
|
||||
const HIGH_VOLUMETRIC_FOG_ENABLED := true
|
||||
|
||||
static var _viewport_scale_states: Dictionary = {}
|
||||
|
||||
@export_enum("High", "Balanced", "Low") var presentation_quality: int = PresentationQuality.BALANCED
|
||||
|
||||
@onready var terrain: Terrain3D = $TerrainRoot/Terrain3D
|
||||
@onready var world_environment: WorldEnvironment = $WorldEnvironment
|
||||
@onready var directional_light: DirectionalLight3D = $DirectionalLight3D
|
||||
@onready var grass_field: Node3D = $TerrainRoot/Terrain3D/CozyGrassField
|
||||
@onready var grass_controller: Node = $TerrainRoot/GrassInteractionController
|
||||
|
||||
var _active_presentation_quality := -1
|
||||
var _authored_presentation_captured := false
|
||||
var _authored_render_scale := 1.0
|
||||
var _authored_glow_enabled := false
|
||||
var _authored_volumetric_fog_enabled := HIGH_VOLUMETRIC_FOG_ENABLED
|
||||
var _authored_adjustment_enabled := false
|
||||
var _authored_shadow_distance := 0.0
|
||||
var _authored_shadow_mode := DirectionalLight3D.SHADOW_PARALLEL_4_SPLITS
|
||||
var _authored_grass_visible := true
|
||||
var _authored_grass_physics_enabled := true
|
||||
var _authored_grass_spacing := 1.0
|
||||
var _authored_grass_fixed_fps := 30
|
||||
var _authored_grass_interactors := 1
|
||||
var _authored_grass_update_interval := 0.1
|
||||
var _authored_grass_controller_enabled := true
|
||||
var _last_applied_render_scale := -1.0
|
||||
var _viewport_instance_id := 0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_isolate_mutable_presentation_resources()
|
||||
_capture_authored_presentation()
|
||||
apply_presentation_quality(_resolve_requested_quality())
|
||||
call_deferred("_initialize_world_presentation")
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
_release_viewport_scale_owner()
|
||||
|
||||
|
||||
func apply_presentation_quality(quality: int) -> bool:
|
||||
if quality not in PresentationQuality.values():
|
||||
push_warning("JajceWorld: unknown presentation quality %s" % quality)
|
||||
return false
|
||||
if not _authored_presentation_captured:
|
||||
_capture_authored_presentation()
|
||||
if not _authored_presentation_captured:
|
||||
return false
|
||||
if quality == _active_presentation_quality:
|
||||
return true
|
||||
|
||||
match quality:
|
||||
PresentationQuality.HIGH:
|
||||
_apply_environment_quality(
|
||||
_authored_render_scale,
|
||||
_authored_glow_enabled,
|
||||
_authored_volumetric_fog_enabled,
|
||||
_authored_adjustment_enabled,
|
||||
_authored_shadow_distance,
|
||||
_authored_shadow_mode
|
||||
)
|
||||
_apply_grass_quality(
|
||||
_authored_grass_visible,
|
||||
_authored_grass_physics_enabled,
|
||||
_authored_grass_spacing,
|
||||
_authored_grass_fixed_fps,
|
||||
_authored_grass_interactors,
|
||||
_authored_grass_update_interval,
|
||||
_authored_grass_controller_enabled
|
||||
)
|
||||
PresentationQuality.BALANCED:
|
||||
_apply_environment_quality(
|
||||
minf(_authored_render_scale, BALANCED_RENDER_SCALE),
|
||||
_authored_glow_enabled,
|
||||
false,
|
||||
_authored_adjustment_enabled,
|
||||
minf(_authored_shadow_distance, BALANCED_SHADOW_DISTANCE),
|
||||
DirectionalLight3D.SHADOW_PARALLEL_2_SPLITS
|
||||
)
|
||||
_apply_grass_quality(
|
||||
_authored_grass_visible,
|
||||
_authored_grass_physics_enabled,
|
||||
maxf(_authored_grass_spacing, BALANCED_GRASS_SPACING),
|
||||
mini(_authored_grass_fixed_fps, BALANCED_GRASS_FIXED_FPS),
|
||||
mini(_authored_grass_interactors, BALANCED_GRASS_INTERACTORS),
|
||||
maxf(_authored_grass_update_interval, BALANCED_GRASS_UPDATE_INTERVAL),
|
||||
_authored_grass_controller_enabled
|
||||
)
|
||||
PresentationQuality.LOW:
|
||||
_apply_environment_quality(
|
||||
minf(_authored_render_scale, LOW_RENDER_SCALE),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
minf(_authored_shadow_distance, LOW_SHADOW_DISTANCE),
|
||||
DirectionalLight3D.SHADOW_ORTHOGONAL
|
||||
)
|
||||
_apply_grass_quality(
|
||||
false,
|
||||
false,
|
||||
maxf(_authored_grass_spacing, LOW_GRASS_SPACING),
|
||||
mini(_authored_grass_fixed_fps, LOW_GRASS_FIXED_FPS),
|
||||
0,
|
||||
_authored_grass_update_interval,
|
||||
false
|
||||
)
|
||||
|
||||
_active_presentation_quality = quality
|
||||
return true
|
||||
|
||||
|
||||
func get_active_presentation_quality() -> int:
|
||||
return _active_presentation_quality
|
||||
|
||||
|
||||
func get_active_presentation_quality_name() -> StringName:
|
||||
match _active_presentation_quality:
|
||||
PresentationQuality.HIGH:
|
||||
return &"high"
|
||||
PresentationQuality.BALANCED:
|
||||
return &"balanced"
|
||||
PresentationQuality.LOW:
|
||||
return &"low"
|
||||
return &"unknown"
|
||||
|
||||
|
||||
func _capture_authored_presentation() -> void:
|
||||
if _authored_presentation_captured:
|
||||
return
|
||||
var environment := _environment()
|
||||
if environment == null or grass_field == null or grass_controller == null:
|
||||
return
|
||||
_register_viewport_scale_owner()
|
||||
_authored_glow_enabled = environment.glow_enabled
|
||||
# Keep the serialized scene compatibility-safe; High deliberately opts into
|
||||
# this Forward+-only authored effect after the quality profile is resolved.
|
||||
_authored_volumetric_fog_enabled = HIGH_VOLUMETRIC_FOG_ENABLED
|
||||
_authored_adjustment_enabled = environment.adjustment_enabled
|
||||
_authored_shadow_distance = directional_light.directional_shadow_max_distance
|
||||
_authored_shadow_mode = directional_light.directional_shadow_mode
|
||||
_authored_grass_visible = grass_field.visible
|
||||
_authored_grass_physics_enabled = grass_field.is_physics_processing()
|
||||
_authored_grass_spacing = float(grass_field.get("instance_spacing"))
|
||||
_authored_grass_fixed_fps = int(grass_field.get("process_fixed_fps"))
|
||||
_authored_grass_interactors = int(grass_controller.get("max_interactors"))
|
||||
_authored_grass_update_interval = float(grass_controller.get("update_interval"))
|
||||
_authored_grass_controller_enabled = grass_controller.is_processing()
|
||||
_authored_presentation_captured = true
|
||||
|
||||
|
||||
func _isolate_mutable_presentation_resources() -> void:
|
||||
var source_environment := _environment()
|
||||
if source_environment != null:
|
||||
var isolated_environment := source_environment.duplicate(true) as Environment
|
||||
if source_environment.sky != null:
|
||||
var isolated_sky := source_environment.sky.duplicate(true) as Sky
|
||||
if source_environment.sky.sky_material != null:
|
||||
isolated_sky.sky_material = source_environment.sky.sky_material.duplicate(true)
|
||||
isolated_environment.sky = isolated_sky
|
||||
world_environment.environment = isolated_environment
|
||||
|
||||
var source_grass_material := grass_field.get("mesh_material_override") as ShaderMaterial
|
||||
if source_grass_material != null:
|
||||
var isolated_grass_material := source_grass_material.duplicate(true) as ShaderMaterial
|
||||
grass_field.set("mesh_material_override", isolated_grass_material)
|
||||
grass_controller.set("grass_material", isolated_grass_material)
|
||||
var source_process_material := grass_field.get("process_material") as ShaderMaterial
|
||||
if source_process_material != null:
|
||||
var isolated_process_material := source_process_material.duplicate(true) as ShaderMaterial
|
||||
grass_field.set("process_material", isolated_process_material)
|
||||
var particles: Array = grass_field.get("particle_nodes")
|
||||
for value in particles:
|
||||
var particle := value as GPUParticles3D
|
||||
if particle != null:
|
||||
particle.process_material = isolated_process_material
|
||||
|
||||
|
||||
func _resolve_requested_quality() -> int:
|
||||
for argument in OS.get_cmdline_user_args():
|
||||
if argument.begins_with("--quality="):
|
||||
return _quality_from_name(argument.trim_prefix("--quality="))
|
||||
if argument.begins_with("--presentation-quality="):
|
||||
return _quality_from_name(argument.trim_prefix("--presentation-quality="))
|
||||
return presentation_quality
|
||||
|
||||
|
||||
func _quality_from_name(value: String) -> int:
|
||||
match value.strip_edges().to_lower():
|
||||
"high":
|
||||
return PresentationQuality.HIGH
|
||||
"low":
|
||||
return PresentationQuality.LOW
|
||||
"balanced":
|
||||
return PresentationQuality.BALANCED
|
||||
push_warning("JajceWorld: unknown presentation quality '%s'; using scene default" % value)
|
||||
return presentation_quality
|
||||
|
||||
|
||||
func _apply_environment_quality(
|
||||
render_scale: float,
|
||||
glow_enabled: bool,
|
||||
volumetric_fog_enabled: bool,
|
||||
adjustment_enabled: bool,
|
||||
shadow_distance: float,
|
||||
shadow_mode: int
|
||||
) -> void:
|
||||
_claim_viewport_scale_owner()
|
||||
get_viewport().scaling_3d_scale = render_scale
|
||||
_last_applied_render_scale = render_scale
|
||||
var environment := _environment()
|
||||
if environment != null:
|
||||
environment.glow_enabled = glow_enabled
|
||||
environment.volumetric_fog_enabled = (volumetric_fog_enabled and _supports_volumetric_fog())
|
||||
environment.adjustment_enabled = adjustment_enabled
|
||||
directional_light.directional_shadow_max_distance = shadow_distance
|
||||
directional_light.directional_shadow_mode = shadow_mode
|
||||
|
||||
|
||||
func _apply_grass_quality(
|
||||
is_visible: bool,
|
||||
physics_enabled: bool,
|
||||
instance_spacing: float,
|
||||
fixed_fps: int,
|
||||
max_interactors: int,
|
||||
update_interval: float,
|
||||
controller_enabled: bool
|
||||
) -> void:
|
||||
grass_field.set("instance_spacing", instance_spacing)
|
||||
grass_field.set("process_fixed_fps", fixed_fps)
|
||||
grass_field.visible = is_visible
|
||||
grass_field.set_physics_process(physics_enabled)
|
||||
grass_controller.set("max_interactors", maxi(max_interactors, 1))
|
||||
grass_controller.set("update_interval", update_interval)
|
||||
grass_controller.set_process(controller_enabled)
|
||||
grass_controller.set("_elapsed", update_interval)
|
||||
|
||||
var particles: Array = grass_field.get("particle_nodes")
|
||||
for value in particles:
|
||||
var particle := value as GPUParticles3D
|
||||
if particle == null:
|
||||
continue
|
||||
particle.emitting = is_visible and physics_enabled
|
||||
if particle.emitting:
|
||||
particle.restart(true)
|
||||
|
||||
var grass_material := grass_field.get("mesh_material_override") as ShaderMaterial
|
||||
if grass_material != null and not controller_enabled:
|
||||
grass_material.set_shader_parameter("interactor_count", 0)
|
||||
if physics_enabled:
|
||||
grass_field.set("last_pos", Vector3.INF)
|
||||
|
||||
|
||||
func _environment() -> Environment:
|
||||
if world_environment == null:
|
||||
return null
|
||||
return world_environment.environment
|
||||
|
||||
|
||||
func _supports_volumetric_fog() -> bool:
|
||||
return RenderingServer.get_current_rendering_method() == "forward_plus"
|
||||
|
||||
|
||||
func _register_viewport_scale_owner() -> void:
|
||||
var viewport := get_viewport()
|
||||
_viewport_instance_id = viewport.get_instance_id()
|
||||
var state: Dictionary = _viewport_scale_states.get(_viewport_instance_id, {})
|
||||
if state.is_empty():
|
||||
state = {"base_scale": viewport.scaling_3d_scale, "owners": []}
|
||||
_authored_render_scale = float(state["base_scale"])
|
||||
_viewport_scale_states[_viewport_instance_id] = state
|
||||
_claim_viewport_scale_owner()
|
||||
|
||||
|
||||
func _claim_viewport_scale_owner() -> void:
|
||||
if _viewport_instance_id == 0:
|
||||
return
|
||||
var state: Dictionary = _viewport_scale_states.get(_viewport_instance_id, {})
|
||||
if state.is_empty():
|
||||
return
|
||||
var owners: Array = state["owners"]
|
||||
for index in range(owners.size() - 1, -1, -1):
|
||||
var owner: Object = (owners[index] as WeakRef).get_ref()
|
||||
if owner == null or owner == self:
|
||||
owners.remove_at(index)
|
||||
owners.append(weakref(self))
|
||||
state["owners"] = owners
|
||||
_viewport_scale_states[_viewport_instance_id] = state
|
||||
|
||||
|
||||
func _release_viewport_scale_owner() -> void:
|
||||
if _viewport_instance_id == 0 or not _viewport_scale_states.has(_viewport_instance_id):
|
||||
return
|
||||
var viewport := get_viewport()
|
||||
var state: Dictionary = _viewport_scale_states[_viewport_instance_id]
|
||||
var owners: Array = state["owners"]
|
||||
var was_active_owner := false
|
||||
for index in range(owners.size() - 1, -1, -1):
|
||||
var owner: Object = (owners[index] as WeakRef).get_ref()
|
||||
if owner == self:
|
||||
was_active_owner = index == owners.size() - 1
|
||||
owners.remove_at(index)
|
||||
elif owner == null:
|
||||
owners.remove_at(index)
|
||||
if not owners.is_empty():
|
||||
state["owners"] = owners
|
||||
_viewport_scale_states[_viewport_instance_id] = state
|
||||
if (
|
||||
was_active_owner
|
||||
and is_equal_approx(viewport.scaling_3d_scale, _last_applied_render_scale)
|
||||
):
|
||||
var previous_owner := (owners[-1] as WeakRef).get_ref() as JajceWorld
|
||||
if previous_owner != null and previous_owner._last_applied_render_scale >= 0.0:
|
||||
viewport.scaling_3d_scale = previous_owner._last_applied_render_scale
|
||||
else:
|
||||
_viewport_scale_states.erase(_viewport_instance_id)
|
||||
if (
|
||||
was_active_owner
|
||||
and is_equal_approx(viewport.scaling_3d_scale, _last_applied_render_scale)
|
||||
):
|
||||
viewport.scaling_3d_scale = float(state["base_scale"])
|
||||
_viewport_instance_id = 0
|
||||
_last_applied_render_scale = -1.0
|
||||
|
||||
|
||||
func _initialize_world_presentation() -> void:
|
||||
var active_camera := get_viewport().get_camera_3d()
|
||||
if active_camera != null:
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
[ext_resource type="Shader" path="res://world/jajce/materials/cozy_grass.gdshader" id="1_grass"]
|
||||
|
||||
[resource]
|
||||
resource_local_to_scene = true
|
||||
render_priority = 0
|
||||
shader = ExtResource("1_grass")
|
||||
shader_parameter/wind_direction = Vector2(1, 0.7)
|
||||
|
||||
@@ -12,6 +12,7 @@ seamless = true
|
||||
noise = SubResource("FastNoise_grass")
|
||||
|
||||
[resource]
|
||||
resource_local_to_scene = true
|
||||
shader = ExtResource("1_process_shader")
|
||||
shader_parameter/main_noise = SubResource("NoiseTexture_grass")
|
||||
shader_parameter/main_noise_scale = 0.012
|
||||
|
||||
@@ -10,8 +10,11 @@ static var _all: Array = []
|
||||
|
||||
@onready var interaction_point: Marker3D = $InteractionPoint
|
||||
|
||||
const PRESENTATION_UPDATE_INTERVAL_SECONDS := 0.25
|
||||
|
||||
var state: StorageStateRecord
|
||||
var _last_label_text := ""
|
||||
var _presentation_elapsed := 0.0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -35,11 +38,15 @@ func _ready() -> void:
|
||||
add_to_group("storage_nodes")
|
||||
_try_register_with_simulation()
|
||||
_update_presentation()
|
||||
set_process(debug_label_enabled)
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if debug_label_enabled:
|
||||
_update_presentation()
|
||||
func _process(delta: float) -> void:
|
||||
_presentation_elapsed += delta
|
||||
if _presentation_elapsed < PRESENTATION_UPDATE_INTERVAL_SECONDS:
|
||||
return
|
||||
_presentation_elapsed = fmod(_presentation_elapsed, PRESENTATION_UPDATE_INTERVAL_SECONDS)
|
||||
_update_presentation()
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
@@ -93,6 +100,8 @@ func _update_presentation() -> void:
|
||||
|
||||
func set_debug_label_enabled(is_enabled: bool) -> void:
|
||||
debug_label_enabled = is_enabled
|
||||
_presentation_elapsed = 0.0
|
||||
set_process(is_enabled)
|
||||
_update_presentation()
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ class_name PlayerInteractionHud
|
||||
extends Control
|
||||
|
||||
const FEEDBACK_SECONDS := 2.0
|
||||
const PROBE_INTERVAL_SECONDS := 0.1
|
||||
const ENTER_OFFSET := 7.0
|
||||
const ACCENT_READY := Color(0.9, 0.63, 0.28, 0.95)
|
||||
const ACCENT_SUCCESS := Color(0.55, 0.78, 0.38, 0.95)
|
||||
@@ -20,6 +21,7 @@ var current_context_key := ""
|
||||
var feedback_version := 0
|
||||
var feedback_active := false
|
||||
var feedback_succeeded := false
|
||||
var _probe_elapsed := 0.0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -34,12 +36,19 @@ func _ready() -> void:
|
||||
call_deferred("refresh_prompt", true)
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if not feedback_active:
|
||||
refresh_prompt()
|
||||
func _process(delta: float) -> void:
|
||||
if feedback_active:
|
||||
return
|
||||
_probe_elapsed += delta
|
||||
if _probe_elapsed < PROBE_INTERVAL_SECONDS:
|
||||
return
|
||||
_probe_elapsed = fmod(_probe_elapsed, PROBE_INTERVAL_SECONDS)
|
||||
refresh_prompt()
|
||||
|
||||
|
||||
func refresh_prompt(force: bool = false) -> void:
|
||||
if force:
|
||||
_probe_elapsed = 0.0
|
||||
if player == null or not player.has_method("get_interaction_context"):
|
||||
_hide_prompt()
|
||||
return
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
class_name PlayerStatusHud
|
||||
extends Control
|
||||
|
||||
const REFRESH_INTERVAL_SECONDS := 0.2
|
||||
|
||||
@export var simulation_manager: Node
|
||||
|
||||
@onready var status_label: Label = $StatusLabel
|
||||
|
||||
var cached_text := ""
|
||||
var _refresh_elapsed := 0.0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -13,7 +16,11 @@ func _ready() -> void:
|
||||
_refresh(true)
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
func _process(delta: float) -> void:
|
||||
_refresh_elapsed += delta
|
||||
if _refresh_elapsed < REFRESH_INTERVAL_SECONDS:
|
||||
return
|
||||
_refresh_elapsed = fmod(_refresh_elapsed, REFRESH_INTERVAL_SECONDS)
|
||||
_refresh(false)
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ const SIZE_DIAL := 60.0
|
||||
const RING_RADIUS := 24.0
|
||||
const RING_THICKNESS := 4.0
|
||||
const GLOW_RADIUS := 2.5
|
||||
const REFRESH_INTERVAL_SECONDS := 0.1
|
||||
|
||||
const COLOR_NIGHT := Color(0.12, 0.16, 0.42, 1)
|
||||
const COLOR_DAWN := Color(0.95, 0.5, 0.2, 1)
|
||||
@@ -14,9 +15,14 @@ const COLOR_DUSK := Color(0.9, 0.35, 0.2, 1)
|
||||
const COLOR_BG := Color(0.06, 0.06, 0.08, 0.65)
|
||||
|
||||
var _last_fraction := -1.0
|
||||
var _refresh_elapsed := 0.0
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
func _process(delta: float) -> void:
|
||||
_refresh_elapsed += delta
|
||||
if _refresh_elapsed < REFRESH_INTERVAL_SECONDS:
|
||||
return
|
||||
_refresh_elapsed = fmod(_refresh_elapsed, REFRESH_INTERVAL_SECONDS)
|
||||
var fraction := _cycle_fraction()
|
||||
if not is_equal_approx(fraction, _last_fraction):
|
||||
_last_fraction = fraction
|
||||
|
||||
@@ -3,6 +3,7 @@ extends Control
|
||||
|
||||
const ENTER_OFFSET := 9.0
|
||||
const EXIT_OFFSET := 4.0
|
||||
const PROBE_INTERVAL_SECONDS := 0.1
|
||||
|
||||
@export var player: Node
|
||||
@export var simulation_manager: Node
|
||||
@@ -21,6 +22,7 @@ var current_context_key := ""
|
||||
var motion_tween: Tween
|
||||
var motion_version := 0
|
||||
var is_hiding := false
|
||||
var _probe_elapsed := 0.0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -32,11 +34,17 @@ func _ready() -> void:
|
||||
call_deferred("refresh_note", true)
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
func _process(delta: float) -> void:
|
||||
_probe_elapsed += delta
|
||||
if _probe_elapsed < PROBE_INTERVAL_SECONDS:
|
||||
return
|
||||
_probe_elapsed = fmod(_probe_elapsed, PROBE_INTERVAL_SECONDS)
|
||||
refresh_note()
|
||||
|
||||
|
||||
func refresh_note(force: bool = false) -> void:
|
||||
if force:
|
||||
_probe_elapsed = 0.0
|
||||
if player == null or not player.has_method("get_nearby_villager_inspection"):
|
||||
current_context_key = ""
|
||||
_hide_note()
|
||||
|
||||
Reference in New Issue
Block a user