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