User prompt
buat layar jadi statik ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
buat efek meledak dan flash merah saat musuh mati ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
tower mampu menyrang dari seluruh mata angin
User prompt
tower non stop menyerang saat musuh datang
User prompt
tower bisa menyerang dari 4 arah sekaligus
User prompt
perbaiki display game
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'toGlobal')' in or related to this line: 'var localPos = gameWorld.toLocal(obj.parent.toGlobal(obj.position));' Line Number: 320
Code edit (1 edits merged)
Please save this source code
User prompt
Spirit Guard: Village Defense
Initial prompt
buat game side crolling gulir ke seluruh arah. game bergenre tower defense melindungi desa dari serangan roh roh jahat
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Projectile = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('projectile', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.target = null; self.damage = 20; self.update = function () { if (!self.target || self.target.destroyed) { self.destroy(); return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 20) { self.target.takeDamage(self.damage); self.destroy(); } else { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } }; self.destroy = function () { for (var i = projectiles.length - 1; i >= 0; i--) { if (projectiles[i] === self) { projectiles.splice(i, 1); break; } } if (self.parent) { self.parent.removeChild(self); } }; return self; }); var Spirit = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('spirit', { anchorX: 0.5, anchorY: 0.5 }); self.health = 50; self.maxHealth = 50; self.speed = 1; self.gold = 10; self.targetX = villageX; self.targetY = villageY; self.update = function () { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else { self.reachVillage(); } }; self.takeDamage = function (damage) { self.health -= damage; LK.getSound('spirit_hit').play(); if (self.health <= 0) { self.die(); } }; self.die = function () { gold += self.gold; goldText.setText('Gold: ' + gold); LK.getSound('spirit_death').play(); for (var i = spirits.length - 1; i >= 0; i--) { if (spirits[i] === self) { spirits.splice(i, 1); break; } } self.destroy(); }; self.reachVillage = function () { villageHealth -= 10; healthText.setText('Health: ' + villageHealth); if (villageHealth <= 0) { LK.showGameOver(); } for (var i = spirits.length - 1; i >= 0; i--) { if (spirits[i] === self) { spirits.splice(i, 1); break; } } self.destroy(); }; return self; }); var Tower = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('tower', { anchorX: 0.5, anchorY: 0.5 }); self.range = 150; self.damage = 20; self.fireRate = 60; // frames between shots self.lastShot = 0; self.level = 1; self.update = function () { self.lastShot++; if (self.lastShot >= self.fireRate) { var target = self.findTarget(); if (target) { self.shoot(target); self.lastShot = 0; } } }; self.findTarget = function () { var closestSpirit = null; var closestDistance = self.range; for (var i = 0; i < spirits.length; i++) { var spirit = spirits[i]; var dx = spirit.x - self.x; var dy = spirit.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.range && distance < closestDistance) { closestDistance = distance; closestSpirit = spirit; } } return closestSpirit; }; self.shoot = function (target) { var projectile = new Projectile(); projectile.x = self.x; projectile.y = self.y; projectile.target = target; projectile.damage = self.damage; projectiles.push(projectile); gameWorld.addChild(projectile); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2F4F2F }); /**** * Game Code ****/ var gameWorld = new Container(); game.addChild(gameWorld); var gameWorldX = 0; var gameWorldY = 0; var isDragging = false; var lastDragX = 0; var lastDragY = 0; var villageX = 0; var villageY = 0; var villageHealth = 100; var gold = 100; var wave = 1; var spiritsToSpawn = 5; var spiritsSpawned = 0; var spawnTimer = 0; var towers = []; var spirits = []; var projectiles = []; var placementMode = false; var placementPreview = null; // Create village at center var village = gameWorld.attachAsset('village', { anchorX: 0.5, anchorY: 0.5, x: villageX, y: villageY }); // UI Elements var goldText = new Text2('Gold: ' + gold, { size: 60, fill: 0xFFD700 }); goldText.anchor.set(0, 0); LK.gui.topRight.addChild(goldText); var healthText = new Text2('Health: ' + villageHealth, { size: 60, fill: 0xFF0000 }); healthText.anchor.set(0, 0); healthText.y = 70; LK.gui.topRight.addChild(healthText); var waveText = new Text2('Wave: ' + wave, { size: 60, fill: 0xFFFFFF }); waveText.anchor.set(0.5, 0); LK.gui.top.addChild(waveText); var buildButton = new Text2('Build Tower (50g)', { size: 50, fill: 0x00FF00 }); buildButton.anchor.set(0.5, 1); LK.gui.bottom.addChild(buildButton); function canPlaceTower(x, y) { // Check distance from village var dx = x - villageX; var dy = y - villageY; var distanceFromVillage = Math.sqrt(dx * dx + dy * dy); if (distanceFromVillage < 150) { return false; } // Check distance from other towers for (var i = 0; i < towers.length; i++) { var tower = towers[i]; var tdx = x - tower.x; var tdy = y - tower.y; var distanceFromTower = Math.sqrt(tdx * tdx + tdy * tdy); if (distanceFromTower < 80) { return false; } } return true; } function spawnSpirit() { if (spiritsSpawned >= spiritsToSpawn) { return; } var spirit = new Spirit(); // Spawn from random edge var edge = Math.floor(Math.random() * 4); var mapSize = 1500; switch (edge) { case 0: // Top spirit.x = Math.random() * mapSize - mapSize / 2; spirit.y = -mapSize / 2; break; case 1: // Right spirit.x = mapSize / 2; spirit.y = Math.random() * mapSize - mapSize / 2; break; case 2: // Bottom spirit.x = Math.random() * mapSize - mapSize / 2; spirit.y = mapSize / 2; break; case 3: // Left spirit.x = -mapSize / 2; spirit.y = Math.random() * mapSize - mapSize / 2; break; } spirits.push(spirit); gameWorld.addChild(spirit); spiritsSpawned++; } function nextWave() { if (spirits.length === 0 && spiritsSpawned >= spiritsToSpawn) { wave++; spiritsToSpawn = 5 + wave * 2; spiritsSpawned = 0; spawnTimer = 0; waveText.setText('Wave: ' + wave); // Bonus gold for completing wave gold += 20; goldText.setText('Gold: ' + gold); } } game.down = function (x, y, obj) { var localPos = gameWorld.toLocal(game.toGlobal({ x: x, y: y })); if (placementMode) { if (canPlaceTower(localPos.x, localPos.y) && gold >= 50) { var tower = new Tower(); tower.x = localPos.x; tower.y = localPos.y; towers.push(tower); gameWorld.addChild(tower); gold -= 50; goldText.setText('Gold: ' + gold); LK.getSound('place_tower').play(); placementMode = false; if (placementPreview) { placementPreview.destroy(); placementPreview = null; } } } else { isDragging = true; lastDragX = x; lastDragY = y; } }; game.move = function (x, y, obj) { var localPos = gameWorld.toLocal(game.toGlobal({ x: x, y: y })); if (placementMode) { if (placementPreview) { placementPreview.destroy(); } if (canPlaceTower(localPos.x, localPos.y)) { placementPreview = gameWorld.attachAsset('validPlacement', { anchorX: 0.5, anchorY: 0.5, x: localPos.x, y: localPos.y, alpha: 0.5 }); } else { placementPreview = gameWorld.attachAsset('invalidPlacement', { anchorX: 0.5, anchorY: 0.5, x: localPos.x, y: localPos.y, alpha: 0.5 }); } } else if (isDragging) { var deltaX = x - lastDragX; var deltaY = y - lastDragY; gameWorldX += deltaX; gameWorldY += deltaY; // Limit scrolling gameWorldX = Math.max(-1000, Math.min(1000, gameWorldX)); gameWorldY = Math.max(-1000, Math.min(1000, gameWorldY)); gameWorld.x = gameWorldX; gameWorld.y = gameWorldY; lastDragX = x; lastDragY = y; } }; game.up = function (x, y, obj) { isDragging = false; }; buildButton.down = function (x, y, obj) { if (gold >= 50) { placementMode = !placementMode; if (!placementMode && placementPreview) { placementPreview.destroy(); placementPreview = null; } } }; game.update = function () { // Spawn spirits spawnTimer++; if (spawnTimer >= 120) { // Spawn every 2 seconds spawnSpirit(); spawnTimer = 0; } // Check for next wave nextWave(); // Update all game objects for (var i = 0; i < towers.length; i++) { towers[i].update(); } for (var i = 0; i < spirits.length; i++) { spirits[i].update(); } for (var i = 0; i < projectiles.length; i++) { projectiles[i].update(); } };
===================================================================
--- original.js
+++ change.js
@@ -279,9 +279,12 @@
goldText.setText('Gold: ' + gold);
}
}
game.down = function (x, y, obj) {
- var localPos = gameWorld.toLocal(obj.parent.toGlobal(obj.position));
+ var localPos = gameWorld.toLocal(game.toGlobal({
+ x: x,
+ y: y
+ }));
if (placementMode) {
if (canPlaceTower(localPos.x, localPos.y) && gold >= 50) {
var tower = new Tower();
tower.x = localPos.x;
@@ -303,9 +306,12 @@
lastDragY = y;
}
};
game.move = function (x, y, obj) {
- var localPos = gameWorld.toLocal(obj.parent.toGlobal(obj.position));
+ var localPos = gameWorld.toLocal(game.toGlobal({
+ x: x,
+ y: y
+ }));
if (placementMode) {
if (placementPreview) {
placementPreview.destroy();
}
image top down hutan pinus luas yang ditengahnya terdapat kerajaan peri daun. 2d anime. In-Game asset. 2d. High contrast. No shadows
kesatria peri pria seluruh badan bercahaya melayang dengan tangan merengggang keatas. In-Game asset. 2d. High contrast. No shadows
roh jahat berbentuk orb gelap. In-Game asset. 2d. High contrast. No shadows
roh jahat bentuk orb api. In-Game asset. 2d. High contrast. No shadows