🎮ArcadeLab

THE LAST WATCH — Night Shift

by LunarPanda23
326 lines22.5 KB
▶ Play

<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<title>THE LAST WATCH — Night Shift</title>
<style>
*{box-sizing:border-box}
:root{color-scheme:dark}
body{margin:0;background:#050708;color:#e5eeee;font-family:Arial,sans-serif}
button{cursor:pointer;font:inherit;color:#e5eeee;touch-action:manipulation}
#game{height:100dvh;min-height:420px;display:flex;flex-direction:column;overflow:hidden;background:#080c0d}
#top{height:58px;flex-shrink:0;background:#0b1011;border-bottom:2px solid #465354;display:flex;justify-content:space-between;align-items:center;padding:6px 12px;direction:ltr}
.small{font-size:10px;letter-spacing:1px;color:#9eaeae}
#clock{font-size:23px;font-weight:bold;margin-top:2px}
#power{font-size:20px;font-weight:bold;text-align:right}
#powerbar{height:5px;width:100px;background:#293233;margin-top:4px}
#powerfill{height:100%;width:100%;background:#b3d1b5}
#view{flex:1;position:relative;min-height:0;overflow:hidden}
canvas{display:block;width:100%;height:100%}
#office{position:absolute;inset:0}
#officeControls{position:absolute;inset:0;pointer-events:none}
.wall{position:absolute;top:34%;width:74px;display:grid;gap:5px;pointer-events:auto}
#leftWall{left:5px}
#rightWall{right:5px}
.wall h4{font-size:9px;letter-spacing:.6px;text-align:center;background:#11191a;border:1px solid #566566;padding:6px 1px;margin:0}
.ctrl{height:43px;background:#182223;border:1px solid #677778;border-radius:3px;font-size:10px;font-weight:bold}
.ctrl.closed{background:#762e2e;border-color:#ed7474}
.ctrl.lit{background:#c6d5b4;color:#101510}
#monitor{position:absolute;bottom:7%;left:50%;transform:translateX(-50%);height:43px;width:min(200px,48%);background:#172324;border:2px solid #778888;font-size:11px;font-weight:bold;pointer-events:auto}
#hint{position:absolute;bottom:1.5%;width:100%;text-align:center;font-size:10px;color:#a9b8b8;text-shadow:0 1px 3px black;pointer-events:none}
#cams{position:absolute;inset:0;display:none;background:#070a0b}
#camTop{position:absolute;top:6px;left:6px;right:6px;display:flex;justify-content:space-between;gap:5px;direction:ltr;pointer-events:none}
.tag{background:#070b0cf0;border:1px solid #637475;padding:6px;font-size:10px}
#camName{font-weight:bold}
#camDesc{color:#aab9b9;font-size:9px;margin-top:3px}
#closeCam{position:absolute;left:7px;top:55px;padding:8px;background:#351a1a;border:1px solid #b25b5b;font-size:10px}
#map{position:absolute;right:6px;top:55px;width:min(152px,39%);background:#080d0ef5;border:1px solid #667576;padding:6px}
#map h3{font-size:10px;letter-spacing:1.5px;margin:0 0 5px}
#mapGrid{display:grid;grid-template-columns:1fr 1fr;gap:4px}
.mapbtn{min-width:0;min-height:32px;background:#172122;border:1px solid #586869;padding:3px;font-size:9px;white-space:pre-line;overflow-wrap:anywhere}
.mapbtn.active{outline:2px solid #f0f4ed}
.mapbtn.danger{background:#351919;border-color:#e56d6d;color:#ffc4c4}
.mapbtn.you{grid-column:1/-1;background:#1b3028;border-color:#8dbca0;color:#d6f6df;font-weight:bold}
#camBottom{position:absolute;bottom:0;left:0;right:0;min-height:59px;background:#111a1b;border-top:2px solid #647576;display:flex;align-items:center;justify-content:space-around;gap:7px;padding:5px;direction:ltr}
.arrow{height:42px;width:45px;background:#243031;border:1px solid #748585;font-size:20px;flex-shrink:0}
#camInfo{text-align:center;min-width:0;flex:1}
#camTitle{font-size:11px;font-weight:bold}
#camDescription{font-size:9px;color:#aab9b9;margin-top:3px}
#camNumber{font-size:9px;color:#829393;margin-top:2px}
#scare{display:none;position:absolute;inset:0;z-index:8;background:#160000}
#scare canvas{width:100%;height:100%}
#overlay{position:absolute;inset:0;z-index:10;background:#000e;display:flex;align-items:center;justify-content:center;padding:18px}
#modal{max-width:430px;width:100%;padding:22px;background:linear-gradient(145deg,#1d292a,#080b0c);border:1px solid #667777;text-align:center}
#modal h1{font-size:clamp(27px,8vw,42px);margin:0;letter-spacing:2px}
#modal p{font-size:13px;line-height:1.8;color:#c1cccc}
#modal button{padding:13px 22px;background:#d7e4de;color:#101615;border:0;font-weight:bold}
@media(orientation:landscape) and (max-height:500px){
 #top{height:42px}.wall{top:23%;width:85px}.ctrl{height:32px}
 #map{top:43px;width:145px}#closeCam{top:43px}
 #camBottom{min-height:47px}.arrow{height:35px}
}
</style>
</head>
<body>
<div id="game">
<header id="top">
 <div><div class="small">THE LAST WATCH / SECURITY</div><div id="clock">12 AM</div><div id="status" class="small">SYSTEM ONLINE</div></div>
 <div><div class="small">NIGHT 01 · POWER</div><div id="power"><span id="powerNum">100</span>%</div><div id="powerbar"><div id="powerfill"></div></div></div>
</header>
<main id="view">
 <canvas id="office"></canvas>
 <div id="officeControls">
  <div class="wall" id="leftWall"><h4>WEST WALL</h4><button id="ld" class="ctrl">CLOSE<br>DOOR</button><button id="ll" class="ctrl">LIGHT OFF</button></div>
  <div class="wall" id="rightWall"><h4>EAST WALL</h4><button id="rd" class="ctrl">CLOSE<br>DOOR</button><button id="rl" class="ctrl">LIGHT OFF</button></div>
  <button id="monitor">▤ OPEN CAMERAS</button>
  <div id="hint">בדוק את החלונות • עקוב אחר המצלמות • שמור על החשמל</div>
 </div>
 <section id="cams">
  <canvas id="feed"></canvas>
  <div id="camTop"><div class="tag"><div id="camName">CAM 01</div><div id="camDesc">LIVE SIGNAL</div></div><div class="tag">NIGHT 01<br><span style="color:#a8c8ad">LIVE FEED</span></div></div>
  <button id="closeCam">✕ CLOSE</button>
  <div id="map"><h3>FACILITY MAP</h3><div id="mapGrid"></div></div>
  <div id="camBottom"><button id="prev" class="arrow">◀</button><div id="camInfo"><div id="camTitle">CAM 01</div><div id="camDescription">MAIN HALL</div><div id="camNumber">1 / 9</div></div><button id="next" class="arrow">▶</button></div>
 </section>
 <section id="scare"><canvas id="scareCanvas"></canvas></section>
 <section id="overlay"><div id="modal"><h1>THE LAST WATCH</h1><hr><p>ארבעה יצורים מסתובבים במבנה.<br>הדליקו אור כדי לבדוק את החלונות.<br>השתמשו במצלמות כדי לעקוב אחריהם.<br>סגרו דלת בזמן, וחסכו בחשמל.<br><b>שרדו עד 6 AM.</b></p><button id="start">START NIGHT</button></div></section>
</main>
</div>
<script>
(()=>{
const $=id=>document.getElementById(id);
const office=$('office'),og=office.getContext('2d'),feed=$('feed'),fg=feed.getContext('2d'),sc=$('scareCanvas'),sg=sc.getContext('2d');
let W=1,H=1,playing=false,camera=false,elapsed=0,power=100,last=0,ci=0,westDoor=false,eastDoor=false,westLight=false,eastLight=false,audio=null,staticLeft=0;
const rooms=[
['SHOWROOM','Main display hall',0],['STORAGE','Storage shelves',1],['WORKSHOP','Repair workshop',2],
['DINING','Dining room',3],['WEST HALL','West corridor',4],['EAST HALL','East corridor',4],
['BACK ROOM','Maintenance room',5],['PARTS ROOM','Parts storage',1],['STAGE','Performance stage',6]
];
const monsters=[
{name:'THE WARDEN',room:0,path:[0,1,4,'W'],side:'W',shape:0,rate:12,move:0,at:0,color:'#aab8ad',eye:'#ff3939'},
{name:'TIN GRIN',room:3,path:[3,7,5,'E'],side:'E',shape:1,rate:17,move:0,at:0,color:'#c7a46d',eye:'#fff29a'},
{name:'THE HOLLOW',room:1,path:[1,2,6,4,'W'],side:'W',shape:2,rate:20,move:0,at:0,color:'#7888bb',eye:'#54f4ff'},
{name:'MOTHER STATIC',room:8,path:[8,6,7,5,'E'],side:'E',shape:3,rate:23,move:0,at:0,color:'#c08aa9',eye:'#ff64c8'}
];
function fit(c,g){let d=Math.min(devicePixelRatio||1,2);c.width=Math.max(1,c.clientWidth*d);c.height=Math.max(1,c.clientHeight*d);g.setTransform(d,0,0,d,0,0)}
function resize(){fit(office,og);fit(feed,fg);fit(sc,sg);W=office.clientWidth;H=office.clientHeight;drawOffice();if(camera)drawFeed()}
window.addEventListener('resize',resize);
function sound(f=180,d=.2,type='sawtooth',vol=.035){
 try{
 audio=audio||new(window.AudioContext||window.webkitAudioContext)();
 if(audio.state==='suspended')audio.resume();
 let o=audio.createOscillator(),g=audio.createGain();o.type=type;o.frequency.setValueAtTime(f,audio.currentTime);
 g.gain.setValueAtTime(vol,audio.currentTime);g.gain.exponentialRampToValueAtTime(.001,audio.currentTime+d);
 o.connect(g);g.connect(audio.destination);o.start();o.stop(audio.currentTime+d);
 }catch(e){}
}
function rect(g,x,y,w,h,c){g.fillStyle=c;g.fillRect(x,y,w,h)}
function text(g,t,x,y,size,c='#dce6e5',align='left'){g.fillStyle=c;g.font=`bold ${size}px Arial`;g.textAlign=align;g.fillText(t,x,y)}
function polygon(g,pts,c){g.fillStyle=c;g.beginPath();g.moveTo(pts[0][0],pts[0][1]);for(let i=1;i<pts.length;i++)g.lineTo(pts[i][0],pts[i][1]);g.closePath();g.fill()}

/* Four original silhouettes with different anatomy and faces. */
function drawMonster(g,x,y,s,m){
 g.save();g.translate(x,y);g.scale(s,s);
 g.fillStyle='#000b';g.beginPath();g.ellipse(0,10,42,11,0,0,7);g.fill();
 if(m.shape===0){
  // Tall angular security warden, pointed crown and broad shoulders
  polygon(g,[[-24,47],[-27,-21],[-39,-39],[-19,-34],[-8,-52],[0,-34],[15,-49],[20,-31],[36,-39],[27,-17],[25,47]],'#080a0b');
  rect(g,-22,-33,44,30,'#7b8981');rect(g,-14,-26,9,6,m.eye);rect(g,5,-26,9,6,m.eye);
  rect(g,-13,-13,26,5,'#111');for(let i=0;i<5;i++)rect(g,-10+i*5,-13,2,4,'#ddd8c5');
  rect(g,-31,0,7,34,'#080a0b');rect(g,24,0,7,34,'#080a0b');
 }else if(m.shape===1){
  // Round brass head, huge jaw and hanging mechanical arms
  g.fillStyle='#090a0a';g.beginPath();g.ellipse(0,-18,31,31,0,0,7);g.fill();
  g.fillStyle=m.color;g.beginPath();g.ellipse(0,-20,24,25,0,0,7);g.fill();
  g.fillStyle='#101111';g.beginPath();g.ellipse(0,-5,26,14,0,0,7);g.fill();
  g.fillStyle=m.eye;g.beginPath();g.ellipse(-11,-24,7,10,-.2,0,7);g.ellipse(11,-24,7,10,.2,0,7);g.fill();
  rect(g,-17,-7,34,5,'#e7dfc8');for(let i=0;i<7;i++)rect(g,-15+i*5,-7,2,7,'#131515');
  polygon(g,[[-25,5],[-20,29],[-12,29],[-14,12],[14,12],[12,29],[21,29],[26,5]],'#090b0b');
  rect(g,-34,0,8,38,'#090b0b');rect(g,26,0,8,38,'#090b0b');
 }else if(m.shape===2){
  // Thin, elongated hollow figure with long fingers and narrow mask
  polygon(g,[[-12,43],[-10,-28],[-18,-43],[-5,-36],[0,-58],[7,-35],[19,-45],[12,-24],[14,43]],'#090b0b');
  polygon(g,[[-13,-34],[-8,-46],[8,-46],[13,-31],[8,-10],[-8,-10]],'#526681');
  rect(g,-9,-29,6,4,m.eye);rect(g,4,-29,6,4,m.eye);
  rect(g,-7,-19,14,3,'#070909');
  g.strokeStyle='#090b0b';g.lineWidth=5;g.beginPath();g.moveTo(-11,-1);g.lineTo(-25,22);g.lineTo(-29,36);g.moveTo(11,-1);g.lineTo(25,21);g.lineTo(30,36);g.stroke();
  g.strokeStyle=m.color;g.lineWidth=2;g.beginPath();g.moveTo(0,-8);g.lineTo(-3,24);g.stroke();
 }else{
  // Static moth-like head, split mask and curved antennae
  g.strokeStyle=m.color;g.lineWidth=4;g.beginPath();g.moveTo(-10,-36);g.quadraticCurveTo(-37,-61,-34,-32);g.moveTo(10,-36);g.quadraticCurveTo(37,-61,34,-32);g.stroke();
  polygon(g,[[-27,44],[-29,-20],[-21,-38],[-5,-29],[0,-45],[7,-29],[23,-39],[29,-17],[26,44]],'#0b090c');
  polygon(g,[[-20,-32],[-4,-38],[-2,-11],[-20,-8]],'#76516a');
  polygon(g,[[4,-38],[20,-32],[20,-8],[2,-11]],'#9b637e');
  rect(g,-15,-27,8,5,m.eye);rect(g,7,-27,8,5,m.eye);
  g.strokeStyle='#1c1018';g.lineWidth=3;g.beginPath();g.moveTo(0,-35);g.lineTo(0,1);g.stroke();
  polygon(g,[[-20,3],[-12,15],[-4,3],[4,15],[12,3],[20,14],[17,30],[-17,30]],'#08090a');
 }
 // chipped surface marks, unique not just color swaps
 g.strokeStyle=m.color;g.globalAlpha=.75;g.lineWidth=2;
 if(m.shape===0){g.beginPath();g.moveTo(-19,-6);g.lineTo(-10,0);g.lineTo(-15,8);g.stroke()}
 if(m.shape===1){g.beginPath();g.arc(0,-19,19,.2,2.6);g.stroke()}
 if(m.shape===2){g.beginPath();g.moveTo(-4,-8);g.lineTo(3,2);g.lineTo(-2,11);g.stroke()}
 if(m.shape===3){g.beginPath();g.moveTo(-19,-16);g.lineTo(-5,-4);g.lineTo(-15,9);g.moveTo(18,-15);g.lineTo(5,-4);g.lineTo(15,9);g.stroke()}
 g.restore();
}
function drawDoor(g,x,y,w,h,closed,light){
 rect(g,x-4,y-4,w+8,h+8,'#637071');rect(g,x,y,w,h,'#050708');
 if(closed){
  let gr=g.createLinearGradient(x,y,x+w,y);gr.addColorStop(0,'#3b4545');gr.addColorStop(.5,'#151a1b');gr.addColorStop(1,'#384142');
  rect(g,x,y,w,h,gr);
  for(let i=1;i<5;i++)rect(g,x+w*i/5,y,1.5,h,'#465151');
  rect(g,x+w*.1,y+h*.46,w*.8,h*.1,'#070909');text(g,'LOCKED',x+w/2,y+h*.53,Math.max(7,w*.1),'#c6d0d0','center');
 }else{
  rect(g,x+w*.07,y+h*.04,w*.86,h*.92,'#0b1011');
  // Clearly visible glass window, safely inside door frame
  rect(g,x+w*.15,y+h*.12,w*.7,h*.30,light?'#b6cbb0':'#202b2b');
  rect(g,x+w*.15,y+h*.12,w*.7,h*.30,'#b9d5d522');
  rect(g,x+w*.48,y+h*.12,w*.035,h*.30,'#758180');
  rect(g,x+w*.15,y+h*.265,w*.7,h*.02,'#758180');
  if(light){let gr=g.createRadialGradient(x+w/2,y+h*.26,1,x+w/2,y+h*.26,w*.8);gr.addColorStop(0,'#f3f5cf88');gr.addColorStop(1,'#d9e5bd00');g.fillStyle=gr;g.fillRect(x-w*.3,y,w*1.6,h)}
 }
 rect(g,x,y,w,3,'#9ca8a7');rect(g,x,y,3,h,'#9ca8a7');rect(g,x+w-3,y,3,h,'#687777');
}
function drawOffice(){
 W=office.clientWidth;H=office.clientHeight;og.clearRect(0,0,W,H);
 rect(og,0,0,W,H,'#080b0c');
 polygon(og,[[0,0],[W,0],[W*.82,H*.24],[W*.18,H*.24]],'#1d2526');
 let gr=og.createLinearGradient(0,H*.2,0,H);gr.addColorStop(0,'#2b3334');gr.addColorStop(.7,'#101516');gr.addColorStop(1,'#060809');
 polygon(og,[[W*.18,H*.24],[W*.82,H*.24],[W,H],[0,H]],'#171e1f');
 for(let i=1;i<8;i++){og.strokeStyle='#46505066';og.beginPath();og.moveTo(W*.18+i*W*.64/8,H*.24);og.lineTo(W*.18+i*W*.64/8,H*.63);og.stroke()}
 for(let i=1;i<8;i++){og.strokeStyle='#374141';og.beginPath();og.moveTo(W*.5,H*.60);og.lineTo(W*i/8,H);og.stroke()}
 for(let i=1;i<5;i++){og.strokeStyle='#293232';og.beginPath();og.moveTo(0,H*(.68+i*.065));og.lineTo(W,H*(.68+i*.065));og.stroke()}
 // Doorways sit in the outer margins, controls do not overlap them.
 const dw=Math.min(W*.14,65),dh=H*.43,dy=H*.28;
 drawDoor(og,W*.025,dy,dw,dh,westDoor,westLight);
 drawDoor(og,W*.975-dw,dy,dw,dh,eastDoor,eastLight);
 monsters.forEach(m=>{
  if(m.room==='W'&&westLight&&!westDoor)drawMonster(og,W*.025+dw*.5,dy+dh*.61,Math.min(.8,H/570),m);
  if(m.room==='E'&&eastLight&&!eastDoor)drawMonster(og,W*.975-dw*.5,dy+dh*.61,Math.min(.8,H/570),m);
 });
 // desk
 polygon(og,[[W*.28,H*.78],[W*.72,H*.78],[W*.83,H*.96],[W*.17,H*.96]],'#111819');
 rect(og,W*.4,H*.73,W*.2,H*.055,'#273333');text(og,'SECURITY SYSTEM',W/2,H*.765,Math.max(8,W*.025),'#d2dedd','center');
 const vg=og.createRadialGradient(W/2,H*.48,10,W/2,H*.48,Math.max(W,H)*.8);vg.addColorStop(0,'#0000');vg.addColorStop(1,'#000c');og.fillStyle=vg;og.fillRect(0,0,W,H);
}
function drawFeed(){
 const w=feed.clientWidth,h=feed.clientHeight;fg.clearRect(0,0,w,h);
 const r=rooms[ci];rect(fg,0,0,w,h,'#0b0f10');
 const wallColors=['#303939','#403629','#2b383b','#3b3027','#252f31','#252f31','#292c3c','#3c3226','#3b2a32'];
 polygon(fg,[[0,0],[w,0],[w*.92,h*.65],[w*.08,h*.65]],'#171d1e');
 polygon(fg,[[w*.08,h*.1],[w*.92,h*.1],[w,h*.68],[0,h*.68]],wallColors[ci]);
 if(r[2]===0||r[2]===6){
  rect(fg,w*.1,h*.15,w*.09,h*.45,'#4c2b34');rect(fg,w*.81,h*.15,w*.09,h*.45,'#4c2b34');
  for(let i=0;i<3;i++){let x=w*(.23+i*.22);rect(fg,x,h*.47,w*.14,h*.13,'#111717');rect(fg,x,h*.46,w*.14,h*.02,'#83918c')}
  if(r[2]===6)text(fg,'MAIN STAGE',w/2,h*.25,Math.max(10,w*.04),'#d7c9a4','center');
 }else if(r[2]===1){
  for(let i=0;i<5;i++){let x=w*(.09+i*.17);rect(fg,x,h*.17,w*.13,h*.43,'#594a37');for(let j=0;j<4;j++)rect(fg,x+3,h*(.22+j*.09),w*.11,h*.035,'#171a19')}
 }else if(r[2]===2){
  rect(fg,w*.17,h*.44,w*.66,h*.08,'#59615d');rect(fg,w*.21,h*.52,w*.035,h*.15,'#131819');rect(fg,w*.75,h*.52,w*.035,h*.15,'#131819');
  for(let i=0;i<4;i++){rect(fg,w*(.23+i*.14),h*.32,w*.09,h*.09,'#121819');rect(fg,w*(.23+i*.14),h*.31,w*.09,h*.012,'#82908a')}
 }else if(r[2]===3){
  for(let i=0;i<3;i++){let x=w*(.15+i*.26);rect(fg,x,h*.43,w*.19,h*.06,'#514539');rect(fg,x+w*.03,h*.49,w*.025,h*.12,'#151a19');rect(fg,x+w*.14,h*.49,w*.025,h*.12,'#151a19')}
 }else if(r[2]===4){
  polygon(fg,[[w*.34,h*.13],[w*.66,h*.13],[w*.81,h*.68],[w*.19,h*.68]],'#080b0c');
  for(let i=0;i<5;i++){fg.strokeStyle='#667574';fg.strokeRect(w*(.31-i*.025),h*(.2+i*.08),w*(.38+i*.05),h*.05)}
 }else{
  for(let i=0;i<6;i++)rect(fg,w*(.12+i*.14),h*.13,w*.025,h*.47,'#5b6661');
  rect(fg,w*.24,h*.31,w*.52,h*.17,'#141b1c');rect(fg,w*.27,h*.34,w*.46,h*.11,'#293638');
 }
 // floor
 polygon(fg,[[0,h*.67],[w,h*.67],[w,h],[0,h]],'#151b1c');
 for(let i=0;i<7;i++){fg.strokeStyle='#46505066';fg.beginPath();fg.moveTo(w*.5,h*.68);fg.lineTo(w*i/6,h);fg.stroke()}
 for(let i=1;i<5;i++){fg.strokeStyle='#303939';fg.beginPath();fg.moveTo(0,h*(.68+i*.06));fg.lineTo(w,h*(.68+i*.06));fg.stroke()}
 monsters.filter(m=>m.room===ci).forEach((m,i)=>drawMonster(fg,w*(.5+(i-.5)*.28),h*.59,Math.min(.95,h/460),m));
 for(let y=0;y<h;y+=4){fg.fillStyle='#e9ffff0a';fg.fillRect(0,y,w,1)}
 if(staticLeft>0){for(let i=0;i<32;i++){fg.fillStyle=Math.random()>.5?'#ffffff25':'#00000030';fg.fillRect(Math.random()*w,Math.random()*h,Math.random()*w*.3+3,2)}}
 rect(fg,0,h-22,w,22,'#000c');text(fg,'● REC   '+r[0]+'   /   LIVE FEED',8,h-7,10,'#e2e9e8');
}
function updateMap(){
 const map=$('mapGrid');map.innerHTML='';
 const you=document.createElement('button');you.className='mapbtn you';you.textContent='YOU  ●  SECURITY OFFICE';you.onclick=()=>{camera=false;$('cams').style.display='none';$('officeControls').style.display='block';drawOffice()};map.appendChild(you);
 rooms.forEach((r,i)=>{
  let b=document.createElement('button');b.className='mapbtn'+(i===ci?' active':'')+(monsters.some(m=>m.room===i)?' danger':'');
  b.textContent='CAM '+String(i+1).padStart(2,'0')+'\n'+r[0];
  b.onclick=()=>{ci=i;staticLeft=.5;updateCamera();sound(380,.05,'square',.015)};map.appendChild(b);
 });
}
function updateCamera(){
 const r=rooms[ci];$('camName').textContent='CAM '+String(ci+1).padStart(2,'0')+' / '+r[0];$('camDesc').textContent=staticLeft>0?'SIGNAL INTERFERENCE':'● SIGNAL STABLE';
 $('camTitle').textContent='CAM '+String(ci+1).padStart(2,'0')+' / '+r[0];$('camDescription').textContent=r[1].toUpperCase();$('camNumber').textContent=(ci+1)+' / '+rooms.length;
 updateMap();drawFeed();
}
function setCamera(v){camera=v;$('cams').style.display=v?'block':'none';$('officeControls').style.display=v?'none':'block';if(v){updateCamera();sound(300,.08,'square',.02)}else drawOffice()}
$('monitor').onclick=()=>{if(playing)setCamera(true)};
$('closeCam').onclick=()=>setCamera(false);
$('prev').onclick=()=>{ci=(ci+8)%9;staticLeft=.35;updateCamera();sound(420,.04,'square',.012)};
$('next').onclick=()=>{ci=(ci+1)%9;staticLeft=.35;updateCamera();sound(420,.04,'square',.012)};
function updateButtons(){
 [['ld',westDoor],['rd',eastDoor]].forEach(([id,v])=>{$(id).classList.toggle('closed',v);$(id).innerHTML=v?'OPEN<br>DOOR':'CLOSE<br>DOOR'});
 [['ll',westLight],['rl',eastLight]].forEach(([id,v])=>{$(id).classList.toggle('lit',v);$(id).textContent=v?'LIGHT ON':'LIGHT OFF'});
}
function bind(id,fn){$(id).onclick=()=>{if(!playing||camera)return;fn();updateButtons();drawOffice()}}
bind('ld',()=>{westDoor=!westDoor;westLight=false;sound(80,.15,'square',.04)});
bind('rd',()=>{eastDoor=!eastDoor;eastLight=false;sound(80,.15,'square',.04)});
bind('ll',()=>{westLight=!westLight;sound(550,.06,'square',.018)});
bind('rl',()=>{eastLight=!eastLight;sound(550,.06,'square',.018)});
function gameOver(m){
 if(!playing)return;playing=false;camera=false;$('cams').style.display='none';$('officeControls').style.display='none';$('scare').style.display='block';
 // Loud layered alarm + descending harsh tones for a real audio jumpscare
 sound(90,1.15,'sawtooth',.24);setTimeout(()=>sound(47,.8,'square',.18),90);setTimeout(()=>sound(980,.35,'sawtooth',.12),160);
 let frame=0;
 function animate(){
  let w=sc.clientWidth,h=sc.clientHeight;sg.clearRect(0,0,w,h);
  rect(sg,0,0,w,h,frame%2?'#090000':'#3b0000');
  // Character fills the screen rather than appearing tiny.
  drawMonster(sg,w/2+(Math.random()-.5)*20,h*.58,Math.min(w/54,h/45)*(1.1+Math.min(frame*.045,.7)),m);
  if(frame%2===0)rect(sg,0,0,w,h,'#ffffff19');
  text(sg,m.name,w/2,h*.13,Math.max(20,w*.055),'#fff','center');
  text(sg,'YOU WERE FOUND',w/2,h*.9,Math.max(15,w*.035),'#ffcccc','center');
  frame++;if(frame<24)setTimeout(animate,55);
  else setTimeout(()=>{$('scare').style.display='none';$('overlay').style.display='flex';$('modal').innerHTML='<h1>GAME OVER</h1><hr><p>'+m.name+' מצא אותך.<br>בדוק את המצלמות והדלתות, ונסה שוב.</p><button onclick="location.reload()">TRY AGAIN</button>'},500);
 }
 animate();
}
function moveEnemies(dt){
 for(const m of monsters){
  if(m.room==='W'||m.room==='E'){
   m.at+=dt;const closed=m.room==='W'?westDoor:eastDoor;
   if(closed&&m.at>3.2){m.room=m.side==='W'?4:5;m.at=0;m.move=0;sound(65,.1,'sawtooth',.02)}
   else if(!closed&&m.at>3.4){gameOver(m);return}
   continue;
  }
  m.move+=dt;
  if(m.move>m.rate){
   m.move=0;
   if(Math.random()<.7){let ix=m.path.indexOf(m.room);if(ix>=0&&ix<m.path.length-1){m.room=m.path[ix+1];if(m.room==='W'||m.room==='E'){m.side=m.room;m.at=0;sound(75,.13,'sawtooth',.025)}}}
  }
 }
}
function tick(now){
 if(!playing)return;let dt=Math.min((now-last)/1000,.06);last=now;elapsed+=dt;
 power=Math.max(0,power-dt*(.12+(camera?.12:0)+(westDoor?.18:0)+(eastDoor?.18:0)+(westLight?.1:0)+(eastLight?.1:0)));
 $('powerNum').textContent=Math.ceil(power);$('powerfill').style.width=power+'%';$('powerfill').style.background=power<20?'#c34b4b':'#b3d1b5';
 let hour=Math.min(6,Math.floor(elapsed/30));$('clock').textContent=hour===0?'12 AM':hour+' AM';
 $('status').textContent=power<20?'LOW POWER':camera?'CAMERAS ACTIVE':'SYSTEM ONLINE';
 staticLeft=Math.max(0,staticLeft-dt);
 if(power===0){westDoor=eastDoor=westLight=eastLight=false;updateButtons();if(elapsed>50&&Math.random()<.002){gameOver(monsters[Math.floor(Math.random()*monsters.length)]);return}}
 moveEnemies(dt);if(!playing)return;
 drawOffice();if(camera){drawFeed();$('camDesc').textContent=staticLeft>0?'SIGNAL INTERFERENCE':'● SIGNAL STABLE';updateMap()}
 if(elapsed>=180){playing=false;$('overlay').style.display='flex';$('modal').innerHTML='<h1>6 AM</h1><hr><p>שרדת את המשמרת. אבל משהו עדיין נמצא בבניין...</p><button onclick="location.reload()">PLAY AGAIN</button>';return}
 requestAnimationFrame(tick);
}
$('start').onclick=()=>{
 $('overlay').style.display='none';playing=true;elapsed=0;power=100;last=performance.now();westDoor=eastDoor=westLight=eastLight=false;ci=0;
 monsters[0].room=0;monsters[1].room=3;monsters[2].room=1;monsters[3].room=8;
 monsters.forEach(m=>{m.move=0;m.at=0});updateButtons();resize();sound(220,.13,'square',.04);requestAnimationFrame(tick);
};
document.addEventListener('keydown',e=>{if(!playing)return;if(e.key.toLowerCase()==='c')setCamera(!camera);if(e.key==='Escape'&&camera)setCamera(false);if(camera&&e.key==='ArrowLeft')$('prev').click();if(camera&&e.key==='ArrowRight')$('next').click()});
resize();updateButtons();
})();
</script>
</body>
</html>

Game Source: THE LAST WATCH — Night Shift

Creator: LunarPanda23

Libraries: none

Complexity: complex (326 lines, 22.5 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: the-last-watch-night-shift-lunarpanda23" to link back to the original. Then publish at arcadelab.ai/publish.