Загрузка данных


<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Apartment V5</title>

<style>
body{margin:0;overflow:hidden;background:black;font-family:monospace;}
#menu{
position:absolute;width:100%;height:100%;
display:flex;flex-direction:column;
justify-content:center;align-items:center;
background:black;color:white;z-index:10;
}
button{padding:12px 18px;font-weight:bold;cursor:pointer;}
#hud{position:absolute;top:10px;left:10px;color:white;z-index:5;}
#inv{position:absolute;bottom:10px;left:10px;color:white;z-index:5;}
#vhs{
position:absolute;width:100%;height:100%;
pointer-events:none;
background:repeating-linear-gradient(0deg,rgba(255,255,255,0.02),rgba(0,0,0,0.02)2px);
animation:noise .1s infinite;
mix-blend-mode:overlay;
}
@keyframes noise{
0%{transform:translate(0,0)}
50%{transform:translate(1px,-1px)}
100%{transform:translate(0,0)}
}
</style>
</head>

<body>

<div id="menu">
<h1>THE APARTMENT SIGNAL</h1>
<p>Оно слушает тебя</p>
<button onclick="start()">START</button>
</div>

<div id="hud">
<button onclick="toggleFlashlight()">Flashlight (F)</button>
</div>

<div id="inv">Inventory: []</div>
<div id="vhs"></div>

<script src="https://cdn.jsdelivr.net/npm/three@0.158.0/build/three.min.js"></script>

<script>

//////////////////////////////////////////////////////
// CORE
//////////////////////////////////////////////////////

let scene,camera,renderer;
let flashlight;
let keys={};
let inventory=[];
let objects=[];

let creature;
let clonePlayer;

let flashlightOn=true;

let audioCtx;
let heartbeatOsc;
let ambientGain;

//////////////////////////////////////////////////////
// AUDIO (страшный музон)
//////////////////////////////////////////////////////

function initAudio(){

audioCtx = new (window.AudioContext || window.webkitAudioContext)();

// AMBIENT DRONE
let osc = audioCtx.createOscillator();
osc.type="sine";
osc.frequency.value=40;

ambientGain = audioCtx.createGain();
ambientGain.gain.value=0.05;

osc.connect(ambientGain);
ambientGain.connect(audioCtx.destination);
osc.start();

// HEART BEAT
heartbeatOsc = audioCtx.createOscillator();
heartbeatOsc.type="square";
heartbeatOsc.frequency.value=1;

let beatGain = audioCtx.createGain();
beatGain.gain.value=0;

heartbeatOsc.connect(beatGain);
beatGain.connect(audioCtx.destination);
heartbeatOsc.start();

setInterval(()=>{
beatGain.gain.value = 0.2;
setTimeout(()=>beatGain.gain.value=0,100);
}, 2000);
}

//////////////////////////////////////////////////////
// START
//////////////////////////////////////////////////////

function start(){
document.getElementById("menu").style.display="none";
init();
initAudio();
animate();

setInterval(horrorEvents, 5000);
setInterval(ai, 50);
}

//////////////////////////////////////////////////////
// WORLD
//////////////////////////////////////////////////////

function init(){

scene=new THREE.Scene();
scene.fog=new THREE.Fog(0x000000,1,30);

camera=new THREE.PerspectiveCamera(75,innerWidth/innerHeight,0.1,1000);
camera.position.set(0,1.6,5);

renderer=new THREE.WebGLRenderer();
renderer.setSize(innerWidth,innerHeight);
document.body.appendChild(renderer.domElement);

// LIGHT
let light=new THREE.PointLight(0xffffff,1,20);
light.position.set(0,3,0);
scene.add(light);

// FLASHLIGHT
flashlight=new THREE.SpotLight(0xffffff,2,30,0.6);
flashlight.position.copy(camera.position);
scene.add(flashlight);

// FLOOR
scene.add(new THREE.Mesh(
new THREE.BoxGeometry(40,0.1,40),
new THREE.MeshBasicMaterial({color:0x111111})
));

// WALLS
wall(0,-20); wall(0,20);
wall(-20,0,Math.PI/2);
wall(20,0,Math.PI/2);

// OBJECTS
spawnRoomObjects();
spawnCreature();
spawnClonePlayer();

spawnDoor();
spawnKey();

// CONTROLS
document.addEventListener("keydown",e=>{
keys[e.key.toLowerCase()]=true;
if(e.key.toLowerCase()==="f") toggleFlashlight();
});

document.addEventListener("keyup",e=>{
keys[e.key.toLowerCase()]=false;
});

document.addEventListener("mousemove",(e)=>{
if(document.pointerLockElement){
camera.rotation.y -= e.movementX*0.002;
camera.rotation.x -= e.movementY*0.002;
}
});

document.body.addEventListener("click",()=>document.body.requestPointerLock());
}

//////////////////////////////////////////////////////
// ROOM
//////////////////////////////////////////////////////

function wall(x,z,rot){
let w=new THREE.Mesh(
new THREE.BoxGeometry(40,4,0.2),
new THREE.MeshBasicMaterial({color:0x222222})
);
w.position.set(x,2,z);
if(rot) w.rotation.y=rot;
scene.add(w);
}

function spawnRoomObjects(){
for(let i=0;i<40;i++){
let o=new THREE.Mesh(
new THREE.BoxGeometry(0.3,0.3,0.3),
new THREE.MeshBasicMaterial({color:0x444444})
);
o.position.set((Math.random()-0.5)*20,0.3,(Math.random()-0.5)*20);
scene.add(o);
}
}

//////////////////////////////////////////////////////
// ITEMS
//////////////////////////////////////////////////////

function spawnKey(){
let k=new THREE.Mesh(
new THREE.BoxGeometry(0.2,0.2,0.2),
new THREE.MeshBasicMaterial({color:0xffff00})
);
k.position.set(3,1,-3);
k.name="key";
scene.add(k);
objects.push(k);
}

function spawnDoor(){
let d=new THREE.Mesh(
new THREE.BoxGeometry(1,2,0.2),
new THREE.MeshBasicMaterial({color:0x552200})
);
d.position.set(0,1,-19);
d.name="door";
scene.add(d);
objects.push(d);
}

//////////////////////////////////////////////////////
// CREATURE AI (умнее)
//////////////////////////////////////////////////////

function spawnCreature(){
creature=new THREE.Mesh(
new THREE.BoxGeometry(0.6,2,0.6),
new THREE.MeshBasicMaterial({color:0xff0000})
);
creature.position.set(10,1,10);
scene.add(creature);
}

function ai(){

let dir=new THREE.Vector3().subVectors(camera.position,creature.position);
dir.y=0;

let dist=dir.length();
dir.normalize();

// если фонарь включен — он боится
let speed = flashlightOn ? 0.01 : 0.05;

// если близко — резко ускоряется
if(dist<6) speed=0.08;

creature.position.addScaledVector(dir,speed);

// если очень близко — атака
if(dist<1.2){
ending("death");
}
}

//////////////////////////////////////////////////////
// CLONE PLAYER (скример)
//////////////////////////////////////////////////////

function spawnClonePlayer(){

clonePlayer=new THREE.Mesh(
new THREE.BoxGeometry(0.6,1.8,0.6),
new THREE.MeshBasicMaterial({color:0x8800ff})
);

clonePlayer.position.set(-8,1,-8);
scene.add(clonePlayer);
}

function horrorEvents(){

let r=Math.random();

// clone appears behind you
if(r<0.2){
clonePlayer.position.set(
camera.position.x - Math.random()*2,
1,
camera.position.z - Math.random()*2
);
}

// doors “breathing”
if(r>0.7){
flash();
}
}

//////////////////////////////////////////////////////
// FLASH
//////////////////////////////////////////////////////

function flash(){
let d=document.createElement("div");
d.style="position:absolute;top:0;left:0;width:100%;height:100%;background:white;z-index:999";
document.body.appendChild(d);
setTimeout(()=>d.remove(),80);
}

//////////////////////////////////////////////////////
// FLASHLIGHT
//////////////////////////////////////////////////////

function toggleFlashlight(){
flashlightOn=!flashlightOn;
}

//////////////////////////////////////////////////////
// ENDINGS
//////////////////////////////////////////////////////

function ending(type){

if(type==="death"){
alert("ENDING: YOU ARE PART OF THE SIGNAL");
location.reload();
}

if(type==="escape"){
alert("ENDING: YOU LEFT... BUT IT DIDN'T");
location.reload();
}
}

//////////////////////////////////////////////////////
// LOOP
//////////////////////////////////////////////////////

function animate(){

requestAnimationFrame(animate);

// movement
if(keys["w"]) camera.position.z -= 0.12;
if(keys["s"]) camera.position.z += 0.12;
if(keys["a"]) camera.position.x -= 0.12;
if(keys["d"]) camera.position.x += 0.12;

// flashlight follows
flashlight.position.copy(camera.position);

renderer.render(scene,camera);
}

</script>

</body>
</html>