Fix rain puddle splash lifecycle

This commit is contained in:
Alexander Sellite 2026-09-02 17:20:05 -04:00
parent 2b5b0a63ec
commit 1163cef320
3 changed files with 55 additions and 28 deletions

View file

@ -83,11 +83,14 @@ func add_item_to_storage(item_id: StringName, quantity: int = 1) -> bool:
if existing != null:
existing.quantity += quantity
else:
var owned := OwnedItemType.new()
owned.item_id = item_id
owned.quantity = quantity
owned.storage_slot = _first_available_storage_slot(item, _items)
_items.append(owned)
var new_owned_item := OwnedItemType.new()
new_owned_item.item_id = item_id
new_owned_item.quantity = quantity
new_owned_item.storage_slot = _first_available_storage_slot(
item,
_items,
)
_items.append(new_owned_item)
contents_changed.emit()
return true
if existing != null:

View file

@ -50,6 +50,19 @@ func _run() -> void:
assert(not puddles.is_processing())
puddles.set_active(true)
assert(puddles.is_processing())
var splash_root := puddles.get_node("PuddleSplashes") as Node3D
assert(splash_root != null)
splash_root.position = Vector3(5.0, 1.0, -4.0)
var expected_splash_position := Vector3(1.5, 2.04, -3.0)
puddles.call("_emit_splash", Vector3(1.5, 0.0, -3.0), 2.0)
assert(splash_root.get_child_count() == 1)
var splash := splash_root.get_child(0) as CPUParticles3D
assert(splash != null and splash.is_inside_tree())
assert(splash.global_position.is_equal_approx(expected_splash_position))
await create_timer(0.55).timeout
await process_frame
assert(not is_instance_valid(splash))
splash_root.position = Vector3.ZERO
for _tick: int in 18:
puddles.call("_process", 0.25)
assert(puddles.get_active_puddle_count() > 0)

View file

@ -286,21 +286,21 @@ func _spawn_puddle_near(player_position: Vector3) -> bool:
if triangle_index < 0 or triangle_index >= _surface_triangles.size():
return false
var triangle: PackedVector3Array = _surface_triangles[triangle_index]
var position: Vector3 = _random_point_in_triangle(triangle)
var candidate_position: Vector3 = _random_point_in_triangle(triangle)
var horizontal_distance: float = Vector2(
position.x,
position.z,
candidate_position.x,
candidate_position.z,
).distance_to(Vector2(player_position.x, player_position.z))
if (
horizontal_distance < MINIMUM_PLAYER_DISTANCE
or horizontal_distance > MAXIMUM_PLAYER_DISTANCE
or _is_candidate_blocked(position)
or _is_near_existing_puddle(position)
or _is_candidate_blocked(candidate_position)
or _is_near_existing_puddle(candidate_position)
):
continue
var maximum_radius: float = minf(
MAXIMUM_PUDDLE_RADIUS,
_minimum_triangle_edge_distance(position, triangle) * 0.65,
_minimum_triangle_edge_distance(candidate_position, triangle) * 0.65,
)
if maximum_radius < MINIMUM_PUDDLE_RADIUS:
continue
@ -311,7 +311,7 @@ func _spawn_puddle_near(player_position: Vector3) -> bool:
var radius_z: float = radius_x * _random.randf_range(0.58, 0.86)
_activate_puddle(
puddle,
position,
candidate_position,
Vector2(radius_x, radius_z),
_random.randf_range(0.0, TAU),
)
@ -440,15 +440,15 @@ func _apply_puddle_alpha(puddle: PuddleState) -> void:
puddle.material.albedo_color = color
func _is_candidate_blocked(position: Vector3) -> bool:
func _is_candidate_blocked(candidate_position: Vector3) -> bool:
if not is_inside_tree():
return false
var world: World3D = get_world_3d()
if world == null:
return false
var query := PhysicsRayQueryParameters3D.create(
position + Vector3.UP * 0.05,
position + Vector3.UP * 1.75,
candidate_position + Vector3.UP * 0.05,
candidate_position + Vector3.UP * 1.75,
TERRAIN_COLLISION_MASK,
)
query.collide_with_areas = false
@ -456,8 +456,11 @@ func _is_candidate_blocked(position: Vector3) -> bool:
return not world.direct_space_state.intersect_ray(query).is_empty()
func _is_near_existing_puddle(position: Vector3) -> bool:
var horizontal_position := Vector2(position.x, position.z)
func _is_near_existing_puddle(candidate_position: Vector3) -> bool:
var horizontal_position := Vector2(
candidate_position.x,
candidate_position.z,
)
for puddle: PuddleState in _puddles:
if not puddle.active:
continue
@ -505,15 +508,14 @@ func _update_player_splashes(
func _emit_splash(player_position: Vector3, surface_height: float) -> void:
if _splash_root == null or not _splash_root.visible:
if (
_splash_root == null
or not _splash_root.is_inside_tree()
or not _splash_root.visible
):
return
var particles := CPUParticles3D.new()
particles.name = "RainPuddleSplash"
particles.global_position = Vector3(
player_position.x,
surface_height + 0.04,
player_position.z,
)
particles.amount = 7
particles.lifetime = 0.38
particles.one_shot = true
@ -535,7 +537,13 @@ func _emit_splash(player_position: Vector3, surface_height: float) -> void:
droplet_mesh.size = Vector3(0.028, 0.04, 0.028)
droplet_mesh.material = material
particles.mesh = droplet_mesh
particles.emitting = false
_splash_root.add_child(particles)
particles.global_position = Vector3(
player_position.x,
surface_height + 0.04,
player_position.z,
)
particles.emitting = true
var cleanup := create_tween()
cleanup.tween_interval(0.5)
@ -548,10 +556,10 @@ func _seed_for_world() -> int:
return max(_test_world.get_generation_seed(), 1) ^ 0x5261696e
func _surface_cell_for(position: Vector3) -> Vector2i:
func _surface_cell_for(surface_position: Vector3) -> Vector2i:
return Vector2i(
floori(position.x / SURFACE_CELL_SIZE),
floori(position.z / SURFACE_CELL_SIZE),
floori(surface_position.x / SURFACE_CELL_SIZE),
floori(surface_position.z / SURFACE_CELL_SIZE),
)
@ -567,10 +575,13 @@ func _random_point_in_triangle(triangle: PackedVector3Array) -> Vector3:
func _minimum_triangle_edge_distance(
position: Vector3,
candidate_position: Vector3,
triangle: PackedVector3Array,
) -> float:
var horizontal_position := Vector2(position.x, position.z)
var horizontal_position := Vector2(
candidate_position.x,
candidate_position.z,
)
return minf(
_horizontal_distance_to_segment(
horizontal_position,