10 <12html 10lang=05"de"> <12head> <12meta 10charset=05"UTF-8"> <12meta 10name=05"viewport" 10content=05"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <12title>HYPERSTITION: SHENZHEN <12style> 15 *15 {15  15 02box-sizing:15 02border-box;15  15 touch-action:15 02manipulation; 15 } 15 12html,15 12body15 { 15 02width:15 0210010%; 15 02height:15 0210010%; 15 02overflow:15 02hidden; 15 02background:15 02#020206; 15 02color:15 02#00ffcc; 15 02font-family:15 05'Courier New',15 05'PingFang SC',15 05'Microsoft YaHei',15 02monospace; 15 02margin:15 020; 15 02padding:15 02610px; 15 02display:15 02flex; 15 02justify-content:15 02center; 15 02align-items:15 02flex-start; 15 02user-select:15 02none; 15 02-webkit-02user-select:15 02none; 15 } 15  15 #10game-wrapper15 { 15 02position:15 02relative; 15 02width:15 0210010%; 15 02max-width:15 0238010px; 15 02display:15 02flex; 15 02flex-direction:15 02column; 15 02gap:15 02510px; 15 02justify-content:15 02flex-start; 15 } 15 15/* CYBERPUNK CHINESE TITLE SCREEN */ 15 #10title-screen15 { 15 02position:15 02absolute; 15 02top:15 020;15 02left:15 020;15 02right:15 020;15 02bottom:15 020; 15 02background:15 10radial-gradient(02circle15 at15 02center,15 02#0e051c15 02010%,15 02#02020815 0210010%); 15 02border:15 02210px15 02solid15 02#ff0055; 15 02box-shadow:15 02inset15 02015 02015 023010px15 10rgba(02255,15 020,15 0285,15 020.5),15 02015 02015 022010px15 10rgba(020,15 02255,15 02204,15 020.3); 15 02display:15 02flex; 15 02flex-direction:15 02column; 15 02justify-content:15 02center; 15 02align-items:15 02center; 15 02z-index:15 02500; 15 02padding:15 021510px; 15 02text-align:15 02center; 15 02overflow:15 02hidden; 15 } 15 15/* CRT Scanline Overlay Effect */ 15 #10title-screen::14before15 { 15 02content:15 05" "; 15 02display:15 02block; 15 02position:15 02absolute; 15 02top:15 020;15 02left:15 020;15 02bottom:15 020;15 02right:15 020; 15 02background:15 10linear-gradient(10rgba(0218,15 0216,15 0216,15 020)15 025010%,15 10rgba(020,15 020,15 020,15 020.35)15 025010%),15 10linear-gradient(029010deg,15 10rgba(02255,15 020,15 020,15 020.05),15 10rgba(020,15 02255,15 020,15 020.02),15 10rgba(020,15 020,15 02255,15 020.05)); 15 02z-index:15 022; 15 02background-size:15 0210010%15 02310px,15 02610px15 0210010%; 15 02pointer-events:15 02none; 15 } 15 15/* Watermark Chinese Characters in Background */ 15 .07title-bg-kanji15 { 15 02position:15 02absolute; 15 02font-size:15 0211010px; 15 02color:15 10rgba(02255,15 020,15 0285,15 020.05); 15 02font-weight:15 02900; 15 02z-index:15 021; 15 02pointer-events:15 02none; 15 02line-height:15 020.9; 15 02letter-spacing:15 02-510px; 15 } 15 .07title-main-box15 { 15 02z-index:15 023; 15 02border-top:15 021.510px15 02solid15 02#00ffcc; 15 02border-bottom:15 021.510px15 02solid15 02#00ffcc; 15 02padding:15 021210px15 021010px; 15 02width:15 0210010%; 15 02background:15 10rgba(028,15 023,15 0218,15 020.75); 15 02box-shadow:15 02015 02015 021510px15 10rgba(020,15 02255,15 02204,15 020.15); 15 } 15 .07title-kanji-top15 { 15 02font-size:15 021310px; 15 02color:15 02#ffcc00; 15 02letter-spacing:15 02510px; 15 02text-shadow:15 02015 02015 02810px15 02#ffcc00; 15 02margin-bottom:15 02410px; 15 02font-weight:15 02bold; 15 } 15 .07title-glitch15 { 15 02font-size:15 022110px; 15 02font-weight:15 02900; 15 02color:15 02#ff0055; 15 02text-shadow:15 02310px15 02015 02#00ffcc,15 02-210px15 02015 02#ffcc00; 15 02letter-spacing:15 02210px; 15 02line-height:15 021.1; 15 02animation:15 glitch15 021.610s15 02infinite15 02alternate; 15 } 15 .07title-sub15 { 15 02font-size:15 021110px; 15 02color:15 02#00ffcc; 15 02margin-top:15 02610px; 15 02letter-spacing:15 02210px; 15 02text-shadow:15 02015 02015 02810px15 02#00ffcc; 15 02font-weight:15 02bold; 15 } 15 .07title-tags15 { 15 02display:15 02flex; 15 02gap:15 02610px; 15 02margin:15 021210px15 02015 02610px15 020; 15 02z-index:15 023; 15 } 15 .07tag-badge15 { 15 02font-size:15 02810px; 15 02background:15 02#160829; 15 02border:15 02110px15 02solid15 02#ff0055; 15 02color:15 02#ff0055; 15 02padding:15 02210px15 02610px; 15 02border-radius:15 02210px; 15 02letter-spacing:15 02110px; 15 } 15 .07title-info15 { 15 02z-index:15 023; 15 02font-size:15 02810px; 15 02color:15 02#8899a6; 15 02margin:15 021010px15 02015 021810px15 020; 15 02line-height:15 021.5; 15 02border-left:15 02210px15 02solid15 02#00ffcc; 15 02padding-left:15 02810px; 15 02text-align:15 02left; 15 02background:15 10rgba(020,15 020,15 020,15 020.4); 15 02width:15 0210010%; 15 } 15 .07start-btn15 { 15 02z-index:15 023; 15 02background:15 02#ff0055; 15 02border:15 021.510px15 02solid15 02#00ffcc; 15 02color:15 02#020208; 15 02font-family:15 02inherit; 15 02font-weight:15 02900; 15 02font-size:15 021110px; 15 02padding:15 021010px15 022210px; 15 02cursor:15 02pointer; 15 02box-shadow:15 02015 02015 021510px15 10rgba(02255,15 020,15 0285,15 020.8); 15 02letter-spacing:15 021.510px; 15 02animation:15 pulse15 021.210s15 02infinite15 02alternate; 15 } 15 .07start-btn:14active15 { 15 02background:15 02#00ffcc; 15 02color:15 02#000; 15 02box-shadow:15 02015 02015 022010px15 02#00ffcc; 15 } 15 15/* GAME UI */ 15 #10ui-stats15 { 15 02width:15 0210010%; 15 02display:15 02grid; 15 02grid-template-columns:15 07repeat(024,15 021fr); 15 02gap:15 02210px; 15 02font-weight:15 02bold; 15 02color:15 02#ff0055; 15 02font-size:15 02910px; 15 02background:15 02#08030f; 15 02padding:15 02410px; 15 02border:15 02110px15 02solid15 02#ff0055; 15 02box-shadow:15 02015 02015 02610px15 10rgba(02255,15 020,15 0285,15 020.3); 15 } 15 #10ui-stats15 12div15 {15 02text-align:15 02center;15 02white-space:15 02nowrap;15 } 15  15 #10game-container15 { 15 02position:15 02relative; 15 02width:15 0210010%; 15 } 15 #10map15 { 15 02background:15 02#000; 15 02border:15 021.510px15 02solid15 02#ff0055; 15 02box-shadow:15 02015 02015 02810px15 10rgba(02255,15 020,15 0285,15 020.25); 15 02padding:15 02210px; 15 02width:15 0210010%; 15 02height:15 0225010px; 15 02display:15 02grid; 15 02grid-template-columns:15 07repeat(0222,15 021fr); 15 02grid-template-rows:15 07repeat(0214,15 021fr); 15 02align-items:15 02center; 15 02justify-items:15 02center; 15 } 15 .07t15 { 15 02width:15 0210010%; 15 02height:15 0210010%; 15 02display:15 02flex; 15 02align-items:15 02center; 15 02justify-content:15 02center; 15 02font-size:15 021110px; 15 02font-weight:15 02bold; 15 02line-height:15 021; 15 } 15 15/* AIMING SKILL-CHECK MODAL */ 15 #10aim-modal15 { 15 02display:15 02none; 15 02position:15 02absolute; 15 02top:15 025010%;15 02left:15 025010%; 15 02transform:15 10translate(02-5010%,15 02-5010%); 15 02width:15 028810%; 15 02background:15 10rgba(026,15 022,15 0214,15 020.95); 15 02border:15 02210px15 02solid15 02#00ffcc; 15 02box-shadow:15 02015 02015 022010px15 10rgba(020,15 02255,15 02204,15 020.6); 15 02padding:15 021010px; 15 02flex-direction:15 02column; 15 02align-items:15 02center; 15 02gap:15 02810px; 15 02z-index:15 02200; 15 02cursor:15 02pointer; 15 } 15 #10aim-title15 { 15 02color:15 02#ff0055; 15 02font-size:15 021010px; 15 02font-weight:15 02bold; 15 02letter-spacing:15 02110px; 15 } 15 #10aim-bar-container15 { 15 02width:15 0210010%; 15 02height:15 022210px; 15 02background:15 02#000; 15 02border:15 02110px15 02solid15 02#00ffcc; 15 02position:15 02relative; 15 02overflow:15 02hidden; 15 } 15 #10aim-target-zone15 { 15 02position:15 02absolute; 15 02top:15 020;15 02bottom:15 020; 15 02background:15 10rgba(020,15 02255,15 02204,15 020.35); 15 02border-left:15 02210px15 02solid15 02#00ffcc; 15 02border-right:15 02210px15 02solid15 02#00ffcc; 15 } 15 #10aim-cursor15 { 15 02position:15 02absolute; 15 02top:15 020;15 02bottom:15 020; 15 02width:15 02410px; 15 02background:15 02#ff0055; 15 02box-shadow:15 02015 02015 02810px15 02#ff0055,15 02015 02015 021210px15 02#ff0055; 15 } 15 #10aim-instruction15 { 15 02font-size:15 02910px; 15 02color:15 02#ffcc00; 15 02font-weight:15 02bold; 15 02letter-spacing:15 02110px; 15 } 15 #10shop-modal15 { 15 02display:15 02none; 15 02position:15 02absolute; 15 02top:15 02410px;15 02left:15 02410px;15 02right:15 02410px;15 02bottom:15 02410px; 15 02background:15 10rgba(024,15 022,15 0210,15 020.98); 15 02border:15 021.510px15 02solid15 02#ffcc00; 15 02box-shadow:15 02015 02015 021510px15 10rgba(02255,15 02204,15 020,15 020.4); 15 02padding:15 02610px; 15 02flex-direction:15 02column; 15 02z-index:15 02100; 15 } 15 #10shop-title15 { 15 02color:15 02#ffcc00; 15 02font-size:15 021010px; 15 02font-weight:15 02bold; 15 02text-align:15 02center; 15 02border-bottom:15 02110px15 02dashed15 02#ffcc00; 15 02padding-bottom:15 02210px; 15 02margin-bottom:15 02410px; 15 } 15 .07shop-items15 { 15 02display:15 02flex; 15 02flex-direction:15 02column; 15 02gap:15 02410px; 15 02overflow-y:15 02auto; 15 02flex:15 021; 15 } 15 .07shop-btn15 { 15 02background:15 02#0a0a1a; 15 02border:15 02110px15 02solid15 02#00ffcc; 15 02color:15 02#00ffcc; 15 02padding:15 02410px15 02610px; 15 02font-size:15 02910px; 15 02font-family:15 02inherit; 15 02cursor:15 02pointer; 15 02text-align:15 02left; 15 02display:15 02flex; 15 02justify-content:15 02space-between; 15 } 15 .07shop-btn:14active15 {15 02background:15 02#00ffcc;15 02color:15 02#000;15 } 15 .07close-shop15 { 15 02margin-top:15 02410px; 15 02background:15 02#ff0055; 15 02color:15 02#fff; 15 02border:15 02none; 15 02padding:15 02510px; 15 02font-weight:15 02bold; 15 02cursor:15 02pointer; 15 02text-align:15 02center; 15 02font-size:15 021010px; 15 } 15 #10action-panel,15 #10inventory-panel15 { 15 02width:15 0210010%; 15 02display:15 02flex; 15 02gap:15 02310px; 15 } 15 .07action-btn,15 .07inv-slot15 { 15 02flex:15 021; 15 02background:15 02#0f0a1c; 15 02border:15 02110px15 02solid15 02#00ffcc; 15 02color:15 02#ffcc00; 15 02font-size:15 02910px; 15 02padding:15 02510px15 02110px; 15 02text-align:15 02center; 15 02cursor:15 02pointer; 15 02border-radius:15 02210px; 15 02white-space:15 02nowrap; 15 02overflow:15 02hidden; 15 02text-overflow:15 02ellipsis; 15 02font-weight:15 02bold; 15 } 15 .07action-btn15 {15 02background:15 02#240a18;15 02border-color:15 02#ff0055;15 02color:15 02#ff0055;15 } 15 .07action-btn:14active,15 .07inv-slot:14active15 {15 02background:15 02#00ffcc;15 02color:15 02#000;15 } 15  15 #10log15 { 15 02width:15 0210010%; 15 02height:15 024210px; 15 02background:15 02#05020c; 15 02border:15 02110px15 02solid15 02#222; 15 02color:15 02#ffcc00; 15 02font-size:15 02910px; 15 02padding:15 02310px15 02510px; 15 02overflow-y:15 02auto; 15 02line-height:15 021.2; 15 } 15 .07controls-wrapper15 { 15 02display:15 02flex; 15 02justify-content:15 02center; 15 02margin-top:15 02210px; 15 } 15 .07controls15 { 15 02display:15 02grid; 15 02grid-template-columns:15 07repeat(023,15 021fr); 15 02gap:15 02410px; 15 02width:15 0217010px; 15 } 15 .07btn15 { 15 02background:15 02#0f0f24; 15 02color:15 02#00ffcc; 15 02border:15 02110px15 02solid15 02#00ffcc; 15 02padding:15 02710px15 020; 15 02font-size:15 021210px; 15 02font-weight:15 02bold; 15 02text-align:15 02center; 15 02border-radius:15 02310px; 15 02cursor:15 02pointer; 15 } 15 .07btn:14active15 {15 02background:15 02#00ffcc;15 02color:15 02#000;15 } 15  15 .07p-player15 {15 02color:15 02#00ffcc;15 } 15 .07p-psychotic15 {15 02color:15 02#ff0055;15 02animation:15 02blink15 020.410s15 02infinite;15 } 15 .07e-triad15 {15 02color:15 02#ff3333;15 } 15 .07e-ministry15 {15 02color:15 02#ffcc00;15 } 15 .07e-drone15 {15 02color:15 02#ff00ff;15 } 15 .07i-vendor15 {15 02color:15 02#ffcc00;15 02text-shadow:15 02015 02015 02510px15 02#ffcc00;15 } 15 .07i-item15 {15 02color:15 02#00ff66;15 } 15 .07i-exit15 {15 02color:15 02#00ffcc;15 02text-shadow:15 02015 02015 02610px15 02#00ffcc;15 02font-size:15 021210px;15 } 15 .07tile-wall15 {15 02color:15 02#3d1c3d;15 } 15 .07tile-floor15 {15 02color:15 02#160f24;15 } 15 .07fog-hidden15 {15 02color:15 02#000000;15 } 15 .07fog-explored15 {15 02color:15 02#12081c;15 } 15 .07stunned15 {15 02color:15 02#77777715 10!important;15 } 15 @02keyframes15 12glitch15 { 15 120%15 {15 02transform:15 07skew(02010deg);15 } 15 1280%15 {15 02transform:15 07skew(02010deg);15 } 15 1285%15 {15 02transform:15 07skew(02310deg);15 } 15 1290%15 {15 02transform:15 07skew(02-310deg);15 } 15 12100%15 {15 02transform:15 07skew(02010deg);15 } 15 } 15 @02keyframes15 12pulse15 { 15 120%15 {15 02transform:15 10scale(021);15 02box-shadow:15 02015 02015 021010px15 10rgba(02255,15 020,15 0285,15 020.6);15 } 15 12100%15 {15 02transform:15 10scale(021.04);15 02box-shadow:15 02015 02015 022010px15 10rgba(02255,15 020,15 0285,15 020.9);15 } 15 } 15 @02keyframes15 12blink15 {15 120%15 {15 02opacity:15 021;15 }15 1250%15 {15 02opacity:15 020.2;15 }15 12100%15 {15 02opacity:15 021;15 }15 } <12body> <12div 10id=05"game-wrapper"> 15 <12div 10id=05"title-screen"> <12div 10class=05"title-bg-kanji">超迷信<12br>深圳 <12div 10class=05"title-main-box"> <12div 10class=05"title-kanji-top">【 超 迷 信 】 <12div 10class=05"title-glitch">HYPERSTITION <12div 10class=05"title-sub">SHENZHEN // 深圳 <12div 10class=05"title-tags"> <12span 10class=05"tag-badge">加速主义 <12span 10class=05"tag-badge">赛博朋克 <12span 10class=05"tag-badge">特区 S-01 <12div 10class=05"title-info"> > SYSTEM: NEURAL INTERFACE ONLINE<12br> > LOCATION: SHENZHEN SEZ [深圳特区]<12br> > WARNING: 人性流失 (HUMANITY DEPLETION) <12button 10class=05"start-btn" 10onclick=05"startGame()">[ 网络接入 // ACCESS ] 15 <12div 10id=05"ui-stats"> <12div 10id=05"stat-level">ZONE: S-01 <12div 10id=05"stat-hp">INT: 35 <12div 10id=05"stat-ram">RAM: 4/4 <12div 10id=05"stat-psyche">HUM: 100% <12div 10id=05"stat-food">CAL: 100% <12div 10id=05"stat-ammo">AMMO: 8 <12div 10id=05"stat-atk">GUN: 4 <12div 10id=05"stat-cred">¥50 <12div 10id=05"game-container"> <12div 10id=05"map"> 15 <12div 10id=05"aim-modal" 10onclick=05"resolveShot()"> <12div 10id=05"aim-title">=== ZIELERFASSUNG // LOCK-ON === <12div 10id=05"aim-bar-container"> <12div 10id=05"aim-target-zone"> <12div 10id=05"aim-cursor"> <12div 10id=05"aim-instruction">[F] / [SPACE] / TIPPE ZUM FEUERN <12div 10id=05"shop-modal"> <12div 10id=05"shop-title">=== SHENZHEN TAOBAO-LOOM [¥] === <12div 10class=05"shop-items"> <12button 10class=05"shop-btn" 10onclick=05"buyShopItem('Sheng-Stim', 35, 0)"><12span>[1] Sheng-Stim (+20 Int)<12span>¥35 <12button 10class=05"shop-btn" 10onclick=05"buyShopItem('Synth-Baozi', 15, 0)"><12span>[2] Synth-Baozi (+50% Cal)<12span>¥15 <12button 10class=05"shop-btn" 10onclick=05"buyShopItem('Mag-Rounds', 25, 0)"><12span>[3] Kinetic-Mag (+6 Ammo)<12span>¥25 <12button 10class=05"shop-btn" 10onclick=05"buyShopItem('Xeno-Blade', 110, 25)"><12span>[4] Xeno-Blade (+3 ATK)<12span>¥110 (-25% H) <12button 10class=05"shop-btn" 10onclick=05"buyShopItem('Subdermal-Weave', 110, 25)"><12span>[5] Subdermal (+15 HP)<12span>¥110 (-25% H) <12button 10class=05"shop-btn" 10onclick=05"buyShopItem('Teleoplex-Chip', 110, 25)"><12span>[6] Teleoplex (+2 RAM)<12span>¥110 (-25% H) <12div 10class=05"close-shop" 10onclick=05"closeShop()">TRENNEN [Ø] <12div 10id=05"action-panel"> <12div 10class=05"action-btn" 10onclick=05"shootNearest()">[F] KINETIK-SCHUSS <12div 10class=05"action-btn" 10onclick=05"hackEnemy()">[H] OVERLOAD <12div 10id=05"inventory-panel"> <12div 10class=05"inv-slot" 10onclick=05"useItem(0)" 10id=05"inv-0">[1] - <12div 10class=05"inv-slot" 10onclick=05"useItem(1)" 10id=05"inv-1">[2] - <12div 10class=05"inv-slot" 10onclick=05"useItem(2)" 10id=05"inv-2">[3] - <12div 10class=05"inv-slot" 10onclick=05"useItem(3)" 10id=05"inv-3">[4] - <12div 10id=05"log"> <12div 10class=05"controls-wrapper"> <12div 10class=05"controls"> <12div> <12div 10class=05"btn" 10onclick=05"handleInput(0, -1)">▲ <12div> <12div 10class=05"btn" 10onclick=05"handleInput(-1, 0)">◄ <12div 10class=05"btn" 10onclick=05"handleInput(0, 0)">Wait <12div 10class=05"btn" 10onclick=05"handleInput(1, 0)">► <12div> <12div 10class=05"btn" 10onclick=05"handleInput(0, 1)">▼ <12div> <12script> 02const15 WIDTH15 =15 0222; 02const15 HEIGHT15 =15 0214; 02const15 MAX_INV15 =15 024; 02const15 VISIBILITY_RADIUS15 =15 026; 02let15 audioCtx15 =15 02null; 02function15 initAudio()15 { 15 02if15 (!audioCtx)15 { 15 audioCtx15 =15 06new15 (10window.AudioContext15 ||15 10window.webkitAudioContext)(); 15 } 15 02if15 (audioCtx.state15 ===15 05'suspended')15 { 15 audioCtx.resume(); 15 } } 02function15 startGame()15 { 15 initAudio(); 15 playSfx(05'exit'); 15 10document.getElementById(05'title-screen').style.display15 =15 05'none'; } 02function15 playSfx(type)15 { 15 initAudio(); 15 02if15 (!audioCtx)15 02return; 15 02const15 now15 =15 audioCtx.currentTime; 15 02const15 osc15 =15 audioCtx.createOscillator(); 15 02const15 gain15 =15 audioCtx.createGain(); 15 osc.connect(gain); 15 gain.connect(audioCtx.destination); 15 02if15 (type15 ===15 05'move')15 { 15 osc.type15 =15 05'triangle'; 15 osc.frequency.setValueAtTime(02110,15 now); 15 osc.frequency.exponentialRampToValueAtTime(0240,15 now15 +15 020.04); 15 gain.gain.setValueAtTime(020.04,15 now); 15 gain.gain.exponentialRampToValueAtTime(020.001,15 now15 +15 020.04); 15 osc.start(now); 15 osc.stop(now15 +15 020.04); 15 }15 02else15 02if15 (type15 ===15 05'shoot')15 { 15 osc.type15 =15 05'sawtooth'; 15 osc.frequency.setValueAtTime(02700,15 now); 15 osc.frequency.exponentialRampToValueAtTime(0260,15 now15 +15 020.12); 15 gain.gain.setValueAtTime(020.12,15 now); 15 gain.gain.exponentialRampToValueAtTime(020.001,15 now15 +15 020.12); 15 osc.start(now); 15 osc.stop(now15 +15 020.12); 15 }15 02else15 02if15 (type15 ===15 05'hit')15 { 15 osc.type15 =15 05'square'; 15 osc.frequency.setValueAtTime(02140,15 now); 15 osc.frequency.exponentialRampToValueAtTime(0230,15 now15 +15 020.18); 15 gain.gain.setValueAtTime(020.15,15 now); 15 gain.gain.exponentialRampToValueAtTime(020.001,15 now15 +15 020.18); 15 osc.start(now); 15 osc.stop(now15 +15 020.18); 15 }15 02else15 02if15 (type15 ===15 05'item')15 { 15 osc.type15 =15 05'square'; 15 osc.frequency.setValueAtTime(02440,15 now); 15 osc.frequency.setValueAtTime(02880,15 now15 +15 020.06); 15 gain.gain.setValueAtTime(020.08,15 now); 15 gain.gain.exponentialRampToValueAtTime(020.001,15 now15 +15 020.18); 15 osc.start(now); 15 osc.stop(now15 +15 020.18); 15 }15 02else15 02if15 (type15 ===15 05'buy')15 { 15 osc.type15 =15 05'square'; 15 osc.frequency.setValueAtTime(02523,15 now); 15 osc.frequency.setValueAtTime(02659,15 now15 +15 020.06); 15 osc.frequency.setValueAtTime(02783,15 now15 +15 020.12); 15 gain.gain.setValueAtTime(020.08,15 now); 15 gain.gain.exponentialRampToValueAtTime(020.001,15 now15 +15 020.22); 15 osc.start(now); 15 osc.stop(now15 +15 020.22); 15 }15 02else15 02if15 (type15 ===15 05'hack')15 { 15 osc.type15 =15 05'sawtooth'; 15 osc.frequency.setValueAtTime(02220,15 now); 15 osc.frequency.linearRampToValueAtTime(02880,15 now15 +15 020.08); 15 osc.frequency.linearRampToValueAtTime(02330,15 now15 +15 020.16); 15 gain.gain.setValueAtTime(020.1,15 now); 15 gain.gain.exponentialRampToValueAtTime(020.001,15 now15 +15 020.18); 15 osc.start(now); 15 osc.stop(now15 +15 020.18); 15 }15 02else15 02if15 (type15 ===15 05'exit')15 { 15 15// 8-Bit Chiptune Warp / Level Clear Sound 15 osc.type15 =15 05'square'; 15 02const15 notes15 =15 [02261.63,15 02329.63,15 02392.00,15 02523.25,15 02659.25,15 02783.99,15 021046.50]; 15 02const15 step15 =15 020.035; 15 notes.forEach((freq,15 i)15 =>15 { 15 osc.frequency.setValueAtTime(freq,15 now15 +15 i15 *15 step); 15 }); 15 gain.gain.setValueAtTime(020.1,15 now); 15 gain.gain.exponentialRampToValueAtTime(020.001,15 now15 +15 notes.length15 *15 step); 15 osc.start(now); 15 osc.stop(now15 +15 notes.length15 *15 step); 15 }15 02else15 02if15 (type15 ===15 05'gameover')15 { 15 osc.type15 =15 05'sawtooth'; 15 osc.frequency.setValueAtTime(02300,15 now); 15 osc.frequency.exponentialRampToValueAtTime(0230,15 now15 +15 020.5); 15 gain.gain.setValueAtTime(020.2,15 now); 15 gain.gain.exponentialRampToValueAtTime(020.001,15 now15 +15 020.5); 15 osc.start(now); 15 osc.stop(now15 +15 020.5); 15 } } 02let15 player15 =15 { 15 x:15 020,15 y:15 020, 15 hp:15 0235,15 maxHp:15 0235, 15 ram:15 024,15 maxRam:15 024, 15 humanity:15 02100, 15 hunger:15 02100, 15 ammo:15 028, 15 atk:15 024, 15 creds:15 0250, 15 level:15 021, 15 noise:15 020, 15 inventory:15 [05'Sheng-Stim',15 05'Synth-Baozi'] }; 02let15 map15 =15 []; 02let15 visibleMap15 =15 []; 02let15 exploredMap15 =15 []; 02let15 enemies15 =15 []; 02let15 items15 =15 []; 02let15 vendors15 =15 []; 02let15 exitGate15 =15 {15 x:15 020,15 y:15 02015 }; 02let15 turnCounter15 =15 020; 02let15 inShop15 =15 02false; 15// AIMING SKILL CHECK VARIABLES 02let15 isAiming15 =15 02false; 02let15 aimPos15 =15 020; 02let15 aimDir15 =15 021; 02let15 aimSpeed15 =15 022.5; 02let15 aimAnimFrame15 =15 02null; 02let15 currentAimTargetIdx15 =15 -021; 02function15 log(msg)15 { 15 02const15 logBox15 =15 10document.getElementById(05'log'); 15 02if15 (logBox)15 { 15 logBox.innerHTML15 +=15 05`05> 05${msg05}05
05`; 15 logBox.scrollTop15 =15 logBox.scrollHeight; 15 } } 02function15 initGame()15 { 15 log(05"SONDERWIRTSCHAFTSZONE LÄDT... NETZWERK GENERIERT."); 15 generateCitySector(); 15 updateVision(); 15 render(); 15 10document.addEventListener(05'keydown',15 e15 =>15 { 15 initAudio(); 15 02if15 (isAiming)15 { 15 02if15 (e.key15 ===15 05'f'15 ||15 e.key15 ===15 05'F'15 ||15 e.key15 ===15 05' '15 ||15 e.key15 ===15 05'Enter')15 { 15 resolveShot(); 15 } 15 02return; 15 } 15 02if15 (inShop)15 { 15 02if15 (e.key15 ===15 05'Escape')15 closeShop(); 15 02return; 15 } 15 02if15 (e.key15 ===15 05'ArrowUp'15 ||15 e.key15 ===15 05'w')15 handleInput(020,15 -021); 15 02if15 (e.key15 ===15 05'ArrowDown'15 ||15 e.key15 ===15 05's')15 handleInput(020,15 021); 15 02if15 (e.key15 ===15 05'ArrowLeft'15 ||15 e.key15 ===15 05'a')15 handleInput(-021,15 020); 15 02if15 (e.key15 ===15 05'ArrowRight'15 ||15 e.key15 ===15 05'd')15 handleInput(021,15 020); 15 02if15 (e.key15 ===15 05' ')15 handleInput(020,15 020); 15 02if15 (e.key15 ===15 05'f'15 ||15 e.key15 ===15 05'F')15 shootNearest(); 15 02if15 (e.key15 ===15 05'h'15 ||15 e.key15 ===15 05'H')15 hackEnemy(); 15 02if15 (e.key15 >=15 05'1'15 &&15 e.key15 <=15 05'4')15 useItem(10parseInt(e.key)15 -15 021); 15 }); } 02function15 generateCitySector()15 { 15 map15 =15 10Array(HEIGHT).fill(02null).map(()15 =>15 10Array(WIDTH).fill(05'▓')); 15 exploredMap15 =15 10Array(HEIGHT).fill(02null).map(()15 =>15 10Array(WIDTH).fill(02false)); 15 visibleMap15 =15 10Array(HEIGHT).fill(02null).map(()15 =>15 10Array(WIDTH).fill(02false)); 15 enemies15 =15 []; 15 items15 =15 []; 15 vendors15 =15 []; 15 02let15 blocks15 =15 []; 15 02let15 attempts15 =15 020; 15  15 02while15 (blocks.length15 <15 02715 &&15 attempts15 <15 02120)15 { 15 attempts++; 15 02let15 w15 =15 10Math.floor(10Math.random()15 *15 023)15 +15 023; 15 02let15 h15 =15 10Math.floor(10Math.random()15 *15 023)15 +15 023; 15 02let15 x15 =15 10Math.floor(10Math.random()15 *15 (WIDTH15 -15 w15 -15 022))15 +15 021; 15 02let15 y15 =15 10Math.floor(10Math.random()15 *15 (HEIGHT15 -15 h15 -15 022))15 +15 021; 15 02let15 overlap15 =15 blocks.some(b15 =>15 !(x15 +15 w15 +15 02115 <15 b.x15 ||15 x15 >15 b.x15 +15 b.w15 +15 02115 ||15 y15 +15 h15 +15 02115 <15 b.y15 ||15 y15 >15 b.y15 +15 b.h15 +15 021)); 15 02if15 (!overlap)15 { 15 blocks.push({15 x,15 y,15 w,15 h,15 center:15 {15 x:15 10Math.floor(x15 +15 w/022),15 y:15 10Math.floor(y15 +15 h/022)15 }15 }); 15 02for15 (02let15 rx15 =15 x;15 rx15 <15 x15 +15 w;15 rx++)15 { 15 02for15 (02let15 ry15 =15 y;15 ry15 <15 y15 +15 h;15 ry++)15 map[ry][rx]15 =15 05'·'; 15 } 15 } 15 } 15 02for15 (02let15 i15 =15 020;15 i15 <15 blocks.length;15 i++)15 { 15 02let15 distances15 =15 blocks 15 .map((b,15 idx)15 =>15 ({15 idx,15 dist:15 10Math.hypot(b.center.x15 -15 blocks[i].center.x,15 b.center.y15 -15 blocks[i].center.y)15 })) 15 .filter(d15 =>15 d.idx15 !==15 i) 15 .sort((a,15 b)15 =>15 a.dist15 -15 b.dist); 15 02let15 neighbors15 =15 distances.slice(020,15 022); 15 neighbors.forEach(n15 =>15 { 15 02let15 c115 =15 blocks[i].center; 15 02let15 c215 =15 blocks[n.idx].center; 15 02let15 currX15 =15 c1.x,15 currY15 =15 c1.y; 15  15 02if15 (10Math.random()15 <15 020.5)15 { 15 02while15 (currX15 !==15 c2.x)15 {15 map[currY][currX]15 =15 05'·';15 currX15 +=15 currX15 <15 c2.x15 ?15 02115 :15 -021;15 } 15 02while15 (currY15 !==15 c2.y)15 {15 map[currY][currX]15 =15 05'·';15 currY15 +=15 currY15 <15 c2.y15 ?15 02115 :15 -021;15 } 15 }15 02else15 { 15 02while15 (currY15 !==15 c2.y)15 {15 map[currY][currX]15 =15 05'·';15 currY15 +=15 currY15 <15 c2.y15 ?15 02115 :15 -021;15 } 15 02while15 (currX15 !==15 c2.x)15 {15 map[currY][currX]15 =15 05'·';15 currX15 +=15 currX15 <15 c2.x15 ?15 02115 :15 -021;15 } 15 } 15 }); 15 } 15 02for15 (02let15 k15 =15 020;15 k15 <15 023;15 k++)15 { 15 02let15 rx15 =15 10Math.floor(10Math.random()15 *15 (WIDTH15 -15 026))15 +15 023; 15 02let15 ry15 =15 10Math.floor(10Math.random()15 *15 (HEIGHT15 -15 026))15 +15 023; 15 02if15 (10Math.random()15 <15 020.5)15 { 15 02for15 (02let15 len15 =15 020;15 len15 <15 025;15 len++)15 {15 02if15 (rx15 +15 len15 <15 WIDTH15 -15 021)15 map[ry][rx15 +15 len]15 =15 05'·';15 } 15 }15 02else15 { 15 02for15 (02let15 len15 =15 020;15 len15 <15 025;15 len++)15 {15 02if15 (ry15 +15 len15 <15 HEIGHT15 -15 021)15 map[ry15 +15 len][rx]15 =15 05'·';15 } 15 } 15 } 15 player.x15 =15 blocks[020].center.x; 15 player.y15 =15 blocks[020].center.y; 15 02let15 sortedByDist15 =15 blocks.slice().sort((a,15 b)15 =>15 { 15 02let15 dA15 =15 10Math.hypot(a.center.x15 -15 player.x,15 a.center.y15 -15 player.y); 15 02let15 dB15 =15 10Math.hypot(b.center.x15 -15 player.x,15 b.center.y15 -15 player.y); 15 02return15 dB15 -15 dA; 15 }); 15 02let15 furthestBlock15 =15 sortedByDist[020]; 15 exitGate15 =15 {15 x:15 furthestBlock.center.x,15 y:15 furthestBlock.center.y15 }; 15 02let15 vendorCandidates15 =15 blocks.filter(b15 =>15 b15 !==15 blocks[020]15 &&15 b15 !==15 furthestBlock); 15 02if15 (vendorCandidates.length15 >15 020)15 { 15 02let15 vBlock15 =15 vendorCandidates[020]; 15 vendors.push({15 x:15 vBlock.center.x,15 y:15 vBlock.center.y,15 02char:15 05'¥'15 }); 15 }15 02else15 02if15 (blocks.length15 >15 021)15 { 15 vendors.push({15 x:15 blocks[021].x15 +15 021,15 y:15 blocks[021].y15 +15 021,15 02char:15 05'¥'15 }); 15 } 15 blocks.forEach((b)15 =>15 { 15 02let15 rand15 =15 10Math.random(); 15  15 02let15 isReserved15 =15 (x,15 y)15 =>15 (x15 ===15 player.x15 &&15 y15 ===15 player.y)15 ||15  15 (x15 ===15 exitGate.x15 &&15 y15 ===15 exitGate.y)15 ||15  15 vendors.some(v15 =>15 v.x15 ===15 x15 &&15 v.y15 ===15 y); 15 02let15 enemyX15 =15 b.x15 +15 021; 15 02let15 enemyY15 =15 b.y15 +15 021; 15 02if15 (!isReserved(enemyX,15 enemyY)15 &&15 rand15 >15 020.3)15 { 15 02let15 enemyType15 =15 rand15 <15 020.615 ?15 05'triad'15 :15 (rand15 <15 020.8515 ?15 05'ministry'15 :15 05'drone'); 15 enemies.push({ 15 x:15 enemyX,15 y:15 enemyY, 15 type:15 enemyType, 15 02char:15 enemyType15 ===15 05'triad'15 ?15 05'帮'15 :15 (enemyType15 ===15 05'ministry'15 ?15 05'官'15 :15 05'∆'), 15 hp:15 enemyType15 ===15 05'triad'15 ?15 021015 :15 (enemyType15 ===15 05'ministry'15 ?15 021615 :15 028), 15 atk:15 enemyType15 ===15 05'triad'15 ?15 02415 :15 (enemyType15 ===15 05'ministry'15 ?15 02715 :15 028), 15 range:15 enemyType15 ===15 05'triad'15 ?15 021.515 :15 (enemyType15 ===15 05'ministry'15 ?15 02415 :15 026), 15 bounty:15 enemyType15 ===15 05'triad'15 ?15 022515 :15 (enemyType15 ===15 05'ministry'15 ?15 024515 :15 0260), 15 stunned:15 020 15 }); 15 } 15 02let15 itemX15 =15 b.x15 +15 b.w15 -15 022; 15 02let15 itemY15 =15 b.y15 +15 b.h15 -15 022; 15 02if15 (!isReserved(itemX,15 itemY)15 &&15 10Math.random()15 >15 020.35)15 { 15 02let15 itemRand15 =15 10Math.random(); 15 02let15 type15 =15 itemRand15 <15 020.415 ?15 05'Sheng-Stim'15 :15 (itemRand15 <15 020.715 ?15 05'Synth-Baozi'15 :15 05'Mag-Rounds'); 15 02let15 02char15 =15 type15 ===15 05'Sheng-Stim'15 ?15 05'生'15 :15 (type15 ===15 05'Synth-Baozi'15 ?15 05'包'15 :15 05'⌖'); 15 items.push({15 x:15 itemX,15 y:15 itemY,15 type,15 02char15 }); 15 } 15 }); } 02function15 updateVision()15 { 15 02for15 (02let15 y15 =15 020;15 y15 <15 HEIGHT;15 y++)15 { 15 02for15 (02let15 x15 =15 020;15 x15 <15 WIDTH;15 x++)15 visibleMap[y][x]15 =15 02false; 15 } 15 02for15 (02let15 angle15 =15 020;15 angle15 <15 02360;15 angle15 +=15 025)15 { 15 02let15 rad15 =15 angle15 *15 (10Math.PI15 /15 02180); 15 02let15 dx15 =15 10Math.cos(rad); 15 02let15 dy15 =15 10Math.sin(rad); 15 02let15 cx15 =15 player.x15 +15 020.5; 15 02let15 cy15 =15 player.y15 +15 020.5; 15 02for15 (02let15 d15 =15 020;15 d15 <15 VISIBILITY_RADIUS;15 d++)15 { 15 02let15 tileX15 =15 10Math.floor(cx); 15 02let15 tileY15 =15 10Math.floor(cy); 15 02if15 (tileX15 >=15 02015 &&15 tileX15 <15 WIDTH15 &&15 tileY15 >=15 02015 &&15 tileY15 <15 HEIGHT)15 { 15 visibleMap[tileY][tileX]15 =15 02true; 15 exploredMap[tileY][tileX]15 =15 02true; 15 02if15 (map[tileY][tileX]15 ===15 05'▓')15 02break; 15 } 15 cx15 +=15 dx; 15 cy15 +=15 dy; 15 } 15 } } 02function15 handleInput(dx,15 dy)15 { 15 initAudio(); 15 02if15 (player.hp15 <=15 02015 ||15 inShop15 ||15 isAiming)15 02return; 15 02let15 newX15 =15 player.x15 +15 dx; 15 02let15 newY15 =15 player.y15 +15 dy; 15 02if15 (map[newY][newX]15 !==15 05'▓')15 { 15 02let15 enemyIdx15 =15 enemies.findIndex(e15 =>15 e.x15 ===15 newX15 &&15 e.y15 ===15 newY); 15 02let15 vendorIdx15 =15 vendors.findIndex(v15 =>15 v.x15 ===15 newX15 &&15 v.y15 ===15 newY); 15 02if15 (enemyIdx15 !==15 -021)15 { 15 attackEnemyMelee(enemyIdx); 15 }15 02else15 02if15 (vendorIdx15 !==15 -021)15 { 15 openShop(); 15 02return; 15 }15 02else15 { 15 player.x15 =15 newX; 15 player.y15 =15 newY; 15 playSfx(05'move'); 15 checkItems(); 15 checkExit(); 15 } 15 endTurn(); 15 } } 02function15 openShop()15 { 15 inShop15 =15 02true; 15 playSfx(05'buy'); 15 10document.getElementById(05'shop-modal').style.display15 =15 05'flex'; 15 log(05`05Verbindung mit Automat [¥] hergestellt.05`); } 02function15 closeShop()15 { 15 inShop15 =15 02false; 15 10document.getElementById(05'shop-modal').style.display15 =15 05'none'; 15 render(); } 02function15 buyShopItem(itemType,15 cost,15 humanityCost)15 { 15 initAudio(); 15 02if15 (player.creds15 <15 cost)15 { 15 log(05`05FEHLER: Zu wenig Yuan! (¥05${cost05}05)05`); 15 02return; 15 } 15 playSfx(05'buy'); 15 02if15 (humanityCost15 >15 020)15 { 15 player.creds15 -=15 cost; 15 player.humanity15 =15 10Math.max(020,15 player.humanity15 -15 humanityCost); 15 02if15 (itemType15 ===15 05'Xeno-Blade')15 player.atk15 +=15 023; 15 02if15 (itemType15 ===15 05'Subdermal-Weave')15 {15 player.maxHp15 +=15 0215;15 player.hp15 +=15 0215;15 } 15 02if15 (itemType15 ===15 05'Teleoplex-Chip')15 {15 player.maxRam15 +=15 022;15 player.ram15 +=15 022;15 } 15 log(05`05CYBERWARE: 05${itemType05}05. Menschlichkeit: 05${player.humanity05}05%.05`); 15  15 02if15 (player.humanity15 <=15 0230)15 { 15 log(05`05HYPERSTITION: Cyber-Dämon [魔]!05`); 15 } 15 }15 02else15 { 15 02if15 (itemType15 ===15 05'Mag-Rounds')15 { 15 player.creds15 -=15 cost; 15 player.ammo15 +=15 026; 15 log(05`05Magazin [⌖] erworben (+6 Ammo).05`); 15 }15 02else15 { 15 02if15 (player.inventory.length15 <15 MAX_INV)15 { 15 player.creds15 -=15 cost; 15 player.inventory.push(itemType); 15 log(05`05${itemType05}05 verstaut.05`); 15 }15 02else15 { 15 log(05`05KAUF ABGEBROCHEN: Inventar voll!05`); 15 } 15 } 15 } 15 render(); } 02function15 shootNearest()15 { 15 initAudio(); 15 02if15 (player.hp15 <=15 02015 ||15 inShop15 ||15 isAiming)15 02return; 15 02if15 (player.ammo15 <=15 020)15 { 15 log(05`05MAGAZIN LEER!05`); 15 02return; 15 } 15 02let15 targetIdx15 =15 -021; 15 02let15 minDistance15 =15 02999; 15 enemies.forEach((e,15 idx)15 =>15 { 15 02if15 (visibleMap[e.y][e.x])15 { 15 02let15 dist15 =15 10Math.hypot(e.x15 -15 player.x,15 e.y15 -15 player.y); 15 02if15 (dist15 <15 minDistance)15 { 15 minDistance15 =15 dist; 15 targetIdx15 =15 idx; 15 } 15 } 15 }); 15 02if15 (targetIdx15 !==15 -021)15 { 15 currentAimTargetIdx15 =15 targetIdx; 15 startAimMiniGame(); 15 }15 02else15 { 15 log(05`05Kein Ziel sichtbar.05`); 15 } } 02function15 startAimMiniGame()15 { 15 isAiming15 =15 02true; 15 aimPos15 =15 020; 15 aimDir15 =15 021; 15 aimSpeed15 =15 022.415 +15 10Math.random()15 *15 021.6; 15 02let15 zoneLeft15 =15 023015 +15 10Math.floor(10Math.random()15 *15 0225); 15 02let15 zoneWidth15 =15 0220; 15 02let15 targetZoneElem15 =15 10document.getElementById(05'aim-target-zone'); 15 targetZoneElem.style.left15 =15 zoneLeft15 +15 05'%'; 15 targetZoneElem.style.width15 =15 zoneWidth15 +15 05'%'; 15 targetZoneElem.dataset.min15 =15 zoneLeft; 15 targetZoneElem.dataset.max15 =15 zoneLeft15 +15 zoneWidth; 15 10document.getElementById(05'aim-modal').style.display15 =15 05'flex'; 15 updateAimLoop(); } 02function15 updateAimLoop()15 { 15 02if15 (!isAiming)15 02return; 15 aimPos15 +=15 aimSpeed15 *15 aimDir; 15 02if15 (aimPos15 >=15 02100)15 { 15 aimPos15 =15 02100; 15 aimDir15 =15 -021; 15 }15 02else15 02if15 (aimPos15 <=15 020)15 { 15 aimPos15 =15 020; 15 aimDir15 =15 021; 15 } 15 10document.getElementById(05'aim-cursor').style.left15 =15 aimPos15 +15 05'%'; 15 aimAnimFrame15 =15 requestAnimationFrame(updateAimLoop); } 02function15 resolveShot()15 { 15 02if15 (!isAiming)15 02return; 15 cancelAnimationFrame(aimAnimFrame); 15 isAiming15 =15 02false; 15 10document.getElementById(05'aim-modal').style.display15 =15 05'none'; 15 player.ammo--; 15 player.noise15 +=15 023; 15 02let15 targetZoneElem15 =15 10document.getElementById(05'aim-target-zone'); 15 02let15 min15 =15 10parseFloat(targetZoneElem.dataset.min); 15 02let15 max15 =15 10parseFloat(targetZoneElem.dataset.max); 15 02let15 center15 =15 min15 +15 (max15 -15 min)15 /15 022; 15 02let15 e15 =15 enemies[currentAimTargetIdx]; 15 02if15 (!e)15 { 15 endTurn(); 15 02return; 15 } 15 02if15 (aimPos15 >=15 min15 &&15 aimPos15 <=15 max)15 { 15 02let15 isCrit15 =15 10Math.abs(aimPos15 -15 center)15 <=15 023.5; 15 02let15 bonusAtk15 =15 player.humanity15 <=15 023015 ?15 02615 :15 020; 15 02let15 dmg15 =15 player.atk15 +15 bonusAtk15 +15 10Math.floor(10Math.random()15 *15 024); 15 02if15 (isCrit)15 dmg15 =15 10Math.floor(dmg15 *15 021.6)15 +15 024; 15 playSfx(05'shoot'); 15 02if15 (isCrit)15 { 15 log(05`05CRITICAL LOCK! [05${e.02char05}05] kritisch getroffen für 05${dmg05}05 DMG!05`); 15 }15 02else15 { 15 log(05`05TREFFER! Schuss auf [05${e.02char05}05] für 05${dmg05}05 DMG!05`); 15 } 15 e.hp15 -=15 dmg; 15 02if15 (e.hp15 <=15 020)15 { 15 player.creds15 +=15 e.bounty; 15 log(05`05[05${e.02char05}05] eliminiert! +¥05${e.bounty05}05.05`); 15 enemies.splice(currentAimTargetIdx,15 021); 15 } 15 }15 02else15 { 15 playSfx(05'hit'); 15 log(05`05FEHLSCHUSS! Zielverfehlung (05${10Math.round(aimPos)05}05%).05`); 15 } 15 endTurn(); } 02function15 hackEnemy()15 { 15 initAudio(); 15 02if15 (player.ram15 <15 02215 ||15 inShop15 ||15 isAiming)15 { 15 log(05`05RAM ZU NIEDRIG! (2 K-RAM nötig)05`); 15 02return; 15 } 15 02let15 targetIdx15 =15 -021; 15 02let15 minDistance15 =15 02999; 15 enemies.forEach((e,15 idx)15 =>15 { 15 02if15 (visibleMap[e.y][e.x]15 &&15 e.stunned15 ===15 020)15 { 15 02let15 dist15 =15 10Math.hypot(e.x15 -15 player.x,15 e.y15 -15 player.y); 15 02if15 (dist15 <15 minDistance)15 { 15 minDistance15 =15 dist; 15 targetIdx15 =15 idx; 15 } 15 } 15 }); 15 02if15 (targetIdx15 !==15 -021)15 { 15 playSfx(05'hack'); 15 player.ram15 -=15 022; 15 02let15 e15 =15 enemies[targetIdx]; 15 e.stunned15 =15 023; 15 log(05`05OVERCLOCK auf [05${e.02char05}05]! 3 Züge gelähmt.05`); 15 endTurn(); 15 }15 02else15 { 15 log(05`05Kein erfassbares Ziel.05`); 15 } } 02function15 attackEnemyMelee(idx)15 { 15 playSfx(05'hit'); 15 02let15 e15 =15 enemies[idx]; 15 02let15 bonusAtk15 =15 player.humanity15 <=15 023015 ?15 02515 :15 020; 15 02let15 dmg15 =15 10Math.floor(player.atk15 *15 020.7)15 +15 bonusAtk; 15 e.hp15 -=15 dmg; 15 log(05`05Nahkampf auf [05${e.02char05}05] für 05${dmg05}05 DMG.05`); 15 02if15 (e.hp15 <=15 020)15 { 15 player.creds15 +=15 e.bounty; 15 log(05`05[05${e.02char05}05] eliminiert! +¥05${e.bounty05}05.05`); 15 enemies.splice(idx,15 021); 15 } } 02function15 useItem(slot)15 { 15 initAudio(); 15 02if15 (player.hp15 <=15 02015 ||15 slot15 >=15 player.inventory.length15 ||15 inShop15 ||15 isAiming)15 02return; 15 playSfx(05'item'); 15 02let15 item15 =15 player.inventory[slot]; 15 02if15 (item15 ===15 05'Sheng-Stim')15 { 15 player.hp15 =15 10Math.min(player.maxHp,15 player.hp15 +15 0220); 15 log(05`05Sheng-Stim [生] (+20 HP).05`); 15 }15 02else15 02if15 (item15 ===15 05'Synth-Baozi')15 { 15 player.hunger15 =15 10Math.min(02100,15 player.hunger15 +15 0250); 15 player.hp15 =15 10Math.min(player.maxHp,15 player.hp15 +15 025); 15 log(05`05Synth-Baozi [包] (+50% Cal).05`); 15 } 15 player.inventory.splice(slot,15 021); 15 endTurn(); } 02function15 checkItems()15 { 15 02let15 idx15 =15 items.findIndex(i15 =>15 i.x15 ===15 player.x15 &&15 i.y15 ===15 player.y); 15 02if15 (idx15 !==15 -021)15 { 15 02let15 item15 =15 items[idx]; 15 playSfx(05'item'); 15 02if15 (item.type15 ===15 05'Mag-Rounds')15 { 15 player.ammo15 +=15 025; 15 log(05`05Magazin [⌖] aufgesammelt (+5).05`); 15 items.splice(idx,15 021); 15 }15 02else15 { 15 02if15 (player.inventory.length15 <15 MAX_INV)15 { 15 player.inventory.push(item.type); 15 log(05`05${item.type05}05 [05${item.02char05}05] aufgesammelt.05`); 15 items.splice(idx,15 021); 15 }15 02else15 { 15 log(05`05Inventar voll für 05${item.type05}05.05`); 15 } 15 } 15 } } 02function15 checkExit()15 { 15 02if15 (player.x15 ===15 exitGate.x15 &&15 player.y15 ===15 exitGate.y)15 { 15 playSfx(05'exit'); 15 player.level++; 15 log(05`05ACCELERATING... Betrete Zone S-005${player.level05}05 [门].05`); 15 generateCitySector(); 15 } } 02function15 endTurn()15 { 15 turnCounter++; 15 updateVision(); 15 02if15 (turnCounter15 %15 02315 ===15 02015 &&15 player.ram15 <15 player.maxRam)15 player.ram++; 15 02if15 (player.humanity15 <=15 023015 &&15 10Math.random()15 <15 020.25)15 { 15 02let15 psychosisEffect15 =15 10Math.random(); 15 02if15 (psychosisEffect15 <15 020.5)15 { 15 player.hp15 -=15 022; 15 playSfx(05'hit'); 15 log(05`05HYPERSTITION! (-2 HP).05`); 15 }15 02else15 { 15 player.noise15 +=15 025; 15 log(05`05TIME-LOOP-PULSE!05`); 15 } 15 } 15 02if15 (player.noise15 >15 020)15 player.noise--; 15 02if15 (turnCounter15 %15 02515 ===15 02015 &&15 player.hunger15 >15 020)15 player.hunger--; 15 02if15 (player.hunger15 <=15 020)15 { 15 player.hp--; 15 playSfx(05'hit'); 15 log(05`05METABOLISCHER KOLLAPS! -1 HP.05`); 15 } 15 enemies.forEach(e15 =>15 { 15 02if15 (e.stunned15 >15 020)15 { 15 e.stunned--; 15 02return; 15 } 15 02let15 isVisible15 =15 visibleMap[e.y][e.x]; 15 02let15 dist15 =15 10Math.hypot(e.x15 -15 player.x,15 e.y15 -15 player.y); 15 02if15 (isVisible15 ||15 dist15 <15 player.noise)15 { 15 02if15 (dist15 <=15 e.range)15 { 15 02let15 dmg15 =15 10Math.max(021,15 e.atk15 -15 10Math.floor(10Math.random()15 *15 022)); 15 player.hp15 -=15 dmg; 15 playSfx(05'hit'); 15 log(05`05[05${e.02char05}05] trifft dich für 05${dmg05}05 DMG!05`); 15 }15 02else15 { 15 02let15 dx15 =15 10Math.sign(player.x15 -15 e.x); 15 02let15 dy15 =15 10Math.sign(player.y15 -15 e.y); 15 02if15 (map[e.y15 +15 dy][e.x15 +15 dx]15 !==15 05'▓'15 &&15 !enemies.some(o15 =>15 o.x15 ===15 e.x15 +15 dx15 &&15 o.y15 ===15 e.y15 +15 dy))15 { 15 e.x15 +=15 dx; 15 e.y15 +=15 dy; 15 } 15 } 15 } 15 }); 15 02if15 (player.hp15 <=15 020)15 { 15 playSfx(05'gameover'); 15 log(05`05FLATLINE: Vom Kapital vollständig reabsorbiert. Game Over.05`); 15 } 15 render(); } 02function15 render()15 { 15 10document.getElementById(05'stat-level').innerText15 =15 05`05ZONE: S-005${player.level05}05`; 15 10document.getElementById(05'stat-hp').innerText15 =15 05`05INT: 05${player.hp05}05/05${player.maxHp05}05`; 15 10document.getElementById(05'stat-ram').innerText15 =15 05`05RAM: 05${player.ram05}05/05${player.maxRam05}05`; 15 10document.getElementById(05'stat-psyche').innerText15 =15 05`05HUM: 05${player.humanity05}05%05`; 15 10document.getElementById(05'stat-food').innerText15 =15 05`05CAL: 05${player.hunger05}05%05`; 15 10document.getElementById(05'stat-ammo').innerText15 =15 05`05AMMO: 05${player.ammo05}05`; 15 10document.getElementById(05'stat-atk').innerText15 =15 05`05GUN: 05${player.atk05}05`; 15 10document.getElementById(05'stat-cred').innerText15 =15 05`05¥05${player.creds05}05`; 15 02for15 (02let15 i15 =15 020;15 i15 <15 MAX_INV;15 i++)15 { 15 02let15 slot15 =15 10document.getElementById(05`05inv-05${i05}05`); 15 02let15 name15 =15 player.inventory[i]15 ||15 05'Leer'; 15 02let15 icon15 =15 name15 ===15 05'Sheng-Stim'15 ?15 05'[生]'15 :15 (name15 ===15 05'Synth-Baozi'15 ?15 05'[包]'15 :15 (name15 ===15 05'Mag-Rounds'15 ?15 05'[⌖]'15 :15 05'')); 15 slot.innerText15 =15 05`05[05${i+02105}05] 05${name05}05 05${icon05}05`; 15 } 15 02let15 htmlMap15 =15 05''; 15 02for15 (02let15 y15 =15 020;15 y15 <15 HEIGHT;15 y++)15 { 15 02for15 (02let15 x15 =15 020;15 x15 <15 WIDTH;15 x++)15 { 15 02let15 isVisible15 =15 visibleMap[y][x]; 15 02let15 isExplored15 =15 exploredMap[y][x]; 15 02if15 (!isExplored)15 { 15 htmlMap15 +=15 05'
'; 15 02continue; 15 } 15 02if15 (x15 ===15 player.x15 &&15 y15 ===15 player.y)15 { 15 02let15 pClass15 =15 player.humanity15 <=15 023015 ?15 05'p-psychotic'15 :15 05'p-player'; 15 02let15 pChar15 =15 player.humanity15 <=15 023015 ?15 05'魔'15 :15 05'@'; 15 htmlMap15 +=15 05`05
05${pChar05}05
05`; 15 }15 02else15 02if15 (x15 ===15 exitGate.x15 &&15 y15 ===15 exitGate.y)15 { 15 htmlMap15 +=15 05'
'; 15 }15 02else15 { 15 02let15 enemy15 =15 enemies.find(e15 =>15 e.x15 ===15 x15 &&15 e.y15 ===15 y); 15 02let15 item15 =15 items.find(i15 =>15 i.x15 ===15 x15 &&15 i.y15 ===15 y); 15 02let15 vendor15 =15 vendors.find(v15 =>15 v.x15 ===15 x15 &&15 v.y15 ===15 y); 15 02if15 (isVisible15 &&15 enemy)15 { 15 02let15 cssClass15 =15 enemy.type15 ===15 05'triad'15 ?15 05'e-triad'15 :15 (enemy.type15 ===15 05'ministry'15 ?15 05'e-ministry'15 :15 05'e-drone'); 15 02if15 (enemy.stunned15 >15 020)15 cssClass15 +=15 05' stunned'; 15 htmlMap15 +=15 05`05
05${enemy.02char05}05
05`; 15 }15 02else15 02if15 (isVisible15 &&15 vendor)15 { 15 htmlMap15 +=15 05`05
05${vendor.02char05}05
05`; 15 }15 02else15 02if15 (isVisible15 &&15 item)15 { 15 htmlMap15 +=15 05`05
05${item.02char05}05
05`; 15 }15 02else15 { 15 02let15 02char15 =15 map[y][x]; 15 02let15 cssClass15 =15 isVisible15 ?15 (02char15 ===15 05'▓'15 ?15 05'tile-wall'15 :15 05'tile-floor')15 :15 05'fog-explored'; 15 htmlMap15 +=15 05`05
05${02char05}05
05`; 15 } 15 } 15 } 15 } 15 10document.getElementById(05'map').innerHTML15 =15 htmlMap; } initGame();