improvements Cyst_Kid
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
<div id="start-screen" class="overlay">
|
||||
<div class="overlay-content">
|
||||
<h1 class="game-title">CYST-KID</h1>
|
||||
<p class="start-text">STARTTEXT FOLGT NOCH</p>
|
||||
<p class="start-text">Sammle Punkte, weiche den Schmerzen aus<br>und bringe die Medikamente rechtzeitig<br>zum Patienten!</p>
|
||||
<div class="controls-info">
|
||||
<p>Steuerung: Pfeiltasten / WASD</p>
|
||||
<p>Sprint: Shift</p>
|
||||
@@ -31,19 +31,23 @@
|
||||
<div id="game-over-screen" class="overlay" style="display:none">
|
||||
<div class="overlay-content">
|
||||
<h2 class="gameover-text">GAME OVER</h2>
|
||||
<p class="lose-text">Schade - verloren. Versuche es noch einmal.</p>
|
||||
<p id="go-score-text"></p>
|
||||
<button id="restartBtn" class="arcade-btn">NOCHMAL (Enter)</button>
|
||||
<p class="lose-text">Schade - verloren.</p>
|
||||
<p id="go-score-text" class="end-score-text"></p>
|
||||
<input type="text" id="go-playerName" placeholder="Dein Name" maxlength="20" class="name-input">
|
||||
<button id="go-submitScoreBtn" class="arcade-btn">EINTRAGEN</button>
|
||||
<div id="go-ranking-table-container" class="ranking-table-container"></div>
|
||||
<button id="restartBtn" class="arcade-btn" style="display:none">ZURÜCK ZUM START</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="win-screen" class="overlay" style="display:none">
|
||||
<div class="overlay-content">
|
||||
<h2 class="win-text">GEWONNEN!</h2>
|
||||
<p class="win-msg">Herzlichen Glückwunsch - du hast gewonnen.<br>Trage dich mit deinem Namen in das Ranking von Cyst-Kid ein.</p>
|
||||
<p class="win-msg">Herzlichen Glückwunsch - du hast gewonnen.</p>
|
||||
<p id="win-score-text" class="end-score-text"></p>
|
||||
<input type="text" id="playerName" placeholder="Dein Name" maxlength="20" class="name-input">
|
||||
<button id="submitScoreBtn" class="arcade-btn">EINTRAGEN</button>
|
||||
<div id="ranking-table-container"></div>
|
||||
<button id="playAgainBtn" class="arcade-btn" style="display:none">NOCHMAL SPIELEN</button>
|
||||
<div id="ranking-table-container" class="ranking-table-container"></div>
|
||||
<button id="playAgainBtn" class="arcade-btn" style="display:none">ZURÜCK ZUM START</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,11 +15,11 @@ const MAP = [
|
||||
[1,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,1,1,1,1,0,1,0,1,1,1,0,1],//5
|
||||
[1,0,0,0,0,0,1,0,0,0,0,1,0,1,1,0,1,0,0,0,0,1,0,0,0,0,0,1],//6
|
||||
[1,1,1,1,1,0,1,1,1,1,0,1,0,1,1,0,1,0,1,1,1,1,0,1,1,1,1,1],//7
|
||||
[2,2,2,2,1,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,1,2,2,2,2],//8
|
||||
[1,1,1,1,1,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,1,1,1,1,1],//8
|
||||
[1,1,1,1,1,0,1,2,1,1,1,3,3,4,4,3,3,1,1,1,2,1,0,1,1,1,1,1],//9
|
||||
[5,2,2,2,2,0,2,2,1,8,8,8,8,8,8,8,8,8,8,1,2,2,0,2,2,2,2,5],//10
|
||||
[1,1,1,1,1,0,1,2,1,8,8,8,8,8,8,8,8,8,8,1,2,1,0,1,1,1,1,1],//11
|
||||
[2,2,2,2,1,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1,2,1,0,1,2,2,2,2],//12
|
||||
[1,1,1,1,1,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1,2,1,0,1,1,1,1,1],//12
|
||||
[1,1,1,1,1,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,1,1,1,1,1],//13
|
||||
[1,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,1],//14
|
||||
[1,0,1,1,1,0,1,1,0,1,1,0,1,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1],//15
|
||||
@@ -33,6 +33,7 @@ const MAP = [
|
||||
|
||||
const PL0 = {c:14,r:16};
|
||||
const PH = [{c:12,r:10},{c:15,r:10},{c:12,r:11},{c:15,r:11}];
|
||||
const MED_SPAWNS = [{r:13,c:14}];
|
||||
|
||||
const TILE = 16;
|
||||
const HUD_TOP = 44;
|
||||
@@ -42,7 +43,29 @@ const CH = ROWS * TILE + HUD_TOP + HUD_BOT;
|
||||
const FPS = 60;
|
||||
const TICK = 1000 / FPS;
|
||||
const BASE_SPD = 2;
|
||||
const SUPER_SEC = 10;
|
||||
|
||||
// ---- Challenge timing ----
|
||||
const CHALL_FIRST_MIN = 7; // seconds before first challenge (min)
|
||||
const CHALL_FIRST_MAX = 13; // seconds before first challenge (max)
|
||||
const CHALL_NEXT_MIN = 8; // seconds between challenges (min)
|
||||
const CHALL_NEXT_MAX = 15; // seconds between challenges (max)
|
||||
const CHALL_DURATION = 10; // seconds player has to deliver the med
|
||||
|
||||
// ---- Player / Ghost ----
|
||||
const LIVES = 3; // starting lives
|
||||
const WIN_LEVEL = 3; // level at which the player wins
|
||||
const SPEED_MULT = 2; // sprint / super-mode speed multiplier
|
||||
const COLL_DIST = 0.8; // collision detection radius (in tiles)
|
||||
const GHOST_SPD_BASE = 1.2; // ghost base speed (tiles/frame)
|
||||
const GHOST_SPD_LEVEL = 0.2; // ghost speed increase per level
|
||||
const GHOST_SCARED_MULT = 0.55; // ghost speed while scared
|
||||
const GHOST_REL_INTERVAL = 80; // frames between ghost releases
|
||||
const GHOST_REL_OFFSET = 50; // frame offset for first ghost release
|
||||
|
||||
// ---- Scoring ----
|
||||
const SC_DOT = 1; // points per dot
|
||||
const SC_GHOST = 5; // points per scared ghost eaten
|
||||
const SC_CHALL = 25; // points for completing a challenge
|
||||
|
||||
const CO = {
|
||||
bg:'#87CEEB', wall:'#8B5CF6', we:'#A78BFA',
|
||||
@@ -71,45 +94,6 @@ function painOk(r,c){
|
||||
const t=tile(r,c);return t===0||t===2||t===5;
|
||||
}
|
||||
|
||||
//const COLS = 28;
|
||||
//const ROWS = 28;
|
||||
|
||||
//const PL0 = {c:14,r:23};
|
||||
//const PH = [{c:12,r:10},{c:15,r:10},{c:12,r:11},{c:15,r:11}];
|
||||
|
||||
|
||||
/* 0=dot 1=wall 2=empty 3=ghostWall 4=ghostDoor 5=tunnel 8=ghostInside */
|
||||
/*
|
||||
const MAP = [
|
||||
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],//0
|
||||
[1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1],//1
|
||||
[1,0,1,1,1,0,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,0,1,1,1,0,1],//2
|
||||
[1,0,1,1,1,0,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,0,1,1,1,0,1],//3
|
||||
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],//4
|
||||
[1,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,1,1,1,1,0,1,0,1,1,1,0,1],//5
|
||||
[1,0,0,0,0,0,1,0,0,0,0,1,0,1,1,0,1,0,0,0,0,1,0,0,0,0,0,1],//6
|
||||
[1,1,1,1,1,0,1,1,1,1,0,1,0,1,1,0,1,0,1,1,1,1,0,1,1,1,1,1],//7
|
||||
[2,2,2,2,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,2,2,2,2],//8
|
||||
[1,1,1,1,1,0,1,0,1,1,1,3,3,4,4,3,3,1,1,1,0,1,0,1,1,1,1,1],//9
|
||||
[5,2,2,2,2,0,0,0,1,8,8,8,8,8,8,8,8,8,8,1,0,0,0,2,2,2,2,5],//10
|
||||
[1,1,1,1,1,0,1,0,1,8,8,8,8,8,8,8,8,8,8,1,0,1,0,1,1,1,1,1],//11
|
||||
[2,2,2,2,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,2,2,2,2],//12
|
||||
[1,1,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1],//13
|
||||
[1,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,1],//14
|
||||
[1,0,1,1,1,0,1,1,0,1,1,0,1,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1],//15
|
||||
[1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1],//16
|
||||
[1,1,1,0,1,0,1,0,1,1,1,1,0,1,1,0,1,1,1,1,0,1,0,1,0,1,1,1],//17
|
||||
[1,0,0,0,0,0,1,0,0,0,0,1,0,1,1,0,1,0,0,0,0,1,0,0,0,0,0,1],//18
|
||||
[1,0,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,0,1,1,1,1,1,1,1,1,0,1],//19
|
||||
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],//20
|
||||
[1,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,1,1,1,1,0,1,0,1,1,1,0,1],//24
|
||||
[1,0,0,0,0,0,1,0,0,0,0,1,0,1,1,0,1,0,0,0,0,1,0,0,0,0,0,1],//25
|
||||
[1,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,0,1,1,1,1,1],//26
|
||||
[1,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,1],//27
|
||||
[1,0,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,0,1,1,1,1,1,1,1,1,0,1],//28
|
||||
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],//29
|
||||
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],//30
|
||||
];*/
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -12,9 +12,7 @@ constructor(){
|
||||
this.lv=1;this.lives=3;this.sc=0;this.lsc=0;
|
||||
this.lt=0;this.ld=0;this.tt=0;this.td=0;
|
||||
this.dots=[];this.nDots=0;this.eDots=0;
|
||||
this.pl=null;this.pains=[];this.cysts=[];this.bios=[];
|
||||
this.ateSt=false;this.ateBi=false;this.combD=0;this.combN=1;
|
||||
this.sup=false;this.supT=0;
|
||||
this.pl=null;this.pains=[];
|
||||
this.fx=[];this.fr=0;this.lastT=0;this.acc=0;
|
||||
// Input
|
||||
this.ks={up:false,down:false,left:false,right:false,shift:false};
|
||||
@@ -33,6 +31,7 @@ _rsz(){
|
||||
// ---------- INPUT ----------
|
||||
_inp(){
|
||||
window.addEventListener('keydown',e=>{
|
||||
if(e.target.tagName==='INPUT')return;
|
||||
let h=true;
|
||||
switch(e.code){
|
||||
case'ArrowUp':case'KeyW':this.ks.up=true;break;
|
||||
@@ -143,16 +142,19 @@ _dir(){
|
||||
_ui(){
|
||||
document.getElementById('startBtn').onclick=()=>{this.snd.init();this._start()};
|
||||
document.getElementById('nextLevelBtn').onclick=()=>this._ent();
|
||||
document.getElementById('restartBtn').onclick=()=>{this.sc=0;this._start()};
|
||||
document.getElementById('restartBtn').onclick=()=>{this.sc=0;this._ov('start')};
|
||||
document.getElementById('submitScoreBtn').onclick=()=>this._sub();
|
||||
document.getElementById('playAgainBtn').onclick=()=>this._ov('start');
|
||||
document.getElementById('playAgainBtn').onclick=()=>{this.sc=0;this._ov('start')};
|
||||
document.getElementById('go-submitScoreBtn').onclick=()=>this._subGO();
|
||||
document.getElementById('playerName').addEventListener('keydown',e=>{if(e.key==='Enter'){e.stopPropagation();this._sub()}});
|
||||
document.getElementById('go-playerName').addEventListener('keydown',e=>{if(e.key==='Enter'){e.stopPropagation();this._subGO()}});
|
||||
}
|
||||
|
||||
_esc(){if(this.state==='playing'){this.sc=0;this.lsc=0;this.state='start';this._ov('start')}}
|
||||
|
||||
_ent(){
|
||||
if(this.state==='lvlDone'){this.lv++;this._init();this.state='playing';this._ov(null)}
|
||||
else if(this.state==='over'){this.sc=0;this._start()}
|
||||
else if(this.state==='over'||this.state==='win'){this.sc=0;this._ov('start')}
|
||||
else if(this.state==='start'){this.snd.init();this._start()}
|
||||
}
|
||||
|
||||
@@ -168,10 +170,13 @@ _ov(n){
|
||||
// ---------- LIFECYCLE ----------
|
||||
_init(){
|
||||
this.dots=[];this.nDots=0;this.eDots=0;
|
||||
this.meds=MED_SPAWNS.map(sp=>({r:sp.r,c:sp.c,eaten:true,pulse:0,respT:0}));
|
||||
for(let r=0;r<ROWS;r++)for(let c=0;c<COLS;c++)if(MAP[r][c]===0){this.dots.push({r,c,e:false});this.nDots++}
|
||||
this.lsc=0;this.lt=0;this.ld=0;this.sup=false;this.supT=0;
|
||||
this.ateSt=false;this.ateBi=false;this.combD=0;this.combN=this.lv;
|
||||
this.fx=[];this.lives=3;this.qDir=null;
|
||||
this.lsc=0;this.lt=0;this.ld=0;
|
||||
this.fx=[];this.lives=LIVES;this.qDir=null;
|
||||
this.hasMed=false;this.patient=null;
|
||||
this.challActive=false;this.challTimer=0;this.challGhostIdx=-1;
|
||||
this.nextChallIn=(FPS*(CHALL_FIRST_MIN+Math.random()*(CHALL_FIRST_MAX-CHALL_FIRST_MIN)))|0;
|
||||
|
||||
this.pl={
|
||||
x:PL0.c*TILE, y:PL0.r*TILE+HUD_TOP,
|
||||
@@ -187,23 +192,16 @@ _init(){
|
||||
x:h.c*TILE, y:h.r*TILE+HUD_TOP,
|
||||
gc:h.c, gr:h.r,
|
||||
dir:'up', moving:false,
|
||||
spd:1.2+this.lv*0.2,
|
||||
strat:st[i], scared:false, eaten:false,
|
||||
spd:GHOST_SPD_BASE+this.lv*GHOST_SPD_LEVEL,
|
||||
strat:st[i], scared:false, eaten:i>=2,
|
||||
inHouse:true, leaving:false,
|
||||
relT:i*80+50,
|
||||
relT:i<2?i*GHOST_REL_INTERVAL+GHOST_REL_OFFSET:999999,
|
||||
dormant:i>=2,
|
||||
pulse:Math.random()*6.28
|
||||
}));
|
||||
|
||||
this.cysts=[];
|
||||
this._rp(this.lv,1,8).forEach(p=>this.cysts.push({r:p.r,c:p.c,e:false,v:true}));
|
||||
this.bios=[];
|
||||
}
|
||||
|
||||
_rp(n,mr,xr){
|
||||
const cs=[];for(let r=mr;r<=xr;r++)for(let c=1;c<COLS-1;c++)if(isPath(r,c))cs.push({r,c});
|
||||
for(let i=cs.length-1;i>0;i--){const j=0|Math.random()*(i+1);[cs[i],cs[j]]=[cs[j],cs[i]]}
|
||||
const o=[];for(const p of cs){if(o.length>=n)break;if(o.every(q=>Math.abs(q.r-p.r)+Math.abs(q.c-p.c)>=4))o.push(p)}return o;
|
||||
}
|
||||
|
||||
// -------- MAIN LOOP --------
|
||||
_loop(ts){
|
||||
@@ -219,15 +217,16 @@ _upd(){
|
||||
const liveDir=this._dir();
|
||||
if(liveDir) this.qDir=liveDir;
|
||||
const wantDir=this.qDir;
|
||||
const base=BASE_SPD+(this.lv-1)*0.2;
|
||||
const base=BASE_SPD+(this.lv-1)*GHOST_SPD_LEVEL;
|
||||
const gp=this._gp();
|
||||
const sprint=this.ks.shift||(gp.sprint||false);
|
||||
this.pl.spd=this.sup?base*2:(sprint?base*2:base);
|
||||
this.pl.spd=this.sup?base*SPEED_MULT:(sprint?base*SPEED_MULT:base);
|
||||
|
||||
this._movePl(wantDir);
|
||||
this._movePains();
|
||||
this._coll();
|
||||
if(this.sup){this.supT--;if(this.supT<=0)this._esup()}
|
||||
this._tickMeds();
|
||||
this._tickChall();
|
||||
this.fx=this.fx.filter(f=>{f.t--;return f.t>0});
|
||||
this._chkDone();
|
||||
}
|
||||
@@ -236,9 +235,8 @@ _upd(){
|
||||
_coll(){
|
||||
for(const p of this.pains){
|
||||
if(p.eaten||p.inHouse||p.leaving)continue;
|
||||
if(Math.abs(this.pl.x-p.x)+Math.abs(this.pl.y-p.y)<TILE*.8){
|
||||
if(this.sup&&p.scared){p.eaten=true;this.lsc+=5;this.snd.bite();this.fx.push({x:p.x+TILE/2,y:p.y+TILE/2,co:'#00ff88',t:20,mx:20,tp:'burst'})}
|
||||
else if(!this.sup){this._die();return}
|
||||
if(Math.abs(this.pl.x-p.x)+Math.abs(this.pl.y-p.y)<TILE*COLL_DIST){
|
||||
this._die();return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,18 +244,77 @@ _coll(){
|
||||
_die(){
|
||||
this.lives--;this.snd.boom();
|
||||
this.fx.push({x:this.pl.x+TILE/2,y:this.pl.y+TILE/2,co:'#FF4444',t:40,mx:40,tp:'exp'});
|
||||
if(this.lives<=0){this.lsc=0;this.state='over';this.snd.over();setTimeout(()=>this._ov('over'),600)}
|
||||
if(this.lives<=0){this.sc+=this.lsc;this.lsc=0;this.state='over';this.snd.over();setTimeout(()=>this._ov('over'),600)}
|
||||
else{
|
||||
this.pl.x=PL0.c*TILE;this.pl.y=PL0.r*TILE+HUD_TOP;this.pl.gc=PL0.c;this.pl.gr=PL0.r;this.pl.dir='right';this.pl.moving=false;
|
||||
this.qDir=null;this.swD=null;this.ks.up=this.ks.down=this.ks.left=this.ks.right=false;
|
||||
PH.forEach((h,i)=>{const p=this.pains[i];p.x=h.c*TILE;p.y=h.r*TILE+HUD_TOP;p.gc=h.c;p.gr=h.r;p.inHouse=true;p.leaving=false;p.eaten=false;p.relT=i*90+60;p.moving=false});
|
||||
PH.forEach((h,i)=>{const p=this.pains[i];
|
||||
if(p.dormant){p.inHouse=true;p.leaving=false;p.eaten=true;p.relT=999999;p.moving=false;return}
|
||||
p.x=h.c*TILE;p.y=h.r*TILE+HUD_TOP;p.gc=h.c;p.gr=h.r;p.inHouse=true;p.leaving=false;p.eaten=false;p.relT=i*GHOST_REL_INTERVAL+GHOST_REL_OFFSET;p.moving=false});
|
||||
this._endChall();
|
||||
}
|
||||
}
|
||||
|
||||
_tickMeds(){
|
||||
for(const m of this.meds){
|
||||
if(m.eaten){if(m.respT>0&&--m.respT<=0){m.eaten=false;m.pulse=0}}
|
||||
else{m.pulse+=0.08}
|
||||
}
|
||||
}
|
||||
|
||||
_tickChall(){
|
||||
if(!this.challActive){if(--this.nextChallIn<=0)this._startChall()}
|
||||
else{if(--this.challTimer<=0)this._failChall()}
|
||||
}
|
||||
|
||||
_startChall(){
|
||||
if(!MED_SPAWNS||!MED_SPAWNS.length)return;
|
||||
const gi=this.pains.findIndex(p=>p.dormant);
|
||||
if(gi<0)return;
|
||||
// pick patient position, far from player
|
||||
const cells=[];
|
||||
for(let r=1;r<ROWS-1;r++)for(let c=1;c<COLS-1;c++)
|
||||
if(isPath(r,c)&&Math.abs(r-this.pl.gr)+Math.abs(c-this.pl.gc)>5)cells.push({r,c});
|
||||
if(!cells.length)return;
|
||||
const pc=cells[0|Math.random()*cells.length];
|
||||
// activate a random med spawn
|
||||
const avail=this.meds.filter(m=>m.eaten);
|
||||
if(!avail.length)return;
|
||||
const med=avail[0|Math.random()*avail.length];
|
||||
med.eaten=false;med.pulse=0;
|
||||
// activate ghost
|
||||
const g=this.pains[gi];
|
||||
g.dormant=false;g.inHouse=true;g.leaving=false;g.eaten=false;
|
||||
g.relT=FPS*CHALL_DURATION;
|
||||
this.patient={r:pc.r,c:pc.c};
|
||||
this.challActive=true;this.challTimer=FPS*CHALL_DURATION;this.challGhostIdx=gi;
|
||||
}
|
||||
|
||||
_succChall(){
|
||||
const g=this.pains[this.challGhostIdx];
|
||||
this.fx.push({x:g.x+TILE/2,y:g.y+TILE/2,co:'#00ff88',t:40,mx:40,tp:'burst'});
|
||||
g.eaten=true;g.dormant=true;g.inHouse=true;g.leaving=false;g.relT=999999;
|
||||
this.lsc+=SC_CHALL;this.snd.laser();
|
||||
this._endChall();
|
||||
}
|
||||
|
||||
_failChall(){
|
||||
if(this.challGhostIdx>=0)this.pains[this.challGhostIdx].relT=0;
|
||||
this._endChall();
|
||||
}
|
||||
|
||||
_endChall(){
|
||||
this.meds.filter(m=>!m.eaten).forEach(m=>{m.eaten=true;});
|
||||
this.patient=null;
|
||||
this.challActive=false;this.challTimer=0;this.challGhostIdx=-1;
|
||||
this.hasMed=false;
|
||||
this.nextChallIn=(FPS*(CHALL_NEXT_MIN+Math.random()*(CHALL_NEXT_MAX-CHALL_NEXT_MIN)))|0;
|
||||
}
|
||||
|
||||
_chkDone(){
|
||||
if(this.eDots>=this.nDots&&this.cysts.every(c=>c.e)&&this.combD>=this.combN){
|
||||
this.sc+=this.lsc;this.tt+=this.lt;this.td+=this.ld;this.snd.lvlUp();
|
||||
if(this.lv>=3){this.state='win';setTimeout(()=>this._ov('win'),800)}
|
||||
if(this.eDots>=this.nDots){
|
||||
this.sc+=this.lsc;this.lsc=0;this.tt+=this.lt;this.td+=this.ld;this.snd.lvlUp();
|
||||
if(this.lv>=WIN_LEVEL){this.state='win';setTimeout(()=>this._ov('win'),800)}
|
||||
else{this.state='lvlDone';setTimeout(()=>this._ov('lvlDone'),800)}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,14 +67,12 @@ _movePl(wantDir){
|
||||
p.gc = COLS-1; p.x = p.gc*TILE;
|
||||
p.gr = T_ROW; p.y = T_ROW*TILE+HUD_TOP;
|
||||
p.moving = false;
|
||||
this._onTun();
|
||||
return;
|
||||
}
|
||||
if(p.gc >= COLS){
|
||||
p.gc = 0; p.x = 0;
|
||||
p.gr = T_ROW; p.y = T_ROW*TILE+HUD_TOP;
|
||||
p.moving = false;
|
||||
this._onTun();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -113,42 +111,18 @@ _movePl(wantDir){
|
||||
}
|
||||
|
||||
// Eat dots
|
||||
for(const d of this.dots)if(!d.e&&d.r===p.gr&&d.c===p.gc){d.e=true;this.eDots++;this.lsc++;if(this.fr%4===0)this.snd.bite()}
|
||||
// Eat cystine
|
||||
for(const cs of this.cysts)if(cs.v&&!cs.e&&cs.r===p.gr&&cs.c===p.gc){
|
||||
cs.e=true;cs.v=false;this.ateSt=true;this.lsc+=10;this.snd.laser();
|
||||
this.fx.push({x:p.x+TILE/2,y:p.y+TILE/2,co:'#FFD700',t:30,mx:30,tp:'glow'});
|
||||
this.cysts.forEach(x=>{if(!x.e)x.v=false});this._sBio();
|
||||
}
|
||||
// Eat bio
|
||||
for(let i=this.bios.length-1;i>=0;i--){const b=this.bios[i];if(b.r===p.gr&&b.c===p.gc){
|
||||
this.bios.splice(i,1);this.ateBi=true;this.lsc+=10;this.snd.laser();
|
||||
this.fx.push({x:p.x+TILE/2,y:p.y+TILE/2,co:'#FFD700',t:30,mx:30,tp:'glow'});
|
||||
}}
|
||||
},
|
||||
|
||||
_onTun(){
|
||||
if(this.ateSt&&this.ateBi&&!this.sup){
|
||||
this._sSup();this.ateSt=false;this.ateBi=false;this.combD++;this.lsc+=20;
|
||||
for(const d of this.dots)if(!d.e&&d.r===p.gr&&d.c===p.gc){d.e=true;this.eDots++;this.lsc+=SC_DOT;if(this.fr%4===0)this.snd.bite()}
|
||||
// Pick up med
|
||||
if(!this.hasMed){for(const m of this.meds){if(!m.eaten&&m.r===p.gr&&m.c===p.gc){
|
||||
m.eaten=true;this.hasMed=true;this.snd.laser();
|
||||
this.fx.push({x:p.x+TILE/2,y:p.y+TILE/2,co:'#EC4899',t:30,mx:30,tp:'glow'});
|
||||
}}}
|
||||
// Deliver med to patient
|
||||
if(this.hasMed&&this.patient&&p.gr===this.patient.r&&p.gc===this.patient.c){
|
||||
this._succChall();
|
||||
}
|
||||
},
|
||||
|
||||
_sBio(){
|
||||
const cs=[];for(let r=1;r<ROWS-1;r++)for(let c=1;c<COLS-1;c++)if(isPath(r,c)&&Math.abs(r-this.pl.gr)+Math.abs(c-this.pl.gc)>5)cs.push({r,c});
|
||||
if(cs.length)this.bios.push(cs[0|Math.random()*cs.length]);
|
||||
},
|
||||
|
||||
_sSup(){
|
||||
this.sup=true;this.supT=SUPER_SEC*FPS;this.snd.laser();
|
||||
this.pains.forEach(p=>p.scared=true);
|
||||
this.fx.push({x:this.pl.x+TILE/2,y:this.pl.y+TILE/2,co:CO.sup,t:40,mx:40,tp:'glow'});
|
||||
},
|
||||
|
||||
_esup(){
|
||||
this.sup=false;
|
||||
PH.forEach((h,i)=>{const p=this.pains[i];p.scared=false;p.eaten=false;p.inHouse=true;p.leaving=false;p.x=h.c*TILE;p.y=h.r*TILE+HUD_TOP;p.gc=h.c;p.gr=h.r;p.relT=i*60+30;p.moving=false});
|
||||
if(this.combD<this.combN){const rem=this.cysts.filter(c=>!c.e);const pos=this._rp(rem.length,1,20);rem.forEach((cs,i)=>{if(pos[i]){cs.r=pos[i].r;cs.c=pos[i].c;cs.v=true}})}
|
||||
},
|
||||
|
||||
// -------- PAIN MOVEMENT --------
|
||||
_movePains(){
|
||||
@@ -179,7 +153,7 @@ _movePains(){
|
||||
}
|
||||
|
||||
// Normal movement using the entity system
|
||||
const spd=p.scared?p.spd*.55:p.spd;
|
||||
const spd=p.scared?p.spd*GHOST_SCARED_MULT:p.spd;
|
||||
|
||||
this._moveEntity(p, spd, (ent)=>{
|
||||
const valid=DIRS.filter(d=>{
|
||||
|
||||
@@ -8,7 +8,14 @@ _draw(){
|
||||
c.fillStyle=CO.bg;c.fillRect(0,0,CW,CH);
|
||||
c.fillStyle=CO.hud;c.fillRect(0,0,CW,HUD_TOP);c.fillRect(0,CH-HUD_BOT,CW,HUD_BOT);
|
||||
if(this.state==='start'){this._hud(c);return}
|
||||
this._maze(c);this._tLbl(c);this._dDots(c);this._dCyst(c);this._dBio(c);this._dPain(c);this._dPl(c);this._dFx(c);this._hud(c);
|
||||
this._maze(c);this._dDots(c);this._dMed(c);this._dPatient(c);this._dPain(c);this._dPl(c);this._dFx(c);
|
||||
if(this.challActive){
|
||||
const pct=this.challTimer/(FPS*CHALL_DURATION),bw=CW-16,bx=8,by=HUD_TOP-5;
|
||||
c.fillStyle='rgba(0,0,0,0.5)';c.fillRect(bx,by,bw,4);
|
||||
c.fillStyle=pct>0.5?'#22c55e':pct>0.25?'#f59e0b':'#ef4444';
|
||||
c.fillRect(bx,by,bw*pct,4);
|
||||
}
|
||||
this._hud(c);
|
||||
},
|
||||
|
||||
_maze(c){
|
||||
@@ -28,29 +35,64 @@ _maze(c){
|
||||
}
|
||||
},
|
||||
|
||||
_tLbl(c){
|
||||
const ty=T_ROW*TILE+HUD_TOP,h=TILE;c.save();
|
||||
c.fillStyle='#7C3AED';c.beginPath();c.moveTo(2,ty+h/2);c.lineTo(18,ty-4);c.lineTo(18,ty+h+4);c.closePath();c.fill();
|
||||
c.fillRect(18,ty-2,72,h+4);c.fillStyle='#FFF';c.font='bold 6px "Press Start 2P",monospace';c.textAlign='center';c.textBaseline='middle';
|
||||
c.fillText('ANALYSE',54,ty+h/2-3);c.fillText('ZENTRUM',54,ty+h/2+5);
|
||||
c.fillStyle='#7C3AED';c.beginPath();c.moveTo(CW-2,ty+h/2);c.lineTo(CW-18,ty-4);c.lineTo(CW-18,ty+h+4);c.closePath();c.fill();
|
||||
c.fillRect(CW-90,ty-2,72,h+4);c.fillStyle='#FFF';c.fillText('ANALYSE',CW-54,ty+h/2-3);c.fillText('ZENTRUM',CW-54,ty+h/2+5);c.restore();
|
||||
|
||||
|
||||
_dPatient(c){
|
||||
if(!this.patient)return;
|
||||
const x=this.patient.c*TILE+TILE/2,y=this.patient.r*TILE+HUD_TOP+TILE/2;
|
||||
c.save();c.translate(x,y);
|
||||
// pulsing glow
|
||||
c.globalAlpha=0.25+0.2*Math.abs(Math.sin(this.fr*0.08));
|
||||
c.fillStyle='#EC4899';c.beginPath();c.arc(0,0,10,0,Math.PI*2);c.fill();
|
||||
c.globalAlpha=1;
|
||||
const sc=0.9+0.08*Math.sin(this.fr*0.1);c.scale(sc,sc);
|
||||
// pain aura
|
||||
c.strokeStyle='#EF4444';c.lineWidth=0.8;
|
||||
for(let i=0;i<6;i++){const a=i*Math.PI/3;c.beginPath();c.moveTo(Math.cos(a)*5,Math.sin(a)*5);c.lineTo(Math.cos(a)*9,Math.sin(a)*9);c.stroke()}
|
||||
// head
|
||||
c.fillStyle='#FCD9B6';c.beginPath();c.arc(0,-4,3,0,Math.PI*2);c.fill();
|
||||
// torso
|
||||
c.fillStyle='#93C5FD';c.fillRect(-2,-1,5,6);
|
||||
// legs
|
||||
c.fillStyle='#FCD9B6';c.fillRect(-3,5,2,4);c.fillRect(1,5,2,4);
|
||||
// arms raised
|
||||
c.strokeStyle='#FCD9B6';c.lineWidth=1.5;
|
||||
c.beginPath();c.moveTo(-2,1);c.lineTo(-8,-3);c.stroke();
|
||||
c.beginPath();c.moveTo(3,1);c.lineTo(9,-3);c.stroke();
|
||||
// sad eyebrows
|
||||
c.strokeStyle='#555';c.lineWidth=0.8;
|
||||
c.beginPath();c.moveTo(-2,-6);c.lineTo(-1,-5);c.stroke();
|
||||
c.beginPath();c.moveTo(2,-6);c.lineTo(1,-5);c.stroke();
|
||||
// eyes
|
||||
c.fillStyle='#333';c.fillRect(-2,-5,1,1);c.fillRect(1,-5,1,1);
|
||||
// frown
|
||||
c.strokeStyle='#8B0000';c.lineWidth=0.8;
|
||||
c.beginPath();c.arc(0,-2,1.5,0,Math.PI,true);c.stroke();
|
||||
c.restore();
|
||||
},
|
||||
|
||||
_dMed(c){
|
||||
for(const m of this.meds){
|
||||
if(m.eaten)continue;
|
||||
const x=m.c*TILE+TILE/2,y=m.r*TILE+HUD_TOP+TILE/2;
|
||||
const sc=1+0.06*Math.sin(m.pulse);
|
||||
c.save();c.translate(x,y);c.scale(sc,sc);
|
||||
const pw=14,ph=7,r=ph/2;
|
||||
// left (red) half
|
||||
c.fillStyle='#EF4444';
|
||||
c.beginPath();c.arc(-pw/2+r,0,r,Math.PI/2,Math.PI*3/2);c.lineTo(0,-r);c.lineTo(0,r);c.closePath();c.fill();
|
||||
// right (white) half
|
||||
c.fillStyle='#FFF';
|
||||
c.beginPath();c.arc(pw/2-r,0,r,-Math.PI/2,Math.PI/2);c.lineTo(0,r);c.lineTo(0,-r);c.closePath();c.fill();
|
||||
// outline
|
||||
c.strokeStyle='rgba(0,0,0,0.4)';c.lineWidth=0.5;
|
||||
c.beginPath();c.arc(-pw/2+r,0,r,Math.PI/2,Math.PI*3/2);c.arc(pw/2-r,0,r,-Math.PI/2,Math.PI/2);c.closePath();c.stroke();
|
||||
c.restore();
|
||||
}
|
||||
},
|
||||
|
||||
_dDots(c){c.fillStyle=CO.dot;for(const d of this.dots)if(!d.e)c.fillRect(d.c*TILE+TILE/2-2,d.r*TILE+HUD_TOP+TILE/2-2,4,4)},
|
||||
|
||||
_dCyst(c){
|
||||
for(const cs of this.cysts){if(!cs.v||cs.e)continue;const x=cs.c*TILE+TILE/2,y=cs.r*TILE+HUD_TOP+TILE/2,sz=TILE*.45;
|
||||
c.fillStyle=CO.cys;c.strokeStyle=CO.cysE;c.lineWidth=1.5;c.beginPath();
|
||||
for(let i=0;i<6;i++){const a=i*Math.PI/3-Math.PI/6,px=x+Math.cos(a)*sz,py=y+Math.sin(a)*sz;i===0?c.moveTo(px,py):c.lineTo(px,py)}
|
||||
c.closePath();c.fill();c.stroke();const sh=.3+.3*Math.sin(this.fr*.1);c.fillStyle=`rgba(255,255,200,${sh})`;c.fillRect(x-2,y-2,4,4)}
|
||||
},
|
||||
|
||||
_dBio(c){
|
||||
for(const b of this.bios){const x=b.c*TILE+TILE/2,y=b.r*TILE+HUD_TOP+TILE/2,s=TILE*.38;
|
||||
c.fillStyle=CO.bag;c.beginPath();c.moveTo(x-s,y+s);c.lineTo(x-s*.7,y-s*.5);c.lineTo(x-s*.3,y-s*.9);c.lineTo(x+s*.3,y-s*.9);c.lineTo(x+s*.7,y-s*.5);c.lineTo(x+s,y+s);c.closePath();c.fill();
|
||||
c.fillStyle=CO.bagS;c.font=`${TILE*.5}px Arial`;c.textAlign='center';c.textBaseline='middle';c.fillText('☣',x,y+1)}
|
||||
},
|
||||
|
||||
_dPain(c){
|
||||
for(const p of this.pains){if(p.eaten)continue;const x=p.x+TILE/2,y=p.y+TILE/2,pu=1+.06*Math.sin(p.pulse),r=TILE*.44*pu;
|
||||
@@ -64,11 +106,18 @@ _dPl(c){
|
||||
const p=this.pl,x=p.x+TILE/2,y=p.y+TILE/2,r=TILE*.44;
|
||||
c.save();c.translate(x,y);const dir=p.moving?p.dir:p.lastFace;
|
||||
c.rotate({right:0,down:Math.PI/2,left:Math.PI,up:-Math.PI/2}[dir]||0);
|
||||
if(this.sup){const g=.3+.2*Math.sin(this.fr*.2);c.shadowColor=CO.sup;c.shadowBlur=14;c.fillStyle=`rgba(0,221,255,${g})`;c.beginPath();c.arc(0,0,r+5,0,Math.PI*2);c.fill();c.shadowBlur=0}
|
||||
const mo=p.mouth*.45;c.fillStyle=this.sup?'#FF9999':CO.kid;c.beginPath();c.arc(0,0,r,mo,Math.PI*2-mo,false);c.lineTo(0,0);c.closePath();c.fill();
|
||||
const mo=p.mouth*.45;c.fillStyle=CO.kid;c.beginPath();c.arc(0,0,r,mo,Math.PI*2-mo,false);c.lineTo(0,0);c.closePath();c.fill();
|
||||
const ey=dir==='left'?r*.4:-r*.4;
|
||||
c.fillStyle=CO.kE;c.beginPath();c.arc(-r*.1,ey,r*.18,0,Math.PI*2);c.fill();
|
||||
c.fillStyle='#000';c.beginPath();c.arc(-r*.05,ey,r*.08,0,Math.PI*2);c.fill();c.restore();
|
||||
c.fillStyle='#000';c.beginPath();c.arc(-r*.05,ey,r*.08,0,Math.PI*2);c.fill();
|
||||
c.restore();
|
||||
if(this.hasMed){
|
||||
c.save();c.translate(x,y-r-6);
|
||||
const pw=7,ph=4,pr=ph/2;
|
||||
c.fillStyle='#EF4444';c.beginPath();c.arc(-pw/2+pr,0,pr,Math.PI/2,Math.PI*3/2);c.lineTo(0,-pr);c.lineTo(0,pr);c.closePath();c.fill();
|
||||
c.fillStyle='#FFF';c.beginPath();c.arc(pw/2-pr,0,pr,-Math.PI/2,Math.PI/2);c.lineTo(0,pr);c.lineTo(0,-pr);c.closePath();c.fill();
|
||||
c.restore();
|
||||
}
|
||||
},
|
||||
|
||||
_dFx(c){
|
||||
@@ -81,12 +130,10 @@ _dFx(c){
|
||||
_hud(c){
|
||||
c.font='10px "Press Start 2P",monospace';c.textBaseline='middle';const ty=HUD_TOP/2;
|
||||
c.fillStyle=CO.hudT;c.textAlign='left';c.fillText('PUNKTE: '+(this.lsc+this.sc),8,ty);
|
||||
c.textAlign='center';c.fillText('LEVEL '+this.lv,CW/2,ty-6);
|
||||
if(this.sup){c.fillStyle=CO.sup;c.fillText('SUPER: '+Math.ceil(this.supT/FPS)+'s',CW/2,ty+8)}
|
||||
c.textAlign='center';c.fillText('LEVEL '+this.lv,CW/2,ty);
|
||||
const sec=0|this.lt/FPS;c.fillStyle=CO.hudT;c.textAlign='right';c.fillText('ZEIT: '+(0|sec/60)+':'+String(sec%60).padStart(2,'0'),CW-8,ty);
|
||||
const by=CH-HUD_BOT/2;
|
||||
for(let i=0;i<this.lives;i++){const lx=16+i*26;c.fillStyle=CO.kid;c.beginPath();c.arc(lx,by,8,.3,Math.PI*2-.3);c.lineTo(lx,by);c.closePath();c.fill();c.fillStyle='#FFF';c.beginPath();c.arc(lx-1,by-3,2.5,0,Math.PI*2);c.fill();c.fillStyle='#000';c.beginPath();c.arc(lx,by-3,1,0,Math.PI*2);c.fill()}
|
||||
c.fillStyle=CO.bag;c.textAlign='right';c.font='9px "Press Start 2P",monospace';c.fillText('\u2623 x'+Math.max(0,this.combN-this.combD),CW-10,by);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -5,7 +5,25 @@ Object.assign(Game.prototype, {
|
||||
|
||||
_showGameOver(){
|
||||
document.getElementById('go-score-text').textContent='Punkte: '+this.sc;
|
||||
document.getElementById('go-submitScoreBtn').style.display='';
|
||||
document.getElementById('go-playerName').style.display='';
|
||||
document.getElementById('go-ranking-table-container').innerHTML='';
|
||||
document.getElementById('restartBtn').style.display='none';
|
||||
document.getElementById('game-over-screen').style.display='flex';
|
||||
document.getElementById('go-playerName').value='';
|
||||
document.getElementById('go-playerName').focus();
|
||||
},
|
||||
|
||||
_subGO(){
|
||||
const nm=document.getElementById('go-playerName').value.trim()||'Anonym';
|
||||
this.rank.push({n:nm,s:this.sc,t:-1,d:-1});
|
||||
this.rank.sort((a,b)=>{if(b.s!==a.s)return b.s-a.s;if(a.t!==b.t)return a.t-b.t;return a.d-b.d});
|
||||
this.rank=this.rank.slice(0,10);localStorage.setItem('ckr3',JSON.stringify(this.rank));
|
||||
document.getElementById('go-ranking-table-container').innerHTML=this._rankingHTML();
|
||||
document.getElementById('go-submitScoreBtn').style.display='none';
|
||||
document.getElementById('go-playerName').style.display='none';
|
||||
document.getElementById('go-playerName').blur();
|
||||
document.getElementById('restartBtn').style.display='inline-block';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
Object.assign(Game.prototype, {
|
||||
|
||||
_showLvlDone(){
|
||||
document.getElementById('level-score-text').textContent='Punkte: '+(this.sc+this.lsc);
|
||||
document.getElementById('level-score-text').textContent='Punkte: '+this.sc;
|
||||
document.getElementById('level-complete-screen').style.display='flex';
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ _start(){
|
||||
},
|
||||
|
||||
_showStart(){
|
||||
this.state='start';
|
||||
document.getElementById('start-screen').style.display='flex';
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
Object.assign(Game.prototype, {
|
||||
|
||||
_showWin(){
|
||||
document.getElementById('win-score-text').textContent='Punkte: '+this.sc;
|
||||
document.getElementById('submitScoreBtn').style.display='';
|
||||
document.getElementById('playerName').style.display='';
|
||||
document.getElementById('playAgainBtn').style.display='none';
|
||||
document.getElementById('ranking-table-container').innerHTML='';
|
||||
document.getElementById('win-screen').style.display='flex';
|
||||
document.getElementById('playerName').value='';
|
||||
document.getElementById('playerName').focus();
|
||||
},
|
||||
|
||||
@@ -17,12 +19,20 @@ _sub(){
|
||||
this.rank.push({n:nm,s:this.sc,t:this.tt,d:Math.round(this.td)});
|
||||
this.rank.sort((a,b)=>{if(b.s!==a.s)return b.s-a.s;if(a.t!==b.t)return a.t-b.t;return a.d-b.d});
|
||||
this.rank=this.rank.slice(0,10);localStorage.setItem('ckr3',JSON.stringify(this.rank));
|
||||
let h='<table><tr><th>#</th><th>Name</th><th>Punkte</th><th>Zeit</th></tr>';
|
||||
this.rank.forEach((r,i)=>{const m=Math.floor(r.t/FPS/60),s=String(Math.floor(r.t/FPS%60)).padStart(2,'0');h+=`<tr><td>${i+1}</td><td>${r.n}</td><td>${r.s}</td><td>${m}:${s}</td></tr>`});
|
||||
document.getElementById('ranking-table-container').innerHTML=h+'</table>';
|
||||
document.getElementById('ranking-table-container').innerHTML=this._rankingHTML();
|
||||
document.getElementById('submitScoreBtn').style.display='none';
|
||||
document.getElementById('playerName').style.display='none';
|
||||
document.getElementById('playerName').blur();
|
||||
document.getElementById('playAgainBtn').style.display='inline-block';
|
||||
},
|
||||
|
||||
_rankingHTML(){
|
||||
let h='<table><tr><th>#</th><th>Name</th><th>Punkte</th><th>Zeit</th></tr>';
|
||||
this.rank.forEach((r,i)=>{
|
||||
const time=r.t<0?'-:--':`${Math.floor(r.t/FPS/60)}:${String(Math.floor(r.t/FPS%60)).padStart(2,'0')}`;
|
||||
h+=`<tr><td>${i+1}</td><td>${r.n}</td><td>${r.s}</td><td>${time}</td></tr>`;
|
||||
});
|
||||
return h+'</table>';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -276,10 +276,12 @@
|
||||
<div class="tool-grid">
|
||||
<button class="tool-btn" onclick="setSpecial('player')">🧑 Spieler-Start</button>
|
||||
<button class="tool-btn" onclick="setSpecial('ghost')">👻 Geist-Haus</button>
|
||||
<button class="tool-btn full" onclick="setSpecial('medSpawn')">💊 Med-Spawn</button>
|
||||
</div>
|
||||
<div class="legend" style="margin-top:8px">
|
||||
<span>Cyan ●</span> = Spielerstart<br>
|
||||
<span>Gold ●</span> = Geist-Startpos
|
||||
<span>Gold ●</span> = Geist-Startpos<br>
|
||||
<span>Pink ●</span> = Med-Spawnpunkt
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -349,6 +351,7 @@ let redoStack = [];
|
||||
// Player start and ghost house positions (mirroring constants)
|
||||
let playerStart = { c: PL0.c, r: PL0.r };
|
||||
let ghostHomes = PH.map(p => ({ c: p.c, r: p.r }));
|
||||
let medSpawns = (typeof MED_SPAWNS !== 'undefined') ? MED_SPAWNS.map(p => ({ c: p.c, r: p.r })) : [];
|
||||
|
||||
let selectedTile = 1; // currently selected tile id
|
||||
let currentTool = 'paint';
|
||||
@@ -477,6 +480,7 @@ function drawGrid() {
|
||||
// Special markers
|
||||
drawMarker(ctxG, playerStart.r, playerStart.c, '#00DDFF', '★');
|
||||
ghostHomes.forEach((gh, i) => drawMarker(ctxG, gh.r, gh.c, '#FFD700', (i + 1).toString()));
|
||||
medSpawns.forEach(sp => drawMarker(ctxG, sp.r, sp.c, '#EC4899', 'M'));
|
||||
updateDotCount();
|
||||
}
|
||||
|
||||
@@ -627,9 +631,9 @@ function setSpecial(mode) {
|
||||
specialMode = mode;
|
||||
ghostSetIndex = 0;
|
||||
document.getElementById('info-bar').textContent =
|
||||
mode === 'player'
|
||||
? 'Klick: Spieler-Startposition setzen'
|
||||
: 'Klick: Geist-Position 1 von 4 setzen';
|
||||
mode === 'player' ? 'Klick: Spieler-Startposition setzen' :
|
||||
mode === 'medSpawn' ? 'Klick: Med-Spawnpunkt hinzufügen / entfernen' :
|
||||
'Klick: Geist-Position 1 von 4 setzen';
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -691,6 +695,15 @@ cvOver.addEventListener('mousedown', e => {
|
||||
document.getElementById('info-bar').textContent =
|
||||
`Geist-Position ${ghostSetIndex + 1} von 4 setzen`;
|
||||
}
|
||||
} else if (specialMode === 'medSpawn') {
|
||||
const idx = medSpawns.findIndex(sp => sp.r === r && sp.c === c);
|
||||
if (idx >= 0) {
|
||||
medSpawns.splice(idx, 1);
|
||||
document.getElementById('info-bar').textContent = `Med-Spawn entfernt bei [${r}, ${c}] | Gesamt: ${medSpawns.length}`;
|
||||
} else {
|
||||
medSpawns.push({ r, c });
|
||||
document.getElementById('info-bar').textContent = `Med-Spawn gesetzt bei [${r}, ${c}] | Gesamt: ${medSpawns.length}`;
|
||||
}
|
||||
}
|
||||
drawGrid();
|
||||
return;
|
||||
@@ -832,6 +845,8 @@ function buildExportCode() {
|
||||
lines.push(`const PL0 = {c:${playerStart.c},r:${playerStart.r}};`);
|
||||
const ph = ghostHomes.map(g => `{c:${g.c},r:${g.r}}`).join(',');
|
||||
lines.push(`const PH = [${ph}];`);
|
||||
const ms = medSpawns.map(s => `{r:${s.r},c:${s.c}}`).join(',');
|
||||
lines.push(`const MED_SPAWNS = [${ms}];`);
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
|
||||
@@ -166,34 +166,40 @@ body {
|
||||
box-shadow: 0 0 10px rgba(139, 92, 246, 0.4);
|
||||
}
|
||||
|
||||
#ranking-table-container {
|
||||
.ranking-table-container {
|
||||
margin-top: 20px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#ranking-table-container table {
|
||||
.ranking-table-container table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
#ranking-table-container th {
|
||||
.ranking-table-container th {
|
||||
color: #FFD700;
|
||||
padding: 6px;
|
||||
border-bottom: 1px solid #444;
|
||||
}
|
||||
|
||||
#ranking-table-container td {
|
||||
.ranking-table-container td {
|
||||
color: #ccc;
|
||||
padding: 5px;
|
||||
border-bottom: 1px solid #222;
|
||||
}
|
||||
|
||||
#ranking-table-container tr:first-child td {
|
||||
.ranking-table-container tr:first-child td {
|
||||
color: #FFD700;
|
||||
}
|
||||
|
||||
.end-score-text {
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* Mobile Joystick */
|
||||
#touch-controls {
|
||||
display: none;
|
||||
|
||||
Reference in New Issue
Block a user