User prompt
תפחית מעט את קצב ייצור המפלצות בשלב הראשון. תגדיל מעט את כמות הזהב שמקבלים ממפלצת בממוצע. תעשה שהמפלצות לא ייוצרו ב-80 פיקסלים הימניים והשמאליים של המסך
User prompt
יצור שנוגע בחומה, יורד לה חיים וזה מתעדכן גם למעלה במסך. אם החיים מגיעים לאפס ומטה אתה נפסל והמשחק נגמר
User prompt
תוסיף ברקע של כל המשחק תמונה ענקית של עולם פנטזיה, ברקע של הכל הכל ביחד
User prompt
תוסיף ברקע של כל המשחק תמונה ענקית של עולם פנטזיה, ברקע של הכל הכל ביחד
User prompt
תעשה שמכשפה פשוט נוצרת עם 2 שלדים מקדימה לה, לא מייצרת אותם תוך כדי. מגיעה עם, והשלדים כל אחד עם 25 חיים
User prompt
תוריד את החיים של החומה ל10. כל אויב שנוגע בחומה נעלם ומוריד חיים 1
User prompt
לא יותר מ-3 גולם במקביל על הלוח. לא יותר מ-20 אויבים בכללי על הלוח
User prompt
עד שלב 3 טרולים עם 5 חיים ושלדים עם 10. משלב 3 טרולים עם 10 ושלדים עם 15
User prompt
עד 4 מכשפות במקביל על המגרש ולא יותר
User prompt
לטרול תן 10 חיים, לשלד 15, מכשפה 30, גולם 100, שדון 75
User prompt
כדור שכוון למטרה מסוימת, והיא נהרסה לפני שהוא פגע, לא ממשיך לחפש מטרה אחרת, אלא ממשיך בקו ישר כמו שהוא היה עד עכשיו עד שהוא יוצא מהמסך. אם בדרך הייתה מטרה - היא נפגעת
User prompt
תהפוך את הקוביות ללא הוגנות. סיכוי הכי גבוה שיצא המינימום בקוביה, והסיכוי הולך ופחות והכי פחות סביר שיצא את המקסימום
User prompt
תגדיל בקצת את כמות המפלצות בכל שלב
User prompt
תגביל מכשפה שלא תוכל שיהיה לה יותר מ-3 שלדים שהיא יצרה במקביל
User prompt
היריות נעלמות אם המטרה שלהם נעלמת. תעשה שהיריות יחליפו מטרה פשוט ויעברו לאחת שלא הושדמה
User prompt
אם דמות ירתה על מפלצת, והמפלצת מתה, היריות לא נעלמות, אלא ממשיכות עד לקצה המסך ואז נעלמות. אם בדרך הייתה עוד מפלצת, היא נפגעת והירייה נעלמת
User prompt
תאפשר, שאם גוררים קוביה ושמים אותה במקום פנוי אחר, היא עוברת לשם והדמות זזה בהתאם
User prompt
את הקסם של הקוסם שמיים, תעשה שהוא פעם אחת ימשיך למפלצת הבאה. נניח פגע במפלצת והוריד לה חיים, ממשיך למפלצת אחת נוספת מוריד לה גם ואז נעלם
User prompt
תוסיף ימין למעלה אינדיקטור באיזה שלב אנחנו
User prompt
מתחת לאיפה שכתוב כמה זהב יש, שיהיה כתוב Wall HP: ואז את הכמה חיים יש
User prompt
Please fix the bug: 'TypeError: Cannot set properties of undefined (setting 'fill')' in or related to this line: 'rollText.style.fill = textColor;' Line Number: 295
User prompt
תאריך את השלב הראשון והשני, ותפחית בממש קצת את קצב הייצור בהם תוסיף למעלה את החיים שיש לחומה את הכיתוב על הקוביות הצהובות, הירוקות והכתומות, תעשה בצבע שחור (דרגה + כמה יצא)
User prompt
תחלק את המשחק לשלבים בשלב הראשון מגיעים רק טרולים, בקצב איטי יחסית בשלב השני טרולים ושלדים בקצב קצת יותר מהיר בשלב השלישי טרולים עם פרשים, ולטרולים יש קצת יותר חיים בשלב הרביעי, פרשים עם מכשפות וטרולים בשלב החמישי, גלמים ובמקביל פרשים ואז מאחורי הגלמים מכשפות
User prompt
כאשר חיים של אויב נהיים שליליים בגלל נזק אזורי, הוא נעלם מיד תגדיל חזרה את קצב ייצור השלדים של המכשפה ל3.5 שניות
User prompt
המכשפה לא מייצרת שלדים, וגם בכללי אני לא רואה שלדים שנוצרים תגביר בהרבה את קצב הייצור של האויבים
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var AreaDamage = Container.expand(function (centerX, centerY, radius, damage, excludeEnemy) { var self = Container.call(this); self.x = centerX; self.y = centerY; self.radius = radius; self.damage = damage; self.excludeEnemy = excludeEnemy; self.lifetime = 5; // Frames to exist // Create visual effect var assetName = radius > 100 ? 'largeAreaDamage' : 'smallAreaDamage'; var visualEffect = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); visualEffect.alpha = 0.6; // Animate the visual effect tween(visualEffect, { alpha: 0, scaleX: 1.2, scaleY: 1.2 }, { duration: 2000, easing: tween.easeOut }); self.update = function () { // Deal damage to all enemies in radius for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy === self.excludeEnemy) continue; // Skip the original target var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.radius) { enemy.takeDamage(self.damage); } } self.lifetime--; if (self.lifetime <= 0) { self.shouldDestroy = true; } }; return self; }); var Bullet = Container.expand(function (startX, startY, damage, target, bulletType) { var self = Container.call(this); self.x = startX; self.y = startY; self.speed = 8; self.damage = damage || 1; self.target = target; self.bulletType = bulletType || 'bullet'; var bulletGraphics = self.attachAsset(self.bulletType, { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { if (self.target && self.target.parent) { // Calculate direction to target var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { // Move towards target self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else { // Close enough to target if (self.target.takeDamage) { self.target.takeDamage(self.damage); } self.shouldDestroy = true; } } else { // Target is gone, destroy bullet self.shouldDestroy = true; } }; return self; }); var Defender = Container.expand(function (type, level) { var self = Container.call(this); self.type = type || 'green'; self.level = level || 1; self.shootTimer = 0; self.shootInterval = 60; // 1 second at 60fps self.damage = 1; var assetNames = { 'green': 'archer', 'yellow': 'spearFighter', 'red': 'musketeer', 'orange': 'cannon', 'blue': 'skyMage', 'black': 'darkMage' }; var defenderGraphics = self.attachAsset(assetNames[self.type], { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { // Check if linked die just finished rolling if (self.linkedDie && self.linkedDie.justFinishedRolling) { self.shoot(); } }; self.shoot = function () { var die = self.linkedDie; if (die && enemies.length > 0 && die.justFinishedRolling) { // Find nearest enemy var nearestEnemy = null; var shortestDistance = Infinity; for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < shortestDistance) { shortestDistance = distance; nearestEnemy = enemy; } } if (nearestEnemy) { var shots = die.getRollValue(); var bulletTypes = { 'green': 'arrow', 'yellow': 'spear', 'red': 'musketBall', 'orange': 'cannonBall', 'blue': 'blueMagic', 'black': 'darkMagic' }; var bulletType = bulletTypes[self.type] || 'bullet'; // Set damage based on defender type var bulletDamage = self.damage; if (self.type === 'red') { bulletDamage = 2; // Musketeer deals 2 damage } else if (self.type === 'blue') { bulletDamage = 2; // Sky mage deals 2 damage } // Fire bullets in sequence with 0.3 second intervals for (var i = 0; i < shots; i++) { LK.setTimeout(function (shotIndex) { return function () { // Find target again (in case original target was destroyed) var currentTarget = nearestEnemy; if (!currentTarget || !currentTarget.parent) { // Find new nearest enemy for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (!currentTarget || distance < Math.sqrt((currentTarget.x - self.x) * (currentTarget.x - self.x) + (currentTarget.y - self.y) * (currentTarget.y - self.y))) { currentTarget = enemy; } } } if (currentTarget && currentTarget.parent) { var bullet = new Bullet(self.x, self.y - 40, bulletDamage, currentTarget, bulletType); bullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); } }; }(i), i * 300); // 300ms = 0.3 seconds } die.justFinishedRolling = false; } } }; return self; }); var Die = Container.expand(function (type, level) { var self = Container.call(this); self.type = type || 'green'; self.level = level || 1; self.rollTimer = 0; self.rollInterval = 180; // 3 seconds at 60fps self.lastRoll = 1; self.isDragging = false; self.justFinishedRolling = false; self.originalPosition = { x: 0, y: 0 }; var assetNames = { 'green': 'greenDie', 'yellow': 'yellowDie', 'red': 'redDie', 'orange': 'orangeDie', 'blue': 'blueDie', 'black': 'blackDie' }; var dieGraphics = self.attachAsset(assetNames[self.type], { anchorX: 0.5, anchorY: 0.5 }); var levelText = new Text2(self.level.toString(), { size: 40, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0.5); levelText.x = 0; levelText.y = -20; self.addChild(levelText); var rollText = new Text2(self.lastRoll.toString(), { size: 60, fill: 0xFFFFFF }); rollText.anchor.set(0.5, 0.5); rollText.x = 0; rollText.y = 20; self.addChild(rollText); self.down = function (x, y, obj) { self.isDragging = true; self.originalPosition.x = self.x; self.originalPosition.y = self.y; draggedDie = self; }; self.update = function () { self.rollTimer++; // Blue dice rotate every 2 seconds (120 ticks), others every 3 seconds (180 ticks) var currentInterval = self.rollInterval; if (self.type === 'blue') { currentInterval = 120; // 2 seconds for blue dice } if (self.rollTimer >= currentInterval) { self.rollTimer = 0; self.lastRoll = Math.floor(Math.random() * 6) + 1; var displayValue = self.lastRoll + (self.level - 1) * 2; rollText.setText(displayValue.toString()); self.justFinishedRolling = true; // Add rolling animation tween(dieGraphics, { rotation: Math.PI * 2 }, { duration: 500, easing: tween.easeOut }); } }; self.getRollValue = function () { return self.lastRoll + (self.level - 1) * 2; }; self.updateLevel = function (newLevel) { self.level = newLevel; levelText.setText(self.level.toString()); }; return self; }); var Enemy = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'troll'; // Set stats based on enemy type var enemyStats = { 'troll': { health: 5, speed: 1, goldValue: Math.floor(Math.random() * 5) + 3, asset: 'troll', behavior: 'melee' }, 'skeleton': { health: 10, speed: 1.5, goldValue: Math.floor(Math.random() * 7) + 5, asset: 'skeleton', behavior: 'melee' }, 'knight': { health: 1, speed: 3, goldValue: Math.floor(Math.random() * 3) + 2, asset: 'knight', behavior: 'melee' }, 'witch': { health: 30, speed: 0.5, goldValue: Math.floor(Math.random() * 10) + 8, asset: 'witch', behavior: 'summoner' }, 'golem': { health: 80, speed: 0.8, goldValue: Math.floor(Math.random() * 15) + 15, asset: 'golem', behavior: 'melee' }, 'demon': { health: 40, speed: 1.2, goldValue: Math.floor(Math.random() * 12) + 10, asset: 'demon', behavior: 'ranged' } }; var stats = enemyStats[self.type] || enemyStats['troll']; self.health = stats.health; self.maxHealth = stats.health; self.speed = stats.speed; self.goldValue = stats.goldValue; self.behavior = stats.behavior; self.summonTimer = 0; self.attackTimer = 0; self.hasAttackedWall = false; var enemyGraphics = self.attachAsset(stats.asset, { anchorX: 0.5, anchorY: 0.5 }); var healthBar = new Text2(self.health.toString(), { size: 30, fill: 0xFF0000 }); healthBar.anchor.set(0.5, 0.5); healthBar.x = 0; healthBar.y = -80; self.addChild(healthBar); self.update = function () { if (self.behavior === 'ranged' && self.y >= 1500) { // Demon ranged attack behavior - stop and shoot self.attackTimer++; if (self.attackTimer >= 120) { // Every 2 seconds self.attackTimer = 0; self.rangedAttack(); } } else if (self.behavior === 'summoner') { // Witch summoning behavior - move slowly and summon skeletons if (self.y < 1770) { self.y += self.speed; } self.summonTimer++; if (self.summonTimer >= 120) { // Every 2 seconds self.summonTimer = 0; self.summonSkeleton(); } } else if (self.y >= 1770 && !self.hasAttackedWall) { // Reached wall - deal damage self.hasAttackedWall = true; self.attackWall(); } else if (self.y < 1770) { // Normal movement self.y += self.speed; } }; self.rangedAttack = function () { if (wallHealth > 0) { var projectile = new RangedProjectile(self.x, self.y, 1770); rangedProjectiles.push(projectile); game.addChild(projectile); } }; self.summonSkeleton = function () { var skeleton = new Enemy('skeleton'); skeleton.x = self.x + (Math.random() - 0.5) * 100; skeleton.y = self.y + 50; enemies.push(skeleton); game.addChild(skeleton); }; self.attackWall = function () { wallHealth -= 1; wallHealthText.setText('Wall Health: ' + wallHealth); LK.getSound('wallHit').play(); LK.effects.flashObject(wall, 0xFF0000, 300); if (wallHealth <= 0) { // Game over LK.effects.flashScreen(0xFF0000, 1000); LK.setTimeout(function () { LK.showGameOver(); }, 1000); } }; self.takeDamage = function (damage) { self.health -= damage; healthBar.setText(self.health.toString()); if (self.health <= 0) { self.die(); } }; self.die = function () { gold += self.goldValue; goldText.setText('Gold: ' + gold); LK.getSound('enemyDie').play(); LK.effects.flashObject(self, 0xFF0000, 500); }; return self; }); var RangedProjectile = Container.expand(function (startX, startY, targetY) { var self = Container.call(this); self.x = startX; self.y = startY; self.targetY = targetY; self.speed = 4; var projectileGraphics = self.attachAsset('rangedAttack', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { self.y += self.speed; if (self.y >= self.targetY) { // Hit the wall wallHealth -= 2; // Demons deal 2 damage to wall wallHealthText.setText('Wall Health: ' + wallHealth); LK.getSound('wallHit').play(); LK.effects.flashObject(wall, 0xFF0000, 300); self.shouldDestroy = true; if (wallHealth <= 0) { // Game over LK.effects.flashScreen(0xFF0000, 1000); LK.setTimeout(function () { LK.showGameOver(); }, 1000); } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2F5233 }); /**** * Game Code ****/ // Game variables var gold = 10; var enemies = []; var bullets = []; var dice = []; var defenders = []; var wallSlots = []; var diceSlots = []; var draggedDie = null; var trashArea = null; var enemySpawnTimer = 0; var enemySpawnInterval = 300; // 5 seconds initially var gameTimer = 0; // Track game time in ticks var dicesPurchased = 0; // Track number of dice purchased for pricing var wallHealth = 100; var rangedProjectiles = []; // UI elements var goldText = new Text2('Gold: 10', { size: 60, fill: 0xFFFF00 }); goldText.anchor.set(0, 0); goldText.x = 150; goldText.y = 50; LK.gui.topLeft.addChild(goldText); // Wall health display var wallHealthText = new Text2('Wall Health: 100', { size: 50, fill: 0xFF0000 }); wallHealthText.anchor.set(0.5, 1); wallHealthText.x = 1024; wallHealthText.y = 1820; game.addChild(wallHealthText); // Create wall var wall = game.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 1, x: 1024, y: 1900 })); // Create wall slots (9 defensive positions) for (var i = 0; i < 9; i++) { var slot = game.addChild(LK.getAsset('wallSlot', { anchorX: 0.5, anchorY: 0.5, x: 200 + i * 200, y: 1900 - 75 })); wallSlots.push(slot); } // Create dice grid (3x3) var diceGridStartX = 824; // Center on screen (1024 - 200) var diceGridStartY = 2100; for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { var slot = game.addChild(LK.getAsset('diceSlot', { anchorX: 0.5, anchorY: 0.5, x: diceGridStartX + col * 200, y: diceGridStartY + row * 200 })); slot.isEmpty = true; slot.gridIndex = row * 3 + col; diceSlots.push(slot); } } // Create trash area to the left of dice grid trashArea = game.addChild(LK.getAsset('trashArea', { anchorX: 0.5, anchorY: 0.5, x: 524, // Left of dice grid y: 2200 })); // Buy button var buyButton = game.addChild(LK.getAsset('buyButton', { anchorX: 0.5, anchorY: 0.5, x: 1600, y: 2200 })); // Price display text var priceText = new Text2('Price: 10', { size: 40, fill: 0xFFFFFF }); priceText.anchor.set(0.5, 0); priceText.x = 1600; priceText.y = 2400; game.addChild(priceText); buyButton.down = function (x, y, obj) { var currentPrice = 10 + dicesPurchased * 5; if (gold >= currentPrice) { buyDie(); } }; function buyDie() { var emptySlot = null; for (var i = 0; i < diceSlots.length; i++) { if (diceSlots[i].isEmpty) { emptySlot = diceSlots[i]; break; } } if (emptySlot) { var currentPrice = 10 + dicesPurchased * 5; gold -= currentPrice; dicesPurchased++; goldText.setText('Gold: ' + gold); // Update price display for next purchase var nextPrice = 10 + dicesPurchased * 5; priceText.setText('Price: ' + nextPrice); var dieTypes = ['green', 'yellow', 'red', 'orange', 'blue', 'black']; var weights = [30, 30, 20, 20, 10, 10]; // Common, Common, Rare, Rare, Legendary, Legendary var randomType = getWeightedRandom(dieTypes, weights); var newDie = new Die(randomType, 1); newDie.x = emptySlot.x; newDie.y = emptySlot.y; newDie.slotIndex = emptySlot.gridIndex; dice.push(newDie); game.addChild(newDie); emptySlot.isEmpty = false; emptySlot.occupiedBy = newDie; // Create corresponding defender createDefender(newDie); } } function getWeightedRandom(items, weights) { var totalWeight = 0; for (var i = 0; i < weights.length; i++) { totalWeight += weights[i]; } var random = Math.random() * totalWeight; var currentWeight = 0; for (var i = 0; i < items.length; i++) { currentWeight += weights[i]; if (random <= currentWeight) { return items[i]; } } return items[0]; } function createDefender(die) { var defender = new Defender(die.type, die.level); var wallSlot = wallSlots[die.slotIndex % 9]; defender.x = wallSlot.x; defender.y = wallSlot.y; defender.linkedDie = die; defenders.push(defender); game.addChild(defender); } function spawnEnemy() { var enemyType = 'troll'; // Phase 1: Only trolls (0-30 seconds) if (gameTimer <= 1800) { enemyType = 'troll'; } // Phase 2: Trolls + skeletons (30-60 seconds) else if (gameTimer <= 3600) { enemyType = Math.random() < 0.7 ? 'troll' : 'skeleton'; } // Phase 3: Add knights (60-90 seconds) else if (gameTimer <= 5400) { var rand = Math.random(); if (rand < 0.5) enemyType = 'troll';else if (rand < 0.8) enemyType = 'skeleton';else enemyType = 'knight'; } // Phase 4: Knights + trolls in large numbers (90-120 seconds) else if (gameTimer <= 7200) { enemyType = Math.random() < 0.6 ? 'knight' : 'troll'; // Spawn multiple enemies for (var i = 0; i < 2; i++) { var enemy = new Enemy(enemyType); enemy.x = Math.random() * 1800 + 124; enemy.y = -100 - i * 100; enemies.push(enemy); game.addChild(enemy); } return; } // Phase 5: Add witches (120-150 seconds) else if (gameTimer <= 9000) { var rand = Math.random(); if (rand < 0.3) enemyType = 'witch';else if (rand < 0.6) enemyType = 'knight';else enemyType = 'troll'; } // Phase 6: Witches + knights + trolls (150-180 seconds) else if (gameTimer <= 10800) { var rand = Math.random(); if (rand < 0.25) enemyType = 'witch';else if (rand < 0.6) enemyType = 'knight';else enemyType = 'troll'; } // Phase 7: Add golems and demons (180+ seconds) else { var rand = Math.random(); if (rand < 0.15) enemyType = 'golem';else if (rand < 0.3) enemyType = 'demon';else if (rand < 0.45) enemyType = 'witch';else if (rand < 0.7) enemyType = 'knight';else enemyType = 'troll'; } var enemy = new Enemy(enemyType); enemy.x = Math.random() * 1800 + 124; // Random X position enemy.y = -100; // Start above screen enemies.push(enemy); game.addChild(enemy); } function handleMove(x, y, obj) { if (draggedDie) { draggedDie.x = x; draggedDie.y = y; } } function handleUp(x, y, obj) { if (draggedDie) { var merged = false; var trashed = false; // Check if dropped on trash area if (draggedDie.intersects(trashArea)) { // Destroy die and give 5 gold gold += 5; goldText.setText('Gold: ' + gold); // Remove die from slot var slot = diceSlots[draggedDie.slotIndex]; slot.isEmpty = true; slot.occupiedBy = null; // Remove corresponding defender for (var i = defenders.length - 1; i >= 0; i--) { if (defenders[i].linkedDie === draggedDie) { defenders[i].destroy(); defenders.splice(i, 1); break; } } // Remove die draggedDie.destroy(); dice.splice(dice.indexOf(draggedDie), 1); trashed = true; } if (!trashed) { // Check for merge with other dice for (var i = 0; i < dice.length; i++) { var otherDie = dice[i]; if (otherDie !== draggedDie && otherDie.type === draggedDie.type && otherDie.level === draggedDie.level && draggedDie.intersects(otherDie)) { // Merge dice mergeDice(draggedDie, otherDie); merged = true; break; } } if (!merged) { // Return to original position draggedDie.x = draggedDie.originalPosition.x; draggedDie.y = draggedDie.originalPosition.y; } } draggedDie = null; } } function mergeDice(die1, die2) { // Remove die1 from game var slot1 = diceSlots[die1.slotIndex]; slot1.isEmpty = true; slot1.occupiedBy = null; // Remove corresponding defender for (var i = defenders.length - 1; i >= 0; i--) { if (defenders[i].linkedDie === die1) { defenders[i].destroy(); defenders.splice(i, 1); break; } } die1.destroy(); dice.splice(dice.indexOf(die1), 1); // Upgrade die2 die2.updateLevel(die2.level + 1); // Update corresponding defender for (var i = 0; i < defenders.length; i++) { if (defenders[i].linkedDie === die2) { defenders[i].level = die2.level; break; } } LK.getSound('merge').play(); } game.move = handleMove; game.up = handleUp; game.update = function () { // Update game timer gameTimer++; // Progressive enemy spawning enemySpawnTimer++; var currentInterval = enemySpawnInterval; // Calculate progressive spawn rate increase var baseInterval = 600; // Start at 10 seconds var minInterval = 60; // Minimum 1 second spawn rate var reductionRate = gameTimer * 0.0001; // Gradual reduction over time currentInterval = Math.max(minInterval, baseInterval - gameTimer * 0.02); // Phase-based adjustments // Phase 1: Moderate spawning (0-30 seconds) if (gameTimer <= 1800) { currentInterval = Math.max(120, currentInterval); // At least 2 seconds } // Phase 2: Fast spawning (30-60 seconds) else if (gameTimer <= 3600) { currentInterval = Math.max(90, currentInterval); // At least 1.5 seconds } // Phase 3: Very fast spawning (60-90 seconds) else if (gameTimer <= 5400) { currentInterval = Math.max(60, currentInterval); // At least 1 second } // Phase 4: Extremely fast spawning for mass knights (90-120 seconds) else if (gameTimer <= 7200) { currentInterval = Math.max(30, currentInterval); // At least 0.5 seconds } // Phase 5: Very fast spawning (120-150 seconds) else if (gameTimer <= 9000) { currentInterval = Math.max(45, currentInterval); // At least 0.75 seconds } // Phase 6: Extremely fast spawning (150-180 seconds) else if (gameTimer <= 10800) { currentInterval = Math.max(30, currentInterval); // At least 0.5 seconds } // Phase 7: Maximum spawning rate (180+ seconds) else { currentInterval = Math.max(20, currentInterval); // At least 0.33 seconds } if (enemySpawnTimer >= currentInterval) { enemySpawnTimer = 0; spawnEnemy(); } // Update bullets and check collisions for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; // Check if bullet should be destroyed if (bullet.shouldDestroy || bullet.y < -50 || bullet.y > 2732 || bullet.x < 0 || bullet.x > 2048) { bullet.destroy(); bullets.splice(i, 1); continue; } // Check collision with target or any enemy var hitEnemy = false; for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (bullet.intersects(enemy)) { // Handle area damage for cannon and dark mage if (bullet.bulletType === 'cannonBall') { // Small area damage for cannon var areaDamage = new AreaDamage(enemy.x, enemy.y, 80, bullet.damage, enemy); areaDamage.lifetime = 120; // 2 seconds at 60fps game.addChild(areaDamage); } else if (bullet.bulletType === 'darkMagic') { // Large area damage for dark mage var areaDamage = new AreaDamage(enemy.x, enemy.y, 150, bullet.damage, enemy); areaDamage.lifetime = 120; // 2 seconds at 60fps game.addChild(areaDamage); } enemy.takeDamage(bullet.damage); bullet.destroy(); bullets.splice(i, 1); if (enemy.health <= 0) { enemy.destroy(); enemies.splice(j, 1); } hitEnemy = true; break; } } } // Update and cleanup area damage objects for (var i = game.children.length - 1; i >= 0; i--) { var child = game.children[i]; if (child.shouldDestroy) { child.destroy(); } } // Update ranged projectiles for (var i = rangedProjectiles.length - 1; i >= 0; i--) { var projectile = rangedProjectiles[i]; if (projectile.shouldDestroy || projectile.y > 2000) { projectile.destroy(); rangedProjectiles.splice(i, 1); } } // Check if enemies reached the wall (only for melee enemies) for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (enemy.behavior !== 'ranged' && enemy.y >= 1770 && !enemy.hasAttackedWall) { // Remove enemy after attacking wall enemy.destroy(); enemies.splice(i, 1); } } }; // Start with one free die buyDie();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var AreaDamage = Container.expand(function (centerX, centerY, radius, damage, excludeEnemy) {
var self = Container.call(this);
self.x = centerX;
self.y = centerY;
self.radius = radius;
self.damage = damage;
self.excludeEnemy = excludeEnemy;
self.lifetime = 5; // Frames to exist
// Create visual effect
var assetName = radius > 100 ? 'largeAreaDamage' : 'smallAreaDamage';
var visualEffect = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
visualEffect.alpha = 0.6;
// Animate the visual effect
tween(visualEffect, {
alpha: 0,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 2000,
easing: tween.easeOut
});
self.update = function () {
// Deal damage to all enemies in radius
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy === self.excludeEnemy) continue; // Skip the original target
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.radius) {
enemy.takeDamage(self.damage);
}
}
self.lifetime--;
if (self.lifetime <= 0) {
self.shouldDestroy = true;
}
};
return self;
});
var Bullet = Container.expand(function (startX, startY, damage, target, bulletType) {
var self = Container.call(this);
self.x = startX;
self.y = startY;
self.speed = 8;
self.damage = damage || 1;
self.target = target;
self.bulletType = bulletType || 'bullet';
var bulletGraphics = self.attachAsset(self.bulletType, {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
if (self.target && self.target.parent) {
// Calculate direction to target
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
// Move towards target
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Close enough to target
if (self.target.takeDamage) {
self.target.takeDamage(self.damage);
}
self.shouldDestroy = true;
}
} else {
// Target is gone, destroy bullet
self.shouldDestroy = true;
}
};
return self;
});
var Defender = Container.expand(function (type, level) {
var self = Container.call(this);
self.type = type || 'green';
self.level = level || 1;
self.shootTimer = 0;
self.shootInterval = 60; // 1 second at 60fps
self.damage = 1;
var assetNames = {
'green': 'archer',
'yellow': 'spearFighter',
'red': 'musketeer',
'orange': 'cannon',
'blue': 'skyMage',
'black': 'darkMage'
};
var defenderGraphics = self.attachAsset(assetNames[self.type], {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
// Check if linked die just finished rolling
if (self.linkedDie && self.linkedDie.justFinishedRolling) {
self.shoot();
}
};
self.shoot = function () {
var die = self.linkedDie;
if (die && enemies.length > 0 && die.justFinishedRolling) {
// Find nearest enemy
var nearestEnemy = null;
var shortestDistance = Infinity;
for (var e = 0; e < enemies.length; e++) {
var enemy = enemies[e];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < shortestDistance) {
shortestDistance = distance;
nearestEnemy = enemy;
}
}
if (nearestEnemy) {
var shots = die.getRollValue();
var bulletTypes = {
'green': 'arrow',
'yellow': 'spear',
'red': 'musketBall',
'orange': 'cannonBall',
'blue': 'blueMagic',
'black': 'darkMagic'
};
var bulletType = bulletTypes[self.type] || 'bullet';
// Set damage based on defender type
var bulletDamage = self.damage;
if (self.type === 'red') {
bulletDamage = 2; // Musketeer deals 2 damage
} else if (self.type === 'blue') {
bulletDamage = 2; // Sky mage deals 2 damage
}
// Fire bullets in sequence with 0.3 second intervals
for (var i = 0; i < shots; i++) {
LK.setTimeout(function (shotIndex) {
return function () {
// Find target again (in case original target was destroyed)
var currentTarget = nearestEnemy;
if (!currentTarget || !currentTarget.parent) {
// Find new nearest enemy
for (var e = 0; e < enemies.length; e++) {
var enemy = enemies[e];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (!currentTarget || distance < Math.sqrt((currentTarget.x - self.x) * (currentTarget.x - self.x) + (currentTarget.y - self.y) * (currentTarget.y - self.y))) {
currentTarget = enemy;
}
}
}
if (currentTarget && currentTarget.parent) {
var bullet = new Bullet(self.x, self.y - 40, bulletDamage, currentTarget, bulletType);
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
}
};
}(i), i * 300); // 300ms = 0.3 seconds
}
die.justFinishedRolling = false;
}
}
};
return self;
});
var Die = Container.expand(function (type, level) {
var self = Container.call(this);
self.type = type || 'green';
self.level = level || 1;
self.rollTimer = 0;
self.rollInterval = 180; // 3 seconds at 60fps
self.lastRoll = 1;
self.isDragging = false;
self.justFinishedRolling = false;
self.originalPosition = {
x: 0,
y: 0
};
var assetNames = {
'green': 'greenDie',
'yellow': 'yellowDie',
'red': 'redDie',
'orange': 'orangeDie',
'blue': 'blueDie',
'black': 'blackDie'
};
var dieGraphics = self.attachAsset(assetNames[self.type], {
anchorX: 0.5,
anchorY: 0.5
});
var levelText = new Text2(self.level.toString(), {
size: 40,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = 0;
levelText.y = -20;
self.addChild(levelText);
var rollText = new Text2(self.lastRoll.toString(), {
size: 60,
fill: 0xFFFFFF
});
rollText.anchor.set(0.5, 0.5);
rollText.x = 0;
rollText.y = 20;
self.addChild(rollText);
self.down = function (x, y, obj) {
self.isDragging = true;
self.originalPosition.x = self.x;
self.originalPosition.y = self.y;
draggedDie = self;
};
self.update = function () {
self.rollTimer++;
// Blue dice rotate every 2 seconds (120 ticks), others every 3 seconds (180 ticks)
var currentInterval = self.rollInterval;
if (self.type === 'blue') {
currentInterval = 120; // 2 seconds for blue dice
}
if (self.rollTimer >= currentInterval) {
self.rollTimer = 0;
self.lastRoll = Math.floor(Math.random() * 6) + 1;
var displayValue = self.lastRoll + (self.level - 1) * 2;
rollText.setText(displayValue.toString());
self.justFinishedRolling = true;
// Add rolling animation
tween(dieGraphics, {
rotation: Math.PI * 2
}, {
duration: 500,
easing: tween.easeOut
});
}
};
self.getRollValue = function () {
return self.lastRoll + (self.level - 1) * 2;
};
self.updateLevel = function (newLevel) {
self.level = newLevel;
levelText.setText(self.level.toString());
};
return self;
});
var Enemy = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'troll';
// Set stats based on enemy type
var enemyStats = {
'troll': {
health: 5,
speed: 1,
goldValue: Math.floor(Math.random() * 5) + 3,
asset: 'troll',
behavior: 'melee'
},
'skeleton': {
health: 10,
speed: 1.5,
goldValue: Math.floor(Math.random() * 7) + 5,
asset: 'skeleton',
behavior: 'melee'
},
'knight': {
health: 1,
speed: 3,
goldValue: Math.floor(Math.random() * 3) + 2,
asset: 'knight',
behavior: 'melee'
},
'witch': {
health: 30,
speed: 0.5,
goldValue: Math.floor(Math.random() * 10) + 8,
asset: 'witch',
behavior: 'summoner'
},
'golem': {
health: 80,
speed: 0.8,
goldValue: Math.floor(Math.random() * 15) + 15,
asset: 'golem',
behavior: 'melee'
},
'demon': {
health: 40,
speed: 1.2,
goldValue: Math.floor(Math.random() * 12) + 10,
asset: 'demon',
behavior: 'ranged'
}
};
var stats = enemyStats[self.type] || enemyStats['troll'];
self.health = stats.health;
self.maxHealth = stats.health;
self.speed = stats.speed;
self.goldValue = stats.goldValue;
self.behavior = stats.behavior;
self.summonTimer = 0;
self.attackTimer = 0;
self.hasAttackedWall = false;
var enemyGraphics = self.attachAsset(stats.asset, {
anchorX: 0.5,
anchorY: 0.5
});
var healthBar = new Text2(self.health.toString(), {
size: 30,
fill: 0xFF0000
});
healthBar.anchor.set(0.5, 0.5);
healthBar.x = 0;
healthBar.y = -80;
self.addChild(healthBar);
self.update = function () {
if (self.behavior === 'ranged' && self.y >= 1500) {
// Demon ranged attack behavior - stop and shoot
self.attackTimer++;
if (self.attackTimer >= 120) {
// Every 2 seconds
self.attackTimer = 0;
self.rangedAttack();
}
} else if (self.behavior === 'summoner') {
// Witch summoning behavior - move slowly and summon skeletons
if (self.y < 1770) {
self.y += self.speed;
}
self.summonTimer++;
if (self.summonTimer >= 120) {
// Every 2 seconds
self.summonTimer = 0;
self.summonSkeleton();
}
} else if (self.y >= 1770 && !self.hasAttackedWall) {
// Reached wall - deal damage
self.hasAttackedWall = true;
self.attackWall();
} else if (self.y < 1770) {
// Normal movement
self.y += self.speed;
}
};
self.rangedAttack = function () {
if (wallHealth > 0) {
var projectile = new RangedProjectile(self.x, self.y, 1770);
rangedProjectiles.push(projectile);
game.addChild(projectile);
}
};
self.summonSkeleton = function () {
var skeleton = new Enemy('skeleton');
skeleton.x = self.x + (Math.random() - 0.5) * 100;
skeleton.y = self.y + 50;
enemies.push(skeleton);
game.addChild(skeleton);
};
self.attackWall = function () {
wallHealth -= 1;
wallHealthText.setText('Wall Health: ' + wallHealth);
LK.getSound('wallHit').play();
LK.effects.flashObject(wall, 0xFF0000, 300);
if (wallHealth <= 0) {
// Game over
LK.effects.flashScreen(0xFF0000, 1000);
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
}
};
self.takeDamage = function (damage) {
self.health -= damage;
healthBar.setText(self.health.toString());
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
gold += self.goldValue;
goldText.setText('Gold: ' + gold);
LK.getSound('enemyDie').play();
LK.effects.flashObject(self, 0xFF0000, 500);
};
return self;
});
var RangedProjectile = Container.expand(function (startX, startY, targetY) {
var self = Container.call(this);
self.x = startX;
self.y = startY;
self.targetY = targetY;
self.speed = 4;
var projectileGraphics = self.attachAsset('rangedAttack', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
self.y += self.speed;
if (self.y >= self.targetY) {
// Hit the wall
wallHealth -= 2; // Demons deal 2 damage to wall
wallHealthText.setText('Wall Health: ' + wallHealth);
LK.getSound('wallHit').play();
LK.effects.flashObject(wall, 0xFF0000, 300);
self.shouldDestroy = true;
if (wallHealth <= 0) {
// Game over
LK.effects.flashScreen(0xFF0000, 1000);
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2F5233
});
/****
* Game Code
****/
// Game variables
var gold = 10;
var enemies = [];
var bullets = [];
var dice = [];
var defenders = [];
var wallSlots = [];
var diceSlots = [];
var draggedDie = null;
var trashArea = null;
var enemySpawnTimer = 0;
var enemySpawnInterval = 300; // 5 seconds initially
var gameTimer = 0; // Track game time in ticks
var dicesPurchased = 0; // Track number of dice purchased for pricing
var wallHealth = 100;
var rangedProjectiles = [];
// UI elements
var goldText = new Text2('Gold: 10', {
size: 60,
fill: 0xFFFF00
});
goldText.anchor.set(0, 0);
goldText.x = 150;
goldText.y = 50;
LK.gui.topLeft.addChild(goldText);
// Wall health display
var wallHealthText = new Text2('Wall Health: 100', {
size: 50,
fill: 0xFF0000
});
wallHealthText.anchor.set(0.5, 1);
wallHealthText.x = 1024;
wallHealthText.y = 1820;
game.addChild(wallHealthText);
// Create wall
var wall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 1,
x: 1024,
y: 1900
}));
// Create wall slots (9 defensive positions)
for (var i = 0; i < 9; i++) {
var slot = game.addChild(LK.getAsset('wallSlot', {
anchorX: 0.5,
anchorY: 0.5,
x: 200 + i * 200,
y: 1900 - 75
}));
wallSlots.push(slot);
}
// Create dice grid (3x3)
var diceGridStartX = 824; // Center on screen (1024 - 200)
var diceGridStartY = 2100;
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
var slot = game.addChild(LK.getAsset('diceSlot', {
anchorX: 0.5,
anchorY: 0.5,
x: diceGridStartX + col * 200,
y: diceGridStartY + row * 200
}));
slot.isEmpty = true;
slot.gridIndex = row * 3 + col;
diceSlots.push(slot);
}
}
// Create trash area to the left of dice grid
trashArea = game.addChild(LK.getAsset('trashArea', {
anchorX: 0.5,
anchorY: 0.5,
x: 524,
// Left of dice grid
y: 2200
}));
// Buy button
var buyButton = game.addChild(LK.getAsset('buyButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1600,
y: 2200
}));
// Price display text
var priceText = new Text2('Price: 10', {
size: 40,
fill: 0xFFFFFF
});
priceText.anchor.set(0.5, 0);
priceText.x = 1600;
priceText.y = 2400;
game.addChild(priceText);
buyButton.down = function (x, y, obj) {
var currentPrice = 10 + dicesPurchased * 5;
if (gold >= currentPrice) {
buyDie();
}
};
function buyDie() {
var emptySlot = null;
for (var i = 0; i < diceSlots.length; i++) {
if (diceSlots[i].isEmpty) {
emptySlot = diceSlots[i];
break;
}
}
if (emptySlot) {
var currentPrice = 10 + dicesPurchased * 5;
gold -= currentPrice;
dicesPurchased++;
goldText.setText('Gold: ' + gold);
// Update price display for next purchase
var nextPrice = 10 + dicesPurchased * 5;
priceText.setText('Price: ' + nextPrice);
var dieTypes = ['green', 'yellow', 'red', 'orange', 'blue', 'black'];
var weights = [30, 30, 20, 20, 10, 10]; // Common, Common, Rare, Rare, Legendary, Legendary
var randomType = getWeightedRandom(dieTypes, weights);
var newDie = new Die(randomType, 1);
newDie.x = emptySlot.x;
newDie.y = emptySlot.y;
newDie.slotIndex = emptySlot.gridIndex;
dice.push(newDie);
game.addChild(newDie);
emptySlot.isEmpty = false;
emptySlot.occupiedBy = newDie;
// Create corresponding defender
createDefender(newDie);
}
}
function getWeightedRandom(items, weights) {
var totalWeight = 0;
for (var i = 0; i < weights.length; i++) {
totalWeight += weights[i];
}
var random = Math.random() * totalWeight;
var currentWeight = 0;
for (var i = 0; i < items.length; i++) {
currentWeight += weights[i];
if (random <= currentWeight) {
return items[i];
}
}
return items[0];
}
function createDefender(die) {
var defender = new Defender(die.type, die.level);
var wallSlot = wallSlots[die.slotIndex % 9];
defender.x = wallSlot.x;
defender.y = wallSlot.y;
defender.linkedDie = die;
defenders.push(defender);
game.addChild(defender);
}
function spawnEnemy() {
var enemyType = 'troll';
// Phase 1: Only trolls (0-30 seconds)
if (gameTimer <= 1800) {
enemyType = 'troll';
}
// Phase 2: Trolls + skeletons (30-60 seconds)
else if (gameTimer <= 3600) {
enemyType = Math.random() < 0.7 ? 'troll' : 'skeleton';
}
// Phase 3: Add knights (60-90 seconds)
else if (gameTimer <= 5400) {
var rand = Math.random();
if (rand < 0.5) enemyType = 'troll';else if (rand < 0.8) enemyType = 'skeleton';else enemyType = 'knight';
}
// Phase 4: Knights + trolls in large numbers (90-120 seconds)
else if (gameTimer <= 7200) {
enemyType = Math.random() < 0.6 ? 'knight' : 'troll';
// Spawn multiple enemies
for (var i = 0; i < 2; i++) {
var enemy = new Enemy(enemyType);
enemy.x = Math.random() * 1800 + 124;
enemy.y = -100 - i * 100;
enemies.push(enemy);
game.addChild(enemy);
}
return;
}
// Phase 5: Add witches (120-150 seconds)
else if (gameTimer <= 9000) {
var rand = Math.random();
if (rand < 0.3) enemyType = 'witch';else if (rand < 0.6) enemyType = 'knight';else enemyType = 'troll';
}
// Phase 6: Witches + knights + trolls (150-180 seconds)
else if (gameTimer <= 10800) {
var rand = Math.random();
if (rand < 0.25) enemyType = 'witch';else if (rand < 0.6) enemyType = 'knight';else enemyType = 'troll';
}
// Phase 7: Add golems and demons (180+ seconds)
else {
var rand = Math.random();
if (rand < 0.15) enemyType = 'golem';else if (rand < 0.3) enemyType = 'demon';else if (rand < 0.45) enemyType = 'witch';else if (rand < 0.7) enemyType = 'knight';else enemyType = 'troll';
}
var enemy = new Enemy(enemyType);
enemy.x = Math.random() * 1800 + 124; // Random X position
enemy.y = -100; // Start above screen
enemies.push(enemy);
game.addChild(enemy);
}
function handleMove(x, y, obj) {
if (draggedDie) {
draggedDie.x = x;
draggedDie.y = y;
}
}
function handleUp(x, y, obj) {
if (draggedDie) {
var merged = false;
var trashed = false;
// Check if dropped on trash area
if (draggedDie.intersects(trashArea)) {
// Destroy die and give 5 gold
gold += 5;
goldText.setText('Gold: ' + gold);
// Remove die from slot
var slot = diceSlots[draggedDie.slotIndex];
slot.isEmpty = true;
slot.occupiedBy = null;
// Remove corresponding defender
for (var i = defenders.length - 1; i >= 0; i--) {
if (defenders[i].linkedDie === draggedDie) {
defenders[i].destroy();
defenders.splice(i, 1);
break;
}
}
// Remove die
draggedDie.destroy();
dice.splice(dice.indexOf(draggedDie), 1);
trashed = true;
}
if (!trashed) {
// Check for merge with other dice
for (var i = 0; i < dice.length; i++) {
var otherDie = dice[i];
if (otherDie !== draggedDie && otherDie.type === draggedDie.type && otherDie.level === draggedDie.level && draggedDie.intersects(otherDie)) {
// Merge dice
mergeDice(draggedDie, otherDie);
merged = true;
break;
}
}
if (!merged) {
// Return to original position
draggedDie.x = draggedDie.originalPosition.x;
draggedDie.y = draggedDie.originalPosition.y;
}
}
draggedDie = null;
}
}
function mergeDice(die1, die2) {
// Remove die1 from game
var slot1 = diceSlots[die1.slotIndex];
slot1.isEmpty = true;
slot1.occupiedBy = null;
// Remove corresponding defender
for (var i = defenders.length - 1; i >= 0; i--) {
if (defenders[i].linkedDie === die1) {
defenders[i].destroy();
defenders.splice(i, 1);
break;
}
}
die1.destroy();
dice.splice(dice.indexOf(die1), 1);
// Upgrade die2
die2.updateLevel(die2.level + 1);
// Update corresponding defender
for (var i = 0; i < defenders.length; i++) {
if (defenders[i].linkedDie === die2) {
defenders[i].level = die2.level;
break;
}
}
LK.getSound('merge').play();
}
game.move = handleMove;
game.up = handleUp;
game.update = function () {
// Update game timer
gameTimer++;
// Progressive enemy spawning
enemySpawnTimer++;
var currentInterval = enemySpawnInterval;
// Calculate progressive spawn rate increase
var baseInterval = 600; // Start at 10 seconds
var minInterval = 60; // Minimum 1 second spawn rate
var reductionRate = gameTimer * 0.0001; // Gradual reduction over time
currentInterval = Math.max(minInterval, baseInterval - gameTimer * 0.02);
// Phase-based adjustments
// Phase 1: Moderate spawning (0-30 seconds)
if (gameTimer <= 1800) {
currentInterval = Math.max(120, currentInterval); // At least 2 seconds
}
// Phase 2: Fast spawning (30-60 seconds)
else if (gameTimer <= 3600) {
currentInterval = Math.max(90, currentInterval); // At least 1.5 seconds
}
// Phase 3: Very fast spawning (60-90 seconds)
else if (gameTimer <= 5400) {
currentInterval = Math.max(60, currentInterval); // At least 1 second
}
// Phase 4: Extremely fast spawning for mass knights (90-120 seconds)
else if (gameTimer <= 7200) {
currentInterval = Math.max(30, currentInterval); // At least 0.5 seconds
}
// Phase 5: Very fast spawning (120-150 seconds)
else if (gameTimer <= 9000) {
currentInterval = Math.max(45, currentInterval); // At least 0.75 seconds
}
// Phase 6: Extremely fast spawning (150-180 seconds)
else if (gameTimer <= 10800) {
currentInterval = Math.max(30, currentInterval); // At least 0.5 seconds
}
// Phase 7: Maximum spawning rate (180+ seconds)
else {
currentInterval = Math.max(20, currentInterval); // At least 0.33 seconds
}
if (enemySpawnTimer >= currentInterval) {
enemySpawnTimer = 0;
spawnEnemy();
}
// Update bullets and check collisions
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
// Check if bullet should be destroyed
if (bullet.shouldDestroy || bullet.y < -50 || bullet.y > 2732 || bullet.x < 0 || bullet.x > 2048) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check collision with target or any enemy
var hitEnemy = false;
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
// Handle area damage for cannon and dark mage
if (bullet.bulletType === 'cannonBall') {
// Small area damage for cannon
var areaDamage = new AreaDamage(enemy.x, enemy.y, 80, bullet.damage, enemy);
areaDamage.lifetime = 120; // 2 seconds at 60fps
game.addChild(areaDamage);
} else if (bullet.bulletType === 'darkMagic') {
// Large area damage for dark mage
var areaDamage = new AreaDamage(enemy.x, enemy.y, 150, bullet.damage, enemy);
areaDamage.lifetime = 120; // 2 seconds at 60fps
game.addChild(areaDamage);
}
enemy.takeDamage(bullet.damage);
bullet.destroy();
bullets.splice(i, 1);
if (enemy.health <= 0) {
enemy.destroy();
enemies.splice(j, 1);
}
hitEnemy = true;
break;
}
}
}
// Update and cleanup area damage objects
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child.shouldDestroy) {
child.destroy();
}
}
// Update ranged projectiles
for (var i = rangedProjectiles.length - 1; i >= 0; i--) {
var projectile = rangedProjectiles[i];
if (projectile.shouldDestroy || projectile.y > 2000) {
projectile.destroy();
rangedProjectiles.splice(i, 1);
}
}
// Check if enemies reached the wall (only for melee enemies)
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.behavior !== 'ranged' && enemy.y >= 1770 && !enemy.hasAttackedWall) {
// Remove enemy after attacking wall
enemy.destroy();
enemies.splice(i, 1);
}
}
};
// Start with one free die
buyDie();
לוחם עם חץ וקשת מהאגדות. In-Game asset. 2d. High contrast. No shadows
חץ של קשת פונה כלפי מעלה. In-Game asset. 2d. High contrast. No shadows
Blue circle magic attack. In-Game asset. 2d. High contrast. No shadows
Dice factory from the fantasy world. In-Game asset. 2d. High contrast. No shadows
Trash area for dice from fantasy. In-Game asset. 2d. High contrast. No shadows
Troll enemy. In-Game asset. 2d. High contrast. No shadows
Skeleton enemy. In-Game asset. 2d. High contrast. No shadows
Cannon from the fantasy. In-Game asset. 2d. High contrast. No shadows
From the bird view, top of the wall. In-Game asset. 2d. High contrast. No shadows
עיגול בצבע מדברי עם חומה מדברית מקיפה אותו במבט אנכי מלמעלה. In-Game asset. 2d. High contrast. No shadows
קוסם אפל מהאגדות. In-Game asset. 2d. High contrast. No shadows
קוסם שמים אגדי. In-Game asset. 2d. High contrast. No shadows
Black magic from fantasy. In-Game asset. 2d. High contrast. No shadows
מכשפה מהאגדות. In-Game asset. 2d. High contrast. No shadows
פרש רוכב על סוס. In-Game asset. 2d. High contrast. No shadows
גולם עשוי אדמה מהאגדות. In-Game asset. 2d. High contrast. No shadows
שדון אדום מעופף. In-Game asset. 2d. High contrast. No shadows
מוסקטיר מהאגדות. In-Game asset. 2d. High contrast. No shadows
לוחם חנית. In-Game asset. 2d. High contrast. No shadows
bullet. In-Game asset. 2d. High contrast. No shadows
רקע למשחק פנטזיה מימין ומשמאל ממש צמוד למסגרת לא רחב מלא בהרים גבהות ויערות באמצע בכל השני שליש העליונים מדשאה ירוקה גדולה. בשליש התחתון גג מלבני ענק של צריח מדברי התמונה במבט מלמעלה בקו אנכי, מעוף הציפור. In-Game asset. 2d. High contrast. No shadows. In-Game asset. 2d. High contrast. No shadows