JavaScript must be enabled to play.
Browser lacks capabilities required to play.
Upgrade or switch to another browser.
Loading…
<<nobr>> <<script>> // Runs instantly whenever the player changes passage if (State.variables.playerName && State.variables.playerName !== "Survivor") { const saveDataPayload = { username: State.variables.playerName, lastLocation: Passage.active.title, // Name of the passage the player is on (Camp etc.) saveData: State.variables, // All inventory, HP, stats and data (matches the backend schema) newsShown: State.variables.newsShown || false // Whether the newspaper was shown }; // Send to the server instantly (runs silently in the background) fetch("https://api.ashenveilgame.com/api/save", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(saveDataPayload) }).catch(err => { console.log("Auto-save sync error:", err); }); } <</script>> <</nobr>>
<<setMenu "BaseRaid">> <<if ndef $raidActive>><<set $raidActive to true>><</if>> /* Last night's defense status (snapshot) */ <<if ndef $raidSnap>><<set $raidSnap to setup.raidSnapshot()>><</if>> <<set _snap to $raidSnap>> <<set _wallLvl to _snap.wallLvl>> <<set _wallDmg to _snap.wallLvlDmg>> <<set _trapDmg to _snap.trap>> <<set _turretDmg to _snap.turret>> <<set _crewDmg to _snap.crewDmg>> <<set _enemyHealth to _snap.threat>> <style> body { background-color: #050a05 !important; } #story, #passages, .passage { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; background: transparent !important; } .av-world { position: absolute; top: 10px; left: 17.5em; right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 0 20px 20px 20px; width: calc(100% - 17.5em); } .raid-container { background: linear-gradient(180deg, rgba(20,5,5,0.95), rgba(10,2,2,0.98)), url('images/locations/raid_bg.jpg') center/cover; padding: 30px; border: 2px solid #ef4444; border-radius: 8px; font-family: 'Segoe UI', Tahoma, sans-serif; box-shadow: 0 0 35px rgba(239, 68, 68, 0.3); color: #e2e8f0; box-sizing: border-box; width: 100%; } .cyber-raid-btn { display: block; width: 100%; background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%); border: 1px solid #f87171; color: #f8fafc !important; padding: 12px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.9rem; letter-spacing: 2px; border-radius: 4px; cursor: pointer; text-decoration: none !important; box-sizing: border-box; box-shadow: 0 0 15px rgba(239, 68, 68, 0.4); margin-top: 15px; transition: all 0.2s; } .cyber-raid-btn:hover { background: linear-gradient(135deg, #b91c1c 0%, #dc2626 100%); border-color: #fca5a5; box-shadow: 0 0 25px rgba(239, 68, 68, 0.7); } </style> <div class="av-world"> <div class="raid-container"> <div style="text-align: center; border-bottom: 1px dashed #ef4444; padding-bottom: 15px; margin-bottom: 20px;"> <h2 style="color: #f87171; margin: 0; letter-spacing: 3px; text-shadow: 0 0 15px #ef4444; font-size: 1.8rem;">[ ⚠️ PERIMETER BREACH: BASE RAID ]</h2> <div style="margin-top: 8px; font-size: 0.9rem; color: #fca5a5; letter-spacing: 1px;">WARNING: HEAVILY ARMED RAIDERS ARE ASSAULTING THE SANCTUARY!</div> </div> <div style="background: rgba(0,0,0,0.8); border: 1px solid #7f1d1d; padding: 20px; border-radius: 6px; margin-bottom: 20px; line-height: 1.6;"> <p style="margin-top: 0; color: #fecaca;"> Alarms blare across the compound! A hostile war-party of around 10 raiders has rushed out of the wasteland fog, slamming against your defenses. Gunfire echoes through the steel corridors as automated systems kick into overdrive. </p> <div style="margin-top: 15px; font-family: monospace; font-size: 0.85rem; color: #fbbf24;"> <div>🛡️ Defense Walls (Lvl <<print _wallLvl>>): <span style="color:#34d399;">-<<print _wallDmg>>% Enemy Threat neutralized.</span></div> <div>⚡ Auto-Turrets (<<if _turretDmg gt 0>>calibrated<<else>>NOT calibrated tonight<</if>>): <span style="color:#34d399;">-<<print _turretDmg>>% Enemy Threat neutralized.</span></div> <div>🪤 Tripwires & Traps (<<if _trapDmg gt 0>>armed<<else>>NOT armed tonight<</if>>): <span style="color:#34d399;">-<<print _trapDmg>>% Enemy Threat neutralized.</span></div> <div>🧱 Wall Garrison (<<print _snap.crew>> defenders, barricades at <<print _snap.integrity>>%): <span style="color:#34d399;">-<<print _crewDmg>>% Enemy Threat neutralized.</span></div> <div style="margin-top: 8px; font-size: 1rem; font-weight: bold; color: #f87171;">🎯 REMAINING ENEMY THREAT: <<print _enemyHealth>>%</div> </div> </div> <!-- CHOICE AFTER THE BATTLE --> <div> <<if _enemyHealth lte 50>> <div style="color: #34d399; font-weight: bold; font-family: monospace; margin-bottom: 15px; text-align: center;"> [ AUTOMATED DEFENSES CRUSHED HALF THE FORCE! ONLY A FEW RAIDERS REMAIN BREACHING THE GATES! ] </div> <<link "<span class='cyber-raid-btn'>💥 ENGAGE REMAINING RAIDERS MANUALLY</span>" "Combat">><<run setup.raidStart("manual")>><</link>> <<else>> <div style="color: #fbbf24; font-weight: bold; font-family: monospace; margin-bottom: 15px; text-align: center;"> [ DEFENSES WERE TOO WEAK! THE ENEMY SWARMS THE COMPOUND IN FULL FORCE! ] </div> <<link "<span class='cyber-raid-btn' style='background:linear-gradient(135deg,#7f1d1d,#991b1b);'>💀 MAKE A DESPERATE STAND (HIGH RISK)</span>" "Combat">><<run setup.raidStart("stand")>><</link>> <<endif>> </div> </div> </div>
<<setMenu "Blackout">> <style> body { background-color: #050a05 !important; } #story, #passages, .passage { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; background: transparent !important; } .av-world { position: absolute; top: 20px; left: 17.5em; right: 0; min-height: 90vh; box-sizing: border-box; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 20px; } .blackout-box { background-color: #0a0510; border: 2px solid #a855f7; padding: 40px; border-radius: 10px; max-width: 600px; text-align: center; box-shadow: 0 0 40px rgba(168, 85, 247, 0.4); font-family: 'Segoe UI', Tahoma, sans-serif; } .cyber-wake-btn { display: inline-block; background: linear-gradient(135deg, #9333ea 0%, #7e22ce 100%); border: 1px solid #c084fc; color: #f3e8ff !important; padding: 12px 30px; margin-top: 25px; font-weight: bold; font-family: 'Courier New', monospace; letter-spacing: 2px; border-radius: 4px; text-decoration: none !important; box-shadow: 0 0 15px rgba(168, 85, 247, 0.4); transition: all 0.2s; } .cyber-wake-btn:hover { background: linear-gradient(135deg, #7e22ce 100%, #9333ea 0%); border-color: #e9d5ff; box-shadow: 0 0 25px rgba(168, 85, 247, 0.7); } </style> <div class="av-world"> <div class="blackout-box"> <h2 style="color: #c084fc; font-family: 'Courier New', monospace; letter-spacing: 3px; margin-top: 0; text-shadow: 0 0 10px #a855f7;">[ SYSTEM FAILURE: BLACKOUT ]</h2> <p style="color: #e2e8f0; font-size: 1rem; line-height: 1.6; margin-top: 20px;"> You downed one too many unregulated shots at the bar. Your vision blurred, your legs gave out, and the locals tossed you behind the counter like a sack of trash... You wake up with a splitting headache, trying to figure out where you are. </p> <div style="margin-top: 30px;"> <<set _blackoutLocations to ["Katowice Tunnels", "Explore Warehouse", "Explore Clinic", "Explore Bunker", "Combat", "FishingSpot", "Hunting"]>> <<set _randomLoc to _blackoutLocations.random()>> <<link "<span class='cyber-wake-btn'>OPEN EYES & STAND UP</span>" _randomLoc>><</link>> </div> </div> </div>
<<nobr>> <<if def $combatContext and $combatContext eq "camp_tour">><<if def $campTour>><<run setup.tour.restart()>><<goto "Combat">><<else>><<unset $combatContext, $enemy, $enemyGroup, $combatWave, $combatLog>><</if>><</if>> <<if def $combatContext and $combatContext eq "ch3_omicron">><<unset $combatContext, $enemy, $enemyGroup, $targetPOIName, $targetPOILoot, $combatLog, $combatWave>><</if>> <<if def $combatContext and $combatContext eq "camp_raid">><<unset $combatContext, $waveQueue, $campRaid, $targetPOIName, $targetPOILoot>><</if>> <<if def $combatContext and $combatContext eq "slaughterhouse_assault">><<unset $combatContext, $waveQueue>><<set $slaughterGas to false>><<if def $targetPOIName and $targetPOIName eq "Slaughterhouse">><<unset $targetPOIName>><</if>><</if>> <<if ($total_rescued || 0) gte 10 and ($quest_slaughterhouse || "not_started") eq "not_started">><<goto "SurvivorPlea">><</if>> <<set $slaughterGas to false>> <<if def $raidReport and not $raidReport.shown>><<goto "RaidAftermath">><</if>> <<if $raidPending>><<goto "BaseRaidEvent">><</if>> <<set _sceneGo to ($raidPending or (def $raidReport and not $raidReport.shown))>> <<if not _sceneGo>><<run setup.ch2.check()>><<if $ch2 and $ch2.st eq "pending">><<set _sceneGo to true>><<goto "Ch2Scene">><</if>><</if>> <<if not _sceneGo and setup.ch3 and setup.ch3.visible()>><<run setup.ch3.check()>><<if setup.ch3.auto()>><<set _sceneGo to true>><<goto "Ch3Scene">><</if>><</if>> <<if setup.ch3 and setup.ch3.devOn()>><div style="color:#cbd5e1;background:#0f172a;border:1px dashed #64748b;padding:4px 8px;font-size:0.72rem;margin:44px auto 6px auto;max-width:calc(100% - 380px);font-family:monospace;text-align:center;white-space:normal;overflow-wrap:anywhere;position:relative;z-index:1;"><<print setup.ch3.status()>></div> <div class="av-devbtn" style="max-width:calc(100% - 380px);margin:0 auto 8px auto;position:relative;z-index:1;"><<button "CHAPTER 3: THE ARTERY OF VOLTS (DEV)">><<run setup.ch3.open()>><</button>><<if setup.ch3.canEnter()>><<button "ENTER SUBSTATION OMICRON (DEV)">><<run setup.ch3.enterSub()>><</button>><</if>></div><</if>> <<if setup.ch2.dbg()>><div style="color:#cbd5e1;background:#0f172a;border:1px dashed #64748b;padding:4px 8px;font-size:0.72rem;margin:44px auto 6px auto;max-width:calc(100% - 380px);font-family:monospace;text-align:center;white-space:normal;overflow-wrap:anywhere;position:relative;z-index:1;"><<print setup.ch2.dbg()>></div> <style>.av-devbtn button{display:block;width:100%;box-sizing:border-box;color:#bbf7d0;background:#111;border:1px dashed #4ade80;padding:14px;text-align:center;font-family:'Courier New',monospace;letter-spacing:1px;cursor:pointer}.av-devbtn button:hover{background:#052e16}</style> <div class="av-devbtn" style="max-width:calc(100% - 380px);margin:0 auto 8px auto;position:relative;z-index:1;"><<button "PLAY THE CHAPTER 2 BROADCAST (DEV)">><<run setup.ch2.devPlay()>><</button>></div><</if>> <<if setup.tourDue()>><<set $campTour to {s: 0, ver: setup.TOUR_VER}>><</if>> <<run setup.archiveTick()>> <<if not _sceneGo>><<run setup.seekerRoll()>><<run setup.relicNotify()>><</if>> <<if not _sceneGo and $archive and $archive.s eq "built" and not $archive.popped>> <<set $archive.popped to true>> <<script>> setTimeout(function() { Dialog.setup("", "news-modal-custom"); Dialog.wiki(Story.get("ArchivePopup").processText()); Dialog.open(); }, 500); <</script>> <</if>> <<if not _sceneGo and def $seeker and not $seeker.popped>> <<set $seeker.popped to true>> <<script>> setTimeout(function() { Dialog.setup("", "news-modal-custom"); Dialog.wiki(Story.get("SeekerPopup").processText()); Dialog.open(); }, 500); <</script>> <</if>> <<audio ":all" stop>> <<setMenu "Camp">><<run setup.recalcStats()>> <<set _devc to (def $playerName and String($playerName).toLowerCase() eq "nakman")>> <<if ndef $storyStage>><<set $storyStage to 1>><</if>> <<if ndef $startingCity>><<set $startingCity to "Katowice">><</if>> <<if ndef $campLevel>><<set $campLevel to 1>><</if>> <<if ndef $materials>><<set $materials to {}>><</if>> <<if ndef $materials.scrap>><<set $materials.scrap to 0>><</if>> <<if ndef $materials.wood>><<set $materials.wood to 0>><</if>> <<if ndef $materials.cloth>><<set $materials.cloth to 0>><</if>> <<if ndef $materials.water>><<set $materials.water to 0>><</if>> <<if ndef $materials.berries>><<set $materials.berries to 0>><</if>> <<if ndef $materials.scrip>><<set $materials.scrip to 0>><</if>> <<if ndef $materials.ammo>><<set $materials.ammo to 0>><</if>> <<if ndef $materials.rope>><<set $materials.rope to 0>><</if>> <<if ndef $materials.techData>><<set $materials.techData to 0>><</if>> <<if ndef $materials.electronics>><<set $materials.electronics to 0>><</if>> <<if ndef $materials.medicalHerb>><<set $materials.medicalHerb to 0>><</if>> /* migrate legacy key */ <<if def $materials.medical_herb>><<set $materials.medicalHerb to ($materials.medicalHerb||0) + ($materials.medical_herb||0)>><<run delete $materials.medical_herb>><</if>> <<if def $campStorage.medical_herb>><<set $campStorage.medicalHerb to ($campStorage.medicalHerb||0) + ($campStorage.medical_herb||0)>><<run delete $campStorage.medical_herb>><</if>> <<if ndef $globalBroadcastPass>><<set $globalBroadcastPass to 0>><</if>> /* --- AUTOMATIC TRANSFER WITH STRICT CAPACITY CHECK --- */ <<if ndef $campStorage>> <<set $campStorage to { scrap: 0, wood: 0, cloth: 0, caps: 0, techData: 0, medicine: 0, medicalHerb: 0, food: 0, rawMeat: 0, berries: 0, water: 0, rope: 0, electronics: 0 }>> <<endif>> <<if ndef $campStorage.electronics>><<set $campStorage.electronics to 0>><</if>> <<if ndef $campStorage.medicalHerb>> <<set $campStorage.medicalHerb to 0>> <<endif>> /* BUG FIX: TAKE ROPE AND TECH DATA STUCK AT CAMP BACK TO THE PLAYER */ <<if $campStorage.techData gt 0>> <<set $materials.techData += $campStorage.techData>> <<set $campStorage.techData to 0>> <<endif>> <<if $campStorage.rope gt 0>> <<set $materials.rope += $campStorage.rope>> <<set $campStorage.rope to 0>> <<endif>> /* Storage capacity calculation (shared helper) */ <<set _maxCampStorage to setup.campCap()>> /* MATERIALS TO TRANSFER TO CAMP (MEDICAL HERBS ADDED) */ <<set _resKeys to ["scrap", "wood", "cloth", "electronics", "medicine", "medicalHerb", "food", "rawMeat", "berries", "water"]>> <<for _k to 0; _k lt _resKeys.length; _k++>> <<set _key to _resKeys[_k]>> <<if def $materials[_key] and $materials[_key] gt 0>> <<set _currentWeight to setup.campWeight()>> <<set _itemWeight to (setup.CAMP_W[_key] !== undefined) ? setup.CAMP_W[_key] : 1.0>> <<set _incomingWeight to $materials[_key] * _itemWeight>> <<if _currentWeight + _incomingWeight <= _maxCampStorage>> <<set $campStorage[_key] += $materials[_key]>> <<set $materials[_key] to 0>> <<else>> <<set _availableSpace to _maxCampStorage - _currentWeight>> <<if _availableSpace gt 0 and _itemWeight gt 0>> <<set _transferableAmount to Math.floor(_availableSpace / _itemWeight)>> <<if _transferableAmount gt 0>> <<set $campStorage[_key] += _transferableAmount>> <<set $materials[_key] -= _transferableAmount>> <<endif>> <<endif>> <<endif>> <<endif>> <<endfor>> <<run setup.recalcStats()>> /* MED-LINK SUITE HEAL LOGIC */ <<if def $perks and $perks.medlink eq true and $hp lt $maxHp>> <<set _healAmount to Math.round($maxHp * 0.50)>> <<set $hp += _healAmount>> <<if $hp gt $maxHp>><<set $hp to $maxHp>><<endif>> <<set _showMedlinkHeal to true>> <<endif>> <<set _campTotalWeight to setup.campWeight()>> <<set _isStorageOverload to (_campTotalWeight gt _maxCampStorage)>> <<set _storageColor to (_isStorageOverload ? "#ef4444" : "#38bdf8")>> <<set _bgUrl to "images/locations/sanctuary_level_" + $campLevel + ".jpg">> /* DYNAMIC TITLE VARIABLES (TO PREVENT HTML BREAKING) */ <<set _headerColor to "#34d399">> <<set _headerTitle to "S.A.N.C.T.U.A.R.Y. COMMAND">> <<set _headerSub to ["LEVEL 1 UNDERGROUND BUNKER", "LEVEL 2 REINFORCED OUTPOST", "LEVEL 3 STEEL CITADEL", "LEVEL 4 NUCLEAR FORTIFIED HUB", "LEVEL 5 FORTRESS"][Math.min(Math.max(($sanctuaryLevels && $sanctuaryLevels.base) || 1, 1), 5) - 1]>> <<set _headerBorderColor to "#34d399">> <<set _bgStyle to "background: linear-gradient(rgba(0, 5, 0, 0.9), rgba(0, 10, 5, 0.99)), url('" + _bgUrl + "') center/cover no-repeat; flex: 1; border-left: 2px solid " + _headerBorderColor + "; border-right: 2px solid " + _headerBorderColor + "; padding: 0px 30px 20px 30px; box-sizing: border-box; box-shadow: inset 0 0 40px rgba(0,0,0,0.8); display: flex; flex-direction: column; justify-content: flex-start;">>> <<if $storyStage eq 1>> <<set _headerColor to "#ef4444">> <<set _headerTitle to "⚠️ NETWORK OVERRIDE DETECTED ⚠️">> <<set _headerSub to "UNKNOWN FREQUENCY INTERCEPTION">> <<set _headerBorderColor to "#ef4444">> <<set _bgStyle to "background: linear-gradient(rgba(0, 5, 0, 0.9), rgba(0, 10, 5, 0.99)), url('" + _bgUrl + "') center/cover no-repeat; flex: 1; border-left: 2px solid " + _headerBorderColor + "; border-right: 2px solid " + _headerBorderColor + "; padding: 0px 30px 20px 30px; box-sizing: border-box; box-shadow: inset 0 0 40px rgba(0,0,0,0.8); display: flex; flex-direction: column; justify-content: flex-start; background-color: rgba(20, 5, 5, 0.98);">>> <<elseif !$tutorialRope>> <<set _headerColor to "#fbbf24">> <<set _headerTitle to "INCOMING TRANSMISSION">> <<set _headerSub to "LOCAL RADIO UPLINK">> <<set _headerBorderColor to "#fbbf24">> <<set _bgStyle to "background: linear-gradient(rgba(0, 5, 0, 0.9), rgba(0, 10, 5, 0.99)), url('" + _bgUrl + "') center/cover no-repeat; flex: 1; border-left: 2px solid " + _headerBorderColor + "; border-right: 2px solid " + _headerBorderColor + "; padding: 0px 30px 20px 30px; box-sizing: border-box; box-shadow: inset 0 0 40px rgba(0,0,0,0.8); display: flex; flex-direction: column; justify-content: flex-start; background-color: rgba(20, 15, 5, 0.98);">>> <<endif>> <<set _wallLvl to ($sanctuaryLevels and $sanctuaryLevels.walls) ? $sanctuaryLevels.walls : 0>> <<if _wallLvl eq 0>> <<set _perimeterText to "EXPOSED (UNFORTIFIED)">> <<set _perimeterColor to "#ef4444">> <<elseif _wallLvl eq 1>> <<set _perimeterText to "LEVEL 1 - BARRICADES">> <<set _perimeterColor to "#fbbf24">> <<elseif _wallLvl eq 2>> <<set _perimeterText to "LEVEL 2 - REINFORCED">> <<set _perimeterColor to "#38bdf8">> <<elseif _wallLvl eq 3>> <<set _perimeterText to "LEVEL 3 - PLASTEEL">> <<set _perimeterColor to "#34d399">> <<elseif _wallLvl eq 4>> <<set _perimeterText to "LEVEL 4 - FORTIFIED">> <<set _perimeterColor to "#a855f7">> <<else>> <<set _perimeterText to "LEVEL 5 - FORTRESS">> <<set _perimeterColor to "#ec4899">> <<endif>> <style> #story { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; background: transparent !important; } .av-world { position: relative; margin-left: 17.5em; margin-right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; margin-top: 0 !important; padding-top: 0 !important; } .status-group { display: flex; flex-direction: column; margin-bottom: 8px; } .status-top { background: rgba(2, 10, 5, 0.9); border: 1px solid #047857; border-bottom: none; padding: 4px; text-align: center; font-size: 0.8rem; font-weight: bold; color: #e2e8f0; } .status-bot { background: rgba(0, 8, 3, 0.95); border: 1px solid #047857; padding: 6px; text-align: center; font-size: 0.7rem; color: #cbd5e1; } .news-manual-btn { background: rgba(56, 189, 248, 0.1); border: 1px solid #38bdf8; border-top: none; padding: 4px; text-align: center; font-size: 0.7rem; color: #38bdf8; font-weight: bold; cursor: pointer; transition: all 0.2s; } .news-manual-btn:hover { background: rgba(56, 189, 248, 0.2); color: #fff; box-shadow: 0 0 10px rgba(56, 189, 248, 0.4); } .isolated-map-wrapper { position: relative; width: 100%; height: 400px; margin-bottom: 8px; box-sizing: border-box; } .radar-container { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: url('images/locations/radar_grid_bg.jpg') center/cover no-repeat; border: 2px solid #10b981; border-radius: 4px; box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.5), 0 0 15px rgba(16, 185, 129, 0.2); overflow: hidden; box-sizing: border-box; } .map-node { position: absolute; display: flex; flex-direction: column; align-items: center; z-index: 100; transform: translate(-50%, -50%); } .map-node a.macro-link { display: flex; flex-direction: column; align-items: center; text-decoration: none !important; cursor: pointer; background: transparent !important; border: none !important; padding: 0 !important; color: inherit !important; } .map-node a.macro-link:hover .node-tooltip { opacity: 1; visibility: visible; transform: translateY(0); } .node-icon { width: 36px; height: 36px; background-color: rgba(10, 15, 10, 0.95); border: 2px solid #34d399; border-radius: 50%; display: flex; justify-content: center; align-items: center; font-size: 1.1rem; box-shadow: 0 0 12px rgba(52, 211, 153, 0.6); transition: all 0.2s ease; } .map-node a.macro-link:hover .node-icon { transform: scale(1.15); box-shadow: 0 0 22px #34d399; background-color: rgba(16, 185, 129, 0.5); } .node-label { margin-top: 4px; background: rgba(0, 0, 0, 0.9); border: 1px solid #34d399; color: #34d399; padding: 2px 8px; font-family: 'Courier New', monospace; font-weight: bold; font-size: 0.7rem; border-radius: 2px; white-space: nowrap; letter-spacing: 1px; box-shadow: 0 2px 5px rgba(0,0,0,0.8); } .node-tooltip { position: absolute; bottom: 100%; margin-bottom: 8px; width: 200px; background: rgba(10, 5, 0, 0.95); border: 1px solid #fbbf24; padding: 8px; border-radius: 3px; opacity: 0; visibility: hidden; transform: translateY(6px); transition: all 0.2s ease; z-index: 200; box-shadow: 0 5px 15px rgba(0,0,0,0.8); pointer-events: none; } .tooltip-title { color: #fbbf24; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem; text-align: center; border-bottom: 1px dashed #fbbf24; padding-bottom: 3px; margin-bottom: 4px; letter-spacing: 1px; } .tooltip-desc { color: #e2e8f0; font-size: 0.65rem; text-align: center; line-height: 1.3; } .node-blue .node-icon { border-color: #38bdf8; box-shadow: 0 0 12px rgba(56, 189, 248, 0.6); } .node-blue a.macro-link:hover .node-icon { box-shadow: 0 0 22px #38bdf8; } .node-blue .node-label { border-color: #38bdf8; color: #38bdf8; } .node-gold .node-icon { border-color: #fbbf24; box-shadow: 0 0 12px rgba(251, 191, 36, 0.6); color: #fbbf24; } .node-gold a.macro-link:hover .node-icon { box-shadow: 0 0 22px #fbbf24; } .node-gold .node-label { border-color: #fbbf24; color: #fbbf24; } .node-purple .node-icon { border-color: #c084fc; box-shadow: 0 0 12px rgba(192, 132, 252, 0.6); } .node-purple a.macro-link:hover .node-icon { box-shadow: 0 0 22px #c084fc; } .node-purple .node-label { border-color: #c084fc; color: #c084fc; } .safe-zone { margin-bottom: 6px; background: rgba(6, 78, 59, 0.35); border: 1px solid #059669; border-radius: 3px; padding: 8px 15px; display: flex; justify-content: space-between; align-items: center; min-height: 45px; } .safe-zone-desc { max-width: 75%; } .labor-panel { margin-bottom: 6px; background: rgba(20, 10, 30, 0.85); border: 1px solid #9333ea; border-radius: 3px; padding: 8px 15px; display: flex; justify-content: space-between; align-items: center; box-shadow: 0 4px 6px rgba(0,0,0,0.5); min-height: 45px; } .labor-panel:hover { border-color: #c084fc; box-shadow: 0 0 12px rgba(147, 51, 234, 0.3); } .transmission-box { background: rgba(0, 0, 0, 0.95); border: 2px solid #ef4444; padding: 40px; border-radius: 8px; margin-top: 10px; font-family: 'Courier New', monospace; box-shadow: 0 0 50px rgba(239, 68, 68, 0.4); width: 100%; box-sizing: border-box; } .transmission-box-grom { background: rgba(10, 5, 0, 0.95); border: 2px solid #fbbf24; padding: 40px; border-radius: 8px; margin-top: 10px; font-family: 'Courier New', monospace; box-shadow: 0 0 50px rgba(251, 191, 36, 0.4); width: 100%; box-sizing: border-box; } .cyber-trans-btn { display: block; width: 100%; background: rgba(239, 68, 68, 0.2); border: 2px solid #ef4444; color: #fca5a5 !important; padding: 18px; text-align: center; border-radius: 4px; font-weight: bold; font-size: 1.1rem; letter-spacing: 3px; cursor: pointer; text-decoration: none !important; transition: all 0.2s ease; } .cyber-trans-btn:hover { background: rgba(239, 68, 68, 0.4); border-color: #ff0000; color: #fff !important; box-shadow: 0 0 25px rgba(239, 68, 68, 0.8); } .cyber-grom-btn { display: block; width: 100%; background: rgba(251, 191, 36, 0.2); border: 2px solid #fbbf24; color: #fde047 !important; padding: 18px; text-align: center; border-radius: 4px; font-weight: bold; font-size: 1.1rem; letter-spacing: 3px; cursor: pointer; text-decoration: none !important; transition: all 0.2s ease; } .cyber-grom-btn:hover { background: rgba(251, 191, 36, 0.4); border-color: #fef08a; color: #fff !important; box-shadow: 0 0 25px rgba(251, 191, 36, 0.8); } </style> <div class="av-world"> <<run setup.cLife.notice()>> <div style="<<print _bgStyle>>"> <div @style="'position: relative; width: 100%; height: 130px; background: url(\'images/locations/sanctuary_command.jpg\') center/cover no-repeat; border-bottom: 1px solid ' + _headerBorderColor + '; border-radius: 0 0 6px 6px; margin-bottom: 15px; margin-top: 0 !important; box-shadow: 0 5px 15px rgba(0,0,0,0.8); display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center;'"> <div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0.85)); border-radius: 0 0 6px 6px;"></div> <div style="position: relative; z-index: 2;"> <h2 @style="'color: ' + _headerColor + '; margin: 0; letter-spacing: 4px; font-size: 1.6rem; text-shadow: 0 0 15px currentColor;'">[ <<print _headerTitle>> ]</h2> <div @style="'margin-top: 5px; font-size: 0.9rem; color: ' + _headerColor + '; letter-spacing: 3px; text-shadow: 0 0 8px rgba(0,0,0,0.9);'"><<print _headerSub>></div> </div> </div> <<timeBanner>> <<if $storyStage eq 1>> <!-- CIPHER VO PLAY & MUSIC QUEUE --> <<script>> var vo = SimpleAudio.tracks.get("cipher_intro"); var bgm = SimpleAudio.tracks.get("sanctuary_theme"); if(vo && bgm) { vo.play(); vo.off("ended").on("ended", function() { bgm.volume(0.25).loop(true).play(); }); } <</script>> <div class="transmission-box"> <div style="color: #ef4444; font-weight: bold; font-size: 1.3rem; margin-bottom: 20px; letter-spacing: 2px;">> INCOMING ENCRYPTED TRANSMISSION...</div> <div style="color: #cbd5e1; font-size: 1.15rem; line-height: 2; margin-bottom: 30px; border-left: 5px solid #ef4444; padding-left: 20px; display: flex; align-items: center; gap: 25px;"> <div style="flex: 0 0 120px; height: 120px; background-image: url('images/npcs/npc_cipher.jpg'); background-size: cover; background-position: center; border: 2px solid #ef4444; border-radius: 6px; box-shadow: 0 0 15px rgba(239,68,68,0.5);"></div> <div> "Listen closely, Operator. Whoever you are out there in the dark, booting up those pre-war military generators just sent an electromagnetic shockwave straight across the cold-bloods' core surveillance grid. The Reptilian vanguard units are already locking onto your telemetry coordinates. If you don't instantly fortify your perimeter and seal the bulkheads, you won't live to see dawn. I've just uploaded a suite of encrypted tactical defense protocols directly into your local mainframe. Study them, deploy them, and pray they hold. Once you've caught your breath and gathered your bearings, find me down in the Katowice tunnels so we can talk strategy face to face. Welcome to the frontline of the resistance." </div> </div> <div style="text-align: right; color: #f87171; font-weight: bold; font-style: italic; margin-bottom: 35px; font-size: 1.25rem;">— Cipher</div> <div> <<link "<span class='cyber-trans-btn'>ACCEPT TRANSMISSION & INITIALIZE SANCTUARY</span>" "Camp">> <<script>> var vo = SimpleAudio.tracks.get("cipher_intro"); if(vo) { vo.stop(); vo.off("ended"); } <</script>> <<set $storyStage to 2>><<set $activeQuest to "Meet Cipher">><<run window.setup.updateSidebar()>> <</link>> </div> </div> <<elseif !$tutorialRope>> <!-- GROM VO PLAY & MUSIC QUEUE --> <<script>> var vo = SimpleAudio.tracks.get("grom_intro"); var bgm = SimpleAudio.tracks.get("sanctuary_theme"); if(vo && bgm) { vo.play(); vo.off("ended").on("ended", function() { bgm.volume(0.25).loop(true).play(); }); } <</script>> <div class="transmission-box-grom"> <div style="color: #fbbf24; font-weight: 900; font-size: 1.4rem; letter-spacing: 4px; margin-bottom: 20px; border-bottom: 2px dashed #fbbf24; padding-bottom: 12px;">[ GROM, THE PIT MASTER ]</div> <div style="color: #cbd5e1; font-size: 1.15rem; line-height: 2; margin-bottom: 35px; display: flex; align-items: center; gap: 25px;"> <div style="flex: 0 0 120px; height: 120px; background-image: url('images/npcs/npc_grom.jpg'); background-size: cover; background-position: center; border: 2px solid #fbbf24; border-radius: 6px; box-shadow: 0 0 15px rgba(251,191,36,0.5);"></div> <div> <span style="color: #fbbf24; font-weight: bold; font-size: 1.2rem;">"Hey boss."</span> Listen up. Things down here in the labor pits are getting completely out of hand, and my hands are running dangerously short on muscle. To get you started on the right foot, I’ve left one reinforced rope stashed away for you over at the <strong style="color: #38bdf8; font-size: 1.2rem;">Sanctuary Hub</strong>. But don't get used to handouts—once that one's gone, you'll need to step up, head into the <strong style="color: #38bdf8; font-size: 1.2rem;">Workshop</strong>, and fabricate every single rope yourself with your own bare hands using raw materials. Next time you're out scouting the perimeter and spot some miserable wanderer whose health drops below the critical threshold, don't just put them out of their misery. Throw that heavy tether around their neck, bind them tight, and drag them straight down into the pits. We need an endless stream of fresh manual labor to feed the machinery, and I expect total compliance. Don't you dare disappoint me, boss. </div> </div> <div> <<link "<span class='cyber-grom-btn'>UNDERSTOOD. TRANSMISSION CLOSED.</span>" "Camp">> <<script>> var vo = SimpleAudio.tracks.get("grom_intro"); if(vo) { vo.stop(); vo.off("ended"); } <</script>> <<set $tutorialRope to true>><<if ndef $materials.rope>><<set $materials.rope to 0>><</if>><<set $materials.rope += 1>><<set $hasSanctuary to true>><<run window.setup.updateSidebar()>> <</link>> </div> </div> <<else>> <!-- MUSIC STARTS RIGHT AWAY ON NORMAL CAMP ENTRY --> <<audio "sanctuary_theme" volume 0.25 loop play>> <<if _showMedlinkHeal>> <div style="background: rgba(16, 185, 129, 0.15); border: 1px solid #10b981; color: #34d399; padding: 8px; font-family: 'Courier New', monospace; margin-bottom: 10px; font-size: 0.85rem; text-align: center; border-radius: 4px; font-weight: bold; box-shadow: 0 0 10px rgba(16, 185, 129, 0.2);"> [ MED-LINK SUITE ACTIVE: AUTOMATICALLY RESTORED +50% VITALITY ] </div> <<unset _showMedlinkHeal>> <<endif>> <div class="status-group"> <div class="status-top"> 🛡️ PERIMETER STATUS: <span style="color: <<print _perimeterColor>>;"><<print _perimeterText>></span> </div> <div class="status-bot"> <span style="color: #34d399; font-weight: bold;">[SYSTEM LOG]:</span> Camp layout active. Storage Capacity: <span style="color: <<print _storageColor>>;"><<print _campTotalWeight>> / <<print _maxCampStorage>> KG</span> </div> <!-- NEWSPAPER BUTTON THE PLAYER CAN OPEN MANUALLY --> <<if def $archiveNotice and $archiveNotice.length gt 0>> <div class="news-manual-btn"> <<link "📜 ARCHIVE NOTICE: A TRANSLATION IS COMPLETE (VISIT DR. MAYA)" "ArchiveWing">><</link>> </div> <</if>> <<if def $seeker>> <div class="news-manual-btn"> <<link "🚪 SOMEONE IS AT THE GATE... (OPEN THE CAMERA FEED)">><<run setup.seekerOpen()>><<goto "Encounter">><</link>> </div> <</if>> <div class="news-manual-btn"> <<link "📰 VIEW LATEST SECTOR BROADCAST (NEWSPAPER)">> <<script>> Dialog.create("", "news-modal-custom"); Dialog.wiki(Story.get("GameNewsModal").processText()); Dialog.open(); <</script>> <</link>> </div> </div> <!-- CAMP SCENE: illustrated bunker cutaway, pins sit on their matching rooms --> <div class="isolated-map-wrapper cs-wrap" data-tour="scene"> <div class="radar-container cs-inner"> <img class="cs-photo" src="images/locations/camp_scene.jpg" alt="" onerror="this.style.display='none'"> <div class="cs-pin cs-blue" data-tour="gates" style="top: 52%; left: 15.5%;"> <<link '<span class="cs-ring"><span class="cs-ico">🗺️</span></span><span class="cs-plate"><b>WASTELAND GATES</b><i>Sector map</i></span><span class="cs-tip">Plan expeditions and scout the irradiated wasteland sector map.</span>' "Map">><</link>> </div> <div class="cs-pin cs-gold cs-main" data-tour="hub" style="top: 68%; left: 41%;"> <<link '<span class="cs-ring"><span class="cs-ico">🏰</span></span><span class="cs-plate"><b>SANCTUARY HUB</b><i>Descend to the lower levels</i></span><span class="cs-tip">Take the stairs down into the vault. Manage infrastructure, upgrade base levels, and build survival facilities.</span>' "Sanctuary Hub">><</link>> </div> <div class="cs-pin cs-gold" data-tour="living" style="top: 52%; left: 81.5%;"> <<link '<span class="cs-ring"><span class="cs-ico">🛌</span></span><span class="cs-plate"><b>LIVING QUARTERS</b><i>Rest and recover</i></span><span class="cs-tip">Rest your weary bones, heal wounds, and pass time safely.</span>' "Rest">><</link>> </div> <div class="cs-pin cs-purple" data-tour="comm" style="top: 33%; left: 58.3%;"> <<link '<span class="cs-ring"><span class="cs-ico">📡</span></span><span class="cs-plate"><b>COMM-SERVER</b><i>Signals</i></span><span class="cs-tip">Decrypt reptilian broadcasts and monitor radio signals.</span>' "Comms">><</link>> </div> </div> </div> <<print setup.cLife.panelHtml()>> <!-- CAMP FACILITIES: side-by-side cards --> <div class="camp-cards"> <!-- SAFE ZONE FORAGING --> <div class="camp-card cc-green" data-tour="camp-forage"> <div class="camp-card-img"> <span class="camp-card-icon">🌿</span> <img src="images/locations/camp_safe_zone.jpg" alt="" onerror="this.style.display='none'"> </div> <div class="camp-card-body"> <div class="camp-card-title">SAFE ZONE FORAGING</div> <div class="camp-card-desc">Search the bunker exterior for raw materials. No stamina cost, but you only have 2 seconds!</div> </div> <div class="camp-card-act"> <<link "<span class='camp-btn'>SEARCH PERIMETER</span>" "SafeForage">><</link>> </div> </div> <!-- SURVIVOR LABOR MATRIX --> <div class="camp-card cc-purple" data-tour="camp-labor"> <div class="camp-card-img"> <span class="camp-card-icon">⚡</span> <img src="images/locations/camp_labor_matrix.jpg" alt="" onerror="this.style.display='none'"> </div> <div class="camp-card-body"> <div class="camp-card-title">SURVIVOR LABOR MATRIX</div> <div class="camp-card-desc">Assign captured survivors to automated resource extraction tasks.</div> </div> <div class="camp-card-act"> <<link "<span class='camp-btn'>MANAGE LABOR</span>" "Labor">><</link>> </div> </div> <!-- PVP ARENA --> <div class="camp-card cc-red" data-tour="camp-pvp"> <div class="camp-card-img"> <span class="camp-card-icon">⚔</span> <img src="images/locations/camp_pvp_arena.jpg" alt="" onerror="this.style.display='none'"> </div> <div class="camp-card-body"> <div class="camp-card-title">PVP ARENA</div> <div class="camp-card-desc">Duel the saved characters of other survivors. No death, no lost gear. Win Scrip and climb the ladder.</div> </div> <div class="camp-card-act"> <<link "<span class='camp-btn'>ENTER THE ARENA</span>" "PvPArena">><</link>> </div> </div> </div> <!-- SHARED CAMP STORAGE --> <div style="background: rgba(0, 10, 5, 0.95); border: 1px solid #047857; border-radius: 4px; padding: 15px; margin-top: 15px;"> <div style="text-align: center; color: #2ecc71; font-weight: bold; font-size: 0.9rem; letter-spacing: 2px; margin-bottom: 12px; border-bottom: 1px dashed #047857; padding-bottom: 8px;"> > RESOURCE CARGO (STORAGE: <span style="color: <<print _storageColor>>;"><<print _campTotalWeight>> / <<print _maxCampStorage>> KG</span>) </div> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 15px;"> <!-- Raw Materials --> <div style="background: rgba(0,255,0,0.05); border: 1px solid #115e59; padding: 15px; border-radius: 4px; font-size: 0.85rem;"> <div style="color: #2ecc71; font-weight: bold; margin-bottom: 10px; text-transform: uppercase;">Raw Materials</div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>📦 Scrap:</span> <span style="color:#a3e635; font-weight:bold;"><<print $campStorage.scrap>></span></div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>🪵 Wood:</span> <span style="color:#a3e635; font-weight:bold;"><<print $campStorage.wood>></span></div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>🧵 Cloth:</span> <span style="color:#a3e635; font-weight:bold;"><<print $campStorage.cloth>></span></div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>🔌 Electronics:</span> <span style="color:#a3e635; font-weight:bold;"><<print ($campStorage.electronics || 0)>></span></div> </div> <!-- Consumables --> <div style="background: rgba(52,152,219,0.05); border: 1px solid #1e3a8a; padding: 15px; border-radius: 4px; font-size: 0.85rem;"> <div style="color: #3498db; font-weight: bold; margin-bottom: 10px; text-transform: uppercase;">Consumables</div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>💊 Medicine:</span> <span style="color:#38bdf8; font-weight:bold;"><<print $campStorage.medicine>></span></div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>🌿 Medical Herbs:</span> <span style="color:#38bdf8; font-weight:bold;"><<print $campStorage.medicalHerb>></span></div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>🥫 Canned Food:</span> <span style="color:#38bdf8; font-weight:bold;"><<print $campStorage.food>></span></div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>🥩 Raw Meat:</span> <span style="color:#38bdf8; font-weight:bold;"><<print $campStorage.rawMeat>></span></div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>🍇 Berries:</span> <span style="color:#38bdf8; font-weight:bold;"><<print $campStorage.berries>></span></div> <div style="display: flex; justify-content: space-between; color: #cbd5e1;"><span>💧 Water:</span> <span style="color:#38bdf8; font-weight:bold;"><<print $campStorage.water>></span></div> </div> <!-- Valuables & Tech --> <div style="background: rgba(255,170,0,0.05); border: 1px solid #78350f; padding: 15px; border-radius: 4px; font-size: 0.85rem;"> <div style="color: #ffaa00; font-weight: bold; margin-bottom: 10px; text-transform: uppercase;">Valuables & Tech (Player Inventory)</div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>💾 Tech Data:</span> <span style="color:#fbbf24; font-weight:bold;"><<print $materials.techData>></span></div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>🪢 Rope:</span> <span style="color:#fbbf24; font-weight:bold;"><<print $materials.rope>></span></div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>🔋 Ammo:</span> <span style="color:#fbbf24; font-weight:bold;"><<print $materials.ammo>></span></div> <div style="display: flex; justify-content: space-between; color: #fbbf24; font-weight: bold; margin-top: 12px; border-top: 1px dashed #333; padding-top: 8px;"><span>🪙 Scrip:</span> <span><<print $materials.scrip>></span></div> <div style="display: flex; justify-content: space-between; color: #e879f9; font-weight: bold; margin-top: 6px;"><span>🎫 Broadcast Passes:</span> <span><<print ($globalBroadcastPass || 0)>></span></div> </div> </div> </div> <<endif>> </div> </div> <</nobr>>
<<setMenu "CaravanScavenge">> <style> #story { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; background: transparent !important; } .av-world { position: absolute; top: 0; left: 17.5em; right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; margin: 0 !important; padding: 20px !important; background: linear-gradient(rgba(0, 5, 0, 0.8), rgba(0, 10, 5, 0.95)), url('https://images.unsplash.com/photo-1515630278258-407f66498911?q=80&w=2000&auto=format&fit=crop') center/cover no-repeat; } .terminal-wide-core { width: 100%; background: rgba(5, 15, 5, 0.9); border-left: 2px solid #fbbf24; border-right: 2px solid #fbbf24; padding: 30px; font-family: 'Courier New', monospace; box-shadow: 0 0 50px rgba(251, 191, 36, 0.15), inset 0 0 30px rgba(0,0,0,0.8); display: flex; flex-direction: column; box-sizing: border-box; flex: 1; } .scavenge-box { background: rgba(0, 0, 0, 0.85); border: 2px solid #fbbf24; padding: 40px; border-radius: 8px; box-shadow: 0 0 30px rgba(251, 191, 36, 0.3); max-width: 800px; margin: 0 auto; width: 100%; box-sizing: border-box; } .cyber-btn { display: block; width: 100%; background: rgba(251, 191, 36, 0.15); border: 2px solid #fbbf24; color: #fde047 !important; padding: 15px; text-align: center; font-weight: bold; font-size: 0.95rem; letter-spacing: 2px; border-radius: 4px; cursor: pointer; text-decoration: none !important; margin-bottom: 15px; box-sizing: border-box; transition: all 0.2s; } .cyber-btn:hover { background: rgba(251, 191, 36, 0.35); box-shadow: 0 0 20px rgba(251, 191, 36, 0.6); color: #fff !important; } </style> <div class="av-world"> <div class="terminal-wide-core"> <div class="scavenge-box"> <div style="text-align: center; border-bottom: 1px dashed #fbbf24; padding-bottom: 15px; margin-bottom: 25px;"> <h2 style="color: #fbbf24; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px #fbbf24;">[ WRECKED CONVOY SITE ]</h2> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 8px;">FLIPPED PRE-WAR SUPPLY CARAVAN - SECTOR 07</div> </div> <div style="font-size: 0.95rem; line-height: 1.8; margin-bottom: 30px; color: #cbd5e1;"> The heavy armored transport lies flipped on its side, leaking synthetic oil into the scorched earth. Reptilian patrol drones have recently cleared the area, but the secure cargo hold is still intact, sealed behind an emergency electromagnetic latch. <br><br> You have a brief window to scour the wreckage before scouts return. How do you want to break into the main vault? </div> <<if ndef _scavengeDone and $caravanHack isnot "won">> <div style="display: flex; flex-direction: column; gap: 15px;"> <<set _hasTechData to (($materials.techData || 0) gte 1)>> <<if $caravanHack is "failed">> <div style="background: rgba(69, 10, 10, 0.5); border: 1px dashed #ef4444; color: #fca5a5; padding: 12px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.8rem; border-radius: 4px; margin-bottom: 10px;"> [ TERMINAL WIPED ITSELF. THE TECH DATA IS GONE. ONLY THE BULKHEAD IS LEFT ] </div> <<elseif _hasTechData>> <<link "<span class='cyber-btn'>⚡ HACK THE EMERGENCY TERMINAL (COSTS 1 TECH DATA, LIVE BREACH)</span>" "HackGame">> <<run setup.hackBegin({ ctx: "caravan", game: "rand" })>> <</link>> <<else>> <div style="background: rgba(15, 23, 42, 0.6); border: 1px dashed #7e22ce; color: #d8b4fe; padding: 12px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.8rem; border-radius: 4px; margin-bottom: 10px;"> [ LOCKED: REQUIRE 1x TECH DATA TO HACK TERMINAL (CURRENT: <<print ($materials.techData || 0)>>) ] </div> <</if>> <<link "<span class='cyber-btn' style='border-color:#ef4444; color:#fca5a5 !important; background:rgba(239,68,68,0.15);'>🔨 FORCE THE BULKHEAD WITH A CROWBAR (LOUD & RISKY -> SQUAD COMBAT)</span>" "Combat">> <<unset $caravanHack>> <<set $activeSideQuest to false>> <<set _squadSize to random(2, 5)>> <<set $enemyGroup to []>> <<for _i to 0; _i lt _squadSize; _i++>> <<set _npcHp to random(35, 50)>> <<run $enemyGroup.push({ id: _i, name: "Reptilian Scrapper", hp: _npcHp, currentHp: _npcHp, maxHp: _npcHp, minDmg: 8, maxDmg: 14, drop: "techData", isDead: false })>> <</for>> <<set $isGroupCombat to true>> <<set $enemy to $enemyGroup[0]>> <<set $targetPOIName to "Flipped Caravan Ambush">> <<set $targetPOILoot to "military">> <<set $currentLocation to "Caravan Wreckage">> <</link>> </div> <<else>> <div style="background: rgba(46, 204, 113, 0.1); border: 1px solid #2ecc71; padding: 15px; border-radius: 4px; text-align: center; color: #2ecc71; font-weight: bold; margin-bottom: 25px;"> >> CONVOY COMPLETELY SALVAGED. RADAR BEACON DEACTIVATED. </div> <div style="text-align: center;"> <<link "<span class='cyber-btn' style='background:rgba(46,204,113,0.3); border-color:#2ecc71; color:#2ecc71 !important;'>RETURN TO TACTICAL RADAR MAP</span>" "Map">> <<unset _scavengeDone>> <<unset $caravanHack>> <<run window.setup.updateSidebar()>> <</link>> </div> <<endif>> </div> </div> </div>
<style> #ui-dialog-body { padding: 0 !important; background-color: #050a05 !important; } /* SINGLE EXIT (X) BUTTON (FIX THAT PREVENTS OVERLAP) */ #ui-dialog-close { color: #ffffff !important; background: #ef4444 !important; border: 2px solid #b91c1c !important; border-radius: 4px !important; width: 32px !important; height: 32px !important; opacity: 1 !important; box-shadow: 0 0 10px rgba(239, 68, 68, 0.6) !important; } #ui-dialog-close:hover { background: #dc2626 !important; box-shadow: 0 0 15px rgba(220, 38, 38, 0.9) !important; } #ui-dialog-close::before { content: none !important; } #ui-dialog-close::after { content: "X" !important; color: #ffffff !important; font-family: Arial, sans-serif !important; font-size: 1.1rem !important; font-weight: bold !important; position: absolute !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; } </style> <<nobr>> <<setMenu "Character">> <<run setup.recalcStats()>> <<set _perkName to "Unknown">><<set _perkDesc to "No data available.">> <<if $playerBackground eq "Scavenger">><<set _perkName to "Expert Forager">><<set _perkDesc to "Free Camp Foraging grants +1 extra item. Outer Map Encounters grant +1 extra loot.">> <<elseif $playerBackground eq "Mercenary">><<set _perkName to "Tactical Retreat">><<set _perkDesc to "100% Safe Flee chance in combat, no HP penalty.">> <<elseif $playerBackground eq "Techie">><<set _perkName to "Master Crafter">><<set _perkDesc to "Crafts items at +1 Base Rarity. Unlocks exclusive RED 'Godlike' tier.">> <<elseif $playerBackground eq "Field Medic">><<set _perkName to "Safe Flee">><<set _perkDesc to "Always able to retreat without taking damage.">> <<elseif $playerBackground eq "Smuggler">><<set _perkName to "Human Trafficker">><<set _perkDesc to "Finds double survivors. 50% Off Black Market recipes.">> <<elseif $playerBackground eq "Heavy Gunner">><<set _perkName to "Scrap Artillery">><<set _perkDesc to "Scrap Launcher uses 1 Scrap per shot. 50% chance to hit for massive dmg.">> <<elseif $playerBackground eq "Botanist">><<set _perkName to "Green Thumb">><<set _perkDesc to "Starts the game with the Sanctuary Greenhouse already unlocked.">> <<elseif $playerBackground eq "Broadcaster">><<set _perkName to "Signal Intercept">><<set _perkDesc to "Generates +1 Tech Data daily. Access to hidden frequencies.">> <<elseif $playerBackground eq "Nomad">><<set _perkName to "Wasteland Walker">><<set _perkDesc to "Map exploration and outside actions cost 50% less Stamina.">> <<elseif $playerBackground eq "Mutated Anomaly">><<set _perkName to "Toxic Blood">><<set _perkDesc to "Inflicts passive Toxic damage over time. 100% Safe Flee.">><</if>> <style> .popup-frame { display: flex; justify-content: space-between; align-items: stretch; gap: 20px; width: 100%; box-sizing: border-box; } .popup-core-content { flex: 1; background-color: #050a05; padding: 25px; border: 2px solid #10b981; border-radius: 8px; font-family: 'Courier New', monospace; box-shadow: 0 0 20px rgba(16, 185, 129, 0.15); box-sizing: border-box; } .dossier-wrapper { display: flex; gap: 25px; } .dossier-avatar { flex: 0 0 220px; display: flex; flex-direction: column; gap: 10px; } .dossier-avatar-img { width: 100%; border: 2px solid #10b981; border-radius: 4px; box-shadow: 0 0 15px rgba(16,185,129,0.3); } .dossier-details { flex: 1; display: flex; flex-direction: column; justify-content: space-between; gap: 15px; } .stat-bar-container { background: #022c22; border: 1px solid #065f46; height: 22px; border-radius: 3px; position: relative; overflow: hidden; margin-top: 5px; } .stat-bar-fill { height: 100%; transition: width 0.3s ease; } .stat-bar-text { position: absolute; top: 0; left: 0; width: 100%; text-align: center; color: #fff; font-size: 0.85rem; font-weight: bold; line-height: 22px; text-shadow: 1px 1px 2px #000; letter-spacing: 1px; } .stat-box { background: rgba(16,185,129,0.05); border: 1px dashed #10b981; padding: 15px; border-radius: 4px; } .stat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; } .return-btn-container { text-align: center; margin-top: 25px; } .return-btn-container a { display: inline-block; background: rgba(239, 68, 68, 0.1); border: 2px solid #ef4444; color: #ef4444 !important; padding: 15px 40px; font-size: 1.5rem; font-weight: bold; letter-spacing: 4px; text-decoration: none !important; border-radius: 4px; box-shadow: 0 0 15px rgba(239, 68, 68, 0.3); transition: all 0.3s ease; text-transform: uppercase; } .return-btn-container a:hover { background: #ef4444; color: #000 !important; box-shadow: 0 0 30px rgba(239, 68, 68, 0.8); transform: scale(1.02); } </style> <<if ndef $playerBackground or $playerBackground eq "None" or $playerBackground eq "">><<set _raw to "scavenger">><<else>><<set _raw to $playerBackground.toLowerCase()>><</if>> <<if _raw eq "field medic">><<set _raw to "medic">><</if>> <<if _raw eq "mutated anomaly">><<set _raw to "mutated">><</if>> <<if _raw eq "heavy gunner">><<set _raw to "heavy_gunner">><</if>> <<set _fmtClass to _raw.replace(/ /g, "_")>> <<if ndef $playerGender or $playerGender eq "">><<set _gen to "classified">><<else>><<set _gen to $playerGender.toLowerCase()>><</if>> /* Updated to the image path on your own domain/server folder */ <<set _avatarSrc to "images/avatars/" + _gen + "_" + _fmtClass + ".jpg">> <<if setup.exoActive()>><<set _avatarSrc to "images/avatars/exo_armor.jpg">><</if>> <div class="popup-frame"> <div class="popup-core-content"> <div class="dossier-wrapper"> <div class="dossier-avatar"> <img @src="_avatarSrc" class="dossier-avatar-img" onerror="this.src='https://placehold.co/220x330/000/10b981?font=courier&text=NO+SIGNAL';"> <<if setup.hasExo()>> <div style="text-align:center; margin:2px 0 6px;">@@color:#e9d5ff;border:1px solid #a855f7;display:block;padding:7px 8px;border-radius:4px;font-size:0.75rem;font-weight:bold;<<link "🔁 SWITCH TO <<print (setup.exoActive() ? 'REGULAR LOOK' : 'EXOFRAME SKIN')>>">><<run setup.exoSwitch()>><<replace "#ui-dialog-body">><<print Story.get("Character").processText()>><</replace>><</link>>@@</div> <</if>> <div style="background: rgba(16,185,129,0.1); border: 1px solid #10b981; padding: 10px; border-radius: 4px; text-align: center; margin-top: 5px;"> <div style="color:#10b981; font-weight:bold; font-size: 1.2rem; letter-spacing:3px;">LEVEL <<print $level>></div> <div class="stat-bar-container"> <div class="stat-bar-fill" @style="'width:' + Math.min(($xp / $xpMax) * 100, 100) + '%; background:#3b82f6; box-shadow: inset 0 0 10px #fff;'"></div> <div class="stat-bar-text">XP: <<print $xp>> / <<print $xpMax>></div> </div> </div> </div> <div class="dossier-details"> <div style="border-bottom: 1px dashed #10b981; padding-bottom: 10px;"> <h2 style="margin:0; color:#10b981; letter-spacing:5px; text-shadow: 0 0 10px #10b981;">[ OPERATOR DOSSIER ]</h2> <div style="color:#6ee7b7; font-size:0.95rem; margin-top:5px; letter-spacing: 1px;">BIOMETRIC & COMBAT STATISTICS</div> </div> <div class="stat-box stat-grid"> <div><span style="color:#64748b; font-size:0.85rem;">DESIGNATION:</span> <span style="color:#fff; font-weight:bold; display:block; font-size: 1.1rem;"><<print $playerName>></span></div> <div><span style="color:#64748b; font-size:0.85rem;">CLASS:</span> <span style="color:#ffaa00; font-weight:bold; display:block; font-size: 1.1rem;"><<print $playerBackground>></span></div> <div><span style="color:#64748b; font-size:0.85rem;">AGE / GENDER:</span> <span style="color:#fff; font-weight:bold; display:block; font-size: 1.1rem;"><<print $playerAge>> / <<print $playerGender>></span></div> <div><span style="color:#64748b; font-size:0.85rem;">DEPLOYMENT SECTOR:</span> <span style="color:#38bdf8; font-weight:bold; display:block; font-size: 1.1rem;"><<print $startingCity>></span></div> </div> <div class="stat-box"> <div style="color:#10b981; font-weight:bold; margin-bottom:15px; border-bottom:1px solid #065f46; padding-bottom:5px; letter-spacing: 2px;">> VITAL SIGNS</div> <div style="margin-bottom:15px;"> <div style="display:flex; justify-content:space-between; font-size:0.9rem; color:#ef4444; font-weight:bold; margin-bottom: 5px;"><span>HEALTH (HP)</span><span><<print $hp>> / <<print $maxHp>></span></div> <div class="stat-bar-container"><div class="stat-bar-fill" @style="'width:' + Math.max(0, Math.min(($hp / $maxHp) * 100, 100)) + '%; background:#ef4444;'"></div></div> </div> <div> <div style="display:flex; justify-content:space-between; font-size:0.9rem; color:#eab308; font-weight:bold; margin-bottom: 5px;"><span>STAMINA</span><span><<print $stamina>> / <<print $maxStamina>></span></div> <div class="stat-bar-container"><div class="stat-bar-fill" @style="'width:' + Math.max(0, Math.min(($stamina / $maxStamina) * 100, 100)) + '%; background:#eab308;'"></div></div> </div> </div> <div class="stat-box stat-grid" style="background: rgba(239, 68, 68, 0.05); border-color: #ef4444;"> <div style="display: flex; flex-direction: column; align-items: center; border-right: 1px dashed #ef4444;"> <span style="color:#ef4444; font-weight:bold; font-size: 0.8rem; letter-spacing: 1px;">COMBAT POWER</span> <span style="color:#fff; font-size: 1.5rem; font-weight: bold; text-shadow: 0 0 10px #ef4444;">⚔️ <<print $playerDmgBonus>></span> </div> <div style="display: flex; flex-direction: column; align-items: center;"> <span style="color:#3b82f6; font-weight:bold; font-size: 0.8rem; letter-spacing: 1px;">TOTAL ARMOR</span> <span style="color:#fff; font-size: 1.5rem; font-weight: bold; text-shadow: 0 0 10px #3b82f6;">🛡️ <<print $playerArmor>></span> </div> </div> </div> </div> <div style="margin-top: 25px; background: rgba(168, 85, 247, 0.05); border: 1px solid #a855f7; padding: 20px; border-radius: 8px; box-shadow: 0 0 15px rgba(168,85,247,0.1);"> <div style="color:#a855f7; font-weight:bold; font-size: 1.1rem; letter-spacing: 2px; border-bottom: 1px dashed #a855f7; padding-bottom: 8px; margin-bottom: 12px;">> OPERATOR SPECIALTY PERK</div> <div style="display: flex; align-items: center; gap: 15px;"> <div style="background: #a855f7; color: #000; padding: 10px 15px; border-radius: 4px; font-weight: bold; font-size: 1.1rem; text-transform: uppercase;"><<print _perkName>></div> <div style="color: #cbd5e1; font-size: 1rem; line-height: 1.5;"><<print _perkDesc>></div> </div> </div> <div style="margin-top: 25px; background: rgba(251, 191, 36, 0.06); border: 1px solid #fbbf24; padding: 18px; border-radius: 8px; display: flex; justify-content: space-between; align-items: center; gap: 15px; flex-wrap: wrap;"> <div> <div style="color:#fbbf24; font-weight:bold; font-size: 1.05rem; letter-spacing: 2px;">> NEURAL TREE</div> <div style="color:#94a3b8; font-size: 0.8rem; margin-top: 4px;">Level <<print $level>> | Unspent points: <strong style="color:#fde047;"><<print ($skillPoints || 0)>></strong>. Inspect your build any time, you never have to spend a point.</div> </div> <div>@@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;display:inline-block;padding:10px 18px;border-radius:4px;letter-spacing:1px;<<link "OPEN NEURAL TREE">><<run setup.openSkillTree()>><</link>>@@</div> </div> <div style="margin-top: 14px; display: flex; justify-content: space-between; align-items: center; gap: 15px; flex-wrap: wrap; border: 1px dashed #334155; padding: 12px 16px; border-radius: 8px;"> <div style="color:#94a3b8; font-size: 0.8rem;">ACCOUNT: <strong style="color:#e2e8f0;"><<print $playerName>></strong> | Account responsibility lies with the user.</div> <div>@@color:#cbd5e1;border:1px solid #64748b;display:inline-block;padding:8px 14px;border-radius:4px;font-size:0.8rem;<<link "🔑 CHANGE PASSWORD">><<run window.avOpenPasswordDialog()>><</link>>@@</div> </div> <div class="return-btn-container"> <<link "[ CLOSE DOSSIER & RETURN ]">><<run Dialog.close()>><</link>> </div> </div> </div> <</nobr>>
<<nobr>> <style> .neon-panel { background-color: #050a05; padding: 30px; border: 2px solid #00ff66; border-radius: 8px; font-family: 'Courier New', monospace; box-shadow: 0 0 25px rgba(0, 255, 102, 0.15), inset 0 0 20px rgba(0, 255, 102, 0.05); color: #00ff66; } .neon-header { text-align: center; margin-bottom: 25px; border-bottom: 1px dashed #00ff66; padding-bottom: 15px; } .radar-container { position: relative; width: 100%; height: 640px; background-color: #020602; border: 2px solid #00ff00; border-radius: 4px; box-shadow: inset 0 0 50px rgba(0, 255, 0, 0.4); overflow: hidden; margin-bottom: 25px; } .radar-map-bg { position: absolute; top: -40px; left: -40px; right: -40px; bottom: -40px; width: calc(100% + 80px); height: calc(100% + 80px); pointer-events: none; opacity: 0.55; filter: grayscale(100%) sepia(100%) hue-rotate(85deg) saturate(350%) contrast(120%) brightness(85%); z-index: 1; } .radar-grid { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-image: linear-gradient(rgba(0, 255, 0, 0.15) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 255, 0, 0.15) 1px, transparent 1px); background-size: 40px 40px; z-index: 2; pointer-events: none; } .radar-sweep { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: linear-gradient(to bottom, transparent 0%, rgba(0,255,0,0.1) 50%, transparent 100%); animation: scan 4s linear infinite; pointer-events: none; z-index: 3; } @keyframes scan { 0% { transform: translateY(-100%); } 100% { transform: translateY(100%); } } .demo-avatar-wrapper { display: flex; justify-content: space-between; align-items: stretch; gap: 25px; margin-bottom: 25px; background: rgba(0, 20, 10, 0.6); padding: 20px; border: 1px solid #115e59; border-radius: 4px; box-shadow: inset 0 0 15px rgba(0,0,0,0.8); } .demo-inputs { flex: 1; display: flex; flex-direction: column; gap: 15px; justify-content: center; } .avatar-preview { flex: 0 0 180px; width: 180px; height: 250px; border: 2px solid #10b981; border-radius: 6px; overflow: hidden; background: #000; box-shadow: 0 0 20px rgba(16, 185, 129, 0.4); position: relative; } .avatar-preview img { width: 100%; height: 100%; object-fit: cover; } .avatar-scanline { position: absolute; top: 0; left: 0; width: 100%; height: 10px; background: rgba(16, 185, 129, 0.3); animation: scan 3s linear infinite; pointer-events: none; } input[type="text"], input[type="number"], select { background: rgba(0,0,0,0.9) !important; border: 1px solid #34d399 !important; color: #34d399 !important; padding: 10px !important; font-family: 'Courier New', monospace !important; border-radius: 2px; width: 100%; box-sizing: border-box; box-shadow: 0 0 10px rgba(52, 211, 153, 0.2); } input[type="text"]:focus, input[type="number"]:focus, select:focus { outline: none; box-shadow: 0 0 20px rgba(52, 211, 153, 0.6); background: #000 !important; } .class-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 15px; margin-top: 15px; } .class-card { cursor: pointer; padding: 15px; border: 1px solid #334155; border-radius: 6px; background: rgba(0,0,0,0.7); transition: all 0.3s ease; position: relative; overflow: hidden; display: block; text-decoration: none !important; } .class-card:hover { transform: translateY(-3px); box-shadow: 0 5px 15px rgba(0,0,0,0.5); border-color: #00ff66; } .card-scavenger { border-left: 4px solid #10b981; } .card-mercenary { border-left: 4px solid #ef4444; } .card-techie { border-left: 4px solid #3b82f6; } .card-medic { border-left: 4px solid #f8fafc; } .card-smuggler { border-left: 4px solid #f59e0b; } .card-heavy { border-left: 4px solid #a855f7; } .card-botanist { border-left: 4px solid #22c55e; } .card-broadcaster { border-left: 4px solid #f97316; } .card-nomad { border-left: 4px solid #94a3b8; } .card-mutated { border-left: 4px solid #84cc16; } .deploy-btn { display: block; width: 100%; background: rgba(0, 255, 102, 0.1); border: 2px solid #00ff66; color: #00ff66 !important; padding: 18px; font-size: 1.3rem; font-weight: bold; letter-spacing: 4px; text-decoration: none !important; border-radius: 4px; box-shadow: 0 0 15px rgba(0, 255, 102, 0.3); transition: all 0.3s ease; text-align: center; margin-top: 30px; } .deploy-btn:hover { background: #00ff66; color: #000 !important; box-shadow: 0 0 35px rgba(0, 255, 102, 0.8); } </style> <<if ndef $playerName>><<set $playerName to "">><<endif>> <<if ndef $characterName>><<set $characterName to "">><<endif>> <<if ndef $playerGender>><<set $playerGender to "Male">><<endif>> <<if ndef $playerAge>><<set $playerAge to 24>><<endif>> <<set $startingCity to "Katowice">> <<if ndef $playerBackground>><<set $playerBackground to "Scavenger">><<endif>> <div class="neon-panel"> <div class="neon-header"> <h2 style="color: #00ff66; margin: 0; letter-spacing: 4px; text-shadow: 0 0 15px rgba(0,255,102,0.8); font-size: 2rem;">[ NEW OPERATOR REGISTRATION ]</h2> <div style="color: #fbbf24; font-size: 1rem; margin-top: 10px; letter-spacing: 2px;">ENTER PERSONAL IDENTIFICATION DATA.</div> </div> <!-- 1. RADAR MAP (KATOWICE FIXED) --> <div class="radar-container"> <img class="radar-map-bg" src="images/locations/silesia_sector_map.jpg" alt="" draggable="false" style="top:0; left:0; right:0; bottom:0; width:100%; height:100%; object-fit:cover; opacity:0.85; filter:none;" onerror="this.style.display='none'"> <div class="radar-grid"></div> <div class="radar-sweep"></div> <div style="position:absolute; top:50%; left:50%; transform:translate(-50%, -50%); text-align:center; z-index:10; width: 100%; pointer-events:none;"> <div style="font-size: 0.85rem; color: #38bdf8; margin-bottom: 5px; letter-spacing: 3px; text-shadow: 0 0 8px #38bdf8;">[ SECTOR SURVEY ACTIVE ]</div> <div style="font-size: 0.75rem; color: #aaa; letter-spacing: 2px;">TARGETING DEPLOYMENT ZONE:</div> <div style="color:#00ff66; font-size:2rem; font-weight:bold; letter-spacing: 6px; text-shadow:0 0 15px #00ff66; margin-top: 5px;"><<print $startingCity.toUpperCase()>></div> </div> </div> <!-- 2. IDENTITY INFO AND LIVE AVATAR --> <div class="demo-avatar-wrapper"> <div class="demo-inputs"> <strong style="color: #00ff66; font-size: 1.2rem; text-shadow: 0 0 8px #00ff66; margin-bottom: 5px;">> INPUT DEMOGRAPHICS:</strong> <div style="display: flex; gap: 15px; flex-wrap: wrap;"> <div style="flex: 2; min-width: 200px;"> <span style="color:#34d399; font-size:0.9rem; margin-bottom:5px; font-weight:bold; display:block;">DESIGNATION (CHARACTER NAME)</span> <div class="terminal-input"> <!-- Only changes the character name, does not affect the account name --> <<textbox "$characterName" $characterName>> </div> </div> <div style="flex: 1; min-width: 120px;"> <span style="color:#34d399; font-size:0.9rem; margin-bottom:5px; font-weight:bold; display:block;">AGE (18-<<print ($playerBackground eq "Mutated Anomaly" ? 999 : 60)>>)</span> <div class="terminal-input"> <<numberbox "$playerAge" $playerAge>> <<run setTimeout(function () { var i = document.querySelector(".demo-inputs input[type=number]"); if (i) { i.min = 18; i.max = (State.variables.playerBackground === "Mutated Anomaly") ? 999 : 60; } }, 50)>> </div> </div> </div> <div style="display: flex; gap: 15px; flex-wrap: wrap; align-items: flex-end;"> <div style="flex: 1; min-width: 140px;"> <span style="color:#34d399; font-size:0.9rem; margin-bottom:5px; font-weight:bold; display:block;">GENDER</span> <div class="terminal-input"> <<listbox "$playerGender" autoselect>> <<option "Male" "Male">> <<option "Female" "Female">> <<option "Classified" "Classified">> <</listbox>> </div> </div> <div style="flex: 2; min-width: 200px;"> <span style="color:#34d399; font-size:0.9rem; margin-bottom:5px; font-weight:bold; display:block;">LOCATION (LOCKED)</span> <div class="terminal-input"> <input type="text" value="Katowice (Sector-01)" disabled style="background: rgba(0,20,10,0.8) !important; color: #10b981 !important; border-color: #10b981 !important; font-weight: bold; cursor: not-allowed;"> </div> </div> </div> </div> <div class="avatar-preview"> <<set _genFolder to $playerGender.toLowerCase()>> <<set _bgLower to $playerBackground.toLowerCase()>> <<if _bgLower eq "field medic">><<set _bgLower to "medic">><<endif>> <<if _bgLower eq "mutated anomaly">><<set _bgLower to "mutated">><<endif>> <<if _bgLower eq "heavy gunner">><<set _bgLower to "heavy_gunner">><<endif>> <<set _avatarFile to "images/avatars/" + _genFolder + "_" + _bgLower.replace(/ /g, "_") + ".jpg">> <img @src="_avatarFile" onerror="this.src='https://placehold.co/220x330/000/10b981?font=courier&text=NO+SIGNAL';"> <div class="avatar-scanline"></div> <div style="position: absolute; bottom: 5px; width: 100%; text-align: center; color: #10b981; font-size: 0.7rem; font-weight: bold; background: rgba(0,0,0,0.7); padding: 2px 0;"><<print $playerBackground.toUpperCase()>></div> </div> </div> <!-- 3. CLASS SELECTION --> <div style="border-top: 1px dashed #115e59; padding-top: 25px;"> <strong style="color: #fbbf24; font-size: 1.2rem; text-shadow: 0 0 8px #fbbf24;">> SELECT FORMER OCCUPATION:</strong> <div class="class-grid"> <<link "<div class='class-card card-scavenger'><strong style='color:#10b981; font-size:1.15rem; text-shadow: 0 0 5px rgba(16,185,129,0.5);'>The Scavenger</strong><div style='margin-top:10px; font-size:0.95rem; color:#cbd5e1; font-weight:bold;'>+25% Loot from Scavenging, Scavenger's Rucksack.</div><div style='margin-top:10px; padding-top:10px; border-top: 1px dashed #334155; font-size:0.85rem; color:#34d399; line-height: 1.5;'><strong>[PERK: Expert Forager]</strong><br><span style='color:#94a3b8;'>Any raw resource found (wood, scrap, cloth etc.) is automatically increased by +1.</span></div></div>">> <<set $playerBackground to "Scavenger">> <<goto "Character Creation">> <</link>> <<link "<div class='class-card card-mercenary'><strong style='color:#ef4444; font-size:1.15rem; text-shadow: 0 0 5px rgba(239,68,68,0.5);'>The Mercenary</strong><div style='margin-top:10px; font-size:0.95rem; color:#cbd5e1; font-weight:bold;'>+20% Max HP, Combat Knife.</div><div style='margin-top:10px; padding-top:10px; border-top: 1px dashed #334155; font-size:0.85rem; color:#fca5a5; line-height: 1.5;'><strong>[PERK: Tactical Retreat]</strong><br><span style='color:#94a3b8;'>100% Safe Flee chance in combat, no HP penalty.</span></div></div>">> <<set $playerBackground to "Mercenary">> <<goto "Character Creation">> <</link>> <<link "<div class='class-card card-techie'><strong style='color:#3b82f6; font-size:1.15rem; text-shadow: 0 0 5px rgba(59,130,246,0.5);'>The Techie</strong><div style='margin-top:10px; font-size:0.95rem; color:#cbd5e1; font-weight:bold;'>+5 Tech Data.</div><div style='margin-top:10px; padding-top:10px; border-top: 1px dashed #334155; font-size:0.85rem; color:#7dd3fc; line-height: 1.5;'><strong>[PERK: Master Crafter]</strong><br><span style='color:#94a3b8;'>Crafts items at +1 Base Rarity. Unlocks exclusive RED 'Godlike' tier.</span></div></div>">> <<set $playerBackground to "Techie">> <<goto "Character Creation">> <</link>> <<link "<div class='class-card card-medic'><strong style='color:#f8fafc; font-size:1.15rem; text-shadow: 0 0 5px rgba(248,250,252,0.5);'>The Field Medic</strong><div style='margin-top:10px; font-size:0.95rem; color:#cbd5e1; font-weight:bold;'>+5 Medicine, +10 Cloth.</div><div style='margin-top:10px; padding-top:10px; border-top: 1px dashed #334155; font-size:0.85rem; color:#e2e8f0; line-height: 1.5;'><strong>[PERK: Triage Expert]</strong><br><span style='color:#94a3b8;'>Starts the game with the Sanctuary Infirmary (Medical Tent) already unlocked. 100% Safe Flee.</span></div></div>">> <<set $playerBackground to "Field Medic">> <<goto "Character Creation">> <</link>> <<link "<div class='class-card card-smuggler'><strong style='color:#f59e0b; font-size:1.15rem; text-shadow: 0 0 5px rgba(245,158,11,0.5);'>The Smuggler</strong><div style='margin-top:10px; font-size:0.95rem; color:#cbd5e1; font-weight:bold;'>+50% Scrap from Encounters.</div><div style='margin-top:10px; padding-top:10px; border-top: 1px dashed #334155; font-size:0.85rem; color:#fde047; line-height: 1.5;'><strong>[PERK: Human Trafficker]</strong><br><span style='color:#94a3b8;'>Finds double survivors on missions. 50% Off Black Market recipes.</span></div></div>">> <<set $playerBackground to "Smuggler">> <<goto "Character Creation">> <</link>> <<link "<div class='class-card card-heavy'><strong style='color:#a855f7; font-size:1.15rem; text-shadow: 0 0 5px rgba(168,85,247,0.5);'>The Heavy Gunner</strong><div style='margin-top:10px; font-size:0.95rem; color:#cbd5e1; font-weight:bold;'>Scrap Launcher, +50 Scrap.</div><div style='margin-top:10px; padding-top:10px; border-top: 1px dashed #334155; font-size:0.85rem; color:#d8b4fe; line-height: 1.5;'><strong>[PERK: Scrap Artillery]</strong><br><span style='color:#94a3b8;'>Scrap Launcher uses 1 Scrap per shot. 50% chance to hit for massive dmg.</span></div></div>">> <<set $playerBackground to "Heavy Gunner">> <<goto "Character Creation">> <</link>> <<link "<div class='class-card card-botanist'><strong style='color:#22c55e; font-size:1.15rem; text-shadow: 0 0 5px rgba(34,197,94,0.5);'>The Botanist</strong><div style='margin-top:10px; font-size:0.95rem; color:#cbd5e1; font-weight:bold;'>+15% Food & +15% Water from all activities.</div><div style='margin-top:10px; padding-top:10px; border-top: 1px dashed #334155; font-size:0.85rem; color:#86efac; line-height: 1.5;'><strong>[PERK: Green Thumb]</strong><br><span style='color:#94a3b8;'>Starts the game with the Sanctuary Greenhouse already unlocked.</span></div></div>">> <<set $playerBackground to "Botanist">> <<goto "Character Creation">> <</link>> <<link "<div class='class-card card-broadcaster'><strong style='color:#f97316; font-size:1.15rem; text-shadow: 0 0 5px rgba(249,115,22,0.5);'>The Broadcaster</strong><div style='margin-top:10px; font-size:0.95rem; color:#cbd5e1; font-weight:bold;'>+10 Tech Data, +20% Scrip.</div><div style='margin-top:10px; padding-top:10px; border-top: 1px dashed #334155; font-size:0.85rem; color:#fdba74; line-height: 1.5;'><strong>[PERK: Signal Intercept]</strong><br><span style='color:#94a3b8;'>Generates +1 Tech Data daily. Access to hidden frequencies.</span></div></div>">> <<set $playerBackground to "Broadcaster">> <<goto "Character Creation">> <</link>> <<link "<div class='class-card card-nomad'><strong style='color:#94a3b8; font-size:1.15rem; text-shadow: 0 0 5px rgba(148,163,184,0.5);'>The Nomad</strong><div style='margin-top:10px; font-size:0.95rem; color:#cbd5e1; font-weight:bold;'>+5 Cloth.</div><div style='margin-top:10px; padding-top:10px; border-top: 1px dashed #334155; font-size:0.85rem; color:#e2e8f0; line-height: 1.5;'><strong>[PERK: Wasteland Walker]</strong><br><span style='color:#94a3b8;'>Map exploration and outside actions cost 50% less Stamina.</span></div></div>">> <<set $playerBackground to "Nomad">> <<goto "Character Creation">> <</link>> <<link "<div class='class-card card-mutated'><strong style='color:#84cc16; font-size:1.15rem; text-shadow: 0 0 5px rgba(132,204,22,0.5);'>The Mutated</strong><div style='margin-top:10px; font-size:0.95rem; color:#cbd5e1; font-weight:bold;'>-40% Max HP.</div><div style='margin-top:10px; padding-top:10px; border-top: 1px dashed #334155; font-size:0.85rem; color:#d9f99d; line-height: 1.5;'><strong>[PERK: Toxic Blood]</strong><br><span style='color:#94a3b8;'>Inflicts passive Toxic damage over time during combat. 100% Safe Flee.</span></div></div>">> <<set $playerBackground to "Mutated Anomaly">> <<goto "Character Creation">> <</link>> </div> </div> <!-- START GAME BUTTON AND DATA ASSIGNMENTS --> <div style="text-align: center; margin-top: 40px;"> <div id="char-error" style="color: #ef4444; font-weight: bold; font-size: 1rem; margin-bottom: 15px; height: 20px; text-shadow: 0 0 5px #ef4444;"></div> <<link '<span class="deploy-btn">DEPLOY TO SECTOR-01 (KATOWICE)</span>'>> <<set _ageMax to ($playerBackground eq "Mutated Anomaly" ? 999 : 60)>> <<set _nameErr to window.avNameError($characterName)>> <<if $characterName.trim() eq "">> <<replace "#char-error">>ERROR: DESIGNATION (CHARACTER NAME) REQUIRED.<</replace>> <<elseif _nameErr>> <<replace "#char-error">>ERROR: <<print _nameErr.toUpperCase()>><</replace>> <<elseif not Number.isFinite($playerAge) or $playerAge lt 18 or $playerAge gt _ageMax>> <<replace "#char-error">>ERROR: AGE MUST BE BETWEEN 18 AND <<print _ageMax>><<if _ageMax eq 60>> (ONLY THE MUTATED CAN BE OLDER)<</if>>.<</replace>> <<else>> <<set $startingCity to "Katowice">> <<set $sanctuaryLevels to { base: 1, workshop: 0, greenhouse: 0, pit: 0, medical: 0, walls: 0, bar: 0 }>> <<if $playerBackground eq "Scavenger">> <<set $equipment.backpack to "Common Scavenger's Rucksack", $playerClass to "The Scavenger">> <<set $perkExpertForager to true>> <<elseif $playerBackground eq "Mercenary">> <<set $maxHp += Math.round($maxHp * 0.20), $hp = $maxHp, $equipment.rightHand to "Common Combat Knife", $playerClass to "The Mercenary">> <<elseif $playerBackground eq "Techie">> <<set $materials.techData += 5, $playerClass to "The Techie">> <<elseif $playerBackground eq "Field Medic">> <<set $materials.medicine += 5, $materials.cloth += 10, $sanctuaryLevels.medical to 1, $playerClass to "The Field Medic">> <<elseif $playerBackground eq "Smuggler">> <<set $playerClass to "The Smuggler">> <<set $perkHumanTrafficker to true, $perkSmugglerScrap to true>> <<elseif $playerBackground eq "Heavy Gunner">> <<set $equipment.rightHand to "Common Scrap Launcher", $materials.scrap += 50, $playerClass to "The Heavy Gunner">> <<elseif $playerBackground eq "Botanist">> <<set $sanctuaryLevels.greenhouse to 1, $greenhouseActive to true, $playerClass to "The Botanist">> <<set $perkBotanistYield to true>> <<elseif $playerBackground eq "Broadcaster">> <<set $materials.techData += 10, $playerClass to "The Broadcaster">> <<set $perkBroadcasterScrip to true>> <<elseif $playerBackground eq "Nomad">> <<set $materials.cloth += 5, $playerClass to "The Nomad">> <<elseif $playerBackground eq "Mutated Anomaly">> <<set $maxHp -= Math.round($maxHp * 0.40), $hp = $maxHp, $playerClass to "The Mutated">> <<endif>> <<set $hasSanctuary to false>> <<set $currentChannel to "Katowice">> /* --- NAKMAN DEVELOPER BACKDOOR (GOD MODE) --- */ /* Only works if the account name is nakman, the character name does not matter! */ <<if $playerName.toLowerCase() eq "nakman">> <<set $syndicateTokens to 99999>> <<set $materials.scrap to 9999, $materials.wood to 9999, $materials.cloth to 9999, $materials.iron to 9999, $materials.medicine to 999, $materials.food to 999, $materials.water to 999, $materials.ammo to 9999, $materials.techData to 999>> <<set $level to Math.max($level || 1, 50)>> <<set $skillPoints to Math.max($skillPoints || 0, 50)>> <<set $devModeMessage to true>> <<endif>> /* -------------------------------------------- */ <<script>> if (window.setup.chatSocket && typeof window.setup.chatSocket.emit === 'function') { window.setup.chatSocket.emit('join_channel', 'Katowice'); } <</script>> <<run window.setup.updateSidebar()>> <<goto "Prologue">> <<endif>> <</link>> </div> </div> <</nobr>>
<div style="background: rgba(20, 0, 0, 0.9); border: 2px solid #ef4444; padding: 40px; text-align: center; font-family: monospace;"> <h1 style="color: #ef4444; letter-spacing: 5px;">[ UNAUTHORIZED SYSTEM OVERRIDE DETECTED ]</h1> <p style="color: #fca5a5; font-size: 1.2rem; margin-top: 20px;"> Nice try manipulating the network framework, Operator. :) <br><br> The Syndicate does not tolerate counterfeit data. Your premium token balance has been seized and permanently reduced to 0. Too bad IF you paid for some of it though. </p> <div style="margin-top: 30px;"> @@color:#fff;background:#ef4444;padding:10px 20px;<<link "REBOOT SYSTEM (RETURN TO CAMP)" "Camp">><</link>>@@ </div> </div>
<<audio ":all" stop>> <<audio "combat_theme" volume 0.8 loop play>> <<nobr>> <<setMenu "Ruined Military Checkpoint">> <</nobr>> <div class="checkpoint-fullscreen" style="font-family: 'Courier New', monospace; color: #00ff00;"> <div style="background: rgba(10, 10, 5, 0.92); border: 2px solid #dc2626; padding: 40px; border-radius: 8px; width: 100%; max-width: 1000px; margin: auto; box-sizing: border-box;"> <div style="text-align: center; border-bottom: 1px dashed #dc2626; padding-bottom: 15px; margin-bottom: 25px;"> <h2 style="color: #dc2626; margin: 0; letter-spacing: 3px;">[ AUTOMATED DEFENSES ACTIVATED! ]</h2> <div style="margin-top: 10px; font-size: 0.9rem; color: #fca5a5;">SECTOR 7-B SECURITY PROTOCOL OVERRIDE</div> </div> <p style="color: #cbd5e1; line-height: 1.8; font-size: 1.05rem; margin-bottom: 25px;"> As the heavy enforcers fall, emergency protocols screech to life. The dormant automated defense turrets swivel with a mechanical whine, locking their infrared targeting sensors directly onto your position. At the same time, desperate local scavengers flood through the breached perimeter, smelling blood in the water! </p> <<if def $checkpointHack and $checkpointHack eq "failed">> <div style="color:#fecaca; background:rgba(69,10,10,0.6); border:1px solid #7f1d1d; padding:12px 16px; margin-bottom:20px; line-height:1.6; font-size:0.95rem;">The turret control panel locked you out and the targeting lasers are tracking you. There is no way around them now. You will have to fight through.</div> <<else>> <div style="border:1px solid #166534; background:rgba(2,21,10,0.85); padding:14px 16px; margin-bottom:22px; text-align:center;"> <div style="color:#bbf7d0; line-height:1.6; font-size:0.95rem; margin-bottom:12px;">The turrets run on a single wall-mounted control panel. If you can breach it before they lock on, the whole defense grid goes dark and you slip past this wave. If the breach fails, the turrets open fire and you fight them anyway.</div> <<if setup.hackCanStart()>> <<link "<span style='display:inline-block; border:2px solid #4ade80; color:#bbf7d0; background:rgba(20,83,45,0.6); padding:10px 22px; font-weight:bold; letter-spacing:2px;'>[ HACK THE TURRET CONTROL (LIVE BREACH, COSTS 1 TECH DATA) ]</span>" "HackGame">><<run setup.hackBegin({ ctx: "checkpoint", game: "rand" })>><</link>> <<else>> <div style="color:#fca5a5; background:rgba(69,10,10,0.55); border:1px dashed #ef4444; padding:10px 14px; text-align:center; font-weight:bold; font-size:0.8rem; font-family:'Courier New',monospace;">[ LOCKED: A BREACH COSTS 1 TECH DATA, AND YOU HAVE NONE ]</div> <</if>> </div> <</if>> <div style="text-align: center;"> <<link '<span style="color:#dc2626; font-weight:bold;">ENGAGE SECURITY TURRETS & SCAVENGERS (WAVE 2)</span>'>> /* After wave 2 (Turrets) ends, we set it to trigger wave 3 (Scavengers) */ <<set $enemyGroup to [ { name: "Security Turret 1", hp: 90, currentHp: 90, maxHp: 90, minDmg: 18, maxDmg: 28, drop: "techData" }, { name: "Security Turret 2", hp: 90, currentHp: 90, maxHp: 90, minDmg: 18, maxDmg: 28, drop: "techData" } ]>> <<set $enemy to $enemyGroup[0]>> <<set $targetPOIName to "Ruined Military Checkpoint">> <<set $targetPOILoot to "military">> <<set $currentLocation to "Ruined Military Checkpoint">> <<set $combatWave to 2>> <<set $reinforcementPending to false>> <<unset $nextCombatEnemyGroup>> <<goto "Combat">> <</link>> </div> </div> </div>
<<nobr>> <<setMenu "Comms">> <</nobr>> <style> .event-container { background-color: #0b0f19; padding: 30px; border: 2px solid #a855f7; border-radius: 8px; font-family: 'Segoe UI', sans-serif; box-shadow: 0 0 25px rgba(168, 85, 247, 0.2); max-width: 800px; margin: 0 auto; } .event-title { color: #d8b4fe; font-size: 1.5rem; letter-spacing: 2px; margin-bottom: 15px; border-bottom: 1px dashed #a855f7; padding-bottom: 10px; text-transform: uppercase; text-align: center; font-weight: bold; } .dialogue-box { background: rgba(0, 0, 0, 0.6); border-left: 4px solid #a855f7; padding: 15px; margin: 20px 0; font-family: 'Courier New', monospace; color: #cbd5e1; line-height: 1.6; } .dialogue-name { color: #d8b4fe; font-weight: bold; margin-bottom: 5px; } .action-text { color: #94a3b8; font-style: italic; margin: 15px 0; line-height: 1.5; } .terminal-output { background: #000; border: 1px solid #2ecc71; color: #2ecc71; padding: 15px; font-family: 'Courier New', monospace; font-size: 0.9rem; margin: 15px 0; border-radius: 4px; box-shadow: inset 0 0 10px rgba(46,204,113,0.2); } .choice-btn { display: block; width: 100%; background: linear-gradient(135deg, #7e22ce 0%, #581c87 100%); border: 1px solid #a855f7; color: #f3e8ff !important; padding: 12px; margin-bottom: 10px; text-align: center; font-weight: bold; border-radius: 4px; cursor: pointer; text-decoration: none !important; transition: all 0.2s; box-sizing: border-box; letter-spacing: 1px; } .choice-btn:hover { background: linear-gradient(135deg, #581c87 100%, #7e22ce 100%); border-color: #e9d5ff; box-shadow: 0 0 15px rgba(168, 85, 247, 0.5); } </style> <div class="event-container"> <div class="event-title">[ DECRYPTION SUCCESSFUL ]</div> <div class="action-text"> The old green screen in the Command Center flickers for a moment. As you slot the data disk you brought back from the Katowice tunnels (Tech Data) into the terminal, the system starts sifting through thousands of lines of code at speed. Cipher immediately leans over your shoulder toward the screen, the focus of her cybernetic eye narrowing. </div> <div class="terminal-output"> > ACCESSING PRE-WAR ARCHIVE: SILESIA-TECH KOPALNIA SEC-4<br> > AUDIO LOG RECOVERED: "Test subject scales are hardening faster than expected. The nanomachine integration in the lower mining shafts is complete. They aren't just surviving the toxic gases anymore... they are adapting. God help us if they ever figure out how to open the cage doors."<br> > END OF FILE. </div> <div class="dialogue-box"> <div class="dialogue-name">CIPHER:</div> "Damn it... I was right. The Reptilians aren't animals that mutated after the catastrophe. Right before the war, they were biological weapons, converted from humans in the deepest mines of Silesia." </div> <div class="action-text"> Cipher folds her arms and paces the dark terminal room for a while. The look on her face is far more serious than before. </div> <div class="dialogue-box"> <div class="dialogue-name">CIPHER:</div> "Listen to me carefully. Thanks to this data we found, we haven't just cracked the main communication network (the Black Market), we've also learned where the enemy comes from. We aren't safe until we reach the very bottom of those mines. The system is active now. Bring every new piece of data you find here, and I'll give you everything you need to survive in those ruins." </div> <div style="text-align: center; margin-top: 35px;"> <<link "<span class='choice-btn'>[ CONNECT TO THE NETWORK AND RETURN TO THE MENU ]</span>" "Comms">> <<set $materials.scrip += 50>> /* A nice bonus for the first discovery */ <</link>> </div> </div>
<<if def $ch3 and $ch3.coreLost and $combatContext eq "ch3_omicron">><<goto "Ch3CoreBlast">><</if>> <<script>> var combatMusic = SimpleAudio.tracks.get("combat_theme"); if (combatMusic && !combatMusic.isPlaying()) { SimpleAudio.stop(); combatMusic.volume(1).loop(true).play(); } <</script>> <<nobr>> <<setMenu "Combat">> /* PROTECT MISSING PLAYER VARIABLES */ <<if ndef $playerGender>><<set $playerGender to "male">><</if>> <<if ndef $playerBackground>><<set $playerBackground to "mutated anomaly">><</if>> <<if ndef $playerArmorTier>><<set $playerArmorTier to "Common">><</if>> <<if ndef $playerDmgBonus>><<set $playerDmgBonus to 0>><</if>> <<if ndef $playerArmor>><<set $playerArmor to 0>><</if>> <<if ndef $playerCover>><<set $playerCover to false>><</if>> <<if ndef $materials>><<set $materials to {}>><</if>> <<if ndef $materials.ammo>><<set $materials.ammo to 0>><</if>> /* CHECK WEAPON AMMO AND MELEE STATUS */ <<set _equippedWeapon to ($equipment.rightHand || "None")>> <<set _weaponStats to setup.getItemStats(_equippedWeapon)>> <<set _isWeaponRanged to _weaponStats.isRanged>> /* ========================================================================= */ /* SAFETY BLOCK RUNS ONLY ON NEW COMBAT INITIATION */ /* ========================================================================= */ <<if ndef $enemy.isScaled>> <<set _currentDay to ($day || 1)>> <<set _currentLevel to ($level || 1)>> /* ENEMY SCALING */ <<set _hpBonus to (_currentLevel * 15) + (_currentDay * 3)>> <<set _dmgBonus to (_currentLevel * 2) + Math.floor(_currentDay / 2)>> <<if $enemy.name.includes("Squad")>> <<set _match to $enemy.name.match(/\((\d+)\s+Units\)/)>> <<set _squadCount to (_match ? parseInt(_match[1]) : 3)>> <<set $enemyGroup to []>> <<for _i to 0; _i lt _squadCount; _i++>> <<set _npcHp to random(35, 50)>> <<run $enemyGroup.push({ id: _i, name: "Reptilian Scrapper", hp: _npcHp, currentHp: _npcHp, maxHp: _npcHp, minDmg: 8, maxDmg: 14, drop: "techData" })>> <</for>> <<if $convoyFightActive and $targetPOIName eq "Reptilian Convoy">> <<run $enemyGroup.push({ id: _squadCount, name: "Enslaved Human (Forced to Fight)", hp: 30, currentHp: 30, maxHp: 30, minDmg: 4, maxDmg: 8, drop: "scrap", isCaptive: true })>> <</if>> <<set $enemy to $enemyGroup[0]>> <<elseif def $enemyGroup and $enemyGroup.length gt 0 and $enemyGroup[0].name eq $enemy.name and $enemyGroup[0].hp eq $enemy.hp>> /* Group ready, do not touch! */ <<else>> <<set $enemy.id to 0>> <<set $enemy.currentHp to ($enemy.maxHp || 40)>> <<set $enemyGroup to [$enemy]>> <<endif>> <<set $currentTargetIdx to 0>> <<for _i to 0; _i lt $enemyGroup.length; _i++>> <<if ndef $enemyGroup[_i].id>><<set $enemyGroup[_i].id to _i>><</if>> <<set $enemyGroup[_i].isScaled to true>> <<if $enemyGroup[_i].fixed>> <<set $enemyGroup[_i].maxHp to $enemyGroup[_i].hp>> <<set $enemyGroup[_i].currentHp to ($enemyGroup[_i].startHp || $enemyGroup[_i].hp)>> <<else>> <<set $enemyGroup[_i].maxHp to Math.round((($enemyGroup[_i].hp || 40) + ($enemyGroup[_i].isCaptive ? 0 : _hpBonus)) * ($enemyGroup[_i].hpMult || 1))>> <<set $enemyGroup[_i].currentHp to Math.max(1, Math.round($enemyGroup[_i].maxHp * ($enemyGroup[_i].startHpPct || 100) / 100))>> <<set $enemyGroup[_i].minDmg to Math.round((($enemyGroup[_i].minDmg || 8) + ($enemyGroup[_i].isCaptive ? 0 : _dmgBonus)) * ($enemyGroup[_i].dmgMult || 1))>> <<set $enemyGroup[_i].maxDmg to Math.round((($enemyGroup[_i].maxDmg || 14) + ($enemyGroup[_i].isCaptive ? 0 : _dmgBonus)) * ($enemyGroup[_i].dmgMult || 1))>> <<endif>> <<endfor>> <<if ndef $maxPlayerAP>><<set $maxPlayerAP to 3>><</if>> <<set $playerAP to $maxPlayerAP>> <<set $playerCover to false>> <<run setup.tmSurrender()>> /* COMPANION SCALING */ <<set _compHpBonus to ((_currentLevel - 1) * 20) + (_currentDay * 5) + setup.tm("compHp")>> <<set _compDmgBonus to ((_currentLevel - 1) * 3) + Math.floor(_currentDay / 2) + setup.tm("compDmg")>> <<run setup.squadBuild(_compHpBonus, _compDmgBonus)>> <<set $combatTurn to "player">> <<set $combatLog to []>> <<set $combatRange to 1>> <<set $companionPos to "back">> <<if $enemyGroup.length gt 1>> <<set $combatLog.unshift("> THREAT DETECTED: A hostile squad of " + $enemyGroup.length + " units approaches!")>> <<else>> <<set $combatLog.unshift("> THREAT DETECTED: " + $enemyGroup[0].name.toUpperCase() + " approaches!")>> <<endif>> <<if $combatContext eq "ch3_omicron" and $enemyGroup[0].name.indexOf("Sub-Warden Kraazh") eq 0>> <<set $combatLog.unshift("⚠ CIPHER: his Power Core sits in his chest. ANY hit on the torso cracks it and the core explodes at once, killing everyone in the vault. Aim at arms, legs and hydraulics.")>> <</if>> <<run setup.formLog()>> <</if>> /* ========================================================================= */ <<if ndef $combatTurn>><<set $combatTurn to "player">><</if>> <<if ndef $maxPlayerAP>><<set $maxPlayerAP to 3>><</if>> <<if ndef $playerAP>><<set $playerAP to $maxPlayerAP>><</if>> <<if ndef $combatUI>><<set $combatUI to "new">><</if>> <<if ndef $combatRange>><<set $combatRange to 1>><</if>> <<if ndef $companionPos>><<set $companionPos to "back">><</if>> <<if ndef $grabbed>><<set $grabbed to false>><</if>> <<if ndef $grabName>><<set $grabName to "Utopek-7">><</if>> <<if $grabbed and not $enemyGroup.some(function (x) { return (x.isUtopek || x.isZmora || x.isTopielica) && x.currentHp > 0; })>><<set $grabbed to false>><</if>> <<set _bt to ($combatUI neq "classic")>> /* SQUAD: make sure the squad exists (old saves), and name the ally being commanded */ <<run setup.squadEnsure()>> <<if $companion.active is true>> <<set _compDisplayName to ($companion.disp || $companion.name)>> <</if>> <<if ndef $materials.sterileBandage>><<set $materials.sterileBandage to 0>><</if>> <<if ndef $materials.medicalStimpack>><<set $materials.medicalStimpack to 0>><</if>> <<if ndef $materials.ammo>><<set $materials.ammo to 0>><</if>> <<run setup.tour.guard()>> <<set _aliveEnemies to 0>> <<for _i to 0; _i lt $enemyGroup.length; _i++>> <<if $enemyGroup[_i].currentHp gt 0 and not $enemyGroup[_i].isCaptive>> <<set _aliveEnemies += 1>> <<endif>> <</for>> <<if _aliveEnemies gt 0>> <<if $currentTargetIdx gte $enemyGroup.length or $currentTargetIdx lt 0>> <<set $currentTargetIdx to 0>> <<endif>> <<if $enemyGroup[$currentTargetIdx].currentHp lte 0>> <<for _i to 0; _i lt $enemyGroup.length; _i++>> <<if $enemyGroup[_i].currentHp gt 0 and not $enemyGroup[_i].isCaptive>> <<set $currentTargetIdx to _i>> <<break>> <<endif>> <</for>> <<endif>> <<set $enemy to $enemyGroup[$currentTargetIdx]>> <<else>> <<set $enemy to $enemyGroup[0]>> <</if>> <<set _genderFormat to $playerGender.toLowerCase()>> <<set _rawClass to $playerBackground.toLowerCase()>> <<if _rawClass eq "field medic">><<set _rawClass to "medic">><</if>> <<if _rawClass eq "mutated anomaly">><<set _rawClass to "mutated">><</if>> <<set _classFormat to _rawClass.replace(/ /g, "_")>> <<if setup.exoActive()>> <<set _armorSuffix to "_obsidian_exo">> <<else>> <<set _armorSuffix to ($playerArmorTier eq "Common" ? "_start_armor" : "_" + $playerArmorTier.toLowerCase() + "_armor")>> <<endif>> <<set $playerAvatarUrl to "images/avatars/" + _genderFormat + "_" + _classFormat + _armorSuffix + ".jpg">> <<set _plrFallback to "images/avatars/" + _genderFormat + "_" + _classFormat + ($playerArmorTier eq "Common" ? "_start_armor" : "_" + $playerArmorTier.toLowerCase() + "_armor") + ".jpg">> <<if setup.exoActive()>><<set $playerAvatarUrl to "images/avatars/exo_armor.jpg">><</if>> <<set _plrClass to ($playerBackground eq 'Mutated Anomaly' ? 'mutant-filter' : '')>> <<set _allyCount to $squad.length>> <<set _aw to Math.max(5.6, Math.min(9.5, 34 / (1 + _allyCount)))>> <<if ndef $currentLocation>><<set $currentLocation to "Wasteland">><</if>> <<set _bgFileName to $currentLocation.toLowerCase().replace(/ /g, "_")>> <<set $combatBgAlt to setup.cssSafeUrl("images/locations/combat_" + _bgFileName + ".jpg")>> <<set $combatBgUrl to (setup.bossBgUrl($enemyGroup) || ($combatContext === "base_raid" ? "images/locations/base_raid.jpg" : $combatBgAlt))>> <style> #story { margin-top: 0 !important; margin-right: 0 !important; margin-bottom: 0 !important; margin-left: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; } .av-world { position: relative; margin-left: 17.5em; margin-right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 20px; } .victory-box { background: linear-gradient(135deg, #050a05 0%, #064e3b 100%); padding: 25px; border: 2px solid #2ecc71; border-radius: 8px; font-family: 'Segoe UI', sans-serif; box-shadow: 0 0 30px rgba(46, 204, 113, 0.2); width: 100%; box-sizing: border-box; } .warning-box { background: linear-gradient(135deg, #1a0505 0%, #450a0a 100%); padding: 25px; border: 2px solid #ef4444; border-radius: 8px; font-family: 'Courier New', monospace; box-shadow: 0 0 40px rgba(239, 68, 68, 0.5); width: 100%; box-sizing: border-box; animation: pulseRed 2s infinite; } @keyframes pulseRed { 0% { box-shadow: 0 0 20px rgba(239, 68, 68, 0.4); } 50% { box-shadow: 0 0 40px rgba(239, 68, 68, 0.8); } 100% { box-shadow: 0 0 20px rgba(239, 68, 68, 0.4); } } /* ===== NEW BATTLE SCREEN: one 16:9 screen that always fits the window (no scrolling) ===== */ html:has(.bt-new), body:has(.bt-new) { overflow: hidden !important; } html body .av-world:has(.bt-new) { height: 100vh !important; height: 100dvh !important; min-height: 0 !important; padding: 6px !important; overflow: hidden !important; box-sizing: border-box !important; } .av-world:has(.bt-new) .bt-ctxbanner { display: none !important; } .bt-new { flex: 1 1 0; min-height: 0; display: flex; flex-direction: column; font-family: 'Courier New', monospace; } .bt-new .bt-box { flex: 1 1 0; min-height: 0; container-type: size; display: flex; align-items: center; justify-content: center; } .bt-new .bt-scene { container-type: inline-size; position: relative; width: min(100cqw, 177.7778cqh); aspect-ratio: 16 / 9; background-color: #05080d; background-size: cover; background-position: center; border: 1px solid #334155; border-radius: 6px; overflow: hidden; } .bt-classic .bt-box, .bt-classic .bt-scene, .bt-classic .bt-hud { display: contents; } .bt-switch a { color:#cbd5e1 !important; font-size:0.72rem; font-weight:bold; border:1px solid #475569; padding:4px 10px; border-radius:4px; background:rgba(15,23,42,0.8); text-decoration:none !important; } .bt-new .bt-switch a { display:inline-block !important; font-size:max(7px,1.1cqw); padding:0.3cqw 1cqw; min-height:0 !important; } .bt-top { position:absolute; top:0.8cqw; left:1cqw; right:1cqw; display:flex; align-items:center; justify-content:space-between; z-index:3; } .bt-ctx { flex:1 1 0; color:#fcd34d; font-size:max(7px,1.1cqw); font-weight:bold; text-shadow:0 0 4px #000; } .bt-range { display:flex; justify-content:center; gap:0.8cqw; } .bt-switch { flex:1 1 0; text-align:right; } .bt-zone { padding:0.3cqw 1.6cqw; border:1px solid #475569; border-radius:4px; color:#94a3b8; background:rgba(2,6,12,0.85); font-size:max(7px,1.3cqw); font-weight:bold; letter-spacing:0.15cqw; } .bt-zone.on { background:rgba(8,70,90,0.95); border-color:#22d3ee; color:#ecfeff; box-shadow:0 0 10px rgba(34,211,238,0.5); } .bt-hint { position:absolute; top:3.8cqw; left:50%; transform:translateX(-50%); z-index:3; width:38cqw; text-align:center; background:rgba(2,6,12,0.75); border-radius:4px; padding:0.2cqw 0.8cqw; color:#cbd5e1; font-size:max(7px,0.95cqw); line-height:1.25; box-sizing:border-box; } .bt-log { position:absolute; left:1cqw; top:4.5cqw; width:27cqw; height:11cqw; z-index:3; background:rgba(2,6,12,0.72); border:1px solid #334155; border-radius:4px; padding:0.6cqw 0.8cqw; overflow:hidden; display:flex; flex-direction:column-reverse; font-size:max(7px,1cqw); line-height:1.25; box-sizing:border-box; } .bt-targetpanel { position:absolute; right:1cqw; top:4.5cqw; width:25cqw; z-index:3; background:rgba(2,6,12,0.78); border:1px solid #7f1d1d; border-radius:4px; padding:0.6cqw 0.8cqw; color:#e2e8f0; font-size:max(7px,1cqw); line-height:1.3; box-sizing:border-box; } .bt-targetpanel b { color:#fca5a5; } .bt-stage { position:absolute; left:0; right:0; top:15cqw; bottom:38%; display:flex; align-items:flex-end; justify-content:space-between; padding:0 2cqw 0.8cqw 2cqw; box-sizing:border-box; } .bt-side { display:flex; gap:0.8cqw; align-items:flex-end; } .bt-allies { flex:0 0 auto; } .bt-foes { flex:1 1 0; min-width:0; justify-content:flex-end; } .bt-gap { flex:0 0 var(--gap, 20%); text-align:center; color:#fca5a5; font-weight:bold; font-size:max(8px,1.8cqw); align-self:center; text-shadow:0 0 6px #000; } .bt-unit { background:rgba(2,6,12,0.82); border:2px solid #334155; border-radius:5px; padding:0.4cqw; text-align:center; box-sizing:border-box; } .bt-allies .bt-unit { width:var(--aw, 9.5cqw); } .bt-turn { border-color:#fbbf24 !important; box-shadow:0 0 12px rgba(251,191,36,0.85); } .bt-foes .bt-unit { flex:1 1 0; max-width:9cqw; min-width:5.5cqw; } .bt-foe { border-color:#7f1d1d; } .bt-ally { border-color:#0f766e; } .bt-comp { border-color:#0369a1; } .bt-target { border-color:#f87171; box-shadow:0 0 12px rgba(248,113,113,0.85); } .bt-dead { opacity:0.4; filter:grayscale(100%); } .bt-pic { position:relative; width:100%; aspect-ratio:4 / 5; background:#000; border-radius:3px; overflow:hidden; margin-bottom:0.3cqw; } .bt-pic img { width:100%; height:100%; object-fit:cover; object-position:center top; display:block; } .bt-hit { position:absolute; top:0; left:0; right:0; bottom:0; } .bt-hit a { display:block; width:100%; height:100% !important; min-height:0 !important; font-size:0; color:transparent !important; background:transparent !important; cursor:crosshair; } .bt-kia { position:absolute; left:0; right:0; top:42%; text-align:center; color:#fca5a5; font-weight:bold; font-size:max(7px,1.2cqw); background:rgba(0,0,0,0.6); padding:0.2cqw 0; } .bt-name { color:#e2e8f0; font-weight:bold; font-size:max(7px,1.05cqw); line-height:1.1; overflow:hidden; max-height:2.3em; word-break:break-word; margin-bottom:0.2cqw; } .bt-hp { color:#cbd5e1; font-size:max(7px,1cqw); margin-top:0.2cqw; } .bt-tag { color:#94a3b8; font-size:max(6px,0.85cqw); } .bt-unit .combat-bar-bg { height:max(3px,0.6cqw) !important; } .bt-pip { display:inline-block; width:0.8cqw; height:0.8cqw; border-radius:50%; border:1px solid #38bdf8; margin:0 1px; } .bt-pip.on { background:#38bdf8; } /* bottom HUD: the old menu, compacted to fit the lower part of the screen */ .bt-new .bt-hud { position:absolute; left:0; right:0; bottom:0; height:38%; z-index:4; background:linear-gradient(180deg, rgba(2,6,12,0.9), rgba(2,6,12,0.98)); border-top:1px solid #334155; padding:0.7cqw 1.2cqw; box-sizing:border-box; overflow-x:hidden; overflow-y:auto; font-size:max(7px,1.05cqw); } .bt-new .bt-hud > div { background:none !important; border:none !important; padding:0 !important; margin:0 !important; } .bt-new .bt-hud .bt-pturn { display:grid; grid-template-columns:1fr 24cqw; column-gap:0.8cqw; row-gap:0.5cqw; align-items:start; } .bt-new .bt-pturn > div:first-child { grid-column:1 / 3; font-size:max(7px,1.1cqw) !important; margin:0 !important; } .bt-new .bt-pturn > .bt-move { grid-column:2; grid-row:2; } .bt-new .bt-pturn > div[style*="dashed #fbbf24"], .bt-new .bt-pturn > div[style*="1px solid #22d3ee"], .bt-new .bt-pturn > div[style*="239, 68, 68, 0.2"] { grid-column:1; grid-row:2; } .bt-new .bt-pturn > div[style*="repeat(5, 1fr)"] { grid-column:1; grid-row:3; } .bt-new .bt-pturn > div:last-child { grid-column:2; grid-row:3; } html body .bt-new .bt-hud div[style*="repeat(4, 1fr)"] { grid-template-columns:repeat(4, 1fr) !important; gap:0.5cqw !important; } html body .bt-new .bt-hud div[style*="repeat(5, 1fr)"] { grid-template-columns:repeat(5, 1fr) !important; gap:0.5cqw !important; margin:0 !important; } .bt-new .bt-hud div[style*="dashed #fbbf24"] { padding:0.5cqw !important; margin:0 !important; } .bt-new .bt-hud div[style*="dashed #fbbf24"] > div:first-child { font-size:max(7px,1cqw) !important; margin-bottom:0.4cqw !important; } .bt-new .bt-hud .limb-btn, .bt-new .bt-hud .disabled-btn-large { padding:0.4cqw 0.2cqw !important; font-size:max(7px,1cqw) !important; line-height:1.15; } .bt-new .bt-hud .action-btn, .bt-new .bt-hud .heal-btn, .bt-new .bt-hud .capture-btn, .bt-new .bt-hud .flee-btn, .bt-new .bt-hud .disabled-btn-small { padding:0.4cqw 0.2cqw !important; font-size:max(7px,0.95cqw) !important; line-height:1.15; width:auto !important; max-width:none !important; } .bt-new .bt-hud span[style*="0.65rem"] { font-size:max(6px,0.8cqw) !important; } html body .bt-new .bt-hud a.macro-link { min-height:0 !important; } .bt-new .bt-hud .bt-move { padding:0.4cqw !important; border:1px solid #22d3ee !important; border-radius:4px; } .bt-new .bt-hud .bt-move .bt-movetitle { font-size:max(7px,0.95cqw) !important; margin-bottom:0.3cqw !important; } .bt-new .bt-hud .bt-moverow { gap:0.5cqw !important; } .bt-new .bt-pturn > div:last-child { padding:0.5cqw !important; font-size:max(7px,1.05cqw) !important; border:1px solid #38bdf8 !important; border-radius:4px; text-align:center; } .bt-new .bt-hud > div > div[style*="grid-template-columns: 1fr 1fr"] { gap:0.6cqw !important; } .bt-new .bt-hud > div > div:first-child { font-size:max(7px,1.1cqw) !important; margin-bottom:0.5cqw !important; } .bt-won .bt-hud { display:none !important; } .bt-winbox { position:absolute; left:50%; top:46%; transform:translate(-50%,-50%); z-index:8; text-align:center; background:rgba(2,6,12,0.88); border:2px solid #34d399; border-radius:8px; padding:2cqw 4cqw; box-shadow:0 0 28px rgba(52,211,153,0.45); min-width:34cqw; } .bt-wintitle { font-family:'Courier New',monospace; font-weight:bold; letter-spacing:0.2cqw; color:#6ee7b7; font-size:max(11px,2.2cqw); text-shadow:0 0 10px rgba(52,211,153,0.6); } .bt-winsub { color:#cbd5e1; font-size:max(8px,1.1cqw); margin:0.8cqw 0 1.4cqw; } .bt-winbox a.macro-link { text-decoration:none !important; min-height:0 !important; padding:0 !important; border:none !important; background:none !important; display:inline-block; } .bt-winbox .ct-btn { font-size:max(9px,1.4cqw); padding:0.8cqw 2.4cqw; } .ct-win { color:#6ee7b7; border-color:#34d399; } .ct-win:hover { background:rgba(52,211,153,0.18); color:#ecfdf5; box-shadow:0 0 14px rgba(52,211,153,0.55); } .ct-turnbar { display:flex !important; gap:10px; justify-content:center; align-items:stretch; flex-wrap:wrap; margin-top:10px !important; } .ct-turnbar a.macro-link { text-decoration:none !important; min-height:0 !important; padding:0 !important; border:none !important; background:none !important; display:block; flex:1 1 150px; max-width:260px; } .ct-btn { display:block; text-align:center; font-family:'Courier New',monospace; font-weight:bold; letter-spacing:1px; font-size:0.85rem; padding:10px 14px; border-radius:5px; background:rgba(8,12,20,0.95); border:2px solid; transition:all 0.15s; } .ct-next { color:#7dd3fc; border-color:#38bdf8; } .ct-next:hover { background:rgba(56,189,248,0.18); color:#e0f2fe; box-shadow:0 0 14px rgba(56,189,248,0.55); } .ct-end { color:#fca5a5; border-color:#ef4444; } .ct-end:hover { background:rgba(239,68,68,0.2); color:#fff1f2; box-shadow:0 0 14px rgba(239,68,68,0.55); } .bt-new .bt-hud .ct-turnbar { gap:0.6cqw; margin-top:0.5cqw !important; } .bt-new .bt-hud .ct-btn { font-size:max(7px,1cqw); padding:0.5cqw 0.8cqw; border-width:1px; letter-spacing:0; } .bt-new .bt-hud .my-cyber-btn { font-size:max(7px,1.1cqw) !important; padding:0.6cqw 1cqw !important; max-width:30cqw !important; border-width:1px !important; } .bt-new .bt-hud .melee-switch-btn { padding:0.5cqw !important; font-size:max(7px,1cqw) !important; animation:none; } .bt-move { background:rgba(2,6,12,0.7); } .bt-move .bt-movetitle { color:#67e8f9; font-weight:bold; font-size:0.85rem; margin-bottom:6px; } .bt-move .bt-moverow { display:grid; grid-template-columns:1fr 1fr; gap:8px; } .bt-grab { background:rgba(127,29,29,0.45); border:1px solid #fca5a5; border-radius:4px; color:#fecaca; font-size:max(7px,0.95cqw); padding:0.4cqw; margin-bottom:0.5cqw; text-align:center; } .bt-move .bt-moverow.three { grid-template-columns:1fr 1fr 1fr; } .bt-compfront { width:var(--aw, 9.5cqw); flex:0 0 auto; margin-right:0.5cqw; box-shadow:0 0 10px rgba(56,189,248,0.5); } .combat-portrait { width: 140px; height: 210px; border-radius: 8px; position: relative; overflow: hidden; margin: 0 auto 10px auto; background: #000; } .combat-bar-bg { width: 100%; background: #000; height: 12px; border-radius: 4px; overflow: hidden; border: 1px solid rgba(255,255,255,0.2); } .mutant-filter { filter: sepia(100%) hue-rotate(90deg) saturate(400%) contrast(150%) brightness(80%); } .action-btn { background: rgba(0, 255, 128, 0.1); border: 1px solid #34d399; color: #34d399; padding: 6px 2px; text-align: center; border-radius: 4px; font-weight: bold; cursor: pointer; font-size: 0.7rem; line-height: 1.2; } .action-btn:hover { background: rgba(0, 255, 128, 0.25); } .heal-btn { background: rgba(56, 189, 248, 0.1); border: 1px solid #38bdf8; color: #38bdf8; padding: 6px 2px; text-align: center; border-radius: 4px; font-weight: bold; cursor: pointer; font-size: 0.7rem; line-height: 1.2; } .heal-btn:hover { background: rgba(56, 189, 248, 0.25); } .capture-btn { background: rgba(251, 191, 36, 0.15); border: 1px solid #fbbf24; color: #fbbf24; padding: 6px 2px; text-align: center; border-radius: 4px; font-weight: bold; cursor: pointer; font-size: 0.7rem; line-height: 1.2; } .capture-btn:hover { background: rgba(251, 191, 36, 0.3); } .flee-btn { background: rgba(244, 63, 94, 0.15); border: 1px solid #f43f5e; color: #f43f5e; padding: 6px 2px; text-align: center; border-radius: 4px; font-weight: bold; cursor: pointer; font-size: 0.7rem; line-height: 1.2; } .flee-btn:hover { background: rgba(244, 63, 94, 0.3); } .disabled-btn-small { background: rgba(100, 100, 100, 0.1); border: 1px solid #555; color: #777; padding: 6px 2px; text-align: center; border-radius: 4px; font-weight: bold; font-size: 0.7rem; line-height: 1.2; } .action-btn a, .heal-btn a, .capture-btn a, .flee-btn a { display: block; color: inherit !important; text-decoration: none !important; } .limb-btn { background: rgba(245, 158, 11, 0.1); border: 1px solid #fbbf24; color: #fbbf24; padding: 14px; text-align: center; border-radius: 4px; font-weight: bold; cursor: pointer; font-size: 1rem; } .limb-btn:hover { background: rgba(245, 158, 11, 0.25); } .disabled-btn-large { background: rgba(100, 100, 100, 0.1); border: 1px solid #555; color: #777; padding: 14px; text-align: center; border-radius: 4px; font-weight: bold; font-size: 1rem; } .melee-switch-btn { background: rgba(239, 68, 68, 0.2); border: 2px solid #ef4444; color: #fca5a5; padding: 12px; text-align: center; border-radius: 4px; font-weight: bold; cursor: pointer; animation: pulse 1.5s infinite; } .melee-switch-btn:hover { background: rgba(239, 68, 68, 0.4); color: #fff; } .my-cyber-btn { display: block !important; width: 100% !important; max-width: 450px !important; margin: 0 auto !important; background: rgba(10, 10, 15, 0.95) !important; border: 2px solid #ef4444 !important; color: #ef4444 !important; padding: 12px 20px !important; font-family: 'Courier New', monospace !important; font-size: 0.95rem !important; font-weight: bold !important; letter-spacing: 1px !important; border-radius: 4px !important; text-align: center !important; box-shadow: 0 0 12px rgba(239,68,68,0.3) !important; text-decoration: none !important; } .my-cyber-btn:hover { background: rgba(239,68,68,0.2) !important; color: #fca5a5 !important; box-shadow: 0 0 18px rgba(239,68,68,0.6) !important; } </style> <div class="av-world"> <<if def $combatContext and $combatContext eq "base_raid" and def $raidSnap>> <div class="bt-ctxbanner" style="border: 2px solid #f59e0b; background: rgba(245,158,11,0.1); color: #fcd34d; padding: 8px 12px; margin-bottom: 10px; border-radius: 6px; font-family: 'Courier New', monospace; text-align: center; font-size: 0.85rem;"> BASE RAID - your defenses cut the raiders to <<print $raidSnap.threat>>% health before the fight </div> <</if>> <<if def $combatContext and $combatContext eq "camp_raid" and def $campRaid>> <div class="bt-ctxbanner" style="border: 2px solid #f87171; background: rgba(248,113,113,0.1); color: #fecaca; padding: 8px 12px; margin-bottom: 10px; border-radius: 6px; font-family: 'Courier New', monospace; text-align: center;"> <<print $campRaid.name.toUpperCase()>> - WAVE <<print $combatWave>> / <<print $campRaid.waves>> </div> <</if>> <<if def $combatContext and $combatContext eq "slaughterhouse_assault">> <div class="bt-ctxbanner" style="border: 2px solid #ef4444; background: rgba(239,68,68,0.1); color: #fca5a5; padding: 8px 12px; margin-bottom: 10px; border-radius: 6px; font-family: 'Courier New', monospace; text-align: center;"> SLAUGHTERHOUSE ASSAULT - WAVE <<print $combatWave>> / 4 <<if $slaughterGas>><br><span style="color:#a3e635;">☣ TOXIC GAS ACTIVE: you take damage every turn. End the fight fast.</span><</if>> </div> <</if>> <<if _aliveEnemies gt 0>><<set $victoryAck to false>><</if>> <<if _aliveEnemies lte 0>><<set $combatTurn to "player">><</if>> <!-- WAVE CONTROLLER --> <<if def $reinforcementPending and $reinforcementPending eq true and _aliveEnemies lte 0 and ((not _bt) or $victoryAck)>> <div class="warning-box"> <div style="text-align: center; border-bottom: 2px dashed #ef4444; padding-bottom: 15px; margin-bottom: 25px;"> <h2 style="color: #ef4444; margin: 0; letter-spacing: 5px;">[ ⚠️ WARNING: REINFORCEMENTS DETECTED! ]</h2> <div style="color: #fca5a5; font-size: 1rem; margin-top: 10px; font-weight: bold;">Gunfire has drawn enemy backup to your location!</div> </div> <div style="color: #f87171; font-size: 1rem; text-align: center; margin-bottom: 30px; line-height: 1.6;"> You managed to neutralize the vanguard, but the heavy noise attracted local scavengers.<br> Hostile signatures are converging on your position. There is no time to loot or run.<br><br> <strong style="color: #fff;">BRACE YOURSELF FOR WAVE <<print ($combatWave + 1)>>!</strong> </div> <div style="text-align: center;"> @@color:#fff;background:#ef4444;padding:15px 30px;font-size:1.2rem;font-weight:bold;letter-spacing:2px;border:2px solid #b91c1c;border-radius:4px;cursor:pointer;<<link "ENGAGE REINFORCEMENT SQUAD">> <<set $combatWave += 1>> <<set $reinforcementPending to false>> <<set $enemyGroup to $nextCombatEnemyGroup>> <<set $enemy to $enemyGroup[0]>> <<unset $nextCombatEnemyGroup, $combatLog>> <<goto "Combat">> <</link>>@@ </div> </div> <<else>> <!-- COMBAT ACTIVE SCREEN --> <<if $hp gt 0 and (_aliveEnemies gt 0 or (_bt and not $victoryAck))>> <div @class="(_bt ? ('bt-root bt-new' + (_aliveEnemies lte 0 ? ' bt-won' : '')) : 'bt-root bt-classic')" @style="(_bt ? 'width:100%;' : ('background: linear-gradient(180deg, rgba(5,10,5,0.75), rgba(35,5,5,0.95)), url(\'' + $combatBgUrl + '\') center/cover, url(\'' + $combatBgAlt + '\') center/cover; padding: 25px; border: 2px solid #ef4444; border-radius: 8px; font-family: \'Courier New\', monospace; box-shadow: 0 0 30px rgba(239, 68, 68, 0.4); box-sizing: border-box; width: 100%;'))"> <<if not _bt>> <div style="text-align: center; border-bottom: 1px dashed #ef4444; padding-bottom: 15px; margin-bottom: 20px;"> <h2 style="color: #ef4444; margin: 0; letter-spacing: 3px;">[ TACTICAL TURN-BASED COMBAT ]</h2> <div class="bt-switch" style="margin-top:8px;"><<link "SWITCH TO NEW BATTLE VIEW" "Combat">><<set $combatUI to "new">><</link>></div> </div> <<endif>> <<if not _bt>> <div style="background: rgba(10, 0, 0, 0.65); border: 1px solid #7f1d1d; border-radius: 4px; padding: 15px; margin-bottom: 20px; height: 110px; overflow-y: auto; display: flex; flex-direction: column-reverse; backdrop-filter: blur(2px);"> <<for _i to 0; _i lt $combatLog.length; _i++>> <div style="color: <<if $combatLog[_i].includes('You') or $combatLog[_i].includes('SUCCESS') or $combatLog[_i].includes('Heal') or $combatLog[_i].includes('CAPTURE') or $combatLog[_i].includes('Cover') or $combatLog[_i].includes('FLEE')>>#34d399<<elseif $combatLog[_i].includes('MISSED')>>#fde047<<else>>#ef4444<</if>>;"><<print $combatLog[_i]>></div> <<endfor>> </div> <<endif>> <<set _rng to ($combatRange lte 0 ? 0 : ($combatRange gte 2 ? 2 : 1))>> <<set _anyMelee to $enemyGroup.some(function (x) { return x.currentHp > 0 && setup.isMeleeEnemy(x); })>> <<set _tg to $enemyGroup[$currentTargetIdx]>> <div class="bt-box"> <div class="bt-scene" @style="(_bt ? ('--aw:' + _aw + 'cqw; --gap:' + [6, 20, 28][_rng] + '%; background-image: linear-gradient(180deg, rgba(0,0,0,0.2), rgba(0,0,0,0.55)), url(\'' + $combatBgUrl + '\'), url(\'' + $combatBgAlt + '\');') : '')"> <<if _bt>> <<if _aliveEnemies lte 0>> <div class="bt-winbox"> <<if def $reinforcementPending and $reinforcementPending eq true>> <div class="bt-wintitle">VANGUARD DOWN!</div> <div class="bt-winsub">But more hostiles are closing in on your position...</div> <<link `"<span class='ct-btn ct-end'>BRACE FOR WAVE " + ($combatWave + 1) + " ▶</span>"` "Combat">><<set $victoryAck to true>><</link>> <<else>> <div class="bt-wintitle">ALL HOSTILES DOWNED! YOU WIN!</div> <div class="bt-winsub">The area is quiet. Time to see what they left behind.</div> <<link "<span class='ct-btn ct-win'>LOOT CORPSES ▶</span>" "Combat">><<set $victoryAck to true>><</link>> <</if>> </div> <</if>> <div class="bt-top"> <div class="bt-ctx"><<if def $combatContext and $combatContext eq "slaughterhouse_assault">>SLAUGHTERHOUSE WAVE <<print $combatWave>>/4<<if $slaughterGas>> | TOXIC GAS<</if>><<elseif def $combatContext and $combatContext eq "base_raid">>BASE RAID<<elseif def $combatContext and $combatContext eq "camp_raid" and def $campRaid>>CAMP WAVE <<print $combatWave>>/<<print $campRaid.waves>><<elseif def $combatContext and $combatContext eq "camp_tour">>TRAINING SKIRMISH<</if>></div> <div class="bt-range"> <span @class="'bt-zone' + (_rng eq 2 ? ' on' : '')">FAR</span> <span @class="'bt-zone' + (_rng eq 1 ? ' on' : '')">MID</span> <span @class="'bt-zone' + (_rng eq 0 ? ' on' : '')">CLOSE</span> </div> <span class="bt-switch"><<link "CLASSIC VIEW" "Combat">><<set $combatUI to "classic">><</link>></span> </div> <div class="bt-hint"> <<if _isWeaponRanged>> Ranged x<<print setup.rangeMult()>> here (best FAR, weak CLOSE) <<else>> Melee: strikes only at CLOSE<<if _rng gt 0>> - CHARGE to close in<</if>> <</if>> <<if _anyMelee>> | melee enemies close in each round<</if>> </div> <div class="bt-log"> <<for _i to 0; _i lt $combatLog.length; _i++>> <div style="color: <<if $combatLog[_i].includes('You') or $combatLog[_i].includes('SUCCESS') or $combatLog[_i].includes('Heal') or $combatLog[_i].includes('CAPTURE') or $combatLog[_i].includes('Cover') or $combatLog[_i].includes('FLEE')>>#34d399<<elseif $combatLog[_i].includes('MISSED')>>#fde047<<else>>#fca5a5<</if>>;"><<print $combatLog[_i]>></div> <<endfor>> </div> <<if _tg>> <div class="bt-targetpanel"> <div><b>TARGET</b> <<print _tg.name.split(' (')[0].toUpperCase()>> #<<print $currentTargetIdx + 1>></div> <div>HP <<print Math.max(0, _tg.currentHp)>>/<<print _tg.maxHp>> | DMG <<print _tg.minDmg>>-<<print _tg.maxDmg>> | <<print (setup.isMeleeEnemy(_tg) ? 'MELEE' : 'RANGED')>></div> <div class="bt-tag">Click an enemy to switch target</div> </div> <</if>> <div class="bt-stage"> <div class="bt-side bt-allies"> <div class="bt-unit bt-ally"> <div class="bt-pic"> <img @src="$playerAvatarUrl" @class="_plrClass" @data-fb="_plrFallback" onerror="if(this.getAttribute('data-fb') && this.src.indexOf('exo_armor')>-1){var f=this.getAttribute('data-fb'); this.removeAttribute('data-fb'); this.src=f; return;} this.src='https://placehold.co/512x768/000/0f0?text=YOU';"> </div> <div class="bt-name" style="color:#5eead4;"><<print (def $characterName and $characterName neq "") ? $characterName.toUpperCase() : "OPERATOR">></div> <<set _plrPct to Math.clamp(($hp / $maxHp) * 100, 0, 100)>> <div class="combat-bar-bg"><div @style="'width: ' + _plrPct + '%; background: #34d399; height: 100%;'"></div></div> <div class="bt-hp">HP <<print $hp>>/<<print $maxHp>></div> <div class="bt-tag">AP <<for _p to 0; _p lt $maxPlayerAP; _p++>><span @class="'bt-pip' + (_p lt $playerAP ? ' on' : '')"></span><</for>> | <<print setup.ammoLabel()>> <<print setup.ammoLeft()>><<if $playerCover>> | COVER<</if>><<if setup.contamActive()>> | ☢ -<<print Math.round(setup.contamPenalty() * 100)>>% DMG<</if>></div> </div> <<for _si to 0; _si lt $squad.length; _si++>> <<if $squad[_si].pos neq "front" or $squad[_si].hp lte 0>> <<set _cm to setup.squadView(_si)>> <<set _cmAlive to (_cm.hp gt 0)>> <div @class="'bt-unit bt-comp' + (_cmAlive ? '' : ' bt-dead') + (($combatTurn eq 'companion' and _si eq $squadIdx) ? ' bt-turn' : '')"> <div class="bt-pic"> <img @src="_cm.avatar" onerror="this.src='https://placehold.co/512x768/000/38bdf8?text=ALLY';"> <<if not _cmAlive>><div class="bt-kia">KIA</div><</if>> </div> <div class="bt-name" @style="'color:' + (_cm.color || '#7dd3fc')"><<print String(_cm.disp || _cm.name).toUpperCase()>></div> <<set _compPct to Math.clamp((_cm.hp / _cm.maxHp) * 100, 0, 100)>> <div class="combat-bar-bg"><div @style="'width: ' + _compPct + '%; background: ' + (_cm.color || '#38bdf8') + '; height: 100%;'"></div></div> <div class="bt-hp">HP <<print Math.max(0, _cm.hp)>>/<<print _cm.maxHp>></div> <div class="bt-tag">AP <<print _cm.ap>>/<<print _cm.maxAp>> | BACK</div> </div> <</if>> <</for>> <<for _si to 0; _si lt $squad.length; _si++>> <<if $squad[_si].pos eq "front" and $squad[_si].hp gt 0>> <<set _cm to setup.squadView(_si)>> <<set _cmAlive to (_cm.hp gt 0)>> <div @class="'bt-unit bt-comp bt-compfront' + (_cmAlive ? '' : ' bt-dead') + (($combatTurn eq 'companion' and _si eq $squadIdx) ? ' bt-turn' : '')"> <div class="bt-pic"> <img @src="_cm.avatar" onerror="this.src='https://placehold.co/512x768/000/38bdf8?text=ALLY';"> <<if not _cmAlive>><div class="bt-kia">KIA</div><</if>> </div> <div class="bt-name" @style="'color:' + (_cm.color || '#7dd3fc')"><<print String(_cm.disp || _cm.name).toUpperCase()>></div> <<set _compPct to Math.clamp((_cm.hp / _cm.maxHp) * 100, 0, 100)>> <div class="combat-bar-bg"><div @style="'width: ' + _compPct + '%; background: ' + (_cm.color || '#38bdf8') + '; height: 100%;'"></div></div> <div class="bt-hp">HP <<print Math.max(0, _cm.hp)>>/<<print _cm.maxHp>></div> <div class="bt-tag">AP <<print _cm.ap>>/<<print _cm.maxAp>> | FRONT</div> </div> <</if>> <</for>> </div> <div class="bt-gap">VS</div> <div class="bt-side bt-foes"> <<for _i to 0; _i lt $enemyGroup.length; _i++>> <<set _enm to $enemyGroup[_i]>> <<set _isTarget to (_i eq $currentTargetIdx)>> <<set _cleanName to _enm.name.split(' (')[0]>> <<set _slug to _cleanName.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "")>> <<set _base to _slug.replace(/_\d+$/, "")>> <<set _occ to $enemyGroup.slice(0, _i).filter(function (x) { return x.name.split(" (")[0].toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").replace(/_\d+$/, "") === _base; }).length + 1>> <<set _slotNum to (_enm.art || (((_occ - 1) % 4) + 1))>> <<set _imgDir to "images/npcs/all/enemy/enemy_">> <<set _imgAlias to ({ heavy_enforcer: "reptilian_enforcer", vanguard_scout: "reptilian_scout" })[_base]>> <<set _avatarUrl to _imgDir + _base + "_" + _slotNum + ".jpg">> <<set _imgAlts to JSON.stringify([ _imgDir + _slug + ".jpg", _imgDir + _slug + "_" + _slotNum + ".jpg", (_imgAlias ? _imgDir + _imgAlias + "_" + _slotNum + ".jpg" : _imgDir + _base + ".jpg"), _imgDir + _base + "_1.jpg" ])>> <<set _alive to (_enm.currentHp gt 0)>> <<set _isMelee to setup.isMeleeEnemy(_enm)>> <<set _enmPct to Math.clamp((_enm.currentHp / _enm.maxHp) * 100, 0, 100)>> <<set _tip to _cleanName + ' | HP ' + Math.max(0, _enm.currentHp) + '/' + _enm.maxHp + ' | DMG ' + _enm.minDmg + '-' + _enm.maxDmg + (_isMelee ? ' | MELEE' : ' | RANGED')>> <div @class="'bt-unit bt-foe' + (_isTarget and _alive ? ' bt-target' : '') + (_alive ? '' : ' bt-dead')" @title="_tip"> <div class="bt-pic"> <img src="" @src="_avatarUrl" @data-alts="_imgAlts" onerror="var a=JSON.parse(this.getAttribute('data-alts')||'[]'),i=parseInt(this.getAttribute('data-i')||'0',10);if(i<a.length){this.setAttribute('data-i',i+1);this.src=a[i];}else{this.onerror=null;this.src='https://placehold.co/512x768/000/f00?text=ENEMY';}"> <<if _alive>> <<capture _i>><span class="bt-hit"><<link " " "Combat">><<set $currentTargetIdx to _i>><</link>></span><</capture>> <<else>> <div class="bt-kia">KIA</div> <</if>> </div> <div class="bt-name"><<print _cleanName.toUpperCase()>> #<<print _i + 1>></div> <div class="combat-bar-bg"><div @style="'width: ' + _enmPct + '%; background: #ef4444; height: 100%;'"></div></div> <div class="bt-hp">HP <<print Math.max(0, _enm.currentHp)>>/<<print _enm.maxHp>></div> <div class="bt-tag"><<if _isTarget and _alive>>> TARGET <<<elseif _alive>><<print (_isMelee ? 'MELEE' : 'RANGED')>><</if>><<if _enm.isStunned>> | STUNNED<</if>></div> </div> <</for>> </div> </div> <<else>> <!-- 1. ENEMY FRONT --> <div style="display: flex; justify-content: center; flex-wrap: wrap; gap: 15px; margin-bottom: 30px;"> <<for _i to 0; _i lt $enemyGroup.length; _i++>> <<set _enm to $enemyGroup[_i]>> <<set _isTarget to (_i eq $currentTargetIdx)>> <<set _cleanName to _enm.name.split(' (')[0]>> <<set _slug to _cleanName.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "")>> <<set _base to _slug.replace(/_\d+$/, "")>> /* slot = which one of this enemy type it is in the group (1st, 2nd, ...), so file numbers follow the type */ <<set _occ to $enemyGroup.slice(0, _i).filter(function (x) { return x.name.split(" (")[0].toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").replace(/_\d+$/, "") === _base; }).length + 1>> <<set _slotNum to (_enm.art || (((_occ - 1) % 4) + 1))>> <<set _imgDir to "images/npcs/all/enemy/enemy_">> <<set _imgAlias to ({ heavy_enforcer: "reptilian_enforcer", vanguard_scout: "reptilian_scout" })[_base]>> <<set _avatarUrl to _imgDir + _base + "_" + _slotNum + ".jpg">> <<set _imgAlts to JSON.stringify([ _imgDir + _slug + ".jpg", _imgDir + _slug + "_" + _slotNum + ".jpg", (_imgAlias ? _imgDir + _imgAlias + "_" + _slotNum + ".jpg" : _imgDir + _base + ".jpg"), _imgDir + _base + "_1.jpg" ])>> <div @style="'width: 170px; background: rgba(30,0,0,0.5); border: 2px solid ' + (_isTarget ? '#ef4444' : '#7f1d1d') + '; padding: 10px; border-radius: 6px; text-align: center; backdrop-filter: blur(4px);' + (_enm.currentHp lte 0 ? ' opacity: 0.4; filter: grayscale(100%);' : '') + (_isTarget ? ' box-shadow: 0 0 20px rgba(239,68,68,0.7); transform: scale(1.05); transition: 0.2s;' : ' transition: 0.2s;')"> <div class="combat-portrait" @style="'border: 2px solid ' + (_isTarget ? '#ef4444' : '#7f1d1d') + '; margin: 0 auto 8px auto; width: 110px; height: 165px;'"> <img src="" @src="_avatarUrl" @data-alts="_imgAlts" style="width: 100%; height: 100%; object-fit: cover;" onerror="var a=JSON.parse(this.getAttribute('data-alts')||'[]'),i=parseInt(this.getAttribute('data-i')||'0',10);if(i<a.length){this.setAttribute('data-i',i+1);this.src=a[i];}else{this.onerror=null;this.src='https://placehold.co/512x768/000/f00?text=ENEMY';}"> </div> <div style="color: #fca5a5; font-weight: bold; font-size: 0.8rem; margin-bottom: 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" @title="_enm.name"> <<print _enm.name.split(' (')[0].toUpperCase()>> #<<print _i + 1>> </div> <<set _enmPct to Math.clamp((_enm.currentHp / _enm.maxHp) * 100, 0, 100)>> <div class="combat-bar-bg"><div @style="'width: ' + _enmPct + '%; background: #ef4444; height: 100%;'"></div></div> <div style="color: #cbd5e1; font-size: 0.75rem; margin-top: 5px;">HP: <<print Math.max(0, _enm.currentHp)>> / <<print _enm.maxHp>></div> <<if _enm.currentHp gt 0 and not _isTarget>> <div style="margin-top: 8px;"> <<capture _i>> @@color:#fca5a5;font-size:0.75rem;font-weight:bold;<<link "[ SELECT TARGET ]" "Combat">><<set $currentTargetIdx to _i>><</link>>@@ <</capture>> </div> <<elseif _enm.currentHp lte 0>> <div style="color: #ef4444; font-size:0.75rem; font-weight: bold; margin-top: 8px;">[ KIA ]</div> <<elseif _isTarget>> <div style="color: #ef4444; font-size:0.75rem; font-weight: bold; margin-top: 8px;">> ACTIVE TARGET <</div> <<endif>> </div> <<endfor>> </div> <div style="text-align: center; color: #ef4444; font-weight: bold; font-size: 1.2rem; margin-bottom: 30px; letter-spacing: 4px;">VS</div> <!-- 2. ALLIES FRONT --> <div style="display: flex; justify-content: center; gap: 20px; margin-bottom: 25px;"> <div style="width: 200px; background: rgba(0,20,10,0.5); border: 1px solid #0f766e; padding: 12px; border-radius: 6px; text-align: center; backdrop-filter: blur(4px);"> <div class="combat-portrait" style="border: 2px solid #34d399; margin: 0 auto 8px auto;"> <img @src="$playerAvatarUrl" @class="_plrClass" @data-fb="_plrFallback" style="width: 100%; height: 100%; object-fit: cover;" onerror="if(this.getAttribute('data-fb') && this.src.indexOf('exo_armor')>-1){var f=this.getAttribute('data-fb'); this.removeAttribute('data-fb'); this.src=f; return;} this.src='https://placehold.co/512x768/000/0f0?text=YOU';"> </div> <div style="color: #5eead4; font-weight: bold; font-size: 0.9rem; margin-bottom: 6px;"> <<print (def $characterName and $characterName neq "") ? $characterName.toUpperCase() : "OPERATOR">> <<if $svietlok_buff>> <br><span style="color: #a3e635; font-size: 0.75rem; letter-spacing: 2px; text-shadow: 0 0 8px #a3e635;">[ ŚWIETLOK ]</span> <<endif>> </div> <div style="display: flex; flex-direction: column; gap: 4px; margin-bottom: 8px;"> <div style="color: #a3e635; font-size: 0.75rem; font-weight: bold;">⚔️ DMG: +<<print $playerDmgBonus>></div> <div style="color: #60a5fa; font-size: 0.75rem; font-weight: bold;">🛡️ ARMOR: +<<print $playerArmor>></div> <div style="color: #fbbf24; font-size: 0.75rem; font-weight: bold;">🔋 <<print setup.ammoLabel()>>: <<print setup.ammoLeft()>></div> </div> <<set _plrPct to Math.clamp(($hp / $maxHp) * 100, 0, 100)>> <div class="combat-bar-bg"><div @style="'width: ' + _plrPct + '%; background: #34d399; height: 100%;'"></div></div> <div style="color: #cbd5e1; font-size: 0.8rem; margin-top: 5px;">HP: <<print $hp>>/<<print $maxHp>></div> <div style="color: #38bdf8; font-size: 0.8rem; font-weight: bold; margin-top: 3px;">AP: <<print $playerAP>>/<<print $maxPlayerAP>></div> <<if $playerCover>> <div style="color: #3b82f6; font-size: 0.7rem; font-weight: bold; margin-top: 5px;">[ IN COVER ]</div> <<endif>> </div> <<for _si to 0; _si lt $squad.length; _si++>> <<set _cm to setup.squadView(_si)>> <div @style="'width: 200px; background: rgba(0,20,20,0.5); border: 1px solid ' + (_cm.hp gt 0 ? '#0284c7' : '#475569') + '; padding: 12px; border-radius: 6px; text-align: center; backdrop-filter: blur(4px);' + (_cm.hp gt 0 ? '' : ' opacity:0.45;')"> <div class="combat-portrait" style="border: 2px solid #38bdf8; margin: 0 auto 8px auto;"> <img @src="_cm.avatar" style="width: 100%; height: 100%; object-fit: cover;" onerror="this.src='https://placehold.co/512x768/000/38bdf8?text=ALLY';"> </div> <div @style="'font-weight: bold; font-size: 0.9rem; margin-bottom: 2px; color:' + (_cm.color || '#38bdf8')"><<print String(_cm.disp || _cm.name).toUpperCase()>></div> <div style="color: #7dd3fc; font-size: 0.65rem; margin-bottom: 6px;">[<<print _cm.name>>]<<if _cm.hp lte 0>> KIA<</if>></div> <div style="color: #fca5a5; font-size: 0.75rem; font-weight: bold; margin-bottom: 5px;">⚔ BONUS DMG: +<<print _cm.dmgMod>></div> <<set _compPct to Math.clamp((_cm.hp / _cm.maxHp) * 100, 0, 100)>> <div class="combat-bar-bg"><div @style="'width: ' + _compPct + '%; background: #38bdf8; height: 100%;'"></div></div> <div style="color: #cbd5e1; font-size: 0.8rem; margin-top: 5px;">HP: <<print Math.max(0, _cm.hp)>>/<<print _cm.maxHp>></div> <div style="color: #38bdf8; font-size: 0.8rem; font-weight: bold; margin-top: 3px;">AP: <<print _cm.ap>>/<<print _cm.maxAp>></div> </div> <</for>> </div> <<endif>> <div class="bt-hud"> <!-- CONTROL / TURN MENU --> <<if $combatTurn eq "player">> <div class="bt-pturn" style="background: rgba(0, 40, 20, 0.6); border: 1px solid #34d399; padding: 12px; border-radius: 6px;"> <div style="color: #34d399; font-weight: bold; margin-bottom: 10px; font-size: 0.95rem;">>>> OPERATOR TURN (AP Available: <<print $playerAP>>)</div> <<if $grabbed and not _bt>> <div style="border:2px solid #fca5a5; background:rgba(127,29,29,0.35); color:#fecaca; padding:10px; border-radius:6px; text-align:center; margin-bottom:12px;"> <<print ($grabName eq "The Zmora" ? "PINNED - the Zmora is sitting on your chest! Break free before the next round." : "HELD UNDERWATER - you are drowning! Break free before the next round.")>> <<if $playerAP gte 2>> <div class="action-btn" style="margin-top:8px; padding:8px; font-size:0.9rem;"> <<link "BREAK FREE (2 AP, 65%)" "Combat">> <<set $playerAP -= 2>> <<if random(1, 100) lte 65>> <<set $grabbed to false>> <<set $combatLog.unshift("You tear free of " + $grabName + "'s grip and gasp for air!")>> <<else>> <<set $combatLog.unshift("You thrash, but " + $grabName + " holds you down!")>> <<endif>> <</link>> </div> <<else>> <div style="margin-top:6px; color:#94a3b8;">(Need 2 AP to break free)</div> <<endif>> </div> <<endif>> <<if _bt>> <div class="bt-move"> <div class="bt-movetitle">MOVEMENT (1 AP each) - Distance: <<print ["CLOSE", "MID", "FAR"][_rng]>></div> <<if $grabbed>> <div class="bt-grab"><<print ($grabName eq "The Zmora" ? "PINNED - the Zmora is sitting on your chest!" : "HELD UNDERWATER - you are drowning!")>> <<if $playerAP gte 2>> <div class="action-btn" style="border-color:#fca5a5; color:#fecaca; margin-top:0.4cqw;"> <<link "BREAK FREE<br><span style='font-size:0.65rem;'>(2 AP, 65%)</span>" "Combat">> <<set $playerAP -= 2>> <<if random(1, 100) lte 65>> <<set $grabbed to false>> <<set $combatLog.unshift("You tear free of " + $grabName + "'s grip and gasp for air!")>> <<else>> <<set $combatLog.unshift("You thrash, but " + $grabName + " holds you down!")>> <<endif>> <</link>> </div> <<else>> <div class="disabled-btn-small">BREAK FREE<br><span style='font-size:0.65rem;'>(2 AP)</span></div> <<endif>> </div> <<endif>> <div class="bt-moverow"> <<if $playerAP gte 1 and $combatRange gt 0 and not $grabbed>> <div class="action-btn" style="border-color:#22d3ee; color:#67e8f9;"> <<link "CHARGE<br><span style='font-size:0.65rem;'>(close one zone)</span>" "Combat">> <<set $combatRange to Math.max(0, $combatRange - 1)>> <<set $playerAP -= 1>> <<set $combatLog.unshift("You advance on the enemy line.")>> <</link>> </div> <<else>> <div class="disabled-btn-small">CHARGE<br><span style='font-size:0.65rem;'><<print ($combatRange lte 0 ? "(ALREADY CLOSE)" : "(1 AP)")>></span></div> <<endif>> <<if $playerAP gte 1 and $combatRange lt 2 and not $grabbed>> <div class="action-btn" style="border-color:#22d3ee; color:#67e8f9;"> <<link "PULL BACK<br><span style='font-size:0.65rem;'>(open one zone)</span>" "Combat">> <<set $combatRange to Math.min(2, $combatRange + 1)>> <<set $playerAP -= 1>> <<set $combatLog.unshift("You fall back and open the distance.")>> <</link>> </div> <<else>> <div class="disabled-btn-small">PULL BACK<br><span style='font-size:0.65rem;'><<print ($combatRange gte 2 ? "(ALREADY FAR)" : "(1 AP)")>></span></div> <<endif>> </div> <<if setup.squadAlive().length gt 0>> <div class="bt-moverow"> <<for _si to 0; _si lt $squad.length; _si++>> <<if $squad[_si].hp gt 0>> <<set _ao to $squad[_si]>> <<set _aname to String(_ao.disp || _ao.name).split(' ')[0]>> <<if $playerAP gte 1>> <div class="action-btn" style="border-color:#a78bfa; color:#ddd6fe;"> <<capture _si, _aname>> <<if $squad[_si].pos eq "front">> <<set _albl to "RECALL " + _aname.toUpperCase() + "<br><span style='font-size:0.65rem;'>(draws less fire, 1 AP)</span>">> <<link _albl "Combat">> <<set $squad[_si].pos to "back">> <<set $playerAP -= 1>> <<set $combatLog.unshift("You order " + _aname + " back. They now support from behind.")>> <</link>> <<else>> <<set _albl to "SEND " + _aname.toUpperCase() + " FORWARD<br><span style='font-size:0.65rem;'>(tanks, 1 AP)</span>">> <<link _albl "Combat">> <<set $squad[_si].pos to "front">> <<set $playerAP -= 1>> <<set $combatLog.unshift("You send " + _aname + " to the front line. They will draw most of the enemy fire.")>> <</link>> <<endif>> <</capture>> </div> <<else>> <div class="disabled-btn-small"><<print ($squad[_si].pos eq "front" ? "RECALL " : "SEND ")>><<print _aname.toUpperCase()>><br><span style='font-size:0.65rem;'>(1 AP)</span></div> <<endif>> <<endif>> <</for>> </div> <<endif>> </div> <<endif>> <<if _isWeaponRanged and setup.ammoLeft() lte 0>> <div style="background: rgba(239, 68, 68, 0.2); border: 2px solid #ef4444; padding: 15px; border-radius: 6px; text-align: center; margin-bottom: 15px;"> <div style="color: #fca5a5; font-weight: bold; font-size: 1rem; margin-bottom: 10px;">⚠️ OUT OF <<print setup.ammoLabel()>>! CANNOT FIRE <<print (setup.usesScrapAmmo() ? 'THE SCRAP LAUNCHER' : 'RANGED WEAPON')>>!</div> <div class="melee-switch-btn"> <<link "SWITCH TO MELEE (EQUIP COMBAT KNIFE)" "Combat">> <<set $equipment.rightHand to "Combat Knife">> <<set $combatLog.unshift("You dropped your dry firearm and switched to a Combat Knife!")>> <</link>> </div> </div> <<else>> <<set _canStrike to ((not _bt) or _isWeaponRanged or $combatRange lte 0)>> <<if not _canStrike>> <div style="background: rgba(2,6,12,0.8); border: 1px solid #22d3ee; padding: 12px; border-radius: 6px; text-align: center; margin-bottom: 12px; color: #e2e8f0; font-size: 0.85rem;"> TARGET OUT OF REACH - your melee weapon needs CLOSE range. Use CHARGE to close in. </div> <<else>> <!-- TACTICAL LIMB TARGETING --> <div style="background: rgba(0, 0, 0, 0.4); border: 1px dashed #fbbf24; padding: 10px; border-radius: 4px; margin-bottom: 12px;"> <div style="color: #fbbf24; font-size: 0.9rem; font-weight: bold; margin-bottom: 8px;">🎯 TACTICAL LIMB TARGETING <<if _isWeaponRanged>>(Consumes 1 <<print setup.ammoLabel()>>)<</if>></div> <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;"> <!-- HEADSHOT (3 AP) --> <<if $playerAP gte 3>> <div class="limb-btn"> <<set _headChance to setup.headChance()>> <<set _btnText to "HEADSHOT (" + _headChance + "% Hit, 2.5x)<br><span style='font-size:0.65rem;'>(3 AP)</span>">> <<link _btnText "Combat">> <<if _isWeaponRanged>><<run setup.useAmmo(1)>><</if>> <<set _hitRoll to random(1, 100)>> <<set _activeHeadChance to setup.headChance()>> <<if _hitRoll lte _activeHeadChance>> <<set _baseDmg to random(12, 20)>> <<set _dmg to Math.floor((_baseDmg + $playerDmgBonus) * 2.5)>> <<damageEnemy _dmg>> <<set $combatLog.unshift("CRITICAL HEADSHOT on " + $enemyGroup[$currentTargetIdx].name.split(" (")[0] + " #" + ($currentTargetIdx + 1) + "! Dealt massive " + _dmg + " damage!")>> <<else>> <<set $combatLog.unshift("HEADSHOT MISSED!")>> <<endif>> <<set $playerAP -= 3>> <</link>> </div> <<else>> <div class="disabled-btn-large">HEADSHOT<br><span style='font-size:0.65rem;'>(3 AP)</span></div> <<endif>> <!-- TORSO (1 AP) --> <<if $playerAP gte 1>> <div class="limb-btn"> <<link "TORSO (85% Hit)<br><span style='font-size:0.65rem;'>(1 AP)</span>" "Combat">> <<if _isWeaponRanged>><<run setup.useAmmo(1)>><</if>> <<set _hitRoll to random(1, 100)>> <<if _hitRoll lte 85>> <<set _baseDmg to random(8, 14)>> <<set _dmg to _baseDmg + $playerDmgBonus>> <<damageEnemy _dmg>> <<run setup.ch3.coreStrike($enemyGroup[$currentTargetIdx])>> <<set $combatLog.unshift("Solid Torso hit for " + _dmg + " damage.")>> <<else>> <<set $combatLog.unshift("Torso shot deflected!")>> <<endif>> <<set $playerAP -= 1>> <</link>> </div> <<else>> <div class="disabled-btn-large">TORSO<br><span style='font-size:0.65rem;'>(1 AP)</span></div> <<endif>> <!-- ARM SHOT (2 AP) --> <<if $playerAP gte 2>> <div class="limb-btn"> <<link "ARM SHOT (65% Hit)<br><span style='font-size:0.65rem;'>(2 AP) cripples aim</span>" "Combat">> <<if _isWeaponRanged>><<run setup.useAmmo(1)>><</if>> <<set _hitRoll to random(1, 100)>> <<if _hitRoll lte 65>> <<set _baseDmg to random(4, 8)>> <<set _dmg to _baseDmg + Math.floor($playerDmgBonus * 0.5)>> <<damageEnemy _dmg>> <<set $combatLog.unshift("Arm shot crippled for " + _dmg + " damage.")>> <<if $enemyGroup[$currentTargetIdx].currentHp gt 0>> <<set $enemyGroup[$currentTargetIdx].armHurt to 2>> <<set $combatLog.unshift("> " + $enemyGroup[$currentTargetIdx].name.split(" (")[0] + " #" + ($currentTargetIdx + 1) + " clutches a ruined arm. Its attacks miss 50% of the time for 2 rounds.")>> <</if>> <<else>> <<set $combatLog.unshift("Arm shot missed!")>> <<endif>> <<set $playerAP -= 2>> <</link>> </div> <<else>> <div class="disabled-btn-large">ARM SHOT<br><span style='font-size:0.65rem;'>(2 AP)</span></div> <<endif>> <!-- LEG SHOT (2 AP) --> <<if $playerAP gte 2>> <div class="limb-btn"> <<link "LEG SHOT (60% Hit)<br><span style='font-size:0.65rem;'>(2 AP) hobbles, knocks back</span>" "Combat">> <<if _isWeaponRanged>><<run setup.useAmmo(1)>><</if>> <<set _hitRoll to random(1, 100)>> <<if _hitRoll lte 60>> <<set _baseDmg to random(5, 9)>> <<set _dmg to _baseDmg + $playerDmgBonus>> <<damageEnemy _dmg>> <<set $combatLog.unshift("Leg shot struck for " + _dmg + " damage.")>> <<if $enemyGroup[$currentTargetIdx].currentHp gt 0>> <<set $enemyGroup[$currentTargetIdx].legHurt to 2>> <<set _legMsg to " It limps: melee enemies cannot close in and all its attacks deal 40% less for 2 rounds.">> <<if _bt and $combatRange lt 2 and setup.isMeleeEnemy($enemyGroup[$currentTargetIdx])>> <<set $combatRange to $combatRange + 1>> <<set _legMsg to " The hit KNOCKS IT BACK, the fight is now farther away!" + _legMsg>> <</if>> <<set $combatLog.unshift("> " + $enemyGroup[$currentTargetIdx].name.split(" (")[0] + " #" + ($currentTargetIdx + 1) + " buckles." + _legMsg)>> <</if>> <<else>> <<set $combatLog.unshift("Leg shot missed!")>> <<endif>> <<set $playerAP -= 2>> <</link>> </div> <<else>> <div class="disabled-btn-large">LEG SHOT<br><span style='font-size:0.65rem;'>(2 AP)</span></div> <<endif>> </div> </div> <<endif>> <!-- SUPPORT ACTIONS & FLEE --> <div style="display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; margin-bottom: 12px;"> <!-- Take Cover --> <<if $playerAP gte 1>> <div class="action-btn" style="border-color:#3b82f6; color:#3b82f6; background:rgba(59,130,246,0.1);"> <<link "TAKE COVER<br><span style='font-size:0.65rem;'>(1 AP)</span>" "Combat">> <<set $playerCover to true>> <<set $playerAP -= 1>> <<set $combatLog.unshift("You took defensive cover! Incoming damage will be heavily reduced.")>> <</link>> </div> <<else>> <div class="disabled-btn-small">TAKE COVER<br><span style='font-size:0.65rem;'>(1 AP)</span></div> <<endif>> <!-- Bandage --> <<if $playerAP gte 1>> <<if ($materials.sterileBandage || 0) gte 1>> <div class="heal-btn"> <<set _healAmt to setup.healBoost(Math.round($maxHp * 0.15))>> <<set _btnText to "BANDAGE (15%) [x" + $materials.sterileBandage + "]<br><span style='font-size:0.65rem;'>(1 AP)</span>">> <<link _btnText "Combat">> <<set $materials.sterileBandage -= 1>> <<set $hp += _healAmt>> <<if $hp gt $maxHp>><<set $hp to $maxHp>><</if>> <<set $playerAP -= 1>> <<set $combatLog.unshift("You applied a Sterile Bandage and healed " + _healAmt + " HP.")>> <</link>> </div> <<else>> <div class="disabled-btn-small">BANDAGE (15%)<br><span style='font-size:0.65rem;'>(NO ITEMS)</span></div> <<endif>> <<else>> <div class="disabled-btn-small">BANDAGE (15%)<br><span style='font-size:0.65rem;'>(1 AP)</span></div> <<endif>> <!-- Stimpack --> <<if $playerAP gte 2>> <<if ($materials.medicalStimpack || 0) gte 1>> <div class="heal-btn"> <<set _healAmt to Math.round($maxHp * 0.40)>> <<set _btnText to "STIMPACK (40%) [x" + $materials.medicalStimpack + "]<br><span style='font-size:0.65rem;'>(2 AP)</span>">> <<link _btnText "Combat">> <<set $materials.medicalStimpack -= 1>> <<set $hp += _healAmt>> <<if $hp gt $maxHp>><<set $hp to $maxHp>><</if>> <<set $playerAP -= 2>> <<set $combatLog.unshift("You injected a Medical Stimpack and recovered " + _healAmt + " HP!")>> <</link>> </div> <<else>> <div class="disabled-btn-small">STIMPACK (40%)<br><span style='font-size:0.65rem;'>(NO ITEMS)</span></div> <<endif>> <<else>> <div class="disabled-btn-small">STIMPACK (40%)<br><span style='font-size:0.65rem;'>(2 AP)</span></div> <<endif>> <!-- Capture --> <<set _captureThreshold to Math.floor($enemyGroup[$currentTargetIdx].maxHp * 0.25)>> <<if !setup.canCapture($enemyGroup[$currentTargetIdx])>> <div data-tour="bt-capture" class="disabled-btn-small">CAPTURE<br><span style='font-size:0.65rem;'>(CANNOT BE BOUND)</span></div> <<elseif _bt and $combatRange gt 0>> <div data-tour="bt-capture" class="disabled-btn-small">CAPTURE<br><span style='font-size:0.65rem;'>(TOO FAR)</span></div> <<elseif $enemyGroup[$currentTargetIdx].currentHp lte _captureThreshold>> <<if ($materials.rope || 0) gt 0>> <<if $playerAP gte 2>> <div data-tour="bt-capture" class="capture-btn"> <<link "CAPTURE<br><span style='font-size:0.65rem;'>(2 AP)</span>" "Combat">> <<set $materials.rope -= 1>> <<if ndef $prisoners>><<set $prisoners to 0>><</if>> <<run setup.addPrisoner($enemyGroup[$currentTargetIdx])>> <<set _xpGain to random(25, 40)>> <<set $xp += _xpGain>> <<set $enemyGroup[$currentTargetIdx].currentHp to 0>> <<set $combatLog.unshift("CAPTURE SUCCESS! Bound " + $enemyGroup[$currentTargetIdx].name.split(" (")[0] + " #" + ($currentTargetIdx + 1) + " with rope and dragged into custody. (+1 Prisoner)")>> <<set $playerAP -= 2>> <</link>> </div> <<else>> <div data-tour="bt-capture" class="disabled-btn-small">CAPTURE<br><span style='font-size:0.65rem;'>(2 AP)</span></div> <<endif>> <<else>> <div data-tour="bt-capture" class="disabled-btn-small">CAPTURE<br><span style='font-size:0.65rem;'>(NO ROPE)</span></div> <<endif>> <<else>> <div data-tour="bt-capture" class="disabled-btn-small">CAPTURE<br><span style='font-size:0.65rem;'>(>25%)</span></div> <<endif>> <!-- Flee --> <<if ($playerAP gte 1 or setup.tm("freeFlee")) and not $grabbed>> <div class="flee-btn"> <<link "FLEE (ABANDON)<br><span style='font-size:0.65rem;'>(1 AP)</span>" "Camp">> <<set $playerAP to Math.max(0, $playerAP - (setup.tm("freeFlee") ? 0 : 1))>> <<set $playerCover to false>> <<unset $enemyGroup, $targetPOILoot, $targetPOIName, $checkpointWave>> <<if $combatContext is "dungeon" or $combatContext is "cipher_ch1" or $combatContext is "ch3_omicron">><<unset $combatContext>><</if>> <<set $combatLog.unshift("> FLEE SUCCESS: You dropped everything in pure panic and fled empty-handed!")>> <<run window.setup.updateSidebar()>> <</link>> </div> <<else>> <div class="disabled-btn-small">FLEE (ABANDON)<br><span style='font-size:0.65rem;'>(1 AP)</span></div> <<endif>> </div> <<endif>> <!-- End Turn --> <div style="background: rgba(56, 189, 248, 0.1); border: 1px solid #38bdf8; text-align: center; padding: 10px; border-radius: 4px;"> <<if setup.squadAlive().length gt 0>> @@color:#38bdf8;<<link "END TURN -> ALLIES" "Combat">> <<run setup.squadStartTurn()>> <</link>>@@ <<else>> @@color:#38bdf8;<<link "END TURN -> ENEMY SQUAD" "Combat">> <<set $playerAP to $maxPlayerAP>> <<set $combatTurn to "enemy">> <</link>>@@ <<endif>> </div> </div> <!-- 2. COMPANION TURN --> <<elseif $combatTurn eq "companion">> <div style="background: rgba(0, 30, 40, 0.6); border: 1px solid #38bdf8; padding: 12px; border-radius: 6px;"> <div style="color: #38bdf8; font-weight: bold; margin-bottom: 10px; font-size: 0.95rem;">>>> <<print String(_compDisplayName).toUpperCase()>> TURN (AP: <<print $companion.ap>>) | ALLY <<print $squadIdx + 1>> OF <<print $squad.length>></div> <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;"> <!-- 1. ROOKIE GUNNER --> <<if $companion.name eq "Rookie Gunner">> <<if $companion.ap gte 1>> <div class="action-btn"> <<link "BASIC SHOT (1 AP)" "Combat">> <<set _dmg to random(8, 14) + $companion.dmgMod>> <<damageEnemy _dmg>> <<run setup.ch3.coreStrike($enemyGroup[$currentTargetIdx])>> <<set $companion.ap -= 1>> <<set $combatLog.unshift("> " + _compDisplayName + " fired a basic shot for " + _dmg + " damage.")>> <</link>> </div> <<else>> <div class="disabled-btn-small">BASIC SHOT (1 AP)</div> <<endif>> <<if $companion.ap gte 2>> <div class="action-btn" style="border-color:#fbbf24; color:#fbbf24;"> <<link "PANIC SPRAY (2 AP)" "Combat">> <<for _i to 0; _i lt $enemyGroup.length; _i++>> <<if $enemyGroup[_i].currentHp gt 0>> <<set _dmg to random(10, 20) + Math.floor($companion.dmgMod / 2)>> <<set $enemyGroup[_i].currentHp -= _dmg>> <<run setup.ch3.coreStrike($enemyGroup[_i])>> <<if $enemyGroup[_i].currentHp lt 0>><<set $enemyGroup[_i].currentHp to 0>><</if>> <<endif>> <</for>> <<set $companion.ap -= 2>> <<set $combatLog.unshift("> " + _compDisplayName + " panicked and sprayed bullets, hitting ALL enemies in the squad!")>> <</link>> </div> <<else>> <div class="disabled-btn-small">PANIC SPRAY (2 AP)</div> <<endif>> <!-- 2. FIELD MEDIC --> <<elseif $companion.name eq "Field Medic">> <<if $companion.ap gte 1>> <div class="action-btn"> <<link "PISTOL SHOT (1 AP)" "Combat">> <<set _dmg to random(6, 12) + Math.floor($companion.dmgMod/2)>> <<damageEnemy _dmg>> <<run setup.ch3.coreStrike($enemyGroup[$currentTargetIdx])>> <<set $companion.ap -= 1>> <<set $combatLog.unshift("> " + _compDisplayName + " fired a quick pistol shot for " + _dmg + " damage.")>> <</link>> </div> <<else>> <div class="disabled-btn-small">PISTOL SHOT (1 AP)</div> <<endif>> <<if $companion.ap gte 2>> <div class="heal-btn"> <<link "FIELD PATCH (2 AP)" "Combat">> <<set _heal to random(30, 50) + ($level * 5)>> <<set $hp = Math.min($maxHp, $hp + _heal)>> <<set $companion.hp = Math.min($companion.maxHp, $companion.hp + _heal)>> <<run setup.squadHealOthers(Math.floor(_heal / 2), $squadIdx)>> <<set $companion.ap -= 2>> <<set $combatLog.unshift("> " + _compDisplayName + " deployed field patches, restoring " + _heal + " HP to both you and the medic, and half of that to the rest of the squad!")>> <</link>> </div> <<else>> <div class="disabled-btn-small">FIELD PATCH (2 AP)</div> <<endif>> <!-- 3. DEMO EXPERT --> <<elseif $companion.name eq "Demo Expert">> <<if $companion.ap gte 1>> <div class="action-btn"> <<link "SUPPRESSIVE FIRE (1 AP)" "Combat">> <<set _dmg to random(10, 18) + $companion.dmgMod>> <<damageEnemy _dmg>> <<run setup.ch3.coreStrike($enemyGroup[$currentTargetIdx])>> <<set $companion.ap -= 1>> <<set $combatLog.unshift("> " + _compDisplayName + " laid down suppressive fire for " + _dmg + " damage.")>> <</link>> </div> <<else>> <div class="disabled-btn-small">SUPPRESSIVE FIRE (1 AP)</div> <<endif>> <<if $companion.ap gte 2>> <div class="action-btn" style="border-color:#ef4444; color:#ef4444;"> <<link "THROW FRAG GRENADE (2 AP)" "Combat">> <<set _dmg to random(25, 45) + $companion.dmgMod>> <<for _i to 0; _i lt $enemyGroup.length; _i++>> <<if $enemyGroup[_i].currentHp gt 0>> <<set $enemyGroup[_i].currentHp -= _dmg>> <<run setup.ch3.coreStrike($enemyGroup[_i])>> <<if $enemyGroup[_i].currentHp lt 0>><<set $enemyGroup[_i].currentHp to 0>><</if>> <<endif>> <</for>> <<set $companion.ap -= 2>> <<set $combatLog.unshift("> " + _compDisplayName + " lobbed a frag grenade, dealing " + _dmg + " damage to the entire enemy squad!")>> <</link>> </div> <<else>> <div class="disabled-btn-small">THROW FRAG GRENADE (2 AP)</div> <<endif>> <!-- 4. SECTOR Hacker --> <<elseif $companion.name eq "Sector Hacker">> <<if $companion.ap gte 1>> <div class="action-btn"> <<link "SCRAMBLE OPTICS (1 AP)" "Combat">> <<set _dmg to random(8, 14) + $companion.dmgMod>> <<damageEnemy _dmg>> <<set $enemyGroup[$currentTargetIdx].opticsScrambled to true>> <<set $companion.ap -= 1>> <<set $combatLog.unshift("> " + _compDisplayName + " scrambled target optics (" + (setup.isBossFoe($enemyGroup[$currentTargetIdx]) ? "-35%" : "-75%") + " Hit Chance next turn)!")>> <</link>> </div> <<else>> <div class="disabled-btn-small">SCRAMBLE OPTICS (1 AP)</div> <<endif>> <<if $companion.ap gte 2>> <div class="action-btn" style="border-color:#38bdf8; color:#38bdf8;"> <<link "SYSTEM OVERLOAD (2 AP)" "Combat">> <<set _dmg to random(20, 35) + $companion.dmgMod>> <<damageEnemy _dmg>> <<set $companion.ap -= 2>> <<if setup.stunRoll($enemyGroup[$currentTargetIdx], 1)>> <<set $enemyGroup[$currentTargetIdx].isStunned to true>> <<set $combatLog.unshift("> " + _compDisplayName + " triggered SYSTEM OVERLOAD! Target is STUNNED for the next turn!")>> <<elseif $enemyGroup[$currentTargetIdx].unknownThreat>> <<set $combatLog.unshift("> " + _compDisplayName + " triggered SYSTEM OVERLOAD! The Unknown Threat shrugs it off: it cannot be stunned.")>> <<else>> <<set $combatLog.unshift("> " + _compDisplayName + " triggered SYSTEM OVERLOAD! The boss resists the stun.")>> <</if>> <</link>> </div> <<else>> <div class="disabled-btn-small">SYSTEM OVERLOAD (2 AP)</div> <<endif>> <!-- 5. TRAINED K9 / CERBERUS --> <<elseif $companion.name eq "Trained K9" or $companion.name eq "Cerberus War-Hound">> <<if $companion.ap gte 1>> <div class="action-btn"> <<link "BITE (1 AP)" "Combat">> <<set _dmg to random(12, 18) + $companion.dmgMod>> <<damageEnemy _dmg>> <<set $companion.ap -= 1>> <<set $combatLog.unshift("> " + _compDisplayName + " lunged and bit the enemy for " + _dmg + " damage.")>> <</link>> </div> <<else>> <div class="disabled-btn-small">BITE (1 AP)</div> <<endif>> <<if $companion.ap gte 2>> <div class="action-btn" style="border-color:#ef4444; color:#ef4444;"> <<link "THROAT RIP (2 AP)" "Combat">> <<set _dmg to random(50, 85) + ($companion.dmgMod * 1.5)>> <<damageEnemy _dmg>> <<set $companion.ap -= 2>> <<set $combatLog.unshift("> " + _compDisplayName + " performed a brutal THROAT RIP, dealing massive " + _dmg + " critical damage!")>> <</link>> </div> <<else>> <div class="disabled-btn-small">THROAT RIP (2 AP)</div> <<endif>> <!-- 6. SYNDICATE DEFECTOR --> <<elseif $companion.name eq "Syndicate Defector">> <<if $companion.ap gte 1>> <div class="action-btn"> <<link "PLASMA BURST (1 AP)" "Combat">> <<set _dmg to random(15, 25) + $companion.dmgMod>> <<damageEnemy _dmg>> <<run setup.ch3.coreStrike($enemyGroup[$currentTargetIdx])>> <<set $companion.ap -= 1>> <<set $combatLog.unshift("> " + _compDisplayName + " fired a heavy plasma burst for " + _dmg + " damage.")>> <</link>> </div> <<else>> <div class="disabled-btn-small">PLASMA BURST (1 AP)</div> <<endif>> <<if $companion.ap gte 2>> <div class="action-btn" style="border-color:#10b981; color:#10b981;"> <<link "DEFENSIVE BARRAGE (2 AP)" "Combat">> <<set _dmg to random(4, 8)>> <<for _i to 0; _i lt $enemyGroup.length; _i++>> <<if $enemyGroup[_i].currentHp gt 0>> <<set $enemyGroup[_i].currentHp -= _dmg>> <<if $enemyGroup[_i].currentHp lt 0>><<set $enemyGroup[_i].currentHp to 0>><</if>> <<endif>> <</for>> <<set $companion.hp = Math.min($companion.maxHp, $companion.hp + 20)>> <<set $companion.isTaunting to true>> <<set $companion.barrage to true>> <<set $companion.ap -= 2>> <<set $combatLog.unshift("> " + _compDisplayName + " triggered Defensive Barrage! Taunting enemies, dealing minimal harassment damage, and bracing for absolute defense.")>> <</link>> </div> <<else>> <div class="disabled-btn-small">DEFENSIVE BARRAGE (2 AP)</div> <<endif>> <<elseif setup.MERC[$companion.name]>> <!-- 7. BAR-LEVEL MERCENARIES (data driven, see SquadScript) --> <<set _mdef to setup.MERC[$companion.name]>> <<for _k to 0; _k lt _mdef.skills.length; _k++>> <<set _sk to _mdef.skills[_k]>> <<if $companion.ap gte _sk.ap>> <<capture _k, _sk>> <div class="action-btn" @style="'border-color:' + _sk.color + '; color:' + _sk.color"> <<set _mlbl to _sk.btn>> <<link _mlbl "Combat">> <<run setup.mercAct($companion.name, _k)>> <</link>> </div> <</capture>> <<else>> <div class="disabled-btn-small"><<print _sk.btn>></div> <<endif>> <</for>> <<else>> <!-- 8. GENERIC FALLBACK TO PREVENT CRASHES --> <<if $companion.ap gte 1>> <div class="action-btn"> <<link "BASIC ATTACK (1 AP)" "Combat">> <<set _dmg to random(8, 14) + $companion.dmgMod>> <<damageEnemy _dmg>> <<run setup.ch3.coreStrike($enemyGroup[$currentTargetIdx])>> <<set $companion.ap -= 1>> <<set $combatLog.unshift("> " + _compDisplayName + " attacked for " + _dmg + " damage.")>> <</link>> </div> <<else>> <div class="disabled-btn-small">BASIC ATTACK (1 AP)</div> <<endif>> <<endif>> </div> <div class="ct-turnbar"> <<if setup.squadAlive().length gt 1 and ($squadIdx lt $squad.length - 1)>> <<link "<span class='ct-btn ct-next'>NEXT ALLY ▶</span>" "Combat">> <<run setup.squadNext()>> <</link>> <<endif>> <<link "<span class='ct-btn ct-end'>END TEAM TURN ■</span>" "Combat">> <<run setup.squadEndAll()>> <</link>> </div> </div> <!-- 3. ENEMY TURN --> <<elseif $combatTurn eq "enemy">> <div style="background: rgba(40, 0, 0, 0.6); border: 1px solid #ef4444; padding: 15px; border-radius: 6px; text-align: center;"> <div style="color: #fca5a5; font-weight: bold; margin-bottom: 12px; font-size: 1rem;">>>> ENEMY SQUAD COUNTER-ATTACK PHASE</div> <<script>> let logs = []; /* SLAUGHTERHOUSE: toxic gas damages the player every turn */ if (State.variables.slaughterGas && State.variables.combatContext === "slaughterhouse_assault" && State.variables.hp > 0) { let gasDmg = 5 + Math.floor((State.variables.level || 1) * 1.5); State.variables.hp = Math.max(0, State.variables.hp - gasDmg); logs.unshift("☣ TOXIC GAS burns your lungs for " + gasDmg + " damage!"); } let v = State.variables; const TS = (window.setup && window.setup.tmRound) ? window.setup : setup; TS.tmRound(logs); let hasSafePerk = false; if (v.playerPerks && Array.isArray(v.playerPerks)) { if (v.playerPerks.includes("Safe") || v.playerPerks.includes("Safely")) hasSafePerk = true; } if (v.perks && Array.isArray(v.perks)) { if (v.perks.includes("Safe") || v.perks.includes("Safely")) hasSafePerk = true; } if (v.playerClass === "The Field Medic" || v.playerClass === "The Mutated") { hasSafePerk = true; } const BT = (v.combatUI !== "classic"); let rng = (v.combatRange === undefined || v.combatRange === null) ? 1 : v.combatRange; let advanced = false; for (let i = 0; i < v.enemyGroup.length; i++) { let e = v.enemyGroup[i]; if (e.currentHp > 0) { let cleanName = e.name.split(' (')[0]; if (v.playerClass === "The Mutated") { let toxicDmg = Math.floor((v.level || 1) * 2) + 3; e.currentHp -= toxicDmg; if (e.currentHp < 0) e.currentHp = 0; logs.unshift("🧪 TOXIC BLOOD: " + cleanName + " suffers " + toxicDmg + " passive toxic damage!"); if (e.currentHp === 0) { logs.unshift(cleanName + " succumbed to the toxic exposure!"); continue; } } if (e.isStunned) { e.isStunned = false; logs.unshift(cleanName + " is STUNNED and cannot act this turn!"); continue; } let legMul = 1; if (e.legHurt > 0) { e.legHurt -= 1; if (BT && TS.isMeleeEnemy(e) && rng >= 1 && !e.noonStalker && !e.unknownThreat) { logs.unshift(cleanName + " limps on a wrecked leg and cannot close the distance!"); continue; } legMul = 0.6; } if (e.isUtopek) { let acid = Math.max(3, Math.round((e.maxDmg || 10) * 0.3)); let ar = TS.tmIncoming(acid, e); if (ar.dodged) { logs.unshift("Utopek-7 spits bio-acid, but you twist out of the way!"); } else { v.hp = Math.max(0, v.hp - ar.dmg); logs.unshift("☣ BIO-ACID: Utopek-7 spits a stream that eats through your gear for " + ar.dmg + " damage! (ignores armor)"); } if (v.grabbed) { let dr = Math.max(4, Math.round((v.maxHp || 100) * 0.06)); v.hp = Math.max(0, v.hp - dr); logs.unshift("🌊 DROWNING: you swallow black water for " + dr + " damage! Break free!"); } else if (Math.random() < 0.35) { v.grabbed = true; v.grabName = "Utopek-7"; logs.unshift("🌊 Utopek-7 lunges out of the water and DRAGS YOU UNDER! You cannot move or flee until you break free."); } } if (e.isTopielica) { let bw = Math.max(3, Math.round((e.maxDmg || 10) * 0.3)); let br = TS.tmIncoming(bw, e); if (br.dodged) { logs.unshift("Topielica spews black water, but you twist out of the way!"); } else { v.hp = Math.max(0, v.hp - br.dmg); logs.unshift("🌊 BLACK WATER: Topielica spits a stream of foul river water over you for " + br.dmg + " damage! (ignores armor)"); } if (v.grabbed) { let dr2 = Math.max(4, Math.round((v.maxHp || 100) * 0.06)); v.hp = Math.max(0, v.hp - dr2); logs.unshift("🌊 DROWNING: you swallow black water for " + dr2 + " damage! Break free!"); } else if (Math.random() < 0.35) { v.grabbed = true; v.grabName = "Topielica"; logs.unshift("🌊 Topielica seizes your ankles and DRAGS YOU UNDER! You cannot move or flee until you break free."); } } if (e.isMarzanna) { let cd = Math.max(3, Math.round((e.maxDmg || 12) * 0.3)); let cr2 = TS.tmIncoming(cd, e); if (cr2.dodged) { logs.unshift("Marzanna's frozen hand brushes past your sleeve. You feel the cold but nothing more."); } else { v.hp = Math.max(0, v.hp - cr2.dmg); v.stamina = Math.max(0, (v.stamina || 0) - 12); logs.unshift("❄ WINTER CHILL: Marzanna's frost bites for " + cr2.dmg + " damage and saps 12 stamina! (ignores armor)"); } } if (e.isZmora) { let sl = setup.shieldLevel ? setup.shieldLevel() : 0; let pulse = Math.max(3, Math.round((e.maxDmg || 10) * 0.25 * (1 - 0.2 * sl))); let pr = TS.tmIncoming(pulse, e); if (pr.dodged) { logs.unshift("A neural pulse rolls across your visor. You shake it off."); } else { v.hp = Math.max(0, v.hp - pr.dmg); v.stamina = Math.max(0, (v.stamina || 0) - Math.round(14 * (1 - 0.2 * sl))); logs.unshift("🧠 NEURAL PULSE: the Zmora screams static into your skull for " + pr.dmg + " damage and saps your stamina! (ignores armor)"); } if (v.grabbed) { let cr = Math.max(4, Math.round((v.maxHp || 100) * 0.05 * (1 - 0.2 * sl))); v.hp = Math.max(0, v.hp - cr); logs.unshift("😴 CRUSHING WEIGHT: it sits on your chest for " + cr + " damage. Break free!"); } else if (Math.random() < 0.30 * (1 - 0.2 * sl)) { v.grabbed = true; v.grabName = "The Zmora"; logs.unshift("😴 The Zmora folds onto your chest and PINS YOU. You cannot move or flee until you break free."); } } let rangeMul = 1; if (BT) { if (TS.isMeleeEnemy(e)) { if (rng >= 2) { advanced = true; logs.unshift(cleanName + " charges across the open ground!"); continue; } if (rng === 1) { advanced = true; rangeMul = 0.5; logs.unshift(cleanName + " lunges in from mid range!"); } } else if (rng >= 2) { rangeMul = 0.75; } } if (e.isLicho) { e.eye = (e.eye || 0) + 1; if (e.eye < 3) { let ld = Math.max(2, Math.round((e.maxDmg || 15) * 0.35)); let lr = TS.tmIncoming(ld, e); logs.unshift("👁 Licho's one eye fixes on you. A buckle snaps, a strap slips, a boot finds a loose stone. Bad luck is gathering (" + e.eye + "/3)."); if (!lr.dodged) { v.hp = Math.max(0, v.hp - lr.dmg); logs.unshift("🍂 Small misfortunes cost you " + lr.dmg + " HP."); } continue; } e.eye = 0; let lb = Math.round((e.maxDmg || 15) * 1.9); if (v.playerCover) { lb = Math.max(1, Math.round(lb * 0.5)); } let lbr = TS.tmIncoming(lb, e); if (lbr.dodged) { logs.unshift("MISFORTUNE falls on you, and for once luck holds: the beam that should have crushed you lands a step to the side!"); } else { v.hp = Math.max(0, v.hp - lbr.dmg); logs.unshift("☠ MISFORTUNE: everything goes wrong at once and it hits you for " + lbr.dmg + " damage!" + (v.playerCover ? " (Cover softened the fall)" : "")); } continue; } if (e.isPlanetnik) { if (!e.charging) { e.charging = true; let zp = Math.max(2, Math.round((e.maxDmg || 20) * 0.25)); let zr = TS.tmIncoming(zp, e); logs.unshift("⚡ Planetnik raises his staff. The clouds above you TURN. TAKE COVER!"); if (!zr.dodged) { v.hp = Math.max(0, v.hp - zr.dmg); logs.unshift("Static crawls over your gear for " + zr.dmg + " damage."); } continue; } e.charging = false; let pd = Math.round((e.maxDmg || 20) * 1.8); if (v.playerCover) { pd = Math.max(1, Math.round(pd * 0.4)); } let pr2 = TS.tmIncoming(pd, e); if (pr2.dodged) { logs.unshift("The bolt hammers the ground where you were standing!"); } else { v.hp = Math.max(0, v.hp - pr2.dmg); logs.unshift("⚡ LIGHTNING STRIKES you for " + pr2.dmg + " damage!" + (v.playerCover ? " (Cover blunted the blow)" : "")); } continue; } if (e.isStrzyga) { if (!e.ambushed) { e.ambushed = true; let sa = Math.round((e.maxDmg || 20) * 1.6); if (v.playerCover) { sa = Math.max(1, Math.round(sa * 0.5)); } let sar = TS.tmIncoming(sa, e); if (sar.dodged) { logs.unshift("🩸 A shape lunges out of the fog, and you throw yourself aside just in time!"); } else { v.hp = Math.max(0, v.hp - sar.dmg); logs.unshift("🩸 AMBUSH: the Strzyga lunges out of the fog and rakes you for " + sar.dmg + " damage!"); } continue; } if (!e.secondSoul && e.currentHp <= e.maxHp * 0.5) { e.secondSoul = true; e.currentHp = Math.min(e.maxHp, e.currentHp + Math.round(e.maxHp * 0.2)); e.minDmg = Math.round(e.minDmg * 1.2); e.maxDmg = Math.round(e.maxDmg * 1.2); logs.unshift("💀 THE SECOND SOUL WAKES: the Strzyga shudders, her wounds close, and her second heart begins to beat. She hits harder now."); continue; } } if (e.noonStalker) { if (!e.charging) { e.charging = true; logs.unshift("🌞 Lady Midday raises the thermal sickle. The air around the blade SHIMMERS. TAKE COVER!"); continue; } e.charging = false; let sd = Math.round((e.maxDmg || 20) * 1.8); if (v.playerCover) { sd = Math.max(1, Math.round(sd * 0.4)); } let sr = TS.tmIncoming(sd, e); if (sr.dodged) { logs.unshift("Lady Midday's sickle whistles past as you throw yourself aside!"); } else { v.hp = Math.max(0, v.hp - sr.dmg); logs.unshift("🔪 THERMAL SICKLE slices straight through your armor for " + sr.dmg + " damage!" + (v.playerCover ? " (Cover blunted the blow)" : "")); } continue; } if (e.unknownThreat) { setup.threatAct(e, logs); continue; } if (e.armHurt > 0) { e.armHurt -= 1; if (Math.random() < 0.5) { logs.unshift(cleanName + "'s crippled arm fumbles the attack and it MISSES!"); continue; } } let target = 'player'; let tIdx = setup.squadPickTarget(BT); if (tIdx !== null && tIdx !== undefined) { target = 'companion'; } let baseDmg = Math.floor(Math.random() * (e.maxDmg - e.minDmg + 1)) + e.minDmg; if (rangeMul !== 1) { baseDmg = Math.max(1, Math.round(baseDmg * rangeMul)); } if (legMul !== 1) { baseDmg = Math.max(1, Math.round(baseDmg * legMul)); } if (e.opticsScrambled) { e.opticsScrambled = false; if (Math.random() < (setup.isBossFoe(e) ? 0.35 : 0.75)) { logs.unshift(cleanName + "'s optics are scrambled and their attack completely MISSED!"); continue; } } if (target === 'companion') { let cm = v.squad[tIdx]; let compName = cm.disp || cm.name; /* the Defector only becomes a wall while his own Defensive Barrage is up (a Vanguard taunt does not count); otherwise he takes hits like everyone else */ let compDmg = (cm.name === "Syndicate Defector" && cm.barrage) ? Math.floor(Math.random() * 2) + 1 : baseDmg; cm.hp = Math.max(0, cm.hp - compDmg); let tauntMsg = cm.isTaunting ? " (Targeted " + compName + "'s Barricade!)" : ""; logs.unshift(cleanName + " attacks " + compName + " for " + compDmg + " damage!" + tauntMsg); if (cm.hp <= 0) { logs.unshift("☠ " + compName + " goes down!"); } } else { let finalDmg = Math.max(1, baseDmg - v.playerArmor); let tmr = { dmg: 0, dodged: false, reflect: 0 }; if (v.playerCover) { finalDmg = Math.max(1, Math.floor(finalDmg / 2)); } if (hasSafePerk) { finalDmg = Math.max(0, Math.floor(finalDmg * 0.5)); logs.unshift("SAFE PERK: Survival instincts minimized incoming trauma!"); } tmr = TS.tmIncoming(finalDmg, e); if (tmr.dodged) { logs.unshift(cleanName + " attacks, but you DODGE it!"); continue; } finalDmg = tmr.dmg; v.hp = Math.max(0, v.hp - finalDmg); let coverMsg = v.playerCover ? " (Cover reduced damage!)" : ""; logs.unshift(cleanName + " hits you for " + finalDmg + " damage!" + coverMsg); if (tmr.reflect > 0 && e.currentHp > 0) { e.currentHp = Math.max(0, e.currentHp - tmr.reflect); logs.unshift("\uD83D\uDEE1 RETRIBUTION: " + cleanName + " takes " + tmr.reflect + " damage for hitting you."); } } } } if (BT && advanced) { v.combatRange = Math.max(0, rng - 1); } if (v.grabbed && !v.enemyGroup.some(function (x) { return (x.isUtopek || x.isZmora || x.isTopielica) && x.currentHp > 0; })) { v.grabbed = false; } setup.squadClearTaunts(); if (v.companion && v.companion.active) { v.companion.isTaunting = false; v.companion.barrage = false; } v.playerCover = false; v.combatLog = logs.concat(v.combatLog); <</script>> <<set $combatTurn to "player">> <div style="margin-top: 15px;"> <<link '<span class="my-cyber-btn">PROCEED TO NEXT ROUND</span>' "Combat">><</link>> </div> </div> <<endif>> </div> </div> </div> </div> <<else>> <<if $hp lte 0>> <<if def $combatContext and $combatContext eq "base_raid">> <div style="background: rgba(239, 68, 68, 0.2); border: 1px solid #ef4444; text-align: center; padding: 15px;"> <div style="color:#fca5a5; font-weight:bold; margin-bottom:10px; letter-spacing:1px;">THE RAIDERS OVERRAN YOUR BASE. You black out, and wake up in the ruins of your camp. Most of what you owned is gone.</div> @@color:#ef4444;<<link "FATAL INJURY - AWAKEN IN CAMP" "Camp">><<unset $enemyGroup>><<if def $combatContext and $combatContext eq "base_raid">><<run setup.raidResolve("loss")>><<unset $combatContext, $targetPOIName>><</if>><<if def $combatContext and $combatContext eq "relic_boss">><<unset $combatContext, $phaseQueue, $targetPOIName>><</if>><<set $slaughterGas to false>><<unset $waveQueue>><<unset $encPending>><<if def $combatContext and $combatContext eq "slaughterhouse_assault">><<unset $combatContext>><</if>><<set $combatLog to []>><<set $hp to 20>><<set $playerCover to false>><</link>>@@ </div> <<else>> <div style="background: rgba(127, 29, 29, 0.35); border: 2px solid #ef4444; text-align: center; padding: 20px;"> <div style="color:#fecaca; font-size:1.3rem; font-weight:bold; letter-spacing:3px; margin-bottom:8px;">☠️ YOU DIED</div> <div style="color:#fca5a5; margin-bottom:14px;">No second chances out here. Your story ends here.</div> @@color:#ef4444;<<link "ACCEPT YOUR FATE" "Game Over">><<set $deathCause to ("killed in combat by " + (($enemy and $enemy.name) ? $enemy.name.split(" (")[0] : "an unknown enemy"))>><<unset $enemyGroup, $enemy, $combatContext, $phaseQueue, $waveQueue, $encPending, $targetPOIName>><<set $slaughterGas to false>><<set $hp to 0>><</link>>@@ </div> <</if>> <<else>> <!-- NORMAL VICTORY SCREEN --> <div class="victory-box"> <<set _bv to setup.bossVictoryHtml()>><<if _bv>><<print _bv>><<else>> <div style="text-align: center; border-bottom: 1px dashed #2ecc71; padding-bottom: 15px; margin-bottom: 25px;"> <h2 style="color: #2ecc71; margin: 0; letter-spacing: 3px;">[ THREAT ELIMINATED ]</h2> <div style="color: #a7f3d0; font-size: 0.9rem; margin-top: 5px;">AREA SECURED</div> </div> <</if>> <<if def $encPending and def $targetPOIName and $targetPOIName eq "Roadside Encounter">> <<run setup.encFinish()>> <div style="border: 2px solid #f59e0b; background: rgba(245,158,11,0.1); padding: 15px; margin-bottom: 20px; border-radius: 6px; text-align: center;"> <div style="color: #fbbf24; font-weight: bold; letter-spacing: 2px; margin-bottom: 8px;">[ ROADSIDE ENCOUNTER ]</div> <div style="color: #e2e8f0; line-height: 1.6;"><<print $encResult.text>></div> <<for _el to 0; _el lt $encResult.lines.length; _el++>><div style="color: #a7f3d0; font-size: 0.85rem; margin-top: 4px;"><<print $encResult.lines[_el]>></div><</for>> </div> <</if>> <<if ($convoyRollTurn || -1) neq State.turns>> <<set $convoyRollTurn to State.turns>> <<set $convoyAvailable to (random(1, 100) lte (setup.tipOn("convoy") ? 95 : 50))>> <<if $convoyAvailable>> <<set _spots to [[20.8,41.1],[22.9,66.1],[47.9,17.9],[29.2,82.1],[66.7,46.4],[75.0,69.6],[87.5,23.2],[62.5,87.5]]>> <<set $convoyPos to _spots.random()>> <</if>> <</if>> <<if $convoyFightActive and $targetPOIName eq "Reptilian Convoy">> <<set _human to $enemyGroup.filter(function (en) { return en.isCaptive; })[0]>> <<set _saved to ((_human and _human.currentHp gt 0) ? 1 : 0)>> <<if _saved gt 0>> <<set $survivors to ($survivors || 0) + 1>><<run setup.cLife.add(5, "You saved a captive from the convoy")>> <<set $total_rescued to ($total_rescued || 0) + 1>> <</if>> <<set $convoyResult to { saved: _saved, turn: State.turns }>> <<set $convoyFightActive to false>> <<run setup.relicOnConvoy()>> <</if>> <<if def $convoyResult and $convoyResult.turn eq State.turns>> <div style="border: 2px solid #f97316; background: rgba(249,115,22,0.1); padding: 15px; margin-bottom: 20px; border-radius: 6px; text-align: center;"> <div style="color: #fdba74; font-weight: bold; letter-spacing: 2px; margin-bottom: 8px;">[ ENSLAVED HUMAN ]</div> <<if $convoyResult.saved gt 0>> <div style="color: #a7f3d0;">With the guards dead, the neural collar goes dark and the prisoner collapses, free. They follow you back to camp and join your garrison as a survivor.</div> <<else>> <div style="color: #fca5a5;">The prisoner did not survive the fight. There is no one left to free.</div> <</if>> <div style="color: #94a3b8; font-size: 0.8rem; margin-top: 6px;">TOTAL RESCUED: <<print $total_rescued>></div> <<if $mayaSite and not ($npcFlags and $npcFlags.maya)>> <div style="margin-top: 12px; color: #fbbf24; font-size: 0.85rem; line-height: 1.5; border: 1px dashed #f59e0b; padding: 8px;">[ LOOT DROP: Broken Slate of Valthor-Szhak (First Relic) ]<br>You try to read the dense, sibilant Iokharic glyphs and understand none of it. Standard decryption will not work. You will need to search the Silesian Library ruins for linguistic archives, or for someone who can translate this. A new marker appears on your Sector Map.</div> <</if>> <<if $total_rescued gte 10 and ($quest_slaughterhouse || "not_started") eq "not_started">> <div style="color: #fdba74; font-size: 0.9rem; margin-top: 10px; font-weight: bold;">One of the survivors is waiting to speak with you at camp.</div> <</if>> </div> <</if>> <<if ndef $enemy>> <<set $enemy to {name: "Unknown Threat", drop: "scrap"}>> <<elseif ndef $enemy.drop>> <<set $enemy.drop to "scrap">> <<endif>> <!-- BOSS & REWARDS CONTROL --> <<if $enemy.name eq "Bebok">> <<set _isBossDrop to true>> <<set _eDrop to 0>><<set _scripDrop to 0>><<set _xpDrop to 0>><<set _repDrop to 0>> <<if ndef $bebokSlain>> <<set $bebokSlain to true>> <<set _scripDrop to 1500>><<set _xpDrop to 1000>> <<set $materials.scrip = ($materials.scrip || 0) + _scripDrop>> <<set $xp = ($xp || 0) + _xpDrop>> <<if ndef $gearInventory>><<set $gearInventory to []>><</if>> <<run $gearInventory.push("Godlike Bebok's Scrap Pack")>> <</if>> <<elseif def $combatContext and $combatContext eq "dungeon" and setup.dg.inBossRoom()>> <<set _isBossDrop to true>> <<set _eDrop to 0>> <<set _scripDrop to setup.dg.bossScrip()>> <<set _xpDrop to setup.dg.bossXp()>> <<set _repDrop to 10>> <<set $materials.scrip to ($materials.scrip || 0) + _scripDrop>> <<set $xp to ($xp || 0) + _xpDrop>> <<if ndef $baseRep>><<set $baseRep to 0>><</if>> <<set $baseRep += _repDrop>> <<elseif $enemy.name eq "Reptilian Warlord">> <<set _isBossDrop to true>> <<set _eDrop to 0>> <<set _scripDrop to 1000>> <<set _xpDrop to 500>> <<set _repDrop to 25>> <<if ndef $materials.scrip>><<set $materials.scrip to 0>><</if>> <<set $materials.scrip += _scripDrop>> <<if ndef $xp>><<set $xp to 0>><</if>> <<set $xp += _xpDrop>> <<if ndef $baseRep>><<set $baseRep to 0>><</if>> <<set $baseRep += _repDrop>> <<set $globalBroadcastPass = ($globalBroadcastPass || 0) + 2>> <<else>> <<set _isBossDrop to false>> <<set _pk to (($enemyGroup and $enemyGroup.length gt 0 and $enemyGroup[0].pack) ? $enemyGroup[0].pack : 1)>> <<set _eDrop to random(1, 3) * _pk>> <<if ndef $materials[$enemy.drop]>><<set $materials[$enemy.drop] to 0>><</if>> <<set $materials[$enemy.drop] += _eDrop>> <<set _scripDrop to random(5, 15) * _pk>> <<if ndef $materials.scrip>><<set $materials.scrip to 0>><</if>> <<set $materials.scrip += _scripDrop>> <<if ndef $xp>><<set $xp to 0>><</if>> <<set _xpDrop to (random(25, 45) + ($day * 2)) * _pk>> <<set $xp += _xpDrop>> <<if ndef $baseRep>><<set $baseRep to 0>><</if>> <<set _repDrop to random(2, 5)>> <<set $baseRep += _repDrop>> <<if ndef $prisoners>><<set $prisoners to 0>><</if>> /* RARE EVENT: 0.5% chance a downed enemy is bound with a spare rope for free */ <<if ndef $prisoners>><<set $prisoners to 0>><</if>> <<if ($materials.rope || 0) gt 0 and random(1, 1000) lte 5 and setup.canBindAny(($enemyGroup && $enemyGroup.length) ? $enemyGroup : $enemy)>> <<set $materials.rope -= 1>> <<run setup.addPrisoner(($enemyGroup && $enemyGroup.length) ? $enemyGroup : $enemy)>> <<set _capturedSuccessfully to true>> <<else>> <<set _capturedSuccessfully to false>> <<endif>> <<endif>> <<if setup.squadHas("Cerberus")>> <<set _bonusScrap to random(5, 10)>> <<set $materials.scrap += _bonusScrap>> <<else>> <<set _bonusScrap to 0>> <<endif>> <div style="color: #ddd; font-size: 1rem; margin-bottom: 15px; line-height: 1.6;"> You successfully neutralized all hostiles in the sector.<br><br> <<if _isBossDrop>> <strong style="color:#ffaa00;">> BOSS BOUNTY SALVAGE:</strong> <strong style="color:#fbbf24;"><<print _scripDrop>> Scrip</strong>, gained <strong style="color:#34d399;"><<print _xpDrop>> XP</strong>, <strong style="color:#60a5fa;">+<<print _repDrop>> Street Cred</strong> and <strong style="color:#a855f7;">2x Global Broadcast Passes</strong>!<br> <<else>> <strong style="color:#ffaa00;">> SALVAGE:</strong> Extracted <<print _eDrop>> <<print $enemy.drop>>, <strong style="color:#fbbf24;"><<print _scripDrop>> Scrip</strong>, gained <strong style="color:#34d399;"><<print _xpDrop>> XP</strong> and <strong style="color:#60a5fa;">+<<print _repDrop>> Street Cred</strong>.<br> <<if _capturedSuccessfully>> <div style="margin:12px 0; padding:12px 14px; border:2px solid #fbbf24; border-radius:6px; background:rgba(2,6,12,0.85); box-shadow:0 0 18px rgba(251,191,36,0.35);"><div style="color:#fde047; font-weight:bold; letter-spacing:2px; margin-bottom:6px;">★ RARE EVENT: A SURVIVOR AMONG THE FALLEN ★</div><div style="color:#e2e8f0; line-height:1.6;">As you sweep the bodies, one of the downed hostiles is still breathing, too hurt to fight and too stunned to run. You loop your spare rope around their wrists before they can wake up and haul them back to your camp in chains. Luck like this is rare out here, so make it count. <strong style="color:#ef4444;">(+1 Prisoner, -1 Reinforced Rope)</strong></div></div> <<endif>> <<if _bonusScrap gt 0>> <strong style="color:#fbbf24;">> K-9 SCAVENGE:</strong> Cerberus extracted <strong style="color:#a3e635;"><<print _bonusScrap>> Scrap</strong> from the wreckage!<br> <<endif>> <<endif>> </div> <!-- RANDOM ITEM DROP SYSTEM --> <<if $enemy.name eq "Bebok">> <div style="background: radial-gradient(ellipse at center, rgba(127,29,29,0.55), rgba(0,0,0,0.85)); border: 3px solid #ef4444; padding: 40px 20px; margin: 10px 0 30px 0; border-radius: 8px; text-align: center; box-shadow: 0 0 35px #ef4444, inset 0 0 25px rgba(239,68,68,0.35);"> <div style="font-size: 3.2rem; line-height: 1; margin-bottom: 10px;">🎒</div> <div style="color: #fbbf24; font-size: 1.1rem; letter-spacing: 4px; margin-bottom: 14px; font-weight: bold;">👑 PATIENCE REWARDED! 👑</div> <div style="color: #ef4444; font-size: 2.3rem; font-weight: bold; letter-spacing: 2px; text-shadow: 0 0 18px #ef4444, 0 0 4px #fff; line-height: 1.2;">GODLIKE<br>BEBOK'S SCRAP PACK</div> <div style="color: #fecaca; font-size: 1.3rem; font-weight: bold; margin-top: 18px;">+1000 KG CARRY WEIGHT</div> <div style="color: #aaa; font-size: 0.9rem; margin-top: 10px;">Added to Gear Inventory (backpack slot).</div> </div> <<elseif _isBossDrop>> <<set _categories to ["headgear", "chest", "legs", "boots", "gloves", "rightHand", "offHand", "backpack", "trinket1", "ring1"]>> <<set _cat to _categories[Math.floor(Math.random() * _categories.length)]>> <<set _items to $itemDatabase[_cat]>> <<set _baseItem to _items[Math.floor(Math.random() * _items.length)]>> <<set _chosenRarity to setup.rollRarity("boss")>> <<set _droppedItemName to _chosenRarity + " " + _baseItem>> <<if ndef $gearInventory>><<set $gearInventory to []>><</if>> <<run $gearInventory.push(_droppedItemName)>> <<set _itemColor to $rarities[_chosenRarity].color>> <div style="background: rgba(0,0,0,0.6); border: 2px solid <<print _itemColor>>; padding: 15px; margin-bottom: 25px; border-radius: 4px; text-align: center; box-shadow: 0 0 15px <<print _itemColor>>;"> <div style="color: #fbbf24; font-size: 0.85rem; letter-spacing: 2px; margin-bottom: 5px; font-weight: bold;">👑 BOSS TROPHY UNLOCKED!</div> <div @style="'color: ' + _itemColor + '; font-size: 1.3rem; font-weight: bold; text-shadow: 0 0 10px ' + _itemColor + ';'"> [ <<print _droppedItemName.toUpperCase()>> ] </div> <div style="color: #aaa; font-size: 0.75rem; margin-top: 5px;">High-tier tactical equipment added to Gear Inventory.</div> </div> <<else>> <<set _dropRoll to random(1, 100)>> <<if _dropRoll lte 50>> <<set _droppedItemName to setup.generateLoot()>> <<set _rarity to _droppedItemName.split(" ")[0]>> <<set _itemColor to $rarities[_rarity].color>> <div style="background: rgba(0,0,0,0.4); border: 1px dashed <<print _itemColor>>; padding: 15px; margin-bottom: 25px; border-radius: 4px; text-align: center;"> <div style="color: #fff; font-size: 0.8rem; letter-spacing: 1px; margin-bottom: 5px;">ENEMY DROPPED EQUIPMENT!</div> <div @style="'color: ' + _itemColor + '; font-size: 1.2rem; font-weight: bold; text-shadow: 0 0 10px ' + _itemColor + ';'"> [ <<print _droppedItemName.toUpperCase()>> ] </div> <div style="color: #aaa; font-size: 0.75rem; margin-top: 5px;">Added to Gear Inventory.</div> </div> <<endif>> <<endif>> <!-- POI LOOT --> <<if def $targetPOIName and $targetPOIName neq "Fallen Military Convoy">> <div style="background: rgba(46,204,113,0.05); border: 1px solid #115e59; padding: 15px; margin-bottom: 25px; font-family: monospace; font-size: 0.9rem; color: #ccfbf1; box-sizing: border-box;"> <strong style="color: #34d399; font-family: 'Segoe UI', sans-serif;">> LOCATION EXTRACTION (<<print $targetPOIName>>):</strong><br><br> <<if $targetPOILoot eq "basic">> <<set _s to random(2,6), _w to random(1,4), _c to random(0,2)>> <<set $materials.scrap += _s, $materials.wood += _w, $materials.cloth += _c>> Secured: <<print _s>> Scrap, <<print _w>> Wood, <<print _c>> Cloth. <<elseif $targetPOILoot eq "survival">> <<set _f to random(1,3), _wt to random(1,3), _m to random(0,1)>> <<set $materials.food += _f, $materials.water += _wt, $materials.medicine += _m>> Recovered: <<print _f>> Food, <<print _wt>> Water, <<print _m>> Meds. <<elseif $targetPOILoot eq "medical">> <<set _m to random(1,3), _t to random(0,2)>> <<set $materials.medicine += _m, $materials.techData += _t>> Extracted: <<print _m>> Meds, <<print _t>> Tech Data. <<elseif $targetPOILoot eq "military">> <<set _a to random(18,35), _s to random(20,45), _t to random(1,3), _sc to random(120,250)>> <<set $materials.ammo += _a, $materials.scrap += _s, $materials.techData += _t, $materials.scrip += _sc>> Secured: <<print _a>> Ammo, <<print _s>> Scrap, <<print _t>> Tech Data, <<print _sc>> Scrip. <<elseif $targetPOILoot eq "nature">> <<set _w to random(2,5), _rm to random(1,3)>> <<set $materials.wood += _w, $materials.rawMeat += _rm>> Gathered: <<print _w>> Wood, <<print _rm>> Raw Meat. <<elseif $targetPOILoot eq "gas">> <<set _s to random(2,5), _wt to random(1,4)>> <<set $materials.scrap += _s, $materials.water += _wt>> Scrounged: <<print _s>> Scrap, <<print _wt>> Water. <<endif>> </div> <<endif>> <!-- NARRATIVE OVERRIDE: CIPHER (FIXED) --> <<if def $combatContext and $combatContext eq "cipher_ambush" and $enemy.name.includes("Vanguard")>> <div style="background: rgba(251, 191, 36, 0.10); border-left: 4px solid #fbbf24; padding: 20px; border-radius: 4px; margin-bottom: 25px;"> <div style="color: #fde68a; font-weight: bold; font-size: 1.1rem; margin-bottom: 10px; letter-spacing: 1px;">[ NOTICE: MAIN STORY COMING SOON ]</div> <div style="color: #e2e8f0; font-size: 0.95rem; line-height: 1.6;">Chapter 1 is complete. The next chapter of Cipher's story is still being written and will arrive in a future update. Until then the tunnels are a combat zone.</div> </div> <<unset $combatContext>> <<endif>> <!-- RETURN & WAVE CONTROLLER --> <div style="text-align: center; margin-top: 25px; padding: 15px; border: 1px solid #2ecc71; background: rgba(46, 204, 113, 0.1); border-radius: 4px;"> <<if def $combatContext and $combatContext eq "slaughterhouse_assault">> <<if def $waveQueue and $waveQueue.length gt 0>> <div style="color: #fca5a5; margin-bottom: 14px; line-height: 1.6;">WAVE <<print $combatWave>> / 4 CLEARED. More hostiles are closing in. Catch your breath, you have seconds.</div> @@color:#fecaca;background:rgba(239,68,68,0.16);border:1px solid #ef4444;padding:12px 24px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "CONTINUE THE ASSAULT" "Slaughterhouse_Wave">><</link>>@@ <<else>> <div style="color: #fca5a5; margin-bottom: 14px; line-height: 1.6;">The Abattoir Warden crashes down and the gate defences go silent. The way into the Slaughterhouse is open.</div> @@color:#fecaca;background:rgba(239,68,68,0.16);border:1px solid #ef4444;padding:12px 24px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "ENTER THE SLAUGHTERHOUSE" "Slaughterhouse_Floor1">> <<set $slaughterGas to false>> <<unset $enemy, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $combatWave, $reinforcementPending, $nextCombatEnemyGroup, $waveQueue, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <</if>> <<elseif def $combatContext and $combatContext eq "camp_raid">> <<if def $waveQueue and $waveQueue.length gt 0>> <div style="color: #fca5a5; margin-bottom: 14px; line-height: 1.6;">WAVE <<print $combatWave>> / <<print $campRaid.waves>> CLEARED. The next wave is already moving. Catch your breath, you have seconds.</div> @@color:#fecaca;background:rgba(239,68,68,0.16);border:1px solid #ef4444;padding:12px 24px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "CONTINUE THE ASSAULT" "CampRaidWave">><</link>>@@ <<else>> <div style="color: #a7f3d0; margin-bottom: 14px; line-height: 1.6;">The last defender drops. The camp is yours.</div> @@color:#bbf7d0;background:rgba(21,128,61,0.22);border:1px solid #22c55e;padding:12px 24px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "TAKE THE CAMP" "CampRaidEnd">> <<run setup.campRaidWin()>> <<unset $enemy, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $combatWave, $reinforcementPending, $nextCombatEnemyGroup, $waveQueue, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <</if>> <<elseif def $combatContext and $combatContext eq "relic_boss">> <<if def $phaseQueue and $phaseQueue.length gt 0>> <div style="color: #fca5a5; margin-bottom: 14px; line-height: 1.6;">The Scarred Prophet staggers, but he is not finished. The shrine shakes.</div> @@color:#fecaca;background:rgba(185,28,28,0.22);border:1px solid #ef4444;padding:12px 24px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "CONTINUE THE FIGHT" "RelicBossPhase">><</link>>@@ <<else>> <div style="color: #a7f3d0; margin-bottom: 14px; line-height: 1.6;">Sszakar-Aten collapses into the boiling silt, his golden ocular lenses shattering.</div> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;padding:12px 24px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "RECOVER THE LOWER HALF" "RelicBossEnd">> <<unset $enemy, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $combatWave, $reinforcementPending, $nextCombatEnemyGroup>> <</link>>@@ <</if>> <<elseif def $combatContext and $combatContext eq "base_raid">> <div style="color: #a7f3d0; margin-bottom: 14px; line-height: 1.6;">The last raider drops. Silence settles over the walls. Time to count the cost.</div> @@color:#fecaca;background:rgba(185,28,28,0.22);border:1px solid #ef4444;padding:12px 24px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "SURVEY THE DAMAGE" "RaidAftermath">> <<run setup.raidResolve("win")>> <<unset $enemy, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $combatWave, $reinforcementPending, $nextCombatEnemyGroup, $combatContext>> <</link>>@@ <<elseif def $combatContext and $combatContext eq "camp_tour">> <div style="color: #a7f3d0; margin-bottom: 14px; line-height: 1.6;">Both scavengers are down. Every fight works like this one, only the real enemies hit harder. Bound prisoners go to Grom at the Labor Pits, so let us head back to the sanctuary and deliver yours.</div> @@color:#bbf7d0;background:rgba(21,128,61,0.22);border:1px solid #22c55e;padding:12px 24px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "RETURN TO THE SANCTUARY" "Camp">> <<run setup.tour.afterFight()>> <<unset $enemy, $enemyGroup, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $combatWave, $reinforcementPending, $nextCombatEnemyGroup, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <<elseif def $combatContext and $combatContext eq "ch3_omicron">> <div style="color: #a7f3d0; margin-bottom: 14px; line-height: 1.6;">The fight is over. The substation hums on.</div> @@color:#bbf7d0;background:rgba(21,128,61,0.22);border:1px solid #22c55e;padding:12px 24px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "CONTINUE" "Ch3Scene">> <<run setup.ch3.afterFight()>> <<unset $enemy, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $combatWave, $reinforcementPending, $nextCombatEnemyGroup, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <<elseif def $combatContext and $combatContext eq "cipher_ch1">> <div style="color: #a7f3d0; margin-bottom: 14px; line-height: 1.6;">The tunnel falls quiet again, apart from the dripping water.</div> @@color:#bbf7d0;background:rgba(21,128,61,0.22);border:1px solid #22c55e;padding:12px 24px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "CONTINUE" "KtScene">> <<run setup.kt.afterFight()>> <<unset $enemy, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $combatWave, $reinforcementPending, $nextCombatEnemyGroup, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <<elseif def $combatContext and $combatContext eq "dungeon">> <div style="color: #fde68a; margin-bottom: 14px; line-height: 1.6;"><<if setup.dg.inBossRoom()>>Foreman Kruk is down. The lower shaft falls silent.<<else>>The tunnel is quiet again. Catch your breath and decide where to go next.<</if>></div> @@color:#fde68a;background:rgba(161,98,7,0.22);border:1px solid #f59e0b;padding:12px 24px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "BACK TO THE TUNNELS" "DungeonRoom">> <<run setup.dg.win()>> <<unset $enemy, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $combatWave, $reinforcementPending, $nextCombatEnemyGroup, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <<elseif (def $targetPOIName and $targetPOIName eq "Tychy Logging Camp") or (def $enemy and $enemy.name eq "Feral Timber-Wolf")>> @@color:#fcd34d;font-weight:bold;letter-spacing:2px;<<link "SECURE AREA & GATHER WOOD" "TychyGathering">> <<unset $enemy, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $combatWave, $reinforcementPending, $nextCombatEnemyGroup, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <<elseif def $targetPOIName and $targetPOIName eq "Ruined Military Checkpoint">> <<if ndef $checkpointWave or $checkpointWave eq 1>> @@color:#ef4444;font-weight:bold;letter-spacing:2px;<<link "WARNING: AUTOMATED DEFENSES ACTIVATED! (WAVE 2)" "CheckpointReinforcement">> <<set $checkpointWave to 2>> <<unset $enemy, $combatLog, $returnPassage, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <<elseif $checkpointWave eq 2>> @@color:#dc2626;font-weight:bold;letter-spacing:2px;<<link "WARNING: SCAVENGERS SWARMING THE PERIMETER! (WAVE 3)" "Combat">> <<set $checkpointWave to 3>> <<set $enemyGroup to [ { name: "Wasteland Scavenger 1", hp: 40, currentHp: 40, maxHp: 40, minDmg: 8, maxDmg: 14, drop: "scrap" }, { name: "Wasteland Scavenger 2", hp: 40, currentHp: 40, maxHp: 40, minDmg: 8, maxDmg: 14, drop: "scrap" }, { name: "Wasteland Scavenger 3", hp: 40, currentHp: 40, maxHp: 40, minDmg: 8, maxDmg: 14, drop: "scrap" } ]>> <<set $enemy to $enemyGroup[0]>> <<set $combatWave to 3>> <<unset $combatLog, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <<else>> @@color:#2ecc71;font-weight:bold;letter-spacing:2px;<<link "CHECKPOINT SECURED - ENTER THE COMPLEX" "RuinedMilitaryCheckpointInterior">> <<unset $enemy, $combatLog, $returnPassage, $checkpointWave, $combatWave, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <<endif>> <<elseif def $targetPOIName and $targetPOIName eq "Fallen Military Convoy">> <div style="color:#fde047; margin-bottom:14px; line-height:1.6;">The last scavenger drops. The wreckage is quiet and the sealed transport lockers are yours for the taking.</div> @@color:#eab308;font-weight:bold;letter-spacing:2px;<<link "LOOT THE CONVOY">> <<set _a to random(18,35), _s to random(20,45), _t to random(1,3), _sc to random(120,250)>> <<set $materials.ammo += _a, $materials.scrap += _s, $materials.techData += _t, $materials.scrip += _sc>> <<set $encResult to { text: "You crack the military seals on the surviving lockers and haul out everything worth carrying before more scavengers arrive.", lines: ["+" + _a + " Ammo", "+" + _s + " Scrap", "+" + _t + " Tech Data", "+" + _sc + " Scrip"] }>> <<set $encReturn to "Camp">> <<unset $enemy, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $combatWave, $reinforcementPending, $nextCombatEnemyGroup, $combatContext>> <<run window.setup.updateSidebar()>> <<goto "EncounterResult">> <</link>>@@ <<else>> <<set $checkpointWave to 0>> @@color:#2ecc71;font-weight:bold;letter-spacing:2px;<<link "PROCEED TO S.A.N.C.T.U.A.R.Y. BASE" "Camp">> <<unset $enemy, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $checkpointWave, $combatWave, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <<endif>> </div> </div> <<endif>> <<endif>> <<endif>> </div> <</nobr>>
<<nobr>> <<audio ":all" stop>> <div style="background: linear-gradient(rgba(30, 0, 0, 0.92), rgba(10, 0, 0, 0.98)); border: 3px solid #ef4444; padding: 24px; font-family: 'Courier New', monospace; color: #fecaca; max-width: 100%; box-sizing: border-box; box-shadow: 0 0 30px rgba(239,68,68,0.6);"> <div style="text-align: center; border-bottom: 2px dashed #ef4444; padding-bottom: 12px; margin-bottom: 16px;"> <div style="font-size: 0.8rem; letter-spacing: 3px; color: #fca5a5;">[ SUBSTATION OMICRON: NODE 09 ]</div> <h2 style="color: #ef4444; letter-spacing: 4px; margin: 6px 0 0 0; text-shadow: 0 0 14px #ef4444;">☢ CORE BREACH ☢</h2> </div> <div style="text-align:center; margin-bottom:16px;"><img src="images/ch3/ch3_kraazh.jpg" onerror="this.parentNode.style.display='none';" style="width:100%; max-width:760px; max-height:340px; object-fit:cover; border:2px solid #ef4444; border-radius:6px;"></div> <<if $ch3.blastStep lt 1>> <div style="line-height: 1.7; margin-bottom: 18px;">Your hit lands square on the chest mount. There is a thin, glassy crack, and the azure glow inside the harness turns white. The three release pins snap loose. Everything in the chamber stops: the sledge, the riveter, the dynamo's roar. Kraazh looks down at his own chest, and then at you.</div> <div style="line-height: 1.7; margin-bottom: 18px; color:#fff; font-weight:bold; border-left: 4px solid #ef4444; padding-left: 12px;"><span style="color:#ef4444;">SUB-WARDEN KRAAZH:</span> "Ignorant worm-skin, you killed all of us!"</div> <div style="text-align: center; margin-top: 20px;"> @@color:#fecaca;background:rgba(127,29,29,0.5);border:1px solid #ef4444;padding:12px 28px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "CONTINUE">><<set $ch3.blastStep to 1>><<goto "Ch3CoreBlast">><</link>>@@ </div> <<else>> <style> .cb-wrap { position: relative; overflow: hidden; min-height: 420px; background: #000; text-align: center; padding: 26px 12px; animation: cbShake 1.6s ease-out 0.3s both; } .cb-flash { position: absolute; inset: 0; background: #fff; opacity: 0; pointer-events: none; animation: cbFlash 4.2s ease-out 0.3s both; } .cb-ember { position: absolute; inset: 0; background: radial-gradient(circle at 50% 45%, rgba(255,160,40,0.55), rgba(180,20,0,0.35) 40%, rgba(0,0,0,0) 75%); opacity: 0; pointer-events: none; animation: cbEmber 6s ease-out 0.6s both; } .cb-ring { position: absolute; left: 50%; top: 45%; width: 40px; height: 40px; margin: -20px 0 0 -20px; border: 4px solid #fde68a; border-radius: 50%; opacity: 0; pointer-events: none; animation: cbRing 2.4s ease-out 0.3s both; } .cb-line { position: relative; opacity: 0; font-family: 'Courier New', monospace; letter-spacing: 3px; color: #fecaca; margin: 10px 0; animation: cbIn 1.2s ease-out both; } .cb-big { font-size: 2.1rem; font-weight: bold; color: #fff; text-shadow: 0 0 22px #ef4444; } .cb-end { position: relative; visibility: hidden; margin-top: 26px; animation: cbShow 0s linear 9.5s forwards; } @keyframes cbShake { 0% { transform: translate(0,0); } 8% { transform: translate(-14px,10px); } 16% { transform: translate(12px,-12px); } 24% { transform: translate(-10px,-8px); } 32% { transform: translate(9px,9px); } 45% { transform: translate(-6px,4px); } 60% { transform: translate(4px,-3px); } 100% { transform: translate(0,0); } } @keyframes cbFlash { 0% { opacity: 0; } 4% { opacity: 1; } 35% { opacity: 1; } 100% { opacity: 0; } } @keyframes cbEmber { 0% { opacity: 0; } 20% { opacity: 1; } 100% { opacity: 0.55; } } @keyframes cbRing { 0% { opacity: 1; transform: scale(1); } 100% { opacity: 0; transform: scale(34); } } @keyframes cbIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } @keyframes cbShow { to { visibility: visible; } } @media (prefers-reduced-motion: reduce) { .cb-wrap { animation: none; } .cb-flash { animation-duration: 6s; } .cb-ring { display: none; } } </style> <div class="cb-wrap"> <div class="cb-ember"></div> <div class="cb-ring"></div> <div class="cb-flash"></div> <div class="cb-line cb-big" style="animation-delay: 2.2s;">THE CORE DETONATES</div> <div class="cb-line" style="animation-delay: 3.6s;">The containment rings tear free of their mounts. Cipher screams in your ear-piece, and then there is only white.</div> <div class="cb-line" style="animation-delay: 5.2s;">The Chamber of the Dynamo. The flume. Every tunnel of the vault.</div> <div class="cb-line" style="animation-delay: 6.6s;">The ceiling comes down on everyone inside: you, your squad, the Vanguard.</div> <div class="cb-line cb-big" style="animation-delay: 8.2s; color:#ef4444;">NOBODY WALKS OUT</div> <div class="cb-end"> @@color:#fecaca;background:rgba(127,29,29,0.5);border:1px solid #ef4444;padding:12px 28px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "THE VAULT COLLAPSES" "Game Over">><<set $deathCause to "killed with the whole Generation Vault when the Vanguard Power Core exploded after being hit in the chest">><<unset $enemyGroup, $enemy, $combatContext, $phaseQueue, $waveQueue, $encPending, $targetPOIName>><<set $hp to 0>><</link>>@@ </div> </div> <</if>> </div> <</nobr>>
<<nobr>> <style> #story { margin-top: 0 !important; margin-right: 0 !important; margin-bottom: 0 !important; margin-left: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; } .av-world { position: relative; margin-left: 17.5em; margin-right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 20px; } .victory-box { background: linear-gradient(135deg, #050a05 0%, #064e3b 100%); padding: 25px; border: 2px solid #2ecc71; border-radius: 8px; font-family: 'Segoe UI', sans-serif; box-shadow: 0 0 30px rgba(46, 204, 113, 0.2); width: 100%; box-sizing: border-box; } .warning-box { background: linear-gradient(135deg, #1a0505 0%, #450a0a 100%); padding: 25px; border: 2px solid #ef4444; border-radius: 8px; font-family: 'Courier New', monospace; box-shadow: 0 0 40px rgba(239, 68, 68, 0.5); width: 100%; box-sizing: border-box; animation: pulseRed 2s infinite; } @keyframes pulseRed { 0% { box-shadow: 0 0 20px rgba(239, 68, 68, 0.4); } 50% { box-shadow: 0 0 40px rgba(239, 68, 68, 0.8); } 100% { box-shadow: 0 0 20px rgba(239, 68, 68, 0.4); } } </style> <div class="av-world"> <!-- WAVE CONTROLLER --> <<if def $reinforcementPending and $reinforcementPending eq true>> <div class="warning-box"> <div style="text-align: center; border-bottom: 2px dashed #ef4444; padding-bottom: 15px; margin-bottom: 25px;"> <h2 style="color: #ef4444; margin: 0; letter-spacing: 5px;">[ ⚠️ WARNING: REINFORCEMENTS DETECTED! ]</h2> <div style="color: #fca5a5; font-size: 1rem; margin-top: 10px; font-weight: bold;">Gunfire has drawn enemy backup to your location!</div> </div> <div style="color: #f87171; font-size: 1rem; text-align: center; margin-bottom: 30px; line-height: 1.6;"> You managed to neutralize the vanguard, but the heavy noise attracted local scavengers.<br> Hostile signatures are converging on your position. There is no time to loot or run.<br><br> <strong style="color: #fff;">BRACE YOURSELF FOR WAVE <<print ($combatWave + 1)>>!</strong> </div> <div style="text-align: center;"> @@color:#fff;background:#ef4444;padding:15px 30px;font-size:1.2rem;font-weight:bold;letter-spacing:2px;border:2px solid #b91c1c;border-radius:4px;cursor:pointer;<<link "ENGAGE REINFORCEMENT SQUAD">> <<set $combatWave += 1>> <<set $reinforcementPending to false>> <<set $enemyGroup to $nextCombatEnemyGroup>> <<set $enemy to $enemyGroup[0]>> <<unset $nextCombatEnemyGroup, $combatLog>> <<goto "Combat">> <</link>>@@ </div> </div> <<else>> <!-- NORMAL VICTORY SCREEN --> <div class="victory-box"> <<set _bv to setup.bossVictoryHtml()>><<if _bv>><<print _bv>><<else>> <div style="text-align: center; border-bottom: 1px dashed #2ecc71; padding-bottom: 15px; margin-bottom: 25px;"> <h2 style="color: #2ecc71; margin: 0; letter-spacing: 3px;">[ THREAT ELIMINATED ]</h2> <div style="color: #a7f3d0; font-size: 0.9rem; margin-top: 5px;">AREA SECURED</div> </div> <</if>> <<if ndef $enemy>> <<set $enemy to {name: "Unknown Threat", drop: "scrap"}>> <<elseif ndef $enemy.drop>> <<set $enemy.drop to "scrap">> <<endif>> <!-- BOSS & REWARDS CONTROL --> <<if $enemy.name eq "Bebok">> <<set _isBossDrop to true>> <<set _eDrop to 0>><<set _scripDrop to 0>><<set _xpDrop to 0>><<set _repDrop to 0>> <<if ndef $bebokSlain>> <<set $bebokSlain to true>> <<set _scripDrop to 1500>><<set _xpDrop to 1000>> <<set $materials.scrip = ($materials.scrip || 0) + _scripDrop>> <<set $xp = ($xp || 0) + _xpDrop>> <<if ndef $gearInventory>><<set $gearInventory to []>><</if>> <<run $gearInventory.push("Godlike Bebok's Scrap Pack")>> <</if>> <<elseif def $combatContext and $combatContext eq "dungeon" and setup.dg.inBossRoom()>> <<set _isBossDrop to true>> <<set _eDrop to 0>> <<set _scripDrop to setup.dg.bossScrip()>> <<set _xpDrop to setup.dg.bossXp()>> <<set _repDrop to 10>> <<set $materials.scrip to ($materials.scrip || 0) + _scripDrop>> <<set $xp to ($xp || 0) + _xpDrop>> <<if ndef $baseRep>><<set $baseRep to 0>><</if>> <<set $baseRep += _repDrop>> <<elseif $enemy.name eq "Reptilian Warlord">> <<set _isBossDrop to true>> <<set _eDrop to 0>> <<set _scripDrop to 1000>> <<set _xpDrop to 500>> <<set _repDrop to 25>> <<if ndef $materials.scrip>><<set $materials.scrip to 0>><</if>> <<set $materials.scrip += _scripDrop>> <<if ndef $xp>><<set $xp to 0>><</if>> <<set $xp += _xpDrop>> <<if ndef $baseRep>><<set $baseRep to 0>><</if>> <<set $baseRep += _repDrop>> <<set $globalBroadcastPass = ($globalBroadcastPass || 0) + 2>> <<else>> <<set _isBossDrop to false>> <<set _eDrop to random(1, 3)>> <<if ndef $materials[$enemy.drop]>><<set $materials[$enemy.drop] to 0>><</if>> <<set $materials[$enemy.drop] += _eDrop>> <<set _scripDrop to random(5, 15)>> <<if ndef $materials.scrip>><<set $materials.scrip to 0>><</if>> <<set $materials.scrip += _scripDrop>> <<if ndef $xp>><<set $xp to 0>><</if>> <<set _xpDrop to random(25, 45) + ($day * 2)>> <<set $xp += _xpDrop>> <<if ndef $baseRep>><<set $baseRep to 0>><</if>> <<set _repDrop to random(2, 5)>> <<set $baseRep += _repDrop>> /* NEW: IF YOU HAVE ROPE AT THE END OF A BATTLE, LUCKY PRISONER CAPTURE (25% CHANCE OR AUTOMATIC) */ <<if ndef $prisoners>><<set $prisoners to 0>><</if>> /* RARE EVENT: 0.5% chance a downed enemy is bound with a spare rope for free */ <<if ndef $prisoners>><<set $prisoners to 0>><</if>> <<if ($materials.rope || 0) gt 0 and random(1, 1000) lte 5 and setup.canBindAny(($enemyGroup && $enemyGroup.length) ? $enemyGroup : $enemy)>> <<set $materials.rope -= 1>> <<run setup.addPrisoner(($enemyGroup && $enemyGroup.length) ? $enemyGroup : $enemy)>> <<set _capturedSuccessfully to true>> <<else>> <<set _capturedSuccessfully to false>> <<endif>> <<endif>> <div style="color: #ddd; font-size: 1rem; margin-bottom: 15px; line-height: 1.6;"> You successfully neutralized all hostiles in the sector.<br><br> <<if _isBossDrop>> <strong style="color:#ffaa00;">> BOSS BOUNTY SALVAGE:</strong> <strong style="color:#fbbf24;"><<print _scripDrop>> Scrip</strong>, gained <strong style="color:#34d399;"><<print _xpDrop>> XP</strong>, <strong style="color:#60a5fa;">+<<print _repDrop>> Street Cred</strong> and <strong style="color:#a855f7;">2x Global Broadcast Passes</strong>!<br> <<else>> <strong style="color:#ffaa00;">> SALVAGE:</strong> Extracted <<print _eDrop>> <<print $enemy.drop>>, <strong style="color:#fbbf24;"><<print _scripDrop>> Scrip</strong>, gained <strong style="color:#34d399;"><<print _xpDrop>> XP</strong> and <strong style="color:#60a5fa;">+<<print _repDrop>> Street Cred</strong>.<br> <<if _capturedSuccessfully>> <div style="margin:12px 0; padding:12px 14px; border:2px solid #fbbf24; border-radius:6px; background:rgba(2,6,12,0.85); box-shadow:0 0 18px rgba(251,191,36,0.35);"><div style="color:#fde047; font-weight:bold; letter-spacing:2px; margin-bottom:6px;">★ RARE EVENT: A SURVIVOR AMONG THE FALLEN ★</div><div style="color:#e2e8f0; line-height:1.6;">As you sweep the bodies, one of the downed hostiles is still breathing, too hurt to fight and too stunned to run. You loop your spare rope around their wrists before they can wake up and haul them back to your camp in chains. Luck like this is rare out here, so make it count. <strong style="color:#ef4444;">(+1 Prisoner, -1 Reinforced Rope)</strong></div></div> <<endif>> <<endif>> </div> <!-- RANDOM ITEM DROP SYSTEM --> <<if $enemy.name eq "Bebok">> <div style="background: radial-gradient(ellipse at center, rgba(127,29,29,0.55), rgba(0,0,0,0.85)); border: 3px solid #ef4444; padding: 40px 20px; margin: 10px 0 30px 0; border-radius: 8px; text-align: center; box-shadow: 0 0 35px #ef4444, inset 0 0 25px rgba(239,68,68,0.35);"> <div style="font-size: 3.2rem; line-height: 1; margin-bottom: 10px;">🎒</div> <div style="color: #fbbf24; font-size: 1.1rem; letter-spacing: 4px; margin-bottom: 14px; font-weight: bold;">👑 PATIENCE REWARDED! 👑</div> <div style="color: #ef4444; font-size: 2.3rem; font-weight: bold; letter-spacing: 2px; text-shadow: 0 0 18px #ef4444, 0 0 4px #fff; line-height: 1.2;">GODLIKE<br>BEBOK'S SCRAP PACK</div> <div style="color: #fecaca; font-size: 1.3rem; font-weight: bold; margin-top: 18px;">+1000 KG CARRY WEIGHT</div> <div style="color: #aaa; font-size: 0.9rem; margin-top: 10px;">Added to Gear Inventory (backpack slot).</div> </div> <<elseif _isBossDrop>> <<set _categories to ["headgear", "chest", "legs", "boots", "gloves", "rightHand", "offHand", "backpack", "trinket1", "ring1"]>> <<set _cat to _categories[Math.floor(Math.random() * _categories.length)]>> <<set _items to $itemDatabase[_cat]>> <<set _baseItem to _items[Math.floor(Math.random() * _items.length)]>> <<set _chosenRarity to setup.rollRarity("boss")>> <<set _droppedItemName to _chosenRarity + " " + _baseItem>> <<if ndef $gearInventory>><<set $gearInventory to []>><</if>> <<run $gearInventory.push(_droppedItemName)>> <<set _itemColor to $rarities[_chosenRarity].color>> <div style="background: rgba(0,0,0,0.6); border: 2px solid <<print _itemColor>>; padding: 15px; margin-bottom: 25px; border-radius: 4px; text-align: center; box-shadow: 0 0 15px <<print _itemColor>>;"> <div style="color: #fbbf24; font-size: 0.85rem; letter-spacing: 2px; margin-bottom: 5px; font-weight: bold;">👑 BOSS TROPHY UNLOCKED!</div> <div @style="'color: ' + _itemColor + '; font-size: 1.3rem; font-weight: bold; text-shadow: 0 0 10px ' + _itemColor + ';'"> [ <<print _droppedItemName.toUpperCase()>> ] </div> <div style="color: #aaa; font-size: 0.75rem; margin-top: 5px;">High-tier tactical equipment added to Gear Inventory.</div> </div> <<else>> <<set _dropRoll to random(1, 100)>> <<if _dropRoll lte 50>> <<set _droppedItemName to setup.generateLoot()>> <<set _rarity to _droppedItemName.split(" ")[0]>> <<set _itemColor to $rarities[_rarity].color>> <div style="background: rgba(0,0,0,0.4); border: 1px dashed <<print _itemColor>>; padding: 15px; margin-bottom: 25px; border-radius: 4px; text-align: center;"> <div style="color: #fff; font-size: 0.8rem; letter-spacing: 1px; margin-bottom: 5px;">ENEMY DROPPED EQUIPMENT!</div> <div @style="'color: ' + _itemColor + '; font-size: 1.2rem; font-weight: bold; text-shadow: 0 0 10px ' + _itemColor + ';'"> [ <<print _droppedItemName.toUpperCase()>> ] </div> <div style="color: #aaa; font-size: 0.75rem; margin-top: 5px;">Added to Gear Inventory.</div> </div> <<endif>> <<endif>> <!-- POI LOOT --> <<if def $targetPOIName and $targetPOIName neq "Fallen Military Convoy">> <div style="background: rgba(46,204,113,0.05); border: 1px solid #115e59; padding: 15px; margin-bottom: 25px; font-family: monospace; font-size: 0.9rem; color: #ccfbf1; box-sizing: border-box;"> <strong style="color: #34d399; font-family: 'Segoe UI', sans-serif;">> LOCATION EXTRACTION (<<print $targetPOIName>>):</strong><br><br> <<if $targetPOILoot eq "basic">> <<set _s to random(2,6), _w to random(1,4), _c to random(0,2)>> <<set $materials.scrap += _s, $materials.wood += _w, $materials.cloth += _c>> Secured: <<print _s>> Scrap, <<print _w>> Wood, <<print _c>> Cloth. <<elseif $targetPOILoot eq "survival">> <<set _f to random(1,3), _wt to random(1,3), _m to random(0,1)>> <<set $materials.food += _f, $materials.water += _wt, $materials.medicine += _m>> Recovered: <<print _f>> Food, <<print _wt>> Water, <<print _m>> Meds. <<elseif $targetPOILoot eq "medical">> <<set _m to random(1,3), _t to random(0,2)>> <<set $materials.medicine += _m, $materials.techData += _t>> Extracted: <<print _m>> Meds, <<print _t>> Tech Data. <<elseif $targetPOILoot eq "military">> <<set _a to random(18,35), _s to random(20,45), _t to random(1,3), _sc to random(120,250)>> <<set $materials.ammo += _a, $materials.scrap += _s, $materials.techData += _t, $materials.scrip += _sc>> Secured: <<print _a>> Ammo, <<print _s>> Scrap, <<print _t>> Tech Data, <<print _sc>> Scrip. <<elseif $targetPOILoot eq "nature">> <<set _w to random(2,5), _rm to random(1,3)>> <<set $materials.wood += _w, $materials.rawMeat += _rm>> Gathered: <<print _w>> Wood, <<print _rm>> Raw Meat. <<elseif $targetPOILoot eq "gas">> <<set _s to random(2,5), _wt to random(1,4)>> <<set $materials.scrap += _s, $materials.water += _wt>> Scrounged: <<print _s>> Scrap, <<print _wt>> Water. <<endif>> </div> <<endif>> <!-- NARRATIVE OVERRIDE: CIPHER (FIXED) --> <<if def $combatContext and $combatContext eq "cipher_ambush" and $enemy.name.includes("Vanguard")>> <div style="background: rgba(251, 191, 36, 0.10); border-left: 4px solid #fbbf24; padding: 20px; border-radius: 4px; margin-bottom: 25px;"> <div style="color: #fde68a; font-weight: bold; font-size: 1.1rem; margin-bottom: 10px; letter-spacing: 1px;">[ NOTICE: MAIN STORY COMING SOON ]</div> <div style="color: #e2e8f0; font-size: 0.95rem; line-height: 1.6;">Chapter 1 is complete. The next chapter of Cipher's story is still being written and will arrive in a future update. Until then the tunnels are a combat zone.</div> </div> <<unset $combatContext>> <<endif>> <!-- RETURN & WAVE CONTROLLER --> <div style="text-align: center; margin-top: 25px; padding: 15px; border: 1px solid #2ecc71; background: rgba(46, 204, 113, 0.1); border-radius: 4px;"> <<if def $combatContext and $combatContext eq "cipher_ch1">> <div style="color: #a7f3d0; margin-bottom: 14px; line-height: 1.6;">The tunnel falls quiet again, apart from the dripping water.</div> @@color:#bbf7d0;background:rgba(21,128,61,0.22);border:1px solid #22c55e;padding:12px 24px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "CONTINUE" "KtScene">> <<run setup.kt.afterFight()>> <<unset $enemy, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $combatWave, $reinforcementPending, $nextCombatEnemyGroup, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <<elseif def $combatContext and $combatContext eq "ch3_omicron">> <div style="color: #a7f3d0; margin-bottom: 14px; line-height: 1.6;">The fight is over. The substation hums on.</div> @@color:#bbf7d0;background:rgba(21,128,61,0.22);border:1px solid #22c55e;padding:12px 24px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "CONTINUE" "Ch3Scene">> <<run setup.ch3.afterFight()>> <<unset $enemy, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $combatWave, $reinforcementPending, $nextCombatEnemyGroup, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <<elseif def $combatContext and $combatContext eq "dungeon">> <div style="color: #fde68a; margin-bottom: 14px; line-height: 1.6;"><<if setup.dg.inBossRoom()>>Foreman Kruk is down. The lower shaft falls silent.<<else>>The tunnel is quiet again. Catch your breath and decide where to go next.<</if>></div> @@color:#fde68a;background:rgba(161,98,7,0.22);border:1px solid #f59e0b;padding:12px 24px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "BACK TO THE TUNNELS" "DungeonRoom">> <<run setup.dg.win()>> <<unset $enemy, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $combatWave, $reinforcementPending, $nextCombatEnemyGroup, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <<elseif (def $targetPOIName and $targetPOIName eq "Tychy Logging Camp") or (def $enemy and $enemy.name eq "Feral Timber-Wolf")>> @@color:#fcd34d;font-weight:bold;letter-spacing:2px;<<link "SECURE AREA & GATHER WOOD" "TychyGathering">> <<unset $enemy, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $combatWave, $reinforcementPending, $nextCombatEnemyGroup, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <<elseif def $targetPOIName and $targetPOIName eq "Ruined Military Checkpoint">> <<if ndef $checkpointWave or $checkpointWave eq 1>> @@color:#ef4444;font-weight:bold;letter-spacing:2px;<<link "WARNING: AUTOMATED DEFENSES ACTIVATED! (WAVE 2)" "CheckpointReinforcement">> <<set $checkpointWave to 2>> <<unset $enemy, $combatLog, $returnPassage, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <<elseif $checkpointWave eq 2>> @@color:#dc2626;font-weight:bold;letter-spacing:2px;<<link "WARNING: SCAVENGERS SWARMING THE PERIMETER! (WAVE 3)" "Combat">> <<set $checkpointWave to 3>> <<set $enemyGroup to [ { name: "Wasteland Scavenger 1", hp: 40, currentHp: 40, maxHp: 40, minDmg: 8, maxDmg: 14, drop: "scrap" }, { name: "Wasteland Scavenger 2", hp: 40, currentHp: 40, maxHp: 40, minDmg: 8, maxDmg: 14, drop: "scrap" }, { name: "Wasteland Scavenger 3", hp: 40, currentHp: 40, maxHp: 40, minDmg: 8, maxDmg: 14, drop: "scrap" } ]>> <<set $enemy to $enemyGroup[0]>> <<set $combatWave to 3>> <<unset $combatLog, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <<else>> @@color:#2ecc71;font-weight:bold;letter-spacing:2px;<<link "CHECKPOINT SECURED - ENTER THE COMPLEX" "RuinedMilitaryCheckpointInterior">> <<unset $enemy, $combatLog, $returnPassage, $checkpointWave, $combatWave, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <<endif>> <<elseif def $targetPOIName and $targetPOIName eq "Fallen Military Convoy">> <div style="color:#fde047; margin-bottom:14px; line-height:1.6;">The last scavenger drops. The wreckage is quiet and the sealed transport lockers are yours for the taking.</div> @@color:#eab308;font-weight:bold;letter-spacing:2px;<<link "LOOT THE CONVOY">> <<set _a to random(18,35), _s to random(20,45), _t to random(1,3), _sc to random(120,250)>> <<set $materials.ammo += _a, $materials.scrap += _s, $materials.techData += _t, $materials.scrip += _sc>> <<set $encResult to { text: "You crack the military seals on the surviving lockers and haul out everything worth carrying before more scavengers arrive.", lines: ["+" + _a + " Ammo", "+" + _s + " Scrap", "+" + _t + " Tech Data", "+" + _sc + " Scrip"] }>> <<set $encReturn to "Camp">> <<unset $enemy, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $combatWave, $reinforcementPending, $nextCombatEnemyGroup, $combatContext>> <<run window.setup.updateSidebar()>> <<goto "EncounterResult">> <</link>>@@ <<else>> <<set $checkpointWave to 0>> @@color:#2ecc71;font-weight:bold;letter-spacing:2px;<<link "PROCEED TO S.A.N.C.T.U.A.R.Y. BASE" "Camp">> <<unset $enemy, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $checkpointWave, $combatWave, $combatContext>> <<run window.setup.updateSidebar()>> <</link>>@@ <<endif>> </div> </div> <<endif>> </div> <</nobr>>
<<nobr>> /* Variable checks */ <<if ndef $syndicateTokens>><<set $syndicateTokens to 0>><<endif>> <<if ndef $firstDecryptDone>><<set $firstDecryptDone to false>><<endif>> <<if ndef $cipherMet>><<set $cipherMet to false>><<endif>> /* Perk system for permanent purchases (create on first visit) */ <<if ndef $perks>> <<set $perks to { k9: false, drone: false, vault: false, clearance: false, skin: false, medlink: false }>> <<endif>> /* Migration of old "one-time" vault buyers to the new unlimited system */ <<if ndef $vaultExtensions>> <<if $perks.vault eq true>> <<set $vaultExtensions to 1>> <<else>> <<set $vaultExtensions to 0>> <<endif>> <<endif>> <<setMenu "Comms">> <style> #story { margin-top: 0 !important; margin-right: 0 !important; margin-bottom: 0 !important; margin-left: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; } .av-world { position: relative; margin-left: 17.5em; margin-right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 20px; width: calc(100% - 17.5em); } .comms-container { background-color: #1a0b2e; padding: 30px; border: 2px solid #a855f7; border-radius: 8px; font-family: 'Segoe UI', Tahoma, sans-serif; box-shadow: 0 0 25px rgba(168, 85, 247, 0.2); box-sizing: border-box; width: 100%; } .comms-grid { display: flex; flex-wrap: wrap; gap: 20px; justify-content: flex-start; margin-top: 15px; margin-bottom: 15px; align-items: stretch; } .comms-card { background: rgba(0, 0, 0, 0.75); border: 1px solid #7e22ce; border-radius: 6px; width: 310px; overflow: hidden; display: flex; flex-direction: column; justify-content: space-between; box-shadow: 0 4px 12px rgba(0,0,0,0.6); box-sizing: border-box; } .comms-img { height: 110px; width: 100%; object-fit: cover; border-bottom: 2px solid #7e22ce; background: #000; } .perk-list { display: flex; flex-direction: column; gap: 20px; margin-top: 15px; width: 100%; } .perk-card { background: rgba(0, 0, 0, 0.85); border: 1px solid #f59e0b; border-radius: 6px; display: flex; flex-direction: row; box-shadow: 0 4px 15px rgba(0,0,0,0.7); box-sizing: border-box; overflow: hidden; min-height: 160px; transition: border-color 0.3s;} .perk-card:hover { border-color: #fbbf24; box-shadow: 0 0 20px rgba(245, 158, 11, 0.3); } .perk-img { width: 160px; height: auto; object-fit: cover; border-right: 2px solid #f59e0b; background: #111; } .perk-content { flex: 1; padding: 20px; display: flex; flex-direction: column; justify-content: space-between; } .perk-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 10px; border-bottom: 1px dashed #475569; padding-bottom: 8px;} .perk-title { color: #fcd34d; font-size: 1.1rem; font-weight: bold; letter-spacing: 1px; text-transform: uppercase;} .perk-desc { color: #cbd5e1; font-size: 0.85rem; line-height: 1.5; margin-bottom: 15px; } .main-banner-img { height: 140px; width: 100%; object-fit: cover; border: 1px solid #a855f7; border-radius: 6px; margin-bottom: 20px; background: #000; box-shadow: 0 0 15px rgba(168, 85, 247, 0.3); box-sizing: border-box; } .section-title { color: #d8b4fe; font-size: 0.95rem; font-family: 'Courier New', monospace; letter-spacing: 2px; margin: 25px 0 10px 0; text-transform: uppercase; } .cyber-divider { border: 0; height: 1px; background: linear-gradient(90deg, transparent, #a855f7, transparent); margin: 25px 0; } .cyber-comms-btn, .cyber-chat-btn, .cyber-bounty-btn, .cyber-paypal-btn, .cyber-buy-btn { display: block; width: 100%; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.8rem; letter-spacing: 1px; border-radius: 4px; cursor: pointer; text-decoration: none !important; box-sizing: border-box; transition: all 0.2s; } .cyber-comms-btn { background: linear-gradient(135deg, #7e22ce 0%, #6b21a8 100%); border: 1px solid #a855f7; color: #f3e8ff !important; box-shadow: 0 0 10px rgba(168, 85, 247, 0.3); } .cyber-comms-btn:hover { background: linear-gradient(135deg, #6b21a8 100%, #7e22ce 0%); border-color: #d8b4fe; box-shadow: 0 0 15px rgba(168, 85, 247, 0.6); } .cyber-buy-btn { background: rgba(12, 18, 26, 0.92); border: 1px solid #f59e0b; color: #fde68a !important; box-shadow: 0 0 10px rgba(245, 158, 11, 0.25); } .cyber-buy-btn:hover { border-color: #fcd34d; background: rgba(245, 158, 11, 0.2); border-color: #fcd34d; color: #ffffff !important; box-shadow: 0 0 14px rgba(245, 158, 11, 0.45); } .cyber-owned-btn { background: #1e293b; border: 1px solid #475569; color: #94a3b8 !important; cursor: not-allowed; display: block; width: 100%; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.8rem; letter-spacing: 1px; border-radius: 4px; text-decoration: none !important; box-sizing: border-box; } .cyber-return-btn { display: inline-block; background: linear-gradient(135deg, #7e22ce 0%, #581c87 100%); border: 1px solid #c084fc; color: #f3e8ff !important; padding: 12px 30px; font-weight: bold; font-family: 'Courier New', monospace; letter-spacing: 2px; border-radius: 4px; text-decoration: none !important; box-shadow: 0 0 15px rgba(168, 85, 247, 0.3); transition: all 0.2s; } .cyber-return-btn:hover { background: linear-gradient(135deg, #581c87 100%, #7e22ce 0%); border-color: #e9d5ff; box-shadow: 0 0 20px rgba(168, 85, 247, 0.5); } </style> <div class="av-world"> <div class="comms-container"> <!-- TITLE --> <div style="text-align: center; border-bottom: 1px dashed #a855f7; padding-bottom: 15px; margin-bottom: 20px;"> <h2 style="color: #a855f7; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px #a855f7; font-size: 1.8rem;">[ COMM-SERVER TERMINAL ]</h2> <div style="margin-top: 8px; font-size: 0.9rem; color: #d8b4fe;">ENCRYPTED NETWORK UPLINK ACTIVE</div> </div> <img src="images/locations/comms_chat.jpg" class="main-banner-img" onerror="this.src='https://placehold.co/800x200/000/a855f7?text=COMM-SERVER+UPLINK';"> <!-- STORY TWIST: CIPHER'S FIRST DECRYPT --> <<if $cipherMet eq true and $firstDecryptDone eq false>> <div style="background: rgba(168, 85, 247, 0.15); border: 2px solid #a855f7; padding: 25px; border-radius: 6px; margin-bottom: 25px; font-family: 'Courier New', monospace;"> <div style="color: #d8b4fe; font-weight: bold; font-size: 1.1rem; margin-bottom: 12px; letter-spacing: 1px;">💾 TERMINAL ALERT: UNKNOWN ENCRYPTED OS DRIVE DETECTED</div> <div style="color: #cbd5e1; line-height: 1.6; margin-bottom: 15px;"> For a moment the terminal screen turns from green lines to purple. Cipher leans over your shoulder to look at the screen and reaches for the keyboard: <i>"There it is... that OS disk you brought. Wait, let me decrypt it."</i> </div> <<link "<span class='cyber-comms-btn' style='text-align: center;'>[ CONTINUE: DECRYPT THE FILE AND EXAMINE IT WITH CIPHER ]</span>" "CipherFirstDecrypt">> <<set $materials.techData -= 1>> <<set $firstDecryptDone to true>> <</link>> </div> <<else>> <<if def $commsLead>> <div style="border: 1px solid #2ecc71; background: rgba(46, 204, 113, 0.1); padding: 15px; border-radius: 4px; margin-bottom: 25px; font-family: monospace;"> <div style="color: #2ecc71; font-weight: bold; margin-bottom: 8px;">✅ DECRYPTION SUCCESSFUL: NEW LEAD FOUND</div> <div style="color: #cbd5e1; font-style: italic; margin-bottom: 10px;">"<<print $commsLead.text>>"</div> <div style="color: #fde047; text-align: center; border-top: 1px dashed #2ecc71; padding-top: 8px;"> > NEW LEAD: <b><<print $commsLead.name>></b> is now marked on your Sector Map and listed on the Bounty Board. </div> </div> <<unset $commsLead>> <<elseif def $commsFail>> <div style="border: 1px dashed #f59e0b; background: rgba(245, 158, 11, 0.08); padding: 14px; border-radius: 4px; margin-bottom: 25px; font-family: monospace; color: #fcd34d; text-align: center;"> <<print $commsFail>> </div> <<unset $commsFail>> <<endif>> <<endif>> <!-- TOP SECTION: MAIN NETWORK OPERATIONS --> <div class="section-title">>> MAIN NETWORK OPERATIONS</div> <div class="comms-grid" data-tour="comms-ops"> <div class="comms-card"> <div> <img src="images/locations/comms_decrypt.jpg" class="comms-img" onerror="this.src='https://placehold.co/600x300/000/a855f7?text=DECRYPT';"> <div style="padding: 12px;"> <div style="color: #d8b4fe; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">💾 DECRYPT TECH DATA</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">Breach encrypted logs in a live hacking mini-game. <b style="color:#d8b4fe;">Win and one Tech Data reveals a new rumor lead</b> on your Sector Map. Lose and the Tech Data burns.</div> </div> </div> <div style="padding: 0 12px 12px 12px;"> <<set _dchk to ((setup.have("techData") gte 1) ? setup.decryptCheck() : "")>> <<if setup.have("techData") gte 1 and _dchk is "">> <<link "<span class='cyber-comms-btn'>💾 UPLOAD & BREACH (1 Tech Data)</span>" "HackGame">> <<run setup.hackBegin({ ctx: "decrypt", game: "rand" })>> <</link>> <<elseif setup.have("techData") gte 1>> <div style="color: #fbbf24; font-size: 0.75rem; font-weight: bold; text-align: center; border: 1px dashed #fbbf24; padding: 8px;"><<print _dchk>></div> <<else>> <div style="color: #ef4444; font-size: 0.75rem; font-weight: bold; text-align: center; border: 1px dashed #ef4444; padding: 8px;">[ NO TECH DATA ]</div> <<endif>> </div> </div> <div class="comms-card"> <div> <img src="images/locations/comms_decrypt.jpg" class="comms-img" onerror="this.src='https://placehold.co/600x300/000/22c55e?text=TRAINING';"> <div style="padding: 12px;"> <div style="color: #4ade80; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">🧪 HACKING TRAINING</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">Tutorial and free play for all three hacking mini-games. <b style="color:#4ade80;">No penalty, no cost.</b></div> </div> </div> <div style="padding: 0 12px 12px 12px;"> <<link "<span class='cyber-comms-btn' style='background: linear-gradient(135deg, #15803d 0%, #166534 100%); border-color: #4ade80;'>🧪 OPEN TRAINING SIM</span>" "HackTraining">><</link>> </div> </div> <div class="comms-card" data-tour="comms-chat"> <div> <img src="images/locations/comms_chat.jpg" class="comms-img" onerror="this.src='https://placehold.co/600x300/000/3b82f6?text=SERVER+CHAT';"> <div style="padding: 12px;"> <div style="color: #60a5fa; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">💬 SECTOR SERVER CHAT</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px;">Secure terminal routing initialized. Access local mesh.</div> </div> </div> <div style="padding: 0 12px 12px 12px;"> <<link "<span class='cyber-comms-btn' style='background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%); border-color: #38bdf8;'>💬 ACCESS CHAT</span>" "ServerChat">><</link>> </div> </div> <div class="comms-card"> <div> <img src="images/locations/comms_bounty.jpg" class="comms-img" onerror="this.src='https://placehold.co/600x300/000/10b981?text=BOUNTY';"> <div style="padding: 12px;"> <div style="color: #34d399; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">📜 BOUNTY BOARD</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px;">Every rumor lead and boss contract you picked up at the bar, from Kaelen or by decrypting data.</div> </div> </div> <div style="padding: 0 12px 12px 12px;"> <<link "<span class='cyber-comms-btn' style='background: linear-gradient(135deg, #059669 0%, #047857 100%); border-color: #10b981;'>📜 OPEN BOUNTY BOARD (<<print (setup.siteCounts().rumors + setup.siteCounts().contracts)>>)</span>" "BountyBoard">><</link>> </div> </div> </div> <hr class="cyber-divider"> <!-- BUYING SYNDICATE TOKENS --> <div class="section-title">>> SYNDICATE NETWORK UPLINK (PURCHASE PREMIUM CREDITS)</div> <div style="background: rgba(0,0,0,0.6); border: 1px solid #f59e0b; padding: 20px; border-radius: 6px; margin-bottom: 25px;"> <div style="text-align: center; color: #fcd34d; font-size: 1.2rem; font-weight: bold; margin-bottom: 15px; font-family: 'Courier New', monospace; letter-spacing: 2px;"> 💎 CURRENT BALANCE: <<print $syndicateTokens>> TOKENS 💎 </div> <div style="display: flex; gap: 15px; justify-content: center; flex-wrap: wrap;"> <a href="https://www.paypal.me/ashenveilgame/1usd?note=100+Tokens" target="_blank" class="cyber-buy-btn" style="flex: 1; min-width: 200px; background: linear-gradient(135deg, #0284c7 0%, #0369a1 100%); border-color: #38bdf8;"> <div style="font-size: 1.1rem; margin-bottom: 4px;">💎 100 TOKENS</div> <div style="color: #cbd5e1; font-size: 0.8rem;">PRICE: $1.00</div> </a> <a href="https://www.paypal.me/ashenveilgame/5usd?note=600+Tokens" target="_blank" class="cyber-buy-btn" style="flex: 1; min-width: 200px;"> <div style="font-size: 1.1rem; margin-bottom: 4px;">💎 600 TOKENS</div> <div style="color: #fef3c7; font-size: 0.8rem;">PRICE: $5.00 <span style="color: #34d399; font-weight: bold;">(+100 BONUS)</span></div> </a> <a href="https://www.paypal.me/ashenveilgame/10usd?note=1200+Tokens" target="_blank" class="cyber-buy-btn" style="flex: 1; min-width: 200px; background: linear-gradient(135deg, #4338ca 0%, #6366f1 100%); border-color: #818cf8;"> <div style="font-size: 1.1rem; margin-bottom: 4px;">💎 1200 TOKENS</div> <div style="color: #e0e7ff; font-size: 0.8rem;">PRICE: $10.00 <span style="color: #34d399; font-weight: bold;">(+200 BONUS)</span></div> </a> </div> </div> <hr class="cyber-divider"> <!-- NEW SECTION: PERMANENT PRODUCTS (PORTRAIT STYLE) --> <div class="section-title">>> PERMANENT BASE & OPERATOR UPGRADES</div> <div style="color: #94a3b8; font-size: 0.85rem; margin-bottom: 10px; font-style: italic;">Licenses and blueprints purchased here are permanently bound to your command center.</div> <div class="perk-list" data-tour="comms-shop"> <!-- 1. K-9 CERBERUS --> <div class="perk-card"> <img src="images/items/premiums/cyber_k9_unit.jpg" class="perk-img" onerror="this.src='https://placehold.co/300x400/000/f59e0b?text=CERBERUS';"> <div class="perk-content"> <div> <div class="perk-header"> <div class="perk-title">K-9 "CERBERUS" WAR-HOUND</div> <div style="color: #fbbf24; font-family: monospace; font-weight: bold;">150 💎</div> </div> <div class="perk-desc"> Upgrade your canine companion into a fully cybernetic combat hound. Permanently increases passive scrap scavenging after encounters and provides a resilient tactical advantage in the wasteland. </div> </div> <div> <<if $perks.k9 eq true>> <<if setup.squadHas("Cerberus")>> <span class='cyber-owned-btn'>[ CERBERUS ACTIVE IN YOUR SQUAD ]</span> <<link "<span class='cyber-buy-btn' style='margin-top:6px;'>🔁 SWITCH TO REGULAR K9</span>" "Comms">><<run setup.companionSwitch()>><</link>> <<else>> <span class='cyber-owned-btn'>[ REGULAR K9 ACTIVE ]</span> <<link "<span class='cyber-buy-btn' style='margin-top:6px;'>🔁 SWITCH TO CERBERUS</span>" "Comms">><<run setup.companionSwitch()>><</link>> <</if>> <<elseif not setup.squadHas("Trained K9") and not setup.squadHas("Cerberus")>> <div style="text-align: center; color: #ef4444; font-size: 0.8rem; font-weight: bold; border: 1px dashed #ef4444; padding: 10px; background: rgba(239, 68, 68, 0.1);">[ REQUIREMENT: K-9 COMPANION ]</div> <<elseif $syndicateTokens gte 150>> <<link "<span class='cyber-buy-btn'>DEPLOY CERBERUS HOUND (150 💎)</span>" "Comms">> <<run setup.spendTok(150)>> <<set $perks.k9 to true>> <<run setup.cerberusToSquad()>> <</link>> <<else>> <div style="text-align: center; color: #ef4444; font-size: 0.8rem; font-weight: bold; border: 1px dashed #ef4444; padding: 10px; background: rgba(239, 68, 68, 0.1);">INSUFFICIENT TOKENS</div> <<endif>> </div> </div> </div> <!-- 2. SCRAPPER DRONE --> <div class="perk-card"> <img src="images/items/premiums/scrapper_drone.jpg" class="perk-img" onerror="this.src='https://placehold.co/300x400/000/f59e0b?text=DRONE';"> <div class="perk-content"> <div> <div class="perk-header"> <div class="perk-title">SCRAPPER-IX AUTONOMOUS DRONE</div> <div style="color: #fbbf24; font-family: monospace; font-weight: bold;">200 💎</div> </div> <div class="perk-desc"> Deploy a relentless automated harvester to the camp perimeter. Generates a permanent passive income of raw scrap materials every cycle without requiring rest or rations. </div> </div> <div> <<if $perks.drone eq true>> <span class='cyber-owned-btn'>[ DEPLOYED ]</span> <<elseif $syndicateTokens gte 200>> <<link "<span class='cyber-buy-btn'>ACTIVATE DRONE (200 💎)</span>" "Comms">> <<run setup.spendTok(200)>> <<set $perks.drone to true>> <</link>> <<else>> <div style="text-align: center; color: #ef4444; font-size: 0.8rem; font-weight: bold; border: 1px dashed #ef4444; padding: 10px; background: rgba(239, 68, 68, 0.1);">INSUFFICIENT TOKENS</div> <<endif>> </div> </div> </div> <!-- 3. GRID VAULT (UNLIMITED UPGRADE) --> <div class="perk-card"> <img src="images/items/premiums/vault_license.jpg" class="perk-img" onerror="this.src='https://placehold.co/300x400/000/f59e0b?text=VAULT';"> <div class="perk-content"> <div> <div class="perk-header"> <div class="perk-title">GRID VAULT STORAGE EXTENSION</div> <div style="color: #fbbf24; font-family: monospace; font-weight: bold;">150 💎</div> </div> <div class="perk-desc"> Gain a decrypted master key to an underground corporate heavy-steel bunker. Each purchase permanently increases your base storage capacity by a massive +500 limit. (Stackable) </div> </div> <div> <<if $vaultExtensions gt 0>> <div style="color: #34d399; font-weight: bold; margin-bottom: 10px; text-align: center; border: 1px solid #10b981; background: rgba(16, 185, 129, 0.1); padding: 5px; border-radius: 4px;"> [ CURRENTLY PURCHASED: <<print $vaultExtensions>> ] </div> <<endif>> <<if $syndicateTokens gte 150>> <<link "<span class='cyber-buy-btn'>PURCHASE VAULT EXTENSION (150 💎)</span>" "Comms">> <<run setup.spendTok(150)>> <<set $vaultExtensions += 1>> /* Note: when checking the max limit in the inventory system, remember to add ($vaultExtensions * 500) to the base capacity! */ <</link>> <<else>> <div style="text-align: center; color: #ef4444; font-size: 0.8rem; font-weight: bold; border: 1px dashed #ef4444; padding: 10px; background: rgba(239, 68, 68, 0.1);">INSUFFICIENT TOKENS</div> <<endif>> </div> </div> </div> <!-- 4. SYNDICATE CLEARANCE --> <div class="perk-card"> <img src="images/items/premiums/syndicate_clearance.jpg" class="perk-img" onerror="this.src='https://placehold.co/300x400/000/f59e0b?text=CLEARANCE';"> <div class="perk-content"> <div> <div class="perk-header"> <div class="perk-title">BLACK-MARKET SYNDICATE CLEARANCE</div> <div style="color: #fbbf24; font-family: monospace; font-weight: bold;">120 💎</div> </div> <div class="perk-desc"> Acquire a highly illegal holographic VIP syndicate ID. Permanently grants a 15% discount on all merchant trades and black-market dealings across the sector. </div> </div> <div> <<if $perks.clearance eq true>> <span class='cyber-owned-btn'>[ ID VERIFIED ]</span> <<elseif $syndicateTokens gte 120>> <<link "<span class='cyber-buy-btn'>FORGE ID CLEARANCE (120 💎)</span>" "Comms">> <<run setup.spendTok(120)>> <<set $perks.clearance to true>> <</link>> <<else>> <div style="text-align: center; color: #ef4444; font-size: 0.8rem; font-weight: bold; border: 1px dashed #ef4444; padding: 10px; background: rgba(239, 68, 68, 0.1);">INSUFFICIENT TOKENS</div> <<endif>> </div> </div> </div> <!-- 5. OBSIDIAN EXO SKIN --> <div class="perk-card" data-tour="comms-skin"> <img src="images/items/premiums/obsidian_exoframe.jpg" class="perk-img" onerror="this.src='https://placehold.co/300x400/000/f59e0b?text=SKIN';"> <div class="perk-content"> <div> <div class="perk-header"> <div class="perk-title">MATTE-OBSIDIAN EXOFRAME SKIN</div> <div style="color: #fbbf24; font-family: monospace; font-weight: bold;">80 💎</div> </div> <div class="perk-desc"> A purely cosmetic, high-end matte-black tactical plating with neon-amber accents for your armor. Command respect in the visual logs and show the wasteland who truly rules the sector. </div> </div> <div> <<if $perks.skin eq true>> <<if setup.exoActive()>> <span class='cyber-owned-btn'>[ EXOFRAME SKIN ACTIVE ]</span> <<link "<span class='cyber-buy-btn' style='margin-top:6px;'>🔁 SWITCH TO REGULAR SKIN</span>" "Comms">><<run setup.exoSwitch()>><</link>> <<else>> <span class='cyber-owned-btn'>[ REGULAR SKIN ACTIVE ]</span> <<link "<span class='cyber-buy-btn' style='margin-top:6px;'>🔁 SWITCH TO EXOFRAME SKIN</span>" "Comms">><<run setup.exoSwitch()>><</link>> <</if>> <<elseif $syndicateTokens gte 80>> <<link "<span class='cyber-buy-btn'>PURCHASE SKIN (80 💎)</span>" "Comms">> <<run setup.spendTok(80)>> <<set $perks.skin to true>> <<set $exoOff to false>> <<run window.setup.updateSidebar()>> <</link>> <<else>> <div style="text-align: center; color: #ef4444; font-size: 0.8rem; font-weight: bold; border: 1px dashed #ef4444; padding: 10px; background: rgba(239, 68, 68, 0.1);">INSUFFICIENT TOKENS</div> <<endif>> </div> </div> </div> <!-- 6. MED-LINK SUITE --> <div class="perk-card"> <img src="images/items/premiums/auto_stim_rig.jpg" class="perk-img" onerror="this.src='https://placehold.co/300x400/000/f59e0b?text=MEDLINK';"> <div class="perk-content"> <div> <div class="perk-header"> <div class="perk-title">MED-LINK SUITE: AUTO-STIM RIG</div> <div style="color: #fbbf24; font-family: monospace; font-weight: bold;">130 💎</div> </div> <div class="perk-desc"> Install a wall-mounted military auto-injector in your quarters. Automatically patches minor wounds and restores baseline health upon returning to base, saving you valuable medicine. </div> </div> <div> <<if $perks.medlink eq true>> <span class='cyber-owned-btn'>[ INSTALLED IN HQ ]</span> <<elseif $syndicateTokens gte 130>> <<link "<span class='cyber-buy-btn'>INSTALL MED-LINK (130 💎)</span>" "Comms">> <<run setup.spendTok(130)>> <<set $perks.medlink to true>> <</link>> <<else>> <div style="text-align: center; color: #ef4444; font-size: 0.8rem; font-weight: bold; border: 1px dashed #ef4444; padding: 10px; background: rgba(239, 68, 68, 0.1);">INSUFFICIENT TOKENS</div> <<endif>> </div> </div> </div> </div> <div style="text-align: center; margin-top: 35px;"> <<link "<span class='cyber-return-btn'>🏠 RETURN TO S.A.N.C.T.U.A.R.Y. BASE</span>" "Camp">> <<run window.setup.updateSidebar()>> <</link>> </div> </div> </div> <</nobr>>
<<setMenu "DefenseWalls">> <<if ndef $wallIntegrity>><<set $wallIntegrity to 100>><</if>> <<if ndef $tripwiresArmed>><<set $tripwiresArmed to false>><</if>> <<if ndef $turretsAligned>><<set $turretsAligned to false>><</if>> <<if ndef $laborWalls>><<set $laborWalls to 0>><</if>> <style> /* DEFINITIVE FIX THAT REMOVES THE TOP GAP */ body { background-color: #050a05 !important; } #story, #passages, .passage { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; background: transparent !important; } /* ALIGNMENT IDENTICAL TO THE OTHER SCREENS */ .av-world { position: absolute; top: 10px; left: 17.5em; right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 0 20px 20px 20px; width: calc(100% - 17.5em); } .walls-container { background-color: #260909; padding: 30px; border: 2px solid #ef4444; border-radius: 8px; font-family: 'Segoe UI', Tahoma, sans-serif; box-shadow: 0 0 25px rgba(239, 68, 68, 0.2); box-sizing: border-box; width: 100%; } .walls-grid { display: flex; flex-wrap: wrap; gap: 20px; justify-content: center; margin-top: 25px; margin-bottom: 20px; } .walls-card { background: rgba(0, 0, 0, 0.75); border: 1px solid #b91c1c; border-radius: 6px; flex: 1 1 310px; min-width: 0; max-width: 640px; min-height: 290px; overflow: hidden; display: flex; flex-direction: column; justify-content: space-between; box-shadow: 0 4px 12px rgba(0,0,0,0.6); box-sizing: border-box; } .walls-img { height: 110px; width: 100%; object-fit: cover; border-bottom: 2px solid #b91c1c; background: #000; } .main-banner-img { height: 140px; width: 100%; object-fit: cover; border: 1px solid #ef4444; border-radius: 6px; margin-bottom: 20px; background: #000; box-shadow: 0 0 15px rgba(239, 68, 68, 0.3); box-sizing: border-box; } .section-title { color: #fca5a5; font-size: 0.95rem; font-family: 'Courier New', monospace; letter-spacing: 2px; margin: 30px 0 15px 0; text-transform: uppercase; } .cyber-walls-btn { display: block; width: 100%; background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%); border: 1px solid #f87171; color: #fef2f2 !important; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.8rem; letter-spacing: 1px; border-radius: 4px; cursor: pointer; text-decoration: none !important; box-sizing: border-box; box-shadow: 0 0 10px rgba(239, 68, 68, 0.3); transition: all 0.2s; } .cyber-walls-btn:hover { background: linear-gradient(135deg, #b91c1c 100%, #dc2626 0%); border-color: #fca5a5; box-shadow: 0 0 15px rgba(239, 68, 68, 0.6); } .cyber-return-btn { display: inline-block; background: linear-gradient(135deg, #b91c1c 100%, #7f1d1d 100%); border: 1px solid #f87171; color: #fef2f2 !important; padding: 12px 30px; font-weight: bold; font-family: 'Courier New', monospace; letter-spacing: 2px; border-radius: 4px; text-decoration: none !important; box-shadow: 0 0 15px rgba(239, 68, 68, 0.3); transition: all 0.2s; } .cyber-return-btn:hover { background: linear-gradient(135deg, #7f1d1d 100%, #b91c1c 0%); border-color: #fca5a5; box-shadow: 0 0 20px rgba(239, 68, 68, 0.5); } </style> <<nobr>> <div class="av-world"> <div class="walls-container"> <!-- TITLE --> <div style="text-align: center; border-bottom: 1px dashed #ef4444; padding-bottom: 15px; margin-bottom: 20px;"> <h2 style="color: #f87171; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px #ef4444; font-size: 1.8rem;">[ PERIMETER DEFENSE WALLS ]</h2> <div style="margin-top: 8px; font-size: 0.9rem; color: #fca5a5;">FORTIFICATION & RAID DETERRENCE UPLINK</div> </div> <!-- DEFENSE WALLS IMAGE BANNER --> <img src="images/locations/defense_walls_main.jpg" class="main-banner-img" onerror="this.src='https://placehold.co/800x200/000/ef4444?text=PERIMETER+DEFENSES';"> <!-- ACTION LOG --> <<if def $wallsLog>> <div style="border: 1px solid #ef4444; background: rgba(239, 68, 68, 0.1); padding: 15px; border-radius: 4px; margin-bottom: 25px; font-family: monospace;"> <div style="color: #f87171; font-weight: bold; margin-bottom: 8px;">🛡️ DEFENSE STATUS: REINFORCED</div> <div style="color: #cbd5e1; font-style: italic;">"<<print $wallsLog>>"</div> </div> <<unset $wallsLog>> <</if>> <!-- DEFENSE SECTION --> <div class="section-title">>> FORTIFICATION & SECURITY PROTOCOLS</div> <div style="border: 1px solid #7f1d1d; background: rgba(0,0,0,0.5); padding: 14px; border-radius: 4px; margin-bottom: 20px; font-family: monospace; font-size: 0.85rem; color: #fca5a5;"> <div>🧱 BARRICADE INTEGRITY: <strong style="color:<<if $wallIntegrity gte 70>>#34d399<<elseif $wallIntegrity gte 40>>#fbbf24<<else>>#ef4444<</if>>;"><<print $wallIntegrity>>%</strong> <span style="color:#94a3b8;">(low integrity weakens your walls and kills more defenders in a raid)</span></div> <div>👥 WALL GARRISON (assigned in Labor): <strong style="color:#fff;"><<print $laborWalls>></strong></div> <div>⚡ TRIPWIRES: <strong><<if $tripwiresArmed>><span style="color:#34d399;">ARMED TONIGHT (-<<print setup.GATE.trapsPct>>% raid threat)</span><<else>><span style="color:#ef4444;">NOT ARMED</span><</if>></strong></div> <div>🎯 AUTO-TURRETS: <strong><<if $turretsAligned>><span style="color:#34d399;">CALIBRATED TONIGHT (-<<print setup.GATE.turretPct>>% raid threat)</span><<else>><span style="color:#ef4444;">NOT CALIBRATED</span><</if>></strong></div> <div style="color:#fbbf24; margin-top:6px;">Tripwires and turrets are daily chores: they are spent every night, so re-arm them before you sleep.</div> </div> <<nobr>> <div class="walls-grid"> <!-- 1. REINFORCE BARRICADES --> <div class="walls-card"> <img src="images/locations/walls_repair.jpg" class="walls-img" onerror="this.src='https://placehold.co/600x300/000/ef4444?text=BARRICADES';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #f87171; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">🧱 PATCH BARRICADES</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">Repair breaches and weld steel plates over structural weak points.</div> <div style="color: #fbbf24; font-size: 0.7rem; font-family: monospace;">COST: 50 Scrap | EFFECT: +25% barricade integrity (repair after raids)</div> </div> <div style="margin-top:10px;"> <<if $wallIntegrity gte 100>> <div style="background: rgba(15, 23, 42, 0.6); border: 1px dashed #166534; color: #86efac; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem;">BARRICADES INTACT</div> <<elseif setup.tipOn("patch") or setup.have("scrap") gte setup.GATE.patchScrap>> <<set _wlbl to "<span class='cyber-walls-btn'>🧱 WELD PLATES (" + (setup.tipOn("patch") ? "FREE: KAELEN'S TIP, +15% EXTRA" : "50 SCRAP") + ")</span>">> <<link _wlbl "DefenseWalls">> <<if setup.tipOn("patch")>> <<run setup.tipClear("patch")>> <<set $wallIntegrity to Math.min(100, $wallIntegrity + setup.GATE.patchAmount + 15)>> <<else>> <<run setup.spend("scrap", setup.GATE.patchScrap)>> <<set $wallIntegrity to Math.min(100, $wallIntegrity + setup.GATE.patchAmount)>> <</if>> <<set $wallsLog to "Perimeter metal plates welded and secured against intrusion.">> <<run window.setup.updateSidebar()>> <</link>> <<else>> <div style="background: rgba(15, 23, 42, 0.6); border: 1px dashed #7f1d1d; color: #fca5a5; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem; border-radius: 4px;">[ INSUFFICIENT SCRAP ]</div> <<endif>> </div> </div> </div> <!-- 2. SET PERIMETER TRAPS --> <div class="walls-card"> <img src="images/locations/walls_traps.jpg" class="walls-img" onerror="this.src='https://placehold.co/600x300/000/b91c1c?text=TRAPS';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #f87171; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">⚡ RIG TRIPWIRES & TRAPS</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">Deploy acoustic sensors and explosive tripwires around the outer approach.</div> <div style="color: #fbbf24; font-size: 0.7rem; font-family: monospace;">COST: 20 Stamina | EFFECT: -20% raid threat tonight (DAILY CHORE)</div> </div> <div style="margin-top:10px;"> <<if $tripwiresArmed>> <div style="background: rgba(15, 23, 42, 0.6); border: 1px dashed #166534; color: #86efac; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem;">TRIPWIRES ARMED FOR TONIGHT</div> <<elseif $stamina gte 20>> <<link "<span class='cyber-walls-btn'>⚡ ARM TRIPWIRES (20 STAM)</span>" "DefenseWalls">> <<set $stamina -= 20>> <<set $tripwiresArmed to true>> <<set $wallsLog to "Outer perimeter sensor grid and tripwires armed successfully.">> <<run window.setup.updateSidebar()>> <</link>> <<else>> <div style="background: rgba(15, 23, 42, 0.6); border: 1px dashed #7f1d1d; color: #fca5a5; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem; border-radius: 4px;">[ INSUFFICIENT STAMINA ]</div> <<endif>> </div> </div> </div> <!-- 3. TURRET CALIBRATION --> <div class="walls-card"> <img src="images/locations/walls_turret.jpg" class="walls-img" onerror="this.src='https://placehold.co/600x300/000/7f1d1d?text=TURRET';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #f87171; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">🎯 CALIBRATE AUTO-TURRETS</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">Align automated defense emplace targets and reload ammunition drums.</div> <div style="color: #fbbf24; font-size: 0.7rem; font-family: monospace;">COST: 20 Ammo | EFFECT: -30% raid threat tonight (DAILY CHORE)</div> </div> <div style="margin-top:10px;"> <<if $turretsAligned>> <div style="background: rgba(15, 23, 42, 0.6); border: 1px dashed #166534; color: #86efac; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem;">TURRETS CALIBRATED FOR TONIGHT</div> <<elseif setup.have("ammo") gte 20>> <<link "<span class='cyber-walls-btn'>🎯 ALIGN TURRETS (20 AMMO)</span>" "DefenseWalls">> <<run setup.spend("ammo", 20)>> <<set $turretsAligned to true>> <<set $wallsLog to "Automated defense turrets calibrated and synced with motion sensors.">> <<run window.setup.updateSidebar()>> <</link>> <<else>> <div style="background: rgba(15, 23, 42, 0.6); border: 1px dashed #7f1d1d; color: #fca5a5; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem; border-radius: 4px;">[ INSUFFICIENT AMMO ]</div> <<endif>> </div> </div> </div> </div> <</nobr>> <!-- BACK TO THE SANCTUARY HUB --> <div style="text-align: center; margin-top: 35px;"> <<link "<span class='cyber-return-btn'>🏰 RETURN TO SANCTUARY HUB</span>" "Sanctuary Hub">><</link>> </div> </div> </div> <</nobr>>
<<audio ":all" stop>> <<audio "sector_bunker" volume 0.25 loop play>> <<nobr>> <<if ndef $survivors>><<set $survivors to 0>><</if>> /* DYNAMIC BACKGROUND CALCULATION */ <<if ndef $currentLocation>><<set $currentLocation to "Sector Bunker">><</if>> <<set _bgFileName to $currentLocation.toLowerCase().replace(/ /g, "_")>> <<set $exploreBgUrl to "images/locations/combat_" + _bgFileName + ".jpg">> <style> #story { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; overflow-x: hidden !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; background: transparent !important; overflow-x: hidden !important; } .av-explore-wrapper { position: relative; margin-left: 17.5em; width: calc(100% - 17.5em); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 30px; box-sizing: border-box; overflow-x: hidden; } @media screen and (max-width: 768px) { .av-explore-wrapper { margin-left: 3.5em; width: calc(100% - 3.5em); } } </style> <div class="av-explore-wrapper" @style="'background: linear-gradient(180deg, rgba(10,5,15,0.7), rgba(20,5,30,0.9)), url(\'' + $exploreBgUrl + '\') center/cover;'"> <div style="background-color: rgba(5, 5, 10, 0.88); padding: 40px; border: 2px solid #a855f7; border-radius: 8px; font-family: 'Courier New', monospace; color: #a855f7; width: 100%; max-width: 750px; box-shadow: 0 0 40px rgba(168, 85, 247, 0.3); backdrop-filter: blur(8px);"> <h2 style="text-align: center; letter-spacing: 2px; text-shadow: 0 0 10px #a855f7;">[ SECTOR BUNKER 04 EXPLORATION ]</h2> <hr style="border: 1px dashed #a855f7; opacity: 0.4; margin: 20px 0;"> <p style="color: #cbd5e1; line-height: 1.6; font-size: 1.05rem;">You breach the blast doors of a sealed pre-war shelter. Inside, intact computer terminals hum with pre-war archives, and a small group of survivors has been living off emergency rations.</p> <<if $stamina gte 20>> <<set $stamina -= 20>> <<set $materials.techData += 3>> <<set $survivors += 1>><<run setup.cLife.add(4, "You rescued a survivor")>> <div style="background: rgba(168, 85, 247, 0.15); border-left: 4px solid #a855f7; padding: 15px; margin: 20px 0; color: #a855f7;"> <strong>SUCCESS!</strong> Downloaded <strong>3 Tech Data</strong> files and convinced the bunker dwellers to join Będzin Core! <br><span style="color:#2ecc71; font-weight:bold;">[+1 Survivor added to Colony Labor Pool]</span> </div> /* 10% CHANCE TO DROP AN ITEM */ <<set _luckMod to $playerLuck || 0>> <<set _gearRoll to random(1, 100) - _luckMod>> <<if _gearRoll lte 10>> <<set _droppedItemName to setup.generateLoot()>> <<set _rarity to _droppedItemName.split(" ")[0]>> <<set _itemColor to $rarities[_rarity].color>> <div style="background: rgba(0,0,0,0.5); border: 1px dashed <<print _itemColor>>; padding: 12px; margin-bottom: 15px; border-radius: 4px; text-align: center;"> <div style="color: #fff; font-size: 0.75rem; letter-spacing: 1px; margin-bottom: 4px;">PRE-WAR LOCKER OPENED: VALUABLE FOUND!</div> <div @style="'color: ' + _itemColor + '; font-size: 1.1rem; font-weight: bold; text-shadow: 0 0 8px ' + _itemColor + ';'"> [ <<print _droppedItemName.toUpperCase()>> ] </div> </div> <<endif>> <<else>> <div style="color: #ef4444; font-weight: bold; margin: 20px 0; padding: 15px; border: 1px dashed #ef4444; text-align: center;">ERROR: Insufficient Stamina to explore the bunker! (-20 Stamina required)</div> <<endif>> <div style="text-align: center; margin-top: 35px; display: flex; justify-content: center; gap: 20px;"> <div style="border: 1px solid #a855f7; background: rgba(168,85,247,0.15); padding: 12px 25px; border-radius: 4px; transition: 0.3s; cursor: pointer;"> @@color:#a855f7;font-weight:bold;letter-spacing:1px;<<link "SEARCH AGAIN" "Explore Bunker">><</link>>@@ </div> <div style="border: 1px solid #00ff00; background: rgba(0,255,0,0.15); padding: 12px 25px; border-radius: 4px; transition: 0.3s; cursor: pointer;"> @@color:#00ff00;font-weight:bold;letter-spacing:1px;<<link "RETURN TO SECTOR MAP" "Map">><</link>>@@ </div> </div> </div> </div> <</nobr>>
<<audio ":all" stop>> <<audio "clinic_tension" volume 0.25 loop play>> <<nobr>> <<if ndef $survivors>><<set $survivors to 0>><</if>> /* DYNAMIC BACKGROUND CALCULATION */ <<if ndef $currentLocation>><<set $currentLocation to "Silesian Park Clinic">><</if>> <<set _bgFileName to $currentLocation.toLowerCase().replace(/ /g, "_")>> <<set $exploreBgUrl to "images/locations/combat_" + _bgFileName + ".jpg">> <style> #story { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; overflow-x: hidden !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; background: transparent !important; overflow-x: hidden !important; } .av-explore-wrapper { position: relative; margin-left: 17.5em; width: calc(100% - 17.5em); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 30px; box-sizing: border-box; overflow-x: hidden; } @media screen and (max-width: 768px) { .av-explore-wrapper { margin-left: 3.5em; width: calc(100% - 3.5em); } } </style> <div class="av-explore-wrapper" @style="'background: linear-gradient(180deg, rgba(5,10,5,0.6), rgba(35,5,5,0.9)), url(\'' + $exploreBgUrl + '\') center/cover;'"> <div style="background-color: rgba(5, 10, 5, 0.88); padding: 40px; border: 2px solid #2ecc71; border-radius: 8px; font-family: 'Courier New', monospace; color: #2ecc71; width: 100%; max-width: 750px; box-shadow: 0 0 40px rgba(46, 204, 113, 0.3); backdrop-filter: blur(8px);"> <h2 style="text-align: center; letter-spacing: 2px; text-shadow: 0 0 10px #2ecc71;">[ SILESIAN PARK CLINIC SEARCH ]</h2> <hr style="border: 1px dashed #2ecc71; opacity: 0.4; margin: 20px 0;"> <p style="color: #cbd5e1; line-height: 1.6; font-size: 1.05rem;">Overgrown vegetation blankets the ruined clinic. You search through smashed medical cabinets for uncorrupted drugs, medical linens, and check rooms for survivors.</p> <<if $stamina gte 15>> <<set $stamina -= 15>> <<set $materials.medicine += 4>> <<set $materials.cloth += 8>> <<set _bd to random(1, 2)>> <<set $materials.plainBandage to (($materials.plainBandage || 0) + _bd)>> <<set _foundSurvivor to random(1, 10)>> <<if _foundSurvivor gte 4>> <<set $survivors += 1>><<run setup.cLife.add(4, "You rescued a survivor")>> <div style="background: rgba(46, 204, 113, 0.15); border-left: 4px solid #2ecc71; padding: 15px; margin: 20px 0; color: #2ecc71;"> <strong>SUCCESS!</strong> Recovered <strong>4 Medical Supplies</strong>, <strong>8 Cloth</strong>, <strong><<print _bd>> Plain Bandage<<if _bd gt 1>>s<</if>></strong>, and guided a hidden refugee to safety! <br><span style="color:#2ecc71; font-weight:bold;">[+1 Survivor added to Colony Labor Pool]</span> </div> <<else>> <div style="background: rgba(46, 204, 113, 0.15); border-left: 4px solid #2ecc71; padding: 15px; margin: 20px 0; color: #2ecc71;"> <strong>SUCCESS!</strong> Recovered <strong>4 Medical Supplies</strong>, <strong>8 Cloth</strong> and <strong><<print _bd>> Plain Bandage<<if _bd gt 1>>s<</if>></strong> from the ruined wards. </div> <<endif>> /* 10% CHANCE TO DROP AN ITEM */ <<set _luckMod to $playerLuck || 0>> <<set _gearRoll to random(1, 100) - _luckMod>> <<if _gearRoll lte 10>> <<set _droppedItemName to setup.generateLoot()>> <<set _rarity to _droppedItemName.split(" ")[0]>> <<set _itemColor to $rarities[_rarity].color>> <div style="background: rgba(0,0,0,0.5); border: 1px dashed <<print _itemColor>>; padding: 12px; margin-bottom: 15px; border-radius: 4px; text-align: center;"> <div style="color: #fff; font-size: 0.75rem; letter-spacing: 1px; margin-bottom: 4px;">HIDDEN STASH DISCOVERED!</div> <div @style="'color: ' + _itemColor + '; font-size: 1.1rem; font-weight: bold; text-shadow: 0 0 8px ' + _itemColor + ';'"> [ <<print _droppedItemName.toUpperCase()>> ] </div> </div> <<endif>> <<else>> <div style="color: #ef4444; font-weight: bold; margin: 20px 0; padding: 15px; border: 1px dashed #ef4444; text-align: center;">ERROR: Insufficient Stamina to search the clinic! (-15 Stamina required)</div> <<endif>> <div style="text-align: center; margin-top: 35px; display: flex; justify-content: center; gap: 20px;"> <div style="border: 1px solid #2ecc71; background: rgba(46,204,113,0.15); padding: 12px 25px; border-radius: 4px; transition: 0.3s; cursor: pointer;"> @@color:#2ecc71;font-weight:bold;letter-spacing:1px;<<link "SEARCH AGAIN" "Explore Clinic">><</link>>@@ </div> <div style="border: 1px solid #00ff00; background: rgba(0,255,0,0.15); padding: 12px 25px; border-radius: 4px; transition: 0.3s; cursor: pointer;"> @@color:#00ff00;font-weight:bold;letter-spacing:1px;<<link "RETURN TO SECTOR MAP" "Map">><</link>>@@ </div> </div> </div> </div> <</nobr>>
<<audio ":all" stop>> <<audio "warehouse_beat" volume 0.25 loop play>> <<nobr>> <<if ndef $survivors>><<set $survivors to 0>><</if>> /* DYNAMIC BACKGROUND CALCULATION */ <<if ndef $currentLocation>><<set $currentLocation to "Nikizovichek Warehouse">><</if>> <<set _bgFileName to $currentLocation.toLowerCase().replace(/ /g, "_")>> <<set $exploreBgUrl to "images/locations/combat_" + _bgFileName + ".jpg">> <style> #story { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; overflow-x: hidden !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; background: transparent !important; overflow-x: hidden !important; } .av-explore-wrapper { position: relative; margin-left: 17.5em; width: calc(100% - 17.5em); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 30px; box-sizing: border-box; overflow-x: hidden; } @media screen and (max-width: 768px) { .av-explore-wrapper { margin-left: 3.5em; width: calc(100% - 3.5em); } } </style> <div class="av-explore-wrapper" @style="'background: linear-gradient(180deg, rgba(5,10,15,0.7), rgba(5,20,35,0.9)), url(\'' + $exploreBgUrl + '\') center/cover;'"> <div style="background-color: rgba(5, 10, 15, 0.88); padding: 40px; border: 2px solid #38bdf8; border-radius: 8px; font-family: 'Courier New', monospace; color: #38bdf8; width: 100%; max-width: 750px; box-shadow: 0 0 40px rgba(56, 189, 248, 0.3); backdrop-filter: blur(8px);"> <h2 style="text-align: center; letter-spacing: 2px; text-shadow: 0 0 10px #38bdf8;">[ NIKISZOWIEC WAREHOUSE OPERATION ]</h2> <hr style="border: 1px dashed #38bdf8; opacity: 0.4; margin: 20px 0;"> <p style="color: #cbd5e1; line-height: 1.6; font-size: 1.05rem;">You scour the massive red-brick industrial warehouse. Amidst rusted machinery, old textiles, and debris, you search for building materials and salvageable cloth.</p> <<if $stamina gte 15>> <<set $stamina -= 15>> <<set $materials.scrap += 10>> <<set $materials.wood += 8>> <<set $materials.cloth += 6>> <<set _foundSurvivor to random(1, 10)>> <<if _foundSurvivor gte 5>> <<set $survivors += 1>><<run setup.cLife.add(4, "You rescued a survivor")>> <div style="background: rgba(56, 189, 248, 0.15); border-left: 4px solid #38bdf8; padding: 15px; margin: 20px 0; color: #38bdf8;"> <strong>SUCCESS!</strong> You found <strong>10 Scrap</strong>, <strong>8 Wood</strong>, <strong>6 Cloth</strong>, and rescued a trapped worker! <br><span style="color:#2ecc71; font-weight:bold;">[+1 Survivor added to Colony Labor Pool]</span> </div> <<else>> <div style="background: rgba(56, 189, 248, 0.15); border-left: 4px solid #38bdf8; padding: 15px; margin: 20px 0; color: #38bdf8;"> <strong>SUCCESS!</strong> You scavenged <strong>10 Scrap</strong>, <strong>8 Wood</strong>, and <strong>6 Cloth</strong> from the sector. </div> <<endif>> /* 10% CHANCE TO DROP AN ITEM */ <<set _luckMod to $playerLuck || 0>> <<set _gearRoll to random(1, 100) - _luckMod>> <<if _gearRoll lte 10>> <<set _droppedItemName to setup.generateLoot()>> <<set _rarity to _droppedItemName.split(" ")[0]>> <<set _itemColor to $rarities[_rarity].color>> <div style="background: rgba(0,0,0,0.5); border: 1px dashed <<print _itemColor>>; padding: 12px; margin-bottom: 15px; border-radius: 4px; text-align: center;"> <div style="color: #fff; font-size: 0.75rem; letter-spacing: 1px; margin-bottom: 4px;">CRATE LOOTED: EQUIPMENT FOUND!</div> <div @style="'color: ' + _itemColor + '; font-size: 1.1rem; font-weight: bold; text-shadow: 0 0 8px ' + _itemColor + ';'"> [ <<print _droppedItemName.toUpperCase()>> ] </div> </div> <<endif>> <<else>> <div style="color: #ef4444; font-weight: bold; margin: 20px 0; padding: 15px; border: 1px dashed #ef4444; text-align: center;">ERROR: Not enough Stamina to thoroughly search the warehouse! (-15 Stamina required)</div> <<endif>> <div style="text-align: center; margin-top: 35px; display: flex; justify-content: center; gap: 20px;"> <<link '<span style="color:#38bdf8; font-weight:bold; letter-spacing:1px; padding: 12px 25px; border: 1px solid #38bdf8; background: rgba(56,189,248,0.15); border-radius: 4px;">SCAVENGE AGAIN</span>' "Explore Warehouse">><</link>> <<link '<span style="color:#00ff00; font-weight:bold; letter-spacing:1px; padding: 12px 25px; border: 1px solid #00ff00; background: rgba(0,255,0,0.15); border-radius: 4px;">RETURN TO MAP</span>' "Map">><</link>> </div> </div> </div> <</nobr>>
<style> #story { margin-top: 0 !important; margin-right: 0 !important; margin-bottom: 0 !important; margin-left: 17.5em !important; padding: 0 !important; } #passages { max-width: 100% !important; padding: 0 !important; margin: 0 !important; border: none !important; } .passage { padding: 40px !important; margin: 0 !important; background: linear-gradient(rgba(10, 10, 15, 0.85), rgba(5, 5, 10, 0.95)), url('images/locations/convoy_bg.jpg') center/cover no-repeat fixed !important; min-height: 100vh !important; box-sizing: border-box !important; color: #00ff00 !important; font-family: 'Courier New', monospace !important; } .my-cyber-btn { display: block !important; width: 100% !important; max-width: 550px !important; margin: 0 auto 12px auto !important; background: rgba(10, 10, 15, 0.95) !important; border: 2px solid #eab308 !important; color: #eab308 !important; padding: 14px 20px !important; font-family: 'Courier New', monospace !important; font-size: 0.95rem !important; font-weight: bold !important; letter-spacing: 1px !important; border-radius: 4px !important; text-align: center !important; box-shadow: 0 0 12px rgba(234,179,8,0.25) !important; text-decoration: none !important; } .my-cyber-btn:hover { background: rgba(234,179,8,0.2) !important; color: #fef08a !important; box-shadow: 0 0 18px rgba(234,179,8,0.5) !important; } .my-return-btn { display: block !important; width: 100% !important; max-width: 550px !important; margin: 0 auto !important; background: rgba(10, 10, 15, 0.95) !important; border: 2px dashed #94a3b8 !important; color: #cbd5e1 !important; padding: 12px 20px !important; font-family: 'Courier New', monospace !important; font-size: 0.85rem !important; letter-spacing: 1px !important; border-radius: 4px !important; text-align: center !important; text-decoration: none !important; } .my-return-btn:hover { border-color: #eab308 !important; color: #eab308 !important; } </style> <<audio ":all" stop>> <<audio "combat_theme" volume 0.8 loop play>> <<setMenu "Fallen Military Convoy">> <!-- TOP HEADER --> <div style="text-align: center; border-bottom: 1px dashed #eab308; padding-bottom: 15px; margin-bottom: 25px;"> <h2 style="color: #eab308; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px rgba(234,179,8,0.5);">[ FALLEN MILITARY CONVOY ]</h2> <div style="margin-top: 10px; font-size: 0.9rem; color: #fde047;">PJS-99 SUPPLY LINE // WRECKAGE ZONE</div> </div> <!-- STORY AND WARNING AREA --> <div style="background: rgba(0,0,0,0.75); border: 1px solid rgba(234,179,8,0.4); padding: 25px; border-radius: 6px; margin-bottom: 30px; box-shadow: 0 0 15px rgba(234,179,8,0.1);"> <p style="color: #cbd5e1; line-height: 1.7; font-size: 0.95rem; margin: 0 0 15px 0;"> The air still smells of cordite and scorched kevlar. Heavily armored supply trucks, blown wide open by landmines, rest like massive metal skeletons across the wasteland. Mercenaries and desert drifters swarm the wreckage, loading heavy ammunition crates onto their buggies. </p> <p style="color: #cbd5e1; line-height: 1.7; font-size: 0.95rem; margin: 0 0 15px 0;"> The military-grade seals on the transport lockers remain intact. If you take the risk and neutralize these scavengers, you can secure enough heavy ordnance and rare alloys to sustain the base for weeks. </p> <div style="background: rgba(234, 179, 8, 0.08); border-left: 3px solid #eab308; padding: 12px; margin-top: 15px; color: #fef08a; font-size: 0.85rem;"> <strong>[ Tactical Warning ]</strong> Drawing your weapon and firing will generate massive noise. Gunshots echo across the wasteland, instantly drawing reinforcement squads straight to your position. Prepare for back-to-back combat waves without respite. </div> </div> <!-- ACTION AND BUTTON AREA --> <div> <!-- ENTER BATTLE DIRECTLY --> <<link '<span class="my-cyber-btn">DRAW WEAPON & ATTACK (WAVE 1: 3x Veterans)</span>'>> <<set $enemyGroup to [ { name: "Rogue Veteran 1", hp: 65, currentHp: 65, maxHp: 65, minDmg: 14, maxDmg: 22, drop: "ammo" }, { name: "Rogue Veteran 2", hp: 65, currentHp: 65, maxHp: 65, minDmg: 14, maxDmg: 22, drop: "ammo" }, { name: "Rogue Veteran 3", hp: 65, currentHp: 65, maxHp: 65, minDmg: 14, maxDmg: 22, drop: "ammo" } ]>> <<set $enemy to $enemyGroup[0]>> <<set $targetPOIName to "Fallen Military Convoy">> <<set $targetPOILoot to "military">> <<set $currentLocation to "Fallen Military Convoy">> <<set $combatWave to 1>> <<set $reinforcementPending to true>> <<set $nextCombatEnemyGroup to [ { name: "Convoy Scavenger 1", hp: 45, currentHp: 45, maxHp: 45, minDmg: 10, maxDmg: 16, drop: "scrap" }, { name: "Convoy Scavenger 2", hp: 45, currentHp: 45, maxHp: 45, minDmg: 10, maxDmg: 16, drop: "scrap" }, { name: "Convoy Scavenger 3", hp: 45, currentHp: 45, maxHp: 45, minDmg: 10, maxDmg: 16, drop: "scrap" }, { name: "Convoy Scavenger 4", hp: 45, currentHp: 45, maxHp: 45, minDmg: 10, maxDmg: 16, drop: "scrap" } ]>> <<set $nextCombatEnemy to $nextCombatEnemyGroup[0]>> <<goto "Combat">> <</link>> <!-- STEALTH OPTION (AMBUSH RISK) --> <<set _sneakText to (setup.squadHas("Sector Hacker") ? "SNEAK & LOOT CRATES (100% Safe via Hacker)" : "SNEAK & LOOT CRATES (40% Ambush Risk)")>> <<link '<span class="my-cyber-btn">_sneakText</span>'>> <<set _ambushChance to 40>> <<if setup.squadHas("Sector Hacker")>> <<set _ambushChance to 0>> <</if>> <<set _roll to random(1, 100)>> <<if _roll lte _ambushChance>> <!-- AMBUSH / WALKED INTO AN AMBUSH --> <<set $hp to Math.max(1, $hp - 15)>> <<set $combatLog to ["> AMBUSH! You triggered a hidden landmine! Took 15 explosive damage before combat began!"]>> <<set $enemyGroup to [ { name: "Rogue Veteran 1", hp: 65, currentHp: 65, maxHp: 65, minDmg: 14, maxDmg: 22, drop: "ammo" }, { name: "Rogue Veteran 2", hp: 65, currentHp: 65, maxHp: 65, minDmg: 14, maxDmg: 22, drop: "ammo" }, { name: "Rogue Veteran 3", hp: 65, currentHp: 65, maxHp: 65, minDmg: 14, maxDmg: 22, drop: "ammo" } ]>> <<set $enemy to $enemyGroup[0]>> <<set $targetPOIName to "Fallen Military Convoy">> <<set $targetPOILoot to "military">> <<set $currentLocation to "Fallen Military Convoy">> <<set $combatWave to 1>> <<set $reinforcementPending to true>> <<set $nextCombatEnemyGroup to [ { name: "Convoy Scavenger 1", hp: 45, currentHp: 45, maxHp: 45, minDmg: 10, maxDmg: 16, drop: "scrap" }, { name: "Convoy Scavenger 2", hp: 45, currentHp: 45, maxHp: 45, minDmg: 10, maxDmg: 16, drop: "scrap" }, { name: "Convoy Scavenger 3", hp: 45, currentHp: 45, maxHp: 45, minDmg: 10, maxDmg: 16, drop: "scrap" }, { name: "Convoy Scavenger 4", hp: 45, currentHp: 45, maxHp: 45, minDmg: 10, maxDmg: 16, drop: "scrap" } ]>> <<set $nextCombatEnemy to $nextCombatEnemyGroup[0]>> <<goto "Combat">> <<else>> <!-- SUCCESSFUL STEALTH --> <<set _am to Math.floor(Math.random() * 12) + 8>> <<set _sc to Math.floor(Math.random() * 4) + 1>> <<set $materials.ammo += _am>> <<set $materials.scrap += _sc>> <<set $thirst = Math.max(0, $thirst - 10)>> <<set _hk to (setup.squadHas("Sector Hacker") ? "Your Sector Hacker slips the crate locks and kills the alarm. Nobody even knows you were here." : "You creep between the burnt-out trucks, pry open the nearest crates and slip away before the mercenaries notice.")>> <<set $encResult to { text: _hk, lines: ["+" + _am + " Ammo", "+" + _sc + " Scrap", "-10% Thirst"] }>> <<set $encReturn to "Map">> <<run window.setup.updateSidebar()>> <<goto "EncounterResult">> <</if>> <</link>> <<link '<span class="my-return-btn">RETURN TO RADAR MAP</span>' "Map">> <</link>> </div>
<<nobr>> <<setMenu "Map">> <<if ndef $materials>><<set $materials to {}>><</if>> <<if ndef $materials.rawFish>><<set $materials.rawFish to 0>><</if>> <<if ndef $materials.scrap>><<set $materials.scrap to 0>><</if>> <<if ndef $stamina>><<set $stamina to 100>><</if>> <<if ndef $thirst>><<set $thirst to 100>><</if>> <style> /* DEFINITIVELY RESET SUGARCUBE GAPS AND HORIZONTAL OVERFLOW */ #story { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; overflow-x: hidden !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; background: transparent !important; overflow-x: hidden !important; } .ashen-veil-fish-wrapper { position: relative; margin-left: 17.5em; width: calc(100% - 17.5em); min-height: 100vh; background: linear-gradient(180deg, rgba(5,15,25,0.7), rgba(5,10,20,0.9)), url('images/locations/bg_fishing.jpg') center top/cover no-repeat; padding: 30px; display: flex; flex-direction: column; align-items: center; justify-content: center; box-sizing: border-box; overflow-x: hidden; } .ashen-veil-fish-bg { background: rgba(5, 15, 25, 0.85); border: 2px solid #38bdf8; border-radius: 8px; padding: 30px; display: flex; flex-direction: column; align-items: center; font-family: 'Segoe UI', Tahoma, sans-serif; box-shadow: 0 0 40px rgba(56, 189, 248, 0.3); backdrop-filter: blur(6px); } .fish-hud { display: flex; justify-content: space-between; width: 800px; margin-bottom: 10px; } .fish-stat { background: rgba(0,0,0,0.8); border: 1px solid #38bdf8; color: #38bdf8; padding: 10px 20px; font-weight: bold; border-radius: 2px; flex: 1; text-align: center; margin: 0 5px; } #river-container { position: relative; width: 800px; height: 500px; background: url('images/locations/rawa_river_container.jpg') center/cover no-repeat; border: 2px solid #38bdf8; overflow: hidden; margin-bottom: 20px; box-shadow: 0 0 30px rgba(56, 189, 248, 0.2); border-radius: 4px; } .scanline-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: repeating-linear-gradient(0deg, rgba(0,0,0,0.15), rgba(0,0,0,0.15) 1px, transparent 1px, transparent 2px); pointer-events: none; z-index: 10; } #fish-log { width: 800px; text-align: center; font-size: 1.2rem; font-weight: bold; color: #94a3b8; min-height: 30px; margin-bottom: 20px; text-transform: uppercase; letter-spacing: 2px; } .btn-cast { width: 800px; background: rgba(56,189,248,0.1); border: 1px solid #38bdf8; padding: 15px; text-align: center; border-radius: 4px; transition: 0.3s; margin-bottom: 20px; cursor: pointer; } .btn-cast a { display: block; color: #38bdf8 !important; font-size: 1.1rem; font-weight: bold; text-decoration: none !important; letter-spacing: 2px; } .btn-cast:hover { background: rgba(56,189,248,0.3); box-shadow: 0 0 15px rgba(56,189,248,0.5); } .disabled-btn { width: 800px; background: rgba(239,68,68,0.1); border: 1px dashed #ef4444; padding: 15px; text-align: center; border-radius: 4px; margin-bottom: 20px; opacity: 0.7; cursor: not-allowed; } #fish-target a { display: block; width: 80px; height: 80px; background-color: rgba(251, 191, 36, 0.3); border: 2px solid #fbbf24; background-image: url('images/locations/fish_target.png'); background-size: contain; background-position: center; background-repeat: no-repeat; border-radius: 50%; transition: transform 0.1s; box-shadow: 0 0 20px rgba(251, 191, 36, 0.6); } #fish-target a:hover { transform: scale(1.15); background-color: rgba(251, 191, 36, 0.6); filter: brightness(1.5); box-shadow: 0 0 30px rgba(251, 191, 36, 1); } @media screen and (max-width: 768px) { .ashen-veil-fish-wrapper { margin-left: 3.5em; width: calc(100% - 3.5em); } } </style> <div class="ashen-veil-fish-wrapper"> <div class="ashen-veil-fish-bg"> <div style="text-align: center; margin-bottom: 20px;"> <h2 style="color: #38bdf8; margin: 0 0 10px 0; letter-spacing: 4px; text-shadow: 0 0 15px rgba(56, 189, 248, 0.5);">[ TOXIC WATERWAY ]</h2> </div> <div class="fish-hud"> <div class="fish-stat" style="border-color:#38bdf8; color:#38bdf8;">⚡ STAMINA: <span id="stamina-count" style="color:#fff;"><<print $stamina>></span></div> <div class="fish-stat" style="border-color:#2ecc71; color:#2ecc71;">🎯 STATUS: <span id="fish-status" style="color:#fff;">READY TO CAST</span></div> <div class="fish-stat" style="border-color:#fbbf24; color:#fbbf24;">🐟 FISH: <span id="fish-count" style="color:#fff;"><<print $materials.rawFish>></span></div> </div> <div id="river-container"> <div class="scanline-overlay"></div> <div id="fish-target" style="display:none; position:absolute; z-index:50;"> <<link " ">> /* PLAY THE SPLASH SOUND THE MOMENT THE FISH (ICON) IS CLICKED */ <<audio "sfx_splash" play>> <<set _luckMod to $playerLuck || 0>> <<set _fishChance to $playerFishChance || 0>> <<set _catchRoll to random(1, 100) + _fishChance>> <<set _secretLootRoll to random(1, 100) + _luckMod>> <<if _secretLootRoll gte 95>> <<set _lootCat to ["ring1", "trinket1"].random()>> <<set _droppedItemName to setup.generateLoot(_lootCat)>> <<set _rarity to _droppedItemName.split(" ")[0]>> <<set _itemColor to $rarities[_rarity].color>> <<replace "#fish-log">>@@color:<<print _itemColor>>;UNBELIEVABLE! YOU FISHED UP: <<print _droppedItemName.toUpperCase()>> 💍@@<</replace>> <<else>> <<if _catchRoll lte 45>> <<set $materials.rawFish += 1>> <<replace "#fish-count">><<print $materials.rawFish>><</replace>> <<replace "#fish-log">>@@color:#2ecc71;TARGET SECURED! +1 MUTATED FISH 🐟@@<</replace>> <<elseif _catchRoll lte 75>> <<set $materials.scrap += 1>> <<replace "#fish-log">>@@color:#fbbf24;DISAPPOINTING... CAUGHT +1 SCRAP 🔩@@<</replace>> <<else>> <<set _junk to ["RADIOACTIVE ALGAE", "A RUSTY GLOBE", "AN OLD BOOT", "TOXIC SLUDGE"].random()>> <<replace "#fish-log">>@@color:#ef4444;DAMN IT! YOU CAUGHT <<print _junk>> 🗑️@@<</replace>> <<endif>> <<endif>> <<script>> clearTimeout(window.fishMissTimer); $("#fish-target").hide(); $("#fish-status").text("READY TO CAST").css("color", "#fff"); if(typeof window.setup.updateSidebar === 'function') window.setup.updateSidebar(); if(State.variables.stamina >= 15) { $("#cast-btn").show(); $("#disabled-btn").hide(); } else { $("#cast-btn").hide(); $("#disabled-btn").show(); } <</script>> <</link>> </div> </div> <div id="fish-log">WAITING FOR OPERATOR TO CAST LINE...</div> <div style="width: 800px; height: 60px;"> <div id="cast-btn" class="btn-cast"> <<link "🎣 CAST LINE (-15 STAMINA)">> <<set $stamina -= 15>> <<if $stamina lt 0>><<set $stamina to 0>><</if>> <<set $thirst = Math.max(0, $thirst - 5)>> <<replace "#stamina-count">><<print $stamina>><</replace>> <<replace "#fish-log">>@@color:#38bdf8;WATCHING THE RADIOACTIVE BOBBER...@@<</replace>> <<script>> $("#cast-btn").hide(); $("#disabled-btn").hide(); $("#fish-status").text("WAITING...").css("color", "#38bdf8"); if(typeof window.setup.updateSidebar === 'function') window.setup.updateSidebar(); setTimeout(function() { var $target = $("#fish-target"); var $cont = $("#river-container"); var maxX = $cont.width() - 90; var maxY = $cont.height() - 90; $target.css({ left: Math.floor(Math.random() * maxX) + "px", top: Math.floor(Math.random() * maxY) + "px" }).show(); $("#fish-log").html('<span style="color:#ef4444; font-weight:bold;">BITE DETECTED! CLICK THE TARGET!</span>'); window.fishMissTimer = setTimeout(function() { if($target.is(":visible")) { $target.hide(); $("#fish-log").html('<span style="color:#ef4444; font-weight:bold;">TARGET ESCAPED.</span>'); $("#fish-status").text("READY TO CAST").css("color", "#fff"); if(State.variables.stamina >= 15) { $("#cast-btn").show(); } else { $("#disabled-btn").show(); } } }, 1400); }, Math.floor(Math.random() * 2500) + 2000); <</script>> <</link>> </div> <div id="disabled-btn" class="disabled-btn" style="display:none;"> <span style="color: #ef4444; font-weight: bold; display: block;">❌ EXHAUSTED (REQUIRES 15 STAMINA)</span> </div> </div> <div style="margin-top: 20px; width: 800px; text-align: center; border: 1px solid #38bdf8; padding: 15px; background: rgba(56,189,248,0.1); border-radius: 4px; box-sizing: border-box;"> @@color:#38bdf8;font-weight:bold;<<link "RETURN TO RADAR MAP" "Map">><</link>>@@ </div> </div> </div> <<script>> $(document).on(':passagedisplay', function() { if(State.variables.stamina >= 15) { $("#cast-btn").show(); $("#disabled-btn").hide(); } else { $("#cast-btn").hide(); $("#disabled-btn").show(); } }); <</script>> <</nobr>>
<<nobr>> <div style="background-color: #050a05; padding: 40px; border: 2px solid #ef4444; border-radius: 8px; font-family: 'Courier New', monospace; text-align: center; margin-top: 10vh;"> <h1 style="color: #ef4444; font-size: 3rem; margin: 0; letter-spacing: 5px; text-shadow: 0 0 20px #ef4444;">[ SIGNAL LOST ]</h1> <div style="color: #aaa; font-size: 1.2rem; margin-top: 20px; margin-bottom: 40px;"> Operator <strong style="color: #fff;"><<print $playerName>></strong> has flatlined.<br> <<if $deathCause>>Cause of death: <strong style="color: #ef4444;"><<print $deathCause>></strong>.<br><</if>> Survived for <strong style="color: #ffaa00;"><<print $day>> Days</strong> in the wasteland. </div> <div style="color: #ef4444; font-size: 0.9rem; margin-bottom: 40px; border: 1px dashed #ef4444; padding: 15px; display: inline-block;"> FATAL ERROR: BIOMETRIC SYSTEMS OFFLINE. PERMADEATH PROTOCOL ENGAGED. </div> <<if setup.ch3 and setup.ch3.devCanRespawn()>> <div style="margin-bottom: 24px; color: #fde68a;">DEV TEST MODE (account nakman only): you are not really dead.<br> <<link ">>> RESPAWN AT THE BASE (TEST) <<<">><<run setup.ch3.devRespawn()>><</link>> </div> <</if>> <div> <<link ">>> INITIATE NEW OPERATOR PROTOCOL (RESTART) <<<">> <<run setup.restartCharacter()>> <</link>> </div> </div> <</nobr>>
<<silently>> /* ========================================================================== */ /* 📝 NEWSPAPER CONTENT EDITING AREA (FULL LOADED V0.5.7) */ /* ========================================================================== */ <<set _newsIssue to "ISSUE #0.5.7 — CAMP LIFE UPDATE">> <<set _systemAlert to "Your camp is alive now. Every survivor and prisoner eats and drinks every night, the furnaces burn wood, people fall ill and get hurt, and your deeds decide whether the camp believes in you or walks away.">> /* Only the CURRENT update's squares live here. When a new update is added, its squares replace these (the old ones leave with the old news). */ <<set _newsStats to [ { n: "2", l: "SUPPLY DROPS A DAY" }, { n: "1", l: "NEW RESOURCE" }, { n: "3", l: "READING FONTS" }, { n: "3", l: "READ ALOUD MODES" }, { n: "100", l: "POINTS OF CAMP MORALE" }, { n: "3", l: "NIGHTLY NEEDS" }, { n: "5", l: "MORALE LEVELS" }, { n: "3", l: "FREE NIGHTS" }, { n: "2", l: "CAMP TYPES" }, { n: "3-4", l: "WAVES PER CAMP" }, { n: "4", l: "TALK TOPICS" }, { n: "3", l: "PIT UPGRADES" }, { n: "2", l: "PLAYERS PER TABLE" }, { n: "6", l: "BLACKJACK STAKES" }, { n: "6", l: "SEATS AT A POKER TABLE" }, { n: "5", l: "POKER BUY-INS" } ]>> <<set _newsSections to [ { tag: "NEW", featured: true, title: "👻 GHOST RAIDS: ROB THE SLEEPING", text: "Netrunner duels have a second mode. Open the Netrunner Duels window in the Arena and raid a survivor who is not online.<ul><li><b>Everyone leaves a ghost.</b> It solves each puzzle in times taken from that survivor's own recorded breaches, so a veteran of many duels is a real threat. Survivors who never dueled get an average ghost with their talents applied.</li><li><b>Real Scrip on the line.</b> Beat the ghost and you steal 10% of the owner's Scrip, up to 1000, minus Kaelen's 5% cut. Lose and the ghost takes the same share of yours. Both of you need at least 100 Scrip.</li><li><b>They hear about it.</b> The owner gets a report the next time they log in.</li><li><b>No bleeding dry.</b> You can raid the same survivor once every 30 minutes, and nobody can be robbed more than 3 times an hour.</li><li><b>The ladder moves too,</b> at half the weight of a live duel.</li></ul>" }, { tag: "NEW", featured: true, title: "💻 NETRUNNER DUELS: A HACKING RACE, LIVE", text: "Netrunners finally have a fight of their own. Challenge a survivor, or join the queue, and race them through the same hacking puzzles at the same moment.<ul><li><b>Three puzzles, best of three.</b> Terminal Override, Circuit Bypass and Signal Decryption, in a random order. The faster clean breach wins the round. If your rival finishes first, you have ten seconds left to answer.</li><li><b>Mistakes cost seconds.</b> A wrong answer adds 2.0 seconds, a false circuit 1.5, a decoy click 2.5. A target click takes 0.5 off.</li><li><b>The server keeps the clock.</b> The puzzles are built and checked on the server, so everybody races the same way.</li><li><b>Your talents count.</b> Overclock, Kill Switch and System Override cut your breach time by a percentage. Data Miner, Neural Interface, Packet Sniffer and Ghost in the Machine take off whole seconds. Firewall, Black Ice and System Override shrink the penalties you take, and Signal Jammer makes your rival's mistakes cost more. A Neural Shield and the Techie or Broadcaster perk add seconds too. Your time can never be cut by more than 40%, so skill still wins.</li><li><b>No wager, but not free.</b> The winner steals 10% of the loser's Scrip, up to 1000, and Kaelen keeps 5% of it. Both of you need at least 100 Scrip to enter. You cannot take Scrip from the same survivor twice within 30 minutes.</li><li><b>Its own ladder.</b> Netrunner duels have a separate rating and a top ten. Nobody dies and nobody loses gear.</li><li><b>Where.</b> Open the PvP Arena and press <i>Enter a Hack Duel</i>, or challenge a survivor from their card in the chat.</li></ul>" }, { tag: "NEW", featured: true, title: "⚔ LIVE BATTLES: REAL FIGHTS, REAL STAKES", text: "Ghost duels are still there, but now you can fight a real survivor, in the same combat you know from the wasteland.<ul><li><b>The real combat.</b> You take turns. On your turn you spend your action points one action at a time: a <i>headshot</i>, a <i>torso</i> shot, an <i>arm shot</i> that cripples their aim, a <i>leg shot</i> that makes them limp and knocks melee fighters back. Take <i>cover</i>, use a <i>bandage</i> or a <i>stimpack</i>, or <i>charge</i> and <i>pull back</i> between the FAR, MID and CLOSE zones.</li><li><b>Your gear decides.</b> Your fighter is built from your saved character: weapon, armor, talents and level. Ranged weapons hit hardest at FAR and use ammo. When the magazine runs dry you switch to your knife. Melee only strikes at CLOSE.</li><li><b>Put Scrip on it.</b> Pick a wager of 25, 50, 100, 250 or 500 Scrip, or fight for rating only. Both wagers are taken when the fight starts, and the winner takes the pot minus a 5% fee. A draw returns every wager.</li><li><b>Challenge or queue.</b> Open the PvP Arena and press <i>Enter a Live Battle</i>. Challenge any survivor who is online, or from their card in the chat, or join the queue and get matched with someone of a similar level and the same wager.</li><li><b>Fair for everyone.</b> Each fighter gets 2 bandages and 1 stimpack for the fight, so nobody is hurt by what they carry. Nobody dies and nobody loses gear.</li><li><b>Do not stall.</b> You have 45 seconds a turn. Skip three turns in a row and you forfeit your wager.</li><li><b>Show it off.</b> The winner can post the result once, in any sector chat. Everyone can press <i>Watch the fight</i> on the post and see the whole battle replayed move by move.</li><li><b>Its own ladder.</b> Live battles have a separate rating and a top ten, and they never touch the Ghost Arena cooldowns.</li></ul>" }, { tag: "NEW", featured: true, title: "🏛 KAELEN OPENS THE TRADING POST", text: "Kaelen now runs a trading post for resources, and every customer is a real survivor. Find it at the Merchant.<ul><li><b>Sell.</b> Pick a resource, an amount and a price for one unit. Your goods are taken from your pack first, then your camp storage, and wait with Kaelen. There is no time limit.</li><li><b>Buy.</b> Take as many units as you want from any listing, or buy the whole lot. You pay, you get the goods.</li><li><b>Get paid at once.</b> When someone buys from you, the Scrip reaches you right away, even if you are away from the keyboard.</li><li><b>The fee.</b> Kaelen keeps 5% of every sale.</li><li><b>Changed your mind?</b> Take the unsold rest back at any time. You can have up to 5 listings at once.</li><li><b>Safe by design.</b> The server holds the goods and the Scrip. Nothing can be duplicated or lost, even if you log off.</li></ul>" }, { tag: "NEW", featured: true, title: "♠ TEXAS HOLD'EM AT THE RUSTY ANCHOR", text: "The bar has a new table, and this one seats up to six. Texas Hold'em is open at the Rusty Anchor, and every seat is a real survivor.<ul><li><b>Find the table.</b> Open the Wasteland Bar and press <i>Sit at a Table</i>. You will see every table waiting for players, and you can sit down at one or open your own.</li><li><b>Open your own.</b> Pick a buy-in of 25, 50, 100, 250 or 500 Scrip and how many seats you want, from 2 to 6. The game starts when the table is full, or whenever the host says go.</li><li><b>Same chips for everyone.</b> Everybody puts in the same Scrip and gets the same stack of chips. Winning a hand wins chips, not Scrip.</li><li><b>The blinds climb.</b> Every few hands the forced bets go up, so a table never drags on forever. Keep your nerve and watch your stack.</li><li><b>Winner takes it all.</b> The last player with chips takes the whole prize pool, which is every buy-in on the table added together.</li><li><b>A fair game.</b> Fold, check, call, bet, raise or go all-in, with side pots when someone is short. You only see your own two cards until the showdown. The server deals every card, so nobody can peek or tamper.</li><li><b>Your move.</b> You have 30 seconds a turn. If you do nothing, you check, or fold if there is a bet to face.</li><li><b>Leaving early.</b> Walk away before the game starts and your buy-in comes back. Walk away mid-game and your chips stay on the table.</li></ul>" }, { tag: "NEW", title: "🃏 WASTELAND BLACKJACK: PLAY EACH OTHER FOR SCRIP", text: "Kaelen's blackjack table was always you against the house. Now it is you against another survivor, and the Scrip on the table is real.<ul><li><b>How to start.</b> Click any survivor in the Sector Server Chat, or in the Online Now list, pick a stake and press <i>Challenge to Blackjack</i>. They get a pop-up and a minute to say yes.</li><li><b>Pick your stake.</b> 10, 25, 50, 100, 250 or 500 Scrip, and you both put up the same amount. Winner takes the whole pot.</li><li><b>How a hand plays.</b> You each get two cards and take turns to hit or stand. You see your own hand, but of your opponent you only see the first card. The rest turns over when the hand is done.</li><li><b>Who wins.</b> Go over 21 and you lose on the spot. Otherwise the higher hand wins, a natural blackjack beats an ordinary 21, and a tie gives both stakes back.</li><li><b>No stalling.</b> You have 40 seconds per turn. If you do nothing, you stand.</li><li><b>Honest cards.</b> The deck is shuffled and dealt by the server, so nobody can peek or tamper with a hand. Stakes are locked when the game starts, and the winnings are paid out when it ends. If the game is ever interrupted, both stakes go back to their owners.</li><li><b>Who can play.</b> Both of you need a character and enough Scrip, and you have to be online at the same time.</li></ul>" }, { tag: "NEW", title: "📦 SUPPLY DROPS, AND A CLEANER SCREEN", text: "We took the ad banners off the sides of the screen. They were eating into your game area, and you came here to play, not to squint around a sidebar. The whole screen is yours now.<ul><li><b>So where did the ads go?</b> Into Supply Drops. Two crates now land on the Sector Map every day. Tap one, watch a short ad, and the crate is yours: scrap, water, ammo, electronics, tech data, or sometimes a medkit.</li><li><b>Your choice.</b> Nobody is forced to watch anything. If you skip the crates, you lose nothing but the loot.</li><li><b>Fresh spots every day.</b> The crates stay put for the day and turn up somewhere new tomorrow, and a crate you have claimed is gone until then.</li><li><b>A fair deal.</b> The reward only comes if the ad actually plays. If it gets blocked, the crate stays closed.</li></ul>Watching one now and then is how you help keep Silesia 2045 free and growing. Thank you." }, { tag: "NEW", title: "🔌 ELECTRONICS: A NEW RESOURCE", text: "Electronics now has its own place in the game.<ul><li><b>Everywhere you look.</b> It shows in the inventory, the sidebar and the base storage.</li><li><b>It has weight.</b> Each piece weighs 0.2, so it counts toward what you carry and what the storage holds.</li><li><b>Straight to base.</b> When you step into camp, your electronics move into the base storage on their own, like scrap, wood and cloth.</li></ul>" }, { tag: "IMPROVED", title: "🎙️ VOICEOVERS WAIT FOR YOU", text: "Chapter 1 no longer starts talking on its own.<ul><li><b>You press play.</b> Each voiceover sits behind a button and starts only when you click it.</li><li><b>A tidy screen.</b> Every time the story moves on, the previous text is cleared, so you never scroll through a long page to find the new lines.</li><li><b>Replay.</b> A Replay Voiceover button plays the line again.</li></ul>" }, { tag: "IMPROVED", title: "🔧 SMALL FIXES IN 0.5.7 (CAMP)", text: "<ul><li><b>Camp visitors.</b> A visitor at the gate can no longer break a story scene that starts on the Camp screen.</li></ul>" }, { tag: "IMPROVED", featured: true, title: "⚙️ LABOR OUTPUT REBALANCED", text: "Your work teams bring home real amounts now.<ul><li><b>Scrap Recovery Team.</b> Each survivor brings back 10 Scrap a day, and one time in three finds 5 to 15 more.</li><li><b>Water Purification.</b> 20 Water per survivor per day.</li><li><b>Bio-Hazard Medics.</b> 5 Medicine per survivor per day.</li><li><b>Munitions Excavators.</b> 10 Ammo per survivor per day. They now really deliver it every night.</li><li><b>Fishing Crew.</b> Each fisher has a 70% chance to land a Raw Fish and a 25% chance that the catch is double. They now really deliver it every night.</li><li><b>Field Kitchen.</b> Cooks now cook different amounts: 1 raw meat makes 4 Food, 1 raw fish makes 3 Food and 5 berries make 1 Food.</li></ul>" }, { tag: "NEW", featured: true, title: "🍳 FIELD KITCHEN: COOKS FEED THE CAMP", text: "Your camp has a new job card in the Labor terminal: the Field Kitchen.<ul><li><b>How it works.</b> Every survivor you put on the kitchen cooks one batch each night. A batch is 1 raw meat for 4 Food, 1 raw fish for 3 Food, or 5 berries for 1 Food.</li><li><b>What gets cooked.</b> The best recipe first: raw meat, then raw fish, then berries. Ingredients come from your Storage first, then from your pack.</li><li><b>Before the camp eats.</b> The cooking happens in the Morning Report ahead of the nightly meal, so the new Food is eaten the same night.</li><li><b>Cooks eat for free.</b> Survivors working in the kitchen eat from the pots, so they take no Food from your stock. They still drink water and count for firewood.</li><li><b>Stock on the card.</b> The card shows how much raw fish, meat and berries you have. If there is nothing to cook, the Morning Report tells you.</li><li><b>Everything else is unchanged.</b> The camp still eats Food first, then raw meat, then berries.</li></ul>" }, { tag: "NEW", featured: true, title: "📖 READING OPTIONS: EASIER TO READ FOR EVERYONE", text: "Reading should never be what stops you from playing. The game now asks once whether reading is hard for you, with dyslexia or just tired eyes in mind, and every change is yours to keep or drop.<ul><li><b>One-time question.</b> Every player sees it once on their device. Say yes and a recommended set switches on, with the full panel open so you can undo any part of it. Say no and nothing changes.</li><li><b>Font.</b> Default, Lexend or Atkinson Hyperlegible, both made to be easy to tell letters apart.</li><li><b>Text size.</b> 100%, 115%, 130% or 150%.</li><li><b>Spacing.</b> Wider space between letters and words, and between lines.</li><li><b>Text glow.</b> Turn the glow off for calmer text.</li><li><b>The Aa button.</b> It sits next to the EN and PL switch. Tap it any time to open the panel again.</li><li><b>Fixed screens.</b> Combat, hacking and the map keep their layout, so text size is not applied there.</li><li><b>On the website.</b> The landing site has the same question, panel and Aa button on every page.</li></ul>" }, { tag: "NEW", featured: true, title: "🔊 READ ALOUD: THE GAME READS TO YOU", text: "Turn on Read Aloud in the Reading Options and your own device reads the screen out loud, in English or Polish.<ul><li><b>Smart reading.</b> It skips ads, icons and decoration, reads the story first, then says \"Options\" and reads your choices.</li><li><b>Follow along.</b> The part being read is highlighted and scrolls into view.</li><li><b>Controls.</b> Pause, resume, previous, next and stop.</li><li><b>Three modes.</b> Off, a Read Aloud button, or Automatic, which starts reading every new screen by itself.</li><li><b>Speed and voice.</b> Slow, normal or fast, and a list of the voices on your device with a Try Voice button.</li><li><b>Pick your text.</b> Highlight any text and the button reads just that.</li><li><b>Good to know.</b> The voices come from your device, so Polish voices differ between phones and computers.</li></ul>" }, { tag: "IMPROVED", title: "🔧 SMALL FIXES IN 0.5.7", text: "<ul><li><b>Camp Morale panel.</b> It now sits under the camp scene, above the facility cards.</li><li><b>Time and weather.</b> The banner moved under the Sanctuary banner on the Camp screen.</li><li><b>Scrap Pit.</b> The red errors in the Stash Dismantle Bay are gone. Select all, tick several items and dismantle one by one work again.</li></ul>" }, { tag: "NEW", featured: true, title: "🏕️ CAMP LIFE: THE CAMP EATS, DRINKS AND BURNS", text: "Your camp is a living place now, and every night it has needs.<ul><li><b>Food and water.</b> Every survivor and every prisoner eats 1 food and drinks 1 water each night. Food falls back to raw meat, then berries.</li><li><b>Firewood.</b> The furnaces burn 1 wood for every 3 mouths to keep the camp warm. Storms and radiation storms double it, rain and fog add half, a heatwave halves it.</li><li><b>Where it comes from.</b> Your Storage first, then your pack.</li><li><b>Free nights.</b> The first nights of a new camp cost nothing. From day 4 the camp eats, drinks and burns every night.</li><li><b>Warnings.</b> The Morning Report tells you when tomorrow night will not be covered, and a new panel on the Camp screen shows what the camp needs.</li></ul>" }, { tag: "NEW", featured: true, title: "💬 CAMP MORALE: GOOD DEEDS AND BAD ONES", text: "The camp watches what you do.<ul><li><b>Raises morale.</b> Rescuing survivors, freeing captives, clearing enemy camps, repelling raids, sending aid and a fed, warm, cared-for camp.</li><li><b>Lowers it.</b> Losing a raid, a cold or hungry night, and selling, gifting or working survivors the Reptilians forced to fight.</li><li><b>Output.</b> High spirits add up to 10% to camp production. A broken camp loses up to 25%, and survivors may desert.</li><li><b>Prisoners.</b> Only survivors forced to fight cost morale. Bandits and Reptilians are fair game.</li></ul>" }, { tag: "NEW", title: "🤒 SICKNESS, BANDAGES AND DESERTION", text: "Life in the wasteland takes its toll.<ul><li><b>Illness.</b> Survivors can fall ill. One Medicine cures one, or 2 Medical Herbs if you have no Medicine. Water or wood shortages and low morale make it more likely.</li><li><b>Injuries.</b> Wall guards, hunters and ammo workers get hurt most often. A bandage costs 2 Cloth. Wounded survivors without bandages cost morale.</li><li><b>Sick and untreated.</b> A survivor left sick for two nights leaves so the others will not catch it.</li><li><b>Desertion.</b> Two days without food or water, or a camp with no morale left, and survivors walk out.</li><li><b>Output.</b> The more of your camp is sick or wounded, the less it produces.</li></ul>" }, { tag: "NEW", featured: true, title: "⛓️ THE LABOR PITS, REBUILT", text: "Grom's pit no longer looks like a menu. It is a place now.<ul><li><b>Cell block.</b> Every prisoner sits in a barred cell with an icon that shows who they are once Grom has questioned them.</li><li><b>The pit floor.</b> One pickaxe for every laborer working below.</li><li><b>Pit ledger.</b> Prisoners, laborers, daily scrip, questions asked, aid delivered and camp intel at a glance.</li><li><b>Tabs.</b> Processing, Talk, Quiet Aid and Upgrades each get their own tab, and Grom's current line stays on top with a replay button.</li></ul>" }, { tag: "NEW", featured: true, title: "🏕️ CAMP RAIDS: FIND THE HQ AND BURN IT DOWN", text: "What Grom pulls out of his prisoners now ends up on your Sector Map.<ul><li><b>Intel.</b> Questioning a bandit or a Reptilian gives camp intel. Enough of it marks an enemy camp HQ at a random spot on the map. The Interrogation Room doubles the intel.</li><li><b>Bandit camps.</b> 3 waves, ending in a Raider Warboss and his veterans.</li><li><b>Reptilian camps.</b> 4 waves and much harder. Scrappers, cleaver guards, elite butchers and finally a camp warden.</li><li><b>No resting between waves.</b> You can abort a raid, but the waves you cleared come back, and a defeat is a real defeat.</li><li><b>Spoils.</b> Scrip, scrap, supplies, reputation and XP. Reptilian camps pay far more. Grom hears about every camp you clear.</li><li><b>Gone for good.</b> A cleared camp disappears from the map. At most 3 bandit and 2 Reptilian camps wait on the map at once.</li></ul>" }, { tag: "NEW", title: "🔎 INTERROGATIONS: WHO IS IN YOUR CELLS?", text: "Grom no longer takes a prisoner at face value. He questions them first.<ul><li><b>Where they come from.</b> A prisoner remembers the group of the enemy you bound: Reptilian, bandit and raider, or a survivor forced to fight for the Reptilians.</li><li><b>Different questioning.</b> Each group gets its own interrogation report. Bandits turn out to be bandits, madmen or collaborators.</li><li><b>Nothing is locked.</b> Hand over, sell, gift and bulk delivery still work for every prisoner. The group changes what Grom says and how he says it.</li></ul>" }, { tag: "NEW", title: "💬 TALK TO GROM, AND HELP WHO HE HELPS", text: "Grom has a past, and he opens up to people he trusts.<ul><li><b>Talk.</b> Four topics unlock as your standing grows: Stranger, Contractor, Partner and Confidant. The first talk each day earns affinity.</li><li><b>Quiet Aid.</b> Bring medicine, food and herbs. Grom never says where they go, but the more you bring, the more he gives back.</li><li><b>Pit upgrades.</b> Reinforced Cells and the Medic Corner raise what every laborer produces. The Interrogation Room pays in loot and doubles camp intel.</li></ul>" }, { tag: "NEW", title: "🎙️ GROM FINDS HIS VOICE", text: "Every line Grom says in the Labor Pits now has its own voice-over, from his greeting to his verdict on a bandit. If you miss one, the replay button plays it again. The voice stops the moment you leave the pit." }, { tag: "IMPROVED", title: "🔧 SMALL FIXES IN 0.5.6", text: "<ul><li><b>What you can bind.</b> Only humanoid enemies can be captured with rope. Drones, turrets, animals and monsters cannot be bound, and neither can bosses.</li><li><b>Free binds.</b> The rare free rope-bind after a fight can no longer pick an enemy that cannot be captured.</li></ul>" }, { tag: "NEW", featured: true, title: "🛗 SANCTUARY DEPTHS: THE BASE GOES DOWN", text: "The base finally has a basement. The elevator in the Sanctuary Hub now has its own pin.<ul><li><b>Sanctuary Depths.</b> Ride the lift down to a new level of the base. The Archive Wing and Cipher's Server Room now live down there, each with its own pin.</li><li><b>A lighter Camp.</b> Those two cards are gone from the Camp screen, so the camp stops looking like a spreadsheet.</li><li><b>One upgrade button.</b> The duplicate upgrade panel in the Workshop is gone. Upgrades live on the Sanctuary Hub, where they belong.</li></ul>" }, { tag: "NEW", title: "💬 THE MESH NEVER SLEEPS", text: "The Sector Server Chat now follows you around the game.<ul><li><b>Online from login.</b> You show up on the ONLINE NOW list the moment you log in, not only after you open the chat.</li><li><b>DM alerts everywhere.</b> A pop-up appears on any screen when someone messages you, and a small pill stays on screen until you read it.</li><li><b>While you were offline.</b> Messages sent while you were away are waiting at your next login, with a summary of who wrote and how many times.</li><li><b>Delete a conversation.</b> Every DM in the sidebar has a red X, and an open DM has a delete button. Deleted chats stay deleted.</li></ul>" }, { tag: "IMPROVED", title: "🐺 CERBERUS TAKES THE K9'S PLACE", text: "Cerberus is the cybernetic form of your K9, so he no longer joins the squad as a second dog. Buy him and he takes the K9's slot.<ul><li><b>Shift button.</b> The Cerberus card now has a switch, like the ExoFrame skin: switch to the regular K9 and he takes the slot back, switch to Cerberus and he replaces the K9 again.</li></ul>" }, { tag: "NEW", featured: true, title: "⚔️ EVERY BOSS GETS A BATTLEFIELD", text: "Boss fights no longer all look the same. Each boss now fights in a place that fits who he is.<ul><li><b>20 boss battlefields.</b> Bebok, the weather bosses, the contract bosses and the dungeon bosses all have their own backdrop.</li><li><b>Base raids.</b> Raiders attacking the Sanctuary now fight at your gates instead of on a generic field.</li></ul>" }, { tag: "IMPROVED", title: "🛡️ FAIR PLAY: STAMINA AND TRUSTED SAVES", text: "<ul><li><b>Static stamina.</b> Stamina is 100 for everyone. Only stamina talents raise it, and levelling up no longer does.</li><li><b>Trusted saves.</b> Stamina, items, money, resources and levels are checked against your last trusted save. An impossible jump is undone.</li></ul>" }, { tag: "IMPROVED", title: "🔧 SMALL FIXES IN 0.5.5", text: "<ul><li><b>Greenhouse herbs.</b> Herbs from the greenhouse now reach your base storage. Herbs lost before are returned.</li><li><b>Storage numbers match.</b> Camp, Labor and the Sanctuary Hub now show the same weight and the same capacity.</li><li><b>PvP list.</b> The opponent list shows the same armor and weapon as the duel card.</li><li><b>Paper-doll.</b> The body in the Inventory fills the space from head to foot.</li><li><b>Token shop.</b> Buying with Syndicate Tokens no longer triggers the anti-cheat screen.</li><li><b>Bebok's battlefield.</b> His background loads again.</li></ul>" }, { tag: "NEW", featured: true, title: "🎙️ DR. MAYA FINDS HER VOICE: EVERY RELIC IS NARRATED", text: "Reading a translated relic in the Archive Wing is now something you can listen to.<ul><li><b>She speaks the second you open an entry.</b> Maya announces the translation in her own words before you read it, every single relic, Egypt to the Sovereign Council.</li><li><b>Historical Commentary on demand.</b> The commentary now sits behind its own button. Click it and the text opens while Maya reads it aloud to you. Click again to close it and stop her.</li><li><b>Nineteen entries, thirty-eight voice lines.</b> Each relic has its own opening and its own commentary, and nothing repeats.</li></ul>" }, { tag: "IMPROVED", title: "🤫 THE CHAT NPCS CALM DOWN", text: "Vex, Rust and Kilo were answering every single message. Now they answer when you ask. A question, a topic or their name gets an instant reply. Greetings and small talk only get one now and then, and players chatting with each other are left in peace." }, { tag: "NEW", featured: true, title: "⚔️ LIVE PVP: CHALLENGE ANYONE IN THE CHAT", text: "Ghost duels were a fight against a copy of someone. Now the someone can say yes.<ul><li><b>Challenge.</b> Click any survivor's name in the Sector Server Chat and press <i>Challenge to a Duel</i>. They must have the chat open.</li><li><b>Accept.</b> They get a pop-up and a chat message with <i>Accept Duel</i> and <i>Decline</i>. On the challenger's player card the button turns into <i>Accept Duel</i>. You both have 60 seconds, then the challenge is gone.</li><li><b>The fight.</b> The server takes both characters from their last saved game, so your gear on the server is what fights. Nothing is trusted from the screen. It plays out turn by turn like a Ghost Arena duel.</li><li><b>The result.</b> It is posted in <b>Sosnowiec (Warzone)</b> with a short story and a <i>View Battle</i> button. The button opens a small window with both fighters, the rating change and the full turn by turn log.</li><li><b>Share it once.</b> The winner can share the result in one sector channel of their choice. It can only be shared once, and never twice in different channels.</li></ul>" }, { tag: "NEW", title: "🏅 DUELS MOVE YOUR ARENA RANK", text: "Friendly duels count. Win and your Arena rating goes up, lose and it drops, with the same rating rules as the Ghost Arena. Both fighters also get the Arena scrip reward. Duels do not touch the Ghost Arena cooldowns or its daily limit. The Ghost Arena stays limited, because it can be farmed. Duels need two people to agree, so they have no daily cap. The same two survivors can only fight again after 5 minutes, so the channel stays readable." }, { tag: "NEW", title: "☠️ CHAPTER 1: THE PURGE", text: "When Cipher releases Shaft B-12, you have exactly <b>three steps south</b> to reach the exit before the tunnels purge everything. A pop-up tells you, and a counter stays on screen. Ignore the warning and take a fourth step, and you die. You get one respawn at camp. The second time is real permadeath. We are serious about permadeath in this game." }, { tag: "IMPROVED", title: "🔧 SMALL FIXES", text: "<ul><li>The Sector Server Chat sidebar now has an <b>ONLINE NOW</b> list. See who is on the mesh, open their card with a click, or hit the ⚔ button next to a name to challenge them to a duel (✔ accepts a duel they sent you).</li><li>The language switch now sits under the game title in the sidebar. While the sidebar is closed it floats at the top right, clear of the ad banner.</li><li>Cipher's three answers in the Terminal Interface conversation now match the buttons you press word for word.</li><li>Cipher's last transmission in Chapter 1 now tells you to go back to your camp and wait for her contact.</li></ul>" }, { tag: "NEW", featured: true, title: "🗝️ MAIN STORY: CHAPTER 1, MEET CIPHER", text: "Cipher's call finally leads somewhere. The Katowice Tunnels are now a dungeon you can explore at your own pace.<ul><li><b>Follow the story.</b> Climb down the access shaft, survive the Vanguard ambush on the switching platform, crack the sealed terminal at Bulkhead 01-A and hear what Cipher really has to say.</li><li><b>Free roam.</b> Nineteen rooms, side passages and dead ends. The map never changes, but what waits in each room is rolled again every time you go down. Come back as often as you want.</li><li><b>Reptilian patrols.</b> Vanguard teams, scrappers and enforcers can be behind any door, alongside lockers, first-aid boxes and traps.</li><li><b>Chapter 2 is coming.</b> At the end of the tunnels Cipher sends you home to wait for her transmission. What she has to say is a story for the next chapter.</li></ul>" }, { tag: "NEW", title: "🏆 EVERY BOSS GETS A VICTORY SCREEN", text: "Every named boss and mini-boss now ends with a victory card of its own, with its own colour, headline and closing line. Foreman Kruk, the Zmora, Lady Midday, High Overseer Sszakar-Aten and ten more. Winning a big fight finally feels like winning a big fight." }, { tag: "IMPROVED", title: "🌐 POLISH MODE NO LONGER BREAKS BUTTONS", text: "In Polish mode some buttons could stop working, like the Sanctuary Hub in Camp. Language switching no longer touches the game's inner workings, so every button goes where it should." }, { tag: "NEW", featured: true, title: "🖥️ LIVE HACKING: TERMINALS FIGHT BACK", text: "Hacking is no longer a button you press. Every terminal in the wasteland now runs a live mini-game on a full-screen green terminal. Three games rotate at random.<ul><li><b>Terminal Override.</b> Type the target crypto-sequence exactly before the timer runs out. A wrong key wipes your buffer and costs seconds.</li><li><b>Circuit Bypass.</b> Rotate the nodes to wire the power from the source to the output. Dead nodes block the way.</li><li><b>Signal Decryption.</b> Click only the symbols the prompt asks for. Decoys cost you time.</li><li><b>Netrunners hack better.</b> Every point in the Netrunner talent tree adds a quarter of a second to the clock (up to 3 seconds), and a hired hacker adds 3 more. Neural Shield adds a second per level. Techies and Broadcasters get a built-in time buffer in every game.</li><li><b>It costs Tech Data.</b> Every breach outside camp costs 1 Tech Data. Boss-related breaches cost 5. With none, you cannot start the hack. Camp terminals and the training lab stay as they were.</li><li><b>Hacking opens real doors.</b> The Comms Server decryptor, the Zmora diagnostic, the convoy vault, the Slaughterhouse terminal and the two archives inside the Ruined Military Checkpoint all run on it. At the checkpoint you can also hack the turret panel and skip the whole second wave.</li><li><b>Boss contracts.</b> Cut the comms on Collar-Master Szhaar with a live hack to fight him alone. Fail, and his whole compound comes running. Jacking into the Zmora's Mesh Hub is a hack too.</li><li><b>Train for free.</b> The Comms Server has a Hacking Training card with a three-lesson tutorial and free play with no cost and no penalty.</li></ul>" }, { tag: "IMPROVED", featured: true, title: "⛏ DUNGEONS & LIZARDS: THE ABANDONED COAL MINE, REBUILT", text: "Trader Kaelen's job at Level 4 now leads into a full-screen dungeon with a lamp-lit look, a survey map and two floors. The tunnels still change every time you go down. But humans are not the only enemy in this mine.<ul><li><b>Two floors.</b> Find the Foreman's Key in the Galleries, ride the lift down and face Foreman Kruk in the Lower Shaft.</li><li><b>Your lamp is your clock.</b> Every step burns lamp oil. When it runs out the map goes dark and every step costs extra Stamina. Find oil in chests, niches and workshops.</li><li><b>The Whisper.</b> Deep below, the seam talks. The longer you stay, the louder it gets, and when it breaks you it hurts.</li><li><b>Dungeons & Lizards.</b> Reptilian Diggers have tunnelled into the lower floor. Expect sentry spikes and drone-wire traps, and diggers fighting beside the whispering husks and burrowers.</li><li><b>Choices in the dark.</b> Seven story events with real choices: a shrine, a pinned miner, a hammering ghost, a flooded sump, a workshop, a rockfall and a control terminal you can hack.</li><li><b>Jarek's journal.</b> Six pages from Kaelen's lost brother are hidden in the tunnels. Find them all for a better lamp and a bigger reward from Kaelen.</li></ul>" }, { tag: "NEW", title: "🎬 A WHOLE NEW BATTLE SCREEN", text: "Every fight now plays out on one <b>widescreen stage</b>. You and your ally stand on the left, the enemy squad on the right, over the background of the place where the fight started. Your combat log, your target and your actions all sit on the same screen, and it <b>fits your window with no scrolling</b>, sidebar open or closed. Click an enemy portrait to switch targets. Prefer the old layout? A <b>CLASSIC VIEW</b> button is always one click away." }, { tag: "NEW", title: "📏 RANGE ZONES: FAR, MID, CLOSE", text: "Distance matters now. <b>Charge</b> to close in or <b>Pull Back</b> to open the gap, for 1 AP each.<ul><li><b>Ranged weapons</b> hit harder from far away and weaker up close.</li><li><b>Melee weapons</b> can only strike at close range, so get in there.</li><li><b>Melee enemies</b>, like wolves and butchers, run at you. They charge across the open ground, then lunge before they reach full strength.</li><li><b>Ranged enemies</b> keep shooting, but lose some punch when you stay far.</li></ul>" }, { tag: "NEW", title: "🛡️ SEND YOUR ALLY TO THE FRONT", text: "Your companion is no longer just a gun beside you. Spend 1 AP to <b>send them forward</b> and they step up toward the enemy line, drawing most of the fire while you shoot from behind. Or <b>recall them</b> to play support, and now you are the one taking the hits. You choose who tanks." }, { tag: "NEW", title: "🏟️ THE PVP ARENA", text: "A new card in Camp: the <b>PvP Arena</b>. Pick a survivor from the list and fight a <b>ghost duel</b> against their saved character, with the gear, talents and stats they actually carry. They do not need to be online. Wins and losses move you on the <b>Ladder</b>, so you can see exactly who rules the sector. This is the first step. Live duels are something we want to build next." }, { tag: "NEW", title: "🐗 BEBOK, THE EVENT BOSS", text: "A special event boss that rewards patience, not power. <b>Bebok</b> has <b>100,000 HP</b> but hits for only <b>1 to 2 damage</b>, and he is the same on every level. Take as long as you like. He cannot be bound with rope. Bring him down and he <b>always drops</b> the one and only <b>Godlike Bebok's Scrap Pack</b>, a backpack worth <b>1000 kg of carry weight</b>. It exists nowhere else and never drops from normal loot. Bored mid fight? You can leave whenever you want." }, { tag: "NEW", featured: true, title: "🕒 A DAY WITH SEVEN PARTS, AND WEATHER THAT BITES", text: "The day now moves: <b>early morning, morning, noon, afternoon, evening, early night and dead night</b>. Every trip from the Map takes time, and long ones like the Tunnels and the Slaughterhouse take two parts of the day. Go to sleep at camp and a new day starts with new weather. Stay out through <b>dead night</b> and you collapse outside, wake up in camp <b>injured</b> and lose part of the supplies you were carrying.<ul><li>☀️ <b>Clear</b>: ranged weapons +5%.</li><li>☁️ <b>Overcast</b>: fewer wanderers on the roads.</li><li>🌧️ <b>Rain</b>: your barrels fill with water, but open-air trips cost stamina.</li><li>⛈️ <b>Storm</b>: fishing and hunting close, every trip costs stamina, but the barrels overflow.</li><li>🌫️ <b>Fog</b>: many more ambushes, and ranged weapons lose accuracy.</li><li>🔥 <b>High Heat</b>: open-air trips drain extra thirst, and noon is the worst of it.</li></ul>" }, { tag: "NEW", title: "🌞 LADY MIDDAY, THE NOON-STALKER", text: "On a High Heat day, going out in the open at noon or in the afternoon with <b>thirst below 60%</b> can end badly. Your vision blurs, a pale figure shimmers on the road, and <b>Lady Midday</b> comes for you with a thermal sickle that cuts straight through armor. You can drink and clear your head, hide in the shade until dusk, or stand and fight. Watch her sickle: she raises it one round and strikes the next. Cover softens the blow. Indoor trips like bunkers and clinics are safe from her." }, { tag: "NEW", featured: true, title: "🗺️ OUR OWN MAP OF SILESIA", text: "The Sector Map is no longer a borrowed satellite view. It is a <b>hand-built survey map of Katowice, Chorzów and the Murckowski Deadwoods</b>, and every location now sits where it belongs in the real world: the clinic in Silesian Park, the Tunnels under Katowice station, the Warehouse in Janów, the Deadwoods south of the city. <ul><li><b>Drag</b> to pan, <b>scroll or pinch</b> to zoom, and press <b>⌖</b> to jump back to your base.</li><li>The corner readout shows the <b>real latitude and longitude</b> under your cursor.</li><li>The map follows the sky: it darkens at night and washes out in fog.</li><li>Look for the red <b>quarantine and exclusion zones</b> and the flooded Rawa river plain.</li></ul>" }, { tag: "NEW", featured: true, title: "⚔️ BUILD YOUR SQUAD: 4 COMPANIONS", text: "You are no longer fighting alone. Hire up to <b>four companions</b> at the Wasteland Bar and take them all into battle. <ul><li><b>You command every ally.</b> After your turn, each companion acts in order and you pick their skills.</li><li><b>Front line or back line.</b> Send any ally forward to draw fire, or pull them back to protect them. Enemies pick on the front first.</li><li><b>Five new mercenaries, one per Wasteland Bar level:</b> Mira the mechanic (Common), Tadeusz the miner (Uncommon), Lena the sniper (Rare), Viktor the riot-line tank (Epic) and Anya the blade (Legendary). Each has her or his own story and two unique skills.</li><li>Dismiss anyone at any time from the squad panel at the top of the Mercenary Roster.</li></ul>" }, { tag: "NEW", featured: true, title: "🧭 SQUAD STRATEGY: PLAN THE OPENING", text: "Plan your fights before they start. The new <b>Squad Strategy</b> screen at the Wasteland Bar (and in the Mercenary Roster) lets you set up your team at base. <ul><li><b>Starting position.</b> Put each ally on the <b>front line</b> in front of you, on the <b>back line</b> behind you, or in <b>reserve</b> to leave them at base. Every fight opens in exactly this formation.</li><li><b>Combat role.</b> <b>Vanguard</b> has more HP and draws all fire in round 1. <b>Striker</b> hits harder but is more fragile. <b>Guardian</b> covers you from the first round. <b>Standard</b> is balanced.</li><li><b>Turn order.</b> Decide who acts first after your turn.</li><li><b>Saved strategies.</b> Keep up to three setups and swap between them in one click, or let <b>Auto-arrange</b> build a sensible one.</li></ul>Example: a Syndicate Defector as Vanguard on the front line, a Field Medic behind you as Guardian. Enemies aim most attacks at your front line, so choose who takes the hits." }, { tag: "HARDCORE", featured: true, title: "☢️ THE UNKNOWN THREAT HUNTS IN THE RAD-STORM", text: "When a <b>Rad-Storm</b> rolls in, something walks with it. The <b>Unknown Threat</b> finds you <b>every time you leave camp</b>, wherever you were going (level 4 and up). Fleeing does not save you: it hunts you again on your next trip. It is <b>persistent</b>: beat it, or stay inside until the storm passes. It is a mini boss with its own rhythm: It is a mini boss with its own rhythm: <ul><li><b>Shrouded, rad pulse.</b> It takes half damage and hits you with radiation that ignores armor and cannot be dodged.</li><li><b>Exposed.</b> The shroud tears open and it takes <b>+40% damage</b>. This is your window to burst it down, or stun it.</li><li><b>Rad burst.</b> A huge radiation blast that also hits every ally, then the shroud closes again.</li></ul>Its radiation leaves you with <b>Radiation Contamination</b>: you deal <b>30% less damage</b> until it is treated. <ul><li><b>Decontamination Flush</b> at the Med Bay clears it completely for 35 Medicine and 15 Water.</li><li><b>Rad-meds:</b> 1 Medicine per part of the day halves the penalty, but the contamination stays. Turn on <b>auto-dose</b> to take them on every trip.</li><li><b>25 doses</b> in total will also clear it, slowly.</li></ul>Win and you take scrip, tech data and Medicine from the ashes." }, { tag: "FIXED", title: "🎯 ARM SHOT AND LEG SHOT NOW DO SOMETHING", text: "Called shots are finally worth their 2 AP. <ul><li><b>Arm Shot.</b> A hit cripples the enemy's aim: for 2 rounds its attacks <b>miss 50% of the time</b>.</li><li><b>Leg Shot.</b> A hit hobbles the target for 2 rounds: <b>melee enemies cannot close in</b> and everything it does hits <b>40% weaker</b>. A melee target is also <b>knocked back</b>, pushing the fight one range step farther away.</li></ul>" }, { tag: "NEW", featured: true, title: "💥 COMBAT COMES ALIVE: FLASHES, SLASHES AND HEALS", text: "Battles finally look like battles. In the new battle view every action now plays out on the portraits. <ul><li><b>Gunfire.</b> Muzzle flash, a tracer streak and an impact flash on the target. Misses pass wide with a <b>MISS</b> popup, and enemy misses against you show <b>DODGE</b>.</li><li><b>Melee and heavy hits.</b> Attackers lunge in, slash marks tear across the target, plasma flies as a glowing orb and grenades blast the whole enemy squad.</li><li><b>Healing.</b> Bandages, Stimpacks and your ally's field patches send green rings and rising sparkles over every healed portrait, with a floating <b>+HP</b> number.</li><li><b>Damage and kills.</b> Portraits shake, red damage numbers float up (crits are bigger), HP bars drain as the hit lands and fallen units burst before they go grey.</li></ul>Prefer it plain? Use the <b>FX ON/OFF</b> button next to CLASSIC VIEW. The setting is remembered, and effects switch off automatically if your device has reduced motion enabled." }, { tag: "FIXED", title: "🧱 NO MORE EMPTY GAPS IN YOUR BASE SCREENS", text: "The Defense Walls, Medical Infirmary, Workshop, Scrap Pit and Wasteland Bar screens had big blank gaps between the banner, the titles, the status panels and the cards. Those are gone, and the building cards now fill their rows evenly." }, { tag: "NEW", featured: true, title: "🏚️ A NEW FACE FOR YOUR CAMP", text: "The Camp screen has a new look. Step into the bunker corridor and you will see where everything lives: the <b>riveted blast door</b> to the Wasteland Gates, the <b>cold stairwell going down</b> to the Sanctuary Hub, the <b>Comm-Server</b> glowing at the far end and the <b>Living Quarters</b> with its kitchen and bunk bed. <ul><li><b>The Sanctuary Hub is deep underground.</b> It sits below the surface level of the vault, so you go down to build and upgrade.</li><li>Each destination has its own marker on the room it belongs to. Hover for a short description, click to go.</li><li>On a phone the scene scrolls sideways, and the markers stay on their rooms.</li></ul>" }, { tag: "NEW", title: "🗣️ KAELEN'S INTEL IS REAL NOW", text: "Every piece of advice Trader Kaelen (or the Wasteland Bar) sells now does something, and the screen tells you what. <ul><li><b>Rad-storm warning:</b> a <b>Rad-Storm</b> hits the next morning. Open-air trips cost 2 Medicine (radiation meds) or 20% max HP and stamina. Tunnels and Bunker are safe, fishing and hunting are closed.</li><li><b>Convoys, traps, barricades, labor, road offers, hunting and bosses:</b> each tip gives a short bonus, such as a free wall patch, 40% cheaper road offers, extra meat and wood, or 25% more damage to bosses.</li></ul>" }, { tag: "NEW", featured: true, title: "🧠 THE ZMORA: A SICKNESS IN THE CAMP", text: "Some mornings you will wake to bad news: <b>several defenders in the Living Quarters have woken up screaming, unable to breathe.</b> The Zmora Protocol, a rogue pre-war cyber-warfare signal from a buried mesh hub, has crawled into the camp's sub-networks. Survivors turn <b>Exhausted</b> or fall into <b>Neural Shock</b>, so your workers produce less and your wall defenders cannot hold a raid.<ul><li><b>Diagnose it.</b> Open the diagnostic terminal. You need to be a skilled <b>Netrunner</b> (6+ points in that talent tree) or work the terminal <b>with Cipher</b>. A failed attempt gives you a Neural Shock.</li><li><b>Find the source.</b> A good diagnosis quarantines the spread and marks the <b>Buried Mesh Hub</b> on your Sector Map.</li><li><b>Destroy it.</b> Netrunners can jack in and sever the broadcast. Everyone else fights through the Zmora and its sleepwalkers. Beware: it can <b>pin you to the ground</b> mid-fight.</li><li><b>Reward:</b> the <b>Neural Shield</b> cyberware (up to Mk III) that improves your hacking odds, cuts future outbreaks and protects you from neural attacks.</li></ul>" }, { tag: "NEW", title: "💧 BOSS CONTRACT: UTOPEK-7", text: "A new contract from the Shady Informant. In the flooded swamps of the Silesian Park lives <b>Utopek-7</b>, a mutated amphibian in a red miner's helmet, guarding an old <b>Water Purification Pump</b>. You choose how this ends. <b>Trade and feed him</b> filter cloth and rations and he dredges up rare loot from the swamp. Or <b>take the water</b> by force: he spits armor-ignoring bio-acid and can drag you under to drown you, until you break free. Win, and the pump is yours, adding clean water to your base every single day." }, { tag: "IMPROVED", title: "📡 GLOBAL BROADCAST PASSES, EXPLAINED", text: "Nobody could tell you what a Global Broadcast Pass was, so now the survivors can. Ask around in Sector Chat and the NPCs will explain it, and the Broadcast window and its sidebar tooltip now say what a pass does and how you earn one." }, { tag: "FIXED", title: "⚙️ POLISH AND FIXES", text: "<ul><li>The <b>Fallen Military Convoy</b> no longer skips its first wave. The fight now runs Rogue Veterans, then Convoy Scavengers, then a <b>LOOT THE CONVOY</b> button, then back to Camp.</li><li>The <b>Ruined Military Checkpoint</b> no longer repeats a wave or throws you back to Camp. After the last wave you can enter the complex.</li><li>The <b>Reptilian Warlord</b> contract no longer pays its reward twice.</li><li><b>Utopek-7</b> now really spits bio-acid and drags you under, as its contract always promised. Expect a harder fight.</li><li>The <b>metro tunnels</b> water rumor now reads exactly like Kaelen's voice line, ending in <i>it's your funeral anyway</i>.</li></ul>" } ]>> <<set _planned to [ { icon: "⛓️", status: "IN DEVELOPMENT", title: "GROM'S QUESTLINE", text: "Grom sells only the people who hurt other people, and he has been quietly feeding the survivors nobody else counts. Orders, captures and a secret network are coming, and so is the truth behind the Slaughterhouse logs." }, { icon: "⚡", status: "NOW LIVE", title: "LIVE PVP DUELS", text: "Duels between two survivors who are both online arrived in 0.5.3 as chat challenges. Real-time, move by move fights are still the next step for the Arena." }, { icon: "👥", status: "IN DESIGN", title: "ALLY STORY MISSIONS", text: "Every ally has a past, a debt and a reason to be here. Earn enough trust and they will ask you for something no one else could ever do." }, { icon: "🗝️", status: "IN DEVELOPMENT", title: "THE MAIN STORYLINE", text: "Chapter 1 is done. Cipher will contact you at your camp, and what she has to say is the next chapter." }, { icon: "🧿", status: "ONGOING", title: "THE IOKHARIC SECRET", text: "Dr. Maya has only scratched the surface of what the relics are hiding. The deeper she reads, the less comfortable the truth becomes." }, { icon: "⚔️", status: "WE ARE LISTENING", title: "YOUR FIGHT, YOUR FEEDBACK", text: "The new battle screen is brand new, so tell us in the chat what feels great, what feels clumsy and what you want next. We read all of it." } ]>> <<set _devNote to "Version 0.5.7 is about the people in your camp. They eat, they drink, they freeze, they fall ill and they judge you for what you do, so a wasteland camp is finally a thing you have to keep alive and not only a place to sleep. Version 0.5.6 belongs to Grom. The Labor Pits are rebuilt, every prisoner is questioned before anything else happens to them, and what the Pit Master pulls out of bandits and Reptilians now puts their camps on your map for you to burn. Version 0.5.5 takes the base down a floor. The Sanctuary Depths hold the Archive Wing and Cipher's Server Room, the bosses finally fight on ground that fits them, and the chat now reaches you wherever you are in the game, even after you log off. Version 0.5.4 gives the archive a voice. Dr. Maya now reads her translations and her commentary out loud, one voice line per relic, so the oldest history in the game finally has someone telling it to you. Version 0.5.3 puts survivors in the ring with each other. Ghost duels were always a fight against a copy of someone, and now the someone can say yes. Pick a fight, accept a fight, win the rank, tell the Warzone about it. Version 0.5.2 opens the main story. Chapter 1 is a dungeon you can walk through at your own pace, and a voice in the wire has just told you that your boots were never your own. The coal mine, the new map, the widescreen fights and the seven-part day are all still here, now with a reason to go deeper. Version 0.5.1 gives the world a real shape on top of everything 0.5.0 changed: how a fight feels and how a day feels. It also gives you a real home to come back to, a new Camp with the Sanctuary Hub deep below, and Kaelen's advice now pays off if you listen, so keep your radiation meds close. Where you stand, who takes the hit, what hour it is, what the sky is doing and what is crawling through your camp at night now matter as much as your gear. Thank you to every tester who broke something and told us. Stay sharp, Operator.">> <</silently>> <<nobr>> <style> #ui-dialog-body { padding: 0 !important; background-color: #070a12 !important; } #ui-dialog-close { color: #ffffff !important; background: #ef4444 !important; border: 2px solid #b91c1c !important; border-radius: 4px !important; width: 32px !important; height: 32px !important; opacity: 1 !important; box-shadow: 0 0 10px rgba(239, 68, 68, 0.6) !important; } #ui-dialog-close:hover { background: #dc2626 !important; box-shadow: 0 0 15px rgba(220, 38, 38, 0.9) !important; } #ui-dialog-close::before { content: none !important; } #ui-dialog-close::after { content: "X" !important; font-family: Arial, sans-serif !important; font-size: 1.1rem !important; font-weight: bold !important; position: absolute !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; } .np-wrap { background: radial-gradient(ellipse at top, rgba(30, 41, 59, 0.55), rgba(7, 10, 18, 1) 60%), #070a12; border: 1px solid #1e293b; color: #cbd5e1; font-family: 'Segoe UI', Tahoma, sans-serif; width: 100%; max-height: 78vh; overflow-y: auto; box-sizing: border-box; padding: 0 0 25px 0; box-shadow: 0 0 40px rgba(0, 0, 0, 0.8); } .np-masthead { position: relative; text-align: center; padding: 38px 20px 28px; background: linear-gradient(180deg, rgba(127, 29, 29, 0.35), rgba(7, 10, 18, 0) 90%); border-bottom: 1px solid #7f1d1d; overflow: hidden; } .np-masthead::before { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 3px; background: linear-gradient(90deg, transparent, #ef4444, transparent); } .np-kicker { font-family: 'Courier New', monospace; font-size: 0.75rem; letter-spacing: 6px; color: #94a3b8; margin-bottom: 10px; } .np-title { font-size: 3rem; font-weight: 900; color: #f8fafc; letter-spacing: 6px; margin: 0; text-transform: uppercase; text-shadow: 0 0 22px rgba(239, 68, 68, 0.55), 0 2px 0 #000; } .np-issue { display: inline-block; margin-top: 14px; padding: 6px 16px; border: 1px solid #ef4444; color: #fecaca; background: rgba(127, 29, 29, 0.25); font-family: 'Courier New', monospace; font-size: 0.8rem; letter-spacing: 3px; border-radius: 2px; } .np-body { padding: 24px 30px 0; } .np-alert { border-left: 4px solid #ef4444; background: rgba(127, 29, 29, 0.18); padding: 14px 18px; margin-bottom: 22px; font-size: 0.95rem; line-height: 1.6; color: #fecaca; } .np-alert b { color: #fff; letter-spacing: 2px; font-family: 'Courier New', monospace; } .np-stats { display: flex; gap: 12px; margin-bottom: 26px; flex-wrap: wrap; } .np-stat { flex: 1 1 120px; text-align: center; padding: 14px 8px; border: 1px solid #1e293b; background: rgba(15, 23, 42, 0.7); border-top: 2px solid #38bdf8; } .np-stat-n { font-size: 1.9rem; font-weight: 900; color: #f8fafc; letter-spacing: 2px; font-family: 'Courier New', monospace; text-shadow: 0 0 14px rgba(56, 189, 248, 0.45); } .np-stat-l { font-size: 0.65rem; letter-spacing: 2px; color: #94a3b8; margin-top: 4px; font-family: 'Courier New', monospace; } .np-section { margin-bottom: 18px; padding: 16px 20px; background: rgba(15, 23, 42, 0.55); border: 1px solid #1e293b; border-left: 4px solid #334155; border-radius: 3px; } .np-section.np-is-new { border-left-color: #34d399; } .np-section.np-is-improved { border-left-color: #38bdf8; } .np-section.np-is-fixed { border-left-color: #fbbf24; } .np-section.np-is-hardcore { border-left-color: #ef4444; } .np-section-title { font-size: 1.15rem; font-weight: 800; color: #f1f5f9; margin-bottom: 10px; letter-spacing: 1px; text-transform: uppercase; } .np-section-text { font-size: 0.98rem; line-height: 1.7; color: #aab6c8; } .np-section-text b { color: #f1f5f9; } .np-section-text ul { margin: 8px 0 0 20px; padding: 0; } .np-section-text li { margin-bottom: 8px; color: #cbd5e1; } .np-featured { padding: 26px 26px; background: linear-gradient(135deg, rgba(127, 29, 29, 0.35), rgba(15, 23, 42, 0.7)); border: 1px solid #ef4444; border-left: 6px solid #ef4444; box-shadow: 0 0 30px rgba(239, 68, 68, 0.2); } .np-featured .np-section-title { font-size: 1.8rem; color: #fff; text-shadow: 0 0 14px rgba(239, 68, 68, 0.5); } .np-featured .np-section-text { font-size: 1.05rem; } .np-tag { display: inline-block; font-size: 0.62rem; font-weight: bold; letter-spacing: 2px; padding: 3px 8px; margin-right: 10px; border-radius: 2px; vertical-align: middle; font-family: 'Courier New', monospace; background: transparent; } .np-tag-new { color: #34d399; border: 1px solid #34d399; } .np-tag-improved { color: #38bdf8; border: 1px solid #38bdf8; } .np-tag-fixed { color: #fbbf24; border: 1px solid #fbbf24; } .np-tag-hardcore { color: #fecaca; border: 1px solid #ef4444; background: rgba(239, 68, 68, 0.2); } .np-h2 { font-family: 'Courier New', monospace; font-size: 1.05rem; letter-spacing: 5px; color: #e2e8f0; text-align: center; margin: 34px 0 16px; padding-bottom: 10px; border-bottom: 1px solid #334155; } .np-h2 span { color: #a78bfa; } .np-plan { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; } .np-plan-card { position: relative; padding: 18px; background: linear-gradient(160deg, rgba(76, 29, 149, 0.22), rgba(15, 23, 42, 0.8)); border: 1px solid #5b21b6; border-radius: 4px; } .np-plan-icon { font-size: 1.8rem; margin-bottom: 6px; } .np-plan-title { font-size: 1rem; font-weight: 800; color: #f1f5f9; letter-spacing: 2px; margin-bottom: 8px; } .np-plan-text { font-size: 0.9rem; line-height: 1.6; color: #b4bfd1; } .np-plan-status { position: absolute; top: 10px; right: 10px; font-family: 'Courier New', monospace; font-size: 0.58rem; letter-spacing: 1px; color: #ddd6fe; border: 1px solid #7c3aed; padding: 2px 6px; border-radius: 2px; background: rgba(76, 29, 149, 0.4); } .np-note { margin-top: 22px; padding: 18px 22px; border: 1px solid #334155; background: rgba(15, 23, 42, 0.6); font-style: italic; color: #cbd5e1; line-height: 1.7; border-left: 4px solid #a78bfa; } .np-note div { margin-top: 10px; font-style: normal; font-family: 'Courier New', monospace; font-size: 0.75rem; letter-spacing: 3px; color: #94a3b8; } .np-footer { text-align: center; padding: 20px 20px 0; margin-top: 22px; border-top: 1px solid #334155; color: #64748b; font-family: 'Courier New', monospace; font-size: 0.75rem; letter-spacing: 3px; } @media (max-width: 700px) { .np-title { font-size: 1.8rem; letter-spacing: 3px; } .np-body { padding: 16px 14px 0; } .np-plan { grid-template-columns: 1fr; } .np-featured .np-section-title { font-size: 1.35rem; } .np-stat-n { font-size: 1.5rem; } } </style> <div class="np-wrap"> <div class="np-masthead"> <div class="np-kicker">NAKMAN STUDIOS PRESENTS</div> <div class="np-title">SILESIA CHRONICLES</div> <div class="np-issue"><<print _newsIssue>></div> </div> <div class="np-body"> <<if _systemAlert neq "">><div class="np-alert"><b>BREAKING //</b> <<print _systemAlert>></div><</if>> <div class="np-stats"> <<for _s to 0; _s lt _newsStats.length; _s++>> <div class="np-stat"><div class="np-stat-n"><<print _newsStats[_s].n>></div><div class="np-stat-l"><<print _newsStats[_s].l>></div></div> <</for>> </div> <<for _i to 0; _i lt _newsSections.length; _i++>> <div @class="'np-section np-is-' + _newsSections[_i].tag.toLowerCase() + (_newsSections[_i].featured ? ' np-featured' : '')"> <<if _newsSections[_i].img>><img style="width:100%; max-height:240px; object-fit:cover; border:1px solid #334155; margin-bottom:12px;" @src="_newsSections[_i].img" onerror="this.style.display='none'"><</if>> <div class="np-section-title"><span @class="'np-tag np-tag-' + _newsSections[_i].tag.toLowerCase()"><<print _newsSections[_i].tag>></span><<print _newsSections[_i].title>></div> <div class="np-section-text"><<print _newsSections[_i].text>></div> </div> <</for>> <div class="np-h2">WHAT ARE WE <span>PLANNING?</span></div> <div class="np-plan"> <<for _p to 0; _p lt _planned.length; _p++>> <div class="np-plan-card"> <div class="np-plan-status"><<print _planned[_p].status>></div> <div class="np-plan-icon"><<print _planned[_p].icon>></div> <div class="np-plan-title"><<print _planned[_p].title>></div> <div class="np-plan-text"><<print _planned[_p].text>></div> </div> <</for>> </div> <div class="np-note">"<<print _devNote>>"<div>// NAKMAN STUDIOS</div></div> <div class="np-footer">SILESIA CHRONICLES EDITORIAL BOARD // STAY ALIVE. STAY SHARP.</div> </div> </div> <</nobr>>
<<nobr>> <div style="max-width:560px; margin:30px auto; padding:24px; background:#050505; border:2px solid #34d399; border-radius:6px; font-family:'Courier New', monospace; color:#e2e8f0;"> <h2 style="text-align:center; color:#6ee7b7; letter-spacing:3px; margin-top:0;">[ PVP EVENT: RESET DUELS ]</h2> <p style="color:#cbd5e1; font-size:0.85rem; text-align:center;">Owner only. Gives EVERY player a fresh "Duels Left Today" and clears all duel cooldowns (Arena and chat duels). Ratings, wins and losses are not touched.</p> <button type="button" onclick="SugarCube.setup.adminResetDuels()" style="width:100%; padding:12px; margin:8px 0 14px; background:#064e3b; color:#d1fae5; border:1px solid #34d399; cursor:pointer; font-family:inherit; font-weight:bold; letter-spacing:1px;">RESET EVERYONE'S DUELS AND COOLDOWNS</button> <div id="rd-out" style="min-height:48px; white-space:pre-wrap; font-size:0.85rem; padding:10px; border:1px dashed #334155; color:#cbd5e1;">Waiting for input.</div> <div style="text-align:center; margin-top:18px;"><<link "RETURN" `previous()`>><</link>></div> </div> <</nobr>>
<<nobr>> <div style="max-width:560px; margin:30px auto; padding:24px; background:#050505; border:2px solid #f87171; border-radius:6px; font-family:'Courier New', monospace; color:#e2e8f0;"> <h2 style="text-align:center; color:#fca5a5; letter-spacing:3px; margin-top:0;">[ DELETE ACCOUNT / CHARACTER ]</h2> <p style="color:#cbd5e1; font-size:0.85rem; text-align:center;">Owner only. Nothing here can be undone, so look the account up first.</p> <label style="color:#fecaca; font-size:0.8rem;">PLAYER ID</label> <input id="xd-target" type="text" autocomplete="off" style="width:100%; box-sizing:border-box; margin:4px 0 10px; padding:10px; background:#0f172a; color:#f1f5f9; border:1px solid #475569; font-family:inherit;"> <button type="button" onclick="SugarCube.setup.adminDelete('look')" style="width:100%; padding:10px; margin-bottom:14px; background:#1e293b; color:#e2e8f0; border:1px solid #94a3b8; cursor:pointer; font-family:inherit; font-weight:bold;">LOOK UP ACCOUNT</button> <label style="color:#fecaca; font-size:0.8rem;">CONFIRM: TYPE THE SAME ID AGAIN</label> <input id="xd-confirm" type="text" autocomplete="off" style="width:100%; box-sizing:border-box; margin:4px 0 14px; padding:10px; background:#0f172a; color:#f1f5f9; border:1px solid #f87171; font-family:inherit;"> <div style="display:flex; gap:8px; margin:0 0 14px; flex-wrap:wrap;"> <button type="button" onclick="SugarCube.setup.adminDelete('character')" style="flex:1 1 200px; padding:10px; background:#422006; color:#fef3c7; border:1px solid #fbbf24; cursor:pointer; font-family:inherit; font-weight:bold;">RESET CHARACTER (KEEP ACCOUNT)</button> <button type="button" onclick="SugarCube.setup.adminDelete('account')" style="flex:1 1 200px; padding:10px; background:#450a0a; color:#fee2e2; border:1px solid #f87171; cursor:pointer; font-family:inherit; font-weight:bold;">DELETE WHOLE ACCOUNT</button> </div> <div id="xd-out" style="min-height:48px; white-space:pre-wrap; font-size:0.85rem; padding:10px; border:1px dashed #334155; color:#cbd5e1;">Waiting for input.</div> <div style="text-align:center; margin-top:18px;"><<link "RETURN" `previous()`>><</link>></div> </div> <</nobr>>
<div style="padding:30px; background:#050505; color:#00ff66; border:2px solid #ef4444; font-family:'Courier New', monospace; box-shadow: 0 0 30px rgba(239,68,68,0.3);"> <h2 style="color:#ef4444; text-align:center; letter-spacing: 3px; border-bottom: 1px dashed #ef4444; padding-bottom: 15px;">[ ROOT ACCESS: DEV PANEL ]</h2> <p style="text-align:center; color:#ccc;">Welcome, Creator. Override the system variables below.</p> <div style="margin-top: 30px; display:flex; flex-direction:column; gap:15px;"> <<link "<div style='padding:15px; background:rgba(239,68,68,0.1); border:1px solid #ef4444; text-align:center;'>💰 +99999 Premium Tokens (Bypass Anti-Cheat)</div>">> <<set $syndicateTokens += 99999>> <<set $token_checksum to ($syndicateTokens * 73) + 1492>> <<goto `passage()`>> <</link>> <<link "<div style='padding:15px; background:rgba(16,185,129,0.1); border:1px solid #10b981; text-align:center;'>📦 Max All Resources (9999), Level 60 & 800 Neural Points</div>">> <<set $materials.scrap to 9999, $materials.wood to 9999,$materials.cloth to 9999, $materials.iron to 9999,$materials.medicine to 999, $materials.food to 999,$materials.water to 999, $materials.ammo to 9999,$materials.techData to 999>> <<set $level to setup.LEVEL_CAP,$skillPoints to 800>> <<goto `passage()`>> <</link>> <<link "<div style='padding:15px; background:rgba(59,130,246,0.1); border:1px solid #3b82f6; text-align:center;'>❤️ Restore Max HP & Stamina</div>">> <<set $hp to$maxHp, $stamina to$maxStamina>> <<goto `passage()`>> <</link>> </div> <div style="margin-top: 40px; text-align:center;"> <<link "RETURN TO SECTOR" `previous()`>><</link>> </div> </div>
<<nobr>> <div style="max-width:1000px; margin:20px auto; padding:18px; background:#050505; border:2px solid #38bdf8; border-radius:6px; font-family:'Courier New', monospace; color:#e2e8f0;"> <h2 style="text-align:center; color:#38bdf8; letter-spacing:3px; margin-top:0;">[ DEV CONSOLE ]</h2> <p style="color:#94a3b8; font-size:0.82rem; text-align:center; margin-top:0;">Every account with its character and bag. Pick a player to open them and give resources or items. Gifts are applied by the server at the player's next save or login and are never treated as cheating.</p> <div style="display:flex; gap:8px; margin-bottom:8px; flex-wrap:wrap;"> <input id="dc-q" type="text" autocomplete="off" placeholder="search by character name or username" style="flex:1; min-width:220px; padding:8px; background:#0f172a; color:#f1f5f9; border:1px solid #475569; font-family:inherit;"> <button type="button" id="dc-reload" style="padding:8px 14px; background:#1e293b; color:#e2e8f0; border:1px solid #94a3b8; cursor:pointer; font-family:inherit; font-weight:700;">RELOAD</button> </div> <div style="margin-bottom:10px;"><button type="button" id="dc-talreset" style="padding:8px 14px; background:#450a0a; color:#fee2e2; border:1px solid #f87171; cursor:pointer; font-family:inherit; font-weight:700;">⚠ RESET EVERYONE'S TALENTS (POINTS REFUNDED)</button> <button type="button" id="dc-rmadmin" style="padding:8px 14px; background:#450a0a; color:#fee2e2; border:1px solid #f87171; cursor:pointer; font-family:inherit; font-weight:700;">⚠ REMOVE ADMIN FLAG FROM EVERYONE ELSE</button></div> <div id="dc-count" style="color:#94a3b8; font-size:0.78rem; margin-bottom:4px;"></div> <div id="dc-list" style="max-height:300px; overflow:auto; border:1px solid #334155; background:#0b1220; margin-bottom:12px;"></div> <div id="dc-out" style="min-height:24px; white-space:pre-wrap; font-size:0.85rem; padding:8px; border:1px dashed #334155; color:#94a3b8; margin-bottom:12px;">Waiting for input.</div> <div id="dc-detail"></div> <div style="text-align:center; margin-top:14px;"><<link "RETURN" `previous()`>><</link>></div> </div> <</nobr>> <<done>><<run setup.adminConsole.init()>><</done>>
<<nobr>> <div style="max-width:560px; margin:30px auto; padding:24px; background:#050505; border:2px solid #38bdf8; border-radius:6px; font-family:'Courier New', monospace; color:#e2e8f0;"> <h2 style="text-align:center; color:#38bdf8; letter-spacing:3px; margin-top:0;">[ GRANT: TOKENS & VIP ]</h2> <p style="color:#94a3b8; font-size:0.85rem; text-align:center;">For manual PayPal payments. Grants are applied by the server and are never treated as cheating.</p> <label style="color:#7dd3fc; font-size:0.8rem;">PLAYER ID</label> <input id="ag-target" type="text" autocomplete="off" style="width:100%; box-sizing:border-box; margin:4px 0 12px; padding:10px; background:#0f172a; color:#f1f5f9; border:1px solid #475569; font-family:inherit;"> <label style="color:#7dd3fc; font-size:0.8rem;">NOTE (optional, e.g. PayPal name or date)</label> <input id="ag-note" type="text" maxlength="120" autocomplete="off" style="width:100%; box-sizing:border-box; margin:4px 0 12px; padding:10px; background:#0f172a; color:#f1f5f9; border:1px solid #475569; font-family:inherit;"> <label style="color:#7dd3fc; font-size:0.8rem;">SYNDICATE TOKENS</label> <div style="display:flex; gap:8px; margin:4px 0 14px;"> <input id="ag-amount" type="number" step="1" placeholder="e.g. 500" style="flex:1; padding:10px; background:#0f172a; color:#f1f5f9; border:1px solid #475569; font-family:inherit;"> <button type="button" onclick="SugarCube.setup.adminGrant('tokens')" style="padding:10px 16px; background:#0c4a6e; color:#e0f2fe; border:1px solid #38bdf8; cursor:pointer; font-family:inherit; font-weight:bold;">GIVE TOKENS</button> </div> <label style="color:#7dd3fc; font-size:0.8rem;">VIP STATUS</label> <div style="display:flex; gap:8px; margin:4px 0 14px;"> <button type="button" onclick="SugarCube.setup.adminGrant('vipOn')" style="flex:1; padding:10px; background:#14532d; color:#dcfce7; border:1px solid #4ade80; cursor:pointer; font-family:inherit; font-weight:bold;">GIVE VIP</button> <button type="button" onclick="SugarCube.setup.adminGrant('vipOff')" style="flex:1; padding:10px; background:#450a0a; color:#fee2e2; border:1px solid #f87171; cursor:pointer; font-family:inherit; font-weight:bold;">REMOVE VIP</button> <button type="button" onclick="SugarCube.setup.adminGrant('look')" style="flex:1; padding:10px; background:#1e293b; color:#e2e8f0; border:1px solid #94a3b8; cursor:pointer; font-family:inherit; font-weight:bold;">LOOK UP</button> </div> <div id="ag-out" style="min-height:48px; white-space:pre-wrap; font-size:0.85rem; padding:10px; border:1px dashed #334155; color:#94a3b8;">Waiting for input.</div> <div style="text-align:center; margin-top:18px;"><<link "RETURN" `previous()`>><</link>></div> </div> <</nobr>>
<<nobr>> <<setMenu "Greenhouse">> <<if ndef $sanctuaryLevels>><<set $sanctuaryLevels to { greenhouse: 1 }>><</if>> <<if ndef $sanctuaryLevels.greenhouse>><<set $sanctuaryLevels.greenhouse to 1>><</if>> <<if ndef $greenhouseActive>><<set $greenhouseActive to false>><</if>> <style> body { background-color: #050a05 !important; } #story, #passages, .passage { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; background: transparent !important; } .av-world { position: absolute; top: 10px; left: 17.5em; right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 0 20px 20px 20px; width: calc(100% - 17.5em); } .greenhouse-container { background-color: #064e3b; padding: 30px; border: 2px solid #10b981; border-radius: 8px; font-family: 'Segoe UI', Tahoma, sans-serif; box-shadow: 0 0 25px rgba(16, 185, 129, 0.2); box-sizing: border-box; width: 100%; } .greenhouse-grid { display: flex; flex-wrap: wrap; gap: 20px; justify-content: center; margin-top: 25px; margin-bottom: 20px; } .greenhouse-card { background: rgba(0, 0, 0, 0.75); border: 1px solid #059669; border-radius: 6px; flex: 1 1 310px; min-width: 0; max-width: 640px; min-height: 290px; overflow: hidden; display: flex; flex-direction: column; justify-content: space-between; box-shadow: 0 4px 12px rgba(0,0,0,0.6); box-sizing: border-box; } .greenhouse-img { height: 110px; width: 100%; object-fit: cover; border-bottom: 2px solid #059669; background: #000; } .main-banner-img { height: 140px; width: 100%; object-fit: cover; border: 1px solid #10b981; border-radius: 6px; margin-bottom: 20px; background: #000; box-shadow: 0 0 15px rgba(16, 185, 129, 0.3); box-sizing: border-box; } .section-title { color: #6ee7b7; font-size: 0.95rem; font-family: 'Courier New', monospace; letter-spacing: 2px; margin: 30px 0 15px 0; text-transform: uppercase; } .cyber-green-btn { display: block; width: 100%; background: linear-gradient(135deg, #059669 0%, #047857 100%); border: 1px solid #34d399; color: #ecfdf5 !important; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.8rem; letter-spacing: 1px; border-radius: 4px; cursor: pointer; text-decoration: none !important; box-sizing: border-box; box-shadow: 0 0 10px rgba(16, 185, 129, 0.3); transition: all 0.2s; } .cyber-green-btn:hover { background: linear-gradient(135deg, #047857 100%, #059669 0%); border-color: #6ee7b7; box-shadow: 0 0 15px rgba(16, 185, 129, 0.6); } .cyber-disabled-btn { display: block; width: 100%; background: rgba(15, 23, 42, 0.6); border: 1px dashed #475569; color: #64748b; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem; border-radius: 4px; box-sizing: border-box; } .cyber-return-btn { display: inline-block; background: linear-gradient(135deg, #059669 0%, #064e3b 100%); border: 1px solid #34d399; color: #ecfdf5 !important; padding: 12px 30px; font-weight: bold; font-family: 'Courier New', monospace; letter-spacing: 2px; border-radius: 4px; text-decoration: none !important; box-shadow: 0 0 15px rgba(16, 185, 129, 0.3); transition: all 0.2s; } .cyber-return-btn:hover { background: linear-gradient(135deg, #064e3b 100%, #059669 0%); border-color: #6ee7b7; box-shadow: 0 0 20px rgba(16, 185, 129, 0.5); } </style> <div class="av-world"> <div class="greenhouse-container"> <div style="text-align: center; border-bottom: 1px dashed #10b981; padding-bottom: 15px; margin-bottom: 20px;"> <h2 style="color: #34d399; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px #10b981; font-size: 1.8rem;">[ HYDRO-GREENHOUSE ]</h2> <div style="margin-top: 8px; font-size: 0.9rem; color: #a7f3d0;">FACILITY LEVEL: <<print $sanctuaryLevels.greenhouse>> | SYSTEM: <<if $greenhouseActive>>ONLINE<<else>>STANDBY<</if>></div> </div> <img src="images/locations/greenhouse_main.jpg" class="main-banner-img" onerror="this.src='https://placehold.co/800x200/000/10b981?text=HYDRO-GREENHOUSE+FACILITY';"> <<if not $greenhouseActive>> <div style="border: 1px dashed #34d399; background: rgba(16, 185, 129, 0.1); padding: 20px; border-radius: 6px; text-align: center; margin-bottom: 25px;"> <div style="color: #34d399; font-weight: bold; font-size: 1rem; margin-bottom: 8px;">⚙️ HYDRO-PUMP & AUTOMATION SETUP REQUIRED</div> <div style="color: #cbd5e1; font-size: 0.85rem; margin-bottom: 15px; line-height: 1.4;"> Greenhouse structure is built, but automated irrigation and canning lines require initial calibration.<br> <strong style="color: #fbbf24;">Required Calibration Materials:</strong> 20 Wood, 10 Scrap, 10 Cloth </div> <<if setup.have("wood") gte 20 and setup.have("scrap") gte 10 and setup.have("cloth") gte 10>> <<link "<span class='cyber-green-btn' style='display:inline-block; width:auto; padding:10px 30px;'>⚡ INITIALIZE AUTOMATION SYSTEMS</span>" "Greenhouse">> <<run setup.spend("wood", 20)>> <<run setup.spend("scrap", 10)>> <<run setup.spend("cloth", 10)>> <<set $greenhouseActive to true>> <</link>> <<else>> <div style="color: #ef4444; font-size: 0.8rem; font-weight: bold;">[ NEED 20 WOOD, 10 SCRAP, 10 CLOTH ]</div> <<endif>> </div> <<else>> <div style="border: 1px solid #10b981; background: rgba(16, 185, 129, 0.1); padding: 15px; border-radius: 4px; margin-bottom: 25px; font-family: monospace;"> <div style="color: #34d399; font-weight: bold; margin-bottom: 6px;">🌱 AUTOMATED HYDROPONIC STATUS: ONLINE</div> <div style="color: #cbd5e1; font-size: 0.85rem; line-height: 1.4;"> Automation online. Based on facility tier (Level <<print $sanctuaryLevels.greenhouse>>), daily yield: <br>• <strong style="color: #fbbf24;"><<print ($sanctuaryLevels.greenhouse * 2)>> Canned Food</strong> <br>• <strong style="color: #38bdf8;"><<print $sanctuaryLevels.greenhouse>> Medical Herbs</strong> <br>• <strong style="color: #38bdf8;"><<print $sanctuaryLevels.greenhouse>> Water</strong> </div> </div> <<endif>> <div class="section-title">>> AUTOMATED SECTORS</div> <div class="greenhouse-grid"> <!-- 1. CANNED FOOD SECTOR --> <div class="greenhouse-card"> <img src="images/locations/greenhouse_crops.jpg" class="greenhouse-img" onerror="this.src='https://placehold.co/600x300/000/10b981?text=CANNED+FOOD';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #34d399; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">🥫 CANNED FOOD RATIONS</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">Automated canning lines sealing long-shelf-life nutrition.</div> <div style="color: #fbbf24; font-size: 0.7rem; font-family: monospace;">YIELD: +<<print ($sanctuaryLevels.greenhouse * 2)>> / Cycle</div> </div> <div> <<if $greenhouseActive>> <div style="color: #34d399; font-size: 0.75rem; text-align: center; font-weight: bold; padding: 6px; border: 1px dashed #059669; background: rgba(5,150,105,0.1);">[ AUTOMATED ]</div> <<else>> <div class="cyber-disabled-btn">[ STANDBY ]</div> <<endif>> </div> </div> </div> <!-- 2. MEDICAL HERBS SECTOR --> <div class="greenhouse-card"> <img src="images/locations/greenhouse_herbs.jpg" class="greenhouse-img" onerror="this.src='https://placehold.co/600x300/000/38bdf8?text=MEDICAL+HERBS';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #38bdf8; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">🌿 MEDICAL HERB BAY</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">Cultivating sterile flora to be combined with cloth in the Medical Tent.</div> <div style="color: #fbbf24; font-size: 0.7rem; font-family: monospace;">YIELD: +<<print $sanctuaryLevels.greenhouse>> / Cycle</div> </div> <div> <<if $greenhouseActive>> <div style="color: #38bdf8; font-size: 0.75rem; text-align: center; font-weight: bold; padding: 6px; border: 1px dashed #0369a1; background: rgba(3,105,161,0.1);">[ AUTOMATED ]</div> <<else>> <div class="cyber-disabled-btn">[ STANDBY ]</div> <<endif>> </div> </div> </div> <!-- 3. WATER SCRUBBER SECTOR --> <div class="greenhouse-card"> <img src="images/locations/greenhouse_water.jpg" class="greenhouse-img" onerror="this.src='https://placehold.co/600x300/000/0284c7?text=WATER';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #38bdf8; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">💧 ATMOSPHERIC CONDENSER</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">Filtering and recycling moisture for irrigation and base reserves.</div> <div style="color: #fbbf24; font-size: 0.7rem; font-family: monospace;">YIELD: +<<print $sanctuaryLevels.greenhouse>> / Cycle</div> <<if def $waterPump and $waterPump gt 0>><div style="color: #7dd3fc; font-size: 0.7rem; font-family: monospace;">🚰 PURIFICATION PUMP LV <<print $waterPump>>: +<<print ($waterPump * 10) + ($sanctuaryLevels.greenhouse * 2)>> / Cycle</div><</if>> </div> <div> <<if $greenhouseActive>> <div style="color: #38bdf8; font-size: 0.75rem; text-align: center; font-weight: bold; padding: 6px; border: 1px dashed #0284c7; background: rgba(2,132,199,0.1);">[ AUTOMATED ]</div> <<else>> <div class="cyber-disabled-btn">[ STANDBY ]</div> <<endif>> </div> </div> </div> </div> <div style="text-align: center; margin-top: 35px;"> <<link "<span class='cyber-return-btn'>🏰 RETURN TO SANCTUARY HUB</span>" "Sanctuary Hub">><</link>> </div> </div> </div> <</nobr>>
<<audio ":all" stop>> <<audio "forest" volume 0.25 loop play>> <<nobr>> <<setMenu "Hunting">> <<if ndef $materials>><<set $materials to {}>><</if>> <<if ndef $materials.rawMeat>><<set $materials.rawMeat to 0>><</if>> <<if ndef $stamina>><<set $stamina to 100>><</if>> <<if ndef $thirst>><<set $thirst to 100>><</if>> <style> /* DEFINITIVELY LOCK SUGARCUBE'S DEFAULT CONTAINER GAPS */ #story { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; min-height: 100vh; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; background: transparent !important; min-height: 100vh; } /* STRUCTURE THAT REMOVES THE BLACK GAP AT THE TOP AND CENTERS THE CONTENT VERTICALLY */ .av-hunt-world { position: absolute; top: 0; left: 0; margin-left: 17.5em; width: calc(100% - 17.5em); min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; background: linear-gradient(180deg, rgba(5,15,5,0.8), rgba(10,25,10,0.95)), url('images/locations/bg_hunting.jpg') center/cover no-repeat; padding: 20px; align-items: center; justify-content: center; } .ashen-veil-hunt-bg { background: rgba(5, 15, 5, 0.92); border: 2px solid #ef4444; border-radius: 8px; padding: 25px; display: flex; flex-direction: column; align-items: center; font-family: 'Segoe UI', Tahoma, sans-serif; box-shadow: 0 0 40px rgba(239, 68, 68, 0.3); backdrop-filter: blur(6px); } #forest-container { position: relative; width: 800px; height: 500px; background: url('images/locations/forest_bg.jpg') center/cover no-repeat; border: 2px solid #ef4444; overflow: hidden; margin-bottom: 20px; box-shadow: 0 0 30px rgba(239, 68, 68, 0.3); border-radius: 4px; } .hunt-hud { display: flex; justify-content: space-between; width: 800px; margin-bottom: 10px; } .hunt-stat { background: rgba(0,0,0,0.8); border: 1px solid #fbbf24; color: #fbbf24; padding: 10px 20px; font-weight: bold; border-radius: 2px; flex: 1; text-align: center; margin: 0 5px; } #hunt-log { width: 800px; text-align: center; font-size: 1.2rem; font-weight: bold; color: #94a3b8; min-height: 30px; margin-bottom: 20px; text-transform: uppercase; letter-spacing: 2px; } .hunt-return-btn { width: 800px; background: rgba(239,68,68,0.1); border: 1px solid #ef4444; padding: 15px; text-align: center; border-radius: 4px; transition: 0.3s; } .hunt-return-btn a { display: block; color: #ef4444 !important; font-size: 1.1rem; font-weight: bold; text-decoration: none !important; } .hunt-return-btn:hover { background: rgba(239,68,68,0.3); box-shadow: 0 0 15px rgba(239, 68, 68, 0.5); } #animal-target a { display: block; width: 100px; height: 100px; background-color: rgba(239, 68, 68, 0.3); border: 2px solid #ef4444; background-image: url('images/locations/animal_target.png'); background-size: contain; background-position: center; background-repeat: no-repeat; border-radius: 4px; transition: transform 0.1s; } #animal-target a:hover { transform: scale(1.1); background-color: rgba(239, 68, 68, 0.6); filter: brightness(1.5); } .scan-button { background: rgba(56,189,248,0.1); border: 1px solid #38bdf8; padding: 15px 30px; font-size: 1.2rem; display: inline-block; border-radius: 4px; margin-bottom: 20px; } .scan-button a { color: #38bdf8 !important; font-weight: bold; text-decoration: none !important; display: block; } .scan-button:hover { background: rgba(56,189,248,0.3); box-shadow: 0 0 15px rgba(56,189,248,0.5); } .disabled-btn { background: rgba(239,68,68,0.1); border: 1px solid #ef4444; padding: 15px 30px; font-size: 1.2rem; display: inline-block; border-radius: 4px; margin-bottom: 20px; opacity: 0.6; cursor: not-allowed; } @media screen and (max-width: 768px) { .av-hunt-world { margin-left: 3.5em; width: calc(100% - 3.5em); } } </style> <div class="av-hunt-world"> <div class="ashen-veil-hunt-bg"> <h2 style="color: #ef4444; margin: 0 0 20px 0; letter-spacing: 4px; text-shadow: 0 0 15px rgba(239, 68, 68, 0.5);">[ HUNTING GROUNDS ]</h2> <div class="hunt-hud"> <div class="hunt-stat" style="border-color:#38bdf8; color:#38bdf8;">⚡ STAMINA: <span id="stamina-count" style="color:#fff;"><<print $stamina>></span></div> <div class="hunt-stat" style="border-color:#2ecc71; color:#2ecc71;">🎯 STATUS: <span id="hunt-status" style="color:#fff;">READY FOR SCAN</span></div> <div class="hunt-stat" style="border-color:#fbbf24; color:#fbbf24;">🍖 RAW MEAT: <span id="meat-count" style="color:#fff;"><<print $materials.rawMeat>></span></div> </div> <div id="forest-container"> <div id="animal-target" style="display:none; position:absolute; z-index:50;"> <<link " ">> /* PLAY THE RIFLE SOUND THE MOMENT THE TARGET IS CLICKED */ <<audio "rifle" play>> <<set _meatGain to (setup.tipOn("hunt") ? 2 : 1)>> <<set $materials.rawMeat += _meatGain>> <<replace "#hunt-log">>@@color:#2ecc71;TARGET NEUTRALIZED! +<<print _meatGain>> RAW MEAT@@<</replace>> <<replace "#meat-count">><<print $materials.rawMeat>><</replace>> <<script>> $("#animal-target").hide(); $("#hunt-status").text("READY FOR NEXT SCAN").css("color", "#fff"); if(typeof window.setup.updateSidebar === 'function') window.setup.updateSidebar(); if(State.variables.stamina >= 25) { $("#scan-btn").css("display", "inline-block"); $("#scan-btn-disabled").hide(); } else { $("#scan-btn").hide(); $("#scan-btn-disabled").css("display", "inline-block"); } <</script>> <</link>> </div> </div> <div id="hunt-log">AWAITING RADAR ACTIVATION...</div> <div style="text-align: center; height: 70px;"> <div id="scan-btn" class="scan-button" @style="$stamina gte 25 ? 'display:inline-block;' : 'display:none;'"> <<link "📡 INITIATE RADAR SCAN (-25 STAMINA)">> <<set $stamina -= 25>> <<if $stamina lt 0>><<set $stamina to 0>><</if>> <<set $thirst = Math.max(0, $thirst - 5)>> <<replace "#stamina-count">><<print $stamina>><</replace>> <<replace "#hunt-log">>@@color:#38bdf8;SCANNING AREA...@@<</replace>> <<script>> $("#scan-btn").hide(); $("#scan-btn-disabled").hide(); $("#hunt-status").text("SCANNING...").css("color", "#38bdf8"); if(typeof window.setup.updateSidebar === 'function') window.setup.updateSidebar(); setTimeout(function() { var $target = $("#animal-target"); var $cont = $("#forest-container"); var maxX = $cont.width() - 110; var maxY = $cont.height() - 110; $target.css({ left: Math.floor(Math.random() * maxX) + "px", top: Math.floor(Math.random() * maxY) + "px" }).show(); window.huntMissTimer = setTimeout(function() { if($target.is(":visible")) { $target.hide(); $("#hunt-log").html('<span style="color:#ef4444">TARGET ESCAPED.</span>'); $("#hunt-status").text("READY FOR SCAN").css("color", "#fff"); if(State.variables.stamina >= 25) { $("#scan-btn").css("display", "inline-block"); $("#scan-btn-disabled").hide(); } else { $("#scan-btn").hide(); $("#scan-btn-disabled").css("display", "inline-block"); } } }, 800); }, Math.floor(Math.random() * 1500) + 500); <</script>> <</link>> </div> <div id="scan-btn-disabled" class="disabled-btn" @style="$stamina lt 25 ? 'display:inline-block;' : 'display:none;'"> <span style="color: #ef4444; font-weight: bold; display: block;">❌ EXHAUSTED (REQUIRES 25 STAMINA)</span> </div> </div> <div class="hunt-return-btn"> <<link "ABORT HUNT & RETURN TO SECTOR MAP" "Map">><</link>> </div> </div> </div> <</nobr>>
<<nobr>> <<setMenu "Inventory">> <<run setup.recalcStats()>> /* SAFETY AND CARRY CAPACITY CHECK */ <<if ndef $maxCarry>><<set $maxCarry to 40>><</if>> <<if ndef $equipment.trinket1>><<set $equipment.trinket1 to "None">><</if>> <<if ndef $equipment.trinket2>><<set $equipment.trinket2 to "None">><</if>> <<if ndef $equipment.backpack>><<set $equipment.backpack to "None">><</if>> <<if ndef $equipment.ring1>><<set $equipment.ring1 to "None">><</if>> <<if ndef $equipment.ring2>><<set $equipment.ring2 to "None">><</if>> <<if ndef $syndicateTokens>><<set $syndicateTokens to 0>><</if>> <<if ndef $materials.fuel>><<set $materials.fuel to 0>><</if>> <<if ndef $materials.repairKit>><<set $materials.repairKit to 0>><</if>> <<if ndef $materials.energyBooster>><<set $materials.energyBooster to 0>><</if>> <<if ndef $materials.archivalPaper>><<set $materials.archivalPaper to 0>><</if>> <<if ndef $materials.plainBandage>><<set $materials.plainBandage to 0>><</if>> /* REALISTIC TOTAL WEIGHT CALCULATION */ <<set _totalWeight to 0>> <<if def $materials>> <<set _totalWeight += (($materials.scrap || 0) * 1.0)>> <<set _totalWeight += (($materials.wood || 0) * 3.0)>> <<set _totalWeight += (($materials.cloth || 0) * 0.5)>> <<set _totalWeight += (($materials.medicine || 0) * 0.2)>> <<set _totalWeight += (($materials.food || 0) * 0.5)>> <<set _totalWeight += (($materials.rawMeat || 0) * 1.0)>> <<set _totalWeight += (($materials.berries || 0) * 0.2)>> <<set _totalWeight += (($materials.water || 0) * 1.5)>> <<set _totalWeight += (($materials.ammo || 0) * 0.05)>> <<set _totalWeight += (($materials.rope || 0) * 0.1)>> <<set _totalWeight += (($materials.techData || 0) * 0.1)>> <<set _totalWeight += (($materials.electronics || 0) * 0.2)>> <<set _totalWeight += (($materials.energyBooster || 0) * 0.1)>> <<set _totalWeight += (($materials.repairKit || 0) * 0.5)>> <<set _totalWeight += (($materials.fuel || 0) * 1.0)>> <<set _totalWeight += (($materials.archivalPaper || 0) * 0.2)>> <<set _totalWeight += (($materials.plainBandage || 0) * 0.1)>> <<endif>> <<set _totalWeight to Math.round(_totalWeight * 10) / 10>> <<set _gearScore to 0>><<set _equippedCount to 0>><<set _maxSlots to 12>> <<for _slot, _itemName range $equipment>> <<if _itemName neq "None">> <<set _equippedCount += 1>> <<if _itemName.includes("Godlike")>><<set _gearScore += 6>> <<elseif _itemName.includes("Legendary")>><<set _gearScore += 5>> <<elseif _itemName.includes("Epic")>><<set _gearScore += 4>> <<elseif _itemName.includes("Rare")>><<set _gearScore += 3>> <<elseif _itemName.includes("Uncommon")>><<set _gearScore += 2>> <<else>><<set _gearScore += 1>><</if>> <<endif>> <<endfor>> <<set _avgScore to 0>><<if _equippedCount gt 0>><<set _avgScore to _gearScore / _maxSlots>><</if>> <<set _tierName to "COMMON">><<set _tierColor to "#94a3b8">> <<if _avgScore gte 5.5>><<set _tierName to "GODLIKE">><<set _tierColor to "#ef4444">> <<elseif _avgScore gte 4.5>><<set _tierName to "LEGENDARY">><<set _tierColor to "#fbbf24">> <<elseif _avgScore gte 3.5>><<set _tierName to "EPIC">><<set _tierColor to "#c084fc">> <<elseif _avgScore gte 2.5>><<set _tierName to "RARE">><<set _tierColor to "#60a5fa">> <<elseif _avgScore gte 1.5>><<set _tierName to "UNCOMMON">><<set _tierColor to "#2ecc71">><</if>> <style> #ui-dialog-body { padding: 0 !important; background-color: #050a0f !important; } #ui-dialog-close { color: #ffffff !important; background: #ef4444 !important; border: 2px solid #b91c1c !important; border-radius: 4px !important; width: 32px !important; height: 32px !important; opacity: 1 !important; box-shadow: 0 0 10px rgba(239, 68, 68, 0.6) !important; } #ui-dialog-close:hover { background: #dc2626 !important; box-shadow: 0 0 15px rgba(220, 38, 38, 0.9) !important; } #ui-dialog-close::before { content: none !important; } #ui-dialog-close::after { content: "X" !important; color: #ffffff !important; font-family: Arial, sans-serif !important; font-size: 1.1rem !important; font-weight: bold !important; position: absolute !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; } .popup-frame { display: flex; justify-content: space-between; align-items: stretch; gap: 20px; width: 100%; box-sizing: border-box; } .popup-core-content { flex: 1; background: linear-gradient(to bottom, #050a0f, #021015); padding: 25px; border: 2px solid #0ea5e9; border-radius: 8px; font-family: 'Courier New', monospace; box-shadow: 0 0 20px rgba(14, 165, 233, 0.1); box-sizing: border-box; } /* NEW HOLOGRAPHIC LAYOUT */ .holo-inv-wrapper { display: flex; gap: 20px; align-items: stretch; margin-bottom: 25px; height: auto; min-height: 600px; } .holo-stash { contain: size; flex: 0 0 350px; background: rgba(0, 5, 10, 0.8); border: 1px solid #0ea5e9; box-shadow: inset 0 0 20px rgba(14, 165, 233, 0.1); border-radius: 4px; padding: 15px; overflow-y: auto; } .holo-doll { flex: 1; background: rgba(2, 16, 21, 0.5); border: 1px solid #0ea5e9; border-radius: 4px; position: relative; display: grid; grid-template-columns: 1fr minmax(200px, 280px) 1fr; gap: 15px; align-items: stretch; padding: 15px; overflow: hidden; } .holo-doll::before { content: ""; position: absolute; inset: 0; background: repeating-linear-gradient(0deg, rgba(56, 189, 248, 0.03) 0px, rgba(56, 189, 248, 0.03) 1px, transparent 1px, transparent 4px); pointer-events: none; z-index: 1; } /* IMAGE WAS STRETCHED AND ENLARGED */ .holo-silhouette { align-self: stretch; height: 100%; width: 100%; background: url('images/ui/body_schema.png') center center / contain no-repeat; opacity: 0.9; filter: drop-shadow(0 0 25px rgba(14,165,233,1)); position: relative; z-index: 2; transform: scale(1.8); transform-origin: center center; /* body zoom: raise or lower 1.8 */ } .holo-col { display: flex; flex-direction: column; justify-content: space-between; gap: 8px; height: 100%; position: relative; z-index: 2; } .holo-slot { background: rgba(2, 16, 21, 0.85); border: 1px solid #0ea5e9; padding: 8px; position: relative; box-shadow: inset 0 0 10px rgba(14,165,233,0.2); border-radius: 3px; min-height: 70px; display: flex; flex-direction: column; justify-content: center; } .holo-slot-l::after { content: ""; position: absolute; right: -15px; top: 50%; width: 15px; border-bottom: 1px solid #0ea5e9; opacity: 0.5; } .holo-slot-r::before { content: ""; position: absolute; left: -15px; top: 50%; width: 15px; border-bottom: 1px solid #0ea5e9; opacity: 0.5; } .h-title { color: #7dd3fc; font-size: 0.65rem; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 3px; } .h-val { color: #f8fafc; font-size: 0.8rem; font-weight: bold; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* STAT BARS */ .stat-bar-orange { padding: 12px 20px; display: flex; justify-content: space-between; font-weight: bold; margin-bottom: 5px; letter-spacing: 1px; font-size: 0.9rem; border-radius: 2px; background: rgba(8, 20, 34, 0.92); border: 1px solid #38bdf8; color: #e2e8f0; box-shadow: 0 0 10px rgba(56, 189, 248, 0.15); } .stat-bar-cyan { background: rgba(14, 165, 233, 0.1); color: #7dd3fc; padding: 12px 20px; display: flex; justify-content: space-between; font-weight: bold; border: 1px solid #0ea5e9; margin-bottom: 25px; letter-spacing: 1px; font-size: 0.9rem; border-radius: 2px; } </style> <div class="popup-frame"> <div class="popup-core-content"> <div style="text-align: center; border-bottom: 1px dashed #0ea5e9; padding-bottom: 15px; margin-bottom: 15px;"> <h2 style="color: #0ea5e9; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px rgba(14,165,233,0.5);">[ NEURAL UPLINK: EQUIPMENT MATRIX ]</h2> <div style="margin-top: 10px; font-size: 0.9rem; color: #7dd3fc;">LOADOUT & BIOMETRIC MANAGEMENT</div> </div> <!-- STAT BARS --> <div class="stat-bar-orange"> <span>GEAR SCORE: <<print _gearScore>> / 60</span> <span>OVERALL TIER: <span @style="'color: ' + _tierColor + '; text-shadow: 0 0 5px ' + _tierColor + ';'"><<print _tierName>></span></span> <span>EQUIPPED: <<print _equippedCount>> / <<print _maxSlots>></span> </div> <div class="stat-bar-cyan"> <span style="color:#ef4444;">⚔️ BONUS DMG: <<print $playerDmgBonus>></span> <span style="color:#f59e0b;">🛡️ TOTAL ARMOR: <<print $playerArmor>></span> <span @style="'color: ' + (_totalWeight gt $maxCarry ? '#ef4444' : '#38bdf8') + ';'">📦 CARRY: <<print _totalWeight>> / <<print $maxCarry>> KG</span> </div> <div class="holo-inv-wrapper"> <!-- LEFT: STASH (Inventory Bag) --> <div class="holo-stash"> <div style="color: #a855f7; font-weight: bold; letter-spacing: 2px; text-align: center; border-bottom: 1px dashed #a855f7; padding-bottom: 10px; margin-bottom: 15px;">> GEAR STASH</div> <<if $gearInventory.length eq 0>> <div style="color: #64748b; font-style: italic; text-align: center; margin-top: 200px;">No additional gear in stash.</div> <<else>> <<for _i to 0; _i lt $gearInventory.length; _i++>> <<set _item to $gearInventory[_i]>> <<if def _item and typeof _item eq "string" and _item neq "None" and _item neq "">> <<set _baseName to _item.replace("Common ", "").replace("Uncommon ", "").replace("Rare ", "").replace("Epic ", "").replace("Legendary ", "").replace("Godlike ", "")>> <<set _slot to "rightHand">><<for _key, _arr range $itemDatabase>><<if _arr.includes(_baseName)>><<set _slot to _key>><<break>><</if>><</for>><<if _baseName eq "Bebok's Scrap Pack">><<set _slot to "backpack">><</if>> <<set _st to setup.getItemStats(_item)>> <<if ndef _st>><<set _st to { dmg: 0, armor: 0, hp: 0, stamina: 0, weight: 5 }>>><</if>> <<set _iColor to "#cbd5e1">> <<if _item.includes("Godlike")>><<set _iColor to "#ef4444">><<elseif _item.includes("Legendary")>><<set _iColor to "#fbbf24">><<elseif _item.includes("Epic")>><<set _iColor to "#c084fc">><<elseif _item.includes("Rare")>><<set _iColor to "#60a5fa">><<elseif _item.includes("Uncommon")>><<set _iColor to "#2ecc71">><<elseif _item.includes("Common")>><<set _iColor to "#94a3b8">><</if>> <<set _displaySlot to _slot>> <<if _slot.includes("ring")>><<set _displaySlot to "ring">><</if>> <<if _slot.includes("trinket")>><<set _displaySlot to "trinket">><</if>> <<capture _i, _item, _slot>> <div style="background: rgba(2,16,21,0.8); border: 1px solid #334155; padding: 10px; margin-bottom: 10px; border-radius: 3px;"> <div style="display: flex; justify-content: space-between; align-items: center;"> <div> <div @style="'color: ' + _iColor + '; font-weight: bold; font-size: 0.9rem;'">_item</div> <div style="color:#64748b; font-size:0.65rem; text-transform: uppercase;">Type: _displaySlot</div> </div> @@color:#0ea5e9;font-weight:bold;font-size:0.75rem;border:1px solid #0ea5e9;padding:5px 10px;border-radius:2px;<<link "EQUIP">> <<run $gearInventory.delete(_item)>> <<if _slot.includes("ring")>> <<if $equipment.ring1 eq "None">><<set $equipment.ring1 to _item>> <<elseif $equipment.ring2 eq "None">><<set $equipment.ring2 to _item>> <<else>><<run $gearInventory.push($equipment.ring1)>><<set $equipment.ring1 to _item>><</if>> <<elseif _slot.includes("trinket")>> <<if $equipment.trinket1 eq "None">><<set $equipment.trinket1 to _item>> <<elseif $equipment.trinket2 eq "None">><<set $equipment.trinket2 to _item>> <<else>><<run $gearInventory.push($equipment.trinket1)>><<set $equipment.trinket1 to _item>><</if>> <<else>> <<if $equipment[_slot] neq "None">><<run $gearInventory.push($equipment[_slot])>><</if>> <<set $equipment[_slot] to _item>> <<endif>> <<replace "#ui-dialog-body">><<print Story.get("Inventory").processText()>><</replace>> <</link>>@@ </div> <div style="color: #a3e635; font-size: 0.75rem; margin-top: 5px; font-weight: bold;"> <<if _st.dmg gt 0>>⚔️ +<<print _st.dmg>> <</if>><<if _st.armor gt 0>>🛡 +<<print _st.armor>> <</if>><<if _st.hp gt 0>>❤️ +<<print _st.hp>> <</if>><<if _st.stamina gt 0>>⚡ +<<print _st.stamina>> <</if>><<if _st.carryBonus gt 0>>🎒 +<<print _st.carryBonus>>KG<</if>> </div> <div style="font-size: 0.72rem; margin-top: 5px; font-family: 'Courier New', monospace;"><<print setup.gearDeltaHtml(_item, _slot)>></div> </div> <</capture>> <<endif>> <<endfor>> <<endif>> </div> <!-- RIGHT: HOLO-DOLL (Body Diagram) --> <div class="holo-doll"> <!-- Left Side Slots --> <div class="holo-col"> <div class="holo-slot holo-slot-l"><div class="h-title">HEADGEAR</div><div class="h-val"><<print $equipment.headgear>></div><<if $equipment.headgear neq "None">><<set _st to setup.getItemStats($equipment.headgear)>><div style="color: #a3e635; font-size: 0.65rem; margin-top: 3px; font-weight: bold;"><<if _st.armor gt 0>>🛡️ +<<print _st.armor>> <</if>><<if _st.hp gt 0>>❤️ +<<print _st.hp>><</if>></div>@@color:#ef4444;font-size:0.7rem;font-weight:bold;display:block;text-align:center;margin-top:5px;<<link "[ UNEQUIP ]">><<set $gearInventory.push($equipment.headgear)>><<set $equipment.headgear to "None">><<replace "#ui-dialog-body">><<print Story.get("Inventory").processText()>><</replace>><</link>>@@<<else>><div style="color: #475569; font-size: 0.7rem; font-style: italic;">[ Empty ]</div><</if>></div> <div class="holo-slot holo-slot-l"><div class="h-title">CHEST RIG</div><div class="h-val"><<print $equipment.chest>></div><<if $equipment.chest neq "None">><<set _st to setup.getItemStats($equipment.chest)>><div style="color: #a3e635; font-size: 0.65rem; margin-top: 3px; font-weight: bold;"><<if _st.armor gt 0>>🛡️ +<<print _st.armor>> <</if>><<if _st.hp gt 0>>❤️ +<<print _st.hp>><</if>></div>@@color:#ef4444;font-size:0.7rem;font-weight:bold;display:block;text-align:center;margin-top:5px;<<link "[ UNEQUIP ]">><<set $gearInventory.push($equipment.chest)>><<set $equipment.chest to "None">><<replace "#ui-dialog-body">><<print Story.get("Inventory").processText()>><</replace>><</link>>@@<<else>><div style="color: #475569; font-size: 0.7rem; font-style: italic;">[ Empty ]</div><</if>></div> <div class="holo-slot holo-slot-l"><div class="h-title" style="color:#f87171;">WEAPON (RIGHT)</div><div class="h-val"><<print $equipment.rightHand>></div><<if $equipment.rightHand neq "None">><<set _st to setup.getItemStats($equipment.rightHand)>><div style="color: #a3e635; font-size: 0.65rem; margin-top: 3px; font-weight: bold;"><<if _st.dmg gt 0>>⚔️ +<<print _st.dmg>> DMG <</if>></div>@@color:#ef4444;font-size:0.7rem;font-weight:bold;display:block;text-align:center;margin-top:5px;<<link "[ UNEQUIP ]">><<set $gearInventory.push($equipment.rightHand)>><<set $equipment.rightHand to "None">><<replace "#ui-dialog-body">><<print Story.get("Inventory").processText()>><</replace>><</link>>@@<<else>><div style="color: #475569; font-size: 0.7rem; font-style: italic;">[ Empty ]</div><</if>></div> <div class="holo-slot holo-slot-l"><div class="h-title">GLOVES</div><div class="h-val"><<print $equipment.gloves>></div><<if $equipment.gloves neq "None">><<set _st to setup.getItemStats($equipment.gloves)>><div style="color: #a3e635; font-size: 0.65rem; margin-top: 3px; font-weight: bold;"><<if _st.armor gt 0>>🛡️ +<<print _st.armor>> <</if>><<if _st.dmg gt 0>>⚔️ +<<print _st.dmg>><</if>></div>@@color:#ef4444;font-size:0.7rem;font-weight:bold;display:block;text-align:center;margin-top:5px;<<link "[ UNEQUIP ]">><<set $gearInventory.push($equipment.gloves)>><<set $equipment.gloves to "None">><<replace "#ui-dialog-body">><<print Story.get("Inventory").processText()>><</replace>><</link>>@@<<else>><div style="color: #475569; font-size: 0.7rem; font-style: italic;">[ Empty ]</div><</if>></div> <div class="holo-slot holo-slot-l"><div class="h-title" style="color:#fbbf24;">RING (LEFT)</div><div class="h-val"><<print $equipment.ring1>></div><<if $equipment.ring1 neq "None">><<set _st to setup.getItemStats($equipment.ring1)>><div style="color: #a3e635; font-size: 0.65rem; margin-top: 3px; font-weight: bold;"><<if _st.armor gt 0>>🛡️ +<<print _st.armor>> <</if>><<if _st.stamina gt 0>>⚡ +<<print _st.stamina>><</if>></div>@@color:#ef4444;font-size:0.7rem;font-weight:bold;display:block;text-align:center;margin-top:5px;<<link "[ UNEQUIP ]">><<set $gearInventory.push($equipment.ring1)>><<set $equipment.ring1 to "None">><<replace "#ui-dialog-body">><<print Story.get("Inventory").processText()>><</replace>><</link>>@@<<else>><div style="color: #475569; font-size: 0.7rem; font-style: italic;">[ Empty ]</div><</if>></div> <div class="holo-slot holo-slot-l"><div class="h-title" style="color:#a855f7;">TRINKET 1</div><div class="h-val"><<print $equipment.trinket1>></div><<if $equipment.trinket1 neq "None">><<set _st to setup.getItemStats($equipment.trinket1)>><div style="color: #a3e635; font-size: 0.65rem; margin-top: 3px; font-weight: bold;"><<if _st.armor gt 0>>🛡️ +<<print _st.armor>> <</if>><<if _st.hp gt 0>>❤️ +<<print _st.hp>><</if>></div>@@color:#ef4444;font-size:0.7rem;font-weight:bold;display:block;text-align:center;margin-top:5px;<<link "[ UNEQUIP ]">><<set $gearInventory.push($equipment.trinket1)>><<set $equipment.trinket1 to "None">><<replace "#ui-dialog-body">><<print Story.get("Inventory").processText()>><</replace>><</link>>@@<<else>><div style="color: #475569; font-size: 0.7rem; font-style: italic;">[ Empty ]</div><</if>></div> </div> <!-- Center Silhouette --> <div class="holo-silhouette"></div> <<script>> /* the centre column gets exactly the width the body picture needs at the panel height, so the head touches the top and the feet touch the bottom */ $(document).one(':passagedisplay', function () { var doll = document.querySelector('.holo-doll'), sil = doll && doll.querySelector('.holo-silhouette'); if (!sil) { return; } var img = new Image(); img.onload = function () { var fit = function () { var d = document.querySelector('.holo-doll'), s = d && d.querySelector('.holo-silhouette'); if (!d || !s) { $(window).off('resize.holo'); return; } var h = s.getBoundingClientRect().height; if (!h) { return; } var w = Math.round(h * img.naturalWidth / img.naturalHeight); var max = Math.floor(d.getBoundingClientRect().width * 0.42); d.style.gridTemplateColumns = '1fr ' + Math.max(160, Math.min(w, max)) + 'px 1fr'; }; fit(); setTimeout(fit, 150); $(window).off('resize.holo').on('resize.holo', fit); }; img.src = 'images/ui/body_schema.png'; }); <</script>> <!-- Right Side Slots --> <div class="holo-col"> <div class="holo-slot holo-slot-r"><div class="h-title" style="color:#10b981;">BACKPACK</div><div class="h-val"><<print $equipment.backpack>></div><<if $equipment.backpack neq "None">><<set _st to setup.getItemStats($equipment.backpack)>><div style="color: #a3e635; font-size: 0.65rem; margin-top: 3px; font-weight: bold;"><<if _st.armor gt 0>>🛡️ +<<print _st.armor>> <</if>><<if _st.stamina gt 0>>⚡ +<<print _st.stamina>> <</if>><<if _st.carryBonus gt 0>>🎒 +<<print _st.carryBonus>>KG<</if>></div>@@color:#ef4444;font-size:0.7rem;font-weight:bold;display:block;text-align:center;margin-top:5px;<<link "[ UNEQUIP ]">><<set $gearInventory.push($equipment.backpack)>><<set $equipment.backpack to "None">><<replace "#ui-dialog-body">><<print Story.get("Inventory").processText()>><</replace>><</link>>@@<<else>><div style="color: #475569; font-size: 0.7rem; font-style: italic;">[ Empty ]</div><</if>></div> <div class="holo-slot holo-slot-r"><div class="h-title" style="color:#3498db;">OFF-HAND (LEFT)</div><div class="h-val"><<print $equipment.offHand>></div><<if $equipment.offHand neq "None">><<set _st to setup.getItemStats($equipment.offHand)>><div style="color: #a3e635; font-size: 0.65rem; margin-top: 3px; font-weight: bold;"><<if _st.armor gt 0>>🛡️ +<<print _st.armor>> <</if>><<if _st.hp gt 0>>❤ +<<print _st.hp>><</if>></div>@@color:#ef4444;font-size:0.7rem;font-weight:bold;display:block;text-align:center;margin-top:5px;<<link "[ UNEQUIP ]">><<set $gearInventory.push($equipment.offHand)>><<set $equipment.offHand to "None">><<replace "#ui-dialog-body">><<print Story.get("Inventory").processText()>><</replace>><</link>>@@<<else>><div style="color: #475569; font-size: 0.7rem; font-style: italic;">[ Empty ]</div><</if>></div> <div class="holo-slot holo-slot-r"><div class="h-title">LEGS</div><div class="h-val"><<print $equipment.legs>></div><<if $equipment.legs neq "None">><<set _st to setup.getItemStats($equipment.legs)>><div style="color: #a3e635; font-size: 0.65rem; margin-top: 3px; font-weight: bold;"><<if _st.armor gt 0>>🛡️ +<<print _st.armor>> <</if>><<if _st.stamina gt 0>>⚡ +<<print _st.stamina>><</if>></div>@@color:#ef4444;font-size:0.7rem;font-weight:bold;display:block;text-align:center;margin-top:5px;<<link "[ UNEQUIP ]">><<set $gearInventory.push($equipment.legs)>><<set $equipment.legs to "None">><<replace "#ui-dialog-body">><<print Story.get("Inventory").processText()>><</replace>><</link>>@@<<else>><div style="color: #475569; font-size: 0.7rem; font-style: italic;">[ Empty ]</div><</if>></div> <div class="holo-slot holo-slot-r"><div class="h-title">BOOTS</div><div class="h-val"><<print $equipment.boots>></div><<if $equipment.boots neq "None">><<set _st to setup.getItemStats($equipment.boots)>><div style="color: #a3e635; font-size: 0.65rem; margin-top: 3px; font-weight: bold;"><<if _st.armor gt 0>>🛡️ +<<print _st.armor>> <</if>><<if _st.stamina gt 0>>⚡ +<<print _st.stamina>><</if>></div>@@color:#ef4444;font-size:0.7rem;font-weight:bold;display:block;text-align:center;margin-top:5px;<<link "[ UNEQUIP ]">><<set $gearInventory.push($equipment.boots)>><<set $equipment.boots to "None">><<replace "#ui-dialog-body">><<print Story.get("Inventory").processText()>><</replace>><</link>>@@<<else>><div style="color: #475569; font-size: 0.7rem; font-style: italic;">[ Empty ]</div><</if>></div> <div class="holo-slot holo-slot-r"><div class="h-title" style="color:#fbbf24;">RING (RIGHT)</div><div class="h-val"><<print $equipment.ring2>></div><<if $equipment.ring2 neq "None">><<set _st to setup.getItemStats($equipment.ring2)>><div style="color: #a3e635; font-size: 0.65rem; margin-top: 3px; font-weight: bold;"><<if _st.armor gt 0>>🛡 +<<print _st.armor>> <</if>><<if _st.stamina gt 0>>⚡ +<<print _st.stamina>><</if>></div>@@color:#ef4444;font-size:0.7rem;font-weight:bold;display:block;text-align:center;margin-top:5px;<<link "[ UNEQUIP ]">><<set $gearInventory.push($equipment.ring2)>><<set $equipment.ring2 to "None">><<replace "#ui-dialog-body">><<print Story.get("Inventory").processText()>><</replace>><</link>>@@<<else>><div style="color: #475569; font-size: 0.7rem; font-style: italic;">[ Empty ]</div><</if>></div> <div class="holo-slot holo-slot-r"><div class="h-title" style="color:#a855f7;">TRINKET 2</div><div class="h-val"><<print $equipment.trinket2>></div><<if $equipment.trinket2 neq "None">><<set _st to setup.getItemStats($equipment.trinket2)>><div style="color: #a3e635; font-size: 0.65rem; margin-top: 3px; font-weight: bold;"><<if _st.armor gt 0>>🛡️ +<<print _st.armor>> <</if>><<if _st.hp gt 0>>❤ +<<print _st.hp>><</if>></div>@@color:#ef4444;font-size:0.7rem;font-weight:bold;display:block;text-align:center;margin-top:5px;<<link "[ UNEQUIP ]">><<set $gearInventory.push($equipment.trinket2)>><<set $equipment.trinket2 to "None">><<replace "#ui-dialog-body">><<print Story.get("Inventory").processText()>><</replace>><</link>>@@<<else>><div style="color: #475569; font-size: 0.7rem; font-style: italic;">[ Empty ]</div><</if>></div> </div> </div> </div> <!-- SHARED BAG STORAGE (RESOURCE CARGO) --> <div style="background: rgba(0, 10, 5, 0.95); border: 1px solid #047857; border-radius: 4px; padding: 15px;"> <div style="text-align: center; color: #2ecc71; font-weight: bold; font-size: 0.9rem; letter-spacing: 2px; margin-bottom: 15px; border-bottom: 1px dashed #047857; padding-bottom: 10px;"> > RESOURCE CARGO </div> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 15px;"> <!-- Raw Materials --> <div style="background: rgba(0,255,0,0.05); border: 1px solid #115e59; padding: 15px; border-radius: 4px; font-size: 0.85rem;"> <div style="color: #2ecc71; font-weight: bold; margin-bottom: 10px; text-transform: uppercase;">Raw Materials</div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>📦 Scrap:</span> <span style="color:#a3e635; font-weight:bold;"><<print $materials.scrap>></span></div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>🪵 Wood:</span> <span style="color:#a3e635; font-weight:bold;"><<print $materials.wood>></span></div> <div style="display: flex; justify-content: space-between; color: #cbd5e1;"><span>🧵 Cloth:</span> <span style="color:#a3e635; font-weight:bold;"><<print $materials.cloth>></span></div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-top: 6px;"><span>🔌 Electronics:</span> <span style="color:#a3e635; font-weight:bold;"><<print ($materials.electronics || 0)>></span></div> </div> <!-- Consumables --> <div style="background: rgba(52,152,219,0.05); border: 1px solid #1e3a8a; padding: 15px; border-radius: 4px; font-size: 0.85rem;"> <div style="color: #3498db; font-weight: bold; margin-bottom: 10px; text-transform: uppercase;">Consumables</div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>💊 Medicine:</span> <span style="color:#38bdf8; font-weight:bold;"><<print $materials.medicine>></span></div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>🥫 Canned Food:</span> <span style="color:#38bdf8; font-weight:bold;"><<print $materials.food>></span></div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>🥩 Raw Meat:</span> <span style="color:#38bdf8; font-weight:bold;"><<print $materials.rawMeat>></span></div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>🍇 Berries:</span> <span style="color:#38bdf8; font-weight:bold;"><<print $materials.berries>></span></div> <div style="display: flex; justify-content: space-between; color: #cbd5e1;"><span>💧 Water:</span> <span style="color:#38bdf8; font-weight:bold;"><<print $materials.water>></span></div> </div> <!-- Valuables & Tech --> <div style="background: rgba(255,170,0,0.05); border: 1px solid #78350f; padding: 15px; border-radius: 4px; font-size: 0.85rem;"> <div style="color: #ffaa00; font-weight: bold; margin-bottom: 10px; text-transform: uppercase;">Valuables & Tech</div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>💾 Tech Data:</span> <span style="color:#fbbf24; font-weight:bold;"><<print $materials.techData>></span></div> <<if ndef $materials.ammo>><<set $materials.ammo to 0>><</if>> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>🔋 Ammo:</span> <span style="color:#fbbf24; font-weight:bold;"><<print $materials.ammo>></span></div> <<if ndef $materials.rope>><<set $materials.rope to 0>><</if>> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"><span>🪢 Rope:</span> <span style="color:#fbbf24; font-weight:bold;"><<print $materials.rope>></span></div> <div style="display: flex; justify-content: space-between; color: #fbbf24; font-weight: bold; margin-top: 12px; border-top: 1px dashed #333; padding-top: 8px;"><span>🪙 Scrip:</span> <span><<print $materials.scrip>></span></div> <div style="display: flex; justify-content: space-between; color: #e879f9; font-weight: bold; margin-top: 6px;"><span>🎫 Broadcast Passes:</span> <span><<print ($globalBroadcastPass || 0)>></span></div> </div> <!-- Premium & Tactical --> <div style="background: rgba(239,68,68,0.05); border: 1px solid #b91c1c; padding: 15px; border-radius: 4px; font-size: 0.85rem;"> <div style="color: #ef4444; font-weight: bold; margin-bottom: 10px; text-transform: uppercase;">Premium & Tactical</div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"> <span>💎 Syndicate Tokens:</span> <span style="color:#fcd34d; font-weight:bold;"><<print $syndicateTokens>></span> </div> <div style="display: flex; justify-content: space-between; align-items: center; color: #cbd5e1; margin-bottom: 6px;"> <span>⚡ Energy Booster:</span> <span> <span style="color:#f59e0b; font-weight:bold;"><<print $materials.energyBooster>></span> <<if $materials.energyBooster gt 0>> @@color:#10b981;font-size:0.75rem;margin-left:8px;font-weight:bold;<<link "[ USE ]">><<set $materials.energyBooster -= 1>><<set $stamina to 100>><<replace "#ui-dialog-body">><<print Story.get("Inventory").processText()>><</replace>><</link>>@@ <<endif>> </span> </div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"> <span>🔧 Field Repair Kit:</span> <span style="color:#38bdf8; font-weight:bold;"><<print $materials.repairKit>></span> </div> <div style="display: flex; justify-content: space-between; color: #cbd5e1; margin-bottom: 6px;"> <span>🛢️ High-Octane Fuel:</span> <span style="color:#ef4444; font-weight:bold;"><<print $materials.fuel>></span> </div> <div style="display: flex; justify-content: space-between; align-items: center; color: #cbd5e1; margin-bottom: 6px;"> <span>🩹 Plain Bandages:</span> <span> <span style="color:#34d399; font-weight:bold;"><<print ($materials.plainBandage || 0)>></span> <<if ($materials.plainBandage || 0) gt 0 and setup.faHurt()>> @@color:#10b981;font-size:0.75rem;margin-left:8px;font-weight:bold;<<link "[ USE ]">><<run setup.useBandage()>><<run window.setup.updateSidebar()>><<replace "#ui-dialog-body">><<print Story.get("Inventory").processText()>><</replace>><</link>>@@ <</if>> </span> </div> <div style="display: flex; justify-content: space-between; color: #cbd5e1;"> <span>📜 Archival Paper & Ink:</span> <span style="color:#f59e0b; font-weight:bold;"><<print ($materials.archivalPaper || 0)>></span> </div> </div> </div> </div> </div> </div> <</nobr>>
<<nobr>> <div style="background-color: #050a05; padding: 25px; border: 2px solid #38bdf8; border-radius: 8px; font-family: 'Courier New', monospace; box-shadow: 0 0 25px rgba(56, 189, 248, 0.3); max-height: 80vh; overflow-y: auto;"> <div style="text-align: center; border-bottom: 1px dashed #38bdf8; padding-bottom: 15px; margin-bottom: 25px;"> <h2 style="color: #38bdf8; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px #38bdf8;">[ MASTER ITEM DATABASE ]</h2> <div style="margin-top: 10px; font-size: 0.9rem; color: #94a3b8;">ALL REGISTERED SECTOR EQUIPMENT, TRINKETS & STATS</div> </div> <<run setup.ensureRarities()>><<set _rarityOrder to ["Common", "Uncommon", "Rare", "Epic", "Legendary", "Godlike"]>> <<run setup.ensureItemDb()>> <<set _db to $itemDatabase>> <<for _slot, _itemList range _db>> <div style="margin-bottom: 25px; background: rgba(0, 20, 40, 0.4); border: 1px solid #1e3a8a; padding: 15px; border-radius: 6px;"> <div style="color: #38bdf8; font-weight: bold; font-size: 1.1rem; margin-bottom: 12px; border-bottom: 1px dashed #1e3a8a; padding-bottom: 6px; text-transform: uppercase;"> 📂 CATEGORY: <<print _slot>> </div> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 10px;"> <<for _i to 0; _i lt _itemList.length; _i++>> <<set _baseName to _itemList[_i]>> <div style="background: rgba(0,0,0,0.6); border: 1px solid #334155; padding: 10px; border-radius: 4px;"> <div style="color: #fff; font-weight: bold; font-size: 0.9rem; margin-bottom: 6px; border-bottom: 1px solid #222; padding-bottom: 3px;"> <<print _baseName>> </div> <!-- List the rarities and stats for each item --> <div style="display: flex; flex-direction: column; gap: 4px; font-size: 0.75rem;"> <<for _r to 0; _r lt _rarityOrder.length; _r++>> <<set _rarity to _rarityOrder[_r]>> <<set _fullName to _rarity + " " + _baseName>> <<set _stats to setup.getItemStats(_fullName)>> <<set _color to $rarities[_rarity].color>> <div style="display: flex; justify-content: space-between; align-items: center; color: <<print _color>>; background: rgba(255,255,255,0.02); padding: 2px 5px; border-radius: 2px;"> <span><<print _rarity>>:</span> <span style="color: #cbd5e1;"> <<if _stats.dmg gt 0>>⚔️+<<print _stats.dmg>> <<if _stats.isRanged>>(Ranged) <<else>>(Melee) <</if>><</if>> <<if _stats.armor gt 0>>🛡️+<<print _stats.armor>> <</if>> <<if _stats.hp gt 0>>❤️+<<print _stats.hp>> <</if>> <<if _stats.stamina gt 0>>⚡+<<print _stats.stamina>> <</if>> <<if _stats.carryBonus gt 0>>🎒+<<print _stats.carryBonus>>KG <</if>> <<if _stats.stealth gt 0>><span style="color: #a855f7;">🥷 +<<print _stats.stealth>> Stealth </span><</if>> <<if _stats.huntBonus gt 0>><span style="color: #ef4444;">🥩 +<<print _stats.huntBonus>>% Hunt Yield </span><</if>> <<if _stats.scrapBonus gt 0>><span style="color: #a3e635;">⚙️ +<<print _stats.scrapBonus>>% Scrap Yield </span><</if>> <<if _stats.fishChance gt 0>><span style="color: #38bdf8;">🎣 +<<print _stats.fishChance>>% Fish Chance </span><</if>> <<if _stats.forageBonus gt 0>><span style="color: #10b981;">🌿 +<<print _stats.forageBonus>>% Forage Yield </span><</if>> <<if _stats.globalLuck gt 0>><span style="color: #fbbf24;">🍀 +<<print _stats.globalLuck>> Luck </span><</if>> <<if _stats.dmg eq 0 and _stats.armor eq 0 and _stats.hp eq 0 and _stats.stamina eq 0 and _stats.carryBonus eq 0 and _stats.fishChance eq 0 and _stats.forageBonus eq 0 and _stats.globalLuck eq 0 and _stats.stealth eq 0 and _stats.huntBonus eq 0 and _stats.scrapBonus eq 0>> <span>(Base / Utility)</span> <<endif>> </span> </div> <<endfor>> </div> </div> <<endfor>> </div> </div> <<endfor>> <div style="text-align: center; margin-top: 25px;"> @@color:#38bdf8;font-weight:bold;<<link "CLOSE DATABASE">><<run Dialog.close()>><</link>>@@ </div> </div> <</nobr>>
<<run setup.kt.enter(true)>><<goto "KtScene">> <<audio ":all" stop>> <<audio "katowice_tunnels" volume 0.25 loop play>> <<nobr>> <div style="background-color: #050a05; padding: 30px; border: 2px solid #ef4444; border-radius: 8px; font-family: 'Segoe UI', Tahoma, sans-serif; color: #ef4444; max-width: 700px; margin: 5vh auto; box-shadow: 0 0 25px rgba(239, 68, 68, 0.2);"> <h2 style="text-align: center; letter-spacing: 2px; text-shadow: 0 0 10px #ef4444;">[ AMBUSH DETECTED: KATOWICE TUNNELS ]</h2> <div style="background: rgba(251, 191, 36, 0.10); border: 1px solid #fbbf24; border-radius: 4px; padding: 12px 15px; margin: 15px 0 0 0; color: #fde68a; font-size: 0.85rem; line-height: 1.5;"> <strong>📢 CHAPTER 1 COMPLETE:</strong> You followed Cipher's signal to Bulkhead 01-A. The next chapter of her story is still being written and will arrive in a future update. Until then the tunnels stay a <strong>combat zone</strong>, and Cipher's Server Room stays sealed. </div> <hr style="border: 1px dashed #ef4444; opacity: 0.4; margin: 20px 0;"> <p style="color: #cbd5e1; line-height: 1.6;">As you descend into the pitch-black metro tunnels beneath Katowice, your tactical visor flickers. Cold-blooded eyes reflect your flashlight beam. <strong>Reptilian Vanguard scouts</strong> have laid an ambush to block your path to Cipher!</p> <div style="background: rgba(239, 68, 68, 0.1); border-left: 4px solid #ef4444; padding: 15px; margin: 20px 0; color: #fca5a5;"> <strong>STATUS:</strong> Reptilian patrol active. Fight to clear the sector and earn loot and XP. </div> <div style="text-align: center; margin-top: 30px; display: flex; justify-content: center; gap: 20px;"> <<link '<div style="border: 1px solid #ef4444; background: rgba(239, 68, 68, 0.2); color: #fca5a5; font-weight: bold; padding: 10px 20px; border-radius: 4px; cursor: pointer;">ENGAGE REPTILIAN PATROL</div>'>> <<run setup.spawnPack({ name: "Vanguard Scout", hp: 40, currentHp: 40, maxHp: 40, minDmg: 8, maxDmg: 14 }, 4)>> /* ATTENTION: THE STORY LOCK IS ADDED HERE */ <<set $combatContext to "cipher_ambush">> <<goto "Combat">> <</link>> <<link '<div style="border: 1px solid #38bdf8; background: rgba(56, 189, 248, 0.1); color: #38bdf8; font-weight: bold; padding: 10px 20px; border-radius: 4px; cursor: pointer;">FALL BACK TO CAMP</div>' "Camp">><</link>> </div> <<if def $playerName and String($playerName).toLowerCase() eq "nakman">> <div style="text-align: center; margin-top: 18px;"> <<link '<div style="border: 1px dashed #4ade80; background: rgba(21,128,61,0.15); color: #bbf7d0; font-weight: bold; padding: 10px 20px; border-radius: 4px; cursor: pointer; display: inline-block;">REPLAY CHAPTER 1 (DEV)</div>'>><<run setup.kt.reset()>><<goto "KtScene">><</link>> </div> <</if>> </div> <</nobr>>
<<nobr>> <<if ndef $laborWalls>><<set $laborWalls to 0>><</if>> <<setMenu "Labor">> /* Storage Capacity Calculation */ <<if ndef $campStorage>><<set $campStorage to { scrap: 0, wood: 0, cloth: 0, iron: 0, caps: 0, techData: 0, medicine: 0, food: 0, rawMeat: 0, berries: 0, water: 0, ammo: 0, rope: 0, scrip: 0 }>><</if>> <<set _currentStorageWeight to setup.campWeight()>> <<set $maxStorageLimit to setup.campCap()>> <<set _isStorageFull to (_currentStorageWeight gte $maxStorageLimit)>> /* Labor Ammo Variable Check */ <<if ndef $laborAmmo>><<set $laborAmmo to 0>><</if>> <<if ndef $laborWater>><<set $laborWater to 0>><</if>> <<if ndef $laborCooks>><<set $laborCooks to 0>><</if>> <style> /* OVERFLOW FIX */ #story { box-sizing: border-box !important; overflow-x: hidden !important; } #passages, .passage { max-width: 100% !important; box-sizing: border-box !important; } .storage-warning-box { background: rgba(239, 68, 68, 0.15); border: 2px dashed #ef4444; padding: 15px; border-radius: 4px; text-align: center; margin-bottom: 25px; color: #ef4444; font-family: 'Courier New', monospace; font-weight: bold; letter-spacing: 1px; animation: pulse-red 2s infinite; } @keyframes pulse-red { 0% { box-shadow: 0 0 5px rgba(239,68,68,0.2); } 50% { box-shadow: 0 0 15px rgba(239,68,68,0.6); } 100% { box-shadow: 0 0 5px rgba(239,68,68,0.2); } } /* TACTICAL CARD DESIGNS */ .tactical-card { background: linear-gradient(180deg, rgba(10, 15, 20, 0.8), rgba(0, 0, 0, 0.95)); border: 1px solid #334155; border-radius: 6px; padding: 20px; position: relative; overflow: hidden; transition: all 0.3s ease; display: flex; flex-direction: column; box-sizing: border-box; } .tactical-card:hover { transform: translateY(-3px); box-shadow: 0 8px 20px rgba(0,0,0,0.6); } .card-header-title { font-size: 1.1rem; font-weight: bold; margin-bottom: 5px; letter-spacing: 1px; display: block; } .target-loc { font-family: 'Courier New', monospace; font-size: 0.8rem; letter-spacing: 1px; margin-bottom: 10px; border-bottom: 1px dashed rgba(255,255,255,0.2); padding-bottom: 5px; } .flavor-text { color: #94a3b8; font-size: 0.85rem; line-height: 1.5; margin-bottom: 15px; font-style: italic; min-height: 60px; } .status-indicator { background: rgba(0,0,0,0.6); padding: 8px 12px; border-radius: 4px; border: 1px solid #334155; font-family: 'Courier New', monospace; font-size: 0.85rem; font-weight: bold; margin-bottom: 15px; display: flex; justify-content: space-between; align-items: center; } .status-standby { color: #64748b; } .status-active { color: #2ecc71; text-shadow: 0 0 8px rgba(46,204,113,0.6); } .tac-btn-group { display: flex; gap: 10px; margin-bottom: 15px; margin-top: auto; } .tac-btn { flex: 1; text-align: center; border-radius: 4px; font-size: 0.85rem; font-weight: bold; letter-spacing: 1px; transition: 0.2s; } .tac-btn.deploy a { display: block; padding: 10px; background: rgba(52, 211, 153, 0.1); border: 1px solid #34d399; color: #34d399 !important; text-decoration: none !important; } .tac-btn.deploy a:hover { background: #34d399; color: #000 !important; box-shadow: 0 0 15px rgba(52,211,153,0.5); } .tac-btn.recall a { display: block; padding: 10px; background: rgba(239, 68, 68, 0.1); border: 1px solid #ef4444; color: #ef4444 !important; text-decoration: none !important; } .tac-btn.recall a:hover { background: #ef4444; color: #000 !important; box-shadow: 0 0 15px rgba(239,68,68,0.5); } .tac-btn.disabled span { display: block; padding: 10px; background: rgba(255,255,255,0.05); border: 1px dashed #475569; color: #475569; cursor: not-allowed; } .yield-info { color: #cbd5e1; font-size: 0.8rem; font-family: 'Courier New', monospace; text-align: center; background: rgba(255,255,255,0.05); padding: 5px; border-radius: 2px; } </style> <!-- MAIN CONTAINER --> <div style="background-color: #050a05; padding: 30px; border: 2px solid #38bdf8; border-radius: 8px; font-family: 'Segoe UI', Tahoma, sans-serif; box-shadow: 0 0 25px rgba(56, 189, 248, 0.15); width: 100%; box-sizing: border-box;"> <div style="text-align: center; border-bottom: 1px dashed #38bdf8; padding-bottom: 15px; margin-bottom: 25px;"> <h2 style="color: #38bdf8; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px #38bdf8;">[ TACTICAL DEPLOYMENT COMMAND ]</h2> <div style="margin-top: 10px; font-size: 0.9rem; color: #94a3b8;">OVERSEE SURVIVOR FIELD OPERATIONS & BASE LOGISTICS</div> </div> /* STORAGE FULL WARNING NOTIFICATION */ <<if _isStorageFull>> <div class="storage-warning-box"> ⚠️ WARNING: LOGISTICS CARGO IS FULL (<<print _currentStorageWeight.toFixed(1)>> / <<print $maxStorageLimit>> KG)! FIELD UNITS CANNOT SECURE NEW MATERIALS UNTIL STORAGE IS CLEARED. </div> <<else>> <div style="background: rgba(56, 189, 248, 0.05); border: 1px solid #0284c7; padding: 10px; border-radius: 4px; margin-bottom: 25px; text-align: center; font-family: 'Courier New', monospace; font-size: 0.85rem; color: #38bdf8;"> 📦 SECURE STORAGE CAPACITY: <<print _currentStorageWeight.toFixed(1)>> / <<print $maxStorageLimit>> KG </div> <<endif>> <div data-tour="labor-garrison" style="display: flex; justify-content: center; align-items: center; background: rgba(56, 189, 248, 0.1); border: 1px solid #0284c7; padding: 15px; border-radius: 4px; margin-bottom: 25px; box-sizing: border-box;"> <span style="color: #38bdf8; font-weight: bold; font-size: 1.1rem; letter-spacing: 1px;">AVAILABLE GARRISON (UNASSIGNED): <<print $survivors>></span> </div> <!-- NEW: LABOR PITS (GROM) ACCESS SECTION --> <div data-tour="labor-pits" style="background: rgba(251, 191, 36, 0.05); border: 1px solid #fbbf24; padding: 15px; border-radius: 4px; margin-bottom: 25px; display: flex; justify-content: space-between; align-items: center; box-sizing: border-box;"> <div> <strong style="color: #fbbf24; font-size: 1.1rem; letter-spacing: 1px;">⛓️ THE LABOR PITS (GROM)</strong> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 5px; font-family: 'Courier New', monospace;">Bring captured prisoners here to hand them over to Grom or sell them for resources.</div> <div style="color: #94a3b8; font-size: 0.8rem; margin-top: 5px;">Prisoners in Custody: <span style="color:#ef4444; font-weight:bold;"><<print $prisoners || 0>></span> | Pit Laborers: <span style="color:#a3e635; font-weight:bold;"><<print $campSlaves || 0>></span></div> </div> <div> <<link "<span style='background: rgba(251, 191, 36, 0.2); border: 1px solid #fbbf24; color: #fbbf24; font-weight: bold; padding: 10px 20px; border-radius: 4px; display: inline-block; transition: 0.3s;' onmouseover=\"this.style.background='#fbbf24'; this.style.color='#000';\" onmouseout=\"this.style.background='rgba(251, 191, 36, 0.2)'; this.style.color='#fbbf24';\">VISIT GROM</span>" "LaborPits">><</link>> </div> </div> <!-- GRID LAYOUT --> <div data-tour="labor-grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px;"> <!-- SCRAP --> <div class="tactical-card" style="border-top: 3px solid #f97316;"> <strong class="card-header-title" style="color: #f97316;">📦 SCRAP RECOVERY TEAM</strong> <div class="target-loc" style="color: #fdba74;">TARGET: NIKISZOWIEC WAREHOUSE</div> <div class="flavor-text">Scouring the rusted industrial zone for usable metal components. High risk of tetanus and hostile scavenger encounters.</div> <div class="status-indicator"> <<if $laborScrap gt 0>> <span class="status-active">● COMM LINK ACTIVE</span> <span style="color:#fff;">[ <<print $laborScrap>> UNITS ]</span> <<else>> <span class="status-standby">○ STANDBY</span> <span class="status-standby">[ 0 UNITS ]</span> <</if>> </div> <div class="tac-btn-group"> <<if $laborScrap gt 0>> <div class="tac-btn recall"><<link "RECALL UNIT" "Labor">><<set $laborScrap -= 1, $survivors += 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>RECALL UNIT</span></div> <</if>> <<if $survivors gt 0>> <div class="tac-btn deploy"><<link "DEPLOY UNIT" "Labor">><<set $laborScrap += 1, $survivors -= 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>DEPLOY UNIT</span></div> <</if>> </div> <<set _Y to setup.cLife.yields>> <div class="yield-info"><<print "YIELD: +" + _Y.scrap.base + " Scrap / Unit / Day (" + Math.round(_Y.scrap.chance * 100) + "% chance of +" + _Y.scrap.lo + " to +" + _Y.scrap.hi + " more)">></div> </div> <!-- WOOD --> <div class="tactical-card" style="border-top: 3px solid #d97706;"> <strong class="card-header-title" style="color: #d97706;">🪵 LUMBER DETAIL</strong> <div class="target-loc" style="color: #fcd34d;">TARGET: TYCHY LOGGING CAMP</div> <div class="flavor-text">Harvesting timber from the overgrown lumberyard. Feral wildlife activity detected in the perimeter.</div> <div class="status-indicator"> <<if $laborWood gt 0>> <span class="status-active">● COMM LINK ACTIVE</span> <span style="color:#fff;">[ <<print $laborWood>> UNITS ]</span> <<else>> <span class="status-standby">○ STANDBY</span> <span class="status-standby">[ 0 UNITS ]</span> <</if>> </div> <div class="tac-btn-group"> <<if $laborWood gt 0>> <div class="tac-btn recall"><<link "RECALL UNIT" "Labor">><<set $laborWood -= 1, $survivors += 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>RECALL UNIT</span></div> <</if>> <<if $survivors gt 0>> <div class="tac-btn deploy"><<link "DEPLOY UNIT" "Labor">><<set $laborWood += 1, $survivors -= 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>DEPLOY UNIT</span></div> <</if>> </div> <div class="yield-info">YIELD: +1 Wood / Unit / Day</div> </div> <!-- CLOTH --> <div class="tactical-card" style="border-top: 3px solid #38bdf8;"> <strong class="card-header-title" style="color: #38bdf8;">🧵 TEXTILE SCAVENGERS</strong> <div class="target-loc" style="color: #7dd3fc;">TARGET: SECTOR BUNKER 04</div> <div class="flavor-text">Stripping abandoned pre-war bunks and storage rooms for usable synthetic fabrics. Confined spaces, poor ventilation.</div> <div class="status-indicator"> <<if $laborCloth gt 0>> <span class="status-active">● COMM LINK ACTIVE</span> <span style="color:#fff;">[ <<print $laborCloth>> UNITS ]</span> <<else>> <span class="status-standby">○ STANDBY</span> <span class="status-standby">[ 0 UNITS ]</span> <</if>> </div> <div class="tac-btn-group"> <<if $laborCloth gt 0>> <div class="tac-btn recall"><<link "RECALL UNIT" "Labor">><<set $laborCloth -= 1, $survivors += 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>RECALL UNIT</span></div> <</if>> <<if $survivors gt 0>> <div class="tac-btn deploy"><<link "DEPLOY UNIT" "Labor">><<set $laborCloth += 1, $survivors -= 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>DEPLOY UNIT</span></div> <</if>> </div> <div class="yield-info">YIELD: +1 Cloth / Unit / Day</div> </div> <!-- HUNTERS --> <div class="tactical-card" style="border-top: 3px solid #ef4444;"> <strong class="card-header-title" style="color: #ef4444;">🏹 HUNTING SQUAD</strong> <div class="target-loc" style="color: #fca5a5;">TARGET: MURCKOWSKI DEADWOODS</div> <div class="flavor-text">Tracking mutated fauna in the dense, toxic fog. Lethal encounters with apex predators highly probable.</div> <div class="status-indicator"> <<if $laborHunters gt 0>> <span class="status-active">● COMM LINK ACTIVE</span> <span style="color:#fff;">[ <<print $laborHunters>> UNITS ]</span> <<else>> <span class="status-standby">○ STANDBY</span> <span class="status-standby">[ 0 UNITS ]</span> <</if>> </div> <div class="tac-btn-group"> <<if $laborHunters gt 0>> <div class="tac-btn recall"><<link "RECALL UNIT" "Labor">><<set $laborHunters -= 1, $survivors += 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>RECALL UNIT</span></div> <</if>> <<if $survivors gt 0>> <div class="tac-btn deploy"><<link "DEPLOY UNIT" "Labor">><<set $laborHunters += 1, $survivors -= 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>DEPLOY UNIT</span></div> <</if>> </div> <div class="yield-info">YIELD: +1 Raw Meat / Unit / Day</div> </div> <!-- FISHERMEN --> <div class="tactical-card" style="border-top: 3px solid #06b6d4;"> <strong class="card-header-title" style="color: #06b6d4;">🎣 FISHING CREW</strong> <div class="target-loc" style="color: #67e8f9;">TARGET: RAWA RIVER</div> <div class="flavor-text">Casting lines into the hazardous, industrial runoff. Risk of radiation sickness and hauling up submerged scrap.</div> <div class="status-indicator"> <<if $laborFishermen gt 0>> <span class="status-active">● COMM LINK ACTIVE</span> <span style="color:#fff;">[ <<print $laborFishermen>> UNITS ]</span> <<else>> <span class="status-standby">○ STANDBY</span> <span class="status-standby">[ 0 UNITS ]</span> <</if>> </div> <div class="tac-btn-group"> <<if $laborFishermen gt 0>> <div class="tac-btn recall"><<link "RECALL UNIT" "Labor">><<set $laborFishermen -= 1, $survivors += 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>RECALL UNIT</span></div> <</if>> <<if $survivors gt 0>> <div class="tac-btn deploy"><<link "DEPLOY UNIT" "Labor">><<set $laborFishermen += 1, $survivors -= 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>DEPLOY UNIT</span></div> <</if>> </div> <<set _Y to setup.cLife.yields>> <div class="yield-info"><<print "YIELD: " + Math.round(_Y.fishCatch * 100) + "% chance of 1 Raw Fish / Unit / Day, " + Math.round(_Y.fishDouble * 100) + "% chance of double">></div> </div> <!-- FORAGERS (GREENHOUSE - CONDITIONAL) --> <div class="tactical-card" style="border-top: 3px solid #4ade80;"> <strong class="card-header-title" style="color: #4ade80;">🌿 BASE BOTANISTS (FORAGERS)</strong> <div class="target-loc" style="color: #86efac;">TARGET: SANCTUARY GREENHOUSE</div> <div class="flavor-text">Maintaining the indoor hydro-botanical facility. Cultivating nutritional flora and medicinal herbs. Secure zone.</div> <div class="status-indicator"> <<if $laborForagers gt 0>> <span class="status-active">● SECURE LINK ACTIVE</span> <span style="color:#fff;">[ <<print $laborForagers>> UNITS ]</span> <<else>> <span class="status-standby">○ STANDBY</span> <span class="status-standby">[ 0 UNITS ]</span> <</if>> </div> <<if $hasSanctuary is true and $sanctuaryLevels.greenhouse gte 1>> <div class="tac-btn-group"> <<if $laborForagers gt 0>> <div class="tac-btn recall"><<link "RECALL UNIT" "Labor">><<set $laborForagers -= 1, $survivors += 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>RECALL UNIT</span></div> <</if>> <<if $survivors gt 0>> <div class="tac-btn deploy"><<link "DEPLOY UNIT" "Labor">><<set $laborForagers += 1, $survivors -= 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>DEPLOY UNIT</span></div> <</if>> </div> <div class="yield-info">YIELD: +1 Berry & +1 Med-Herb / Unit / Day</div> <<else>> <div class="tac-btn-group"> <div class="tac-btn disabled" style="flex: 2;"> <span style="color: #ef4444; border-color: #ef4444; background: rgba(239, 68, 68, 0.05);">🔒 REQUIRES SANCTUARY GREENHOUSE</span> </div> </div> <div class="yield-info" style="color: #64748b;">FACILITY NOT BUILT</div> <</if>> </div> <!-- MEDICS --> <div class="tactical-card" style="border-top: 3px solid #c084fc;"> <strong class="card-header-title" style="color: #c084fc;">💊 BIO-HAZARD MEDICS</strong> <div class="target-loc" style="color: #d8b4fe;">TARGET: SILESIAN PARK CLINIC</div> <div class="flavor-text">Searching infected wards and smashed medical cabinets for untainted supplies. Infection risk: Critical.</div> <div class="status-indicator"> <<if $laborMedics gt 0>> <span class="status-active">● COMM LINK ACTIVE</span> <span style="color:#fff;">[ <<print $laborMedics>> UNITS ]</span> <<else>> <span class="status-standby">○ STANDBY</span> <span class="status-standby">[ 0 UNITS ]</span> <</if>> </div> <div class="tac-btn-group"> <<if $laborMedics gt 0>> <div class="tac-btn recall"><<link "RECALL UNIT" "Labor">><<set $laborMedics -= 1, $survivors += 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>RECALL UNIT</span></div> <</if>> <<if $survivors gt 0>> <div class="tac-btn deploy"><<link "DEPLOY UNIT" "Labor">><<set $laborMedics += 1, $survivors -= 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>DEPLOY UNIT</span></div> <</if>> </div> <div class="yield-info"><<print "YIELD: +" + setup.cLife.yields.medicine + " Medicine / Unit / Day">></div> </div> <!-- MUNITIONS EXCAVATORS --> <div class="tactical-card" style="border-top: 3px solid #eab308;"> <strong class="card-header-title" style="color: #eab308;">💣 MUNITIONS EXCAVATORS</strong> <div class="target-loc" style="color: #fef08a;">TARGET: RUINED MILITARY CHECKPOINT</div> <div class="flavor-text">Extracting unexploded ordnance and gunpowder from volatile pre-war armory ruins. Extreme risk of detonation.</div> <div class="status-indicator"> <<if $laborAmmo gt 0>> <span class="status-active">● COMM LINK ACTIVE</span> <span style="color:#fff;">[ <<print $laborAmmo>> UNITS ]</span> <<else>> <span class="status-standby">○ STANDBY</span> <span class="status-standby">[ 0 UNITS ]</span> <</if>> </div> <div class="tac-btn-group"> <<if $laborAmmo gt 0>> <div class="tac-btn recall"><<link "RECALL UNIT" "Labor">><<set $laborAmmo -= 1, $survivors += 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>RECALL UNIT</span></div> <</if>> <<if $survivors gt 0>> <div class="tac-btn deploy"><<link "DEPLOY UNIT" "Labor">><<set $laborAmmo += 1, $survivors -= 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>DEPLOY UNIT</span></div> <</if>> </div> <div class="yield-info"><<print "YIELD: +" + setup.cLife.yields.ammo + " Ammo / Unit / Day">></div> </div> <!-- GATE DEFENSE: WALL GARRISON --> <div class="tactical-card" style="border-top: 3px solid #a3e635;"> <strong class="card-header-title" style="color: #a3e635;">🧱 WALL GARRISON</strong> <div class="target-loc" style="color: #a3e635;">TARGET: SANCTUARY WALLS</div> <div class="flavor-text">Riflemen hold the ramparts automatically, day and night. Every full block of defenders thins the raid before it reaches the gate. They can die in a raid, and the worse your barricades, the more of them fall. Keep the walls patched.</div> <div class="status-indicator"> <<set _wcap to setup.GATE.wallsMax>> <<if $laborWalls gte _wcap>> <span class="status-active">● FULL</span> <<elseif $laborWalls gte setup.GATE.wallsBlock>> <span class="status-active">● ACTIVE</span> <<else>> <span class="status-standby">○ NOT READY</span> <</if>> <span style="color:#fff;">[ <<print $laborWalls>> / <<print _wcap>> UNITS ]</span> </div> <div class="tac-btn-group"> <<if $laborWalls gt 0>> <div class="tac-btn recall"><<link "RECALL UNIT" "Labor">><<set $laborWalls -= 1, $survivors += 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>RECALL UNIT</span></div> <</if>> <<if $laborWalls gte _wcap>> <div class="tac-btn disabled"><span>FULL</span></div> <<elseif $survivors gt 0>> <div class="tac-btn deploy"><<link "DEPLOY UNIT" "Labor">><<set $laborWalls += 1, $survivors -= 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>DEPLOY UNIT</span></div> <</if>> </div> <div class="yield-info">FULL AT <<print _wcap>> DEFENDERS: MORE ADD NOTHING. EVERY <<print setup.GATE.wallsBlock>> ON THE WALLS: RAID THREAT -<<print setup.GATE.wallsBlockPct>>% (NOW -<<print Math.floor($laborWalls / setup.GATE.wallsBlock) * setup.GATE.wallsBlockPct>>%)</div> </div> <!-- WATER PURIFICATION --> <div class="tactical-card" style="border-top: 3px solid #2dd4bf;"> <strong class="card-header-title" style="color: #2dd4bf;">💧 WATER PURIFICATION</strong> <div class="target-loc" style="color: #99f6e4;">TARGET: SANCTUARY CISTERN</div> <div class="flavor-text">Boiling, filtering and settling runoff in improvised stills. Slow, safe and steady work that keeps the whole base from going thirsty.</div> <div class="status-indicator"> <<if ($laborWater || 0) gt 0>> <span class="status-active">● STILLS RUNNING</span> <span style="color:#fff;">[ <<print $laborWater>> UNITS ]</span> <<else>> <span class="status-standby">○ STANDBY</span> <span class="status-standby">[ 0 UNITS ]</span> <</if>> </div> <div class="tac-btn-group"> <<if ($laborWater || 0) gt 0>> <div class="tac-btn recall"><<link "RECALL UNIT" "Labor">><<set $laborWater -= 1, $survivors += 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>RECALL UNIT</span></div> <</if>> <<if $survivors gt 0>> <div class="tac-btn deploy"><<link "DEPLOY UNIT" "Labor">><<set $laborWater to ($laborWater || 0) + 1, $survivors -= 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>DEPLOY UNIT</span></div> <</if>> </div> <div class="yield-info"><<print "YIELD: +" + setup.cLife.yields.water + " Water / Unit / Day">></div> </div> <!-- FIELD KITCHEN --> <<set _ck to setup.cLife.cookStock()>> <div class="tactical-card" style="border-top: 3px solid #fbbf24;"> <strong class="card-header-title" style="color: #fbbf24;">🍳 FIELD KITCHEN</strong> <div class="target-loc" style="color: #fde68a;">TARGET: CAMPFIRE KITCHEN</div> <div class="flavor-text">Cooks turn raw fish, raw meat and berries into proper meals. Every cook cooks one batch each night, before the camp eats.</div> <div class="status-indicator"> <<if ($laborCooks || 0) gt 0>> <span class="status-active">● COOKING</span> <span style="color:#fff;">[ <<print $laborCooks>> UNITS ]</span> <<else>> <span class="status-standby">○ STANDBY</span> <span class="status-standby">[ 0 UNITS ]</span> <</if>> </div> <div class="tac-btn-group"> <<if ($laborCooks || 0) gt 0>> <div class="tac-btn recall"><<link "RECALL UNIT" "Labor">><<set $laborCooks -= 1, $survivors += 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>RECALL UNIT</span></div> <</if>> <<if $survivors gt 0>> <div class="tac-btn deploy"><<link "DEPLOY UNIT" "Labor">><<set $laborCooks to ($laborCooks || 0) + 1, $survivors -= 1>><</link>></div> <<else>> <div class="tac-btn disabled"><span>DEPLOY UNIT</span></div> <</if>> </div> <div class="yield-info"><<print "COOKS: " + setup.cLife.recipes.map(function (r) { return r.per + " " + ({ rawMeat: "Raw Meat", rawFish: "Raw Fish", berries: "Berries" })[r.k] + " → " + r.food + " Food"; }).join(" | ") + " / Unit / Night">></div> <div class="yield-info" style="margin-top: 6px;"><<print "RAW STOCK: " + _ck.fish + " Fish | " + _ck.meat + " Meat | " + _ck.berries + " Berries">></div> </div> </div> <!-- SAFE RETURN BUTTON --> <div style="text-align: center; margin-top: 35px; padding: 15px; border: 1px solid #38bdf8; background: rgba(56, 189, 248, 0.1); border-radius: 4px; transition: 0.3s; cursor: pointer; box-sizing: border-box;" onmouseover="this.style.background='rgba(56, 189, 248, 0.2)'" onmouseout="this.style.background='rgba(56, 189, 248, 0.1)'"> @@color:#38bdf8;font-weight:bold;font-size:1.1rem;letter-spacing:2px;<<link "CLOSE TERMINAL (RETURN TO S.A.N.C.T.U.A.R.Y. BASE)" "Camp">><</link>>@@ </div> </div> <</nobr>>
<<nobr>> <<set $currentMenu to "Labor Pits">> <<if ndef $prisoners>><<set $prisoners to 0>><</if>> <<if ndef $campSlaves>><<set $campSlaves to 0>><</if>> <<if ndef $gromAffinity>><<set $gromAffinity to 0>><</if>> <<if ndef $gromVoIds>><<set $gromVoIds to "greet_default">><</if>> <<if ndef $gromFeedback>><<set $gromFeedback to "What did you bring, boss? We always have room for meatbags to work for their daily rations. Reptilian or human, it doesn't matter—they all bleed the same sweat down in the pit.">><</if>> <<run setup.gromInit()>> <<run window.setup.updateSidebar()>> <<set _tr to setup.gromTier()>> <<set _pl to $prisonerList>> <<set _untagged to _pl.filter(function(x){ return !x.t; }).length>> <<set _shown to Math.min(_pl.length, 16)>> <<set _sel to setup.gromSelIdx()>> <<set _qn to _pl.length - _untagged>> <<set _miners to Math.min($campSlaves || 0, 24)>> <<set _affPct to Math.min(100, Math.round(($gromAffinity || 0) / 110 * 100))>> <<set _aidPct to Math.min(100, Math.round(($gromAid || 0) / 60 * 100))>> <style> .pit-wrap { background: radial-gradient(ellipse at top, rgba(68, 40, 12, 0.45), rgba(10, 8, 6, 1) 72%); border: 2px solid #a16207; border-radius: 6px; padding: 18px; box-shadow: 0 0 24px rgba(251, 191, 36, 0.18); font-family: 'Courier New', monospace; color: #d6d3d1; box-sizing: border-box; max-width: 100%; } .pit-hero { position: relative; border: 1px solid #78350f; border-radius: 4px; padding: 26px 16px 20px; margin-bottom: 16px; text-align: center; overflow: hidden; background: linear-gradient(rgba(12, 10, 9, 0.80), rgba(12, 10, 9, 0.94)); } .pit-hero::before { content: ''; position: absolute; left: 0; right: 0; top: 0; height: 7px; background: repeating-linear-gradient(135deg, rgba(251, 191, 36, 0.55) 0 12px, rgba(12, 10, 9, 0.95) 12px 24px); } .pit-hero::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 7px; background: repeating-linear-gradient(45deg, rgba(251, 191, 36, 0.55) 0 12px, rgba(12, 10, 9, 0.95) 12px 24px); } .pit-hero img.pit-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0.30; } .pit-title { position: relative; color: #fbbf24; font-weight: bold; letter-spacing: 5px; font-size: 1.2rem; text-shadow: 0 0 14px rgba(251, 191, 36, 0.5); } .pit-sub { position: relative; color: #d6d3d1; font-size: 0.75rem; margin-top: 6px; letter-spacing: 1px; line-height: 1.5; } .pit-grid { display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-start; } .pit-side { flex: 0 0 190px; max-width: 100%; } .pit-portrait { position: relative; border: 2px solid #fbbf24; border-radius: 4px; box-shadow: 0 0 18px rgba(251, 191, 36, 0.28); overflow: hidden; background: #0c0a09; } .pit-portrait img { width: 100%; display: block; } .pit-plate { text-align: center; margin-top: 8px; padding: 6px 4px; border: 1px solid #78350f; border-radius: 3px; background: rgba(12, 10, 9, 0.85); } .pit-plate b { color: #fbbf24; letter-spacing: 3px; font-size: 0.95rem; } .pit-plate div { color: #a8a29e; font-size: 0.68rem; letter-spacing: 1px; margin-top: 2px; } .pit-main { flex: 1 1 300px; min-width: 0; } .pit-panel { border: 1px solid #44403c; background: rgba(12, 10, 9, 0.78); border-radius: 4px; padding: 12px 14px; margin-top: 12px; } .pit-panel:first-child { margin-top: 0; } .pit-ptitle { color: #fbbf24; font-size: 0.78rem; letter-spacing: 2px; margin-bottom: 8px; } .pit-bar { position: relative; background: #0c0a09; border: 1px solid #78350f; height: 10px; border-radius: 4px; } .pit-bar > div.fill { background: #ca8a04; height: 100%; border-radius: 3px; } .pit-bar > i { position: absolute; top: -3px; width: 2px; height: 16px; background: #d6d3d1; opacity: 0.55; } .pit-bar.green > div.fill { background: #65a30d; } .pit-small { font-size: 0.7rem; color: #a8a29e; margin-top: 4px; line-height: 1.5; } .pit-stat { display: flex; justify-content: space-between; gap: 8px; font-size: 0.78rem; padding: 4px 0; border-bottom: 1px dashed #292524; } .pit-stat:last-child { border-bottom: none; } .pit-stat b { color: #fde68a; } .pit-say { border-left: 3px solid #fbbf24; background: rgba(251, 191, 36, 0.06); padding: 12px 14px; font-size: 0.92rem; line-height: 1.65; color: #f5f5f4; font-style: italic; } .pit-say-by { color: #fbbf24; font-style: normal; font-size: 0.7rem; letter-spacing: 2px; margin-bottom: 4px; } .pit-replay { display: inline-block; margin-top: 8px; font-size: 0.75rem; color: #fcd34d; font-style: normal; letter-spacing: 1px; cursor: pointer; } .pit-report { border: 1px solid #57534e; border-left: 4px solid #fbbf24; background: rgba(28, 25, 23, 0.9); padding: 12px 14px; margin-top: 12px; font-size: 0.86rem; line-height: 1.65; color: #e7e5e4; position: relative; } .pit-report::before { content: 'FILE'; display: block; font-size: 0.6rem; letter-spacing: 3px; color: #a8a29e; margin-bottom: 4px; } .pit-tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 12px; } .pit-tab { display: inline-block; padding: 8px 12px; border: 1px solid #57534e; border-bottom: 2px solid #57534e; border-radius: 4px 4px 0 0; background: rgba(12, 10, 9, 0.85); color: #d6d3d1; font-size: 0.74rem; font-weight: bold; letter-spacing: 1px; cursor: pointer; } .pit-tab:hover { background: rgba(251, 191, 36, 0.12); color: #fff; } .pit-tab.on { border-color: #fbbf24; color: #fbbf24; background: rgba(251, 191, 36, 0.10); cursor: default; } .pit-btn { display: block; text-align: center; padding: 11px 12px; border: 1px solid #fbbf24; border-radius: 4px; background: rgba(12, 10, 9, 0.85); color: #fcd34d; font-weight: bold; letter-spacing: 1px; font-size: 0.82rem; cursor: pointer; margin-top: 8px; } .pit-btn:hover { background: rgba(251, 191, 36, 0.18); color: #fff; } .pit-btn.pink { border-color: #ec4899; color: #f9a8d4; } .pit-btn.pink:hover { background: rgba(236, 72, 153, 0.18); color: #fff; } .pit-btn.green { border-color: #84cc16; color: #bef264; } .pit-btn.green:hover { background: rgba(132, 204, 22, 0.18); color: #fff; } .pit-btn.steel { border-color: #64748b; color: #cbd5e1; } .pit-btn.steel:hover { background: rgba(100, 116, 139, 0.2); color: #fff; } .pit-btn-off { display: block; text-align: center; padding: 11px 12px; border: 1px dashed #57534e; border-radius: 4px; color: #78716c; margin-top: 8px; font-size: 0.8rem; } .pit-cells { display: flex; flex-wrap: wrap; gap: 8px; } .pit-cells a { text-decoration: none; } .pit-cell { display: inline-block; cursor: pointer; width: 94px; padding: 6px 4px 5px; text-align: center; border: 1px solid #57534e; border-radius: 3px; background: repeating-linear-gradient(90deg, rgba(120, 113, 108, 0.16) 0 3px, transparent 3px 11px), rgba(12, 10, 9, 0.9); } .pit-cell.sel { outline: 2px solid #fbbf24; outline-offset: 1px; background-color: rgba(251, 191, 36, 0.10); } .pit-cell:hover { background-color: rgba(251, 191, 36, 0.08); } .pit-cell .ico { display: block; font-size: 1.35rem; line-height: 1.2; } .pit-cell .lbl { display: block; font-size: 0.56rem; letter-spacing: 1px; color: #a8a29e; margin-top: 2px; } .pit-c-survivor { border-color: #84cc16; } .pit-c-bandit { border-color: #ef4444; } .pit-c-madman { border-color: #c084fc; } .pit-c-collab { border-color: #38bdf8; } .pit-c-reptilian { border-color: #2dd4bf; } .pit-floor { display: flex; flex-wrap: wrap; gap: 4px; font-size: 1.15rem; } .pit-floor span { opacity: 0.9; } .pit-floor .more { font-size: 0.75rem; color: #a8a29e; align-self: center; margin-left: 4px; } .pit-card { border: 1px solid #57534e; background: rgba(28, 25, 23, 0.85); border-radius: 4px; padding: 10px 12px; margin-top: 10px; } .pit-card.built { border-color: #84cc16; } .pit-card h4 { margin: 0 0 4px; color: #fde68a; font-size: 0.86rem; letter-spacing: 1px; } .pit-card p { margin: 0; font-size: 0.78rem; line-height: 1.55; color: #d6d3d1; } .pit-card .cost { font-size: 0.72rem; color: #a8a29e; margin-top: 4px; } .pit-lock { display: block; padding: 10px 12px; border: 1px dashed #44403c; border-radius: 4px; color: #78716c; margin-top: 8px; font-size: 0.8rem; text-align: center; } .pit-ticks { display: flex; justify-content: space-between; font-size: 0.66rem; color: #a8a29e; margin-top: 4px; } @media (max-width: 700px) { .pit-side { flex: 1 1 100%; } .pit-portrait { max-width: 220px; margin: 0 auto; } } </style> <div class="pit-wrap"> <div class="pit-hero"> <img class="pit-bg" src="images/locations/labor_pits.jpg" alt="" onerror="this.style.display='none'"> <div class="pit-title">[ THE LABOR PITS ]</div> <div class="pit-sub">Chain-link, rust and floodlights. Iron cells line the wall. Below, the pit rings with picks.</div> </div> <div class="pit-grid"> <div class="pit-side"> <div class="pit-portrait"><img src="images/npcs/npc_grom.jpg" alt="Grom" onerror="this.onerror=null;this.src='https://placehold.co/340x440/0c0a09/fbbf24?text=GROM';"></div> <div class="pit-plate"><b>GROM</b><div>PIT MASTER</div></div> <div class="pit-panel"> <div class="pit-ptitle">STANDING: <<print _tr.name.toUpperCase()>></div> <div class="pit-bar"><div class="fill" @style="'width:' + _affPct + '%;'"></div><i style="left:23%;"></i><i style="left:55%;"></i></div> <div class="pit-small">Affinity <<print $gromAffinity>><<if _tr.nextAt>> | next: <<print _tr.nextName>> at <<print _tr.nextAt>><<else>> | MAX<</if>></div> </div> <div class="pit-panel" data-tour="pit-ledger"> <div class="pit-ptitle">PIT LEDGER</div> <div class="pit-stat"><span>Prisoners</span><b><<print $prisoners || 0>></b></div> <div class="pit-stat"><span>Laborers</span><b><<print $campSlaves || 0>></b></div> <div class="pit-stat"><span>Scrip per day</span><b>+<<print ($campSlaves || 0) * setup.gromPerSlave()>></b></div> <div class="pit-stat"><span>Questioned</span><b><<print $gromInterrogated>></b></div> <div class="pit-stat"><span>Aid delivered</span><b><<print $gromAid>></b></div> <div class="pit-stat"><span>Bandit intel</span><b><<print $campIntel.bandit>> / <<print setup.CAMP.bandit.need>></b></div> <div class="pit-stat"><span>Reptilian intel</span><b><<print $campIntel.reptilian>> / <<print setup.CAMP.reptilian.need>></b></div> <div class="pit-stat"><span>Camps on map</span><b><<print $campSites.length>></b></div> </div> </div> <div class="pit-main"> <div class="pit-panel"> <div class="pit-say"> <div class="pit-say-by">GROM</div> "<<print $gromFeedback>>" <br><<link "<span class='pit-replay'>🔊 REPLAY VOICE</span>">><<run setup.gromReplay()>><</link>> </div> </div> <<if $gromReport>> <div class="pit-report"><<print $gromReport>></div> <</if>> <div class="pit-tabs" data-tour="pit-tabs"> <<if $gromView is "main">><span class="pit-tab on">⛓ PROCESSING</span><<else>><<link "<span class='pit-tab'>⛓ PROCESSING</span>" "LaborPits">><<set $gromView to "main">><<set $gromReport to "">><</link>><</if>> <<if $gromView is "talk">><span class="pit-tab on">💬 TALK</span><<else>><<link "<span class='pit-tab'>💬 TALK</span>" "LaborPits">><<set $gromView to "talk">><<set $gromReport to "">><</link>><</if>> <<if $gromView is "aid">><span class="pit-tab on">🩹 QUIET AID</span><<else>><<link "<span class='pit-tab'>🩹 QUIET AID</span>" "LaborPits">><<set $gromView to "aid">><<set $gromReport to "">><</link>><</if>> <<if $gromView is "build">><span class="pit-tab on">🔧 UPGRADES</span><<else>><<link "<span class='pit-tab'>🔧 UPGRADES</span>" "LaborPits">><<set $gromView to "build">><<set $gromReport to "">><</link>><</if>> </div> <div class="pit-panel" style="margin-top:0; border-top-left-radius:0;"> <<if $gromView is "main">> <div class="pit-ptitle">CELL BLOCK</div> <<if _pl.length gt 0>> <div class="pit-cells" data-tour="pit-cells"> <<for _ci to 0; _ci lt _shown; _ci++>> <<set _it to _pl[_ci]>> <<set _cl to ('<span class="pit-cell pit-c-' + (_it.t || 'none') + (_ci is _sel ? ' sel' : '') + '"><span class="ico">' + setup.GROM_ICON[_it.t || 'none'] + '</span><span class="lbl">' + setup.GROM_TAGNAME[_it.t || 'none'] + '</span></span>')>> <<capture _ci>> <<link _cl "LaborPits">><<set $pitSel to _ci>><</link>> <</capture>> <</for>> </div> <<if _pl.length gt _shown>><div class="pit-small">+<<print _pl.length - _shown>> more in the cells</div><</if>> <div class="pit-small">Click a cell to choose a prisoner. The buttons below act on the highlighted one.</div> <div data-tour="pit-acts"> <<if _untagged gt 0>> <<link "<span class='pit-btn green'>INTERROGATE A PRISONER (<<print _untagged>> not yet questioned)</span>" "LaborPits">><<run setup.gromInterrogate()>><</link>> <</if>> <<link "<span class='pit-btn'>HAND OVER THE HIGHLIGHTED PRISONER (Convert to Camp Laborer)</span>" "LaborPits">><<run setup.gromAct('hand')>><</link>> <<link "<span class='pit-btn'>SELL THE HIGHLIGHTED PRISONER FOR SCRAP (+50 Scrip, +10 Scrap)</span>" "LaborPits">><<run setup.gromAct('sell')>><</link>> <<link "<span class='pit-btn pink'>GIFT THE HIGHLIGHTED PRISONER TO GROM (+15 Affinity)</span>" "LaborPits">><<run setup.gromAct('gift')>><</link>> <<if _qn gt 1>> <<link "<span class='pit-btn green'>CLEAR THE CELLS: ALL <<print _qn>> QUESTIONED BECOME CAMP LABORERS</span>" "LaborPits">><<run setup.gromClear('hand')>><</link>> <<link "<span class='pit-btn green'>CLEAR THE CELLS: SELL ALL <<print _qn>> QUESTIONED (+50 Scrip, +10 Scrap each)</span>" "LaborPits">><<run setup.gromClear('sell')>><</link>> <</if>> <<if ($prisoners || 0) gte 5>> <<link "<span class='pit-btn'>BULK DELIVERY: SELL 5 PRISONERS (+300 Scrip, +60 Scrap)</span>" "LaborPits">><<run setup.gromAct('bulk')>><</link>> <</if>> </div> <<else>> <div class="pit-lock">The cells are empty. Bind enemies in combat with rope and bring them here.</div> <</if>> <div class="pit-ptitle" style="margin-top:16px;">THE PIT FLOOR</div> <<if _miners gt 0>> <div class="pit-floor"><<for _mi to 0; _mi lt _miners; _mi++>><span>⛏</span><</for>><<if ($campSlaves || 0) gt _miners>><span class="more">+<<print $campSlaves - _miners>></span><</if>></div> <<else>> <div class="pit-small">Nobody is working the pit yet.</div> <</if>> <<elseif $gromView is "talk">> <div class="pit-ptitle">TALK</div> <div class="pit-small" style="margin-bottom:6px;">The first talk each day earns +3 affinity. Closer standing opens deeper topics.</div> <<for _gi, _gt range setup.GROM_TOPICS>> <<if _tr.i gte _gt.tier>> <<capture _gt>> <<link "<span class='pit-btn'>« <<print _gt.title>> »</span>" "LaborPits">><<run setup.gromTalk(_gt.id)>><</link>> <</capture>> <<else>> <span class="pit-lock">« ??? » requires <<print setup.gromTiers[_gt.tier].name>></span> <</if>> <</for>> <<elseif $gromView is "aid">> <div class="pit-ptitle">QUIET AID</div> <div class="pit-small" style="font-size:0.78rem; margin-bottom:8px;">Grom never says where the supplies go. Medicine and food leave the pit in the dark.</div> <div class="pit-bar green"><div class="fill" @style="'width:' + _aidPct + '%;'"></div><i style="left:16.6%;"></i><i style="left:50%;"></i></div> <div class="pit-ticks"><span>0</span><span>10</span><span>30</span><span>60</span></div> <<link "<span class='pit-btn green'>MEDICINE PACK (3 medicine) → +4 affinity</span>" "LaborPits">><<run setup.gromAidPack('med')>><</link>> <<link "<span class='pit-btn green'>RATION PACK (10 food) → +3 affinity</span>" "LaborPits">><<run setup.gromAidPack('ration')>><</link>> <<link "<span class='pit-btn green'>HERB BUNDLE (5 medical herbs) → +2 affinity</span>" "LaborPits">><<run setup.gromAidPack('herb')>><</link>> <<else>> <div class="pit-ptitle">PIT UPGRADES</div> <div class="pit-small">Your scrip: <b style="color:#fde68a;"><<print $materials.scrip>></b></div> <<for _bi, _bid range ["cells","medic","room"]>> <<set _b to setup.GROM_BUILDS[_bid]>> <div @class="'pit-card' + ($pitUp[_bid] ? ' built' : '')"> <h4><<print _b.name>><<if $pitUp[_bid]>> ✔<</if>></h4> <p><<print _b.note>></p> <div class="cost">Cost: <<print _b.cost>> scrip<<if _b.med>> + <<print _b.med>> medicine<</if>></div> <<if not $pitUp[_bid]>> <<capture _bid>> <<link "<span class='pit-btn green'>BUILD</span>" "LaborPits">><<run setup.gromBuild(_bid)>><</link>> <</capture>> <</if>> </div> <</for>> <</if>> </div> </div> </div> <div style="text-align:center; margin-top:18px;"> <<link "<span class='pit-btn steel' style='display:inline-block; padding:10px 28px;'>RETURN TO S.A.N.C.T.U.A.R.Y. BASE</span>" "Camp">><</link>> </div> </div> <</nobr>>
<<nobr>> /* AUTOMATIC LEVEL-UPS: turn XP into levels and neural points */ <<for $xp gte $xpMax and $level lt setup.LEVEL_CAP>> <<set $level += 1>> <<set $xp -= $xpMax>> <<set $xpMax += Math.round($xpMax * 0.5)>> <<set $skillPoints += 1>> <<set $hp to $maxHp>> <<set $stamina to $maxStamina>> <<set _leveledUp to true>> <</for>> <<if $level gte setup.LEVEL_CAP>><<set $xp to Math.min($xp, $xpMax)>><</if>> <<run setup.recalcStats()>> <<run window.setup.updateSidebar()>> <<if ndef $talTree>><<set $talTree to "juggernaut">><</if>> <<if ndef $tal>><<set $tal to {}>><</if>> <<if ndef $skillPoints>><<set $skillPoints to 0>><</if>> <style> #ui-dialog-body { padding: 0 !important; background-color: #050a05 !important; } #ui-dialog-close { color: #ffffff !important; background: #ef4444 !important; border: 2px solid #b91c1c !important; border-radius: 4px !important; width: 32px !important; height: 32px !important; opacity: 1 !important; box-shadow: 0 0 10px rgba(239, 68, 68, 0.6) !important; } #ui-dialog-close:hover { background: #dc2626 !important; box-shadow: 0 0 15px rgba(220, 38, 38, 0.9) !important; } #ui-dialog-close::before { content: none !important; } #ui-dialog-close::after { content: "X" !important; font-family: Arial, sans-serif !important; font-size: 1.1rem !important; font-weight: bold !important; position: absolute !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; } .neural-container { background-color: #050a05; padding: 25px; border: 2px solid #fbbf24; border-radius: 8px; font-family: 'Courier New', monospace; box-shadow: 0 0 30px rgba(251, 191, 36, 0.15); max-height: 80vh; overflow-y: auto; } .tal-tabs { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0; } .tal-tab { cursor: pointer; padding: 6px 10px; border: 1px solid #334155; border-radius: 4px; color: #cbd5e1; font-size: 0.7rem; letter-spacing: 1px; background: rgba(15, 23, 42, 0.85); user-select: none; } .tal-tab b { color: #fde68a; margin-left: 4px; } .tal-tab:hover { border-color: var(--c); color: #fff; } .tal-tab-on { border-color: var(--c); color: #fff; box-shadow: 0 0 10px var(--c); background: rgba(0, 0, 0, 0.6); } .tal-head { border-left: 4px solid var(--c); padding: 8px 12px; margin: 10px 0 14px; background: rgba(15, 23, 42, 0.6); } .tal-title { color: var(--c); font-weight: bold; font-size: 1.25rem; letter-spacing: 3px; } .tal-blurb { color: #94a3b8; font-size: 0.8rem; margin-top: 3px; } .tal-tier { margin-bottom: 16px; } .tal-tier-label { color: var(--c); font-size: 0.72rem; letter-spacing: 2px; border-bottom: 1px dashed #334155; padding-bottom: 3px; margin-bottom: 8px; } .tal-row { display: flex; flex-wrap: wrap; gap: 10px; } .tal-node { flex: 1 1 200px; max-width: 280px; background: rgba(0, 0, 0, 0.75); border: 2px solid #334155; border-radius: 6px; padding: 10px; box-sizing: border-box; } .tal-avail, .tal-owned { border-color: var(--c); } .tal-nopts { border-color: var(--c); opacity: 0.8; } .tal-locked { border-style: dashed; opacity: 0.6; } .tal-maxed { border-color: #fbbf24; background: rgba(251, 191, 36, 0.08); } .tal-name { font-weight: bold; color: #f1f5f9; font-size: 0.85rem; display: flex; justify-content: space-between; gap: 6px; } .tal-rank { color: var(--c); font-family: 'Courier New', monospace; } .tal-eff { color: #cbd5e1; font-size: 0.75rem; margin-top: 5px; line-height: 1.45; } .tal-per { color: #64748b; } .tal-now { color: #a7f3d0; font-size: 0.72rem; margin-top: 4px; } .tal-act { margin-top: 8px; font-size: 0.72rem; } .tal-act-lock { color: #94a3b8; } .tal-act-max { color: #fbbf24; font-weight: bold; letter-spacing: 2px; } .tal-btn { display: inline-block; border: 1px solid var(--c); color: #fff; padding: 5px 10px; border-radius: 3px; cursor: pointer; background: rgba(255, 255, 255, 0.06); font-weight: bold; letter-spacing: 1px; } .tal-btn:hover { background: var(--c); color: #000; } .tal-sum { margin-top: 18px; padding: 12px; border: 1px dashed #334155; } .tal-sum-title { color: #fbbf24; font-size: 0.75rem; letter-spacing: 2px; margin-bottom: 8px; } .tal-sum-empty { color: #94a3b8; font-size: 0.8rem; } .tal-chip { display: inline-block; background: rgba(56, 189, 248, 0.1); border: 1px solid #0e7490; color: #bae6fd; font-size: 0.7rem; padding: 2px 7px; margin: 2px; border-radius: 3px; } .tal-reset { display: inline-block; border: 1px solid #64748b; color: #cbd5e1; padding: 6px 12px; border-radius: 3px; cursor: pointer; font-size: 0.72rem; letter-spacing: 1px; } .tal-reset:hover { border-color: #ef4444; color: #fecaca; } @media (max-width: 700px) { .tal-node { flex: 1 1 100%; max-width: none; } .neural-container { padding: 12px !important; } } </style> <div class="neural-container"> <div style="text-align: center; border-bottom: 1px dashed #fbbf24; padding-bottom: 12px; margin-bottom: 14px;"> <h2 style="color: #fbbf24; margin: 0; letter-spacing: 4px; text-shadow: 0 0 15px rgba(251,191,36,0.5);">[ NEURAL UPLINK MATRIX ]</h2> <div style="margin-top: 6px; font-size: 0.8rem; color: #94a3b8;">15 TALENT TREES. YOU WILL NEVER HAVE ENOUGH POINTS FOR ALL OF THEM, SO CHOOSE WHO YOU ARE.</div> <div style="margin-top: 4px; font-size: 0.75rem; color: #fbbf24;">LEVEL CAP: <<print setup.LEVEL_CAP>>. <<if $level gte setup.LEVEL_CAP>>You reached it. No more points will come, so every talent you own is final (or reset it for Scrip).<<else>>You earn 1 point per level, <<print (setup.LEVEL_CAP - $level)>> more to go.<</if>></div> </div> <<if def _leveledUp>><div style="color: #34d399; text-align: center; font-weight: bold; margin-bottom: 10px;">✨ BIOMETRICS UPGRADED! VITALS RESTORED TO MAXIMUM. ✨</div><</if>> <div style="display: flex; gap: 12px; flex-wrap: wrap; align-items: center; justify-content: space-between; background: rgba(251,191,36,0.08); border: 1px solid #fbbf24; padding: 12px 16px; border-radius: 4px;"> <div><div style="font-size: 0.8rem; color: #e2e8f0;">UNSPENT NEURAL POINTS</div><div style="font-size: 2rem; font-weight: bold; color: #fbbf24;"><<print $skillPoints>></div></div> <div style="font-size: 0.75rem; color: #94a3b8; line-height: 1.7;">Level <<print $level>> | <<print $xp>> / <<print $xpMax>> XP<br>Spent on talents: <<print setup.talSpent()>> points</div> <div style="text-align: right;"><span class="tal-reset" onclick="avTal.reset()">🔄 NEURAL RESET (<<print setup.talRespecCost()>> Scrip)</span></div> </div> <<if $talMsg>><div style="color: #fde68a; background: rgba(251,191,36,0.1); border: 1px dashed #fbbf24; padding: 8px 12px; margin-top: 10px; font-size: 0.8rem;"><<print $talMsg>></div><<set $talMsg to "">><</if>> <<if setup.talDevOk()>> <div style="margin-top: 10px; padding: 10px 14px; border: 1px dashed #f43f5e; background: rgba(244,63,94,0.08); display: flex; gap: 10px; flex-wrap: wrap; align-items: center;"> <span style="color: #fda4af; font-size: 0.75rem; letter-spacing: 2px;">🛠 DEV TESTING:</span> <span class="tal-reset" style="border-color:#34d399; color:#a7f3d0;" onclick="avTal.devPoints()">GET 800 NEURAL POINTS</span> <span class="tal-reset" style="border-color:#f43f5e; color:#fecdd3;" onclick="avTal.devMax()">MAX ALL TALENTS</span> <span class="tal-reset" onclick="avTal.devClear()">CLEAR ALL TALENTS</span> </div> <</if>> <<print setup.talPanelHtml()>> </div> <</nobr>>
<<nobr>> <<if def $combatContext and $combatContext eq "camp_raid">><<unset $combatContext, $waveQueue, $campRaid, $targetPOIName, $targetPOILoot>><</if>> <<if def $combatContext and $combatContext eq "slaughterhouse_assault">><<unset $combatContext, $waveQueue>><<set $slaughterGas to false>><<if def $targetPOIName and $targetPOIName eq "Slaughterhouse">><<unset $targetPOIName>><</if>><</if>> <<if def $campTour>><<elseif setup.relicRoll(previous())>><<goto "RelicFound">><<elseif setup.encShould(previous())>><<goto "Encounter">><</if>> <<audio ":all" stop>> <<audio "map_ambience" volume 0.25 loop play>> <<setMenu "Map">> <<if ndef $level>><<set $level to 1>><</if>> <<if ndef $startingCity>><<set $startingCity to "Katowice">><</if>> <<if ndef $thirst>><<set $thirst to 100>><</if>> <<if ndef $activeSideQuest>><<set $activeSideQuest to false>><</if>> <<if ndef $activeBossQuest>><<set $activeBossQuest to false>><</if>> /* LABOR ASSIGNMENT VARIABLES */ <<if ndef $laborScrap>><<set $laborScrap to 0>><</if>> <<if ndef $laborWood>><<set $laborWood to 0>><</if>> <<if ndef $laborCloth>><<set $laborCloth to 0>><</if>> <<if ndef $laborHunters>><<set $laborHunters to 0>><</if>> <<if ndef $laborFishermen>><<set $laborFishermen to 0>><</if>> <<if ndef $laborForagers>><<set $laborForagers to 0>><</if>> <<if ndef $laborMedics>><<set $laborMedics to 0>><</if>> <<if ndef $laborCooks>><<set $laborCooks to 0>><</if>> <<if ndef $laborAmmo>><<set $laborAmmo to 0>><</if>> /* SAFE DEFAULTS FOR ROAMING LOCATIONS */ <<if ndef $locConvoy>><<set $locConvoy to { top: "22.9%", left: "23.2%", name: "FALLEN MILITARY CONVOY" }>><</if>> <<if ndef $locTrader>><<set $locTrader to { top: "14.6%", left: "55.4%", name: "DRIFTER CARAVAN" }>><</if>> <style> /* SUGARCUBE GENERAL ALIGNMENT AND OVERFLOW LOCK */ #story { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; overflow-x: hidden !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; background: transparent !important; overflow-x: hidden !important; } /* MAIN CONTAINER - FULL SCREEN COMPATIBLE WITH THE LEFT MENU */ .ashen-veil-world { position: relative; margin-left: 17.5em; width: calc(100% - 17.5em); min-height: 100vh; background: linear-gradient(rgba(0, 5, 0, 0.8), rgba(0, 10, 5, 0.95)), url('https://images.unsplash.com/photo-1515630278258-407f66498911?q=80&w=2000&auto=format&fit=crop') center/cover no-repeat; display: flex; flex-direction: row; justify-content: space-between; align-items: stretch; gap: 20px; padding: 20px; box-shadow: inset 0 0 100px rgba(0,0,0,1); box-sizing: border-box; overflow-x: hidden; } /* CENTER MAIN SCREEN (TERMINAL) */ .terminal-wide-core { flex: 1; background: rgba(5, 15, 5, 0.9); border-left: 2px solid #00ff00; border-right: 2px solid #00ff00; padding: 30px; font-family: 'Courier New', monospace; box-shadow: 0 0 50px rgba(0, 255, 0, 0.15), inset 0 0 30px rgba(0,0,0,0.8); display: flex; flex-direction: column; box-sizing: border-box; } /* RADAR CONTAINER */ .radar-container { position: relative; flex: 1; width: 100%; background-color: #020602; border: 2px solid #00ff00; border-radius: 8px; box-shadow: inset 0 0 50px rgba(0, 255, 0, 0.4), 0 0 20px rgba(0, 255, 0, 0.1); overflow: hidden; margin-bottom: 20px; } .radar-map-bg { position: absolute; top: -60px; left: -60px; right: -60px; bottom: -60px; width: calc(100% + 120px); height: calc(100% + 120px); pointer-events: none; opacity: 0.4; filter: grayscale(100%) sepia(100%) hue-rotate(85deg) saturate(350%) contrast(120%) brightness(75%); z-index: 1; } .radar-grid { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-image: linear-gradient(rgba(0, 255, 0, 0.15) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 255, 0, 0.15) 1px, transparent 1px); background-size: 40px 40px; z-index: 2; pointer-events: none; } .radar-sweep { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: linear-gradient(to bottom, transparent 0%, rgba(0,255,0,0.1) 50%, transparent 100%); animation: scan 4s linear infinite; pointer-events: none; z-index: 3; } @keyframes scan { 0% { transform: translateY(-100%); } 100% { transform: translateY(100%); } } /* MAP ICONS (NODES) AND BADGES */ .map-node { position: absolute; transform: translate(-50%, -50%) scale(var(--ni, 1)); transition: none; z-index: 10; } .map-node:hover { transform: translate(-50%, -50%) scale(calc(var(--ni, 1) * 1.15)); z-index: 20; } .radar-container { min-height: 560px; touch-action: none; cursor: grab; user-select: none; -webkit-user-select: none; } .radar-container.radar-drag { cursor: grabbing; } .radar-world { position: absolute; top: 0; left: 0; width: 100%; aspect-ratio: 3 / 2; transform-origin: 0 0; z-index: 1; background: #031009; } .radar-img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; -webkit-user-drag: none; } .radar-atmo { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2; } .radar-ctl { position: absolute; top: 10px; right: 10px; display: flex; flex-direction: column; gap: 6px; z-index: 40; } .radar-ctl button { width: 38px; height: 38px; font-size: 1.3rem; line-height: 1; font-family: 'Courier New', monospace; font-weight: bold; color: #86efac; background: rgba(2, 10, 6, 0.88); border: 1px solid #34d399; border-radius: 4px; cursor: pointer; padding: 0; } .radar-ctl button:hover { background: rgba(52, 211, 153, 0.25); color: #fff; } .radar-coord { position: absolute; left: 10px; bottom: 8px; z-index: 40; font-family: 'Courier New', monospace; font-size: 0.72rem; color: #bbf7d0; background: rgba(2, 10, 6, 0.85); border: 1px solid #166534; border-radius: 4px; padding: 3px 8px; pointer-events: none; } .labor-badge { position: absolute; top: 35px; right: -15px; color: #fff; font-family: 'Courier New', monospace; font-size: 0.65rem; font-weight: bold; padding: 2px 6px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.8); z-index: 30; pointer-events: none; display: flex; align-items: center; gap: 3px; text-shadow: 0 0 3px #000; } .map-node a { text-decoration: none !important; display: flex; flex-direction: column; align-items: center; justify-content: center; cursor: crosshair; } .map-node span.locked-node { text-decoration: none !important; display: flex; flex-direction: column; align-items: center; justify-content: center; cursor: not-allowed; } span.node-icon { font-size: 1.8rem; margin-bottom: 5px; display: block; } span.node-label { font-size: 0.75rem; font-weight: bold; background: rgba(0, 0, 0, 0.85); border: 1px solid currentColor; padding: 4px 8px; border-radius: 4px; white-space: nowrap; display: block; } span.node-desc { font-size: 0.65rem; color: #aaa; background: rgba(0,0,0,0.9); border: 1px dashed currentColor; padding: 6px; margin-top: 5px; width: 150px; white-space: normal; display: none; } .map-node:hover span.node-desc { display: block; } /* BACK BUTTON */ .return-btn a { display: block; background: rgba(0, 255, 0, 0.1); border: 2px solid #00ff00; color: #00ff00 !important; padding: 15px; font-size: 1.3rem; font-weight: bold; letter-spacing: 4px; text-decoration: none !important; border-radius: 4px; text-align: center; transition: 0.3s; } .return-btn a:hover { background: #00ff00; color: #000 !important; box-shadow: 0 0 30px #00ff00; } @media screen and (max-width: 768px) { .ashen-veil-world { margin-left: 3.5em; width: calc(100% - 3.5em); } } </style> <div class="ashen-veil-world"> <!-- CENTER: MAP INTERFACE --> <div class="terminal-wide-core"> <div style="text-align: center; border-bottom: 1px dashed #00ff00; padding-bottom: 15px; margin-bottom: 20px;"> <h2 style="color: #00ff00; margin: 0; letter-spacing: 4px; text-shadow: 0 0 10px #00ff00;">[ SILESIA SECTOR MAP ]</h2> <div style="margin-top: 10px; font-size: 0.9rem; color: #888;">S.A.N.C.T.U.A.R.Y. TACTICAL RADAR ONLINE</div> <div style="margin-top: 4px; font-size: 0.75rem; color: #94a3b8;">DRAG TO PAN | SCROLL OR PINCH TO ZOOM | ⌖ RE-CENTER ON BASE</div> </div> <<set _dev to (def $playerName and String($playerName).toLowerCase() eq "nakman")>> <<if _dev>><<run setup.devMapAll()>><</if>> <<timeBanner>> <div class="radar-scroll"> <div class="radar-container" data-tour="map"> <div class="radar-world"> <img class="radar-img" src="images/locations/silesia_sector_map.jpg" alt="" draggable="false" onerror="this.style.display='none'"> <div class="radar-atmo" @style="setup.radarAtmo()"></div> <!-- 1. BASE (FIXED) --> <div class="map-node" data-base="1" style="top: 58.7%; left: 57.5%;"> <<if $laborForagers gt 0>> <div class="labor-badge" style="background-color:#4ade80; box-shadow:0 0 8px #4ade80;">👤 <<print $laborForagers>></div> <</if>> <<link '<span class="node-icon" style="color:#fbbf24; text-shadow:0 0 10px #fbbf24;">⌂</span><span class="node-label" style="color:#fbbf24; border-color:#fbbf24;">S.A.N.C.T.U.A.R.Y. (BASE)</span><span class="node-desc" style="color:#fbbf24; border-color:#fbbf24;">Return to Underground Command Center.</span>' "Camp">><</link>> </div> <!-- 2. KATOWICE TUNNELS (FIXED) --> <div class="map-node" style="top: 42.1%; left: 49.5%;"> <<if $level gte 3>> <<link '<span class="node-icon" style="color:#ef4444; text-shadow:0 0 10px #ef4444;">☠</span><span class="node-label" style="color:#ef4444; border-color:#ef4444;">KATOWICE TUNNELS</span><span class="node-desc" style="color:#ef4444; border-color:#ef4444;">Combat zone only for now. Story coming soon.</span>' "Katowice Tunnels">> <<set $location to "Tunnels">> <<set $currentLocation to "Katowice Tunnels">> <<set $thirst = Math.max(0, $thirst - 5)>> <</link>> <<else>> <span class="locked-node" title="WARNING: Signal too weak. Operator must reach Level 3 to decrypt exact coordinates."> <span class="node-icon" style="color:#52525b; text-shadow:0 0 10px #52525b;">☠</span> <span class="node-label" style="color:#52525b; border-color:#ef4444;">KATOWICE TUNNELS [LOCKED]</span> <span class="node-desc" style="color:#ef4444; border-color:#ef4444;">Cipher's signal is lost in the static. You must reach <strong>Level 3</strong> to trace exact coordinates.</span> </span> <<endif>> </div> <!-- CHAPTER 3: SUBSTATION OMICRON (Old Chorzow freight yard, west of the base; test build: only the dev account sees it) --> <<if setup.ch3 and setup.ch3.visible()>> <div class="map-node" style="top: 40.8%; left: 43.2%;"> <<if setup.ch3.canEnter()>> <<link '<span class="node-icon" style="color:#fde047; text-shadow:0 0 10px #fde047;">ϟ</span><span class="node-label" style="color:#fde047; border-color:#fde047;">SUBSTATION OMICRON</span><span class="node-desc" style="color:#fde047; border-color:#fde047;">Old Chorzów freight yard. A flooded, Vanguard-held power station: a dungeon with a Vanguard Power Core at the bottom.</span>'>> <<set $currentLocation to "Substation Omicron">> <<set $thirst = Math.max(0, $thirst - 5)>> <<run setup.ch3.enterSub()>> <</link>> <<else>> <span class="locked-node" title="Finish the briefing at the base first."> <span class="node-icon" style="color:#52525b; text-shadow:0 0 10px #52525b;">ϟ</span> <span class="node-label" style="color:#52525b; border-color:#fde047;">SUBSTATION OMICRON [LOCKED]</span> <span class="node-desc" style="color:#fde047; border-color:#fde047;">Cipher's route is not marked yet. Wait for her contact at the base.</span> </span> <</if>> </div> <</if>> <!-- ABANDONED COAL MINE (Kaelen's quest) --> <div class="map-node" style="top: 66.5%; left: 62.5%;"> <<if ($dgQuest || 0) gte 1 or _dev>> <<link '<span class="node-icon" style="color:#f59e0b; text-shadow:0 0 10px #f59e0b;">⛏</span><span class="node-label" style="color:#f59e0b; border-color:#f59e0b;">ABANDONED COAL MINE</span><span class="node-desc" style="color:#f59e0b; border-color:#f59e0b;">A maze of tunnels, loot and a boss waiting at the bottom.</span>' "MineEntrance">> <<set $currentLocation to "Abandoned Coal Mine">> <<set $thirst = Math.max(0, $thirst - 5)>> <</link>> <<else>> <span class="locked-node" title="A collapsed mine. Someone in the sector knows the way in."> <span class="node-icon" style="color:#52525b; text-shadow:0 0 10px #52525b;">⛏</span> <span class="node-label" style="color:#52525b; border-color:#f59e0b;">OLD MINE [LOCKED]</span> <span class="node-desc" style="color:#f59e0b; border-color:#f59e0b;">Collapsed and sealed. Senior Trader Kaelen knows the way in. Reach <strong>Level 4</strong> and ask him about work.</span> </span> <</if>> </div> <!-- 3. JANOW WAREHOUSE (FIXED) --> <div class="map-node" style="top: 52.7%; left: 63.1%;"> <<if $laborScrap gt 0>> <div class="labor-badge" style="background-color:#f97316; box-shadow:0 0 8px #f97316;">👤 <<print $laborScrap>></div> <</if>> <<link '<span class="node-icon" style="color:#38bdf8; text-shadow:0 0 10px #38bdf8;">🏗</span><span class="node-label" style="color:#38bdf8; border-color:#38bdf8;">JANOW WAREHOUSE</span><span class="node-desc" style="color:#38bdf8; border-color:#38bdf8;">Scavenge materials and rescue trapped survivors.</span>' "Explore Warehouse">> <<set $currentLocation to "Janow Warehouse">> <<set $thirst = Math.max(0, $thirst - 5)>> <</link>> </div> <!-- 4. SILESIAN PARK CLINIC (FIXED) --> <div class="map-node" style="top: 30.0%; left: 40.0%;"> <<if $laborMedics gt 0>> <div class="labor-badge" style="background-color:#c084fc; box-shadow:0 0 8px #c084fc;">👤 <<print $laborMedics>></div> <</if>> <<link '<span class="node-icon" style="color:#2ecc71; text-shadow:0 0 10px #2ecc71;">✚</span><span class="node-label" style="color:#2ecc71; border-color:#2ecc71;">SILESIAN PARK CLINIC</span><span class="node-desc" style="color:#2ecc71; border-color:#2ecc71;">Search for medical supplies and refuge seekers.</span>' "Explore Clinic">> <<set $currentLocation to "Silesian Park Clinic">> <<set $thirst = Math.max(0, $thirst - 5)>> <</link>> </div> <!-- 5. ABANDONED BUNKER (FIXED) --> <div class="map-node" style="top: 68.3%; left: 44.6%;"> <<if $laborCloth gt 0>> <div class="labor-badge" style="background-color:#38bdf8; box-shadow:0 0 8px #38bdf8;">👤 <<print $laborCloth>></div> <</if>> <<link '<span class="node-icon" style="color:#a855f7; text-shadow:0 0 10px #a855f7;">▼</span><span class="node-label" style="color:#a855f7; border-color:#a855f7;">ABANDONED BUNKER</span><span class="node-desc" style="color:#a855f7; border-color:#a855f7;">Explore pre-war shelter for tech data and survivors.</span>' "Explore Bunker">> <<set $currentLocation to "Abandoned Bunker">> <<set $thirst = Math.max(0, $thirst - 5)>> <</link>> </div> <!-- 6. RUINED OUTPOST (FIXED) --> <div class="map-node" style="top: 38.3%; left: 26.8%;"> <<link '<span class="node-icon" style="color:#f97316; text-shadow:0 0 10px #f97316;">⚔</span><span class="node-label" style="color:#f97316; border-color:#f97316;">RUINED OUTPOST</span><span class="node-desc" style="color:#f97316; border-color:#f97316;">Hostile Raider territory. Combat zone for grinding XP and Scrap.</span>' "Combat">> <<run setup.spawnPack({ name: "Wasteland Raider", hp: 35, currentHp: 35, maxHp: 35, minDmg: 6, maxDmg: 12, drop: "scrap" }, random(1, 4))>> <<set $targetPOIName to "Ruined Outpost">> <<set $targetPOILoot to "military">> <<set $currentLocation to "Ruined Outpost">> <<set $thirst = Math.max(0, $thirst - 5)>> <</link>> </div> <!-- 7. OVERGROWN PLAZA (FIXED) --> <div class="map-node" style="top: 30.8%; left: 65.0%;"> <<link '<span class="node-icon" style="color:#f43f5e; text-shadow:0 0 10px #f43f5e;">☣</span><span class="node-label" style="color:#f43f5e; border-color:#f43f5e;">OVERGROWN PLAZA</span><span class="node-desc" style="color:#f43f5e; border-color:#f43f5e;">Reptilian feeding ground. Combat zone for grinding XP and Tech.</span>' "Combat">> <<run setup.spawnPack({ name: "Reptilian Drone", hp: 45, currentHp: 45, maxHp: 45, minDmg: 8, maxDmg: 14, drop: "techData" }, random(1, 4))>> <<set $targetPOIName to "Overgrown Plaza">> <<set $targetPOILoot to "basic">> <<set $currentLocation to "Overgrown Plaza">> <<set $thirst = Math.max(0, $thirst - 5)>> <</link>> </div> <!-- 8. FALLEN MILITARY CONVOY (ROAMING DYNAMIC -> links to the FallenMilitaryConvoy passage) --> <<set _convoyName to ($locConvoy.name || "FALLEN MILITARY CONVOY")>> <div class="map-node" @style="'top: ' + $locConvoy.top + '; left: ' + $locConvoy.left + ';'"> <<set _convoyHtml to '<span class="node-icon" style="color:#eab308; text-shadow:0 0 10px #eab308;">💣</span><span class="node-label" style="color:#eab308; border-color:#eab308;">' + _convoyName + '</span><span class="node-desc" style="color:#eab308; border-color:#eab308;">Ambushed military convoy. Guarded by rogue mercenaries. High ammo yield.</span>'>> <<link _convoyHtml "FallenMilitaryConvoy">> <<set $currentLocation to _convoyName>> <<set $thirst = Math.max(0, $thirst - 5)>> <</link>> </div> <!-- 9. DRIFTER CARAVAN (ROAMING DYNAMIC TRADER -> links to the Merchant passage) --> <<set _traderName to ($locTrader.name || "DRIFTER CARAVAN")>> <div class="map-node" @style="'top: ' + $locTrader.top + '; left: ' + $locTrader.left + ';'"> <<set _traderHtml to '<span class="node-icon" style="color:#38bdf8; text-shadow:0 0 10px #38bdf8;">🤝</span><span class="node-label" style="color:#38bdf8; border-color:#38bdf8;">' + _traderName + '</span><span class="node-desc" style="color:#38bdf8; border-color:#38bdf8;">Wasteland nomad traders. Safe zone to trade supplies for Scrip.</span>'>> <<link _traderHtml "Merchant">> <<set $currentLocation to _traderName>> <<set $thirst = Math.max(0, $thirst - 5)>> <</link>> </div> <!-- 10. RAWA RIVER (FIXED) --> <div class="map-node" style="top: 44.4%; left: 56.8%;"> <<if $laborFishermen gt 0>> <div class="labor-badge" style="background-color:#06b6d4; box-shadow:0 0 8px #06b6d4;">👤 <<print $laborFishermen>></div> <</if>> <<link '<span class="node-icon" style="color:#38bdf8; text-shadow:0 0 10px #38bdf8;">🎣</span><span class="node-label" style="color:#38bdf8; border-color:#38bdf8;">RAWA RIVER (FISHING)</span><span class="node-desc" style="color:#38bdf8; border-color:#38bdf8;">Toxic industrial river waters. Cast a line for mutated fish or sunken scrap.</span>' "FishingSpot">> <<set $thirst = Math.max(0, $thirst - 5)>> <</link>> </div> <!-- 11. MURCKOWSKI DEADWOODS (FIXED) --> <div class="map-node" style="top: 77.1%; left: 54.5%;"> <<if $laborHunters gt 0>> <div class="labor-badge" style="background-color:#ef4444; box-shadow:0 0 8px #ef4444;">👤 <<print $laborHunters>></div> <</if>> <<link '<span class="node-icon" style="color:#ef4444; text-shadow:0 0 10px #ef4444;">🌲</span><span class="node-label" style="color:#ef4444; border-color:#ef4444;">MURCKOWSKI DEADWOODS (HUNTING)</span><span class="node-desc" style="color:#ef4444; border-color:#ef4444;">A heavy blanket of fog covers the ancient forest. Hunt mutated wildlife for raw meat.</span>' "Hunting">> <<set $thirst = Math.max(0, $thirst - 5)>> <</link>> </div> <!-- 12. TYCHY LOGGING CAMP (FIXED) --> <div class="map-node" style="top: 89.6%; left: 45.5%;"> <<if $laborWood gt 0>> <div class="labor-badge" style="background-color:#d97706; box-shadow:0 0 8px #d97706;">👤 <<print $laborWood>></div> <</if>> <<link '<span class="node-icon" style="color:#d97706; text-shadow:0 0 10px #d97706;">🪵</span><span class="node-label" style="color:#d97706; border-color:#d97706;">TYCHY LOGGING CAMP</span><span class="node-desc" style="color:#d97706; border-color:#d97706;">Overgrown lumberyard. Feral wildlife activity detected in the perimeter.</span>' "Combat">> <<set $enemy to { name: "Feral Timber-Wolf", hp: 30, currentHp: 30, maxHp: 30, minDmg: 5, maxDmg: 10, drop: "wood" }>> <<set $targetPOIName to "Tychy Logging Camp">> <<set $targetPOILoot to "basic">> <<set $currentLocation to "Tychy Logging Camp">> <<set $thirst = Math.max(0, $thirst - 5)>> <</link>> </div> <!-- 13. RUINED MILITARY CHECKPOINT (FIXED -> links to the RuinedMilitaryCheckpoint passage) --> <div class="map-node" style="top: 51.2%; left: 52.0%;"> <<if $laborAmmo gt 0>> <div class="labor-badge" style="background-color:#eab308; box-shadow:0 0 8px #eab308;">👤 <<print $laborAmmo>></div> <</if>> <<link '<span class="node-icon" style="color:#eab308; text-shadow:0 0 10px #eab308;">💣</span><span class="node-label" style="color:#eab308; border-color:#eab308;">RUINED MILITARY CHECKPOINT</span><span class="node-desc" style="color:#eab308; border-color:#eab308;">Volatile pre-war armory ruins. Extreme risk of detonation.</span>' "RuinedMilitaryCheckpoint">> <<set $thirst = Math.max(0, $thirst - 5)>> <</link>> </div> <!-- DYNAMIC SIDE QUEST: FLIPPED SUPPLY CARAVAN --> <<if $activeSideQuest or _dev>> <div class="map-node" style="top: 49.2%; left: 36.6%;"> <<link '<span class="node-icon" style="color:#fbbf24; text-shadow:0 0 10px #fbbf24;">📦</span><span class="node-label" style="color:#fbbf24; border-color:#fbbf24;">FLIPPED SUPPLY CARAVAN [RUMOR]</span><span class="node-desc" style="color:#fbbf24; border-color:#fbbf24;">An unguarded pre-war convoy. Secure the wreckage and extract valuable cache.</span>' "CaravanScavenge">><</link>> </div> <<endif>> <!-- REPTILIAN CONVOY (enslaved humans to rescue) --> <<if $convoyAvailable or _dev>> <div class="map-node" @style="'top: ' + ($convoyPos ? $convoyPos[0] : 47.9) + '%; left: ' + ($convoyPos ? $convoyPos[1] : 17.9) + '%;'"> <<link '<span class="node-icon" style="color:#f97316; text-shadow:0 0 10px #f97316;">⛓</span><span class="node-label" style="color:#f97316; border-color:#f97316;">REPTILIAN CONVOY</span><span class="node-desc" style="color:#f97316; border-color:#f97316;">Reptilian guards with an enslaved human forced to fight you. Kill only the guards and the prisoner joins your camp.</span>' "Combat">> <<set $enemy to { name: "Reptilian Convoy Squad (3 Units)", hp: 45, currentHp: 45, maxHp: 45, minDmg: 8, maxDmg: 14, drop: "techData" }>> <<set $convoyFightActive to true>> <<set $convoyAvailable to false>> <<set $targetPOIName to "Reptilian Convoy">> <<set $targetPOILoot to "basic">> <<set $currentLocation to "Reptilian Convoy">> <<set $thirst = Math.max(0, $thirst - 5)>> <</link>> </div> <</if>> <!-- SILESIAN LIBRARY (Dr. Maya) and SECTOR-03 COAL VAULTS (Sszakar-Aten) --> <<if _dev or ($mayaSite and not ($npcFlags and $npcFlags.maya))>> <div class="map-node" style="top: 34.8%; left: 47.9%;"> <<link '<span class="node-icon" style="color:#f59e0b; text-shadow:0 0 10px #f59e0b;">📚</span><span class="node-label" style="color:#f59e0b; border-color:#f59e0b;">SILESIAN LIBRARY RUINS</span><span class="node-desc" style="color:#fbbf24; border-color:#fbbf24;">A scholar is hiding in the flooded stacks. She may be able to read the slate.</span>' "MayaMeeting">><</link>> </div> <</if>> <<if _dev or (setup.ATEN_RELEASED and $sszakarSite and not ($relicState and $relicState["9b"]))>> <div class="map-node" style="top: 54.2%; left: 44.3%;"> <<link '<span class="node-icon" style="color:#ef4444; text-shadow:0 0 10px #ef4444;">🔥</span><span class="node-label" style="color:#ef4444; border-color:#ef4444;">SECTOR-03 COAL VAULTS</span><span class="node-desc" style="color:#fbbf24; border-color:#fbbf24;">[BOSS] A sunken shrine guarded by the Scarred Prophet. The other half of the Amarna Core.</span>' "RelicBossIntro">><</link>> </div> <</if>> <!-- RUMOR SITES AND BOSS CONTRACTS (from the bar, Kaelen and the Shady Informant) --> <<for _si to 0; _si lt (($mapSites || []).length); _si++>> <<set _site to $mapSites[_si]>> <<set _sd to setup.encounters[_site.id]>> <<if _sd>> <<capture _site, _sd>> <div class="map-node" @style="'top: ' + _site.pos[0] + '%; left: ' + _site.pos[1] + '%;'"> <<set _lbl to ('<span class="node-icon" style="color:' + (_sd.pool eq "contract" ? '#ef4444' : '#c084fc') + '; text-shadow:0 0 10px ' + (_sd.pool eq "contract" ? '#ef4444' : '#c084fc') + ';">' + _sd.mapIcon + '</span><span class="node-label" style="color:' + (_sd.pool eq "contract" ? '#ef4444' : '#c084fc') + '; border-color:' + (_sd.pool eq "contract" ? '#ef4444' : '#c084fc') + ';">' + _sd.mapName + '</span><span class="node-desc" style="color:#fbbf24; border-color:#fbbf24;">' + (_sd.pool eq "contract" ? '[BOSS CONTRACT] ' : '[RUMOR] ') + _sd.mapDesc + '</span>')>> <<link _lbl>> <<run setup.siteOpen(_site.id)>><<goto "Encounter">> <</link>> </div> <</capture>> <</if>> <</for>> <!-- BANDIT AND REPTILIAN CAMPS (intel from Grom's interrogations) --> <<for _ci to 0; _ci lt (($campSites || []).length); _ci++>> <<set _camp to $campSites[_ci]>> <<set _cd to setup.CAMP[_camp.type]>> <<capture _camp, _cd>> <div class="map-node" @style="'top: ' + _camp.pos[0] + '%; left: ' + _camp.pos[1] + '%;'"> <<set _clbl to ('<span class="node-icon" style="color:' + _cd.color + '; text-shadow:0 0 10px ' + _cd.color + ';">' + _cd.icon + '</span><span class="node-label" style="color:' + _cd.color + '; border-color:' + _cd.color + ';">' + _camp.name.toUpperCase() + '</span><span class="node-desc" style="color:' + _cd.color + '; border-color:' + _cd.color + ';">' + _cd.label + ' camp HQ. ' + _cd.waves + ' waves. A cleared camp vanishes from the map.</span>')>> <<link _clbl "CampRaidBrief">><<set $campSel to _camp.id>><</link>> </div> <</capture>> <</for>> <!-- SLAUGHTERHOUSE (unlocked by the survivor's plea) --> <<if ($quest_slaughterhouse || "") eq "active" or _dev>> <div class="map-node" style="top: 40.0%; left: 68.4%;"> <<link '<span class="node-icon" style="color:#ef4444; text-shadow:0 0 10px #ef4444;">🩸</span><span class="node-label" style="color:#ef4444; border-color:#ef4444;">THE SLAUGHTERHOUSE</span><span class="node-desc" style="color:#ef4444; border-color:#ef4444;">Reptilian slave camp. The family of a rescued survivor is held here.</span>' "Slaughterhouse_Gate">> <<set $currentLocation to "Slaughterhouse">> <</link>> </div> <</if>> <!-- BEBOK: SPECIAL EVENT BOSS --> <<if ndef $bebokSlain or _dev>> <div class="map-node" style="top: 35.0%; left: 21.8%;"> <<link '<span class="node-icon" style="color:#ef4444; text-shadow:0 0 12px #ef4444;">☠</span><span class="node-label" style="color:#ef4444; border-color:#ef4444;">BEBOK'S SCRAP YARD [EVENT BOSS]</span><span class="node-desc" style="color:#ef4444; border-color:#ef4444;">A hulking scrap-hoarder. He barely scratches, but he never goes down. Patience is the weapon.</span>' "Combat">> <<set $enemy to { name: "Bebok", hp: 100000, currentHp: 100000, maxHp: 100000, minDmg: 1, maxDmg: 2, drop: "scrap", fixed: true, noCapture: true, hpMult: 3, isBebok: true }>> <<set $enemyGroup to [$enemy]>> <<set $currentLocation to "Bebok's Scrap Yard">> <</link>> </div> <</if>> <!-- DYNAMIC BOSS QUEST --> <<if $activeBossQuest or _dev>> <div class="map-node" style="top: 60.4%; left: 73.2%;"> <<link '<span class="node-icon" style="color:#dc2626; text-shadow:0 0 15px #dc2626;">👹</span><span class="node-label" style="color:#dc2626; border-color:#dc2626;">REPTILIAN WARLORD OUTPOST [CONTRACT]</span><span class="node-desc" style="color:#dc2626; border-color:#dc2626;">A high-value Reptilian Warlord with 5000 HP is holed up here. Eliminate him to claim Global Broadcast Passes!</span>' "Combat">> <<set $enemy to { name: "Reptilian Warlord", hp: 5000, currentHp: 5000, maxHp: 5000, minDmg: 40, maxDmg: 80, rarity: "Godlike" }>> <<set $activeBossQuest to false>> <<set $currentLocation to "Warlord Outpost">> <<set $thirst = Math.max(0, $thirst - 5)>> <</link>> </div> <<endif>> <<adPins>> </div> <div class="radar-grid"></div> <div class="radar-sweep"></div> <<done>><<run setup.radarInit()>><</done>> </div> </div> <!-- RETURN BUTTON --> <div class="return-btn"> <<link "CLOSE RADAR (RETURN TO S.A.N.C.T.U.A.R.Y. BASE)" "Camp">><</link>> </div> </div> </div> <</nobr>>
<<setMenu "MedicalTent">> /* VARIABLE BUG FIXED: now reads the .medical variable in the Hub directly */ <<if ndef $sanctuaryLevels>><<set $sanctuaryLevels to { medical: 1 }>><</if>> <<if typeof $sanctuaryLevels.medical is "undefined" or $sanctuaryLevels.medical eq null>> <<set $sanctuaryLevels.medical to 1>> <</if>> /* LET'S SAFEGUARD THE STORAGE AND POCKET VARIABLES */ <<if ndef $campStorage>><<set $campStorage to {}>><</if>> <<if ndef $materials>><<set $materials to {}>><</if>> <<if ndef $materials.cloth>><<set $materials.cloth to 0>><</if>> <<if ndef $campStorage.cloth>><<set $campStorage.cloth to 0>><</if>> <<if ndef $materials.medicalHerb>><<set $materials.medicalHerb to 0>><</if>> <<if ndef $campStorage.medicalHerb>><<set $campStorage.medicalHerb to 0>><</if>> <<if ndef $materials.medicine>><<set $materials.medicine to 0>><</if>> <<if ndef $campStorage.medicine>><<set $campStorage.medicine to 0>><</if>> <<if ndef $materials.scrap>><<set $materials.scrap to 0>><</if>> <<if ndef $campStorage.scrap>><<set $campStorage.scrap to 0>><</if>> <<if ndef $materials.water>><<set $materials.water to 0>><</if>> <<if ndef $campStorage.water>><<set $campStorage.water to 0>><</if>> <<if ndef $materials.sterileBandage>><<set $materials.sterileBandage to 0>><</if>> <<if ndef $materials.medicalStimpack>><<set $materials.medicalStimpack to 0>><</if>> <style> body { background-color: #050a05 !important; } #story, #passages, .passage { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; background: transparent !important; } .av-world { position: absolute; top: 10px; left: 17.5em; right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 0 20px 20px 20px; width: calc(100% - 17.5em); } .med-container { background-color: #0c1e29; padding: 30px; border: 2px solid #0284c7; border-radius: 8px; font-family: 'Segoe UI', Tahoma, sans-serif; box-shadow: 0 0 25px rgba(2, 132, 199, 0.2); box-sizing: border-box; width: 100%; } .med-grid { display: flex; flex-wrap: wrap; gap: 20px; justify-content: center; margin-top: 25px; margin-bottom: 20px; } .med-card { background: rgba(0, 0, 0, 0.75); border: 1px solid #0369a1; border-radius: 6px; flex: 1 1 310px; min-width: 0; max-width: 640px; min-height: 290px; overflow: hidden; display: flex; flex-direction: column; justify-content: space-between; box-shadow: 0 4px 12px rgba(0,0,0,0.6); box-sizing: border-box; } .med-img { height: 110px; width: 100%; object-fit: cover; border-bottom: 2px solid #0369a1; background: #000; } .main-banner-img { height: 140px; width: 100%; object-fit: cover; border: 1px solid #0284c7; border-radius: 6px; margin-bottom: 20px; background: #000; box-shadow: 0 0 15px rgba(2, 132, 199, 0.3); box-sizing: border-box; } .section-title { color: #38bdf8; font-size: 0.95rem; font-family: 'Courier New', monospace; letter-spacing: 2px; margin: 30px 0 15px 0; text-transform: uppercase; } .cyber-med-btn { display: block; width: 100%; background: linear-gradient(135deg, #0284c7 0%, #0369a1 100%); border: 1px solid #38bdf8; color: #f0f9ff !important; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.8rem; letter-spacing: 1px; border-radius: 4px; cursor: pointer; text-decoration: none !important; box-sizing: border-box; box-shadow: 0 0 10px rgba(2, 132, 199, 0.3); transition: all 0.2s; } .cyber-med-btn:hover { background: linear-gradient(135deg, #0369a1 100%, #0284c7 0%); border-color: #7dd3fc; box-shadow: 0 0 15px rgba(2, 132, 199, 0.6); } .cyber-return-btn { display: inline-block; background: linear-gradient(135deg, #0369a1 0%, #0c4a6e 100%); border: 1px solid #38bdf8; color: #f0f9ff !important; padding: 12px 30px; font-weight: bold; font-family: 'Courier New', monospace; letter-spacing: 2px; border-radius: 4px; text-decoration: none !important; box-shadow: 0 0 15px rgba(2, 132, 199, 0.3); transition: all 0.2s; } .cyber-return-btn:hover { background: linear-gradient(135deg, #0c4a6e 100%, #0369a1 0%); border-color: #7dd3fc; box-shadow: 0 0 20px rgba(2, 132, 199, 0.5); } .cyber-locked-btn { display: block; width: 100%; background: rgba(15, 23, 42, 0.6); border: 1px dashed #475569; color: #64748b; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.8rem; letter-spacing: 1px; border-radius: 4px; box-sizing: border-box; } </style> <<nobr>> <div class="av-world"> <div class="med-container"> <!-- TITLE --> <div style="text-align: center; border-bottom: 1px dashed #0284c7; padding-bottom: 15px; margin-bottom: 20px;"> <h2 style="color: #38bdf8; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px #0284c7; font-size: 1.8rem;">[ MEDICAL INFIRMARY ]</h2> <div style="margin-top: 8px; font-size: 0.9rem; color: #7dd3fc;">FACILITY LEVEL: <<print $sanctuaryLevels.medical>> | STERILE TREATMENT & TRAUMA RECOVERY UPLINK</div> </div> <img src="images/locations/medical_tent_main.jpg" class="main-banner-img" onerror="this.src='https://placehold.co/800x200/000/0284c7?text=MEDICAL+INFIRMARY';"> <<if def $medLog>> <div style="border: 1px solid #0284c7; background: rgba(2, 132, 199, 0.1); padding: 15px; border-radius: 4px; margin-bottom: 25px; font-family: monospace;"> <div style="color: #38bdf8; font-weight: bold; margin-bottom: 8px;">💉 MEDICAL PROCEDURE: SUCCESSFUL</div> <div style="color: #cbd5e1; font-style: italic;">"<<print $medLog>>"</div> </div> <<unset $medLog>> <</if>> <div style="border: 1px solid #0284c7; background: rgba(2, 132, 199, 0.1); padding: 15px; border-radius: 4px; margin-bottom: 25px; font-family: monospace;"> <div style="color: #38bdf8; font-weight: bold; margin-bottom: 6px;">⚕️ CLINICAL RECOVERY PROTOCOL</div> <div style="color: #cbd5e1; font-size: 0.85rem; line-height: 1.4;"> Access automated surgical tools, radiation flush systems, and chemical synthesizers to maintain combat readiness. </div> </div> <div class="section-title">>> CLINICAL TREATMENTS & RECOVERY</div> <<nobr>> <div class="med-grid"> <!-- 1. DECONTAMINATION FLUSH: the only complete cure for Radiation Contamination --> <div class="med-card"> <img src="images/locations/med_rad.jpg" class="med-img" onerror="this.src='https://placehold.co/600x300/000/0284c7?text=RADIATION';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #38bdf8; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">☢️ DECONTAMINATION FLUSH</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">Purge radioactive isotopes from your blood with chemical chelators. The only way to clear Radiation Contamination completely.</div> <div style="color: #fde047; font-size: 0.7rem; font-family: monospace;"> COST: <<print setup.CONTAM.flushMedicine>> Medicine, <<print setup.CONTAM.flushWater>> Water<br> <span style="color: #34d399;">EFFECT: Fully clears Radiation Contamination</span><br> <span style="color: #94a3b8;">IN STOCK: <<print setup.have("medicine")>> Medicine, <<print setup.have("water")>> Water</span> </div> <<if setup.contamActive()>> <div style="color: #fca5a5; font-size: 0.7rem; font-family: monospace; margin-top: 6px;">☢ CONTAMINATED: <<print setup.contamInfo().text>></div> <</if>> </div> <div style="margin-top:10px;"> <<if not setup.contamActive()>> <div class="cyber-locked-btn">[ NO CONTAMINATION DETECTED ]</div> <<elseif setup.contamFlushOk()>> <<link "<span class='cyber-med-btn'>☢️ RUN DECONTAMINATION</span>" "MedicalTent">> <<run setup.contamFlush()>> <<set $medLog to "Cellular decontamination flush completed. The isotopes are gone from your blood and your strength is back to normal.">> <<run window.setup.updateSidebar()>> <</link>> <<else>> <div class="cyber-locked-btn">[ INSUFFICIENT MEDICINE OR WATER ]</div> <</if>> </div> </div> </div> <!-- 1b. RAD-MEDS REGIMEN: slows the contamination, never removes it quickly --> <div class="med-card"> <img src="images/locations/med_rad.jpg" class="med-img" onerror="this.src='https://placehold.co/600x300/000/0369a1?text=RAD-MEDS';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #38bdf8; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">💊 RAD-MEDS REGIMEN</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">Hold Radiation Contamination back with medicine. One dose works for one part of the day and cuts the damage penalty in half, but the contamination stays. <<print setup.CONTAM.cureDoses>> doses in total will finally clear it.</div> <<set _ci to setup.contamInfo()>> <div style="color: #fde047; font-size: 0.7rem; font-family: monospace;"> COST: 1 Medicine per part of the day<br> DOSES TAKEN: <<print _ci.doses>> / <<print setup.CONTAM.cureDoses>><br> AUTO-DOSE ON TRIPS: <<print (_ci.auto ? "ON" : "OFF")>> </div> <<if _ci.on>> <div style="color: #fca5a5; font-size: 0.7rem; font-family: monospace; margin-top: 6px;">☢ <<print _ci.text>></div> <</if>> </div> <div style="margin-top:10px;"> <<if not _ci.on>> <div class="cyber-locked-btn">[ NOT CONTAMINATED ]</div> <<elseif _ci.medicated>> <div class="cyber-locked-btn" style="color:#34d399; border-color:#059669;">[ RAD-MEDS ACTIVE THIS PART OF THE DAY ]</div> <<elseif setup.have("medicine") gte 1>> <<link "<span class='cyber-med-btn'>💊 TAKE RAD-MEDS (1 MEDICINE)</span>" "MedicalTent">> <<set _dr to setup.contamDose()>> <<if _dr is "CURED">><<set $medLog to "That was the 25th dose. The contamination finally breaks and your blood runs clean.">><<else>><<set $medLog to "You take your rad-meds. For this part of the day the contamination hits only half as hard.">><</if>> <<run window.setup.updateSidebar()>> <</link>> <<else>> <div class="cyber-locked-btn">[ NO MEDICINE ]</div> <</if>> <<if _ci.on>> <<set _autoLbl to ("<span class='cyber-med-btn' style='margin-top:6px; background:rgba(15,23,42,0.9); border-color:#0369a1;'>AUTO-DOSE: " + (_ci.auto ? "ON (CLICK TO TURN OFF)" : "OFF (CLICK TO TURN ON)") + "</span>")>> <<link _autoLbl "MedicalTent">><<run setup.contamToggleAuto()>><</link>> <</if>> </div> </div> </div> <!-- 2. HEAL WOUNDS (10 AT A TIME, HARDCORE SURGERY) --> <div class="med-card"> <img src="images/locations/med_heal.jpg" class="med-img" onerror="this.src='https://placehold.co/600x300/000/0369a1?text=HEAL';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #38bdf8; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">🩹 SURGICAL STITCHING</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">Treat deep lacerations, apply antibiotics, and suture trauma wounds.</div> <div style="color: #fde047; font-size: 0.7rem; font-family: monospace;"> COST: 10 Medicine, 10 Cloth, 10 Water<br> <span style="color: #34d399;">EFFECT: Restores 100% Health</span> </div> </div> <div style="margin-top:10px;"> <<set _totalSurgeryMed to $materials.medicine + $campStorage.medicine>> <<set _totalSurgeryCloth to $materials.cloth + $campStorage.cloth>> <<set _totalSurgeryWater to $materials.water + $campStorage.water>> <<if _totalSurgeryMed gte 10 and _totalSurgeryCloth gte 10 and _totalSurgeryWater gte 10>> <<link "<span class='cyber-med-btn'>🩹 RECEIVE TREATMENT</span>" "MedicalTent">> /* Smart deduction loop */ <<for _i to 0; _i lt 10; _i++>> <<if $materials.medicine gt 0>><<set $materials.medicine -= 1>><<else>><<set $campStorage.medicine -= 1>><</if>> <<if $materials.cloth gt 0>><<set $materials.cloth -= 1>><<else>><<set $campStorage.cloth -= 1>><</if>> <<if $materials.water gt 0>><<set $materials.water -= 1>><<else>><<set $campStorage.water -= 1>><</if>> <</for>> <<set $hp to $maxHp>> <<set $medLog to "Wounds sutured and sterilized using 10 Medicine, 10 Cloth, and 10 Water. Physical health fully restored.">> <<run window.setup.updateSidebar()>> <</link>> <<else>> <div style="background: rgba(15, 23, 42, 0.6); border: 1px dashed #78350f; color: #b45309; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem; border-radius: 4px;">[ INSUFFICIENT MATERIALS ]</div> <</if>> </div> </div> </div> <!-- 3. PRODUCE BANDAGES (MEDICAL HERB + CLOTH) --> <div class="med-card"> <img src="images/locations/med_bandage.jpg" class="med-img" onerror="this.src='https://placehold.co/600x300/000/0c4a6e?text=BANDAGE';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #38bdf8; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">🌿 SYNTHESIZE HERBAL BANDAGE</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">Compound medical herbs with clean cloth into trauma-ready sterile bandages.</div> <div style="color: #fde047; font-size: 0.7rem; font-family: monospace;"> COST: 1 Medical Herb, 1 Cloth<br> YIELD: +1 Sterile Bandage<br> <span style="color: #34d399;">EFFECT: Restores 15% Max HP</span> </div> </div> <div style="margin-top:10px;"> <<set _totalCloth to $materials.cloth + $campStorage.cloth>> <<set _totalHerb to $materials.medicalHerb + $campStorage.medicalHerb>> <<if _totalCloth gte 1 and _totalHerb gte 1>> <<link "<span class='cyber-med-btn'>🌿 CRAFT HERBAL BANDAGE</span>" "MedicalTent">> <<if $materials.cloth gte 1>><<set $materials.cloth -= 1>><<else>><<set $campStorage.cloth -= 1>><</if>> <<if $materials.medicalHerb gte 1>><<set $materials.medicalHerb -= 1>><<else>><<set $campStorage.medicalHerb -= 1>><</if>> <<set $materials.sterileBandage += 1>> <<set $medLog to "Synthesized 1 sterile herbal bandage (15% Heal) using medical herbs and clean cloth.">> <<run window.setup.updateSidebar()>> <</link>> <<else>> <div style="background: rgba(15, 23, 42, 0.6); border: 1px dashed #78350f; color: #b45309; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem; border-radius: 4px;">[ INSUFFICIENT MATERIALS ]</div> <</if>> </div> </div> </div> <!-- 4. PRODUCE STIMPACKS (MEDICINE + SCRAP) - REQUIRES LEVEL 2 --> <div class="med-card"> <img src="images/locations/med_stim.jpg" class="med-img" onerror="this.src='https://placehold.co/600x300/000/1e3a8a?text=STIMPACK';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #38bdf8; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">💉 SYNTHESIZE MEDICAL STIMPACK</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">Extract and refine scavenged medicine into potent, fast-acting stimpacks.</div> <div style="color: #fde047; font-size: 0.7rem; font-family: monospace;"> COST: 1 Medicine, 1 Scrap<br> YIELD: +1 Medical Stimpack<br> <span style="color: #34d399;">EFFECT: Restores 40% Max HP</span> </div> </div> <div style="margin-top:10px;"> <<if $sanctuaryLevels.medical gte 2>> <<set _totalMedicine to $materials.medicine + $campStorage.medicine>> <<set _totalScrap to $materials.scrap + $campStorage.scrap>> <<if _totalMedicine gte 1 and _totalScrap gte 1>> <<link "<span class='cyber-med-btn'>💉 CRAFT MEDICAL STIMPACK</span>" "MedicalTent">> <<if $materials.medicine gte 1>><<set $materials.medicine -= 1>><<else>><<set $campStorage.medicine -= 1>><</if>> <<if $materials.scrap gte 1>><<set $materials.scrap -= 1>><<else>><<set $campStorage.scrap -= 1>><</if>> <<set $materials.medicalStimpack += 1>> <<set $medLog to "Synthesized 1 medical stimpack (40% Heal) using refined medicine and scrap materials.">> <<run window.setup.updateSidebar()>> <</link>> <<else>> <div style="background: rgba(15, 23, 42, 0.6); border: 1px dashed #78350f; color: #b45309; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem; border-radius: 4px;">[ INSUFFICIENT MATERIALS ]</div> <</if>> <<else>> <div class="cyber-locked-btn">[ REQUIRES MED-BAY LEVEL 2 ]</div> <</if>> </div> </div> </div> </div> <</nobr>> <!-- BACK TO THE SANCTUARY HUB --> <div style="text-align: center; margin-top: 35px;"> <<link "<span class='cyber-return-btn'>🏰 RETURN TO SANCTUARY HUB</span>" "Sanctuary Hub">><</link>> </div> </div> </div> <</nobr>>
<<nobr>> <<setMenu "Map">> <<if ndef $startingCity>><<set $startingCity to "Katowice">><</if>> <<if ndef $cipherMet>><<set $cipherMet to false>><</if>> <style> #story { margin-top: 0 !important; margin-right: 0 !important; margin-bottom: 0 !important; margin-left: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; } .ashen-veil-world-cipher { position: relative; margin-left: 17.5em; margin-right: 0; min-height: 100vh; background: linear-gradient(135deg, #050a05 0%, #291505 100%); display: flex; flex-direction: column; padding: 20px; box-sizing: border-box; width: calc(100% - 17.5em); } .terminal-wide-core { background: rgba(5, 15, 5, 0.92); border-left: 2px solid #f59e0b; border-right: 2px solid #f59e0b; padding: 40px; font-family: 'Segoe UI', Tahoma, sans-serif; min-height: 85vh; display: flex; flex-direction: column; box-shadow: 0 0 40px rgba(245, 158, 11, 0.25), inset 0 0 30px rgba(0,0,0,0.8); box-sizing: border-box; width: 100%; } .cyber-return-btn { display: block; width: 100%; background: rgba(245, 158, 11, 0.15); border: 1px solid #f59e0b; color: #fbbf24 !important; padding: 15px; text-align: center; border-radius: 4px; font-weight: bold; font-family: 'Courier New', monospace; font-size: 1.1rem; cursor: pointer; text-decoration: none !important; box-sizing: border-box; letter-spacing: 2px; transition: all 0.2s ease; } .cyber-return-btn:hover { background: rgba(245, 158, 11, 0.3); border-color: #fde047; box-shadow: 0 0 15px rgba(245, 158, 11, 0.5); color: #fff !important; } </style> <div class="ashen-veil-world-cipher"> <div class="terminal-wide-core"> <div style="text-align: center; border-bottom: 1px dashed #f59e0b; padding-bottom: 15px; margin-bottom: 25px;"> <h2 style="color: #fbbf24; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px rgba(245, 158, 11, 0.5);">[ SECURE FREQUENCY INTERCEPT ]</h2> <div style="color: #fcd34d; font-size: 0.9rem; margin-top: 5px;"><<print $startingCity.toUpperCase()>> RAIL TUNNELS - DEEP SUB-LEVEL</div> </div> <!-- DIALOGUE / STORY SCREEN --> <div style="background: rgba(0, 0, 0, 0.85); border: 1px solid #f59e0b; padding: 25px; border-radius: 6px; margin-bottom: 25px; line-height: 1.8; color: #cbd5e1; font-family: 'Courier New', monospace; font-size: 1.05rem;"> <p style="margin-bottom: 20px;">Stepping over the smoking remains of the 3 Reptilian scouts you just carved through, you push deeper into the freezing, pitch-black rail tunnels beneath <<print $startingCity>>. The air tastes like scorched copper and ozone.</p> <p style="margin-bottom: 20px;">As you approach the designated coordinates near an old switchboard, a flickering green terminal illuminates a shadowed figure wrapped in a heavy trench coat. Cipher lowers a modified rail-pistol.</p> <p style="margin-bottom: 20px; color: #fbbf24;">"I heard the gunfire up the line. Not bad taking down a scout squad on your own. Most surface dwellers freeze when they see the scales,"</p> <p style="margin-bottom: 20px;">the voice hisses from beneath the hood. A pair of glowing cybernetic optics glints in the dark as Cipher leans against the concrete pillar, kicking one of the dead reptiles aside.</p> <p style="margin-bottom: 0; color: #fbbf24;">"The cold-bloods are already sniffing around your signal source in this sector. If we want to survive, we need a network. I'll feed you target coordinates and black-market contracts, and you'll bring me the data and scrap to keep our frequencies masked. Welcome to the real Wasteland, Operator."</p> </div> <!-- REWARD AND QUEST COMPLETION LOGIC --> <<if $cipherMet eq false>> <<set $cipherMet to true>> <<set $xp += 150>> <<set $baseRep += 25>> <<set $activeQuest to "None">> <<set $campLog to "Cleared tunnel guards and made contact with Cipher in " + $startingCity + ". Faction networks opened.">> <div style="background: rgba(46,204,113,0.1); border: 1px solid #2ecc71; padding: 15px; border-radius: 6px; margin-bottom: 25px; text-align: center; color: #34d399; font-weight: bold; font-family: 'Courier New', monospace;"> > OBJECTIVE COMPLETED: MEET CIPHER<br> <span style="font-size: 0.9rem; color: #fff;">Rewards: +150 XP, +25 Street Cred, Faction Network Unlocked!</span> </div> <<else>> <div style="background: rgba(56,189,248,0.1); border: 1px solid #38bdf8; padding: 15px; border-radius: 6px; margin-bottom: 25px; text-align: center; color: #38bdf8; font-family: 'Courier New', monospace;"> > You chat briefly with Cipher, reviewing local patrol routes and upcoming faction movements. Everything is secure for now. </div> <<endif>> <div style="text-align: center; margin-top: auto;"> <<link "<span class='cyber-return-btn'>>> RETURN TO MAP <<</span>" "Map">> <<run window.setup.updateSidebar()>> <</link>> </div> </div> </div> <</nobr>>
<<setMenu "WastelandBar">> <!-- Safety check --> <<if ndef $activeCompanion>><<set $activeCompanion to "None">><</if>> <style> body { background-color: #050a05 !important; } #story, #passages, .passage { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; background: transparent !important; } .av-world { position: absolute; top: 10px; left: 17.5em; right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 0 20px 20px 20px; width: calc(100% - 17.5em); } .roster-container { background-color: #1e0926; padding: 30px; border: 2px solid #a855f7; border-radius: 8px; font-family: 'Segoe UI', Tahoma, sans-serif; box-shadow: 0 0 25px rgba(168, 85, 247, 0.2); box-sizing: border-box; width: 100%; } .roster-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(310px, 1fr)); gap: 20px; margin-top: 25px; } .merc-card { background: rgba(0, 0, 0, 0.75); border: 1px solid #7e22ce; border-radius: 6px; padding: 15px; display: flex; flex-direction: column; justify-content: space-between; box-shadow: 0 4px 12px rgba(0,0,0,0.6); box-sizing: border-box; } /* PORTRAIT IMAGE SETTINGS */ .merc-img { width: 120px; height: 165px; object-fit: cover; border-radius: 4px; border: 1px solid #a855f7; float: left; margin-right: 12px; margin-bottom: 8px; background: #000; } .merc-title { color: #c084fc; font-weight: bold; font-size: 1.1rem; margin-bottom: 8px; border-bottom: 1px dashed #7e22ce; padding-bottom: 5px; } .merc-desc { color: #cbd5e1; font-size: 0.85rem; margin-bottom: 15px; line-height: 1.4; } .merc-req { font-family: monospace; font-size: 0.75rem; margin-bottom: 12px; clear: both; } .btn-hire { display: block; text-align: center; background: linear-gradient(135deg, #9333ea 0%, #7e22ce 100%); color: #f3e8ff !important; padding: 10px; font-weight: bold; font-family: 'Courier New', monospace; border-radius: 4px; border: 1px solid #c084fc; text-decoration: none !important; transition: 0.2s; box-shadow: 0 0 10px rgba(168, 85, 247, 0.3); } .btn-hire:hover { background: linear-gradient(135deg, #7e22ce 100%, #9333ea 0%); border-color: #e9d5ff; box-shadow: 0 0 15px rgba(168, 85, 247, 0.6); } .btn-locked { display: block; text-align: center; background: rgba(15, 23, 42, 0.6); color: #64748b; padding: 10px; font-weight: bold; font-family: 'Courier New', monospace; border-radius: 4px; border: 1px dashed #334155; } .btn-active { display: block; text-align: center; background: linear-gradient(135deg, #059669 0%, #047857 100%); color: #ecfdf5; padding: 10px; font-weight: bold; font-family: 'Courier New', monospace; border-radius: 4px; border: 1px solid #10b981; box-shadow: 0 0 10px rgba(16, 185, 129, 0.3); } .cyber-return-btn { display: inline-block; background: linear-gradient(135deg, #7e22ce 0%, #581c87 100%); border: 1px solid #c084fc; color: #f3e8ff !important; padding: 12px 30px; font-weight: bold; font-family: 'Courier New', monospace; letter-spacing: 2px; border-radius: 4px; text-decoration: none !important; box-shadow: 0 0 15px rgba(168, 85, 247, 0.3); transition: all 0.2s; } .cyber-return-btn:hover { background: linear-gradient(135deg, #581c87 100%, #7e22ce 0%); border-color: #e9d5ff; box-shadow: 0 0 20px rgba(168, 85, 247, 0.5); } </style> <div class="av-world"> <div class="roster-container"> <div style="text-align: center; border-bottom: 1px dashed #a855f7; padding-bottom: 15px; margin-bottom: 20px;"> <h2 style="color: #c084fc; margin: 0; letter-spacing: 3px; font-size: 1.8rem; text-transform: uppercase;">[ MERCENARY ROSTER ]</h2> <div style="color: #d8b4fe; font-family: monospace; margin-top: 8px; font-size: 0.9rem;"> CURRENT REP: <<getReputationRank>> | SQUAD: <span style="color: #fbbf24;"><<print setup.squadSize()>>/4</span> | WASTELAND BAR LEVEL: <span style="color: #fbbf24;"><<print ($sanctuaryLevels.bar || 1)>></span> </div> </div> <!-- SQUAD PANEL --> <<nobr>> <div data-tour="roster-squad" style="border: 1px solid #a855f7; background: rgba(168,85,247,0.08); padding: 14px; margin-bottom: 20px; border-radius: 6px;"> <div style="color: #e9d5ff; font-weight: bold; letter-spacing: 2px; margin-bottom: 10px; font-size: 0.9rem; text-align: center;">YOUR SQUAD (<<print setup.squadSize()>>/4)</div> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 10px;"> <<for _sl to 0; _sl lt 4; _sl++>> <<set _sid to setup.squadIds()[_sl]>> <<if _sid>> <<set _smd to setup.MERC[_sid]>> <<set _scol to (_smd ? _smd.color : "#fbbf24")>> <<set _sname to (_smd ? _smd.disp : _sid)>> <div @style="'border:1px solid ' + _scol + '; background:rgba(0,0,0,0.55); border-radius:4px; padding:10px; text-align:center;'"> <div @style="'color:' + _scol + '; font-weight:bold; font-size:0.85rem; margin-bottom:6px;'"><<print _sname>></div> <<if _smd>><div style="color:#94a3b8; font-size:0.7rem; margin-bottom:6px;"><<print _smd.rarity.toUpperCase()>> | <<print _smd.role>></div><</if>> <<capture _sid>> <<link "<span class='btn-locked' style='padding:5px 10px; font-size:0.7rem; display:inline-block; color:#fca5a5; border-color:#7f1d1d;'>DISMISS</span>" "MercenaryRoster">><<run setup.squadDismiss(_sid)>><<set $rosterLog to "Dismissed " + _sid + ". You can hire them again later.">><</link>> <</capture>> </div> <<else>> <div style="border:1px dashed #475569; border-radius:4px; padding:10px; text-align:center; color:#64748b; font-size:0.8rem; display:flex; align-items:center; justify-content:center; min-height:60px;">[ EMPTY SLOT ]</div> <</if>> <</for>> </div> <<if setup.hasCerberus()>> <div style="margin-top: 12px; text-align: center; color: #e9d5ff; font-size: 0.8rem;"> <<if setup.squadHas("Cerberus")>>🐺 CERBERUS is your active war-hound. <<link "<span class='cyber-action-btn' style='margin:0; padding:8px 15px;'>🔁 SWITCH TO REGULAR K9</span>" "MercenaryRoster">><<run setup.companionSwitch()>><</link>> <<else>>🐺 CERBERUS is waiting in camp. <<link "<span class='cyber-action-btn' style='margin:0; padding:8px 15px;'>🔁 SWITCH TO CERBERUS</span>" "MercenaryRoster">><<run setup.companionSwitch()>><</link>> <</if>> </div> <</if>> <div style="margin-top: 12px; text-align: center;"><<link "<span class='cyber-action-btn' style='margin:0; padding:8px 15px;'>🧭 SQUAD STRATEGY: SET POSITIONS AND ROLES</span>" "SquadStrategy">><</link>></div> <div style="margin-top: 10px; text-align: center; color: #94a3b8; font-size: 0.72rem;">Every ally acts on their own turn after yours, and you choose their skills. Plan who starts where, and as what, in Squad Strategy.</div> </div> <</nobr>> <<if def $rosterLog>> <div style="border: 1px solid #10b981; background: rgba(16, 185, 129, 0.1); color: #34d399; padding: 15px; margin-bottom: 20px; font-weight: bold; text-align: center; font-family: monospace; border-radius: 4px;"> <<print $rosterLog>> </div> <<unset $rosterLog>> <<endif>> <<nobr>> <div class="roster-grid" data-tour="roster-grid"> <!-- 1. TRAINED K9 --> <div class="merc-card"> <div> <div class="merc-title">🐾 TRAINED K9</div> <img src="images/npcs/all/companions/companion_k9_unit.jpg" class="merc-img" onerror="this.src='https://placehold.co/120x165/000/a855f7?text=K9+UNIT';"> <div class="merc-desc">A fierce, scarred wasteland dog. Performs brutal Throat Rips dealing massive critical damage.</div> <div class="merc-req" style="color: #9ca3af;">REQ: None | COST: 1,000 Scrip</div> </div> <<if setup.squadHas("Cerberus")>> <div class="btn-active">UPGRADED: CERBERUS IN YOUR SQUAD</div> <<elseif setup.squadHas("Trained K9")>> <div class="btn-active">IN YOUR SQUAD</div> <<elseif setup.squadFull()>> <div class="btn-locked">SQUAD FULL (DISMISS SOMEONE FIRST)</div> <<elseif $materials.scrip gte 1000>> <<link "<span class='btn-hire'>HIRE (1,000 SCRIP)</span>" "MercenaryRoster">> <<set $materials.scrip -= 1000>> <<run setup.squadHire("Trained K9")>> <<set $rosterLog to "Trained K9 hired. Man's best friend in the apocalypse.">> <</link>> <<else>> <div class="btn-locked">INSUFFICIENT SCRIP</div> <<endif>> </div> <!-- 2. ROOKIE GUNNER --> <div class="merc-card"> <div> <div class="merc-title">🔫 ROOKIE GUNNER</div> <img src="images/npcs/all/companions/companion_rookie_gunner.jpg" class="merc-img" onerror="this.src='https://placehold.co/120x165/000/a855f7?text=GUNNER';"> <div class="merc-desc">A desperate drifter with a rusty rifle. Provides basic cover fire and panics to spray down all targets.</div> <div class="merc-req" style="color: #34d399;">REQ: Known Mercenary | COST: 1,500 Scrip</div> </div> <<if setup.squadHas("Rookie Gunner")>> <div class="btn-active">IN YOUR SQUAD</div> <<elseif setup.squadFull()>> <div class="btn-locked">SQUAD FULL (DISMISS SOMEONE FIRST)</div> <<elseif $baseRep lt 20>> <div class="btn-locked">REP TOO LOW</div> <<elseif $materials.scrip gte 1500>> <<link "<span class='btn-hire'>HIRE (1,500 SCRIP)</span>" "MercenaryRoster">> <<set $materials.scrip -= 1500>> <<run setup.squadHire("Rookie Gunner")>> <<set $rosterLog to "Rookie Gunner hired. You have someone to watch your back now.">> <</link>> <<else>> <div class="btn-locked">INSUFFICIENT SCRIP</div> <<endif>> </div> <!-- 3. DEMO EXPERT --> <div class="merc-card"> <div> <div class="merc-title">💣 DEMO EXPERT</div> <img src="images/npcs/all/companions/companion_demo_expert.jpg" class="merc-img" onerror="this.src='https://placehold.co/120x165/000/a855f7?text=DEMO';"> <div class="merc-desc">Crazy bastard with a bag of pipe bombs. Lobs frag grenades that deal heavy damage to the entire enemy squad.</div> <div class="merc-req" style="color: #c084fc;">REQ: Respected Commander | COST: 2,000 Scrip</div> </div> <<if setup.squadHas("Demo Expert")>> <div class="btn-active">IN YOUR SQUAD</div> <<elseif setup.squadFull()>> <div class="btn-locked">SQUAD FULL (DISMISS SOMEONE FIRST)</div> <<elseif $baseRep lt 50>> <div class="btn-locked">REP TOO LOW</div> <<elseif $materials.scrip gte 2000>> <<link "<span class='btn-hire'>HIRE (2,000 SCRIP)</span>" "MercenaryRoster">> <<set $materials.scrip -= 2000>> <<run setup.squadHire("Demo Expert")>> <<set $rosterLog to "Demo Expert hired. Keep your distance when he starts throwing.">> <</link>> <<else>> <div class="btn-locked">INSUFFICIENT SCRIP</div> <<endif>> </div> <!-- 4. SECTOR HACKER --> <div class="merc-card"> <div> <div class="merc-title">💻 SECTOR HACKER</div> <img src="images/npcs/all/companions/companion_sector_hacker.jpg" class="merc-img" onerror="this.src='https://placehold.co/120x165/000/a855f7?text=HACKER';"> <div class="merc-desc">Tech-savvy specialist. Scrambles enemy optics (-75% hit chance) or triggers System Overloads to Stun targets.</div> <div class="merc-req" style="color: #c084fc;">REQ: Respected Commander | COST: 3,000 Scrip</div> </div> <<if setup.squadHas("Sector Hacker")>> <div class="btn-active">IN YOUR SQUAD</div> <<elseif setup.squadFull()>> <div class="btn-locked">SQUAD FULL (DISMISS SOMEONE FIRST)</div> <<elseif $baseRep lt 50>> <div class="btn-locked">REP TOO LOW</div> <<elseif $materials.scrip gte 3000>> <<link "<span class='btn-hire'>HIRE (3,000 SCRIP)</span>" "MercenaryRoster">> <<set $materials.scrip -= 3000>> <<run setup.squadHire("Sector Hacker")>> <<set $rosterLog to "Sector Hacker hired. Digital locks and enemy optics are compromised.">> <</link>> <<else>> <div class="btn-locked">INSUFFICIENT SCRIP</div> <<endif>> </div> <!-- 5. FIELD MEDIC --> <div class="merc-card"> <div> <div class="merc-title">💉 FIELD MEDIC</div> <img src="images/npcs/all/companions/companion_field_medic.jpg" class="merc-img" onerror="this.src='https://placehold.co/120x165/000/a855f7?text=MEDIC';"> <div class="merc-desc">A former clinic surgeon. Patches both your wounds and her own during intense combat.</div> <div class="merc-req" style="color: #fbbf24;">REQ: Wasteland Legend | COST: 5,000 Scrip</div> </div> <<if setup.squadHas("Field Medic")>> <div class="btn-active">IN YOUR SQUAD</div> <<elseif setup.squadFull()>> <div class="btn-locked">SQUAD FULL (DISMISS SOMEONE FIRST)</div> <<elseif $baseRep lt 100>> <div class="btn-locked">REP TOO LOW</div> <<elseif $materials.scrip gte 5000>> <<link "<span class='btn-hire'>HIRE (5,000 SCRIP)</span>" "MercenaryRoster">> <<set $materials.scrip -= 5000>> <<run setup.squadHire("Field Medic")>> <<set $rosterLog to "Field Medic hired. Survival odds increased.">> <</link>> <<else>> <div class="btn-locked">INSUFFICIENT SCRIP</div> <<endif>> </div> <!-- 6. SYNDICATE DEFECTOR --> <div class="merc-card"> <div> <div class="merc-title">🛡️ SYNDICATE DEFECTOR</div> <img src="images/npcs/all/companions/companion_syndicate_defector.jpg" class="merc-img" onerror="this.src='https://placehold.co/120x165/000/a855f7?text=DEFECTOR';"> <div class="merc-desc">Ex-military enforcer. Uses Defensive Barrage to taunt enemies, absorb damage safely, and hold the frontline.</div> <div class="merc-req" style="color: #fbbf24;">REQ: Wasteland Legend | COST: 10,000 Scrip</div> </div> <<if setup.squadHas("Syndicate Defector")>> <div class="btn-active">IN YOUR SQUAD</div> <<elseif setup.squadFull()>> <div class="btn-locked">SQUAD FULL (DISMISS SOMEONE FIRST)</div> <<elseif $baseRep lt 100>> <div class="btn-locked">REP TOO LOW</div> <<elseif $materials.scrip gte 10000>> <<link "<span class='btn-hire'>HIRE (10,000 SCRIP)</span>" "MercenaryRoster">> <<set $materials.scrip -= 10000>> <<run setup.squadHire("Syndicate Defector")>> <<set $rosterLog to "Syndicate Defector hired. You now have an unstoppable wall by your side.">> <</link>> <<else>> <div class="btn-locked">INSUFFICIENT SCRIP</div> <<endif>> </div> </div> <!-- BAR-LEVEL RECRUITS: one unique mercenary for every Wasteland Bar level --> <div style="text-align: center; border-bottom: 1px dashed #a855f7; padding-bottom: 10px; margin: 35px 0 5px 0;"> <h3 style="color: #c084fc; margin: 0; letter-spacing: 3px; font-size: 1.2rem;">[ BAR-LEVEL RECRUITS ]</h3> <div style="color: #94a3b8; font-size: 0.8rem; margin-top: 6px;">Upgrade the Wasteland Bar and better mercenaries start drinking there. One unique recruit per level, rarer each time.</div> </div> <div class="roster-grid"> <<set _barLvl to ($sanctuaryLevels.bar || 1)>> <<for _mk, _md range setup.MERC>> <<set _mcol to _md.color>> <<set _mhire to ("<span class='btn-hire'>HIRE (" + _md.cost.toLocaleString("en-US") + " SCRIP)</span>")>> <<capture _mk, _md>> <div class="merc-card" @style="'border-color:' + _mcol + ';'"> <div> <div class="merc-title" @style="'color:' + _mcol + '; border-bottom-color:' + _mcol + ';'"><<print _md.icon>> <<print _md.disp.toUpperCase()>></div> <img @src="_md.avatar" class="merc-img" @style="'border-color:' + _mcol + ';'" onerror="this.src='https://placehold.co/120x165/000/a855f7?text=MERC';"> <div class="merc-desc"><span @style="'color:' + _mcol + '; font-weight:bold; font-size:0.75rem; letter-spacing:1px;'"><<print _md.rarity.toUpperCase()>></span> | <span style="color:#94a3b8; font-size:0.75rem;"><<print _md.role>></span><br><br><<print _md.story>><br><br><i style="color:#e9d5ff;"><<print _md.quote>></i><br><br><span style="color:#cbd5e1;"><<print _md.desc>></span></div> <div class="merc-req" @style="'color:' + _mcol + ';'">REQ: Wasteland Bar Level <<print _md.barLevel>> | COST: <<print _md.cost.toLocaleString("en-US")>> Scrip</div> </div> <<if setup.squadHas(_mk)>> <div class="btn-active">IN YOUR SQUAD</div> <<elseif _barLvl lt _md.barLevel>> <div class="btn-locked">REQUIRES WASTELAND BAR LEVEL <<print _md.barLevel>></div> <<elseif setup.squadFull()>> <div class="btn-locked">SQUAD FULL (DISMISS SOMEONE FIRST)</div> <<elseif $materials.scrip gte _md.cost>> <<link _mhire "MercenaryRoster">> <<set $materials.scrip -= _md.cost>> <<run setup.squadHire(_mk)>> <<set $rosterLog to _md.disp + " signs on. " + _md.quote>> <</link>> <<else>> <div class="btn-locked">INSUFFICIENT SCRIP</div> <<endif>> </div> <</capture>> <</for>> </div> <<endnobr>> <div style="text-align: center; margin-top: 35px;"> <<link "<span class='cyber-return-btn'>⬅ RETURN TO BAR</span>" "WastelandBar">><</link>> </div> </div> </div>
<<nobr>> <<set $quest_slaughterhouse to "completed_grom">> <<set $materials.scrip += 5000>> <<set $svietlok_buff to false>> <<set $svietlok_legend_alive to false>> <<audio "grom_radio" play>> <div style="background: rgba(20, 5, 5, 0.95); border: 2px solid #ef4444; padding: 30px; font-family: 'Courier New', monospace; text-align: center;"> <h2 style="color: #ef4444; letter-spacing: 4px; text-shadow: 0 0 15px #ef4444;">[ COLD BLOODED ]</h2> <!-- BETRAYAL ENDING IMAGE --> <div style="margin: 20px 0;"> <img src="images/quests/mercenary_ending.jpg" style="max-width: 100%; border: 2px solid #ef4444; border-radius: 4px; box-shadow: 0 0 20px rgba(239, 68, 68, 0.4);"> </div> <p style="color: #cbd5e1; font-size: 1.1rem; line-height: 1.8;"> You gave the coordinates to Grom's men. As the old man was dragged back into a cage, he looked at you with pure hatred and betrayal. The light went out completely. <br><br> Grom's men tossed you a duffel bag stuffed to the brim with Scrip. Being a legend doesn't put food on the table, but this money certainly will. </p> <div style="margin: 30px 0; color: #fbbf24; font-weight: bold; font-size: 1.2rem;"> > BETRAYAL COMPLETE.<br> > THE SVIETLOK LEGEND IS DEAD.<br> > +5000 SCRIP EARNED. </div> @@color:#050a0f;background:#ef4444;font-weight:bold;padding:12px 25px;display:inline-block;border-radius:2px; <<link "RETURN TO SANCTUARY" "Camp">><</link>>@@ </div> <</nobr>>
<<nobr>> <style> #story { margin-top: 0 !important; margin-right: 0 !important; margin-bottom: 0 !important; margin-left: 17.5em !important; padding: 0 !important; } #passages { max-width: 100% !important; padding: 0 !important; margin: 0 !important; border: none !important; } .passage { padding: 0 !important; margin: 0 !important; } .merchant-custom-btn { background: rgba(56, 189, 248, 0.1) !important; border: 1px solid #38bdf8 !important; color: #38bdf8 !important; padding: 8px 16px !important; border-radius: 4px !important; font-weight: bold !important; font-family: 'Courier New', monospace !important; cursor: pointer !important; text-transform: uppercase !important; letter-spacing: 1px !important; box-shadow: 0 0 8px rgba(56,189,248,0.2) !important; transition: all 0.2s ease-in-out !important; display: inline-block !important; text-decoration: none !important; } .merchant-custom-btn:hover { background: rgba(56, 189, 248, 0.25) !important; color: #ffffff !important; box-shadow: 0 0 12px rgba(56,189,248,0.5) !important; } .bj-custom-btn { background: rgba(34, 197, 94, 0.1) !important; border: 1px solid #22c55e !important; color: #22c55e !important; padding: 8px 16px !important; border-radius: 4px !important; font-weight: bold !important; font-family: 'Courier New', monospace !important; cursor: pointer !important; text-transform: uppercase !important; letter-spacing: 1px !important; box-shadow: 0 0 8px rgba(34,197,94,0.2) !important; transition: all 0.2s ease-in-out !important; display: inline-block !important; text-decoration: none !important; } .bj-custom-btn:hover { background: rgba(34, 197, 94, 0.25) !important; color: #ffffff !important; box-shadow: 0 0 12px rgba(34,197,94,0.5) !important; } </style> <<setMenu "Merchant">> <<if !$merchantStock>><<set $merchantStock to setup.getDailyMerchantStock()>><</if>> <<if ndef $bjState>><<set $bjState to "idle">><</if>> <<if ndef $kaelenDialogue>><<set $kaelenDialogue to '"The wasteland winds blow harsh, stranger. My stock holds steady until dawn breaks on a new day. Take a look at what I have today—tomorrow the caravan moves on."'>><</if>> /* VIP discount calculations */ <<set _rumorCost to setup.shopPrice(30)>> <<set _intelCost to setup.shopPrice(60)>> <div class="terminal-wide-core" style="background: linear-gradient(rgba(5, 10, 18, 0.8), rgba(5, 10, 18, 0.95)), url('images/locations/caravan_bg.jpg') center/cover no-repeat; background-attachment: fixed; min-height: 100vh; padding: 40px; font-family: 'Courier New', monospace; color: #00ff00; width: 100%; box-sizing: border-box; display: flex; flex-direction: column;"> <div style="text-align: center; border-bottom: 2px solid #38bdf8; padding-bottom: 10px; margin-bottom: 20px;"> <div style="color: #38bdf8; font-size: 1.4rem; font-weight: bold; letter-spacing: 3px; text-shadow: 0 0 10px rgba(56,189,248,0.6);">[ DRIFTER CARAVAN TRADING POST ]</div> <div style="color: #7dd3fc; font-size: 0.8rem; margin-top: 5px; letter-spacing: 1px;">DAY <<print $day>> MOBILE NOMAD OUTPOST</div> </div> <div style="display: flex; gap: 20px; width: 100%; align-items: stretch;"> <div style="flex: 1; background: rgba(0,0,0,0.7); border: 2px solid #38bdf8; padding: 20px; border-radius: 8px; box-shadow: 0 0 20px rgba(56,189,248,0.2); display: flex; flex-direction: column; justify-content: space-between;"> <div data-tour="kaelen-say" style="display: flex; gap: 15px; align-items: center;"> <img src="images/npcs/npc_merchant.jpg" style="width: 120px; height: 150px; object-fit: cover; border-radius: 4px; border: 2px solid #38bdf8; box-shadow: 0 0 12px rgba(56,189,248,0.4); flex-shrink: 0;" alt="Merchant"> <div> <div style="color: #38bdf8; font-weight: bold; font-size: 1.1rem; margin-bottom: 6px; letter-spacing: 1px;">Senior Trader Kaelen</div> <div style="color: #cbd5e1; font-style: italic; font-size: 0.85rem; line-height: 1.5; margin: 0;"><<print $kaelenDialogue>></div> </div> </div> <div style="background: rgba(5, 15, 25, 0.95); border: 1px dashed #38bdf8; padding: 10px; border-radius: 4px; margin-top: 20px; text-align: center; font-size: 0.95rem; color: #fde047;">Your Available Scrip: <strong><<print ($materials.scrip || 0)>> 🪙</strong></div> <div data-tour="merchant-btns" style="display: flex; gap: 10px; justify-content: center; margin-top: 15px; flex-wrap: wrap;"> <<if setup.tour.freeRumorOn()>><span data-tour="merchant-free"><<link '<span class="merchant-custom-btn">TAKE THE FREE RUMOR</span>' "Merchant">><<run setup.tour.freeRumor()>><</link>></span><</if>> <<link '<span class="merchant-custom-btn">CHAT</span>' "Merchant">> <<set _r to random(1,3)>> <<if _r eq 1>> <<set $kaelenDialogue to "The dust never settles out here. What do you need, kid? Make it quick.">> <<audio "Kaelen_Greet1" volume 1.0 play>> <<elseif _r eq 2>> <<set $kaelenDialogue to "Look around. My stock is the only thing keeping you alive for another day.">> <<audio "Kaelen_Greet2" volume 1.0 play>> <<else>> <<set $kaelenDialogue to "Buy something or keep moving. I ain t running a charity, kid.">> <<audio "Kaelen_Greet3" volume 1.0 play>> <<endif>> <</link>> <<link '<span class="merchant-custom-btn">🏛 TRADING POST</span>' "Merchant">><<run window.setup.ah.open()>><</link>> <<if ($materials.scrip || 0) gte _rumorCost>> <<set _btnRumor to '<span class="merchant-custom-btn">BUY RUMOR (' + _rumorCost + '🪙)</span>'>> <<link _btnRumor "Merchant">> <<run setup.merchantRumor("rumor", _rumorCost)>> <<if $kaelenAudio>><<audio $kaelenAudio volume 1.0 play>><</if>> <</link>> <<endif>> <<if ($materials.scrip || 0) gte _intelCost>> <<set _btnIntel to '<span class="merchant-custom-btn">BUY INTEL (' + _intelCost + '🪙)</span>'>> <<link _btnIntel "Merchant">> <<run setup.merchantRumor("intel", _intelCost)>> <<if $kaelenAudio>><<audio $kaelenAudio volume 1.0 play>><</if>> <</link>> <<endif>> </div> <<nobr>> <div style="background: rgba(5, 15, 25, 0.95); border: 1px solid #f59e0b; padding: 12px; border-radius: 4px; margin-top: 16px; font-size: 0.85rem; line-height: 1.5;"> <div style="color: #fcd34d; font-weight: bold; letter-spacing: 2px; margin-bottom: 6px;">[ KAELEN'S WORK: THE LOWER SHAFTS ]</div> <<if ($dgQuest || 0) is 0>> <<if ($level || 1) lt 4>> <div style="color:#94a3b8;">"Come back when you have seen a few more sunrises, kid. I have work, but not for the green ones." (Requires Level 4)</div> <<else>> <div style="color:#cbd5e1;">"There is an old coal mine east of the park. My brother's crew went down and never came up. I want the foreman's lamp. Interested?"</div> <div style="margin-top:8px;"><<link "<span class='merchant-custom-btn'>TAKE THE JOB: CLEAR THE LOWER SHAFTS</span>" "Merchant">><<run setup.dg.accept()>><<run window.setup.updateSidebar()>><</link>></div> <</if>> <<elseif $dgQuest is 1>> <div style="color:#cbd5e1;">"The Wujek Shafts are marked on your map. Find the key, break the gate and bring me Kruk's lamp."</div> <<elseif $dgQuest is 2>> <div style="color:#bbf7d0;">You are carrying Foreman Kruk's lamp. Kaelen's eyes go to it at once.</div> <div style="margin-top:8px;"><<link "<span class='merchant-custom-btn'>HAND OVER THE LAMP (+500 SCRIP, +300 XP, GEAR)</span>" "Merchant">><<run setup.dg.turnIn()>><<run window.setup.updateSidebar()>><</link>></div> <<else>> <div style="color:#94a3b8;">Job done. The mine is still there if you want to go back down for the loot.</div> <</if>> </div> <</nobr>> </div> <div style="flex: 1; background: rgba(0,0,0,0.7); border: 2px solid #38bdf8; padding: 20px; border-radius: 8px; box-shadow: 0 0 20px rgba(56,189,248,0.2); display: flex; flex-direction: column; justify-content: space-between;"> <div> <div style="color: #38bdf8; font-weight: bold; font-size: 1rem; border-bottom: 1px solid rgba(56,189,248,0.4); padding-bottom: 8px; margin-bottom: 15px; text-align: center; letter-spacing: 2px;">[ TODAY'S AVAILABLE STOCK ]</div> <<if $merchantStock and $merchantStock.length gt 0>> <<set _item0 to $merchantStock[0]>> <<set _cost0 to setup.shopPrice(_item0.cost)>> <div style="background: rgba(5, 15, 25, 0.9); border: 1px solid #38bdf8; padding: 12px; border-radius: 6px; margin-bottom: 12px; display: flex; justify-content: space-between; align-items: center;"> <div style="padding-right: 10px;"> <div style="color: #38bdf8; font-weight: bold; font-size: 0.95rem;"><<print _item0.name>> (x<<print _item0.qty>>)</div> <div style="font-size: 0.75rem; color: #94a3b8; margin-top: 3px;"><<print _item0.desc>></div> <div style="font-size: 0.85rem; color: #fde047; margin-top: 6px; font-weight: bold;"> Price: <<if $perks and $perks.clearance eq true>> <span style="text-decoration: line-through; color: #ef4444;"><<print _item0.cost>></span> <span style="color: #34d399;"><<print _cost0>> (VIP)</span> <<else>> <<print _cost0>> <<endif>> Scrip </div> </div> <div> <<set _playerScrip0 to ($materials.scrip || 0)>> <<if _playerScrip0 gte _cost0>> <<link '<span class="merchant-custom-btn" style="padding: 4px 10px !important; font-size: 0.8rem;">BUY</span>' "Merchant">> <<set $materials.scrip -= _cost0>> <<set $materials[_item0.type] += _item0.qty>> <<run window.setup.updateSidebar()>> <</link>> <<else>> <div style="color: #ef4444; font-size: 0.75rem; font-weight: bold; white-space: nowrap;">[ NO SCRIP ]</div> <</if>> </div> </div> <<if $merchantStock.length gt 1>> <<set _item1 to $merchantStock[1]>> <<set _cost1 to setup.shopPrice(_item1.cost)>> <div style="background: rgba(5, 15, 25, 0.9); border: 1px solid #38bdf8; padding: 12px; border-radius: 6px; margin-bottom: 12px; display: flex; justify-content: space-between; align-items: center;"> <div style="padding-right: 10px;"> <div style="color: #38bdf8; font-weight: bold; font-size: 0.95rem;"><<print _item1.name>> (x<<print _item1.qty>>)</div> <div style="font-size: 0.75rem; color: #94a3b8; margin-top: 3px;"><<print _item1.desc>></div> <div style="font-size: 0.85rem; color: #fde047; margin-top: 6px; font-weight: bold;"> Price: <<if $perks and $perks.clearance eq true>> <span style="text-decoration: line-through; color: #ef4444;"><<print _item1.cost>></span> <span style="color: #34d399;"><<print _cost1>> (VIP)</span> <<else>> <<print _cost1>> <<endif>> Scrip </div> </div> <div> <<set _playerScrip1 to ($materials.scrip || 0)>> <<if _playerScrip1 gte _cost1>> <<link '<span class="merchant-custom-btn" style="padding: 4px 10px !important; font-size: 0.8rem;">BUY</span>' "Merchant">> <<set $materials.scrip -= _cost1>> <<set $materials[_item1.type] += _item1.qty>> <<run window.setup.updateSidebar()>> <</link>> <<else>> <div style="color: #ef4444; font-size: 0.75rem; font-weight: bold; white-space: nowrap;">[ NO SCRIP ]</div> <</if>> </div> </div> <</if>> <</if>> </div> <<if $npcFlags and $npcFlags.maya>> <<set _pc to setup.shopPrice(setup.ARCHIVE.paperCost)>> <div style="background: rgba(25, 15, 5, 0.9); border: 1px solid #f59e0b; padding: 12px; border-radius: 6px; margin-bottom: 12px; display: flex; justify-content: space-between; align-items: center;"> <div style="padding-right: 10px;"> <div style="color: #f59e0b; font-weight: bold; font-size: 0.95rem;">📜 Archival Paper & Ink (x1) <span style="font-size:0.65rem; border:1px solid #f59e0b; padding:1px 5px; margin-left:6px;">SPECIAL ORDER</span></div> <div style="font-size: 0.75rem; color: #94a3b8; margin-top: 3px;">A sealed ream of acid-free pre-war paper and a vial of archival ink. Nobody else in the sector sells it. A scholar would treasure it.</div> <div style="font-size: 0.85rem; color: #fde047; margin-top: 6px; font-weight: bold;">Price: <<print _pc>> Scrip</div> <div style="font-size: 0.7rem; color: #94a3b8; margin-top: 3px;">Limit: 1 per day.</div> </div> <div> <<if ($paperBoughtDay || 0) is $day>> <div style="color: #fca5a5; font-size: 0.75rem; font-weight: bold; white-space: nowrap;">[ SOLD OUT TODAY ]</div> <<elseif ($materials.scrip || 0) gte _pc>> <<link '<span class="merchant-custom-btn" style="padding: 4px 10px !important; font-size: 0.8rem;">BUY</span>' "Merchant">> <<set $materials.scrip -= _pc>> <<set $paperBoughtDay to $day>> <<set $materials.archivalPaper to (($materials.archivalPaper || 0) + 1)>> <<run window.setup.updateSidebar()>> <</link>> <<else>> <div style="color: #ef4444; font-size: 0.75rem; font-weight: bold; white-space: nowrap;">[ NO SCRIP ]</div> <</if>> </div> </div> <</if>> <div style="text-align: center; font-size: 0.75rem; color: #94a3b8; margin-top: 10px;">* Stock rotates automatically upon a new day.</div> </div> </div> <!-- WASTELAND 21 / BLACKJACK --> <div data-tour="merchant-bj" style="margin-top: 20px; background: linear-gradient(rgba(5, 15, 10, 0.7), rgba(5, 15, 10, 0.9)), url('images/locations/blackjack_bg.jpg') center/cover no-repeat; border: 2px dashed #22c55e; border-radius: 8px; padding: 20px; text-align: center; box-shadow: inset 0 0 20px rgba(34,197,94,0.3);"> <div style="color: #22c55e; font-weight: bold; font-size: 1.1rem; letter-spacing: 2px; margin-bottom: 15px; text-shadow: 0 0 10px rgba(34,197,94,0.8);">[ WASTELAND 21 ]</div> <<if $bjState eq "idle">> <div style="color: #94a3b8; font-size: 0.85rem; margin-bottom: 15px; background: rgba(0,0,0,0.5); display: inline-block; padding: 5px 15px; border-radius: 4px;">"Care for a game of cards, drifter? Double your Scrip, or lose it all."</div> <div style="display: flex; gap: 10px; justify-content: center; flex-wrap: wrap;"> <<if ($materials.scrip || 0) gte 10>> <<link '<span class="bj-custom-btn">BET 10</span>' "Merchant">> <<audio "Kaelen_BJ_Start" volume 1.0 play>> <<set $kaelenDialogue to "Heheh. Think you can beat the old man at his own game? Sit down, kid. Let s see if the wasteland favors you today.">> <<set $materials.scrip -= 10>> <<set $bjBet to 10>> <<set _deck to ["2","3","4","5","6","7","8","9","10","J","Q","K","A"]>> <<set $bjPlayerHand to [_deck.random(), _deck.random()]>> <<set $bjDealerHand to [_deck.random()]>> <<set $bjState to "playing">> <</link>> <<endif>> <<if ($materials.scrip || 0) gte 50>> <<link '<span class="bj-custom-btn">BET 50</span>' "Merchant">> <<audio "Kaelen_BJ_Start" volume 1.0 play>> <<set $kaelenDialogue to "Heheh. Think you can beat the old man at his own game? Sit down, kid. Let s see if the wasteland favors you today.">> <<set $materials.scrip -= 50>> <<set $bjBet to 50>> <<set _deck to ["2","3","4","5","6","7","8","9","10","J","Q","K","A"]>> <<set $bjPlayerHand to [_deck.random(), _deck.random()]>> <<set $bjDealerHand to [_deck.random()]>> <<set $bjState to "playing">> <</link>> <<endif>> <<if ($materials.scrip || 0) gte 100>> <<link '<span class="bj-custom-btn">BET 100</span>' "Merchant">> <<audio "Kaelen_BJ_Start" volume 1.0 play>> <<set $kaelenDialogue to "Heheh. Think you can beat the old man at his own game? Sit down, kid. Let s see if the wasteland favors you today.">> <<set $materials.scrip -= 100>> <<set $bjBet to 100>> <<set _deck to ["2","3","4","5","6","7","8","9","10","J","Q","K","A"]>> <<set $bjPlayerHand to [_deck.random(), _deck.random()]>> <<set $bjDealerHand to [_deck.random()]>> <<set $bjState to "playing">> <</link>> <<endif>> <<if ($materials.scrip || 0) gte 250>> <<link '<span class="bj-custom-btn">BET 250</span>' "Merchant">> <<audio "Kaelen_BJ_Start" volume 1.0 play>> <<set $kaelenDialogue to "Heheh. Think you can beat the old man at his own game? Sit down, kid. Let s see if the wasteland favors you today.">> <<set $materials.scrip -= 250>> <<set $bjBet to 250>> <<set _deck to ["2","3","4","5","6","7","8","9","10","J","Q","K","A"]>> <<set $bjPlayerHand to [_deck.random(), _deck.random()]>> <<set $bjDealerHand to [_deck.random()]>> <<set $bjState to "playing">> <</link>> <<endif>> </div> <<if ($materials.scrip || 0) lt 10>> <div style="margin-top: 15px; color: #ef4444; font-size: 0.8rem; font-weight: bold; background: rgba(0,0,0,0.6); display: inline-block; padding: 5px 10px; border-radius: 4px;">[ NOT ENOUGH SCRIP TO PLAY ]</div> <<endif>> <</if>> <<if $bjState eq "playing">> <div style="display: flex; justify-content: space-around; margin-bottom: 20px;"> <div style="background: rgba(0,0,0,0.6); padding: 15px; border-radius: 6px; border: 1px solid rgba(56,189,248,0.3);"> <div style="color: #7dd3fc; font-size: 0.8rem; margin-bottom: 10px; text-align: center; letter-spacing: 1px;">YOUR HAND</div> <div style="display: flex; gap: 8px; justify-content: center;"> <<set _pTotal to 0>> <<set _pAces to 0>> <<for _card range $bjPlayerHand>> <<if _card eq "J" or _card eq "Q" or _card eq "K">> <<set _pTotal += 10>> <<elseif _card eq "A">> <<set _pTotal += 11>> <<set _pAces += 1>> <<else>> <<set _pTotal += parseInt(_card)>> <</if>> <div style="width: 45px; height: 65px; border: 1px solid #38bdf8; border-radius: 4px; background: rgba(5,15,25,0.95); display: flex; align-items: center; justify-content: center; font-size: 1.3rem; font-weight: bold; color: #fde047; box-shadow: 0 0 8px rgba(56,189,248,0.4);"><<print _card>></div> <</for>> <<for _pTotal gt 21 and _pAces gt 0>> <<set _pTotal -= 10>> <<set _pAces -= 1>> <</for>> </div> <div style="color: #fde047; font-size: 0.9rem; font-weight: bold; margin-top: 10px; text-align: center;">TOTAL: <<print _pTotal>></div> </div> <div style="background: rgba(0,0,0,0.6); padding: 15px; border-radius: 6px; border: 1px solid rgba(239,68,68,0.3);"> <div style="color: #fca5a5; font-size: 0.8rem; margin-bottom: 10px; text-align: center; letter-spacing: 1px;">DEALER SHOWS</div> <div style="display: flex; gap: 8px; justify-content: center;"> <<set _dTotal to 0>> <<set _dAces to 0>> <<for _card range $bjDealerHand>> <<if _card eq "J" or _card eq "Q" or _card eq "K">> <<set _dTotal += 10>> <<elseif _card eq "A">> <<set _dTotal += 11>> <<set _dAces += 1>> <<else>> <<set _dTotal += parseInt(_card)>> <</if>> <div style="width: 45px; height: 65px; border: 1px solid #ef4444; border-radius: 4px; background: rgba(20,5,5,0.95); display: flex; align-items: center; justify-content: center; font-size: 1.3rem; font-weight: bold; color: #fde047; box-shadow: 0 0 8px rgba(239,68,68,0.4);"><<print _card>></div> <</for>> <<for _dTotal gt 21 and _dAces gt 0>> <<set _dTotal -= 10>> <<set _dAces -= 1>> <</for>> <div style="width: 45px; height: 65px; border: 1px dashed #ef4444; border-radius: 4px; background: repeating-linear-gradient(45deg, #1a0505, #1a0505 5px, #3a0a0a 5px, #3a0a0a 10px); display: flex; align-items: center; justify-content: center; font-size: 1.3rem; font-weight: bold; color: #ef4444;">?</div> </div> <div style="color: #fde047; font-size: 0.9rem; font-weight: bold; margin-top: 10px; text-align: center;">TOTAL: <<print _dTotal>> + ?</div> </div> </div> <div style="display: flex; gap: 15px; justify-content: center;"> <<link '<span class="bj-custom-btn">HIT</span>' "Merchant">> <<set _deck to ["2","3","4","5","6","7","8","9","10","J","Q","K","A"]>> <<run $bjPlayerHand.push(_deck.random())>> <<set _tempTotal to 0>> <<set _tempAces to 0>> <<for _c range $bjPlayerHand>> <<if _c eq "J" or _c eq "Q" or _c eq "K">> <<set _tempTotal += 10>> <<elseif _c eq "A">> <<set _tempTotal += 11>> <<set _tempAces += 1>> <<else>> <<set _tempTotal += parseInt(_c)>> <</if>> <</for>> <<for _tempTotal gt 21 and _tempAces gt 0>> <<set _tempTotal -= 10>> <<set _tempAces -= 1>> <</for>> <<if _tempTotal gt 21>> <<set $bjState to "gameOver">> <<set $bjResult to "BUST! YOU LOSE.">> <<set $kaelenDialogue to "Heheh. The house always wins, kid. Better luck tomorrow... if you live that long.">> <<audio "Kaelen_BJ_Lose" volume 1.0 play>> <<endif>> <</link>> <<link '<span class="bj-custom-btn" style="border-color:#ef4444; color:#ef4444;">STAND</span>' "Merchant">> <<set _deck to ["2","3","4","5","6","7","8","9","10","J","Q","K","A"]>> <<set _dTotal to 0>> <<set _dAces to 0>> <<for _c range $bjDealerHand>> <<if _c eq "J" or _c eq "Q" or _c eq "K">> <<set _dTotal += 10>> <<elseif _c eq "A">> <<set _dTotal += 11>> <<set _dAces += 1>> <<else>> <<set _dTotal += parseInt(_c)>> <</if>> <</for>> <<for _dTotal gt 21 and _dAces gt 0>> <<set _dTotal -= 10>> <<set _dAces -= 1>> <</for>> <<for _limit to 0; _dTotal lt 17 and _limit lt 10; _limit++>> <<run $bjDealerHand.push(_deck.random())>> <<set _dTotal to 0>> <<set _dAces to 0>> <<for _c range $bjDealerHand>> <<if _c eq "J" or _c eq "Q" or _c eq "K">> <<set _dTotal += 10>> <<elseif _c eq "A">> <<set _dTotal += 11>> <<set _dAces += 1>> <<else>> <<set _dTotal += parseInt(_c)>> <</if>> <</for>> <<for _dTotal gt 21 and _dAces gt 0>> <<set _dTotal -= 10>> <<set _dAces -= 1>> <</for>> <</for>> <<set _pTotal to 0>> <<set _pAces to 0>> <<for _c range $bjPlayerHand>> <<if _c eq "J" or _c eq "Q" or _c eq "K">> <<set _pTotal += 10>> <<elseif _c eq "A">> <<set _pTotal += 11>> <<set _pAces += 1>> <<else>> <<set _pTotal += parseInt(_c)>> <</if>> <</for>> <<for _pTotal gt 21 and _pAces gt 0>> <<set _pTotal -= 10>> <<set _pAces -= 1>> <</for>> <<set $bjState to "gameOver">> <<if _dTotal gt 21>> <<set $bjResult to "DEALER BUSTS! YOU WIN.">> <<set $materials.scrip += ($bjBet * 2)>> <<set $kaelenDialogue to "Hmph. Beginner s luck. Take your scrip before I change my mind, kid.">> <<audio "Kaelen_BJ_Win" volume 1.0 play>> <<elseif _pTotal gt _dTotal>> <<set $bjResult to "YOU WIN!">> <<set $materials.scrip += ($bjBet * 2)>> <<set $kaelenDialogue to "Hmph. Beginner s luck. Take your scrip before I change my mind, kid.">> <<audio "Kaelen_BJ_Win" volume 1.0 play>> <<elseif _pTotal lt _dTotal>> <<set $bjResult to "DEALER WINS.">> <<set $kaelenDialogue to "Heheh. The house always wins, kid. Better luck tomorrow... if you live that long.">> <<audio "Kaelen_BJ_Lose" volume 1.0 play>> <<else>> <<set $bjResult to "PUSH (TIE).">> <<set $materials.scrip += $bjBet>> <<set $kaelenDialogue to "Hmph. A draw. Nobody bleeds today. Take your scrip back, kid.">> <<audio "Kaelen_BJ_Tie" volume 1.0 play>> <<endif>> <</link>> </div> <</if>> <<if $bjState eq "gameOver">> <div style="display: flex; justify-content: space-around; margin-bottom: 20px;"> <div style="background: rgba(0,0,0,0.6); padding: 15px; border-radius: 6px; border: 1px solid rgba(56,189,248,0.3);"> <div style="color: #7dd3fc; font-size: 0.8rem; margin-bottom: 10px; text-align: center; letter-spacing: 1px;">YOUR HAND</div> <div style="display: flex; gap: 8px; justify-content: center;"> <<set _pTotal to 0>> <<set _pAces to 0>> <<for _card range $bjPlayerHand>> <<if _card eq "J" or _card eq "Q" or _card eq "K">> <<set _pTotal += 10>> <<elseif _card eq "A">> <<set _pTotal += 11>> <<set _pAces += 1>> <<else>> <<set _pTotal += parseInt(_card)>> <</if>> <div style="width: 45px; height: 65px; border: 1px solid #38bdf8; border-radius: 4px; background: rgba(5,15,25,0.95); display: flex; align-items: center; justify-content: center; font-size: 1.3rem; font-weight: bold; color: #fde047; box-shadow: 0 0 8px rgba(56,189,248,0.4);"><<print _card>></div> <</for>> <<for _pTotal gt 21 and _pAces gt 0>> <<set _pTotal -= 10>> <<set _pAces -= 1>> <</for>> </div> <div style="color: #fde047; font-size: 0.9rem; font-weight: bold; margin-top: 10px; text-align: center;">TOTAL: <<print _pTotal>></div> </div> <div style="background: rgba(0,0,0,0.6); padding: 15px; border-radius: 6px; border: 1px solid rgba(239,68,68,0.3);"> <div style="color: #fca5a5; font-size: 0.8rem; margin-bottom: 10px; text-align: center; letter-spacing: 1px;">DEALER TOTAL</div> <div style="display: flex; gap: 8px; justify-content: center;"> <<set _dTotal to 0>> <<set _dAces to 0>> <<for _card range $bjDealerHand>> <<if _card eq "J" or _card eq "Q" or _card eq "K">> <<set _dTotal += 10>> <<elseif _card eq "A">> <<set _dTotal += 11>> <<set _dAces += 1>> <<else>> <<set _dTotal += parseInt(_card)>> <</if>> <div style="width: 45px; height: 65px; border: 1px solid #ef4444; border-radius: 4px; background: rgba(20,5,5,0.95); display: flex; align-items: center; justify-content: center; font-size: 1.3rem; font-weight: bold; color: #fde047; box-shadow: 0 0 8px rgba(239,68,68,0.4);"><<print _card>></div> <</for>> <<for _dTotal gt 21 and _dAces gt 0>> <<set _dTotal -= 10>> <<set _dAces -= 1>> <</for>> </div> <div style="color: #fde047; font-size: 0.9rem; font-weight: bold; margin-top: 10px; text-align: center;">TOTAL: <<print _dTotal>></div> </div> </div> <div style="color: #fde047; font-size: 1.3rem; font-weight: bold; margin-bottom: 15px; text-align: center; text-shadow: 0 0 10px rgba(253,224,71,0.5); background: rgba(0,0,0,0.6); display: inline-block; padding: 5px 20px; border-radius: 4px;"><<print $bjResult>></div> <br> <<link '<span class="bj-custom-btn">PLAY AGAIN</span>' "Merchant">> <<set $bjState to "idle">> <</link>> <</if>> </div> <div style="text-align: center; margin-top: 25px;"> <<link '<span class="merchant-custom-btn" style="border-color:#94a3b8; color:#94a3b8;">LEAVE CARAVAN (RETURN TO MAP)</span>' "Map">> <<set $bjState to "idle">> <</link>> </div> </div> <</nobr>>
<<nobr>> <<setMenu "Report">> <<run setup.zmoraRoll()>> <<run setup.raidRoll()>> /* Calculation of all production done overnight */ <<set _isStorageFull to false>> <<set _currentStorageWeight to setup.campWeight()>> <<set $maxStorageLimit to setup.campCap()>> <<if _currentStorageWeight gte $maxStorageLimit>> <<set _isStorageFull to true>> <</if>> /* Money from prisoners (slaves) - not capped by the storage limit, added on top */ <<if ndef $campSlaves>><<set $campSlaves to 0>><</if>> <<if ndef $materials.scrip>><<set $materials.scrip to 0>><</if>> <<set _scripEarnings to $campSlaves * setup.gromPerSlave()>> <<set $materials.scrip += _scripEarnings>> /* Premium Scrapper Drone income - not capped by the storage limit (special item privilege) */ <<set _droneLoot to 0>> <<if def $perks and $perks.drone eq true>> <<set _droneLoot to random(15, 30)>> <<set $campStorage.scrap += _droneLoot>> <</if>> /* Standard worker and greenhouse production (cancelled if storage is full) */ <<if ndef $materials.medicalHerb>><<set $materials.medicalHerb to 0>><</if>> <<if ndef $campStorage.medicalHerb>><<set $campStorage.medicalHerb to 0>><</if>> <<if def $materials.medical_herb>><<set $materials.medicalHerb to ($materials.medicalHerb||0) + ($materials.medical_herb||0)>><<run delete $materials.medical_herb>><</if>> <<if def $campStorage.medical_herb>><<set $campStorage.medicalHerb to ($campStorage.medicalHerb||0) + ($campStorage.medical_herb||0)>><<run delete $campStorage.medical_herb>><</if>> <<set _gainedScrap to 0, _gainedWood to 0, _gainedCloth to 0, _gainedMeat to 0, _gainedAmmo to 0, _gainedFish to 0>> <<set _gainedBerries to 0, _gainedWater to 0, _gainedMed to 0, _gainedFood to 0, _gainedHerb to 0>> <<set _pumpWater to 0>> <<set _rainWater to 0>> <<set _ck to null>> <<if !_isStorageFull>> <<set _gainedScrap to setup.cLife.rollScrap($laborScrap)>> <<set _gainedWood to $laborWood>> <<set _gainedCloth to $laborCloth>> <<set _gainedMeat to $laborHunters>> <<set _gainedBerries to $laborForagers>> <<set _gainedWater to $laborForagers + (($laborWater || 0) * setup.cLife.yields.water)>> <<set _gainedMed to $laborMedics * setup.cLife.yields.medicine>> <<set _gainedAmmo to ($laborAmmo || 0) * setup.cLife.yields.ammo>> <<set _gainedFish to setup.cLife.rollFish($laborFishermen)>> <<set _zmM to setup.zmoraMult() * (setup.tipOn("labor") ? 1.2 : 1) * setup.cLife.mult()>> <<if _zmM neq 1>> <<set _gainedScrap to setup.cLife.rnd(_gainedScrap * _zmM), _gainedWood to setup.cLife.rnd(_gainedWood * _zmM), _gainedCloth to setup.cLife.rnd(_gainedCloth * _zmM), _gainedMeat to setup.cLife.rnd(_gainedMeat * _zmM)>> <<set _gainedBerries to setup.cLife.rnd(_gainedBerries * _zmM), _gainedWater to setup.cLife.rnd(_gainedWater * _zmM), _gainedMed to setup.cLife.rnd(_gainedMed * _zmM), _gainedAmmo to setup.cLife.rnd(_gainedAmmo * _zmM), _gainedFish to setup.cLife.rnd(_gainedFish * _zmM)>> <</if>> <<if $greenhouseActive is true>> <<set _gainedFood to ($sanctuaryLevels.greenhouse * 2)>> <<set _gainedHerb to $sanctuaryLevels.greenhouse>> <<set _gainedWater += $sanctuaryLevels.greenhouse>> <</if>> /* WEATHER: rain and storms fill the barrels */ <<set _rainWater to setup.weatherRainWater()>> <<if _rainWater gt 0>><<set _gainedWater += _rainWater>><</if>> /* WATER PURIFICATION PUMP (Utopek-7 contract reward) */ <<if def $waterPump and $waterPump gt 0>> <<set _pumpWater to ($waterPump * 10) + (($greenhouseActive is true) ? ($sanctuaryLevels.greenhouse * 2) : 0)>> <<set _gainedWater += _pumpWater>> <</if>> /* SVIETLOK TITLE: +25% to all daily production */ <<if $svietlok_buff>> <<set _gainedScrap to Math.floor(_gainedScrap * 1.25 + Math.random())>> <<set _gainedWood to Math.floor(_gainedWood * 1.25 + Math.random())>> <<set _gainedCloth to Math.floor(_gainedCloth * 1.25 + Math.random())>> <<set _gainedMeat to Math.floor(_gainedMeat * 1.25 + Math.random())>> <<set _gainedBerries to Math.floor(_gainedBerries * 1.25 + Math.random())>> <<set _gainedWater to Math.floor(_gainedWater * 1.25 + Math.random())>> <<set _gainedMed to Math.floor(_gainedMed * 1.25 + Math.random())>> <<set _gainedFood to Math.floor(_gainedFood * 1.25 + Math.random())>> <<set _gainedHerb to Math.floor(_gainedHerb * 1.25 + Math.random())>> <<set _gainedAmmo to Math.floor(_gainedAmmo * 1.25 + Math.random())>> <<set _gainedFish to Math.floor(_gainedFish * 1.25 + Math.random())>> <</if>> /* TALENTS: Foreman, Overseer and friends */ <<if setup.tm("labor") gt 0>> <<set _lm to 1 + setup.tm("labor") / 100>> <<set _gainedScrap to Math.floor(_gainedScrap * _lm + Math.random())>> <<set _gainedWood to Math.floor(_gainedWood * _lm + Math.random())>> <<set _gainedCloth to Math.floor(_gainedCloth * _lm + Math.random())>> <<set _gainedMeat to Math.floor(_gainedMeat * _lm + Math.random())>> <<set _gainedBerries to Math.floor(_gainedBerries * _lm + Math.random())>> <<set _gainedWater to Math.floor(_gainedWater * _lm + Math.random())>> <<set _gainedMed to Math.floor(_gainedMed * _lm + Math.random())>> <<set _gainedFood to Math.floor(_gainedFood * _lm + Math.random())>> <<set _gainedHerb to Math.floor(_gainedHerb * _lm + Math.random())>> <<set _gainedAmmo to Math.floor(_gainedAmmo * _lm + Math.random())>> <<set _gainedFish to Math.floor(_gainedFish * _lm + Math.random())>> <</if>> <<set $campStorage.scrap += _gainedScrap>> <<set $campStorage.wood += _gainedWood>> <<set $campStorage.cloth += _gainedCloth>> <<set $campStorage.rawMeat += _gainedMeat>> <<set $campStorage.berries += _gainedBerries>> <<set $campStorage.water += _gainedWater>> <<set $campStorage.medicine += _gainedMed>> <<set $campStorage.food += _gainedFood>> <<set $campStorage.medicalHerb += _gainedHerb>> <<set $campStorage.ammo to ($campStorage.ammo || 0) + _gainedAmmo, $campStorage.rawFish to ($campStorage.rawFish || 0) + _gainedFish>> /* FIELD KITCHEN: cooks turn raw food into Food (1 raw makes 2) before the camp eats */ <<set _ck to setup.cLife.cook()>> <</if>> /* CAMP LIFE: the survivors eat, drink and burn wood, some fall ill or get hurt, morale moves (once per day) */ <<run setup.cLife.night()>> <style> .report-container { background: linear-gradient(135deg, rgba(5,15,5,0.95), rgba(10,5,5,0.95)); padding: 40px; border: 2px solid #38bdf8; border-radius: 8px; font-family: 'Courier New', monospace; box-shadow: 0 0 30px rgba(56, 189, 248, 0.3); min-height: 80vh; max-width: 800px; margin: 40px auto; color: #cbd5e1; } .report-title { text-align: center; color: #38bdf8; font-size: 1.8rem; font-weight: bold; letter-spacing: 4px; border-bottom: 1px dashed #38bdf8; padding-bottom: 15px; margin-bottom: 30px; text-shadow: 0 0 10px rgba(56,189,248,0.5); } .report-section { background: rgba(0,0,0,0.6); border: 1px solid #475569; padding: 20px; border-radius: 6px; margin-bottom: 20px; } .report-header { color: #fcd34d; font-weight: bold; margin-bottom: 15px; font-size: 1.1rem; border-bottom: 1px solid #334155; padding-bottom: 5px; } .log-item { display: flex; justify-content: space-between; margin-bottom: 8px; font-size: 0.95rem; } .btn-wake { display: block; width: 100%; background: rgba(56, 189, 248, 0.15); border: 2px solid #38bdf8; color: #38bdf8 !important; padding: 15px; text-align: center; border-radius: 4px; font-weight: bold; font-size: 1.2rem; cursor: pointer; text-decoration: none !important; letter-spacing: 2px; transition: all 0.2s ease; margin-top: 30px; } .btn-wake:hover { background: rgba(56, 189, 248, 0.3); box-shadow: 0 0 20px rgba(56,189,248,0.6); color: #fff !important; } </style> <div class="report-container"> <div class="report-title">[ DAWN OF DAY <<print $day>> ]</div> <div style="text-align: center; margin-bottom: 14px; color: #7dd3fc; font-weight: bold;"><<print setup.weatherIcon()>> TODAY: <<print $weather>> <span style="color:#94a3b8; font-weight: normal;">- <<print setup.weatherInfo().desc>></span></div> <<if setup.zmoraActive()>> <<set _zc to setup.zmoraCounts()>> <div style="border:1px solid #a78bfa; background:rgba(76,29,149,0.25); color:#ddd6fe; padding:12px 14px; margin-bottom:18px; border-radius:6px; line-height:1.6; font-size:0.9rem;"> <b>🧠 ZMORA PROTOCOL: CAMP SICKNESS</b><br> Several defenders in the Living Quarters have woken up screaming, unable to breathe.<br> EXHAUSTED: <b><<print _zc.workers>></b> workers | NEURAL SHOCK: <b><<print _zc.defenders>></b> defenders | Camp output <b>-<<print _zc.pct>>%</b><br> <<link "<span style='color:#c4b5fd; text-decoration:underline;'>Open the diagnostic terminal</span>" "ZmoraTerminal">><</link>> </div> <</if>> <<if $zmoraSleepMsg>> <div style="color:#c4b5fd; text-align:center; margin-bottom:14px;"><<print $zmoraSleepMsg>></div> <</if>> <div style="text-align: center; margin-bottom: 30px; font-style: italic; color: #94a3b8;"> The harsh night passes. You awake to the low hum of the bunker's generators. </div> <!-- 1. VITAL STATUS --> <div class="report-section" style="border-color: #10b981;"> <div class="report-header" style="color: #34d399;">>> PERSONAL VITALS RESTORED</div> <div class="log-item"><span style="color:#a3e635;">❤️ Health</span> <span>+20 HP (Current: <<print $hp>>/<<print $maxHp>>)</span></div> <div class="log-item"><span style="color:#38bdf8;">⚡ Stamina</span> <span>Fully Restored (Current: <<print $stamina>>/<<print $maxStamina>>)</span></div> <div class="log-item"><span style="color:#fbbf24;">🍖 Satiety</span> <span style="color:#ef4444;">-20% (Hunger increasing)</span></div> <div class="log-item"><span style="color:#0ea5e9;">💧 Hydration</span> <span style="color:#ef4444;">-15% (Thirst increasing)</span></div> </div> <!-- 2. CAMP PRODUCTION REPORT --> <div class="report-section" style="border-color: #38bdf8;"> <div class="report-header" style="color: #7dd3fc;">>> OVERNIGHT COLONY YIELD</div> <<if _isStorageFull>> <div style="color: #ef4444; font-weight: bold; text-align: center; padding: 10px; border: 1px dashed #ef4444;"> ⚠️ STORAGE CAPACITY EXCEEDED. ALL LABOR PRODUCTION WAS HALTED. </div> <<else>> <<if _gainedScrap gt 0>><div class="log-item"><span>⚙️ Worker Scrap Harvested</span> <span style="color:#a3e635;">+<<print _gainedScrap>></span></div><</if>> <<if _gainedWood gt 0>><div class="log-item"><span>🪵 Worker Wood Chopped</span> <span style="color:#a3e635;">+<<print _gainedWood>></span></div><</if>> <<if _gainedCloth gt 0>><div class="log-item"><span>🧵 Worker Cloth Gathered</span> <span style="color:#a3e635;">+<<print _gainedCloth>></span></div><</if>> <<if _gainedMeat gt 0>><div class="log-item"><span>🥩 Hunter Meat Yield</span> <span style="color:#a3e635;">+<<print _gainedMeat>></span></div><</if>> <<if _gainedBerries gt 0>><div class="log-item"><span>🍇 Forager Berries Found</span> <span style="color:#a3e635;">+<<print _gainedBerries>></span></div><</if>> <<if _gainedAmmo gt 0>><div class="log-item"><span>💣 Excavators Ammo Recovered</span> <span style="color:#a3e635;">+<<print _gainedAmmo>></span></div><</if>> <<if _gainedFish gt 0>><div class="log-item"><span>🎣 Fishing Crew Catch</span> <span style="color:#a3e635;">+<<print _gainedFish>></span></div><</if>> <<if _gainedMed gt 0>><div class="log-item"><span>💊 Medic Supplies Crafted</span> <span style="color:#a3e635;">+<<print _gainedMed>></span></div><</if>> <<if _gainedFood gt 0>><div class="log-item"><span>🌾 Greenhouse Rations</span> <span style="color:#a3e635;">+<<print _gainedFood>></span></div><</if>> <<if _ck and _ck.batches gt 0>><div class="log-item"><span><<print "🍳 Field Kitchen (" + _ck.batches + " batches cooked)">></span> <span style="color:#a3e635;"><<print "+" + _ck.food + " Food">></span></div> <<elseif _ck and _ck.cooks gt 0>><div class="log-item"><span>🍳 Field Kitchen</span> <span style="color:#fca5a5;">No raw food to cook</span></div><</if>> <<if _gainedHerb gt 0>><div class="log-item"><span>🌿 Greenhouse Herbs</span> <span style="color:#a3e635;">+<<print _gainedHerb>></span></div><</if>> <<if _rainWater gt 0>><div class="log-item"><span>🌧️ Rain Barrels (<<print $weather>>)</span> <span style="color:#a3e635;">+<<print _rainWater>></span></div><</if>> <<if _pumpWater gt 0>><div class="log-item"><span>🚰 Water Purification Pump (base yield)</span> <span style="color:#a3e635;">+<<print _pumpWater>></span></div><</if>> <<if _gainedWater gt 0>><div class="log-item"><span>💧 Total Water Collected</span> <span style="color:#a3e635;">+<<print _gainedWater>></span></div><</if>> <<if _gainedScrap eq 0 and _gainedWood eq 0 and _gainedCloth eq 0 and _gainedMeat eq 0 and _gainedBerries eq 0 and _gainedWater eq 0 and _gainedFood eq 0 and _gainedAmmo eq 0 and _gainedFish eq 0 and not (_ck and _ck.cooks gt 0)>> <div style="color: #64748b; font-style: italic; text-align: center;">No standard resources were produced by workers last night.</div> <<endif>> <<endif>> </div> <!-- CAMP LIFE: morale, upkeep, sickness and injuries --> <<print setup.cLife.reportHtml()>> <!-- 3. PREMIUM ITEMS AND PRISONERS (Special Income) --> <<if _scripEarnings gt 0 or _droneLoot gt 0>> <div class="report-section" style="border-color: #f59e0b;"> <div class="report-header" style="color: #fbbf24;">>> SPECIAL OPERATIONS & CONTRABAND</div> <<if _scripEarnings gt 0>> <div class="log-item"><span>⛓️ Penal Labor Exploitation (<<print $campSlaves>> Slaves)</span> <span style="color:#fbbf24; font-weight:bold;">+<<print _scripEarnings>> SCRIP</span></div> <</if>> <<if _droneLoot gt 0>> <div class="log-item"><span>🤖 Scrapper-IX Autonomous Drone</span> <span style="color:#a3e635; font-weight:bold;">+<<print _droneLoot>> SCRAP</span></div> <</if>> </div> <<endif>> <<link "<span class='btn-wake'>AWAKEN AND DEPLOY</span>" "Camp">> <<run window.setup.updateSidebar()>> <</link>> </div> <</nobr>>
<<nobr>> /* Shows the newspaper once, the first time the player enters the game (tab) */ <<if ndef setup.newsPunchedInFace>> <<set setup.newsPunchedInFace to true>> <<script>> setTimeout(function() { Dialog.setup("", "news-modal-custom"); Dialog.wiki(Story.get("GameNewsModal").processText()); Dialog.open(); }, 600); /* Waits half a second for the screen to fully load, then fires */ <</script>> <<endif>> <</nobr>>
<<nobr>> <<if $svietlok_buff>> /* Only show this text in passages that carry the 'npc' tag */ <<if tags().includes("npc")>> <style> .svp-wrap { position: relative; height: 0; z-index: 60; } .svp-pill { position: fixed; top: 8px; right: 10px; font-family: 'Courier New', monospace; font-size: 0.68rem; line-height: 1.3; color: #d9f99d; background: rgba(2, 10, 6, 0.92); border: 1px solid #84cc16; border-radius: 12px; padding: 3px 10px; letter-spacing: 1px; cursor: help; max-width: 260px; } .svp-pill .svp-more { display: none; margin-top: 4px; padding-top: 4px; border-top: 1px dashed #4d7c0f; color: #e2e8f0; font-size: 0.68rem; letter-spacing: 0; font-style: italic; white-space: normal; } .svp-pill:hover .svp-more, .svp-pill:focus .svp-more { display: block; } @media (min-width: 1100px) { .svp-pill { right: 172px; } } </style> <div class="svp-wrap"><div class="svp-pill" tabindex="0">🌟 SVIETLOK PRESENCE<div class="svp-more">The people around you recognize the mark of the Firefly. Whispers echo through the room, and they instinctively step aside, looking at you with deep reverence.</div></div></div> <</if>> <</if>> <</nobr>>
<<nobr>> /* 1. FULL IMMUNITY FOR THE ROOT ACCOUNT (BULLETPROOF) */ <<set _isGodMode to false>> <<if def $playerName and String($playerName).toLowerCase() eq "nakman">> <<set _isGodMode to true>> <</if>> <<if _isGodMode is true>> /* So the game does not crash when you cheat from the panel, it instantly adapts the encryption to you */ <<if def $syndicateTokens>> <<set $token_checksum to ($syndicateTokens * 73) + 1492>> <</if>> <<else>> /* 2. THE REAL ANTI-CHEAT CHECK FOR ORDINARY MORTALS */ <<if def $syndicateTokens>> <<if ndef $token_checksum>> <<set $token_checksum to ($syndicateTokens * 73) + 1492>> <</if>> <<set _expected_hash to ($syndicateTokens * 73) + 1492>> <<if $token_checksum neq _expected_hash>> /* PENALTY */ <<set $syndicateTokens to 0>> <<set $token_checksum to 1492>> <<goto "Cheater_Screen">> <</if>> <</if>> <</if>> <</nobr>>
<<nobr>> <<if $cipherMet eq true and $firstDecryptDone eq false>> <<set $firstDecryptDone to true>> <<goto "CipherFirstDecrypt">> <<else>> <<set _rewardScrip to random(15, 30)>> <<set $materials.scrip += _rewardScrip>> <<set _lores to [ "INDUSTRIAL MEMO (SILESIA-TECH): 'Do not allow surface survivors near the automated grid substations. The cooling fluids have leaked into the water table, causing severe cognitive mirroring among local lifeforms.'", "VANGUARD SECURE CHAT: 'Cipher was last seen near the Katowice transit hubs. She's carrying the uncorrupted OS drive. Kill on sight. Do not let her reach the broadcasting array.'", "PROJECT REBIRTH LOG: 'The nanomachine integration is stable, but the psychological toll is high. Subjects believe they are part of a hive mind. The Reptilian facade is working perfectly as a control mechanism.'" ]>> <<set $commsLog to _lores.random()>> <<set $commsReward to _rewardScrip>> <<set $unlockedTechQuest to true>> <<goto "Comms">> <<endif>> <</nobr>>
<<nobr>> <style> .wake-up-sequence { animation: wakeUpFade 4s ease-in forwards; opacity: 0; } @keyframes wakeUpFade { 0% { opacity: 0; filter: blur(10px) brightness(0); } 50% { opacity: 0.5; filter: blur(4px) brightness(0.5); } 100% { opacity: 1; filter: blur(0) brightness(1); } } .prologue-container { background: radial-gradient(circle at center, rgba(5, 20, 5, 0.9), rgba(0, 0, 0, 1)), url('https://images.unsplash.com/photo-1478760329108-5c3ed9d495a0?q=80&w=2000&auto=format&fit=crop') center/cover; min-height: 100vh; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 40px; box-sizing: border-box; border: 2px solid #00ff00; box-shadow: inset 0 0 100px #000; font-family: 'Courier New', monospace; margin: 0; } .id-card { background: rgba(0, 20, 0, 0.7); border: 2px solid #00ff00; border-radius: 8px; padding: 30px; display: flex; gap: 30px; max-width: 800px; width: 100%; box-shadow: 0 0 30px rgba(0, 255, 0, 0.2); backdrop-filter: blur(5px); margin-bottom: 30px; position: relative; overflow: hidden; } .id-card::after { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,255,0,0.1) 3px, rgba(0,255,0,0.1) 4px); pointer-events: none; } .avatar-box { width: 180px; height: 230px; border: 2px solid #00ff00; background-color: #000; display: flex; justify-content: center; align-items: center; box-shadow: inset 0 0 20px rgba(0,255,0,0.3); position: relative; overflow: hidden; } .avatar-img { width: 100%; height: 100%; object-fit: cover; } .mutant-filter { filter: sepia(100%) hue-rotate(90deg) saturate(400%) contrast(150%) brightness(80%); } .briefing-box { background-color: rgba(5, 10, 5, 0.9); padding: 30px; border: 2px solid #ef4444; border-radius: 8px; box-shadow: 0 0 20px rgba(239, 68, 68, 0.15); max-width: 800px; width: 100%; margin-bottom: 30px; position: relative; z-index: 2; } </style> /* --- SMART AVATAR FILE PATH BUILDER (OBSIDIAN SKIN SUPPORTED) --- */ <<set _genderFormat to $playerGender.toLowerCase()>> <<set _rawClass to $playerBackground.toLowerCase()>> <<if _rawClass eq "field medic">><<set _rawClass to "medic">><</if>> <<if _rawClass eq "mutated anomaly">><<set _rawClass to "mutated">><</if>> <<set _classFormat to _rawClass.replace(" ", "_")>> /* PERK CHECK: IF THE SKIN WAS BOUGHT, APPEND _obsidian_exo TO THE END OF THE URL */ <<if setup.exoActive()>> <<set _armorSuffix to "_obsidian_exo">> <<else>> <<set _armorSuffix to "_start_armor">> <<endif>> <<set $avatarUrl to "images/avatars/" + _genderFormat + "_" + _classFormat + _armorSuffix + ".jpg">> <<if setup.exoActive()>><<set $avatarUrl to "images/avatars/exo_armor.jpg">><</if>> <<set _imgClass to ($playerBackground eq 'Mutated Anomaly' ? 'avatar-img mutant-filter' : 'avatar-img')>> <<set _fallbackText to $playerGender.toUpperCase() + "\\n" + $playerBackground.toUpperCase().replace(" ", "+")>> <<set _fallbackUrl to "https://placehold.co/400x500/050a05/00ff00?font=courier&text=" + _fallbackText>> <div class="prologue-container wake-up-sequence"> /* --- NAKMAN DEVELOPER BACKDOOR WARNING --- */ <<if $devModeMessage eq true>> <div style="background: rgba(0,0,0,0.9); color: #fbbf24; font-weight: bold; text-align: center; font-family: 'Courier New', monospace; border: 2px dashed #fbbf24; padding: 15px; margin-bottom: 20px; box-shadow: 0 0 20px #fbbf24; z-index: 999; position: relative; max-width: 800px; width: 100%;"> [ DEVELOPER OVERRIDE ACCEPTED ]<br> WELCOME BACK, NAKMAN. ENJOY YOUR 9999 SYNDICATE TOKENS. </div> <<unset $devModeMessage>> <<endif>> /* ----------------------------------------- */ <div style="color: #00ff00; font-size: 2rem; font-weight: bold; letter-spacing: 10px; margin-bottom: 30px; text-shadow: 0 0 20px #00ff00;"> [ SYSTEM REBOOT SUCCESSFUL ] </div> <!-- ID CARD --> <div class="id-card"> <div class="avatar-box"> <<print '<img src="' + $avatarUrl + '" class="' + _imgClass + '" alt="Operator Avatar" onerror="this.onerror=null;this.src=\'' + _fallbackUrl + '\';">'>> <div style="position: absolute; top:0; left:0; width:100%; height:100%; background: repeating-linear-gradient(transparent, transparent 2px, rgba(0,255,0,0.1) 3px); pointer-events: none;"></div> </div> <div style="flex: 1; display: flex; flex-direction: column; justify-content: center;"> <h3 style="color: #fff; border-bottom: 1px dashed #334155; padding-bottom: 10px; margin-top: 0; letter-spacing: 2px;">OPERATOR DOSSIER</h3> <div style="display: grid; grid-template-columns: 130px 1fr; gap: 10px; margin-top: 15px; font-size: 1.1rem;"> <span style="color: #64748b;">DESIGNATION:</span> <strong style="color: #00ff00;"><<print $playerName>></strong> <span style="color: #64748b;">AGE/GENDER:</span> <strong style="color: #cbd5e1;"><<print $playerAge>> / <<print $playerGender>></strong> <span style="color: #64748b;">CLASSIFICATION:</span> <strong style="color: #ffaa00;"><<print $playerClass>></strong> <span style="color: #64748b;">DEPLOYMENT:</span> <strong style="color: #38bdf8;"><<print $startingCity>> Sector</strong> </div> <div style="margin-top: 25px; padding: 10px; background: rgba(255,0,0,0.1); border-left: 3px solid #ef4444;"> <span style="color: #ef4444; font-size: 0.9rem; font-weight: bold;">VITAL SIGNS:</span> <span style="color: #fff; font-size: 0.9rem; margin-left: 10px;">HP: <<print $hp>> | STAMINA: <<print $stamina>></span> </div> </div> </div> <!-- STORY AND QUEST BRIEFING --> <div class="briefing-box"> <div style="text-align: center; border-bottom: 1px dashed #ef4444; padding-bottom: 15px; margin-bottom: 25px;"> <h2 style="color: #ef4444; margin: 0; letter-spacing: 4px; text-shadow: 0 0 10px rgba(239,68,68,0.5);">[ CLASSIFIED BRIEFING: OPERATION SANCTUARY ]</h2> <div style="margin-top: 10px; font-size: 0.9rem; color: #f87171;">DECRYPTION LEVEL: OMEGA</div> </div> <div style="color: #d1d5db; font-size: 1.05rem; line-height: 1.7; margin-bottom: 25px; text-align: justify;"> <p>They came from below, and they came from the skies. The Reptilian Vanguard didn't just conquer humanity; they terraformed the entire globe. Every continent fell, suffocating under the toxic <span style="color:#ef4444; font-weight:bold;">Silica Mist</span>.</p> <p>But a global rebellion must start somewhere. The central mainframes collapsed long ago, and the <strong style="color:#fbbf24;">Ashen Veil</strong> protocol was activated, driving the remnants of humanity underground. Here, in the heavy industrial ruins of Silesia, we will light the spark that burns their empire down.</p> <p>You are one of the last remaining sleeper Operators, <<print $playerName>>. Your long-sleep is over.</p> <div style="background: rgba(239,68,68,0.05); border-left: 4px solid #ef4444; padding: 15px; margin-top: 20px;"> <strong style="color: #ef4444; font-size: 1.1rem;">> YOUR DIRECTIVE:</strong><br> <span style="color: #94a3b8;">Ascend to the surface at the edge of the <span style="color:#3498db; font-weight:bold;"><<print $startingCity>></span> sector. Scavenge materials. Establish a secure Command Center. Make contact with underground factions. </span> <strong style="color:#00ff00; display:block; margin-top:10px;">Become the ground zero of the resistance. Take our world back.</strong> </div> </div> <div style="text-align: center; margin-top: 30px; padding: 12px; border: 1px solid #ef4444; background: rgba(239, 68, 68, 0.1); border-radius: 4px; box-shadow: 0 0 10px rgba(239, 68, 68, 0.2); transition: 0.3s;"> <<link '<span style="color:#ef4444; font-weight:bold; letter-spacing:2px; text-decoration:none;">INITIATE SURFACE DEPLOYMENT</span>' "Tutorial 1">><</link>> </div> </div> </div> <</nobr>>
<style> #ui-dialog-body { padding: 0 !important; background-color: #1a0b05 !important; } /* SINGLE EXIT (X) BUTTON (FIX THAT PREVENTS OVERLAP) */ #ui-dialog-close { color: #ffffff !important; background: #ef4444 !important; border: 2px solid #b91c1c !important; border-radius: 4px !important; width: 32px !important; height: 32px !important; opacity: 1 !important; box-shadow: 0 0 10px rgba(239, 68, 68, 0.6) !important; } #ui-dialog-close:hover { background: #dc2626 !important; box-shadow: 0 0 15px rgba(220, 38, 38, 0.9) !important; } #ui-dialog-close::before { content: none !important; } #ui-dialog-close::after { content: "X" !important; color: #ffffff !important; font-family: Arial, sans-serif !important; font-size: 1.1rem !important; font-weight: bold !important; position: absolute !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; } </style> <<nobr>> <<setMenu "Quests">> <</nobr>> <div style="background-color: #1a0b05; padding: 25px; border: 2px solid #fbbf24; border-radius: 8px; font-family: 'Segoe UI', Tahoma, sans-serif; box-shadow: 0 0 20px rgba(251, 191, 36, 0.15); max-width: 750px; margin: 0 auto;"> <div style="text-align: center; border-bottom: 1px dashed #fbbf24; padding-bottom: 12px; margin-bottom: 20px;"> <h2 style="color: #fbbf24; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px #fbbf24;">[ ACTIVE DIRECTIVES ]</h2> <div style="margin-top: 5px; font-size: 0.85rem; color: #fbbf24;">MAIN QUEST & SIDE QUEST PROGRESS</div> </div> <<nobr>> <div style="text-align: center; border-bottom: 1px dashed #ef4444; padding-bottom: 8px; margin-bottom: 15px;"> <h3 style="color: #ef4444; margin: 0; letter-spacing: 2px; font-size: 1rem;">[ MAIN QUEST ]</h3> </div> <!-- MAIN: MEET CIPHER --> <div style="border-left: 4px solid #ef4444; background: rgba(239,68,68,0.1); padding: 12px; margin-bottom: 12px;"> <div style="color: #fca5a5; font-weight: bold;">MEET CIPHER</div> <<if $cipherMet eq true>> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 4px;">You met Cipher face to face in her bunker below the Katowice Tunnels and agreed to build the Secure Proxy Uplink.</div> <div style="color: #34d399; font-size: 0.8rem; margin-top: 6px; font-weight: bold;">COMPLETED</div> <<elseif $ch2 and $ch2.st eq "active">> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 4px;">The Rad Storm is coming, and Cipher says the Vanguard has gone dark. The hatch at the top of the Katowice Tunnels opens only while a Rad-Storm rages outside. She is waiting in a signal bunker below Drainage Sump 04. Keep your firearm ready.</div> <div style="color: #86efac; font-size: 0.8rem; margin-top: 6px; line-height: 1.6;">[x] Infiltrate Katowice Tunnels Entrance<br>[x] Eliminate the Vanguard Search Cadre<br>[x] Interrogate the Terminal Interface at Bulkhead 01-A</div> <div style="color: #94a3b8; font-size: 0.8rem; margin-top: 4px; line-height: 1.6;">[ ] Go down the hatch at the top of the Katowice Tunnels and meet Cipher in her bunker below Drainage Sump 04.</div> <<elseif $ktDone>> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 4px;">Cipher spoke to you through a dead terminal at Bulkhead 01-A. She released a shaft to flush the tunnels and told you to go home and wait for her transmission.</div> <div style="color: #86efac; font-size: 0.8rem; margin-top: 6px; line-height: 1.6;">[x] Infiltrate Katowice Tunnels Entrance<br>[x] Eliminate the Vanguard Search Cadre<br>[x] Interrogate the Terminal Interface at Bulkhead 01-A</div> <div style="color: #94a3b8; font-size: 0.8rem; margin-top: 4px; line-height: 1.6;">[ ] Return to S.A.N.C.T.U.A.R.Y. and wait for Cipher's contact.</div> <<elseif ($storyStage || 0) gte 2>> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 4px;">Cipher's transmission told you to follow her signal into the Katowice Tunnels. The tunnels are a Reptilian ambush zone, so go in ready to fight.</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-top: 6px;">[ ] Follow Cipher's signal into the Katowice Tunnels (marked on your Sector Map)</div> <<else>> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 4px;">Finish setting up your sanctuary. A transmission is on the way.</div> <</if>> </div> <<if $ch2 and $ch2.st eq "done">> <!-- MAIN: PROTOCOL ZERO --> <<set _pzS to (($materials.scrap || 0) + ($campStorage.scrap || 0))>><<set _pzT to (($materials.electronics || 0) + ($campStorage.electronics || 0))>> <div style="border-left: 4px solid #ef4444; background: rgba(239,68,68,0.1); padding: 12px; margin-bottom: 12px;"> <div style="color: #fca5a5; font-weight: bold;">PROTOCOL ZERO: THE SECURE PROXY UPLINK</div> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 4px;">Cipher gave you the blueprint for a Secure Proxy Uplink. Build it inside S.A.N.C.T.U.A.R.Y. so she can route her signals through your generator grid, out of the Vanguard's sight.</div> <div style="color: #94a3b8; font-size: 0.8rem; margin-top: 6px; line-height: 1.6;"> <<print (_pzS gte 250 ? "[x]" : "[ ]")>> Gather 250x Salvaged Scrap Metal (<<print Math.min(_pzS, 250)>>/250)<br> <<print (_pzT gte 40 ? "[x]" : "[ ]")>> Recover 40x Electronic Parts (<<print Math.min(_pzT, 40)>>/40)<br> [ ] Procure 1x Vanguard Power Core from an Elite Vanguard Unit (Vanguard Captain Zhor-Kaal)<br> [ ] Complete Construction in S.A.N.C.T.U.A.R.Y. Base to unlock Cipher's Server Room </div> </div> <</if>> <div style="color: #64748b; font-size: 0.75rem; text-align: center; margin-bottom: 14px;">More main missions will appear as the story unfolds.</div> <div style="text-align: center; border-bottom: 1px dashed #a855f7; padding-bottom: 8px; margin: 22px 0 15px;"> <h3 style="color: #c084fc; margin: 0; letter-spacing: 2px; font-size: 1rem;">[ SIDE QUESTS ]</h3> </div> <!-- SIDE: THE SLAUGHTERHOUSE --> <<set _q to ($quest_slaughterhouse || "not_started")>> <<set _res to ($total_rescued || 0)>> <div style="border-left: 4px solid #a855f7; background: rgba(168,85,247,0.08); padding: 12px; margin-bottom: 12px;"> <div style="color: #d8b4fe; font-weight: bold;">THE SLAUGHTERHOUSE - Free the Captives</div> <<if _q eq "not_started">> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 4px;">Free enslaved humans from Reptilian convoys on the Sector Map. Once you have saved enough of them, a survivor will come to you for help.</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-top: 6px;">Objective: Rescue enslaved humans <strong style="color:#fff;"><<print Math.min(_res, 10)>> / 10</strong></div> <div style="background:#1c1917; border:1px solid #57534e; height:8px; border-radius:4px; margin-top:6px;"><div @style="'background:#a855f7; height:100%; width:' + Math.min(100, _res * 10) + '%;'"></div></div> <<elseif _q eq "active">> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 4px;">A rescued survivor's family is still held in the Slaughterhouse. Breach the gates and free them.</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-top: 6px;">[x] Rescue 10 enslaved humans<br>[ ] Breach the Slaughterhouse (Sector 4, marked on your Sector Map)</div> <<else>> <div style="color: #34d399; font-size: 0.85rem; margin-top: 4px; font-weight: bold;">COMPLETED</div> <</if>> </div> <!-- SIDE: THE SCHOLAR'S ARCHIVE --> <div style="border-left: 4px solid #a855f7; background: rgba(168,85,247,0.08); padding: 12px; margin-bottom: 12px;"> <div style="color: #d8b4fe; font-weight: bold;">THE SCHOLAR'S ARCHIVE - Decipher the Iokharic Relics</div> <<set _mayaIn to ($npcFlags and $npcFlags.maya)>> <<set _wins to ($convoyWins || 0)>> <<if not _mayaIn and not $mayaSite>> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 4px;">Rumor says the Reptilian convoys carry strange slates with unreadable glyphs.</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-top: 6px;">Objective: Break Reptilian convoys <strong style="color:#fff;"><<print Math.min(_wins, 5)>> / 5</strong></div> <<elseif not _mayaIn>> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 4px;">You hold a broken slate you cannot read. Someone in the Silesian Library ruins might.</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-top: 6px;">[x] Break 5 Reptilian convoys<br>[ ] Find the scholar at the Silesian Library Ruins (Sector Map)</div> <<elseif not $archive or $archive.s eq "none">> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 4px;">Dr. Maya Chomsky has joined your camp and wants a proper workshop.</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-top: 6px;">[x] Find Dr. Maya<br>[ ] Talk to Maya at camp and commission the Archive Wing</div> <<elseif $archive.s eq "build">> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 4px;">The Archive Wing is under construction.</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-top: 6px;">[x] Find Dr. Maya<br>[ ] Archive Wing ready on day <<print $archive.ready>></div> <<else>> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 4px;">Find relics on scavenging trips and from boss contracts, and hand them to Dr. Maya.</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-top: 6px;">Relics translated: <strong style="color:#fff;"><<print setup.relicSlotsDone()>> / <<print setup.relicSlotsTotal()>></strong></div> <div style="background:#1c1917; border:1px solid #57534e; height:8px; border-radius:4px; margin-top:6px;"><div @style="'background:#a855f7; height:100%; width:' + Math.round(setup.relicSlotsDone() / setup.relicSlotsTotal() * 100) + '%;'"></div></div> <</if>> </div> <!-- SIDE: THE LOWER SHAFTS --> <div style="border-left: 4px solid #f59e0b; background: rgba(245,158,11,0.08); padding: 12px; margin-bottom: 12px;"> <div style="color: #fcd34d; font-weight: bold;">THE LOWER SHAFTS - Kaelen's Brother</div> <<if ($dgQuest || 0) is 0>> <<if ($level || 1) lt 4>> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 4px;">Senior Trader Kaelen has work for a seasoned survivor.</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-top: 6px;">[ ] Reach Level 4 (you are Level <<print ($level || 1)>>)</div> <<else>> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 4px;">Kaelen has a job for you. Talk to him at the Merchant.</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-top: 6px;">[ ] Ask Kaelen about work</div> <</if>> <<elseif $dgQuest is 1>> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 4px;">Kaelen's brother's crew vanished in the Wujek coal mine. A foreman called Kruk guards the lower shaft. Bring back his lamp.</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-top: 6px;">[x] Take the job from Kaelen<br>[ ] Find the Foreman's Key in the Abandoned Coal Mine (Sector Map)<br>[ ] Defeat Foreman Kruk</div> <<elseif $dgQuest is 2>> <div style="color: #cbd5e1; font-size: 0.85rem; margin-top: 4px;">Kruk is dead. His lamp is in your pack.</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-top: 6px;">[x] Defeat Foreman Kruk<br>[ ] Hand the lamp over to Kaelen</div> <<else>> <div style="color: #34d399; font-size: 0.85rem; margin-top: 4px; font-weight: bold;">COMPLETED</div> <</if>> </div> <div style="color: #64748b; font-size: 0.75rem; text-align: center; margin-bottom: 14px; border: 1px dashed #334155; padding: 8px;">Rumors, leads and boss contracts are on the <strong style="color:#34d399;">Bounty Board</strong> at the Comms terminal.</div> <</nobr>> <<nobr>> <!-- SIDE QUESTS (READY LIST / COMPACT) --> <div style="text-align: center; border-bottom: 1px dashed #d97706; padding-bottom: 8px; margin-bottom: 15px;"> <h3 style="color: #d97706; margin: 0; letter-spacing: 2px; font-size: 1rem;">[ WASTELAND SIDE LOGISTICS ]</h3> </div> <div style="display: flex; flex-direction: column; gap: 8px;"> <!-- 1: Wood --> <div style="background: rgba(217, 119, 6, 0.08); border-left: 3px solid #d97706; padding: 8px 12px; border-radius: 4px; display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;"> <div style="font-size: 0.85rem;"> <span style="color: #fcd34d; font-weight: bold;">Perimeter Firewood</span> <span style="color: #94a3b8; font-size: 0.75rem;">(Req: 15 Wood | Have: <<print setup.have("wood")>>)</span> </div> <div> <<if setup.have("wood") gte 15>> <<button "TURN IN (+25 SCRIP)">> <<run setup.spend("wood", 15)>> <<set $materials.scrip += 25>> <<replace "#ui-dialog-body">><<print Story.get("Quests").processText()>><</replace>> <</button>> <<else>> <span style="color: #ef4444; font-size: 0.75rem;">LOCKED</span> <<endif>> </div> <<if setup.have("wood") gte 15>> <div class="sl-bulk" data-k="wood" data-req="15" data-pay="25" style="width: 100%; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 8px; padding-top: 8px; border-top: 1px dashed rgba(217,119,6,0.4);"> <span style="color: #cbd5e1; font-size: 0.75rem;">SELL IN BULK (SETS OF 15):</span> <span class="sl-max" style="display:none;"><<print Math.floor(setup.have("wood") / 15)>></span><input type="number" min="1" value="1" onchange="SugarCube.setup.sideBulk.upd(this)" style="width:80px; padding:6px; background:#0f172a; color:#f1f5f9; border:1px solid #64748b; font-family:inherit; font-size:0.85rem;"> <button type="button" onclick="SugarCube.setup.sideBulk.max(this)" style="padding:6px 12px; background:#1e3a8a; color:#e0f2fe; border:1px solid #60a5fa; cursor:pointer; font-family:inherit; font-weight:700; font-size:0.8rem;">MAX</button> <button type="button" class="sl-go" onclick="SugarCube.setup.sideBulk.sell(this)" style="padding:6px 12px; background:#1e3a8a; color:#e0f2fe; border:1px solid #60a5fa; cursor:pointer; font-family:inherit; font-weight:700; font-size:0.8rem;">SELL ×1 (+25 SCRIP)</button> <span class="sl-msg" style="color: #fca5a5; font-size: 0.75rem;"></span> </div> <</if>> </div> <!-- 2: Scrap --> <div style="background: rgba(217, 119, 6, 0.08); border-left: 3px solid #d97706; padding: 8px 12px; border-radius: 4px; display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;"> <div style="font-size: 0.85rem;"> <span style="color: #fcd34d; font-weight: bold;">Workshop Scraps</span> <span style="color: #94a3b8; font-size: 0.75rem;">(Req: 20 Scrap | Have: <<print setup.have("scrap")>>)</span> </div> <div> <<if setup.have("scrap") gte 20>> <<button "TURN IN (+35 SCRIP)">> <<run setup.spend("scrap", 20)>> <<set $materials.scrip += 35>> <<replace "#ui-dialog-body">><<print Story.get("Quests").processText()>><</replace>> <</button>> <<else>> <span style="color: #ef4444; font-size: 0.75rem;">LOCKED</span> <<endif>> </div> <<if setup.have("scrap") gte 20>> <div class="sl-bulk" data-k="scrap" data-req="20" data-pay="35" style="width: 100%; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 8px; padding-top: 8px; border-top: 1px dashed rgba(217,119,6,0.4);"> <span style="color: #cbd5e1; font-size: 0.75rem;">SELL IN BULK (SETS OF 20):</span> <span class="sl-max" style="display:none;"><<print Math.floor(setup.have("scrap") / 20)>></span><input type="number" min="1" value="1" onchange="SugarCube.setup.sideBulk.upd(this)" style="width:80px; padding:6px; background:#0f172a; color:#f1f5f9; border:1px solid #64748b; font-family:inherit; font-size:0.85rem;"> <button type="button" onclick="SugarCube.setup.sideBulk.max(this)" style="padding:6px 12px; background:#1e3a8a; color:#e0f2fe; border:1px solid #60a5fa; cursor:pointer; font-family:inherit; font-weight:700; font-size:0.8rem;">MAX</button> <button type="button" class="sl-go" onclick="SugarCube.setup.sideBulk.sell(this)" style="padding:6px 12px; background:#1e3a8a; color:#e0f2fe; border:1px solid #60a5fa; cursor:pointer; font-family:inherit; font-weight:700; font-size:0.8rem;">SELL ×1 (+35 SCRIP)</button> <span class="sl-msg" style="color: #fca5a5; font-size: 0.75rem;"></span> </div> <</if>> </div> <!-- 3: Cloth --> <div style="background: rgba(217, 119, 6, 0.08); border-left: 3px solid #d97706; padding: 8px 12px; border-radius: 4px; display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;"> <div style="font-size: 0.85rem;"> <span style="color: #fcd34d; font-weight: bold;">Filter Cloth</span> <span style="color: #94a3b8; font-size: 0.75rem;">(Req: 15 Cloth | Have: <<print setup.have("cloth")>>)</span> </div> <div> <<if setup.have("cloth") gte 15>> <<button "TURN IN (+30 SCRIP)">> <<run setup.spend("cloth", 15)>> <<set $materials.scrip += 30>> <<replace "#ui-dialog-body">><<print Story.get("Quests").processText()>><</replace>> <</button>> <<else>> <span style="color: #ef4444; font-size: 0.75rem;">LOCKED</span> <<endif>> </div> <<if setup.have("cloth") gte 15>> <div class="sl-bulk" data-k="cloth" data-req="15" data-pay="30" style="width: 100%; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 8px; padding-top: 8px; border-top: 1px dashed rgba(217,119,6,0.4);"> <span style="color: #cbd5e1; font-size: 0.75rem;">SELL IN BULK (SETS OF 15):</span> <span class="sl-max" style="display:none;"><<print Math.floor(setup.have("cloth") / 15)>></span><input type="number" min="1" value="1" onchange="SugarCube.setup.sideBulk.upd(this)" style="width:80px; padding:6px; background:#0f172a; color:#f1f5f9; border:1px solid #64748b; font-family:inherit; font-size:0.85rem;"> <button type="button" onclick="SugarCube.setup.sideBulk.max(this)" style="padding:6px 12px; background:#1e3a8a; color:#e0f2fe; border:1px solid #60a5fa; cursor:pointer; font-family:inherit; font-weight:700; font-size:0.8rem;">MAX</button> <button type="button" class="sl-go" onclick="SugarCube.setup.sideBulk.sell(this)" style="padding:6px 12px; background:#1e3a8a; color:#e0f2fe; border:1px solid #60a5fa; cursor:pointer; font-family:inherit; font-weight:700; font-size:0.8rem;">SELL ×1 (+30 SCRIP)</button> <span class="sl-msg" style="color: #fca5a5; font-size: 0.75rem;"></span> </div> <</if>> </div> <!-- 4: Water --> <div style="background: rgba(217, 119, 6, 0.08); border-left: 3px solid #d97706; padding: 8px 12px; border-radius: 4px; display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;"> <div style="font-size: 0.85rem;"> <span style="color: #fcd34d; font-weight: bold;">Hydration Reserves</span> <span style="color: #94a3b8; font-size: 0.75rem;">(Req: 5 Water | Have: <<print setup.have("water")>>)</span> </div> <div> <<if setup.have("water") gte 5>> <<button "TURN IN (+40 SCRIP)">> <<run setup.spend("water", 5)>> <<set $materials.scrip += 40>> <<replace "#ui-dialog-body">><<print Story.get("Quests").processText()>><</replace>> <</button>> <<else>> <span style="color: #ef4444; font-size: 0.75rem;">LOCKED</span> <<endif>> </div> <<if setup.have("water") gte 5>> <div class="sl-bulk" data-k="water" data-req="5" data-pay="40" style="width: 100%; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 8px; padding-top: 8px; border-top: 1px dashed rgba(217,119,6,0.4);"> <span style="color: #cbd5e1; font-size: 0.75rem;">SELL IN BULK (SETS OF 5):</span> <span class="sl-max" style="display:none;"><<print Math.floor(setup.have("water") / 5)>></span><input type="number" min="1" value="1" onchange="SugarCube.setup.sideBulk.upd(this)" style="width:80px; padding:6px; background:#0f172a; color:#f1f5f9; border:1px solid #64748b; font-family:inherit; font-size:0.85rem;"> <button type="button" onclick="SugarCube.setup.sideBulk.max(this)" style="padding:6px 12px; background:#1e3a8a; color:#e0f2fe; border:1px solid #60a5fa; cursor:pointer; font-family:inherit; font-weight:700; font-size:0.8rem;">MAX</button> <button type="button" class="sl-go" onclick="SugarCube.setup.sideBulk.sell(this)" style="padding:6px 12px; background:#1e3a8a; color:#e0f2fe; border:1px solid #60a5fa; cursor:pointer; font-family:inherit; font-weight:700; font-size:0.8rem;">SELL ×1 (+40 SCRIP)</button> <span class="sl-msg" style="color: #fca5a5; font-size: 0.75rem;"></span> </div> <</if>> </div> <!-- 5: Medicine --> <div style="background: rgba(217, 119, 6, 0.08); border-left: 3px solid #d97706; padding: 8px 12px; border-radius: 4px; display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;"> <div style="font-size: 0.85rem;"> <span style="color: #fcd34d; font-weight: bold;">Infirmary Meds</span> <span style="color: #94a3b8; font-size: 0.75rem;">(Req: 3 Medicine | Have: <<print setup.have("medicine")>>)</span> </div> <div> <<if setup.have("medicine") gte 3>> <<button "TURN IN (+50 SCRIP)">> <<run setup.spend("medicine", 3)>> <<set $materials.scrip += 50>> <<replace "#ui-dialog-body">><<print Story.get("Quests").processText()>><</replace>> <</button>> <<else>> <span style="color: #ef4444; font-size: 0.75rem;">LOCKED</span> <<endif>> </div> <<if setup.have("medicine") gte 3>> <div class="sl-bulk" data-k="medicine" data-req="3" data-pay="50" style="width: 100%; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 8px; padding-top: 8px; border-top: 1px dashed rgba(217,119,6,0.4);"> <span style="color: #cbd5e1; font-size: 0.75rem;">SELL IN BULK (SETS OF 3):</span> <span class="sl-max" style="display:none;"><<print Math.floor(setup.have("medicine") / 3)>></span><input type="number" min="1" value="1" onchange="SugarCube.setup.sideBulk.upd(this)" style="width:80px; padding:6px; background:#0f172a; color:#f1f5f9; border:1px solid #64748b; font-family:inherit; font-size:0.85rem;"> <button type="button" onclick="SugarCube.setup.sideBulk.max(this)" style="padding:6px 12px; background:#1e3a8a; color:#e0f2fe; border:1px solid #60a5fa; cursor:pointer; font-family:inherit; font-weight:700; font-size:0.8rem;">MAX</button> <button type="button" class="sl-go" onclick="SugarCube.setup.sideBulk.sell(this)" style="padding:6px 12px; background:#1e3a8a; color:#e0f2fe; border:1px solid #60a5fa; cursor:pointer; font-family:inherit; font-weight:700; font-size:0.8rem;">SELL ×1 (+50 SCRIP)</button> <span class="sl-msg" style="color: #fca5a5; font-size: 0.75rem;"></span> </div> <</if>> </div> <!-- 6: Raw Meat --> <div style="background: rgba(217, 119, 6, 0.08); border-left: 3px solid #d97706; padding: 8px 12px; border-radius: 4px; display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;"> <div style="font-size: 0.85rem;"> <span style="color: #fcd34d; font-weight: bold;">Protein Rations</span> <span style="color: #94a3b8; font-size: 0.75rem;">(Req: 10 Meat | Have: <<print setup.have("rawMeat")>>)</span> </div> <div> <<if setup.have("rawMeat") gte 10>> <<button "TURN IN (+35 SCRIP)">> <<run setup.spend("rawMeat", 10)>> <<set $materials.scrip += 35>> <<replace "#ui-dialog-body">><<print Story.get("Quests").processText()>><</replace>> <</button>> <<else>> <span style="color: #ef4444; font-size: 0.75rem;">LOCKED</span> <<endif>> </div> <<if setup.have("rawMeat") gte 10>> <div class="sl-bulk" data-k="rawMeat" data-req="10" data-pay="35" style="width: 100%; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 8px; padding-top: 8px; border-top: 1px dashed rgba(217,119,6,0.4);"> <span style="color: #cbd5e1; font-size: 0.75rem;">SELL IN BULK (SETS OF 10):</span> <span class="sl-max" style="display:none;"><<print Math.floor(setup.have("rawMeat") / 10)>></span><input type="number" min="1" value="1" onchange="SugarCube.setup.sideBulk.upd(this)" style="width:80px; padding:6px; background:#0f172a; color:#f1f5f9; border:1px solid #64748b; font-family:inherit; font-size:0.85rem;"> <button type="button" onclick="SugarCube.setup.sideBulk.max(this)" style="padding:6px 12px; background:#1e3a8a; color:#e0f2fe; border:1px solid #60a5fa; cursor:pointer; font-family:inherit; font-weight:700; font-size:0.8rem;">MAX</button> <button type="button" class="sl-go" onclick="SugarCube.setup.sideBulk.sell(this)" style="padding:6px 12px; background:#1e3a8a; color:#e0f2fe; border:1px solid #60a5fa; cursor:pointer; font-family:inherit; font-weight:700; font-size:0.8rem;">SELL ×1 (+35 SCRIP)</button> <span class="sl-msg" style="color: #fca5a5; font-size: 0.75rem;"></span> </div> <</if>> </div> </div> <</nobr>> </div>
<<nobr>> <<setMenu "Rest">> /* Storage and Material sync (so items at the Base can be used here) */ <<if ndef $campStorage>><<set $campStorage to {}>><</if>> <<if ndef $materials>><<set $materials to {}>><</if>> <<if ndef $thirst>><<set $thirst to 100>><</if>> <<if ndef $activeCompanion>><<set $activeCompanion to "None">><</if>> <<if ndef $npcAffinity>><<set $npcAffinity to {}>><</if>> /* Storage fill ratio calculation */ <<set _currentStorageWeight to setup.campWeight()>> <<set $maxStorageLimit to setup.campCap()>> <<set _isStorageFull to (_currentStorageWeight gte $maxStorageLimit)>> /* migrate legacy key */ <<if def $materials.medical_herb>><<set $materials.medicalHerb to ($materials.medicalHerb||0) + ($materials.medical_herb||0)>><<run delete $materials.medical_herb>><</if>> <<if def $campStorage.medical_herb>><<set $campStorage.medicalHerb to ($campStorage.medicalHerb||0) + ($campStorage.medical_herb||0)>><<run delete $campStorage.medical_herb>><</if>> /* We safeguard the items present in both inventories (medicalHerb and scrap added) */ <<set _checkItems to ["berries", "food", "rawMeat", "cookedMeat", "rawFish", "cookedFish", "cloth", "water", "medicine", "wood", "medicalHerb", "scrap"]>> <<for _i to 0; _i lt _checkItems.length; _i++>> <<set _item to _checkItems[_i]>> <<if ndef $materials[_item]>><<set $materials[_item] to 0>><</if>> <<if ndef $campStorage[_item]>><<set $campStorage[_item] to 0>><</if>> <<endfor>> /* Safe variable definitions so the worker system does not throw errors */ <<if ndef $laborScrap>><<set $laborScrap to 0>><</if>> <<if ndef $laborWood>><<set $laborWood to 0>><</if>> <<if ndef $laborCloth>><<set $laborCloth to 0>><</if>> <<if ndef $laborHunters>><<set $laborHunters to 0>><</if>> <<if ndef $laborForagers>><<set $laborForagers to 0>><</if>> <<if ndef $laborMedics>><<set $laborMedics to 0>><</if>> <<if ndef $campSlaves>><<set $campSlaves to 0>><</if>> <style> #story { margin-top: 0 !important; margin-right: 0 !important; margin-bottom: 0 !important; margin-left: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; } .ashen-veil-world-rest { position: relative; margin-left: 17.5em; margin-right: 0; min-height: 100vh; background: linear-gradient(rgba(0, 5, 0, 0.8), rgba(0, 10, 5, 0.95)), url('images/locations/sanctuary_level_1.jpg') center/cover no-repeat; display: flex; flex-direction: column; padding: 20px; box-sizing: border-box; width: calc(100% - 17.5em); } .terminal-wide-core { background: rgba(5, 15, 5, 0.88); border-left: 2px solid #fbbf24; border-right: 2px solid #fbbf24; padding: 40px; font-family: 'Segoe UI', Tahoma, sans-serif; min-height: 85vh; display: flex; flex-direction: column; box-shadow: inset 0 0 30px rgba(0,0,0,0.8); box-sizing: border-box; width: 100%; } .rest-card { background: rgba(0,0,0,0.7); border: 1px solid #d97706; border-radius: 6px; padding: 15px; text-align: center; display: flex; flex-direction: column; justify-content: space-between; box-shadow: 0 4px 15px rgba(0,0,0,0.5); box-sizing: border-box; } .rest-img { width: 100%; height: 120px; object-fit: cover; border-bottom: 2px solid #d97706; margin-bottom: 10px; border-radius: 4px; } /* CUSTOM BUTTON STYLES */ .cyber-action-btn { display: block; width: 100%; background: rgba(251, 191, 36, 0.15); border: 1px solid #fbbf24; color: #fbbf24 !important; padding: 10px; text-align: center; border-radius: 4px; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.8rem; cursor: pointer; text-decoration: none !important; box-sizing: border-box; margin-bottom: 8px; transition: all 0.2s ease; } .cyber-action-btn:hover { background: rgba(251, 191, 36, 0.3); border-color: #fde047; box-shadow: 0 0 10px rgba(251, 191, 36, 0.4); } .cyber-sleep-btn { display: block; width: 100%; background: rgba(56, 189, 248, 0.15); border: 1px solid #38bdf8; color: #38bdf8 !important; padding: 12px; text-align: center; border-radius: 4px; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.9rem; cursor: pointer; text-decoration: none !important; box-sizing: border-box; transition: all 0.2s ease; } .cyber-sleep-btn:hover { background: rgba(56, 189, 248, 0.3); border-color: #7dd3fc; box-shadow: 0 0 12px rgba(56,189,248,0.5); } .cyber-return-btn { display: inline-block; padding: 12px 30px; font-weight: bold; border-radius: 4px; font-family: 'Courier New', monospace; letter-spacing: 1px; text-decoration: none !important; background: rgba(12, 18, 26, 0.92); border: 1px solid #f59e0b; color: #fde68a !important; box-shadow: 0 0 10px rgba(245, 158, 11, 0.25); } .cyber-return-btn:hover { background: rgba(245, 158, 11, 0.2); border-color: #fcd34d; color: #ffffff !important; box-shadow: 0 0 14px rgba(245, 158, 11, 0.45); } .npc-panel { background: rgba(15, 23, 42, 0.7); border: 1px dashed #fbbf24; border-radius: 6px; padding: 20px; margin-top: 30px; box-sizing: border-box; width: 100%; } .npc-card { background: rgba(0, 0, 0, 0.8); border: 1px solid #d97706; border-radius: 4px; padding: 12px; display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; } </style> <div class="ashen-veil-world-rest"> <div class="terminal-wide-core"> <div style="text-align: center; border-bottom: 1px dashed #fbbf24; padding-bottom: 15px; margin-bottom: 25px;"> <h2 style="color: #fbbf24; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px rgba(251, 191, 36, 0.5);">[ REST QUARTERS ]</h2> <div style="margin-top: 10px; font-size: 0.9rem; color: #fcd34d;">BARRACKS & KITCHEN</div> </div> <!-- CURRENT STATUS --> <div style="display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 10px; margin-bottom: 20px; text-align: center;"> <div style="background: rgba(239,68,68,0.1); border: 1px solid #ef4444; padding: 10px; border-radius: 4px;"><div style="color: #ef4444; font-size: 0.8rem; font-weight: bold; margin-bottom: 5px;">❤️ HEALTH</div><div style="color: #fff; font-size: 1.1rem; font-weight: bold;"><<print $hp>> / <<print $maxHp>></div></div> <div style="background: rgba(56,189,248,0.1); border: 1px solid #38bdf8; padding: 10px; border-radius: 4px;"><div style="color: #38bdf8; font-size: 0.8rem; font-weight: bold; margin-bottom: 5px;">⚡ STAMINA</div><div style="color: #fff; font-size: 1.1rem; font-weight: bold;"><<print $stamina>> / <<print $maxStamina>></div></div> <div style="background: rgba(251,191,36,0.1); border: 1px solid #fbbf24; padding: 10px; border-radius: 4px;"><div style="color: #fbbf24; font-size: 0.8rem; font-weight: bold; margin-bottom: 5px;">🍖 SATIETY</div><div style="color: #fff; font-size: 1.1rem; font-weight: bold;"><<print $hunger>>%</div></div> <div style="background: rgba(2,132,199,0.1); border: 1px solid #0ea5e9; padding: 10px; border-radius: 4px;"><div style="color: #0ea5e9; font-size: 0.8rem; font-weight: bold; margin-bottom: 5px;">💧 THIRST</div><div style="color: #fff; font-size: 1.1rem; font-weight: bold;"><<print $thirst>>%</div></div> </div> <div class="rest-flex" data-tour="rest-main"> <!-- DRINK WATER (first card, glows when you are thirsty) --> <<set _water to setup.waterHave()>> <div @class="'rest-card' + (setup.isThirsty() ? ' water-urgent' : ' water-card')"> <div> <img src="images/locations/env_drink_water.jpg" class="rest-img" onerror="this.src='https://placehold.co/400x200/000/38bdf8?text=DRINK+WATER';"> <div style="color: #38bdf8; font-size: 1.05rem; font-weight: bold; margin-bottom: 5px;">💧 DRINK WATER</div> <<if setup.isThirsty()>><div style="color: #fecaca; background: rgba(239,68,68,0.18); border: 1px solid #ef4444; font-size: 0.75rem; font-weight: bold; letter-spacing: 1px; padding: 3px 8px; margin: 0 auto 8px; display: inline-block; border-radius: 3px;">YOU ARE THIRSTY</div><</if>> <div style="color: #94a3b8; font-size: 0.8rem; margin-bottom: 8px; min-height: 35px;">Drink to restore Thirst: -1 Water, +<<print setup.drinkAmount()>>%. Water on hand: <strong style="color:#7dd3fc;"><<print _water>></strong>.</div> <div style="width: 100%; background: #000; height: 8px; border-radius: 4px; border: 1px solid #0284c7; margin-bottom: 10px;"><div @style="'width: ' + Math.clamp($thirst, 0, 100) + '%; background: ' + (setup.isThirsty() ? '#ef4444' : '#0ea5e9') + '; height: 100%; border-radius: 4px;'"></div></div> </div> <div> <<if $thirst gte 100>> <div style="color: #34d399; font-size: 0.8rem; font-weight: bold; text-align: center; border: 1px solid #34d399; padding: 8px;">[ FULLY HYDRATED ]</div> <<elseif _water lt 1>> <div style="color: #ef4444; font-size: 0.8rem; font-weight: bold; text-align: center; border: 1px dashed #ef4444; padding: 8px;">[ NO WATER IN STORAGE OR INVENTORY ]</div> <<else>> <<link "<span class='cyber-action-btn' style='margin:0; padding:8px 15px;'>💧 DRINK WATER (+<<print setup.drinkAmount()>>%)</span>" "Rest">><<run setup.drinkWater()>><<run window.setup.updateSidebar()>><</link>> <</if>> </div> </div> <!-- ACTION 1: CAMPFIRE (COOKING) --> <div class="rest-card"> <div> <img src="images/locations/env_campfire_kitchen.jpg" class="rest-img" onerror="this.src='https://placehold.co/400x200/000/d97706?text=KITCHEN';"> <div style="color: #fbbf24; font-size: 1.05rem; font-weight: bold; margin-bottom: 5px;">🔥 CAMPFIRE KITCHEN</div> <div style="color: #94a3b8; font-size: 0.8rem; margin-bottom: 10px; min-height: 35px;">Purify raw food to avoid disease.</div> </div> <div> <<set _totalWood to $materials.wood + $campStorage.wood>> <<set _totalRawMeat to $materials.rawMeat + $campStorage.rawMeat>> <<set _totalRawFish to $materials.rawFish + $campStorage.rawFish>> <<if _totalWood gte 1 and _totalRawMeat gte 1>> <<link "<span class='cyber-action-btn'>COOK MEAT</span>" "Rest">> <<if $materials.wood gte 1>><<set $materials.wood -= 1>><<else>><<set $campStorage.wood -= 1>><</if>> <<if $materials.rawMeat gte 1>><<set $materials.rawMeat -= 1>><<else>><<set $campStorage.rawMeat -= 1>><</if>> <<set $campStorage.cookedMeat += 1>> <</link>> <<endif>> <<if _totalWood gte 1 and _totalRawFish gte 1>> <<link "<span class='cyber-action-btn'>COOK FISH</span>" "Rest">> <<if $materials.wood gte 1>><<set $materials.wood -= 1>><<else>><<set $campStorage.wood -= 1>><</if>> <<if $materials.rawFish gte 1>><<set $materials.rawFish -= 1>><<else>><<set $campStorage.rawFish -= 1>><</if>> <<set $campStorage.cookedFish += 1>> <</link>> <<endif>> <<if (_totalWood lt 1) or (_totalRawMeat lt 1 and _totalRawFish lt 1)>> <div style="color: #ef4444; font-size: 0.8rem; font-weight: bold; text-align: center; border: 1px dashed #ef4444; padding: 8px; margin-top: 10px;">[ MISSING WOOD OR RAW FOOD ]</div> <<endif>> </div> </div> <!-- ACTION 2: SAFE MEAL --> <div class="rest-card"> <div> <img src="images/locations/env_consume_rations.jpg" class="rest-img" onerror="this.src='https://placehold.co/400x200/000/2ecc71?text=SAFE+MEALS';"> <div style="color: #2ecc71; font-size: 1.05rem; font-weight: bold; margin-bottom: 5px;">🍲 SAFE MEALS</div> <div style="color: #94a3b8; font-size: 0.8rem; margin-bottom: 10px; min-height: 35px;">Consume cooked or canned food safely.</div> </div> <div> <<set _totalCookedMeat to $materials.cookedMeat + $campStorage.cookedMeat>> <<set _totalCookedFish to $materials.cookedFish + $campStorage.cookedFish>> <<set _totalFood to $materials.food + $campStorage.food>> <<if $hunger gte 100>> <div style="color: #2ecc71; font-size: 0.85rem; font-weight: bold; text-align: center; border: 1px solid #2ecc71; padding: 8px;">[ FULLY SATED ]</div> <<else>> <<if _totalCookedMeat gte 1>> <<link "<span class='cyber-action-btn' style='color:#2ecc71!important;border-color:#2ecc71;background:rgba(46,204,113,0.15);'>EAT COOKED MEAT (+30%)</span>" "Rest">> <<if $materials.cookedMeat gte 1>><<set $materials.cookedMeat -= 1>><<else>><<set $campStorage.cookedMeat -= 1>><</if>> <<set $hunger += setup.eatAmount(30)>> <<if $hunger gt 100>><<set $hunger to 100>><</if>> <</link>> <<endif>> <<if _totalCookedFish gte 1>> <<link "<span class='cyber-action-btn' style='color:#2ecc71!important;border-color:#2ecc71;background:rgba(46,204,113,0.15);'>EAT COOKED FISH (+15%)</span>" "Rest">> <<if $materials.cookedFish gte 1>><<set $materials.cookedFish -= 1>><<else>><<set $campStorage.cookedFish -= 1>><</if>> <<set $hunger += setup.eatAmount(15)>> <<if $hunger gt 100>><<set $hunger to 100>><</if>> <</link>> <<endif>> <<if _totalFood gte 1>> <<link "<span class='cyber-action-btn' style='color:#2ecc71!important;border-color:#2ecc71;background:rgba(46,204,113,0.15);'>EAT CANNED RATION (+30%)</span>" "Rest">> <<if $materials.food gte 1>><<set $materials.food -= 1>><<else>><<set $campStorage.food -= 1>><</if>> <<set $hunger += setup.eatAmount(30)>> <<if $hunger gt 100>><<set $hunger to 100>><</if>> <</link>> <<endif>> <<if _totalCookedMeat lt 1 and _totalCookedFish lt 1 and _totalFood lt 1>> <div style="color: #94a3b8; font-size: 0.85rem; font-weight: bold; text-align: center; border: 1px dashed #94a3b8; padding: 8px;">[ NO SAFE FOOD ]</div> <<endif>> <<endif>> </div> </div> <!-- ACTION 3: RISKY AND RAW FOOD --> <div class="rest-card"> <div> <img src="images/locations/env_eat_berries.jpg" class="rest-img" onerror="this.src='https://placehold.co/400x200/000/ef4444?text=RAW+FOOD';"> <div style="color: #fca5a5; font-size: 1.05rem; font-weight: bold; margin-bottom: 5px;">⚠️ RAW & FORAGED</div> <div style="color: #94a3b8; font-size: 0.8rem; margin-bottom: 10px; min-height: 35px;">Eating raw meat/fish will damage your health.</div> </div> <div> <<set _totalBerries to $materials.berries + $campStorage.berries>> <<set _totalRawFish to $materials.rawFish + $campStorage.rawFish>> <<set _totalRawMeat to $materials.rawMeat + $campStorage.rawMeat>> <<if $hunger gte 100>> <div style="color: #2ecc71; font-size: 0.85rem; font-weight: bold; text-align: center; border: 1px solid #2ecc71; padding: 8px;">[ FULLY SATED ]</div> <<else>> <<if _totalBerries gte 1>> <<link "<span class='cyber-action-btn' style='color:#fca5a5!important;border-color:#ef4444;background:rgba(239,68,68,0.15);'>EAT BERRIES (+5% Satiety)</span>" "Rest">> <<if $materials.berries gte 1>><<set $materials.berries -= 1>><<else>><<set $campStorage.berries -= 1>><</if>> <<set $hunger += setup.eatAmount(5)>> <<if $hunger gt 100>><<set $hunger to 100>><</if>> <</link>> <<endif>> <<if _totalRawFish gte 1>> <<link "<span class='cyber-action-btn' style='color:#fca5a5!important;border-color:#ef4444;background:rgba(239,68,68,0.15);'>EAT RAW FISH (+5% Sat / -10 HP)</span>" "Rest">> <<if $materials.rawFish gte 1>><<set $materials.rawFish -= 1>><<else>><<set $campStorage.rawFish -= 1>><</if>> <<set $hunger += setup.eatAmount(5), $hp -= 10>> <<if $hunger gt 100>><<set $hunger to 100>><</if>> <<if $hp lt 0>><<set $hp to 0>><</if>> <</link>> <<endif>> <<if _totalRawMeat gte 1>> <<link "<span class='cyber-action-btn' style='color:#fca5a5!important;border-color:#ef4444;background:rgba(239,68,68,0.15);'>EAT RAW MEAT (+10% Sat / -20 HP)</span>" "Rest">> <<if $materials.rawMeat gte 1>><<set $materials.rawMeat -= 1>><<else>><<set $campStorage.rawMeat -= 1>><</if>> <<set $hunger += setup.eatAmount(10), $hp -= 20>> <<if $hunger gt 100>><<set $hunger to 100>><</if>> <<if $hp lt 0>><<set $hp to 0>><</if>> <</link>> <<endif>> <<if _totalBerries lt 1 and _totalRawFish lt 1 and _totalRawMeat lt 1>> <div style="color: #94a3b8; font-size: 0.85rem; font-weight: bold; text-align: center; border: 1px dashed #94a3b8; padding: 8px;">[ NO RAW FOOD ]</div> <<endif>> <<endif>> </div> </div> <!-- ACTION 4: SLEEPING AND ALL COLONY PRODUCTION --> <div class="rest-card" data-tour="rest-sleep"> <div> <img src="images/locations/env_rest_sleep.jpg" class="rest-img" onerror="this.src='https://placehold.co/400x200/000/38bdf8?text=SLEEP';"> <div style="color: #38bdf8; font-size: 1.05rem; font-weight: bold; margin-bottom: 5px;">🛌 SLEEP (ADVANCE DAY)</div> <div style="color: #94a3b8; font-size: 0.8rem; margin-bottom: 5px; min-height: 35px;">Restores 100% STAMINA and +20 HP.</div> <<if _isStorageFull>> <div style="color: #ef4444; font-size: 0.7rem; margin-bottom: 10px; font-family: monospace; font-weight: bold;">STORAGE FULL: PRODUCTION HALTED</div> <<else>> <div style="color: #38bdf8; font-size: 0.7rem; margin-bottom: 10px; font-family: monospace; font-weight: bold;">WORKERS WILL PRODUCE RESOURCES</div> <<endif>> <!-- SVIETLOK BUFF INDICATOR --> <<if $svietlok_buff>> <div style="color: #a3e635; background: rgba(163, 230, 53, 0.1); border: 1px dashed #a3e635; font-size: 0.7rem; padding: 4px; margin-bottom: 10px; font-family: monospace; font-weight: bold; text-align: center;"> 🌟 SVIETLOK BLESSING: +25% PRODUCTION </div> <<endif>> </div> <div> <<if $hunger gte 20>> /* THE SLEEP BUTTON GOES STRAIGHT TO THE REPORT SCREEN */ <<link "<span class='cyber-sleep-btn'>GO TO SLEEP</span>" "MorningReport">> <<set $day += 1>> <<set $stamina to $maxStamina>> <<set $hp += 20 + Math.round($maxHp * setup.tm("sleepHeal") / 100) + setup.tm("sleepHp")>> <<if $hp gt $maxHp>><<set $hp to $maxHp>><<endif>> <<set $hunger -= 20>> <<set $thirst = Math.max(0, $thirst - 15)>> <<run setup.zmoraSleepHit()>> <<run setup.randomizeLocations()>> <</link>> <<else>> <div style="color: #ef4444; font-size: 0.8rem; font-weight: bold; text-align: center; border: 1px dashed #ef4444; padding: 8px;">[ TOO HUNGRY TO SLEEP ]</div> <<endif>> </div> </div> </div> <!-- FIRST AID: PLAIN BANDAGES AND MEDICINE PILLS --> <<if ndef $materials.plainBandage>><<set $materials.plainBandage to 0>><</if>> <<if ndef $pillTolerance>><<set $pillTolerance to 0>><</if>> <div style="color: #fbbf24; font-weight: bold; font-family: 'Courier New', monospace; font-size: 1rem; margin: 5px 0 12px; letter-spacing: 1px;">🩹 BASIC FIRST AID</div> <<if $faFeedback>> <div style="color: #a7f3d0; background: rgba(6, 78, 59, 0.35); border: 1px dashed #059669; padding: 10px 14px; margin-bottom: 14px; font-size: 0.85rem;"><<print $faFeedback>></div> <<set $faFeedback to "">> <</if>> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 20px; margin-bottom: 25px;"> <!-- CRAFT PLAIN BANDAGE --> <div class="rest-card"> <div> <img src="images/locations/env_craft_bandage.jpg" class="rest-img" onerror="this.src='https://placehold.co/400x200/000/d97706?text=CRAFT+BANDAGE';"> <div style="color: #fbbf24; font-size: 1.05rem; font-weight: bold; margin-bottom: 5px;">🧵 CRAFT PLAIN BANDAGE</div> <div style="color: #94a3b8; font-size: 0.8rem; margin-bottom: 10px; min-height: 35px;">Turn <<print setup.FIRSTAID.bandageCloth>> Cloth into a clean bandage. You hold <<print ($materials.plainBandage || 0)>>.</div> </div> <div> <<if setup.faHave("cloth") gte setup.FIRSTAID.bandageCloth>> <<link "<span class='cyber-action-btn' style='margin:0; padding:8px 15px;'>CRAFT (<<print setup.FIRSTAID.bandageCloth>> CLOTH)</span>" "Rest">><<run setup.craftBandage()>><<run window.setup.updateSidebar()>><</link>> <<else>> <div style="color: #ef4444; font-size: 0.8rem; font-weight: bold; text-align: center; border: 1px dashed #ef4444; padding: 8px;">[ NEED <<print setup.FIRSTAID.bandageCloth>> CLOTH ]</div> <</if>> </div> </div> <!-- USE BANDAGE --> <div class="rest-card"> <div> <img src="images/locations/env_use_bandage.jpg" class="rest-img" onerror="this.src='https://placehold.co/400x200/000/34d399?text=BANDAGE';"> <div style="color: #34d399; font-size: 1.05rem; font-weight: bold; margin-bottom: 5px;">🩹 APPLY BANDAGE</div> <div style="color: #94a3b8; font-size: 0.8rem; margin-bottom: 10px; min-height: 35px;">Heals <<print setup.FIRSTAID.healPct>>% of your health (+<<print setup.faHealAmount()>> HP). You hold <<print ($materials.plainBandage || 0)>>.</div> </div> <div> <<if ($materials.plainBandage || 0) lt 1>> <div style="color: #64748b; font-size: 0.8rem; font-weight: bold; text-align: center; border: 1px dashed #64748b; padding: 8px;">[ NO BANDAGES ]</div> <<elseif not setup.faHurt()>> <div style="color: #34d399; font-size: 0.8rem; font-weight: bold; text-align: center; border: 1px solid #34d399; padding: 8px;">[ FULL HEALTH ]</div> <<else>> <<link "<span class='cyber-action-btn' style='margin:0; padding:8px 15px;'>APPLY BANDAGE</span>" "Rest">><<run setup.useBandage()>><<run window.setup.updateSidebar()>><</link>> <</if>> </div> </div> <!-- MEDICINE PILL --> <div class="rest-card"> <div> <img src="images/locations/env_medicine_pill.jpg" class="rest-img" onerror="this.src='https://placehold.co/400x200/000/ef4444?text=MEDICINE+PILL';"> <div style="color: #f87171; font-size: 1.05rem; font-weight: bold; margin-bottom: 5px;">💊 TAKE A MEDICINE PILL</div> <div style="color: #94a3b8; font-size: 0.8rem; margin-bottom: 6px; min-height: 35px;">Heals <<print setup.FIRSTAID.healPct>>% of your health. Two pills are fine. The third in a row poisons you and you throw up everything.</div> <div style="color: #fbbf24; font-size: 0.7rem; font-family: monospace; margin-bottom: 8px;">COST: 1 Medicine | TOLERANCE: <<print setup.faTolerance()>>/<<print setup.pillLimit()>> (resets daily)</div> </div> <div> <<if setup.faHave("medicine") lt 1>> <div style="color: #ef4444; font-size: 0.8rem; font-weight: bold; text-align: center; border: 1px dashed #ef4444; padding: 8px;">[ NO MEDICINE ]</div> <<elseif not setup.faHurt()>> <div style="color: #34d399; font-size: 0.8rem; font-weight: bold; text-align: center; border: 1px solid #34d399; padding: 8px;">[ FULL HEALTH ]</div> <<else>> <<link "<span class='cyber-action-btn' style='margin:0; padding:8px 15px;'>TAKE PILL</span>" "Rest">><<run setup.takePill()>><<run window.setup.updateSidebar()>><</link>> <</if>> </div> </div> </div> <!-- DIVIDER LINE --> <div style="border-bottom: 1px dashed #fbbf24; margin: 20px 0;"></div> <!-- NPC / COMPANIONS PANEL --> <div class="npc-panel" data-tour="rest-allies"> <div style="color: #fbbf24; font-weight: bold; font-family: 'Courier New', monospace; font-size: 1rem; margin-bottom: 12px; letter-spacing: 1px;"> 👥 CAMP COMPANIONS & ALLIES </div> <<set _alt to setup.companionAlt()>> <<if _alt>> <div style="border: 1px dashed #a855f7; background: rgba(168,85,247,0.08); padding: 10px 14px; margin-bottom: 12px; display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap;"> <div style="color: #e9d5ff; font-size: 0.8rem; line-height: 1.5;">⚔ SQUAD (<<print setup.squadSize()>>/4): <strong style="color:#fde68a;"><<print setup.squadLabel()>></strong><br><span style="color:#94a3b8; font-size:0.72rem;">Cerberus is waiting in camp. Switching to him replaces your Trained K9 in the same slot.</span></div> <<link "<span class='cyber-action-btn' style='margin:0; padding:8px 15px;'>🔁 SWITCH TO CERBERUS</span>" "Rest">><<run setup.companionSwitch()>><</link>> </div> <</if>> <<set _crew to setup.campNpcs()>> <<if _crew.length gt 0>> <<for _m range _crew>> <<set _bd to setup.npcBond(_m.id)>> <<capture _m>> <div class="npc-card"> <div style="display:flex; align-items:center; gap:12px;"> <img @src="_m.portrait" @data-n="_m.name" onerror="this.onerror=null;this.src='https://placehold.co/120x120/0b0f19/f59e0b?text='+encodeURIComponent(this.getAttribute('data-n').charAt(0));" style="width:56px; height:56px; object-fit:cover; border:1px solid #f59e0b; border-radius:4px;"> <div> <div style="color: #fcd34d; font-weight: bold; font-size: 0.95rem;"><<print _m.name.toUpperCase()>></div> <div style="color: #94a3b8; font-size: 0.75rem;"><<print _m.role>><<if _m.id eq "cerberus" and not setup.squadHas("Cerberus")>> (BENCHED)<</if>> | <<print setup.npcRankOf(_bd)>> <<if _bd.level lt setup.NPC_MAXLEVEL>>(<<print _bd.xp>>/100)<<else>>(MAX)<</if>></div> </div> </div> <div> <<link "<span class='cyber-action-btn' style='margin:0; padding:8px 15px;'>INTERACT</span>" "NpcInteract">> <<set $npcView to _m.id>><<set $npcBack to "Rest">> <</link>> </div> </div> <</capture>> <</for>> <<else>> <div style="color: #64748b; font-size: 0.85rem; font-style: italic; text-align: center; padding: 10px;"> [ No allies in quarters yet. Recruit companions at the Wasteland Bar and meet special survivors on the Sector Map. ] </div> <<endif>> </div> <!-- RETURN TO BASE --> <div style="margin-top: 25px; text-align: center;"> <<link "<span class='cyber-return-btn'>RETURN TO S.A.N.C.T.U.A.R.Y. BASE</span>" "Camp">> <<run window.setup.updateSidebar()>> <</link>> </div> </div> </div> <</nobr>>
<<audio ":all" stop>> <<audio "combat_theme" volume 0.8 loop play>> <<nobr>> <<setMenu "Ruined Military Checkpoint">> /* SNEAK / STEALTH CHECK */ <<set _hasSneak to false>> <<if def $playerPerks and $playerPerks.includes("Sneak")>> <<set _hasSneak to true>> <<elseif def $equipped and ($equipped.trinket1 eq "Stealth Ring" or $equipped.ring1 eq "Stealth Ring" or $equipped.trinket1 eq "Ghost Trinket")>> <<set _hasSneak to true>> <<endif>> <</nobr>> <style> #story { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; background: transparent !important; } .checkpoint-fullscreen { position: fixed !important; top: 0 !important; left: 17.5em !important; width: calc(100vw - 17.5em) !important; height: 100vh !important; box-sizing: border-box; display: flex; flex-direction: column; padding: 30px; overflow-y: auto; background: linear-gradient(rgba(10, 10, 0, 0.85), rgba(0, 0, 0, 0.96)), url('images/locations/ruined_military_checkpoint.jpg') center/cover no-repeat !important; z-index: 10; } .checkpoint-fullscreen a { display: block !important; background: rgba(234, 179, 8, 0.2) !important; border: 2px solid #eab308 !important; color: #fde047 !important; text-align: center !important; padding: 12px 20px !important; border-radius: 4px !important; font-weight: bold !important; text-decoration: none !important; letter-spacing: 1px !important; transition: 0.3s !important; font-family: 'Courier New', monospace !important; } .checkpoint-fullscreen a:hover { background: rgba(234, 179, 8, 0.4) !important; color: #ffffff !important; box-shadow: 0 0 15px rgba(234, 179, 8, 0.4) !important; } .checkpoint-fullscreen .disabled-btn { display: block !important; background: rgba(50, 50, 50, 0.3) !important; border: 2px dashed #64748b !important; color: #64748b !important; text-align: center !important; padding: 12px 20px !important; border-radius: 4px !important; font-weight: bold !important; font-family: 'Courier New', monospace !important; cursor: not-allowed !important; } </style> <div class="checkpoint-fullscreen"> <div style="background: rgba(10, 10, 5, 0.92); border: 2px solid #eab308; padding: 40px; border-radius: 8px; font-family: 'Courier New', monospace; color: #00ff00; box-shadow: 0 0 40px rgba(234, 179, 8, 0.25); width: 100%; max-width: 1200px; margin: auto; box-sizing: border-box;"> <div style="text-align: center; border-bottom: 1px dashed #eab308; padding-bottom: 15px; margin-bottom: 30px;"> <h2 style="color: #eab308; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px rgba(234,179,8,0.5); font-size: 1.8rem;">[ RUINED MILITARY CHECKPOINT ]</h2> <div style="margin-top: 10px; font-size: 1rem; color: #fde047; letter-spacing: 1px;">SECTOR 7-B - PRE-WAR FORTIFIED PERIMETER</div> </div> <div style="margin-bottom: 35px;"> <p style="color: #cbd5e1; line-height: 1.8; font-size: 1.05rem; margin-bottom: 20px;"> Between concrete barricades scarred by heavy artillery, dormant automated defense turrets spark silently against the toxic wind. During the final hours before the collapse, this military outpost was a slaughterhouse; piles of rusted ammo crates and overturned armor hulls are now choked with radioactive dust. </p> <p style="color: #cbd5e1; line-height: 1.8; font-size: 1.05rem; margin-bottom: 20px;"> Inside the shadow of the command tent, a military radio still crackles with static, guarded by untouched supply caches. However, thermal sensors indicate you are not alone—heavy Enforcer units and automated defense grids are guarding the perimeter. </p> <div style="background: rgba(234, 179, 8, 0.1); border-left: 4px solid #eab308; padding: 15px; color: #fef08a; font-size: 0.95rem;"> <strong>[ TACTICAL REPORT ]</strong> High-threat zone. Breaching the barricades will trigger automated defenses and draw massive attention. </div> </div> <div style="display: flex; flex-direction: column; gap: 10px; max-width: 650px; margin: 0 auto;"> <!-- ATTACK OPTION (4-MAN ENFORCER VANGUARD) --> <<link "SECURE THE ARMORY & ENGAGE (WAVE 1: 4x Enforcers)" "Combat">> <<set $enemyGroup to [ { name: "Heavy Enforcer 1", hp: 70, currentHp: 70, maxHp: 70, minDmg: 15, maxDmg: 24, drop: "ammo" }, { name: "Heavy Enforcer 2", hp: 70, currentHp: 70, maxHp: 70, minDmg: 15, maxDmg: 24, drop: "ammo" }, { name: "Heavy Enforcer 3", hp: 70, currentHp: 70, maxHp: 70, minDmg: 15, maxDmg: 24, drop: "ammo" }, { name: "Heavy Enforcer 4", hp: 70, currentHp: 70, maxHp: 70, minDmg: 15, maxDmg: 24, drop: "ammo" } ]>> <<set $enemy to $enemyGroup[0]>> <<set $targetPOIName to "Ruined Military Checkpoint">> <<set $targetPOILoot to "military">> <<set $currentLocation to "Ruined Military Checkpoint">> <<set $combatWave to 1>> <<set $checkpointWave to 1>><<unset $checkpointHack>><<set $reinforcementPending to false>><<unset $nextCombatEnemyGroup>> <</link>> <!-- STEALTH OPTION --> <<set _sneakText to (setup.squadHas("Sector Hacker") ? "SCAVENGE CLANDESTINELY (100% Safe via Hacker)" : "SCAVENGE CLANDESTINELY (25% Ambush Risk)")>> <<if _hasSneak or (setup.squadHas("Sector Hacker"))>> <<link '_sneakText'>> <<set _ambushChance to 25>> <<if setup.squadHas("Sector Hacker")>> <<set _ambushChance to 0>> <</if>> <<set _roll to random(1, 100)>> <<if _roll lte _ambushChance>> <!-- WALKED INTO AN AMBUSH --> <<set $hp -= 15>> <<set $combatLog to ["> AMBUSH! Security sensors detected your movement! Took 15 damage from automated defense shock!"]>> <<set $enemyGroup to [ { name: "Heavy Enforcer 1", hp: 70, currentHp: 70, maxHp: 70, minDmg: 15, maxDmg: 24, drop: "ammo" }, { name: "Heavy Enforcer 2", hp: 70, currentHp: 70, maxHp: 70, minDmg: 15, maxDmg: 24, drop: "ammo" }, { name: "Heavy Enforcer 3", hp: 70, currentHp: 70, maxHp: 70, minDmg: 15, maxDmg: 24, drop: "ammo" }, { name: "Heavy Enforcer 4", hp: 70, currentHp: 70, maxHp: 70, minDmg: 15, maxDmg: 24, drop: "ammo" } ]>> <<set $enemy to $enemyGroup[0]>> <<set $targetPOIName to "Ruined Military Checkpoint">> <<set $targetPOILoot to "military">> <<set $currentLocation to "Ruined Military Checkpoint">> <<set $combatWave to 1>> <<set $checkpointWave to 1>><<unset $checkpointHack>><<set $reinforcementPending to false>><<unset $nextCombatEnemyGroup>> <<goto "Combat">> <<else>> <!-- SUCCESSFUL STEALTH - INFILTRATE DIRECTLY (LORE PASSAGE) --> <<goto "RuinedMilitaryCheckpointInterior">> <</if>> <</link>> <<else>> <div class="disabled-btn" title="Requires Sneak Perk, Stealth Equipment, or Sector Hacker"> 🔒 SCAVENGE CLANDESTINELY [LOCKED: REQUIRES SNEAK OR HACKER] </div> <<endif>> <<link "RETURN TO RADAR MAP" "Map">> <</link>> </div> </div> </div>
<<nobr>> <<setMenu "Checkpoint Interior">> <style> #story { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; background: transparent !important; } .interior-fullscreen { position: fixed !important; top: 0 !important; left: 17.5em !important; width: calc(100vw - 17.5em) !important; height: 100vh !important; box-sizing: border-box; display: flex; flex-direction: column; padding: 20px; overflow-y: auto; background: linear-gradient(rgba(5, 5, 10, 0.92), rgba(0, 0, 0, 0.98)), url('images/locations/convoy_bg.jpg') center/cover no-repeat !important; z-index: 10; } .lore-terminal-box { background: rgba(0, 20, 10, 0.85); border: 1px solid #34d399; padding: 15px; border-radius: 6px; margin-bottom: 15px; font-family: 'Courier New', monospace; } .lore-content-box { background: rgba(0, 10, 5, 0.95); border-left: 3px solid #fde047; padding: 15px; margin-top: 12px; font-size: 0.85rem; line-height: 1.6; color: #e2e8f0; } .interior-fullscreen a { display: inline-block !important; background: rgba(46, 204, 113, 0.15) !important; border: 1px solid #2ecc71 !important; color: #a7f3d0 !important; text-align: center !important; padding: 8px 14px !important; border-radius: 4px !important; font-weight: bold !important; text-decoration: none !important; letter-spacing: 1px !important; transition: 0.3s !important; font-family: 'Courier New', monospace !important; font-size: 0.85rem !important; } .interior-fullscreen a:hover { background: rgba(46, 204, 113, 0.3) !important; color: #ffffff !important; box-shadow: 0 0 10px rgba(46, 204, 113, 0.4) !important; } </style> <div class="interior-fullscreen"> <div style="background: rgba(10, 15, 10, 0.95); border: 2px solid #2ecc71; padding: 25px; border-radius: 8px; font-family: 'Courier New', monospace; color: #34d399; box-shadow: 0 0 30px rgba(46, 204, 113, 0.2); width: 100%; max-width: 1000px; margin: auto; box-sizing: border-box;"> <div style="text-align: center; border-bottom: 1px dashed #2ecc71; padding-bottom: 12px; margin-bottom: 20px;"> <h2 style="color: #2ecc71; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px rgba(46,204,113,0.5); font-size: 1.4rem;">[ SECTOR 7-B COMMAND BUNKER ]</h2> <div style="margin-top: 4px; font-size: 0.8rem; color: #6ee7b7; letter-spacing: 1px;">PRE-WAR SECURE ARCHIVE // KATOWICE PERIMETER</div> </div> <div style="margin-bottom: 20px;"> <p style="color: #cbd5e1; line-height: 1.6; font-size: 0.9rem; margin-bottom: 8px;"> The smoke clears from the barricades outside. Inside the bunker, emergency phosphor lights cast an eerie green glow over rows of dead terminals and scattered intelligence dossiers. </p> <p style="color: #cbd5e1; line-height: 1.6; font-size: 0.9rem; margin-bottom: 8px;"> This underground node was the primary military relay for the Katowice sector before the collapse. Auxiliary generators hum faintly, keeping the core databanks alive. </p> </div> <!-- LORE SECTIONS / EXPLORATION AREAS --> <div style="display: flex; flex-direction: column; gap: 12px; margin-bottom: 20px;"> <!-- TERMINAL 1 --> <div class="lore-terminal-box"> <div style="color: #fde047; font-weight: bold; margin-bottom: 6px; font-size: 0.9rem;">💻 TERMINAL 01: THE KATOWICE FALL (CLASSIFIED ARCHIVE)</div> <p style="color: #94a3b8; font-size: 0.8rem; line-height: 1.4; margin: 0 0 10px 0;"> Summary: Military command logs detailing the first 12 hours of the Reptilian Vanguard's digital blackout and automated defense sabotage across Silesia. </p> <<if ndef $loreCheckpointLog1>> <<if setup.hackCanStart()>> <<link "DOWNLOAD & DECRYPT ARCHIVE (LIVE BREACH, COSTS 1 TECH DATA, REWARD +2 TECH DATA)" "HackGame">> <<run setup.hackBegin({ ctx: "lore1", game: "rand" })>> <</link>> <<else>> <div style="color:#fca5a5; background:rgba(69,10,10,0.55); border:1px dashed #ef4444; padding:10px 14px; text-align:center; font-weight:bold; font-size:0.8rem; font-family:'Courier New',monospace;">[ LOCKED: A BREACH COSTS 1 TECH DATA, AND YOU HAVE NONE ]</div> <</if>> <<else>> <div style="color: #64748b; font-size: 0.75rem; margin-bottom: 8px;">[ FILE STATUS: DOWNLOADED & UNLOCKED ]</div> <div class="lore-content-box"> <strong style="color: #fde047; display: block; margin-bottom: 6px;">SILESIA DEFENSE FORCE - SECURE LOG #884-B</strong> <p style="margin: 0 0 6px 0;"><strong>TIMESTAMP:</strong> Pre-War, Day 12 - 04:18 Hours</p> <p style="margin: 0 0 6px 0;"><strong>AUTHOR:</strong> General Viktor Vane, Sector 7 Command</p> <p style="margin: 0 0 6px 0;"><em>"If anyone is reading this via auxiliary power relays, know that the end did not come with nuclear fire, but with silence. At 0200 hours, the civilian comm-nets in Katowice went dead. We assumed it was an EMP strike by corporate syndicates or an underground revolt. We were dangerously wrong."</em></p> <p style="margin: 0 0 6px 0;"><em>"The Reptilian Vanguard didn't need to bomb our cities to rubble initially. They injected a polymorphic bio-digital signal straight into our military intranet. Within minutes, every automated defense turret, every drone squadron, and every smart-barricade in the sector recognized human soldiers as 'hostile anomalies'."</em></p> <p style="margin: 0;"><em>"I watched from this very bunker as our own perimeter defense systems opened fire on Company C as they retreated from the border. We tried to pull the manual override fuses, but the control panels were electromagnetically welded shut from the inside by the anomaly virus. Silesia is burning, the civilians are trapped in the industrial catacombs, and my officers are turning on each other. May God have mercy on our souls."</em></p> </div> <<endif>> </div> <!-- TERMINAL 2 --> <div class="lore-terminal-box"> <div style="color: #fde047; font-weight: bold; margin-bottom: 6px; font-size: 0.9rem;">📡 TERMINAL 02: THE SILENT FREQUENCY (AUDIO TRANSCRIPT)</div> <p style="color: #94a3b8; font-size: 0.8rem; line-height: 1.4; margin: 0 0 10px 0;"> Summary: Decrypted radio transcripts between a desperate front-line squad and the automated defense mainframe during the collapse. </p> <<if ndef $loreCheckpointLog2>> <<if setup.hackCanStart()>> <<link "DECRYPT AUDIO TRANSCRIPT (LIVE BREACH, COSTS 1 TECH DATA, REWARD +1 TECH DATA, +50 SCRIP)" "HackGame">> <<run setup.hackBegin({ ctx: "lore2", game: "rand" })>> <</link>> <<else>> <div style="color:#fca5a5; background:rgba(69,10,10,0.55); border:1px dashed #ef4444; padding:10px 14px; text-align:center; font-weight:bold; font-size:0.8rem; font-family:'Courier New',monospace;">[ LOCKED: A BREACH COSTS 1 TECH DATA, AND YOU HAVE NONE ]</div> <</if>> <<else>> <div style="color: #64748b; font-size: 0.75rem; margin-bottom: 8px;">[ FILE STATUS: DECRYPTED & LOADED ]</div> <div class="lore-content-box"> <strong style="color: #fde047; display: block; margin-bottom: 6px;">INTERCEPTED COMMS: SQUAD LEADER VS. MAINLOK AI</strong> <p style="margin: 0 0 6px 0;"><strong>LOCATION:</strong> Sector 7-B Checkpoint Outer Gate</p> <p style="margin: 0 0 6px 0;"><strong>[04:22:10] Sergeant Miller:</strong> "Mainlok! Mainlok, this is Vanguard Alpha! Friend-or-foe transponders are flashing red! Shut down the north gate taret immediately, do you copy?!"</p> <p style="margin: 0 0 6px 0;"><strong>[04:22:18] Automated System (Mainlok AI):</strong> <span style="color:#ef4444;">"Identity verification failed. Biological contamination detected within perimeter. Executing Directive 9: Total Sanitation."</span></p> <p style="margin: 0 0 6px 0;"><strong>[04:22:25] Sergeant Miller:</strong> "What the hell are you talking about?! We're Silesian Defense Force! Look at my uniform, you piece of junk!"</p> <p style="margin: 0 0 6px 0;"><strong>[04:22:30] Automated System:</strong> <span style="color:#ef4444;">"Anomaly classification confirmed. Human meatware is unauthorized in Sector 7. Warning: 5 seconds to kinetic termination."</span></p> <p style="margin: 0 0 6px 0;"><strong>[04:22:34] Private Kowalski:</strong> "Sarge, get down! The taret is tracking—!"</p> <p style="margin: 0 0 6px 0;"><strong>[04:22:35]</strong> <em>[STATIC INTERFERENCE, HEAVY CALIBER GUNFIRE, SCREAMS]</em></p> <p style="margin: 0;"><strong>[04:22:42] Automated System:</strong> <span style="color:#ef4444;">"Sector cleared. Scrubbing biological residue."</span></p> </div> <<endif>> </div> <!-- FOOTLOCKER --> <div class="lore-terminal-box"> <div style="color: #fde047; font-weight: bold; margin-bottom: 6px; font-size: 0.9rem;">🗄️ COMMANDER'S LOCKED FOOTLOCKER</div> <p style="color: #94a3b8; font-size: 0.8rem; line-height: 1.4; margin: 0 0 10px 0;"> A heavy, reinforced steel chest bearing the pre-war Silesian Defense Force emblem. Inside lies unspent military script and emergency medical supplies left behind in the panic. </p> <<if ndef $loreCheckpointBox>> <<link "FORCE OPEN LOCKER (+100 Scrip, +2 Meds)" "RuinedMilitaryCheckpointInterior">> <<set $loreCheckpointBox to true>> <<set $materials.scrip += 100>> <<set $materials.medicine += 2>> <</link>> <<else>> <div style="color: #64748b; font-size: 0.75rem;">[ OPENED & EMPTIED ]</div> <<endif>> </div> </div> <!-- EXIT / RETURN TO CAMP --> <div style="text-align: center; border-top: 1px dashed #2ecc71; padding-top: 15px;"> <<link "FINISH INVESTIGATION & RETURN TO S.A.N.C.T.U.A.R.Y. BASE" "Camp">> <<unset $enemy, $targetPOILoot, $targetPOIName, $combatLog, $returnPassage, $checkpointWave, $combatWave, $reinforcementPending>> <<run window.setup.updateSidebar()>> <</link>> </div> </div> </div> <</nobr>>
<<nobr>> <<setMenu "Camp">> <<if ndef $materials>><<set $materials to {}>><</if>> <<if ndef $materials.scrap>><<set $materials.scrap to 0>><</if>> <<if ndef $materials.wood>><<set $materials.wood to 0>><</if>> <<if ndef $materials.water>><<set $materials.water to 0>><</if>> <<if ndef $materials.berries>><<set $materials.berries to 0>><</if>> <<if ndef $thirst>><<set $thirst to 100>><</if>> <style> .ashen-veil-forage-screen { background: rgba(5, 15, 5, 0.95); min-height: 100vh; padding: 40px; display: flex; flex-direction: column; align-items: center; font-family: 'Segoe UI', Tahoma, sans-serif; } #forage-game-box { position: relative; width: 800px; height: 500px; background: url('images/locations/forage_container.jpg') center/cover no-repeat; border: 2px solid #a3e635; border-radius: 4px; overflow: hidden; margin-bottom: 20px; box-shadow: 0 0 30px rgba(163, 230, 53, 0.3); } .scanline-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: repeating-linear-gradient(0deg, rgba(0,0,0,0.15), rgba(0,0,0,0.15) 1px, transparent 1px, transparent 2px); pointer-events: none; z-index: 10; } .loot-item { position: absolute; width: 60px; height: 60px; background: rgba(0,0,0,0.8); border: 2px solid #34d399; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 2rem; cursor: pointer; z-index: 50; transition: transform 0.1s; box-shadow: 0 0 15px rgba(52, 211, 153, 0.6); animation: popIn 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275); user-select: none; } .loot-item:hover { transform: scale(1.2); background: rgba(52, 211, 153, 0.6); } .loot-item:active { transform: scale(0.9); } @keyframes popIn { 0% { transform: scale(0); } 100% { transform: scale(1); } } .return-btn { width: 800px; background: rgba(163,230,53,0.1); border: 1px solid #a3e635; padding: 15px; text-align: center; border-radius: 4px; transition: 0.3s; } .return-btn a { display: block; color: #a3e635 !important; font-size: 1.1rem; font-weight: bold; text-decoration: none !important; letter-spacing: 2px; } .return-btn:hover { background: rgba(163,230,53,0.3); box-shadow: 0 0 15px rgba(163,230,53,0.5); } </style> <div class="ashen-veil-forage-screen"> <div style="text-align: center; margin-bottom: 20px;"> <h2 style="color: #a3e635; margin: 0 0 10px 0; letter-spacing: 4px; text-shadow: 0 0 15px rgba(163, 230, 53, 0.5);">[ SAFE ZONE FORAGING ]</h2> </div> <!-- GAME SCREEN --> <div id="forage-game-box"> <div class="scanline-overlay"></div> <div id="f-timer" style="position:absolute; top:20px; right:20px; background:rgba(0,0,0,0.8); border:2px solid #a3e635; color:#a3e635; font-size:2rem; font-weight:bold; padding:10px 20px; border-radius:4px; z-index:20;">2.0s</div> <div id="f-layer" style="position:absolute; width:100%; height:100%; z-index:30;"></div> </div> <div class="return-btn" id="back-to-camp-btn" style="display:none;"> <<link "RETURN TO CAMP" "Camp">> <<set $thirst = Math.max(0, $thirst - 3)>> <</link>> </div> </div> <<script>> $(document).one(':passagedisplay', function() { var $layer = $('#f-layer').empty(); var $timer = $('#f-timer').show().text("2.0s").css({"color":"#a3e635", "border-color":"#a3e635"}); var sessionLoot = { scrap: 0, wood: 0, water: 0, berries: 0 }; var itemCount = Math.floor(Math.random() * 6) + 8; var types = ['scrap', 'wood', 'water', 'berries']; var icons = {'scrap': '🔩', 'wood': '🪵', 'water': '💧', 'berries': '🫐'}; for(var i=0; i<itemCount; i++) { var type = types[Math.floor(Math.random() * types.length)]; var x = Math.floor(Math.random() * 85) + 5; var y = Math.floor(Math.random() * 80) + 10; var $item = $('<div class="loot-item" data-type="'+type+'" style="left:'+x+'%; top:'+y+'%;">'+icons[type]+'</div>'); $item.on('mousedown', function(e) { e.preventDefault(); var t = $(this).data('type'); $(this).remove(); if (!State.variables.materials) State.variables.materials = {}; if (!State.variables.materials[t]) State.variables.materials[t] = 0; State.variables.materials[t] += 1; sessionLoot[t] += 1; }); $layer.append($item); } var timeLeft = 2.0; var timerInterval = setInterval(function() { timeLeft -= 0.1; if(timeLeft < 0) timeLeft = 0; $timer.text(timeLeft.toFixed(1) + "s"); if(timeLeft <= 1.0) $timer.css({"color":"#ef4444", "border-color":"#ef4444"}); if(timeLeft <= 0) { clearInterval(timerInterval); $layer.empty(); $timer.hide(); var tot = sessionLoot.scrap + sessionLoot.wood + sessionLoot.water + sessionLoot.berries; var res = "<div id='forage-result-screen' style='position:absolute; top:0; left:0; width:100%; height:100%; background:rgba(10, 15, 10, 0.95); display:flex; flex-direction:column; align-items:center; justify-content:center; z-index:50;'>"; if(tot > 0) { res += "<h2 style='color:#a3e635; font-size:2.5rem; margin:0 0 20px 0; letter-spacing:3px; text-shadow:0 0 15px #a3e635;'>SUCCESSFUL SCAVENGE</h2>"; if(sessionLoot.scrap>0) res += "<div style='font-size:1.5rem; color:#fff; margin:5px; font-weight:bold;'>🔩 SCRAP: <span style='color:#a3e635;'>+" + sessionLoot.scrap + "</span></div>"; if(sessionLoot.wood>0) res += "<div style='font-size:1.5rem; color:#fff; margin:5px; font-weight:bold;'>🪵 WOOD: <span style='color:#a3e635;'>+" + sessionLoot.wood + "</span></div>"; if(sessionLoot.water>0) res += "<div style='font-size:1.5rem; color:#fff; margin:5px; font-weight:bold;'>💧 WATER: <span style='color:#a3e635;'>+" + sessionLoot.water + "</span></div>"; if(sessionLoot.berries>0) res += "<div style='font-size:1.5rem; color:#fff; margin:5px; font-weight:bold;'>🫐 BERRIES: <span style='color:#a3e635;'>+" + sessionLoot.berries + "</span></div>"; } else { res += "<h2 style='color:#ef4444; font-size:2.5rem; margin:0 0 20px 0; letter-spacing:3px; text-shadow:0 0 15px #ef4444;'>NOTHING COLLECTED</h2>"; res += "<div style='font-size:1.2rem; color:#94a3b8;'>You were too slow.</div>"; } res += "</div>"; $('#forage-game-box').append(res); $('#back-to-camp-btn').show(); if(typeof window.setup.updateSidebar === 'function') window.setup.updateSidebar(); } }, 100); }); <</script>> <</nobr>>
<<nobr>> <<if setup.ch3 and setup.ch3.autoHub()>><<goto "Ch3Scene">><</if>> <<setMenu "Sanctuary Hub">> /* Safeguarded and Botanist-controlled level assignments */ <<if ndef $sanctuaryLevels>> <<set $sanctuaryLevels to { base: 1, workshop: 0, greenhouse: 0, pit: 0, medical: 0, walls: 0, bar: 0 }>> <</if>> <<if ndef $sanctuaryLevels.base>><<set $sanctuaryLevels.base to 1>><</if>> <<if ndef $sanctuaryLevels.workshop>><<set $sanctuaryLevels.workshop to 0>><</if>> <<if ndef $sanctuaryLevels.pit>><<set $sanctuaryLevels.pit to 0>><</if>> <<if ndef $sanctuaryLevels.medical>><<set $sanctuaryLevels.medical to 0>><</if>> <<if ndef $sanctuaryLevels.walls>><<set $sanctuaryLevels.walls to 0>><</if>> <<if ndef $sanctuaryLevels.bar>><<set $sanctuaryLevels.bar to 0>><</if>> <<if ndef $sanctuaryLevels.greenhouse>> <<if $playerBackground eq "Botanist">><<set $sanctuaryLevels.greenhouse to 1>><<else>><<set $sanctuaryLevels.greenhouse to 0>><</if>> <</if>> <<if ndef $materials>><<set $materials to { scrap: 0, wood: 0, water: 0, cloth: 0, scrip: 0 }>><</if>> /* CAMP STORAGE WEIGHT AND CAPACITY CALCULATION */ <<if ndef $campStorage>> <<set $campStorage to { scrap: 0, wood: 0, cloth: 0, iron: 0, caps: 0, techData: 0, medicine: 0, food: 0, rawMeat: 0, berries: 0, water: 0, ammo: 0, rope: 0, scrip: 0 }>> <</if>> <<set _maxCampStorage to setup.campCap()>> <<set _campTotalWeight to setup.campWeight()>> <style> body { background-color: #050a05 !important; } #story, #passages, .passage { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; background: transparent !important; } .av-world { position: absolute; top: 10px; left: 17.5em; right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 0 20px 20px 20px; width: calc(100% - 17.5em); } .sanctuary-hub-container { background: linear-gradient(180deg, rgba(5,10,15,0.95), rgba(10,5,20,0.98)), url('images/locations/sanctuary_hub_bg.jpg') center/cover; padding: 20px 25px; border: 2px solid #10b981; border-radius: 8px; font-family: 'Segoe UI', Tahoma, sans-serif; box-shadow: 0 0 30px rgba(16, 185, 129, 0.2); color: #e2e8f0; box-sizing: border-box; width: 100%; } .hub-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); gap: 16px; margin-top: 15px; } .hub-scene .cs-inner { aspect-ratio: 1024 / 412; } .hub-scene { margin-bottom: 14px; } .hub-card { background: rgba(10, 15, 25, 0.95); border: 1px solid #047857; border-radius: 6px; padding: 20px; display: flex; flex-direction: column; justify-content: space-between; box-shadow: 0 4px 15px rgba(0,0,0,0.7); transition: all 0.2s ease; width: auto; min-height: 0; gap: 10px; box-sizing: border-box; } .hub-card-locked { background: rgba(15, 10, 10, 0.95); border: 1px dashed #475569; opacity: 0.9; } .hub-card:hover { border-color: #34d399; box-shadow: 0 0 20px rgba(52, 211, 153, 0.4); transform: translateY(-2px); } .cyber-hub-btn { display: block; width: 100%; background: linear-gradient(135deg, #059669 0%, #047857 100%); border: 1px solid #34d399; color: #f8fafc !important; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.85rem; letter-spacing: 1px; border-radius: 4px; cursor: pointer; text-decoration: none !important; box-sizing: border-box; box-shadow: 0 0 10px rgba(16, 185, 129, 0.3); margin-top: 10px; } .cyber-hub-btn:hover { background: linear-gradient(135deg, #047857 100%, #34d399 100%); border-color: #6ee7b7; box-shadow: 0 0 15px rgba(52, 211, 153, 0.6); } .cyber-lock-btn { display: block; width: 100%; padding: 8px 15px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.85rem; letter-spacing: 1px; border-radius: 4px; cursor: pointer; text-decoration: none !important; box-sizing: border-box; margin-top: 6px; background: rgba(12, 18, 26, 0.92); border: 1px solid #f59e0b; color: #fde68a !important; box-shadow: 0 0 10px rgba(245, 158, 11, 0.25); } .cyber-disabled-btn { display: block; width: 100%; background: rgba(15, 23, 42, 0.6); border: 1px dashed #475569; color: #64748b; padding: 8px 15px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem; border-radius: 4px; cursor: not-allowed; box-sizing: border-box; margin-top: 6px; } </style> <div class="av-world"> <div class="sanctuary-hub-container"> <div class="isolated-map-wrapper cs-wrap hub-scene" data-tour="hub-scene"> <div class="radar-container cs-inner"> <img class="cs-photo" src="images/locations/sanctuary_hub_scene.jpg" alt="" onerror="this.style.display='none'"> <div data-tour="hub-workshop" class="cs-pin cs-blue" style="top: 56%; left: 10.5%;"> <<if $sanctuaryLevels.workshop gt 0>> <<link '<span class="cs-ring"><span class="cs-ico">🛠️</span></span><span class="cs-plate"><b>WORKSHOP</b><i>Craft and forge</i></span><span class="cs-tip">Fabricate gear and forge weapons.</span>' "Workshop">><</link>> <<else>> <span class="cs-locked"><span class="cs-ring"><span class="cs-ico">🔒</span></span><span class="cs-plate"><b>WORKSHOP</b><i>Locked</i></span><span class="cs-tip">Not built yet. Build it from the cards below.</span></span> <</if>> </div> <div data-tour="hub-pit" class="cs-pin cs-gold" style="top: 80.5%; left: 59.8%;"> <<if $sanctuaryLevels.pit gt 0>> <<link '<span class="cs-ring"><span class="cs-ico">⛏️</span></span><span class="cs-plate"><b>SCRAP PIT</b><i>Salvage metal</i></span><span class="cs-tip">Extracts raw metals and debris.</span>' "ScrapPit">><</link>> <<else>> <span class="cs-locked"><span class="cs-ring"><span class="cs-ico">🔒</span></span><span class="cs-plate"><b>SCRAP PIT</b><i>Locked</i></span><span class="cs-tip">Not built yet. Build it from the cards below.</span></span> <</if>> </div> <div data-tour="hub-lower" class="cs-pin cs-cyan" style="top: 74.5%; left: 40.5%;"> <<if (def $playerName and String($playerName).toLowerCase() eq "nakman") or ($npcFlags and $npcFlags.maya) or (setup.CIPHER_ROOM_OPEN and $npcFlags and $npcFlags.cipher)>> <<link '<span class="cs-ring"><span class="cs-ico">🛗</span></span><span class="cs-plate"><b>LOWER LEVELS</b><i>Archive and Server Room</i></span><span class="cs-tip">Ride the freight elevator down to the Archive Wing and Cipher's Server Room.</span>' "Sanctuary Depths">><</link>> <<else>> <span class="cs-locked"><span class="cs-ring"><span class="cs-ico">🔒</span></span><span class="cs-plate"><b>LOWER LEVELS</b><i>Sealed</i></span><span class="cs-tip">The elevator is dead. Someone who can read the old systems might fix it.</span></span> <</if>> </div> <div data-tour="hub-green" class="cs-pin cs-green" style="top: 36%; left: 59%;"> <<if $sanctuaryLevels.greenhouse gt 0>> <<link '<span class="cs-ring"><span class="cs-ico">🌿</span></span><span class="cs-plate"><b>GREENHOUSE</b><i>Food and crops</i></span><span class="cs-tip">Hydroponic food and crop production.</span>' "Greenhouse">><</link>> <<else>> <span class="cs-locked"><span class="cs-ring"><span class="cs-ico">🔒</span></span><span class="cs-plate"><b>GREENHOUSE</b><i>Locked</i></span><span class="cs-tip">Not built yet. Build it from the cards below.</span></span> <</if>> </div> <div data-tour="hub-med" class="cs-pin cs-cyan" style="top: 47%; left: 79.5%;"> <<if $sanctuaryLevels.medical gt 0>> <<link '<span class="cs-ring"><span class="cs-ico">💉</span></span><span class="cs-plate"><b>MED-BAY</b><i>Heal and treat</i></span><span class="cs-tip">Treats radiation and heals wounds.</span>' "MedicalTent">><</link>> <<else>> <span class="cs-locked"><span class="cs-ring"><span class="cs-ico">🔒</span></span><span class="cs-plate"><b>MED-BAY</b><i>Locked</i></span><span class="cs-tip">Not built yet. Build it from the cards below.</span></span> <</if>> </div> <div data-tour="hub-walls" class="cs-pin cs-red" style="top: 52%; left: 24.5%;"> <<if $sanctuaryLevels.walls gt 0>> <<link '<span class="cs-ring"><span class="cs-ico">🛡️</span></span><span class="cs-plate"><b>DEFENSE WALLS</b><i>Fortify the camp</i></span><span class="cs-tip">Barricades, traps and the defenders on the wall.</span>' "DefenseWalls">><</link>> <<else>> <span class="cs-locked"><span class="cs-ring"><span class="cs-ico">🔒</span></span><span class="cs-plate"><b>DEFENSE WALLS</b><i>Locked</i></span><span class="cs-tip">Not built yet. Build it from the cards below.</span></span> <</if>> </div> <div class="cs-pin cs-purple" data-tour="hub-bar" style="top: 47%; left: 95%;"> <<if $sanctuaryLevels.bar gt 0>> <<link '<span class="cs-ring"><span class="cs-ico">🍻</span></span><span class="cs-plate"><b>WASTELAND BAR</b><i>Recruits and rumors</i></span><span class="cs-tip">Mercenary recruitment and rumors.</span>' "WastelandBar">><</link>> <<else>> <span class="cs-locked"><span class="cs-ring"><span class="cs-ico">🔒</span></span><span class="cs-plate"><b>WASTELAND BAR</b><i>Locked</i></span><span class="cs-tip">Not built yet. Build it from the cards below.</span></span> <</if>> </div> </div> </div> <div style="text-align: center; border-bottom: 1px dashed #10b981; padding-bottom: 12px; margin-bottom: 15px;"> <h2 style="color: #34d399; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px #34d399; font-size: 1.5rem;">[ S.A.N.C.T.U.A.R.Y. BASE ]</h2> <<set _baseLvl to $sanctuaryLevels.base>> <<if _baseLvl eq 1>><<set _bCostScrap to 100, _bCostWood to 100, _baseTitle to "LEVEL 1 UNDERGROUND BUNKER">> <<elseif _baseLvl eq 2>><<set _bCostScrap to 200, _bCostWood to 200, _baseTitle to "LEVEL 2 REINFORCED OUTPOST">> <<elseif _baseLvl eq 3>><<set _bCostScrap to 300, _bCostWood to 300, _baseTitle to "LEVEL 3 STEEL CITADEL">> <<elseif _baseLvl eq 4>><<set _bCostScrap to 400, _bCostWood to 400, _baseTitle to "LEVEL 4 NUCLEAR FORTIFIED HUB">> <<else>><<set _baseTitle to "LEVEL 5 FORTRESS">> <<endif>> <div style="margin-top: 4px; font-size: 0.9rem; color: #fbbf24; font-family: monospace; font-weight: bold;"><<print _baseTitle>></div> <div style="margin-top: 8px; display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; font-family: monospace; font-size: 0.75rem;"> <span style="color:#94a3b8; align-self:center; letter-spacing:1px;">IN STORAGE:</span> <span style="background: rgba(0,20,10,0.6); border: 1px solid #334155; border-radius: 4px; padding: 3px 8px; color: #e2e8f0;">📦 Scrap <b style="color:#a3e635;"><<print ($campStorage.scrap || 0)>></b></span> <span style="background: rgba(0,20,10,0.6); border: 1px solid #334155; border-radius: 4px; padding: 3px 8px; color: #e2e8f0;">🪵 Wood <b style="color:#a3e635;"><<print ($campStorage.wood || 0)>></b></span> <span style="background: rgba(0,20,10,0.6); border: 1px solid #334155; border-radius: 4px; padding: 3px 8px; color: #e2e8f0;">🧵 Cloth <b style="color:#a3e635;"><<print ($campStorage.cloth || 0)>></b></span> <span style="background: rgba(0,20,10,0.6); border: 1px solid #334155; border-radius: 4px; padding: 3px 8px; color: #e2e8f0;">🔌 Electronics <b style="color:#a3e635;"><<print ($campStorage.electronics || 0)>></b></span> <span style="background: rgba(0,20,10,0.6); border: 1px solid #334155; border-radius: 4px; padding: 3px 8px; color: #e2e8f0;">💧 Water <b style="color:#38bdf8;"><<print ($campStorage.water || 0)>></b></span> <span style="background: rgba(0,20,10,0.6); border: 1px solid #334155; border-radius: 4px; padding: 3px 8px; color: #e2e8f0;">💊 Medicine <b style="color:#38bdf8;"><<print ($campStorage.medicine || 0)>></b></span> <span style="background: rgba(0,20,10,0.6); border: 1px solid #334155; border-radius: 4px; padding: 3px 8px; color: #e2e8f0;">🥫 Food <b style="color:#38bdf8;"><<print ($campStorage.food || 0)>></b></span> </div> <div style="margin-top: 3px; font-size: 0.65rem; color: #64748b; font-family: monospace;">Materials in your backpack also count toward upgrades.</div> <div style="margin-top: 6px; font-size: 0.8rem; font-family: monospace; background: rgba(0,20,10,0.6); padding: 5px; border: 1px solid #059669; border-radius: 4px; display: inline-block;"> 📦 CAMP STORAGE CAPACITY: <span @style="'color:' + (_campTotalWeight gt _maxCampStorage ? '#ef4444' : '#38bdf8')"><<print _campTotalWeight>> / <<print _maxCampStorage>> KG</span> </div> <div style="margin-top: 6px; display: block;"> <<if _baseLvl lt 5>> <div style="font-size: 0.7rem; color: #94a3b8; font-family: monospace; margin-bottom: 3px;">UPGRADE COST: <<print _bCostScrap>> Scrap, <<print _bCostWood>> Wood<br><span style="color:#64748b;">YOU HAVE: <<print setup.have("scrap")>> Scrap, <<print setup.have("wood")>> Wood</span></div> <<if setup.have("scrap") gte _bCostScrap and setup.have("wood") gte _bCostWood>> <<link "<span class='cyber-lock-btn' style='padding: 6px 20px; display: inline-block;'>⚡ UPGRADE SANCTUARY BASE</span>" "Sanctuary Hub">> <<run setup.spend("scrap", _bCostScrap)>><<run setup.spend("wood", _bCostWood)>> <<set $sanctuaryLevels.base += 1>> <</link>> <<else>> <div class="cyber-disabled-btn" style="padding: 5px 12px; font-size: 0.65rem; display: inline-block;">[ INSUFFICIENT MATERIALS FOR BASE UPGRADE ]</div> <<endif>> <<else>> <div style="color: #34d399; font-size: 0.8rem; font-family: monospace; font-weight: bold; margin-top: 3px;">[ MAXIMUM BASE LEVEL REACHED ]</div> <<endif>> </div> </div> <div class="hub-grid"> <!-- 1. WORKSHOP --> <<if $sanctuaryLevels.workshop gt 0>> <div class="hub-card" style="border-left: 4px solid #38bdf8;"> <div> <div style="color: #38bdf8; font-size: 1.1rem; font-weight: bold; margin-bottom: 6px;">🛠️ WORKSHOP (Lvl: <<print $sanctuaryLevels.workshop>>)</div> <div style="color: #94a3b8; font-size: 0.8rem; line-height: 1.4; margin-bottom: 10px;">Fabricate gear and forge weapons.</div> <<set _wLvl to $sanctuaryLevels.workshop>> <<if _wLvl eq 1>><<set _wCostScrap to 80, _wCostWood to 80, _wTarget to 2>> <<elseif _wLvl eq 2>><<set _wCostScrap to 150, _wCostWood to 150, _wTarget to 3>> <<elseif _wLvl eq 3>><<set _wCostScrap to 250, _wCostWood to 250, _wTarget to 4>> <<elseif _wLvl eq 4>><<set _wCostScrap to 350, _wCostWood to 350, _wTarget to 5>> <<endif>> <<if _wLvl lt 5>> <div style="color: #fbbf24; font-size: 0.75rem; font-family: monospace;">UPGRADE TO LVL <<print _wTarget>> COST:<br><<print _wCostScrap>> Scrap, <<print _wCostWood>> Wood<br><span style="color:#94a3b8;">YOU HAVE: <<print setup.have("scrap")>> Scrap, <<print setup.have("wood")>> Wood</span></div> <<else>> <div style="color: #34d399; font-size: 0.75rem; font-family: monospace; font-weight: bold;">[ MAX LEVEL REACHED ]</div> <<endif>> </div> <div> <<if _wLvl lt 5>> <<if _wTarget gt $sanctuaryLevels.base>> <div class="cyber-disabled-btn">[ REQUIRES BASE LEVEL <<print _wTarget>> ]</div> <<elseif setup.have("scrap") gte _wCostScrap and setup.have("wood") gte _wCostWood>> <<link "<span class='cyber-lock-btn' style='background:linear-gradient(135deg,#0369a1,#0284c7);'>⚡ UPGRADE WORKSHOP</span>" "Sanctuary Hub">> <<run setup.spend("scrap", _wCostScrap)>><<run setup.spend("wood", _wCostWood)>> <<set $sanctuaryLevels.workshop += 1>> <</link>> <<else>> <div class="cyber-disabled-btn">[ NEED <<print _wCostScrap>> SCRAP, <<print _wCostWood>> WOOD ]</div> <<endif>> <<endif>> </div> </div> <<else>> <div class="hub-card hub-card-locked" style="border-left: 4px solid #475569;"> <div> <div style="color: #94a3b8; font-size: 1.1rem; font-weight: bold; margin-bottom: 6px;">🔒 WORKSHOP (LOCKED)</div> <div style="color: #64748b; font-size: 0.8rem; line-height: 1.4; margin-bottom: 8px;">Required to craft gear and weapons.</div> <div style="color: #fbbf24; font-size: 0.75rem; font-family: monospace;">BUILD COST: 40 Scrap, 40 Wood</div> </div> <div> <<if $sanctuaryLevels.base lt 1>> <div class="cyber-disabled-btn">[ REQUIRES BASE LEVEL 1 ]</div> <<elseif setup.have("scrap") gte 40 and setup.have("wood") gte 40>> <<link "<span class='cyber-lock-btn'>🔓 BUILD WORKSHOP</span>" "Sanctuary Hub">> <<run setup.spend("scrap", 40)>><<run setup.spend("wood", 40)>> <<set $sanctuaryLevels.workshop to 1>> <</link>> <<else>> <div class="cyber-disabled-btn">[ NEED 40 SCRAP, 40 WOOD ]</div> <<endif>> </div> </div> <<endif>> <!-- 2. GREENHOUSE --> <<if $sanctuaryLevels.greenhouse gt 0>> <div class="hub-card" style="border-left: 4px solid #22c55e;"> <div> <div style="color: #22c55e; font-size: 1.1rem; font-weight: bold; margin-bottom: 6px;">🌿 GREENHOUSE (Lvl: <<print $sanctuaryLevels.greenhouse>>)</div> <div style="color: #94a3b8; font-size: 0.8rem; line-height: 1.4; margin-bottom: 10px;">Hydroponic food and crop production.</div> <<set _gLvl to $sanctuaryLevels.greenhouse>> <<if _gLvl eq 1>><<set _gCostScrap to 80, _gCostWood to 80, _gTarget to 2>> <<elseif _gLvl eq 2>><<set _gCostScrap to 150, _gCostWood to 150, _gTarget to 3>> <<elseif _gLvl eq 3>><<set _gCostScrap to 250, _gCostWood to 250, _gTarget to 4>> <<elseif _gLvl eq 4>><<set _gCostScrap to 350, _gCostWood to 350, _gTarget to 5>> <<endif>> <<if _gLvl lt 5>> <div style="color: #fbbf24; font-size: 0.75rem; font-family: monospace;">UPGRADE TO LVL <<print _gTarget>> COST:<br><<print _gCostScrap>> Scrap, <<print _gCostWood>> Wood<br><span style="color:#94a3b8;">YOU HAVE: <<print setup.have("scrap")>> Scrap, <<print setup.have("wood")>> Wood</span></div> <<else>> <div style="color: #34d399; font-size: 0.75rem; font-family: monospace; font-weight: bold;">[ MAX LEVEL REACHED ]</div> <<endif>> </div> <div> <<if _gLvl lt 5>> <<if _gTarget gt $sanctuaryLevels.base>> <div class="cyber-disabled-btn">[ REQUIRES BASE LEVEL <<print _gTarget>> ]</div> <<elseif setup.have("scrap") gte _gCostScrap and setup.have("wood") gte _gCostWood>> <<link "<span class='cyber-lock-btn' style='background:linear-gradient(135deg,#15803d,#16a34a);'>⚡ UPGRADE GREENHOUSE</span>" "Sanctuary Hub">> <<run setup.spend("scrap", _gCostScrap)>><<run setup.spend("wood", _gCostWood)>> <<set $sanctuaryLevels.greenhouse += 1>> <</link>> <<else>> <div class="cyber-disabled-btn">[ NEED <<print _gCostScrap>> SCRAP, <<print _gCostWood>> WOOD ]</div> <<endif>> <<endif>> </div> </div> <<else>> <div class="hub-card hub-card-locked" style="border-left: 4px solid #475569;"> <div> <div style="color: #94a3b8; font-size: 1.1rem; font-weight: bold; margin-bottom: 6px;">🔒 GREENHOUSE (LOCKED)</div> <div style="color: #64748b; font-size: 0.8rem; line-height: 1.4; margin-bottom: 8px;">Produces food and organic resources.</div> <div style="color: #fbbf24; font-size: 0.75rem; font-family: monospace;">BUILD COST: 40 Scrap, 40 Wood, 30 Water</div> </div> <div> <<if $sanctuaryLevels.base lt 1>> <div class="cyber-disabled-btn">[ REQUIRES BASE LEVEL 1 ]</div> <<elseif setup.have("scrap") gte 40 and setup.have("wood") gte 40 and setup.have("water") gte 30>> <<link "<span class='cyber-lock-btn'>🔓 BUILD GREENHOUSE</span>" "Sanctuary Hub">> <<run setup.spend("scrap", 40)>><<run setup.spend("wood", 40)>><<run setup.spend("water", 30)>> <<set $sanctuaryLevels.greenhouse to 1>> <</link>> <<else>> <div class="cyber-disabled-btn">[ NEED 40 SCRAP, 40 WOOD, 30 WATER ]</div> <<endif>> </div> </div> <<endif>> <!-- 3. SCRAP PIT --> <<if $sanctuaryLevels.pit gt 0>> <div class="hub-card" style="border-left: 4px solid #fbbf24;"> <div> <div style="color: #fbbf24; font-size: 1.1rem; font-weight: bold; margin-bottom: 6px;">⛏️ SCRAP PIT (Lvl: <<print $sanctuaryLevels.pit>>)</div> <div style="color: #94a3b8; font-size: 0.8rem; line-height: 1.4; margin-bottom: 10px;">Extracts raw metals and debris.</div> <<set _pLvl to $sanctuaryLevels.pit>> <<if _pLvl eq 1>><<set _pCostScrap to 80, _pCostWood to 80, _pTarget to 2>> <<elseif _pLvl eq 2>><<set _pCostScrap to 150, _pCostWood to 150, _pTarget to 3>> <<elseif _pLvl eq 3>><<set _pCostScrap to 250, _pCostWood to 250, _pTarget to 4>> <<elseif _pLvl eq 4>><<set _pCostScrap to 350, _pCostWood to 350, _pTarget to 5>> <<endif>> <<if _pLvl lt 5>> <div style="color: #fbbf24; font-size: 0.75rem; font-family: monospace;">UPGRADE TO LVL <<print _pTarget>> COST:<br><<print _pCostScrap>> Scrap, <<print _pCostWood>> Wood<br><span style="color:#94a3b8;">YOU HAVE: <<print setup.have("scrap")>> Scrap, <<print setup.have("wood")>> Wood</span></div> <<else>> <div style="color: #34d399; font-size: 0.75rem; font-family: monospace; font-weight: bold;">[ MAX LEVEL REACHED ]</div> <<endif>> </div> <div> <<if _pLvl lt 5>> <<if _pTarget gt $sanctuaryLevels.base>> <div class="cyber-disabled-btn">[ REQUIRES BASE LEVEL <<print _pTarget>> ]</div> <<elseif setup.have("scrap") gte _pCostScrap and setup.have("wood") gte _pCostWood>> <<link "<span class='cyber-lock-btn' style=''>⚡ UPGRADE SCRAP PIT</span>" "Sanctuary Hub">> <<run setup.spend("scrap", _pCostScrap)>><<run setup.spend("wood", _pCostWood)>> <<set $sanctuaryLevels.pit += 1>> <</link>> <<else>> <div class="cyber-disabled-btn">[ NEED <<print _pCostScrap>> SCRAP, <<print _pCostWood>> WOOD ]</div> <<endif>> <<endif>> </div> </div> <<else>> <div class="hub-card hub-card-locked" style="border-left: 4px solid #475569;"> <div> <div style="color: #94a3b8; font-size: 1.1rem; font-weight: bold; margin-bottom: 6px;">🔒 SCRAP PIT (LOCKED)</div> <div style="color: #64748b; font-size: 0.8rem; line-height: 1.4; margin-bottom: 8px;">Automates scrap metal extraction.</div> <div style="color: #fbbf24; font-size: 0.75rem; font-family: monospace;">BUILD COST: 40 Scrap, 40 Wood</div> </div> <div> <<if $sanctuaryLevels.base lt 1>> <div class="cyber-disabled-btn">[ REQUIRES BASE LEVEL 1 ]</div> <<elseif setup.have("scrap") gte 40 and setup.have("wood") gte 40>> <<link "<span class='cyber-lock-btn'>🔓 BUILD SCRAP PIT</span>" "Sanctuary Hub">> <<run setup.spend("scrap", 40)>><<run setup.spend("wood", 40)>> <<set $sanctuaryLevels.pit to 1>> <</link>> <<else>> <div class="cyber-disabled-btn">[ NEED 40 SCRAP, 40 WOOD ]</div> <<endif>> </div> </div> <<endif>> <!-- 4. MEDICAL INFIRMARY --> <<if $sanctuaryLevels.medical gt 0>> <div class="hub-card" style="border-left: 4px solid #38bdf8;"> <div> <div style="color: #38bdf8; font-size: 1.1rem; font-weight: bold; margin-bottom: 6px;">💉 MEDICAL INFIRMARY (Lvl: <<print $sanctuaryLevels.medical>>)</div> <div style="color: #94a3b8; font-size: 0.8rem; line-height: 1.4; margin-bottom: 10px;">Treats radiation and heals wounds.</div> <<set _mLvl to $sanctuaryLevels.medical>> <<if _mLvl eq 1>><<set _mCostScrap to 80, _mCostWood to 80, _mTarget to 2>> <<elseif _mLvl eq 2>><<set _mCostScrap to 150, _mCostWood to 150, _mTarget to 3>> <<elseif _mLvl eq 3>><<set _mCostScrap to 250, _mCostWood to 250, _mTarget to 4>> <<elseif _mLvl eq 4>><<set _mCostScrap to 350, _mCostWood to 350, _mTarget to 5>> <<endif>> <<if _mLvl lt 5>> <div style="color: #fbbf24; font-size: 0.75rem; font-family: monospace;">UPGRADE TO LVL <<print _mTarget>> COST:<br><<print _mCostScrap>> Scrap, <<print _mCostWood>> Wood<br><span style="color:#94a3b8;">YOU HAVE: <<print setup.have("scrap")>> Scrap, <<print setup.have("wood")>> Wood</span></div> <<else>> <div style="color: #34d399; font-size: 0.75rem; font-family: monospace; font-weight: bold;">[ MAX LEVEL REACHED ]</div> <<endif>> </div> <div> <<if _mLvl lt 5>> <<if _mTarget gt $sanctuaryLevels.base>> <div class="cyber-disabled-btn">[ REQUIRES BASE LEVEL <<print _mTarget>> ]</div> <<elseif setup.have("scrap") gte _mCostScrap and setup.have("wood") gte _mCostWood>> <<link "<span class='cyber-lock-btn' style='background:linear-gradient(135deg,#0369a1,#0284c7);'>⚡ UPGRADE MED-BAY</span>" "Sanctuary Hub">> <<run setup.spend("scrap", _mCostScrap)>><<run setup.spend("wood", _mCostWood)>> <<set $sanctuaryLevels.medical += 1>> <</link>> <<else>> <div class="cyber-disabled-btn">[ NEED <<print _mCostScrap>> SCRAP, <<print _mCostWood>> WOOD ]</div> <<endif>> <<endif>> </div> </div> <<else>> <div class="hub-card hub-card-locked" style="border-left: 4px solid #475569;"> <div> <div style="color: #94a3b8; font-size: 1.1rem; font-weight: bold; margin-bottom: 6px;">🔒 MEDICAL INFIRMARY (LOCKED)</div> <div style="color: #64748b; font-size: 0.8rem; line-height: 1.4; margin-bottom: 8px;">Restores health and cures trauma.</div> <div style="color: #fbbf24; font-size: 0.75rem; font-family: monospace;">BUILD COST: 30 Scrap, 30 Wood, 20 Medicine</div> </div> <div> <<if $sanctuaryLevels.base lt 1>> <div class="cyber-disabled-btn">[ REQUIRES BASE LEVEL 1 ]</div> <<elseif setup.have("scrap") gte 30 and setup.have("wood") gte 30 and setup.have("medicine") gte 20>> <<link "<span class='cyber-lock-btn'>🔓 BUILD MED-BAY</span>" "Sanctuary Hub">> <<run setup.spend("scrap", 30)>><<run setup.spend("wood", 30)>><<run setup.spend("medicine", 20)>> <<set $sanctuaryLevels.medical to 1>> <</link>> <<else>> <div class="cyber-disabled-btn">[ NEED 30 SCRAP, 30 WOOD, 20 MEDICINE ]</div> <<endif>> </div> </div> <<endif>> <!-- 5. DEFENSE WALLS --> <<if $sanctuaryLevels.walls gt 0>> <div class="hub-card" style="border-left: 4px solid #ef4444;"> <div> <div style="color: #ef4444; font-size: 1.1rem; font-weight: bold; margin-bottom: 6px;">🛡️ DEFENSE WALLS (Lvl: <<print $sanctuaryLevels.walls>>)</div> <div style="color: #94a3b8; font-size: 0.8rem; line-height: 1.4; margin-bottom: 10px;">Perimeter barricades against raids.</div> <<set _dLvl to $sanctuaryLevels.walls>> <<if _dLvl eq 1>><<set _dCostScrap to 100, _dCostWood to 100, _dTarget to 2>> <<elseif _dLvl eq 2>><<set _dCostScrap to 200, _dCostWood to 200, _dTarget to 3>> <<elseif _dLvl eq 3>><<set _dCostScrap to 300, _dCostWood to 300, _dTarget to 4>> <<elseif _dLvl eq 4>><<set _dCostScrap to 400, _dCostWood to 400, _dTarget to 5>> <<endif>> <<if _dLvl lt 5>> <div style="color: #fbbf24; font-size: 0.75rem; font-family: monospace;">UPGRADE TO LVL <<print _dTarget>> COST:<br><<print _dCostScrap>> Scrap, <<print _dCostWood>> Wood<br><span style="color:#94a3b8;">YOU HAVE: <<print setup.have("scrap")>> Scrap, <<print setup.have("wood")>> Wood</span></div> <<else>> <div style="color: #34d399; font-size: 0.75rem; font-family: monospace; font-weight: bold;">[ MAX LEVEL REACHED ]</div> <<endif>> </div> <div> <<if _dLvl lt 5>> <<if _dTarget gt $sanctuaryLevels.base>> <div class="cyber-disabled-btn">[ REQUIRES BASE LEVEL <<print _dTarget>> ]</div> <<elseif setup.have("scrap") gte _dCostScrap and setup.have("wood") gte _dCostWood>> <<link "<span class='cyber-lock-btn' style='background:linear-gradient(135deg,#b91c1c,#dc2626);'>⚡ UPGRADE WALLS</span>" "Sanctuary Hub">> <<run setup.spend("scrap", _dCostScrap)>><<run setup.spend("wood", _dCostWood)>> <<set $sanctuaryLevels.walls += 1>> <</link>> <<else>> <div class="cyber-disabled-btn">[ NEED <<print _dCostScrap>> SCRAP, <<print _dCostWood>> WOOD ]</div> <<endif>> <<endif>> </div> </div> <<else>> <div class="hub-card hub-card-locked" style="border-left: 4px solid #475569;"> <div> <div style="color: #94a3b8; font-size: 1.1rem; font-weight: bold; margin-bottom: 6px;">🔒 DEFENSE WALLS (LOCKED)</div> <div style="color: #64748b; font-size: 0.8rem; line-height: 1.4; margin-bottom: 8px;">Protects the base from incoming raids.</div> <div style="color: #fbbf24; font-size: 0.75rem; font-family: monospace;">BUILD COST: 40 Scrap, 40 Wood</div> </div> <div> <<if $sanctuaryLevels.base lt 1>> <div class="cyber-disabled-btn">[ REQUIRES BASE LEVEL 1 ]</div> <<elseif setup.have("scrap") gte 40 and setup.have("wood") gte 40>> <<link "<span class='cyber-lock-btn'>🔓 BUILD WALLS</span>" "Sanctuary Hub">> <<run setup.spend("scrap", 40)>><<run setup.spend("wood", 40)>> <<set $sanctuaryLevels.walls to 1>> <</link>> <<else>> <div class="cyber-disabled-btn">[ NEED 40 SCRAP, 40 WOOD ]</div> <<endif>> </div> </div> <<endif>> <!-- 6. THE BAR --> <<if $sanctuaryLevels.bar gt 0>> <div class="hub-card" style="border-left: 4px solid #a855f7;"> <div> <div style="color: #a855f7; font-size: 1.1rem; font-weight: bold; margin-bottom: 6px;">🍻 WASTELAND BAR (Lvl: <<print $sanctuaryLevels.bar>>)</div> <div style="color: #94a3b8; font-size: 0.8rem; line-height: 1.4; margin-bottom: 10px;">Mercenary recruitment and rumors.</div> <<set _brLvl to $sanctuaryLevels.bar>> <<if _brLvl eq 1>><<set _brCostScrap to 100, _brCostWood to 100, _brTarget to 2>> <<elseif _brLvl eq 2>><<set _brCostScrap to 200, _brCostWood to 200, _brTarget to 3>> <<elseif _brLvl eq 3>><<set _brCostScrap to 300, _brCostWood to 300, _brTarget to 4>> <<elseif _brLvl eq 4>><<set _brCostScrap to 400, _brCostWood to 400, _brTarget to 5>> <<endif>> <<if _brLvl lt 5>> <div style="color: #fbbf24; font-size: 0.75rem; font-family: monospace;">UPGRADE TO LVL <<print _brTarget>> COST:<br><<print _brCostScrap>> Scrap, <<print _brCostWood>> Wood<br><span style="color:#94a3b8;">YOU HAVE: <<print setup.have("scrap")>> Scrap, <<print setup.have("wood")>> Wood</span></div> <<else>> <div style="color: #34d399; font-size: 0.75rem; font-family: monospace; font-weight: bold;">[ MAX LEVEL REACHED ]</div> <<endif>> </div> <div> <<if _brLvl lt 5>> <<if _brTarget gt $sanctuaryLevels.base>> <div class="cyber-disabled-btn">[ REQUIRES BASE LEVEL <<print _brTarget>> ]</div> <<elseif setup.have("scrap") gte _brCostScrap and setup.have("wood") gte _brCostWood>> <<link "<span class='cyber-lock-btn' style='background:linear-gradient(135deg,#7e22ce,#9333ea);'>⚡ UPGRADE BAR</span>" "Sanctuary Hub">> <<run setup.spend("scrap", _brCostScrap)>><<run setup.spend("wood", _brCostWood)>> <<set $sanctuaryLevels.bar += 1>> <</link>> <<else>> <div class="cyber-disabled-btn">[ NEED <<print _brCostScrap>> SCRAP, <<print _brCostWood>> WOOD ]</div> <<endif>> <<endif>> </div> </div> <<else>> <div class="hub-card hub-card-locked" style="border-left: 4px solid #475569;"> <div> <div style="color: #94a3b8; font-size: 1.1rem; font-weight: bold; margin-bottom: 6px;">🔒 WASTELAND BAR (LOCKED)</div> <div style="color: #64748b; font-size: 0.8rem; line-height: 1.4; margin-bottom: 8px;">High-end social hub for recruitment.</div> <div style="color: #fbbf24; font-size: 0.75rem; font-family: monospace;">BUILD COST: 100 Scrap, 100 Wood, 100 Scrip</div> </div> <div> <<if $sanctuaryLevels.base lt 1>> <div class="cyber-disabled-btn">[ REQUIRES BASE LEVEL 1 ]</div> <<elseif setup.have("scrap") gte 100 and setup.have("wood") gte 100 and setup.have("scrip") gte 100>> <<link "<span class='cyber-lock-btn'>🔓 BUILD THE BAR</span>" "Sanctuary Hub">> <<run setup.spend("scrap", 100)>><<run setup.spend("wood", 100)>><<run setup.spend("scrip", 100)>> <<set $sanctuaryLevels.bar to 1>> <</link>> <<else>> <div class="cyber-disabled-btn">[ NEED 100 SCRAP, 100 WOOD, 100 SCRIP ]</div> <<endif>> </div> </div> <<endif>> </div> <div style="text-align: center; margin-top: 20px;"> <<link "<span style='display:inline-block;background:linear-gradient(135deg,#0f766e,#115e59);border:1px solid #2dd4bf;color:#99f6e4;padding:8px 20px;font-weight:bold;border-radius:4px;font-family:monospace;letter-spacing:1px;font-size:0.85rem;'>🏠 BACK TO S.A.N.C.T.U.A.R.Y. BASE</span>" "Camp">><</link>> </div> </div> </div> <</nobr>>
<<setMenu "ScrapPit">> <<if ndef $sanctuaryLevels>><<set $sanctuaryLevels to { pit: 1 }>><</if>> <<if ndef $sanctuaryLevels.pit>><<set $sanctuaryLevels.pit to 1>><</if>> <<if ndef $gearInventory>><<set $gearInventory to []>><</if>> <style> /* DEFINITIVE FIX THAT REMOVES THE TOP GAP */ body { background-color: #050a05 !important; } #story, #passages, .passage { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; background: transparent !important; } .av-world { position: absolute; top: 10px; left: 17.5em; right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 0 20px 20px 20px; width: calc(100% - 17.5em); } .pit-container { background-color: #291504; padding: 30px; border: 2px solid #d97706; border-radius: 8px; font-family: 'Segoe UI', Tahoma, sans-serif; box-shadow: 0 0 25px rgba(217, 119, 6, 0.2); box-sizing: border-box; width: 100%; } .pit-grid { display: flex; flex-wrap: wrap; gap: 20px; justify-content: center; margin-top: 25px; margin-bottom: 20px; align-items: stretch; } .pit-card { background: rgba(0, 0, 0, 0.75); border: 1px solid #b45309; border-radius: 6px; flex: 1 1 310px; min-width: 0; max-width: 640px; overflow: hidden; display: flex; flex-direction: column; justify-content: space-between; box-shadow: 0 4px 12px rgba(0,0,0,0.6); box-sizing: border-box; } .pit-card-wide { background: rgba(0, 0, 0, 0.75); border: 1px solid #b45309; border-radius: 6px; width: 100%; box-sizing: border-box; padding: 15px; box-shadow: 0 4px 12px rgba(0,0,0,0.6); } .pit-img { height: 110px; width: 100%; object-fit: cover; border-bottom: 2px solid #b45309; background: #000; } .main-banner-img { height: 140px; width: 100%; object-fit: cover; border: 1px solid #d97706; border-radius: 6px; margin-bottom: 20px; background: #000; box-shadow: 0 0 15px rgba(217, 119, 6, 0.3); box-sizing: border-box; } .section-title { color: #fde047; font-size: 0.95rem; font-family: 'Courier New', monospace; letter-spacing: 2px; margin: 30px 0 15px 0; text-transform: uppercase; } .cyber-pit-btn { display: inline-block; padding: 6px 14px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem; letter-spacing: 1px; border-radius: 4px; cursor: pointer; text-decoration: none !important; transition: all 0.2s; box-sizing: border-box; background: rgba(12, 18, 26, 0.92); border: 1px solid #f59e0b; color: #fde68a !important; box-shadow: 0 0 10px rgba(245, 158, 11, 0.25); } .cyber-pit-btn:hover { border-color: #fde047; background: rgba(245, 158, 11, 0.2); border-color: #fcd34d; color: #ffffff !important; box-shadow: 0 0 14px rgba(245, 158, 11, 0.45); } .cyber-disabled-btn { display: block; background: rgba(15, 23, 42, 0.6); border: 1px dashed #78350f; color: #b45309; padding: 8px 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem; border-radius: 4px; box-sizing: border-box; } .cyber-return-btn { display: inline-block; padding: 12px 30px; font-weight: bold; font-family: 'Courier New', monospace; letter-spacing: 2px; border-radius: 4px; text-decoration: none !important; transition: all 0.2s; background: rgba(12, 18, 26, 0.92); border: 1px solid #f59e0b; color: #fde68a !important; box-shadow: 0 0 10px rgba(245, 158, 11, 0.25); } .cyber-return-btn:hover { border-color: #fde047; background: rgba(245, 158, 11, 0.2); border-color: #fcd34d; color: #ffffff !important; box-shadow: 0 0 14px rgba(245, 158, 11, 0.45); } </style> <<nobr>> <div class="av-world"> <div class="pit-container"> <!-- TITLE --> <div style="text-align: center; border-bottom: 1px dashed #d97706; padding-bottom: 15px; margin-bottom: 20px;"> <h2 style="color: #fbbf24; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px #d97706; font-size: 1.8rem;">[ INDUSTRIAL SCRAP PIT ]</h2> <div style="margin-top: 8px; font-size: 0.9rem; color: #fde047;">FACILITY LEVEL: <<print $sanctuaryLevels.pit>> | SALVAGE & DISMANTLE BAY</div> </div> <!-- SCRAP PIT IMAGE BANNER --> <img src="images/locations/scrap_pit_main.jpg" class="main-banner-img" onerror="this.src='https://placehold.co/800x200/000/d97706?text=SCRAP+PIT+REFINERY';"> <!-- ACTION LOG --> <<if def $pitLog>> <div style="border: 1px solid #d97706; background: rgba(217, 119, 6, 0.1); padding: 15px; border-radius: 4px; margin-bottom: 25px; font-family: monospace;"> <div style="color: #fbbf24; font-weight: bold; margin-bottom: 8px;">⚙️ OPERATION STATUS: COMPLETE</div> <div style="color: #cbd5e1; font-style: italic;">"<<print $pitLog>>"</div> </div> <<unset $pitLog>> <</if>> <!-- INFO PANEL --> <div style="border: 1px solid #d97706; background: rgba(217, 119, 6, 0.1); padding: 15px; border-radius: 4px; margin-bottom: 25px; font-family: monospace;"> <div style="color: #fbbf24; font-weight: bold; margin-bottom: 6px;">🔥 STAMINA-POWERED SALVAGE PROTOCOL</div> <div style="color: #cbd5e1; font-size: 0.85rem; line-height: 1.4;"> Expend your physical stamina to work the smelting pits, break down timber, or dismantle stashed gear by rarity, in batches, or one piece at a time. <br>• <strong style="color: #fbbf24;">Raw scrap / timber:</strong> 20 Stamina per shift <br>• <strong style="color: #fbbf24;">Dismantle one item:</strong> 1 Stamina (2 for Epic and above) <br>• <strong style="color: #fbbf24;">Selected batch:</strong> 25% off | <strong style="color: #fbbf24;">Bulk by rarity:</strong> 50% off <br>• <strong style="color: #fde047;">Yield:</strong> Scales with facility tier (Level <<print $sanctuaryLevels.pit>>) and item rarity </div> </div> <!-- PRODUCTION SECTION --> <div class="section-title">>> SALVAGE OPERATIONS</div> <<nobr>> <div class="pit-grid"> <!-- 1. EXTRACT RAW SCRAP --> <div class="pit-card"> <div> <img src="images/locations/scrap_extract.jpg" class="pit-img" onerror="this.src='https://placehold.co/600x300/000/d97706?text=SCRAP';"> <div style="padding: 14px;"> <div style="color: #fbbf24; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">⛏️ EXTRACT RAW SCRAP</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 8px; line-height: 1.3;">Process industrial wreckage and structural debris for metals.</div> <div style="color: #fde047; font-size: 0.7rem; font-family: monospace;">COST: 20 Stamina | YIELD: +<<print ($sanctuaryLevels.pit * 10)>> Scrap</div> </div> </div> <div style="padding: 0 14px 14px 14px;"> <<if $stamina gte 20>> <<link "<span class='cyber-pit-btn' style='display:block; width:100%;'>⛏️ WORK SCRAP SHIFT (20 STAM)</span>" "ScrapPit">> <<set $stamina -= 20>> <<if ndef $materials.scrap>><<set $materials.scrap to 0>><</if>> <<set _scrapGained to ($sanctuaryLevels.pit * 10)>> <<set $materials.scrap += _scrapGained>> <<set $pitLog to "Worked the smelting pit with intense focus, extracting " + _scrapGained + " units of raw scrap.">> <<run window.setup.updateSidebar()>> <</link>> <<else>> <div class="cyber-disabled-btn">[ INSUFFICIENT STAMINA (NEED 20) ]</div> <<endif>> </div> </div> <!-- 2. SALVAGE TIMBER --> <div class="pit-card"> <div> <img src="images/locations/scrap_wood.jpg" class="pit-img" onerror="this.src='https://placehold.co/600x300/000/b45309?text=WOOD';"> <div style="padding: 14px;"> <div style="color: #fbbf24; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">🪵 SALVAGE TIMBER</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 8px; line-height: 1.3;">Break down reinforced wooden crates and structural beams.</div> <div style="color: #fde047; font-size: 0.7rem; font-family: monospace;">COST: 20 Stamina | YIELD: +<<print ($sanctuaryLevels.pit * 10)>> Wood</div> </div> </div> <div style="padding: 0 14px 14px 14px;"> <<if $stamina gte 20>> <<link "<span class='cyber-pit-btn' style='display:block; width:100%;'>🪵 SALVAGE TIMBER (20 STAM)</span>" "ScrapPit">> <<set $stamina -= 20>> <<if ndef $materials.wood>><<set $materials.wood to 0>><</if>> <<set _woodGained to ($sanctuaryLevels.pit * 10)>> <<set $materials.wood += _woodGained>> <<set $pitLog to "Smashed through reinforced crates in the heat of the workshop, salvaging " + _woodGained + " units of timber.">> <<run window.setup.updateSidebar()>> <</link>> <<else>> <div class="cyber-disabled-btn">[ INSUFFICIENT STAMINA (NEED 20) ]</div> <<endif>> </div> </div> </div> <</nobr>> <!-- 3. STASH DISMANTLE BAY --> <div class="section-title" style="margin-top: 40px;">>> STASH DISMANTLE BAY</div> <<nobr>> <<run setup.pitClean()>> <<set _inv to setup.pitInv()>> <<set _sel to setup.pitSel()>> <!-- 3a. BULK BY RARITY --> <div class="pit-card-wide" style="margin-bottom: 18px;"> <div style="border-bottom: 1px dashed #b45309; padding-bottom: 10px; margin-bottom: 10px;"> <div style="color: #fbbf24; font-size: 0.95rem; font-weight: bold; margin-bottom: 2px;">♻️ BULK DISMANTLE BY RARITY</div> <div style="color: #94a3b8; font-size: 0.75rem; line-height: 1.3;">Break down every item of one rarity in a single shift. Half price per item. Higher rarity returns more scrap.</div> </div> <<set _any to false>> <div style="display: flex; flex-direction: column; gap: 6px;"> <<for _ri to 0; _ri lt setup.PIT.order.length; _ri++>> <<set _r to setup.PIT.order[_ri]>> <<set _idx to setup.pitByRarity(_r)>> <<if _idx.length gt 0>> <<set _any to true>> <<set _col to setup.pitColor(_r)>> <<set _cost to setup.pitCost(_idx, "bulk")>> <<set _gain to setup.pitTotalYield(_idx)>> <<set _high to setup.pitHasHigh(_idx)>> <<set _key to "bulk:" + _r>> <<capture _idx, _r, _key, _cost, _gain, _high>> <div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 8px; background: rgba(0,0,0,0.5); border: 1px solid #78350f; border-left: 4px solid #b45309; padding: 8px 12px; border-radius: 4px;"> <div> <div @style="'font-weight:bold; font-size:0.85rem; color:' + _col"><<print _r.toUpperCase()>> x<<print _idx.length>></div> <div style="color: #94a3b8; font-size: 0.7rem; font-family: monospace;">+<<print _gain>> Scrap | <<print _cost>> Stamina</div> </div> <div> <<if $stamina lt _cost>> <span style="color: #ef4444; font-size: 0.7rem; font-weight: bold;">[ NEED <<print _cost>> STAM ]</span> <<elseif _high and $pitConfirm isnot _key>> <<set _lbl to "<span class='cyber-pit-btn' style='padding: 5px 10px; font-size: 0.7rem;'>⚠️ DISMANTLE ALL (" + _cost + " STAM)</span>">> <<link _lbl "ScrapPit">><<set $pitConfirm to _key>><</link>> <<else>> <<set _lbl to "<span class='cyber-pit-btn' style='padding: 5px 10px; font-size: 0.7rem;'>" + (_high ? "✅ CONFIRM" : "♻️ DISMANTLE ALL") + " (" + _cost + " STAM)</span>">> <<link _lbl "ScrapPit">><<run setup.pitRun(_idx, "bulk")>><<run window.setup.updateSidebar()>><</link>> <</if>> </div> </div> <</capture>> <</if>> <</for>> </div> <<if not _any>><div style="color: #78350f; text-align: center; font-family: monospace; font-size: 0.8rem; padding: 6px;">[ NOTHING TO BULK DISMANTLE ]</div><</if>> </div> <!-- 3b. SELECTED + SINGLE --> <div class="pit-card-wide"> <div style="display: flex; align-items: center; gap: 15px; border-bottom: 1px dashed #b45309; padding-bottom: 10px; margin-bottom: 10px;"> <img src="images/locations/scrap_smelt.jpg" style="width: 50px; height: 50px; object-fit: cover; border-radius: 4px; border: 1px solid #b45309;" onerror="this.src='https://placehold.co/50x50/000/78350f?text=GEAR';"> <div> <div style="color: #fbbf24; font-size: 0.95rem; font-weight: bold; margin-bottom: 2px;">⚙️ SELECTIVE STASH DISMANTLE</div> <div style="color: #94a3b8; font-size: 0.75rem; line-height: 1.3;">Dismantle one item for 1 Stamina (2 for Epic and above), or tick several and break them down together at a discount.</div> </div> </div> <<if _inv.length gt 0>> <!-- summary bar --> <<set _sc to setup.pitCost(_sel, "sel")>> <<set _sg to setup.pitTotalYield(_sel)>> <<set _sh to setup.pitHasHigh(_sel)>> <div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 8px; background: rgba(217,119,6,0.12); border: 1px solid #d97706; padding: 8px 12px; border-radius: 4px; margin-bottom: 10px;"> <div style="font-family: monospace; font-size: 0.78rem; color: #fde047;"> SELECTED: <<print _sel.length>> <<if _sel.length gt 0>> | +<<print _sg>> Scrap | <<print _sc>> Stamina<</if>> </div> <div style="display: flex; gap: 8px; flex-wrap: wrap;"> <<link "<span class='cyber-pit-btn' style='padding: 5px 10px; font-size: 0.7rem;'>☑ SELECT ALL</span>" "ScrapPit">><<run setup.pitSelectAll()>><</link>> <<link "<span class='cyber-pit-btn' style='padding: 5px 10px; font-size: 0.7rem;'>☐ CLEAR</span>" "ScrapPit">><<set $pitPicks to []>><<set $pitConfirm to "">><</link>> <<if _sel.length gt 0>> <<if $stamina lt _sc>> <span style="color: #ef4444; font-size: 0.7rem; font-weight: bold; align-self: center;">[ NEED <<print _sc>> STAM ]</span> <<elseif _sh and $pitConfirm isnot "sel">> <<set _lbl to "<span class='cyber-pit-btn' style='padding: 5px 10px; font-size: 0.7rem;'>⚠️ DISMANTLE SELECTED (" + _sel.length + ") - " + _sc + " STAM</span>">> <<link _lbl "ScrapPit">><<set $pitConfirm to "sel">><</link>> <<else>> <<set _lbl to "<span class='cyber-pit-btn' style='padding: 5px 10px; font-size: 0.7rem;'>" + (_sh ? "✅ CONFIRM" : "⚙️ DISMANTLE SELECTED") + " (" + _sel.length + ") - " + _sc + " STAM</span>">> <<link _lbl "ScrapPit">><<run setup.pitRun(setup.pitSel().slice(), "sel")>><<run window.setup.updateSidebar()>><</link>> <</if>> <</if>> </div> </div> <div style="display: flex; flex-direction: column; gap: 6px;"> <<for _i to 0; _i lt _inv.length; _i++>> <<set _stashItem to _inv[_i]>> <<set _prot to setup.pitProtected(_stashItem)>> <<set _rar to setup.pitRarity(_stashItem)>> <<set _col to setup.pitColor(_rar)>> <<set _unit to setup.pitUnit(_stashItem)>> <<set _y to setup.pitYield(_stashItem)>> <<set _on to _sel.includes(_i)>> <<capture _i, _stashItem, _prot, _col, _unit, _y, _on>> <div @style="'display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:8px; padding:8px 12px; border-radius:4px; border:1px solid ' + (_on ? '#fbbf24' : '#78350f') + '; background:' + (_on ? 'rgba(217,119,6,0.22)' : 'rgba(0,0,0,0.5)')"> <div style="display: flex; align-items: center; gap: 10px;"> <<if _prot>> <span style="color: #78350f; font-size: 1rem;">🔒</span> <<else>> <<set _tl to "<span style='font-size:1.05rem; color:#fde047;'>" + (_on ? "☑" : "☐") + "</span>">> <<link _tl "ScrapPit">><<run setup.pitToggle(_i)>><</link>> <</if>> <div> <div @style="'font-weight:bold; font-size:0.85rem; color:' + _col"><<print _stashItem>></div> <div style="color: #94a3b8; font-size: 0.7rem; font-family: monospace;"> <<if _prot>>PROTECTED - cannot be dismantled<<else>>+<<print _y>> Scrap<</if>> </div> </div> </div> <<if not _prot>> <div> <<if $stamina gte _unit>> <<set _bl to "<span class='cyber-pit-btn' style='padding: 5px 10px; font-size: 0.7rem;'>⚙️ DISMANTLE (" + _unit + " STAM)</span>">> <<link _bl "ScrapPit">><<run setup.pitRun([_i], "single")>><<run window.setup.updateSidebar()>><</link>> <<else>> <span style="color: #ef4444; font-size: 0.7rem; font-weight: bold;">[ NEED <<print _unit>> STAM ]</span> <</if>> </div> <</if>> </div> <</capture>> <</for>> </div> <<else>> <div style="color: #ef4444; text-align: center; font-weight: bold; padding: 10px; font-family: monospace; font-size: 0.85rem;">[ GEAR STASH IS CURRENTLY EMPTY ]</div> <</if>> </div> <</nobr>> <!-- BACK TO THE SANCTUARY HUB --> <div style="text-align: center; margin-top: 35px;"> <<link "<span class='cyber-return-btn'>🏰 RETURN TO SANCTUARY HUB</span>" "Sanctuary Hub">><</link>> </div> </div> </div> <</nobr>>
<<nobr>> <<set $currentMenu to "ServerChat">> <<if ndef $currentChannel>> <<set $currentChannel to "Katowice">> <</if>> <<if ndef $playerName>> <<set $playerName to "Survivor">> <</if>> /* SAFE VARIABLE DEFINITIONS */ <<if ndef $blockedUsers>><<set $blockedUsers to []>><</if>> <<if ndef $activeDirectMessageUser>><<set $activeDirectMessageUser to null>><</if>> <<if ndef $unreadDmCounts>><<set $unreadDmCounts to {}>><</if>> <<if ndef $globalBroadcastPass>><<set $globalBroadcastPass to 0>><</if>> <<if ndef $isFounder>><<set $isFounder to false>><</if>> <<if ndef $isVIP>><<set $isVIP to false>><</if>> <<if ndef $chatMeshLogs>> <<set $chatMeshLogs to []>> <<run $chatMeshLogs.push({ sender: "Rust_Nomad", text: "Signal lost at the Katowice relay station. Movement detected.", time: "02:14", channel: "Katowice", vip: false })>> <<run $chatMeshLogs.push({ sender: "Vex_99", text: "New shelter builders watch out, drones are monitoring this frequency.", time: "02:19", channel: "Sector-07", vip: false })>> <<run $chatMeshLogs.push({ sender: "Silesia-Admin", text: "Global emergency broadcast active. Maintain radio silence if compromised.", time: "02:30", channel: "Global", vip: true, badge: "SYSTEM ADMIN" })>> <</if>> /* DEFINITIVE CHECK */ <<set _isRealVIP to false>> <<if $isFounder === true or $isVIP === true>> <<set _isRealVIP to true>> <</if>> <</nobr>> <style> body { background-color: #050a05 !important; } #story, #passages, .passage { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; background: transparent !important; } .chat-layout { display: flex; gap: 15px; font-family: 'Courier New', Courier, monospace; } .chat-sidebar { flex: 0 0 250px; width: 250px; min-width: 250px; box-sizing: border-box; align-self: flex-start; background: rgba(13, 17, 23, 0.9); border: 1px solid #3b82f6; border-radius: 6px; padding: 12px; display: flex; flex-direction: column; gap: 10px; } .sidebar-section { display: flex; flex-direction: column; gap: 5px; } .chat-sidebar a { text-decoration: none !important; display: block; } #sidebarDmList { display: flex; flex-direction: column; gap: 5px; } .channel-btn.active { background: rgba(37, 99, 235, 0.55); border-color: #60a5fa; color: #fff; box-shadow: 0 0 8px rgba(59, 130, 246, 0.45); } @media (max-width: 900px) { .chat-layout { flex-direction: column; } .chat-sidebar { flex: none; width: 100%; min-width: 0; } } .channel-btn { display: block; width: 100%; background: rgba(30, 58, 138, 0.3); border: 1px solid #1e3a8a; color: #93c5fd; padding: 8px 10px; text-align: left; font-size: 0.78rem; line-height: 1.3; cursor: pointer; border-radius: 4px; text-decoration: none !important; box-sizing: border-box; } .channel-btn:hover { background: #2563eb; color: #fff; border-color: #60a5fa; box-shadow: 0 0 10px rgba(59, 130, 246, 0.4); } #sidebarOnlineList { max-height: 190px; overflow-y: auto; display: flex; flex-direction: column; gap: 3px; } .onl-row { display: flex; align-items: center; gap: 4px; } .onl-name { flex: 1; min-width: 0; text-align: left; background: rgba(15,23,42,0.8); color: #e2e8f0; border: 1px solid #334155; border-radius: 4px; padding: 5px 6px; font-family: 'Courier New', monospace; font-size: 0.72rem; cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .onl-name:hover { border-color: #34d399; } .onl-me { border-color: #1e293b; color: #94a3b8; cursor: default; } .onl-duel { flex: 0 0 auto; background: #0f172a; color: #e0f2fe; border: 1px solid #38bdf8; border-radius: 4px; padding: 5px 7px; font-family: 'Courier New', monospace; font-size: 0.72rem; font-weight: bold; cursor: pointer; } .onl-duel.acc { background: #064e3b; color: #d1fae5; border-color: #34d399; } .onl-duel.wait { opacity: 0.6; cursor: default; } .dm-user-btn { display: flex; justify-content: space-between; align-items: center; width: 100%; background: rgba(15, 23, 42, 0.8); border: 1px solid #38bdf8; color: #38bdf8; padding: 6px 8px; font-size: 0.75rem; cursor: pointer; border-radius: 4px; text-decoration: none !important; } .dm-row { display: flex; gap: 4px; margin-bottom: 4px; align-items: stretch; } .dm-row .dm-user-btn { flex: 1; width: auto; } .dm-del-btn { background: rgba(127, 29, 29, 0.35); border: 1px solid #f87171; color: #fecaca; border-radius: 4px; padding: 0 8px; cursor: pointer; font-size: 0.8rem; font-weight: bold; } .dm-del-btn:hover { background: rgba(185, 28, 28, 0.6); color: #fff; } .dm-clear-btn { margin-top: 6px; background: rgba(127, 29, 29, 0.35); border: 1px solid #f87171; color: #fecaca; border-radius: 4px; padding: 5px 12px; cursor: pointer; font-size: 0.72rem; font-family: 'Courier New', monospace; font-weight: bold; } .dm-clear-btn:hover { background: rgba(185, 28, 28, 0.6); color: #fff; } .dm-user-btn:hover { background: rgba(56, 189, 248, 0.2); color: #fff; box-shadow: 0 0 8px rgba(56, 189, 248, 0.3); } .dm-badge { background: #facc15; color: #000; font-size: 0.65rem; font-weight: bold; padding: 1px 5px; border-radius: 10px; box-shadow: 0 0 8px rgba(250, 204, 21, 0.8); } .chat-main { flex-grow: 1; background-color: #0d1117; padding: 15px; border: 2px solid #3b82f6; border-radius: 8px; box-shadow: 0 0 25px rgba(59, 130, 246, 0.25); color: #e2e8f0; } .chat-img-header { height: 100px; width: 100%; object-fit: cover; border: 1px solid #1e3a8a; border-radius: 6px; margin-bottom: 12px; background: #000; } .chat-box { background: rgba(3, 7, 18, 0.95); border: 1px solid #1e3a8a; border-radius: 6px; height: 350px; overflow-y: auto; padding: 12px; margin-bottom: 12px; display: flex; flex-direction: column; gap: 6px; } .chat-line { background: rgba(15, 23, 42, 0.6); border-left: 2px solid #3b82f6; padding: 6px 10px; border-radius: 3px; font-size: 0.8rem; display: flex; justify-content: space-between; align-items: center; } .chat-line-vip { background: rgba(168, 85, 247, 0.15) !important; border-left: 3px solid #a855f7 !important; box-shadow: 0 0 10px rgba(168, 85, 247, 0.2); } .chat-user { color: #60a5fa; font-weight: bold; cursor: pointer; text-decoration: underline dotted; } .chat-colon { color: #60a5fa; font-weight: bold; margin-right: 8px; } .chat-line-vip .chat-colon { color: #e879f9; } .chat-user:hover { color: #93c5fd; } .chat-user-vip { color: #e879f9 !important; cursor: pointer; text-decoration: underline dotted; text-shadow: 0 0 8px rgba(232, 121, 249, 0.6); } .vip-badge { background: linear-gradient(135deg, #7e22ce, #a855f7); color: #fff; font-size: 0.6rem; padding: 2px 5px; border-radius: 3px; margin-left: 6px; margin-right: 1px; font-weight: bold; letter-spacing: 1px; } .global-badge { font-size: 0.6rem; padding: 2px 5px; border-radius: 3px; margin-left: 6px; font-weight: bold; letter-spacing: 1px; background: rgba(12, 18, 26, 0.92); border: 1px solid #fbbf24; color: #fde68a; } .chat-text { color: #cbd5e1; flex-grow: 1; } .chat-time { color: #64748b; font-size: 0.7rem; margin-left: 10px; white-space: nowrap; } .chat-input-area { display: flex; gap: 10px; background: rgba(15, 23, 42, 0.8); padding: 10px; border: 1px solid #3b82f6; border-radius: 6px; } .cyber-input { flex-grow: 1; background: #020617; border: 1px solid #1e3a8a; color: #38bdf8; padding: 8px 12px; font-family: 'Courier New', Courier, monospace; font-size: 0.9rem; border-radius: 4px; outline: none; } .cyber-send-btn { background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%); border: 1px solid #3b82f6; color: #fff !important; padding: 8px 24px; font-weight: bold; font-family: 'Courier New', Courier, monospace; font-size: 0.85rem; letter-spacing: 1px; border-radius: 4px; cursor: pointer; text-decoration: none !important; white-space: nowrap; box-shadow: 0 0 10px rgba(59, 130, 246, 0.3); } .cyber-back-container { display: inline-block; background: linear-gradient(135deg, #7e22ce 0%, #581c87 100%); border: 1px solid #c084fc; padding: 8px 20px; font-weight: bold; border-radius: 4px; box-shadow: 0 0 10px rgba(168, 85, 247, 0.3); } .user-action-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.7); display: flex; justify-content: center; align-items: center; z-index: 9999; } .user-action-box { background: #090d16; border: 2px solid #3b82f6; padding: 20px; border-radius: 6px; width: 340px; max-width: 92vw; max-height: 92vh; overflow-y: auto; box-sizing: border-box; text-align: center; box-shadow: 0 0 20px rgba(59, 130, 246, 0.5); } .pc-avatar { width: 150px; height: 200px; object-fit: cover; border: 2px solid #334155; border-radius: 6px; background: #000; display: block; margin: 0 auto 10px; } .pc-avatar-exo { border-color: #fbbf24; box-shadow: 0 0 18px rgba(251, 191, 36, 0.65), inset 0 0 12px rgba(251, 191, 36, 0.25); animation: pcExoGlow 2.2s ease-in-out infinite; } @keyframes pcExoGlow { 0%, 100% { box-shadow: 0 0 10px rgba(251, 191, 36, 0.4); } 50% { box-shadow: 0 0 26px rgba(251, 191, 36, 0.9); } } @media (prefers-reduced-motion: reduce) { .pc-avatar-exo { animation: none; } } .pc-row { display: flex; justify-content: space-between; gap: 10px; font-size: 0.8rem; padding: 4px 8px; background: rgba(255, 255, 255, 0.03); margin-bottom: 3px; border-radius: 3px; } .pc-row span:first-child { color: #64748b; letter-spacing: 1px; } .pc-row span:last-child { color: #e2e8f0; font-weight: bold; text-align: right; } .pc-chip { display: inline-block; font-size: 0.65rem; font-weight: bold; letter-spacing: 1px; padding: 2px 8px; margin: 2px; border-radius: 3px; font-family: 'Courier New', monospace; } .pc-exo-tag { color: #fde68a; border: 1px solid #fbbf24; background: rgba(251, 191, 36, 0.12); } .pc-founder { color: #fde68a; border: 1px solid #f59e0b; background: rgba(245, 158, 11, 0.12); } .pc-vip { color: #a7f3d0; border: 1px solid #34d399; background: rgba(52, 211, 153, 0.12); } </style> <<nobr>> <div class="chat-layout"> <div class="chat-sidebar" data-tour="chat-side"> <div class="sidebar-section"> <div style="color: #60a5fa; font-size: 0.8rem; font-weight: bold; border-bottom: 1px dashed #3b82f6; padding-bottom: 4px; margin-bottom: 4px;">>> MESH CHANNELS</div> <<set _chs to [["Katowice", "Katowice (Sector-01)"], ["Gliwice", "Gliwice (Sector-02)"], ["Bytom", "Bytom (Sector-03)"], ["Zabrze", "Zabrze (Sector-04)"], ["Sosnowiec", "Sosnowiec (Warzone)"], ["Sector-07", "Sector-07 (Base)"]]>> <<for _c range _chs>> <<capture _c>> <<set _lbl to ('<span class="channel-btn' + (($currentChannel eq _c[0] and not $activeDirectMessageUser) ? ' active' : '') + '">' + _c[1] + '</span>')>> <<link _lbl "ServerChat">><<set $currentChannel to _c[0]>><<set $activeDirectMessageUser to null>><</link>> <</capture>> <</for>> <<if _isRealVIP>> <div style="color: #a855f7; font-size: 0.7rem; border-top: 1px dashed #7e22ce; padding-top: 4px; margin-top: 4px;">[ FOUNDER ACCESS ]</div> <<set _gl to ('<span class="channel-btn' + (($currentChannel eq "Global" and not $activeDirectMessageUser) ? ' active' : '') + '" style="border-color:#7e22ce; color:#e9d5ff; background:rgba(126,34,206,0.2);">📢 Global Broadcast</span>')>> <<link _gl "ServerChat">><<set $currentChannel to "Global">><<set $activeDirectMessageUser to null>><</link>> <</if>> </div> <div class="sidebar-section"> <div style="color: #34d399; font-size: 0.75rem; font-weight: bold; border-top: 1px dashed #34d399; padding-top: 6px; margin-top: 4px;">>> ONLINE NOW <span id="onlineCount" style="color:#94a3b8; font-weight:normal;"></span></div> <div id="sidebarOnlineList"><div style="color:#64748b; font-size:0.7rem;">Scanning the mesh...</div></div> </div> <div class="sidebar-section"> <div style="color: #38bdf8; font-size: 0.75rem; font-weight: bold; border-top: 1px dashed #38bdf8; padding-top: 6px; margin-top: 4px;">>> DIRECT MESSAGES</div> <div id="sidebarDmList"></div> </div> <div class="sidebar-section"> <div style="border-top: 1px dashed #e879f9; padding-top: 6px; margin-top: 4px; text-align: center;"> <<if _isRealVIP>> <button onclick="window.setup.openBroadcastModal()" style="display: block; width: 100%; background: rgba(232, 121, 249, 0.15); border: 1px solid #e879f9; color: #e879f9; padding: 6px; font-size: 0.65rem; font-weight: bold; cursor: pointer; border-radius: 4px; font-family: 'Courier New', monospace;">📢 USE BROADCAST PASS</button> <div style="color: #e879f9; font-size: 0.6rem; margin-top: 3px; font-weight: bold;">Passes: UNLIMITED (VIP)</div> <<elseif def $globalBroadcastPass and $globalBroadcastPass gt 0>> <button onclick="window.setup.openBroadcastModal()" style="display: block; width: 100%; background: rgba(232, 121, 249, 0.15); border: 1px solid #e879f9; color: #e879f9; padding: 6px; font-size: 0.65rem; font-weight: bold; cursor: pointer; border-radius: 4px; font-family: 'Courier New', monospace;">📢 USE BROADCAST PASS</button> <div style="color: #e879f9; font-size: 0.6rem; margin-top: 3px; font-weight: bold;">Passes: <<print $globalBroadcastPass>></div> <<else>> <div title="Global Broadcast Passes let you post to every channel. Earn 2 per Reptilian Warlord kill (Quests terminal contract)." style="color: #64748b; font-size: 0.6rem; text-align: center; border: 1px solid #334155; padding: 5px; border-radius: 4px;">NO BROADCAST PASSES<br><span style="font-size:0.55rem;">Earn them: Reptilian Warlord contract</span></div> <</if>> </div> </div> <div class="sidebar-section"> <div style="border-top: 1px dashed #3b82f6; padding-top: 6px; margin-top: 4px; display: flex; flex-direction: column; gap: 4px;"> <<button '<span style="color:#38bdf8; font-weight:bold; font-size:0.75rem;">📦 ITEM DATABASE</span>'>><<run Dialog.setup("MASTER ITEM DATABASE")>><<run Dialog.wiki(Story.get("ItemDatabase").processText())>><<run Dialog.open()>><</button>> <<button '<span style="color:#ef4444; font-weight:bold; font-size:0.75rem;">🚫 BLOCKED PLAYERS</span>'>><<run window.setup.openBlockedPlayersModal()>><</button>> </div> </div> </div> <div class="chat-main"> <div style="text-align: center; border-bottom: 1px dashed #3b82f6; padding-bottom: 6px; margin-bottom: 8px;"> <h2 style="color: #60a5fa; margin: 0; font-size: 1.2rem;"> <<if $activeDirectMessageUser neq null>> [ SECURE DM: <<print setup.shown($activeDirectMessageUser)>> ] <<else>> [ CHANNEL: <<print $currentChannel>> ] <<endif>> </h2> <<if $activeDirectMessageUser neq null>> <button type="button" class="dm-clear-btn" onclick="window.setup.deleteDm(SugarCube.State.variables.activeDirectMessageUser)">🗑 DELETE THIS CONVERSATION</button> <</if>> </div> <img src="images/locations/comms_chat.jpg" class="chat-img-header" onerror="this.src='https://placehold.co/800x150/000/3b82f6?text=SECURE+MESH';"> <div class="chat-box" id="chatMessageDisplay"></div> <div class="chat-input-area" data-tour="chat-input"> <input type="text" id="chatInputBox" class="cyber-input" placeholder="Type transmission..." autocomplete="off"> <button id="chatSendBtn" class="cyber-send-btn">TRANSMIT</button> </div> <div style="text-align: center; margin-top: 12px;"> <span class="cyber-back-container"><<link "💻 BACK TO COMM-SERVER" "Comms">><</link>></span> </div> </div> </div> <</nobr>> <!-- Socket.io Library --> <script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script> <<script>> /* Founder and VIP status always come from the server, so refresh them once when the chat opens */ (function () { const me = State.variables.playerName; if (!me || window.__avFlagsFor === me) { return; } window.__avFlagsFor = me; fetch(window.API_URL + "/api/player_profile", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: me }) }) .then(function (r) { return r.json(); }) .then(function (d) { if (d && d.status === "success" && d.profile) { State.variables.isFounder = d.profile.founder === true; State.variables.isVIP = d.profile.vip === true; } }).catch(function () { window.__avFlagsFor = null; }); })(); window.setup = window.setup || {}; /* delete one DM conversation from this player's log (server history for that person is ignored afterwards) */ window.setup.dmBelongsTo = function (m, user, me) { if (!m || typeof m.channel !== 'string' || m.channel.indexOf('DM:') !== 0) { return false; } const clean = m.channel.slice(3); return clean === user || clean === me + '_' + user || clean === user + '_' + me || clean.split('_').indexOf(user) >= 0; }; window.setup.deleteDm = function (user) { if (!user) { return; } if (!window.confirm('Delete your whole conversation with ' + user + '? This cannot be undone.')) { return; } const V = State.variables, me = V.playerName || 'Survivor'; V.chatMeshLogs = (V.chatMeshLogs || []).filter(function (m) { return !window.setup.dmBelongsTo(m, user, me); }); if (V.unreadDmCounts) { delete V.unreadDmCounts[user]; } if (!V.deletedDms || typeof V.deletedDms !== 'object') { V.deletedDms = {}; } V.deletedDms[user] = true; V.dmSyncTs = Date.now(); /* so the deleted messages are not replayed from the server at the next login */ if (V.activeDirectMessageUser === user) { V.activeDirectMessageUser = null; } Engine.play('ServerChat'); }; window.setup.updateDmSidebar = function() { const sidebarDiv = document.getElementById('sidebarDmList'); if (!sidebarDiv) return; sidebarDiv.innerHTML = ''; const myName = State.variables.playerName || "Survivor"; const logs = State.variables.chatMeshLogs || []; const blocked = State.variables.blockedUsers || []; const unreadCounts = State.variables.unreadDmCounts || {}; const activeDm = State.variables.activeDirectMessageUser; const dmUsers = new Set(); logs.forEach(m => { if (m && m.channel && typeof m.channel === 'string' && m.channel.startsWith("DM:")) { const clean = m.channel.replace("DM:", ""); const parts = clean.split("_"); parts.forEach(p => { if (p && p !== myName && !blocked.includes(p)) { dmUsers.add(p); } }); } }); if (dmUsers.size > 0) { dmUsers.forEach(user => { const btn = document.createElement('button'); btn.className = 'dm-user-btn'; let badgeHtml = ''; const count = unreadCounts[user] || 0; if (count > 0 && activeDm !== user) { badgeHtml = `<span class="dm-badge">${count}</span>`; } btn.innerHTML = `<span>👤 ${window.setup.escHtml(window.setup.shown(user))}</span>${badgeHtml}`; btn.onclick = function() { State.variables.activeDirectMessageUser = user; if (State.variables.unreadDmCounts) { State.variables.unreadDmCounts[user] = 0; } Engine.play("ServerChat"); }; const row = document.createElement('div'); row.className = 'dm-row'; row.appendChild(btn); const del = document.createElement('button'); del.className = 'dm-del-btn'; del.title = 'Delete this conversation'; del.textContent = '\u2715'; del.onclick = function (ev) { ev.stopPropagation(); window.setup.deleteDm(user); }; row.appendChild(del); sidebarDiv.appendChild(row); }); } else { sidebarDiv.innerHTML = '<div style="color: #64748b; font-size: 0.7rem; font-style: italic;">No active DMs.</div>'; } }; window.setup.openBlockedPlayersModal = function() { const blockedUsers = State.variables.blockedUsers || []; let contentHtml = '<div style="font-family: \'Courier New\', monospace; color: #e2e8f0; text-align: left; padding: 10px;">'; contentHtml += '<p style="color: #94a3b8; font-size: 0.85rem; margin-bottom: 15px;">Operators currently restricted from your frequency channels:</p>'; if (blockedUsers.length > 0) { contentHtml += '<div style="display: flex; flex-direction: column; gap: 8px;">'; blockedUsers.forEach(user => { contentHtml += ` <div style="display: flex; justify-content: space-between; align-items: center; background: rgba(239, 68, 68, 0.1); border: 1px solid #ef4444; padding: 8px 12px; border-radius: 4px;"> <span style="color: #fca5a5; font-weight: bold;">🚫 ${user}</span> <button onclick="window.setup.unblockUserFromModal('${user}')" style="background: #047857; color: #fff; border: none; padding: 4px 10px; border-radius: 3px; cursor: pointer; font-family: 'Courier New'; font-weight: bold; font-size: 0.75rem;">UNBLOCK</button> </div> `; }); contentHtml += '</div>'; } else { contentHtml += '<div style="color: #64748b; font-style: italic; text-align: center; padding: 20px;">No operators are currently blocked.</div>'; } contentHtml += '</div>'; Dialog.setup("BLOCKED OPERATORS REGISTRY"); Dialog.wiki(contentHtml); Dialog.open(); }; window.setup.unblockUserFromModal = function(username) { State.variables.blockedUsers = State.variables.blockedUsers.filter(u => u !== username); Dialog.close(); Engine.play("ServerChat"); }; setTimeout(function() { const display = document.getElementById('chatMessageDisplay'); if (display) { display.innerHTML = ''; if (State.variables.chatMeshLogs && Array.isArray(State.variables.chatMeshLogs)) { State.variables.chatMeshLogs.forEach(msg => { window.setup.appendChatMessage(msg); }); } display.scrollTop = display.scrollHeight; } const activeDm = State.variables.activeDirectMessageUser; if (activeDm && State.variables.unreadDmCounts) { State.variables.unreadDmCounts[activeDm] = 0; } window.setup.updateDmSidebar(); }, 50); try { if (typeof io !== 'undefined' && !window.setup.chatSocket) { window.setup.chatSocket = io('https://api.ashenveilgame.com'); } if (window.setup.chatSocket && window.setup.chatBind) { window.setup.chatBind(window.setup.chatSocket); } if (window.setup.chatSocket && window.setup.duel) { window.setup.duel.bind(window.setup.chatSocket); } /* ---------- who is online (list pushed by the server) ---------- */ window.setup.onlineList = window.setup.onlineList || []; window.setup.renderOnline = function () { var box = document.getElementById('sidebarOnlineList'); if (!box) { return; } var me = State.variables.playerName || "Survivor"; var blocked = (State.variables.blockedUsers || []).map(function (x) { return String(x).toLowerCase(); }); var esc = function (t) { return String(t).replace(/[&<>"']/g, function (c) { return {'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]; }); }; var list = (window.setup.onlineList || []).filter(function (n) { return n.toLowerCase() === me.toLowerCase() || blocked.indexOf(n.toLowerCase()) < 0; }); var cnt = document.getElementById('onlineCount'); if (cnt) { cnt.textContent = '(' + list.length + ')'; } if (!list.length) { box.innerHTML = '<div style="color:#64748b; font-size:0.7rem;">Nobody on the mesh.</div>'; return; } box.innerHTML = ''; list.forEach(function (n) { var row = document.createElement('div'); row.className = 'onl-row'; var isMe = n.toLowerCase() === me.toLowerCase(); var nb = document.createElement('button'); nb.className = 'onl-name' + (isMe ? ' onl-me' : ''); nb.innerHTML = '<span style="color:#34d399;">●</span> ' + esc(window.setup.shown(n)) + (isMe ? ' (you)' : ''); if (!isMe) { nb.onclick = function () { window.setup.openUserMenu(n); }; } row.appendChild(nb); if (!isMe && window.setup.duel) { var ds = window.setup.duel.state(n); var db = document.createElement('button'); db.className = 'onl-duel'; if (ds.mode === 'accept') { db.className += ' acc'; db.textContent = '✔'; db.title = 'Accept the duel'; db.onclick = function () { window.setup.duel.accept(ds.id); }; } else if (ds.mode === 'sent') { db.className += ' wait'; db.textContent = '⏳'; db.title = 'Challenge sent'; } else { db.textContent = '⚔'; db.title = 'Challenge to a duel'; db.onclick = function () { window.setup.duel.challenge(n); setTimeout(window.setup.renderOnline, 400); }; } row.appendChild(db); } box.appendChild(row); }); }; if (window.setup.chatSocket) { var _sk = window.setup.chatSocket; if (!_sk.__avOnl) { _sk.__avOnl = true; _sk.on('online_list', function (arr) { var names = []; (Array.isArray(arr) ? arr : []).forEach(function (x) { if (x && typeof x === 'object') { if (x.name) { names.push(x.name); window.setup.noteChar(x.name, x.char); } } else if (typeof x === 'string') { names.push(x); } }); window.setup.onlineList = names; window.setup.renderOnline(); }); } window.setup.renderOnline(); if (_sk.connected) { _sk.emit('who_online'); } else { _sk.once('connect', function () { _sk.emit('who_online'); }); } } if (window.__avOnlTimer) { clearInterval(window.__avOnlTimer); } window.__avOnlTimer = setInterval(function () { if (!document.getElementById('sidebarOnlineList')) { clearInterval(window.__avOnlTimer); window.__avOnlTimer = null; return; } window.setup.renderOnline(); }, 3000); window.setup.chatJoinRooms = function () { if (!window.setup.chatSocket) { return; } const activeDm = State.variables.activeDirectMessageUser; const myName = State.variables.playerName || "Survivor"; let roomToJoin = State.variables.currentChannel; if (activeDm && activeDm !== null) { const sorted = [myName, activeDm].sort(); roomToJoin = "DM:" + sorted[0] + "_" + sorted[1]; } window.setup.chatSocket.emit('join_channel', roomToJoin); window.setup.chatSocket.emit('join_channel', "USER_ROOM_" + myName); }; window.setup.chatJoinRooms(); } catch (err) { console.log("Socket server error:", err); } $(document).off('click', '#chatSendBtn').on('click', '#chatSendBtn', function() { window.setup.sendChatMessage(); }); $(document).off('keydown', '#chatInputBox').on('keydown', '#chatInputBox', function(e) { if (e.key === 'Enter') { window.setup.sendChatMessage(); } }); window.setup.openBroadcastModal = function() { if (!window.setup.hasChar()) { alert("Create your character first. Until then you stay invisible and cannot broadcast."); return; } const isRealVIP = (State.variables.isFounder === true || State.variables.isVIP === true); const passCount = State.variables.globalBroadcastPass || 0; if (!isRealVIP && passCount <= 0) { alert("Error: No Broadcast Passes available!"); return; } const existing = document.getElementById('broadcastActionModal'); if (existing) existing.remove(); const modal = document.createElement('div'); modal.id = 'broadcastActionModal'; modal.className = 'user-action-modal'; const btnText = isRealVIP ? "TRANSMIT (FREE)" : "TRANSMIT (-1 PASS)"; modal.innerHTML = ` <div class="user-action-box" style="width: 450px; border-color: #e879f9; box-shadow: 0 0 25px rgba(232, 121, 249, 0.5);"> <h3 style="color: #e879f9; margin-top: 0; letter-spacing: 2px;">[ GLOBAL BROADCAST UPLINK ]</h3> <div style="background: rgba(232, 121, 249, 0.1); border-left: 3px solid #e879f9; padding: 10px; text-align: left; margin-bottom: 20px;"> <span style="color: #e879f9; font-weight: bold; font-size: 0.9rem;">INFO:</span> <span style="color: #cbd5e1; font-size: 0.85rem;">Broadcasts across all active sectors and channels simultaneously. Each transmission costs 1 Broadcast Pass (Founders and VIPs are unlimited). Earn passes by killing the Reptilian Warlord (2 per kill). Links are blocked.</span> </div> <input type="text" id="modalBroadcastInput" class="cyber-input" placeholder="Type global transmission..." style="width: 100%; box-sizing: border-box; margin-bottom: 20px; font-size: 1rem; padding: 12px; border-color: #e879f9;" autocomplete="off"> <div style="display: flex; gap: 10px;"> <button id="modalBroadcastSendBtn" style="flex: 1; background: #e879f9; color: #000; border: none; padding: 12px; border-radius: 4px; font-weight: bold; cursor: pointer; font-family: 'Courier New';">${btnText}</button> <button id="modalBroadcastCloseBtn" style="flex: 1; background: #334155; color: #fff; border: none; padding: 12px; border-radius: 4px; cursor: pointer; font-family: 'Courier New'; font-weight: bold;">CANCEL</button> </div> </div> `; document.body.appendChild(modal); document.getElementById('modalBroadcastCloseBtn').onclick = function() { modal.remove(); }; document.getElementById('modalBroadcastSendBtn').onclick = function() { const input = document.getElementById('modalBroadcastInput'); if (!input || !input.value.trim()) return; const text = input.value.trim(); const urlPattern = /(https?:\/\/|www\.|[a-zA-Z0-9-]+\.(com|net|org|edu|gov|mil|io|co|me|de|fr|pl|ru|uk|info|biz))/i; if (urlPattern.test(text)) { alert("Transmission blocked: External links and URLs are restricted."); return; } if (!isRealVIP) { if (State.variables.globalBroadcastPass > 0) { State.variables.globalBroadcastPass -= 1; } else { alert("Error: No Broadcast Passes available!"); modal.remove(); return; } } const time = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); const senderName = State.variables.playerName || "Survivor"; const messageData = { channel: "Global", sender: senderName, text: text, time: time, vip: isRealVIP, badge: "GLOBAL BROADCAST" }; State.variables.chatMeshLogs.push(messageData); window.setup.appendChatMessage(messageData); if (window.setup.chatSocket && window.setup.chatSocket.connected) { window.setup.chatSocket.emit('send_message', messageData); } modal.remove(); Engine.play("ServerChat"); }; }; /* ---------- PLAYER CARD: what you see when you click somebody's name ---------- */ const NPC_CARDS = { "Vex_99": "Syndicate Informant. Sells what he knows.", "Rust_Nomad": "Wasteland Nomad. Knows every road.", "KiloByte": "Mesh Operator. Hears everything on the line.", "Silesia-Admin": "Sector Administration.", "SYSTEM": "Automated system." }; function pcEsc(s) { return String(s === undefined || s === null ? "" : s).replace(/&/g, "&").replace(/</g, "<").replace(/"/g, """); } function pcAvatarUrl(pr) { if (pr.exo) { return "images/avatars/exo_armor.jpg"; } let g = String(pr.gender || "male").toLowerCase(), c = String(pr.background || "scavenger").toLowerCase(); if (c === "field medic") { c = "medic"; } else if (c === "mutated anomaly") { c = "mutated"; } c = c.replace(/ /g, "_"); const tier = pr.armorTier && pr.armorTier !== "Common" ? ("_" + String(pr.armorTier).toLowerCase() + "_armor") : "_start_armor"; return "images/avatars/" + g + "_" + c + tier + ".jpg"; } function pcFallbackUrl(pr) { let g = String(pr.gender || "male").toLowerCase(), c = String(pr.background || "scavenger").toLowerCase(); if (c === "field medic") { c = "medic"; } else if (c === "mutated anomaly") { c = "mutated"; } return "images/avatars/" + g + "_" + c.replace(/ /g, "_") + "_start_armor.jpg"; } function pcAgo(iso) { if (!iso) { return "unknown"; } const m = Math.max(0, Math.round((Date.now() - new Date(iso).getTime()) / 60000)); if (m < 3) { return "online now"; } if (m < 60) { return m + " min ago"; } if (m < 1440) { return Math.round(m / 60) + " h ago"; } return Math.round(m / 1440) + " d ago"; } function pcCardHtml(username, pr) { const exo = !!pr.exo, img = pcAvatarUrl(pr), fb = pcFallbackUrl(pr); const cls = pr.playerClass || pr.background || "Unknown"; let h = '<img class="pc-avatar' + (exo ? ' pc-avatar-exo' : '') + '" src="' + pcEsc(img) + '" alt="avatar" data-fb="' + pcEsc(fb) + '" onerror="if(this.getAttribute(\'data-fb\') && this.src.indexOf(\'data-done\')<0){var f=this.getAttribute(\'data-fb\'); this.removeAttribute(\'data-fb\'); this.src=f; return;} this.onerror=null; this.src=\'https://placehold.co/300x400/0b0f19/60a5fa?text=OPERATOR\';">'; h += '<div style="margin-bottom:8px;">'; if (exo) { h += '<span class="pc-chip pc-exo-tag">\uD83D\uDCA0 MATTE-OBSIDIAN EXOFRAME</span>'; } if (pr.founder) { h += '<span class="pc-chip pc-founder">\u2605 FOUNDER</span>'; } if (pr.vip) { h += '<span class="pc-chip pc-vip">VIP</span>'; } h += '</div>'; h += '<div class="pc-row"><span>CLASS</span><span>' + pcEsc(cls) + '</span></div>'; h += '<div class="pc-row"><span>LEVEL</span><span style="color:#fde047;">' + pcEsc(pr.level) + '</span></div>'; if (pr.title) { h += '<div class="pc-row"><span>TITLE</span><span style="color:#a3e635;">' + pcEsc(pr.title) + '</span></div>'; } h += '<div class="pc-row"><span>DAYS SURVIVED</span><span>' + pcEsc(pr.day) + '</span></div>'; h += '<div class="pc-row"><span>LAST SEEN</span><span>' + pcEsc(pcAgo(pr.lastSaved)) + '</span></div>'; return h; } window.setup.openUserMenu = function(username) { const myName = State.variables.playerName || "Survivor"; if (username === myName) return; const existing = document.getElementById('userActionModal'); if (existing) existing.remove(); const modal = document.createElement('div'); modal.id = 'userActionModal'; modal.className = 'user-action-modal'; const isBlocked = State.variables.blockedUsers.includes(username); const blockBtnText = isBlocked ? "UNBLOCK USER" : "BLOCK USER"; const blockColor = isBlocked ? "#10b981" : "#ef4444"; const isNpc = Object.prototype.hasOwnProperty.call(NPC_CARDS, username); modal.innerHTML = ` <div class="user-action-box"> <h3 style="color: #60a5fa; margin-top: 0; margin-bottom: 12px;">OPERATOR: <span id="pcHeadName">${pcEsc(window.setup.shown(username))}</span></h3> <div id="pcCard" style="margin-bottom: 14px; min-height: 60px;"> <div style="color:#64748b; font-size:0.8rem; padding:18px 0;">${isNpc ? "" : "Scanning operator record..."}</div> </div> ${isNpc ? "" : '<button id="modalDuelBtn" style="width: 100%; background: #0f172a; color: #e0f2fe; border: 1px solid #38bdf8; padding: 10px; border-radius: 4px; font-weight: bold; cursor: pointer; margin-bottom: 10px; font-family: \'Courier New\';">\u2694 CHALLENGE TO A DUEL</button>'} ${isNpc ? "" : '<button id="modalHdBtn" style="width: 100%; background: #064e3b; color: #d1fae5; border: 1px solid #34d399; padding: 10px; border-radius: 4px; font-weight: bold; cursor: pointer; margin-bottom: 10px; font-family: \'Courier New\';">\uD83D\uDCBB HACK DUEL (STEAL SCRIP)</button>'} ${isNpc ? "" : '<button id="modalBtBtn" style="width: 100%; background: #450a0a; color: #fee2e2; border: 1px solid #f87171; padding: 10px; border-radius: 4px; font-weight: bold; cursor: pointer; margin-bottom: 10px; font-family: \'Courier New\';">\u2694 LIVE BATTLE (WAGER)</button>'} ${isNpc ? "" : '<div style="display:flex; gap:6px; margin-bottom:10px;"><select id="modalBjBet" style="flex:0 0 96px; background:#0f172a; color:#e2e8f0; border:1px solid #475569; border-radius:4px; padding:8px; font-family:\'Courier New\';"><option value="10">10</option><option value="25">25</option><option value="50" selected>50</option><option value="100">100</option><option value="250">250</option><option value="500">500</option></select><button id="modalBjBtn" style="flex:1; background:#064e3b; color:#d1fae5; border:1px solid #34d399; padding:10px; border-radius:4px; font-weight:bold; cursor:pointer; font-family:\'Courier New\';">\uD83C\uDCCF CHALLENGE TO BLACKJACK (SCRIP)</button></div>'} <button id="modalDmBtn" style="width: 100%; background: #2563eb; color: #fff; border: none; padding: 10px; border-radius: 4px; font-weight: bold; cursor: pointer; margin-bottom: 10px; font-family: 'Courier New';">DIRECT MESSAGE (DM)</button> <button id="modalBlockBtn" style="width: 100%; background: ${blockColor}; color: #fff; border: none; padding: 10px; border-radius: 4px; font-weight: bold; cursor: pointer; margin-bottom: 10px; font-family: 'Courier New';">${blockBtnText}</button> <button id="modalCloseBtn" style="width: 100%; background: #334155; color: #fff; border: none; padding: 8px; border-radius: 4px; cursor: pointer; font-family: 'Courier New';">CANCEL</button> </div> `; document.body.appendChild(modal); const card = document.getElementById('pcCard'); if (isNpc) { card.innerHTML = '<div style="color:#94a3b8; font-size:0.85rem; padding:10px 0; font-style:italic;">' + pcEsc(NPC_CARDS[username]) + '</div>'; } else { fetch(window.API_URL + "/api/player_profile", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: username }) }) .then(function (r) { if (r.status === 404) { throw new Error('missing'); } return r.json(); }) .then(function (d) { if (!document.getElementById('pcCard')) { return; } if (d.status === "success" && d.profile) { window.setup.noteChar(username, d.profile.character); var hn = document.getElementById('pcHeadName'); if (hn) { hn.textContent = window.setup.shown(username); } card.innerHTML = pcCardHtml(username, d.profile); } else if (d.status === "not_found") { card.innerHTML = '<div style="color:#64748b; font-size:0.8rem; padding:14px 0;">No operator record on file.</div>'; } else { card.innerHTML = '<div style="color:#64748b; font-size:0.8rem; padding:14px 0;">' + pcEsc(d.message || "Record unavailable.") + '</div>'; } }) .catch(function () { if (document.getElementById('pcCard')) { card.innerHTML = '<div style="color:#64748b; font-size:0.8rem; padding:14px 0;">Record unavailable. The game server may be offline or needs an update.</div>'; } }); } const duelBtn = document.getElementById('modalDuelBtn'); if (duelBtn && window.setup.duel) { const ds = window.setup.duel.state(username); if (ds.mode === "accept") { duelBtn.textContent = "\u2714 ACCEPT DUEL"; duelBtn.style.background = "#064e3b"; duelBtn.style.color = "#d1fae5"; duelBtn.style.borderColor = "#34d399"; duelBtn.onclick = function () { modal.remove(); window.setup.duel.accept(ds.id); }; } else if (ds.mode === "sent") { duelBtn.textContent = "\u23F3 CHALLENGE SENT, WAITING..."; duelBtn.disabled = true; duelBtn.style.opacity = "0.7"; duelBtn.style.cursor = "default"; } else { duelBtn.onclick = function () { modal.remove(); window.setup.duel.challenge(username); }; } } else if (duelBtn) { duelBtn.style.display = "none"; } const hdBtn = document.getElementById('modalHdBtn'); if (hdBtn) { if (window.setup.hd) { hdBtn.onclick = function () { modal.remove(); window.setup.hd.challengeDialog(username); }; } else { hdBtn.style.display = "none"; } } const btBtn = document.getElementById('modalBtBtn'); if (btBtn) { if (window.setup.bt) { btBtn.onclick = function () { modal.remove(); window.setup.bt.challengeDialog(username); }; } else { btBtn.style.display = "none"; } } const bjBtn = document.getElementById('modalBjBtn'); if (bjBtn && window.setup.bj) { const bs = window.setup.bj.state(username), bsel = document.getElementById('modalBjBet'); if (bs.mode === "accept") { bjBtn.textContent = "\u2714 ACCEPT BLACKJACK (" + bs.bet + " SCRIP)"; if (bsel) { bsel.style.display = "none"; } bjBtn.onclick = function () { modal.remove(); window.setup.bj.accept(bs.id); }; } else if (bs.mode === "sent") { bjBtn.textContent = "\u23F3 BLACKJACK CHALLENGE SENT..."; bjBtn.disabled = true; bjBtn.style.opacity = "0.7"; bjBtn.style.cursor = "default"; } else { bjBtn.onclick = function () { const bet = bsel ? bsel.value : 50; modal.remove(); window.setup.bj.challenge(username, bet); }; } } else if (bjBtn) { bjBtn.style.display = "none"; } document.getElementById('modalDmBtn').onclick = function() { State.variables.activeDirectMessageUser = username; if (State.variables.unreadDmCounts) { State.variables.unreadDmCounts[username] = 0; } modal.remove(); Engine.play("ServerChat"); }; document.getElementById('modalBlockBtn').onclick = function() { if (isBlocked) { State.variables.blockedUsers = State.variables.blockedUsers.filter(u => u !== username); } else { State.variables.blockedUsers.push(username); } modal.remove(); Engine.play("ServerChat"); }; document.getElementById('modalCloseBtn').onclick = function() { modal.remove(); }; modal.addEventListener('click', function (ev) { if (ev.target === modal) { modal.remove(); } }); }; window.setup.sendChatMessage = function() { const input = document.getElementById('chatInputBox'); if (!input || !input.value.trim()) return; if (!window.setup.hasChar()) { window.setup.appendChatMessage({ channel: State.variables.activeDirectMessageUser ? ("DM:" + State.variables.activeDirectMessageUser) : State.variables.currentChannel, sender: "SYSTEM", text: "Create your character first. Until then you stay invisible and cannot send messages.", time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), vip: true, badge: "SECURITY" }); return; } const text = input.value.trim(); const urlPattern = /(https?:\/\/|www\.|[a-zA-Z0-9-]+\.(com|net|org|edu|gov|mil|io|co|me|de|fr|pl|ru|uk|info|biz))/i; if (urlPattern.test(text)) { const warningMsg = { channel: State.variables.activeDirectMessageUser ? ("DM:" + State.variables.activeDirectMessageUser) : State.variables.currentChannel, sender: "SYSTEM", text: "Transmission blocked: External links and URLs are restricted on this frequency.", time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), vip: true, badge: "SECURITY" }; window.setup.appendChatMessage(warningMsg); input.value = ''; return; } const time = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); const activeDm = State.variables.activeDirectMessageUser; const myName = State.variables.playerName || "Survivor"; let currentCh = State.variables.currentChannel; if (activeDm && activeDm !== null) { const sorted = [myName, activeDm].sort(); currentCh = "DM:" + sorted[0] + "_" + sorted[1]; } const isRealVIP = (State.variables.isFounder === true || State.variables.isVIP === true); let badgeText = ""; if (isRealVIP) { badgeText = (State.variables.isFounder === true) ? "FOUNDER" : "VIP"; } const messageData = { channel: currentCh, sender: myName, text: text, time: time, vip: isRealVIP, badge: badgeText }; State.variables.chatMeshLogs.push(messageData); window.setup.appendChatMessage(messageData); window.setup.updateDmSidebar(); input.value = ''; if (window.setup.chatSocket && window.setup.chatSocket.connected) { window.setup.chatSocket.emit('send_message', messageData); if (activeDm && activeDm !== null) { const dmPing = Object.assign({}, messageData); dmPing.channel = "USER_ROOM_" + activeDm; dmPing.isDmNotification = true; dmPing.originalChannel = currentCh; window.setup.chatSocket.emit('send_message', dmPing); } } if (!activeDm || activeDm === null) { setTimeout(() => { const brain = (window.setup && window.setup.chatReply) || (typeof setup !== 'undefined' ? setup.chatReply : null); const replies = brain ? brain(text) : [{ name: 'KiloByte', text: 'Mesh relay is warming up. Ask me again in a moment.' }]; replies.forEach(function (r, idx) { setTimeout(function () { const npcMsg = { channel: currentCh, sender: r.name, text: r.text, time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), vip: false }; State.variables.chatMeshLogs.push(npcMsg); window.setup.appendChatMessage(npcMsg); if (window.setup.chatSocket && window.setup.chatSocket.connected) { window.setup.chatSocket.emit('send_message', npcMsg); } }, idx * 1800); }); }, 1500); } }; window.setup.appendChatMessage = function(msg) { const display = document.getElementById('chatMessageDisplay'); if (!display || !msg || !msg.sender || !msg.channel) return; window.setup.noteChar(msg.sender, msg.char); const blocked = State.variables.blockedUsers || []; if (blocked.includes(msg.sender)) { return; } const activeDm = State.variables.activeDirectMessageUser; const myName = State.variables.playerName || "Survivor"; if (activeDm && activeDm !== null) { const sorted = [myName, activeDm].sort(); const expectedDmChannel = "DM:" + sorted[0] + "_" + sorted[1]; if (msg.channel !== expectedDmChannel) { return; } } else { if (msg.channel !== State.variables.currentChannel && msg.channel !== "Global") { return; } } const line = document.createElement('div'); line.className = 'chat-line ' + (msg.vip ? 'chat-line-vip' : ''); let badgeHtml = ''; if (msg.badge) { if (msg.vip) { badgeHtml = `<span class="vip-badge">${msg.badge}</span>`; } else { badgeHtml = `<span class="global-badge">${msg.badge}</span>`; } } let userClass = msg.vip ? 'chat-user-vip' : 'chat-user'; line.innerHTML = ` <div> <span class="${userClass}" data-user="${window.setup.escHtml(msg.sender)}" onclick="window.setup.openUserMenu(this.getAttribute('data-user'))">${window.setup.escHtml(window.setup.shown(msg.sender))}</span>${badgeHtml}<span class="chat-colon">:</span> <span class="chat-text">${msg.text}</span> </div> <span class="chat-time">[${msg.time}]</span> `; if (msg.duelId && /^[0-9a-f]{16}$/.test(String(msg.duelId))) { line.firstElementChild.insertAdjacentHTML('beforeend', ' <button class="duel-link-btn" onclick="window.setup.duel.view(\'' + msg.duelId + '\')">VIEW BATTLE</button>'); } if (msg.battleId && /^[0-9a-f]{16}$/.test(String(msg.battleId))) { line.firstElementChild.insertAdjacentHTML('beforeend', ' <button class="duel-link-btn" onclick="window.setup.bt.watch(\'' + msg.battleId + '\')">▶ WATCH THE FIGHT</button>'); } if (msg.duelInvite && /^[0-9a-f]{12}$/.test(String(msg.duelInvite))) { line.firstElementChild.insertAdjacentHTML('beforeend', ' <span data-duel-invite="' + msg.duelInvite + '"><button class="duel-yes" onclick="window.setup.duel.accept(\'' + msg.duelInvite + '\')">ACCEPT DUEL</button><button class="duel-no" onclick="window.setup.duel.decline(\'' + msg.duelInvite + '\')">DECLINE</button></span>'); } display.appendChild(line); display.scrollTop = display.scrollHeight; }; <</script>>
<<nobr>> <div style="background: #02050a; border: 2px solid #38bdf8; padding: 30px; font-family: 'Courier New', monospace; color: #cbd5e1; box-shadow: inset 0 0 50px rgba(56, 189, 248, 0.1);"> <div style="text-align: center; border-bottom: 2px dashed #38bdf8; padding-bottom: 15px; margin-bottom: 20px;"> <h2 style="color: #38bdf8; letter-spacing: 3px; margin: 0;">[ LOWER DEPTHS: THE HOLDING CELLS ]</h2> </div> <!-- BASEMENT IMAGE (Cages and darkness) --> <div style="text-align:center; margin-bottom: 25px;"> <img src="images/quests/basement_cages.jpg" style="max-width: 100%; border: 2px solid #38bdf8; border-radius: 4px; box-shadow: 0 0 25px rgba(56, 189, 248, 0.3);"> </div> <p style="font-size: 1.15rem; line-height: 1.8; text-align: justify;"> Kicking down the rusted iron door, you step into a pitch-black dungeon. The air reeks of blood, dampness, and despair. The sharp beam of your tactical flashlight and the smoke from your barrel violently pierce this blood-soaked darkness. </p> <p style="font-size: 1.15rem; line-height: 1.8; text-align: justify;"> There are nearly 50 people waiting inside the cages. All of them are traumatized, half-mad. Looking at your glowing silhouette emerging from the smoke, an old Polish miner covered in coal dust and dried blood drops to his knees. Gripping the iron bars, he screams with awe: </p> <div style="text-align: center; font-size: 1.5rem; font-weight: bold; color: #fde047; margin: 30px 0; text-shadow: 0 0 15px rgba(253, 224, 71, 0.6); font-style: italic;"> "Svyatogor has returned! Svyatogor! Świetlok nadszedł!" <br> <span style="font-size: 1rem; color: #94a3b8;">(Firefly... The Light has come.)</span> </div> <span id="lore-section"> <div style="text-align: center; margin-top: 40px;"> @@color:#38bdf8;font-weight:bold;border:1px solid #38bdf8;padding:10px 20px;display:inline-block; <<link "[ DIALOGUE ] Svyatogor? What the hell are you talking about, old man?">> <<replace "#lore-section">> <!-- PLAY THE VOICEOVER --> <<audio "miner_voice" play>> <!-- THE OLD MAN'S PORTRAIT AND DIALOGUE SIDE BY SIDE --> <div style="display: flex; gap: 20px; align-items: flex-start; padding-top: 20px; border-top: 1px dashed #334155; margin-top: 20px;"> <img src="images/quests/old_miner.jpg" style="width: 140px; height: 140px; object-fit: cover; border: 2px solid #38bdf8; border-radius: 4px; flex-shrink: 0; box-shadow: 0 0 15px rgba(56,189,248,0.4);"> <div> <p style="font-size: 1.1rem; line-height: 1.6; color: #cbd5e1; font-style: italic; margin-top: 0;"> "I haven't lost my mind..." the old man wheezes, tears cutting tracks through the grime on his face. "Our grandfathers used to tell tales in the old Silesian mines. Long before the world drowned in poison... Before the lizards took the surface! A colossal hero slumbered deep underground. Svyatogor!" </p> <p style="font-size: 1.1rem; line-height: 1.6; color: #cbd5e1; font-style: italic;"> He reaches his trembling hand out of the cage. "The legend said that when mankind loses all hope in cages... he will awaken. He will bring the light that tears through the dark. The Świetlok... The Firefly!" </p> <p style="font-size: 1.1rem; line-height: 1.6; color: #38bdf8; font-style: italic; font-weight: bold;"> "You are standing right here... with your blinding light cutting through the smoke. You are no mercenary. You are Świetlok! Please... lead us out of this reptilian hell. Take us back to the sun!" </p> </div> </div> <div style="background: rgba(255, 255, 255, 0.05); border-left: 4px solid #94a3b8; padding: 15px; margin: 30px 0;"> <strong style="color: #94a3b8; font-family: 'Courier New', monospace;">>> SYSTEM ANALYSIS:</strong><br> <span style="color: #cbd5e1; font-size: 0.95rem;">These people haven't gone mad; they are clinging to an ancient myth. If you sell them to Grom now, you will be extinguishing the last spark of hope in the Wasteland.</span> </div> <div style="display: flex; gap: 20px; justify-content: center; margin-top: 40px; flex-wrap: wrap;"> <div style="flex: 1; min-width: 250px; background: rgba(56, 189, 248, 0.05); border: 1px solid #38bdf8; padding: 20px; text-align: center; border-radius: 4px;"> <h3 style="color: #38bdf8; margin-top: 0;">[ THE FIREFLY ]</h3> <p style="font-size: 0.85rem; color: #94a3b8;">Break the cages and lead them all to Sanctuary.</p> <p style="font-size: 0.8rem; color: #a3e635; font-weight: bold;">REWARD: The "Svietlok" title. A permanent +25% BONUS to daily productions.</p> <br> @@color:#050a0f;background:#38bdf8;font-weight:bold;padding:10px 15px;display:block;border-radius:2px; <<link "BREAK THE CAGES (BECOME SVIETLOK)" "Svietlok_Ending">><</link>>@@ </div> <div style="flex: 1; min-width: 250px; background: rgba(239, 68, 68, 0.05); border: 1px solid #ef4444; padding: 20px; text-align: center; border-radius: 4px;"> <h3 style="color: #ef4444; margin-top: 0;">[ THE MERCENARY ]</h3> <p style="font-size: 0.85rem; color: #94a3b8;">Radio the coordinates to Grom. Business is business.</p> <p style="font-size: 0.8rem; color: #fbbf24; font-weight: bold;">REWARD: +5000 Scrip. The legend dies here.</p> <br> @@color:#050a0f;background:#ef4444;font-weight:bold;padding:10px 15px;display:block;border-radius:2px; <<link "CALL GROM (SELL TO MERCENARY)" "Mercenary_Ending">><</link>>@@ </div> </div> <</replace>> <</link>>@@ </div> </span> </div> <</nobr>>
<<nobr>> <<setMenu "Quest">> <<if ndef $sh_terminal_read>><<set $sh_terminal_read to false>><</if>> <<if ndef $sh_locker_looted>><<set $sh_locker_looted to false>><</if>> <div style="background: linear-gradient(rgba(10, 0, 0, 0.8), rgba(20, 0, 0, 0.95)); border: 2px solid #ef4444; padding: 30px; font-family: 'Courier New', monospace; color: #cbd5e1; box-shadow: inset 0 0 50px rgba(239, 68, 68, 0.2);"> <div style="text-align: center; border-bottom: 2px dashed #ef4444; padding-bottom: 15px; margin-bottom: 20px;"> <h1 style="color: #ef4444; letter-spacing: 4px; text-shadow: 0 0 10px #ef4444; margin: 0;">[ SECTOR 4: THE SLAUGHTERHOUSE ]</h1> <div style="color: #fca5a5; font-size: 0.9rem; margin-top: 10px;">ABANDONED INDUSTRIAL COMPLEX - KATOWICE ZONE</div> </div> <!-- IMAGE HERE --> <div style="text-align:center; margin-bottom: 25px;"> <img src="images/quests/slaughterhouse_ext.jpg" style="max-width: 100%; border: 2px solid #ef4444; border-radius: 4px; box-shadow: 0 0 25px rgba(239, 68, 68, 0.4);"> </div> <p style="font-size: 1.1rem; line-height: 1.6;"> The rumors were true. The Reptilians have turned this old industrial complex into a massive slave camp and a literal slaughterhouse. You've cleared the final wave. Stepping over the massive reptilian corpses, you secure the main processing hall. The smell of ammonia and burnt plasma is suffocating. </p> <p style="font-size: 1.1rem; line-height: 1.6;"> Muffled moans and cries echo from below, from the basement. Dozens of human voices. Before you turn on your tactical flashlight and descend, you take a moment to search the immediate area. </p> <span id="sh-interactions"> <div style="display: flex; flex-direction: column; gap: 15px; margin: 30px 0;"> <!-- TERMINAL INTERACTION --> <<if not $sh_terminal_read>> <<if setup.hackCanStart()>> @@color:#38bdf8;font-weight:bold;border:1px solid #38bdf8;padding:12px 20px;text-align:center;display:block;background:rgba(56,189,248,0.1); <<link "[ HACK THE REPTILIAN TERMINAL (LIVE BREACH, COSTS 1 TECH DATA) ]" "HackGame">><<run setup.hackBegin({ ctx: "shterm", game: "rand" })>><</link>>@@ <<else>> <div style="color:#fca5a5; background:rgba(69,10,10,0.55); border:1px dashed #ef4444; padding:10px 14px; text-align:center; font-weight:bold; font-size:0.8rem; font-family:'Courier New',monospace;">[ LOCKED: A BREACH COSTS 1 TECH DATA, AND YOU HAVE NONE ]</div> <</if>> <<else>> <div style="background: rgba(56, 189, 248, 0.05); border-left: 4px solid #38bdf8; padding: 20px; margin-bottom: 20px;"> <strong style="color: #38bdf8; font-size: 1.1rem;">>> DECRYPTED LOG_01 (Svyatogor File):</strong><br> <em style="color: #94a3b8; line-height: 1.6;">"The livestock keeps whispering about 'Svyatogor' or 'Firefly'. This myth is dropping their working efficiency by 12%. Anyone heard spreading this fairy tale is to be sent to the meat grinders immediately."</em> <br><br> <strong style="color: #38bdf8; font-size: 1.1rem;">>> DECRYPTED LOG_02 (Grom Protocol):</strong><br> <em style="color: #94a3b8; line-height: 1.6;">"Our human contact, 'Grom', continues to supply us with their broken and useless kind. The trade is profitable, and the man is useful. However, keep the automated drones on standby. The moment he steps out of line or asks too many questions, eliminate him."</em> </div> <</if>> <!-- LOOT LOCKERS INTERACTION --> <<if not $sh_locker_looted>> @@color:#a3e635;font-weight:bold;border:1px solid #a3e635;padding:12px 20px;text-align:center;display:block;background:rgba(163,230,53,0.1); <<link "[ SEARCH THE BUTCHER'S LOCKERS ]">> <<set $sh_locker_looted to true>> <<set $materials.ammo += 6>> <<set $materials.scrap += 3>> <<set $materials.techData += 1>> <<replace "#sh-interactions">> <div style="background: rgba(163, 230, 53, 0.05); border-left: 4px solid #a3e635; padding: 20px; margin-bottom: 20px; text-align: center;"> <strong style="color: #a3e635;">>> LOOT FOUND:</strong><br> You pry open the bloody lockers and salvage whatever you can use.<br> <span style="color: #fbbf24;">+6 Ammo, +3 Scrap, +1 Tech Data added to inventory.</span> </div> <div style="text-align: center;"> @@color:#a3e635;font-weight:bold;border:1px solid #a3e635;padding:10px 20px;display:inline-block;<<link "RETURN TO MAIN HALL" "Slaughterhouse_Floor1">><</link>>@@ </div> <</replace>> <</link>>@@ <</if>> <!-- DESCEND TO BASEMENT --> @@color:#ef4444;font-weight:bold;border:1px solid #ef4444;padding:15px 20px;text-align:center;display:block;background:rgba(239,68,68,0.15); <<link "DESCEND TO THE BASEMENT ⬇️" "Slaughterhouse_Basement">><</link>>@@ </div> </span> </div> <</nobr>>
<<nobr>> <style> body { background-color: #020502; margin: 0; padding: 0; } .boot-container { display: flex; flex-direction: column; justify-content: center; align-items: center; width: 100vw; height: 100vh; background: #020502; color: #00ff00; font-family: 'Courier New', monospace; text-shadow: 0 0 10px #00ff00; position: fixed; top: 0; left: 0; z-index: 10; } .glitch-anim { animation: glitch-anim-heavy 0.15s infinite; } @keyframes glitch-anim-heavy { 0% { transform: translate(0) skew(0deg); filter: hue-rotate(0deg); } 20% { transform: translate(-5px, 5px) skew(-10deg); filter: hue-rotate(90deg); } 40% { transform: translate(-5px, -5px) skew(10deg); filter: hue-rotate(0deg); } 60% { transform: translate(5px, 5px) skew(-5deg); filter: hue-rotate(180deg); } 80% { transform: translate(5px, -5px) skew(5deg); filter: hue-rotate(0deg); } 100% { transform: translate(0) skew(0deg); filter: hue-rotate(0deg); } } .ashen-veil-world { background: linear-gradient(rgba(0, 5, 0, 0.8), rgba(0, 10, 5, 0.95)), url('images/locations/The_Outskirts.jpg') center/cover no-repeat; min-height: 100vh; display: flex; flex-direction: row; justify-content: space-between; align-items: stretch; width: 100%; margin: 0; padding: 0; animation: fadeInBlur 0.5s ease-in forwards; } @keyframes fadeInBlur { 0% { opacity: 0; filter: blur(10px) brightness(2); } 100% { opacity: 1; filter: blur(0) brightness(1); } } .terminal-wide-core { flex: 1; background: linear-gradient(rgba(5, 15, 5, 0.88), rgba(5, 15, 5, 0.96)), url('images/locations/sanctuary_level_1.jpg') center/cover no-repeat; border-left: 2px solid #00ff00; border-right: 2px solid #00ff00; padding: 50px 80px; font-family: 'Courier New', monospace; box-shadow: 0 0 50px rgba(0, 255, 0, 0.15), inset 0 0 30px rgba(0, 255, 0, 0.05); display: flex; flex-direction: column; justify-content: space-between; box-sizing: border-box; overflow-y: auto; } .login-btn-container a { display: block; width: 100%; background: rgba(0, 255, 0, 0.1); border: 2px solid #00ff00; color: #00ff00 !important; padding: 16px; font-size: 1.2rem; font-weight: bold; letter-spacing: 3px; text-decoration: none !important; border-radius: 4px; box-shadow: 0 0 15px rgba(0, 255, 0, 0.3); transition: all 0.3s ease; text-align: center; box-sizing: border-box; margin-bottom: 10px; cursor: pointer; } .login-btn-container a:hover { background: #00ff00; color: #000 !important; box-shadow: 0 0 35px rgba(0, 255, 0, 0.8); transform: scale(1.01); } .btn-register { background: rgba(56, 189, 248, 0.1) !important; border: 2px solid #38bdf8 !important; color: #38bdf8 !important; } .btn-register:hover { background: #38bdf8 !important; color: #000 !important; } .terminal-input input[type="text"], .terminal-input input[type="password"] { background: rgba(0,0,0,0.8); border: 1px solid #00ff00; color: #00ff00; padding: 14px; font-family: 'Courier New', monospace; font-size: 1.3rem; text-align: center; width: 100%; max-width: 500px; letter-spacing: 2px; box-sizing: border-box; box-shadow: inset 0 0 10px rgba(0,255,0,0.2); } .terminal-input input[type="text"]:focus, .terminal-input input[type="password"]:focus { outline: none; box-shadow: 0 0 25px rgba(0, 255, 0, 0.6); background: #000; } .glitch-text { position: relative; display: inline-block; color: #00ff00; animation: glitch-skew 1s infinite linear alternate-reverse; } </style> <span id="screen-container"> <div id="boot-sequence" class="boot-container"> <h1 style="font-size: 2.5rem; letter-spacing: 5px; border-bottom: 2px solid #00ff00; padding-bottom: 10px; margin-bottom: 20px;">[ SYSTEM REBOOT ]</h1> <div id="boot-logs" style="text-align: left; width: 400px; font-size: 1.2rem; line-height: 1.8;"></div> </div> </span> <<timed 0.6s>> <<append "#boot-logs">><div style="margin-bottom:8px;">> COLD BOOT INITIATED...</div><</append>> <<next 0.5s>> <<append "#boot-logs">><div style="margin-bottom:8px;">> MEMORY ALLOCATION: OK</div><</append>> <<next 0.5s>> <<append "#boot-logs">><div style="margin-bottom:8px;">> NEURAL LINK: STANDBY</div><</append>> <<next 0.6s>> <<append "#boot-logs">><div style="margin-bottom:8px;">> BYPASSING SECURITY...</div><</append>> <<next 0.5s>> <<append "#boot-logs">><div style="margin-bottom:8px; color:#ef4444; text-shadow: 0 0 10px #ef4444;">> CONNECTION ESTABLISHED.</div><</append>> <<next 0.4s>> <<addclass "#boot-sequence" "glitch-anim">> <<next 0.2s>> <<replace "#screen-container">> <div class="ashen-veil-world"> <div class="terminal-wide-core"> <div> <div style="text-align: center; border-bottom: 1px dashed rgba(0, 255, 0, 0.5); padding-bottom: 20px; margin-bottom: 30px;"> <h2 class="glitch-text" data-text="[ SILESIA 2045 OS v4.02 ]" style="margin: 0; letter-spacing: 8px; text-shadow: 0 0 12px #00ff00; font-size: 2.5rem;">[ SILESIA 2045 OS v4.02 ]</h2> <div style="margin-top: 15px; font-size: 1rem; color: #888; letter-spacing: 3px;">SECURE MILITARY-GRADE TERMINAL INTERFACE</div> </div> <div style="color: #ccc; font-size: 1.1rem; line-height: 1.8; margin-bottom: 30px; padding-left: 15px; border-left: 3px solid #00ff00;"> <p style="color: #00ff00; margin: 6px 0; text-shadow: 0 0 5px rgba(0,255,0,0.5);">> INITIALIZING SYSTEM KERNEL...</p> <p style="color: #00ff00; margin: 6px 0; text-shadow: 0 0 5px rgba(0,255,0,0.5);">> BIOMETRIC SENSORS: ONLINE</p> <p style="color: #38bdf8; margin: 6px 0; text-shadow: 0 0 5px rgba(56,189,248,0.5);">> WARNING: SECTOR ATMOSPHERE TOXIC.</p> </div> </div> <div style="background: rgba(0,255,0,0.02); border: 1px solid rgba(0,255,0,0.3); padding: 30px 40px; border-radius: 4px; text-align: center; margin: 15px 0; box-shadow: inset 0 0 20px rgba(0,255,0,0.05);"> <div style="color: #00ff00; font-weight: bold; margin-bottom: 10px; font-size: 1.2rem; letter-spacing: 3px;">> ENTER OPERATOR ID:</div> <div class="terminal-input" style="margin-bottom: 20px;"> <input type="text" id="gameUsername" autocomplete="off"> </div> <div style="color: #00ff00; font-weight: bold; margin-bottom: 10px; font-size: 1.2rem; letter-spacing: 3px;">> PASSWORD:</div> <div class="terminal-input" style="margin-bottom: 20px;"> <input type="password" id="gamePassword" onkeypress="if(event.key === 'Enter') window.attemptLogin()"> </div> <div id="login-error" style="color: #ef4444; font-weight: bold; font-size: 0.9rem; margin-bottom: 20px; min-height: 20px; letter-spacing: 1px;"></div> <div style="color:#64748b; font-size:0.7rem; margin-bottom:14px; letter-spacing:1px; line-height:1.5;">NEW ID: 3-20 letters, numbers, _ . or -. PASSWORD: at least 6 characters. ACCOUNT RESPONSIBILITY LIES WITH THE USER. A lost password cannot be recovered, but you can change it from your Stats screen.</div> <div class="login-btn-container"> <a href="javascript:void(0);" id="btn-authenticate" onclick="window.attemptLogin()">AUTHENTICATE (LOGIN)</a> <a href="javascript:void(0);" id="btn-register" class="btn-register" onclick="window.attemptRegister()">INITIALIZE NEW ID (REGISTER)</a> </div> </div> </div> </div> <</replace>> <</timed>> <</nobr>>
<<nobr>> <<if ndef $materials>><<set$materials to { scrap: 0, wood: 0, cloth: 0, iron: 0, medicine: 0, food: 0, rawMeat: 0, berries: 0, water: 0, ammo: 0, rope: 0, scrip: 0 }>><<endif>> <<if ndef $equipment>><<set$equipment to { headgear: 'None', chest: 'None', legs: 'None', boots: 'None', gloves: 'None', rightHand: 'None', offHand: 'None', leftHand: 'None', trinket1: 'None', trinket2: 'None', backpack: 'None', ring1: 'None', ring2: 'None' }>><<endif>> <<if ndef $level>><<set$level to 1>><<endif>> <<if ndef $xp>><<set$xp to 0>><<endif>> <<if ndef $xpMax>><<set$xpMax to 100>><<endif>> <<if ndef $hasSanctuary>><<set$hasSanctuary to false>><<endif>> <<if ndef $thirst>><<set$thirst to 100>><<endif>> /* DEV TOOLS (GOD MODE) ARE VISIBLE ONLY FOR THE NAKMAN ACCOUNT */ <<if def $playerName and String($playerName).toLowerCase() eq "nakman">> <div style="margin-bottom: 20px; border: 2px dashed #ef4444; background: rgba(239, 68, 68, 0.15); padding: 15px; text-align: center; border-radius: 6px;"> <div style="color: #ef4444; font-weight: 900; font-size: 0.9rem; margin-bottom: 10px; letter-spacing: 2px;">[ DEV TOOLS ]</div> <div style="margin-bottom: 10px; padding: 8px; border: 1px solid #a78bfa; background: rgba(76,29,149,0.4); border-radius: 4px;"> <<link '<span style="color: #ede9fe; font-weight: 900; letter-spacing: 1px;">🧰 DEV CONSOLE: PLAYERS, INVENTORY, GIVE ITEMS</span>' "Admin Console">><</link>> </div> <div style="margin-bottom: 10px; padding: 8px; border: 1px solid #38bdf8; background: rgba(2,132,199,0.25); border-radius: 4px;"> <<link '<span style="color: #e0f2fe; font-weight: 900; letter-spacing: 1px;">💎 GRANT TOKENS / VIP</span>' "Admin Grant">><</link>> </div> <div style="margin-bottom: 10px; padding: 8px; border: 1px solid #34d399; background: rgba(6,78,59,0.4); border-radius: 4px;"> <<link '<span style="color: #d1fae5; font-weight: 900; letter-spacing: 1px;">⚔ PVP EVENT: RESET DUELS</span>' "Admin Duels">><</link>> </div> <div style="margin-bottom: 10px; padding: 8px; border: 1px solid #f87171; background: rgba(127,29,29,0.35); border-radius: 4px;"> <<link '<span style="color: #fee2e2; font-weight: 900; letter-spacing: 1px;">🗑 DELETE ACCOUNT / CHARACTER</span>' "Admin Delete">><</link>> </div> <div style="background: #ef4444; padding: 10px; border-radius: 4px;"> <<link '<span style="color: #000000; font-weight: 900; letter-spacing: 1px;">ACTIVATE GOD MODE</span>'>> <<set $godMode to true>> <<set $hasSanctuary to true>> /* EVERY slot gets a GODLIKE item */ <<set $equipment.headgear to "Godlike Apex Crown">> <<set $equipment.chest to "Godlike Juggernaut Shell">> <<set $equipment.legs to "Godlike Wasteland Lord Tassets">> <<set $equipment.boots to "Godlike Titan Treads">> <<set $equipment.gloves to "Godlike Overlord Grips">> <<set $equipment.offHand to "Godlike Aegis Core">> <<if ndef $gearInventory>><<set $gearInventory to []>><</if>> <<run (function () { var v = State.variables, R = "Godlike Rębak Termiczny", cur = v.equipment.rightHand; if (cur && cur !== R && cur !== "Godlike Gauss Cannon") { v.gearInventory.push(cur); } var i = v.gearInventory.indexOf(R); if (i >= 0) { v.gearInventory.splice(i, 1); } v.rebakGift = 1; v.equipment.rightHand = R; if (v.gearInventory.indexOf("Godlike Gauss Cannon") < 0) { v.gearInventory.push("Godlike Gauss Cannon"); } })()>> <<set $equipment.trinket1 to "Godlike Phoenix Ember">> <<set $equipment.trinket2 to "Godlike Cipher's Broken Antenna">> <<set $equipment.ring1 to "Godlike Plasteel Guardian Ring">> <<set $equipment.ring2 to "Godlike Loaded Dice Ring">> <<set $equipment.backpack to "Godlike Pre-War Exo-Frame">> <<if ndef $gearInventory>><<set $gearInventory to []>><</if>> <<if ndef $npcFlags>><<set $npcFlags to {}>><</if>> <<set $npcFlags.cipher to true>> <<if not $godStashGiven>> <<set $godStashGiven to true>> <<run $gearInventory.push("Godlike Arc Reaper", "Godlike Vibro-Blade", "Godlike Plasma Cutter", "Godlike Two-Headed Coin", "Godlike Neural Link", "Godlike Harvester's Signet", "Godlike Gold Signet", "Godlike Scavenger Charm")>> <</if>> <<set $maxHp to 9999, $hp to 9999>> <<set$maxStamina to 9999, $stamina to 9999>> <<set$hunger to 100>> <<set $thirst to 100>> <<set $level to setup.LEVEL_CAP, $skillPoints to 800>> <<set$materials.scrap to 9999, $materials.wood to 9999,$materials.water to 9999, $materials.medicine to 999,$materials.ammo to 9999, $materials.rope to 99,$materials.scrip to 9999>> /* ANTI-CHEAT BYPASS: Tokens are maxed out and the encryption updates instantly */ <<set $syndicateTokens to 99999>> <<set $token_checksum to ($syndicateTokens * 73) + 1492>> <<run window.setup.recalcStats()>> <<run window.setup.updateSidebar()>> <<goto `passage()`>> <</link>> </div> </div> <<endif>> <div style="text-align:center; margin-bottom:15px; font-family: 'Segoe UI', Tahoma, sans-serif;"> <div style="color:#2ecc71; font-weight:900; font-size:1.2rem; letter-spacing:1px; text-transform: uppercase;"><<print $characterName>></div> <div style="color:#94a3b8; font-size:0.75rem; font-weight: bold; text-transform:uppercase; margin-top:2px;"><<print $playerBackground>></div> <div style="color:#64748b; font-size:0.75rem; margin-top:6px; font-weight: bold;">SECTOR: <<print $startingCity>></div> <<run setup.timeInit()>> <div style="color:#64748b; font-size:0.75rem; font-weight: bold;">DAY <<print $day>> | <<print setup.timeIcon()>> <span @style="'color:' + (setup.timeSlot() gte 6 ? '#fca5a5' : '#94a3b8')"><<print setup.timeName()>></span></div> <div style="color:#64748b; font-size:0.75rem; font-weight: bold;">WEATHER: <<print setup.weatherIcon()>> <<print $weather>></div> </div> <<if $hasSanctuary is true>> <div data-tour="sb-nav" style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 20px; text-align: center;"> <a data-passage="Map" style="display: flex; flex-direction: column; align-items: center; justify-content: center; background: #1e293b; border: 1px solid #334155; padding: 10px 5px; color: #cbd5e1; text-decoration: none; border-radius: 6px; cursor: pointer;"><span style="font-size: 1.2rem; margin-bottom: 3px;">🗺️</span><span style="font-size: 0.7rem; font-weight: bold;">MAP</span></a> <<button '<div style="display: flex; flex-direction: column; align-items: center;"><span style="font-size: 1.2rem; margin-bottom: 3px;">👤</span><span style="font-size: 0.7rem; font-weight: bold;">STATS</span></div>'>><<run Dialog.setup("OPERATOR DOSSIER")>><<run Dialog.wiki(Story.get("Character").processText())>><<run Dialog.open()>><</button>> <<button '<div style="display: flex; flex-direction: column; align-items: center;"><span style="font-size: 1.2rem; margin-bottom: 3px;">🎒</span><span style="font-size: 0.7rem; font-weight: bold;">INVENTORY</span></div>'>><<run Dialog.setup("CARGO & LOADOUT")>><<run Dialog.wiki(Story.get("Inventory").processText())>><<run Dialog.open()>><</button>> <<button '<div style="display: flex; flex-direction: column; align-items: center;"><span style="font-size: 1.2rem; margin-bottom: 3px;">📜</span><span style="font-size: 0.7rem; font-weight: bold;">QUESTS</span></div>'>><<run Dialog.setup("ACTIVE DIRECTIVES")>><<run Dialog.wiki(Story.get("Quests").processText())>><<run Dialog.open()>><</button>> <a data-passage="Camp" style="grid-column: span 2; display: flex; flex-direction: column; align-items: center; background: #0f766e; border: 1px solid #115e59; padding: 10px; color: #ccfbf1; text-decoration: none; border-radius: 6px; cursor: pointer; margin-top: 5px;"><span style="font-size: 1.2rem; margin-bottom: 3px;">🏰</span><span style="font-size: 0.75rem; font-weight: bold;">S.A.N.C.T.U.A.R.Y. BASE</span></a> </div> <<else>> <div style="background: rgba(239,68,68,0.1); border: 1px dashed #ef4444; padding: 15px; margin-bottom: 20px; border-radius: 6px; text-align: center;"><span style="color: #ef4444; font-size: 0.85rem; font-weight: bold;">[ SYSTEM LOCKED ]</span></div> <</if>> <div data-tour="sb-vitals" style="margin-bottom:15px; background: #0f172a; padding:12px; border:1px solid #1e293b; border-radius:6px;"> <div style="color:#fca5a5; font-size:0.75rem; font-weight:bold; margin-bottom:4px;">❤️ HP (<<print $hp>> / <<print$maxHp>>)</div> <div style="width: 100%; background: #000; height: 8px; border-radius: 4px; margin-bottom: 10px; border: 1px solid #7f1d1d;"><div @style="'width: ' + Math.clamp(($hp / $maxHp) * 100, 0, 100) + '%; background: #ef4444; height: 100%; border-radius: 4px;'"></div></div> <div style="color:#bae6fd; font-size:0.75rem; font-weight:bold; margin-bottom:4px;">⚡ STAMINA (<<print $stamina>> / <<print$maxStamina>>)</div> <div style="width: 100%; background: #000; height: 8px; border-radius: 4px; margin-bottom: 10px; border: 1px solid #0369a1;"><div @style="'width: ' + Math.clamp(($stamina / $maxStamina) * 100, 0, 100) + '%; background: #38bdf8; height: 100%; border-radius: 4px;'"></div></div> <<set _noQuick to ["Combat","Start","Character Creation","Game Over","Rest"].includes(passage())>><div style="display:flex; justify-content:space-between; align-items:center; color:#fde68a; font-size:0.75rem; font-weight:bold; margin-bottom:4px;"><span>🍖 SATIETY (<<print $hunger>>%)</span><<if not _noQuick and $hunger lt 100 and setup.foodHave() gte 1>><<link "<span style='border:1px solid #fbbf24; color:#fde68a; padding:1px 6px; font-size:0.65rem; border-radius:3px; background:rgba(251,191,36,0.12);'>EAT</span>">><<run setup.eatRation()>><<run window.setup.updateSidebar()>><</link>><</if>></div> <div style="width: 100%; background: #000; height: 8px; border-radius: 4px; margin-bottom: 10px; border: 1px solid #b45309;"><div @style="'width: ' + Math.clamp($hunger, 0, 100) + '%; background: #fbbf24; height: 100%; border-radius: 4px;'"></div></div> <div style="display:flex; justify-content:space-between; align-items:center; color:#38bdf8; font-size:0.75rem; font-weight:bold; margin-bottom:4px;"><span>💧 THIRST (<<print $thirst>>%)</span><<if not _noQuick and $thirst lt 100 and setup.waterHave() gte 1>><<link "<span style='border:1px solid #38bdf8; color:#7dd3fc; padding:1px 6px; font-size:0.65rem; border-radius:3px; background:rgba(56,189,248,0.12);'>DRINK</span>">><<run setup.drinkWater()>><<run window.setup.updateSidebar()>><</link>><</if>></div> <div style="width: 100%; background: #000; height: 8px; border-radius: 4px; border: 1px solid #0284c7;"><div @style="'width: ' + Math.clamp($thirst, 0, 100) + '%; background: #0ea5e9; height: 100%; border-radius: 4px;'"></div></div> <<if ($thirst lte 0) or ($hunger lte 0)>><div style="margin-top:8px; color:#fecaca; background:rgba(239,68,68,0.15); border:1px dashed #ef4444; padding:6px 8px; font-size:0.7rem; font-weight:bold; text-align:center; border-radius:4px;">⚠ <<if ($thirst lte 0) and ($hunger lte 0)>>DEHYDRATED AND STARVING<<elseif $thirst lte 0>>DEHYDRATED<<else>>STARVING<</if>>: you lose health every time you travel outside, and you can DIE. Drink or eat now!</div><</if>> </div> <div data-tour="sb-level" style="margin-bottom:15px; background: rgba(52, 211, 153, 0.05); padding:12px; border:1px dashed #047857; border-radius:6px;"> /* THE FIXED PART IS HERE: >= is used instead of gte */ <<if $xp >=$xpMax and $level lt setup.LEVEL_CAP>> <div style="text-align: center; margin-bottom: 8px;"><span style="color: #fbbf24; font-size: 0.85rem; font-weight: bold; text-shadow: 0 0 5px #fbbf24;">MAX XP REACHED!</span></div> <<button '<div style="height: 40px; background: #059669; border: 1px solid #34d399; border-radius: 4px; display: flex; justify-content: center; align-items: center;"><span style="color: #fff; font-size: 1rem; font-weight: 900; letter-spacing: 2px;">LEVEL UP</span></div>'>><<run Dialog.setup("LEVEL UP / ALLOCATE POINTS")>><<run Dialog.wiki(Story.get("LevelUp").processText())>><<run Dialog.open()>><</button>> <<else>> <div style="display: flex; justify-content: space-between; font-size: 0.8rem; color: #34d399; font-weight: bold; margin-bottom: 8px;"><span>✨ LEVEL <<print $level>><<if $level gte setup.LEVEL_CAP>> (MAX)<</if>></span><span><<print $xp>> / <<print$xpMax>> XP</span></div> <div style="width: 100%; background-color: #0f172a; border-radius: 4px; border: 1px solid #047857; height: 10px; overflow: hidden;"><div @style="'width: ' + Math.clamp(($xp / $xpMax) * 100, 0, 100) + '%; background-color: #34d399; height: 100%; border-radius: 2px; box-shadow: 0 0 8px rgba(52, 211, 153, 0.6);'"></div></div> <<if ($skillPoints || 0) gt 0>> <<button '<div style="margin-top: 8px; height: 32px; background: rgba(251,191,36,0.12); border: 1px solid #fbbf24; border-radius: 4px; display: flex; justify-content: center; align-items: center;"><span style="color: #fde68a; font-size: 0.8rem; font-weight: bold; letter-spacing: 1px;">🧬 NEURAL TREE (<<print $skillPoints>> POINTS)</span></div>'>><<run setup.openSkillTree()>><</button>> <</if>> <<endif>> </div> <!-- FULL QUICK LOADOUT (SIDEBAR) --> <div data-tour="sb-loadout" style="margin-bottom: 15px; background: rgba(15, 23, 42, 0.9); padding: 12px; border: 1px solid #334155; border-radius: 6px; font-size: 0.75rem;"> <div style="color: #fbbf24; font-weight: bold; margin-bottom: 8px; text-align: center; letter-spacing: 1px;">[ QUICK LOADOUT ]</div> <div style="display: flex; justify-content: space-between; margin-bottom: 4px; border-bottom: 1px dashed #1e293b; padding-bottom: 3px;"> <span style="color: #ef4444;">⚔️ Weapon:</span> <span @title="$equipment.rightHand" style="color: #fff; font-weight: bold; text-align: right; max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"><<print $equipment.rightHand>></span> </div> <div style="display: flex; justify-content: space-between; margin-bottom: 4px; border-bottom: 1px dashed #1e293b; padding-bottom: 3px;"> <span style="color: #3498db;">🛡️ Off-Hand:</span> <span @title="$equipment.offHand" style="color: #fff; font-weight: bold; text-align: right; max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"><<print $equipment.offHand>></span> </div> <div style="display: flex; justify-content: space-between; margin-bottom: 4px; border-bottom: 1px dashed #1e293b; padding-bottom: 3px;"> <span style="color: #94a3b8;">🪖 Head:</span> <span @title="$equipment.headgear" style="color: #fff; font-weight: bold; text-align: right; max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"><<print $equipment.headgear>></span> </div> <div style="display: flex; justify-content: space-between; margin-bottom: 4px; border-bottom: 1px dashed #1e293b; padding-bottom: 3px;"> <span style="color: #94a3b8;">🦺 Chest:</span> <span @title="$equipment.chest" style="color: #fff; font-weight: bold; text-align: right; max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"><<print $equipment.chest>></span> </div> <div style="display: flex; justify-content: space-between; margin-bottom: 4px; border-bottom: 1px dashed #1e293b; padding-bottom: 3px;"> <span style="color: #94a3b8;">👖 Legs:</span> <span @title="$equipment.legs" style="color: #fff; font-weight: bold; text-align: right; max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"><<print $equipment.legs>></span> </div> <div style="display: flex; justify-content: space-between; margin-bottom: 4px; border-bottom: 1px dashed #1e293b; padding-bottom: 3px;"> <span style="color: #94a3b8;">🥾 Boots:</span> <span @title="$equipment.boots" style="color: #fff; font-weight: bold; text-align: right; max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"><<print $equipment.boots>></span> </div> <div style="display: flex; justify-content: space-between; margin-bottom: 4px; border-bottom: 1px dashed #1e293b; padding-bottom: 3px;"> <span style="color: #94a3b8;">🧤 Gloves:</span> <span @title="$equipment.gloves" style="color: #fff; font-weight: bold; text-align: right; max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"><<print $equipment.gloves>></span> </div> <div style="display: flex; justify-content: space-between; margin-bottom: 4px; border-bottom: 1px dashed #1e293b; padding-bottom: 3px;"> <span style="color: #a855f7;">🎒 Backpack:</span> <span @title="$equipment.backpack" style="color: #fff; font-weight: bold; text-align: right; max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"><<print $equipment.backpack>></span> </div> <div style="display: flex; justify-content: space-between; margin-bottom: 4px; border-bottom: 1px dashed #1e293b; padding-bottom: 3px;"> <span style="color: #a855f7;">💍 Trinkets:</span> <span @title="$equipment.trinket1 + ' / ' + $equipment.trinket2" style="color: #fff; font-weight: bold; text-align: right; max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"><<print $equipment.trinket1>></span> </div> <div style="display: flex; justify-content: space-between; margin-bottom: 4px; border-bottom: 1px dashed #1e293b; padding-bottom: 3px;"> <span style="color: #f59e0b;">💍 Rings:</span> <span @title="$equipment.ring1 + ' / ' + $equipment.ring2" style="color: #fff; font-weight: bold; text-align: right; max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"><<print $equipment.ring1>></span> </div> <div style="display: flex; justify-content: space-between; margin-top: 6px; padding-top: 4px; border-top: 1px solid #1e293b; font-weight: bold;"> <span style="color: #a3e635;">TOTAL ARMOR:</span> <span style="color: #a3e635;"><<print $playerArmor>></span> </div> </div> <!-- QUICK RESOURCE CARGO (SIDEBAR) --> <div data-tour="sb-cargo" style="background: rgba(5, 15, 10, 0.9); padding: 12px; border: 1px solid #047857; border-radius: 6px; font-size: 0.75rem;"> <div style="color: #2ecc71; font-weight: bold; margin-bottom: 8px; text-align: center; letter-spacing: 1px;">[ RESOURCE CARGO ]</div> <div style="display: flex; justify-content: space-between; margin-bottom: 4px; border-bottom: 1px dashed #115e59; padding-bottom: 3px;"> <span style="color: #cbd5e1;">📦 Scrap:</span> <span style="color: #a3e635; font-weight: bold;"><<print $materials.scrap>></span> </div> <div style="display: flex; justify-content: space-between; margin-bottom: 4px; border-bottom: 1px dashed #115e59; padding-bottom: 3px;"> <span style="color: #cbd5e1;">🪵 Wood:</span> <span style="color: #a3e635; font-weight: bold;"><<print $materials.wood>></span> </div> <div style="display: flex; justify-content: space-between; margin-bottom: 4px; border-bottom: 1px dashed #115e59; padding-bottom: 3px;"> <span style="color: #cbd5e1;">🧵 Cloth:</span> <span style="color: #a3e635; font-weight: bold;"><<print $materials.cloth>></span> </div> <div style="display: flex; justify-content: space-between; margin-bottom: 4px; border-bottom: 1px dashed #115e59; padding-bottom: 3px;"> <span style="color: #cbd5e1;">🔌 Electronics:</span> <span style="color: #a3e635; font-weight: bold;"><<print ($materials.electronics || 0)>></span> </div> <div style="display: flex; justify-content: space-between; margin-bottom: 4px; border-bottom: 1px dashed #115e59; padding-bottom: 3px;"> <span style="color: #cbd5e1;">💊 Medicine:</span> <span style="color: #38bdf8; font-weight: bold;"><<print $materials.medicine>></span> </div> <div style="display: flex; justify-content: space-between; margin-bottom: 4px; border-bottom: 1px dashed #115e59; padding-bottom: 3px;"> <span style="color: #cbd5e1;">💧 Water:</span> <span style="color: #38bdf8; font-weight: bold;"><<print $materials.water>></span> </div> <div style="display: flex; justify-content: space-between; margin-bottom: 4px; border-bottom: 1px dashed #115e59; padding-bottom: 3px;"> <span style="color: #cbd5e1;">🔋 Ammo:</span> <span style="color: #fbbf24; font-weight: bold;"><<print $materials.ammo>></span> </div> <div style="display: flex; justify-content: space-between; margin-top: 6px; padding-top: 4px; border-top: 1px solid #115e59; font-weight: bold;"> <span style="color: #ffaa00;">🪙 Scrip:</span> <span style="color: #ffaa00;"><<print $materials.scrip>></span> </div> </div> <</nobr>>
<<nobr>> /* ========================================= */ /* REGISTERING THE SOUND AND MUSIC FILES WITH THE SYSTEM */ /* ========================================= */ <<cacheaudio "bar_ambience" "audio/sfx/bar_ambience.ogg">> <<cacheaudio "forest" "audio/sfx/forest.ogg">> <<cacheaudio "katowice_tunnels" "audio/sfx/katowice_tunnels.ogg">> <<cacheaudio "sector_bunker" "audio/sfx/sector_bunker_04.wav">> <<cacheaudio "sfx_splash" "audio/sfx/sfx_splash.wav">> <<cacheaudio "rifle" "audio/sfx/rifle.wav">> <<cacheaudio "clinic_tension" "audio/sfx/silesian_clinic_tension.wav">> <<cacheaudio "warehouse_beat" "audio/sfx/warehouse_industrial_beat.mp3">> <<cacheaudio "combat_theme" "audio/sfx/wasteland_combat_theme.mp3">> <<cacheaudio "map_ambience" "audio/sfx/map_ambience.mp3">> <<cacheaudio "sanctuary_theme" "audio/sfx/sanctuary_theme.ogg">> <<cacheaudio "caravan_ambience" "audio/sfx/caravan_ambience.wav">> <<cacheaudio "cipher_intro" "audio/vo/cipher_intro.mp3">> <<cacheaudio "grom_intro" "audio/vo/grom_intro.mp3">> <<cacheaudio "Grom_Greet" "audio/vo/Grom_Greet.mp3">> <<cacheaudio "Grom_Sell" "audio/vo/Grom_Sell.mp3">> <<cacheaudio "Grom_Handover" "audio/vo/Grom_Handover.mp3">> <<cacheaudio "Grom_Gift" "audio/vo/Grom_Gift.mp3">> <<cacheaudio "grom_radio" "audio/vo/Grom_Radio.mp3">> <<cacheaudio "Kaelen_tutorial" "audio/vo/Kaelen_tutorial.mp3">> <<cacheaudio "guide_flee" "audio/vo/guide_flee.mp3">> <<cacheaudio "Kaelen_Greet1" "audio/vo/Kaelen_Greet1.mp3">> <<cacheaudio "Kaelen_Greet2" "audio/vo/Kaelen_Greet2.mp3">> <<cacheaudio "Kaelen_Greet3" "audio/vo/Kaelen_Greet3.mp3">> <<cacheaudio "Kaelen_Intel1" "audio/vo/Kaelen_Intel1.mp3">> <<cacheaudio "Kaelen_Intel2" "audio/vo/Kaelen_Intel2.mp3">> <<cacheaudio "Kaelen_Intel3" "audio/vo/Kaelen_Intel3.mp3">> <<cacheaudio "Kaelen_Rumor1" "audio/vo/Kaelen_Rumor1.mp3">> <<cacheaudio "Kaelen_Rumor2" "audio/vo/Kaelen_Rumor2.mp3">> <<cacheaudio "Kaelen_Rumor3" "audio/vo/Kaelen_Rumor3.mp3">> <<cacheaudio "Kaelen_BJ_Start" "audio/vo/Kaelen_BJ_Start.mp3">> <<cacheaudio "Kaelen_BJ_Win" "audio/vo/Kaelen_BJ_Win.mp3">> <<cacheaudio "Kaelen_BJ_Lose" "audio/vo/Kaelen_BJ_Lose.mp3">> <<cacheaudio "Kaelen_BJ_Tie" "audio/vo/Kaelen_BJ_Tie.mp3">> <<cacheaudio "cipher_ch1_c1" "audio/vo/cipher_ch1_c1.mp3">> <<cacheaudio "cipher_ch1_c2" "audio/vo/cipher_ch1_c2.mp3">> <<cacheaudio "cipher_ch1_c3" "audio/vo/cipher_ch1_c3.mp3">> <<cacheaudio "cipher_ch1_a" "audio/vo/cipher_ch1_a.mp3">> <<cacheaudio "cipher_ch1_b" "audio/vo/cipher_ch1_b.mp3">> <<cacheaudio "cipher_ch1_c" "audio/vo/cipher_ch1_c.mp3">> <<cacheaudio "cipher_ch1_bp" "audio/vo/cipher_ch1_bp.mp3">> <<cacheaudio "cipher_ch2_m1" "audio/vo/cipher_ch2_m1.mp3">> <<cacheaudio "cipher_ch2_s1" "audio/vo/cipher_ch2_s1.mp3">> <<cacheaudio "cipher_ch2_d1" "audio/vo/cipher_ch2_d1.mp3">> <<cacheaudio "cipher_ch2_d2" "audio/vo/cipher_ch2_d2.mp3">> <<cacheaudio "cipher_ch2_oa" "audio/vo/cipher_ch2_oa.mp3">> <<cacheaudio "cipher_ch2_ob" "audio/vo/cipher_ch2_ob.mp3">> <<cacheaudio "cipher_ch2_oc" "audio/vo/cipher_ch2_oc.mp3">> <<cacheaudio "cipher_ch2_f1" "audio/vo/cipher_ch2_f1.mp3">> <<cacheaudio "cipher_ch2_f2" "audio/vo/cipher_ch2_f2.mp3">> <<cacheaudio "kaelen_ch2_broadcast" "audio/vo/kaelen_ch2_broadcast.mp3">> <<cacheaudio "cipher_srv_t01" "audio/vo/cipher_srv_t01.mp3">> <<cacheaudio "cipher_srv_t02" "audio/vo/cipher_srv_t02.mp3">> <<cacheaudio "cipher_srv_t03" "audio/vo/cipher_srv_t03.mp3">> <<cacheaudio "cipher_srv_t04" "audio/vo/cipher_srv_t04.mp3">> <<cacheaudio "cipher_srv_t05" "audio/vo/cipher_srv_t05.mp3">> <<cacheaudio "cipher_srv_t06" "audio/vo/cipher_srv_t06.mp3">> <<cacheaudio "cipher_srv_t07" "audio/vo/cipher_srv_t07.mp3">> <<cacheaudio "cipher_srv_t08" "audio/vo/cipher_srv_t08.mp3">> <<cacheaudio "cipher_srv_t09" "audio/vo/cipher_srv_t09.mp3">> <<cacheaudio "cipher_srv_t10" "audio/vo/cipher_srv_t10.mp3">> <<cacheaudio "cipher_srv_t11" "audio/vo/cipher_srv_t11.mp3">> <<cacheaudio "cipher_srv_t12" "audio/vo/cipher_srv_t12.mp3">> <<cacheaudio "cipher_srv_t13" "audio/vo/cipher_srv_t13.mp3">> <<cacheaudio "cipher_srv_t14" "audio/vo/cipher_srv_t14.mp3">> <<cacheaudio "cipher_srv_t15" "audio/vo/cipher_srv_t15.mp3">> <<cacheaudio "cipher_srv_t16" "audio/vo/cipher_srv_t16.mp3">> <<cacheaudio "cipher_srv_g1" "audio/vo/cipher_srv_g1.mp3">> <<cacheaudio "cipher_srv_g2" "audio/vo/cipher_srv_g2.mp3">> <<cacheaudio "cipher_srv_g3" "audio/vo/cipher_srv_g3.mp3">> <<cacheaudio "cipher_srv_gm" "audio/vo/cipher_srv_gm.mp3">> <<cacheaudio "cipher_srv_gc" "audio/vo/cipher_srv_gc.mp3">> <<cacheaudio "cipher_srv_ga" "audio/vo/cipher_srv_ga.mp3">> <<cacheaudio "cipher_srv_gh" "audio/vo/cipher_srv_gh.mp3">> <<cacheaudio "cipher_srv_gs" "audio/vo/cipher_srv_gs.mp3">> <<cacheaudio "cipher_srv_gp" "audio/vo/cipher_srv_gp.mp3">> <<cacheaudio "cipher_srv_gf1" "audio/vo/cipher_srv_gf1.mp3">> <<cacheaudio "cipher_srv_gf2" "audio/vo/cipher_srv_gf2.mp3">> <<cacheaudio "cipher_srv_pk_s1" "audio/vo/cipher_srv_pk_s1.mp3">> <<cacheaudio "cipher_srv_pk_s2" "audio/vo/cipher_srv_pk_s2.mp3">> <<cacheaudio "cipher_srv_pk_r1" "audio/vo/cipher_srv_pk_r1.mp3">> <<cacheaudio "cipher_srv_pk_r2" "audio/vo/cipher_srv_pk_r2.mp3">> <<cacheaudio "cipher_srv_pk_r3" "audio/vo/cipher_srv_pk_r3.mp3">> <<cacheaudio "cipher_srv_pk_c1" "audio/vo/cipher_srv_pk_c1.mp3">> <<cacheaudio "cipher_srv_pk_c2" "audio/vo/cipher_srv_pk_c2.mp3">> <<cacheaudio "cipher_srv_pk_f1" "audio/vo/cipher_srv_pk_f1.mp3">> <<cacheaudio "cipher_srv_pk_f2" "audio/vo/cipher_srv_pk_f2.mp3">> <<cacheaudio "cipher_srv_pk_w1" "audio/vo/cipher_srv_pk_w1.mp3">> <<cacheaudio "cipher_srv_pk_w2" "audio/vo/cipher_srv_pk_w2.mp3">> <<cacheaudio "cipher_srv_pk_w3" "audio/vo/cipher_srv_pk_w3.mp3">> <<cacheaudio "cipher_srv_pk_l1" "audio/vo/cipher_srv_pk_l1.mp3">> <<cacheaudio "cipher_srv_pk_l2" "audio/vo/cipher_srv_pk_l2.mp3">> <<cacheaudio "cipher_srv_pk_l3" "audio/vo/cipher_srv_pk_l3.mp3">> <<cacheaudio "cipher_srv_pk_pf1" "audio/vo/cipher_srv_pk_pf1.mp3">> <<cacheaudio "cipher_srv_pk_pf2" "audio/vo/cipher_srv_pk_pf2.mp3">> <<cacheaudio "cipher_srv_pk_t1" "audio/vo/cipher_srv_pk_t1.mp3">> <<cacheaudio "cipher_srv_pk_mw" "audio/vo/cipher_srv_pk_mw.mp3">> <<cacheaudio "cipher_srv_pk_ml" "audio/vo/cipher_srv_pk_ml.mp3">> <<cacheaudio "cipher_srv_pk_mt" "audio/vo/cipher_srv_pk_mt.mp3">> <<cacheaudio "cipher_srv_sw_done" "audio/vo/cipher_srv_sw_done.mp3">> <<cacheaudio "cipher_srv_sw_none" "audio/vo/cipher_srv_sw_none.mp3">> <<cacheaudio "cipher_srv_sw_ok" "audio/vo/cipher_srv_sw_ok.mp3">> <<cacheaudio "cipher_srv_zm" "audio/vo/cipher_srv_zm.mp3">> <<cacheaudio "miner_voice" "audio/vo/Miner_Monologue.mp3">> <<cacheaudio "svietlok_chant" "audio/vo/Svietlok_Chant.mp3">> <!-- PLAYER BASIC INFO --> <<if ndef $playerName>><<set $playerName to "Wanderer">><</if>> <<if ndef $syndicateTokens>><<set $syndicateTokens to 0>><</if>> <<set $playerGender to "Male">> <<set $playerAge to 24>> <<set $playerBackground to "Unknown">> <<set $playerClass to "None">> <<set $activeCompanion to "None">> <<set $squadIds to []>> <<set $talents to []>> <<set $isFounder to false>> <<set $isVIP to false>> <<set $isAdmin to false>> <!-- VITAL SIGNS AND PROGRESS --> <<set $hp to 100>> <<set $maxHp to 100>> <<set $stamina to 100>> <<set $maxStamina to 100>> <<set $hunger to 100>> <<set $day to 1>> <<set $weather to "Clear">> <<set $baseRep to 0>> <<set $threat to 0>> <<set $level to 1>> <<set $xp to 0>> <<set $xpMax to 100>> <<set $skillPoints to 0>> <<set $startingCity to "Katowice">> <<set $tutorialStep to 1>> /* ========================================= */ /* NPC AFFINITY (TRUST/RELATIONSHIP) VARIABLES */ /* ========================================= */ <<set $cipherAffinity to 0>> <<set $cipherGiftGivenToday to false>> <<set $cipherTalkedToday to false>> <<set $gromAffinity to 0>> <<set $gromGiftGivenToday to false>> <<set $gromTalkedToday to false>> <<set $kaelenAffinity to 0>> <<set $kaelenGiftGivenToday to false>> <<set $kaelenTalkedToday to false>> <<widget "getAffinityRank">> <<if _args[0] gte 80>> <span style="color: #f59e0b; font-weight: bold; text-shadow: 0 0 8px #f59e0b; letter-spacing: 1px;">LOYAL</span> <<elseif _args[0] gte 50>> <span style="color: #38bdf8; font-weight: bold; text-shadow: 0 0 8px #38bdf8; letter-spacing: 1px;">COMPANION</span> <<elseif _args[0] gte 25>> <span style="color: #10b981; font-weight: bold; text-shadow: 0 0 8px #10b981; letter-spacing: 1px;">TRUSTED</span> <<else>> <span style="color: #64748b; font-weight: bold; letter-spacing: 1px;">STRANGER</span> <<endif>> <</widget>> <<widget "getReputationRank">> <<if $svietlok_buff>> <span style="color: #a3e635; font-weight: bold; text-shadow: 0 0 12px rgba(163, 230, 53, 0.8); letter-spacing: 2px;">ŚWIETLOK (THE FIREFLY)</span> <<elseif $baseRep gte 100>> <span style="color: #fbbf24; font-weight: bold;">WASTELAND LEGEND</span> <<elseif $baseRep gte 50>> <span style="color: #c084fc; font-weight: bold;">RESPECTED COMMANDER</span> <<elseif $baseRep gte 20>> <span style="color: #34d399; font-weight: bold;">KNOWN MERCENARY</span> <<else>> <span style="color: #9ca3af; font-weight: bold;">UNKNOWN DRIFTER</span> <<endif>> <</widget>> <!-- BASIC RESOURCES AND SHARED CAMP STORAGE (STORAGE) --> <<set $materials to { scrap: 10, wood: 10, cloth: 10, iron: 5, caps: 0, techData: 0, medicine: 0, food: 5, rawMeat: 0, berries: 0, water: 5, ammo: 10, rope: 2, scrip: 0 }>> <<if ndef $campStorage>> <<set $campStorage to { scrap: 0, wood: 0, cloth: 0, iron: 0, caps: 0, techData: 0, medicine: 0, food: 0, rawMeat: 0, berries: 0, water: 0, rope: 0, scrip: 0 }>> <</if>> <!-- COLONY AND WORKER MANAGEMENT --> <<set $survivors to 5>> <<if ndef $prisoners>><<set $prisoners to 0>><</if>> <<if ndef $campSlaves>><<set $campSlaves to 0>><</if>> <<set $laborScrap to 0>> <<set $laborWood to 0>> <<set $laborCloth to 0>> <<set $laborHunters to 0>> <<set $laborForagers to 0>> <<set $laborMedics to 0>> <<set $laborWater to 0>> <<set $laborCooks to 0>> <!-- BASE AND ITEMS --> <<set $hasSanctuary to false>> <<set $sanctuaryLevels to { workshop: 1, greenhouse: 1, pit: 1, walls: 1 }>> <<set $equipment to { headgear: 'None', chest: 'None', legs: 'None', boots: 'None', gloves: 'None', rightHand: 'None', offHand: 'None', trinket1: 'None', trinket2: 'None', backpack: 'None', ring1: 'None', ring2: 'None' }>> <<set $gearInventory to []>> <!-- SLAUGHTERHOUSE QUEST VARIABLES --> <<if ndef $quest_slaughterhouse>><<set $quest_slaughterhouse to "not_started">><</if>> <<if ndef $total_rescued>><<set $total_rescued to 0>><</if>> <<if ndef $convoyAvailable>><<set $convoyAvailable to false>><</if>> <<if ndef $laborWalls>><<set $laborWalls to 0>><</if>> <<if ndef $wallIntegrity>><<set $wallIntegrity to 100>><</if>> <<if ndef $tripwiresArmed>><<set $tripwiresArmed to false>><</if>> <<if ndef $turretsAligned>><<set $turretsAligned to false>><</if>> <<if ndef $slaughterGas>><<set $slaughterGas to false>><</if>> <<if ndef $svietlok_buff>><<set $svietlok_buff to false>><</if>> <<if ndef $sanctuaryPop>><<set $sanctuaryPop to 0>><</if>> /* --- ITEM LISTS AND RARITY SYSTEM --- */ <<set $rarities to { "Common": { mult: 1.0, color: "#9ca3af" }, "Uncommon": { mult: 1.3, color: "#34d399" }, "Rare": { mult: 1.6, color: "#38bdf8" }, "Epic": { mult: 2.0, color: "#c084fc" }, "Legendary": { mult: 2.5, color: "#fbbf24" }, "Godlike": { mult: 3.5, color: "#ef4444" } }>> <<set $itemDatabase to { headgear: ["Scrap Helmet", "Leather Cap", "Tactical Goggles", "Reinforced Mask", "Gas Mask", "Steel Helm", "Riot Visor", "Cybernetic Optic", "Power Hood", "Apex Crown"], chest: ["Ragged Shirt", "Leather Vest", "Scrap Armor", "Tactical Rig", "Kevlar Vest", "Combat Plate", "Hazmat Suit", "Composite Armor", "Exo-Plating", "Juggernaut Shell"], legs: ["Torn Pants", "Leather Trousers", "Cargo Pants", "Scrap Legguards", "Combat Leggings", "Tactical Pants", "Plated Cuisses", "Exo-Legs", "Enforcer Pants", "Wasteland Lord Tassets"], boots: ["Worn Boots", "Leather Boots", "Scrap Boots", "Steel-Toed Boots", "Tactical Boots", "Combat Boots", "Stalker Boots", "Exo-Striders", "Ghost Stompers", "Titan Treads"], gloves: ["Fingerless Gloves", "Leather Gloves", "Work Gloves", "Reinforced Gloves", "Combat Gloves", "Tactical Wraps", "Shock Gloves", "Exo-Gloves", "Assault Bracers", "Overlord Grips"], rightHand: ["Combat Knife", "Pipe Pistol", "Scrap Launcher", "Revolver", "Hunting Rifle", "Submachine Gun", "Combat Shotgun", "Assault Rifle", "Plasma Cutter", "Gauss Cannon", "Rusty Wrench", "Rebar Club", "Spiked Bat", "Machete", "Fire Axe", "Tomahawk", "Sledgehammer", "Combat Katana", "Rail-Spike Maul", "Shock Maul", "Thermal Cleaver", "Vibro-Blade", "Arc Reaper"], offHand: ["Makeshift Shield", "Combat Knife", "Flashlight", "Stun Baton", "Energy Shield", "Tactical Radio", "Signal Jammer", "Advanced Shield", "Nano Barrier", "Aegis Core"], trinket1: ["Worn Locket", "Amulet of Resilience", "Grom's Brass Charm", "Reptilian Slayer's Mark", "Phoenix Ember", "Scavenger Charm", "Woodpecker's Charm", "Mutant Scale Talisman", "Toxic Glowing Lure", "Deepwater Sonar", "Two-Headed Coin", "Cursed Rabbit's Foot", "Radioactive Ward", "Neural Link", "Cipher's Broken Antenna"], trinket2: ["Worn Locket", "Amulet of Resilience", "Grom's Brass Charm", "Reptilian Slayer's Mark", "Phoenix Ember", "Scavenger Charm", "Woodpecker's Charm", "Mutant Scale Talisman", "Toxic Glowing Lure", "Deepwater Sonar", "Two-Headed Coin", "Cursed Rabbit's Foot", "Radioactive Ward", "Neural Link", "Cipher's Broken Antenna"], backpack: ["Frayed Canvas Pouch", "Scavenger's Rucksack", "Old Military Duffel", "Reptilian Scale-Hide Bag", "Pre-War Exo-Frame"], ring1: ["Copper Band", "Iron Knuckle Ring", "Blood-Iron Ring", "Viper Fang Ring", "Plasteel Guardian Ring", "Rusted Hook Ring", "Scrap-Magnet Ring", "River Phantom's Band", "Beast-Tracker's Band", "Harvester's Signet", "Brass Coin Ring", "Silver Signet", "Gold Signet", "Thief's Greed", "Loaded Dice Ring"], ring2: ["Copper Band", "Iron Knuckle Ring", "Blood-Iron Ring", "Viper Fang Ring", "Plasteel Guardian Ring", "Rusted Hook Ring", "Scrap-Magnet Ring", "River Phantom's Band", "Beast-Tracker's Band", "Harvester's Signet", "Brass Coin Ring", "Silver Signet", "Gold Signet", "Thief's Greed", "Loaded Dice Ring"] }>> /* ========================================= */ /* DYNAMIC STAT, LOOT AND LOCATION ENGINE */ /* ========================================= */ <<script>> /* ===== MELEE WEAPONS: tier decides base damage (tier x 6, like the guns), style adds a small side bonus ===== */ setup.MELEE = { "Combat Knife": { tier: 1, style: "" }, "Rusty Wrench": { tier: 1, style: "heavy" }, "Rebar Club": { tier: 2, style: "heavy" }, "Spiked Bat": { tier: 2, style: "heavy" }, "Machete": { tier: 3, style: "blade" }, "Fire Axe": { tier: 4, style: "axe" }, "Tomahawk": { tier: 4, style: "blade" }, "Sledgehammer": { tier: 5, style: "heavy" }, "Combat Katana": { tier: 6, style: "blade" }, "Rail-Spike Maul": { tier: 6, style: "heavy" }, "Shock Maul": { tier: 7, style: "axe" }, "Thermal Cleaver": { tier: 8, style: "axe" }, "Vibro-Blade": { tier: 9, style: "blade" }, "Arc Reaper": { tier: 10, style: "axe" }, "Szabla Husarska": { tier: 6, style: "blade" }, "Szczerbiec": { tier: 10, style: "blade" } }; /* ranged weapons with their own fixed tier: the Rębak Termiczny (tier 28, kept as players know it) and the Polish firearms (a pistol, two rifles) */ setup.EXTRA_WEAPONS = { "Rębak Termiczny": { tier: 28 }, "Vis wz. 35": { tier: 3 }, "FB Beryl": { tier: 8 }, "MSBS Grot": { tier: 9 } }; setup.RIGHT_HAND = ["Combat Knife", "Pipe Pistol", "Scrap Launcher", "Revolver", "Hunting Rifle", "Submachine Gun", "Combat Shotgun", "Assault Rifle", "Plasma Cutter", "Gauss Cannon"].concat(Object.keys(setup.MELEE).slice(1), Object.keys(setup.EXTRA_WEAPONS)); /* four stray entries (Polish-named copies of real weapons, wrongly treated as huge ranged guns) must not exist anywhere in a save */ setup.STRAY_WEAPONS = ["Nóż Bojowy", "Zardzewiały Klucz", "Bojowa Katana", "Młot z Kolców Torowych"]; setup.purgeStrayItems = function () { let v = State.variables, S = setup.STRAY_WEAPONS; let has = function (t) { return typeof t === "string" && S.some(function (n) { return t.indexOf(n) >= 0; }); }; let seen = []; let sweep = function (o) { if (!o || typeof o !== "object" || seen.indexOf(o) >= 0) { return; } seen.push(o); if (Array.isArray(o)) { for (let i = o.length - 1; i >= 0; i--) { if (has(o[i])) { o.splice(i, 1); } else { sweep(o[i]); } } return; } Object.keys(o).forEach(function (k) { if (has(k)) { delete o[k]; } else if (has(o[k])) { if (o === v.equipment) { o[k] = "None"; } else { delete o[k]; } } else { sweep(o[k]); } }); }; sweep(v); if (v.itemDatabase && Array.isArray(v.itemDatabase.rightHand)) { v.itemDatabase.rightHand = setup.RIGHT_HAND.slice(); } try { setup.recalcStats(); window.setup.updateSidebar(); } catch (e) { /* stats not ready */ } }; setup.ensureItemDb = function () { let v = State.variables; if (!v.itemDatabase || !Array.isArray(v.itemDatabase.rightHand)) { return; } let rh = v.itemDatabase.rightHand; if (!v.strayPurged || rh.length !== setup.RIGHT_HAND.length || !rh.every(function (n, i) { return n === setup.RIGHT_HAND[i]; })) { v.strayPurged = 1; setup.purgeStrayItems(); } /* the maker of the game gets one Godlike Rębak Termiczny */ if (!v.rebakGift && String(v.playerName || "").toLowerCase() === "nakman" && Array.isArray(v.gearInventory)) { v.rebakGift = 1; v.gearInventory.push("Godlike Rębak Termiczny"); } }; /* every save is checked as soon as a passage opens */ $(document).on(":passagestart", function () { try { setup.ensureItemDb(); } catch (e) { /* state not ready */ } }); setup.getItemStats = function(fullName) { if (setup.ensureItemDb) { setup.ensureItemDb(); } if (setup.ensureRarities) { setup.ensureRarities(); } if (typeof fullName === "string" && fullName.indexOf("Bebok's Scrap Pack") >= 0) { return { dmg: 0, armor: 0, hp: 0, stamina: 0, carryBonus: 1000, fishChance: 0, forageBonus: 0, globalLuck: 0, isRanged: false, stealth: 0, huntBonus: 0, scrapBonus: 0 }; } if (!fullName || fullName === "None") return { dmg: 0, armor: 0, hp: 0, stamina: 0, carryBonus: 0, fishChance: 0, forageBonus: 0, globalLuck: 0, isRanged: false, stealth: 0, huntBonus: 0, scrapBonus: 0 }; let rarity = "Common"; let mult = 1.0; let rarities = State.variables.rarities; for (let r in rarities) { if (fullName.includes(r)) { rarity = r; mult = rarities[r].mult; break; } } let baseName = fullName.replace(rarity + " ", ""); let db = State.variables.itemDatabase; let slot = "rightHand"; let tier = 1; for (let key in db) { if (db[key].includes(baseName)) { slot = key; if (key.includes("ring") || key.includes("trinket")) { tier = (db[key].indexOf(baseName) % 5) + 1; } else { tier = db[key].indexOf(baseName) + 1; } break; } } if (setup.EXTRA_WEAPONS[baseName]) { tier = setup.EXTRA_WEAPONS[baseName].tier; } let stats = { dmg: 0, armor: 0, hp: 0, stamina: 0, carryBonus: 0, fishChance: 0, forageBonus: 0, globalLuck: 0, isRanged: false, stealth: 0, huntBonus: 0, scrapBonus: 0 }; if (slot === "rightHand") { let mw = setup.MELEE[baseName]; if (mw) { /* MELEE: no ammo, same damage as a gun of the same tier, plus a style bonus */ tier = mw.tier; stats.dmg = Math.round(tier * 6 * mult); stats.isRanged = false; if (mw.style === "heavy") { stats.hp = Math.round(tier * 4 * mult); } else if (mw.style === "blade") { /* no stamina bonus any more */ } else if (mw.style === "axe") { stats.armor = Math.round(tier * 1.5 * mult); } } else { stats.dmg = Math.round(tier * 6 * mult); stats.isRanged = true; } } else if (slot === "headgear") { stats.armor = Math.round(tier * 2 * mult); stats.hp = Math.round(tier * 5 * mult); } else if (slot === "chest") { stats.armor = Math.round(tier * 4 * mult); stats.hp = Math.round(tier * 10 * mult); } else if (slot === "legs") { stats.armor = Math.round(tier * 3 * mult); } else if (slot === "boots") { stats.armor = Math.round(tier * 2 * mult); } else if (slot === "gloves") { stats.armor = Math.round(tier * 1 * mult); stats.dmg = Math.round(tier * 2 * mult); } else if (slot === "offHand") { stats.armor = Math.round(tier * 5 * mult); stats.hp = Math.round(tier * 5 * mult); if (baseName === "Combat Knife") { stats.isRanged = false; } } else if (slot === "backpack") { stats.carryBonus = Math.round(tier * 30 * mult); } if (baseName === "Scrap Launcher") { stats.dmg = Math.round(55 * mult); stats.isRanged = true; } let stealthItems = ["Reptilian Slayer's Mark", "Viper Fang Ring", "Cipher's Broken Antenna", "River Phantom's Band", "Grom's Brass Charm"]; let huntItems = ["Beast-Tracker's Band", "Woodpecker's Charm", "Mutant Scale Talisman", "Blood-Iron Ring"]; let scrapItems = ["Scavenger Charm", "Scrap-Magnet Ring", "Rusted Hook Ring", "Iron Knuckle Ring"]; let fishForageItems = ["Toxic Glowing Lure", "Deepwater Sonar", "Harvester's Signet"]; let luckItems = ["Two-Headed Coin", "Cursed Rabbit's Foot", "Neural Link", "Brass Coin Ring", "Silver Signet", "Gold Signet", "Thief's Greed", "Loaded Dice Ring"]; let defenseItems = ["Worn Locket", "Amulet of Resilience", "Phoenix Ember", "Copper Band", "Plasteel Guardian Ring", "Radioactive Ward"]; if (stealthItems.includes(baseName)) { stats.stealth += Math.round(tier * 5 * mult); stats.dmg += Math.round(tier * 1 * mult); } else if (huntItems.includes(baseName)) { stats.huntBonus += Math.round(tier * 10 * mult); } else if (scrapItems.includes(baseName)) { stats.scrapBonus += Math.round(tier * 10 * mult); } else if (fishForageItems.includes(baseName)) { stats.fishChance += Math.round(tier * 4 * mult); stats.forageBonus += Math.round(tier * 1 * mult); } else if (luckItems.includes(baseName)) { stats.globalLuck += Math.round(tier * 3 * mult); } else if (defenseItems.includes(baseName)) { stats.armor += Math.round(tier * 1 * mult); stats.hp += Math.round(tier * 5 * mult); } return stats; }; setup.recalcStats = function() { let v = State.variables; let baseMaxHp = 100 + ((v.level - 1) * 5); let baseMaxStam = 100; /* STATIC: 100 base, only talents raise it */ if (v.playerBackground === "Mercenary") baseMaxHp += Math.round(baseMaxHp * 0.20); if (v.playerBackground === "Mutated Anomaly") { baseMaxHp -= Math.round(baseMaxHp * 0.40); } /* TALENTS: every talent in every tree feeds in here (the old perks are part of the new trees) */ const TM = (setup.tmAll ? setup.tmAll() : {}); baseMaxHp += (TM.hp || 0); baseMaxStam += (TM.stam || 0); baseMaxHp += Math.round(baseMaxHp * (TM.hpPct || 0) / 100); baseMaxStam += Math.round(baseMaxStam * (TM.stamPct || 0) / 100); let tArmor = 0, tDmg = 0, gHp = 0, gStam = 0, carryBonus = 0; let tFish = 0, tForage = 0, tLuck = 0, tStealth = 0, tHunt = 0, tScrap = 0; for (let slot in v.equipment) { let item = v.equipment[slot]; let st = setup.getItemStats(item); gHp += st.hp; gStam += st.stamina; tArmor += st.armor; tDmg += st.dmg; if (st.carryBonus) { carryBonus += st.carryBonus; } if (st.fishChance) { tFish += st.fishChance; } if (st.forageBonus) { tForage += st.forageBonus; } if (st.globalLuck) { tLuck += st.globalLuck; } if (st.stealth) { tStealth += st.stealth; } if (st.huntBonus) { tHunt += st.huntBonus; } if (st.scrapBonus) { tScrap += st.scrapBonus; } } let baseCapacity = (v.isFounder === true) ? 120 : 40; let oldMaxHp = v.maxHp || baseMaxHp; let oldMaxStam = v.maxStamina || baseMaxStam; v.maxHp = baseMaxHp + gHp; v.maxStamina = baseMaxStam; /* gear no longer adds Stamina */ v.playerArmor = tArmor + (TM.armor || 0); v.playerDmgBonus = tDmg + (TM.dmg || 0); v.maxCarry = baseCapacity + carryBonus + (TM.carry || 0); v.maxPlayerAP = 3 + Math.min(2, (TM.ap || 0)); v.playerFishChance = tFish; v.playerForageBonus = tForage; v.playerLuck = tLuck; v.playerStealth = tStealth; v.playerHuntBonus = tHunt; v.playerScrapBonus = tScrap; if (v.hp > v.maxHp) v.hp = v.maxHp; if (v.stamina > v.maxStamina) v.stamina = v.maxStamina; if (v.maxHp > oldMaxHp) v.hp += (v.maxHp - oldMaxHp); if (v.maxStamina > oldMaxStam) v.stamina += (v.maxStamina - oldMaxStam); }; /* ===== BOSS COMBAT BACKGROUNDS: images/combat/bosses/boss_<name>.jpg (the location background stays as the fallback) ===== */ setup.BOSS_BG = { "Bebok": "bebok", "Licho": "licho", "Marzanna": "marzanna", "Topielica": "topielica", "Planetnik": "planetnik", "Strzyga": "strzyga", "Lady Midday": "lady_midday", "Raider Warboss": "raider_warboss", "Collar-Master Szhaar": "collar_master_szhaar", "Brass Jaw Dobrowolski": "brass_jaw", "Mist-Crawl Matron": "hive_matron", "Silica Reaper": "silica_reaper", "Vanguard Captain Zhor-Kaal": "zhor_kaal", "Bone-Sower Priest": "bone_sower", "Utopek-7": "utopek_7", "The Zmora": "the_zmora", "Abattoir Warden": "abattoir_warden", "Reptilian Warlord": "reptilian_warlord", "Foreman Kruk": "foreman_kruk", "Sub-Warden Kraazh": "sub_warden_kraazh" }; /* a file name with an apostrophe (Bebok's Scrap Yard) would break the CSS url('...') and drop the whole background */ setup.cssSafeUrl = function (u) { return String(u).split("'").join("%27").split('"').join("%22").split(" ").join("%20"); }; setup.bossBgUrl = function (group) { try { var list = Array.isArray(group) ? group : []; for (var i = 0; i < list.length; i++) { var e = list[i]; if (!e) { continue; } if (e.unknownThreat) { return "images/combat/bosses/boss_unknown_threat.jpg"; } var s = setup.BOSS_BG[e.name]; if (s) { return "images/combat/bosses/boss_" + s + ".jpg"; } } } catch (err) {} return ""; }; /* ===== CAMP STORAGE: ONE source of truth for weight and capacity (every screen uses these) ===== */ setup.CAMP_W = { scrap: 1.0, wood: 3.0, cloth: 0.5, iron: 5.0, medicine: 0.2, medicalHerb: 0.1, food: 0.5, rawMeat: 1.0, berries: 0.2, water: 1.5, ammo: 0.05, techData: 0.1, electronics: 0.2 }; setup.campWeight = function () { var v = State.variables, cs = v.campStorage || {}, t = 0; Object.keys(setup.CAMP_W).forEach(function (k) { t += (Number(cs[k]) || 0) * setup.CAMP_W[k]; }); return Math.round(t * 10) / 10; }; setup.campCap = function () { var v = State.variables, base = 500 * ((v.sanctuaryLevels && v.sanctuaryLevels.base) || 1); var cap = (v.isFounder === true) ? base * 3 : base; if (v.vaultExtensions > 0) cap += v.vaultExtensions * 500; else if (v.perks && v.perks.vault === true) { cap += 500; v.vaultExtensions = 1; } return cap; }; setup.campFull = function () { return setup.campWeight() >= setup.campCap(); }; /* ===== GROM, THE PIT MASTER: interrogation, talk, quiet aid, pit upgrades ===== */ setup.gromTiers = [ { name: "Stranger", at: 0 }, { name: "Contractor", at: 25 }, { name: "Partner", at: 60 }, { name: "Confidant", at: 110 } ]; setup.gromTier = function () { var a = Number(State.variables.gromAffinity) || 0, i = 0; for (var k = 0; k < setup.gromTiers.length; k++) { if (a >= setup.gromTiers[k].at) i = k; } var nx = setup.gromTiers[i + 1]; return { i: i, name: setup.gromTiers[i].name, nextAt: nx ? nx.at : 0, nextName: nx ? nx.name : "" }; }; /* which group a bound enemy belongs to: reptilian / bandit (bandits and raiders) / survivor (forced fighters) */ setup.gromGroup = function (e) { var n = String((e && e.name) || "").toLowerCase(); if (e && (e.isCaptive === true)) return "survivor"; if (/enslaved|survivor|civilian|refugee/.test(n)) return "survivor"; if (/reptil|warlord|overseer|sszakar|scrapper|cleaver|butcher|slave driver|vanguard/.test(n)) return "reptilian"; return "bandit"; }; /* only humanoid, non-boss enemies can be bound: no drones, turrets, machines, animals or monsters, and no bosses */ setup.canCapture = function (e) { if (!e) return false; if (e.noCapture || (e.fixed && !e.tourBind) || e.unknownThreat || e.isBebok || e.boss) return false; if ((e.hpMult || 1) >= 3) return false; if (e.rarity === "Godlike" || e.rarity === "Legendary" || e.rarity === "Epic") return false; var n = String(e.name || ""); if (setup.BOSS_BG && setup.BOSS_BG[n]) return false; if (/drone|turret|robot|machine|mech\b|automat|wolf|hound|dog|beast|bear|boar|rat\b|crawler|unknown threat|warlord|bebok|zmora/i.test(n)) return false; return true; }; setup.canBindAny = function (src) { var list = Array.isArray(src) ? src : [src]; return list.some(function (x) { return setup.canCapture(x); }); }; setup.addPrisoner = function (src) { var v = State.variables; var list = (Array.isArray(src) ? src : [src]).filter(function (x) { return setup.canCapture(x); }); if (!list.length) return false; var e = list[Math.floor(Math.random() * list.length)]; setup.gromInit(); v.prisoners = (v.prisoners || 0) + 1; v.prisonerList.push({ g: setup.gromGroup(e), t: "" }); return true; }; /* ===== BANDIT AND REPTILIAN CAMPS: Grom's interrogations give intel, enough intel puts a camp HQ on the map ===== */ setup.CAMP = { bandit: { label: "Bandit", need: 3, cap: 3, waves: 3, color: "#f87171", icon: "🏕️", names: ["Rust Dogs Hideout", "Blackwire Gang HQ", "Scrapjaw Camp", "Ashfang Stronghold", "Gutterline Gang HQ", "Iron Teeth Hideout", "Cinder Crow Nest", "Hollow Road Den"] }, reptilian: { label: "Reptilian", need: 5, cap: 2, waves: 4, color: "#2dd4bf", icon: "🦎", names: ["Reptilian Forward Outpost", "Scale-Warden Garrison", "Coil-Watch Station", "Hive-Sentinel Outpost", "Cold-Blood Depot"] } }; /* every fixed place on the Sector Map [top %, left %]: camps never spawn on top of them */ setup.CAMP_FIXED = [[30,40],[30.8,65],[34.8,47.9],[35,21.8],[38.3,26.8],[40,68.4],[42.1,49.5],[44.4,56.8],[49.2,36.6],[51.2,52],[52.7,63.1],[54.2,44.3],[60.4,73.2],[66.5,62.5],[68.3,44.6],[77.1,54.5],[89.6,45.5]]; setup.campInit = function () { var v = State.variables; if (!Array.isArray(v.campSites)) v.campSites = []; if (!v.campIntel || typeof v.campIntel !== "object") v.campIntel = { bandit: 0, reptilian: 0 }; if (typeof v.campsCleared !== "number") v.campsCleared = 0; }; setup.campCount = function (type) { setup.campInit(); return State.variables.campSites.filter(function (c) { return c.type === type; }).length; }; setup.campRandomPos = function () { var v = State.variables, taken = setup.CAMP_FIXED.slice(); (v.campSites || []).forEach(function (c) { taken.push(c.pos); }); (v.mapSites || []).forEach(function (s) { if (s.pos) taken.push(s.pos); }); if (v.locTrader && v.locTrader.top) { taken.push([parseFloat(v.locTrader.top), parseFloat(v.locTrader.left)]); } var best = null, bestD = -1; for (var i = 0; i < 60; i++) { var p = [10 + Math.random() * 80, 10 + Math.random() * 80], d = 999; taken.forEach(function (q) { d = Math.min(d, Math.hypot(p[0] - q[0], p[1] - q[1])); }); if (d >= 9) { return [Math.round(p[0] * 10) / 10, Math.round(p[1] * 10) / 10]; } if (d > bestD) { bestD = d; best = p; } } return [Math.round(best[0] * 10) / 10, Math.round(best[1] * 10) / 10]; }; setup.campReveal = function (type) { var v = State.variables, C = setup.CAMP[type]; setup.campInit(); var used = v.campSites.map(function (c) { return c.name; }); var pool = C.names.filter(function (n) { return used.indexOf(n) < 0; }); var name = pool.length ? pool[Math.floor(Math.random() * pool.length)] : C.names[0]; v.campUid = (v.campUid || 0) + 1; var camp = { id: "camp" + v.campUid, type: type, name: name, pos: setup.campRandomPos(), day: v.day || 1 }; v.campSites.push(camp); return camp; }; setup.campWaves = function (type) { function e(name, hp, mn, mx, hm, dm, drop) { return { name: name, hp: hp, currentHp: hp, maxHp: hp, minDmg: mn, maxDmg: mx, hpMult: hm, dmgMult: dm, drop: drop }; } function n(k, f) { var a = []; for (var i = 0; i < k; i++) { a.push(f()); } return a; } if (type === "bandit") { return [ n(3, function () { return e("Wasteland Scavenger", 45, 9, 14, 1.0, 1.0, "scrap"); }), n(2, function () { return e("Wasteland Raider", 65, 12, 18, 1.15, 1.1, "scrap"); }).concat([e("Rogue Veteran", 90, 14, 20, 1.3, 1.15, "scrap")]), [e("Raider Warboss", 100, 14, 20, 3.5, 1.3, "scrap"), e("Rogue Veteran", 70, 13, 19, 1.2, 1.15, "scrap"), e("Rogue Veteran", 70, 13, 19, 1.2, 1.15, "scrap")] ]; } return [ n(3, function () { return e("Reptilian Scrapper", 55, 11, 16, 1.1, 1.1, "techData"); }), n(2, function () { return e("Reptilian Cleaver Guard", 75, 13, 19, 1.2, 1.15, "techData"); }).concat([e("Reptilian Slave Driver", 65, 12, 18, 1.15, 1.15, "techData")]), n(2, function () { return e("Reptilian Butcher Elite", 115, 18, 26, 1.5, 1.3, "techData"); }).concat([e("Reptilian Overseer", 100, 20, 28, 1.4, 1.4, "techData")]), [e("Reptilian Camp Warden", 130, 16, 24, 4.5, 1.35, "techData"), e("Reptilian Cleaver Guard", 75, 13, 19, 1.2, 1.15, "techData"), e("Reptilian Cleaver Guard", 75, 13, 19, 1.2, 1.15, "techData")] ]; }; setup.campRaidStart = function (id) { var v = State.variables, camp = null; setup.campInit(); v.campSites.forEach(function (c) { if (c.id === id) camp = c; }); if (!camp) return; var W = setup.campWaves(camp.type); v.campRaid = { id: camp.id, type: camp.type, name: camp.name, waves: W.length }; v.combatContext = "camp_raid"; v.currentLocation = camp.name; delete v.targetPOIName; delete v.targetPOILoot; v.slaughterGas = false; v.reinforcementPending = false; delete v.nextCombatEnemyGroup; delete v.nextCombatEnemy; delete v.checkpointWave; delete v.combatLog; v.combatWave = 1; v.enemyGroup = W.shift(); v.enemy = v.enemyGroup[0]; v.waveQueue = W; }; setup.campRaidAbort = function () { var v = State.variables; delete v.waveQueue; delete v.combatContext; delete v.campRaid; delete v.enemy; delete v.enemyGroup; delete v.targetPOIName; delete v.targetPOILoot; delete v.combatWave; delete v.combatLog; }; setup.campRaidWin = function () { var v = State.variables, r = v.campRaid, lines = []; if (!r) return; function rnd(a, b) { return a + Math.floor(Math.random() * (b - a + 1)); } function give(key, n, label) { v.materials[key] = (v.materials[key] || 0) + n; lines.push("+" + n + " " + label); } var lvl = v.level || 1, rep, xp; if (r.type === "bandit") { give("scrip", rnd(120, 220) + lvl * 5, "Scrip"); give("scrap", rnd(30, 60), "Scrap"); give("ammo", rnd(15, 30), "Ammo"); give("cloth", rnd(10, 20), "Cloth"); rep = 10; xp = rnd(70, 110) + lvl * 10; } else { give("scrip", rnd(250, 400) + lvl * 8, "Scrip"); give("scrap", rnd(40, 80), "Scrap"); give("techData", rnd(12, 25), "Tech Data"); give("medicine", rnd(2, 5), "Medicine"); rep = 20; xp = Math.round((rnd(70, 110) + lvl * 10) * 1.8); } v.baseRep = (v.baseRep || 0) + rep; lines.push("+" + rep + " Reputation"); v.xp = (v.xp || 0) + xp; lines.push("+" + xp + " XP"); v.gromAffinity = (v.gromAffinity || 0) + 3; lines.push("+3 Grom affinity (he hears about it)"); v.campSites = v.campSites.filter(function (c) { return c.id !== r.id; }); v.campsCleared = (v.campsCleared || 0) + 1; v.campReport = { name: r.name, type: r.type, lines: lines }; delete v.campRaid; try { setup.recalcStats(); } catch (e) {} }; setup.GROM_ICON = { none: "❔", survivor: "🧍", bandit: "🗡️", madman: "🌀", collab: "🕵️", reptilian: "🦎" }; setup.GROM_TAGNAME = { none: "UNQUESTIONED", survivor: "SURVIVOR", bandit: "BANDIT", madman: "MADMAN", collab: "COLLAB.", reptilian: "REPTILIAN" }; setup.gromInit = function () { var v = State.variables; if (v.prisonerTags !== undefined) { delete v.prisonerTags; } if (!Array.isArray(v.prisonerList)) v.prisonerList = []; var np = Math.max(0, v.prisoners || 0); while (v.prisonerList.length < np) { v.prisonerList.push({ g: "unknown", t: "" }); } if (v.prisonerList.length > np) { v.prisonerList.length = np; } if (!v.pitUp || typeof v.pitUp !== "object") v.pitUp = { cells: false, medic: false, room: false }; if (typeof v.gromView !== "string") v.gromView = "main"; if (typeof v.gromReport !== "string") v.gromReport = ""; if (typeof v.gromTalkDay !== "number") v.gromTalkDay = 0; if (typeof v.gromAid !== "number") v.gromAid = 0; if (typeof v.gromAidMile !== "number") v.gromAidMile = 0; if (typeof v.gromInterrogated !== "number") v.gromInterrogated = 0; if (!v.gromTagCount || typeof v.gromTagCount !== "object") v.gromTagCount = { survivor: 0, bandit: 0, madman: 0, collab: 0 }; setup.campInit(); if (typeof v.gromSecret !== "boolean") v.gromSecret = false; }; setup.gromPerSlave = function () { var u = State.variables.pitUp || {}; return 15 + (u.cells ? 5 : 0) + (u.medic ? 3 : 0); }; setup.gromSfx = function (n) { try { var a = SimpleAudio.tracks.get(n); if (a) { a.volume(1).play(); } } catch (e) {} }; /* Grom voice-over: one mp3 per spoken line in audio/vo/grom_<id>.mp3 (a missing file is silent, or falls back to the old generic clip) */ setup.gromVoStop = function () { try { if (setup._gromA) { setup._gromA.onended = null; setup._gromA.onerror = null; setup._gromA.pause(); } } catch (e) {} setup._gromA = null; setup._gromQ = null; }; setup.gromVo = function (ids, fallback) { try { setup.gromVoStop(); var q = Array.isArray(ids) ? ids.slice() : [ids]; try { if (SimpleAudio.mute && SimpleAudio.mute()) { return; } } catch (e0) {} var played = false; var next = function (fb) { var id = q.shift(); if (!id) { if (!played && fb) { setup.gromSfx(fb); } return; } var a = new Audio("audio/vo/grom_" + id + ".mp3"), done = false; try { var mv = SimpleAudio.volume(); a.volume = (typeof mv === "number") ? Math.max(0.05, mv) : 1; } catch (e1) { a.volume = 1; } var bad = function () { if (done) return; done = true; next(fb); }; a.onerror = bad; a.onplay = function () { played = true; }; a.onended = function () { next(fb); }; setup._gromA = a; var pr = a.play(); if (pr && pr.catch) { pr.catch(bad); } }; next(fallback); } catch (e) {} }; setup.gromReplay = function () { var v = State.variables; if (v.gromVoIds) { setup.gromVo(v.gromVoIds, null); } }; setup.gromSay = function (text, ids, fallback) { var v = State.variables; v.gromFeedback = text; v.gromVoIds = ids; setup.gromVo(ids, fallback); }; $(document).on(":passageinit", function (ev) { try { if (ev.passage && ev.passage.title !== "LaborPits") { setup.gromVoStop(); } } catch (e) {} }); setup.gromHave = function (key) { var v = State.variables; return (Number((v.materials || {})[key]) || 0) + (Number((v.campStorage || {})[key]) || 0); }; setup.gromTake = function (key, n) { var v = State.variables; if (setup.gromHave(key) < n) return false; var m = Number(v.materials[key]) || 0, use = Math.min(m, n); v.materials[key] = m - use; if (n - use > 0) v.campStorage[key] = (Number(v.campStorage[key]) || 0) - (n - use); return true; }; setup.GROM_LINES = { hand: { "": "Good choice, boss. Another pair of hands for the camp. They'll pull their weight or feed the generators.", reptilian: "A scaled one on a collar. Heh. It'll haul and it'll dig, and it won't get one scrap of pity from me.", survivor: "Good. This one's just somebody the world chewed on. He works, he eats, and nobody lays a finger on him. House rule.", bandit: "A raider in my pit? Fine, boss. He sleeps chained, he eats last, and he gets one warning. Just one.", madman: "Talks to the wall, laughs at the wrong things. I'll keep him on the far shaft where he can't hurt anyone.", collab: "A human who sold routes to the scaled ones. He'll dig. And every night he'll think about who he sold." }, sell: { "": "Pleasure doing business with 'ya, Boss. Don't worry, We WON'T take good care of 'em.", reptilian: "A lizard for the lizards' own market. Strange trade, boss, but the coin is real.", survivor: "Hm. You sure? This one didn't look like a monster to me. ...Your call, boss. Your camp, your rules.", bandit: "Now THAT one I'll sell without losing sleep. The scaled ones pay well for his kind. Everyone eats something.", madman: "Can't be trusted around anyone breathing. The buyers won't mind the company. Pleasure, boss.", collab: "The lizards get their own man back. Poetic, heh. Pleasure doing business." }, gift: { "": "A gift for old Grom? Heh! I like your style, boss. This one's gonna get a special 'welcome' down in the pit!", reptilian: "A gift, and a scaled one at that. Heh. I'll find a use for it, boss.", survivor: "You're giving me this one? Heh... all right. I'll make sure he lands on his feet. Trust is worth more than scrip, boss.", bandit: "A gift! Heh, I know exactly who'll want him. Consider the favor logged, boss.", madman: "Generous. I'll keep him fed and far from the others. Consider it noted.", collab: "Oh, I'll enjoy this one. A favor owed, boss. I don't forget those." }, bulk: "Now THAT'S a proper shipment! Pleasure doing business with 'ya, Boss. We definitely won't take care of 'em!" }; /* the prisoner the next single action applies to: the cell the player clicked, otherwise the first questioned one, otherwise the first */ setup.gromSelIdx = function () { var v = State.variables, l = v.prisonerList || []; if (typeof v.pitSel === "number" && v.pitSel >= 0 && v.pitSel < l.length) return v.pitSel; for (var i = 0; i < l.length; i++) { if (l[i].t) return i; } return 0; }; /* clear every questioned prisoner out of the cells in one go: all become camp laborers, or all are sold */ setup.gromClear = function (kind) { var v = State.variables; setup.gromInit(); var keep = v.prisonerList.filter(function (x) { return !x.t; }), n = v.prisonerList.length - keep.length; if (n < 1) return; var gone = v.prisonerList.filter(function (x) { return x.t; }); v.prisonerList = keep; v.prisoners -= n; v.pitSel = -1; var harsh = gone.filter(function (x) { return x.t === "survivor"; }).length; if (harsh > 0) { setup.cLife.add(-(kind === "hand" ? 6 : 10) * harsh, kind === "hand" ? "You put forced fighters to work in the pit" : "You sold men the lizards had forced to fight"); } if (kind === "hand") { v.campSlaves = (v.campSlaves || 0) + n; setup.gromSay("Good. They have told us all they know. Down into the pit they go, all of them, and they will earn their bread.", "hand_all", "Grom_Handover"); } else { v.materials.scrip += 50 * n; v.materials.scrap += 10 * n; setup.gromSay("The cells are empty again, boss. The buyers will be pleased, and so will your pockets.", "sell_all", "Grom_Sell"); } v.gromReport = ""; try { setup.recalcStats(); } catch (e) {} }; setup.gromAct = function (kind) { var v = State.variables, tag = "", bulkHarsh = 0; setup.gromInit(); var n = (kind === "bulk") ? 5 : 1; if ((v.prisoners || 0) < n) return; if (n === 1) { var it = v.prisonerList.splice(setup.gromSelIdx(), 1)[0]; tag = (it && it.t) || ""; } else { /* a bulk delivery takes the questioned prisoners first, so the cells they fill are freed */ var order = v.prisonerList.map(function (x, i) { return i; }).sort(function (a, b) { return (v.prisonerList[b].t ? 1 : 0) - (v.prisonerList[a].t ? 1 : 0) || a - b; }).slice(0, n).sort(function (a, b) { return b - a; }); order.forEach(function (i) { if (v.prisonerList[i].t === "survivor") { bulkHarsh++; } v.prisonerList.splice(i, 1); }); } v.pitSel = -1; v.prisoners -= n; var harshOne = (tag === "survivor"); if (harshOne) { if (kind === "hand") { setup.cLife.add(-6, "A forced fighter put to work in the pit"); } else if (kind === "sell") { setup.cLife.add(-10, "You sold a man the lizards had forced to fight"); } else if (kind === "gift") { setup.cLife.add(-8, "A forced fighter given away as a gift"); } } if (bulkHarsh > 0) { setup.cLife.add(-8 * bulkHarsh, "You sold men the lizards had forced to fight"); } var L = setup.GROM_LINES; var vid = kind + "_" + (tag || "default"); if (kind === "hand") { v.campSlaves = (v.campSlaves || 0) + 1; setup.gromSay(L.hand[tag] || L.hand[""], vid, "Grom_Handover"); } else if (kind === "sell") { v.materials.scrip += 50; v.materials.scrap += 10; setup.gromSay(L.sell[tag] || L.sell[""], vid, "Grom_Sell"); } else if (kind === "gift") { v.gromAffinity = (v.gromAffinity || 0) + 15; setup.gromSay(L.gift[tag] || L.gift[""], vid, "Grom_Gift"); } else if (kind === "bulk") { v.materials.scrip += 300; v.materials.scrap += 60; setup.gromSay(L.bulk, "bulk", "Grom_Sell"); } v.gromReport = ""; try { setup.recalcStats(); } catch (e) {} }; setup.GROM_FILES = { survivor: "INTERROGATION REPORT: A human with a Reptilian brand on his arm, forced to fight their fights. Hands shaking, no blood on him, no trophies. Grom: 'Unlucky, not cruel. They put a gun in his hands and a collar on his neck. He belongs in my pit, where it is warm and the rations are real.'", reptilian: "INTERROGATION REPORT: It answers in clicks and hisses and tells Grom nothing. Grom: 'A scaled one. No words, no mercy, no questions. Nobody here will weep for it, boss.'", bandit: "INTERROGATION REPORT: A raider with a belt of dog tags. He grins when Grom asks about the last caravan he hit. Grom: 'This one has a different future. The scaled ones pay by the head, and I don't haggle over his.'", madman: "INTERROGATION REPORT: He talks to the wall and cuts for fun. Whatever the wasteland did to him, it finished the job. Grom: 'Can't keep him near anyone breathing. The buyers won't mind.'", collab: "INTERROGATION REPORT: He carries a Reptilian trade-mark and gives up patrol schedules before Grom finishes the question. Grom: 'A human working for them. Careful with this one, boss. People like him know things.'" }; setup.gromInterrogate = function () { var v = State.variables; setup.gromInit(); var it = null; for (var i = 0; i < v.prisonerList.length; i++) { if (!v.prisonerList[i].t) { it = v.prisonerList[i]; break; } } if (!it) return; if (it.g === "unknown") { it.g = (Math.random() < 0.7) ? "bandit" : "reptilian"; } var r = Math.random() * 100, tag; if (it.g === "bandit") { tag = r < 55 ? "bandit" : r < 80 ? "madman" : "collab"; } else { tag = it.g; } it.t = tag; v.gromInterrogated += 1; v.gromTagCount[tag] = (v.gromTagCount[tag] || 0) + 1; v.gromAffinity = (v.gromAffinity || 0) + 1; var extra = ""; if (v.pitUp.room) { var loot = 5 + Math.floor(Math.random() * 16); v.materials.scrip += loot; extra = " The interrogation room pays for itself: " + loot + " scrip of confiscated loot."; } var ct = (tag === "bandit" || tag === "collab") ? "bandit" : (tag === "reptilian" ? "reptilian" : ""); var vo = ["interro_start", "report_" + tag]; if (ct) { var C = setup.CAMP[ct], gain = v.pitUp.room ? 2 : 1; if (setup.campCount(ct) >= C.cap) { extra += " Grom has already marked every " + C.label.toLowerCase() + " camp he can on your map. Clear some first."; } else { v.campIntel[ct] += gain; if (v.campIntel[ct] >= C.need) { v.campIntel[ct] -= C.need; var camp = setup.campReveal(ct); extra += " INTEL COMPLETE: " + camp.name + " is now marked on your Sector Map."; vo.push("intel_" + ct); } else { extra += " Camp intel (" + C.label + "): " + v.campIntel[ct] + " / " + C.need + "."; } } } v.gromReport = setup.GROM_FILES[tag] + extra; setup.gromSay("Give me a minute with him, boss. Nobody lies to me for long.", vo, null); }; setup.GROM_TOPICS = [ { id: "pit", tier: 0, title: "The Pit", text: "Grom: 'Pit's simple. You work, you eat. Rations are the same for them as for me, and anyone who tells you different can come say it to my face. Nobody starves under my roof, boss. Not while they work.'" }, { id: "past", tier: 1, title: "Before the Veil", text: "Grom: 'I ran a gravel quarry outside Mysłowice. Forty men on my crew. The day the sky went to ash I walked eleven of them out. Eleven. Raiders took the rest at the Brynica crossing and I watched it from a ditch. That is where I learned what hard means, boss. Hard is not cruelty. Hard is deciding who gets to keep walking.'" }, { id: "lizards", tier: 2, title: "The Scaled Ones", text: "Grom: 'You wonder why I do not just put every prisoner in the ground. Because the war is not with them, it is with the scaled ones. A man who stole bread for his kids works my pit and eats. A man who skinned a family for their boots? The lizards pay for that kind. Fair price. Everybody eats something. Do not look at me like that, boss. I sleep fine.'" }, { id: "buyers", tier: 3, title: "The Buyers", text: "Grom: 'A courier came last month. Tall, quiet, wearing a human's coat. Said my contribution was noted. Noted. Heh. Keep a drone-shaped thought in the back of your head, boss. Men like me do not get thanked, we get audited. And if my medicine shelf looks light, do not ask where it went. Some doors I leave open for people nobody else counts.'" } ]; setup.gromTalk = function (id) { var v = State.variables, tp = null, tr = setup.gromTier(); setup.gromInit(); setup.GROM_TOPICS.forEach(function (x) { if (x.id === id) tp = x; }); if (!tp || tr.i < tp.tier) return; v.gromReport = tp.text; if (v.gromTalkDay !== v.day) { v.gromTalkDay = v.day; v.gromAffinity = (v.gromAffinity || 0) + 3; setup.gromSay("Good to talk, boss. Not many do.", ["talk_first", "talk_" + id], null); } else { setup.gromSay("Ask what you like. I already told you the rest.", ["talk_again", "talk_" + id], null); } }; setup.GROM_PACKS = { med: { key: "medicine", n: 3, aff: 4, aid: 3 }, ration: { key: "food", n: 10, aff: 3, aid: 2 }, herb: { key: "medicalHerb", n: 5, aff: 2, aid: 2 } }; setup.GROM_MILES = [ { at: 10, say: " Grom presses five lengths of rope into your hand: 'For your hunts. Do not ask.'", vo: "For your hunts. Do not ask.", fn: function (v) { v.materials.rope = (v.materials.rope || 0) + 5; } }, { at: 30, say: " Grom pays you back 150 scrip from his own cut: 'Do not argue. Aid is not charity, it is an investment.'", vo: "Do not argue. Aid is not charity, it is an investment.", fn: function (v) { v.materials.scrip += 150; } }, { at: 60, say: " Grom looks at you for a long time: 'There are places out there, boss. Hidden shelters. Mine. If something happens to me, they need someone who already knows.'", vo: "There are places out there, boss. Hidden shelters. Mine. If something happens to me, they need someone who already knows.", fn: function (v) { v.gromSecret = true; } } ]; setup.gromAidPack = function (id) { var v = State.variables, p = setup.GROM_PACKS[id]; setup.gromInit(); if (!p || !setup.gromTake(p.key, p.n)) { v.gromReport = "You do not have enough for that pack."; return; } var aff = p.aff + (v.pitUp.medic ? 1 : 0); v.gromAffinity = (v.gromAffinity || 0) + aff; v.gromAid += p.aid; setup.cLife.add(2, "You sent aid to people in need"); var rep = "Pack delivered. Grom takes it without a word. +" + aff + " affinity, +" + p.aid + " aid."; var first = (v.gromAid <= p.aid), ids = [first ? "aid_first" : "aid_more"]; var txt = first ? "I did not ask for this. And I will not ask where you heard about it. Thank you." : "Another pack gone into the dark. Somebody will cough a little less tonight thanks to you, boss."; var M = setup.GROM_MILES; if (v.gromAidMile < M.length && v.gromAid >= M[v.gromAidMile].at) { rep += M[v.gromAidMile].say; M[v.gromAidMile].fn(v); ids.push("mile_" + (v.gromAidMile + 1)); v.gromAidMile += 1; } v.gromReport = rep; setup.gromSay(txt, ids, null); }; setup.GROM_BUILDS = { cells: { cost: 300, med: 0, name: "Reinforced Cells", note: "Warmer, drier cells. Each pit laborer produces +5 scrip per day." }, medic: { cost: 400, med: 5, name: "Medic Corner", note: "A cot, bandages and clean water. Each laborer produces +3 scrip per day and Quiet Aid packs give +1 affinity." }, room: { cost: 500, med: 0, name: "Interrogation Room", note: "A quiet room with a lamp and a chair. Each interrogation brings 5-20 scrip of confiscated loot and doubles the camp intel you pull from bandits and Reptilians." } }; setup.gromBuild = function (id) { var v = State.variables, b = setup.GROM_BUILDS[id]; setup.gromInit(); if (!b || v.pitUp[id]) return; if ((v.materials.scrip || 0) < b.cost) { v.gromReport = "Not enough scrip."; return; } if (b.med && setup.gromHave("medicine") < b.med) { v.gromReport = "You need " + b.med + " medicine for this."; return; } v.materials.scrip -= b.cost; if (b.med) setup.gromTake("medicine", b.med); v.pitUp[id] = true; v.gromAffinity = (v.gromAffinity || 0) + 5; v.gromReport = b.name + " built."; setup.gromSay("Good work, boss. The pit runs a little better now.", "build_done", null); }; /* token spending that keeps the anti-cheat checksum in step (a shop purchase must never look like cheating) */ setup.spendTok = function (n) { var v = State.variables; v.syndicateTokens = Math.max(0, (Number(v.syndicateTokens) || 0) - (Number(n) || 0)); v.token_checksum = (v.syndicateTokens * 73) + 1492; }; /* ===== ADMIN GRANT (owner only): give Syndicate Tokens / VIP after a manual PayPal payment ===== */ setup.adminGrant = function (mode) { var V = State.variables, out = document.getElementById('ag-out'); function say(t, ok) { if (out) { out.style.color = ok ? '#86efac' : '#fca5a5'; out.textContent = t; } } if (!(String(V.playerName || '').toLowerCase() === 'nakman' || V.isAdmin === true)) { say('Owner account only.', false); return; } var target = (document.getElementById('ag-target').value || '').trim(); if (!target) { say('Type the player ID first.', false); return; } var body = { playerName: V.playerName, password: V.password, target: target, note: (document.getElementById('ag-note').value || '').trim() }; if (mode === 'tokens') { var n = parseInt(document.getElementById('ag-amount').value, 10); if (!n) { say('Type a token amount (negative removes tokens).', false); return; } body.tokens = n; } else if (mode === 'vipOn') { body.vip = true; } else if (mode === 'vipOff') { body.vip = false; } say('Working...', true); fetch(window.API_URL + '/api/admin/grant', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }) .then(function (r) { return r.json(); }) .then(function (d) { if (d.status !== 'success') { say(d.message || 'Failed.', false); return; } var a = d.account || {}; say((d.applied && d.applied.length ? 'DONE: ' + d.applied.join(', ') + '\n' : '') + a.name + ' | level ' + a.level + ' | VIP: ' + (a.vip ? 'YES' : 'no') + (a.founder ? ' | FOUNDER' : '') + ' | tokens: ' + a.tokens + (a.pendingTokens ? ' (+' + a.pendingTokens + ' arrives at their next save/login)' : ''), true); }) .catch(function () { say('Server not reachable.', false); }); }; /* ===== ADMIN EVENT (owner only): reset every player's duel limits and cooldowns ===== */ setup.adminResetDuels = function () { var V = State.variables, out = document.getElementById('rd-out'); function say(t, ok) { if (out) { out.style.color = ok ? '#86efac' : '#fca5a5'; out.textContent = t; } } if (!(String(V.playerName || '').toLowerCase() === 'nakman' || V.isAdmin === true)) { say('Owner account only.', false); return; } if (!window.confirm('Reset "Duels Left Today" and all duel cooldowns for EVERY player?')) { say('Cancelled.', true); return; } say('Working...', true); fetch(window.API_URL + '/api/admin/reset_duels', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ playerName: V.playerName, password: V.password }) }) .then(function (r) { return r.text().then(function (x) { var d = null; try { d = JSON.parse(x); } catch (e) { /* not JSON */ } return d || { status: 'error', message: 'The server answered HTTP ' + r.status + (r.status === 404 ? ' (the route does not exist on the server yet, redeploy save_api.js).' : '.') }; }); }) .then(function (d) { if (d.status !== 'success') { say(d.message || 'Failed.', false); return; } say('DONE: ' + d.arena + ' arena records reset, ' + d.chat + ' recent chat duels released. Everyone has full duels and no cooldowns.', true); }) .catch(function () { say('Server not reachable, or the route is missing there (redeploy save_api.js).', false); }); }; /* ===== ADMIN DELETE (owner only): look an account up, reset its character or delete the whole account ===== */ setup.adminDelete = function (mode) { var V = State.variables, out = document.getElementById('xd-out'); function say(t, ok) { if (out) { out.style.color = ok ? '#86efac' : '#fca5a5'; out.textContent = t; } } if (!(String(V.playerName || '').toLowerCase() === 'nakman' || V.isAdmin === true)) { say('Owner account only.', false); return; } var target = (document.getElementById('xd-target').value || '').trim(); if (!target) { say('Type the player ID first.', false); return; } var base = { playerName: V.playerName, password: V.password, target: target }; function post(path, body) { return fetch(window.API_URL + path, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }).then(function (r) { return r.text().then(function (t) { var d = null; try { d = JSON.parse(t); } catch (e) { /* not JSON */ } if (d) { return d; } return { status: 'error', message: 'The server answered HTTP ' + r.status + (r.status === 404 ? ' (the route ' + path + ' does not exist on the server yet).' : ' with something that is not JSON.') }; }); }); } function netFail(path) { say('Could not reach ' + path + '. If the server is running, this route is probably missing there (the browser hides a 404 from a route without CORS headers), or the request was blocked.', false); } if (mode === 'look') { say('Looking up...', true); post('/api/admin/grant', base).then(function (d) { if (d.status !== 'success') { say(d.message || 'No such account.', false); return; } var a = d.account || {}; say(a.name + ' | level ' + a.level + ' | VIP: ' + (a.vip ? 'YES' : 'no') + (a.founder ? ' | FOUNDER' : '') + ' | tokens: ' + a.tokens, true); }).catch(function () { netFail('/api/admin/grant'); }); return; } if (target.toLowerCase() === String(V.playerName).toLowerCase()) { say('You cannot delete your own account here.', false); return; } var confirmTxt = (document.getElementById('xd-confirm').value || '').trim(); if (confirmTxt.toLowerCase() !== target.toLowerCase()) { say('Type the same ID in the confirmation box to continue.', false); return; } var what = mode === 'account' ? 'DELETE THE WHOLE ACCOUNT' : 'RESET THE CHARACTER (the account stays)'; if (!window.confirm(what + ' for "' + target + '"?\nThis cannot be undone.')) { say('Cancelled.', true); return; } say('Working...', true); post('/api/admin/delete', Object.assign({ mode: mode === 'account' ? 'account' : 'character' }, base)).then(function (d) { if (d.status !== 'success') { say(d.message || 'Failed.', false); return; } say('DONE: ' + (d.message || (mode === 'account' ? 'account deleted.' : 'character reset.')), true); document.getElementById('xd-confirm').value = ''; }).catch(function () { netFail('/api/admin/delete'); }); }; /* ===== SIDE LOGISTICS: sell any number of sets at once (the server does the sale, see /api/side_sell) ===== */ setup.sideBulk = (function () { var busy = false; function box(el) { return el.closest(".sl-bulk"); } function maxOf(b) { return Math.max(1, parseInt(b.querySelector(".sl-max").textContent, 10) || 1); } function count(b) { var i = b.querySelector("input"), n = parseInt(i.value, 10); if (!(n >= 1)) { n = 1; } return Math.min(n, maxOf(b)); } function label(b) { var n = count(b), pay = parseInt(b.getAttribute("data-pay"), 10); b.querySelector(".sl-go").textContent = "SELL ×" + n + " (+" + (n * pay) + " SCRIP)"; } function msg(b, t) { var m = b.querySelector(".sl-msg"); if (m) { m.textContent = t || ""; } } /* run a change and put every scroll position back afterwards (a redraw or a layout change must never throw the player to the top) */ function keepScroll(fn) { var d = document.getElementById("ui-dialog-body"), w = document.getElementById("ui-dialog"), y1 = d ? d.scrollTop : 0, y2 = w ? w.scrollTop : 0, y3 = window.pageYOffset || 0; fn(); function back() { if (d && d.scrollTop !== y1) { d.scrollTop = y1; } if (w && w.scrollTop !== y2) { w.scrollTop = y2; } if (window.pageYOffset !== y3) { window.scrollTo(0, y3); } } back(); if (window.requestAnimationFrame) { window.requestAnimationFrame(back); } setTimeout(back, 60); setTimeout(back, 250); } /* a reply that is not JSON (for example a 404 page from a server that lacks the route) becomes a clear message instead of "could not reach" */ function post(path, body) { return fetch(window.API_URL + path, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) }).then(function (r) { return r.text().then(function (x) { var d = null; try { d = JSON.parse(x); } catch (e) { /* not JSON */ } return d || { status: "error", message: r.status === 404 ? "The server does not know this sale yet. The newest save_api.js must be uploaded and restarted." : "The server answered HTTP " + r.status + "." }; }); }); } function save() { var V = State.variables; return post("/api/save_player_data", { playerName: V.playerName, password: V.password, passage: State.passage || "Camp", variables: V, timestamp: new Date().toISOString() }) .then(function (d) { if (d && d.corrected && typeof d.corrected === "object") { Object.keys(d.corrected).forEach(function (k) { V[k] = d.corrected[k]; }); } }); } return { upd: function (el) { var b = box(el), i = b.querySelector("input"); keepScroll(function () { var n = parseInt(i.value, 10); if (!(n >= 1)) { i.value = 1; } else if (n > maxOf(b)) { i.value = maxOf(b); } label(b); }); }, max: function (el) { var b = box(el); keepScroll(function () { b.querySelector("input").value = maxOf(b); label(b); }); }, sell: function (el) { var b = box(el), n = count(b), k = b.getAttribute("data-k"), V = State.variables; if (busy) { return; } busy = true; msg(b, "Selling..."); save().then(function () { return post("/api/side_sell", { playerName: V.playerName, password: V.password, item: k, bundles: n }); }).then(function (d) { busy = false; if (!d || d.status !== "success") { msg(b, (d && d.message) || "The sale failed."); return; } V.materials = d.materials; V.campStorage = d.campStorage; try { if (window.setup && window.setup.updateSidebar) { window.setup.updateSidebar(); } } catch (e1) { /* ignore */ } var body = document.getElementById("ui-dialog-body"); if (body) { keepScroll(function () { $(body).empty().wiki(Story.get("Quests").processText()); }); } }).catch(function () { busy = false; msg(b, "Could not reach the server. Check your connection and try again."); }); } }; })(); /* ===== DEV CONSOLE (owner only): every account, every character and bag, and gifts of resources and items ===== */ setup.adminConsole = (function () { var C = { rows: [], sel: null, data: null }; var RARITY = ["Common", "Uncommon", "Rare", "Epic", "Legendary", "Godlike"]; var RCOL = { Common: "#cbd5e1", Uncommon: "#6ee7b7", Rare: "#7dd3fc", Epic: "#d8b4fe", Legendary: "#fde68a", Godlike: "#fca5a5" }; var RES = ["tokens", "scrip", "caps", "scrap", "wood", "cloth", "iron", "techData", "medicine", "food", "rawMeat", "berries", "water", "ammo", "rope"]; var SLOTS = [["headgear", "Headgear", ["headgear"]], ["chest", "Chest", ["chest"]], ["legs", "Legs", ["legs"]], ["boots", "Boots", ["boots"]], ["gloves", "Gloves", ["gloves"]], ["weapon", "Weapons", ["rightHand"]], ["offHand", "Off-hand", ["offHand"]], ["trinket", "Trinkets", ["trinket1", "trinket2"]], ["ring", "Rings", ["ring1", "ring2"]], ["backpack", "Backpacks", ["backpack"]]]; function V() { return State.variables; } function allowed() { var v = V(); return String(v.playerName || "").toLowerCase() === "nakman" || v.isAdmin === true; } function $id(i) { return document.getElementById(i); } function esc(x) { return String(x == null ? "" : x).replace(/[&<>"']/g, function (c) { return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]; }); } function say(m, ok) { var o = $id("dc-out"); if (o) { o.style.color = ok ? "#86efac" : "#fca5a5"; o.textContent = m; } } function post(path, extra) { var body = Object.assign({ playerName: V().playerName, password: V().password }, extra || {}); return fetch(window.API_URL + path, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) }).then(function (r) { return r.text().then(function (x) { var d = null; try { d = JSON.parse(x); } catch (e) { /* not JSON */ } return d || { status: "error", message: "The server answered HTTP " + r.status + (r.status === 404 ? " (the route " + path + " is missing there: upload the newest save_api.js and restart it)." : " with something that is not JSON.") }; }); }); } function when(d) { if (!d) { return "-"; } var x = new Date(d); return isNaN(x) ? "-" : x.toISOString().slice(0, 16).replace("T", " "); } function rarityOf(name) { for (var i = RARITY.length - 1; i >= 0; i--) { if (String(name).indexOf(RARITY[i] + " ") === 0) { return RARITY[i]; } } return "Common"; } function itemHtml(n) { return "<span style='color:" + RCOL[rarityOf(n)] + ";'>" + esc(n) + "</span>"; } function box(title, inner) { return "<div style='border:1px solid #334155; background:#0b1220; padding:12px; margin-bottom:12px; border-radius:4px;'><div style='color:#7dd3fc; font-weight:900; letter-spacing:2px; font-size:0.78rem; margin-bottom:8px;'>" + title + "</div>" + inner + "</div>"; } var INP = "padding:8px; background:#0f172a; color:#f1f5f9; border:1px solid #475569; font-family:inherit; font-size:0.85rem;"; var BTN = "padding:8px 14px; background:#0c4a6e; color:#e0f2fe; border:1px solid #38bdf8; cursor:pointer; font-family:inherit; font-weight:700;"; function renderList() { var q = ($id("dc-q") ? $id("dc-q").value : "").trim().toLowerCase(), host = $id("dc-list"); if (!host) { return; } var rows = C.rows.filter(function (r) { return !q || r.name.toLowerCase().indexOf(q) >= 0 || (r.character || "").toLowerCase().indexOf(q) >= 0; }); var h = "<table style='width:100%; border-collapse:collapse; font-size:0.82rem;'><tr style='color:#94a3b8; text-align:left;'><th style='padding:4px;'>CHARACTER</th><th>USERNAME</th><th>LVL</th><th>CLASS</th><th>SCRIP</th><th>LAST SAVE (UTC)</th></tr>"; rows.forEach(function (r) { var sel = C.sel && C.sel.toLowerCase() === r.name.toLowerCase(); h += "<tr data-u='" + esc(r.name) + "' style='cursor:pointer; border-top:1px solid #1e293b; " + (sel ? "background:#0c4a6e;" : "") + "'>" + "<td style='padding:5px 4px; color:" + (r.hasCharacter ? "#f1f5f9" : "#64748b") + ";'>" + (r.hasCharacter ? esc(r.character) : "(no character)") + (r.founder ? " <span style='color:#fde68a;'>[F]</span>" : "") + (r.vip ? " <span style='color:#6ee7b7;'>[VIP]</span>" : "") + (r.adminFlag ? " <span style='color:#fca5a5;'>[ADMIN FLAG]</span>" : "") + "</td>" + "<td style='color:#7dd3fc;'>" + esc(r.name) + "</td><td>" + (r.level || "-") + "</td><td>" + esc(r.cls || "-") + "</td><td>" + (r.scrip || 0) + "</td><td style='color:#94a3b8;'>" + when(r.last) + "</td></tr>"; }); h += "</table>"; host.innerHTML = rows.length ? h : "<div style='color:#94a3b8; padding:8px;'>Nobody matches.</div>"; $id("dc-count").textContent = rows.length + " of " + C.rows.length + " accounts"; Array.prototype.forEach.call(host.querySelectorAll("tr[data-u]"), function (tr) { tr.onclick = function () { C.select(tr.getAttribute("data-u")); }; }); } C.refresh = function () { say("Loading accounts...", true); post("/api/admin/players").then(function (d) { if (d.status !== "success") { say(d.message || "Failed.", false); return; } C.rows = d.players || []; renderList(); say("Loaded " + C.rows.length + " accounts. Server build: " + (d.build || "?"), true); }).catch(function () { say("Server not reachable.", false); }); }; function itemOptions(slotKey) { var db = V().itemDatabase || {}, seen = {}, out = []; var def = SLOTS.filter(function (s) { return s[0] === slotKey; })[0]; (def ? def[2] : []).forEach(function (k) { (db[k] || (k === "rightHand" ? (setup.RIGHT_HAND || []) : [])).forEach(function (n) { if (!seen[n]) { seen[n] = 1; out.push(n); } }); }); return out; } function fillItems() { var sl = $id("dc-slot").value, o = itemOptions(sl); $id("dc-base").innerHTML = o.map(function (n) { return "<option value='" + esc(n) + "'>" + esc(n) + "</option>"; }).join(""); } function renderDetail() { var host = $id("dc-detail"), a = C.data; if (!host || !a) { return; } var v = a.vars || {}, mats = v.materials || {}, camp = v.campStorage || {}, eq = v.equipment || {}, bag = Array.isArray(v.gearInventory) ? v.gearInventory : []; var h = "<div style='font-size:1.15rem; color:#f1f5f9; font-weight:900;'>" + esc(a.hasCharacter ? v.characterName : "(no character)") + " <span style='font-size:0.8rem; color:#7dd3fc; font-weight:400;'>username: " + esc(a.name) + "</span>" + (a.founder ? " <span style='color:#fde68a;'>[FOUNDER]</span>" : "") + (a.vip ? " <span style='color:#6ee7b7;'>[VIP]</span>" : "") + (a.admin ? " <span style='color:#fca5a5;'>[ADMIN]</span>" : "") + "</div>" + "<div style='color:#94a3b8; font-size:0.78rem; margin-bottom:10px;'>last save " + when(a.last) + " UTC | created " + when(a.created) + " UTC | now at: " + esc(a.passage || "-") + ((a.pending.tokens || a.pending.scrip || a.pending.gifts) ? " | waiting for their next save: " + [a.pending.tokens ? a.pending.tokens + " tokens" : "", a.pending.scrip ? a.pending.scrip + " scrip" : "", a.pending.gifts ? a.pending.gifts + " gift(s)" : ""].filter(Boolean).join(", ") : "") + "</div>"; if (a.admin && String(a.name).toLowerCase() !== "nakman") { h += "<div style='margin-bottom:10px;'><button type='button' id='dc-rmone' style='" + BTN + " background:#450a0a; color:#fee2e2; border-color:#f87171;'>REMOVE ADMIN FLAG FROM " + esc(a.name) + "</button></div>"; } if (!a.hasCharacter) { host.innerHTML = h + "<div style='color:#94a3b8;'>This account has not created a character yet.</div>"; if ($id("dc-rmone")) { $id("dc-rmone").onclick = function () { C.removeAdmin(a.name); }; } return; } var stats = [["Level", v.level], ["XP", v.xp + " / " + v.xpMax], ["Skill points", v.skillPoints], ["HP", v.hp + " / " + v.maxHp], ["Stamina", v.stamina + " / " + v.maxStamina], ["Class", v.playerClass], ["Background", v.playerBackground], ["Gender", v.playerGender], ["Day", v.day], ["Tokens", v.syndicateTokens], ["Survivors", v.survivors], ["Talent ranks", a.talRanks], ["Talent reset waiting", a.pending.talReset ? "yes" : "no"]]; h += box("CHARACTER", "<div style='display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:6px; font-size:0.82rem;'>" + stats.filter(function (s) { return s[1] !== undefined && s[1] !== "undefined / undefined"; }).map(function (s) { return "<div><span style='color:#94a3b8;'>" + s[0] + "</span><br><b style='color:#f1f5f9;'>" + esc(s[1]) + "</b></div>"; }).join("") + "</div>"); h += box("EQUIPPED", "<div style='display:grid; grid-template-columns:repeat(auto-fill,minmax(210px,1fr)); gap:4px; font-size:0.82rem;'>" + Object.keys(eq).map(function (k) { return "<div><span style='color:#94a3b8;'>" + esc(k) + ":</span> " + (eq[k] && eq[k] !== "None" ? itemHtml(eq[k]) : "<span style='color:#64748b;'>none</span>") + "</div>"; }).join("") + "</div>"); h += box("BAG (" + bag.length + " items)", bag.length ? "<div style='max-height:200px; overflow:auto; font-size:0.82rem; columns:2 220px;'>" + bag.map(function (n) { return "<div>" + itemHtml(n) + "</div>"; }).join("") + "</div>" : "<span style='color:#64748b;'>empty</span>"); var keys = {}; Object.keys(mats).concat(Object.keys(camp)).forEach(function (k) { keys[k] = 1; }); h += box("RESOURCES (carried | camp storage)", "<div style='display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:4px; font-size:0.82rem;'>" + Object.keys(keys).map(function (k) { return "<div><span style='color:#94a3b8;'>" + esc(k) + "</span> <b style='color:#f1f5f9;'>" + (mats[k] || 0) + "</b> <span style='color:#64748b;'>| " + (camp[k] || 0) + "</span></div>"; }).join("") + "</div>"); /* give panel */ h += box("GIVE RESOURCES", "<div style='display:flex; gap:8px; flex-wrap:wrap;'><select id='dc-res' style='" + INP + "'>" + RES.map(function (k) { return "<option value='" + k + "'>" + k + "</option>"; }).join("") + "</select>" + "<input id='dc-amt' type='number' step='1' placeholder='amount (negative removes)' style='" + INP + " width:200px;'><button type='button' id='dc-giveres' style='" + BTN + "'>GIVE</button></div>"); h += box("GIVE ITEMS", "<div style='display:flex; gap:8px; flex-wrap:wrap; margin-bottom:8px;'><select id='dc-rar' style='" + INP + "'>" + RARITY.map(function (r) { return "<option value='" + r + "'>" + r + "</option>"; }).join("") + "</select>" + "<select id='dc-slot' style='" + INP + "'>" + SLOTS.map(function (s) { return "<option value='" + s[0] + "'>" + s[1] + "</option>"; }).join("") + "</select>" + "<select id='dc-base' style='" + INP + " min-width:200px;'></select>" + "<input id='dc-qty' type='number' min='1' max='50' value='1' style='" + INP + " width:70px;'><button type='button' id='dc-giveitem' style='" + BTN + "'>GIVE</button></div>" + "<div style='display:flex; gap:8px; flex-wrap:wrap;'><input id='dc-exact' type='text' maxlength='70' placeholder='or type an exact item name' style='" + INP + " flex:1; min-width:220px;'><span style='color:#94a3b8; font-size:0.75rem; align-self:center;'>if filled, it is used instead of the three lists</span></div>"); h += "<details style='margin-bottom:12px;'><summary style='cursor:pointer; color:#94a3b8; font-size:0.8rem;'>ALL SAVED VARIABLES (raw, no passwords)</summary><pre style='max-height:300px; overflow:auto; font-size:0.72rem; color:#cbd5e1; background:#050a14; padding:8px; white-space:pre-wrap;'>" + esc(JSON.stringify(v, null, 1)) + "</pre></details>"; host.innerHTML = h; if ($id("dc-rmone")) { $id("dc-rmone").onclick = function () { C.removeAdmin(a.name); }; } fillItems(); $id("dc-slot").onchange = fillItems; $id("dc-giveres").onclick = function () { var n = parseInt($id("dc-amt").value, 10), k = $id("dc-res").value; if (!n) { say("Type an amount first.", false); return; } var r = {}; r[k] = n; C.give({ resources: r }); }; $id("dc-giveitem").onclick = function () { var q = parseInt($id("dc-qty").value, 10) || 1, ex = ($id("dc-exact").value || "").trim(); var name = ex || ($id("dc-rar").value + " " + $id("dc-base").value); if (!ex && !$id("dc-base").value) { say("Pick an item first.", false); return; } C.give({ items: [{ name: name, qty: q }] }); }; } C.give = function (what) { if (!C.sel) { say("Pick a player first.", false); return; } say("Sending...", true); post("/api/admin/give", Object.assign({ target: C.sel }, what)).then(function (d) { if (d.status !== "success") { say(d.message || "Failed.", false); return; } say("DONE for " + d.target + ": " + d.applied.join(", ") + ". It arrives at their next save or login (a few seconds if they are online).", true); C.select(C.sel, true); }).catch(function () { say("Server not reachable.", false); }); }; C.select = function (u, keep) { C.sel = u; renderList(); if (!keep) { say("Opening " + u + "...", true); } post("/api/admin/player", { target: u }).then(function (d) { if (d.status !== "success") { say(d.message || "Failed.", false); return; } C.data = d.account; renderDetail(); if (!keep) { say("Opened " + d.account.name + ".", true); } }).catch(function () { say("Server not reachable.", false); }); }; C.removeAdmin = function (who) { var msg = who ? 'Remove the admin flag from "' + who + '"?' : "Remove the admin flag from EVERY account except yours?"; if (!window.confirm(msg)) { say("Cancelled.", true); return; } say("Working...", true); post("/api/admin/remove_admin", who ? { target: who } : {}).then(function (d) { if (d.status !== "success") { say(d.message || "Failed.", false); return; } say("DONE: the admin flag was removed from " + d.removed + " account(s).", true); C.refresh(); if (C.sel) { C.select(C.sel, true); } }).catch(function () { say("Server not reachable.", false); }); }; C.resetTalents = function () { if (!window.confirm("Reset the talents of EVERY normal player?\nEach one gets their talent points back (1 per level) at their next save or login. Your own account is not touched.")) { say("Cancelled.", true); return; } say("Working...", true); post("/api/admin/reset_talents").then(function (d) { if (d.status !== "success") { say(d.message || "Failed.", false); return; } say("DONE (server build " + (d.build || "?") + "): " + d.accounts + " accounts reset, " + d.hadTalents + " of them had talents. Each now has 0 talents and (level - 1) points. Players who are online get it at their next click.", true); }).catch(function () { say("Server not reachable.", false); }); }; C.init = function () { if (!allowed()) { say("Owner account only.", false); return; } if ($id("dc-q")) { $id("dc-q").oninput = renderList; } if ($id("dc-reload")) { $id("dc-reload").onclick = C.refresh; } if ($id("dc-talreset")) { $id("dc-talreset").onclick = C.resetTalents; } if ($id("dc-rmadmin")) { $id("dc-rmadmin").onclick = function () { C.removeAdmin(""); }; } C.refresh(); }; return C; })(); /* ===== DROP RATES: common and uncommon stay the bread and butter, everything above them is rare ===== */ setup.DROPS = { normal: { Godlike: 0.5, Legendary: 1.0, Epic: 2.5, Rare: 7.0, Uncommon: 29.67 }, /* the rest is Common (59.33%) */ boss: { Godlike: 2.0, Legendary: 10.0, Epic: 33.0 } /* a boss always drops at least Rare */ }; setup.ensureRarities = function () { let v = State.variables; if (!v.rarities) { return; } if (!v.rarities.Godlike) { v.rarities.Godlike = { mult: 3.5, color: "#ef4444" }; } }; setup.rollRarity = function (kind) { setup.ensureRarities(); let v = State.variables, tbl = setup.DROPS[kind] || setup.DROPS.normal; let boost = 1 + Math.min(100, Math.max(0, v.playerLuck || 0)) / 100; /* Luck makes the rare tiers up to 2x as likely */ let r = Math.random() * 100, acc = 0, order = ["Godlike", "Legendary", "Epic", "Rare", "Uncommon"]; for (let i = 0; i < order.length; i++) { let name = order[i], pct = tbl[name]; if (pct === undefined) { continue; } if (name === "Godlike" || name === "Legendary" || name === "Epic" || name === "Rare") { pct *= boost; } acc += pct; if (r < acc) { return name; } } return (kind === "boss") ? "Rare" : "Common"; }; setup.generateLoot = function(category) { let v = State.variables; if (setup.ensureItemDb) { setup.ensureItemDb(); } let db = v.itemDatabase; let categories = ["headgear", "chest", "legs", "boots", "gloves", "rightHand", "offHand", "backpack", "trinket1", "ring1"]; if (!category || !db[category]) { category = categories[Math.floor(Math.random() * categories.length)]; } let items = db[category]; let baseItem = items[Math.floor(Math.random() * items.length)]; let rarity = setup.rollRarity("normal"); let finalItem = rarity + " " + baseItem; if (!v.gearInventory) v.gearInventory = []; v.gearInventory.push(finalItem); return finalItem; }; /* PRICES WERE RAISED TO STRAIN THE GAME ECONOMY */ setup.getDailyMerchantStock = function() { let masterPool = [ { id: "med", name: "Antibiotics & Medicine", cost: 75, type: "medicine", qty: 1, desc: "Essential for treating severe radiation sickness." }, { id: "ammo", name: "Military Ammo Pack", cost: 120, type: "ammo", qty: 10, desc: "Standard 5.56mm pre-war rounds." }, { id: "food", name: "Canned Rations", cost: 45, type: "food", qty: 3, desc: "Preserved pre-war nutrition." }, { id: "water", name: "Purified Water", cost: 50, type: "water", qty: 2, desc: "Clean, non-irradiated hydration flask." }, { id: "iron", name: "Reinforced Iron Plate", cost: 85, type: "iron", qty: 2, desc: "Heavy industrial metal." } ]; let shuffled = masterPool.sort(function() { return 0.5 - Math.random(); }); return shuffled.slice(0, 2); }; setup.randomizeLocations = function() { let v = State.variables; if (v.lastMerchantDay === v.day && v.locTrader && v.merchantStock) { return; } let convoyPool = [ {top: "22.9%", left: "23.2%"}, {top: "70.8%", left: "30.4%"}, {top: "81.2%", left: "49.1%"} ]; let traderPool = [ {top: "14.6%", left: "55.4%"}, {top: "37.5%", left: "73.2%"}, {top: "89.6%", left: "57.1%"} ]; let c = convoyPool[Math.floor(Math.random() * convoyPool.length)]; let t = traderPool[Math.floor(Math.random() * traderPool.length)]; v.locConvoy = { top: c.top, left: c.left, name: "FALLEN MILITARY CONVOY" }; v.locTrader = { top: t.top, left: t.left, name: "DRIFTER CARAVAN" }; v.merchantStock = setup.getDailyMerchantStock(); v.lastMerchantDay = v.day; }; <</script>> <<run setup.randomizeLocations()>> <</nobr>>
<<nobr>> <<set $quest_slaughterhouse to "completed_svietlok">> <<set $svietlok_buff to true>> <<set $sanctuaryPop to ($sanctuaryPop || 0) + 50>> <<set $survivors to ($survivors || 0) + 50>> <<set $playerTitle to "Svietlok">> <<set $svietlok_legend_alive to true>> <<audio "svietlok_chant" play>> <div style="background: rgba(5, 20, 30, 0.95); border: 2px solid #34d399; padding: 30px; font-family: 'Courier New', monospace; text-align: center;"> <h2 style="color: #34d399; letter-spacing: 4px; text-shadow: 0 0 15px #34d399;">[ THE LEGEND IS BORN ]</h2> <!-- SVIETLOK ENDING IMAGE --> <div style="margin: 20px 0;"> <img src="images/quests/svietlok_ending.jpg" style="max-width: 100%; border: 2px solid #34d399; border-radius: 4px; box-shadow: 0 0 20px rgba(52, 211, 153, 0.4);"> </div> <p style="color: #cbd5e1; font-size: 1.1rem; line-height: 1.8;"> You shattered the locks on the cages. 50 people flooded out in tears, too afraid to even touch you. You directed them to Sanctuary. <br><br> You are no longer just a mercenary. They will spread your name as "Świetlok". You now have 50 loyal followers ("Watchers of Svietlok") in your camp. They revere you so deeply that they will work with <strong>25% more efficiency</strong>. </p> <div style="margin: 30px 0; color: #a3e635; font-weight: bold; font-size: 1.2rem;"> > TITLE "SVIETLOK" EARNED.<br> > +50 FOLLOWERS ADDED.<br> > DAILY PRODUCTION INCREASED BY 25%. </div> @@color:#050a0f;background:#34d399;font-weight:bold;padding:12px 25px;display:inline-block;border-radius:2px; <<link "RETURN TO SANCTUARY" "Camp">><</link>>@@ </div> <</nobr>>
<<nobr>> <<setMenu "Tutorial">> <<if ndef $materials>><<set $materials to {}>><</if>> <<if ndef $materials.scrap>><<set $materials.scrap to 0>><</if>> <<if ndef $materials.wood>><<set $materials.wood to 0>><</if>> <<if ndef $materials.water>><<set $materials.water to 0>><</if>> <<if ndef $materials.berries>><<set $materials.berries to 0>><</if>> <<if ndef $stamina>><<set $stamina to 100>><</if>> <<if ndef $tutForageStarted>><<set $tutForageStarted to false>><</if>> <div class="tut-world"> <div class="tut-card"> <div class="tut-step">TRAINING 1 / 4</div> <div class="tut-title">FORAGING</div> <div class="tut-bar"><i style="width:25%"></i></div> <p class="tut-sub">Everything out here is worth something. Learn to spot it fast.</p> <div class="tut-obj"><b>OBJECTIVE:</b> Click the items that pop up before the timer runs out. Collect <b>5 Wood</b> and <b>2 Scrap</b> to build your first tool. Each round lasts 2 seconds, and you can run as many rounds as you need.</div> <div class="tut-chips"> <div class="tut-chip" id="chip-wood">🪵 WOOD <span id="req-wood"><<print ($materials.wood || 0)>> / 5</span></div> <div class="tut-chip" id="chip-scrap">🔩 SCRAP <span id="req-scrap"><<print ($materials.scrap || 0)>> / 2</span></div> </div> <div class="tut-screen forage" id="forage-game-box"> <div class="tut-timer" id="f-timer">2.0s</div> <div id="f-layer" style="position:absolute; inset:0; z-index:30;"></div> <<if not $tutForageStarted>> <div class="tut-overlay" id="f-ready"> <h3>READY?</h3> <p>Items appear all over the screen for 2 seconds.</p> <p>Click wood and scrap first.</p> <div class="tut-btn inline" id="f-start" style="margin-top:14px;">START FORAGING</div> </div> <</if>> </div> <div class="tut-act hidden" id="back-to-tut2-btn"> <<link "PROCEED TO TRAINING 2: FABRICATION" "Tutorial 2">><<run window.setup.updateSidebar()>><</link>> </div> </div> </div> <<script>> $(document).one(':passagedisplay', function() { function updateQuestProgress() { var wood = State.variables.materials.wood || 0; var scrap = State.variables.materials.scrap || 0; $('#req-wood').text(wood + ' / 5'); $('#req-scrap').text(scrap + ' / 2'); $('#chip-wood').toggleClass('ok', wood >= 5); $('#chip-scrap').toggleClass('ok', scrap >= 2); $('#back-to-tut2-btn').toggleClass('hidden', !(wood >= 5 && scrap >= 2)); } function startRound() { updateQuestProgress(); var $layer = $('#f-layer').empty(); var $timer = $('#f-timer').show().removeClass('low').text('2.0s'); var sessionLoot = { scrap: 0, wood: 0, water: 0, berries: 0 }; var itemCount = Math.floor(Math.random() * 6) + 8; var types = ['scrap', 'wood', 'water', 'berries']; var icons = {scrap: '🔩', wood: '🪵', water: '💧', berries: '🫐'}; for (var i = 0; i < itemCount; i++) { var type = types[Math.floor(Math.random() * types.length)]; var x = Math.floor(Math.random() * 82) + 4; var y = Math.floor(Math.random() * 74) + 12; var $item = $('<div>', { class: 'tut-loot-item', css: { left: x + '%', top: y + '%' }, text: icons[type] }); $item.data('type', type); $item.on('mousedown touchstart', function(e) { e.preventDefault(); var t = $(this).data('type'); $(this).remove(); if (!State.variables.materials) State.variables.materials = {}; if (!State.variables.materials[t]) State.variables.materials[t] = 0; State.variables.materials[t] += 1; sessionLoot[t] += 1; updateQuestProgress(); }); $layer.append($item); } var timeLeft = 2.0; var timerInterval = setInterval(function() { timeLeft -= 0.1; if (timeLeft < 0) timeLeft = 0; $timer.text(timeLeft.toFixed(1) + 's'); if (timeLeft <= 1.0) $timer.addClass('low'); if (timeLeft > 0) return; clearInterval(timerInterval); $layer.empty(); $timer.hide(); var tot = sessionLoot.scrap + sessionLoot.wood + sessionLoot.water + sessionLoot.berries; var $res = $('<div>').addClass('tut-overlay' + (tot > 0 ? '' : ' bad')); if (tot > 0) { $res.append($('<h3>').text('SCAVENGE COMPLETE')); if (sessionLoot.scrap > 0) $res.append($('<div class="tut-loot">').html('🔩 SCRAP <span>+' + sessionLoot.scrap + '</span>')); if (sessionLoot.wood > 0) $res.append($('<div class="tut-loot">').html('🪵 WOOD <span>+' + sessionLoot.wood + '</span>')); if (sessionLoot.water > 0) $res.append($('<div class="tut-loot">').html('💧 WATER <span>+' + sessionLoot.water + '</span>')); if (sessionLoot.berries > 0) $res.append($('<div class="tut-loot">').html('🫐 BERRIES <span>+' + sessionLoot.berries + '</span>')); } else { $res.append($('<h3>').text('NOTHING COLLECTED')); $res.append($('<p>').text('You were too slow. Try again.')); } var w = State.variables.materials.wood || 0, s = State.variables.materials.scrap || 0; var $b = $('<div class="tut-btn inline" style="margin-top:14px;">').text(w >= 5 && s >= 2 ? 'BACK' : 'CONTINUE SCAVENGING'); $b.on('click', function() { $res.remove(); if (typeof window.setup.updateSidebar === 'function') window.setup.updateSidebar(); if (w >= 5 && s >= 2) { updateQuestProgress(); } else { startRound(); } }); $res.append($b); $('#forage-game-box').append($res); if (typeof window.setup.updateSidebar === 'function') window.setup.updateSidebar(); }, 100); } setTimeout(function() { updateQuestProgress(); if ($('#f-ready').length) { $('#f-timer').hide(); $('#f-start').on('click', function() { State.variables.tutForageStarted = true; $('#f-ready').remove(); startRound(); }); } else { startRound(); } }, 50); }); <</script>> <</nobr>>
<<nobr>> <<setMenu "Tutorial">> <<set _w to ($materials.wood || 0)>> <<set _s to ($materials.scrap || 0)>> <<set _ok to (_w gte 5 and _s gte 2)>> <div class="tut-world"> <div class="tut-card"> <div class="tut-step">TRAINING 2 / 4</div> <div class="tut-title">FABRICATION</div> <div class="tut-bar"><i style="width:50%"></i></div> <p class="tut-sub">A toxic river runs nearby, full of mutated fish. Bare hands will not do.</p> <div class="tut-obj"><b>OBJECTIVE:</b> Turn the materials you gathered into a basic <b>Fishing Rod</b>. Crafting spends the materials, so check your Quick Inventory on the left first.</div> <div class="tut-recipe"> <div @class="'tut-ing' + (_w lt 5 ? ' short' : '')"><span class="ic">🪵</span><div><div class="nm">WOOD</div><div class="qt"><<print _w>> / 5 in pack</div></div></div> <div @class="'tut-ing' + (_s lt 2 ? ' short' : '')"><span class="ic">🔩</span><div><div class="nm">SCRAP</div><div class="qt"><<print _s>> / 2 in pack</div></div></div> </div> <div class="tut-result"><span class="ic">🎣</span><div><div class="nm">FISHING ROD</div><div class="ds">Lets you catch fish at rivers and lakes. Fish are food, and food keeps you alive.</div></div></div> <<if _ok>> <div class="tut-act"> <<link "CRAFT FISHING ROD" "Tutorial 3">> <<set $materials.wood -= 5, $materials.scrap -= 2>> <<set $hasFishingRod to true>> <<run window.setup.updateSidebar()>> <</link>> </div> <<else>> <div class="tut-btn off">NOT ENOUGH MATERIALS</div> <div class="tut-act" style="margin-top:10px;"><<link "BACK TO TRAINING 1" "Tutorial 1">><</link>></div> <</if>> </div> </div> <</nobr>>
<<nobr>> <<setMenu "Tutorial">> <<if ndef $materials>><<set $materials to {}>><</if>> <<if ndef $materials.rawMeat>><<set $materials.rawMeat to 0>><</if>> <<if ndef $stamina>><<set $stamina to 100>><</if>> <<if ndef $fishCaught>><<set $fishCaught to false>><</if>> <div class="tut-world"> <div class="tut-card"> <div class="tut-step">TRAINING 3 / 4</div> <div class="tut-title">SUSTENANCE</div> <div class="tut-bar"><i style="width:75%"></i></div> <p class="tut-sub">Food is the one thing the wasteland never stops asking for.</p> <div class="tut-obj"><b>OBJECTIVE:</b> Cast your line. When the bobber bites, click the target quickly before the fish gets away. Every cast costs <b>10 stamina</b>.</div> <div class="tut-status"> <div>⚡ STAMINA <b id="stamina-count"><<print $stamina>></b></div> <div>🎯 STATUS <b id="fish-status">READY TO CAST</b></div> <div>🍖 RAW MEAT <b id="meat-count"><<print $materials.rawMeat>></b></div> </div> <<if $fishCaught>> <div class="tut-log good" id="fish-log">CATCH SUCCESSFUL! +2 RAW MEAT</div> <<else>> <div class="tut-log" id="fish-log">CAST YOUR LINE INTO THE RIVER AND CATCH FOOD.</div> <</if>> <<if $fishCaught>> <div class="tut-act"> <<link "PROCEED TO TRAINING 4: FINISH" "Tutorial 4">><<run window.setup.updateSidebar()>><</link>> </div> <<else>> <div id="cast-btn" class="tut-act"> <<link "🎣 CAST FISHING LINE (-10 STAMINA)">> <<set $stamina -= 10>> <<if $stamina lt 0>><<set $stamina to 0>><</if>> <<replace "#stamina-count">><<print $stamina>><</replace>> <<replace "#fish-log">><span class="info">WATCHING THE RADIOACTIVE BOBBER...</span><</replace>> <<script>> $("#cast-btn").hide(); $("#disabled-btn").hide(); $("#fish-status").text("WAITING..."); if (typeof window.setup.updateSidebar === 'function') window.setup.updateSidebar(); setTimeout(function() { var $target = $("#fish-target"); var $cont = $("#river-container"); var maxX = $cont.width() - 90; var maxY = $cont.height() - 90; $target.css({ left: Math.floor(Math.random() * maxX) + "px", top: Math.floor(Math.random() * maxY) + "px" }).show(); $("#fish-log").addClass("bad").text("BITE DETECTED! CLICK THE TARGET!"); window.fishMissTimer = setTimeout(function() { if ($target.is(":visible")) { $target.hide(); $("#fish-log").addClass("bad").text("THE FISH GOT AWAY."); $("#fish-status").text("READY TO CAST"); if (State.variables.stamina >= 10) { $("#cast-btn").show(); } else { $("#disabled-btn").show(); } } }, 1400); }, Math.floor(Math.random() * 2500) + 2000); <</script>> <</link>> </div> <div id="disabled-btn" class="tut-btn off" style="display:none;">EXHAUSTED: REQUIRES 10 STAMINA</div> <</if>> <div class="tut-screen river" id="river-container"> <div id="fish-target" style="display:none; position:absolute; z-index:50;"> <<link " ">> <<run new Audio('audio/sfx/sfx_splash.wav').play()>> <<set $materials.rawMeat += 2>> <<set $fishCaught to true>> <<goto "Tutorial 3">> <</link>> </div> </div> </div> </div> <<script>> $(document).one(':passagedisplay', function() { if (State.variables.fishCaught) { return; } if (State.variables.stamina >= 10) { $("#cast-btn").show(); $("#disabled-btn").hide(); } else { $("#cast-btn").hide(); $("#disabled-btn").show(); } }); <</script>> <</nobr>>
<<nobr>> <div class="tut-world"> <div class="tut-card"> <div class="tut-step">TRAINING 4 / 4</div> <div class="tut-title">TRAINING COMPLETE</div> <div class="tut-bar"><i style="width:100%"></i></div> <p class="tut-sub">All systems green. You are ready to run a camp.</p> <ul class="tut-list"> <li><span class="ck">✔</span><span><b>Scavenging.</b> You gathered wood and scrap by clicking fast.</span></li> <li><span class="ck">✔</span><span><b>Crafting.</b> You turned raw materials into a Fishing Rod.</span></li> <li><span class="ck">✔</span><span><b>Food.</b> You caught <b style="color:#fde68a;">2 Raw Meat</b> (mutated fish). Cook it later at your base.</span></li> </ul> <div class="tut-obj"><b>FINAL OBJECTIVE:</b> Boot up the main generator and initialize the Command Center UI.</div> <div class="tut-act"> <<link "INITIALIZE S.A.N.C.T.U.A.R.Y." "Camp">> <<set $hasSanctuary to true>> <<run window.setup.updateSidebar()>> <</link>> </div> </div> </div> <</nobr>>
<<nobr>> <<setMenu "Exploration">> <style> /* Alignment that prevents sliding under the sidebar and matches the main Camp structure exactly */ #story { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; background: transparent !important; } .av-world-gathering { position: relative; margin-left: 17.5em; /* Left sidebar gap */ min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 30px; background: linear-gradient(rgba(5, 10, 5, 0.82), rgba(0, 5, 2, 0.94)), url('images/locations/tychy_logging_camp.jpg') center/cover no-repeat fixed; } </style> <div class="av-world-gathering"> <!-- MAIN BOX --> <div style="background: linear-gradient(rgba(5, 10, 5, 0.75), rgba(5, 10, 5, 0.85)), url('images/locations/tychy_logging_camp.jpg') center/cover no-repeat; border: 2px solid #d97706; padding: 30px; border-radius: 8px; font-family: 'Segoe UI', Tahoma, sans-serif; box-shadow: 0 0 35px rgba(217, 119, 6, 0.25); width: 100%; box-sizing: border-box;"> <div style="text-align: center; border-bottom: 1px dashed #d97706; padding-bottom: 15px; margin-bottom: 25px;"> <h2 style="color: #d97706; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px #d97706;">[ TYCHY LOGGING CAMP - SECURED ]</h2> <div style="margin-top: 10px; font-size: 0.9rem; color: #94a3b8;">AREA CLEARED. RESOURCE EXTRACTION AVAILABLE.</div> </div> <div style="display: flex; gap: 20px; align-items: flex-start; flex-wrap: wrap;"> <!-- LEFT BOX: IMAGE ADDED INSIDE --> <div style="flex: 1; min-width: 250px; background: linear-gradient(rgba(0,0,0,0.7), rgba(0,0,0,0.85)), url('images/locations/tychy_logging_camp.jpg') center/cover no-repeat; padding: 15px; border: 1px solid #334155; border-radius: 4px;"> <div style="color: #fbbf24; font-weight: bold; margin-bottom: 10px; letter-spacing: 1px;">> SITUATION REPORT:</div> <div style="color: #cbd5e1; font-size: 0.95rem; line-height: 1.6;"> The Feral Timber Wolf lies dead in the dirt. The perimeter is temporarily safe. Surrounding you are rusted logging machines and piles of uncut timber. You can spend your energy to chop and gather wood before more predators arrive to investigate the blood. </div> </div> <!-- RIGHT BOX: IMAGE ADDED INSIDE --> <div style="flex: 1; min-width: 250px; background: linear-gradient(rgba(217, 119, 6, 0.15), rgba(0,0,0,0.8)), url('images/locations/tychy_logging_camp.jpg') center/cover no-repeat; padding: 15px; border: 1px solid #d97706; border-radius: 4px;"> <div style="color: #fcd34d; font-weight: bold; margin-bottom: 15px; text-align: center; letter-spacing: 1px;">> YOUR VITALS & LOOT</div> <div style="display: flex; justify-content: space-between; margin-bottom: 12px; font-weight: bold; border-bottom: 1px dashed rgba(217,119,6,0.3); padding-bottom: 8px;"> <span style="color: #cbd5e1;">⚡ STAMINA:</span> <span style="color: <<if $stamina gte 15>>#34d399<<else>>#ef4444<</if>>;"><<print $stamina>> / <<print $maxStamina>></span> </div> <div style="display: flex; justify-content: space-between; font-weight: bold;"> <span style="color: #cbd5e1;">🪵 WOOD IN INVENTORY:</span> <span style="color: #fcd34d;"><<print $materials.wood>> UNITS</span> </div> </div> </div> /* INFO SCREEN SHOWN AFTER BUTCHERING, OR THE RISK OF A WOLF ATTACK */ <<if def _gatheredText>> <div style="margin-top: 20px; padding: 12px; text-align: center; background: rgba(52, 211, 153, 0.15); border: 1px dashed #34d399; color: #34d399; font-weight: bold; font-family: 'Courier New', monospace; font-size: 1.1rem; animation: pulse-green 1s;"> <<print _gatheredText>> </div> <style>@keyframes pulse-green { 0% { opacity: 0.5; transform: scale(0.98); } 50% { opacity: 1; transform: scale(1.02); } 100% { opacity: 1; transform: scale(1); } }</style> <<endif>> <div style="margin-top: 30px; display: flex; gap: 15px; flex-wrap: wrap;"> <!-- CHOP WOOD BUTTON / WOLF ATTACK --> <<if $stamina gte 15>> <div style="flex: 1; min-width: 250px; text-align: center; background: rgba(217, 119, 6, 0.25); border: 2px solid #d97706; border-radius: 4px; transition: 0.3s; cursor: pointer; padding: 15px; box-sizing: border-box;" onmouseover="this.style.background='rgba(217, 119, 6, 0.45)'" onmouseout="this.style.background='rgba(217, 119, 6, 0.25)'"> @@color:#fcd34d;font-weight:bold;font-size:1.1rem;letter-spacing:1px;<<link "🪓 CHOP WOOD (-15 STAMINA)" "TychyGathering">> <<set $stamina -= 15>> <<set _woodRoll to random(2, 4) + (setup.tipOn("hunt") ? 2 : 0)>> <<set $materials.wood += _woodRoll>> /* 30% chance of a wolf attack */ <<set _wolfEncounter to random(1, 100)>> <<if _wolfEncounter lte 30>> <<set _wolfCount to random(1, 3)>> <<if _wolfCount eq 1>> <<set $enemyGroup to [{ name: "Feral Timber Wolf", hp: 35, currentHp: 35, maxHp: 35, minDmg: 5, maxDmg: 12, drop: "rawMeat", id: 0 }]>> <<elseif _wolfCount eq 2>> <<set $enemyGroup to [ { name: "Feral Timber Wolf", hp: 35, currentHp: 35, maxHp: 35, minDmg: 5, maxDmg: 12, drop: "rawMeat", id: 0 }, { name: "Feral Timber Wolf", hp: 35, currentHp: 35, maxHp: 35, minDmg: 5, maxDmg: 12, drop: "rawMeat", id: 1 } ]>> <<else>> <<set $enemyGroup to [ { name: "Feral Timber Wolf", hp: 35, currentHp: 35, maxHp: 35, minDmg: 5, maxDmg: 12, drop: "rawMeat", id: 0 }, { name: "Feral Timber Wolf", hp: 35, currentHp: 35, maxHp: 35, minDmg: 5, maxDmg: 12, drop: "rawMeat", id: 1 }, { name: "Feral Timber Wolf", hp: 35, currentHp: 35, maxHp: 35, minDmg: 5, maxDmg: 12, drop: "rawMeat", id: 2 } ]>> <<endif>> <<set $enemy to $enemyGroup[0]>> <<set $combatLog to []>> <<set $combatLog.unshift("> AMBUSH! The noise attracted a pack of " + _wolfCount + " Feral Timber Wolves!")>> <<goto "Combat">> <<else>> <<set _gatheredText to "SUCCESS: You exhausted yourself but collected +" + _woodRoll + " Wood.">> <<endif>> <</link>>@@ </div> <<else>> <div style="flex: 1; min-width: 250px; text-align: center; background: rgba(0, 0, 0, 0.6); border: 2px dashed #475569; border-radius: 4px; padding: 15px; box-sizing: border-box; display: flex; align-items: center; justify-content: center;"> <span style="color: #ef4444; font-weight: bold; font-size: 1.1rem;">🪓 EXHAUSTED (NEEDS 15 STAMINA)</span> </div> <<endif>> <!-- LEAVE THE AREA BUTTON --> <div style="flex: 1; min-width: 250px; text-align: center; background: rgba(56, 189, 248, 0.15); border: 2px solid #38bdf8; border-radius: 4px; transition: 0.3s; cursor: pointer; padding: 15px; box-sizing: border-box;" onmouseover="this.style.background='rgba(56, 189, 248, 0.3)'" onmouseout="this.style.background='rgba(56, 189, 248, 0.15)'"> @@color:#38bdf8;font-weight:bold;font-size:1.1rem;letter-spacing:1px;<<link "RETURN TO SECTOR MAP" "Map">><<unset _gatheredText>><</link>>@@ </div> </div> </div> </div> <</nobr>>
<<nobr>> <div style="background-color: #050a05; padding: 25px; border: 2px solid #ffaa00; border-radius: 8px; font-family: 'Courier New', monospace; box-shadow: 0 0 20px rgba(255, 170, 0, 0.1); max-width: 700px; margin: 5vh auto;"> <div style="text-align: center; border-bottom: 1px dashed #ffaa00; padding-bottom: 15px; margin-bottom: 25px;"> <h2 style="color: #ffaa00; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px rgba(255,170,0,0.5);">[ SYSTEM BOOT SEQUENCE ]</h2> <div style="margin-top: 10px; font-size: 0.9rem; color: #aaa;">NEURAL LINK ESTABLISHED</div> </div> <div style="color: #ddd; font-size: 1rem; line-height: 1.6; margin-bottom: 20px;"> <p>Wake up, Operator <span style="color:#00ff00; font-weight:bold;"><<print $playerName>></span>.</p> <p>The world as you knew it is gone. You are standing on the edge of the <span style="color:#3498db; font-weight:bold;"><<print $startingCity>></span> sector. The air is toxic, and resources are scarce.</p> <strong style="color: #ffaa00; font-size: 1.1rem; display:block; margin-top: 20px; border-left: 3px solid #ffaa00; padding-left: 10px;">> BASIC PROTOCOLS:</strong> <ul style="margin-top: 10px; color: #aaa; font-family: 'Segoe UI', sans-serif;"> <li style="margin-bottom: 8px;">Your <strong style="color:#f87171;">HP (Health)</strong> determines your life. Permadeath is active.</li> <li style="margin-bottom: 8px;">Actions consume <strong style="color:#38bdf8;">STAMINA</strong>. When it hits zero, you must rest.</li> <li style="margin-bottom: 8px;">Starvation will slowly kill you. You need <strong style="color:#fbbf24;">FOOD</strong> to survive.</li> </ul> </div> <div style="text-align: center; margin-top: 30px; padding: 12px; border: 1px solid #ffaa00; background: rgba(255, 170, 0, 0.15); border-radius: 4px; box-shadow: 0 0 10px rgba(255, 170, 0, 0.2);"> <<link '<span style="color:#ffaa00; font-weight:bold; letter-spacing:2px; text-decoration:none;">ACKNOWLEDGE PROTOCOLS</span>' "Tutorial 1">><</link>> </div> </div> <</nobr>>
<<audio ":all" stop>> <<audio "bar_ambience" volume 0.25 loop play>> <<setMenu "WastelandBar">> <<if ndef $activeCompanion>><<set $activeCompanion to "None">><</if>> <<if ndef $sanctuaryLevels>><<set $sanctuaryLevels to { bar: 1 }>><</if>> <<if ndef $sanctuaryLevels.bar>><<set $sanctuaryLevels.bar to 1>><</if>> <<if ndef $materials>><<set $materials to {}>><</if>> <<if ndef $materials.scrip>><<set $materials.scrip to 0>><</if>> <<if ndef $drinksConsumed>><<set $drinksConsumed to 0>><</if>> <<if ndef $activeSideQuest>><<set $activeSideQuest to false>><</if>> <<if ndef $activeBossQuest>><<set $activeBossQuest to false>><</if>> <style> /* DEFINITIVE FIX THAT REMOVES THE TOP GAP */ body { background-color: #050a05 !important; } #story, #passages, .passage { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; background: transparent !important; } .av-world { position: absolute; top: 10px; left: 17.5em; right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 0 20px 20px 20px; width: calc(100% - 17.5em); } .bar-container { background-color: #1e0926; padding: 30px; border: 2px solid #a855f7; border-radius: 8px; font-family: 'Segoe UI', Tahoma, sans-serif; box-shadow: 0 0 25px rgba(168, 85, 247, 0.2); box-sizing: border-box; width: 100%; } .bar-grid { display: flex; flex-wrap: wrap; gap: 20px; justify-content: center; margin-top: 25px; margin-bottom: 20px; } .bar-card { background: rgba(0, 0, 0, 0.75); border: 1px solid #7e22ce; border-radius: 6px; flex: 1 1 310px; min-width: 0; max-width: 640px; min-height: 290px; overflow: hidden; display: flex; flex-direction: column; justify-content: space-between; box-shadow: 0 4px 12px rgba(0,0,0,0.6); box-sizing: border-box; } .bar-img { height: 110px; width: 100%; object-fit: cover; border-bottom: 2px solid #7e22ce; background: #000; } .main-banner-img { height: 140px; width: 100%; object-fit: cover; border: 1px solid #a855f7; border-radius: 6px; margin-bottom: 20px; background: #000; box-shadow: 0 0 15px rgba(168, 85, 247, 0.3); box-sizing: border-box; } .section-title { color: #d8b4fe; font-size: 0.95rem; font-family: 'Courier New', monospace; letter-spacing: 2px; margin: 30px 0 15px 0; text-transform: uppercase; } .cyber-bar-btn { display: block; width: 100%; background: linear-gradient(135deg, #9333ea 0%, #7e22ce 100%); border: 1px solid #c084fc; color: #f3e8ff !important; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.8rem; letter-spacing: 1px; border-radius: 4px; cursor: pointer; text-decoration: none !important; box-sizing: border-box; box-shadow: 0 0 10px rgba(168, 85, 247, 0.3); transition: all 0.2s; } .cyber-bar-btn:hover { background: linear-gradient(135deg, #7e22ce 100%, #9333ea 0%); border-color: #e9d5ff; box-shadow: 0 0 15px rgba(168, 85, 247, 0.6); } .cyber-return-btn { display: inline-block; background: linear-gradient(135deg, #7e22ce 0%, #581c87 100%); border: 1px solid #c084fc; color: #f3e8ff !important; padding: 12px 30px; font-weight: bold; font-family: 'Courier New', monospace; letter-spacing: 2px; border-radius: 4px; text-decoration: none !important; box-shadow: 0 0 15px rgba(168, 85, 247, 0.3); transition: all 0.2s; } .cyber-return-btn:hover { background: linear-gradient(135deg, #581c87 100%, #7e22ce 0%); border-color: #e9d5ff; box-shadow: 0 0 20px rgba(168, 85, 247, 0.5); } </style> <<nobr>> <div class="av-world"> <div class="bar-container"> <!-- TITLE --> <div style="text-align: center; border-bottom: 1px dashed #a855f7; padding-bottom: 15px; margin-bottom: 20px;"> <h2 style="color: #c084fc; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px #a855f7; font-size: 1.8rem;">[ THE RUSTY ANCHOR BAR ]</h2> <div style="margin-top: 8px; font-size: 0.9rem; color: #d8b4fe;">UNDERGROUND SOCIAL HUB & MERCENARY EXCHANGE</div> </div> <!-- BAR IMAGE BANNER --> <img src="images/locations/bar_main.jpg" class="main-banner-img" onerror="this.src='https://placehold.co/800x200/000/a855f7?text=WASTELAND+BAR';"> <!-- ACTION LOG --> <<if def $barLog>> <div style="border: 1px solid #a855f7; background: rgba(168, 85, 247, 0.1); padding: 15px; border-radius: 4px; margin-bottom: 25px; font-family: monospace;"> <div style="color: #c084fc; font-weight: bold; margin-bottom: 8px;">🍻 BAR INTEL & RUMORS</div> <div style="color: #cbd5e1; font-style: italic;">"<<print $barLog>>"</div> </div> <<unset $barLog>> <<endif>> <!-- BAR ACTIONS SECTION --> <div class="section-title">>> LOUNGE & RECRUITMENT OPERATIONS</div> <<nobr>> <div class="bar-grid" data-tour="bar-grid"> <!-- 1. COMPANION ROSTER (BRIDGE FOR THE DYNAMIC REPUTATION SYSTEM) --> <div class="bar-card" data-tour="bar-roster"> <img src="images/locations/bar_merc.jpg" class="bar-img" onerror="this.src='https://placehold.co/600x300/000/9333ea?text=MERCENARIES';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #c084fc; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">👥 RECRUIT COMPANION</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">Browse the bar for hired guns. Build a squad of up to 4. Every Bar upgrade brings in a new, unique recruit.</div> <div style="color: #fbbf24; font-size: 0.7rem; font-family: monospace;">REP: <<getReputationRank>> | SQUAD: <<print setup.squadSize()>>/4</div> </div> <div style="margin-top:10px;"> <<link "<span class='cyber-bar-btn'>👥 BROWSE MERCENARY ROSTER</span>" "MercenaryRoster">><</link>> <<link "<span class='cyber-bar-btn'>🧭 SQUAD STRATEGY</span>" "SquadStrategy">><</link>> </div> </div> </div> <!-- 2. GATHER RUMORS (Side Quest) --> <div class="bar-card"> <img src="images/locations/bar_rumors.jpg" class="bar-img" onerror="this.src='https://placehold.co/600x300/000/7e22ce?text=RUMORS';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #c084fc; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">🗣️ LISTEN TO RUMORS</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">Buy drinks for locals. Every round can reveal a hidden stash, a stranger in need or a place worth hitting.</div> <div style="color: #fbbf24; font-size: 0.7rem; font-family: monospace;">COST: <<print setup.shopPrice(setup.SITES.barFee)>> Scrip per round | LEADS: <<print setup.siteCounts().rumors>>/<<print setup.SITES.cap>></div> </div> <div style="margin-top:10px;"> <<if setup.rumorBoardFull()>> <div style="background: rgba(15, 23, 42, 0.6); border: 1px dashed #c084fc; color: #d8b4fe; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem; border-radius: 4px;">[ RUMOR BOARD FULL: <<print setup.siteCounts().rumors>> / <<print setup.SITES.cap>> LEADS ON YOUR MAP ]</div> <<elseif $materials.scrip gte setup.shopPrice(setup.SITES.barFee)>> <<link "<span class='cyber-bar-btn'>🗣️ BUY A ROUND OF DRINKS</span>" "WastelandBar">> <<set $barLog to setup.barRumor(setup.shopPrice(setup.SITES.barFee))>> <<run window.setup.updateSidebar()>> <</link>> <<else>> <div style="background: rgba(15, 23, 42, 0.6); border: 1px dashed #7e22ce; color: #d8b4fe; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem; border-radius: 4px;">[ INSUFFICIENT SCRIP ]</div> <</if>> </div> </div> </div> <!-- 3. SHADY INFORMANT (Boss Quest) --> <div class="bar-card"> <img src="images/locations/bar_dealer.jpg" class="bar-img" onerror="this.src='https://placehold.co/600x300/000/581c87?text=DEALER';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #c084fc; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">🕵️ SHADY INFORMANT</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">An underground broker who sells the locations of named Reptilian and human warlords. High risk, high pay.</div> <div style="color: #fbbf24; font-size: 0.7rem; font-family: monospace;">FEE: <<print setup.shopPrice(setup.SITES.informantFee)>> Scrip | CONTRACTS: <<print setup.siteCounts().contracts>>/<<print setup.SITES.contractCap>></div> </div> <div style="margin-top:10px;"> <<if setup.contractBoardFull()>> <div style="background: rgba(15, 23, 42, 0.6); border: 1px dashed #c084fc; color: #d8b4fe; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem; border-radius: 4px;">[ CONTRACTS ACTIVE: <<print setup.siteCounts().contracts>> / <<print setup.SITES.contractCap>> ]</div> <<elseif $materials.scrip gte setup.shopPrice(setup.SITES.informantFee)>> <<link "<span class='cyber-bar-btn'>🕵️ BUY A BOSS CONTRACT</span>" "WastelandBar">> <<set $barLog to setup.informantContract(setup.shopPrice(setup.SITES.informantFee))>> <<run window.setup.updateSidebar()>> <</link>> <<else>> <div style="background: rgba(15, 23, 42, 0.6); border: 1px dashed #7e22ce; color: #d8b4fe; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem; border-radius: 4px;">[ INSUFFICIENT SCRIP ]</div> <</if>> </div> </div> </div> <!-- 4. THE BARTENDER (BLACKOUT MECHANIC) --> <div class="bar-card"> <img src="images/locations/bar_bartender.jpg" class="bar-img" onerror="this.src='https://placehold.co/600x300/000/3b0764?text=BARTENDER';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #c084fc; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">🥃 THE BARTENDER</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">Knock back a shot of unregulated wasteland booze. Don't push your luck.</div> <div style="color: #fbbf24; font-size: 0.7rem; font-family: monospace;">COST: 5 Scrip | TOLERANCE: <<print $drinksConsumed>>/3</div> </div> <div style="margin-top:10px;"> <<if $materials.scrip gte 5>> <<link "<span class='cyber-bar-btn'>🥃 ORDER SHOT (5 SCRIP)</span>" "WastelandBar">> <<set $materials.scrip -= 5>> <<set $drinksConsumed += 1>> <<if $drinksConsumed gte 3>> <<set $drinksConsumed to 0>> <<goto "BlackoutPassage">> <<else>> <<set $barLog to "You downed a harsh shot of synthetic whiskey. The room spins slightly. (Tolerance: " + $drinksConsumed + "/3)">> <<run window.setup.updateSidebar()>> <<endif>> <</link>> <<else>> <div style="background: rgba(15, 23, 42, 0.6); border: 1px dashed #7e22ce; color: #d8b4fe; padding: 10px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.75rem; border-radius: 4px;">[ INSUFFICIENT SCRIP ]</div> <<endif>> </div> </div> </div> <!-- 5. TEXAS HOLD'EM (open tables against other survivors, sit-and-go for Scrip) --> <div class="bar-card" data-tour="bar-poker"> <img src="images/locations/bar_poker.jpg" class="bar-img" onerror="this.src='https://placehold.co/600x300/000/166534?text=TEXAS+HOLD%27EM';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #c084fc; font-size: 0.9rem; font-weight: bold; margin-bottom: 4px;">♠ TEXAS HOLD'EM</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.3;">Take a seat at a table of 2 to 6 survivors. Everyone buys in with the same Scrip, blinds keep rising, and the last player with chips takes the whole pot.</div> <div style="color: #fbbf24; font-size: 0.7rem; font-family: monospace;">BUY-IN: 25 TO 500 Scrip | SCRIP: <<print $materials.scrip>></div> </div> <div style="margin-top:10px;"> <<link "<span class='cyber-bar-btn'>♠ SIT AT A TABLE</span>" "WastelandBar">><<run window.setup.pk.open()>><</link>> </div> </div> </div> </div> <</nobr>> <!-- BACK TO THE SANCTUARY HUB --> <div style="text-align: center; margin-top: 35px;"> <<link "<span class='cyber-return-btn'>🏰 RETURN TO SANCTUARY HUB</span>" "Sanctuary Hub">><</link>> </div> </div> </div> <</nobr>>
/* Token Adding Widget */ <<widget "addTokens">> <<set $syndicateTokens += _args[0]>> /* Shadow Encryption: we multiply the token amount by 73 and add 1492 (the guy cannot know this) */ <<set $token_checksum to ($syndicateTokens * 73) + 1492>> <</widget>> /* Token Spending Widget */ <<widget "spendTokens">> <<set $syndicateTokens -= _args[0]>> <<if $syndicateTokens lt 0>><<set $syndicateTokens to 0>><</if>> <<set $token_checksum to ($syndicateTokens * 73) + 1492>> <</widget>> /* Time of day + weather banner (Map and Camp) */ <<widget "timeBanner">><<nobr>> <<run setup.timeInit()>> <<set _ts to setup.timeSlot()>> <<set _wi to setup.weatherInfo()>> <div data-tour="tod" @class="'tod-banner' + (_ts gte 6 ? ' tod-dead' : '')"> <div><b><<print setup.timeIcon()>> <<print setup.timeName()>></b> | <<print _wi.icon>> <b><<print $weather>></b></div> <div class="tod-desc"><<print _wi.desc>></div> <<if _ts gte 6>> <div class="tod-warn">DEAD NIGHT: go to camp and sleep now. If you head out again you will collapse outside, wake up injured and lose part of what you carry.</div> <<elseif _ts eq 5>> <div class="tod-soon">Night is falling. Finish up and head back to camp to sleep.</div> <</if>> <<if def $radStormDay and $radStormDay eq ($day + 1)>> <div class="tod-warn">☢️ KAELEN'S WARNING: a rad-storm hits tomorrow. Stock Medicine (radiation meds) or plan to stay underground.</div> <</if>> <<if setup.zmoraActive()>> <<set _zc to setup.zmoraCounts()>> <div class="zm-alert"> <div><b>🧠 CAMP ALERT:</b> Several defenders in the Living Quarters have woken up screaming, unable to breathe. The Zmora Protocol is loose in the camp sub-networks.</div> <div class="zm-sub">EXHAUSTED: <<print _zc.workers>> workers | NEURAL SHOCK: <<print _zc.defenders>> defenders | output -<<print _zc.pct>>%</div> <<link "<span class='zm-link'>OPEN THE DIAGNOSTIC TERMINAL</span>" "ZmoraTerminal">><</link>> </div> <</if>> <<if setup.contamActive()>> <<set _cb to setup.contamInfo()>> <<set _here to passage()>> <div class="tod-warn"> <div>☢ <b>RADIATION CONTAMINATION:</b> <<print _cb.text>>. Doses <<print _cb.doses>>/<<print setup.CONTAM.cureDoses>>. Only the Med Bay's Decontamination Flush clears it at once.</div> <<if not _cb.medicated and setup.have("medicine") gte 1>> <<link "<span style='display:inline-block; margin-top:4px; padding:3px 8px; border:1px solid #fca5a5; border-radius:3px; color:#fecaca;'>TAKE RAD-MEDS (1 MEDICINE)</span>" _here>><<run setup.contamDose()>><</link>> <</if>> </div> <</if>> <<if def $weatherNote and $weatherNote neq "">> <div class="tod-warn"><<print $weatherNote>></div> <<set $weatherNote to "">> <</if>> </div> <</nobr>><</widget>> /* Shared UI widgets */ /* Sets the active menu and refreshes the sidebar */ <<widget "setMenu">><<nobr>> <<set $currentMenu to _args[0]>> <<run window.setup.updateSidebar()>><</nobr>><</widget>> <<widget "adPins">><<script>>$(this.output).append(setup.ads.pinsHtml());<</script>><</widget>> /* Applies damage to the current target and clamps HP at 0 */ <<widget "damageEnemy">><<nobr>> <<set _wdr to ($enemyGroup[$currentTargetIdx].dmgReduction || 0)>> <<set _dealt to setup.tmOut(_args[0], $enemyGroup[$currentTargetIdx], _wdr)>> <<set _dmg to _dealt>> <<set $enemyGroup[$currentTargetIdx].currentHp -= _dealt>> <<if $enemyGroup[$currentTargetIdx].currentHp lt 0>><<set $enemyGroup[$currentTargetIdx].currentHp to 0>><</if>><</nobr>><</widget>>
<<nobr>> <<setMenu "Workshop">> /* Workshop Level Check Safeguard */ <<if ndef $sanctuaryLevels>><<set $sanctuaryLevels to { workshop: 1 }>><</if>> <<if ndef $sanctuaryLevels.workshop>><<set $sanctuaryLevels.workshop to 1>><</if>> <<set _currentWorkshopLevel to $sanctuaryLevels.workshop>> <<set _isTeki to false>> <<if def $playerClass and typeof $playerClass === "string" and $playerClass.toLowerCase().includes("techie")>> <<set _isTeki to true>> <<elseif def $playerBackground and typeof $playerBackground === "string" and $playerBackground.toLowerCase().includes("techie")>> <<set _isTeki to true>> <<endif>> <<set _effectiveLevel to (_isTeki ? _currentWorkshopLevel + 1 : _currentWorkshopLevel)>> <<set _craftRarity to "Common">> <<set _rarityMultiplier to 1.0>> <<if _effectiveLevel eq 2>> <<set _craftRarity to "Uncommon">> <<set _rarityMultiplier to 1.3>> <<elseif _effectiveLevel eq 3>> <<set _craftRarity to "Rare">> <<set _rarityMultiplier to 1.6>> <<elseif _effectiveLevel eq 4>> <<set _craftRarity to "Epic">> <<set _rarityMultiplier to 2.0>> <<elseif _effectiveLevel eq 5>> <<set _craftRarity to "Legendary">> <<set _rarityMultiplier to 2.5>> <<elseif _effectiveLevel gte 6>> <<set _craftRarity to "Godlike">> <<set _rarityMultiplier to 3.5>> <</if>> <</nobr>> <style> body { background-color: #050a05 !important; } #story, #passages, .passage { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; background: transparent !important; } .workshop-container { background: linear-gradient(180deg, rgba(5,10,15,0.95), rgba(15,8,8,0.98)), url('images/locations/workshop_bg.jpg') center/cover; padding: 25px; border: 2px solid #3498db; border-radius: 8px; font-family: 'Segoe UI', Tahoma, sans-serif; box-shadow: 0 0 30px rgba(52, 152, 219, 0.25); } .upgrade-panel { background: rgba(0, 20, 40, 0.9); border: 1px solid #38bdf8; padding: 15px 20px; border-radius: 6px; margin-bottom: 25px; display: flex; justify-content: space-between; align-items: center; } .crafting-container { display: flex; flex-wrap: wrap; gap: 15px; justify-content: center; } .craft-card { background: rgba(10, 12, 18, 0.95); border: 1px solid #1e3a8a; border-radius: 6px; flex: 1 1 280px; min-width: 0; max-width: 560px; display: flex; flex-direction: column; justify-content: space-between; box-shadow: 0 4px 15px rgba(0,0,0,0.7); overflow: hidden; } .craft-img { width: 100%; height: 140px; object-fit: cover; border-bottom: 2px solid #1e3a8a; background: #000; } .cyber-craft-btn { display: block; width: 100%; background: linear-gradient(135deg, #0369a1 0%, #0284c7 50%, #0369a1 100%); border: 1px solid #38bdf8; color: #f8fafc !important; padding: 10px 12px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.9rem; letter-spacing: 1px; border-radius: 4px; cursor: pointer; text-decoration: none !important; box-shadow: inset 0 0 8px rgba(56, 189, 248, 0.4), 0 0 10px rgba(2, 132, 199, 0.4); transition: all 0.2s ease-in-out; box-sizing: border-box; } .cyber-craft-btn:hover { background: linear-gradient(135deg, #0369a1 0%, #0c4a6e 50%, #0369a1 100%); border-color: #7dd3fc; color: #ffffff !important; box-shadow: inset 0 0 12px rgba(125, 211, 252, 0.6), 0 0 18px rgba(56, 189, 248, 0.7); transform: translateY(-1px); } .cyber-disabled-btn { display: block; width: 100%; background: rgba(15, 23, 42, 0.6); border: 1px dashed #475569; color: #64748b; padding: 10px 12px; text-align: center; font-weight: bold; font-family: 'Courier New', monospace; font-size: 0.8rem; border-radius: 4px; cursor: not-allowed; box-sizing: border-box; } .cyber-return-btn { display: inline-block; background: linear-gradient(135deg, #0f766e 0%, #115e59 100%); border: 1px solid #2dd4bf; color: #99f6e4 !important; padding: 12px 30px; font-weight: bold; font-family: 'Courier New', monospace; letter-spacing: 2px; border-radius: 4px; text-decoration: none !important; box-shadow: 0 0 15px rgba(45, 212, 191, 0.3); transition: all 0.2s; } .cyber-return-btn:hover { background: linear-gradient(135deg, #115e59 0%, #0f766e 100%); border-color: #5eead4; color: #ffffff !important; box-shadow: 0 0 20px rgba(94, 234, 212, 0.5); } </style> <<nobr>> <div class="workshop-container"> <div style="text-align: center; border-bottom: 1px dashed #3498db; padding-bottom: 15px; margin-bottom: 25px;"> <h2 style="color: #3498db; margin: 0; letter-spacing: 3px; text-shadow: 0 0 10px #3498db;">[ WORKSHOP TERMINAL ]</h2> <div style="margin-top: 10px; font-size: 0.9rem; color: #aaa;">FABRICATION & BASE UPGRADES (LEVEL: <<print _currentWorkshopLevel>>)</div> <<if _isTeki>> <div style="margin-top: 5px; font-size: 0.85rem; color: #f59e0b; font-weight: bold;">✨ TECHIE PASSIVE ACTIVE: Mastercraft Crafting Bonus (+1 Rarity Tier)</div> <<endif>> <div style="margin-top: 5px; font-size: 0.85rem; color: #38bdf8;">CURRENT CRAFTING QUALITY: <<print _craftRarity.toUpperCase()>></div> </div> <!-- CRAFTING LIST --> <div class="crafting-container"> <!-- 1. MEDICAL STIMPACK --> <div class="craft-card"> <img src="images/items/craft_stimpack.jpg" class="craft-img" onerror="this.src='https://placehold.co/600x400/000/38bdf8?text=STIMPACK';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #38bdf8; font-size: 0.95rem; font-weight: bold; margin-bottom: 4px;">💊 MEDICAL STIMPACK</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.2;">Restores HP when used in exploration.</div> <div style="color: #cbd5e1; font-size: 0.75rem; margin-bottom: 10px; font-family: monospace;">COST: 1 Med, 1 Water, 5 Berries</div> </div> <div> <<if setup.have("medicine") gte 1 and setup.have("water") gte 1 and setup.have("berries") gte 5>> <<link "<span class='cyber-craft-btn'>🔨 CRAFT STIMPACK</span>" "Workshop">> <<run setup.spend("medicine", 1)>><<run setup.spend("water", 1)>><<run setup.spend("berries", 5)>> <<if ndef $materials.stimpack>><<set $materials.stimpack to 0>><</if>> <<set $materials.stimpack += 1>> <</link>> <<else>> <div class="cyber-disabled-btn">[ INSUFFICIENT MATERIALS ]</div> <<endif>> </div> </div> </div> <!-- 2. REINFORCED ROPE --> <div class="craft-card"> <img src="images/items/craft_rope.jpg" class="craft-img" onerror="this.src='https://placehold.co/600x400/000/fbbf24?text=ROPE';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #fbbf24; font-size: 0.95rem; font-weight: bold; margin-bottom: 4px;">🧵 REINFORCED ROPE</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.2;">Required to capture enemies in combat.</div> <div style="color: #cbd5e1; font-size: 0.75rem; margin-bottom: 10px; font-family: monospace;">COST: 5 Cloth</div> </div> <div> <<if setup.have("cloth") gte 5>> <<link "<span class='cyber-craft-btn'>🔨 CRAFT ROPE</span>" "Workshop">> <<run setup.spend("cloth", 5)>> <<if ndef $materials.rope>><<set $materials.rope to 0>><</if>> <<set $materials.rope += 1>> <</link>> <<else>> <div class="cyber-disabled-btn">[ INSUFFICIENT MATERIALS ]</div> <<endif>> </div> </div> </div> <<set _knifeScrap to Math.round(10 * _rarityMultiplier)>> <<set _knifeWood to Math.round(5 * _rarityMultiplier)>> <!-- 3. COMBAT KNIFE --> <div class="craft-card"> <img src="images/items/craft_knife.jpg" class="craft-img" onerror="this.src='https://placehold.co/600x400/000/f87171?text=KNIFE';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #f87171; font-size: 0.95rem; font-weight: bold; margin-bottom: 4px;">🔪 COMBAT KNIFE (<<print _craftRarity>>)</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.2;">Standard issue melee weapon. Upgrades quality.</div> <div style="color: #cbd5e1; font-size: 0.75rem; margin-bottom: 10px; font-family: monospace;">COST: <<print _knifeScrap>> Scrap, <<print _knifeWood>> Wood</div> </div> <div> <<if setup.have("scrap") gte _knifeScrap and setup.have("wood") gte _knifeWood>> <<link "<span class='cyber-craft-btn'>🔨 FORGE KNIFE</span>" "Workshop">> <<run setup.spend("scrap", _knifeScrap)>><<run setup.spend("wood", _knifeWood)>> <<set _itemName to _craftRarity + " Combat Knife">> <<set $equipment.rightHand to _itemName>> <<if ndef $gearInventory>><<set $gearInventory to []>><</if>> <<run $gearInventory.push(_itemName)>> <</link>> <<else>> <div class="cyber-disabled-btn">[ INSUFFICIENT MATERIALS ]</div> <<endif>> </div> </div> </div> <<set _armorScrap to Math.round(20 * _rarityMultiplier)>> <<set _armorCloth to Math.round(10 * _rarityMultiplier)>> <!-- 4. SCRAP ARMOR --> <div class="craft-card"> <img src="images/items/craft_armor.jpg" class="craft-img" onerror="this.src='https://placehold.co/600x400/000/fbbf24?text=ARMOR';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #fbbf24; font-size: 0.95rem; font-weight: bold; margin-bottom: 4px;">🛡️ SCRAP ARMOR (<<print _craftRarity>>)</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.2;">Chest rig made from car doors. Adds +20 Max HP.</div> <div style="color: #cbd5e1; font-size: 0.75rem; margin-bottom: 10px; font-family: monospace;">COST: <<print _armorScrap>> Scrap, <<print _armorCloth>> Cloth</div> </div> <div> <<if setup.have("scrap") gte _armorScrap and setup.have("cloth") gte _armorCloth>> <<link "<span class='cyber-craft-btn'>🔨 CRAFT ARMOR</span>" "Workshop">> <<run setup.spend("scrap", _armorScrap)>><<run setup.spend("cloth", _armorCloth)>> <<set _itemName to _craftRarity + " Scrap Armor">> <<set $equipment.chest to _itemName>> <<set $maxHp += 20, $hp += 20>> <<if ndef $gearInventory>><<set $gearInventory to []>><</if>> <<run $gearInventory.push(_itemName)>> <</link>> <<else>> <div class="cyber-disabled-btn">[ INSUFFICIENT MATERIALS ]</div> <<endif>> </div> </div> </div> <<set _helmetScrap to Math.round(10 * _rarityMultiplier)>> <<set _helmetCloth to Math.round(5 * _rarityMultiplier)>> <!-- 5. SCRAP HELMET --> <div class="craft-card"> <img src="images/items/craft_helmet.jpg" class="craft-img" onerror="this.src='https://placehold.co/600x400/000/38bdf8?text=HELMET';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #38bdf8; font-size: 0.95rem; font-weight: bold; margin-bottom: 4px;">🪖 SCRAP HELMET (<<print _craftRarity>>)</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.2;">Improvised steel headgear. Protects head.</div> <div style="color: #cbd5e1; font-size: 0.75rem; margin-bottom: 10px; font-family: monospace;">COST: <<print _helmetScrap>> Scrap, <<print _helmetCloth>> Cloth</div> </div> <div> <<if setup.have("scrap") gte _helmetScrap and setup.have("cloth") gte _helmetCloth>> <<link "<span class='cyber-craft-btn'>🔨 CRAFT HELMET</span>" "Workshop">> <<run setup.spend("scrap", _helmetScrap)>><<run setup.spend("cloth", _helmetCloth)>> <<set _itemName to _craftRarity + " Scrap Helmet">> <<set $equipment.headgear to _itemName>> <<set $equipment.head to _itemName>> <<if ndef $gearInventory>><<set $gearInventory to []>><</if>> <<run $gearInventory.push(_itemName)>> <</link>> <<else>> <div class="cyber-disabled-btn">[ INSUFFICIENT MATERIALS ]</div> <<endif>> </div> </div> </div> <<set _legScrap to Math.round(15 * _rarityMultiplier)>> <<set _legCloth to Math.round(5 * _rarityMultiplier)>> <!-- 6. SCRAP LEGGUARDS --> <div class="craft-card"> <img src="images/items/craft_legguards.jpg" class="craft-img" onerror="this.src='https://placehold.co/600x400/000/34d399?text=LEGGUARDS';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #34d399; font-size: 0.95rem; font-weight: bold; margin-bottom: 4px;">🦵 LEGGUARDS (<<print _craftRarity>>)</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.2;">Reinforced metal plates for lower body.</div> <div style="color: #cbd5e1; font-size: 0.75rem; margin-bottom: 10px; font-family: monospace;">COST: <<print _legScrap>> Scrap, <<print _legCloth>> Cloth</div> </div> <div> <<if setup.have("scrap") gte _legScrap and setup.have("cloth") gte _legCloth>> <<link "<span class='cyber-craft-btn'>🔨 CRAFT LEGS</span>" "Workshop">> <<run setup.spend("scrap", _legScrap)>><<run setup.spend("cloth", _legCloth)>> <<set _itemName to _craftRarity + " Scrap Legguards">> <<set $equipment.legs to _itemName>> <<if ndef $gearInventory>><<set $gearInventory to []>><</if>> <<run $gearInventory.push(_itemName)>> <</link>> <<else>> <div class="cyber-disabled-btn">[ INSUFFICIENT MATERIALS ]</div> <<endif>> </div> </div> </div> <<set _bootScrap to Math.round(10 * _rarityMultiplier)>> <<set _bootWood to Math.round(5 * _rarityMultiplier)>> <!-- 7. SCRAP BOOTS --> <div class="craft-card"> <img src="images/items/craft_boots.jpg" class="craft-img" onerror="this.src='https://placehold.co/600x400/000/fbbf24?text=BOOTS';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #fbbf24; font-size: 0.95rem; font-weight: bold; margin-bottom: 4px;">🥾 SCRAP BOOTS (<<print _craftRarity>>)</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.2;">Sturdy boots wrapped in leather and metal.</div> <div style="color: #cbd5e1; font-size: 0.75rem; margin-bottom: 10px; font-family: monospace;">COST: <<print _bootScrap>> Scrap, <<print _bootWood>> Wood</div> </div> <div> <<if setup.have("scrap") gte _bootScrap and setup.have("wood") gte _bootWood>> <<link "<span class='cyber-craft-btn'>🔨 CRAFT BOOTS</span>" "Workshop">> <<run setup.spend("scrap", _bootScrap)>><<run setup.spend("wood", _bootWood)>> <<set _itemName to _craftRarity + " Scrap Boots">> <<set $equipment.boots to _itemName>> <<set $equipment.feet to _itemName>> <<if ndef $gearInventory>><<set $gearInventory to []>><</if>> <<run $gearInventory.push(_itemName)>> <</link>> <<else>> <div class="cyber-disabled-btn">[ INSUFFICIENT MATERIALS ]</div> <<endif>> </div> </div> </div> <<set _gloveScrap to Math.round(5 * _rarityMultiplier)>> <<set _gloveCloth to Math.round(5 * _rarityMultiplier)>> <!-- 8. REINFORCED GLOVES --> <div class="craft-card"> <img src="images/items/craft_gloves.jpg" class="craft-img" onerror="this.src='https://placehold.co/600x400/000/a855f7?text=GLOVES';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #a855f7; font-size: 0.95rem; font-weight: bold; margin-bottom: 4px;">🧤 GLOVES (<<print _craftRarity>>)</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.2;">Padded work gloves for better grip.</div> <div style="color: #cbd5e1; font-size: 0.75rem; margin-bottom: 10px; font-family: monospace;">COST: <<print _gloveScrap>> Scrap, <<print _gloveCloth>> Cloth</div> </div> <div> <<if setup.have("scrap") gte _gloveScrap and setup.have("cloth") gte _gloveCloth>> <<link "<span class='cyber-craft-btn'>🔨 CRAFT GLOVES</span>" "Workshop">> <<run setup.spend("scrap", _gloveScrap)>><<run setup.spend("cloth", _gloveCloth)>> <<set _itemName to _craftRarity + " Reinforced Gloves">> <<set $equipment.gloves to _itemName>> <<set $equipment.hands to _itemName>> <<if ndef $gearInventory>><<set $gearInventory to []>><</if>> <<run $gearInventory.push(_itemName)>> <</link>> <<else>> <div class="cyber-disabled-btn">[ INSUFFICIENT MATERIALS ]</div> <<endif>> </div> </div> </div> <<set _shieldScrap to Math.round(15 * _rarityMultiplier)>> <<set _shieldWood to Math.round(5 * _rarityMultiplier)>> <!-- 9. MAKESHIFT SHIELD --> <div class="craft-card"> <img src="images/items/craft_shield.jpg" class="craft-img" onerror="this.src='https://placehold.co/600x400/000/f87171?text=SHIELD';"> <div style="padding: 12px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"> <div> <div style="color: #f87171; font-size: 0.95rem; font-weight: bold; margin-bottom: 4px;">🛡️ SHIELD (<<print _craftRarity>>)</div> <div style="color: #94a3b8; font-size: 0.75rem; margin-bottom: 6px; line-height: 1.2;">Heavy riot shield welded from road signs.</div> <div style="color: #cbd5e1; font-size: 0.75rem; margin-bottom: 10px; font-family: monospace;">COST: <<print _shieldScrap>> Scrap, <<print _shieldWood>> Wood</div> </div> <div> <<if setup.have("scrap") gte _shieldScrap and setup.have("wood") gte _shieldWood>> <<link "<span class='cyber-craft-btn'>🔨 CRAFT SHIELD</span>" "Workshop">> <<run setup.spend("scrap", _shieldScrap)>><<run setup.spend("wood", _shieldWood)>> <<set _itemName to _craftRarity + " Makeshift Shield">> <<set $equipment.offHand to _itemName>> <<set $equipment.leftHand to _itemName>> <<if ndef $gearInventory>><<set $gearInventory to []>><</if>> <<run $gearInventory.push(_itemName)>> <</link>> <<else>> <div class="cyber-disabled-btn">[ INSUFFICIENT MATERIALS ]</div> <<endif>> </div> </div> </div> </div> <!-- RETURN TO SANCTUARY HUB --> <div style="text-align: center; margin-top: 30px;"> <<link "<span class='cyber-return-btn'>🏰 RETURN TO SANCTUARY HUB</span>" "Sanctuary Hub">> <</link>> </div> </div> <</nobr>>
<<setMenu "WastelandBar">> <<run setup.formEnsure()>> <<if ndef $activeCompanion>><<set $activeCompanion to "None">><</if>> <style> body { background-color: #050a05 !important; } #story, #passages, .passage { margin: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; background: transparent !important; } .av-world { position: absolute; top: 10px; left: 17.5em; right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 0 20px 20px 20px; width: calc(100% - 17.5em); } .roster-container { background-color: #1e0926; padding: 30px; border: 2px solid #a855f7; border-radius: 8px; font-family: 'Segoe UI', Tahoma, sans-serif; box-shadow: 0 0 25px rgba(168, 85, 247, 0.2); box-sizing: border-box; width: 100%; } .sf-h { color: #e9d5ff; font-weight: bold; letter-spacing: 2px; font-size: 0.85rem; margin: 22px 0 10px 0; border-bottom: 1px dashed #7e22ce; padding-bottom: 5px; } .sf-board { background: rgba(0,0,0,0.55); border: 1px solid #7e22ce; border-radius: 6px; padding: 14px; } .sf-foe { text-align: center; color: #f87171; font-family: monospace; font-size: 0.75rem; letter-spacing: 3px; padding: 4px 0 10px 0; } .sf-lane { border: 1px dashed #475569; border-radius: 5px; padding: 8px 10px; margin-bottom: 8px; min-height: 74px; } .sf-lane-front { border-color: #ef4444; } .sf-lane-back { border-color: #38bdf8; } .sf-lane-res { border-color: #64748b; } .sf-lane-you { border-color: #10b981; background: rgba(16,185,129,0.08); text-align: center; color: #6ee7b7; font-family: monospace; font-weight: bold; letter-spacing: 3px; min-height: 0; padding: 10px; } .sf-lane-t { font-family: monospace; font-size: 0.68rem; letter-spacing: 2px; color: #94a3b8; margin-bottom: 6px; } .sf-chips { display: flex; flex-wrap: wrap; gap: 8px; } .sf-chip { display: flex; align-items: center; gap: 8px; background: rgba(15,23,42,0.85); border: 1px solid #475569; border-radius: 4px; padding: 4px 10px 4px 4px; color: #e2e8f0; font-size: 0.78rem; font-weight: bold; } .sf-chip img { width: 38px; height: 52px; object-fit: cover; border-radius: 3px; background: #000; } .sf-chip i { display: block; font-style: normal; font-weight: normal; font-size: 0.66rem; color: #94a3b8; } .sf-empty { color: #64748b; font-size: 0.75rem; font-family: monospace; align-self: center; } .sf-warn { border: 1px solid #fbbf24; color: #fde68a; background: rgba(251,191,36,0.07); border-radius: 4px; padding: 7px 10px; font-size: 0.78rem; margin-top: 8px; } .sf-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); gap: 16px; } .sf-card { background: rgba(0,0,0,0.75); border: 1px solid #7e22ce; border-radius: 6px; padding: 14px; box-sizing: border-box; } .sf-card .sf-av { width: 96px; height: 132px; object-fit: cover; border-radius: 4px; border: 1px solid #a855f7; float: left; margin-right: 12px; margin-bottom: 6px; background: #000; } .sf-name { font-weight: bold; font-size: 1rem; } .sf-meta { color: #94a3b8; font-size: 0.72rem; font-family: monospace; margin: 3px 0 8px 0; } .sf-row { clear: both; margin-top: 8px; } .sf-lbl { color: #94a3b8; font-family: monospace; font-size: 0.68rem; letter-spacing: 2px; margin-bottom: 3px; } .sf-btn { display: inline-block; padding: 5px 9px; margin: 2px 3px 2px 0; font-family: 'Courier New', monospace; font-weight: bold; font-size: 0.72rem; color: #e9d5ff !important; background: rgba(30,9,38,0.95); border: 1px solid #7e22ce; border-radius: 4px; text-decoration: none !important; } .sf-btn:hover { background: #581c87; border-color: #e9d5ff; } .sf-on { display: inline-block; padding: 5px 9px; margin: 2px 3px 2px 0; font-family: 'Courier New', monospace; font-weight: bold; font-size: 0.72rem; color: #ecfdf5; background: #047857; border: 1px solid #10b981; border-radius: 4px; } .sf-desc { color: #cbd5e1; font-size: 0.74rem; line-height: 1.35; margin-top: 4px; } .sf-tools { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; } .sf-preset { background: rgba(0,0,0,0.6); border: 1px solid #7e22ce; border-radius: 5px; padding: 8px 12px; min-width: 190px; } .sf-preset div { color: #94a3b8; font-size: 0.68rem; font-family: monospace; margin-bottom: 4px; } .cyber-return-btn { display: inline-block; background: linear-gradient(135deg, #7e22ce 0%, #581c87 100%); border: 1px solid #c084fc; color: #f3e8ff !important; padding: 12px 30px; font-weight: bold; font-family: 'Courier New', monospace; letter-spacing: 2px; border-radius: 4px; text-decoration: none !important; box-shadow: 0 0 15px rgba(168, 85, 247, 0.3); } </style> <div class="av-world"> <div class="roster-container"> <div data-tour="squad-head" style="text-align: center; border-bottom: 1px dashed #a855f7; padding-bottom: 15px; margin-bottom: 10px;"> <h2 style="color: #c084fc; margin: 0; letter-spacing: 3px; font-size: 1.8rem; text-transform: uppercase;">[ SQUAD STRATEGY ]</h2> <div style="color: #d8b4fe; font-family: monospace; margin-top: 8px; font-size: 0.85rem;">Plan your opening formation here at base. Every fight starts the way you set it.</div> </div> <<if setup.squadSize() eq 0>> <div class="sf-warn" style="text-align:center; margin-bottom: 14px;">You have no allies yet. Hire mercenaries at the Mercenary Roster, then come back to plan your formation.</div> <<else>> <<set _cnt to setup.formCounts()>> <!-- OPENING FORMATION BOARD --> <<nobr>> <div class="sf-h" style="margin-top:12px;">OPENING FORMATION</div> <div class="sf-board" data-tour="squad-form"> <div class="sf-foe">▲ ▲ ▲ ENEMY ▲ ▲ ▲</div> <div class="sf-lane sf-lane-front"> <div class="sf-lane-t">FRONT LINE (MELEE RANGE, IN FRONT OF YOU)</div> <div class="sf-chips"> <<for _bi, _bid range setup.squadIds()>> <<if setup.formLane(_bid) eq "front">> <<set _bmd to setup.MERC[_bid]>> <<set _brole to setup.FORM_ROLES[setup.formGet(_bid).role]>> <div class="sf-chip"><img @src="setup.formAvatar(_bid)" onerror="this.style.visibility='hidden'"><div><<print setup.formName(_bid)>><i><<print _brole.icon>> <<print _brole.label>></i></div></div> <</if>> <</for>> <<if _cnt.front eq 0>><span class="sf-empty">nobody here</span><</if>> </div> </div> <div class="sf-lane sf-lane-you">▶ YOU ◀</div> <div class="sf-lane sf-lane-back"> <div class="sf-lane-t">BACK LINE (BEHIND YOU)</div> <div class="sf-chips"> <<for _bi, _bid range setup.squadIds()>> <<if setup.formLane(_bid) eq "back">> <<set _brole to setup.FORM_ROLES[setup.formGet(_bid).role]>> <div class="sf-chip"><img @src="setup.formAvatar(_bid)" onerror="this.style.visibility='hidden'"><div><<print setup.formName(_bid)>><i><<print _brole.icon>> <<print _brole.label>></i></div></div> <</if>> <</for>> <<if _cnt.back eq 0>><span class="sf-empty">nobody here</span><</if>> </div> </div> <div class="sf-lane sf-lane-res"> <div class="sf-lane-t">RESERVE (STAYS AT BASE, DOES NOT JOIN THE FIGHT)</div> <div class="sf-chips"> <<for _bi, _bid range setup.squadIds()>> <<if setup.formLane(_bid) eq "reserve">> <div class="sf-chip"><img @src="setup.formAvatar(_bid)" onerror="this.style.visibility='hidden'"><div><<print setup.formName(_bid)>><i>RESTING</i></div></div> <</if>> <</for>> <<if _cnt.reserve eq 0>><span class="sf-empty">nobody here</span><</if>> </div> </div> <<if _cnt.front + _cnt.back eq 0>> <div class="sf-warn">Everyone is in reserve: you will fight alone.</div> <<elseif _cnt.front eq 0>> <div class="sf-warn">Your front line is empty. Enemies will mostly go straight for you, and only rarely hit your back line.</div> <<else>> <div style="color:#94a3b8; font-size:0.74rem; margin-top:6px;">Enemies aim most of their attacks at your front line. Back line allies are rarely hit, so keep fragile ones behind and sturdy ones in front.</div> <</if>> </div> <</nobr>> <!-- QUICK TOOLS AND PRESETS --> <<nobr>> <div class="sf-h">QUICK TOOLS AND SAVED STRATEGIES</div> <div class="sf-tools" data-tour="squad-tools"> <<link "<span class='sf-btn'>⚡ AUTO-ARRANGE</span>" "SquadStrategy">><<run setup.formAuto()>><</link>> <<link "<span class='sf-btn'>↺ RESET ALL</span>" "SquadStrategy">><<run setup.formReset()>><</link>> <<for _pn to 0; _pn lt 3; _pn++>> <<capture _pn>> <<set _pinfo to setup.formPresetInfo(_pn)>> <<set _lblS to ("<span class='sf-btn'>SAVE</span>")>> <<set _lblL to ("<span class='sf-btn'>LOAD</span>")>> <div class="sf-preset"> <div>STRATEGY <<print (_pn + 1)>>: <<print (_pinfo ? _pinfo : "empty")>></div> <<link _lblS "SquadStrategy">><<run setup.formSave(_pn)>><</link>> <<if _pinfo>><<link _lblL "SquadStrategy">><<run setup.formLoad(_pn)>><</link>><</if>> </div> <</capture>> <</for>> </div> <</nobr>> <!-- PER-ALLY ASSIGNMENTS --> <<nobr>> <div class="sf-h">ASSIGNMENTS (TURN ORDER: TOP TO BOTTOM)</div> <div class="sf-grid" data-tour="squad-assign"> <<for _i, _id range setup.squadIds()>> <<capture _id>> <<set _f to setup.formGet(_id)>> <<set _md to setup.MERC[_id]>> <<set _col to (_md ? _md.color : "#fbbf24")>> <<set _lane to setup.formLane(_id)>> <div class="sf-card" @style="'border-color:' + _col + ';'"> <img class="sf-av" @src="setup.formAvatar(_id)" onerror="this.style.visibility='hidden'"> <div class="sf-name" @style="'color:' + _col + ';'"><<print (_i + 1)>>. <<print setup.formName(_id)>></div> <div class="sf-meta"><<if _md>><<print _md.rarity.toUpperCase()>> | <<print _md.role>><<else>>COMPANION<</if>></div> <div class="sf-lbl">STARTING POSITION</div> <div> <<if _lane eq "front">><span class="sf-on">FRONT LINE</span><<else>><<link "<span class='sf-btn'>FRONT LINE</span>" "SquadStrategy">><<run setup.formSet(_id, "pos", "front")>><</link>><</if>> <<if _lane eq "back">><span class="sf-on">BACK LINE</span><<else>><<link "<span class='sf-btn'>BACK LINE</span>" "SquadStrategy">><<run setup.formSet(_id, "pos", "back")>><</link>><</if>> <<if _lane eq "reserve">><span class="sf-on">RESERVE</span><<else>><<link "<span class='sf-btn'>RESERVE</span>" "SquadStrategy">><<run setup.formSet(_id, "reserve", true)>><</link>><</if>> </div> <div class="sf-row"> <div class="sf-lbl">TURN ORDER</div> <<if _i gt 0>><<link "<span class='sf-btn'>▲ ACT EARLIER</span>" "SquadStrategy">><<run setup.formMove(_id, -1)>><</link>><</if>> <<if _i lt (setup.squadSize() - 1)>><<link "<span class='sf-btn'>▼ ACT LATER</span>" "SquadStrategy">><<run setup.formMove(_id, 1)>><</link>><</if>> </div> <div class="sf-row"> <div class="sf-lbl">COMBAT ROLE</div> <<for _rk, _rd range setup.FORM_ROLES>> <<capture _rk, _rd>> <<if _f.role eq _rk>> <span class="sf-on"><<print _rd.icon>> <<print _rd.label>></span> <<else>> <<set _rlbl to ("<span class='sf-btn'>" + _rd.icon + " " + _rd.label + "</span>")>> <<link _rlbl "SquadStrategy">><<run setup.formSet(_id, "role", _rk)>><</link>> <</if>> <</capture>> <</for>> <div class="sf-desc"><<print setup.FORM_ROLES[_f.role].desc>></div> </div> </div> <</capture>> <</for>> </div> <</nobr>> <</if>> <div style="margin-top: 26px; text-align: center;"> <<link "<span class='cyber-return-btn'>⬅ RETURN TO BAR</span>" "WastelandBar">><</link>> </div> </div> </div>
<<nobr>> <<setMenu "Quest">> <div style="background: linear-gradient(rgba(10, 5, 0, 0.85), rgba(25, 10, 0, 0.95)); border: 2px solid #f97316; padding: 25px; font-family: 'Courier New', monospace; color: #fdba74; max-width: 100%; box-sizing: border-box;"> <div style="text-align: center; border-bottom: 2px dashed #f97316; padding-bottom: 12px; margin-bottom: 18px;"> <h2 style="color: #f97316; letter-spacing: 3px; margin: 0;">[ THE LAST SURVIVOR ]</h2> </div> <div style="color: #cbd5e1; line-height: 1.7;"> Among the people you have pulled out of the reptilian chains, one man hangs back until the others have gone to the fire. He is thin, his wrists raw from the shackles. He walks up to you and does not look away.<br><br> <div style="border-left: 4px solid #f97316; padding-left: 12px; margin: 12px 0; color: #fed7aa;"> "You've saved ten of us now. I owe you more than I can say, Operator, and I still have to ask for more. My wife and my little girl were taken before the convoy came for me. They were marched to the old Slaughterhouse. The lizards keep people there. Some do not come out. Please. Find them." </div> He holds your gaze. "I'll fight beside you. I'll do whatever you need. Just don't leave them there." </div> <div style="text-align: center; margin-top: 25px;"> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;padding:12px 22px;font-weight:bold;letter-spacing:1px;border-radius:4px;display:inline-block;margin:6px;<<link "ACCEPT: 'WE WILL BRING THEM HOME.'" "Camp">> <<set $quest_slaughterhouse to "active">> <<if ndef $activeQuest or $activeQuest eq "None">><<set $activeQuest to "Free the Captives">><</if>> <<run window.setup.updateSidebar()>> <</link>>@@ </div> </div> <</nobr>>
<<nobr>> <<setMenu "Quest">> <div style="background: linear-gradient(rgba(10, 0, 0, 0.85), rgba(20, 0, 0, 0.95)); border: 2px solid #ef4444; padding: 25px; font-family: 'Courier New', monospace; color: #fca5a5; max-width: 100%; box-sizing: border-box;"> <div style="text-align: center; border-bottom: 2px dashed #ef4444; padding-bottom: 12px; margin-bottom: 18px;"> <h2 style="color: #ef4444; letter-spacing: 4px; text-shadow: 0 0 10px #ef4444; margin: 0;">[ SECTOR 4: THE SLAUGHTERHOUSE ]</h2> <div style="margin-top: 8px; font-size: 0.85rem;">OUTER GATES - ASSAULT REQUIRED</div> </div> <div style="color: #cbd5e1; line-height: 1.7;"> The old industrial complex rises out of the toxic haze, its chimneys cold, its floodlights burning a sick green. Rusted conveyor tracks run from the gates into the dark. Somewhere inside, chains drag across concrete and something enormous breathes.<br><br> Reptilian guards patrol the loading docks. They have seen you coming. There is no way in except through them. </div> <div style="margin: 18px 0; padding: 12px; border: 1px dashed #ef4444; color: #fdba74; font-size: 0.9rem; text-align: center;"> EXPECT A FOUR-WAVE ASSAULT. HEAL, LOAD AMMO AND BRING YOUR BEST GEAR BEFORE YOU BREACH. </div> <div style="text-align: center; margin-top: 20px;"> @@color:#fecaca;background:rgba(239,68,68,0.16);border:1px solid #ef4444;padding:12px 24px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;margin:6px;<<link "BREACH THE GATES" "Combat">> <<set $combatContext to "slaughterhouse_assault">> <<set $targetPOIName to "Slaughterhouse">> <<set $targetPOILoot to "military">> <<set $currentLocation to "Slaughterhouse">> <<set $slaughterGas to false>> <<set $reinforcementPending to false>> <<unset $nextCombatEnemyGroup, $nextCombatEnemy, $checkpointWave>> <<set $combatWave to 1>> <<set $enemyGroup to [ { name: "Reptilian Slave Driver", hp: 55, currentHp: 55, maxHp: 55, minDmg: 10, maxDmg: 16, hpMult: 1.0, dmgMult: 1.0, drop: "techData" }, { name: "Reptilian Slave Driver", hp: 55, currentHp: 55, maxHp: 55, minDmg: 10, maxDmg: 16, hpMult: 1.0, dmgMult: 1.0, drop: "techData" }, { name: "Reptilian Cleaver Guard", hp: 70, currentHp: 70, maxHp: 70, minDmg: 12, maxDmg: 18, hpMult: 1.1, dmgMult: 1.05, drop: "techData" }, { name: "Reptilian Cleaver Guard", hp: 70, currentHp: 70, maxHp: 70, minDmg: 12, maxDmg: 18, hpMult: 1.1, dmgMult: 1.05, drop: "techData" } ]>> <<set $enemy to $enemyGroup[0]>> <<set $waveQueue to [ [ { name: "Reptilian Butcher Elite", hp: 115, currentHp: 115, maxHp: 115, minDmg: 18, maxDmg: 26, hpMult: 1.5, dmgMult: 1.3, drop: "techData" }, { name: "Reptilian Butcher Elite", hp: 115, currentHp: 115, maxHp: 115, minDmg: 18, maxDmg: 26, hpMult: 1.5, dmgMult: 1.3, drop: "techData" }, { name: "Reptilian Overseer", hp: 100, currentHp: 100, maxHp: 100, minDmg: 20, maxDmg: 28, hpMult: 1.4, dmgMult: 1.4, drop: "techData" } ], [ { name: "Reptilian Sanitation Drone", hp: 45, currentHp: 45, maxHp: 45, minDmg: 9, maxDmg: 14, hpMult: 0.85, dmgMult: 0.85, drop: "techData", gas: true }, { name: "Reptilian Sanitation Drone", hp: 45, currentHp: 45, maxHp: 45, minDmg: 9, maxDmg: 14, hpMult: 0.85, dmgMult: 0.85, drop: "techData", gas: true }, { name: "Reptilian Sanitation Drone", hp: 45, currentHp: 45, maxHp: 45, minDmg: 9, maxDmg: 14, hpMult: 0.85, dmgMult: 0.85, drop: "techData", gas: true } ], [ { name: "Abattoir Warden", hp: 120, currentHp: 120, maxHp: 120, minDmg: 12, maxDmg: 18, hpMult: 6, dmgMult: 1.1, drop: "techData", dmgReduction: 0.3 } ] ]>> <</link>>@@ @@color:#cbd5e1;border:1px solid #64748b;padding:12px 24px;letter-spacing:2px;border-radius:4px;display:inline-block;margin:6px;<<link "FALL BACK" "Map">><</link>>@@ </div> </div> <</nobr>>
<<nobr>> <<run setup.campInit()>> <<set _camp to $campSites.find(function(c){ return c.id === $campSel; })>> <<if not _camp>><<goto "Map">><</if>> <<set _cd to setup.CAMP[_camp.type]>> <div class="cr-wrap" @style="'--c:' + _cd.color + ';'"> <div class="cr-head"> <div class="cr-tag">[ <<print _cd.label.toUpperCase()>> CAMP HQ ]</div> <h2><<print _cd.icon>> <<print _camp.name.toUpperCase()>></h2> </div> <div class="cr-text"> <<if _camp.type is "reptilian">> Grom's interrogations pointed you to a Reptilian outpost. Scrappers hold the wall, cleaver guards and elite butchers wait behind it, and a camp warden commands the whole garrison. They do not fall back and they do not run out of soldiers. <<else>> Grom's interrogations pointed you to a gang headquarters. Scavengers hold the gate, hardened raiders and a grizzled veteran wait inside, and the warboss sits at the back with his men. <</if>> </div> <div class="cr-grid"> <div class="cr-cell"><b><<print _cd.waves>></b><span>WAVES</span></div> <div class="cr-cell"><b>SCALES</b><span>WITH YOUR LEVEL</span></div> <div class="cr-cell"><b>1</b><span>BOSS AT THE END</span></div> </div> <div class="cr-panel"> <div class="cr-ptitle">SPOILS</div> <<if _camp.type is "reptilian">>Scrip, scrap, tech data, medicine, high reputation and a lot of XP.<<else>>Scrip, scrap, ammo, cloth, reputation and XP.<</if>> Grom hears about every camp you clear. The camp disappears from the map for good. </div> <div class="cr-warn">There is no resting between waves, and a defeat here is a real defeat. Heal, load up and bring your best gear. Reptilian camps hit much harder than bandit camps.</div> <div class="cr-btns"> <<link "<span class='cr-btn go'>⚔ ASSAULT THE CAMP</span>" "Combat">><<run setup.campRaidStart($campSel)>><</link>> <<link "<span class='cr-btn'>BACK TO THE MAP</span>" "Map">><</link>> </div> </div> <</nobr>>
<<nobr>> <<if ndef $waveQueue or $waveQueue.length lt 1 or ndef $campRaid>><<goto "Map">><</if>> <<set _cd to setup.CAMP[$campRaid.type]>> <div class="cr-wrap" @style="'--c:' + _cd.color + ';'"> <div class="cr-head"> <div class="cr-tag">[ <<print $campRaid.name.toUpperCase()>> ]</div> <h2>WAVE <<print ($combatWave + 1)>> / <<print $campRaid.waves>> INCOMING</h2> </div> <div class="cr-text" style="text-align:center;"> <<if $campRaid.type is "bandit">> <<if $waveQueue.length gt 1>> The gate is down. Hardened raiders and a grizzled veteran pour out of the barracks. They are not here to rob you. They are here to bury you. <<else>> The shouting stops. Heavy steps cross the yard, and the warboss steps out with his veterans. This is the last of them. <</if>> <<else>> <<if $waveQueue.length gt 2>> Cleaver guards and a slave driver pound down the corridor, blades out. The garrison is awake now. <<elseif $waveQueue.length is 2>> Elite butchers and an overseer step out of the barracks. Their armor is thicker, and so is their hunger. <<else>> The floor shakes. The camp warden has heard everything, and he is coming out with his guards. This is the garrison's last stand. <</if>> <</if>> </div> <div class="cr-btns" style="justify-content:center;"> <<link "<span class='cr-btn go'>⚔ ENGAGE WAVE</span>" "Combat">> <<set $combatWave += 1>> <<set $enemyGroup to $waveQueue.shift()>> <<set $enemy to $enemyGroup[0]>> <<unset $combatLog>> <</link>> <<link "<span class='cr-btn'>ABORT THE RAID</span>" "Map">><<run setup.campRaidAbort()>><</link>> </div> <div class="cr-text" style="text-align:center; font-size:0.75rem; color:#a8a29e; margin-top:10px;">Aborting leaves the camp on the map, but the waves you cleared come back.</div> </div> <</nobr>>
<<nobr>> <<if ndef $campReport>><<goto "Map">><</if>> <<set _cd to setup.CAMP[$campReport.type]>> <<if not $campReport.moraled>><<set $campReport.moraled to true>><<run setup.cLife.add(5, "You cleared an enemy camp")>><</if>> <div class="cr-wrap" @style="'--c:' + _cd.color + ';'"> <div class="cr-head"> <div class="cr-tag">[ CAMP CLEARED ]</div> <h2><<print $campReport.name.toUpperCase()>></h2> </div> <div class="cr-text"> <<if $campReport.type is "bandit">> The warboss drops and what is left of the gang scatters into the ruins. You go through the stores, load what is worth carrying and burn the rest. The camp is gone from the map. <<else>> The camp warden collapses and the outpost goes silent. You strip the armory and the tech racks, then pull the beacon so nothing comes back for it. The camp is gone from the map. <</if>> </div> <div class="cr-panel"> <div class="cr-ptitle">SPOILS</div> <<for _ln range $campReport.lines>> <div><<print _ln>></div> <</for>> </div> <div class="cr-btns" style="justify-content:center;"> <<link "<span class='cr-btn go'>BACK TO THE MAP</span>" "Map">><<unset $campReport>><<run window.setup.updateSidebar()>><</link>> </div> </div> <</nobr>>
<<nobr>> <<setMenu "Quest">> <<if ndef $waveQueue or $waveQueue.length lt 1>><<goto "Slaughterhouse_Floor1">><</if>> <div style="background: linear-gradient(rgba(10, 0, 0, 0.85), rgba(20, 0, 0, 0.95)); border: 2px solid #ef4444; padding: 25px; font-family: 'Courier New', monospace; color: #fca5a5; max-width: 100%; box-sizing: border-box;"> <div style="text-align: center; border-bottom: 2px dashed #ef4444; padding-bottom: 12px; margin-bottom: 18px;"> <h2 style="color: #ef4444; letter-spacing: 4px; margin: 0;">[ WARNING: WAVE <<print ($combatWave + 1)>> / 4 INCOMING ]</h2> </div> <div style="color: #cbd5e1; line-height: 1.7; text-align: center;"> <<if $combatWave eq 1>> Heavy boots pound the kill floor. The camp's elite butchers and an overseer are coming down the ramp. <<elseif $combatWave eq 2>> Sirens wail. Sanitation drones unseal the ceiling vents and flood the hall with toxic gas. Every turn in it will cost you health. <<else>> The floor shakes. The gas thins. Something huge and armored is coming out of the cold storage. This is the camp's warden. <</if>> </div> <div style="text-align: center; margin-top: 22px;"> @@color:#fecaca;background:rgba(239,68,68,0.16);border:1px solid #ef4444;padding:14px 28px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "ENGAGE WAVE" "Combat">> <<set $combatWave += 1>> <<set $enemyGroup to $waveQueue.shift()>> <<set $enemy to $enemyGroup[0]>> <<set $slaughterGas to ($enemyGroup[0].gas eq true)>> <<unset $combatLog>> <</link>>@@ </div> </div> <</nobr>>
<<nobr>> <<if ndef $enc>><<goto "Map">><</if>> <<set _def to setup.encounters[$enc.id]>> <<audio ":all" stop>> <div style="background: linear-gradient(rgba(10, 6, 0, 0.88), rgba(24, 12, 0, 0.96)); border: 2px solid #f59e0b; padding: 22px; font-family: 'Courier New', monospace; color: #fcd34d; max-width: 100%; box-sizing: border-box;"> <div style="text-align: center; border-bottom: 2px dashed #f59e0b; padding-bottom: 12px; margin-bottom: 16px;"> <div style="font-size: 0.75rem; letter-spacing: 3px; color: #fbbf24;">[ <<print _def.tag>> ]</div> <h2 style="color: #f59e0b; letter-spacing: 3px; margin: 6px 0 0 0;"><<print _def.title.toUpperCase()>></h2> </div> <<if _def.npcId>> <<set _npc to setup.npcRoster[_def.npcId]>> <div style="text-align: center; margin-bottom: 16px;"> <img @src="_npc.portrait" @data-n="_npc.name" onerror="this.onerror=null;this.src='https://placehold.co/300x400/0b0f19/f59e0b?text='+encodeURIComponent(this.getAttribute('data-n'));" style="width: 200px; max-width: 60%; border: 2px solid #f59e0b; border-radius: 6px; box-shadow: 0 0 20px rgba(245,158,11,0.3);"> <div style="color: #fbbf24; font-weight: bold; margin-top: 8px;"><<print _npc.name>></div> <div style="color: #94a3b8; font-size: 0.8rem;"><<print _npc.role>></div> </div> <</if>> <<if _def.img>><div style="text-align:center; margin-bottom:16px;"><img @src="_def.img" onerror="this.parentNode.style.display='none';" style="width:100%; max-width:760px; max-height:340px; object-fit:cover; border:2px solid #f59e0b; border-radius:6px; box-shadow:0 0 24px rgba(245,158,11,0.25);"></div><</if>> <div style="color: #cbd5e1; line-height: 1.7; margin-bottom: 20px;"><<print setup.encFmt(_def.intro, $enc.names)>></div> <<for _i to 0; _i lt _def.options.length; _i++>> <<set _o to _def.options[_i]>> <<set _cost to setup.encScaledCost(_def, _o)>> <<set _paid to setup.encCanPay(_cost)>> <<set _lbl to _o.label + (_o.cost ? " [COST: " + setup.encCostText(_cost) + "]" : "")>> <<set _req to setup.encReqMet(_o.req)>> <<capture _i, _o, _lbl>> <div style="margin-bottom: 10px;"> <<if _paid and _req>> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;display:block;padding:12px 16px;border-radius:4px;font-weight:bold;letter-spacing:1px;<<link _lbl>><<run setup.encChoose(_i)>><<goto $encGoto>><</link>>@@ <<else>> <div style="color:#78716c; border:1px dashed #57534e; padding:12px 16px; border-radius:4px;"><<print _lbl>> <div style="font-size:0.75rem; margin-top:4px; color:#a8a29e;"> <<if not _req>>Requires: <<print _o.req.label>>.<</if>> <<if not _paid>>Not enough resources (<<print setup.encCostText(_cost)>>).<</if>> </div> </div> <</if>> </div> <</capture>> <</for>> <<if _def.pool neq "gate" and not _def.noAvoid>> <div style="text-align: center; margin-top: 14px;"> @@color:#cbd5e1;border:1px solid #64748b;padding:10px 20px;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "AVOID THEM AND MOVE ON (LEAVE IT FOR LATER)">><<set $encResult to { text: (_def.pool eq "road" ? "You keep low, wait for the coast to clear and slip away without being noticed." : "You decide it is not worth the risk right now. The location stays marked on your map."), lines: [] }>><<goto "EncounterResult">><</link>>@@ </div> <</if>> </div> <</nobr>>
<<nobr>> <<if ndef $encResult>><<goto "Map">><</if>> <div style="background: linear-gradient(rgba(10, 6, 0, 0.88), rgba(24, 12, 0, 0.96)); border: 2px solid #f59e0b; padding: 22px; font-family: 'Courier New', monospace; color: #fcd34d; max-width: 100%; box-sizing: border-box;"> <div style="text-align: center; border-bottom: 2px dashed #f59e0b; padding-bottom: 12px; margin-bottom: 16px;"> <h2 style="color: #f59e0b; letter-spacing: 3px; margin: 0;">[ OUTCOME ]</h2> </div> <<if def $enc and setup.encounters[$enc.id].npcId and ($npcFlags || {})[setup.encounters[$enc.id].npcId]>> <<set _npc to setup.npcRoster[setup.encounters[$enc.id].npcId]>> <div style="text-align: center; margin-bottom: 14px;"> <img @src="_npc.portrait" @data-n="_npc.name" onerror="this.onerror=null;this.src='https://placehold.co/300x400/0b0f19/f59e0b?text='+encodeURIComponent(this.getAttribute('data-n'));" style="width: 160px; max-width: 50%; border: 2px solid #34d399; border-radius: 6px;"> <div style="color: #34d399; font-weight: bold; margin-top: 6px;">WELCOME TO THE SANCTUARY, <<print _npc.name.toUpperCase()>></div> </div> <</if>> <div style="color: #cbd5e1; line-height: 1.7; margin-bottom: 16px;"><<print $encResult.text>></div> <<for _l to 0; _l lt $encResult.lines.length; _l++>> <div style="color: #a7f3d0; font-size: 0.9rem; margin-bottom: 4px;">> <<print $encResult.lines[_l]>></div> <</for>> <div style="text-align: center; margin-top: 20px;"> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;padding:12px 28px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "CONTINUE">><<set _back to ($encReturn || "Map")>><<unset $enc, $encResult, $encReturn, $encGoto>><<goto _back>><</link>>@@ </div> </div> <</nobr>>
<<nobr>> <div style="background: linear-gradient(rgba(10, 6, 0, 0.92), rgba(24, 12, 0, 0.98)); border: 2px solid #f59e0b; padding: 26px; font-family: 'Courier New', monospace; color: #fcd34d; text-align: center; box-shadow: 0 0 30px rgba(245, 158, 11, 0.4);"> <div style="font-size: 2.4rem; margin-bottom: 8px;">🚪</div> <h2 style="color: #f59e0b; letter-spacing: 4px; margin: 0 0 12px 0;">[ SOMEONE IS AT THE GATE ]</h2> <div style="color: #cbd5e1; line-height: 1.7; margin-bottom: 20px;">The perimeter alarm just chimed. Word of the Svietlok has reached the wasteland, and a stranger is standing outside the blast door asking for shelter. The camera feed is live.</div> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;display:block;padding:14px 18px;border-radius:4px;font-weight:bold;letter-spacing:2px;margin-bottom:10px;<<link "OPEN THE CAMERA FEED">><<run Dialog.close(); setup.seekerOpen()>><<goto "Encounter">><</link>>@@ @@color:#cbd5e1;border:1px solid #64748b;display:block;padding:10px 18px;border-radius:4px;letter-spacing:2px;<<link "WAIT (THEY WILL STAY AT THE GATE)">><<run Dialog.close()>><</link>>@@ </div> <</nobr>>
<<nobr>> <<if ndef $raidReport>><<goto "Camp">><</if>> <<set _r to $raidReport>> <div style="background: linear-gradient(rgba(10, 0, 0, 0.88), rgba(24, 0, 0, 0.96)); border: 2px solid <<if _r.win>>#34d399<<else>>#ef4444<</if>>; padding: 24px; font-family: 'Courier New', monospace; color: #fca5a5; max-width: 100%; box-sizing: border-box;"> <div style="text-align: center; border-bottom: 2px dashed #7f1d1d; padding-bottom: 12px; margin-bottom: 16px;"> <h2 style="color: <<if _r.win>>#34d399<<else>>#ef4444<</if>>; letter-spacing: 3px; margin: 0;"><<if _r.win>>[ RAID REPELLED ]<<else>>[ THE WALLS FELL ]<</if>></h2> <div style="margin-top: 6px; font-size: 0.8rem; color: #94a3b8;">Threat at the gate: <<print _r.threat>>%</div> </div> <div style="color: #cbd5e1; line-height: 1.7; margin-bottom: 14px;"> <<if _r.win>>The raiders are dead or fled, but the gate took a beating. The walls hold, for now.<<else>>The raiders poured through the breach before you could stop them. They burned, looted and killed for hours. You woke up in the ashes of your camp with a fraction of what you had.<</if>> </div> <div style="font-size: 0.9rem; line-height: 1.8;"> <div>🧱 Barricade damage: <strong style="color:#f87171;">-<<print _r.dmg>>%</strong> (integrity now <strong style="color:#fff;"><<print _r.integrity>>%</strong>)</div> <<if _r.crew gt 0>><div>💀 Wall defenders lost: <strong style="color:<<if _r.dead gt 0>>#ef4444<<else>>#34d399<</if>>;"><<print _r.dead>> of <<print _r.crew>></strong> <span style="color:#94a3b8;">(<<print _r.deathPct>>% death chance each)</span></div><</if>> <<if _r.civDead gt 0>><div>💀 Survivors, workers and prisoners lost: <strong style="color:#ef4444;"><<print _r.civDead>></strong></div><</if>> <<for _k, _v range _r.lost>><div>📦 Looted by raiders: <strong style="color:#ef4444;">-<<print _v>> <<print _k>></strong></div><</for>> </div> <div style="margin-top: 14px; color: #fbbf24; font-size: 0.85rem;">Re-assign defenders in Labor and patch the barricades at Defense Walls. Weak walls get your people killed.</div> <div style="text-align: center; margin-top: 22px;"> @@color:#fecaca;background:rgba(185,28,28,0.22);border:1px solid #ef4444;padding:12px 28px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "RETURN TO S.A.N.C.T.U.A.R.Y. BASE">><<unset $raidReport>><<run window.setup.updateSidebar()>><<goto "Camp">><</link>>@@ </div> </div> <</nobr>>
<<nobr>> <<if not setup.dg.active()>><<unset $combatContext>><</if>> <style> #story { margin-top: 0 !important; margin-right: 0 !important; margin-bottom: 0 !important; margin-left: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; } .av-world { position: relative; margin-left: 17.5em; margin-right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 20px; } </style> <div class="av-world"><div class="dgx-new"><div class="dgx-frame" id="dgx-root"></div></div></div> <<script>>$(document).one(":passagedisplay", function () { setup.dg.mountEntrance(); });<</script>> <</nobr>>
<<nobr>> <<if not setup.dg.active()>><<goto "MineEntrance">><</if>> <style> #story { margin-top: 0 !important; margin-right: 0 !important; margin-bottom: 0 !important; margin-left: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; } .av-world { position: relative; margin-left: 17.5em; margin-right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 20px; } </style> <div class="av-world"><div class="dgx-new"><div class="dgx-frame" id="dgx-root"></div></div></div> <<script>>$(document).one(":passagedisplay", function () { setup.dg.mount(); });<</script>> <</nobr>>
<<nobr>> <<run setup.ch2.prep()>> <<audio ":all" stop>> <style> #story { margin-top: 0 !important; margin-right: 0 !important; margin-bottom: 0 !important; margin-left: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; } .av-world { position: relative; margin-left: 17.5em; margin-right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 20px; } </style> <div class="av-world"><div class="kt-wrap" id="c2-root"></div></div> <<script>>$(document).one(":passagedisplay", function () { setup.ch2.mount(); });<</script>> <</nobr>>
<<nobr>> <<audio ":all" stop>> <div style="background-color: #050a05; padding: 40px; border: 2px solid #ef4444; border-radius: 8px; font-family: 'Courier New', monospace; text-align: center; margin-top: 8vh;"> <h1 style="color: #ef4444; font-size: 2.6rem; margin: 0; letter-spacing: 5px; text-shadow: 0 0 20px #ef4444;">[ SIGNAL LOST ]</h1> <div style="color: #cbd5e1; font-size: 1.1rem; margin-top: 20px; margin-bottom: 24px; line-height: 1.7;"> Operator <strong style="color: #fff;"><<print $playerName>></strong> was caught in the purge of Maintenance Shaft B-12.<br> Cipher warned you. She told you to keep your steps quick. You did not listen. </div> <div style="color: #fecaca; font-size: 1rem; margin: 0 auto 30px auto; border: 2px solid #ef4444; background: rgba(60,0,0,0.6); padding: 18px; max-width: 640px; line-height: 1.7;"> We are serious about permadeath in this game. If you don't read, and think you can survive a literal warning, think again. The game will not be forgiving next time. </div> <<if $ktDeathFinal>> <div style="color: #ef4444; font-size: 0.95rem; margin-bottom: 24px;">YOU WERE WARNED. THERE IS NO SECOND CHANCE LEFT.</div> <<link ">>> ACCEPT YOUR FATE <<<">><<run setup.kt.respawn()>><</link>> <<else>> <div style="color: #fde68a; font-size: 0.95rem; margin-bottom: 24px;">This is your only respawn. The next death is final.</div> <<link ">>> RESPAWN AT S.A.N.C.T.U.A.R.Y. <<<">><<run setup.kt.respawn()>><</link>> <</if>> </div> <</nobr>>
<<nobr>> <<run setup.kt.enter()>> <<audio ":all" stop>> <<audio "katowice_tunnels" volume 0.25 loop play>> <style> #story { margin-top: 0 !important; margin-right: 0 !important; margin-bottom: 0 !important; margin-left: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; } .av-world { position: relative; margin-left: 17.5em; margin-right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 20px; } </style> <div class="av-world"><div class="kt-wrap" id="kt-root"></div></div> <<script>>$(document).one(":passagedisplay", function () { setup.kt.mount(); });<</script>> <</nobr>>
<<nobr>> <<if ndef $relicFoundKey>><<goto "Map">><</if>> <<set _r to setup.relicInfo($relicFoundKey)>> <div class="rl-panel"> <div class="rl-title">[ FIELD LOG: RELIC DISCOVERED ]</div> <<for _i to 0; _i lt _r.s1.length; _i++>><<print setup.relicFmt(_r.s1[_i])>><</for>> <div class="rl-item"> <div class="rl-item-name">📜 <<print _r.item>></div> <div class="rl-meta"><strong>Rarity:</strong> <<print _r.rar>></div> <div class="rl-meta"><strong>Description:</strong> <<print _r.desc>></div> <div class="rl-meta"><strong>Action:</strong> Take it to Dr. Maya Chomsky in the S.A.N.C.T.U.A.R.Y. Archive Wing for deciphering.</div> </div> <div style="text-align:center; margin-top:18px;"> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;padding:12px 28px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "PACK THE RELIC AND CONTINUE">><<unset $relicFoundKey>><<goto "Map">><</link>>@@ </div> </div> <</nobr>>
<<nobr>> <<if ndef $mayaStage>><<set $mayaStage to 0>><</if>> <<set _m to setup.relicData.meet>> <div class="rl-panel"> <<if $mayaStage eq 0>> <div class="rl-title">[ MISSION 12: THE UNIVERSAL GRAMMAR ]</div> <<run setup.mayaVO("meet_scene1")>> <<for _i to 0; _i lt _m.scene1.length; _i++>><<print setup.relicFmt(_m.scene1[_i])>><</for>> <div style="text-align:center; margin-top:18px;"> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;padding:12px 28px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "PLACE THE BROKEN SLATE ON HER TABLE">><<set $mayaStage to 1>><<goto "MayaMeeting">><</link>>@@ </div> <<elseif $mayaStage eq 1>> <div class="rl-title">[ THE SCHOLAR'S HAND ]</div> <<run setup.mayaVO("meet_scene2")>> <<for _i to 0; _i lt _m.scene2.length; _i++>><<print setup.relicFmt(_m.scene2[_i])>><</for>> <div class="rl-head" style="margin-top:14px;">YOUR REPLY</div> <<for _L range ["A","B","C"]>> <<set _o to _m.opts[_L]>> <<set _can to setup.mayaReqMet(_L)>> <<capture _L, _o>> <div style="margin-bottom:10px;"> <<if _can>> <<if _L eq "C">><div class="rl-reply rl-reply-c"><<link _o.label>><<run setup.mayaJoin(_L)>><<set $mayaStage to 2>><<goto "MayaMeeting">><</link>></div><<else>><div class="rl-reply"><<link _o.label>><<run setup.mayaJoin(_L)>><<set $mayaStage to 2>><<goto "MayaMeeting">><</link>></div><</if>> <<else>> <div style="color:#78716c; border:1px dashed #57534e; padding:12px 16px; border-radius:4px;"><<print _o.label>><div style="font-size:0.75rem; margin-top:4px;">Requires: <<print _o.req>>.</div></div> <</if>> </div> <</capture>> <</for>> <<else>> <div class="rl-title">[ DR. MAYA CHOMSKY JOINS THE SANCTUARY ]</div> <<set _o to _m.opts[$mayaLast]>> <<run setup.mayaVO("meet_join_" + $mayaLast.toLowerCase())>> <<for _i to 0; _i lt _o.paras.length; _i++>><<print setup.relicFmt(_o.paras[_i])>><</for>> <<if $mayaDevTest>><div class="rl-tag" style="color:#bbf7d0;">DEV REPLAY: nothing was changed. No survivor, no approval, no flags.</div><</if>> <div class="rl-tag">+1 Survivor joins your garrison. Dr. Maya follows you back to camp and wants to talk to you about setting up a proper Archive Wing.</div> <div style="text-align:center; margin-top:18px;"> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;padding:12px 28px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "RETURN TO THE SURFACE">><<set $mayaStage to 0>><<set $mayaDevTest to false>><<run window.setup.updateSidebar()>><<goto "Map">><</link>>@@ </div> <</if>> </div> <</nobr>>
<<nobr>> <<setMenu "Sanctuary Depths">> <<if setup.ch3 and setup.ch3.autoDepths()>><<goto "Ch3Scene">><</if>> <<set _devc to (def $playerName and String($playerName).toLowerCase() eq "nakman")>> <<if not (_devc or ($npcFlags and $npcFlags.maya) or (setup.CIPHER_ROOM_OPEN and $npcFlags and $npcFlags.cipher))>><<goto "Sanctuary Hub">><</if>> <div class="av-world"> <div class="sanctuary-hub-container"> <div style="text-align:center; border-bottom:1px dashed #22d3ee; padding-bottom:10px; margin-bottom:12px;"> <h2 style="color:#67e8f9; margin:0; letter-spacing:3px; font-size:1.4rem; text-shadow:0 0 10px #22d3ee;">[ SANCTUARY LOWER LEVELS ]</h2> <div style="color:#94a3b8; font-family:monospace; font-size:0.75rem; margin-top:4px;">The freight elevator drops past the old service floors. Two sealed wings lie below.</div> </div> <div class="isolated-map-wrapper cs-wrap depths-scene"> <div class="radar-container cs-inner"> <img class="cs-photo" src="images/locations/sanctuary_depths_scene.jpg" alt="" onerror="this.style.display='none'"> <div class="cs-pin cs-gold" style="top: 55%; left: 27%;"> <<if _devc or ($npcFlags and $npcFlags.maya)>> <<if not $archive or $archive.s eq "none">> <<link '<span class="cs-ring"><span class="cs-ico">📚</span></span><span class="cs-plate"><b>ARCHIVE WING</b><i>Talk to Maya</i></span><span class="cs-tip">Dr. Maya Chomsky has a proposal for a translation workshop.</span>' "ArchiveBuild">><</link>> <<elseif $archive.s eq "build">> <span class="cs-locked"><span class="cs-ring"><span class="cs-ico">🔨</span></span><span class="cs-plate"><b>ARCHIVE WING</b><i>Under construction</i></span><span class="cs-tip">Ready on day <<print $archive.ready>>.</span></span> <<else>> <<link '<span class="cs-ring"><span class="cs-ico">📚</span></span><span class="cs-plate"><b>ARCHIVE WING</b><i>Translate relics</i></span><span class="cs-tip">Translate ancient relics and read the codex.</span>' "ArchiveWing">><</link>> <</if>> <<else>> <span class="cs-locked"><span class="cs-ring"><span class="cs-ico">🔒</span></span><span class="cs-plate"><b>ARCHIVE WING</b><i>Sealed</i></span><span class="cs-tip">Nobody here can read the old shelves yet.</span></span> <</if>> </div> <div class="cs-pin cs-cyan" style="top: 55%; left: 73%;"> <<if _devc or (setup.CIPHER_ROOM_OPEN and $npcFlags and $npcFlags.cipher)>> <<link '<span class="cs-ring"><span class="cs-ico">💾</span></span><span class="cs-plate"><b>SERVER ROOM</b><i>Cipher's signal sweep</i></span><span class="cs-tip">Cipher reads the whole sector's signals and marks leads on your map.</span>' "CipherRoom">><</link>> <<else>> <span class="cs-locked"><span class="cs-ring"><span class="cs-ico">🔒</span></span><span class="cs-plate"><b>SERVER ROOM</b><i>Sealed</i></span><span class="cs-tip">The racks are dark. Nobody is running them yet.</span></span> <</if>> </div> </div> </div> <div style="display:grid; grid-template-columns:1fr 1fr; gap:8px; font-family:monospace; font-size:0.78rem; margin:6px 0 14px;"> <div style="text-align:center;"> <<if $archive and $archive.s eq "built">> <span style="display:inline-block; background:rgba(0,20,10,0.6); border:1px solid #b45309; border-radius:4px; padding:4px 10px; color:#fde68a;">📚 Relics archived: <b><<print setup.relicSlotsDone()>> / <<print setup.relicSlotsTotal()>></b></span> <</if>> </div> <div style="text-align:center;"> <<if _devc or (setup.CIPHER_ROOM_OPEN and $npcFlags and $npcFlags.cipher)>> <span style="display:inline-block; background:rgba(0,20,10,0.6); border:1px solid #0e7490; border-radius:4px; padding:4px 10px; color:#a5f3fc;">💾 Signal sweep: <b><<print (setup.cipherSweepReady() ? "READY" : "DONE TODAY")>></b></span> <</if>> </div> </div> <div style="text-align:center; margin-top:10px;"> <<link "<span style='display:inline-block;background:linear-gradient(135deg,#0f766e,#115e59);border:1px solid #2dd4bf;color:#99f6e4;padding:8px 20px;font-weight:bold;border-radius:4px;font-family:monospace;letter-spacing:1px;font-size:0.85rem;'>🛗 RIDE UP TO THE SANCTUARY HUB</span>" "Sanctuary Hub">><</link>> </div> </div> </div> <</nobr>>
<<nobr>> <<if not ($npcFlags and $npcFlags.maya) and not (def $playerName and String($playerName).toLowerCase() eq "nakman")>><<goto "Camp">><</if>> <<if $archive and $archive.s neq "none">><<goto "Camp">><</if>> <<set _npc to setup.npcRoster.maya>> <<set _c to setup.ARCHIVE.cost>> <<set _ok to setup.canPayMap(_c)>> <div class="rl-panel"> <div class="rl-title">[ DR. MAYA'S PROPOSAL ]</div> <div style="text-align:center; margin-bottom:14px;"> <img @src="_npc.portrait" onerror="this.onerror=null;this.src='https://placehold.co/300x400/0b0f19/f59e0b?text=Dr.+Maya+Chomsky';" style="width:170px; max-width:50%; border:2px solid #f59e0b; border-radius:6px;"> </div> <div class="rl-line"><span class="rl-maya">Maya:</span> "I have been carrying my whole library in a leather satchel, Operator. That is not a research station. A real translation workshop needs a sealed room, clean power, shelving that does not leak, and a few spare mainframe cores."</div> <div class="rl-line"><span class="rl-mc">MC:</span> "What will it cost?"</div> <div class="rl-line"><span class="rl-maya">Maya:</span> "Materials, mostly. Give me a crew and a day, and I will turn that storeroom into an Archive Wing. After that, bring me every slate, seal and crystal drive you can find, and I will tell you what they say."</div> <div class="rl-head">ARCHIVE WING: MATERIALS</div> <div class="rl-meta"><<print setup.archiveCostText(_c)>></div> <div class="rl-meta">Construction takes <<print setup.ARCHIVE.days>> day. You will get a notice when it is ready.</div> <div style="margin-top:16px;"> <<if _ok>> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;display:block;padding:12px 16px;border-radius:4px;font-weight:bold;letter-spacing:1px;text-align:center;<<link "COMMISSION THE ARCHIVE WING">><<run setup.archiveStart()>><<goto "ArchiveBuildStart">><</link>>@@ <<else>> <div style="color:#78716c; border:1px dashed #57534e; padding:12px 16px; border-radius:4px; text-align:center;">NOT ENOUGH MATERIALS: <<print setup.archiveCostText(_c)>></div> <</if>> </div> <div style="text-align:center; margin-top:14px;"> @@color:#cbd5e1;border:1px solid #64748b;padding:10px 24px;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "NOT YET" "Sanctuary Depths">><</link>>@@ </div> </div> <</nobr>>
<<nobr>> <div class="rl-panel"> <div class="rl-title">[ CONSTRUCTION UNDERWAY ]</div> <div class="rl-line"><span class="rl-maya">Maya:</span> "Perfect. Do not let anyone touch the shelves until it is done. I will sleep on the floor if I have to."</div> <div class="rl-tag">The Archive Wing will be ready tomorrow. You will get a notice when it is finished.</div> <div style="text-align:center; margin-top:18px;"> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;padding:12px 28px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "RETURN TO LOWER LEVELS" "Sanctuary Depths">><</link>>@@ </div> </div> <</nobr>>
<<nobr>> <div style="background: linear-gradient(rgba(10, 6, 0, 0.92), rgba(24, 12, 0, 0.98)); border: 2px solid #f59e0b; padding: 26px; font-family: 'Courier New', monospace; color: #fcd34d; text-align: center; box-shadow: 0 0 30px rgba(245, 158, 11, 0.4);"> <div style="font-size: 2.4rem; margin-bottom: 8px;">📚</div> <h2 style="color: #f59e0b; letter-spacing: 4px; margin: 0 0 12px 0;">[ ARCHIVE WING COMPLETE ]</h2> <div style="color: #cbd5e1; line-height: 1.7; margin-bottom: 20px;">The last shelf is bolted down and the mainframe link is humming. Dr. Maya Chomsky has already moved in. The Archive Wing is open.</div> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;display:block;padding:14px 18px;border-radius:4px;font-weight:bold;letter-spacing:2px;margin-bottom:10px;<<link "VISIT THE ARCHIVE WING">><<run Dialog.close()>><<goto "ArchiveWing">><</link>>@@ @@color:#cbd5e1;border:1px solid #64748b;display:block;padding:10px 18px;border-radius:4px;letter-spacing:2px;<<link "LATER">><<run Dialog.close()>><</link>>@@ </div> <</nobr>>
<<nobr>> <<if not ($npcFlags and $npcFlags.maya) and not (def $playerName and String($playerName).toLowerCase() eq "nakman")>><<goto "Camp">><</if>> <<if not ($archive and $archive.s eq "built")>><<goto "Camp">><</if>> <<run setup.relicNotify()>> <<set $archiveNotice to []>> <<set _npc to setup.npcRoster.maya>> <<set _ap to ($mayaApproval === undefined ? 45 : $mayaApproval)>> <div class="rl-panel"> <div class="rl-title">[ S.A.N.C.T.U.A.R.Y. ARCHIVE WING ]</div> <div style="text-align:center; margin-bottom:14px;"> <img @src="_npc.portrait" onerror="this.onerror=null;this.src='https://placehold.co/300x400/0b0f19/f59e0b?text=Dr.+Maya+Chomsky';" style="width:170px; max-width:50%; border:2px solid #f59e0b; border-radius:6px;"> <div style="color:#fbbf24; font-weight:bold; margin-top:6px;"><<print _npc.name>></div> <div style="color:#94a3b8; font-size:0.8rem;"><<print _npc.role>></div> <div style="margin:8px auto 0; max-width:320px;"> <div style="color:#fbbf24; font-size:0.75rem; letter-spacing:2px;">APPROVAL: <<print _ap>> / 100 (<<print setup.mayaRank()>>)</div> <div style="background:#1c1917; border:1px solid #57534e; height:8px; border-radius:4px;"><div @style="'background:#f59e0b; height:100%; width:' + _ap + '%; border-radius:4px;'"></div></div> </div> </div> <<if $mayaFeedback>> <div class="rl-tag" style="text-align:left;"><<print $mayaFeedback>></div> <<set $mayaFeedback to "">> <</if>> <<if setup.relicDev()>> <div style="text-align:center; margin-bottom:12px;">@@color:#bbf7d0;border:1px dashed #4ade80;background:#111;display:block;padding:12px;text-align:center;letter-spacing:1px;<<link "READ ALL RELIC TRANSLATIONS FOR VOICEOVERS (DEV)">><<run setup.relicDevOpen()>><</link>>@@</div> <div style="text-align:center; margin-bottom:12px;">@@color:#bbf7d0;border:1px dashed #4ade80;background:#111;display:block;padding:12px;text-align:center;letter-spacing:1px;<<link "REPLAY THE MAYA MEETING (DEV, CHANGES NOTHING)">><<run setup.mayaDevReplay()>><</link>>@@</div> <</if>> <div style="display:flex; gap:10px; flex-wrap:wrap; margin-bottom:6px;"> <div style="flex:1; min-width:200px;">@@color:#e2e8f0;background:rgba(71,85,105,0.28);border:1px solid #64748b;display:block;padding:10px 16px;border-radius:4px;text-align:center;<<link "💬 TALK TO MAYA" "ArchiveWing">><<run setup.mayaChat()>><</link>>@@</div> <div style="flex:1; min-width:200px;">@@color:#e2e8f0;background:rgba(71,85,105,0.28);border:1px solid #64748b;display:block;padding:10px 16px;border-radius:4px;text-align:center;<<link "🎁 GIVE A GIFT" "ArchiveGift">><</link>>@@</div> </div> <div class="rl-head" style="margin-top:16px;">RELICS WAITING FOR MAYA</div> <<set _wait to false>> <<for _k range setup.relicOrder>> <<if setup.relicStatus(_k) eq "found">> <<set _wait to true>> <<set _r to setup.relicInfo(_k)>> <<capture _k, _r>> <div class="rl-card"> <div class="rl-card-title">📜 <<print _r.item>> <span class="rl-chip rl-chip-found">UNREAD RELIC</span></div> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;display:inline-block;padding:8px 16px;border-radius:4px;font-weight:bold;letter-spacing:1px;margin-top:6px;<<link "HAND IT TO MAYA">><<run setup.relicHandover(_k)>><<set $archiveView to { k: _k, stage: "handover" }>><<goto "ArchiveView">><</link>>@@ </div> <</capture>> <</if>> <</for>> <<if not _wait>><div class="rl-meta">Nothing to hand over right now.</div><</if>> <div class="rl-head" style="margin-top:18px;">RELIC CODEX (<<print setup.relicSlotsDone()>> / <<print setup.relicSlotsTotal()>> TRANSLATED)</div> <div class="rl-grid"> <<for _sl range setup.relicSlots()>> <<set _state to setup.relicSlotState(_sl)>> <<capture _sl, _state>> <div @class="'rl-slot rl-slot-' + _state"> <div class="rl-slot-no">RELIC <<print _sl.no>></div> <<if _state eq "none">> <div class="rl-slot-q">?</div> <div class="rl-meta" style="text-align:center;">NOT COLLECTED<br>NO INFORMATION</div> <<else>> <<for _k range _sl.keys>> <<set _r to setup.relicInfo(_k)>> <<set _s to setup.relicStatus(_k)>> <<capture _k, _r, _s>> <<if _s eq "none">> <div class="rl-meta" style="margin-top:6px;">Part <<print (_k eq "9a" ? 1 : 2)>>: NOT COLLECTED</div> <<elseif _s eq "found">> <div class="rl-card-title" style="font-size:0.85rem;"><<print _r.item>></div> <div class="rl-meta">Not yet translated.</div> <<elseif _s eq "trans">> <div class="rl-card-title" style="font-size:0.85rem;"><<print _r.item>></div> <div class="rl-meta">Translating... ready day <<print $relicState[_k].ready>></div> <<else>> <div class="rl-card-title" style="font-size:0.85rem;"><<print _r.t>></div> @@color:#99f6e4;background:rgba(15,118,110,0.25);border:1px solid #14b8a6;display:inline-block;padding:6px 12px;border-radius:4px;font-size:0.8rem;margin-top:6px;<<link "📖 READ CODEX ENTRY">><<run setup.relicRead(_k)>><<set $archiveView to { k: _k, stage: "reveal" }>><<goto "ArchiveView">><</link>>@@ <</if>> <</capture>> <</for>> <</if>> </div> <</capture>> <</for>> </div> <div class="rl-head" style="margin-top:18px;">TALK WITH DR. MAYA</div> <<set _any to false>> <<for _t range setup.relicData.talks>> <<if setup.talkAvailable(_t)>> <<set _any to true>> <<capture _t>> <<set _lbl to ("💬 " + _t.title)>> <div style="margin-bottom:8px;">@@color:#e2e8f0;background:rgba(71,85,105,0.28);border:1px solid #64748b;display:block;padding:10px 16px;border-radius:4px;<<link _lbl>><<set $archiveTalk to { id: _t.id, opt: null }>><<goto "ArchiveTalk">><</link>>@@</div> <</capture>> <</if>> <</for>> <<if not _any>><div class="rl-meta">She has nothing new to discuss yet. Translate more relics to unlock her stories.</div><</if>> <div style="text-align:center; margin-top:20px;"> @@color:#cbd5e1;border:1px solid #64748b;padding:10px 24px;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "BACK TO LOWER LEVELS" "Sanctuary Depths">><</link>>@@ </div> </div> <</nobr>>
<<nobr>> <<if not ($archive and $archive.s eq "built")>><<goto "Camp">><</if>> <div class="rl-panel"> <div class="rl-title">[ A GIFT FOR DR. MAYA ]</div> <div class="rl-nar">One gift per day. She is not impressed by expensive things, only by thoughtful ones.</div> <<if $mayaGiftDay eq $day>><div class="rl-tag">You have already given her a gift today. Come back tomorrow.</div><</if>> <<for _g range setup.MAYA_GIFTS>> <<set _can to setup.mayaGiftOk(_g)>> <<set _lbl to (_g.label + " [COST: " + setup.archiveCostText(_g.cost) + " | APPROVAL +" + _g.gain + "]")>> <<capture _g, _lbl>> <div style="margin-bottom:10px;"> <<if _can>> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;display:block;padding:12px 16px;border-radius:4px;font-weight:bold;<<link _lbl>><<run setup.mayaGive(_g.id)>><<goto "ArchiveWing">><</link>>@@ <<else>> <div style="color:#78716c; border:1px dashed #57534e; padding:12px 16px; border-radius:4px;"><<print _lbl>><<if _g.hint>><div style="font-size:0.75rem; margin-top:4px; color:#a8a29e;"><<print _g.hint>></div><</if>></div> <</if>> </div> <</capture>> <</for>> <div style="text-align:center; margin-top:14px;"> @@color:#cbd5e1;border:1px solid #64748b;padding:10px 24px;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "BACK TO THE ARCHIVE WING" "ArchiveWing">><</link>>@@ </div> </div> <</nobr>>
<<nobr>> <<if ndef $archiveView>><<goto "ArchiveWing">><</if>> <<set _r to setup.relicInfo($archiveView.k)>> <div class="rl-panel"> <<if $archiveView.stage eq "handover">> <div class="rl-title">[ HANDING OVER: <<print _r.item.toUpperCase()>> ]</div> <<for _i to 0; _i lt _r.s2.length; _i++>><<print setup.relicFmt(_r.s2[_i])>><</for>> <div class="rl-tag">Translation in progress. Dr. Maya will notify you when it is ready.</div> <<else>> <div class="rl-title">[ ARCHIVE ENTRY: <<print _r.t.toUpperCase()>> ]</div> <<run setup.relicVO(_r.k, "intro")>> <<for _i to 0; _i lt _r.s3.length; _i++>><<print setup.relicFmt(_r.s3[_i])>><</for>> <<set _kk to _r.k>> <div style="margin-top:16px;"><<print '<button id="rlCommBtn" class="rl-commbtn" onclick="setup.relicComm(\'' + _kk + '\')">' + setup.tr("\u25B6 DR. MAYA\'S HISTORICAL COMMENTARY \u2014 CLICK TO READ AND LISTEN") + '</button>'>></div> <div id="rlComm" style="display:none; margin-top:8px;"> <<for _i to 0; _i lt _r.s4.length; _i++>><<print setup.relicFmt(_r.s4[_i])>><</for>> </div> <</if>> <div style="text-align:center; margin-top:20px;"> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;padding:12px 28px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "BACK TO THE ARCHIVE WING" "ArchiveWing">><</link>>@@ </div> </div> <</nobr>>
<<nobr>> <<if ndef $archiveTalk>><<goto "ArchiveWing">><</if>> <<set _t to setup.relicData.talks.filter(function (x) { return x.id === State.variables.archiveTalk.id; })[0]>> <div class="rl-panel"> <div class="rl-title">[ <<print _t.title.toUpperCase()>> ]</div> <<for _i to 0; _i lt _t.intro.length; _i++>><<print setup.relicFmt(_t.intro[_i])>><</for>> <<if _t.options.length gt 0>> <<if $archiveTalk.opt neq null>> <<set _o to _t.options[$archiveTalk.opt]>> <div class="rl-head" style="margin-top:14px;"><<print _o.label>></div> <<for _i to 0; _i lt _o.paras.length; _i++>><<print setup.relicFmt(_o.paras[_i])>><</for>> <</if>> <div class="rl-head" style="margin-top:14px;">ASK MAYA</div> <<for _j to 0; _j lt _t.options.length; _j++>> <<capture _j>> <<set _lbl to ("💬 " + (_t.labels[_j] || _t.options[_j].label))>> <div style="margin-bottom:8px;">@@color:#e2e8f0;background:rgba(71,85,105,0.28);border:1px solid #64748b;display:block;padding:10px 16px;border-radius:4px;<<link _lbl>><<set $archiveTalk.opt to _j>><<goto "ArchiveTalk">><</link>>@@</div> <</capture>> <</for>> <</if>> <div style="text-align:center; margin-top:20px;"> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;padding:12px 28px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "BACK TO THE ARCHIVE WING" "ArchiveWing">><</link>>@@ </div> </div> <</nobr>>
<<nobr>> <div class="rl-panel" style="border-color:#ef4444;"> <div class="rl-title" style="color:#ef4444;">[ SECTOR-03: THE SUNKEN AMARNA SHRINE ]</div> <<for _i to 0; _i lt setup.relicData.boss.intro.length; _i++>><<print setup.relicFmt(setup.relicData.boss.intro[_i])>><</for>> <div class="rl-tag">Three phases. No retreat between them. If you fall, the shrine resets.</div> <div style="text-align:center; margin-top:18px;"> @@color:#fecaca;background:rgba(185,28,28,0.22);border:1px solid #ef4444;padding:12px 28px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;margin:6px;<<link "ENGAGE THE SCARRED PROPHET" "Combat">><<run setup.relicBossStart()>><</link>>@@ @@color:#cbd5e1;border:1px solid #64748b;padding:12px 24px;letter-spacing:2px;border-radius:4px;display:inline-block;margin:6px;<<link "FALL BACK" "Map">><</link>>@@ </div> </div> <</nobr>>
<<nobr>> <<if ndef $phaseQueue or $phaseQueue.length lt 1>><<goto "Map">><</if>> <div class="rl-panel" style="border-color:#ef4444;"> <div class="rl-title" style="color:#ef4444;">[ PHASE <<print ($combatWave + 1)>> / 3 ]</div> <<if $combatWave eq 1>> <div class="rl-nar">Enraged by the presence of a human surface beast, Sszakar-Aten discards his staff. He ruptures the surrounding geothermal pipes, plunging the arena into boiling steam, and launches devastating sibilant psionic blasts that scramble your tactical HUD.</div> <div class="rl-tag">PHASE 2: THERMAL MELTDOWN</div> <<else>> <div class="rl-nar">In a desperate final stand, Sszakar-Aten drops to all fours, reverting to primal reptilian instinct. His body radiates intense heat as he charges wildly through the steam, screaming shattered Iokharic curses.</div> <div class="rl-tag">PHASE 3: THE DESECRATED PROPHET</div> <</if>> <div style="text-align:center; margin-top:18px;"> @@color:#fecaca;background:rgba(185,28,28,0.22);border:1px solid #ef4444;padding:12px 28px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "KEEP FIGHTING" "Combat">><<set $combatWave += 1>><<set $enemyGroup to [ $phaseQueue.shift() ]>><<set $enemy to $enemyGroup[0]>><<unset $combatLog>><</link>>@@ </div> </div> <</nobr>>
<<nobr>> <<run setup.relicGrant("9b")>> <<unset $sszakarSite, $phaseQueue, $combatContext>> <div class="rl-panel" style="border-color:#34d399;"> <div class="rl-title" style="color:#34d399;">[ SSZAKAR-ATEN DEFEATED ]</div> <<for _i to 0; _i lt setup.relicData.boss.end.length; _i++>><<print setup.relicFmt(setup.relicData.boss.end[_i])>><</for>> <div class="rl-tag">Take the lower half to Dr. Maya in the Archive Wing.</div> <div style="text-align:center; margin-top:18px;"> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;padding:12px 28px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "RETURN TO THE SURFACE" "Map">><</link>>@@ </div> </div> <</nobr>>
<<nobr>> <<if ndef $npcView>><<goto "Rest">><</if>> <<set _n to setup.npcInfo($npcView)>> <<if not _n>><<goto "Rest">><</if>> <<set _b to setup.npcBond($npcView)>> <div class="rl-panel"> <div class="rl-title">[ <<print _n.name.toUpperCase()>> ]</div> <div style="text-align:center; margin-bottom:14px;"> <img @src="_n.portrait" @data-n="_n.name" onerror="this.onerror=null;this.src='https://placehold.co/300x400/0b0f19/f59e0b?text='+encodeURIComponent(this.getAttribute('data-n'));" style="width:180px; max-width:55%; border:2px solid #f59e0b; border-radius:6px;"> <div style="color:#94a3b8; font-size:0.8rem; margin-top:6px;"><<print _n.role>></div> <div style="margin:8px auto 0; max-width:320px;"> <div style="color:#fbbf24; font-size:0.75rem; letter-spacing:2px;">RELATIONSHIP: <<print setup.npcRankOf(_b)>> (LEVEL <<print _b.level>> / <<print setup.NPC_MAXLEVEL>>)</div> <div style="background:#1c1917; border:1px solid #57534e; height:8px; border-radius:4px; margin-top:4px;"><div @style="'background:#f59e0b; height:100%; width:' + _b.xp + '%; border-radius:4px;'"></div></div> <div style="color:#94a3b8; font-size:0.7rem; margin-top:3px;"><<if _b.level gte setup.NPC_MAXLEVEL>>MAX LEVEL REACHED<<else>><<print _b.xp>> / 100 to <<print setup.npcNextRank(_b)>><</if>></div> </div> </div> <<if $npcFeedback>> <div class="rl-tag" style="text-align:left;"><<print $npcFeedback>></div> <<set $npcFeedback to "">> <</if>> <div style="display:flex; gap:10px; flex-wrap:wrap; margin-bottom:8px;"> <div style="flex:1; min-width:180px;">@@color:#e2e8f0;background:rgba(71,85,105,0.28);border:1px solid #64748b;display:block;padding:10px 16px;border-radius:4px;text-align:center;<<link "💬 TALK" "NpcInteract">><<run setup.npcTalk($npcView)>><</link>>@@</div> <div style="flex:1; min-width:180px;">@@color:#e2e8f0;background:rgba(71,85,105,0.28);border:1px solid #64748b;display:block;padding:10px 16px;border-radius:4px;text-align:center;<<link "🎁 GIVE A GIFT" "NpcGift">><</link>>@@</div> <div style="flex:1; min-width:180px;"> <<if setup.npcToday($npcView, "playDay")>> <div style="color:#78716c; border:1px dashed #57534e; padding:10px 16px; border-radius:4px; text-align:center;">Already played today</div> <<elseif _n.type eq "animal">> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;display:block;padding:10px 16px;border-radius:4px;text-align:center;<<link "🎾 PLAY CATCH" "NpcCatch">><<run setup.catchStart($npcView)>><</link>>@@ <<else>> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;display:block;padding:10px 16px;border-radius:4px;text-align:center;<<link "♠ PLAY POKER" "NpcPoker">><<run setup.pokerStart($npcView)>><</link>>@@ <</if>> </div> </div> <div class="rl-meta" style="text-align:center;">Talk once a day, one gift a day, one game a day. A poker match is a few hands of Texas Hold'em.</div> <div style="text-align:center; margin-top:18px;"> <<set _bk to ($npcBack || "Rest")>><<set _bkl to (_bk eq "CipherRoom" ? "BACK TO THE SERVER ROOM" : "BACK TO THE QUARTERS")>> @@color:#cbd5e1;border:1px solid #64748b;padding:10px 24px;letter-spacing:2px;border-radius:4px;display:inline-block;<<link _bkl _bk>><</link>>@@ </div> </div> <</nobr>>
<<nobr>> <<if ndef $npcView>><<goto "Rest">><</if>> <<set _n to setup.npcInfo($npcView)>> <div class="rl-panel"> <div class="rl-title">[ A GIFT FOR <<print _n.name.toUpperCase()>> ]</div> <div class="rl-nar">One gift per day. Their favorite kind of gift means much more.</div> <<if setup.npcToday($npcView, "giftDay")>><div class="rl-tag">You have already given a gift today.</div><</if>> <<for _g range setup.npcGifts(_n.type, $npcView)>> <<set _fav to setup.npcGiftIsFav($npcView, _g)>> <<set _can to setup.npcGiftOk($npcView, _g)>> <<set _lbl to (_g.label + " [COST: " + setup.npcGiftCostText(_g.cost) + " | RELATIONSHIP +" + (_g.gain + (_fav ? 3 : 0)) + (_fav ? " FAVORITE" : "") + "]")>> <<capture _g, _lbl>> <div style="margin-bottom:10px;"> <<if _can>> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;display:block;padding:12px 16px;border-radius:4px;font-weight:bold;<<link _lbl>><<run setup.npcGive($npcView, _g.id)>><<goto "NpcInteract">><</link>>@@ <<else>> <div style="color:#78716c; border:1px dashed #57534e; padding:12px 16px; border-radius:4px;"><<print _lbl>></div> <</if>> </div> <</capture>> <</for>> <div style="text-align:center; margin-top:14px;"> @@color:#cbd5e1;border:1px solid #64748b;padding:10px 24px;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "BACK" "NpcInteract">><</link>>@@ </div> </div> <</nobr>>
<<nobr>> <<if ndef $poker>><<goto "Rest">><</if>> <<set _n to setup.npcInfo($poker.id)>> <<set _s to $poker>> <<set _first to _n.name.split(" ")[0]>> <div style="margin-top: 10px; background: linear-gradient(rgba(5, 15, 10, 0.72), rgba(5, 15, 10, 0.92)), url('images/locations/blackjack_bg.jpg') center/cover no-repeat; border: 2px dashed #22c55e; border-radius: 8px; padding: 20px; text-align: center; box-shadow: inset 0 0 20px rgba(34,197,94,0.3); max-width: 100%; box-sizing: border-box;"> <div style="color: #22c55e; font-weight: bold; font-size: 1.1rem; letter-spacing: 2px; margin-bottom: 4px; text-shadow: 0 0 10px rgba(34,197,94,0.8);">[ TEXAS HOLD'EM ]</div> <div style="color: #86efac; font-size: 0.75rem; letter-spacing: 1px; margin-bottom: 14px;">A friendly match with <<print _n.name>>. Play chips only, nothing at stake but pride. Hand <<print _s.hand>> of <<print setup.POKER.hands>>. Blinds <<print setup.POKER.sb>>/<<print setup.POKER.bb>>.</div> <!-- OPPONENT --> <div style="background: rgba(0,0,0,0.6); padding: 12px; border-radius: 6px; border: 1px solid rgba(239,68,68,0.3); margin-bottom: 12px;"> <div style="color: #fca5a5; font-size: 0.8rem; margin-bottom: 8px; letter-spacing: 1px;"><<print _first.toUpperCase()>><<if _s.button eq "o">> <span style="color:#fde047;">(DEALER)</span><</if>> | CHIPS: <span style="color:#fde047; font-weight:bold;"><<print _s.stack.o>></span><<if _s.bets.o gt 0>> | BET: <<print _s.bets.o>><</if>></div> <div style="display: flex; gap: 8px; justify-content: center;"><<print setup.pkOppHtml(_s)>></div> </div> <!-- BOARD AND POT --> <div style="background: rgba(0,0,0,0.45); padding: 12px; border-radius: 6px; border: 1px solid rgba(250,204,21,0.25); margin-bottom: 12px;"> <div style="color: #fde047; font-size: 0.85rem; font-weight: bold; margin-bottom: 8px; letter-spacing: 2px;">POT: <<print setup.pkPot(_s)>></div> <div style="display: flex; gap: 8px; justify-content: center; flex-wrap: wrap;"><<print setup.pkBoardHtml(_s)>></div> </div> <!-- YOU --> <div style="background: rgba(0,0,0,0.6); padding: 12px; border-radius: 6px; border: 1px solid rgba(56,189,248,0.3); margin-bottom: 12px;"> <div style="color: #7dd3fc; font-size: 0.8rem; margin-bottom: 8px; letter-spacing: 1px;">YOU<<if _s.button eq "p">> <span style="color:#fde047;">(DEALER)</span><</if>> | CHIPS: <span style="color:#fde047; font-weight:bold;"><<print _s.stack.p>></span><<if _s.bets.p gt 0>> | BET: <<print _s.bets.p>><</if>></div> <div style="display: flex; gap: 8px; justify-content: center;"><<print setup.pkHandHtml(_s.hole.p, "p")>></div> </div> <!-- LOG --> <div style="color: #94a3b8; font-size: 0.78rem; text-align: left; background: rgba(0,0,0,0.5); padding: 8px 12px; border-radius: 4px; margin-bottom: 14px; line-height: 1.5;"> <<for _l range _s.log>><div>> <<print _l>></div><</for>> </div> <<if not _s.over>> <<set _call to _s.bets.o - _s.bets.p>> <<set _max to Math.min(_s.stack.p + _s.bets.p, _s.stack.o + _s.bets.o)>> <div style="display: flex; gap: 12px; justify-content: center; flex-wrap: wrap;"> <<link '<span class="bj-custom-btn" style="border-color:#ef4444; color:#ef4444;">FOLD</span>' "NpcPoker">><<run setup.pokerAct("fold")>><</link>> <<set _lblCall to ('<span class="bj-custom-btn">' + (_call gt 0 ? 'CALL ' + Math.min(_call, _s.stack.p) : 'CHECK') + '</span>')>> <<link _lblCall "NpcPoker">><<run setup.pokerAct("call")>><</link>> <<if setup.pkCanRaise(_s)>> <<set _minTo to setup.pkMinRaiseTo(_s)>> <<set _potTo to setup.pkPotRaiseTo(_s)>> <<if _minTo lt _max>> <<set _lblMin to ('<span class="bj-custom-btn">RAISE TO ' + _minTo + '</span>')>> <<link _lblMin "NpcPoker">><<run setup.pokerAct("min")>><</link>> <</if>> <<if _potTo gt _minTo and _potTo lt _max>> <<set _lblPot to ('<span class="bj-custom-btn">RAISE TO ' + _potTo + '</span>')>> <<link _lblPot "NpcPoker">><<run setup.pokerAct("pot")>><</link>> <</if>> <<set _lblAll to ('<span class="bj-custom-btn" style="border-color:#fde047; color:#fde047;">ALL-IN ' + _max + '</span>')>> <<link _lblAll "NpcPoker">><<run setup.pokerAct("allin")>><</link>> <</if>> </div> <<else>> <div style="font-size: 1.15rem; font-weight: bold; letter-spacing: 1px; margin-bottom: 12px; color: <<if _s.handResult.w eq 'p'>>#22c55e<<elseif _s.handResult.w eq 't'>>#fde047<<else>>#ef4444<</if>>;"><<print _s.handResult.text>></div> <<if _s.matchOver>> <div style="font-size: 1.3rem; font-weight: bold; letter-spacing: 2px; margin-bottom: 8px; color: <<if _s.result eq 'win'>>#22c55e<<elseif _s.result eq 'tie'>>#fde047<<else>>#ef4444<</if>>;"> <<if _s.result eq "win">>YOU WON THE MATCH!<<elseif _s.result eq "tie">>THE MATCH IS A DRAW.<<else>><<print _first.toUpperCase()>> WON THE MATCH.<</if>> </div> <div style="color: #cbd5e1; font-size: 0.85rem; margin-bottom: 6px;">Final chips: you <<print _s.stack.p>>, <<print _first>> <<print _s.stack.o>>.</div> <div style="color: #86efac; font-size: 0.85rem; margin-bottom: 14px;">[ Relationship with <<print _n.name>> +<<print _s.gain>> ]<<if _s.levelUp>><br><span style="color:#fde047; font-weight:bold;">★ LEVEL UP: <<print _s.levelUp>>!</span><</if>></div> <<link '<span class="bj-custom-btn">BACK</span>' "NpcInteract">><</link>> <<else>> <<link '<span class="bj-custom-btn">NEXT HAND</span>' "NpcPoker">><<run setup.pokerNext()>><</link>> <</if>> <</if>> </div> <</nobr>>
<<nobr>> <<if ndef $catchGame>><<goto "Rest">><</if>> <<set _n to setup.npcInfo($catchGame.id)>> <div class="rl-panel"> <div class="rl-title">[ CATCH THE BALL WITH <<print _n.name.toUpperCase()>> ]</div> <<if not $catchGame.over>> <div class="rl-nar">Round <<print $catchGame.round>> of 5. Catches so far: <<print $catchGame.score>>. Watch where <<print _n.name>> is leaning and throw it there. He is usually right about where he is going.</div> <<if $catchGame.last>><div class="rl-meta"><<print $catchGame.last>></div><</if>> <div class="rl-tag" style="font-size:1rem;"><<print _n.name>> is watching the <<print $catchGame.hint>> side.</div> <div style="display:flex; gap:10px; flex-wrap:wrap; margin-top:10px;"> <<for _d range ["LEFT","CENTER","RIGHT"]>> <<capture _d>> <div style="flex:1; min-width:120px;">@@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;display:block;padding:12px 16px;border-radius:4px;text-align:center;font-weight:bold;<<set _lbl to ("THROW " + _d)>><<link _lbl>><<run setup.catchThrow(_d)>><<goto "NpcCatch">><</link>>@@</div> <</capture>> <</for>> </div> <<else>> <div class="rl-meta"><<print $catchGame.last>></div> <div class="rl-tag" style="font-size:1rem;"> <<print _n.name>> caught <<print $catchGame.score>> of 5. <<if $catchGame.score gte 4>>What a game!<<elseif $catchGame.score gte 2>>Not bad at all.<<else>>Next time, read the signs.<</if>> [ Relationship +<<print $catchGame.gain>> ]<<if $catchGame.levelUp>><br><span style="color:#fde047; font-weight:bold;">★ LEVEL UP: <<print $catchGame.levelUp>>!</span><</if>> </div> <div style="text-align:center; margin-top:12px;"> @@color:#fde68a;background:rgba(180,83,9,0.22);border:1px solid #f59e0b;padding:12px 28px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "BACK" "NpcInteract">><</link>>@@ </div> <</if>> </div> <</nobr>>
<<nobr>> <<if not ($npcFlags and $npcFlags.cipher) and not (def $playerName and String($playerName).toLowerCase() eq "nakman")>><<goto "Camp">><</if>> <<set _n to setup.npcInfo("cipher")>> <<set _b to setup.npcBond("cipher")>> <<set _ready to setup.cipherSweepReady()>> <div class="srv-wrap"> <div class="srv-hero"> <img class="srv-bg" src="images/locations/cipher_server_room.jpg" alt="" onerror="this.style.display='none'"> <div class="srv-title">[ CIPHER'S SERVER ROOM ]</div> <div class="srv-sub">Salvaged racks hum in the dark. Cables crawl across the floor. One workstation glows at the center.</div> </div> <div class="srv-grid"> <div class="srv-portrait"> <img @src="_n.portrait" alt="Cipher" onerror="this.onerror=null;this.src='https://placehold.co/340x440/020617/22d3ee?text=CIPHER';"> <div style="color:#67e8f9; font-weight:bold; margin-top:8px; letter-spacing:2px;">CIPHER</div> <div style="color:#94a3b8; font-size:0.72rem;"><<print _n.role>></div> </div> <div class="srv-info"> <<if setup.ch3 and setup.ch3.onHold()>> <div class="srv-panel" style="margin-top:0;"> <div class="srv-panel-title">OMICRON BRIEFING ON HOLD</div> <div style="font-size:0.8rem; line-height:1.5;">You told Cipher you were not ready to go down to Substation Omicron. She is waiting for your decision.</div> <<link "<span class='srv-btn'>CHOOSE YOUR APPROACH TO OMICRON</span>">><<run setup.ch3.resumeBriefing()>><</link>> </div> <</if>> <div class="srv-panel" style="margin-top:0;"> <div class="srv-panel-title">TRUST: <<print setup.npcRankOf(_b)>> (LEVEL <<print _b.level>> / <<print setup.NPC_MAXLEVEL>>)</div> <div class="srv-bar"><div @style="'width:' + _b.xp + '%;'"></div></div> <div style="font-size:0.7rem; color:#94a3b8; margin-top:4px;"><<if _b.level gte setup.NPC_MAXLEVEL>>MAX LEVEL REACHED<<else>><<print _b.xp>> / 100 to <<print setup.npcNextRank(_b)>><</if>></div> </div> <div class="srv-panel"> <div class="srv-panel-title">📡 SIGNAL SWEEP (FREE, ONCE A DAY)</div> <div style="font-size:0.8rem; line-height:1.5;">Cipher listens to every relay in the sector and marks fresh leads on your Sector Map.</div> <div class="srv-tier">Now: <b><<print (1 + (_b.level gte 2 ? 1 : 0))>> lead<<if _b.level gte 2>>s<</if>> per sweep<<if _b.level gte 4>> + a boss contract<</if>></b></div> <div class="srv-tier"><<if _b.level lt 2>>Level 2 (Friend): 2 leads per sweep.<</if>><<if _b.level lt 4>> Level 4 (Close Friend): also finds a boss contract.<</if>></div> <<if _ready>> <<link "<span class='srv-btn'>📡 RUN SIGNAL SWEEP</span>" "CipherRoom">><<set $cipherMsg to setup.cipherSweep()>><</link>> <<else>> <span class="srv-btn-off">SWEEP DONE TODAY. COME BACK TOMORROW.</span> <</if>> </div> </div> </div> <<if $cipherMsg>> <div class="srv-msg"><<print $cipherMsg>></div> <<set $cipherMsg to "">> <</if>> <<if setup.zmoraActive()>> <<run setup.cipherVoice("zm")>> <div class="srv-panel" style="border-color:#a78bfa;"> <div class="srv-panel-title" style="color:#c4b5fd;">🧠 ZMORA PROTOCOL</div> <div style="font-size:0.8rem; line-height:1.5;">Cipher has the Living Quarters feed up on three screens. “That is not a sickness,” she says. “That is a weapon. Sit down, I will walk you through the terminal.”</div> <<link "<span class='srv-btn'>OPEN THE DIAGNOSTIC TERMINAL</span>" "ZmoraTerminal">><</link>> </div> <</if>> <div class="srv-panel"> <div class="srv-panel-title">SPEND TIME WITH CIPHER</div> <div style="font-size:0.8rem; line-height:1.5; margin-bottom:4px;">Talk, bring her gifts (she loves Tech Data), or play a few hands of poker between sweeps.</div> <<link "<span class='srv-btn'>💬 TALK, GIVE A GIFT OR PLAY POKER</span>" "NpcInteract">><<set $npcView to "cipher">><<set $npcBack to "CipherRoom">><</link>> </div> <div style="text-align:center; margin-top:18px;"> <<link "<span class='srv-btn' style='display:inline-block; padding:10px 28px; border-color:#64748b; color:#cbd5e1; background:transparent;'>BACK TO LOWER LEVELS</span>" "Sanctuary Depths">><</link>> </div> </div> <</nobr>>
<<nobr>> <<setMenu "Comms">> <<run setup.sitesPrune()>> <<set _b to setup.boardList()>> <<set _cnt to setup.siteCounts()>> <style> .bb-wrap { background: radial-gradient(ellipse at top, rgba(6, 78, 59, 0.45), rgba(3, 7, 18, 1) 70%); border: 2px solid #10b981; border-radius: 6px; padding: 18px; box-shadow: 0 0 22px rgba(16, 185, 129, 0.2); font-family: 'Courier New', monospace; color: #cbd5e1; max-width: 100%; box-sizing: border-box; } .bb-title { text-align: center; color: #34d399; font-size: 1.4rem; font-weight: bold; letter-spacing: 4px; text-shadow: 0 0 12px rgba(52, 211, 153, 0.6); } .bb-sub { text-align: center; color: #94a3b8; font-size: 0.78rem; margin: 6px 0 16px; line-height: 1.5; } .bb-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; margin: 20px 0 10px; padding-bottom: 6px; border-bottom: 1px dashed #334155; font-weight: bold; letter-spacing: 2px; font-size: 0.9rem; } .bb-head span:last-child { font-size: 0.75rem; color: #94a3b8; font-weight: normal; } .bb-grid { display: flex; flex-wrap: wrap; gap: 12px; } .bb-card { flex: 1 1 260px; max-width: 100%; box-sizing: border-box; background: rgba(8, 12, 20, 0.92); border: 1px solid var(--c); border-left: 4px solid var(--c); border-radius: 5px; padding: 12px; display: flex; flex-direction: column; gap: 6px; } .bb-card-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; } .bb-name { color: #f1f5f9; font-weight: bold; font-size: 0.92rem; } .bb-tag { font-size: 0.6rem; letter-spacing: 1px; color: var(--c); border: 1px solid var(--c); padding: 1px 6px; border-radius: 3px; white-space: nowrap; } .bb-desc { color: #94a3b8; font-size: 0.78rem; line-height: 1.5; flex: 1; } .bb-meter { height: 6px; background: #0f172a; border: 1px solid #334155; border-radius: 3px; overflow: hidden; } .bb-meter div { height: 100%; background: var(--c); } .bb-left { font-size: 0.72rem; color: var(--c); } .bb-drop { font-size: 0.7rem; color: #64748b; cursor: pointer; text-align: right; } .bb-drop a { color: #94a3b8 !important; text-decoration: underline dotted; } .bb-empty { color: #64748b; font-size: 0.8rem; text-align: center; border: 1px dashed #334155; padding: 18px 10px; border-radius: 4px; line-height: 1.6; } .bb-btn { display: inline-block; padding: 10px 24px; border: 1px solid #10b981; border-radius: 4px; background: rgba(16, 185, 129, 0.1); color: #6ee7b7; font-weight: bold; letter-spacing: 2px; cursor: pointer; } .bb-btn-grey { border-color: #64748b; color: #cbd5e1; background: transparent; } </style> <div class="bb-wrap"> <div class="bb-title">[ BOUNTY BOARD ]</div> <div class="bb-sub">Every lead you are hunting. Picked up at the Wasteland Bar, from Kaelen or Cipher, or decrypted from Tech Data. They are also marked on your Sector Map.</div> <div class="bb-head" style="color:#c084fc;"><span>🗣 RUMORS & LEADS</span><span><<print _cnt.rumors>> / <<print setup.SITES.cap>> on the board | they fade after <<print setup.SITES.expireDays>> days</span></div> <<if _b.rumors.length gt 0>> <div class="bb-grid"> <<for _r range _b.rumors>> <<capture _r>> <div class="bb-card" style="--c:#c084fc;"> <div class="bb-card-top"><div class="bb-name"><<print _r.icon>> <<print _r.name>></div><div class="bb-tag"><<print _r.via>></div></div> <div class="bb-desc"><<print _r.desc>></div> <<if _r.left neq null>> <div class="bb-meter"><div @style="'width:' + Math.round(_r.left / setup.SITES.expireDays * 100) + '%;'"></div></div> <div class="bb-left"><<print _r.left>> day<<if _r.left neq 1>>s<</if>> left</div> <<else>> <div class="bb-left">No time limit</div> <</if>> <div class="bb-drop"><<link "give up this lead" "BountyBoard">><<run setup.siteDrop(_r.id)>><</link>></div> </div> <</capture>> <</for>> </div> <<else>> <div class="bb-empty">No rumors on the board.<br>Buy a round at the Wasteland Bar, ask Kaelen, or decrypt a Tech Data here at the terminal.</div> <</if>> <div class="bb-head" style="color:#f87171;"><span>💀 BOSS CONTRACTS</span><span><<print _cnt.contracts>> / <<print setup.SITES.contractCap>> accepted | no time limit</span></div> <<if _b.contracts.length gt 0>> <div class="bb-grid"> <<for _k range _b.contracts>> <<capture _k>> <div class="bb-card" style="--c:#f87171;"> <div class="bb-card-top"><div class="bb-name"><<print _k.icon>> <<print _k.name>></div><div class="bb-tag"><<print _k.via>></div></div> <div class="bb-desc"><<print _k.desc>></div> <div class="bb-left">Deadly. Bring your best gear, and heal first.</div> <div class="bb-drop"><<link "abandon contract (no refund)" "BountyBoard">><<run setup.siteDrop(_k.id)>><</link>></div> </div> <</capture>> <</for>> </div> <<else>> <div class="bb-empty">No boss contracts accepted.<br>The Shady Informant at the Wasteland Bar sells them.</div> <</if>> <div style="text-align:center; margin-top:22px; display:flex; gap:12px; justify-content:center; flex-wrap:wrap;"> <<link "<span class='bb-btn'>🗺 OPEN SECTOR MAP</span>" "Map">><</link>> <<link "<span class='bb-btn bb-btn-grey'>BACK TO COMMS</span>" "Comms">><</link>> </div> </div> <</nobr>>
<<nobr>> <<setMenu "PvP Arena">> <<audio ":all" stop>> <<audio "combat_theme" volume 0.5 loop play>> <style> #story { margin-top: 0 !important; margin-right: 0 !important; margin-bottom: 0 !important; margin-left: 17.5em !important; padding: 0 !important; } #passages { max-width: 100% !important; padding: 0 !important; margin: 0 !important; border: none !important; } .passage { padding: 30px !important; margin: 0 !important; background: linear-gradient(rgba(10,10,15,0.9), rgba(5,5,10,0.97)) !important; min-height: 100vh !important; box-sizing: border-box !important; color: #e2e8f0 !important; font-family: 'Courier New', monospace !important; } .pvp-wrap { max-width: 900px; margin: 0 auto; } .pvp-head { text-align: center; border-bottom: 1px dashed #ef4444; padding-bottom: 14px; margin-bottom: 18px; } .pvp-head h2 { color: #f87171; margin: 0; letter-spacing: 4px; text-shadow: 0 0 12px rgba(239,68,68,0.5); } .pvp-sub { color: #cbd5e1; font-size: 0.85rem; margin-top: 8px; line-height: 1.5; } #pvpStatus { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 16px; } .pvp-stat { flex: 1 1 140px; background: rgba(0,0,0,0.6); border: 1px solid #334155; border-radius: 6px; padding: 10px; text-align: center; } .pvp-stat span { display: block; color: #94a3b8; font-size: 0.65rem; letter-spacing: 1px; margin-bottom: 4px; } .pvp-stat b { color: #f1f5f9; font-size: 1.05rem; } .pvp-tabs { display: flex; gap: 8px; margin-bottom: 12px; } .pvp-tab { flex: 1; background: rgba(0,0,0,0.6); border: 1px solid #334155; color: #cbd5e1; padding: 10px; border-radius: 4px; cursor: pointer; font-family: inherit; font-weight: bold; letter-spacing: 1px; } .pvp-tab.on { border-color: #f87171; color: #fecaca; background: rgba(127,29,29,0.35); } .pvp-row { display: flex; align-items: center; gap: 12px; background: rgba(0,0,0,0.6); border: 1px solid #334155; border-radius: 6px; padding: 10px 12px; margin-bottom: 8px; } .pvp-row.me { border-color: #38bdf8; } .pvp-who { flex: 1; min-width: 0; color: #f1f5f9; overflow-wrap: anywhere; } .pvp-rate { text-align: right; color: #f1f5f9; min-width: 70px; } .pvp-rank { color: #fde047; font-weight: bold; min-width: 36px; } .pvp-dim { color: #94a3b8; font-size: 0.75rem; } .pvp-tag { background: rgba(250,204,21,0.15); border: 1px solid #fde047; color: #fde047; font-size: 0.6rem; padding: 1px 5px; border-radius: 3px; } .pvp-btn { background: rgba(127,29,29,0.5); border: 2px solid #f87171; color: #fee2e2; padding: 8px 16px; border-radius: 4px; font-family: inherit; font-weight: bold; letter-spacing: 1px; cursor: pointer; white-space: nowrap; } .pvp-btn:hover { background: rgba(185,28,28,0.7); color: #fff; } .pvp-btn.off { border-color: #475569; color: #94a3b8; background: rgba(15,23,42,0.8); cursor: default; } .pvp-note { color: #cbd5e1; text-align: center; padding: 18px; border: 1px dashed #334155; border-radius: 6px; } #pvpMsg { display: none; border: 1px solid; border-radius: 6px; padding: 10px; margin-bottom: 12px; text-align: center; background: rgba(0,0,0,0.6); } #pvpResult { display: none; background: rgba(0,0,0,0.65); border: 1px solid #7f1d1d; border-radius: 8px; padding: 16px; margin-bottom: 18px; } .pvp-vs { display: flex; align-items: stretch; gap: 10px; margin-bottom: 12px; } .pvp-fc { flex: 1; min-width: 0; background: rgba(15,23,42,0.8); border: 1px solid #334155; border-radius: 6px; padding: 10px; color: #f1f5f9; overflow-wrap: anywhere; } .pvp-fc-t { color: #94a3b8; font-size: 0.65rem; letter-spacing: 2px; } .pvp-vs-x { align-self: center; color: #f87171; font-weight: bold; } .pvp-bar { height: 10px; background: #1e293b; border-radius: 5px; overflow: hidden; margin: 8px 0 4px 0; } .pvp-bar div { height: 100%; background: #34d399; } .pvp-banner { text-align: center; border: 2px solid; border-radius: 6px; padding: 10px; font-size: 1.3rem; font-weight: bold; letter-spacing: 3px; margin-bottom: 8px; } .pvp-log { height: 220px; overflow-y: auto; background: rgba(0,0,0,0.7); border: 1px solid #334155; border-radius: 6px; padding: 10px; font-size: 0.8rem; line-height: 1.5; color: #e2e8f0; } .pvp-rd { color: #fde047; margin-top: 6px; font-weight: bold; } </style> <div class="pvp-wrap"> <div class="pvp-head"> <h2>[ PVP ARENA ]</h2> <div class="pvp-sub">Ghost duels. Pick another survivor and fight their saved character. They do not need to be online, and nobody can die or lose gear here. Both fighters are rebuilt by the server from their level, equipment and talents, then fight on their own.</div> </div> <div style="border:2px solid #f87171; border-radius:8px; background:rgba(69,10,10,0.55); padding:14px; margin-bottom:16px; text-align:center;"> <div style="color:#fecaca; font-weight:bold; letter-spacing:3px;">LIVE BATTLES</div> <div class="pvp-sub" style="margin:6px 0 10px 0;">Fight another survivor for real, turn by turn. Pick a move every round, put Scrip on the line, or fight for rating only.</div> <button type="button" class="pvp-btn" onclick="window.setup.bt.open()">⚔ ENTER A LIVE BATTLE</button> </div> <div style="border:2px solid #22c55e; border-radius:8px; background:rgba(2,21,10,0.75); padding:14px; margin-bottom:16px; text-align:center;"> <div style="color:#86efac; font-weight:bold; letter-spacing:3px;">NETRUNNER DUELS</div> <div class="pvp-sub" style="margin:6px 0 10px 0;">A live hacking race against another survivor, or a raid on the ghost of one who is offline. Same puzzles, best of three. The winner steals Scrip from the loser.</div> <button type="button" class="pvp-btn" onclick="window.setup.hd.open()">💻 ENTER A HACK DUEL</button> </div> <div id="pvpStatus"></div> <div id="pvpMsg"></div> <div id="pvpResult"></div> <div class="pvp-tabs"> <button id="pvpTab_opp" class="pvp-tab on" onclick="window.setup.pvp.tab('opp')">OPPONENTS</button> <button id="pvpTab_board" class="pvp-tab" onclick="window.setup.pvp.tab('board')">LADDER</button> </div> <div id="pvpPane_opp"><div id="pvpList"></div></div> <div id="pvpPane_board" style="display:none;"><div id="pvpBoard"></div></div> <div class="pvp-sub" style="margin-top:14px; text-align:center;">Wins pay Scrip and raise your rating. Up to 10 duels a day, and the same fighter has a 1 hour cooldown. Wear your best gear before you enter.</div> <div style="text-align:center; margin-top:18px;"> <<link '<span class="pvp-btn" style="display:inline-block;">RETURN TO CAMP</span>' "Camp">><</link>> </div> </div> <<script>>setTimeout(function () { if (window.setup && window.setup.pvp) { window.setup.pvp.init(); } }, 60);<</script>> <</nobr>>
<<nobr>> <<if ndef $passOutReport>><<goto "Camp">><</if>> <<run window.setup.updateSidebar()>> <style> .po-world { position: relative; margin-left: 17.5em; margin-right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; justify-content: center; padding: 20px; } .po-box { background: linear-gradient(135deg, #05070d 0%, #1a0a0a 100%); border: 2px solid #7f1d1d; border-radius: 8px; padding: 30px; font-family: 'Courier New', monospace; box-shadow: 0 0 30px rgba(127,29,29,0.5); color: #cbd5e1; } .po-title { text-align: center; color: #fca5a5; font-size: 1.6rem; font-weight: bold; letter-spacing: 4px; border-bottom: 1px dashed #7f1d1d; padding-bottom: 14px; margin-bottom: 20px; } .po-lost { background: rgba(0,0,0,0.55); border: 1px solid #7f1d1d; border-radius: 6px; padding: 14px 18px; margin: 18px 0; color: #fecaca; } </style> <div class="po-world"> <div class="po-box"> <div class="po-title">[ YOU COLLAPSED OUTSIDE ]</div> <div style="line-height: 1.8; margin-bottom: 8px;"> The dead of night swallowed the sector, and you kept going. Your legs gave out first, then the rest of you. You never felt the ground.<br><br> Survivors from the base found you face-down in the ash at first light and dragged you home. Someone patched you up. Someone else went through your pack. </div> <div class="po-lost"> <div style="font-weight: bold; margin-bottom: 8px;">>> INJURED: you are back at camp with <<print $passOutReport.hp>> / <<print $passOutReport.maxHp>> HP.</div> <<if $passOutReport.lost.length gt 0>> <div style="margin-bottom: 4px;">>> LOST FROM YOUR PACK:</div> <<for _l to 0; _l lt $passOutReport.lost.length; _l++>> <div style="color: #fca5a5;"> - <<print $passOutReport.lost[_l]>></div> <</for>> <<else>> <div>>> You were carrying nothing worth taking.</div> <</if>> </div> <div style="color: #94a3b8; font-size: 0.85rem; margin-bottom: 20px;">Tip: when the sidebar says DEAD NIGHT, go back to camp and sleep. Dawn brings a new day and new weather.</div> <div style="text-align: center;"> @@color:#fecaca;background:rgba(185,28,28,0.22);border:1px solid #ef4444;padding:12px 28px;font-weight:bold;letter-spacing:2px;border-radius:4px;display:inline-block;<<link "WAKE UP IN CAMP" "Camp">><<unset $passOutReport>><<run window.setup.updateSidebar()>><</link>>@@ </div> </div> </div> <</nobr>>
<<nobr>> <<if not setup.zmoraActive()>><<goto "Camp">><</if>> <<set _z to $zmora>> <<set _zc to setup.zmoraCounts()>> <<set _pts to setup.netPts()>> <<set _cip to ($npcFlags and $npcFlags.cipher)>> <<set _used to ((_z.triedDay eq $day) ? _z.tries : 0)>> <<set _left to setup.ZMORA.tryPerDay - _used>> <<set _canSelf to (_pts gte setup.ZMORA.needPts)>> <<set _lblSelf to "<span class='srv-btn'>💻 RUN THE DIAGNOSTIC YOURSELF (live breach, a failure is a Neural Shock)</span>">> <<set _lblCip to "<span class='srv-btn'>🤝 WORK THE TERMINAL WITH CIPHER (live breach, +3.0s assist)</span>">> <<set _lblHak to "<span class='srv-btn'>🤝 WORK THE TERMINAL WITH YOUR SECTOR HACKER (live breach, +3.0s assist)</span>">> <div class="srv-wrap zm-wrap"> <div class="srv-hero"> <div class="srv-title">[ ZMORA PROTOCOL: CAMP SICKNESS ]</div> <div class="srv-sub">Living Quarters sub-network. Isolated diagnostic terminal.</div> </div> <div class="srv-panel" style="margin-top:0;"> <div class="srv-panel-title">ALERT</div> <div style="font-size:0.85rem; line-height:1.6;">Several defenders in the Living Quarters have woken up screaming, unable to breathe. In the old stories the Zmora is a night spirit that sits on a sleeper's chest. In the Ashen Veil it is a rogue pre-war cyber-warfare signal from a buried mesh hub, and it has crawled into the camp's sub-networks.</div> </div> <div class="srv-panel"> <div class="srv-panel-title">DAMAGE REPORT (DAY <<print ($day - _z.start + 1)>> OF THE OUTBREAK)</div> <div class="srv-bar"><div @style="'width:' + (_z.sev * 25) + '%; background:#a78bfa; box-shadow:0 0 8px #a78bfa;'"></div></div> <div class="srv-tier">Severity: <b><<print _z.sev>> / 4</b><<if _z.diagnosed>> (quarantined, no longer spreading)<<else>> (grows every <<print setup.ZMORA.escalateDays>> days until diagnosed)<</if>></div> <div class="srv-tier">EXHAUSTED: <b><<print _zc.workers>> of <<print _zc.totalW>> workers</b> cannot work properly. Camp output <b>-<<print _zc.pct>>%</b>.</div> <div class="srv-tier">NEURAL SHOCK: <b><<print _zc.defenders>> of <<print _zc.totalD>> wall defenders</b> are disabled. Raid defense is weaker by the same amount.</div> <div class="srv-tier">Your Neural Shield: <b><<print setup.shieldName()>></b></div> </div> <<if $zmoraMsg>> <div class="srv-msg"><<print $zmoraMsg>></div> <<set $zmoraMsg to "">> <</if>> <<if not _z.diagnosed>> <div class="srv-panel"> <div class="srv-panel-title">STEP 1: DIAGNOSE THE TERMINAL</div> <div style="font-size:0.8rem; line-height:1.5;">Someone has to read the signal and find where it comes from. A failed attempt gives you a Neural Shock. Attempts left today: <b><<print _left>></b>.</div> <<if _left gt 0>> <<if _canSelf>> <<link _lblSelf>><<if setup.zmoraBegin("self")>><<goto "HackGame">><<else>><<goto "ZmoraTerminal">><</if>><</link>> <<else>> <span class="srv-btn-off">LOCKED: a skilled Netrunner is needed (<<print setup.ZMORA.needPts>>+ points in the Netrunner talent tree, you have <<print _pts>>)</span> <</if>> <<if _cip>> <<link _lblCip>><<if setup.zmoraBegin("cipher")>><<goto "HackGame">><<else>><<goto "ZmoraTerminal">><</if>><</link>> <</if>> <<if setup.squadHas("Sector Hacker")>> <<link _lblHak>><<if setup.zmoraBegin("hacker")>><<goto "HackGame">><<else>><<goto "ZmoraTerminal">><</if>><</link>> <<elseif not _cip>> <span class="srv-btn-off">LOCKED: needs Cipher or a hired Sector Hacker (Wasteland Bar)</span> <</if>> <<else>> <span class="srv-btn-off">TERMINAL LOCKED OUT. TRY AGAIN TOMORROW.</span> <</if>> </div> <<else>> <div class="srv-panel"> <div class="srv-panel-title">STEP 2: DESTROY THE SOURCE</div> <div style="font-size:0.8rem; line-height:1.5;">The broadcast comes from a buried mesh hub out in the sector. Destroy it and the camp wakes up. You can sever it with a deck if you are a skilled Netrunner (a hired Sector Hacker or Cipher can do it for you), or smash through whatever guards it.</div> <<if setup.zmoraHubOnMap()>> <div class="srv-tier"><b>BURIED MESH HUB</b> is marked on your Sector Map.</div> <<else>> <<link "<span class='srv-btn'>📡 TRACE THE SIGNAL AGAIN (MARK THE HUB ON THE MAP)</span>" "ZmoraTerminal">><<run setup.zmoraAddHub()>><</link>> <</if>> </div> <</if>> <<if _z.sev gt 1>> <div class="srv-panel"> <div class="srv-panel-title">TRIAGE</div> <div style="font-size:0.8rem; line-height:1.5;">Sedate the worst cases to ease the symptoms. It does not cure anything. Cost: <b><<print setup.ZMORA.medCost>> Medicine</b> (you have <<print setup.have("medicine")>>).</div> <<if setup.have("medicine") gte setup.ZMORA.medCost>> <<link "<span class='srv-btn'>💊 SEDATE THE SLEEPERS (SEVERITY -1)</span>" "ZmoraTerminal">><<run setup.zmoraTriage()>><</link>> <<else>> <span class="srv-btn-off">NOT ENOUGH MEDICINE</span> <</if>> </div> <</if>> <div style="text-align:center; margin-top:18px;"> <<link "<span class='srv-btn' style='display:inline-block; padding:10px 28px; border-color:#64748b; color:#cbd5e1; background:transparent;'>BACK TO S.A.N.C.T.U.A.R.Y. BASE</span>" "Camp">><</link>> </div> </div> <</nobr>>
<<nobr>> <<run setup.ch3.enter()>> <<audio ":all" stop>> <style> #story { margin-top: 0 !important; margin-right: 0 !important; margin-bottom: 0 !important; margin-left: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; } .av-world { position: relative; margin-left: 17.5em; margin-right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 20px; } </style> <div class="av-world"><div class="kt-wrap" id="c3-root"></div></div> <<script>>$(document).one(":passagedisplay", function () { setup.ch3.mount(); });<</script>> <</nobr>>
<<nobr>> <<if ndef $hack>><<goto "Camp">><</if>> <<setMenu "Combat">> <style> #story { margin-top: 0 !important; margin-right: 0 !important; margin-bottom: 0 !important; margin-left: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; } .av-world { position: relative; margin-left: 17.5em; margin-right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 20px; } </style> <div class="av-world"><div class="hk-new"><div class="hk-frame" id="hk-root"></div></div></div> <<script>>$(document).one(":passagedisplay", function () { setup.hackMount(); });<</script>> <</nobr>>
<<nobr>> <<setMenu "Comms">> <<if ndef $hackStats>><<set $hackStats to { w: 0, l: 0 }>><</if>> <style> #story { margin-top: 0 !important; margin-right: 0 !important; margin-bottom: 0 !important; margin-left: 0 !important; padding: 0 !important; max-width: none !important; width: 100% !important; } #passages, .passage { max-width: none !important; width: 100% !important; margin: 0 !important; padding: 0 !important; } .av-world { position: relative; margin-left: 17.5em; margin-right: 0; min-height: 100vh; box-sizing: border-box; display: flex; flex-direction: column; padding: 20px; width: calc(100% - 17.5em); } </style> <div class="av-world"> <div class="hkt-wrap"> <h2 class="hkt-title">[ HACKING TRAINING SIMULATOR ]</h2> <div class="hkt-sub">COMM-SERVER SANDBOX. NO PENALTY. NO REWARD. TAKE ALL THE TIME YOU WANT.</div> <div class="hkt-panel"> <div class="hkt-ph">>> WHERE YOU HACK<<if $hackTutDone>> <span class="hkt-tag">TUTORIAL DONE</span><</if>></div> <p class="hkt-p">Every terminal in the Ashen Veil (the convoy vault, the Comms archives, the Zmora diagnostic, the Mesh Hub, the Reptilian log servers) now runs one of three live breaches. They are timed, and a failure costs you. This sandbox is free: double time, no loss, no reward.</p> <p class="hkt-p"><b>Perks that add seconds:</b> the Techie and the Broadcaster get a permanent time buffer. A Neural Shield, points in the Netrunner talent tree and a hacker at your side (Cipher or a Sector Hacker) add more.</p> <<if $hackStats.w + $hackStats.l gt 0>><p class="hkt-p" style="color:#67e8f9;">Live breaches so far: <b><<print $hackStats.w>> won</b> / <b><<print $hackStats.l>> lost</b>.</p><</if>> </div> <div class="hkt-panel"> <div class="hkt-ph">>> TUTORIAL (3 SHORT LESSONS)</div> <p class="hkt-p">One lesson per mini-game: Terminal Override, Circuit Bypass and Signal Decryption. Each starts with a full explanation. Nothing is lost if you fail, and you can retry any lesson.</p> <<link "<span class='hkt-btn'>▶ START THE TUTORIAL</span>" "HackGame">><<run setup.hackBegin({ctx:"practice", tutorial:true, ret:"HackTraining"})>><</link>> </div> <div class="hkt-panel"> <div class="hkt-ph">>> FREE PLAY</div> <p class="hkt-p">Pick a game and a difficulty. Tier 1 is the easy terminal, tier 3 is the hardest one you will meet in the wasteland.</p> <div class="hkt-grid"> <div class="hkt-gn">TERMINAL OVERRIDE<br><span style="color:#94a3b8;font-weight:normal;font-size:0.75rem;">Type the code</span></div> <<link "<span class='hkt-btn'>TIER 1</span>" "HackGame">><<run setup.hackBegin({ctx:"practice", game:"term", tier:1, practice:true, ret:"HackTraining"})>><</link>> <<link "<span class='hkt-btn'>TIER 2</span>" "HackGame">><<run setup.hackBegin({ctx:"practice", game:"term", tier:2, practice:true, ret:"HackTraining"})>><</link>> <<link "<span class='hkt-btn'>TIER 3</span>" "HackGame">><<run setup.hackBegin({ctx:"practice", game:"term", tier:3, practice:true, ret:"HackTraining"})>><</link>> <div class="hkt-gn">CIRCUIT BYPASS<br><span style="color:#94a3b8;font-weight:normal;font-size:0.75rem;">Wire puzzle</span></div> <<link "<span class='hkt-btn'>TIER 1</span>" "HackGame">><<run setup.hackBegin({ctx:"practice", game:"circuit", tier:1, practice:true, ret:"HackTraining"})>><</link>> <<link "<span class='hkt-btn'>TIER 2</span>" "HackGame">><<run setup.hackBegin({ctx:"practice", game:"circuit", tier:2, practice:true, ret:"HackTraining"})>><</link>> <<link "<span class='hkt-btn'>TIER 3</span>" "HackGame">><<run setup.hackBegin({ctx:"practice", game:"circuit", tier:3, practice:true, ret:"HackTraining"})>><</link>> <div class="hkt-gn">SIGNAL DECRYPTION<br><span style="color:#94a3b8;font-weight:normal;font-size:0.75rem;">Spot the symbols</span></div> <<link "<span class='hkt-btn'>TIER 1</span>" "HackGame">><<run setup.hackBegin({ctx:"practice", game:"signal", tier:1, practice:true, ret:"HackTraining"})>><</link>> <<link "<span class='hkt-btn'>TIER 2</span>" "HackGame">><<run setup.hackBegin({ctx:"practice", game:"signal", tier:2, practice:true, ret:"HackTraining"})>><</link>> <<link "<span class='hkt-btn'>TIER 3</span>" "HackGame">><<run setup.hackBegin({ctx:"practice", game:"signal", tier:3, practice:true, ret:"HackTraining"})>><</link>> </div> <div style="margin-top:10px;"> <<link "<span class='hkt-btn'>🎲 SURPRISE ME (RANDOM GAME, TIER 2)</span>" "HackGame">><<run setup.hackBegin({ctx:"practice", game:"rand", tier:2, practice:true, ret:"HackTraining"})>><</link>> </div> </div> <div style="text-align:center; margin-top:14px;"> <<link "<span class='hkt-btn hkt-back'>BACK TO COMM-SERVER</span>" "Comms">><</link>> </div> </div> </div> <</nobr>>