🎮ArcadeLab

NIGHT SHIFT — Security Network

by LunarPanda23
2833 lines49.0 KB
▶ Play
<!DOCTYPE html>
<html lang="he" dir="ltr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<title>NIGHT SHIFT — Security Network</title>

<style>
*{
    box-sizing:border-box;
    -webkit-tap-highlight-color:transparent;
}

html,body{
    margin:0;
    width:100%;
    height:100%;
    overflow:hidden;
    background:#050708;
    color:#d9e2e2;
    font-family:Arial,Helvetica,sans-serif;
}

button{
    font:inherit;
    cursor:pointer;
    user-select:none;
}

:root{
    --panel:#111719;
    --panel2:#182023;
    --line:#637174;
    --bright:#dce8e5;
    --dim:#8d9b9b;
    --red:#c95353;
    --red2:#7d2c2c;
    --green:#8ebba3;
    --yellow:#d1b86c;
}

/* =========================
   MENU
========================= */

#menu{
    position:fixed;
    inset:0;
    display:flex;
    align-items:center;
    justify-content:center;
    background:
        radial-gradient(circle at center,#172022 0,#080b0c 55%,#030404 100%);
    z-index:1000;
}

.menuBox{
    width:min(720px,90vw);
    border:1px solid #59686a;
    background:rgba(11,15,16,.96);
    box-shadow:0 0 60px #000;
    padding:42px;
    position:relative;
}

.menuTop{
    border-bottom:1px solid #4f5c5e;
    padding-bottom:20px;
    margin-bottom:30px;
}

.menuSmall{
    color:#81908f;
    letter-spacing:5px;
    font-size:13px;
}

.menuTitle{
    font-size:clamp(42px,8vw,82px);
    font-weight:800;
    letter-spacing:7px;
    margin:12px 0 4px;
}

.menuSub{
    color:#8da49a;
    letter-spacing:4px;
    font-size:13px;
}

.nightSelect{
    display:grid;
    grid-template-columns:repeat(5,1fr);
    gap:8px;
    margin:20px 0;
}

.nightSelect button{
    min-height:62px;
    border:1px solid #4e5d5e;
    color:#aebbbb;
    background:#12191b;
}

.nightSelect button.unlocked{
    border-color:#78908b;
}

.nightSelect button.selected{
    border-color:#d5e5df;
    box-shadow:inset 0 0 0 2px #263437;
    color:white;
}

.nightSelect button.locked{
    opacity:.28;
    cursor:not-allowed;
}

.mainButtons{
    display:grid;
    gap:10px;
}

.mainButtons button{
    height:62px;
    border:1px solid #718080;
    background:#172022;
    color:#dce7e4;
    letter-spacing:3px;
    font-weight:bold;
}

.mainButtons button:hover{
    background:#202c2e;
}

#continueBtn{
    border-color:#8da99b;
}

.menuInfo{
    margin-top:25px;
    border-top:1px solid #303b3d;
    padding-top:15px;
    color:#748181;
    font-size:12px;
    line-height:1.7;
}

/* =========================
   GAME
========================= */

#game{
    display:none;
    position:fixed;
    inset:0;
    background:#050708;
}

#game.active{
    display:block;
}

#office{
    position:absolute;
    inset:0;
    overflow:hidden;
    background:
        linear-gradient(90deg,
            #090d0e 0%,
            #151b1d 13%,
            #202729 28%,
            #151b1d 72%,
            #090d0e 87%,
            #050708 100%);
}

/* ceiling */

.ceiling{
    position:absolute;
    left:12%;
    right:12%;
    top:12%;
    height:23%;
    background:
        linear-gradient(145deg,#202729,#0c1112 65%);
    clip-path:polygon(0 0,100% 0,78% 100%,22% 100%);
    border-bottom:2px solid #343f40;
}

/* back wall */

.backWall{
    position:absolute;
    left:18%;
    right:18%;
    top:34%;
    bottom:19%;
    background:
        repeating-linear-gradient(
            90deg,
            rgba(255,255,255,.025) 0,
            rgba(255,255,255,.025) 2px,
            transparent 2px,
            transparent 55px
        ),
        linear-gradient(#171e20,#101617);
    border-left:2px solid #273133;
    border-right:2px solid #273133;
}

/* floor */

.floor{
    position:absolute;
    left:0;
    right:0;
    bottom:0;
    height:31%;
    background:
        repeating-linear-gradient(90deg,
            transparent 0,
            transparent 70px,
            rgba(150,170,170,.09) 71px,
            transparent 73px),
        repeating-linear-gradient(0deg,
            transparent 0,
            transparent 68px,
            rgba(150,170,170,.07) 69px,
            transparent 71px),
        linear-gradient(#11191a,#070a0b);
    transform:perspective(500px) rotateX(9deg);
    transform-origin:bottom;
}

/* top HUD */

.hud{
    position:absolute;
    top:0;
    left:0;
    right:0;
    height:92px;
    background:rgba(5,8,9,.94);
    border-bottom:2px solid #475254;
    display:flex;
    align-items:center;
    justify-content:space-between;
    padding:15px 28px;
    z-index:30;
}

.hudLeft small,
.hudRight small{
    display:block;
    color:#879594;
    letter-spacing:4px;
    font-size:11px;
}

.time{
    font-size:40px;
    font-weight:800;
    letter-spacing:2px;
}

.power{
    text-align:right;
}

.powerNumber{
    font-size:30px;
    font-weight:bold;
}

.powerBar{
    width:190px;
    height:7px;
    border:1px solid #687978;
    margin-top:6px;
}

.powerFill{
    width:100%;
    height:100%;
    background:#a8c8b8;
}

/* =========================
   DOORS
========================= */

.door{
    position:absolute;
    top:39%;
    bottom:25%;
    width:15%;
    min-width:115px;
    background:#070a0b;
    border:4px solid #566264;
    box-shadow:inset 0 0 30px #000;
    z-index:12;
}

.door.left{
    left:2%;
}

.door.right{
    right:2%;
}

.doorFrame{
    position:absolute;
    inset:5px;
    border:2px solid #242e30;
}

.doorWindow{
    position:absolute;
    left:12%;
    right:12%;
    top:7%;
    height:45%;
    border:4px solid #687879;
    background:
        linear-gradient(rgba(15,22,23,.9),rgba(2,4,5,.98));
    overflow:hidden;
}

.doorWindow .windowGlow{
    position:absolute;
    inset:0;
    background:radial-gradient(circle,#aabda9 0,rgba(110,145,125,.22) 30%,transparent 68%);
    opacity:0;
    transition:.15s;
}

.door.lightOn .windowGlow{
    opacity:.9;
}

.door.lightOn .doorWindow{
    background:#283331;
}

.door.lightOn .doorWindow::after{
    content:"";
    position:absolute;
    inset:0;
    background:linear-gradient(90deg,rgba(220,240,220,.2),transparent,rgba(220,240,220,.12));
}

.doorClosed{
    position:absolute;
    left:0;
    right:0;
    top:0;
    height:0;
    background:
        repeating-linear-gradient(90deg,#151c1d 0,#151c1d 18px,#0c1112 20px);
    border-bottom:3px solid #737f80;
    overflow:hidden;
    transition:height .28s ease;
    z-index:5;
}

.door.closed .doorClosed{
    height:100%;
}

.doorLabel{
    position:absolute;
    bottom:10px;
    width:100%;
    text-align:center;
    color:#728080;
    font-size:10px;
    letter-spacing:3px;
}

.creatureAtDoor{
    position:absolute;
    left:50%;
    bottom:-5px;
    transform:translateX(-50%);
    opacity:0;
    transition:.12s;
    z-index:3;
}

.door.lightOn .creatureAtDoor{
    opacity:1;
}

/* =========================
   CREATURE CSS MODELS
========================= */

.monster{
    position:relative;
    width:70px;
    height:130px;
    filter:drop-shadow(0 5px 8px #000);
}

.monster .head{
    position:absolute;
    width:65px;
    height:67px;
    left:3px;
    top:0;
    border-radius:35% 35% 42% 42%;
    background:#222;
    border:3px solid #0b0c0c;
}

.monster .eye{
    position:absolute;
    width:9px;
    height:13px;
    background:#d8e6b4;
    border-radius:50%;
    top:25px;
    box-shadow:0 0 9px #d8e6b4;
}

.monster .eye.a{left:16px}
.monster .eye.b{right:16px}

.monster .mouth{
    position:absolute;
    left:14px;
    right:14px;
    bottom:8px;
    height:13px;
    background:#080909;
    border-radius:50%;
}

.monster .body{
    position:absolute;
    left:14px;
    top:58px;
    width:43px;
    height:72px;
    background:#272e2e;
    border:3px solid #0b0c0c;
    border-radius:16px 16px 4px 4px;
}

.monster .arm{
    position:absolute;
    top:68px;
    width:13px;
    height:55px;
    background:#202727;
    border:3px solid #0b0c0c;
}

.monster .arm.a{
    left:3px;
    transform:rotate(10deg);
}

.monster .arm.b{
    right:3px;
    transform:rotate(-10deg);
}

.monster.red .head,
.monster.red .body{background:#4b2727}

.monster.blue .head,
.monster.blue .body{background:#263d47}

.monster.gold .head,
.monster.gold .body{background:#4b452b}

.monster.white .head,
.monster.white .body{background:#404848}

.monster.tall{
    transform:scaleY(1.2);
}

.monster.round .head{
    border-radius:48%;
}

.monster.long .head{
    width:56px;
    left:7px;
    border-radius:42% 42% 32% 32%;
}

.monster.long .body{
    width:36px;
    left:18px;
}

/* =========================
   CONTROLS
========================= */

.control{
    position:absolute;
    top:52%;
    width:118px;
    padding:8px;
    background:rgba(9,13,14,.96);
    border:2px solid #566466;
    z-index:25;
}

.control.left{
    left:calc(2% + 8px);
}

.control.right{
    right:calc(2% + 8px);
}

.controlTitle{
    text-align:center;
    color:#879493;
    font-size:10px;
    letter-spacing:3px;
    margin-bottom:7px;
}

.control button{
    width:100%;
    min-height:47px;
    margin:3px 0;
    background:#131b1d;
    border:1px solid #657476;
    color:#dce5e3;
    font-weight:bold;
    font-size:11px;
}

.control button.active{
    background:#703131;
    border-color:#d05c5c;
}

.control button.lightActive{
    background:#566347;
    border-color:#b3c58f;
    color:white;
}

/* =========================
   DESK
========================= */

.desk{
    position:absolute;
    left:50%;
    bottom:13%;
    transform:translateX(-50%);
    width:300px;
    height:100px;
    background:#0a0e0f;
    border:2px solid #343e40;
    z-index:15;
    box-shadow:0 -12px 30px #000;
}

.monitor{
    position:absolute;
    left:50%;
    top:-55px;
    transform:translateX(-50%);
    width:100px;
    height:65px;
    background:#080c0d;
    border:3px solid #4b595a;
    display:flex;
    align-items:center;
    justify-content:center;
    font-size:10px;
    color:#879494;
    letter-spacing:2px;
}

/* =========================
   CAMERA BAR
========================= */

.cameraBar{
    position:absolute;
    left:50%;
    bottom:0;
    transform:translateX(-50%);
    width:min(700px,86%);
    height:86px;
    background:rgba(9,13,14,.98);
    border:2px solid #536163;
    border-bottom:0;
    z-index:100;
    display:grid;
    grid-template-columns:70px 1fr 70px;
    align-items:center;
    padding:8px 14px;
}

.cameraBar button{
    height:54px;
    background:#121a1c;
    border:2px solid #617072;
    color:#d9e3e1;
    font-size:30px;
}

.cameraBar button:active{
    transform:scale(.96);
}

.cameraName{
    text-align:center;
    font-weight:bold;
    letter-spacing:5px;
    font-size:13px;
}

.cameraHint{
    color:#718080;
    font-size:9px;
    letter-spacing:2px;
    text-align:center;
    margin-top:5px;
}

/* =========================
   CAMERA SCREEN
========================= */

#cameraScreen{
    display:none;
    position:absolute;
    inset:0;
    z-index:80;
    background:#101516;
}

#cameraScreen.open{
    display:block;
}

.cameraScene{
    position:absolute;
    left:0;
    right:0;
    top:92px;
    bottom:86px;
    overflow:hidden;
    background:#101718;
}

.camCeiling{
    position:absolute;
    left:0;
    right:0;
    top:0;
    height:22%;
    background:linear-gradient(#1e2829,#0d1213);
    border-bottom:2px solid #465254;
}

.camRoom{
    position:absolute;
    left:5%;
    right:5%;
    top:20%;
    bottom:0;
    border:2px solid #354143;
    background:
        repeating-linear-gradient(
            90deg,
            transparent 0,
            transparent 70px,
            rgba(190,200,200,.045) 71px,
            transparent 73px
        ),
        linear-gradient(135deg,#222b2c,#111718 65%);
}

.camFloor{
    position:absolute;
    left:0;
    right:0;
    bottom:0;
    height:38%;
    background:
        repeating-linear-gradient(90deg,
            transparent 0,
            transparent 60px,
            rgba(190,200,200,.07) 61px,
            transparent 63px),
        repeating-linear-gradient(0deg,
            transparent 0,
            transparent 55px,
            rgba(190,200,200,.06) 56px,
            transparent 58px);
    transform:perspective(400px) rotateX(15deg);
    transform-origin:bottom;
}

.camObject{
    position:absolute;
    border:2px solid #4b595a;
    background:#101516;
}

.camObject.one{
    width:25%;
    height:24%;
    left:10%;
    bottom:25%;
}

.camObject.two{
    width:17%;
    height:32%;
    right:13%;
    bottom:22%;
}

.camObject.three{
    width:32%;
    height:11%;
    left:34%;
    top:34%;
}

.camLabel{
    position:absolute;
    left:18px;
    top:18px;
    border:1px solid #697878;
    background:rgba(6,9,10,.85);
    padding:9px 14px;
    font-size:12px;
    letter-spacing:3px;
    z-index:10;
}

.signal{
    position:absolute;
    right:18px;
    top:18px;
    color:#a6bbb1;
    font-size:11px;
    letter-spacing:2px;
}

.static{
    position:absolute;
    inset:0;
    pointer-events:none;
    opacity:.16;
    background:
        repeating-linear-gradient(
            0deg,
            transparent 0,
            transparent 2px,
            rgba(255,255,255,.12) 3px,
            transparent 4px
        );
    animation:staticMove .12s infinite;
}

@keyframes staticMove{
    from{transform:translateY(0)}
    to{transform:translateY(4px)}
}

.camMonster{
    position:absolute;
    z-index:8;
    left:50%;
    bottom:19%;
    transform:translateX(-50%) scale(1.35);
}

.camMonster.hidden{
    display:none;
}

/* =========================
   MAP
========================= */

.map{
    position:absolute;
    right:20px;
    top:120px;
    width:270px;
    background:rgba(7,10,11,.95);
    border:2px solid #5a696b;
    padding:14px;
    z-index:20;
}

.mapTitle{
    color:#9eacab;
    font-size:11px;
    letter-spacing:4px;
    margin-bottom:10px;
}

.mapGrid{
    display:grid;
    grid-template-columns:1fr 1fr;
    gap:7px;
}

.map button{
    min-height:54px;
    background:#11191a;
    border:2px solid #566567;
    color:#ccd7d5;
    font-size:10px;
    line-height:1.2;
}

.map button.current{
    border-color:#e2ece9;
    box-shadow:0 0 10px rgba(210,230,225,.25);
}

.map button.danger{
    border-color:#9b3d3d;
    color:#e18d8d;
}

.you{
    margin-top:11px;
    padding:8px;
    border-top:1px solid #364244;
    color:#9cb4aa;
    font-size:10px;
    letter-spacing:2px;
}

.closeCams{
    position:absolute;
    left:20px;
    top:120px;
    z-index:30;
    padding:12px 18px;
    background:#101819;
    border:2px solid #647274;
    color:#dce5e3;
    font-weight:bold;
}

/* =========================
   STATUS / ALERT
========================= */

.status{
    position:absolute;
    top:105px;
    left:20px;
    z-index:50;
    font-size:10px;
    color:#778686;
    letter-spacing:2px;
}

.alert{
    position:absolute;
    top:130px;
    left:50%;
    transform:translateX(-50%);
    z-index:200;
    padding:10px 20px;
    border:1px solid #8b4a4a;
    background:rgba(48,20,20,.95);
    color:#dfaaaa;
    opacity:0;
    pointer-events:none;
    transition:.2s;
}

.alert.show{
    opacity:1;
}

/* =========================
   JUMPSCARE
========================= */

#jumpscare{
    position:fixed;
    inset:0;
    display:none;
    z-index:10000;
    background:#050505;
    align-items:center;
    justify-content:center;
    overflow:hidden;
}

#jumpscare.show{
    display:flex;
    animation:shake .07s infinite;
}

.scareMonster{
    transform:scale(4);
}

.scareFlash{
    position:absolute;
    inset:0;
    background:#eee;
    opacity:0;
    animation:flash .7s;
}

.scareText{
    position:absolute;
    bottom:30px;
    color:#fff;
    font-size:11px;
    letter-spacing:5px;
    opacity:.65;
}

@keyframes shake{
    0%{transform:translate(0)}
    25%{transform:translate(-12px,8px)}
    50%{transform:translate(9px,-7px)}
    75%{transform:translate(-7px,-9px)}
    100%{transform:translate(10px,5px)}
}

@keyframes flash{
    0%{opacity:0}
    15%{opacity:.9}
    30%{opacity:.05}
    45%{opacity:.8}
    100%{opacity:0}
}

/* =========================
   NIGHT COMPLETE
========================= */

#nightComplete{
    position:fixed;
    inset:0;
    display:none;
    align-items:center;
    justify-content:center;
    background:rgba(3,5,6,.96);
    z-index:9000;
}

.completeBox{
    width:min(600px,90vw);
    border:2px solid #617072;
    padding:38px;
    background:#0c1112;
    text-align:center;
}

.completeBox h1{
    font-size:48px;
    letter-spacing:6px;
    margin:0 0 10px;
}

.completeBox p{
    color:#899797;
    letter-spacing:2px;
}

.completeBox button{
    margin-top:20px;
    width:100%;
    height:55px;
    background:#182223;
    border:1px solid #778687;
    color:#e2e9e7;
}

/* =========================
   RESPONSIVE
========================= */

@media(max-width:650px){

    .hud{
        height:78px;
        padding:9px 14px;
    }

    .time{
        font-size:31px;
    }

    .powerNumber{
        font-size:22px;
    }

    .powerBar{
        width:110px;
    }

    .door{
        width:20%;
        min-width:90px;
        top:37%;
        bottom:27%;
    }

    .control{
        width:95px;
        padding:5px;
        top:54%;
    }

    .control.left{
        left:1%;
    }

    .control.right{
        right:1%;
    }

    .control button{
        min-height:42px;
        font-size:9px;
    }

    .doorWindow{
        top:8%;
        height:40%;
    }

    .desk{
        width:180px;
        height:70px;
        bottom:16%;
    }

    .monitor{
        width:75px;
        height:48px;
        top:-42px;
        font-size:8px;
    }

    .cameraBar{
        width:94%;
        height:76px;
        grid-template-columns:55px 1fr 55px;
    }

    .cameraBar button{
        height:48px;
        font-size:24px;
    }

    .cameraName{
        font-size:10px;
        letter-spacing:3px;
    }

    .map{
        width:220px;
        right:8px;
        top:95px;
        padding:8px;
    }

    .map button{
        min-height:47px;
        font-size:9px;
    }

    .closeCams{
        top:95px;
        left:8px;
        padding:9px 12px;
        font-size:10px;
    }

    .cameraScene{
        top:78px;
        bottom:76px;
    }
}

@media(max-height:650px){
    .door{
        top:31%;
        bottom:22%;
    }

    .control{
        top:47%;
    }

    .cameraBar{
        height:70px;
    }
}
</style>
</head>

<body>

<!-- ================= MENU ================= -->

<div id="menu">

    <div class="menuBox">

        <div class="menuTop">
            <div class="menuSmall">SECURITY NETWORK / ENTERTAINMENT FACILITY</div>
            <div class="menuTitle">NIGHT SHIFT</div>
            <div class="menuSub">ORIGINAL SURVIVAL HORROR</div>
        </div>

        <div style="color:#82908f;font-size:11px;letter-spacing:3px">
            SELECT NIGHT
        </div>

        <div class="nightSelect" id="nightSelect">
            <button data-night="1">01</button>
            <button data-night="2">02</button>
            <button data-night="3">03</button>
            <button data-night="4">04</button>
            <button data-night="5">05</button>
        </div>

        <div class="mainButtons">
            <button id="startBtn">START NIGHT</button>
            <button id="continueBtn">CONTINUE</button>
            <button id="resetBtn">RESET PROGRESS</button>
        </div>

        <div class="menuInfo">
            Survive from 12 AM to 6 AM.<br>
            Watch the cameras. Listen carefully. Check both doors.<br>
            Every night becomes more aggressive.
        </div>

    </div>

</div>

<!-- ================= GAME ================= -->

<div id="game">

    <div id="office">

        <div class="hud">

            <div class="hudLeft">
                <small>NIGHT SHIFT / SECURITY NETWORK</small>
                <div class="time" id="clock">12 AM</div>
                <small>SYSTEM ONLINE</small>
            </div>

            <div class="hudRight">
                <small id="nightLabel">NIGHT 01</small>
                <div class="powerNumber"><span id="power">100</span>%</div>
                <div class="powerBar">
                    <div class="powerFill" id="powerFill"></div>
                </div>
            </div>

        </div>

        <div class="backWall"></div>
        <div class="ceiling"></div>
        <div class="floor"></div>

        <!-- LEFT DOOR -->

        <div class="door left" id="leftDoor">

            <div class="doorFrame"></div>

            <div class="doorWindow">
                <div class="windowGlow"></div>

                <div class="creatureAtDoor" id="leftCreature">
                    <div class="monster blue">
                        <div class="head">
                            <div class="eye a"></div>
                            <div class="eye b"></div>
                            <div class="mouth"></div>
                        </div>
                        <div class="body"></div>
                        <div class="arm a"></div>
                        <div class="arm b"></div>
                    </div>
                </div>

            </div>

            <div class="doorClosed"></div>
        </div>

        <!-- RIGHT DOOR -->

        <div class="door right" id="rightDoor">

            <div class="doorFrame"></div>

            <div class="doorWindow">
                <div class="windowGlow"></div>

                <div class="creatureAtDoor" id="rightCreature">
                    <div class="monster red long">
                        <div class="head">
                            <div class="eye a"></div>
                            <div class="eye b"></div>
                            <div class="mouth"></div>
                        </div>
                        <div class="body"></div>
                        <div class="arm a"></div>
                        <div class="arm b"></div>
                    </div>
                </div>

            </div>

            <div class="doorClosed"></div>
        </div>

        <!-- LEFT CONTROLS -->

        <div class="control left">

            <div class="controlTitle">WEST CONTROL</div>

            <button id="leftDoorBtn">CLOSE DOOR</button>

            <button id="leftLightBtn">LIGHT OFF</button>

        </div>

        <!-- RIGHT CONTROLS -->

        <div class="control right">

            <div class="controlTitle">EAST CONTROL</div>

            <button id="rightDoorBtn">CLOSE DOOR</button>

            <button id="rightLightBtn">LIGHT OFF</button>

        </div>

        <div class="desk">
            <div class="monitor">SECURITY</div>
        </div>

        <div class="status" id="status">
            SYSTEM READY / LISTEN FOR MOVEMENT
        </div>

        <div class="alert" id="alert">
            WARNING
        </div>

        <!-- CAMERA BAR -->

        <div class="cameraBar">

            <button id="prevCam">◀</button>

            <div>
                <div class="cameraName" id="cameraName">CAM 01 / SHOWROOM</div>
                <div class="cameraHint">
                    CAMERA SYSTEM / LEFT & RIGHT TO CHANGE VIEW
                </div>
            </div>

            <button id="nextCam">▶</button>

        </div>

    </div>

    <!-- CAMERA SCREEN -->

    <div id="cameraScreen">

        <div class="cameraScene">

            <div class="camCeiling"></div>

            <div class="camRoom">

                <div class="camObject one"></div>
                <div class="camObject two"></div>
                <div class="camObject three"></div>

                <div class="camFloor"></div>

                <div class="camMonster hidden" id="camMonster">

                    <div class="monster gold" id="cameraMonsterVisual">
                        <div class="head">
                            <div class="eye a"></div>
                            <div class="eye b"></div>
                            <div class="mouth"></div>
                        </div>
                        <div class="body"></div>
                        <div class="arm a"></div>
                        <div class="arm b"></div>
                    </div>

                </div>

            </div>

            <div class="camLabel" id="camLabel">
                CAM 01 / SHOWROOM
            </div>

            <div class="signal">
                SIGNAL ● STABLE
            </div>

            <div class="static"></div>

        </div>

        <!-- MAP -->

        <div class="map">

            <div class="mapTitle">FACILITY MAP</div>

            <div class="mapGrid" id="mapGrid">

                <button data-cam="2">CAM 02<br>STORAGE</button>
                <button data-cam="1">CAM 01<br>SHOWROOM</button>

                <button data-cam="4">CAM 04<br>DINING</button>
                <button data-cam="3">CAM 03<br>WORKSHOP</button>

                <button data-cam="6">CAM 06<br>EAST HALL</button>
                <button data-cam="5">CAM 05<br>WEST HALL</button>

                <button data-cam="8">CAM 08<br>STAGE</button>
                <button data-cam="7">CAM 07<br>BACK ROOM</button>

                <button data-cam="9">CAM 09<br>LOADING</button>

            </div>

            <div class="you">
                ● YOU — SECURITY OFFICE
            </div>

        </div>

        <button class="closeCams" id="closeCams">
            CLOSE CAMERAS
        </button>

    </div>

</div>

<!-- ================= JUMPSCARE ================= -->

<div id="jumpscare">

    <div class="scareMonster">

        <div class="monster red tall">

            <div class="head">
                <div class="eye a"></div>
                <div class="eye b"></div>
                <div class="mouth"></div>
            </div>

            <div class="body"></div>
            <div class="arm a"></div>
            <div class="arm b"></div>

        </div>

    </div>

    <div class="scareFlash"></div>

    <div class="scareText">
        SIGNAL LOST
    </div>

</div>

<!-- ================= NIGHT COMPLETE ================= -->

<div id="nightComplete">

    <div class="completeBox">

        <h1>6 AM</h1>

        <p id="completeText">
            NIGHT COMPLETE
        </p>

        <button id="nextNightBtn">
            CONTINUE TO NEXT NIGHT
        </button>

        <button id="menuAfterNight">
            RETURN TO MENU
        </button>

    </div>

</div>

<script>

/* =========================================================
   NIGHT SHIFT
   COMPLETE GAME ENGINE
========================================================= */

const CAMERAS = [

    {
        id:1,
        name:"CAM 01 / SHOWROOM",
        zone:"showroom"
    },

    {
        id:2,
        name:"CAM 02 / STORAGE",
        zone:"storage"
    },

    {
        id:3,
        name:"CAM 03 / WORKSHOP",
        zone:"workshop"
    },

    {
        id:4,
        name:"CAM 04 / DINING",
        zone:"dining"
    },

    {
        id:5,
        name:"CAM 05 / WEST HALL",
        zone:"westHall"
    },

    {
        id:6,
        name:"CAM 06 / EAST HALL",
        zone:"eastHall"
    },

    {
        id:7,
        name:"CAM 07 / BACK ROOM",
        zone:"backRoom"
    },

    {
        id:8,
        name:"CAM 08 / STAGE",
        zone:"stage"
    },

    {
        id:9,
        name:"CAM 09 / LOADING",
        zone:"loading"
    }

];

/* ---------------------------------------------------------
   FOUR DIFFERENT CHARACTERS
--------------------------------------------------------- */

const characters = [

    {
        id:"MILO",
        name:"MILO",
        side:"left",
        color:"blue",
        route:[8,7,2,5,"LEFT_DOOR"],
        position:0,
        aggression:1
    },

    {
        id:"RAVEN",
        name:"RAVEN",
        side:"right",
        color:"red",
        route:[1,3,6,"RIGHT_DOOR"],
        position:0,
        aggression:1
    },

    {
        id:"BRASS",
        name:"BRASS",
        side:"left",
        color:"gold",
        route:[4,2,5,"LEFT_DOOR"],
        position:0,
        aggression:1
    },

    {
        id:"NOVA",
        name:"NOVA",
        side:"right",
        color:"white",
        route:[9,7,6,"RIGHT_DOOR"],
        position:0,
        aggression:1
    }

];

/* ---------------------------------------------------------
   STATE
--------------------------------------------------------- */

let selectedNight = 1;

let currentNight = Number(localStorage.getItem("nightCurrent")) || 1;

let highestNight = Number(localStorage.getItem("nightUnlocked")) || 1;

let running = false;

let cameraOpen = false;

let currentCam = 1;

let power = 100;

let hour = 0;

let minute = 0;

let gameSeconds = 0;

let nightTimer = null;

let aiTimer = null;

let powerTimer = null;

let soundContext = null;

let leftDoorClosed = false;

let rightDoorClosed = false;

let leftLight = false;

let rightLight = false;

/* =========================================================
   ELEMENTS
========================================================= */

const menu = document.getElementById("menu");

const game = document.getElementById("game");

const clock = document.getElementById("clock");

const powerText = document.getElementById("power");

const powerFill = document.getElementById("powerFill");

const nightLabel = document.getElementById("nightLabel");

const leftDoor = document.getElementById("leftDoor");

const rightDoor = document.getElementById("rightDoor");

const leftCreature = document.getElementById("leftCreature");

const rightCreature = document.getElementById("rightCreature");

const leftDoorBtn = document.getElementById("leftDoorBtn");

const rightDoorBtn = document.getElementById("rightDoorBtn");

const leftLightBtn = document.getElementById("leftLightBtn");

const rightLightBtn = document.getElementById("rightLightBtn");

const cameraScreen = document.getElementById("cameraScreen");

const cameraName = document.getElementById("cameraName");

const camLabel = document.getElementById("camLabel");

const camMonster = document.getElementById("camMonster");

const closeCams = document.getElementById("closeCams");

const alertBox = document.getElementById("alert");

const statusText = document.getElementById("status");

const jumpscare = document.getElementById("jumpscare");

const nightComplete = document.getElementById("nightComplete");

const completeText = document.getElementById("completeText");

const nextNightBtn = document.getElementById("nextNightBtn");

/* =========================================================
   MENU
========================================================= */

function refreshMenu(){

    document.querySelectorAll(".nightSelect button").forEach(btn=>{

        const n = Number(btn.dataset.night);

        btn.classList.remove("unlocked","selected","locked");

        if(n <= highestNight){
            btn.classList.add("unlocked");
        }else{
            btn.classList.add("locked");
        }

        if(n === selectedNight){
            btn.classList.add("selected");
        }

    });

    document.getElementById("continueBtn").disabled =
        currentNight > 5;

}

document.querySelectorAll(".nightSelect button").forEach(btn=>{

    btn.addEventListener("click",()=>{

        const n = Number(btn.dataset.night);

        if(n <= highestNight){

            selectedNight = n;

            refreshMenu();

        }

    });

});

document.getElementById("startBtn").onclick=()=>{

    if(selectedNight <= highestNight){

        startNight(selectedNight);

    }

};

document.getElementById("continueBtn").onclick=()=>{

    const n = Math.min(5,Math.max(1,currentNight));

    startNight(n);

};

document.getElementById("resetBtn").onclick=()=>{

    localStorage.removeItem("nightUnlocked");
    localStorage.removeItem("nightCurrent");

    highestNight=1;
    currentNight=1;
    selectedNight=1;

    refreshMenu();

};

/* =========================================================
   AUDIO ENGINE
   No external files required.
========================================================= */

function initAudio(){

    if(!soundContext){

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

    }

    if(soundContext.state === "suspended"){
        soundContext.resume();
    }

}

function tone(freq,duration,type="sine",volume=.04){

    if(!soundContext)return;

    const osc=soundContext.createOscillator();

    const gain=soundContext.createGain();

    osc.type=type;

    osc.frequency.value=freq;

    gain.gain.setValueAtTime(volume,soundContext.currentTime);

    gain.gain.exponentialRampToValueAtTime(
        .001,
        soundContext.currentTime+duration
    );

    osc.connect(gain);

    gain.connect(soundContext.destination);

    osc.start();

    osc.stop(soundContext.currentTime+duration);

}

function doorSound(){

    tone(90,.12,"square",.07);

    setTimeout(()=>{
        tone(55,.16,"square",.04);
    },80);

}

function lightSound(){

    tone(300,.06,"triangle",.025);

}

function cameraSound(){

    tone(700,.05,"square",.025);

    setTimeout(()=>{
        tone(450,.06,"square",.018);
    },60);

}

function movementSound(){

    tone(80,.08,"sawtooth",.025);

    setTimeout(()=>{
        tone(62,.1,"sawtooth",.018);
    },100);

}

function scareSound(){

    if(!soundContext)return;

    const osc=soundContext.createOscillator();

    const gain=soundContext.createGain();

    osc.type="sawtooth";

    osc.frequency.setValueAtTime(
        80,
        soundContext.currentTime
    );

    osc.frequency.exponentialRampToValueAtTime(
        900,
        soundContext.currentTime+.5
    );

    gain.gain.setValueAtTime(
        .001,
        soundContext.currentTime
    );

    gain.gain.linearRampToValueAtTime(
        .28,
        soundContext.currentTime+.08
    );

    gain.gain.exponentialRampToValueAtTime(
        .001,
        soundContext.currentTime+.9
    );

    osc.connect(gain);

    gain.connect(soundContext.destination);

    osc.start();

    osc.stop(soundContext.currentTime+1);

}

/* =========================================================
   START NIGHT
========================================================= */

function startNight(n){

    initAudio();

    if(n > highestNight){
        return;
    }

    currentNight=n;

    localStorage.setItem(
        "nightCurrent",
        String(n)
    );

    running=true;

    menu.style.display="none";

    nightComplete.style.display="none";

    jumpscare.classList.remove("show");

    game.classList.add("active");

    resetGameState();

    nightLabel.textContent =
        "NIGHT " + String(n).padStart(2,"0");

    statusText.textContent =
        "SYSTEM READY / LISTEN FOR MOVEMENT";

    cameraOpen=false;

    cameraScreen.classList.remove("open");

    startTimers();

}

/* =========================================================
   RESET STATE
========================================================= */

function resetGameState(){

    power=100;

    hour=0;

    minute=0;

    gameSeconds=0;

    currentCam=1;

    leftDoorClosed=false;

    rightDoorClosed=false;

    leftLight=false;

    rightLight=false;

    characters.forEach((c,index)=>{

        c.position=0;

        c.aggression =
            currentNight +
            index*.15;

    });

    updateOffice();

    updateClock();

    updatePower();

    updateCamera();

}

/* =========================================================
   TIMERS
========================================================= */

function startTimers(){

    stopTimers();

    /*
       One game hour = 38 seconds.
       Full night = 3 minutes 48 seconds.
       Long enough to actually watch cameras.
    */

    nightTimer=setInterval(()=>{

        if(!running)return;

        gameSeconds++;

        if(gameSeconds % 38 === 0){

            hour++;

            if(hour >= 6){

                winNight();

                return;

            }

        }

        updateClock();

    },1000);

    aiTimer=setInterval(()=>{

        if(running){

            moveCharacters();

        }

    },7000);

    powerTimer=setInterval(()=>{

        if(running){

            consumePower();

        }

    },1000);

}

/* =========================================================
   STOP
========================================================= */

function stopTimers(){

    clearInterval(nightTimer);
    clearInterval(aiTimer);
    clearInterval(powerTimer);

}

/* =========================================================
   CLOCK
========================================================= */

function updateClock(){

    let displayHour;

    if(hour===0){
        displayHour=12;
    }else{
        displayHour=hour;
    }

    clock.textContent =
        displayHour + " AM";

}

/* =========================================================
   POWER
========================================================= */

function consumePower(){

    let drain=.035;

    if(leftDoorClosed) drain+=.035;

    if(rightDoorClosed) drain+=.035;

    if(leftLight) drain+=.025;

    if(rightLight) drain+=.025;

    if(cameraOpen) drain+=.025;

    drain += currentNight*.004;

    power-=drain;

    if(power<0)power=0;

    updatePower();

    if(power<=0){

        blackout();

    }

}

function updatePower(){

    powerText.textContent =
        Math.max(0,Math.floor(power));

    powerFill.style.width =
        Math.max(0,power)+"%";

    if(power<20){

        powerFill.style.background="#b64d4d";

    }else if(power<40){

        powerFill.style.background="#b6a15c";

    }else{

        powerFill.style.background="#a8c8b8";

    }

}

/* =========================================================
   DOORS
========================================================= */

function toggleDoor(side){

    if(!running)return;

    if(power<=0)return;

    initAudio();

    if(side==="left"){

        leftDoorClosed=!leftDoorClosed;

    }else{

        rightDoorClosed=!rightDoorClosed;

    }

    doorSound();

    updateOffice();

}

function toggleLight(side){

    if(!running)return;

    if(power<=0)return;

    initAudio();

    if(side==="left"){

        leftLight=!leftLight;

    }else{

        rightLight=!rightLight;

    }

    lightSound();

    updateOffice();

}

leftDoorBtn.onclick=()=>toggleDoor("left");

rightDoorBtn.onclick=()=>toggleDoor("right");

leftLightBtn.onclick=()=>toggleLight("left");

rightLightBtn.onclick=()=>toggleLight("right");

/* =========================================================
   OFFICE UPDATE
========================================================= */

function updateOffice(){

    leftDoor.classList.toggle(
        "closed",
        leftDoorClosed
    );

    rightDoor.classList.toggle(
        "closed",
        rightDoorClosed
    );

    leftDoor.classList.toggle(
        "lightOn",
        leftLight
    );

    rightDoor.classList.toggle(
        "lightOn",
        rightLight
    );

    leftDoorBtn.textContent =
        leftDoorClosed
        ? "OPEN DOOR"
        : "CLOSE DOOR";

    rightDoorBtn.textContent =
        rightDoorClosed
        ? "OPEN DOOR"
        : "CLOSE DOOR";

    leftDoorBtn.classList.toggle(
        "active",
        leftDoorClosed
    );

    rightDoorBtn.classList.toggle(
        "active",
        rightDoorClosed
    );

    leftLightBtn.textContent =
        leftLight
        ? "LIGHT ON"
        : "LIGHT OFF";

    rightLightBtn.textContent =
        rightLight
        ? "LIGHT ON"
        : "LIGHT OFF";

    leftLightBtn.classList.toggle(
        "lightActive",
        leftLight
    );

    rightLightBtn.classList.toggle(
        "lightActive",
        rightLight
    );

    /*
       Only show a creature at the door
       when that character has reached it.
    */

    const leftDanger =
        characters.some(c=>
            c.side==="left" &&
            c.route[c.position]==="LEFT_DOOR"
        );

    const rightDanger =
        characters.some(c=>
            c.side==="right" &&
            c.route[c.position]==="RIGHT_DOOR"
        );

    leftCreature.style.opacity =
        leftLight && leftDanger ? "1" : "0";

    rightCreature.style.opacity =
        rightLight && rightDanger ? "1" : "0";

}

/* =========================================================
   CHARACTER AI
========================================================= */

function moveCharacters(){

    if(power<=0)return;

    characters.forEach((c,index)=>{

        const nightFactor =
            currentNight * .12;

        /*
           Night 1 is deliberately slow.
           Night 5 is much harder.
        */

        const chance =
            .22 +
            nightFactor +
            c.aggression*.04;

        if(Math.random() > chance){

            return;

        }

        /*
           A character doesn't move every time.
           Each has an independent route.
        */

        if(c.position < c.route.length-1){

            c.position++;

            movementSound();

            statusText.textContent =
                c.name +
                " MOVEMENT DETECTED";

            showAlert(
                c.name +
                " MOVED"
            );

        }else{

            /*
               Character is at the door.
               Closed door sends it back.
               Open door causes jumpscare.
            */

            const isLeft =
                c.route[c.position]==="LEFT_DOOR";

            const isRight =
                c.route[c.position]==="RIGHT_DOOR";

            if(isLeft){

                if(leftDoorClosed){

                    c.position =
                        Math.max(0,c.position-1);

                    movementSound();

                }else{

                    triggerJumpscare(c);

                }

            }

            if(isRight){

                if(rightDoorClosed){

                    c.position =
                        Math.max(0,c.position-1);

                    movementSound();

                }else{

                    triggerJumpscare(c);

                }

            }

        }

    });

    updateOffice();

    updateCamera();

}

/* =========================================================
   CAMERA
========================================================= */

function openCameras(){

    if(!running)return;

    initAudio();

    cameraOpen=true;

    cameraScreen.classList.add("open");

    cameraSound();

    updateCamera();

}

function closeCameras(){

    cameraOpen=false;

    cameraScreen.classList.remove("open");

    cameraSound();

}

document.querySelector(".cameraBar").onclick=
e=>{

    /*
       Camera bar itself opens system,
       but not arrow buttons.
    */

    if(
        e.target!==document.getElementById("prevCam") &&
        e.target!==document.getElementById("nextCam")
    ){

        openCameras();

    }

};

closeCams.onclick=closeCameras;

document.getElementById("prevCam").onclick=()=>{

    if(!running)return;

    openCameras();

    currentCam--;

    if(currentCam<1){
        currentCam=9;
    }

    cameraSound();

    updateCamera();

};

document.getElementById("nextCam").onclick=()=>{

    if(!running)return;

    openCameras();

    currentCam++;

    if(currentCam>9){
        currentCam=1;
    }

    cameraSound();

    updateCamera();

};

document.querySelectorAll(".map button").forEach(btn=>{

    btn.onclick=()=>{

        currentCam=
            Number(btn.dataset.cam);

        cameraSound();

        updateCamera();

    };

});

/* =========================================================
   CAMERA UPDATE
========================================================= */

function updateCamera(){

    const cam =
        CAMERAS.find(c=>c.id===currentCam);

    if(!cam)return;

    cameraName.textContent=cam.name;

    camLabel.textContent=cam.name;

    /*
       Determine if any character is
       currently in this camera.
    */

    const occupants =
        characters.filter(c=>
            c.route[c.position]===currentCam
        );

    if(occupants.length){

        camMonster.classList.remove("hidden");

        const c=occupants[0];

        const visual=
            document.getElementById(
                "cameraMonsterVisual"
            );

        visual.className=
            "monster " +
            c.color;

        /*
           Different character proportions.
        */

        if(c.id==="MILO"){
            visual.classList.add("round");
        }

        if(c.id==="RAVEN"){
            visual.classList.add("long");
        }

        if(c.id==="NOVA"){
            visual.classList.add("tall");
        }

    }else{

        camMonster.classList.add("hidden");

    }

    document.querySelectorAll(
        ".map button"
    ).forEach(btn=>{

        const n=Number(btn.dataset.cam);

        btn.classList.toggle(
            "current",
            n===currentCam
        );

        const danger =
            characters.some(c=>
                c.route[c.position]===n
            );

        btn.classList.toggle(
            "danger",
            danger
        );

    });

}

/* =========================================================
   ALERT
========================================================= */

let alertTimeout;

function showAlert(text){

    alertBox.textContent=text;

    alertBox.classList.add("show");

    clearTimeout(alertTimeout);

    alertTimeout=setTimeout(()=>{

        alertBox.classList.remove("show");

    },1300);

}

/* =========================================================
   JUMPSCARE
========================================================= */

function triggerJumpscare(character){

    if(!running)return;

    running=false;

    stopTimers();

    closeCameras();

    initAudio();

    scareSound();

    jumpscare.classList.add("show");

    setTimeout(()=>{

        jumpscare.classList.remove("show");

        menu.style.display="flex";

        game.classList.remove("active");

        /*
           Continue stays on the night
           where the player died.
        */

        currentNight=currentNight;

        localStorage.setItem(
            "nightCurrent",
            String(currentNight)
        );

        selectedNight=currentNight;

        refreshMenu();

    },2200);

}

/* =========================================================
   BLACKOUT
========================================================= */

function blackout(){

    if(!running)return;

    power=0;

    updatePower();

    leftDoorClosed=false;

    rightDoorClosed=false;

    leftLight=false;

    rightLight=false;

    updateOffice();

    statusText.textContent=
        "TOTAL POWER FAILURE";

    showAlert("POWER OUT");

    setTimeout(()=>{

        triggerJumpscare({
            name:"UNKNOWN",
            side:"right"
        });

    },1500);

}

/* =========================================================
   WIN NIGHT
========================================================= */

function winNight(){

    if(!running)return;

    running=false;

    stopTimers();

    closeCameras();

    /*
       Unlock next night.
    */

    if(currentNight<5){

        highestNight =
            Math.max(
                highestNight,
                currentNight+1
            );

        localStorage.setItem(
            "nightUnlocked",
            String(highestNight)
        );

        currentNight++;

        localStorage.setItem(
            "nightCurrent",
            String(currentNight)
        );

        completeText.textContent =
            "NIGHT " +
            String(currentNight-1).padStart(2,"0") +
            " COMPLETE — NIGHT " +
            String(currentNight).padStart(2,"0") +
            " UNLOCKED";

        nextNightBtn.textContent =
            "START NIGHT " +
            String(currentNight).padStart(2,"0");

    }else{

        /*
           Completed all five.
           Continue remains at Night 5.
        */

        highestNight=5;

        currentNight=5;

        localStorage.setItem(
            "nightUnlocked",
            "5"
        );

        localStorage.setItem(
            "nightCurrent",
            "5"
        );

        completeText.textContent =
            "ALL FIVE NIGHTS SURVIVED";

        nextNightBtn.textContent =
            "PLAY NIGHT 05 AGAIN";

    }

    nightComplete.style.display="flex";

}

/* =========================================================
   COMPLETE BUTTONS
========================================================= */

nextNightBtn.onclick=()=>{

    nightComplete.style.display="none";

    startNight(currentNight);

};

document.getElementById("menuAfterNight").onclick=()=>{

    nightComplete.style.display="none";

    game.classList.remove("active");

    menu.style.display="flex";

    selectedNight=
        Math.min(5,currentNight);

    refreshMenu();

};

/* =========================================================
   KEYBOARD
========================================================= */

document.addEventListener("keydown",e=>{

    if(!running)return;

    if(e.key.toLowerCase()==="q"){
        toggleDoor("left");
    }

    if(e.key.toLowerCase()==="a"){
        toggleLight("left");
    }

    if(e.key.toLowerCase()==="e"){
        toggleDoor("right");
    }

    if(e.key.toLowerCase()==="d"){
        toggleLight("right");
    }

    if(e.key==="ArrowLeft"){

        currentCam--;

        if(currentCam<1)currentCam=9;

        openCameras();

        updateCamera();

    }

    if(e.key==="ArrowRight"){

        currentCam++;

        if(currentCam>9)currentCam=1;

        openCameras();

        updateCamera();

    }

    if(e.key==="Escape"){

        closeCameras();

    }

});

/* =========================================================
   INITIALIZE
========================================================= */

if(highestNight<1){
    highestNight=1;
}

if(currentNight<1){
    currentNight=1;
}

if(currentNight>highestNight){
    currentNight=highestNight;
}

selectedNight=currentNight;

refreshMenu();

</script>

</body>
</html>

Game Source: NIGHT SHIFT — Security Network

Creator: LunarPanda23

Libraries: none

Complexity: complex (2833 lines, 49.0 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: night-shift-security-network-lunarpanda23" to link back to the original. Then publish at arcadelab.ai/publish.