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.direction = null;
self.damage = 20;
self.update = function () {
if (self.target && !self.target.destroyed) {
// Move towards 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 < 20) {
self.target.takeDamage(self.damage);
self.destroy();
return;
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
} else if (self.direction) {
// Move in fixed direction
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
// Check if we hit any spirit along the way
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 < 30) {
spirit.takeDamage(self.damage);
self.destroy();
return;
}
}
// Destroy if projectile goes too far
if (Math.abs(self.x) > 1000 || Math.abs(self.y) > 1000) {
self.destroy();
return;
}
} else {
// No target and no direction, destroy
self.destroy();
return;
}
};
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) {
// Shoot continuously if there are any spirits on the map
if (spirits.length > 0) {
self.shoot(null); // Pass null since we're shooting in all directions
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) {
// Fire projectiles in 4 cardinal directions
var directions = [{
x: 0,
y: -1
},
// Up
{
x: 1,
y: 0
},
// Right
{
x: 0,
y: 1
},
// Down
{
x: -1,
y: 0
} // Left
];
for (var d = 0; d < directions.length; d++) {
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
// Always find closest spirit in this direction for targeting
var dirTarget = self.findTargetInDirection(directions[d]);
if (dirTarget) {
projectile.target = dirTarget;
} else {
// Always shoot in the direction even if no specific target
projectile.direction = directions[d];
}
projectile.damage = self.damage;
projectiles.push(projectile);
gameWorld.addChild(projectile);
}
};
self.findTargetInDirection = function (direction) {
var bestTarget = null;
var bestDistance = 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);
// Check if spirit is within range
if (distance <= self.range && distance < bestDistance) {
// Check if spirit is roughly in the desired direction
var normalizedDx = dx / distance;
var normalizedDy = dy / distance;
// Calculate dot product to see if spirit aligns with direction
var dot = normalizedDx * direction.x + normalizedDy * direction.y;
// If dot product > 0.5, spirit is in this general direction
if (dot > 0.5) {
bestDistance = distance;
bestTarget = spirit;
}
}
}
return bestTarget;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2F4F2F
});
/****
* Game Code
****/
var gameWorld = new Container();
game.addChild(gameWorld);
var gameWorldX = 1024; // Center horizontally (2048/2)
var gameWorldY = 1366; // Center vertically (2732/2)
gameWorld.x = gameWorldX;
gameWorld.y = gameWorldY;
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: 80,
fill: 0xFFD700
});
goldText.anchor.set(1, 0);
goldText.x = -20;
goldText.y = 120;
LK.gui.topRight.addChild(goldText);
var healthText = new Text2('Health: ' + villageHealth, {
size: 80,
fill: 0xFF0000
});
healthText.anchor.set(1, 0);
healthText.x = -20;
healthText.y = 220;
LK.gui.topRight.addChild(healthText);
var waveText = new Text2('Wave: ' + wave, {
size: 100,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
waveText.y = 120;
LK.gui.top.addChild(waveText);
var buildButton = new Text2('Build Tower (50g)', {
size: 80,
fill: 0x00FF00
});
buildButton.anchor.set(0.5, 1);
buildButton.y = -50;
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
@@ -141,11 +141,11 @@
self.level = 1;
self.update = function () {
self.lastShot++;
if (self.lastShot >= self.fireRate) {
- var target = self.findTarget();
- if (target) {
- self.shoot(target);
+ // Shoot continuously if there are any spirits on the map
+ if (spirits.length > 0) {
+ self.shoot(null); // Pass null since we're shooting in all directions
self.lastShot = 0;
}
}
};
@@ -189,14 +189,14 @@
for (var d = 0; d < directions.length; d++) {
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
- // Find closest spirit in this direction
+ // Always find closest spirit in this direction for targeting
var dirTarget = self.findTargetInDirection(directions[d]);
if (dirTarget) {
projectile.target = dirTarget;
} else {
- // If no target in direction, shoot straight in that direction
+ // Always shoot in the direction even if no specific target
projectile.direction = directions[d];
}
projectile.damage = self.damage;
projectiles.push(projectile);
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