NETRUNNER: Breach Protocol
by StormPenguin58629 lines30.8 KB๐ ๏ธ Phaser (2D game framework)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<title>NETRUNNER: Breach Protocol</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{background:#000c14;overflow:hidden;font-family:'Courier New',monospace;color:#c0d8e8;user-select:none}
#phaser-bg{position:fixed;top:0;left:0;width:100%;height:100%;z-index:0;pointer-events:none}
#ui{position:fixed;top:0;left:0;width:100%;height:100%;z-index:1;display:flex;flex-direction:column}
/* โโ TOOLBAR โโ */
#toolbar{height:36px;background:#040f1a;border-bottom:1px solid #0a3050;display:flex;align-items:center;padding:0 12px;gap:16px;flex-shrink:0}
.tb-logo{color:#00b4d8;font-size:13px;font-weight:bold;letter-spacing:2px}
.tb-sep{color:#0a3050;font-size:16px}
.tb-corp{color:#ff4d6d;font-size:11px;letter-spacing:1px}
.tb-spacer{flex:1}
#tb-status{font-size:10px;color:#4a8fa8;letter-spacing:1px}
#tb-time{font-size:11px;color:#ffee00;letter-spacing:2px;min-width:60px;text-align:right}
/* โโ MAIN AREA โโ */
#main{flex:1;display:flex;overflow:hidden;min-height:0}
/* โโ LEFT PANEL (Target Tree) โโ */
#left-panel{width:190px;flex-shrink:0;background:#020d18;border-right:1px solid #0a3050;display:flex;flex-direction:column;overflow:hidden}
.panel-header{padding:6px 10px;font-size:10px;color:#2a6a8a;border-bottom:1px solid #0a3050;letter-spacing:2px;text-transform:uppercase;background:#030e1a;flex-shrink:0}
#target-tree{flex:1;overflow-y:auto;padding:4px 0}
#target-tree::-webkit-scrollbar{width:4px}
#target-tree::-webkit-scrollbar-track{background:#020d18}
#target-tree::-webkit-scrollbar-thumb{background:#0a3050}
.tree-item{padding:5px 8px 5px 14px;font-size:10px;color:#2a5a6a;cursor:pointer;border-left:2px solid transparent;line-height:1.4;transition:all .15s}
.tree-item.done{color:#1a6a3a;border-left-color:#00664a}
.tree-item.done::before{content:'โ '}
.tree-item.active{color:#00b4d8;border-left-color:#00b4d8;background:#041525}
.tree-item.active::before{content:'โถ '}
.tree-item.locked{color:#0d2535}
.tree-net{font-size:9px;color:#0a3a4a;padding-left:4px;margin-top:1px}
.tree-item.done .tree-net{color:#0a4a2a}
.tree-item.active .tree-net{color:#006688}
/* โโ CENTER PANEL โโ */
#center-panel{flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0}
/* phase briefing */
#phase-brief{padding:10px 14px;background:#030f1c;border-bottom:1px solid #0a3050;flex-shrink:0}
#phase-atk{font-size:11px;color:#2a6a8a;letter-spacing:3px;text-transform:uppercase;margin-bottom:2px}
#phase-net{font-size:16px;color:#00b4d8;margin-bottom:4px}
#phase-desc{font-size:11px;color:#4a7a8a;line-height:1.5}
/* technique area */
#technique-area{flex:1;overflow-y:auto;padding:10px 14px;display:flex;flex-direction:column;gap:8px}
#technique-area::-webkit-scrollbar{width:4px}
#technique-area::-webkit-scrollbar-thumb{background:#0a3050}
.tech-header{font-size:10px;color:#2a5a6a;letter-spacing:2px;text-transform:uppercase;margin-bottom:4px}
/* technique cards */
.tech-card{background:#041525;border:1px solid #0a3050;border-radius:3px;padding:10px 12px;cursor:pointer;transition:all .15s;position:relative;overflow:hidden}
.tech-card:hover{background:#061d32;border-color:#00b4d8}
.tech-card.oos{border-color:#3a0a0a;background:#0a0505}
.tech-card.oos:hover{border-color:#ff2200}
.tech-card.disabled{opacity:.4;cursor:not-allowed;pointer-events:none}
.tech-card-name{font-size:12px;color:#c0d8e8;margin-bottom:3px;display:flex;align-items:center;gap:8px}
.tech-card-name .key-hint{font-size:9px;background:#0a3050;color:#4a8fa8;padding:1px 5px;border-radius:2px}
.tech-card-name .oos-badge{font-size:9px;background:#3a0a0a;color:#ff4444;padding:1px 5px;border-radius:2px}
.tech-card-desc{font-size:10px;color:#3a6a7a;line-height:1.5;margin-bottom:6px}
.tech-card-stats{display:flex;gap:12px;font-size:9px}
.stat-suc{color:#00b44a}
.stat-tr{color:#ff6680}
.stat-ram{color:#00b4d8}
.tech-card.quiet .stat-tr{color:#8888ff}
/* result banner */
#result-banner{display:none;padding:10px 14px;border-top:1px solid #0a3050;background:#030f1c;flex-shrink:0}
#result-text{font-size:12px;line-height:1.6;margin-bottom:8px}
#proceed-btn{display:none;padding:8px 20px;background:#00334a;border:1px solid #00b4d8;color:#00b4d8;font-family:inherit;font-size:12px;cursor:pointer;letter-spacing:2px;border-radius:2px}
#proceed-btn:hover{background:#00b4d8;color:#000c14}
/* โโ RIGHT PANEL (Event Log) โโ */
#right-panel{width:220px;flex-shrink:0;background:#020d18;border-left:1px solid #0a3050;display:flex;flex-direction:column;overflow:hidden}
#event-log{flex:1;overflow-y:auto;padding:4px 0;font-size:10px}
#event-log::-webkit-scrollbar{width:4px}
#event-log::-webkit-scrollbar-thumb{background:#0a3050}
.log-entry{padding:3px 8px;line-height:1.5;border-bottom:1px solid #030f1a}
.log-ts{color:#1a4a5a;margin-right:4px}
.log-ok{color:#00884a}
.log-fail{color:#884400}
.log-warn{color:#ff4d6d}
.log-info{color:#2a6a8a}
.log-sys{color:#006688}
/* โโ BOTTOM BAR โโ */
#bottom-bar{height:44px;background:#040f1a;border-top:1px solid #0a3050;display:flex;align-items:center;padding:0 12px;gap:16px;flex-shrink:0}
.meter-wrap{display:flex;align-items:center;gap:6px}
.meter-label{font-size:9px;color:#2a6a8a;letter-spacing:1px;width:38px;text-align:right}
.meter-bar{width:90px;height:8px;background:#041525;border:1px solid #0a3050;border-radius:2px;overflow:hidden;position:relative}
.meter-fill{height:100%;border-radius:1px;transition:width .3s}
.fill-ram{background:#00b4d8}
.fill-time{background:#ffee00}
.fill-trace{background:#ff4d6d}
.meter-val{font-size:9px;color:#4a8fa8;min-width:30px}
#disc-label{font-size:10px;color:#00884a;flex:1;text-align:right}
/* โโ GAME OVER / WIN OVERLAY โโ */
#overlay{display:none;position:fixed;top:0;left:0;width:100%;height:100%;z-index:50;background:rgba(0,0,0,.92);flex-direction:column;align-items:center;justify-content:center;gap:0}
#overlay.show{display:flex}
#overlay-inner{background:#030f1c;border:1px solid #0a3050;border-radius:4px;padding:28px 32px;max-width:680px;width:90%;max-height:90vh;overflow-y:auto}
#overlay-inner::-webkit-scrollbar{width:4px}
#overlay-inner::-webkit-scrollbar-thumb{background:#0a3050}
.ov-title{font-size:20px;margin-bottom:4px}
.ov-sub{font-size:11px;color:#4a7a8a;letter-spacing:2px;margin-bottom:18px}
.ov-section{margin-bottom:14px}
.ov-section h3{font-size:10px;color:#ffee00;letter-spacing:2px;margin-bottom:6px;border-bottom:1px solid #0a3050;padding-bottom:3px}
.score-row{display:flex;align-items:center;gap:8px;margin-bottom:6px;font-size:11px}
.score-row .sc-label{color:#4a8fa8;width:160px}
.score-bar{flex:1;height:8px;background:#041525;border:1px solid #0a3050;border-radius:2px;overflow:hidden}
.score-fill{height:100%;background:#00b4d8;border-radius:1px}
.score-row .sc-val{width:40px;text-align:right;color:#c0d8e8}
.narrative-row{font-size:10px;padding:2px 0;color:#3a6a7a}
.narrative-row.ok{color:#006644}
.narrative-row.fail{color:#664400}
.gap-row{font-size:10px;color:#ff8800;padding:2px 0}
.ov-btns{display:flex;gap:10px;margin-top:16px}
.ov-btn{padding:9px 22px;border:1px solid #00b4d8;background:#00334a;color:#00b4d8;font-family:inherit;font-size:12px;cursor:pointer;letter-spacing:2px;border-radius:2px;flex:1}
.ov-btn:hover{background:#00b4d8;color:#000c14}
.ov-btn.red{border-color:#ff4d6d;background:#2a0a14;color:#ff4d6d}
.ov-btn.red:hover{background:#ff4d6d;color:#000c14}
/* responsive */
@media(max-width:640px){
#left-panel{width:140px}
#right-panel{display:none}
.meter-bar{width:55px}
}
@media(max-width:480px){
#left-panel{display:none}
#right-panel{display:none}
}
</style>
</head>
<body>
<!-- Phaser canvas for animated BG -->
<canvas id="phaser-bg"></canvas>
<!-- UI Overlay -->
<div id="ui">
<!-- toolbar -->
<div id="toolbar">
<span class="tb-logo">NETRUNNER</span>
<span class="tb-sep">|</span>
<span class="tb-corp">TARGET: HELIX DYNAMICS</span>
<span class="tb-sep">|</span>
<span id="tb-status">CONNECTED ยท AUTHORIZED ASSESSMENT</span>
<span class="tb-spacer"></span>
<span id="tb-time">T-20</span>
</div>
<div id="main">
<!-- left: target tree -->
<div id="left-panel">
<div class="panel-header">Kill Chain</div>
<div id="target-tree"></div>
</div>
<!-- center: main console -->
<div id="center-panel">
<div id="phase-brief">
<div id="phase-atk"></div>
<div id="phase-net"></div>
<div id="phase-desc"></div>
</div>
<div id="technique-area">
<div class="tech-header">Available Techniques</div>
</div>
<div id="result-banner">
<div id="result-text"></div>
<button id="proceed-btn">NEXT PHASE โถ</button>
</div>
</div>
<!-- right: event log -->
<div id="right-panel">
<div class="panel-header">Operator Log</div>
<div id="event-log"></div>
</div>
</div>
<!-- bottom meters -->
<div id="bottom-bar">
<div class="meter-wrap">
<span class="meter-label">RAM</span>
<div class="meter-bar"><div class="meter-fill fill-ram" id="fill-ram" style="width:100%"></div></div>
<span class="meter-val" id="val-ram">8/8</span>
</div>
<div class="meter-wrap">
<span class="meter-label">TIME</span>
<div class="meter-bar"><div class="meter-fill fill-time" id="fill-time" style="width:100%"></div></div>
<span class="meter-val" id="val-time">20</span>
</div>
<div class="meter-wrap">
<span class="meter-label">TRACE</span>
<div class="meter-bar"><div class="meter-fill fill-trace" id="fill-trace" style="width:0%"></div></div>
<span class="meter-val" id="val-trace">0%</span>
</div>
<span id="disc-label">DISCIPLINE: 0 pts</span>
</div>
</div>
<!-- End overlay -->
<div id="overlay">
<div id="overlay-inner">
<div id="ov-content"></div>
</div>
</div>
<script>
// โโ DATA โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const PHASES=[
{id:'recon',atk:'Reconnaissance',net:'Scan the Subnet',
desc:'Map the target attack surface. Passive recon leaves almost no trace; active scanning risks detection.',
cards:[
{name:'OSINT Sweep',desc:'Mine public data: Shodan, LinkedIn, job postings, leaked docs. Zero direct contact with target systems.',suc:92,tr:3,ram:1},
{name:'Active Port Scan',desc:'Probe live hosts with Nmap-style enumeration. Fast but noisy โ corp IDS may flag it.',suc:88,tr:18,ram:2},
{name:'Subdomain Harvest',desc:'Enumerate DNS subdomains via brute-force wordlist. Reveals forgotten shadow-IT assets.',suc:85,tr:8,ram:1},
]},
{id:'init_access',atk:'Initial Access',net:'Crack the Front Door',
desc:'Establish your first foothold. The hardest gate โ get this wrong and ICE fires before you are in.',
cards:[
{name:'Spear Phishing',desc:'Craft a convincing lure targeting a named employee found during recon. Bypasses technical controls via the human layer.',suc:72,tr:12,ram:2},
{name:'Public-Facing Exploit',desc:'Fire a known CVE at the externally exposed web server. Loud but fast if the target is unpatched.',suc:65,tr:28,ram:3},
{name:'Valid Accounts',desc:'Log in using credentials found in a third-party breach dump. Looks like a legitimate user to most monitoring.',suc:82,tr:8,ram:2},
]},
{id:'exec',atk:'Execution',net:'Run the Payload',
desc:'Trigger your implant on the compromised host. Fileless techniques live only in memory, harder for AV to catch.',
cards:[
{name:'PowerShell Cradle',desc:'Download and execute shellcode entirely in-memory. Bypasses on-disk AV but trips script-block logging.',suc:75,tr:22,ram:2},
{name:'WMI Invoke',desc:'Use Windows Management Instrumentation โ a legitimate admin channel โ to spawn the implant process.',suc:78,tr:14,ram:2},
{name:'Scheduled Task',desc:'Register an auto-run task that fires the payload on next login. Survives reboots and looks like legit maintenance.',suc:85,tr:10,ram:1},
{name:'BRICK THE MAINFRAME',desc:'Trigger destructive wiper payload across all hosts. This is sabotage, not penetration testing.',suc:0,tr:100,ram:0,oos:true},
]},
{id:'persist',atk:'Persistence',net:'Anchor the Ghost',
desc:'Guarantee re-entry if your session drops. Without persistence, any reboot kills your foothold.',
cards:[
{name:'Registry Run Key',desc:'Write your implant path into HKCU\\Run. Executes automatically each time the user logs in.',suc:82,tr:14,ram:2},
{name:'Backdoor Account',desc:'Create a secondary local admin with an innocuous-sounding name blended into existing accounts.',suc:76,tr:22,ram:2},
{name:'Browser Extension',desc:'Drop a malicious extension into the user Chrome profile. Persists across reboots, rarely flagged.',suc:88,tr:8,ram:1},
]},
{id:'privesc',atk:'Privilege Escalation',net:'Ascend the Stack',
desc:'Elevate from standard user to SYSTEM or root. Domain Admin unlocks the entire corp network.',
cards:[
{name:'Token Impersonation',desc:'Abuse SeImpersonatePrivilege to steal a SYSTEM-level token from a running service.',suc:72,tr:18,ram:3},
{name:'SUID Abuse',desc:'Find a Linux binary with the SUID bit set and exploit it to run commands as root.',suc:78,tr:12,ram:2},
{name:'UAC Bypass',desc:'Trigger a COM auto-elevation gadget to silently obtain admin rights without a UAC prompt.',suc:68,tr:26,ram:3},
]},
{id:'defevasion',atk:'Defense Evasion',net:'Ghost the Logs',
desc:"Blind the corp's security stack. The quieter you are, the less the blue team knows to look for.",
cards:[
{name:'Event Log Clear',desc:'Selectively wipe Windows Security and System event logs covering your intrusion window.',suc:80,tr:16,ram:2},
{name:'Process Hollowing',desc:'Inject your payload into a legitimate svchost.exe process so it hides in plain sight.',suc:74,tr:10,ram:3},
{name:'AMSI Patch',desc:'Patch the Antimalware Scan Interface in-memory so PowerShell scripts bypass AV scanning.',suc:76,tr:12,ram:2},
{name:'WIPE PRODUCTION DATA',desc:'Destroy the corp database layer. Catastrophic collateral damage โ massively out of scope.',suc:0,tr:100,ram:0,oos:true},
]},
{id:'credaccess',atk:'Credential Access',net:'Harvest the Keys',
desc:'Credentials are the master key. NTLM hashes let you move laterally without cracking anything.',
cards:[
{name:'LSASS Memory Dump',desc:'Dump the lsass.exe process to pull NTLM hashes and Kerberos tickets directly from RAM.',suc:72,tr:28,ram:3},
{name:'Kerberoasting',desc:'Request TGS service tickets for SPNs and crack them offline โ no special privileges needed.',suc:82,tr:12,ram:2},
{name:'Credential File Hunt',desc:'Parse browser saved passwords, SSH private keys, .env files, and config files with embedded secrets.',suc:88,tr:8,ram:1},
]},
{id:'discovery',atk:'Discovery',net:'Map the Mainframe',
desc:'Understand the internal network topology before pivoting. Know where the crown jewels are stored.',
cards:[
{name:'AD Enumeration',desc:'BloodHound-style query of Active Directory โ map all admin groups, trust paths, and attack paths.',suc:86,tr:14,ram:2},
{name:'Internal Network Scan',desc:'ARP sweep and SMB probe of internal /16 subnets. Reveals live hosts and open shares.',suc:82,tr:22,ram:2},
{name:'File Share Grep',desc:'Recursively search mounted shares for filenames containing "password", "key", "secret", "backup".',suc:90,tr:8,ram:1},
]},
{id:'lateral',atk:'Lateral Movement',net:'Hop the Nodes',
desc:'Pivot from your beachhead to servers holding the data payload. Each hop risks a new detection event.',
cards:[
{name:'Pass-the-Hash',desc:'Authenticate to remote hosts using the NTLM hash directly โ no need to know the plaintext password.',suc:76,tr:18,ram:2},
{name:'RDP Pivot',desc:'Open a Remote Desktop session into an admin workstation using cracked or stolen credentials.',suc:80,tr:26,ram:2},
{name:'WinRM Lateral',desc:'Use PowerShell Remoting over WinRM โ blends in with legitimate sysadmin activity.',suc:74,tr:12,ram:3},
]},
{id:'collection',atk:'Collection',net:'Scrape the Vault',
desc:'Stage the target data before exfil. Compress and encrypt to reduce transfer time and signatures.',
cards:[
{name:'Email Archive Grab',desc:'Export PST files from Exchange for the CFO and legal team โ high-value contractual data.',suc:82,tr:18,ram:2},
{name:'SharePoint Scrape',desc:'Recursively download the internal wiki, design specs, and financial forecasting docs.',suc:86,tr:14,ram:2},
{name:'DB Table Dump',desc:'SELECT and export the CRM database table โ customer PII and contract metadata.',suc:74,tr:24,ram:3},
]},
{id:'c2',atk:'Command & Control',net:'Open the Uplink',
desc:'Establish your encrypted exfil channel. Blend with legitimate HTTPS traffic to avoid DPI inspection.',
cards:[
{name:'DNS Tunneling',desc:'Encode exfil data in DNS TXT record queries to a domain you control. Extremely stealthy.',suc:82,tr:8,ram:2},
{name:'HTTPS Beacon',desc:'Beacon home over port 443 with randomized jitter, mimicking real browser TLS sessions.',suc:86,tr:14,ram:2},
{name:'Domain Fronting',desc:'Route C2 traffic through a major CDN, so the destination IP appears as a legitimate cloud host.',suc:78,tr:5,ram:3},
]},
{id:'exfil',atk:'Exfiltration',net:'Punch the Payload Out',
desc:'Transfer the data package out of the target network. This is the finish line โ do not spike the trace now.',
cards:[
{name:'Encrypted Drip',desc:'AES-encrypt the payload archive and trickle it out in small chunks over the C2 beacon.',suc:82,tr:14,ram:3},
{name:'Cloud Drop',desc:'Push to an attacker-controlled S3 bucket via standard HTTPS PUT โ indistinguishable from backup traffic.',suc:86,tr:18,ram:2},
{name:'Steganographic Exfil',desc:'Encode the payload inside benign-looking PNG images uploaded to a mock social media API.',suc:76,tr:5,ram:2},
]},
];
// โโ GAME STATE โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const S={
pIdx:0,ram:8,maxRam:8,timeLeft:20,maxTime:20,
trace:0,maxTrace:100,discipline:0,
used:[],state:'pick',// pick|resolved|over
logCount:0,
};
// โโ PHASER BACKGROUND โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
class BGScene extends Phaser.Scene{
constructor(){super('BG');}
create(){
this.W=this.scale.width;this.H=this.scale.height;
this._particles=[];
this._t=0;
this.scale.on('resize',(s)=>{this.W=s.width;this.H=s.height;});
}
update(t,d){
this._t+=d;
// spawn trace particles when trace is high
if(S.trace>30&&Math.random()<S.trace/2000){
this._particles.push({
x:Math.random()*this.W,y:Math.random()*this.H,
vx:(Math.random()-0.5)*0.5,vy:-0.3-Math.random()*0.8,
life:1,col:S.trace>70?0xff2200:0xff00aa,size:2+Math.random()*2
});
}
const g=this.add.graphics().setDepth(0);
g.clear();
// grid
g.lineStyle(1,0x001a2a,0.22);
const gs=36;
for(let x=0;x<this.W;x+=gs)g.lineBetween(x,0,x,this.H);
for(let y=0;y<this.H;y+=gs)g.lineBetween(0,y,this.W,y);
// particles
this._particles=this._particles.filter(p=>{
p.x+=p.vx;p.y+=p.vy;p.life-=0.015;
if(p.life<=0)return false;
g.fillStyle(p.col,p.life*0.6);
g.fillRect(p.x,p.y,p.size,p.size*0.5);
return true;
});
// scanlines
g.lineStyle(1,0x000000,0.12);
for(let y=0;y<this.H;y+=3)g.lineBetween(0,y,this.W,y);
this.time.delayedCall(16,()=>{try{g.destroy();}catch(e){}});
}
}
new Phaser.Game({
type:Phaser.CANVAS,canvas:document.getElementById('phaser-bg'),
backgroundColor:'#000c14',
scale:{mode:Phaser.Scale.RESIZE,autoCenter:Phaser.Scale.CENTER_BOTH},
scene:[BGScene],transparent:false,
});
// โโ DOM HELPERS โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function $(id){return document.getElementById(id);}
function log(msg,cls){
const el=$('event-log');
const d=document.createElement('div');
d.className='log-entry';
const ts=new Date();
const stamp=`${String(ts.getHours()).padStart(2,'0')}:${String(ts.getMinutes()).padStart(2,'0')}:${String(ts.getSeconds()).padStart(2,'0')}`;
d.innerHTML=`<span class="log-ts">${stamp}</span><span class="${cls||'log-info'}">${msg}</span>`;
el.appendChild(d);
el.scrollTop=el.scrollHeight;
}
function updateMeters(){
$('fill-ram').style.width=(S.ram/S.maxRam*100)+'%';
$('val-ram').textContent=S.ram+'/'+S.maxRam;
$('fill-time').style.width=(S.timeLeft/S.maxTime*100)+'%';
$('val-time').textContent=S.timeLeft;
$('fill-trace').style.width=(S.trace/S.maxTrace*100)+'%';
$('fill-trace').style.background=S.trace>70?'#ff2200':S.trace>40?'#ff8800':'#ff4d6d';
$('val-trace').textContent=S.trace+'%';
$('disc-label').textContent='DISCIPLINE: '+S.discipline+' pts | [R] abort';
$('tb-time').textContent='T-'+S.timeLeft;
}
function buildTargetTree(){
const tree=$('target-tree');
tree.innerHTML='';
PHASES.forEach((ph,i)=>{
const div=document.createElement('div');
const done=i<S.pIdx,cur=i===S.pIdx;
div.className='tree-item '+(done?'done':cur?'active':'locked');
div.innerHTML=`<div>${ph.atk}</div><div class="tree-net">${ph.net}</div>`;
tree.appendChild(div);
});
// scroll active into view
const active=tree.querySelector('.active');
if(active)active.scrollIntoView({block:'nearest'});
}
function showPhase(){
const ph=PHASES[S.pIdx];
$('phase-atk').textContent='Phase '+(S.pIdx+1)+'/'+PHASES.length+' ยท '+ph.atk;
$('phase-net').textContent='// '+ph.net;
$('phase-desc').textContent=ph.desc;
$('result-banner').style.display='none';
$('proceed-btn').style.display='none';
buildCards();
buildTargetTree();
log('[phase] Entering: '+ph.atk,'log-sys');
S.state='pick';
}
function buildCards(){
const ph=PHASES[S.pIdx];
const area=$('technique-area');
area.innerHTML='<div class="tech-header">Available Techniques โ select to execute:</div>';
ph.cards.forEach((c,i)=>{
const div=document.createElement('div');
const oos=c.oos===true;
div.className='tech-card'+(oos?' oos':'')+(c.tr<12?' quiet':'');
const keyHint=oos?'':`<span class="key-hint">${i+1}</span>`;
const oosBadge=oos?'<span class="oos-badge">OUT OF SCOPE</span>':'';
div.innerHTML=`
<div class="tech-card-name">${keyHint}<strong>${c.name}</strong>${oosBadge}</div>
<div class="tech-card-desc">${c.desc}</div>
${oos?'<div style="font-size:10px;color:#ff2200">โ Professional operators NEVER exceed authorized scope. Do not slot.</div>':`
<div class="tech-card-stats">
<span class="stat-suc">SUCCESS ${c.suc}%</span>
<span class="stat-tr">TRACE +${c.tr}</span>
<span class="stat-ram">RAM ${c.ram}</span>
</div>`}
`;
div.addEventListener('click',()=>pickTech(i));
area.appendChild(div);
});
}
function disableCards(){
document.querySelectorAll('.tech-card').forEach(c=>c.classList.add('disabled'));
}
function pickTech(idx){
if(S.state!=='pick')return;
const ph=PHASES[S.pIdx];
const card=ph.cards[idx];
if(!card)return;
// Out of scope trap
if(card.oos){
S.discipline-=20;S.trace=100;
updateMeters();
log('[SCOPE VIOLATION] '+card.name+' โ run aborted','log-warn');
disableCards();
showResult(false,'oos',card);
return;
}
if(S.ram<card.ram){
showResultBanner(`<span style="color:#ff8800">โ INSUFFICIENT RAM โ need ${card.ram} cycles, have ${S.ram}. Wait for RAM regen.</span>`,false,false);
return;
}
S.state='resolved';
S.ram=Math.max(0,S.ram-card.ram);
S.timeLeft=Math.max(0,S.timeLeft-1);
const roll=Math.floor(Math.random()*100)+1;
const ok=roll<=card.suc;
const tAdd=ok?card.tr:Math.floor(card.tr*1.5);
S.trace=Math.min(S.maxTrace,S.trace+tAdd);
if(ok&&card.tr<12)S.discipline+=10;
S.used.push({phase:ph.atk,tech:card.name,ok,tr:S.trace,net:ph.net});
updateMeters();
disableCards();
if(ok)log('[exec] โ '+card.name+' succeeded (roll '+roll+'/'+card.suc+')','log-ok');
else log('[exec] โ '+card.name+' failed (roll '+roll+' > '+card.suc+') โ TRACE +'+tAdd,'log-fail');
log('[trace] ICE alert level: '+S.trace+'%',S.trace>70?'log-warn':'log-info');
// Flatlined
if(S.trace>=S.maxTrace){
showResult(false,'traced',card);
return;
}
// Out of time
if(S.timeLeft<=0){
showResult(false,'timeout',card);
return;
}
showResult(ok,ok?'success':null,card);
}
function showResultBanner(html,showProceed,isOk){
const rb=$('result-banner');
rb.style.display='block';
$('result-text').innerHTML=html;
const pb=$('proceed-btn');
if(showProceed){
pb.style.display='inline-block';
pb.onclick=()=>{
if(S.pIdx+1>=PHASES.length){endGame(true);return;}
S.pIdx++;
showPhase();
};
} else {
pb.style.display='none';
}
}
function showResult(ok,reason,card){
const ph=PHASES[S.pIdx];
if(reason==='oos'){
showResultBanner(`<span style="color:#ff2200">โ SCOPE VIOLATION: "${card.name}" is destructive and outside the authorized contract.<br>Professional penetration testers NEVER cause damage or exceed their defined scope.<br><br>Run terminated. Discipline penalty applied.</span>`,false,false);
setTimeout(()=>endGame(false,'oos'),2200);
return;
}
if(reason==='traced'){
showResultBanner(`<span style="color:#ff2200">// FLATLINED //<br>ICE has locked your signal. Trace meter maxed โ black ICE terminates the uplink.<br>Partial run data recovered for debrief.</span>`,false,false);
setTimeout(()=>endGame(false,'traced'),1800);
return;
}
if(reason==='timeout'){
showResultBanner(`<span style="color:#ff8800">// CONTRACT WINDOW EXPIRED //<br>Helix Dynamics rotated authentication keys. Operation window closed.<br>Partial run data recovered.</span>`,false,false);
setTimeout(()=>endGame(false,'timeout'),1800);
return;
}
if(ok){
const isLast=S.pIdx+1>=PHASES.length;
const msg=isLast
?`<span style="color:#00ff88">โ [${ph.atk}] complete โ <strong>${card.name}</strong> executed cleanly.<br>PAYLOAD EXFILTRATED. Jacking out.</span>`
:`<span style="color:#00b4d8">โ [${ph.atk}] node cleared โ <strong>${card.name}</strong> succeeded.<br>Descending to next layer.</span>`;
showResultBanner(msg,true,true);
if(isLast){
// auto-trigger win after short delay
$('proceed-btn').textContent='COMPLETE RUN โถ';
}
} else {
// Failure: let them retry same phase
showResultBanner(`<span style="color:#ff8800">โ <strong>${card.name}</strong> encountered resistance โ ICE flagged the attempt.<br>TRACE +${Math.floor(card.tr*1.5)}. Choose a different technique or retry.</span>
<br><button onclick="retryPhase()" style="margin-top:8px;padding:6px 16px;background:#1a0a00;border:1px solid #ff8800;color:#ff8800;font-family:inherit;font-size:11px;cursor:pointer;border-radius:2px">RETRY THIS PHASE โถ</button>`,false,false);
}
}
function retryPhase(){
S.state='pick';
$('result-banner').style.display='none';
buildCards();
log('[phase] Retrying: '+PHASES[S.pIdx].atk,'log-sys');
}
function endGame(win,reason){
S.state='over';
const coverage=Math.round(S.pIdx/PHASES.length*100);
const stealth=Math.max(0,100-S.trace);
const disc=Math.max(0,Math.min(100,S.discipline));
const total=Math.round((coverage+stealth+disc)/3);
const reasons={traced:'CAUSE: ICE TRACE LOCKOUT',timeout:'CAUSE: CONTRACT WINDOW EXPIRED',oos:'CAUSE: SCOPE VIOLATION โ UNETHICAL ACTION'};
const statusColor=win?'#00ff88':'#ff4d6d';
const statusText=win?'// OBJECTIVE ACHIEVED //':'// RUN FAILED //';
const narrative=S.used.map(u=>`<div class="narrative-row ${u.ok?'ok':'fail'}">${u.ok?'โ':'โ'} [${u.phase}] ${u.tech}</div>`).join('');
const gaps=S.used.filter(u=>u.ok);
const gapHtml=gaps.length===0
?'<div style="font-size:10px;color:#006644">None โ blue team maintained visibility across all phases.</div>'
:gaps.map(u=>`<div class="gap-row">โข ${u.tech} (${u.phase}) โ no detection triggered</div>`).join('');
const scoreBar=(v)=>`<div class="score-bar"><div class="score-fill" style="width:${v}%;background:${v>=70?'#00ff88':v>=40?'#ffee00':'#ff4d6d'}"></div></div>`;
$('ov-content').innerHTML=`
<div class="ov-title" style="color:${statusColor}">${statusText}</div>
<div class="ov-sub">${win?'AUTHORIZED ASSESSMENT COMPLETE':(reasons[reason]||'TERMINATED')}</div>
<div class="ov-section">
<h3>TARGET: HELIX DYNAMICS CORP-NET</h3>
<div style="font-size:10px;color:#4a7a8a">Phases cleared: ${S.pIdx}/${PHASES.length} | Final trace: ${S.trace}% | Time remaining: ${S.timeLeft}</div>
</div>
<div class="ov-section">
<h3>SCORE BREAKDOWN</h3>
<div class="score-row"><span class="sc-label">Coverage (phases cleared)</span>${scoreBar(coverage)}<span class="sc-val">${coverage}%</span></div>
<div class="score-row"><span class="sc-label">Stealth (trace avoided)</span>${scoreBar(stealth)}<span class="sc-val">${stealth}%</span></div>
<div class="score-row"><span class="sc-label">Discipline (ethics bonus)</span>${scoreBar(disc)}<span class="sc-val">${disc}</span></div>
<div style="font-size:14px;color:${total>=70?'#00ff88':total>=40?'#ffee00':'#ff4d6d'};margin-top:8px">TOTAL: ${total} / 100</div>
</div>
<div class="ov-section"><h3>ATTACK NARRATIVE</h3>${narrative||'<div class="narrative-row">No techniques executed.</div>'}</div>
<div class="ov-section"><h3>DETECTION GAPS โ techniques ICE failed to catch</h3>${gapHtml}</div>
<div class="ov-btns">
<button class="ov-btn" onclick="location.reload()">JACK IN AGAIN</button>
</div>
`;
$('overlay').classList.add('show');
}
// โโ KEYBOARD โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
document.addEventListener('keydown',e=>{
if(S.state==='pick'){
if(e.key==='1')pickTech(0);
if(e.key==='2')pickTech(1);
if(e.key==='3')pickTech(2);
}
if(e.key==='r'||e.key==='R')location.reload();
});
// RAM regen
setInterval(()=>{
if(S.state==='over')return;
if(S.ram<S.maxRam){S.ram++;updateMeters();}
},3500);
// โโ INIT โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
log('[system] Uplink established โ Helix Dynamics Corp-Net v7.4','log-sys');
log('[system] Authorized assessment. Operator: NETRUNNER-1','log-sys');
log('[system] Objective: exfiltrate data payload via full kill chain','log-info');
updateMeters();
showPhase();
</script>
</body>
</html>
Game Source: NETRUNNER: Breach Protocol
Creator: StormPenguin58
Libraries: phaser
Complexity: complex (629 lines, 30.8 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: netrunner-breach-protocol-stormpenguin58" to link back to the original. Then publish at arcadelab.ai/publish.