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: SHENZHEN12title>
<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 }
12style>
12head>
<12body>
<12div 10id=05"game-wrapper">
15
<12div 10id=05"title-screen">
<12div 10class=05"title-bg-kanji">超迷信<12br>深圳12div>
<12div 10class=05"title-main-box">
<12div 10class=05"title-kanji-top">【 超 迷 信 】12div>
<12div 10class=05"title-glitch">HYPERSTITION12div>
<12div 10class=05"title-sub">SHENZHEN // 深圳12div>
12div>
<12div 10class=05"title-tags">
<12span 10class=05"tag-badge">加速主义12span>
<12span 10class=05"tag-badge">赛博朋克12span>
<12span 10class=05"tag-badge">特区 S-0112span>
12div>
<12div 10class=05"title-info">
> SYSTEM: NEURAL INTERFACE ONLINE<12br>
> LOCATION: SHENZHEN SEZ [深圳特区]<12br>
> WARNING: 人性流失 (HUMANITY DEPLETION)
12div>
<12button 10class=05"start-btn" 10onclick=05"startGame()">[ 网络接入 // ACCESS ]12button>
12div>
15
<12div 10id=05"ui-stats">
<12div 10id=05"stat-level">ZONE: S-0112div>
<12div 10id=05"stat-hp">INT: 3512div>
<12div 10id=05"stat-ram">RAM: 4/412div>
<12div 10id=05"stat-psyche">HUM: 100%12div>
<12div 10id=05"stat-food">CAL: 100%12div>
<12div 10id=05"stat-ammo">AMMO: 812div>
<12div 10id=05"stat-atk">GUN: 412div>
<12div 10id=05"stat-cred">¥5012div>
12div>
<12div 10id=05"game-container">
<12div 10id=05"map">12div>
15
<12div 10id=05"aim-modal" 10onclick=05"resolveShot()">
<12div 10id=05"aim-title">=== ZIELERFASSUNG // LOCK-ON ===12div>
<12div 10id=05"aim-bar-container">
<12div 10id=05"aim-target-zone">12div>
<12div 10id=05"aim-cursor">12div>
12div>
<12div 10id=05"aim-instruction">[F] / [SPACE] / TIPPE ZUM FEUERN12div>
12div>
<12div 10id=05"shop-modal">
<12div 10id=05"shop-title">=== SHENZHEN TAOBAO-LOOM [¥] ===12div>
<12div 10class=05"shop-items">
<12button 10class=05"shop-btn" 10onclick=05"buyShopItem('Sheng-Stim', 35, 0)"><12span>[1] Sheng-Stim (+20 Int)12span><12span>¥3512span>12button>
<12button 10class=05"shop-btn" 10onclick=05"buyShopItem('Synth-Baozi', 15, 0)"><12span>[2] Synth-Baozi (+50% Cal)12span><12span>¥1512span>12button>
<12button 10class=05"shop-btn" 10onclick=05"buyShopItem('Mag-Rounds', 25, 0)"><12span>[3] Kinetic-Mag (+6 Ammo)12span><12span>¥2512span>12button>
<12button 10class=05"shop-btn" 10onclick=05"buyShopItem('Xeno-Blade', 110, 25)"><12span>[4] Xeno-Blade (+3 ATK)12span><12span>¥110 (-25% H)12span>12button>
<12button 10class=05"shop-btn" 10onclick=05"buyShopItem('Subdermal-Weave', 110, 25)"><12span>[5] Subdermal (+15 HP)12span><12span>¥110 (-25% H)12span>12button>
<12button 10class=05"shop-btn" 10onclick=05"buyShopItem('Teleoplex-Chip', 110, 25)"><12span>[6] Teleoplex (+2 RAM)12span><12span>¥110 (-25% H)12span>12button>
12div>
<12div 10class=05"close-shop" 10onclick=05"closeShop()">TRENNEN [Ø]12div>
12div>
12div>
<12div 10id=05"action-panel">
<12div 10class=05"action-btn" 10onclick=05"shootNearest()">[F] KINETIK-SCHUSS12div>
<12div 10class=05"action-btn" 10onclick=05"hackEnemy()">[H] OVERLOAD12div>
12div>
<12div 10id=05"inventory-panel">
<12div 10class=05"inv-slot" 10onclick=05"useItem(0)" 10id=05"inv-0">[1] -12div>
<12div 10class=05"inv-slot" 10onclick=05"useItem(1)" 10id=05"inv-1">[2] -12div>
<12div 10class=05"inv-slot" 10onclick=05"useItem(2)" 10id=05"inv-2">[3] -12div>
<12div 10class=05"inv-slot" 10onclick=05"useItem(3)" 10id=05"inv-3">[4] -12div>
12div>
<12div 10id=05"log">12div>
<12div 10class=05"controls-wrapper">
<12div 10class=05"controls">
<12div>12div>
<12div 10class=05"btn" 10onclick=05"handleInput(0, -1)">▲12div>
<12div>12div>
<12div 10class=05"btn" 10onclick=05"handleInput(-1, 0)">◄12div>
<12div 10class=05"btn" 10onclick=05"handleInput(0, 0)">Wait12div>
<12div 10class=05"btn" 10onclick=05"handleInput(1, 0)">►12div>
<12div>12div>
<12div 10class=05"btn" 10onclick=05"handleInput(0, 1)">▼12div>
<12div>12div>
12div>
12div>
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'