fix(types): satisfy strict tsc for production build (deploy unblock)
🔍 Lint / 🪄 Check lint (push) Has been cancelled
🔍 Lint / 🎨 Check format (push) Has been cancelled
🔍 Lint / 🔎 Typecheck (push) Has been cancelled
📊 Quality / 🔒 Security Audit (push) Has been cancelled
📊 Quality / 📋 Dependency Freshness (push) Has been cancelled
📊 Quality / 📦 Bundle Size (push) Has been cancelled
🔍 Lint / 🏗 Build (push) Has been cancelled

This commit is contained in:
Tom Boullay
2026-06-03 02:40:54 +02:00
parent e9808f8473
commit d8b916d31f
12 changed files with 56 additions and 51 deletions
+4 -1
View File
@@ -181,9 +181,12 @@ export const EbikeGPSMap: React.FC<EbikeGPSMapProps> = ({
// Sync texture into uniform when it changes (canvas resize)
useEffect(() => {
const mapUniform = shaderMat.uniforms.map;
if (!mapUniform) return;
// External Three.js material uniform sync — intentional side effect.
// eslint-disable-next-line react-hooks/immutability
shaderMat.uniforms.map.value = texture;
mapUniform.value = texture;
}, [shaderMat, texture]);
// Cleanup on unmount
@@ -146,16 +146,6 @@ export function EbikeIntroSequence(): React.JSX.Element | null {
return null;
}
if (mainState == "pylon") {
if (pylonStep === "approaching") {
return <MissionNotification mission="pylon" visible />;
}
if (pylonStep === "narrator-outro") {
return <MissionNotification mission="farm" visible />;
}
return null;
}
if (
introStep !== "reveal" &&
introStep !== "await-ebike-mount" &&
@@ -3,7 +3,8 @@ import { useGameStore } from "@/managers/stores/useGameStore";
import { useSubtitleStore } from "@/managers/stores/useSubtitleStore";
import { AudioManager } from "@/managers/AudioManager";
const HISTOIRE_AUDIO_PATH = "/sounds/dialogue/narrateur_histoireelectricienne.mp3";
const HISTOIRE_AUDIO_PATH =
"/sounds/dialogue/narrateur_histoireelectricienne.mp3";
const OUTRO_DELAY_MS = 5_000; // delay after audio ends before transitioning to outro
/**
@@ -78,9 +79,12 @@ function useHistoireSubtitlePlayback(
({ start, end }) => t >= start && t < end,
);
if (idx >= 0) {
const text = HISTOIRE_BLOCKS[idx];
if (text === undefined) return;
setActiveSubtitle({
speaker: "Narrateur",
text: HISTOIRE_BLOCKS[idx],
text,
});
}
}
@@ -136,7 +140,9 @@ export function FarmNarrativeFlow(): null {
// After the audio finishes, wait 5 s then transition to outro.
// The timeout ID is kept in a ref so we can cancel on unmount.
const outroTimeoutRef = useRef<ReturnType<typeof window.setTimeout> | null>(null);
const outroTimeoutRef = useRef<ReturnType<typeof window.setTimeout> | null>(
null,
);
useEffect(() => {
return () => {
@@ -33,7 +33,9 @@ export function PylonDownedPylon(): React.JSX.Element | null {
);
// Snap to terrain so the downed/upright model sits flush on the ground,
// matching the Y adjustment that InstancedMapAsset applies to the same node.
const position = useTerrainSnappedPosition(pylonAnchor ?? PYLON_WORLD_POSITION);
const position = useTerrainSnappedPosition(
pylonAnchor ?? PYLON_WORLD_POSITION,
);
const [isStraightening, setIsStraightening] = useState(false);
// Keeps the pylon upright after the animation completes while
// PylonFarmerNPC plays the post-raise audio sequence.
@@ -63,7 +65,9 @@ export function PylonDownedPylon(): React.JSX.Element | null {
if (!group) return;
if (!isStraightening || straightenStartRef.current === null) {
group.rotation.set(...(isRaised ? PYLON_UPRIGHT_ROTATION : PYLON_DOWNED_ROTATION));
group.rotation.set(
...(isRaised ? PYLON_UPRIGHT_ROTATION : PYLON_DOWNED_ROTATION),
);
return;
}
@@ -104,11 +108,7 @@ export function PylonDownedPylon(): React.JSX.Element | null {
if (!shouldRender) return null;
return (
<group
ref={groupRef}
position={position}
rotation={PYLON_DOWNED_ROTATION}
>
<group ref={groupRef} position={position} rotation={PYLON_DOWNED_ROTATION}>
<primitive object={scene.clone(true)} />
{isPylonInteractive ? (
<InteractableObject
@@ -159,7 +159,9 @@ function PylonFarmerNPCContent(): React.JSX.Element {
} else if (step === "done") {
// NPC reappears at repair completion — position at the post-raise spot,
// facing the pylon, playing idle.
currentPosRef.current.set(...PYLON_FARMER_NPC_AFTER_POSITION_pylone_straight);
currentPosRef.current.set(
...PYLON_FARMER_NPC_AFTER_POSITION_pylone_straight,
);
savedRotationYRef.current = faceToward(
currentPosRef.current,
PYLON_WORLD_POSITION,
@@ -28,11 +28,9 @@ export function PylonNarrativeFlow(): React.JSX.Element | null {
void (async () => {
// 1. Play the generator powerdown sound effect
const sfx = AudioManager.getInstance().playSound(
PYLON_POWERDOWN_SFX,
1,
{ category: "sfx" },
);
const sfx = AudioManager.getInstance().playSound(PYLON_POWERDOWN_SFX, 1, {
category: "sfx",
});
// 2. Wait for it to finish (or skip if it can't load)
if (sfx) {
-1
View File
@@ -15,7 +15,6 @@ import {
} from "@/utils/dialogues/playDialogue";
const TYPEWRITER_CHAR_DELAY_MS = 150;
const TYPEWRITER_START_DELAY_MS = 12000;
// Fallback in case nothing else triggers the typewriter (audio failed to
// load, no subtitles, "ended" never fires). Long enough not to fire
// before the narration on a slow load.
+4 -1
View File
@@ -16,7 +16,10 @@ export function OutroVideoOverlay(): React.JSX.Element | null {
setVisible(true);
}
window.addEventListener("outro-cinematic-complete", handleCinematicComplete);
window.addEventListener(
"outro-cinematic-complete",
handleCinematicComplete,
);
return () => {
window.removeEventListener(
"outro-cinematic-complete",
+3 -5
View File
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from "react";
import { useEffect, useRef } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import * as THREE from "three";
import { isDebugEnabled } from "@/utils/debug/isDebugEnabled";
@@ -50,11 +50,10 @@ export function ZoneDetection({
zone,
onEnter,
height,
}: ZoneDetectionProps): React.JSX.Element {
}: ZoneDetectionProps): React.JSX.Element | null {
const camera = useThree((state) => state.camera);
const hasTriggeredRef = useRef(false);
const onEnterRef = useRef(onEnter);
const [isActive, setIsActive] = useState(false);
useEffect(() => {
onEnterRef.current = onEnter;
@@ -75,9 +74,8 @@ export function ZoneDetection({
if (_cameraPos.y > zone.position[1] + zoneHeight / 2) return;
hasTriggeredRef.current = true;
setIsActive(true);
onEnterRef.current();
});
return <ZoneDebugVisual zone={zone} active={isActive} />;
return null;
}