Improve nearby water recovery and camera prompts
Recover players on generated worlds to the nearest safe grass or sand surface. Use the active gameplay camera when positioning shop and storage prompts.
This commit is contained in:
parent
ae802493a0
commit
4575830857
10 changed files with 396 additions and 25 deletions
14
main/main.gd
14
main/main.gd
|
|
@ -1006,7 +1006,8 @@ func _initialize_application(dedicated: bool) -> void:
|
|||
_game_ui,
|
||||
_game_ui.get_screen_fade(),
|
||||
_test_world.get_player_water_triggers(),
|
||||
_test_world.get_safe_respawn_points()
|
||||
_test_world.get_safe_respawn_points(),
|
||||
Callable(_test_world, "get_water_recovery_position"),
|
||||
)
|
||||
_ui_pixelation.effective_pixel_size_changed.connect(
|
||||
_on_effective_ui_pixel_size_changed
|
||||
|
|
@ -2344,17 +2345,9 @@ func _on_remote_recovery_requested(
|
|||
peer_id,
|
||||
"Fishing attempt ended."
|
||||
)
|
||||
var target_position: Vector3 = _test_world.get_player_spawn_transform().origin
|
||||
var nearest_distance: float = INF
|
||||
for point: SafeRespawnPoint in _test_world.get_safe_respawn_points():
|
||||
if point == null or not point.enabled:
|
||||
continue
|
||||
var distance: float = point.get_horizontal_distance_squared(
|
||||
var target_position := _test_world.get_water_recovery_position(
|
||||
entry_position
|
||||
)
|
||||
if distance < nearest_distance:
|
||||
nearest_distance = distance
|
||||
target_position = point.global_position
|
||||
target_position.y += _water_recovery.respawn_height_offset
|
||||
avatar.global_position = target_position
|
||||
avatar.velocity = Vector3.ZERO
|
||||
|
|
@ -2432,6 +2425,7 @@ func _apply_world(
|
|||
spawn_transform,
|
||||
_test_world.get_player_water_triggers(),
|
||||
_test_world.get_safe_respawn_points(),
|
||||
Callable(_test_world, "get_water_recovery_position"),
|
||||
)
|
||||
_shoreline_ambience.configure(
|
||||
_player,
|
||||
|
|
|
|||
|
|
@ -416,6 +416,12 @@ func _run() -> void:
|
|||
)
|
||||
assert(normal_camera.is_position_behind(chat_anchor))
|
||||
assert(not free_camera.is_position_behind(chat_anchor))
|
||||
var shop_prompt := game_ui.get_node("%ShopPrompt") as Control
|
||||
var storage_prompt := game_ui.get_node("%StoragePrompt") as Control
|
||||
game_ui.set_shop_prompt_visible(true, chat_anchor)
|
||||
game_ui.set_storage_prompt_visible(true, chat_anchor)
|
||||
assert(shop_prompt.visible)
|
||||
assert(storage_prompt.visible)
|
||||
chat_ui.show_local_speech("hello there friend")
|
||||
await process_frame
|
||||
assert(bool(player.get("_speech_mouth_active")))
|
||||
|
|
|
|||
|
|
@ -265,6 +265,15 @@ func _run() -> void:
|
|||
assert(bool(water_recovery.get("_prior_movement_enabled")))
|
||||
assert(bool(water_recovery.get("_prior_camera_input_enabled")))
|
||||
assert(not pause_menu.visible)
|
||||
var test_world := main.get_node("TestWorld") as TestWorld
|
||||
assert(test_world != null)
|
||||
var recovery_entry := water_recovery.get("_entry_position") as Vector3
|
||||
var resolved_recovery := water_recovery.call(
|
||||
"_resolve_recovery_position"
|
||||
) as Vector3
|
||||
assert(resolved_recovery.is_equal_approx(
|
||||
test_world.get_water_recovery_position(recovery_entry)
|
||||
))
|
||||
water_recovery.call("_finish_recovery")
|
||||
assert(player.is_movement_enabled())
|
||||
|
||||
|
|
|
|||
|
|
@ -536,6 +536,11 @@ func _validate_generated_region(
|
|||
-INF,
|
||||
0.35,
|
||||
)
|
||||
_validate_water_recovery_positions(
|
||||
region,
|
||||
grass_prop_surface_triangles,
|
||||
sand_prop_surface_triangles,
|
||||
)
|
||||
var anchors := region.get_node(
|
||||
"GatherableAnchors/ReachableTreeTrunks"
|
||||
) as GatherableAnchorSet3D
|
||||
|
|
@ -1268,6 +1273,76 @@ func _triangle_surface_height_at(
|
|||
return highest
|
||||
|
||||
|
||||
func _validate_water_recovery_positions(
|
||||
region: GeneratedWorldRegion,
|
||||
grass_triangles: Array[PackedVector3Array],
|
||||
sand_triangles: Array[PackedVector3Array],
|
||||
) -> void:
|
||||
var fresh_water_root := region.get_node(
|
||||
"WaterBodies/FreshWaterBodies"
|
||||
) as Node3D
|
||||
assert(fresh_water_root != null and fresh_water_root.get_child_count() > 0)
|
||||
var fresh_water := fresh_water_root.get_child(0) as WaterBodyAuthoring
|
||||
assert(fresh_water != null)
|
||||
var entry_position := fresh_water.global_position
|
||||
var fallback_position := region.get_player_spawn_transform().origin
|
||||
var recovery_position := region.get_water_recovery_position(
|
||||
entry_position,
|
||||
fallback_position,
|
||||
)
|
||||
assert(recovery_position.is_finite())
|
||||
assert(
|
||||
_horizontal_distance_squared(entry_position, recovery_position)
|
||||
< _horizontal_distance_squared(entry_position, fallback_position),
|
||||
"Fresh-water recovery returned the distant generated-world spawn.",
|
||||
)
|
||||
assert(
|
||||
recovery_position.y
|
||||
> GeneratedWorldRegion.WATER_HEIGHT
|
||||
+ GeneratedWorldRegion.WATER_RECOVERY_MINIMUM_GROUND_CLEARANCE
|
||||
)
|
||||
var recovery_triangles: Array[PackedVector3Array] = []
|
||||
recovery_triangles.append_array(grass_triangles)
|
||||
recovery_triangles.append_array(sand_triangles)
|
||||
var center_height := _triangle_surface_height_at(
|
||||
recovery_position,
|
||||
recovery_triangles,
|
||||
)
|
||||
assert(center_height > -INF)
|
||||
assert(absf(center_height - recovery_position.y) <= 0.01)
|
||||
for direction: Vector2 in GeneratedWorldRegion.PROP_SURFACE_SAMPLE_DIRECTIONS:
|
||||
var footprint_position := recovery_position + Vector3(
|
||||
direction.x * GeneratedWorldRegion.WATER_RECOVERY_FOOTPRINT_RADIUS,
|
||||
0.0,
|
||||
direction.y * GeneratedWorldRegion.WATER_RECOVERY_FOOTPRINT_RADIUS,
|
||||
)
|
||||
var footprint_height := _triangle_surface_height_at(
|
||||
footprint_position,
|
||||
recovery_triangles,
|
||||
)
|
||||
assert(footprint_height > -INF)
|
||||
assert(
|
||||
absf(footprint_height - recovery_position.y)
|
||||
<= GeneratedWorldRegion.PROP_MAXIMUM_SUPPORT_HEIGHT_DIFFERENCE
|
||||
)
|
||||
assert(
|
||||
region.get_water_recovery_position(
|
||||
entry_position,
|
||||
fallback_position,
|
||||
).is_equal_approx(recovery_position)
|
||||
)
|
||||
assert(
|
||||
region.get_water_recovery_position(
|
||||
Vector3(INF, 0.0, 0.0),
|
||||
fallback_position,
|
||||
).is_equal_approx(fallback_position)
|
||||
)
|
||||
|
||||
|
||||
func _horizontal_distance_squared(a: Vector3, b: Vector3) -> float:
|
||||
return Vector2(a.x - b.x, a.z - b.z).length_squared()
|
||||
|
||||
|
||||
func _validate_tree_gatherable_anchors(
|
||||
region: GeneratedWorldRegion,
|
||||
decorations: Node3D,
|
||||
|
|
|
|||
|
|
@ -100,6 +100,14 @@ func _validate_world_switching() -> void:
|
|||
assert(world.get_fishing_shop() != null)
|
||||
assert(world.get_player_storage() != null)
|
||||
assert(not world.get_fishable_water_regions().is_empty())
|
||||
var starter_safe_points := world.get_safe_respawn_points()
|
||||
assert(not starter_safe_points.is_empty())
|
||||
var authored_safe_point: SafeRespawnPoint = starter_safe_points.back()
|
||||
assert(
|
||||
world.get_water_recovery_position(
|
||||
authored_safe_point.global_position
|
||||
).is_equal_approx(authored_safe_point.global_position)
|
||||
)
|
||||
assert(world.activate_world(WorldLayout.GENERATED, 24680))
|
||||
assert(world.get_world_layout() == WorldLayout.GENERATED)
|
||||
assert(world.get_generation_seed() == 24680)
|
||||
|
|
|
|||
|
|
@ -1892,7 +1892,7 @@ func _position_storage_prompt(world_anchor: Vector3) -> void:
|
|||
if _player == null:
|
||||
_storage_prompt.hide()
|
||||
return
|
||||
var camera := _player.get_gameplay_camera()
|
||||
var camera := _player.get_active_gameplay_camera()
|
||||
if camera == null or camera.is_position_behind(world_anchor):
|
||||
_storage_prompt.hide()
|
||||
return
|
||||
|
|
@ -2006,7 +2006,7 @@ func _position_shop_prompt(world_anchor: Vector3) -> void:
|
|||
if _player == null:
|
||||
_shop_prompt.hide()
|
||||
return
|
||||
var camera: Camera3D = _player.get_gameplay_camera()
|
||||
var camera: Camera3D = _player.get_active_gameplay_camera()
|
||||
if camera == null or camera.is_position_behind(world_anchor):
|
||||
_shop_prompt.hide()
|
||||
return
|
||||
|
|
|
|||
|
|
@ -43,6 +43,12 @@ const PROP_MINIMUM_GROUND_CLEARANCE := 0.05
|
|||
const PROP_MAXIMUM_SUPPORT_HEIGHT_DIFFERENCE := 0.35
|
||||
const PROP_CHANCE_SCALE := 10000
|
||||
const PROP_SELECTION_WEIGHT_SCALE := 1000
|
||||
const WATER_RECOVERY_MINIMUM_GROUND_CLEARANCE := 0.05
|
||||
const WATER_RECOVERY_MINIMUM_UP_DOT := 0.6
|
||||
const WATER_RECOVERY_FOOTPRINT_RADIUS := 0.6
|
||||
const WATER_RECOVERY_INSET_STEP := 0.25
|
||||
const WATER_RECOVERY_MAXIMUM_INSET := 1.5
|
||||
const WATER_RECOVERY_SEARCH_EXPANSIONS: Array[float] = [1.5, 4.0, 12.0]
|
||||
const PROP_SURFACE_SAMPLE_DIRECTIONS: Array[Vector2] = [
|
||||
Vector2(1.0, 0.0),
|
||||
Vector2(0.70710678, 0.70710678),
|
||||
|
|
@ -90,6 +96,7 @@ var _placed_prop_clearance_radii: Array[float] = []
|
|||
var _placed_prop_groups: Array[StringName] = []
|
||||
var _placed_group_coordinates: Dictionary[StringName, Array] = {}
|
||||
var _biome_assignments: Dictionary[Vector2i, StringName] = {}
|
||||
var _water_recovery_triangles_by_coordinate: Dictionary[Vector2i, Array] = {}
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
|
@ -190,6 +197,37 @@ func get_spawn_surface_triangles(
|
|||
return triangles
|
||||
|
||||
|
||||
func get_water_recovery_position(
|
||||
entry_position: Vector3,
|
||||
fallback_position: Vector3,
|
||||
) -> Vector3:
|
||||
if (
|
||||
not entry_position.is_finite()
|
||||
or _water_recovery_triangles_by_coordinate.is_empty()
|
||||
):
|
||||
return super(entry_position, fallback_position)
|
||||
var nearest_surface_distance := _nearest_recovery_surface_distance(
|
||||
entry_position
|
||||
)
|
||||
if not is_finite(nearest_surface_distance):
|
||||
return super(entry_position, fallback_position)
|
||||
for expansion: float in WATER_RECOVERY_SEARCH_EXPANSIONS:
|
||||
var maximum_distance := nearest_surface_distance + expansion
|
||||
var result: Variant = _nearest_safe_recovery_surface_position(
|
||||
entry_position,
|
||||
maximum_distance,
|
||||
)
|
||||
if result is Vector3:
|
||||
return result as Vector3
|
||||
var map_wide_result: Variant = _nearest_safe_recovery_surface_position(
|
||||
entry_position,
|
||||
get_playable_half_extents().length() * 2.0,
|
||||
)
|
||||
if map_wide_result is Vector3:
|
||||
return map_wide_result as Vector3
|
||||
return super(entry_position, fallback_position)
|
||||
|
||||
|
||||
func _on_generation_completed(summary: Dictionary) -> void:
|
||||
_diggable_beach.invalidate_surface_cache()
|
||||
var records: Array[Dictionary] = _generator.placement_records()
|
||||
|
|
@ -325,6 +363,10 @@ func _on_generation_completed(summary: Dictionary) -> void:
|
|||
grass_surface_triangles_by_coordinate,
|
||||
sand_surface_triangles_by_coordinate,
|
||||
)
|
||||
_cache_water_recovery_surfaces(
|
||||
grass_surface_triangles_by_coordinate,
|
||||
sand_surface_triangles_by_coordinate,
|
||||
)
|
||||
_place_spawn_amenities(spawn_position)
|
||||
_configure_fresh_water(records)
|
||||
_configure_diggable_area()
|
||||
|
|
@ -1261,6 +1303,203 @@ func _terrain_surface_triangles_by_coordinate(
|
|||
return triangles_by_coordinate
|
||||
|
||||
|
||||
func _cache_water_recovery_surfaces(
|
||||
grass_triangles: Dictionary[Vector2i, Array],
|
||||
sand_triangles: Dictionary[Vector2i, Array],
|
||||
) -> void:
|
||||
_water_recovery_triangles_by_coordinate.clear()
|
||||
_append_water_recovery_surfaces(grass_triangles)
|
||||
_append_water_recovery_surfaces(sand_triangles)
|
||||
|
||||
|
||||
func _append_water_recovery_surfaces(
|
||||
source: Dictionary[Vector2i, Array],
|
||||
) -> void:
|
||||
for coordinate: Vector2i in source:
|
||||
var eligible: Array = _water_recovery_triangles_by_coordinate.get(
|
||||
coordinate,
|
||||
[],
|
||||
)
|
||||
for triangle: PackedVector3Array in source[coordinate]:
|
||||
if triangle.size() != 3:
|
||||
continue
|
||||
if minf(
|
||||
triangle[0].y,
|
||||
minf(triangle[1].y, triangle[2].y),
|
||||
) <= WATER_HEIGHT + WATER_RECOVERY_MINIMUM_GROUND_CLEARANCE:
|
||||
continue
|
||||
var cross := (triangle[1] - triangle[0]).cross(
|
||||
triangle[2] - triangle[0]
|
||||
)
|
||||
if (
|
||||
cross.length_squared() <= 0.0000001
|
||||
or absf(cross.normalized().dot(Vector3.UP))
|
||||
< WATER_RECOVERY_MINIMUM_UP_DOT
|
||||
):
|
||||
continue
|
||||
eligible.append(triangle)
|
||||
if not eligible.is_empty():
|
||||
_water_recovery_triangles_by_coordinate[coordinate] = eligible
|
||||
|
||||
|
||||
func _nearest_recovery_surface_distance(entry_position: Vector3) -> float:
|
||||
var entry_horizontal := Vector2(entry_position.x, entry_position.z)
|
||||
var nearest_distance_squared := INF
|
||||
for coordinate: Vector2i in _water_recovery_triangles_by_coordinate:
|
||||
for triangle: PackedVector3Array in (
|
||||
_water_recovery_triangles_by_coordinate[coordinate]
|
||||
):
|
||||
var closest := _closest_horizontal_point_on_triangle(
|
||||
entry_horizontal,
|
||||
triangle,
|
||||
)
|
||||
nearest_distance_squared = minf(
|
||||
nearest_distance_squared,
|
||||
entry_horizontal.distance_squared_to(closest),
|
||||
)
|
||||
return sqrt(nearest_distance_squared)
|
||||
|
||||
|
||||
func _nearest_safe_recovery_surface_position(
|
||||
entry_position: Vector3,
|
||||
maximum_distance: float,
|
||||
) -> Variant:
|
||||
var entry_horizontal := Vector2(entry_position.x, entry_position.z)
|
||||
var maximum_distance_squared := maximum_distance * maximum_distance
|
||||
var best_distance_squared := INF
|
||||
var best_position: Variant = null
|
||||
for coordinate: Vector2i in _water_recovery_triangles_by_coordinate:
|
||||
for triangle: PackedVector3Array in (
|
||||
_water_recovery_triangles_by_coordinate[coordinate]
|
||||
):
|
||||
var closest := _closest_horizontal_point_on_triangle(
|
||||
entry_horizontal,
|
||||
triangle,
|
||||
)
|
||||
if (
|
||||
entry_horizontal.distance_squared_to(closest)
|
||||
> maximum_distance_squared
|
||||
):
|
||||
continue
|
||||
var centroid := Vector2(
|
||||
(triangle[0].x + triangle[1].x + triangle[2].x) / 3.0,
|
||||
(triangle[0].z + triangle[1].z + triangle[2].z) / 3.0,
|
||||
)
|
||||
var inset_distance := minf(
|
||||
closest.distance_to(centroid),
|
||||
WATER_RECOVERY_MAXIMUM_INSET,
|
||||
)
|
||||
var inset_steps := ceili(
|
||||
inset_distance / WATER_RECOVERY_INSET_STEP
|
||||
)
|
||||
for inset_index: int in inset_steps + 1:
|
||||
var candidate_horizontal := closest.move_toward(
|
||||
centroid,
|
||||
minf(
|
||||
float(inset_index) * WATER_RECOVERY_INSET_STEP,
|
||||
inset_distance,
|
||||
),
|
||||
)
|
||||
var distance_squared := entry_horizontal.distance_squared_to(
|
||||
candidate_horizontal
|
||||
)
|
||||
if (
|
||||
distance_squared > maximum_distance_squared
|
||||
or distance_squared >= best_distance_squared
|
||||
):
|
||||
continue
|
||||
var candidate := Vector3(
|
||||
candidate_horizontal.x,
|
||||
entry_position.y,
|
||||
candidate_horizontal.y,
|
||||
)
|
||||
var safe_position: Variant = _safe_recovery_surface_position(
|
||||
candidate
|
||||
)
|
||||
if safe_position is Vector3:
|
||||
best_distance_squared = distance_squared
|
||||
best_position = safe_position
|
||||
return best_position
|
||||
|
||||
|
||||
func _safe_recovery_surface_position(candidate: Vector3) -> Variant:
|
||||
var nearby_triangles := _water_recovery_surface_triangles_near(candidate)
|
||||
var surface_height := _surface_height_at(candidate, nearby_triangles)
|
||||
if (
|
||||
surface_height <= WATER_HEIGHT + WATER_RECOVERY_MINIMUM_GROUND_CLEARANCE
|
||||
or not _surface_supports_prop_footprint(
|
||||
candidate,
|
||||
surface_height,
|
||||
WATER_RECOVERY_FOOTPRINT_RADIUS,
|
||||
nearby_triangles,
|
||||
)
|
||||
or not _has_prop_clearance(
|
||||
candidate,
|
||||
WATER_RECOVERY_FOOTPRINT_RADIUS,
|
||||
)
|
||||
):
|
||||
return null
|
||||
candidate.y = surface_height
|
||||
return candidate
|
||||
|
||||
|
||||
func _water_recovery_surface_triangles_near(
|
||||
position: Vector3,
|
||||
) -> Array[PackedVector3Array]:
|
||||
var result: Array[PackedVector3Array] = []
|
||||
if _generator.catalog == null or _generator.catalog.chunk_size <= 0.0:
|
||||
return result
|
||||
var local_position := _generator.to_local(position)
|
||||
var half_grid := Vector2(
|
||||
float(_generator.grid_size.x - 1) * 0.5,
|
||||
float(_generator.grid_size.y - 1) * 0.5,
|
||||
)
|
||||
var coordinate := Vector2i(
|
||||
roundi(local_position.x / _generator.catalog.chunk_size + half_grid.x),
|
||||
roundi(local_position.z / _generator.catalog.chunk_size + half_grid.y),
|
||||
)
|
||||
for offset_x: int in range(-1, 2):
|
||||
for offset_y: int in range(-1, 2):
|
||||
var nearby_coordinate := coordinate + Vector2i(offset_x, offset_y)
|
||||
result.append_array(
|
||||
_water_recovery_triangles_by_coordinate.get(
|
||||
nearby_coordinate,
|
||||
[],
|
||||
)
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
func _closest_horizontal_point_on_triangle(
|
||||
point: Vector2,
|
||||
triangle: PackedVector3Array,
|
||||
) -> Vector2:
|
||||
var polygon := PackedVector2Array([
|
||||
Vector2(triangle[0].x, triangle[0].z),
|
||||
Vector2(triangle[1].x, triangle[1].z),
|
||||
Vector2(triangle[2].x, triangle[2].z),
|
||||
])
|
||||
if Geometry2D.is_point_in_polygon(point, polygon):
|
||||
return point
|
||||
var closest := Geometry2D.get_closest_point_to_segment(
|
||||
point,
|
||||
polygon[0],
|
||||
polygon[1],
|
||||
)
|
||||
var nearest_distance_squared := point.distance_squared_to(closest)
|
||||
for edge_index: int in range(1, 3):
|
||||
var edge_closest := Geometry2D.get_closest_point_to_segment(
|
||||
point,
|
||||
polygon[edge_index],
|
||||
polygon[(edge_index + 1) % 3],
|
||||
)
|
||||
var distance_squared := point.distance_squared_to(edge_closest)
|
||||
if distance_squared < nearest_distance_squared:
|
||||
nearest_distance_squared = distance_squared
|
||||
closest = edge_closest
|
||||
return closest
|
||||
|
||||
|
||||
func _terrain_coordinate_for(mesh_instance: MeshInstance3D) -> Vector2i:
|
||||
var current: Node = mesh_instance
|
||||
while current != null and current != _generator:
|
||||
|
|
|
|||
|
|
@ -72,6 +72,24 @@ func get_safe_respawn_points() -> Array[SafeRespawnPoint]:
|
|||
return points
|
||||
|
||||
|
||||
func get_water_recovery_position(
|
||||
entry_position: Vector3,
|
||||
fallback_position: Vector3,
|
||||
) -> Vector3:
|
||||
if not entry_position.is_finite():
|
||||
return fallback_position
|
||||
var target_position := fallback_position
|
||||
var nearest_distance := INF
|
||||
for point: SafeRespawnPoint in get_safe_respawn_points():
|
||||
if point == null or not is_instance_valid(point) or not point.enabled:
|
||||
continue
|
||||
var distance := point.get_horizontal_distance_squared(entry_position)
|
||||
if distance < nearest_distance:
|
||||
nearest_distance = distance
|
||||
target_position = point.global_position
|
||||
return target_position
|
||||
|
||||
|
||||
func get_diggable_areas() -> Array[DiggableArea3D]:
|
||||
var areas: Array[DiggableArea3D] = []
|
||||
var root: Node = get_node_or_null(diggable_area_root)
|
||||
|
|
|
|||
|
|
@ -49,6 +49,16 @@ func get_safe_respawn_points() -> Array[SafeRespawnPoint]:
|
|||
)
|
||||
|
||||
|
||||
func get_water_recovery_position(entry_position: Vector3) -> Vector3:
|
||||
if _active_region == null:
|
||||
return global_position
|
||||
var fallback_position := get_player_spawn_transform().origin
|
||||
return _active_region.get_water_recovery_position(
|
||||
entry_position,
|
||||
fallback_position,
|
||||
)
|
||||
|
||||
|
||||
func get_fishing_shop() -> FishingShopInteractionType:
|
||||
return _active_region.get_fishing_shop()
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ var _game_ui: GameUI
|
|||
var _screen_fade: ScreenFade
|
||||
var _water_triggers: Array[PlayerWaterTrigger] = []
|
||||
var _safe_points: Array[SafeRespawnPoint] = []
|
||||
var _recovery_position_resolver := Callable()
|
||||
var _initial_spawn_transform: Transform3D
|
||||
var _entry_position: Vector3
|
||||
var _bob_base_position: Vector3
|
||||
|
|
@ -43,6 +44,7 @@ func setup(
|
|||
screen_fade: ScreenFade,
|
||||
water_triggers: Array[PlayerWaterTrigger],
|
||||
safe_points: Array[SafeRespawnPoint],
|
||||
recovery_position_resolver: Callable = Callable(),
|
||||
) -> void:
|
||||
_player = player
|
||||
_fishing_spot = fishing_spot
|
||||
|
|
@ -50,6 +52,7 @@ func setup(
|
|||
_screen_fade = screen_fade
|
||||
_water_triggers = water_triggers
|
||||
_safe_points = safe_points
|
||||
_recovery_position_resolver = recovery_position_resolver
|
||||
_initial_spawn_transform = player.global_transform
|
||||
for water_trigger: PlayerWaterTrigger in _water_triggers:
|
||||
if (
|
||||
|
|
@ -73,6 +76,7 @@ func update_world_context(
|
|||
initial_spawn_transform: Transform3D,
|
||||
water_triggers: Array[PlayerWaterTrigger],
|
||||
safe_points: Array[SafeRespawnPoint],
|
||||
recovery_position_resolver: Callable = Callable(),
|
||||
) -> void:
|
||||
for water_trigger: PlayerWaterTrigger in _water_triggers:
|
||||
if (
|
||||
|
|
@ -88,6 +92,7 @@ func update_world_context(
|
|||
_initial_spawn_transform = initial_spawn_transform
|
||||
_water_triggers = water_triggers
|
||||
_safe_points = safe_points
|
||||
_recovery_position_resolver = recovery_position_resolver
|
||||
for water_trigger: PlayerWaterTrigger in _water_triggers:
|
||||
if (
|
||||
water_trigger != null
|
||||
|
|
@ -183,17 +188,7 @@ func _on_fade_transition_completed(
|
|||
|
||||
|
||||
func _respawn_player() -> void:
|
||||
var target_position: Vector3 = _initial_spawn_transform.origin
|
||||
var nearest_distance: float = INF
|
||||
for point: SafeRespawnPoint in _safe_points:
|
||||
if point == null or not is_instance_valid(point) or not point.enabled:
|
||||
continue
|
||||
var distance: float = point.get_horizontal_distance_squared(
|
||||
_entry_position
|
||||
)
|
||||
if distance < nearest_distance:
|
||||
nearest_distance = distance
|
||||
target_position = point.global_position
|
||||
var target_position := _resolve_recovery_position()
|
||||
target_position.y += respawn_height_offset
|
||||
var respawn_transform: Transform3D = _player.global_transform
|
||||
respawn_transform.basis = _recovery_root_basis
|
||||
|
|
@ -204,6 +199,23 @@ func _respawn_player() -> void:
|
|||
local_respawn_completed.emit(_entry_position)
|
||||
|
||||
|
||||
func _resolve_recovery_position() -> Vector3:
|
||||
if _recovery_position_resolver.is_valid():
|
||||
var resolved: Variant = _recovery_position_resolver.call(_entry_position)
|
||||
if resolved is Vector3 and (resolved as Vector3).is_finite():
|
||||
return resolved as Vector3
|
||||
var target_position := _initial_spawn_transform.origin
|
||||
var nearest_distance := INF
|
||||
for point: SafeRespawnPoint in _safe_points:
|
||||
if point == null or not is_instance_valid(point) or not point.enabled:
|
||||
continue
|
||||
var distance := point.get_horizontal_distance_squared(_entry_position)
|
||||
if distance < nearest_distance:
|
||||
nearest_distance = distance
|
||||
target_position = point.global_position
|
||||
return target_position
|
||||
|
||||
|
||||
func _finish_recovery() -> void:
|
||||
_player.restore_gameplay_orientation_after_recovery()
|
||||
_player.set_water_recovery_active(false)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue