NS-24: Fix GDScript lint (line length, gdlint, gdformat)

- Shorten file-header and doc comments to satisfy max-line-length.
- Rename bump collision preload const to BUMP_COLLISION_CONSTS_SCRIPT.
- Refactor idle floor-angle and walk-step logic for return/format rules.
pull/25/head
VinPropane 2026-04-06 00:41:32 -04:00
parent 7b11d4e238
commit 72897a4924
4 changed files with 61 additions and 87 deletions

View File

@ -3,7 +3,8 @@ extends Node3D
## NS-16: composes ground pick + server authority; see `ground_pick.gd` /
## `position_authority_client.gd`. NS-18: interaction POST + radius glow preview (see child nodes).
## NS-23: bakes `NavigationRegion3D` on startup; boot snap vs nav goal from authority signal.
## Prototype: two **random short bumps** (sibling `StaticBody3D` under `NavigationRegion3D`; see `random_floor_bumps.gd`) **before** nav bake.
## Prototype: two random short bumps (sibling StaticBody3D under NavigationRegion3D; see
## `random_floor_bumps.gd`) before nav bake.
const MOVE_REJECT_MSG_SECONDS: float = 4.0

View File

@ -1,66 +1,42 @@
extends CharacterBody3D
## NS-23: Follow server move target on walkable geometry.
## NS-24: Idle — **`velocity = 0`**, **`floor_snap_length = FLOOR_SNAP_IDLE`** (~**11 cm**), **`move_and_slide()`**
## only. (Post-slide **ray Y lerp** was removed: on bump **rims** hit counts / spread can cross thresholds
## frame-to-frame and **fight** the solver.) Walking snap **`FLOOR_SNAP_MOVING`**. **`snap_to_server`** calls
## **`reset_physics_interpolation()`** when **`physics/common/physics_interpolation`** is on.
## **`project.godot`:** **`physics/3d/physics_engine`** = **Jolt Physics**; **`physics/common/physics_interpolation`**
## = **off** — when **on**, micro **position** changes on bump **rims** blend across frames and read as **ghosting / jitter**.
## **`Player`** under **`World`**.
## **Rendering (main.tscn / project):** curved default capsule + directional light → **specular highlight**
## and **floor z-fight** often read as “jitter” even when `global_position` is stable — player uses
## **no-specular** material, capsule mesh **Y** lift vs **`CapsuleShape3D`** (**z-fight** vs walkables), **no cast shadow** on mesh, **MSAA 3D**, **`light_specular=0`**.
## **Timing:** **120** physics TPS. Jitter that vanishes at **2× run speed** points at **render vs physics** cadence.
## NS-23 path-follow; NS-24 idle. Details in `client/README.md` (movement, idle).
##
## Horizontal velocity toward nav waypoint or goal only; `velocity.y` stays 0 while walking.
## Move **legality** is server-only.
##
## Nav detours except when goal Y is below us: then xz toward goal (waypoints sit under bump rims).
##
## **Upright + floor angle (NS-24):** **`_snap_capsule_upright()`** each tick after motion — **`Transform3D(Basis.IDENTITY, pos)`**
## so the capsule cannot **bank** on rim contacts. **`floor_max_angle`** is **looser while walking**; **stricter when idle** on
## flat floor; **`_floor_angle_loose_ticks`** after a walk **stops**. **Rim / straddle idle:** **moving** `floor_max_angle` when
## **`get_floor_normal()`** is **shallow** or **`_idle_ridged_floor_contacts()`** (slab + cylinder **edge**). After idle slide,
## **`_maybe_idle_rim_settle_nudge()`** + **`_maybe_idle_bump_proximity_escape()`** (uses **`random_floor_bump_mesh`** group mesh radius vs
## **`random_floor_bump_collision_constants.gd`** collision lip, including **cylinder wall** hug). Do **not** reset **`global_transform`** in **`_process`** — that can **desync**
## **`CharacterBody3D`** from the physics/render path and read as **ghosting**. **`NavigationAgent3D.avoidance_enabled`** = **false**.
## **`_try_walk_step_assist()`** — **Y nudge** when the nav goal is **higher**, **`test_move`** says there is **headroom**,
## we have **support** (floor or slide normal with upward component), a **wall-ish** slide opposes **XZ** toward the goal,
## and **horizontal motion** is **not** clearly advancing. A **second** **`move_and_slide()`** runs after a nudge to fix overlap
## (avoids “floating” off the bump edge).
## Summary: Jolt, no interp; dual floor_max_angle; rim/straddle, bump lip escape.
## Snap upright after motion. Do not set global_transform in _process.
## Walk assist may run a second move_and_slide().
const MOVE_SPEED: float = 5.0
## `is_on_floor` / snap: **moving** — seam / curved-side transitions onto NS-19 bumps (~original 50°).
## Moving: floor angle for seams onto stepped QA bumps (~50°).
const FLOOR_MAX_ANGLE_MOVING_DEG: float = 50.0
## **Idle** — tighter to reject **vertex / vertical-face** flicker when stably at rest.
## Idle: tighter angle to reduce vertex / vertical-face flicker (~35°).
const FLOOR_MAX_ANGLE_IDLE_DEG: float = 35.0
## Physics ticks to keep **moving** floor angle after **`_has_walk_goal`** becomes false (arrival / clear).
## Physics ticks to keep moving floor angle after walk goal clears (arrival).
const FLOOR_ANGLE_LOOSE_TICKS_AFTER_STOP: int = 96
const ARRIVE_EPS: float = 0.35
const VERT_ARRIVE_EPS: float = 0.055
const DIRECT_APPROACH_RADIUS: float = 0.85
const FLOOR_SNAP_MOVING: float = 0.32
## Idle snap — slightly **strong** to pin capsule on **rim** contacts without a second slide pass.
## Idle floor snap length (stronger to pin rim contacts).
const FLOOR_SNAP_IDLE: float = 0.11
## Idle on a **rim**: if **`get_floor_normal().y`** dot world up is **below** this, use **moving** `floor_max_angle` for that tick.
## Below this floor-normal dot up, idle tick uses **moving** `floor_max_angle` (rim).
const IDLE_RIM_MIN_FLOOR_UP_DOT: float = 0.968
## Per-tick horizontal pull when idle on a **rim** or **straddling** slab + bump (see **`_maybe_idle_rim_settle_nudge`**).
## Horizontal nudge per tick for rim / straddle settle (**`_maybe_idle_rim_settle_nudge`**).
const IDLE_RIM_SETTLE_STEP: float = 0.004
const DESCEND_GOAL_Y_MARGIN: float = 0.06
## Per-tick lift when **stuck** on a low vertical face while the nav goal is **higher** (NS-19 bump height ~0.15 m).
## Y lift when blocked by wall-ish slide but nav goal is higher (stepped bumps).
const WALK_STEP_ASSIST_DELTA: float = 0.11
## If **`vel_h.dot(want) >` this**, we are making enough XZ progress — do not assist (corners may still slide slowly).
## Skip assist when horizontal velocity already aligns enough with want-dir (corners slide slowly).
const WALK_STEP_ASSIST_MAX_FORWARD_DOT: float = 0.52
const WALK_STEP_ASSIST_COOLDOWN_TICKS: int = 8
## Capsule **`height`** in **`main.tscn`** (**1.0**) → origin at **center**; feet ≈ **`y - 0.5`**.
## Capsule height 1.0 in scene: origin at center, feet ~ **`y - 0.5`**.
const CAPSULE_HALF_HEIGHT: float = 0.5
const _random_floor_bump_collision: Script = preload(
const BUMP_COLLISION_CONSTS_SCRIPT: Script = preload(
"res://scripts/random_floor_bump_collision_constants.gd"
)
## Idle-only radial push off bump **lip** / **wall** (m/tick at **120** TPS).
## Idle radial push per tick off bump lip / wall (meters; 120 Hz physics).
const IDLE_BUMP_ESCAPE_STEP: float = 0.014
## Player **`CapsuleShape3D.radius`** in **`main.tscn`** — wall contact can occur with axis distance **>** **`col_r`**.
## Player capsule radius; wall touch can happen past axis distance **`col_r`**.
const PLAYER_CAPSULE_RADIUS: float = 0.4
var _has_walk_goal: bool = false
@ -138,9 +114,7 @@ func _step_assist_can_raise_by(dy: float) -> bool:
func _try_walk_step_assist() -> bool:
if not _has_walk_goal:
return false
if _auth_walk_goal.y < global_position.y + 0.025:
if not _has_walk_goal or _auth_walk_goal.y < global_position.y + 0.025:
return false
if not _step_assist_has_support():
return false
@ -149,16 +123,12 @@ func _try_walk_step_assist() -> bool:
if to_h.length_squared() < 0.03:
return false
var want: Vector3 = to_h.normalized()
if not _step_assist_wallish_blocks(want):
return false
var vel_h: Vector3 = Vector3(velocity.x, 0.0, velocity.z)
if vel_h.dot(want) > WALK_STEP_ASSIST_MAX_FORWARD_DOT:
if not _step_assist_wallish_blocks(want) or vel_h.dot(want) > WALK_STEP_ASSIST_MAX_FORWARD_DOT:
return false
var lift: float = minf(WALK_STEP_ASSIST_DELTA, _auth_walk_goal.y - global_position.y + 0.45)
lift = minf(lift, maxf(0.0, _auth_walk_goal.y + 0.55 - global_position.y))
if lift <= 0.002:
return false
if not _step_assist_can_raise_by(lift):
if lift <= 0.002 or not _step_assist_can_raise_by(lift):
return false
global_position.y += lift
return true
@ -206,8 +176,7 @@ func _maybe_idle_rim_settle_nudge() -> void:
if get_slide_collision_count() < 1:
return
var shallow_floor: bool = (
is_on_floor()
and get_floor_normal().dot(Vector3.UP) < IDLE_RIM_MIN_FLOOR_UP_DOT
is_on_floor() and get_floor_normal().dot(Vector3.UP) < IDLE_RIM_MIN_FLOOR_UP_DOT
)
if not shallow_floor and not _idle_ridged_floor_contacts():
return
@ -232,7 +201,7 @@ func _maybe_idle_rim_settle_nudge() -> void:
func _maybe_idle_bump_proximity_escape() -> void:
var feet_y: float = global_position.y - CAPSULE_HALF_HEIGHT
for node: Node in get_tree().get_nodes_in_group(
_random_floor_bump_collision.RANDOM_FLOOR_BUMP_MESH_GROUP
BUMP_COLLISION_CONSTS_SCRIPT.RANDOM_FLOOR_BUMP_MESH_GROUP
):
if not node is MeshInstance3D:
continue
@ -245,8 +214,8 @@ func _maybe_idle_bump_proximity_escape() -> void:
var r_mesh: float = cyl.top_radius
var h: float = cyl.height
var col_r: float = minf(
r_mesh + _random_floor_bump_collision.COLLISION_RADIUS_EXTRA,
_random_floor_bump_collision.COLLISION_RADIUS_MAX
r_mesh + BUMP_COLLISION_CONSTS_SCRIPT.COLLISION_RADIUS_EXTRA,
BUMP_COLLISION_CONSTS_SCRIPT.COLLISION_RADIUS_MAX
)
var dx: float = global_position.x - c.x
var dz: float = global_position.z - c.z
@ -266,11 +235,16 @@ func _maybe_idle_bump_proximity_escape() -> void:
global_position += away
return
# On / above the disc, hugging the **visual** rim — step onto open slab.
if feet_y >= bottom_y - 0.06 and feet_y <= top_y + 0.1 and d >= r_mesh * 0.86 and d <= r_mesh + 0.14:
if (
feet_y >= bottom_y - 0.06
and feet_y <= top_y + 0.1
and d >= r_mesh * 0.86
and d <= r_mesh + 0.14
):
if not test_move(global_transform, away, null, safe_margin):
global_position += away
return
# Floor beside **vertical** cylinder side: axis distance can exceed **`col_r`** while capsule still touches wall.
# Beside vertical cylinder: axis distance may exceed **`col_r`** while capsule still touches wall.
if (
feet_y <= top_y + 0.14
and d > r_mesh * 0.82
@ -283,24 +257,19 @@ func _maybe_idle_bump_proximity_escape() -> void:
func _snap_capsule_upright() -> void:
# TODO(NS-24 follow-on): When the avatar has **facing yaw**, clear **pitch/roll** only — keep **Y** rotation;
# full **`Basis.IDENTITY`** would erase look direction.
# TODO(NS-24 follow-on): With facing yaw, clear pitch/roll only; keep Y rotation.
# Full **`Basis.IDENTITY`** would erase look direction.
var p: Vector3 = global_position
global_transform = Transform3D(Basis.IDENTITY, p)
func _physics_process(_delta: float) -> void:
var use_loose_floor_angle: bool = (
_has_walk_goal or _floor_angle_loose_ticks > 0
)
if not _has_walk_goal and (
(
is_on_floor()
and get_floor_normal().dot(Vector3.UP) < IDLE_RIM_MIN_FLOOR_UP_DOT
)
or _idle_ridged_floor_contacts()
):
use_loose_floor_angle = true
var use_loose_floor_angle: bool = _has_walk_goal or _floor_angle_loose_ticks > 0
if not _has_walk_goal:
var floor_up_dot: float = get_floor_normal().dot(Vector3.UP)
var shallow_idle_floor: bool = is_on_floor() and floor_up_dot < IDLE_RIM_MIN_FLOOR_UP_DOT
if shallow_idle_floor or _idle_ridged_floor_contacts():
use_loose_floor_angle = true
floor_max_angle = deg_to_rad(
FLOOR_MAX_ANGLE_MOVING_DEG if use_loose_floor_angle else FLOOR_MAX_ANGLE_IDLE_DEG
)

View File

@ -1,10 +1,10 @@
extends RefCounted
## Shared **`CylinderShape3D.radius`** fudge vs mesh for procedural **random floor QA bumps** (`random_floor_bumps.gd`).
## Used by **`player.gd`** (idle lip escape). Change here only.
## (Movement/step validation UX was originally tracked in Jira **NS-19** — not part of this scripts name.)
## CylinderShape3D.radius fudge vs mesh for procedural QA bumps (spawned in random_floor_bumps.gd).
## Used by `player.gd` (idle lip escape). Change here only.
## (NS-19 tracked movement/step UX; unrelated to this script name.)
## **`MeshInstance3D.add_to_group`** for spawned bumps; **`player.gd`** queries this for proximity escape.
## Group name for spawned bump meshes; `player.gd` queries it for proximity escape.
const RANDOM_FLOOR_BUMP_MESH_GROUP: String = "random_floor_bump_mesh"
const COLLISION_RADIUS_EXTRA: float = 0.105

View File

@ -1,11 +1,12 @@
extends RefCounted
## Prototype QA: two short **cylinder** bumps, each on its own **`StaticBody3D`** **sibling** of **`Floor`** (same parent:
## **`NavigationRegion3D`**). Avoids **compound internal edges** (floor box + cylinder on one body), which caused strong
## idle jitter beside the bump. Meshes use group **`random_floor_bump_mesh`** (see **`random_floor_bump_collision_constants.gd`**). Loaded via **`main.gd`** **`load().call`**.
## **Collision radius > mesh radius** (lip before visual rim); values in **`random_floor_bump_collision_constants.gd`**. Spawn **before** `NavigationRegion3D.bake_navigation_mesh()`.
## Prototype QA: two short bumps on StaticBody3D siblings of Floor (same NavigationRegion3D parent).
## Avoids one body with floor + cylinder (compound internal edges caused idle jitter beside bumps).
## Bump mesh group and collision fudge: random_floor_bump_collision_constants.gd.
## Spawner called from main.gd before nav bake.
## Collision radius > mesh radius; spawn before `NavigationRegion3D.bake_navigation_mesh()`.
const _random_floor_bump_collision: Script = preload(
const BUMP_COLLISION_CONSTS_SCRIPT: Script = preload(
"res://scripts/random_floor_bump_collision_constants.gd"
)
@ -20,6 +21,7 @@ const MIN_PAIR_SEPARATION: float = 2.85
const MAX_PLACE_ATTEMPTS: int = 64
const EXCLUSION_MARGIN: float = 0.72
static func spawn_short_random_bumps(floor_node: StaticBody3D) -> void:
var rng := RandomNumberGenerator.new()
rng.randomize()
@ -34,10 +36,7 @@ static func spawn_short_random_bumps(floor_node: StaticBody3D) -> void:
var xz: Vector2 = Vector2.ZERO
var ok := false
for _a: int in MAX_PLACE_ATTEMPTS:
xz = Vector2(
rng.randf_range(-XZ_RANGE, XZ_RANGE),
rng.randf_range(-XZ_RANGE, XZ_RANGE)
)
xz = Vector2(rng.randf_range(-XZ_RANGE, XZ_RANGE), rng.randf_range(-XZ_RANGE, XZ_RANGE))
if _xz_in_exclusion(xz):
continue
var far_enough := true
@ -50,7 +49,12 @@ static func spawn_short_random_bumps(floor_node: StaticBody3D) -> void:
ok = true
break
if not ok:
push_warning("RandomFloorBumps: could not place bump %d after %d tries" % [idx, MAX_PLACE_ATTEMPTS])
push_warning(
(
"RandomFloorBumps: could not place bump %d after %d tries"
% [idx, MAX_PLACE_ATTEMPTS]
)
)
continue
placed_xz.append(xz)
var h: float = rng.randf_range(HEIGHT_MIN, HEIGHT_MAX)
@ -99,7 +103,7 @@ static func _add_cylinder_bump(
var mesh_inst := MeshInstance3D.new()
mesh_inst.name = "RandomTestBump%d_mesh" % idx
mesh_inst.add_to_group(_random_floor_bump_collision.RANDOM_FLOOR_BUMP_MESH_GROUP)
mesh_inst.add_to_group(BUMP_COLLISION_CONSTS_SCRIPT.RANDOM_FLOOR_BUMP_MESH_GROUP)
var cm := CylinderMesh.new()
cm.height = height
cm.top_radius = radius
@ -115,8 +119,8 @@ static func _add_cylinder_bump(
var csh := CylinderShape3D.new()
csh.height = height
csh.radius = minf(
radius + _random_floor_bump_collision.COLLISION_RADIUS_EXTRA,
_random_floor_bump_collision.COLLISION_RADIUS_MAX
radius + BUMP_COLLISION_CONSTS_SCRIPT.COLLISION_RADIUS_EXTRA,
BUMP_COLLISION_CONSTS_SCRIPT.COLLISION_RADIUS_MAX
)
col.shape = csh
col.position = Vector3.ZERO