Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Tree Kingdom Battler</title>
- <style>
- * {
- box-sizing: border-box;
- margin: 0;
- padding: 0;
- }
- body {
- margin: 0;
- padding: 20px;
- background: linear-gradient(to bottom, #1a2a6c, #b21f1f, #fdbb2d);
- display: flex;
- justify-content: center;
- align-items: flex-start;
- min-height: 100vh;
- font-family: 'Courier New', monospace;
- color: #fff;
- overflow: auto;
- }
- #game-wrapper {
- display: flex;
- flex-direction: column;
- align-items: center;
- background: rgba(0, 0, 0, 0.8);
- padding: 20px;
- border-radius: 10px;
- box-shadow: 0 0 20px rgba(255, 215, 0, 0.5);
- max-width: 1400px;
- width: 100%;
- }
- h1 {
- color: #FFD700;
- text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
- margin-bottom: 20px;
- text-align: center;
- font-size: 2em;
- }
- #game-container {
- position: relative;
- display: flex;
- flex-wrap: wrap;
- justify-content: center;
- gap: 20px;
- width: 100%;
- }
- #canvas-area {
- position: relative;
- }
- canvas {
- border: 3px solid #FFD700;
- background-color: #222;
- border-radius: 5px;
- image-rendering: pixelated;
- image-rendering: -moz-crisp-edges;
- image-rendering: crisp-edges;
- }
- #ui-sidebar {
- display: flex;
- flex-direction: column;
- gap: 20px;
- min-width: 300px;
- max-width: 400px;
- }
- #hud {
- background: rgba(0,0,0,0.8);
- padding: 15px;
- border-radius: 5px;
- border: 1px solid #FFD700;
- width: 100%;
- }
- #log {
- width: 100%;
- height: 150px;
- overflow-y: scroll;
- background: rgba(0,0,0,0.8);
- padding: 15px;
- border-radius: 5px;
- font-size: 12px;
- border: 1px solid #FFD700;
- }
- #log::-webkit-scrollbar {
- width: 8px;
- }
- #log::-webkit-scrollbar-thumb {
- background: #FFD700;
- border-radius: 4px;
- }
- #inventory {
- background: rgba(0,0,0,0.8);
- padding: 15px;
- border-radius: 5px;
- border: 1px solid #FFD700;
- width: 100%;
- }
- #tree-map {
- background: rgba(0,0,0,0.8);
- padding: 15px;
- border-radius: 5px;
- font-size: 12px;
- max-height: 300px;
- overflow: auto;
- border: 1px solid #FFD700;
- width: 100%;
- }
- #controls {
- margin-top: 20px;
- background: rgba(0,0,0,0.8);
- padding: 15px;
- border-radius: 5px;
- text-align: center;
- border: 1px solid #FFD700;
- width: 100%;
- max-width: 1000px;
- }
- .health-bar {
- display: inline-block;
- width: 100px;
- height: 10px;
- background: #555;
- border-radius: 5px;
- overflow: hidden;
- margin-left: 5px;
- }
- .health-fill {
- height: 100%;
- background: linear-gradient(to right, #ff0000, #ff4500);
- transition: width 0.3s ease;
- }
- .neural-overlay {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- pointer-events: none;
- background: repeating-linear-gradient(
- 0deg,
- rgba(0, 255, 0, 0.1),
- rgba(0, 255, 0, 0.1) 1px,
- rgba(255, 255, 255, 0.05) 1px,
- rgba(255, 255, 255, 0.05) 2px
- );
- opacity: 0.6;
- z-index: 10;
- display: none;
- animation: neural-flicker 2s infinite;
- }
- @keyframes neural-flicker {
- 0%, 100% { opacity: 0.6; }
- 50% { opacity: 0.3; }
- }
- .debug-button {
- position: fixed;
- bottom: 10px;
- right: 10px;
- padding: 8px 12px;
- background: rgba(0,0,0,0.8);
- color: white;
- border: 1px solid #FFD700;
- border-radius: 5px;
- cursor: pointer;
- z-index: 100;
- font-family: 'Courier New', monospace;
- font-size: 12px;
- }
- .debug-button:hover {
- background: rgba(255, 215, 0, 0.2);
- }
- .completion-message {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- background: rgba(0, 0, 0, 0.95);
- padding: 30px;
- border-radius: 15px;
- border: 3px solid #FFD700;
- text-align: center;
- z-index: 20;
- display: none;
- box-shadow: 0 0 30px rgba(255, 215, 0, 0.8);
- }
- .completion-message h2 {
- color: #FFD700;
- margin-bottom: 10px;
- }
- /* Responsive design for mobile */
- @media (max-width: 768px) {
- body {
- padding: 10px;
- }
- #game-container {
- flex-direction: column;
- }
- canvas {
- width: 100%;
- height: auto;
- max-width: 500px;
- }
- #ui-sidebar {
- min-width: auto;
- max-width: none;
- width: 100%;
- }
- h1 {
- font-size: 1.5em;
- }
- }
- /* High DPI displays */
- @media (min-width: 1920px) {
- canvas {
- width: 800px;
- height: 800px;
- }
- #game-wrapper {
- padding: 30px;
- }
- #ui-sidebar {
- min-width: 350px;
- }
- }
- @media (min-width: 2560px) {
- canvas {
- width: 900px;
- height: 900px;
- }
- #game-wrapper {
- padding: 40px;
- }
- #ui-sidebar {
- min-width: 400px;
- }
- }
- </style>
- </head>
- <body>
- <div id="game-wrapper">
- <h1>๐ณ Tree Kingdom Battler โ๏ธ</h1>
- <div id="game-container">
- <div id="canvas-area">
- <canvas id="gameCanvas"></canvas>
- <div class="neural-overlay" id="neuralOverlay"></div>
- <div class="completion-message" id="completionMessage">
- <h2>๐ Level Complete! ๐</h2>
- <p>Portals to new areas have appeared</p>
- </div>
- </div>
- <div id="ui-sidebar">
- <div id="hud">
- <div>โค๏ธ Health: <span id="health">100</span>
- <div class="health-bar"><div class="health-fill" id="health-bar" style="width: 100%;"></div></div></div>
- <div>โญ Level: <span id="level">1</span> | ๐ฐ Gold: <span id="gold">0</span></div>
- <div>๐ Node: <span id="currentNode">Root</span></div>
- </div>
- <div id="inventory">
- <div><strong>โ๏ธ Equipment:</strong></div>
- <div>๐ก๏ธ Weapon: <span id="weapon">Sword</span></div>
- <div>๐ฏ Abilities: <span id="abilities">Sword, Bow</span></div>
- <div>โจ Special: <span id="special">None</span></div>
- </div>
- <div id="tree-map">
- <strong>๐บ๏ธ Tree Map:</strong>
- <div id="tree-structure"></div>
- </div>
- <div id="log"></div>
- </div>
- </div>
- <div id="controls">
- <p>๐ฎ <strong>Controls:</strong></p>
- <p>โฌ ๏ธโก๏ธโฌ๏ธโฌ๏ธ Arrow Keys or WASD to Move | Auto-attack enemies | Collect treasures to progress</p>
- <p>๐ Press 'Q' to switch weapons | โก Press 'E' for special ability (when available)</p>
- <p>๐๏ธ Press 'M' to toggle neural overlay | ๐ Press 'R' to reset level</p>
- </div>
- </div>
- <button class="debug-button" onclick="toggleNeuralOverlay()">Neural View (M)</button>
- <script>
- // Core game constants - will be adjusted based on screen size
- let CANVAS_WIDTH = 600;
- let CANVAS_HEIGHT = 600;
- let BLOX_SIZE = 30;
- let MAP_WIDTH = CANVAS_WIDTH / BLOX_SIZE;
- let MAP_HEIGHT = CANVAS_HEIGHT / BLOX_SIZE;
- const PLAYER_SPEED = 180;
- const ENEMY_BASE_SPEED = 60;
- const SWORD_RANGE = 1.5 * BLOX_SIZE;
- const BOW_RANGE_MIN = SWORD_RANGE;
- const BOW_RANGE_MAX = 10 * BLOX_SIZE;
- const SPELL_RANGE_MIN = 2 * BLOX_SIZE;
- const SPELL_RANGE_MAX = 5 * BLOX_SIZE;
- const ARROW_SPEED = 300;
- const ATTACK_COOLDOWN = 500;
- const MAX_ENEMIES = 11;
- const MIN_ENEMIES = 7;
- // Enhanced color palette for better visibility
- const COLORS = {
- grass: '#2d5016',
- tree: '#8B4513',
- wall: '#A52A2A',
- player: '#FFD700',
- enemyRed: '#FF4444',
- enemyOrange: '#FF8844',
- enemyBlue: '#4444FF',
- enemyPurple: '#8844FF',
- sword: '#C0C0C0',
- bow: '#8B4513',
- arrow: '#DAA520',
- spell: '#9400D3',
- treasure: '#FFFF00',
- healthPotion: '#FF6666',
- manaPotion: '#6666FF',
- portal: '#00FF88'
- };
- // Enemy types with balanced stats
- const ENEMY_TYPES = [
- { color: 'enemyRed', size: 0.5, type: 'Goblin Scout', damage: 5, health: 30, speed: ENEMY_BASE_SPEED * 1.5, weapon: 'sword' },
- { color: 'enemyOrange', size: 0.75, type: 'Fire Mage', damage: 8, health: 50, speed: ENEMY_BASE_SPEED, weapon: 'spell' },
- { color: 'enemyBlue', size: 0.9, type: 'Armored Knight', damage: 12, health: 80, speed: ENEMY_BASE_SPEED * 0.8, weapon: 'mace' },
- { color: 'enemyPurple', size: 1.1, type: 'Shadow Archer', damage: 10, health: 60, speed: ENEMY_BASE_SPEED * 1.2, weapon: 'bow-spell' }
- ];
- const ABILITIES = ['fireball', 'shield', 'speed-boost', 'health-regen', 'double-damage'];
- // Tree structure based on file system - optimized for gameplay
- class Node {
- constructor(name, type = 'folder', children = []) {
- this.name = name;
- this.type = type;
- this.children = children;
- this.cleared = false;
- this.visited = false;
- }
- }
- // Simplified tree structure for better gameplay flow
- const root = new Node('๐ Root Directory', 'folder', [
- new Node('๐ Documents', 'folder', [
- new Node('๐ 3D Printing', 'folder', [
- new Node('๐ Nintendo Switch Stand.stl', 'file'),
- new Node('๐ Phone Holder.stl', 'file'),
- new Node('๐ Game Cases.stl', 'file')
- ]),
- new Node('๐ Projects', 'folder', [
- new Node('๐ Summer Catalog 2019.pdf', 'file'),
- new Node('๐ Product Designs.doc', 'file')
- ]),
- new Node('๐ Personal', 'folder', [
- new Node('๐ Resume.pdf', 'file'),
- new Node('๐ Certificates.zip', 'file')
- ])
- ]),
- new Node('๐ผ๏ธ Pictures', 'folder', [
- new Node('๐ Gaming Setup', 'folder', [
- new Node('๐ธ Desktop.jpg', 'file'),
- new Node('๐ธ Controllers.jpg', 'file')
- ]),
- new Node('๐ 3D Prints', 'folder', [
- new Node('๐ธ Nintendo Accessories.jpg', 'file'),
- new Node('๐ธ Phone Stands.jpg', 'file')
- ])
- ]),
- new Node('๐ฎ Games', 'folder', [
- new Node('๐ Saves', 'folder', [
- new Node('๐พ Minecraft World', 'file'),
- new Node('๐พ Skyrim Save', 'file')
- ]),
- new Node('๐ ROMs', 'folder', [
- new Node('๐ฏ Retro Collection', 'file')
- ])
- ]),
- new Node('โฌ๏ธ Downloads', 'folder', [
- new Node('๐ฆ Software.zip', 'file'),
- new Node('๐ต Music.mp3', 'file'),
- new Node('๐ฌ Videos.mp4', 'file')
- ])
- ]);
- // Game state
- let player = {
- x: CANVAS_WIDTH / 2,
- y: CANVAS_HEIGHT / 2,
- size: 1 * BLOX_SIZE,
- health: 100,
- maxHealth: 100,
- level: 1,
- gold: 0,
- abilities: ['sword', 'bow'],
- currentWeapon: 'sword',
- lastAttack: 0,
- arrows: [],
- specialAbility: null,
- specialCooldown: 0,
- kills: 0
- };
- let enemies = [];
- let treasures = [];
- let items = [];
- let portals = [];
- let currentNode = root;
- let map = [];
- let lastLogTime = 0;
- let gameOver = false;
- let lastTime = 0;
- let levelCompleted = false;
- let enemiesKilledThisLevel = 0;
- let treasuresCollectedThisLevel = 0;
- let itemsCollectedThisLevel = 0;
- // DOM elements
- const canvas = document.getElementById('gameCanvas');
- const ctx = canvas.getContext('2d');
- const logDiv = document.getElementById('log');
- const healthSpan = document.getElementById('health');
- const healthBar = document.getElementById('health-bar');
- const levelSpan = document.getElementById('level');
- const goldSpan = document.getElementById('gold');
- const currentNodeSpan = document.getElementById('currentNode');
- const abilitiesSpan = document.getElementById('abilities');
- const weaponSpan = document.getElementById('weapon');
- const specialSpan = document.getElementById('special');
- const treeStructureDiv = document.getElementById('tree-structure');
- const neuralOverlay = document.getElementById('neuralOverlay');
- const completionMessage = document.getElementById('completionMessage');
- // Adjust canvas size based on screen resolution
- function adjustCanvasSize() {
- const isMobile = window.innerWidth <= 768;
- if (isMobile) {
- CANVAS_WIDTH = Math.min(window.innerWidth - 40, 500);
- CANVAS_HEIGHT = CANVAS_WIDTH;
- } else if (window.matchMedia("(min-width: 2560px)").matches) {
- CANVAS_WIDTH = 900;
- CANVAS_HEIGHT = 900;
- } else if (window.matchMedia("(min-width: 1920px)").matches) {
- CANVAS_WIDTH = 800;
- CANVAS_HEIGHT = 800;
- } else {
- CANVAS_WIDTH = 600;
- CANVAS_HEIGHT = 600;
- }
- canvas.width = CANVAS_WIDTH;
- canvas.height = CANVAS_HEIGHT;
- BLOX_SIZE = Math.max(20, Math.floor(CANVAS_WIDTH / 25));
- MAP_WIDTH = Math.floor(CANVAS_WIDTH / BLOX_SIZE);
- MAP_HEIGHT = Math.floor(CANVAS_HEIGHT / BLOX_SIZE);
- // Reset player position
- player.x = CANVAS_WIDTH / 2 - player.size / 2;
- player.y = CANVAS_HEIGHT / 2 - player.size / 2;
- player.size = BLOX_SIZE;
- // Regenerate map with new dimensions
- if (map.length > 0) generateMap();
- }
- function generateMap() {
- map = Array.from({length: MAP_HEIGHT}, () => Array(MAP_WIDTH).fill('grass'));
- // Add natural-looking tree clusters
- const treeClusterCount = Math.floor(Math.random() * 8) + 4;
- for (let cluster = 0; cluster < treeClusterCount; cluster++) {
- let centerX = Math.floor(Math.random() * (MAP_WIDTH - 4)) + 2;
- let centerY = Math.floor(Math.random() * (MAP_HEIGHT - 4)) + 2;
- let clusterSize = Math.floor(Math.random() * 4) + 2;
- for (let i = 0; i < clusterSize; i++) {
- let ox = centerX + Math.floor(Math.random() * 3) - 1;
- let oy = centerY + Math.floor(Math.random() * 3) - 1;
- if (ox >= 0 && ox < MAP_WIDTH && oy >= 0 && oy < MAP_HEIGHT) {
- map[oy][ox] = 'tree';
- }
- }
- }
- // Generate balanced enemy count
- enemies = [];
- const enemyCount = Math.floor(Math.random() * (MAX_ENEMIES - MIN_ENEMIES + 1)) + MIN_ENEMIES;
- for (let i = 0; i < enemyCount; i++) {
- const type = ENEMY_TYPES[Math.floor(Math.random() * ENEMY_TYPES.length)];
- let enemyX, enemyY;
- // Ensure enemies don't spawn too close to player
- do {
- enemyX = Math.random() * (CANVAS_WIDTH - BLOX_SIZE);
- enemyY = Math.random() * (CANVAS_HEIGHT - BLOX_SIZE);
- } while (Math.abs(enemyX - player.x) < BLOX_SIZE * 3 && Math.abs(enemyY - player.y) < BLOX_SIZE * 3);
- enemies.push({
- ...type,
- x: enemyX,
- y: enemyY,
- currentHealth: type.health,
- abilities: Math.random() > 0.7 ? [ABILITIES[Math.floor(Math.random() * ABILITIES.length)]] : [],
- lastAttack: 0
- });
- }
- // Generate treasures based on current node's children
- treasures = [];
- const treasureCount = Math.max(1, currentNode.children.length);
- for (let i = 0; i < treasureCount; i++) {
- treasures.push({
- x: Math.random() * (CANVAS_WIDTH - BLOX_SIZE),
- y: Math.random() * (CANVAS_HEIGHT - BLOX_SIZE)
- });
- }
- // Add helpful items
- items = [];
- const itemCount = Math.floor(Math.random() * 3) + 1;
- for (let i = 0; i < itemCount; i++) {
- items.push({
- x: Math.random() * (CANVAS_WIDTH - BLOX_SIZE),
- y: Math.random() * (CANVAS_HEIGHT - BLOX_SIZE),
- type: Math.random() > 0.6 ? 'healthPotion' : 'manaPotion'
- });
- }
- // Reset level state
- levelCompleted = false;
- enemiesKilledThisLevel = 0;
- treasuresCollectedThisLevel = 0;
- itemsCollectedThisLevel = 0;
- portals = [];
- completionMessage.style.display = 'none';
- gameOver = false;
- logMessage(`๐ Entered: ${currentNode.name}`);
- if (enemies.length > 0) logMessage(`โ๏ธ ${enemies.length} enemies detected!`);
- if (treasures.length > 0) logMessage(`๐ฐ ${treasures.length} treasures to collect!`);
- updateTreeMap();
- }
- function spawnPortals() {
- portals = [];
- // Portal back to parent
- if (currentNode.name !== '๐ Root Directory') {
- const parent = findParent(root, currentNode);
- if (parent) {
- portals.push({
- x: Math.random() * (CANVAS_WIDTH - 100) + 50,
- y: Math.random() * (CANVAS_HEIGHT - 100) + 50,
- target: parent,
- type: 'parent',
- name: `โฌ ๏ธ ${parent.name}`
- });
- }
- }
- // Portals to children
- currentNode.children.forEach(child => {
- portals.push({
- x: Math.random() * (CANVAS_WIDTH - 100) + 50,
- y: Math.random() * (CANVAS_HEIGHT - 100) + 50,
- target: child,
- type: 'child',
- name: `โก๏ธ ${child.name}`
- });
- });
- completionMessage.style.display = 'block';
- setTimeout(() => {
- completionMessage.style.display = 'none';
- }, 4000);
- logMessage('๐ Level cleared! Portals activated!');
- }
- function findParent(root, node) {
- if (root.children.includes(node)) return root;
- for (const child of root.children) {
- if (child.children && child.children.length > 0) {
- const parent = findParent(child, node);
- if (parent) return parent;
- }
- }
- return null;
- }
- function updateTreeMap() {
- let html = '';
- function buildTree(node, depth = 0) {
- const indent = ' '.repeat(depth * 2);
- const isCurrent = node === currentNode;
- const clearedIndicator = node.cleared ? ' โ ' : '';
- const visitedIndicator = node.visited && !node.cleared ? ' ๐๏ธ' : '';
- html += `${indent}${isCurrent ? '<strong style="color: #FFD700;">' : ''}${node.name}${clearedIndicator}${visitedIndicator}${isCurrent ? '</strong>' : ''}<br>`;
- if (node.children && node.children.length > 0) {
- node.children.forEach(child => {
- buildTree(child, depth + 1);
- });
- }
- }
- buildTree(root);
- treeStructureDiv.innerHTML = html;
- }
- function logMessage(msg) {
- if (Date.now() - lastLogTime > 50) {
- const timestamp = new Date().toLocaleTimeString('en-US', {
- hour12: false,
- hour: '2-digit',
- minute: '2-digit',
- second: '2-digit'
- });
- logDiv.innerHTML += `<span style="color: #888;">[${timestamp}]</span> ${msg}<br>`;
- logDiv.scrollTop = logDiv.scrollHeight;
- lastLogTime = Date.now();
- }
- }
- // Enhanced input handling
- const keys = {};
- let touchStartX = 0;
- let touchStartY = 0;
- let touchMoveThreshold = 10;
- window.addEventListener('keydown', e => {
- keys[e.key.toLowerCase()] = true;
- if (e.key.toLowerCase() === 'm') {
- e.preventDefault();
- toggleNeuralOverlay();
- }
- if (e.key.toLowerCase() === 'r') {
- e.preventDefault();
- logMessage('๐ Resetting level...');
- generateMap();
- }
- if (e.key.toLowerCase() === 'q') {
- e.preventDefault();
- switchWeapon();
- }
- if (e.key.toLowerCase() === 'e') {
- e.preventDefault();
- useSpecialAbility();
- }
- });
- window.addEventListener('keyup', e => {
- keys[e.key.toLowerCase()] = false;
- });
- // Touch controls for mobile
- canvas.addEventListener('touchstart', e => {
- e.preventDefault();
- const touch = e.touches[0];
- const rect = canvas.getBoundingClientRect();
- touchStartX = touch.clientX - rect.left;
- touchStartY = touch.clientY - rect.top;
- });
- canvas.addEventListener('touchmove', e => {
- e.preventDefault();
- const touch = e.touches[0];
- const rect = canvas.getBoundingClientRect();
- const touchX = touch.clientX - rect.left;
- const touchY = touch.clientY - rect.top;
- const deltaX = touchX - touchStartX;
- const deltaY = touchY - touchStartY;
- if (Math.abs(deltaX) > touchMoveThreshold || Math.abs(deltaY) > touchMoveThreshold) {
- // Convert touch to movement
- if (Math.abs(deltaX) > Math.abs(deltaY)) {
- keys['arrowleft'] = deltaX < 0;
- keys['arrowright'] = deltaX > 0;
- keys['arrowup'] = false;
- keys['arrowdown'] = false;
- } else {
- keys['arrowup'] = deltaY < 0;
- keys['arrowdown'] = deltaY > 0;
- keys['arrowleft'] = false;
- keys['arrowright'] = false;
- }
- }
- });
- canvas.addEventListener('touchend', e => {
- e.preventDefault();
- // Clear movement keys
- keys['arrowleft'] = false;
- keys['arrowright'] = false;
- keys['arrowup'] = false;
- keys['arrowdown'] = false;
- });
- function toggleNeuralOverlay() {
- const isVisible = neuralOverlay.style.display !== 'none';
- neuralOverlay.style.display = isVisible ? 'none' : 'block';
- logMessage(isVisible ? '๐๏ธ Neural overlay disabled' : '๐ Neural overlay enabled');
- }
- function switchWeapon() {
- const currentIndex = player.abilities.indexOf(player.currentWeapon);
- const nextIndex = (currentIndex + 1) % player.abilities.length;
- player.currentWeapon = player.abilities[nextIndex];
- weaponSpan.textContent = player.currentWeapon.charAt(0).toUpperCase() + player.currentWeapon.slice(1);
- logMessage(`๐ Switched to ${player.currentWeapon}`);
- }
- function getClosestEnemy() {
- let closest = null;
- let minDist = Infinity;
- enemies.forEach(enemy => {
- let dx = (player.x + player.size/2) - (enemy.x + enemy.size * BLOX_SIZE/2);
- let dy = (player.y + player.size/2) - (enemy.y + enemy.size * BLOX_SIZE/2);
- let dist = Math.sqrt(dx*dx + dy*dy);
- if (dist < minDist) {
- minDist = dist;
- closest = enemy;
- }
- });
- return { enemy: closest, dist: minDist };
- }
- function predictPosition(enemy, projectileSpeed, dt) {
- let dx = (player.x + player.size/2) - (enemy.x + enemy.size * BLOX_SIZE/2);
- let dy = (player.y + player.size/2) - (enemy.y + enemy.size * BLOX_SIZE/2);
- let dist = Math.sqrt(dx*dx + dy*dy);
- let timeToHit = dist / projectileSpeed;
- // Predict where enemy will be
- let enemyVx = (dx / dist) * -enemy.speed;
- let enemyVy = (dy / dist) * -enemy.speed;
- return {
- x: enemy.x + enemyVx * timeToHit,
- y: enemy.y + enemyVy * timeToHit
- };
- }
- function attackEnemy(enemy, dist, dt) {
- if (Date.now() - player.lastAttack < ATTACK_COOLDOWN) return;
- let damage = 0;
- let attacked = false;
- if (player.currentWeapon === 'sword' && dist <= SWORD_RANGE) {
- damage = Math.floor(20 * (1 - dist / SWORD_RANGE)) + 5;
- logMessage(`โ๏ธ Sword strike! ${damage} damage`);
- attacked = true;
- // Visual sword effect
- setTimeout(() => {
- ctx.save();
- ctx.globalAlpha = 0.7;
- ctx.beginPath();
- ctx.moveTo(player.x + player.size/2, player.y + player.size/2);
- let ex = enemy.x + enemy.size * BLOX_SIZE / 2;
- let ey = enemy.y + enemy.size * BLOX_SIZE / 2;
- ctx.lineTo(ex, ey);
- ctx.strokeStyle = COLORS.sword;
- ctx.lineWidth = 4;
- ctx.stroke();
- ctx.restore();
- }, 0);
- } else if (player.currentWeapon === 'bow' && dist > BOW_RANGE_MIN && dist < BOW_RANGE_MAX) {
- damage = Math.floor(15 + (dist / BOW_RANGE_MAX) * 10);
- logMessage(`๐น Arrow shot! ${damage} damage`);
- attacked = true;
- // Fire arrow with prediction
- let pred = predictPosition(enemy, ARROW_SPEED, dt);
- player.arrows.push({
- x: player.x + player.size/2,
- y: player.y + player.size/2,
- tx: pred.x + enemy.size * BLOX_SIZE/2,
- ty: pred.y + enemy.size * BLOX_SIZE/2,
- speed: ARROW_SPEED,
- damage: damage,
- startTime: Date.now()
- });
- } else if (player.currentWeapon === 'fireball' && dist >= SPELL_RANGE_MIN && dist <= SPELL_RANGE_MAX) {
- damage = 25;
- logMessage(`๐ฅ Fireball! ${damage} damage`);
- attacked = true;
- player.arrows.push({
- x: player.x + player.size/2,
- y: player.y + player.size/2,
- tx: enemy.x + enemy.size * BLOX_SIZE/2,
- ty: enemy.y + enemy.size * BLOX_SIZE/2,
- speed: ARROW_SPEED * 0.7,
- damage: damage,
- isFireball: true,
- startTime: Date.now()
- });
- }
- if (attacked) {
- if (player.currentWeapon === 'sword') {
- enemy.currentHealth -= damage;
- handleEnemyDeath(enemy);
- }
- player.lastAttack = Date.now();
- }
- }
- function handleEnemyDeath(enemy) {
- if (enemy.currentHealth <= 0) {
- logMessage(`๐ Defeated ${enemy.type}! +10 gold`);
- enemiesKilledThisLevel++;
- player.kills++;
- // Grant abilities from defeated enemies
- if (enemy.abilities.length > 0) {
- const ability = enemy.abilities[0];
- if (!player.abilities.includes(ability)) {
- player.abilities.push(ability);
- abilitiesSpan.textContent = player.abilities.join(', ');
- logMessage(`โจ Gained ability: ${ability}!`);
- }
- }
- player.gold += 10;
- goldSpan.textContent = player.gold;
- player.level++;
- levelSpan.textContent = player.level;
- checkLevelCompletion();
- // Progressive difficulty
- if (player.level % 5 === 0) {
- ENEMY_TYPES.forEach(type => {
- type.health += 10;
- type.damage += 2;
- });
- logMessage(`โฌ๏ธ Enemies grow stronger! (Level ${player.level})`);
- }
- const index = enemies.indexOf(enemy);
- if (index !== -1) enemies.splice(index, 1);
- }
- }
- function checkLevelCompletion() {
- if (enemies.length === 0 && treasures.length === 0 && items.length === 0 && !levelCompleted) {
- levelCompleted = true;
- currentNode.cleared = true;
- spawnPortals();
- } else if (!levelCompleted && enemiesKilledThisLevel >= Math.ceil(enemies.length * 0.6) && treasuresCollectedThisLevel >= Math.ceil(treasures.length * 0.7)) {
- levelCompleted = true;
- currentNode.cleared = true;
- spawnPortals();
- }
- }
- function useSpecialAbility() {
- if (!player.specialAbility || player.specialCooldown > 0) return;
- switch(player.specialAbility) {
- case 'shield':
- logMessage('๐ก๏ธ Shield activated! Temporary invulnerability');
- player.specialCooldown = 300;
- break;
- case 'double-damage':
- logMessage('๐ฅ Double damage activated!');
- player.specialCooldown = 450;
- break;
- case 'speed-boost':
- logMessage('๐จ Speed boost activated!');
- player.specialCooldown = 240;
- break;
- case 'health-regen':
- player.health = Math.min(player.maxHealth, player.health + 50);
- healthSpan.textContent = player.health;
- healthBar.style.width = `${(player.health / player.maxHealth) * 100}%`;
- logMessage('๐ Health regeneration activated!');
- player.specialCooldown = 600;
- break;
- }
- }
- function enemyAttack(enemy, dist) {
- if (Date.now() - enemy.lastAttack < ATTACK_COOLDOWN * 2) return;
- if (dist > SWORD_RANGE * 2) return;
- let damage = Math.floor(enemy.damage * (1 + Math.random() * 0.5));
- // Different attack patterns
- if (enemy.weapon === 'spell' && dist >= SPELL_RANGE_MIN && dist <= SPELL_RANGE_MAX) {
- damage = Math.floor(damage * 1.2);
- logMessage(`๐ฅ ${enemy.type} casts spell! -${damage} health`);
- } else if (enemy.weapon === 'bow-spell' && Math.random() > 0.6 && dist > SWORD_RANGE) {
- damage = Math.floor(damage * 0.8);
- logMessage(`๐น ${enemy.type} shoots! -${damage} health`);
- } else if (dist <= SWORD_RANGE) {
- logMessage(`โ๏ธ ${enemy.type} attacks! -${damage} health`);
- } else {
- return; // Too far for attack
- }
- player.health -= damage;
- player.health = Math.max(0, player.health);
- healthSpan.textContent = player.health;
- healthBar.style.width = `${(player.health / player.maxHealth) * 100}%`;
- enemy.lastAttack = Date.now();
- if (player.health <= 0) {
- gameOver = true;
- logMessage('๐ You died! Press R to restart level');
- }
- }
- function updateArrows(dt) {
- player.arrows.forEach((arrow, i) => {
- let dx = arrow.tx - arrow.x;
- let dy = arrow.ty - arrow.y;
- let dist = Math.sqrt(dx*dx + dy*dy);
- if (dist > 5) {
- arrow.x += (dx / dist) * arrow.speed * dt;
- arrow.y += (dy / dist) * arrow.speed * dt;
- } else {
- // Arrow reached target
- player.arrows.splice(i, 1);
- return;
- }
- // Check collision with enemies
- enemies.forEach((enemy, enemyIndex) => {
- let ex = enemy.x + enemy.size * BLOX_SIZE/2 - arrow.x;
- let ey = enemy.y + enemy.size * BLOX_SIZE/2 - arrow.y;
- if (Math.sqrt(ex*ex + ey*ey) < enemy.size * BLOX_SIZE/2) {
- enemy.currentHealth -= arrow.damage;
- handleEnemyDeath(enemy);
- player.arrows.splice(i, 1);
- }
- });
- // Remove old arrows
- if (Date.now() - arrow.startTime > 3000) {
- player.arrows.splice(i, 1);
- }
- });
- }
- function update(currentTime) {
- if (gameOver) {
- requestAnimationFrame(update);
- return;
- }
- const dt = Math.min(0.016, (currentTime - lastTime) / 1000);
- lastTime = currentTime;
- // Player movement with bounds checking
- let moveSpeed = PLAYER_SPEED * dt;
- if (keys['arrowup'] || keys['w']) {
- player.y = Math.max(0, player.y - moveSpeed);
- }
- if (keys['arrowdown'] || keys['s']) {
- player.y = Math.min(CANVAS_HEIGHT - player.size, player.y + moveSpeed);
- }
- if (keys['arrowleft'] || keys['a']) {
- player.x = Math.max(0, player.x - moveSpeed);
- }
- if (keys['arrowright'] || keys['d']) {
- player.x = Math.min(CANVAS_WIDTH - player.size, player.x + moveSpeed);
- }
- // Auto-attack system
- const { enemy, dist } = getClosestEnemy();
- if (enemy) {
- attackEnemy(enemy, dist, dt);
- enemyAttack(enemy, dist);
- }
- // Enemy AI and movement
- enemies.forEach(enemy => {
- let dx = (player.x + player.size/2) - (enemy.x + enemy.size * BLOX_SIZE/2);
- let dy = (player.y + player.size/2) - (enemy.y + enemy.size * BLOX_SIZE/2);
- let dist = Math.sqrt(dx*dx + dy*dy);
- if (dist > 0) {
- let moveSpeed = enemy.speed * dt;
- enemy.x += (dx / dist) * moveSpeed;
- enemy.y += (dy / dist) * moveSpeed;
- // Keep enemies in bounds
- enemy.x = Math.max(0, Math.min(CANVAS_WIDTH - enemy.size * BLOX_SIZE, enemy.x));
- enemy.y = Math.max(0, Math.min(CANVAS_HEIGHT - enemy.size * BLOX_SIZE, enemy.y));
- }
- });
- // Treasure collection
- treasures.forEach((treasure, i) => {
- let dx = (player.x + player.size/2) - (treasure.x + BLOX_SIZE/2);
- let dy = (player.y + player.size/2) - (treasure.y + BLOX_SIZE/2);
- let dist = Math.sqrt(dx*dx + dy*dy);
- if (dist < player.size/2 + BLOX_SIZE/2) {
- player.gold += 25;
- goldSpan.textContent = player.gold;
- treasuresCollectedThisLevel++;
- logMessage('๐ฐ Treasure collected! +25 gold');
- treasures.splice(i, 1);
- checkLevelCompletion();
- }
- });
- // Portal usage
- portals.forEach(portal => {
- let dx = (player.x + player.size/2) - portal.x;
- let dy = (player.y + player.size/2) - portal.y;
- let dist = Math.sqrt(dx*dx + dy*dy);
- if (dist < player.size/2 + BLOX_SIZE) {
- currentNode.visited = true;
- currentNode = portal.target;
- currentNodeSpan.textContent = currentNode.name;
- logMessage(`๐ช Entering ${currentNode.name}...`);
- generateMap();
- }
- });
- // Item collection
- items.forEach((item, i) => {
- let dx = (player.x + player.size/2) - (item.x + BLOX_SIZE/2);
- let dy = (player.y + player.size/2) - (item.y + BLOX_SIZE/2);
- let dist = Math.sqrt(dx*dx + dy*dy);
- if (dist < player.size/2 + BLOX_SIZE/2) {
- if (item.type === 'healthPotion') {
- player.health = Math.min(player.maxHealth, player.health + 30);
- healthSpan.textContent = player.health;
- healthBar.style.width = `${(player.health / player.maxHealth) * 100}%`;
- logMessage('๐งช Health potion! +30 health');
- } else if (item.type === 'manaPotion') {
- player.specialCooldown = Math.max(0, player.specialCooldown - 100);
- logMessage('๐ Mana potion! Special cooldown reduced');
- }
- itemsCollectedThisLevel++;
- items.splice(i, 1);
- checkLevelCompletion();
- }
- });
- updateArrows(dt);
- // Passive abilities
- if (player.abilities.includes('health-regen') && player.health < player.maxHealth) {
- player.health = Math.min(player.maxHealth, player.health + 10 * dt);
- healthSpan.textContent = Math.floor(player.health);
- healthBar.style.width = `${(player.health / player.maxHealth) * 100}%`;
- }
- // Update cooldowns
- if (player.specialCooldown > 0) {
- player.specialCooldown -= 60 * dt;
- player.specialCooldown = Math.max(0, player.specialCooldown);
- }
- requestAnimationFrame(update);
- }
- function render() {
- ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
- // Render map tiles
- for (let y = 0; y < MAP_HEIGHT; y++) {
- for (let x = 0; x < MAP_WIDTH; x++) {
- ctx.fillStyle = COLORS[map[y][x]];
- ctx.fillRect(x * BLOX_SIZE, y * BLOX_SIZE, BLOX_SIZE, BLOX_SIZE);
- // Add texture to trees
- if (map[y][x] === 'tree') {
- ctx.fillStyle = '#654321';
- ctx.fillRect(x * BLOX_SIZE + 2, y * BLOX_SIZE + 2, BLOX_SIZE - 4, BLOX_SIZE - 4);
- }
- }
- }
- // Render player with glow effect
- ctx.shadowColor = COLORS.player;
- ctx.shadowBlur = 10;
- ctx.fillStyle = COLORS.player;
- ctx.fillRect(player.x, player.y, player.size, player.size);
- ctx.shadowBlur = 0;
- // Render enemies with health bars
- enemies.forEach(enemy => {
- let es = enemy.size * BLOX_SIZE;
- ctx.fillStyle = COLORS[enemy.color];
- ctx.fillRect(enemy.x, enemy.y, es, es);
- // Enemy health bar
- const healthPercent = enemy.currentHealth / enemy.health;
- ctx.fillStyle = '#333';
- ctx.fillRect(enemy.x, enemy.y - 8, es, 4);
- ctx.fillStyle = healthPercent > 0.5 ? '#4CAF50' : healthPercent > 0.25 ? '#FF9800' : '#F44336';
- ctx.fillRect(enemy.x, enemy.y - 8, es * healthPercent, 4);
- });
- // Render treasures with sparkle effect
- treasures.forEach((treasure, i) => {
- const sparkle = Math.sin(Date.now() * 0.01 + i) * 0.3 + 0.7;
- ctx.globalAlpha = sparkle;
- ctx.fillStyle = COLORS.treasure;
- ctx.beginPath();
- ctx.arc(treasure.x + BLOX_SIZE/2, treasure.y + BLOX_SIZE/2, BLOX_SIZE/3, 0, Math.PI * 2);
- ctx.fill();
- ctx.globalAlpha = 1;
- });
- // Render items
- items.forEach(item => {
- ctx.fillStyle = COLORS[item.type];
- ctx.beginPath();
- ctx.arc(item.x + BLOX_SIZE/2, item.y + BLOX_SIZE/2, BLOX_SIZE/3, 0, Math.PI * 2);
- ctx.fill();
- });
- // Render portals with animated effect
- portals.forEach((portal, i) => {
- const pulse = Math.sin(Date.now() * 0.005 + i) * 0.2 + 0.8;
- ctx.globalAlpha = pulse;
- ctx.fillStyle = COLORS.portal;
- ctx.beginPath();
- ctx.arc(portal.x, portal.y, BLOX_SIZE * 0.7, 0, Math.PI * 2);
- ctx.fill();
- ctx.globalAlpha = 1;
- // Portal ring
- ctx.strokeStyle = '#FFFFFF';
- ctx.lineWidth = 2;
- ctx.beginPath();
- ctx.arc(portal.x, portal.y, BLOX_SIZE * 0.7, 0, Math.PI * 2);
- ctx.stroke();
- // Portal label
- ctx.fillStyle = '#FFFFFF';
- ctx.font = `${Math.max(10, BLOX_SIZE/3)}px Courier New`;
- ctx.textAlign = 'center';
- const shortName = portal.name.length > 15 ? portal.name.substring(0, 12) + '...' : portal.name;
- ctx.fillText(shortName, portal.x, portal.y - BLOX_SIZE);
- ctx.textAlign = 'left';
- });
- // Render projectiles
- player.arrows.forEach(arrow => {
- if (arrow.isFireball) {
- ctx.fillStyle = '#FF4500';
- ctx.beginPath();
- ctx.arc(arrow.x, arrow.y, 6, 0, Math.PI * 2);
- ctx.fill();
- // Fire trail
- ctx.fillStyle = '#FF6500';
- ctx.beginPath();
- ctx.arc(arrow.x - 3, arrow.y, 3, 0, Math.PI * 2);
- ctx.fill();
- } else {
- ctx.fillStyle = COLORS.arrow;
- ctx.fillRect(arrow.x - 2, arrow.y - 2, 4, 4);
- }
- });
- requestAnimationFrame(render);
- }
- // Initialize game
- function initGame() {
- adjustCanvasSize();
- currentNodeSpan.textContent = currentNode.name;
- abilitiesSpan.textContent = player.abilities.join(', ');
- weaponSpan.textContent = player.currentWeapon.charAt(0).toUpperCase() + player.currentWeapon.slice(1);
- specialSpan.textContent = player.specialAbility || 'None';
- generateMap();
- logMessage('๐ฎ Welcome to Tree Kingdom Battler!');
- logMessage('โ๏ธ Battle through your file system!');
- logMessage('๐ก Tip: Collect treasures and defeat enemies to unlock new areas!');
- lastTime = performance.now();
- requestAnimationFrame(update);
- requestAnimationFrame(render);
- }
- // Event listeners
- window.addEventListener('load', initGame);
- window.addEventListener('resize', adjustCanvasSize);
- // Prevent context menu on canvas
- canvas.addEventListener('contextmenu', e => e.preventDefault());
- </script>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment