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(obj.parent.toGlobal(obj.position)); 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(obj.parent.toGlobal(obj.position)); 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
@@ -1,6 +1,375 @@
-/****
+/****
+* 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: 0x000000
-});
\ No newline at end of file
+ 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(obj.parent.toGlobal(obj.position));
+ 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(obj.parent.toGlobal(obj.position));
+ 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();
+ }
+};
\ No newline at end of file
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