Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4691a9fbf4 | |||
| 0fca4db440 | |||
| 930139d1df | |||
| 4120e5ec72 | |||
| fd314cf2ec | |||
| e3ab974988 | |||
| e4a0c57e79 | |||
| ebda4b281e | |||
| 8dcc888d5c | |||
| b7e61b4240 | |||
| 23c39a71a6 | |||
| a0ee50703c |
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -8,7 +8,7 @@ import { Suspense, useEffect, useState } from "react";
|
|||||||
|
|
||||||
import { AmbientSound } from './scene-components/ambient-sound';
|
import { AmbientSound } from './scene-components/ambient-sound';
|
||||||
|
|
||||||
import { fearState } from './state';
|
import { FEAR_SETTINGS, fearState } from './state';
|
||||||
|
|
||||||
import TheCreature from './scene-components/creature';
|
import TheCreature from './scene-components/creature';
|
||||||
import Player from './scene-components/player';
|
import Player from './scene-components/player';
|
||||||
@@ -28,7 +28,7 @@ function PostProcessing() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (<EffectComposer>
|
return (<EffectComposer>
|
||||||
<Pixelation granularity={wasCaught ? 18 : 12} />
|
<Pixelation granularity={wasCaught ? 18 : 10} />
|
||||||
<Vignette />
|
<Vignette />
|
||||||
<Noise opacity={wasCaught ? 0.01 : 0.005} />
|
<Noise opacity={wasCaught ? 0.01 : 0.005} />
|
||||||
<BrightnessContrast
|
<BrightnessContrast
|
||||||
@@ -78,7 +78,7 @@ export default function Fear() {
|
|||||||
shadows
|
shadows
|
||||||
gl={{ antialias: true }}
|
gl={{ antialias: true }}
|
||||||
className='canvas'
|
className='canvas'
|
||||||
camera={{ position: [0, 3, -5], fov: 65, far: 100 }}
|
camera={{ position: [0, 3, -5], fov: 55, far: 100 }}
|
||||||
>
|
>
|
||||||
<FearStateUpdater />
|
<FearStateUpdater />
|
||||||
|
|
||||||
@@ -86,9 +86,9 @@ export default function Fear() {
|
|||||||
|
|
||||||
<color attach="background" args={['#050505']} />
|
<color attach="background" args={['#050505']} />
|
||||||
|
|
||||||
<ambientLight intensity={0.0225} />
|
{FEAR_SETTINGS.TEST_MODE ? <ambientLight intensity={2} /> : <ambientLight intensity={0.0225} />}
|
||||||
<fogExp2 attach='fog' args={[0x050505, 0.035]} />
|
{FEAR_SETTINGS.TEST_MODE ? null : <fogExp2 attach='fog' args={[0x050505, 0.035]} />}
|
||||||
<PostProcessing />
|
{FEAR_SETTINGS.TEST_MODE ? null : < PostProcessing />}
|
||||||
|
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<Hallway />
|
<Hallway />
|
||||||
@@ -99,7 +99,7 @@ export default function Fear() {
|
|||||||
<AmbientSound
|
<AmbientSound
|
||||||
key="ambient-1"
|
key="ambient-1"
|
||||||
url='fear/snd/ambience.mp3'
|
url='fear/snd/ambience.mp3'
|
||||||
volume={isRustActive ? 0 : 1}
|
volume={isRustActive ? 0 : 0.5}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AmbientSound
|
<AmbientSound
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ export default function TheCreature() {
|
|||||||
camera.position.y += (Math.random() - 0.5) * shakeIntensity;
|
camera.position.y += (Math.random() - 0.5) * shakeIntensity;
|
||||||
|
|
||||||
if (globalDistance.current <= 0.1) {
|
if (globalDistance.current <= 0.1) {
|
||||||
window.location.href = '/';
|
|
||||||
fearState.registerCaught();
|
fearState.registerCaught();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,22 +19,54 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
grid-auto-rows: 2.5vh;
|
/* filter: invert(100%); */
|
||||||
|
backdrop-filter: brightness(100%);
|
||||||
|
|
||||||
|
grid-auto-rows: 5vh;
|
||||||
/* grid-template-columns: 0; */
|
/* grid-template-columns: 0; */
|
||||||
grid-template-rows: repeat(auto-fit, max-content);
|
grid-template-rows: repeat(auto-fit, max-content);
|
||||||
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
|
will-change: filter;
|
||||||
|
animation: invertFlicker 0.07s infinite alternate;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes invertFlicker {
|
||||||
|
|
||||||
|
0%,
|
||||||
|
43%,
|
||||||
|
45%,
|
||||||
|
88%,
|
||||||
|
92% {
|
||||||
|
filter: invert(0%) contrast(100%) brightness(100%);
|
||||||
|
backdrop-filter: brightness(100%) hue-rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
44%,
|
||||||
|
46%,
|
||||||
|
89%,
|
||||||
|
93%,
|
||||||
|
100% {
|
||||||
|
filter: invert(100%) contrast(300%) brightness(150%);
|
||||||
|
backdrop-filter: brightness(30%) hue-rotate(180deg) saturate(500%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.finale-text {
|
.finale-text {
|
||||||
font-family: 'VCR', sans-serif;
|
font-family: 'VCR', sans-serif;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
|
||||||
height: 0px;
|
height: 0px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: rgb(255, 255, 255);
|
color: rgb(255, 255, 255);
|
||||||
font-size: 5vh;
|
font-size: 8vh;
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.scanlines {
|
.scanlines {
|
||||||
|
|||||||
@@ -1,36 +1,56 @@
|
|||||||
import { JSX, useEffect, useState } from "react"
|
import { JSX, useEffect, useState } from "react"
|
||||||
import { FEAR_SETTINGS, fearState } from "../state"
|
import { fearState } from "../state"
|
||||||
|
|
||||||
import './finale-text.css';
|
import './finale-text.css';
|
||||||
|
|
||||||
|
const BLOCKS = [
|
||||||
|
"▀", "▂", "▃", "▄", "▅", "▆", "▇",
|
||||||
|
"█", "▉", "▊", "▋", "▌", "▍", "▎", "▏",
|
||||||
|
"▐", "░", "▒", "▓", "▔", "▕", "▖", "▗",
|
||||||
|
"▘", "▙", "▚", "▛", "▜", "▝", "▞", "▟"
|
||||||
|
];
|
||||||
|
|
||||||
export default function FinaleText() {
|
export default function FinaleText() {
|
||||||
const [progression, setProgression] = useState(fearState.finaleProgression);
|
const [wasCaught, setWasCaught] = useState(fearState.wasCaught);
|
||||||
const [wasCaught, setWasCaught] = useState(fearState.isRustActive);
|
const [elements, setElements] = useState<JSX.Element[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubscribe = fearState.subscribe(() => {
|
const unsubscribe = fearState.subscribe(() => {
|
||||||
setProgression(fearState.finaleProgression);
|
|
||||||
setWasCaught(fearState.wasCaught)
|
setWasCaught(fearState.wasCaught)
|
||||||
});
|
});
|
||||||
return () => unsubscribe();
|
return () => unsubscribe();
|
||||||
});
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
let elementCount = (FEAR_SETTINGS.EVENT_FINALE_TEXT_COUNT / FEAR_SETTINGS.EVENT_FINALE_DURATION) * progression;
|
useEffect(() => {
|
||||||
|
if (!wasCaught)
|
||||||
|
return;
|
||||||
|
|
||||||
let testElements: Array<JSX.Element> = [];
|
const interval = setInterval(() => {
|
||||||
|
if (Math.random() > 0.9) return;
|
||||||
|
|
||||||
for (let x = 0; x < elementCount; x++)
|
const baseText = "the deal has been sealed";
|
||||||
testElements.push(<span className="finale-text" key={x}>the deal has been sealed</span>)
|
const corrupted = baseText
|
||||||
|
.split("")
|
||||||
|
.map((char) => (Math.random() > 0.98 ? BLOCKS[Math.floor(Math.random() * BLOCKS.length)] : char))
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
setElements((prev) => [...prev.slice(-30),
|
||||||
|
<span className="finale-text" key={crypto.randomUUID()}>
|
||||||
|
{corrupted}
|
||||||
|
</span>
|
||||||
|
]);
|
||||||
|
}, 10);
|
||||||
|
|
||||||
if (wasCaught)
|
return () => clearInterval(interval);
|
||||||
return (<>
|
}, [wasCaught]);
|
||||||
<div className="finale-container">
|
|
||||||
{testElements}
|
|
||||||
</div>
|
|
||||||
<div className="scanlines" />
|
|
||||||
</>)
|
|
||||||
|
|
||||||
return <></>
|
if (!wasCaught) return null;
|
||||||
|
|
||||||
|
return (<>
|
||||||
|
<div className="finale-container">
|
||||||
|
{elements}
|
||||||
|
</div>
|
||||||
|
<div className="scanlines" />
|
||||||
|
</>)
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,64 @@ import { useTexture, PositionalAudio } from "@react-three/drei";
|
|||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useFrame } from "@react-three/fiber";
|
import { useFrame } from "@react-three/fiber";
|
||||||
|
|
||||||
|
function ShaderPatch(shader: { vertexShader: string, fragmentShader: string, uniforms: Object }) {
|
||||||
|
shader.vertexShader = `
|
||||||
|
varying float vDepth;
|
||||||
|
#ifdef USE_MAP
|
||||||
|
varying vec2 vAffineUv;
|
||||||
|
#endif
|
||||||
|
${shader.vertexShader}
|
||||||
|
`;
|
||||||
|
|
||||||
|
shader.vertexShader = shader.vertexShader.replace(
|
||||||
|
`#include <project_vertex>`,
|
||||||
|
`
|
||||||
|
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
|
||||||
|
gl_Position = projectionMatrix * mvPosition;
|
||||||
|
|
||||||
|
float precisionModifier = 200.0;
|
||||||
|
gl_Position.xy /= gl_Position.w;
|
||||||
|
gl_Position.xy = floor(gl_Position.xy * precisionModifier) / precisionModifier;
|
||||||
|
gl_Position.xy *= gl_Position.w;
|
||||||
|
|
||||||
|
vDepth = gl_Position.w;
|
||||||
|
|
||||||
|
#ifdef USE_MAP
|
||||||
|
vAffineUv = vMapUv * gl_Position.w;
|
||||||
|
#endif
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
shader.fragmentShader = `
|
||||||
|
varying float vDepth;
|
||||||
|
#ifdef USE_MAP
|
||||||
|
varying vec2 vAffineUv;
|
||||||
|
#endif
|
||||||
|
${shader.fragmentShader}
|
||||||
|
`;
|
||||||
|
|
||||||
|
shader.fragmentShader = shader.fragmentShader.replace(
|
||||||
|
`#include <map_fragment>`,
|
||||||
|
`
|
||||||
|
#ifdef USE_MAP
|
||||||
|
vec2 flatAffineUV = vAffineUv / max(vDepth, 0.001);
|
||||||
|
|
||||||
|
vec2 warpDiff = flatAffineUV - vMapUv;
|
||||||
|
float warpDist = length(warpDiff);
|
||||||
|
|
||||||
|
float maxDistortion = 0.25;
|
||||||
|
|
||||||
|
float falloff = maxDistortion / (maxDistortion + warpDist);
|
||||||
|
|
||||||
|
vec2 finalUV = vMapUv + (warpDiff * falloff);
|
||||||
|
|
||||||
|
vec4 texelColor = texture2D( map, finalUV );
|
||||||
|
diffuseColor *= texelColor;
|
||||||
|
#endif
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
interface DoorProps {
|
interface DoorProps {
|
||||||
position: [number, number, number];
|
position: [number, number, number];
|
||||||
rotation: [number, number, number];
|
rotation: [number, number, number];
|
||||||
@@ -12,6 +70,7 @@ interface DoorProps {
|
|||||||
function Door({ position, rotation }: DoorProps) {
|
function Door({ position, rotation }: DoorProps) {
|
||||||
const [soundUrl, setSoundUrl] = useState<string | null>(null);
|
const [soundUrl, setSoundUrl] = useState<string | null>(null);
|
||||||
const currentSound = useRef<string | null>(null);
|
const currentSound = useRef<string | null>(null);
|
||||||
|
const steelTex = useTexture('fear/img/steel.png');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
@@ -33,19 +92,22 @@ function Door({ position, rotation }: DoorProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<group position={position} rotation={rotation}>
|
<group position={position} rotation={rotation}>
|
||||||
<mesh position={[0, 2, -0.14]}>
|
{/* frame */}
|
||||||
<boxGeometry args={[2.4, 4.0, 0.2]} />
|
<mesh position={[0, 2, -0.1]}>
|
||||||
<meshStandardMaterial color="#8a8585" roughness={0.8} metalness={0.2} />
|
<boxGeometry args={[2.4, 4.0, 0.2, 4, 4, 1]} />
|
||||||
|
<meshStandardMaterial map={steelTex} color="#8d8d8d" onBeforeCompile={ShaderPatch} />
|
||||||
</mesh>
|
</mesh>
|
||||||
|
|
||||||
<mesh position={[0, 1.95, -0.08]}>
|
{/* panel */}
|
||||||
<boxGeometry args={[2.1, 3.8, 0.1]} />
|
<mesh position={[0, 1.95, -0.0]}>
|
||||||
<meshStandardMaterial color="#4e4b4b" roughness={0.7} metalness={0.2} />
|
<boxGeometry args={[2.1, 3.8, 0.1, 4, 4, 1]} />
|
||||||
|
<meshStandardMaterial map={steelTex} color="#4e4a4a" onBeforeCompile={ShaderPatch} />
|
||||||
</mesh>
|
</mesh>
|
||||||
|
|
||||||
<mesh position={[0.9, 1.8, 0.08]}>
|
{/* handle */}
|
||||||
<boxGeometry args={[0.08, 0.08, 0.15]} />
|
<mesh position={[0.75, 1.8, .085]}>
|
||||||
<meshStandardMaterial color="#4e4b4b" roughness={0.4} metalness={0.2} />
|
<boxGeometry args={[0.3, 0.08, 0.1]} />
|
||||||
|
<meshStandardMaterial map={steelTex} color="#ffffff" onBeforeCompile={ShaderPatch} />
|
||||||
</mesh>
|
</mesh>
|
||||||
|
|
||||||
{soundUrl && (
|
{soundUrl && (
|
||||||
@@ -283,6 +345,7 @@ export default function Hallway() {
|
|||||||
emissive="#a8a1a1"
|
emissive="#a8a1a1"
|
||||||
emissiveIntensity={0.8}
|
emissiveIntensity={0.8}
|
||||||
roughness={0.9}
|
roughness={0.9}
|
||||||
|
onBeforeCompile={ShaderPatch}
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
</group>
|
</group>
|
||||||
@@ -293,12 +356,11 @@ export default function Hallway() {
|
|||||||
rotation={[-Math.PI / 2, 0, 0]}
|
rotation={[-Math.PI / 2, 0, 0]}
|
||||||
position={[0, 0, -FEAR_SETTINGS.HALLWAY_LENGTH / 2]}
|
position={[0, 0, -FEAR_SETTINGS.HALLWAY_LENGTH / 2]}
|
||||||
>
|
>
|
||||||
<planeGeometry args={[FEAR_SETTINGS.HALLWAY_WIDTH, FEAR_SETTINGS.HALLWAY_LENGTH]} />
|
<planeGeometry args={[FEAR_SETTINGS.HALLWAY_WIDTH, FEAR_SETTINGS.HALLWAY_LENGTH, 4, 10]} />
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
ref={(el) => { if (el) floorMaterialsRef.current.push(el); }}
|
ref={(el) => { if (el) floorMaterialsRef.current.push(el); }}
|
||||||
map={floorTex}
|
map={floorTex}
|
||||||
roughness={0.8}
|
onBeforeCompile={ShaderPatch}
|
||||||
metalness={0.2}
|
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
|
|
||||||
@@ -308,24 +370,22 @@ export default function Hallway() {
|
|||||||
rotation={[Math.PI / 2, 0, 0]}
|
rotation={[Math.PI / 2, 0, 0]}
|
||||||
position={[0, FEAR_SETTINGS.HALLWAY_HEIGHT, -FEAR_SETTINGS.HALLWAY_LENGTH / 2]}
|
position={[0, FEAR_SETTINGS.HALLWAY_HEIGHT, -FEAR_SETTINGS.HALLWAY_LENGTH / 2]}
|
||||||
>
|
>
|
||||||
<planeGeometry args={[FEAR_SETTINGS.HALLWAY_WIDTH, FEAR_SETTINGS.HALLWAY_LENGTH]} />
|
<planeGeometry args={[FEAR_SETTINGS.HALLWAY_WIDTH, FEAR_SETTINGS.HALLWAY_LENGTH, 4, 10]} />
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
ref={(el) => { if (el) floorMaterialsRef.current.push(el); }}
|
ref={(el) => { if (el) floorMaterialsRef.current.push(el); }}
|
||||||
map={floorTex}
|
map={floorTex}
|
||||||
roughness={0.8}
|
onBeforeCompile={ShaderPatch}
|
||||||
metalness={0.2}
|
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
|
|
||||||
{/* left wall */}
|
{/* left wall */}
|
||||||
<group name="left-wall-group">
|
<group name="left-wall-group">
|
||||||
<mesh rotation={[0, Math.PI / 2, 0]} position={[0, FEAR_SETTINGS.HALLWAY_HEIGHT / 2, -FEAR_SETTINGS.HALLWAY_LENGTH / 2]}>
|
<mesh rotation={[0, Math.PI / 2, 0]} position={[0, FEAR_SETTINGS.HALLWAY_HEIGHT / 2, -FEAR_SETTINGS.HALLWAY_LENGTH / 2]}>
|
||||||
<planeGeometry args={[FEAR_SETTINGS.HALLWAY_LENGTH, FEAR_SETTINGS.HALLWAY_HEIGHT]} />
|
<planeGeometry args={[FEAR_SETTINGS.HALLWAY_LENGTH, FEAR_SETTINGS.HALLWAY_HEIGHT, 10, 4]} />
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
ref={(el) => { if (el) wallMaterialsRef.current.push(el); }}
|
ref={(el) => { if (el) wallMaterialsRef.current.push(el); }}
|
||||||
map={wallTex}
|
map={wallTex}
|
||||||
roughness={0.7}
|
onBeforeCompile={ShaderPatch}
|
||||||
metalness={0.1}
|
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
{!isRustActive && (
|
{!isRustActive && (
|
||||||
@@ -339,12 +399,11 @@ export default function Hallway() {
|
|||||||
{/* right wall */}
|
{/* right wall */}
|
||||||
<group name="right-wall-group">
|
<group name="right-wall-group">
|
||||||
<mesh rotation={[0, -Math.PI / 2, 0]} position={[0, FEAR_SETTINGS.HALLWAY_HEIGHT / 2, -FEAR_SETTINGS.HALLWAY_LENGTH / 2]}>
|
<mesh rotation={[0, -Math.PI / 2, 0]} position={[0, FEAR_SETTINGS.HALLWAY_HEIGHT / 2, -FEAR_SETTINGS.HALLWAY_LENGTH / 2]}>
|
||||||
<planeGeometry args={[FEAR_SETTINGS.HALLWAY_LENGTH, FEAR_SETTINGS.HALLWAY_HEIGHT]} />
|
<planeGeometry args={[FEAR_SETTINGS.HALLWAY_LENGTH, FEAR_SETTINGS.HALLWAY_HEIGHT, 10, 4]} />
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
ref={(el) => { if (el) wallMaterialsRef.current.push(el); }}
|
ref={(el) => { if (el) wallMaterialsRef.current.push(el); }}
|
||||||
map={wallTex}
|
map={wallTex}
|
||||||
roughness={0.7}
|
onBeforeCompile={ShaderPatch}
|
||||||
metalness={0.1}
|
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
{!isRustActive && (
|
{!isRustActive && (
|
||||||
@@ -366,6 +425,7 @@ export default function Hallway() {
|
|||||||
color="#a5aca8"
|
color="#a5aca8"
|
||||||
roughness={0.0}
|
roughness={0.0}
|
||||||
metalness={0.4}
|
metalness={0.4}
|
||||||
|
onBeforeCompile={ShaderPatch}
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
))}
|
))}
|
||||||
@@ -385,6 +445,7 @@ export default function Hallway() {
|
|||||||
color="#a5aca8"
|
color="#a5aca8"
|
||||||
roughness={0.0}
|
roughness={0.0}
|
||||||
metalness={0.4}
|
metalness={0.4}
|
||||||
|
onBeforeCompile={ShaderPatch}
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -54,10 +54,32 @@ export default function Player() {
|
|||||||
const confirmedSegment = useRef<number>(0);
|
const confirmedSegment = useRef<number>(0);
|
||||||
const hasTriggeredThisSegment = useRef<boolean>(false);
|
const hasTriggeredThisSegment = useRef<boolean>(false);
|
||||||
|
|
||||||
|
const footstepAudio = useRef<HTMLAudioElement[]>([]);
|
||||||
|
const hasStepped = useRef<boolean>(false);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
playerRoot.set(camera.position.x, FEAR_SETTINGS.PLAYER_HEIGHT, camera.position.z);
|
playerRoot.set(camera.position.x, FEAR_SETTINGS.PLAYER_HEIGHT, camera.position.z);
|
||||||
|
footstepAudio.current = Array.from({ length: 6 }, (_, i) => {
|
||||||
|
const audio = new Audio(`fear/snd/footstep${i + 1}.mp3`);
|
||||||
|
audio.volume = 0.4;
|
||||||
|
return audio;
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const playRandomFootstep = () => {
|
||||||
|
if (footstepAudio.current.length === 0) return;
|
||||||
|
|
||||||
|
const randomIndex = Math.floor(Math.random() * footstepAudio.current.length);
|
||||||
|
const audio = footstepAudio.current[randomIndex];
|
||||||
|
|
||||||
|
audio.currentTime = 0;
|
||||||
|
audio.play().catch((err) => {
|
||||||
|
console.warn("Footstep playback blocked by browser autocomplete/interaction rules.", err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
useFrame((state, delta) => {
|
useFrame((state, delta) => {
|
||||||
const dt = Math.min(delta, 0.1);
|
const dt = Math.min(delta, 0.1);
|
||||||
|
|
||||||
@@ -92,9 +114,19 @@ export default function Player() {
|
|||||||
if (isMoving)
|
if (isMoving)
|
||||||
movementCounter.current += dt * 12;
|
movementCounter.current += dt * 12;
|
||||||
|
|
||||||
const moveBobY = Math.sin(movementCounter.current) * 0.06 * bobIntensity.current;
|
const sinWave = Math.sin(movementCounter.current);
|
||||||
|
const moveBobY = sinWave * 0.06 * bobIntensity.current;
|
||||||
const moveBobX = Math.cos(movementCounter.current / 2) * 0.04 * bobIntensity.current;
|
const moveBobX = Math.cos(movementCounter.current / 2) * 0.04 * bobIntensity.current;
|
||||||
|
|
||||||
|
if (isMoving && sinWave < -0.9) {
|
||||||
|
if (!hasStepped.current) {
|
||||||
|
playRandomFootstep();
|
||||||
|
hasStepped.current = true;
|
||||||
|
}
|
||||||
|
} else if (sinWave > 0) {
|
||||||
|
hasStepped.current = false;
|
||||||
|
}
|
||||||
|
|
||||||
const breatheTime = state.clock.elapsedTime * 1.8;
|
const breatheTime = state.clock.elapsedTime * 1.8;
|
||||||
const breatheBobY = Math.sin(breatheTime) * 0.03 * (1 - bobIntensity.current * 0.5);
|
const breatheBobY = Math.sin(breatheTime) * 0.03 * (1 - bobIntensity.current * 0.5);
|
||||||
|
|
||||||
@@ -103,7 +135,7 @@ export default function Player() {
|
|||||||
camera.position.addScaledVector(side, moveBobX);
|
camera.position.addScaledVector(side, moveBobX);
|
||||||
|
|
||||||
if (flashlightRef.current) {
|
if (flashlightRef.current) {
|
||||||
flashlightRef.current.position.copy(camera.position);
|
flashlightRef.current.position.lerp(camera.position, 7 * dt);
|
||||||
camera.getWorldDirection(viewDirection);
|
camera.getWorldDirection(viewDirection);
|
||||||
|
|
||||||
targetDest
|
targetDest
|
||||||
|
|||||||
+11
-4
@@ -14,8 +14,9 @@ export const FEAR_SETTINGS = {
|
|||||||
EVENT_RUST_LOOP_COUNT: 4,
|
EVENT_RUST_LOOP_COUNT: 4,
|
||||||
EVENT_FINALE_LOOP_COUNT: 5,
|
EVENT_FINALE_LOOP_COUNT: 5,
|
||||||
|
|
||||||
EVENT_FINALE_DURATION: 3,
|
EVENT_FINALE_DURATION: 1,
|
||||||
EVENT_FINALE_TEXT_COUNT: 128
|
|
||||||
|
TEST_MODE: false
|
||||||
};
|
};
|
||||||
|
|
||||||
const listeners = new Set<() => void>();
|
const listeners = new Set<() => void>();
|
||||||
@@ -45,8 +46,14 @@ export const fearState = {
|
|||||||
this.currentWidth = newWidth;
|
this.currentWidth = newWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.wasCaught && this.finaleProgression < FEAR_SETTINGS.EVENT_FINALE_DURATION)
|
if (this.wasCaught) {
|
||||||
this.finaleProgression = Math.min(this.finaleProgression + delta, FEAR_SETTINGS.EVENT_FINALE_DURATION);
|
if (this.finaleProgression < FEAR_SETTINGS.EVENT_FINALE_DURATION) {
|
||||||
|
|
||||||
|
this.finaleProgression = Math.min(this.finaleProgression + delta, FEAR_SETTINGS.EVENT_FINALE_DURATION);
|
||||||
|
} else {
|
||||||
|
window.location.href = '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.emit();
|
this.emit();
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user