/** * Einzige Quelle für "wo ist der Spieler gerade" (Befund A, Fix-Runde 3). main.js trieb * Streaming, isReadyAt, Auffangnetz, lastSafe und Schattenwurf an sechs Stellen ausnahmslos * mit player.playerCapsule.position an - das ist falsch, sobald ein Fahrzeug aktiv ist: * PlayerController.syncMountedPlayer() hängt die Kapsel beim Einsteigen als Kind an * vehicle.vehicleGroup und überschreibt cap.position mit driverSeatPosition * scale, einer * festen, kleinen lokalen Zahl nahe dem Ursprung - NICHT der Weltposition. playerCapsule. * position bleibt also beim Fahren nicht etwa nur stehen, es springt auf einen bedeutungslosen * Sitzplatz-Offset. Jede Stelle, die "wo ist der Spieler" braucht, muss über playerPosition() * laufen, sonst hört das Streaming beim Fahren auf nachzuladen. */ /** * getActiveVehicle() allein reicht nicht: VehicleSystem löscht this.active beim Aussteigen * NICHT (weder exit() noch das stopActive(), das reset() bei aktivem Fahrzeug aufruft, setzen * es zurück - nur destroy() tut das, beim kompletten Abbau aller Fahrzeuge). getActiveVehicle() * bleibt also auf dem zuletzt benutzten Fahrzeug stehen, lange nachdem man ausgestiegen ist. * getControllerMode() === 1 ist das einzige verlässliche "fährt gerade wirklich"-Signal (von * enter()/exit() korrekt gesetzt) - per Hand im Browser nachgewiesen: nach reset() bei aktivem * Fahrzeug lieferte getActiveVehicle() weiterhin das verlassene Fahrzeug, obwohl die Kapsel * schon zurück auf dem Spawnpunkt stand und controllerMode schon wieder 0 war. */ function activeVehicleOrNull(player) { return player.getControllerMode() === 1 ? player.getActiveVehicle() : null; } export function playerPosition(player) { const vehicle = activeVehicleOrNull(player); return vehicle ? vehicle.vehicleGroup.position : player.playerCapsule.position; } /** * Echtes Bodensignal für lastSafe, fahrzeugbewusst (Ruling 52 gilt auch im Auto). Zu Fuß * das vorhandene, raycast-gestützte getIsOnGround(). Im Fahrzeug NICHT dasselbe * getIsOnGround(): controllerMode 1 (Fahrzeug) läuft nie durch CharacterMovement, das * einzige, was playerIsOnGround setzt - der Wert bliebe beim Fahren auf dem Zustand von * kurz vor dem Einsteigen eingefroren (nahezu immer "true", man steigt ja stehend ein) und * würde lastSafe jeden Frame unbesehen überschreiben, auch wenn das Fahrzeug gerade über * einer Klippe oder einer noch nicht geladenen Lücke schwebt - exakt das Verhalten, das * Ruling 52 ("an ein echtes Bodensignal binden statt an die Höhe") verhindern sollte. * Stattdessen derselbe Radkontakt-Test, den VehicleSystem.finishPhysics() intern für * "hasWheelContact" benutzt: mindestens ein Rad mit Bodenkontakt. */ export function playerIsGrounded(player) { const vehicle = activeVehicleOrNull(player); if (!vehicle) return player.getIsOnGround(); const wheels = vehicle.vehicleController; for (let i = 0; i < wheels.numWheels(); i++) { if (wheels.wheelIsInContact(i)) return true; } return false; }