Battle Realms
by FrozenOtter31569 lines38.3 KB
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Battle Realms</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Heebo:wght@400;700;900&display=swap');
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent;touch-action:manipulation}
body{font-family:'Heebo',sans-serif;background:#1a0f2e;color:#fff;min-height:100vh;overflow:hidden}
.screen{display:none;width:100%;height:100vh;overflow:hidden;position:relative}
.screen.active{display:flex;flex-direction:column}
/* Menu */
#menuScreen{justify-content:center;align-items:center;text-align:center;padding:20px}
#menuScreen h1{font-size:3rem;font-weight:900;background:linear-gradient(90deg,#ffd700,#ff8c00,#ffd700);-webkit-background-clip:text;-webkit-text-fill-color:transparent;margin-bottom:8px}
.subtitle{color:#b8a8d8;font-size:1.1rem;margin-bottom:30px}
.stats-row{display:flex;gap:20px;margin-bottom:30px;flex-wrap:wrap;justify-content:center}
.stat-pill{background:rgba(255,255,255,0.08);border:1px solid rgba(255,215,0,0.3);border-radius:16px;padding:12px 24px}
.stat-pill .lbl{color:#b8a8d8;font-size:0.8rem}
.stat-pill .val{font-size:1.5rem;font-weight:700;color:#ffd700}
.btn{padding:14px 40px;font-size:1.2rem;font-weight:700;border:none;border-radius:14px;cursor:pointer;transition:all 0.2s;font-family:'Heebo',sans-serif;min-width:260px;margin:6px 0}
.btn-gold{background:linear-gradient(135deg,#ffd700,#ff8c00);color:#1a0f2e;box-shadow:0 4px 20px rgba(255,140,0,0.4)}
.btn-gold:active{transform:scale(0.96)}
.btn-dark{background:rgba(255,255,255,0.1);color:#fff;border:1px solid rgba(255,255,255,0.2)}
.btn-red{background:linear-gradient(135deg,#ff4444,#cc0000);color:#fff}
.back-btn{position:absolute;top:10px;right:10px;z-index:200;padding:8px 16px;background:rgba(255,255,255,0.15);border:1px solid rgba(255,255,255,0.3);border-radius:10px;color:#fff;cursor:pointer;font-family:'Heebo',sans-serif;font-weight:700;font-size:0.9rem}
.back-btn:active{background:rgba(255,255,255,0.25)}
/* Deck */
#deckScreen,#mapScreen,#collectionScreen,#trainingScreen{padding:20px;overflow-y:auto}
.screen-title{text-align:center;font-size:1.6rem;margin-bottom:15px;margin-top:40px}
.deck-count{text-align:center;color:#b8a8d8;margin-bottom:15px}
.selected-row{display:flex;gap:8px;justify-content:center;flex-wrap:wrap;margin-bottom:20px}
.sel-slot{width:60px;height:60px;border-radius:10px;background:rgba(255,215,0,0.15);border:2px solid #ffd700;display:flex;align-items:center;justify-content:center;font-size:1.8rem}
.sel-slot.empty{background:rgba(255,255,255,0.05);border-color:rgba(255,255,255,0.15);color:rgba(255,255,255,0.3)}
.char-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;max-width:500px;margin:0 auto 20px}
.char-cell{background:rgba(255,255,255,0.06);border:2px solid rgba(255,255,255,0.1);border-radius:12px;padding:10px 5px;text-align:center;cursor:pointer;position:relative;transition:all 0.2s}
.char-cell:active{transform:scale(0.95)}
.char-cell.picked{border-color:#ffd700;background:rgba(255,215,0,0.15)}
.char-cell.locked{opacity:0.35;pointer-events:none}
.char-cell .c-icon{font-size:2rem}
.char-cell .c-name{font-size:0.75rem;font-weight:700;margin-top:3px}
.char-cell .c-info{font-size:0.65rem;color:#b8a8d8;line-height:1.5}
.char-cell .c-lvl{position:absolute;top:3px;right:3px;background:#ffd700;color:#1a0f2e;font-size:0.6rem;font-weight:700;padding:1px 5px;border-radius:5px}
.char-cell .c-lock{position:absolute;top:3px;left:3px;background:#ff4444;color:#fff;font-size:0.55rem;padding:1px 5px;border-radius:5px}
/* Maps */
.map-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:12px;max-width:400px;margin:0 auto 20px}
.map-cell{aspect-ratio:16/10;border-radius:14px;display:flex;flex-direction:column;align-items:center;justify-content:center;border:2px solid rgba(255,255,255,0.15);cursor:pointer;transition:all 0.2s}
.map-cell:active{transform:scale(0.95)}
.map-cell.chosen{border-color:#ffd700;box-shadow:0 0 20px rgba(255,215,0,0.3)}
.map-cell .m-emoji{font-size:2rem}
.map-cell .m-name{font-size:0.8rem;font-weight:700;margin-top:4px;color:#fff;text-shadow:0 1px 4px rgba(0,0,0,0.5)}
/* Game */
#gameScreen{padding:0}
.game-hud{display:flex;justify-content:space-between;align-items:center;padding:8px 15px;background:rgba(0,0,0,0.5);z-index:100;position:relative}
.hud-side{text-align:center}
.hud-side .h-num{font-size:1.3rem;font-weight:700}
.hud-side .h-lbl{font-size:0.6rem;color:#aaa}
.hud-timer{font-size:1.4rem;font-weight:700;color:#ffd700}
.arena{flex:1;position:relative;overflow:hidden;background:linear-gradient(180deg,#4a7c59,#3d6b4a,#2d5a3a)}
.arena-river{position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);height:40px;background:linear-gradient(180deg,#4a90d9,#357abd,#4a90d9);border-top:2px solid rgba(255,255,255,0.3);border-bottom:2px solid rgba(255,255,255,0.3);z-index:5}
.arena-bridge{position:absolute;width:50px;height:50px;background:linear-gradient(180deg,#8B7355,#6B5344);border:2px solid #5a4335;border-radius:8px;top:50%;transform:translateY(-50%);z-index:6}
.arena-bridge.left{left:25%}
.arena-bridge.right{right:25%}
.scenery{position:absolute;pointer-events:none;z-index:1;user-select:none;text-shadow:0 2px 8px rgba(0,0,0,0.3)}
.tower{position:absolute;width:55px;height:65px;border-radius:10px;display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:20;border:3px solid;transition:all 0.3s;box-shadow:0 4px 15px rgba(0,0,0,0.5)}
.tower .t-icon{font-size:1.6rem}
.tower .t-hp{font-size:0.65rem;font-weight:700}
.tower.player{border-color:#4CAF50;background:rgba(76,175,80,0.4)}
.tower.bot{border-color:#f44336;background:rgba(244,67,54,0.4)}
.tower.inactive{opacity:0.4;filter:grayscale(0.7)}
.tower.destroyed{opacity:0.2;filter:grayscale(1)}
.tower.princess-left{left:5%}
.tower.princess-right{right:5%}
.tower.king{left:50%;transform:translateX(-50%);width:65px;height:75px}
.tower.bot-side{top:8%}
.tower.player-side{bottom:8%}
.unit{position:absolute;width:42px;height:42px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1.4rem;border:2px solid;z-index:30;transition:transform 0.1s;box-shadow:0 2px 8px rgba(0,0,0,0.4)}
.unit.player{border-color:#4CAF50;background:rgba(76,175,80,0.5)}
.unit.bot{border-color:#f44336;background:rgba(244,67,54,0.5)}
.unit-hp-bg{position:absolute;bottom:-6px;left:50%;transform:translateX(-50%);width:36px;height:4px;background:rgba(0,0,0,0.6);border-radius:2px;overflow:hidden}
.unit-hp-fg{height:100%;background:#4CAF50;border-radius:2px;transition:width 0.2s}
.dmg-pop{position:absolute;top:-18px;font-weight:900;font-size:0.75rem;color:#ff4444;animation:popUp 0.8s forwards;pointer-events:none}
@keyframes popUp{0%{opacity:1;transform:translateY(0)scale(1)}100%{opacity:0;transform:translateY(-25px)scale(1.3)}}
.hand-bar{display:flex;justify-content:center;gap:8px;padding:10px;background:rgba(0,0,0,0.6);z-index:100;position:relative}
.hand-card{width:70px;height:90px;border-radius:12px;background:linear-gradient(180deg,rgba(255,255,255,0.12),rgba(255,255,255,0.05));border:2px solid rgba(255,255,255,0.2);display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer;position:relative;transition:all 0.2s}
.hand-card:active{transform:scale(0.92)}
.hand-card.selected{border-color:#ffd700;box-shadow:0 0 15px rgba(255,215,0,0.4);transform:translateY(-5px)}
.hand-card.disabled{opacity:0.35;pointer-events:none}
.hand-card .hc-cost{position:absolute;top:-8px;right:-8px;width:22px;height:22px;background:linear-gradient(135deg,#9C27B0,#E91E63);border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:0.7rem;font-weight:700;border:2px solid #fff}
.hand-card .hc-icon{font-size:1.6rem}
.hand-card .hc-name{font-size:0.6rem;margin-top:2px;font-weight:700}
.elixir-bar{height:14px;background:rgba(0,0,0,0.4);border-radius:7px;margin:0 15px 8px;overflow:hidden;position:relative}
.elixir-fill{height:100%;background:linear-gradient(90deg,#E91E63,#9C27B0,#E91E63);border-radius:7px;transition:width 0.3s}
.elixir-text{text-align:center;font-size:0.8rem;color:#E91E63;font-weight:700;margin-bottom:5px}
.placement-ghost{position:absolute;width:40px;height:40px;border-radius:50%;border:3px dashed #ffd700;background:rgba(255,215,0,0.2);pointer-events:none;z-index:50;display:none;animation:ghostPulse 1s infinite}
@keyframes ghostPulse{0%,100%{opacity:0.6}50%{opacity:1}}
/* Result */
.result-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.85);display:none;align-items:center;justify-content:center;z-index:1000}
.result-overlay.on{display:flex}
.result-box{background:linear-gradient(135deg,#2d1b4e,#1a0f2e);border:2px solid #ffd700;border-radius:24px;padding:35px;text-align:center;max-width:400px;width:90%}
.result-box .rtitle{font-size:2.2rem;font-weight:900;margin-bottom:15px}
.result-box .rtitle.win{color:#4CAF50}
.result-box .rtitle.lose{color:#f44336}
.result-box .rtitle.draw{color:#ffd700}
.r-reward{font-size:1.1rem;margin:6px 0}
.r-reward b{color:#ffd700}
.toast{position:fixed;top:15px;left:50%;transform:translateX(-50%) translateY(-80px);background:rgba(0,0,0,0.9);border:1px solid #ffd700;border-radius:12px;padding:12px 25px;font-weight:700;z-index:3000;transition:transform 0.4s;font-size:0.9rem}
.toast.show{transform:translateX(-50%) translateY(0)}
/* Collection */
.upgrade-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;max-width:500px;margin:0 auto}
.up-card{background:rgba(255,255,255,0.06);border:2px solid rgba(255,255,255,0.1);border-radius:12px;padding:12px;text-align:center}
.up-card .up-icon{font-size:2rem}
.up-card .up-name{font-size:0.85rem;font-weight:700}
.up-card .up-stat{font-size:0.7rem;color:#b8a8d8}
.up-btn{padding:6px 14px;border:none;border-radius:8px;font-weight:700;font-family:'Heebo',sans-serif;font-size:0.8rem;cursor:pointer;background:linear-gradient(135deg,#ffd700,#ff8c00);color:#1a0f2e;margin-top:6px}
.up-btn:disabled{background:#444;color:#777}
.up-max{color:#4CAF50;font-size:0.8rem;font-weight:700;margin-top:6px}
@media(max-width:400px){
.hand-card{width:60px;height:78px}
.char-grid{grid-template-columns:repeat(2,1fr)}
.tower{width:45px;height:55px}
.tower.king{width:55px;height:65px}
}
</style>
</head>
<body>
<div class="toast" id="toast"></div>
<div class="result-overlay" id="resultOverlay">
<div class="result-box">
<div class="rtitle" id="rTitle"></div>
<div id="rRewards"></div>
<button class="btn btn-gold" style="margin-top:15px;" onclick="closeResult()">חזור לתפריט</button>
</div>
</div>
<!-- MENU -->
<div class="screen active" id="menuScreen">
<h1>Battle Realms</h1>
<div class="subtitle">ממלכות הקרב</div>
<div class="stats-row">
<div class="stat-pill"><div class="lbl">גביעים</div><div class="val" id="mTrophies">0</div></div>
<div class="stat-pill"><div class="lbl">מטבעות</div><div class="val" id="mCoins">0</div></div>
<div class="stat-pill"><div class="lbl">רמת בוט</div><div class="val" id="mBot">1</div></div>
</div>
<button class="btn btn-gold" onclick="goDeck()">שחק נגד בוט</button>
<button class="btn btn-dark" onclick="goTraining()">מוד אימון</button>
<button class="btn btn-dark" onclick="goCollection()">אוסף ושדרוגים</button>
<button class="btn btn-red" onclick="doReset()">איפוס משחק</button>
</div>
<!-- DECK -->
<div class="screen" id="deckScreen">
<button class="back-btn" onclick="goMenu()">חזור</button>
<div class="screen-title">בחר 10 דמויות לדק</div>
<div class="deck-count">נבחרו: <span id="dCount">0</span>/10</div>
<div class="selected-row" id="selRow"></div>
<div class="char-grid" id="charGrid"></div>
<div style="text-align:center;">
<button class="btn btn-gold" id="dNext" onclick="goMaps()" disabled>המשך</button>
</div>
</div>
<!-- MAPS -->
<div class="screen" id="mapScreen">
<button class="back-btn" onclick="goDeck()">חזור</button>
<div class="screen-title">בחר מפה</div>
<div class="map-grid" id="mapGrid"></div>
<div style="text-align:center;">
<button class="btn btn-gold" id="mStart" onclick="startGame()" disabled>התחל!</button>
</div>
</div>
<!-- TRAINING -->
<div class="screen" id="trainingScreen">
<button class="back-btn" onclick="goMenu()">חזור</button>
<div style="flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;">
<div class="screen-title">מוד אימון</div>
<button class="btn btn-gold" onclick="startTrain('easy')">קל 😊</button>
<button class="btn btn-gold" onclick="startTrain('medium')">בינוני 😐</button>
<button class="btn btn-gold" onclick="startTrain('hard')">קשה 😠</button>
<button class="btn btn-red" onclick="startTrain('impossible')">בלתי אפשרי 😈</button>
</div>
</div>
<!-- GAME -->
<div class="screen" id="gameScreen">
<div class="game-hud">
<div class="hud-side"><div class="h-num" id="hPlayer">3</div><div class="h-lbl">מבנים</div></div>
<div class="hud-timer" id="hTimer">3:00</div>
<div class="hud-side"><div class="h-num" id="hBot">3</div><div class="h-lbl">מבנים</div></div>
</div>
<div class="arena" id="arena">
<div class="arena-river"></div>
<div class="arena-bridge left"></div>
<div class="arena-bridge right"></div>
<div class="tower bot bot-side princess-left" id="tB0">🏯<div class="t-hp" id="hpB0">2400</div></div>
<div class="tower bot bot-side princess-right" id="tB1">🏯<div class="t-hp" id="hpB1">2400</div></div>
<div class="tower bot bot-side king inactive" id="tB2">👑<div class="t-hp" id="hpB2">4000</div></div>
<div class="tower player player-side princess-left" id="tP0">🏰<div class="t-hp" id="hpP0">2400</div></div>
<div class="tower player player-side princess-right" id="tP1">🏰<div class="t-hp" id="hpP1">2400</div></div>
<div class="tower player player-side king inactive" id="tP2">👑<div class="t-hp" id="hpP2">4000</div></div>
<div class="placement-ghost" id="ghost"></div>
</div>
<div class="elixir-text" id="eText">אליקסיר: 0/10</div>
<div class="elixir-bar"><div class="elixir-fill" id="eFill" style="width:0%"></div></div>
<div class="hand-bar" id="handBar"></div>
</div>
<!-- COLLECTION -->
<div class="screen" id="collectionScreen">
<button class="back-btn" onclick="goMenu()">חזור</button>
<div class="screen-title">אוסף ושדרוגים</div>
<div style="text-align:center;color:#ffd700;margin-bottom:15px;">💰 <span id="cCoins">0</span></div>
<div class="upgrade-grid" id="upGrid"></div>
</div>
<script>
// ==================== DATA ====================
const CHARS=[
{id:0,name:"לוחם",icon:"⚔️",hp:800,dmg:120,speed:1.2,range:30,cost:3,special:"מתקפה כפולה",unlock:0},
{id:1,name:"קשת",icon:"🏹",hp:400,dmg:150,speed:0.9,range:150,cost:3,special:"ירי מרחוק",unlock:0},
{id:2,name:"מגן",icon:"🛡️",hp:1500,dmg:60,speed:0.7,range:30,cost:4,special:"בלימת נזק",unlock:0},
{id:3,name:"מכשף",icon:"🔮",hp:350,dmg:200,speed:0.8,range:120,cost:5,special:"כדור אש",unlock:0},
{id:4,name:"רוכב",icon:"🐴",hp:1000,dmg:180,speed:1.8,range:40,cost:5,special:"סערת פרשים",unlock:0},
{id:5,name:"מרפא",icon:"💚",hp:450,dmg:40,speed:0.8,range:100,cost:4,special:"ריפוי יחידה",unlock:0},
{id:6,name:"גולם",icon:"🗿",hp:2500,dmg:100,speed:0.5,range:35,cost:6,special:"הלם קרקע",unlock:0},
{id:7,name:"צלף",icon:"🎯",hp:300,dmg:300,speed:0.7,range:200,cost:5,special:"ירי קריטי",unlock:0},
{id:8,name:"נינג'ה",icon:"🥷",hp:600,dmg:220,speed:2.0,range:30,cost:4,special:"היעלמות",unlock:0},
{id:9,name:"כומת",icon:"🔨",hp:700,dmg:160,speed:1.0,range:30,cost:4,special:"הרס מבנים",unlock:0},
{id:10,name:"דרקון",icon:"🐉",hp:2000,dmg:250,speed:1.5,range:80,cost:7,special:"נשיפת אש",unlock:500},
{id:11,name:"רוח",icon:"👻",hp:500,dmg:180,speed:1.4,range:50,cost:4,special:"חדירת שריון",unlock:500},
{id:12,name:"אלף",icon:"🧝",hp:550,dmg:170,speed:1.3,range:110,cost:4,special:"חץ קסום",unlock:1000},
{id:13,name:"ענק",icon:"👹",hp:3500,dmg:200,speed:0.6,range:50,cost:8,special:"ריסוק",unlock:1000},
{id:14,name:"פניקס",icon:"🔥",hp:1200,dmg:280,speed:1.6,range:90,cost:7,special:"תחייה",unlock:1500},
{id:15,name:"קריפטון",icon:"💀",hp:900,dmg:240,speed:1.1,range:40,cost:5,special:"שאיבת חיים",unlock:1500},
{id:16,name:"טיטאן",icon:"⚡",hp:2800,dmg:220,speed:0.8,range:60,cost:8,special:"ברק",unlock:2000},
{id:17,name:"צייד",icon:"🐺",hp:750,dmg:190,speed:1.7,range:45,cost:4,special:"זאבים",unlock:2500},
{id:18,name:"מלך",icon:"👑",hp:1800,dmg:300,speed:1.0,range:50,cost:7,special:"עידוד צבא",unlock:3000},
{id:19,name:"אל",icon:"✨",hp:2200,dmg:350,speed:1.2,range:100,cost:9,special:"שיפור כללי",unlock:5000},
];
const MAPS=[
{id:0,name:"עמק הזריחה",emoji:"🌅",bg:"linear-gradient(180deg,#7a9a6a,#5a8a5a,#4a7a4a)",scenery:[
{icon:"🌅",x:45,y:5,s:5,o:0.4},{icon:"☀️",x:75,y:8,s:4,o:0.35},
{icon:"🌻",x:15,y:85,s:2.5,o:0.5},{icon:"🌻",x:85,y:88,s:2.5,o:0.5},
{icon:"🦋",x:30,y:40,s:1.8,o:0.4},{icon:"🦋",x:70,y:55,s:1.8,o:0.4},
{icon:"🌸",x:10,y:70,s:2,o:0.45},{icon:"🌸",x:90,y:65,s:2,o:0.45},
]},
{id:1,name:"יער האפלה",emoji:"🌲",bg:"linear-gradient(180deg,#2a5a2a,#1f4a1f,#153a15)",scenery:[
{icon:"🌲",x:8,y:5,s:4.5,o:0.5},{icon:"🌲",x:22,y:8,s:5,o:0.45},
{icon:"🌲",x:78,y:6,s:4.8,o:0.5},{icon:"🌲",x:92,y:9,s:4.2,o:0.45},
{icon:"🌳",x:5,y:75,s:5,o:0.5},{icon:"🌳",x:35,y:82,s:4.5,o:0.45},
{icon:"🌳",x:65,y:78,s:5,o:0.5},{icon:"🌳",x:95,y:85,s:4.8,o:0.45},
{icon:"🍄",x:18,y:55,s:2,o:0.4},{icon:"🍄",x:82,y:50,s:2,o:0.4},
{icon:"🦉",x:50,y:15,s:2.2,o:0.35},{icon:"🦊",x:40,y:70,s:2,o:0.3},
]},
{id:2,name:"מדבר הלוהט",emoji:"🏜️",bg:"linear-gradient(180deg,#d4b46a,#c4a45a,#b4944a)",scenery:[
{icon:"🏜️",x:12,y:5,s:6,o:0.45},{icon:"🏜️",x:88,y:8,s:5.5,o:0.4},
{icon:"🌵",x:8,y:80,s:3.5,o:0.5},{icon:"🌵",x:25,y:88,s:3,o:0.45},
{icon:"🌵",x:75,y:82,s:3.5,o:0.5},{icon:"🌵",x:92,y:90,s:3,o:0.45},
{icon:"🐪",x:40,y:65,s:3,o:0.35},{icon:"🦂",x:60,y:75,s:2,o:0.4},
{icon:"💀",x:18,y:45,s:2,o:0.3},{icon:"☀️",x:80,y:12,s:4,o:0.35},
]},
{id:3,name:"הרי הקרח",emoji:"🏔️",bg:"linear-gradient(180deg,#9ac8d8,#8ab8c8,#7aa8b8)",scenery:[
{icon:"🏔️",x:10,y:3,s:6,o:0.45},{icon:"🏔️",x:85,y:5,s:5.5,o:0.4},
{icon:"🏔️",x:50,y:2,s:7,o:0.5},{icon:"❄️",x:20,y:25,s:2,o:0.4},
{icon:"❄️",x:70,y:30,s:2.2,o:0.4},{icon:"❄️",x:45,y:40,s:1.8,o:0.35},
{icon:"⛄",x:15,y:80,s:3,o:0.5},{icon:"⛄",x:85,y:85,s:3,o:0.45},
{icon:"🐧",x:35,y:70,s:2.2,o:0.4},{icon:"🐧",x:65,y:75,s:2.2,o:0.4},
{icon:"🧊",x:5,y:50,s:3,o:0.35},{icon:"🧊",x:95,y:55,s:3,o:0.35},
]},
{id:4,name:"המצודה",emoji:"🏰",bg:"linear-gradient(180deg,#6a5a7a,#5a4a6a,#4a3a5a)",scenery:[
{icon:"🏰",x:50,y:2,s:6,o:0.5},{icon:"🕯️",x:15,y:15,s:2,o:0.4},
{icon:"🕯️",x:85,y:18,s:2,o:0.4},{icon:"🦇",x:25,y:35,s:2,o:0.35},
{icon:"🦇",x:75,y:40,s:2,o:0.35},{icon:"🕷️",x:10,y:55,s:1.8,o:0.3},
{icon:"🕷️",x:90,y:60,s:1.8,o:0.3},{icon:"⚔️",x:30,y:78,s:2.5,o:0.35},
{icon:"🛡️",x:70,y:82,s:2.5,o:0.35},{icon:"💎",x:50,y:88,s:2,o:0.4},
{icon:"🔥",x:5,y:25,s:2.5,o:0.3},{icon:"🔥",x:95,y:28,s:2.5,o:0.3},
]},
];
const UGCOSTS=[20,40,60,80,100,130,160,200,300,400,500,600,700,800,900,1000];
// ==================== STATE ====================
let GS=loadGS();
function loadGS(){const s=localStorage.getItem('brSave');if(s)return JSON.parse(s);return{trophies:0,coins:0,unlocked:[0,1,2,3,4,5,6,7,8,9],levels:{},deck:[0,1,2,3,4,5,6,7,8,9],wins:0,losses:0};}
function saveGS(){localStorage.setItem('brSave',JSON.stringify(GS));}
function doReset(){if(!confirm('לאפס הכל?'))return;localStorage.removeItem('brSave');GS=loadGS();updateMenu();toast('אופס!');}
function toast(t){const el=document.getElementById('toast');el.textContent=t;el.classList.add('show');setTimeout(()=>el.classList.remove('show'),2500);}
function getBotLvl(){return Math.floor(GS.trophies/500)+1;}
function getLvl(id){return GS.levels[id]||1;}
function getCost(lvl){if(lvl<=8)return UGCOSTS[lvl-1]||200;return 300+(lvl-9)*100;}
function getMaxLvl(){const t=GS.trophies;if(t>=10000)return 16;if(t>=9000)return 15;if(t>=8000)return 14;if(t>=7000)return 13;if(t>=6000)return 12;if(t>=5000)return 11;if(t>=4000)return 10;if(t>=3000)return 10;if(t>=2000)return 10;if(t>=1000)return 9;return 8;}
function getStats(id){const b=CHARS.find(c=>c.id===id);const l=getLvl(id);const m=1+(l-1)*0.1;return{...b,hp:Math.floor(b.hp*m),dmg:Math.floor(b.dmg*m),speed:b.speed*(1+(l-1)*0.03)};}
function isUnl(id){return GS.unlocked.includes(id);}
function canUnl(id){return GS.trophies>=CHARS.find(c=>c.id===id).unlock;}
// ==================== NAVIGATION ====================
function showScreen(id){document.querySelectorAll('.screen').forEach(s=>s.classList.remove('active'));document.getElementById(id).classList.add('active');}
function goMenu(){document.getElementById('resultOverlay').classList.remove('on');showScreen('menuScreen');saveGS();updateMenu();}
function closeResult(){document.getElementById('resultOverlay').classList.remove('on');goMenu();}
function updateMenu(){document.getElementById('mTrophies').textContent=GS.trophies;document.getElementById('mCoins').textContent=GS.coins;document.getElementById('mBot').textContent=getBotLvl();}
// ==================== DECK ====================
let tDeck=[];
function goDeck(){tDeck=[...GS.deck];showScreen('deckScreen');renderDeck();}
function renderDeck(){
document.getElementById('dCount').textContent=tDeck.length;
document.getElementById('dNext').disabled=tDeck.length!==10;
const sel=document.getElementById('selRow');sel.innerHTML='';
for(let i=0;i<10;i++){
const d=document.createElement('div');d.className='sel-slot'+(tDeck[i]===undefined?' empty':'');
d.textContent=tDeck[i]!==undefined?CHARS.find(c=>c.id===tDeck[i]).icon:'+';
if(tDeck[i]!==undefined)d.onclick=()=>{tDeck.splice(i,1);renderDeck();};
sel.appendChild(d);
}
const g=document.getElementById('charGrid');g.innerHTML='';
CHARS.forEach(c=>{
const u=isUnl(c.id),p=tDeck.includes(c.id);
const d=document.createElement('div');d.className='char-cell'+(p?' picked':'')+(!u?' locked':'');
const s=getStats(c.id);
d.innerHTML=(!u?`<div class="c-lock">${c.unlock}</div>`:`<div class="c-lvl">${getLvl(c.id)}</div>`)+`<div class="c-icon">${c.icon}</div><div class="c-name">${c.name}</div><div class="c-info">❤️${s.hp} ⚔️${s.dmg}<br>💧${c.cost} 🎯${c.special}</div>`;
if(u){d.onclick=()=>{if(p)tDeck=tDeck.filter(x=>x!==c.id);else if(tDeck.length<10)tDeck.push(c.id);renderDeck();};}
g.appendChild(d);
});
}
function goMaps(){GS.deck=[...tDeck];saveGS();showScreen('mapScreen');renderMaps();}
// ==================== MAPS ====================
let cMap=null,isTrain=false,trainDiff='medium';
function renderMaps(){
const g=document.getElementById('mapGrid'),b=document.getElementById('mStart');g.innerHTML='';
MAPS.forEach(m=>{
const d=document.createElement('div');d.className='map-cell'+(cMap===m.id?' chosen':'');d.style.background=m.bg;
d.innerHTML=`<div class="m-emoji">${m.emoji}</div><div class="m-name">${m.name}</div>`;
d.onclick=()=>{cMap=m.id;renderMaps();};g.appendChild(d);
});b.disabled=cMap===null;
}
// ==================== TRAINING ====================
function goTraining(){showScreen('trainingScreen');}
function startTrain(d){trainDiff=d;isTrain=true;cMap=0;startGame();}
// ==================== GAME ====================
let units=[],towers={p:[2400,2400,4000],b:[2400,2400,4000]},princessAlive={p:[true,true],b:[true,true]};
let elixir=0,botElixir=0,maxElixir=10,timeLeft=180,active=false;
let hand=[],deckQueue=[],botHand=[],botDeckQueue=[];
let tmr=null,loop=null,manaTmr=null,botTmr=null;
let selCard=null,dest={p:0,b:0};
function startGame(){
showScreen('gameScreen');
units.forEach(u=>u.el&&u.el.remove());units=[];
elixir=3;botElixir=3;timeLeft=180;active=true;dest={p:0,b:0};
towers={p:[2400,2400,4000],b:[2400,2400,4000]};
princessAlive={p:[true,true],b:[true,true]};
selCard=null;document.getElementById('ghost').style.display='none';
hand=[];deckQueue=[...GS.deck];shuffle(deckQueue);
for(let i=0;i<4;i++)if(deckQueue.length>0)hand.push(deckQueue.shift());
initBotDeck();
const m=MAPS[cMap||0];const arena=document.getElementById('arena');
arena.style.background=m.bg;
arena.querySelectorAll('.scenery').forEach(s=>s.remove());
if(m.scenery){m.scenery.forEach(s=>{const el=document.createElement('div');el.className='scenery';el.textContent=s.icon;el.style.left=s.x+'%';el.style.top=s.y+'%';el.style.fontSize=s.s+'rem';el.style.opacity=s.o;arena.appendChild(el);});}
for(let i=0;i<3;i++){
document.getElementById('tP'+i).classList.remove('destroyed','inactive');
document.getElementById('tB'+i).classList.remove('destroyed','inactive');
}
document.getElementById('tP2').classList.add('inactive');
document.getElementById('tB2').classList.add('inactive');
updateHUD();renderHand();updateTowers();
tmr=setInterval(()=>{timeLeft--;updateHUD();if(timeLeft<=0)finish('timeout');},1000);
manaTmr=setInterval(()=>{
if(elixir<maxElixir){elixir=Math.min(maxElixir,elixir+1.0);updateElixir();renderHand();}
if(botElixir<maxElixir){botElixir=Math.min(maxElixir,botElixir+1.0);}
},1000);
loop=setInterval(gameTick,80);
const bd=isTrain?trainDelay():Math.max(1800,3500-getBotLvl()*200);
botTmr=setInterval(botPlay,bd);
arena.onclick=(e)=>placeCard(e);
}
function shuffle(a){for(let i=a.length-1;i>0;i--){const j=Math.floor(Math.random()*(i+1));[a[i],a[j]]=[a[j],a[i]];}}
function trainDelay(){const d={easy:4000,medium:2800,hard:1800,impossible:1200};return d[trainDiff]||2800;}
function initBotDeck(){let a=[0,1,2,3,4,5,6,7,8,9];const l=getBotLvl();if(l>=2)a.push(10,11);if(l>=3)a.push(12,13);if(l>=4)a.push(14,15);if(l>=6)a.push(16,17);if(l>=8)a.push(18);if(l>=10)a.push(19);botDeckQueue=[];for(let i=0;i<8;i++)botDeckQueue.push(a[Math.floor(Math.random()*a.length)]);botHand=[];for(let i=0;i<4;i++)if(botDeckQueue.length>0)botHand.push(botDeckQueue.shift());}
function updateHUD(){const m=Math.floor(timeLeft/60),s=timeLeft%60;document.getElementById('hTimer').textContent=m+':'+s.toString().padStart(2,'0');document.getElementById('hPlayer').textContent=towers.p.filter(h=>h>0).length;document.getElementById('hBot').textContent=towers.b.filter(h=>h>0).length;}
function updateElixir(){document.getElementById('eFill').style.width=(elixir/maxElixir*100)+'%';document.getElementById('eText').textContent='אליקסיר: '+Math.floor(elixir)+'/'+maxElixir;}
function updateTowers(){for(let i=0;i<3;i++){document.getElementById('hpP'+i).textContent=Math.max(0,Math.floor(towers.p[i]));document.getElementById('hpB'+i).textContent=Math.max(0,Math.floor(towers.b[i]));const tp=document.getElementById('tP'+i),tb=document.getElementById('tB'+i);if(towers.p[i]<=0)tp.classList.add('destroyed');else tp.classList.remove('destroyed');if(towers.b[i]<=0)tb.classList.add('destroyed');else tb.classList.remove('destroyed');}if(!princessAlive.p[0]&&!princessAlive.p[1])document.getElementById('tP2').classList.remove('inactive');if(!princessAlive.b[0]&&!princessAlive.b[1])document.getElementById('tB2').classList.remove('inactive');}
function renderHand(){const bar=document.getElementById('handBar');bar.innerHTML='';hand.forEach((id,idx)=>{const c=CHARS.find(x=>x.id===id);const d=document.createElement('div');d.className='hand-card'+(elixir<c.cost?' disabled':'')+(selCard===idx?' selected':'');d.innerHTML=`<div class="hc-cost">${c.cost}</div><div class="hc-icon">${c.icon}</div><div class="hc-name">${c.name}</div>`;if(elixir>=c.cost&&active){d.onclick=(e)=>{e.stopPropagation();selectCard(idx);};}bar.appendChild(d);});}
function selectCard(idx){if(selCard===idx){selCard=null;document.getElementById('ghost').style.display='none';}else{selCard=idx;document.getElementById('ghost').style.display='block';}renderHand();}
function placeCard(e){
if(selCard===null||!active)return;
const id=hand[selCard];const c=CHARS.find(x=>x.id===id);
if(elixir<c.cost)return;
const arena=document.getElementById('arena');const rect=arena.getBoundingClientRect();
const x=e.clientX-rect.left;const y=e.clientY-rect.top;
if(y<rect.height*0.55){toast('שים רק בצד שלך!');return;}
elixir-=c.cost;updateElixir();
const lane=x<rect.width/2?0:1;
spawnUnit(id,'player',x,y,lane);
const playedId=hand[selCard];hand.splice(selCard,1);deckQueue.push(playedId);if(deckQueue.length>0)hand.push(deckQueue.shift());
selCard=null;document.getElementById('ghost').style.display='none';renderHand();
}
function spawnUnit(charId,side,x,y,lane){
const s=getStats(charId);const arena=document.getElementById('arena');
const el=document.createElement('div');el.className='unit '+side;
el.innerHTML=s.icon+'<div class="unit-hp-bg"><div class="unit-hp-fg" style="width:100%"></div></div>';
el.style.left=(x-21)+'px';el.style.top=(y-21)+'px';arena.appendChild(el);
const rect=arena.getBoundingClientRect();
const isPlayer=side==='player';const ty=isPlayer?rect.height*0.12:rect.height*0.88;
const tx=lane===0?rect.width*0.12:rect.width*0.88;
units.push({el:el,side:side,hp:s.hp,maxHp:s.hp,dmg:s.dmg,speed:s.speed*2.5,range:s.range,special:s.special,x:x-21,y:y-21,tx:tx,ty:ty,lane:lane,lastAtk:0,id:charId,invisible:false,revived:false});
}
function botPlay(){
if(!active)return;
let id=null;
if(isTrain){
const pool=trainDeck();const affordable=pool.filter(cid=>{const c=CHARS.find(x=>x.id===cid);return c&&botElixir>=c.cost;});
const use=affordable.length>0?affordable:pool;id=use[Math.floor(Math.random()*use.length)];
const c=CHARS.find(x=>x.id===id);if(!c||botElixir<c.cost)return;botElixir-=c.cost;
}else{
const affordable=botHand.filter(cid=>{const c=CHARS.find(x=>x.id===cid);return c&&botElixir>=c.cost;});
if(affordable.length===0)return;id=affordable[Math.floor(Math.random()*affordable.length)];
const c=CHARS.find(x=>x.id===id);botElixir-=c.cost;
botHand=botHand.filter(x=>x!==id);botDeckQueue.push(id);if(botDeckQueue.length>0)botHand.push(botDeckQueue.shift());
}
if(id===null)return;
const arena=document.getElementById('arena');const rect=arena.getBoundingClientRect();
const lane=Math.random()<0.5?0:1;
const x=lane===0?rect.width*0.25+Math.random()*50:rect.width*0.75+Math.random()*50;
const y=rect.height*0.15+Math.random()*60;
spawnUnit(id,'bot',x,y,lane);
}
function gameTick(){
if(!active)return;const now=Date.now();const arena=document.getElementById('arena');const rect=arena.getBoundingClientRect();
units.forEach(u=>{
if(u.hp<=0)return;
let tgt=null,bestD=Infinity;
units.forEach(e=>{if(e.side===u.side||e.hp<=0)return;const d=Math.hypot(e.x-u.x,e.y-u.y);if(d<bestD&&d<=u.range+25){bestD=d;tgt=e;}});
if(!tgt){
const isPlayer=u.side==='player';const tList=isPlayer?towers.b:towers.p;const alive=isPlayer?princessAlive.b:princessAlive.p;
// If tower in my lane is destroyed, target king tower if active, else other tower
const myTowerIdx=u.lane;
let targetOrder=[];
if(alive[myTowerIdx]&&tList[myTowerIdx]>0){
targetOrder=[myTowerIdx]; // my lane tower still alive
}else if((!alive[0]||tList[0]<=0)&&(!alive[1]||tList[1]<=0)&&tList[2]>0){
targetOrder=[2]; // both princess down, king is active
}else{
targetOrder=[1-myTowerIdx]; // go to other lane's tower
}
for(let i=0;i<targetOrder.length;i++){const idx=targetOrder[i];if(idx===2&&(!alive[0]||!alive[1])){}else if(idx<2&&!alive[idx])continue;if(tList[idx]<=0)continue;const tx=idx===0?rect.width*0.12:(idx===1?rect.width*0.88:rect.width/2);const ty=isPlayer?rect.height*0.12:rect.height*0.88;const d=Math.hypot(tx-u.x,ty-u.y);if(d<bestD&&d<=u.range+50){bestD=d;tgt={type:'tower',idx:idx,tx,ty};}}
// Also check king tower if both princess towers are down
if((!alive[0]||tList[0]<=0)&&(!alive[1]||tList[1]<=0)&&tList[2]>0){const tx=rect.width/2,ty=isPlayer?rect.height*0.12:rect.height*0.88;const d=Math.hypot(tx-u.x,ty-u.y);if(d<bestD&&d<=u.range+60){bestD=d;tgt={type:'tower',idx:2,tx,ty};}}
}
if(tgt){
if(now-u.lastAtk>900){
u.lastAtk=now;let dmg=u.dmg;
if(u.special==='מתקפה כפולה'&&Math.random()<0.3)dmg*=2;
if(u.special==='ירי קריטי'&&Math.random()<0.25)dmg*=2.5;
if(u.special==='היעלמות')u.invisible=true;
if(u.special==='הרס מבנים'&&tgt.type==='tower')dmg*=1.5;
if(u.special==='חדירת שריון')dmg*=1.3;
if(tgt.type==='tower'){const tList=u.side==='player'?towers.b:towers.p;tList[tgt.idx]-=dmg;if(tList[tgt.idx]<=0){if(tgt.idx<2){const a=u.side==='player'?princessAlive.b:princessAlive.p;a[tgt.idx]=false;}dest[u.side==='player'?'b':'p']++;}updateTowers();checkWin();}
else{let ad=dmg;if(tgt.invisible)ad*=0.5;tgt.hp-=ad;showDmg(tgt.el,Math.floor(ad));updateUHP(tgt);if(u.special==='שאיבת חיים'){u.hp=Math.min(u.maxHp,u.hp+ad*0.3);updateUHP(u);}if(u.special==='ריפוי יחידה'){units.forEach(a=>{if(a.side===u.side&&a!==u&&a.hp>0&&Math.hypot(a.x-u.x,a.y-u.y)<100){a.hp=Math.min(a.maxHp,a.hp+50);updateUHP(a);}});}if(u.special==='הלם קרקע'){units.forEach(e=>{if(e.side!==u.side&&e.hp>0&&Math.hypot(e.x-u.x,e.y-u.y)<80){e.hp-=30;showDmg(e.el,30);updateUHP(e);}});}}
}
}else{
// Movement: go to tower in my lane. If destroyed, go to king (if active) or other tower
const isPlayer=u.side==='player';const tList=isPlayer?towers.b:towers.p;const alive=isPlayer?princessAlive.b:princessAlive.p;
let tx,ty;
const myTower=u.lane;
if(alive[myTower]&&tList[myTower]>0){
tx=myTower===0?rect.width*0.12:rect.width*0.88;ty=isPlayer?rect.height*0.12:rect.height*0.88;
}else if((!alive[0]||tList[0]<=0)&&(!alive[1]||tList[1]<=0)&&tList[2]>0){
tx=rect.width/2;ty=isPlayer?rect.height*0.12:rect.height*0.88; // king tower
}else{
const other=1-myTower;
tx=other===0?rect.width*0.12:rect.width*0.88;ty=isPlayer?rect.height*0.12:rect.height*0.88;
}
const dx=tx-u.x,dy=ty-u.y;const dist=Math.hypot(dx,dy);
if(dist>2){const mx=(dx/dist)*u.speed,my=(dy/dist)*u.speed;u.x+=mx;u.y+=my;u.el.style.left=u.x+'px';u.el.style.top=u.y+'px';}
}
if(u.special==='תחייה'&&u.hp<=0&&!u.revived){u.revived=true;u.hp=u.maxHp*0.5;updateUHP(u);toast('פניקס קם לתחייה!');}
});
units=units.filter(u=>{if(u.hp<=0&&!(u.special==='תחייה'&&!u.revived)){u.el.remove();return false;}return true;});
}
function showDmg(el,dmg){const d=document.createElement('div');d.className='dmg-pop';d.textContent='-'+dmg;el.appendChild(d);setTimeout(()=>d.remove(),800);}
function updateUHP(u){const f=u.el.querySelector('.unit-hp-fg');if(f)f.style.width=Math.max(0,u.hp/u.maxHp*100)+'%';}
function checkWin(){const pa=towers.p.some(h=>h>0),ba=towers.b.some(h=>h>0);if(!ba)finish('win');else if(!pa)finish('lose');}
function finish(res){
active=false;clearInterval(tmr);clearInterval(loop);clearInterval(manaTmr);clearInterval(botTmr);
document.querySelectorAll('.scenery').forEach(s=>s.remove());
let tr=0,cn=15;
if(res==='win'){tr=dest.b>=3?30:21;GS.wins++;}
else if(res==='lose'){tr=-11;cn=0;GS.losses++;}
else if(res==='timeout'){const pc=towers.p.filter(h=>h>0).length,bc=towers.b.filter(h=>h>0).length;if(pc>bc){res='win';tr=21;GS.wins++;}else if(bc>pc){res='lose';tr=-11;GS.losses++;}else{res='draw';tr=0;cn=5;}}
if(isTrain){tr=0;cn=res==='win'?5:0;}
const ot=GS.trophies;GS.trophies=Math.max(0,GS.trophies+tr);GS.coins+=cn;
const nt=GS.trophies;let mc=0,mch=false,cch=false;
if(Math.floor(nt/50)>Math.floor(ot/50))mc+=(Math.floor(nt/50)-Math.floor(ot/50))*100;
if(Math.floor(nt/100)>Math.floor(ot/100))mch=true;
if(Math.floor(nt/500)>Math.floor(ot/500))cch=true;
GS.coins+=mc;CHARS.forEach(c=>{if(!isUnl(c.id)&&canUnl(c.id))GS.unlocked.push(c.id);});saveGS();
const ov=document.getElementById('resultOverlay');
document.getElementById('rTitle').className='rtitle '+res;
document.getElementById('rTitle').textContent=res==='win'?'ניצחון! 🎉':res==='lose'?'הפסד 😢':'תיקו 🤝';
let h='';if(!isTrain)h+='<div class="r-reward">🏆 גביעים: <b>'+(tr>0?'+':'')+tr+'</b></div>';
h+='<div class="r-reward">💰 מטבעות: <b>+'+(cn+mc)+'</b></div>';
if(mch){const cc=Math.floor(Math.random()*451)+50;GS.coins+=cc;saveGS();h+='<div class="r-reward">📦 תיבה: <b>+'+cc+'💰</b></div>';}
if(cch){const lk=CHARS.filter(c=>!isUnl(c.id));if(lk.length>0){const rc=lk[Math.floor(Math.random()*lk.length)];GS.unlocked.push(rc.id);saveGS();h+='<div class="r-reward">📦 דמות: <b>'+rc.icon+' '+rc.name+'</b></div>';}}
h+='<div class="r-reward">סה"כ: <b>'+GS.trophies+' 🏆</b></div>';
document.getElementById('rRewards').innerHTML=h;ov.classList.add('on');isTrain=false;saveGS();
}
// ==================== COLLECTION ====================
function goCollection(){showScreen('collectionScreen');renderCollection();}
function renderCollection(){
document.getElementById('cCoins').textContent=GS.coins;
const g=document.getElementById('upGrid');g.innerHTML='';
CHARS.forEach(c=>{
const u=isUnl(c.id),l=getLvl(c.id),s=getStats(c.id);
const mx=getMaxLvl(),cost=getCost(l),can=u&&l<mx&&GS.coins>=cost;
const d=document.createElement('div');d.className='up-card';
d.innerHTML='<div class="up-icon">'+(u?c.icon:'🔒')+'</div><div class="up-name">'+c.name+' '+(u?'(ר'+l+')':'')+'</div>'+(u?'<div class="up-stat">❤️'+s.hp+' ⚔️'+s.dmg+'</div>':'<div class="up-stat" style="color:#888">'+c.unlock+' גביעים</div>')+(u?(l<mx?'<button class="up-btn" '+(can?'':'disabled')+' onclick="upChar('+c.id+')">'+cost+'💰</button>':'<div class="up-max">מקס!</div>'):'');
g.appendChild(d);
});
}
function upChar(id){
const l=getLvl(id),c=getCost(id),mx=getMaxLvl();
if(GS.coins<c||l>=mx){toast(l>=mx?'רמה מקסימלית!':'אין מספיק מטבעות!');return;}
GS.coins-=c;GS.levels[id]=l+1;saveGS();renderCollection();toast(CHARS.find(x=>x.id===id).name+' רמה '+(l+1)+'!');
}
updateMenu();
</script>
</body>
</html>Game Source: Battle Realms
Creator: FrozenOtter31
Libraries: none
Complexity: complex (569 lines, 38.3 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: battle-realms-frozenotter31-mssjldiq" to link back to the original. Then publish at arcadelab.ai/publish.