Fix casting beneath overhangs

This commit is contained in:
Alexander Sellite 2026-08-09 12:24:11 -04:00
parent 40696b6bd9
commit deab51a18a
4 changed files with 46 additions and 8 deletions

View file

@ -56,6 +56,7 @@ var _rod: Node3D
var _rod_tip: Marker3D
var _rod_neutral_rotation: Vector3
var _cast_arrival_position: Vector3
var _active_cast_arc_height: float = 0.0
var _cast_tracks_water_surface: bool = false
var _bobber_surface_position: Vector3
var _bobber_idle_elapsed: float = 0.0
@ -153,6 +154,7 @@ func begin_cast(
target: Vector3,
blocked_landing_position: Vector3 = Vector3.ZERO,
cast_is_blocked: bool = false,
resolved_arc_height: float = -1.0,
) -> void:
if _mode != VisualMode.AIMING or _rod_tip == null:
return
@ -165,6 +167,11 @@ func begin_cast(
_cast_arrival_position = (
blocked_landing_position if cast_is_blocked else target
)
_active_cast_arc_height = (
maxf(resolved_arc_height, 0.0)
if resolved_arc_height >= 0.0
else cast_arc_height
)
_cast_tracks_water_surface = not cast_is_blocked
var cast_start: Vector3 = _rod_tip.global_position
@ -302,6 +309,7 @@ func cleanup() -> void:
_bobber_base_scale = Vector3.ONE
_bobber.rotation = Vector3.ZERO
_cast_arrival_position = Vector3.ZERO
_active_cast_arc_height = 0.0
_cast_tracks_water_surface = false
_bobber_surface_position = Vector3.ZERO
_bobber_idle_elapsed = 0.0
@ -319,7 +327,7 @@ func _set_cast_sample(
target: Vector3,
) -> void:
var sample: Vector3 = start.lerp(target, progress)
sample.y += sin(progress * PI) * cast_arc_height
sample.y += sin(progress * PI) * _active_cast_arc_height
if _cast_tracks_water_surface:
sample.y += (
WaterSurfaceMotionType.get_default_height_offset()

View file

@ -783,19 +783,31 @@ func _confirm_cast() -> void:
return
var cast_launch_position := _get_cast_launch_position()
_cast_path_is_clear = is_cast_path_clear(
var resolved_cast_arc: Dictionary = _surface_resolver.resolve_cast_arc(
get_world_3d().direct_space_state,
cast_launch_position,
_cast_target,
_presentation.cast_arc_height,
)
var resolved_arc_height: float = float(
resolved_cast_arc.get("arc_height", _presentation.cast_arc_height)
)
var resolved_cast_collision: Dictionary = resolved_cast_arc.get(
"collision",
{},
)
_cast_path_is_clear = resolved_cast_collision.is_empty()
var cast_is_invalid: bool = (
not _aim_surface_sample.is_fishable() or not _cast_path_is_clear
)
var arrival_position: Vector3 = _cast_target
if not _cast_path_is_clear:
arrival_position = _resolve_cast_impact_position(
cast_launch_position,
var collision_position: Variant = resolved_cast_collision.get(
"position",
_cast_target,
)
if typeof(collision_position) == TYPE_VECTOR3:
arrival_position = collision_position
if _active_player != null:
# Movement is available while aiming, then locks once the cast is
# released so the active bobber/fishing event remains anchored.
@ -806,6 +818,7 @@ func _confirm_cast() -> void:
_cast_target,
arrival_position,
cast_is_invalid,
resolved_arc_height,
)
_presentation.set_line_mode(FishingPresentationType.LineMode.TAUT)
@ -1410,6 +1423,7 @@ func resolve_fishing_surface(
get_world_3d().direct_space_state,
target,
resolved_reference_y,
water_occlusion_tolerance,
)

View file

@ -23,6 +23,7 @@ func resolve_surface(
space_state: PhysicsDirectSpaceState3D,
query_position: Vector3,
reference_y: float,
maximum_solid_rise: float = INF,
) -> FishingSurfaceSampleType:
var sample := FishingSurfaceSampleType.new()
var ray_top: float = maxf(query_position.y, reference_y) + ray_start_height
@ -33,10 +34,16 @@ func resolve_surface(
ray_top,
ray_bottom,
)
var solid_hit: Dictionary = _find_top_solid_surface(
var solid_ray_top: float = minf(
ray_top,
reference_y + maximum_solid_rise,
)
var solid_hit: Dictionary = {}
if solid_ray_top > ray_bottom:
solid_hit = _find_top_solid_surface(
space_state,
query_position,
ray_top,
solid_ray_top,
ray_bottom,
)

View file

@ -50,6 +50,7 @@ func _validate_resolver_layers() -> void:
_add_solid_box(world, Vector3(40.0, 2.0, 12.0), Vector3(10.0, -1.0, 0.0))
_add_water_region(world, Vector3(0.0, 2.0, 0.0), Vector2(8.0, 8.0), PondPool)
_add_solid_box(world, Vector3(1.0, 1.0, 1.0), Vector3(2.0, 2.25, 0.0))
_add_solid_box(world, Vector3(1.0, 1.0, 1.0), Vector3(-2.0, 6.0, 0.0))
_add_water_region(world, Vector3(10.0, 5.0, 0.0), Vector2(6.0, 6.0), PondPool)
_add_water_region(world, Vector3(20.0, 3.0, 0.0), Vector2(6.0, 6.0), null)
await physics_frame
@ -64,6 +65,14 @@ func _validate_resolver_layers() -> void:
)
assert(water.is_fishable())
assert(is_equal_approx(water.position.y, 2.0))
var water_under_overhang: FishingSurfaceSampleType = resolver.resolve_surface(
space_state,
Vector3(-2.0, 4.0, 0.0),
4.0,
0.08,
)
assert(water_under_overhang.is_fishable())
assert(is_equal_approx(water_under_overhang.position.y, 2.0))
var covered_water: FishingSurfaceSampleType = resolver.resolve_surface(
space_state,