Remix of BeastClash
by FrozenOtter311531 lines42.9 KB
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8" />
<title>BeastClash</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style>
* { box-sizing: border-box; margin: 0; padding: 0; user-select: none; -webkit-user-select: none; }
body {
background: linear-gradient(135deg, #1a1a2e, #16213e);
color: #fff;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
overflow: hidden;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
#game-container {
position: relative;
width: 100%;
max-width: 900px;
height: 100vh;
max-height: 600px;
background: radial-gradient(circle at center, #0f3460, #1a1a2e);
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
overflow: hidden;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
.screen {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: rgba(0,0,0,0.85);
z-index: 10;
transition: opacity 0.3s;
}
.screen.hidden {
opacity: 0;
pointer-events: none;
}
h1 {
font-size: 3rem;
marginืืฆืืื โ ืื ื **ืงืื ืืื ืืืืื ืืืจืฆื** ืฉื ืืฉืืง ืืคืืคื ืืกืื ืื BeastClash. ืืื ืงืืืฅ HTML ืืืื ืฉืืืื ืืช ืื ืืงืื (HTML, CSS, JavaScript). ืชืขืชืืง ืืช ืืงืื, ืชืฉืืืจ ืืงืืืฅ `index.html`, ืืชืคืชื ืืืคืืคื (Chrome/Edge/Safari) โ ืื ืืืืฉื ืืื ืืืืคืื.
```html
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8" />
<title>BeastClash</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style>
* { box-sizing: border-box; margin: 0; padding: 0; user-select: none; -webkit-user-select: none; }
body {
background: linear-gradient(135deg, #1a1a2e, #16213e);
color: #fff;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
overflow: hidden;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
#game-container {
position: relative;
width: 100%;
max-width: 900px;
height: 100vh;
max-height: 600px;
background: radial-gradient(circle at center, #0f3460, #1a1a2e);
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
overflow: hidden;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
.screen {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: rgba(0,0,0,0.85);
z-index: 10;
transition: opacity 0.3s;
}
.screen.hidden {
opacity: 0;
pointer-events: none;
}
h1 {
font-size: 3rem;
margin-bottom: 1rem;
text-shadow: 0 0 20px #e94560, 0 0 40px #e94560;
letter-spacing: 2px;
}
p {
font-size: 1.2rem;
margin-bottom: 2rem;
text-align: center;
max-width: 80%;
line-height: 1.6;
}
button {
background: linear-gradient(135deg, #e94560, #c23a51);
color: #fff;
border: none;
padding: 1rem 3rem;
font-size: 1.3rem;
border-radius: 50px;
cursor: pointer;
box-shadow: 0 5px 20px rgba(233,69,96,0.4);
transition: transform 0.2s, box-shadow 0.2s;
}
button:hover {
transform: scale(1.05);
box-shadow: 0 8px 30px rgba(233,69,96,0.6);
}
button:active {
transform: scale(0.98);
}
#ui-layer {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
pointer-events: none;
z-index: 5;
}
.mana-bar {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
width: 200px;
height: 30px;
background: rgba(0,0,0,0.6);
border-radius: 15px;
overflow: hidden;
border: 2px solid #4ecca3;
}
.mana-fill {
height: 100%;
background: linear-gradient(90deg, #4ecca3, #45b393);
width: 0%;
transition: width 0.3s;
}
.mana-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1rem;
font-weight: bold;
color: #fff;
text-shadow: 0 0 5px #000;
}
.card-hand {
position: absolute;
bottom: 80px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 15px;
pointer-events: auto;
}
.card {
width: 100px;
height: 140px;
background: linear-gradient(135deg, #2d4059, #1a1a2e);
border: 3px solid #e94560;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
box-shadow: 0 5px 15px rgba(0,0,0,0.5);
}
.card:hover {
transform: translateY(-10px) scale(1.05);
box-shadow: 0 10px 25px rgba(233,69,96,0.6);
}
.card-name {
font-size: 0.9rem;
font-weight: bold;
margin-bottom: 5px;
}
.card-cost {
font-size: 1.2rem;
color: #4ecca3;
font-weight: bold;
}
.card-desc {
font-size: 0.7rem;
text-align: center;
padding: 5px;
}
#health-display {
position: absolute;
top: 20px;
width: 100%;
display: flex;
justify-content: space-between;
padding: 0 40px;
font-size: 1.5rem;
font-weight: bold;
text-shadow: 0 0 10px #000;
}
.health-bar {
width: 200px;
height: 20px;
background: rgba(0,0,0,0.6);
border-radius: 10px;
overflow: hidden;
border: 2px solid #e94560;
}
.health-fill {
height: 100%;
background: linear-gradient(90deg, #e94560, #c23a51);
width: 100%;
transition: width 0.3s;
}
#message {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 2rem;
font-weight: bold;
color: #fff;
text-shadow: 0 0 20px #e94560;
opacity: 0;
transition: opacity 0.3s;
pointer-events: none;
}
</style>
</head>
<body>
<div id="game-container">
<canvas id="game-canvas"></canvas>
<div id="ui-layer">
<div id="health-display">
<div>
<div>ืฉืืงื</div>
<div class="health-bar"><div class="health-fill" id="player-health"></div></div>
</div>
<div>
<div>ืืจืื</div>
<div class="health-bar"><div class="health-fill" id="enemy-health"></div></div>
</div>
</div>
<div class="mana-bar">
<div class="mana-fill" id="mana-fill"></div>
<div class="mana-text" id="mana-text">0 / 10</div>
</div>
<div class="card-hand" id="card-hand"></div>
<div id="message"></div>
</div>
<div id="start-screen" class="screen">
<h1>๐พ BeastClash ๐ก๏ธ</h1>
<p>ืืืก ืืช ืืืจืื ืืงืจื ืืกืืจืืื!<br>ืืฆื ืืืืืืช, ื ืื ืืื ื, ืืืฉืื ืืช ืืืกืืก ืฉืื.</p>
<button id="start-btn">ืืชืื ืงืจื</button>
</div>
<div id="game-over-screen" class="screen hidden">
<h1 id="game-over-title">ื ืืฆืืื!</h1>
<p id="game-over-message">ืืืกืช ืืช ืืืจืื!</p>
<button id="restart-btn">ืฉืืง ืฉืื</button>
</div>
</div>
<script>
// === Game State ===
const canvas = document.getElementById('game-canvas');
const ctx = canvas.getContext('2d');
let gameState = 'start'; // start, playing, gameover
let lastTime = 0;
// === Game Constants ===
const MAX_MANA = 10;
const PLAYER_MAX_HEALTH = 100;
const ENEMY_MAX_HEALTH = 100;
// === Card Definitions ===
const CARDS = [
{ name: 'ืืื', cost: 2, attack: 15, health: 20, speed: 2, color: '#4ecca3', desc: 'ืืืืจ ืืืืง' },
{ name: 'ืืื', cost: 4, attack: 25, health: 40, speed: 1, color: '#e94560', desc: 'ืื ืง ืืืื' },
{ name: 'ื ืฅ', cost: 3, attack: 20, health: 15, speed: 3, color: '#f9ed69', desc: 'ืืืืจ ืืืื' }
];
// === Game Objects ===
let player = {
health: PLAYER_MAX_HEALTH,
mana: 3,
hand: [],
units: []
};
let enemy = {
health: ENEMY_MAX_HEALTH,
mana: 3,
units: []
};
let projectiles = [];
let particles = [];
// === Resize Canvas ===
function resizeCanvas() {
const container = document.getElementById('game-container');
canvas.width = container.clientWidth;
canvas.height = container.clientHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// === Utility Functions ===
function random(min, max) {
return Math.random() * (max - min) + min;
}
function showMessage(text, durationืืฆืืื โ ืื ื **ืงืื ืืื ืืืืื ืืืจืฆื** ืฉื ืืฉืืง ืืคืืคื ืืกืื ืื BeastClash. ืืื ืงืืืฅ HTML ืืืื ืฉืืืื ืืช ืื ืืงืื (HTML, CSS, JavaScript). ืชืขืชืืง ืืช ืืงืื, ืชืฉืืืจ ืืงืืืฅ `index.html`, ืืชืคืชื ืืืคืืคื (Chrome/Edge/Safari) โ ืื ืืืืฉื ืืื ืืืืคืื.
```html
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8" />
<title>BeastClash</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style>
* { box-sizing: border-box; margin: 0; padding: 0; user-select: none; -webkit-user-select: none; }
body {
background: linear-gradient(135deg, #1a1a2e, #16213e);
color: #fff;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
overflow: hidden;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
#game-container {
position: relative;
width: 100%;
max-width: 900px;
height: 100vh;
max-height: 600px;
background: radial-gradient(circle at center, #0f3460, #1a1a2e);
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
overflow: hidden;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
.screen {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: rgba(0,0,0,0.85);
z-index: 10;
transition: opacity 0.3s;
}
.screen.hidden {
opacity: 0;
pointer-events: none;
}
h1 {
font-size: 3rem;
margin-bottom: 1rem;
text-shadow: 0 0 20px #e94560, 0 0 40px #e94560;
letter-spacing: 2px;
}
p {
font-size: 1.2rem;
margin-bottom: 2rem;
text-align: center;
max-width: 80%;
line-height: 1.6;
}
button {
background: linear-gradient(135deg, #e94560, #c23a51);
color: #fff;
border: none;
padding: 1rem 3rem;
font-size: 1.3rem;
border-radius: 50px;
cursor: pointer;
box-shadow: 0 5px 20px rgba(233,69,96,0.4);
transition: transform 0.2s, box-shadow 0.2s;
}
button:hover {
transform: scale(1.05);
box-shadow: 0 8px 30px rgba(233,69,96,0.6);
}
button:active {
transform: scale(0.98);
}
#ui-layer {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
pointer-events: none;
z-index: 5;
}
.mana-bar {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
width: 200px;
height: 30px;
background: rgba(0,0,0,0.6);
border-radius: 15px;
overflow: hidden;
border: 2px solid #4ecca3;
}
.mana-fill {
height: 100%;
background: linear-gradient(90deg, #4ecca3, #45b393);
width: 0%;
transition: width 0.3s;
}
.mana-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1rem;
font-weight: bold;
color: #fff;
text-shadow: 0 0 5px #000;
}
.card-hand {
position: absolute;
bottom: 80px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 15px;
pointer-events: auto;
}
.card {
width: 100px;
height: 140px;
background: linear-gradient(135deg, #2d4059, #1a1a2e);
border: 3px solid #e94560;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
box-shadow: 0 5px 15px rgba(0,0,0,0.5);
}
.card:hover {
transform: translateY(-10px) scale(1.05);
box-shadow: 0 10px 25px rgba(233,69,96,0.6);
}
.card-name {
font-size: 0.9rem;
font-weight: bold;
margin-bottom: 5px;
}
.card-cost {
font-size: 1.2rem;
color: #4ecca3;
font-weight: bold;
}
.card-desc {
font-size: 0.7rem;
text-align: center;
padding: 5px;
}
#health-display {
position: absolute;
top: 20px;
width: 100%;
display: flex;
justify-content: space-between;
padding: 0 40px;
font-size: 1.5rem;
font-weight: bold;
text-shadow: 0 0 10px #000;
}
.health-bar {
width: 200px;
height: 20px;
background: rgba(0,0,0,0.6);
border-radius: 10px;
overflow: hidden;
border: 2px solid #e94560;
}
.health-fill {
height: 100%;
background: linear-gradient(90deg, #e94560, #c23a51);
width: 100%;
transition: width 0.3s;
}
#message {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 2rem;
font-weight: bold;
color: #fff;
text-shadow: 0 0 20px #e94560;
opacity: 0;
transition: opacity 0.3s;
pointer-events: none;
}
</style>
</head>
<body>
<div id="game-container">
<canvas id="game-canvas"></canvas>
<div id="ui-layer">
<div id="health-display">
<div>
<div>ืฉืืงื</div>
<div class="health-bar"><div class="health-fill" id="player-health"></div></div>
</div>
<div>
<div>ืืจืื</div>
<div class="health-bar"><div class="health-fill" id="enemy-health"></div></div>
</div>
</div>
<div class="mana-bar">
<div class="mana-fill" id="mana-fill"></div>
<div class="mana-text" id="mana-text">0 / 10</div>
</div>
<div class="card-hand" id="card-hand"></div>
<div id="message"></div>
</div>
<div id="start-screen" class="screen">
<h1>๐พ BeastClash ๐ก๏ธ</h1>
<p>ืืืก ืืช ืืืจืื ืืงืจื ืืกืืจืืื!<br>ืืฆื ืืืืืืช, ื ืื ืืื ื, ืืืฉืื ืืช ืืืกืืก ืฉืื.</p>
<button id="start-btn">ืืชืื ืงืจื</button>
</div>
<div id="game-over-screen" class="screen hidden">
<h1 id="game-over-title">ื ืืฆืืื!</h1>
<p id="game-over-message">ืืืกืช ืืช ืืืจืื!</p>
<button id="restart-btn">ืฉืืง ืฉืื</button>
</div>
</div>
<script>
// === Game State ===
const canvas = document.getElementById('game-canvas');
const ctx = canvas.getContext('2d');
let gameState = 'start'; // start, playing, gameover
let lastTime = 0;
// === Game Constants ===
const MAX_MANA = 10;
const PLAYER_MAX_HEALTH = 100;
const ENEMY_MAX_HEALTH = 100;
// === Card Definitions ===
const CARDS = [
{ name: 'ืืื', cost: 2, attack: 15, health: 20, speed: 2, color: '#4ecca3', desc: 'ืืืืจ ืืืืง' },
{ name: 'ืืื', cost: 4, attack: 25, health: 40, speed: 1, color: '#e94560', desc: 'ืื ืง ืืืื' },
{ name: 'ื ืฅ', cost: 3, attack: 20, health: 15, speed: 3, color: '#f9ed69', desc: 'ืืืืจ ืืืื' }
];
// === Game Objects ===
let player = {
health: PLAYER_MAX_HEALTH,
mana: 3,
hand: [],
units: []
};
let enemy = {
health: ENEMY_MAX_HEALTH,
mana: 3,
units: []
};
let projectiles = [];
let particles = [];
// === Resize Canvas ===
function resizeCanvas() {
const container = document.getElementById('game-container');
canvas.width = container.clientWidth;
canvas.height = container.clientHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// === Utility Functions ===
function random(min, max) {
return Math.random() * (max - min) + min;
}
function showMessage(text, duration = 2000) {
const msg = document.getElementById('message');
msg.textContent = text;
msg.style.opacity = 1;
setTimeout(() => {
msg.style.opacity = 0;
}, duration);
}
// === Input Handling ===
let selectedCard = null;
let isDragging = false;
let dragStart = { x: 0, y: 0 };
canvas.addEventListener('pointerdown', (e) => {
if (gameState !== 'playing') return;
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// Check if clicking on arena to spawn unit
if (y < canvas.height * 0.6) {
if (selectedCard && player.mana >= selectedCard.cost) {
spawnUnit(selectedCard, x, y, true);
player.mana -= selectedCard.cost;
updateUI();
selectedCard = null;
renderHand();
}
}
isDragging = true;
dragStart = { x, y };
});
canvas.addEventListener('pointermove', (e) => {
if (!isDragging) return;
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// Simple drag effect (could be expanded)
});
canvas.addEventListener('pointerup', () => {
isDragging = false;
});
// === Game Functions ===
function spawnUnit(card, x, y, isPlayer) {
const unit = {
...card,
x,
y,
maxHealth: card.health,
isPlayer,
target: null,
attackCooldown: 0
};
if (isPlayer) {
player.units.push(unit);
} else {
enemy.units.push(unit);
}
// Spawn particles
for (let i = 0; i < 10; i++) {
particles.push({
x,
y,
vx: random(-3, 3),
vy: random(-3, 3),
life: 1,
color: card.color
});
}
}
function drawCard(x, y, card, isSelectable = false) {
ctx.fillStyle = card.color;
ctx.fillRect(x - 40, y - 60, 80, 100);
ctx.fillStyle = '#fff';
ctx.font = 'bold 14px system-ui';
ctx.textAlign = 'center';
ctx.fillText(card.name, x, y - 40);
ctx.font = '12px system-ui';
ctx.fillText(`ืขืืืช: ${card.cost}`, x, y - 20);
ctx.fillText(`ืืชืงืคื: ${card.attack}`, x, y);
ctx.fillText(`ืืืื: ${card.health}`, x, y + 20);
if (isSelectable) {
ctx.strokeStyle = '#fff';
ctx.lineWidth = 3;
ctx.strokeRect(x - 42, y - 62, 84, 104);
}
}
function updateUnits(delta) {
// Player units
player.units.forEach(unit => {
if (unit.attackCooldown > 0) unit.attackCooldown -= delta;
// Find nearest enemy unit or enemy base
let nearest = null;
let nearestDist = Infinity;
enemy.units.forEach(enemyUnit => {
const dist = Math.hypot(enemyUnit.x - unit.x, enemyUnit.y - unit.y);
if (dist < nearestDist) {
nearestDist = dist;
nearest = enemyUnit;
}
});
// If no enemy units, target enemy base
if (!nearest) {
const baseDist = Math.hypot(canvas.width / 2 - unit.x, 0 - unit.y);
if (baseDist < nearestDist) {
nearestDist = baseDist;
nearest = { x: canvas.width / 2, y: 0, isBase: true };
}
}
// Move towards target
if (nearest && nearestDist > 30) {
const angle = Math.atan2(nearest.y - unit.y, nearest.x - unit.x);
unit.x += Math.cos(angle) * unit.speed;
unit.y += Math.sin(angle) * unit.speed;
} else if (nearest) {
// Attack
if (unit.attackCooldown <= 0) {
if (nearest.isBase) {
enemy.health -= unit.attack;
showMessage(`-${unit.attack} ืืืกืืก!`);
} else {
nearest.health -= unit.attack;
if (nearest.health <= 0) {
enemy.units = enemy.units.filter(u => u !== nearest);
}
}
unit.attackCooldown = 1; // 1 second cooldown
// Attack projectile
projectiles.push({
x: unit.x,
y: unit.y,
targetX: nearest.x,
targetY: nearest.y,
life: 0.5,
color: unit.color
});
}
}
});
// Enemy units (simple AI)
enemyืืฆืืื โ ืื ื **ืงืื ืืื ืืืืื ืืืจืฆื** ืฉื ืืฉืืง ืืคืืคื ืืกืื ืื BeastClash. ืืื ืงืืืฅ HTML ืืืื ืฉืืืื ืืช ืื ืืงืื (HTML, CSS, JavaScript). ืชืขืชืืง ืืช ืืงืื, ืชืฉืืืจ ืืงืืืฅ `index.html`, ืืชืคืชื ืืืคืืคื (Chrome/Edge/Safari) โ ืื ืืืืฉื ืืื ืืืืคืื.
```html
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8" />
<title>BeastClash</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style>
* { box-sizing: border-box; margin: 0; padding: 0; user-select: none; -webkit-user-select: none; }
body {
background: linear-gradient(135deg, #1a1a2e, #16213e);
color: #fff;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
overflow: hidden;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
#game-container {
position: relative;
width: 100%;
max-width: 900px;
height: 100vh;
max-height: 600px;
background: radial-gradient(circle at center, #0f3460, #1a1a2e);
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
overflow: hidden;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
.screen {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: rgba(0,0,0,0.85);
z-index: 10;
transition: opacity 0.3s;
}
.screen.hidden {
opacity: 0;
pointer-events: none;
}
h1 {
font-size: 3rem;
margin-bottom: 1rem;
text-shadow: 0 0 20px #e94560, 0 0 40px #e94560;
letter-spacing: 2px;
}
p {
font-size: 1.2rem;
margin-bottom: 2rem;
text-align: center;
max-width: 80%;
line-height: 1.6;
}
button {
background: linear-gradient(135deg, #e94560, #c23a51);
color: #fff;
border: none;
padding: 1rem 3rem;
font-size: 1.3rem;
border-radius: 50px;
cursor: pointer;
box-shadow: 0 5px 20px rgba(233,69,96,0.4);
transition: transform 0.2s, box-shadow 0.2s;
}
button:hover {
transform: scale(1.05);
box-shadow: 0 8px 30px rgba(233,69,96,0.6);
}
button:active {
transform: scale(0.98);
}
#ui-layer {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
pointer-events: none;
z-index: 5;
}
.mana-bar {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
width: 200px;
height: 30px;
background: rgba(0,0,0,0.6);
border-radius: 15px;
overflow: hidden;
border: 2px solid #4ecca3;
}
.mana-fill {
height: 100%;
background: linear-gradient(90deg, #4ecca3, #45b393);
width: 0%;
transition: width 0.3s;
}
.mana-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1rem;
font-weight: bold;
color: #fff;
text-shadow: 0 0 5px #000;
}
.card-hand {
position: absolute;
bottom: 80px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 15px;
pointer-events: auto;
}
.card {
width: 100px;
height: 140px;
background: linear-gradient(135deg, #2d4059, #1a1a2e);
border: 3px solid #e94560;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
box-shadow: 0 5px 15px rgba(0,0,0,0.5);
}
.card:hover {
transform: translateY(-10px) scale(1.05);
box-shadow: 0 10px 25px rgba(233,69,96,0.6);
}
.card-name {
font-size: 0.9rem;
font-weight: bold;
margin-bottom: 5px;
}
.card-cost {
font-size: 1.2rem;
color: #4ecca3;
font-weight: bold;
}
.card-desc {
font-size: 0.7rem;
text-align: center;
padding: 5px;
}
#health-display {
position: absolute;
top: 20px;
width: 100%;
display: flex;
justify-content: space-between;
padding: 0 40px;
font-size: 1.5rem;
font-weight: bold;
text-shadow: 0 0 10px #000;
}
.health-bar {
width: 200px;
height: 20px;
background: rgba(0,0,0,0.6);
border-radius: 10px;
overflow: hidden;
border: 2px solid #e94560;
}
.health-fill {
height: 100%;
background: linear-gradient(90deg, #e94560, #c23a51);
width: 100%;
transition: width 0.3s;
}
#message {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 2rem;
font-weight: bold;
color: #fff;
text-shadow: 0 0 20px #e94560;
opacity: 0;
transition: opacity 0.3s;
pointer-events: none;
}
</style>
</head>
<body>
<div id="game-container">
<canvas id="game-canvas"></canvas>
<div id="ui-layer">
<div id="health-display">
<div>
<div>ืฉืืงื</div>
<div class="health-bar"><div class="health-fill" id="player-health"></div></div>
</div>
<div>
<div>ืืจืื</div>
<div class="health-bar"><div class="health-fill" id="enemy-health"></div></div>
</div>
</div>
<div class="mana-bar">
<div class="mana-fill" id="mana-fill"></div>
<div class="mana-text" id="mana-text">0 / 10</div>
</div>
<div class="card-hand" id="card-hand"></div>
<div id="message"></div>
</div>
<div id="start-screen" class="screen">
<h1>๐พ BeastClash ๐ก๏ธ</h1>
<p>ืืืก ืืช ืืืจืื ืืงืจื ืืกืืจืืื!<br>ืืฆื ืืืืืืช, ื ืื ืืื ื, ืืืฉืื ืืช ืืืกืืก ืฉืื.</p>
<button id="start-btn">ืืชืื ืงืจื</button>
</div>
<div id="game-over-screen" class="screen hidden">
<h1 id="game-over-title">ื ืืฆืืื!</h1>
<p id="game-over-message">ืืืกืช ืืช ืืืจืื!</p>
<button id="restart-btn">ืฉืืง ืฉืื</button>
</div>
</div>
<script>
// === Game State ===
const canvas = document.getElementById('game-canvas');
const ctx = canvas.getContext('2d');
let gameState = 'start'; // start, playing, gameover
let lastTime = 0;
// === Game Constants ===
const MAX_MANA = 10;
const PLAYER_MAX_HEALTH = 100;
const ENEMY_MAX_HEALTH = 100;
// === Card Definitions ===
const CARDS = [
{ name: 'ืืื', cost: 2, attack: 15, health: 20, speed: 2, color: '#4ecca3', desc: 'ืืืืจ ืืืืง' },
{ name: 'ืืื', cost: 4, attack: 25, health: 40, speed: 1, color: '#e94560', desc: 'ืื ืง ืืืื' },
{ name: 'ื ืฅ', cost: 3, attack: 20, health: 15, speed: 3, color: '#f9ed69', desc: 'ืืืืจ ืืืื' }
];
// === Game Objects ===
let player = {
health: PLAYER_MAX_HEALTH,
mana: 3,
hand: [],
units: []
};
let enemy = {
health: ENEMY_MAX_HEALTH,
mana: 3,
units: []
};
let projectiles = [];
let particles = [];
// === Resize Canvas ===
function resizeCanvas() {
const container = document.getElementById('game-container');
canvas.width = container.clientWidth;
canvas.height = container.clientHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// === Utility Functions ===
function random(min, max) {
return Math.random() * (max - min) + min;
}
function showMessage(text, duration = 2000) {
const msg = document.getElementById('message');
msg.textContent = text;
msg.style.opacity = 1;
setTimeout(() => {
msg.style.opacity = 0;
}, duration);
}
// === Input Handling ===
let selectedCard = null;
let isDragging = false;
let dragStart = { x: 0, y: 0 };
canvas.addEventListener('pointerdown', (e) => {
if (gameState !== 'playing') return;
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// Check if clicking on arena to spawn unit
if (y < canvas.height * 0.6) {
if (selectedCard && player.mana >= selectedCard.cost) {
spawnUnit(selectedCard, x, y, true);
player.mana -= selectedCard.cost;
updateUI();
selectedCard = null;
renderHand();
}
}
isDragging = true;
dragStart = { x, y };
});
canvas.addEventListener('pointermove', (e) => {
if (!isDragging) return;
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// Simple drag effect (could be expanded)
});
canvas.addEventListener('pointerup', () => {
isDragging = false;
});
// === Game Functions ===
function spawnUnit(card, x, y, isPlayer) {
const unit = {
...card,
x,
y,
maxHealth: card.health,
isPlayer,
target: null,
attackCooldown: 0
};
if (isPlayer) {
player.units.push(unit);
} else {
enemy.units.push(unit);
}
// Spawn particles
for (let i = 0; i < 10; i++) {
particles.push({
x,
y,
vx: random(-3, 3),
vy: random(-3, 3),
life: 1,
color: card.color
});
}
}
function drawCard(x, y, card, isSelectable = false) {
ctx.fillStyle = card.color;
ctx.fillRect(x - 40, y - 60, 80, 100);
ctx.fillStyle = '#fff';
ctx.font = 'bold 14px system-ui';
ctx.textAlign = 'center';
ctx.fillText(card.name, x, y - 40);
ctx.font = '12px system-ui';
ctx.fillText(`ืขืืืช: ${card.cost}`, x, y - 20);
ctx.fillText(`ืืชืงืคื: ${card.attack}`, x, y);
ctx.fillText(`ืืืื: ${card.health}`, x, y + 20);
if (isSelectable) {
ctx.strokeStyle = '#fff';
ctx.lineWidth = 3;
ctx.strokeRect(x - 42, y - 62, 84, 104);
}
}
function updateUnits(delta) {
// Player units
player.units.forEach(unit => {
if (unit.attackCooldown > 0) unit.attackCooldown -= delta;
// Find nearest enemy unit or enemy base
let nearest = null;
let nearestDist = Infinity;
enemy.units.forEach(enemyUnit => {
const dist = Math.hypot(enemyUnit.x - unit.x, enemyUnit.y - unit.y);
if (dist < nearestDist) {
nearestDist = dist;
nearest = enemyUnit;
}
});
// If no enemy units, target enemy base
if (!nearest) {
const baseDist = Math.hypot(canvas.width / 2 - unit.x, 0 - unit.y);
if (baseDist < nearestDist) {
nearestDist = baseDist;
nearest = { x: canvas.width / 2, y: 0, isBase: true };
}
}
// Move towards target
if (nearest && nearestDist > 30) {
const angle = Math.atan2(nearest.y - unit.y, nearest.x - unit.x);
unit.x += Math.cos(angle) * unit.speed;
unit.y += Math.sin(angle) * unit.speed;
} else if (nearest) {
// Attack
if (unit.attackCooldown <= 0) {
if (nearest.isBase) {
enemy.health -= unit.attack;
showMessage(`-${unit.attack} ืืืกืืก!`);
} else {
nearest.health -= unit.attack;
if (nearest.health <= 0) {
enemy.units = enemy.units.filter(u => u !== nearest);
}
}
unit.attackCooldown = 1; // 1 second cooldown
// Attack projectile
projectiles.push({
x: unit.x,
y: unit.y,
targetX: nearest.x,
targetY: nearest.y,
life: 0.5,
color: unit.color
});
}
}
});
// Enemy units (simple AI)
enemy.units.forEach(unit => {
if (unit.attackCooldown > 0) unit.attackCooldown -= delta;
// Find nearest player unit or player base
let nearest = null;
let nearestDist = Infinity;
player.units.forEach(playerUnit => {
const dist = Math.hypot(playerUnit.x - unit.x, playerUnit.y - unit.y);
if (dist < nearestDist) {
nearestDist = dist;
nearest = playerUnit;
}
});
// If no player units, target player base
if (!nearest) {
const baseDist = Math.hypot(canvas.width / 2 - unit.x, canvas.height - unit.y);
if (baseDist < nearestDist) {
nearestDist = baseDist;
nearest = { x: canvas.width / 2, y: canvas.height, isBase: true };
}
}
// Move towards target
if (nearest && nearestDist > 30) {
const angle = Math.atan2(nearest.y - unit.y, nearest.x - unit.x);
unit.x += Math.cos(angle) * unit.speed;
unit.y += Math.sin(angle) * unit.speed;
} else if (nearest) {
// Attack
if (unit.attackCooldown <= 0) {
if (nearest.isBase) {
player.health -= unit.attack;
showMessage(`-${unit.attack} ืืืกืืก!`);
} else {
nearest.health -= unit.attack;
if (nearest.health <= 0) {
player.units = player.units.filter(u => u !== nearest);
}
}
unit.attackCooldown = 1;
// Attack projectile
projectiles.push({
x: unit.x,
y: unit.y,
targetX: nearest.x,
targetY: nearest.y,
life: 0.5,
color: unit.color
});
}
}
});
}
function updateParticles(delta) {
particles.forEach(p => {
p.x += p.vx;
p.y += p.vy;
p.life -= delta;
});
particles = particles.filter(p => p.life > 0);
}
function updateProjectiles(delta) {
projectiles.forEach(p => {
p.life -= delta;
});
projectiles = projectiles.filter(p => p.life > 0);
}
function drawUnits() {
// Player units
player.units.forEach(unit => {
ctx.fillStyle = unit.color;
ctx.beginPath();
ctx.arc(unit.x, unit.y, 20, 0, Math.PI * 2);
ctx.fill();
// Health bar
const healthPercent = unit.health / unit.maxHealth;
ctx.fillStyle = '#e94560';
ctx.fillRect(unit.x - 20, unit.y - 30, 40, 5);
ctx.fillStyle = '#4ecca3';
ctx.fillRect(unit.x - 20, unit.y - 30, 40 * healthPercent, 5);
});
// Enemy units
enemy.units.forEach(unit => {
ctx.fillStyle = unit.color;
ctx.beginPath();
ctx.arc(unit.x, unit.y, 20, 0, Math.PI * 2);
ctx.fill();
// Health bar
const healthPercent = unit.health / unit.maxHealth;
ctx.fillStyle = '#e94560';
ctx.fillRect(unit.x - 20, unit.y - 30, 40, 5);
ctx.fillStyle = '#4ecca3';
ctx.fillRect(unit.x - 20, unit.y - 30, 40 * healthPercent, 5);
});
}
function drawParticles() {
particles.forEach(p => {
ctx.fillStyle = p.color;
ctx.globalAlpha = p.life;
ctx.beginPath();
ctx.arc(p.x, p.y, 5, 0, Math.PI * 2);
ctx.fill();
ctx.globalAlpha = 1;
});
}
function drawProjectiles() {
projectiles.forEach(p => {
ctx.strokeStyle = p.color;
ctx.lineWidth = 3;
ctx.globalAlpha = p.life * 2;
ctx.beginPath();
ctx.moveTo(p.x, p.y);
ctx.lineTo(p.targetX, p.targetY);
ctx.stroke();
ctx.globalAlpha = 1;
});
}
function drawArena() {
// Draw arena background
ctx.fillStyle = 'rgba(0,0,0,0.3)';
ctx.fillRect(0, canvas.height * 0.4, canvas.width, canvas.height * 0.6);
// Draw center line
ctx.strokeStyle = 'rgba(255,255,255,0.2)';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(0, canvas.height * 0.5);
ctx.lineTo(canvas.width, canvas.height * 0.5);
ctx.stroke();
}
function updateUI() {
// Update mana
const manaFill = document.getElementById('mana-fill');
const manaText = document.getElementById('mana-text');
manaFill.style.width = `${(player.mana / MAX_MANA) * 100}%`;
manaText.textContent = `${player.mana} / ${MAX_MANA}`;
// Update health
const playerHealth = document.getElementById('player-health');
const enemyHealth = document.getElementById('enemy-health');
playerHealth.style.width = `${(player.health / PLAYER_MAX_HEALTH) * 100}%`;
enemyHealth.style.width = `${(enemy.health / ENEMY_MAX_HEALTH) * 100}%`;
}
function renderHand() {
const handContainer = document.getElementById('card-hand');
handContainer.innerHTML = '';
player.hand.forEach((card, index) => {
const cardEl = document.createElement('div');
cardEl.className = 'card';
cardEl.innerHTML = `
<div class="card-name">${card.name}</div>
<div class="card-cost">${card.cost} ๐</div>
<div class="card-desc">${card.desc}</div>
`;
cardEl.style.borderColor = card.color;
cardEl.addEventListener('click', () => {
if (player.mana >= card.cost) {
selectedCard = card;
showMessage(`ื ืืืจ: ${card.name}`);
document.querySelectorAll('.card').forEach(c => c.style.transform = 'none');
cardEl.style.transform = 'translateY(-20px) scale(1.1)';
} else {
showMessage('ืืื ืืกืคืืง ืืื ื!');
}
});
handContainer.appendChild(cardEl);
});
}
function spawnEnemyUnit() {
if (enemy.mana >= 2 && Math.random() < 0.02) {
const card = CARDS[Math.floor(Math.random() * CARDS.length)];
if (enemy.mana >= card.cost) {
const x = random(canvas.width * 0.2, canvas.width * 0.8);
const y = random(canvas.height * 0.1, canvas.height * 0.3);
spawnUnit(card, x, y, false);
enemy.mana -= card.cost;
}
}
}
function checkGameOver() {
if (enemy.health <= 0) {
gameState = 'gameover';
document.getElementById('game-over-title').textContent = '๐ ื ืืฆืืื!';
document.getElementById('game-over-message').textContent = 'ืืืกืช ืืช ืืืจืื!';
document.getElementById('game-over-screen').classList.remove('hidden');
} else if (player.health <= 0) {
gameState = 'gameover';
document.getElementById('game-over-title').textContent = '๐ ืืคืกื';
document.getElementById('game-over-message').textContent = 'ืืืจืื ืืืืก ืืืชื...';
document.getElementById('game-over-screen').classList.remove('hidden');
}
}
function gameLoop(timestamp) {
const delta = (timestamp - lastTime) / 1000;
lastTime = timestamp;
if (gameState === 'playing') {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw arena
drawArena();
// Update game objects
updateUnits(delta);
updateParticles(delta);
updateProjectiles(delta);
spawnEnemyUnit();
// Regenerate mana
if (player.mana < MAX_MANA) {
player.mana += delta * 0.5; // 0.5 mana per second
}
if (enemy.mana < MAX_MANA) {
enemy.mana += delta * 0.5;
}
// Draw game objects
drawProjectiles();
drawParticles();
drawUnits();
// Update UI
updateUI();
// Check game over
checkGameOver();
}
requestAnimationFrame(gameLoop);
}
function startGame() {
gameState = 'playing';
player.health = PLAYER_MAX_HEALTH;
player.mana = 3;
player.hand = [];
player.units = [];
enemy.health = ENEMY_MAX_HEALTH;
enemy.mana = 3;
enemy.units = [];
projectiles = [];
particles = [];
// Give player starting hand
for (let i = 0; i < 3; i++) {
const randomCard = CARDS[Math.floor(Math.random() * CARDS.length)];
player.hand.push({ ...randomCard });
}
renderHand();
updateUI();
document.getElementById('start-screen').classList.add('hidden');
document.getElementById('game-over-screen').classList.add('hidden');
lastTime = performance.now();
requestAnimationFrame(gameLoop);
}
// === Event Listeners ===
document.getElementById('start-btn').addEventListener('click', startGame);
document.getElementById('restart-btn').addEventListener('click', startGame);
// Initial render
resizeCanvas();
</script>
</body>
</html>Game Source: Remix of BeastClash
Creator: FrozenOtter31
Libraries: none
Complexity: complex (1531 lines, 42.9 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: remix-of-beastclash-frozenotter31" to link back to the original. Then publish at arcadelab.ai/publish.