BASE COUNCIL WAR ROOM INTEL GEAR

DEFCON 4

THREAT: ELEVATED

TEMPORAL SYNC
SEISMIC
USGS LIVE
ESTABLISHING UPLINK...
AUDIO INTERCEPT
STATUS: SILENT
OMNI-VISION SYSTEM
LIVE UNRESTRICTED UPLINK
ELDER COMMS TERMINAL
SYSTEM: Uplink Secure. Base Command Online.
GRAY: Awaiting your orders. Enter overrides below.
MARKET PULSE BOT

🔥 LIVE CRYPTO TARGETS

const fwdX = anim.position.x + Math.sin(anim.rotation.y) * 10; const fwdZ = anim.position.z + Math.cos(anim.rotation.y) * 10; const bwdX = anim.position.x - Math.sin(anim.rotation.y) * 10; const bwdZ = anim.position.z - Math.cos(anim.rotation.y) * 10; anim.rotation.x += (Math.atan2(getTerrainHeight(bwdX, bwdZ) - getTerrainHeight(fwdX, fwdZ), 20) - anim.rotation.x) * dt * 5; const speedRatio = data.speed / data.maxSpeed; if(!data.isGLTF) { if (speedRatio > 0.1) { const animSpeed = time * 15 * (speedRatio > 0.5 ? 1.5 : 1); if(data.legs) data.legs.forEach((leg, i) => { leg.rotation.x = Math.sin(animSpeed + (i % 2 === 0 ? 0 : Math.PI)) * 0.6 * speedRatio; }); if(data.body) data.body.position.y = data.baseY + Math.abs(Math.sin(animSpeed)) * 1.5 * speedRatio; } else { if(data.legs) data.legs.forEach(leg => leg.rotation.x += (0 - leg.rotation.x) * dt * 5); if(data.body) data.body.position.y += (data.baseY + Math.sin(time * 2) * 0.5 - data.body.position.y) * dt * 5; } } } /** ========================================== * MAIN UPDATE LOOP * ========================================== */ function startLoop() { requestAnimationFrame(mainLoop); } async function mainLoop() { const dt = Math.min(clock.getDelta(), 0.1); const time = Date.now() * 0.005; // Update GLTF Animations animMixers.forEach(m => m.mixer.update(dt)); updateInstancedFlock(); if(window.treeShaders) window.treeShaders.forEach(s => s.uniforms.time.value = time); if(waterMat) waterMat.uniforms.time.value += dt; if(gameSun) { gameSun.position.x = Math.sin(time * 0.1) * 300; gameSun.position.z = Math.cos(time * 0.1) * 300 - 500; } if(dustSystem) dustSystem.rotation.y = time * 0.05; // Ensure shadows update even if autoUpdate is false (Better performance) if(renderer.shadowMap.autoUpdate === false) renderer.shadowMap.needsUpdate = true; if(window.activeFires) { window.activeFires.forEach(f => { f.children.forEach(p => { p.position.y += p.userData.speed * dt * 300; p.rotation.x += p.userData.speed * dt * 10; p.rotation.y += p.userData.speed * dt * 10; if(p.position.y > p.userData.yMax) p.position.y = 0; }); }); } if(window.whirlwindGrp) { window.whirlwindGrp.children.forEach(p => { p.userData.angle += p.userData.speed * dt * 50; p.position.x = Math.sin(p.userData.angle) * p.userData.radius; p.position.z = Math.cos(p.userData.angle) * p.userData.radius; if(p.userData.isDebris) { p.rotation.x += 0.2; p.rotation.z += 0.2; } else { p.rotation.y += 0.1; } }); } let targetSky = new THREE.Color(0x87ceeb); if (sim.mode === 'WEALTH' && sim.threat > 60) targetSky = new THREE.Color(0xd4af37); else if (sim.mode === 'STRUCK' || sim.day === 5) targetSky = new THREE.Color(0x222222); if (sim.state === 'WHIRLWIND') targetSky = new THREE.Color(0x1a1a5a); scene.fog.color.lerp(targetSky, 0.01); const ring = document.getElementById('danger-ring'); if(sim.al < 30) { ring.style.borderWidth = "40px"; ring.style.borderColor = `rgba(139, 0, 0, ${0.2 + Math.sin(time)*0.2})`; } else { ring.style.borderWidth = "0px"; } if (sim.ritual && !isDisasterRunning) { sim.nPos += 4.8 * dt * 60; if (sim.nPos > 96) sim.nPos = 0; const needleEl = document.getElementById('needle'); needleEl.style.left = sim.nPos + "%"; } if(!isDisasterRunning && sim.mode === 'WEALTH') { sim.threat = Math.min(100, sim.threat + (sim.grain * 0.0001)); if(sim.day > 1 && sim.day < 4) { let raiderCount = enemies.filter(e => e.userData.type === 'raider').length; if(Math.random() < sim.threat * 0.00005 && raiderCount < 2) { createRaiderMesh(Math.random()*2000, Math.random()*2000); logUpdate("A lone raider scout detected."); sfx.danger(); } if(Math.random() < 0.001 && enemies.length < 4) { spawnWolf((Math.random()-0.5)*3000, (Math.random()-0.5)*3000); sfx.wolf(); } } enemies.forEach(r => { const data = r.userData; const dist = r.position.distanceTo(player.position); if(dist > 2000) return; if(data.state === 'PATROL') { r.position.x += Math.sin(time + (r.id||1)) * 0.5; r.position.z += Math.cos(time + (r.id||1)) * 0.5; if(dist < 400) data.state = 'CHASE'; } else if(data.state === 'CHASE') { const dir = player.position.clone().sub(r.position).normalize(); r.position.add(dir.multiplyScalar(120 * dt)); r.rotation.y = lerpAngle(r.rotation.y, Math.atan2(dir.x, dir.z), dt * 8); if(dist < 60) data.state = 'ATTACK'; } else if(data.state === 'ATTACK') { if(data.cooldown <= 0) { sim.al -= 1; data.cooldown = 1.5; sfx.hit(); flashDanger(); vibrate([50, 50]); } data.cooldown -= dt; if(dist > 80) data.state = 'CHASE'; } r.position.y += ((getTerrainHeight(r.position.x, r.position.z) + 15) - r.position.y) * dt * 10; }); } birds.forEach(b => { const d = b.userData; d.angle += d.speed; b.position.set(Math.sin(d.angle) * 800, d.height + Math.sin(time) * 10, Math.cos(d.angle) * 800); b.lookAt(0, 300, 0); if(!d.isGLTF) { if(d.wingL) d.wingL.rotation.z = Math.sin(time * 8) * 0.4; if(d.wingR) d.wingR.rotation.z = -Math.sin(time * 8) * 0.4; } }); npcs.forEach(npc => { if(player.position.distanceTo(npc.position) > 1500) return; const dist = player.position.distanceTo(npc.position); if(dist < 120 && !npc.userData.prompted) { logUpdate(npc.userData.label + " awaits."); npc.userData.prompted = true; sfx.ui(); } if(dist > 150) npc.userData.prompted = false; let desiredRot = npc.rotation.y; if(npc.userData.task === 'HERD') { let targetAnimal = null; let minDist = 9999; [...flock, ...camelHerd].forEach(a => { if(!a.userData.secured) { let d = npc.position.distanceTo(a.position); if(d < minDist) { minDist = d; targetAnimal = a; } } }); if(targetAnimal) { let penTgt = targetAnimal.userData.type === 'sheep' ? sheepPenTarget : camelPenTarget; let dirFromPen = targetAnimal.position.clone().sub(penTgt).normalize(); let approachPos = targetAnimal.position.clone().add(dirFromPen.multiplyScalar(80)); const dir = approachPos.sub(npc.position).normalize(); if(npc.position.distanceTo(approachPos) > 10) { npc.position.add(dir.multiplyScalar(60*dt)); desiredRot = Math.atan2(dir.x, dir.z); } } else { npc.userData.task = 'IDLE'; logUpdate(npc.userData.label + " finished herding."); } } else { if(Math.random() < 0.005) npc.userData.target.set(npc.position.x + (Math.random()-0.5)*300, 0, npc.position.z + (Math.random()-0.5)*300); const dir = npc.userData.target.clone().sub(npc.position); if(dir.length() > 10) { dir.normalize(); npc.position.add(dir.multiplyScalar(20 * dt)); desiredRot = Math.atan2(dir.x, dir.z); } } npc.rotation.y = lerpAngle(npc.rotation.y, desiredRot, dt * 5); npc.position.y += (getTerrainHeight(npc.position.x, npc.position.z) - npc.position.y) * dt * 10; }); if(!sim.ritual && !sim.diag && sim.playerState !== 'LAMENTING') { let ix = 0, iz = 0; if(keys['w']) iz -= 1; if(keys['s']) iz += 1; if(keys['a']) ix -= 1; if(keys['d']) ix += 1; if(joystickMove) { ix = moveVec.x; iz = moveVec.z; } let inputVec = new THREE.Vector3(ix, 0, iz); if(inputVec.lengthSq() > 1) inputVec.normalize(); inputVec.applyAxisAngle(new THREE.Vector3(0,1,0), camYaw); if(inputVec.lengthSq() > 0.01) { pVelocity.add(inputVec.multiplyScalar(pAccel * dt)); const currentMax = pMaxSpeed * (sim.stam < 20 ? 0.5 : 1.0); if(pVelocity.length() > currentMax) pVelocity.normalize().multiplyScalar(currentMax); } pVelocity.lerp(new THREE.Vector3(0,0,0), pFriction * dt); pSpeed = pVelocity.length(); if(pSpeed > 1) { player.position.add(pVelocity.clone().multiplyScalar(dt)); let targetRot = Math.atan2(pVelocity.x, pVelocity.z); player.rotation.y = lerpAngle(player.rotation.y, targetRot, dt * 10); handleCollisions(); // Dust kick-up when moving fast if (pSpeed > 200 && Math.random() < 0.1) { spawnImpactVFX(new THREE.Vector3(player.position.x, player.position.y - 10, player.position.z), 0xddaa77, true); } } player.position.y = getTerrainHeight(player.position.x, player.position.z); const fwdX = player.position.x + Math.sin(player.rotation.y) * 10; const fwdZ = player.position.z + Math.cos(player.rotation.y) * 10; const bwdX = player.position.x - Math.sin(player.rotation.y) * 10; const bwdZ = player.position.z - Math.cos(player.rotation.y) * 10; const slopeX = Math.atan2(getTerrainHeight(bwdX, bwdZ) - getTerrainHeight(fwdX, fwdZ), 20); donkeyGrp.rotation.x = lerpAngle(donkeyGrp.rotation.x, slopeX, dt * 5); if(!donkeyGrp.userData.isGLTF) { if(pSpeed > 10) { const animSpd = time * 15 * (pSpeed/pMaxSpeed); donkeyLegs.forEach((leg, i) => { leg.rotation.x = Math.sin(animSpd + (i%2===0?0:Math.PI)) * 0.8 * (pSpeed/pMaxSpeed); }); donkeyGrp.position.y = Math.abs(Math.sin(animSpd)) * 3; heroGrp.position.y = 55 + Math.abs(Math.sin(animSpd)) * 1.5; } else { donkeyLegs.forEach(leg => leg.rotation.x += (0 - leg.rotation.x) * dt * 8); donkeyGrp.position.y += (0 - donkeyGrp.position.y) * dt * 8; heroGrp.position.y += (55 - heroGrp.position.y) * dt * 8; } } let pSheep = 0, pCamel = 0; flock.forEach(s => { if(player.position.distanceTo(s.position) < 1500) updateAnimalAI(s, dt, time, flock, sheepPenTarget); if(s.userData.secured) pSheep++; }); camelHerd.forEach(c => { if(player.position.distanceTo(c.position) < 1500) updateAnimalAI(c, dt, time, camelHerd, camelPenTarget); if(c.userData.secured) pCamel++; }); if (sim.sheep > 0 && pSheep !== parseInt(document.getElementById('v-s').innerText)) animateCounter('v-s'); if (sim.camels > 0 && pCamel !== parseInt(document.getElementById('v-c').innerText)) animateCounter('v-c'); document.getElementById('v-s').innerText = sim.sheep === 0 ? 0 : sim.sheep - 16 + pSheep; document.getElementById('v-c').innerText = sim.camels === 0 ? 0 : sim.camels - 5 + pCamel; } renderLabels(); const camTargetObj = sim.playerState === 'LAMENTING' ? heroGrp : player; const cX = camTargetObj.position.x + Math.sin(camYaw) * 220; const cZ = camTargetObj.position.z + Math.cos(camYaw) * 220; let cY = camTargetObj.position.y + 150; if(pSpeed > 15 && sim.playerState !== 'LAMENTING') { cY += Math.sin(time * 15) * 4 * (pSpeed/pMaxSpeed); } targetCamPos.set(cX, cY, cZ); camera.position.lerp(targetCamPos, dt * 4); camera.lookAt(camTargetObj.position.x, camTargetObj.position.y + 50, camTargetObj.position.z); if(isWebGPU) { await renderer.renderAsync(scene, camera); } else { composer.render(); } requestAnimationFrame(mainLoop); } /** ========================================== * INTERACTIONS & SYSTEMS * ========================================== */ function logUpdate(m){ const l = document.getElementById('chronicle'); l.innerHTML = `
• ${m}
` + l.innerHTML; } function flashDanger() { const r=document.getElementById('danger-ring'); r.style.background="rgba(139,0,0,0.3)"; setTimeout(()=>r.style.background="transparent",200); } function animateCounter(id) { const el = document.getElementById(id); el.style.transform = 'scale(1.5) rotate(-5deg)'; el.style.color = '#ffe55c'; setTimeout(() => { el.style.transform = 'scale(1) rotate(0deg)'; el.style.color = 'inherit'; }, 200); } function updateTheme() { const root = document.documentElement; if (sim.mode === 'STRUCK') { root.style.setProperty('--paper-bg', 'linear-gradient(145deg, rgba(60,50,45,0.95), rgba(30,20,15,0.95))'); root.style.setProperty('--gold', '#888'); root.style.setProperty('--gold-dark', '#444'); if(!isWebGPU && bloomPass) bloomPass.strength = 0.4; } else if (sim.state === 'WHIRLWIND') { root.style.setProperty('--paper-bg', 'linear-gradient(145deg, rgba(30,10,40,0.95), rgba(10,5,20,0.95))'); if(!isWebGPU && bloomPass) bloomPass.strength = 0.8; } else if (sim.state === 'RESTORED') { root.style.setProperty('--paper-bg', 'linear-gradient(145deg, rgba(255,250,230,0.95), rgba(212,175,55,0.95))'); if(!isWebGPU && bloomPass) bloomPass.strength = 1.8; } } function act(n,c){ if(sim.day === 5) { logUpdate("In your grief, labor means nothing. (Action blocked)"); sfx.danger(); return; } if(sim.ap>=c){ sim.ap-=c; sim.stam-=10; sim.threat+=5; sim.grain+=20; sim.wood+=15; logUpdate(n+" work finished."); sfx.harvest(); animateCounter('v-g'); animateCounter('v-w'); updateHUD(); } } function startOffering(){ if(sim.ap < 10) return; sim.ritual=true; document.getElementById('ritual-ui').style.display='block'; sim.strikes=0; document.getElementById('r-status').innerText="STRIKES: 0/5 (Tap/Space)"; sfx.ui(); } function attemptRitualStrike() { if (!sim.ritual || isDisasterRunning) return; const needle = document.getElementById('needle'); const perfect = sim.nPos > 39 && sim.nPos < 61; if (perfect) { sim.strikes++; document.getElementById('r-status').innerText = `STRIKES: ${sim.strikes}/5 (PERFECT!)`; needle.style.boxShadow = '0 0 30px #ff0'; sfx.ritualSuccess(); setTimeout(() => needle.style.boxShadow = '0 0 10px #fff', 200); const f = document.getElementById('fader'); f.style.transition = 'none'; f.style.background = '#ffe55c'; f.style.opacity = 0.5; setTimeout(() => { f.style.transition = 'opacity 0.4s'; f.style.opacity = 0; setTimeout(()=>f.style.background='black',400); }, 50); } else { sfx.ritualMiss(); needle.style.boxShadow = '0 0 20px #f00'; setTimeout(() => needle.style.boxShadow = '0 0 10px #fff', 150); } if (sim.strikes >= 5) { sim.ritual = false; document.getElementById('ritual-ui').style.display = 'none'; sim.ap -= 10; sim.al += 15; updateHUD(); if (sim.day === 5) triggerWife(); logUpdate("Burnt offering accepted by YHWH."); } } function buildAltar() { if(sim.wood >= 20 && sim.ap >= 10) { sim.wood -= 20; sim.ap -= 10; sim.hasAltar = true; logUpdate("Sacred Altar built."); const altarGrp = new THREE.Group(); const base = new THREE.Mesh(new THREE.BoxGeometry(40, 20, 40), pbrMat('#aaaaaa', 0.9)); base.position.y = 10; const top = new THREE.Mesh(new THREE.BoxGeometry(30, 10, 30), pbrMat('#888888', 0.9)); top.position.y = 25; altarGrp.add(base, top); const spawnPos = player.position.clone().add(new THREE.Vector3(Math.sin(player.rotation.y)*80, 0, Math.cos(player.rotation.y)*80)); spawnPos.y = getTerrainHeight(spawnPos.x, spawnPos.z); altarGrp.position.copy(spawnPos); altarGrp.userData = { label: "Sacred Altar" }; scene.add(altarGrp); tags.push(altarGrp); colliders.push({pos: spawnPos, radius: 25}); updateHUD(); sfx.wood(); } else { logUpdate("Not enough Wood (20) or AP (10) for Altar."); sfx.danger(); } } function expandFold() { if(sim.wood >= 30 && sim.ap >= 10) { sim.wood -= 30; sim.ap -= 10; logUpdate("Herds expanded. Estate grows."); for(let i=0; i<3; i++) spawnSheep(); for(let i=0; i<1; i++) spawnCamel(); sim.sheep += 300; sim.camels += 100; sfx.wood(); updateHUD(); animateCounter('v-s'); animateCounter('v-c'); } else { logUpdate("Need 30 Wood and 10 AP to expand fold."); sfx.danger(); } } function pray() { if(sim.ap >= 5) { sim.ap -= 5; let alGain = sim.hasAltar ? 10 : 5; sim.al = Math.min(100, sim.al + alGain); logUpdate(sim.hasAltar ? "You prayed at the altar. Spirit lifted." : "You prayed in the wilderness."); sfx.ui(); updateHUD(); } else { logUpdate("Not enough AP to pray."); sfx.danger(); } } function executeStaffStrike() { const now = Date.now(); if(now - lastStaffStrike < 400 || sim.ritual || sim.playerState === 'LAMENTING') return; lastStaffStrike = now; sfx.harvest(); vibrate(50); if(window.staffMesh) { window.staffMesh.rotation.x -= 1.2; setTimeout(() => { window.staffMesh.rotation.x += 1.2; }, 150); } let hit = false; enemies.forEach((enemy, idx) => { if(player.position.distanceTo(enemy.position) < 110) { enemy.userData.hp -= 45; spawnImpactVFX(enemy.position, 0xff0000); sfx.hit(); logUpdate("You struck at the threat!"); hit = true; if(enemy.userData.hp <= 0) { removeAndDispose(enemy); enemies.splice(idx, 1); tags.splice(tags.indexOf(enemy), 1); } } }); if(!hit && harvestNodes && sim.day !== 5) { harvestNodes.forEach((node, idx) => { if(player.position.distanceTo(node.position) < 130) { node.userData.hp--; spawnImpactVFX(node.position, 0xaaaaaa); sfx.wood(); if(node.userData.hp <= 0) { if(node.userData.type === 'grain') { sim.grain += 10; logUpdate("Harvested Field."); animateCounter('v-g'); } else if(node.userData.type === 'wood') { sim.wood += 5; logUpdate("Timber Felled."); animateCounter('v-w'); } removeAndDispose(node); harvestNodes.splice(idx, 1); tags.splice(tags.indexOf(node), 1); } } }); } } function npcInteraction() { sfx.ui(); vibrate(20); if(sim.mode === 'WEALTH') { const nearNpc = npcs.find(n => player.position.distanceTo(n.position) < 150); if(nearNpc) { openDialogue(nearNpc.userData.label, "Yes, my lord Job?", [ {t: "Command: Herd the animals.", f: () => { nearNpc.userData.task = 'HERD'; logUpdate(nearNpc.userData.label + " is securing the herds."); }}, {t: "Nevermind.", f: () => {}} ]); } else logUpdate("No one is nearby to hear you."); } else if (sim.mode === 'STRUCK') { sim.round++; if(sim.round === 1) { openDialogue("Eliphaz the Temanite", "Those who plow iniquity reap the same. The innocent do not perish. Call upon YHWH.", [ {t: "I will hold fast my integrity until death!", f: ()=>{sim.al+=20;}}, {t: "My days are swifter than a weaver’s shuttle...", f: ()=>{sim.al-=10;}} ]); } else if(sim.round === 2) { openDialogue("Bildad the Shuhite", "Does YHWH pervert justice? Seek Him diligently and He will restore you.", [ {t: "How can a man be righteous before YHWH?", f: ()=>{sim.al+=15;}}, {t: "He destroys both the blameless and the wicked.", f: ()=>{sim.al-=25;}} ]); } else { openDialogue("Zophar the Naamathite", "Should a man full of talk be justified? YHWH knows the empty words of man. Repent!", [ {t: "Oh that YHWH would answer me!", f: ()=>{sim.al+=10; whirlwindTrigger();}}, {t: "I am weary of this.", f: ()=>{sim.al-=5;}} ]); } } } function updateHUD(){ document.getElementById('ap-fill').style.width = (sim.ap/30*100) + "%"; document.getElementById('al-fill').style.width = sim.al + "%"; document.getElementById('th-fill').style.width = sim.threat + "%"; document.getElementById('stam-fill').style.width = sim.stam + "%"; document.getElementById('v-g').innerText = sim.grain; document.getElementById('v-w').innerText = sim.wood; document.getElementById('end-day-btn').style.display = (sim.ap < 3 || sim.day === 5) ? 'block' : 'none'; } function renderLabels(){ const lHost = document.getElementById('labels-hud'); lHost.innerHTML = ""; tags.forEach(t => { const dist = camera.position.distanceTo(t.position); if(dist > 1200) return; const v = t.position.clone().add(new THREE.Vector3(0,100,0)).project(camera); if(v.z < 1) { const x = (v.x*0.5+0.5) * window.innerWidth; const y = (v.y*-0.5+0.5) * window.innerHeight; const scale = Math.max(0.4, 1 - (dist/1200)); const opacity = Math.max(0.1, 1 - (dist/900)); lHost.innerHTML += `
${t.userData.label}
`; } }); } function openDialogue(n,m,opts){ sim.diag = true; sfx.ui(); const box=document.getElementById('script-overlay'); document.getElementById('d-name').innerText=n; const dText = document.getElementById('d-text'); dText.innerText = ""; clearInterval(window.typewriterInt); let charIdx = 0; window.typewriterInt = setInterval(() => { dText.innerText += m.charAt(charIdx); charIdx++; if (charIdx >= m.length) clearInterval(window.typewriterInt); }, 25); const choiceBox=document.getElementById('d-choices'); choiceBox.innerHTML=""; opts.forEach(o => { const btn=document.createElement('button'); btn.className="choice-btn"; btn.innerText=o.t; btn.onclick=() => { clearInterval(window.typewriterInt); o.f(); sfx.ui(); vibrate(15); box.style.display='none'; sim.diag=false; updateHUD(); }; choiceBox.appendChild(btn); }); box.style.display='block'; } /** ========================================== * STORY EVENTS & CALAMITIES * ========================================== */ function nightTransition() { document.getElementById('fader').style.opacity = 1; setTimeout(() => { if(sim.day === 3) { triggerCalamityWaves(); document.getElementById('fader').style.opacity = 0; } else if(sim.day === 4) { setupDay5(); document.getElementById('fader').style.opacity = 0; } else if(sim.day === 5) { postCalamitySetup(); } else { sim.day++; sim.ap = 30; sim.stam = 100; document.getElementById('fader').style.opacity = 0; document.getElementById('v-day').innerText = sim.day; logUpdate("Sun rises. Tend the stewardship of Uz."); } updateHUD(); }, 1500); } function triggerCalamityWaves() { isDisasterRunning = true; sim.ritual = true; sfx.danger(); document.getElementById('fader').style.opacity = "0.6"; document.getElementById('fader').style.background = "radial-gradient(circle, transparent 20%, #4a1e1e 100%)"; // Day 3 FIX applied const w4 = () => { openDialogue("Messenger 4", "A great wind struck the house—your children are gone, Job.", [ { t: "Blessed be the Name of YHWH.", f: () => { sim.al += 50; sim.day = 4; isDisasterRunning = false; sim.ritual = false; nightTransition(); } }, { t: "Why has this happened!?", f: () => { sim.al -= 20; sim.day = 4; isDisasterRunning = false; sim.ritual = false; nightTransition(); } } ]); }; const w3 = () => { for(let i=0; i<3; i++) createRaiderMesh(player.position.x+300, player.position.z+300); openDialogue("Messenger 3", "Chaldeans swept down! Your children...", [ {t: "Continue in Silence...", f: w4} ]); }; const w2 = () => { sim.sheep=0; flock.forEach(s=>{ if (s.userData.instanceIndex >= 0 && sheepInstances) { sheepInstances.setMatrixAt(s.userData.instanceIndex, new THREE.Matrix4().scale(new THREE.Vector3(0,0,0))); sheepInstances.instanceMatrix.needsUpdate = true; } removeAndDispose(s); }); spawnFire(-450, -600); spawnFire(600, -850); spawnFire(player.position.x+100, player.position.z-100); openDialogue("Messenger 2", "The fire of YHWH fell and burned up the sheep!", [ {t: "Prepare for Wave 3", f: w3} ]); }; openDialogue("Messenger 1", "Master! The Sabeans took the oxen and camels!", [ {t: "Endure Wave 1", f: w2} ]); sim.camels = 0; camelHerd.forEach(c=>removeAndDispose(c)); } function setupDay5() { isDisasterRunning = false; sim.ritual = false; sim.day = 5; sim.ap = 30; document.getElementById('fader').style.opacity = "0"; document.getElementById('fader').style.background = "black"; document.getElementById('job-title').innerText = "Day of Mourning"; document.getElementById('env-mode').innerText = "The Silence of Uz"; document.getElementById('btn-lament').style.display = 'block'; logUpdate("Your wealth is gone. You cannot harvest or labor. Pray, Sacrifice, or Lament."); } function lamentOption() { sfx.ui(); openDialogue("The Darkness", "Despair overwhelms you.", [ {t: "Curse the day I was born.", f: gameOver }, {t: "Turn to Prayer instead.", f: () => { document.getElementById('btn-lament').style.display = 'none'; triggerWife(); }} ]); } function triggerWife() { openDialogue("Job's Wife", "Do you still hold fast your integrity? Curse YHWH and die!", [ {t: "You speak as a foolish woman. Shall we receive good from YHWH, and not evil?", f: () => { sim.al+=20; document.getElementById('btn-lament').style.display='none'; logUpdate("You maintained integrity. Proceed to Conclude Day."); }}, {t: "Perhaps you are right... (Curse YHWH)", f: gameOver } ]); } function gameOver() { document.getElementById('fader').style.opacity = 1; setTimeout(() => { document.body.innerHTML += `

SPIRIT BROKEN

You have fallen to the adversary's temptation.

`; }, 1500); } function postCalamitySetup() { document.getElementById('btn-lament').style.display = 'none'; document.getElementById('fader').style.opacity = "0"; if(window.activeFires) { window.activeFires.forEach(f => removeAndDispose(f)); window.activeFires = []; } sim.day = 6; sim.ap = 0; document.getElementById('job-title').innerText = "Job: Afflicted in the Ashes"; document.getElementById('env-mode').innerText = "Outside the Walls of Uz"; sim.mode = 'STRUCK'; sim.playerState = 'LAMENTING'; updateTheme(); scene.add(heroGrp); heroGrp.position.copy(player.position); heroGrp.position.y = getTerrainHeight(heroGrp.position.x, heroGrp.position.z); heroGrp.rotation.set(0.5, player.rotation.y, 0); scene.add(donkeyGrp); donkeyGrp.position.copy(player.position); donkeyGrp.position.x += 40; donkeyGrp.position.y = getTerrainHeight(donkeyGrp.position.x, donkeyGrp.position.z); donkeyGrp.rotation.set(0, player.rotation.y + 1, 0); const boilMat = new THREE.MeshBasicMaterial({color: 0x8b0000}); for(let i=0; i<20; i++) { const boil = new THREE.Mesh(new THREE.SphereGeometry(1.5, 4, 4), boilMat); boil.position.set((Math.random()-0.5)*15, Math.random()*25, (Math.random()-0.5)*15); heroGrp.add(boil); } logUpdate("Satan struck Job with boils. He sat in the ashes."); sfx.danger(); const friends = [ {n:"Eliphaz", m:"eliphaz", c:'#2d4a69', x:50, z:50}, {n:"Bildad", m:"bildad", c:'#2d694a', x:-50, z:50}, {n:"Zophar", m:"zophar", c:'#693a2d', x:0, z:-70} ]; friends.forEach(f => { const g = new THREE.Group(); if(modelCache[f.m]) { const mod = SkeletonUtils.clone(modelCache[f.m].scene); mod.scale.set(10,10,10); g.add(mod); } else { const b = new THREE.Mesh(new THREE.BoxGeometry(18, 50, 18), pbrMat(f.c)); b.position.y=25; const beard = new THREE.Mesh(new THREE.BoxGeometry(10, 15, 4), pbrMat('#aaaaaa')); beard.position.set(0, 40, 9); g.add(b, beard); } g.position.set(f.x, 0, f.z); g.userData={label:f.n+" the Temanite"}; scene.add(g); tags.push(g); npcs.push(g); }); updateHUD(); } function whirlwindTrigger() { sim.state = 'WHIRLWIND'; updateTheme(); document.getElementById('env-mode').innerText = "THE WHIRLWIND OF YHWH"; logUpdate("YHWH ANSWERED JOB OUT OF THE WHIRLWIND..."); sfx.play(40, 'square', 4, 0.6); spawnWhirlwind(); let shakeT = 0; const sInt = setInterval(() => { camera.position.x+=(Math.random()-0.5)*5; camera.position.y+=(Math.random()-0.5)*5; shakeT++; if(shakeT>60){ clearInterval(sInt); openDialogue("YHWH out of the Whirlwind", "Where were you when I laid the foundations of the earth? Have you commanded the morning? Declare if you have understanding!", [ {t: "Behold, I am of small account. I lay my hand over my mouth.", f: finaleRestoration}, {t: "I repent in dust and ashes before YHWH.", f: finaleRestoration} ]); } }, 50); } function finaleRestoration() { document.getElementById('fader').style.opacity = 1; setTimeout(() => { document.getElementById('fader').style.opacity = 0; sim.mode = 'WEALTH'; sim.state = 'RESTORED'; updateTheme(); if(window.whirlwindGrp) { removeAndDispose(window.whirlwindGrp); window.whirlwindGrp = null; } sim.playerState = 'MOUNTED'; player.add(donkeyGrp); player.add(heroGrp); donkeyGrp.position.set(0,0,0); donkeyGrp.rotation.set(0,0,0); heroGrp.position.set(0,55,0); heroGrp.rotation.set(0,0,0); heroGrp.children.forEach(c => { if(c.geometry.type === 'SphereGeometry' && c.scale.x < 1) removeAndDispose(c); }); sim.sheep=14000; sim.camels=6000; sim.grain=400; sim.al=100; sfx.play(400, 'sine', 0.5); setTimeout(()=>sfx.play(600,'sine',1), 500); document.getElementById('v-day').innerText = "RESTORED"; document.getElementById('job-title').innerText = "Ancient Patriarch"; logUpdate("YHWH BLESSED JOB MORE THAN HIS BEGINNING."); updateHUD(); for(let i=0; i<5; i++) setTimeout(()=>spawnImpactVFX(player.position, 0xffd700), i*200); let r = "PATIENT SERVANT"; if(sim.al>80) r="GLORIFIED GUARDIAN"; else if(sim.al<40) r="OUTCAST SEEKER"; document.body.innerHTML += `

MISSION COMPLETE

RANK ATTAINED: ${r}

Your Spirit Alignment of ${sim.al}% has been uploaded to the APEX Network.

`; }, 2000); } window.toggleMenu = toggleMenu; window.act = act; window.startOffering = startOffering; window.buildAltar = buildAltar; window.expandFold = expandFold; window.pray = pray; window.npcInteraction = npcInteraction; window.lamentOption = lamentOption; window.nightTransition = nightTransition; window.executeStaffStrike = executeStaffStrike; window.attemptRitualStrike = attemptRitualStrike; // TRIGGER PRELOADER ON LOAD preloadAndInit();