fix(ebike): unlock walking during breakdown + hide interact prompt + 450m ride

Three fixes for the ebike-breakdown substep:

1. PlayerController: the previous `if (!isEbikeBreakdown)` guard
   zeroed _wishDir for everyone during breakdown, including the
   player after they had been auto-dismounted to walk mode. Narrow
   the guard to `isEbikeMounted && isEbikeBreakdown` so the bike
   stops accepting drive input but the player on foot can move.

2. Ebike: track `window.ebikeBreakdownActive` in component state
   and hide the InteractableObject (and therefore the interact
   prompt UI) while the breakdown sequence is active. The bike must
   read as inert and non-interactive while the panne dialogue plays
   and during the auto-dismount that follows.

3. ebikeConfig: bump EBIKE_INTRO_BREAKDOWN_DISTANCE from 15 m to
   450 m so the panne triggers after a real ride instead of a few
   meters from the parked spawn.
This commit is contained in:
Tom Boullay
2026-06-03 00:46:30 +02:00
parent 675a45f02b
commit 00b1ff9e93
3 changed files with 13 additions and 3 deletions
+7 -1
View File
@@ -344,13 +344,19 @@ export function Ebike({
// pollute the view. The prompt comes back the moment the bike comes to // pollute the view. The prompt comes back the moment the bike comes to
// a stop. window.ebikeDriveInputActive is published every frame by // a stop. window.ebikeDriveInputActive is published every frame by
// PlayerController based on whether a movement key is currently held. // PlayerController based on whether a movement key is currently held.
// Also hide entirely while the breakdown sequence is active — the bike
// must read as inert and non-interactive while the panne dialogue plays
// and during the auto-dismount that follows.
const [isEbikeDriving, setIsEbikeDriving] = useState(false); const [isEbikeDriving, setIsEbikeDriving] = useState(false);
const [isEbikeBreakdown, setIsEbikeBreakdown] = useState(false);
useFrame(() => { useFrame(() => {
const driving = const driving =
movementMode === "ebike" && window.ebikeDriveInputActive === true; movementMode === "ebike" && window.ebikeDriveInputActive === true;
if (driving !== isEbikeDriving) setIsEbikeDriving(driving); if (driving !== isEbikeDriving) setIsEbikeDriving(driving);
const breakdown = window.ebikeBreakdownActive === true;
if (breakdown !== isEbikeBreakdown) setIsEbikeBreakdown(breakdown);
}); });
const showInteractPrompt = !isEbikeDriving; const showInteractPrompt = !isEbikeDriving && !isEbikeBreakdown;
const handleInteract = useCallback((): void => { const handleInteract = useCallback((): void => {
if (window.ebikeBreakdownActive === true) return; if (window.ebikeBreakdownActive === true) return;
+1 -1
View File
@@ -19,7 +19,7 @@ export const EBIKE_WORLD_POSITION: Vector3Tuple = [68, 0.8, 65];
export const EBIKE_WORLD_ROTATION_Y = -2.5; export const EBIKE_WORLD_ROTATION_Y = -2.5;
export const EBIKE_WORLD_SCALE = 0.35; export const EBIKE_WORLD_SCALE = 0.35;
export const EBIKE_INTRO_BREAKDOWN_DISTANCE = 15; export const EBIKE_INTRO_BREAKDOWN_DISTANCE = 450;
export const EBIKE_BREAKDOWN_DIALOGUE_DELAY_MS = 250; export const EBIKE_BREAKDOWN_DIALOGUE_DELAY_MS = 250;
export const EBIKE_ACCELERATION_DURATION_MS = 2000; export const EBIKE_ACCELERATION_DURATION_MS = 2000;
+5 -1
View File
@@ -363,7 +363,11 @@ export function PlayerController({
} }
_wishDir.set(0, 0, 0); _wishDir.set(0, 0, 0);
if (!isEbikeBreakdown) { // Block drive input only when still on the bike during breakdown.
// Once auto-dismounted (movementMode === "walk"), the player must
// remain free to walk around even though ebikeBreakdownActive is true.
const blockDriveInput = isEbikeMounted && isEbikeBreakdown;
if (!blockDriveInput) {
if (keys.current.forward) _wishDir.add(_forward); if (keys.current.forward) _wishDir.add(_forward);
if (keys.current.backward) _wishDir.sub(_forward); if (keys.current.backward) _wishDir.sub(_forward);
if (!isEbikeMounted) { if (!isEbikeMounted) {