User prompt
add background asset
User prompt
make enemy slow moving
User prompt
repair the control
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'x')' in or related to this line: 'var dx = enemies[i].x - barriers[j].x;' Line Number: 624
Code edit (1 edits merged)
Please save this source code
User prompt
Crystal Guardian: Arcane Defender
Initial prompt
fantasy action game
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
spellsUnlocked: 1
});
/****
* Classes
****/
var AreaSpell = Container.expand(function () {
var self = Container.call(this);
var spellGraphic = self.attachAsset('areaSpell', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
self.damage = 30;
self.lifespan = 60; // 1 second at 60fps
self.age = 0;
self.update = function () {
self.age++;
// Fade out as it ages
spellGraphic.alpha = 0.7 * (1 - self.age / self.lifespan);
// Scale up slightly
var scale = 1 + self.age / self.lifespan * 0.5;
spellGraphic.scaleX = scale;
spellGraphic.scaleY = scale;
if (self.age >= self.lifespan) {
return true; // Mark for removal
}
return false;
};
return self;
});
var Barrier = Container.expand(function () {
var self = Container.call(this);
var barrierGraphic = self.attachAsset('barrier', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 30;
self.maxHealth = 30;
self.lifespan = 300; // 5 seconds at 60fps
self.age = 0;
self.takeDamage = function (amount) {
self.health -= amount;
// Visual feedback
LK.effects.flashObject(self, 0xffffff, 200);
// Update transparency based on remaining health
barrierGraphic.alpha = 0.3 + self.health / self.maxHealth * 0.7;
return self.health <= 0;
};
self.update = function () {
self.age++;
// Fade out as it approaches end of life
if (self.age > self.lifespan * 0.7) {
barrierGraphic.alpha = 0.3 + self.health / self.maxHealth * 0.7 * (1 - (self.age - self.lifespan * 0.7) / (self.lifespan * 0.3));
}
if (self.age >= self.lifespan) {
return true; // Mark for removal
}
return false;
};
return self;
});
var Crystal = Container.expand(function () {
var self = Container.call(this);
var crystalGraphic = self.attachAsset('crystal', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.pulseStrength = 0;
self.canUseAreaSpell = false;
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health <= 0) {
self.health = 0;
LK.showGameOver();
}
// Visual feedback
LK.effects.flashObject(self, 0xff0000, 500);
LK.getSound('crystalHit').play();
// Update pulse strength (for area spell availability)
self.pulseStrength += amount;
if (self.pulseStrength >= 30) {
self.pulseStrength = 30;
self.canUseAreaSpell = true;
// Pulse animation to show area spell is ready
if (!self.isPulsing) {
self.isPulsing = true;
self.pulseAnimation();
}
}
};
self.useAreaSpell = function () {
if (!self.canUseAreaSpell) {
return false;
}
self.pulseStrength = 0;
self.canUseAreaSpell = false;
self.isPulsing = false;
// Reset scale from pulsing animation
tween(crystalGraphic, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
return true;
};
self.pulseAnimation = function () {
if (!self.canUseAreaSpell) {
self.isPulsing = false;
return;
}
tween(crystalGraphic, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 700,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(crystalGraphic, {
scaleX: 1,
scaleY: 1
}, {
duration: 700,
easing: tween.easeInOut,
onFinish: self.pulseAnimation
});
}
});
};
self.heal = function (amount) {
self.health += amount;
if (self.health > self.maxHealth) {
self.health = self.maxHealth;
}
};
return self;
});
var Enemy = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'basic';
var assetId = 'basicEnemy';
var enemyScale = 1;
self.health = 20;
self.maxHealth = 20;
self.speed = 2;
self.damage = 10;
self.scoreValue = 10;
if (self.type === 'fast') {
assetId = 'fastEnemy';
self.health = 10;
self.maxHealth = 10;
self.speed = 3.5;
self.damage = 5;
self.scoreValue = 15;
} else if (self.type === 'tough') {
assetId = 'toughEnemy';
self.health = 40;
self.maxHealth = 40;
self.speed = 1.2;
self.damage = 15;
self.scoreValue = 25;
} else if (self.type === 'miniBoss') {
assetId = 'miniBoss';
self.health = 100;
self.maxHealth = 100;
self.speed = 0.8;
self.damage = 25;
self.scoreValue = 100;
enemyScale = 1.5;
}
var enemyGraphic = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: enemyScale,
scaleY: enemyScale
});
self.takeDamage = function (amount) {
self.health -= amount;
// Visual feedback
LK.effects.flashObject(self, 0xffffff, 200);
LK.getSound('enemyHit').play();
if (self.health <= 0) {
LK.getSound('enemyDeath').play();
return true; // Enemy is defeated
}
return false;
};
self.update = function () {
// Move toward crystal
var dx = crystal.x - self.x;
var dy = crystal.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.speed) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Enemy reached crystal, attack it
crystal.takeDamage(self.damage);
return true; // Mark for removal
}
return false;
};
return self;
});
var Guardian = Container.expand(function () {
var self = Container.call(this);
var guardianGraphic = self.attachAsset('guardian', {
anchorX: 0.5,
anchorY: 0.5
});
self.targetX = 0;
self.targetY = 0;
self.movementSpeed = 6;
self.castSpell = function (targetX, targetY, type) {
var spell = new Spell(type);
spell.x = self.x;
spell.y = self.y;
// Calculate direction
var dx = targetX - self.x;
var dy = targetY - self.y;
var magnitude = Math.sqrt(dx * dx + dy * dy);
// Normalize and set velocity
spell.velocityX = dx / magnitude * spell.speed;
spell.velocityY = dy / magnitude * spell.speed;
return spell;
};
self.createBarrier = function (x, y, rotation) {
var barrier = new Barrier();
barrier.x = x;
barrier.y = y;
barrier.rotation = rotation;
return barrier;
};
self.update = function () {
// Move toward target position
if (self.targetX !== 0 || self.targetY !== 0) {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.movementSpeed) {
self.x += dx / distance * self.movementSpeed;
self.y += dy / distance * self.movementSpeed;
} else {
self.x = self.targetX;
self.y = self.targetY;
self.targetX = 0;
self.targetY = 0;
}
}
};
return self;
});
var Spell = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'basic';
var assetId = 'basicSpell';
var spellColor = 0xffff00;
self.damage = 10;
self.speed = 12;
self.lifespan = 120; // in frames (2 seconds at 60fps)
self.age = 0;
if (self.type === 'fire') {
self.damage = 15;
spellColor = 0xff4400;
} else if (self.type === 'ice') {
self.damage = 8;
self.speed = 10;
spellColor = 0x44ddff;
} else if (self.type === 'lightning') {
self.damage = 20;
self.speed = 16;
spellColor = 0xddff00;
}
var spellGraphic = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
tint: spellColor
});
self.velocityX = 0;
self.velocityY = 0;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.age++;
if (self.age >= self.lifespan) {
return true; // Mark for removal
}
return false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x112233
});
/****
* Game Code
****/
// Game dimensions
var gameWidth = 2048;
var gameHeight = 2732;
// Game state variables
var crystal;
var guardian;
var spells = [];
var barriers = [];
var enemies = [];
var areaSpells = [];
var wave = 1;
var score = 0;
var spawnTimer = 0;
var spawnDelay = 120; // 2 seconds at 60fps
var waveInProgress = false;
var enemiesInWave = 0;
var enemiesSpawned = 0;
var dragBarrierStart = null;
// UI elements
var scoreTxt;
var waveTxt;
var healthTxt;
var areaSpellReadyTxt;
// Initialize game
function initGame() {
// Play background music
LK.playMusic('battleMusic');
// Create and position crystal
crystal = new Crystal();
crystal.x = gameWidth / 2;
crystal.y = gameHeight / 2;
game.addChild(crystal);
// Create guardian
guardian = new Guardian();
guardian.x = gameWidth / 2;
guardian.y = gameHeight / 2 + 300;
game.addChild(guardian);
// Create UI elements
createUI();
// Reset game state
wave = 1;
score = 0;
LK.setScore(score);
spawnTimer = 60; // Start first wave after 1 second
waveInProgress = false;
updateUI();
}
function createUI() {
// Score text
scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(1, 0); // Right aligned
LK.gui.topRight.addChild(scoreTxt);
// Wave text
waveTxt = new Text2('Wave: 1', {
size: 60,
fill: 0xFFFFFF
});
waveTxt.anchor.set(0, 0); // Left aligned
// Make sure this doesn't overlap with the top left corner menu icon
waveTxt.x = 120; // Add some padding from the left edge
LK.gui.top.addChild(waveTxt);
// Health text
healthTxt = new Text2('Crystal: 100/100', {
size: 60,
fill: 0xFFFFFF
});
healthTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(healthTxt);
// Area spell ready indicator
areaSpellReadyTxt = new Text2('Area Spell: Ready!', {
size: 50,
fill: 0xFFFF00
});
areaSpellReadyTxt.anchor.set(0.5, 0);
areaSpellReadyTxt.y = 70;
areaSpellReadyTxt.visible = false;
LK.gui.top.addChild(areaSpellReadyTxt);
}
function updateUI() {
scoreTxt.setText('Score: ' + score);
waveTxt.setText('Wave: ' + wave);
healthTxt.setText('Crystal: ' + crystal.health + '/' + crystal.maxHealth);
// Show/hide area spell indicator
areaSpellReadyTxt.visible = crystal.canUseAreaSpell;
}
function startWave() {
waveInProgress = true;
enemiesInWave = 5 + wave * 2;
enemiesSpawned = 0;
spawnTimer = 0;
// Determine if this wave has a mini-boss
hasMiniBase = wave % 3 === 0;
if (hasMiniBase) {
enemiesInWave += 1; // Add mini-boss to count
}
}
function spawnEnemy() {
// Determine enemy type based on wave and some randomness
var enemyType = 'basic';
var randomValue = Math.random();
if (wave >= 3 && enemiesSpawned === enemiesInWave - 1 && wave % 3 === 0) {
// Spawn mini-boss as last enemy in every third wave
enemyType = 'miniBoss';
} else if (wave >= 2 && randomValue < 0.3) {
enemyType = 'fast';
} else if (wave >= 4 && randomValue < 0.5) {
enemyType = 'tough';
}
var enemy = new Enemy(enemyType);
// Spawn at random position around the edges
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
enemy.x = Math.random() * gameWidth;
enemy.y = -50;
break;
case 1:
// Right
enemy.x = gameWidth + 50;
enemy.y = Math.random() * gameHeight;
break;
case 2:
// Bottom
enemy.x = Math.random() * gameWidth;
enemy.y = gameHeight + 50;
break;
case 3:
// Left
enemy.x = -50;
enemy.y = Math.random() * gameHeight;
break;
}
enemies.push(enemy);
game.addChild(enemy);
enemiesSpawned++;
// Check if we've spawned all enemies for this wave
if (enemiesSpawned >= enemiesInWave) {
waveInProgress = false;
}
}
function checkWaveCompletion() {
if (!waveInProgress && enemies.length === 0 && enemiesSpawned >= enemiesInWave && enemiesInWave > 0) {
// Wave completed
wave++;
LK.getSound('waveComplete').play();
// Add bonus points for completing wave
score += wave * 50;
LK.setScore(score);
// Update high score if needed
if (score > storage.highScore) {
storage.highScore = score;
}
updateUI();
// Short delay before next wave
spawnTimer = 180; // 3 seconds at 60fps
enemiesInWave = 0;
}
}
function castAreaSpell() {
if (!crystal.canUseAreaSpell) {
return;
}
var areaSpell = new AreaSpell();
areaSpell.x = crystal.x;
areaSpell.y = crystal.y;
areaSpells.push(areaSpell);
game.addChild(areaSpell);
LK.getSound('areaSpell').play();
// Use the crystal's area spell ability
crystal.useAreaSpell();
updateUI();
}
// Event handlers
game.down = function (x, y, obj) {
// Check if clicking on crystal and area spell is ready
if (crystal.canUseAreaSpell) {
var crystalGlobalPos = game.toLocal(crystal.position);
var dx = x - crystalGlobalPos.x;
var dy = y - crystalGlobalPos.y;
var distanceSquared = dx * dx + dy * dy;
if (distanceSquared <= 120 * 120) {
castAreaSpell();
return;
}
}
// Start barrier creation
dragBarrierStart = {
x: x,
y: y
};
// Set guardian target
guardian.targetX = x;
guardian.targetY = y;
};
game.move = function (x, y, obj) {
if (dragBarrierStart) {
// Visual feedback for barrier drawing
// In a real implementation, we'd show a preview of the barrier
}
};
game.up = function (x, y, obj) {
if (dragBarrierStart) {
// Calculate barrier properties
var dx = x - dragBarrierStart.x;
var dy = y - dragBarrierStart.y;
var length = Math.sqrt(dx * dx + dy * dy);
// Only create barrier if drag distance is sufficient
if (length > 100) {
// Calculate barrier position (middle of the line)
var barrierX = dragBarrierStart.x + dx / 2;
var barrierY = dragBarrierStart.y + dy / 2;
// Calculate rotation angle
var rotation = Math.atan2(dy, dx);
var barrier = guardian.createBarrier(barrierX, barrierY, rotation);
barriers.push(barrier);
game.addChild(barrier);
LK.getSound('barrierCreate').play();
} else {
// If not creating a barrier, cast a spell instead
var spell = guardian.castSpell(x, y, 'basic');
spells.push(spell);
game.addChild(spell);
LK.getSound('spellCast').play();
}
dragBarrierStart = null;
}
};
// Game update loop
game.update = function () {
// Initialize game on first update
if (!crystal) {
initGame();
return;
}
// Update guardian
guardian.update();
// Spawn enemies
if (spawnTimer <= 0) {
if (!waveInProgress && enemiesInWave === 0) {
startWave();
} else if (waveInProgress && enemiesSpawned < enemiesInWave) {
spawnEnemy();
spawnTimer = spawnDelay - wave * 5; // Decrease spawn delay as waves progress
if (spawnTimer < 30) {
spawnTimer = 30;
} // Minimum spawn delay
}
} else {
spawnTimer--;
}
// Update spells and check for collisions
for (var i = spells.length - 1; i >= 0; i--) {
var removeSpell = spells[i].update();
// Check for collisions with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
if (spells[i].intersects(enemies[j])) {
removeSpell = true;
// Apply damage to enemy
if (enemies[j].takeDamage(spells[i].damage)) {
// Enemy defeated
score += enemies[j].scoreValue;
LK.setScore(score);
enemies[j].destroy();
enemies.splice(j, 1);
}
break;
}
}
// Remove spell if needed
if (removeSpell) {
spells[i].destroy();
spells.splice(i, 1);
}
}
// Update barriers
for (var i = barriers.length - 1; i >= 0; i--) {
if (barriers[i].update()) {
barriers[i].destroy();
barriers.splice(i, 1);
continue;
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var removeEnemy = enemies[i].update();
// Check for collisions with barriers
for (var j = barriers.length - 1; j >= 0; j--) {
if (enemies[i].intersects(barriers[j])) {
// Enemy hits barrier
if (barriers[j].takeDamage(1)) {
barriers[j].destroy();
barriers.splice(j, 1);
}
// Slow down or push back enemy slightly (simplified physics)
var dx = enemies[i].x - barriers[j].x;
var dy = enemies[i].y - barriers[j].y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
enemies[i].x += dx / dist * 2;
enemies[i].y += dy / dist * 2;
}
break;
}
}
// Remove enemy if needed
if (removeEnemy) {
enemies[i].destroy();
enemies.splice(i, 1);
}
}
// Update area spells
for (var i = areaSpells.length - 1; i >= 0; i--) {
if (areaSpells[i].update()) {
areaSpells[i].destroy();
areaSpells.splice(i, 1);
continue;
}
// Check for enemy collisions
for (var j = enemies.length - 1; j >= 0; j--) {
if (areaSpells[i].intersects(enemies[j])) {
// Apply damage to enemy
if (enemies[j].takeDamage(areaSpells[i].damage)) {
// Enemy defeated
score += enemies[j].scoreValue;
LK.setScore(score);
enemies[j].destroy();
enemies.splice(j, 1);
}
}
}
}
// Check if wave is complete
checkWaveCompletion();
// Update UI
updateUI();
}; ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,651 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+var storage = LK.import("@upit/storage.v1", {
+ highScore: 0,
+ spellsUnlocked: 1
+});
+
+/****
+* Classes
+****/
+var AreaSpell = Container.expand(function () {
+ var self = Container.call(this);
+ var spellGraphic = self.attachAsset('areaSpell', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.7
+ });
+ self.damage = 30;
+ self.lifespan = 60; // 1 second at 60fps
+ self.age = 0;
+ self.update = function () {
+ self.age++;
+ // Fade out as it ages
+ spellGraphic.alpha = 0.7 * (1 - self.age / self.lifespan);
+ // Scale up slightly
+ var scale = 1 + self.age / self.lifespan * 0.5;
+ spellGraphic.scaleX = scale;
+ spellGraphic.scaleY = scale;
+ if (self.age >= self.lifespan) {
+ return true; // Mark for removal
+ }
+ return false;
+ };
+ return self;
+});
+var Barrier = Container.expand(function () {
+ var self = Container.call(this);
+ var barrierGraphic = self.attachAsset('barrier', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.health = 30;
+ self.maxHealth = 30;
+ self.lifespan = 300; // 5 seconds at 60fps
+ self.age = 0;
+ self.takeDamage = function (amount) {
+ self.health -= amount;
+ // Visual feedback
+ LK.effects.flashObject(self, 0xffffff, 200);
+ // Update transparency based on remaining health
+ barrierGraphic.alpha = 0.3 + self.health / self.maxHealth * 0.7;
+ return self.health <= 0;
+ };
+ self.update = function () {
+ self.age++;
+ // Fade out as it approaches end of life
+ if (self.age > self.lifespan * 0.7) {
+ barrierGraphic.alpha = 0.3 + self.health / self.maxHealth * 0.7 * (1 - (self.age - self.lifespan * 0.7) / (self.lifespan * 0.3));
+ }
+ if (self.age >= self.lifespan) {
+ return true; // Mark for removal
+ }
+ return false;
+ };
+ return self;
+});
+var Crystal = Container.expand(function () {
+ var self = Container.call(this);
+ var crystalGraphic = self.attachAsset('crystal', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.health = 100;
+ self.maxHealth = 100;
+ self.pulseStrength = 0;
+ self.canUseAreaSpell = false;
+ self.takeDamage = function (amount) {
+ self.health -= amount;
+ if (self.health <= 0) {
+ self.health = 0;
+ LK.showGameOver();
+ }
+ // Visual feedback
+ LK.effects.flashObject(self, 0xff0000, 500);
+ LK.getSound('crystalHit').play();
+ // Update pulse strength (for area spell availability)
+ self.pulseStrength += amount;
+ if (self.pulseStrength >= 30) {
+ self.pulseStrength = 30;
+ self.canUseAreaSpell = true;
+ // Pulse animation to show area spell is ready
+ if (!self.isPulsing) {
+ self.isPulsing = true;
+ self.pulseAnimation();
+ }
+ }
+ };
+ self.useAreaSpell = function () {
+ if (!self.canUseAreaSpell) {
+ return false;
+ }
+ self.pulseStrength = 0;
+ self.canUseAreaSpell = false;
+ self.isPulsing = false;
+ // Reset scale from pulsing animation
+ tween(crystalGraphic, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 100
+ });
+ return true;
+ };
+ self.pulseAnimation = function () {
+ if (!self.canUseAreaSpell) {
+ self.isPulsing = false;
+ return;
+ }
+ tween(crystalGraphic, {
+ scaleX: 1.2,
+ scaleY: 1.2
+ }, {
+ duration: 700,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ tween(crystalGraphic, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 700,
+ easing: tween.easeInOut,
+ onFinish: self.pulseAnimation
+ });
+ }
+ });
+ };
+ self.heal = function (amount) {
+ self.health += amount;
+ if (self.health > self.maxHealth) {
+ self.health = self.maxHealth;
+ }
+ };
+ return self;
+});
+var Enemy = Container.expand(function (type) {
+ var self = Container.call(this);
+ self.type = type || 'basic';
+ var assetId = 'basicEnemy';
+ var enemyScale = 1;
+ self.health = 20;
+ self.maxHealth = 20;
+ self.speed = 2;
+ self.damage = 10;
+ self.scoreValue = 10;
+ if (self.type === 'fast') {
+ assetId = 'fastEnemy';
+ self.health = 10;
+ self.maxHealth = 10;
+ self.speed = 3.5;
+ self.damage = 5;
+ self.scoreValue = 15;
+ } else if (self.type === 'tough') {
+ assetId = 'toughEnemy';
+ self.health = 40;
+ self.maxHealth = 40;
+ self.speed = 1.2;
+ self.damage = 15;
+ self.scoreValue = 25;
+ } else if (self.type === 'miniBoss') {
+ assetId = 'miniBoss';
+ self.health = 100;
+ self.maxHealth = 100;
+ self.speed = 0.8;
+ self.damage = 25;
+ self.scoreValue = 100;
+ enemyScale = 1.5;
+ }
+ var enemyGraphic = self.attachAsset(assetId, {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: enemyScale,
+ scaleY: enemyScale
+ });
+ self.takeDamage = function (amount) {
+ self.health -= amount;
+ // Visual feedback
+ LK.effects.flashObject(self, 0xffffff, 200);
+ LK.getSound('enemyHit').play();
+ if (self.health <= 0) {
+ LK.getSound('enemyDeath').play();
+ return true; // Enemy is defeated
+ }
+ return false;
+ };
+ self.update = function () {
+ // Move toward crystal
+ var dx = crystal.x - self.x;
+ var dy = crystal.y - self.y;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance > self.speed) {
+ self.x += dx / distance * self.speed;
+ self.y += dy / distance * self.speed;
+ } else {
+ // Enemy reached crystal, attack it
+ crystal.takeDamage(self.damage);
+ return true; // Mark for removal
+ }
+ return false;
+ };
+ return self;
+});
+var Guardian = Container.expand(function () {
+ var self = Container.call(this);
+ var guardianGraphic = self.attachAsset('guardian', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.targetX = 0;
+ self.targetY = 0;
+ self.movementSpeed = 6;
+ self.castSpell = function (targetX, targetY, type) {
+ var spell = new Spell(type);
+ spell.x = self.x;
+ spell.y = self.y;
+ // Calculate direction
+ var dx = targetX - self.x;
+ var dy = targetY - self.y;
+ var magnitude = Math.sqrt(dx * dx + dy * dy);
+ // Normalize and set velocity
+ spell.velocityX = dx / magnitude * spell.speed;
+ spell.velocityY = dy / magnitude * spell.speed;
+ return spell;
+ };
+ self.createBarrier = function (x, y, rotation) {
+ var barrier = new Barrier();
+ barrier.x = x;
+ barrier.y = y;
+ barrier.rotation = rotation;
+ return barrier;
+ };
+ self.update = function () {
+ // Move toward target position
+ if (self.targetX !== 0 || self.targetY !== 0) {
+ var dx = self.targetX - self.x;
+ var dy = self.targetY - self.y;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance > self.movementSpeed) {
+ self.x += dx / distance * self.movementSpeed;
+ self.y += dy / distance * self.movementSpeed;
+ } else {
+ self.x = self.targetX;
+ self.y = self.targetY;
+ self.targetX = 0;
+ self.targetY = 0;
+ }
+ }
+ };
+ return self;
+});
+var Spell = Container.expand(function (type) {
+ var self = Container.call(this);
+ self.type = type || 'basic';
+ var assetId = 'basicSpell';
+ var spellColor = 0xffff00;
+ self.damage = 10;
+ self.speed = 12;
+ self.lifespan = 120; // in frames (2 seconds at 60fps)
+ self.age = 0;
+ if (self.type === 'fire') {
+ self.damage = 15;
+ spellColor = 0xff4400;
+ } else if (self.type === 'ice') {
+ self.damage = 8;
+ self.speed = 10;
+ spellColor = 0x44ddff;
+ } else if (self.type === 'lightning') {
+ self.damage = 20;
+ self.speed = 16;
+ spellColor = 0xddff00;
+ }
+ var spellGraphic = self.attachAsset(assetId, {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ tint: spellColor
+ });
+ self.velocityX = 0;
+ self.velocityY = 0;
+ self.update = function () {
+ self.x += self.velocityX;
+ self.y += self.velocityY;
+ self.age++;
+ if (self.age >= self.lifespan) {
+ return true; // Mark for removal
+ }
+ return false;
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x112233
+});
+
+/****
+* Game Code
+****/
+// Game dimensions
+var gameWidth = 2048;
+var gameHeight = 2732;
+// Game state variables
+var crystal;
+var guardian;
+var spells = [];
+var barriers = [];
+var enemies = [];
+var areaSpells = [];
+var wave = 1;
+var score = 0;
+var spawnTimer = 0;
+var spawnDelay = 120; // 2 seconds at 60fps
+var waveInProgress = false;
+var enemiesInWave = 0;
+var enemiesSpawned = 0;
+var dragBarrierStart = null;
+// UI elements
+var scoreTxt;
+var waveTxt;
+var healthTxt;
+var areaSpellReadyTxt;
+// Initialize game
+function initGame() {
+ // Play background music
+ LK.playMusic('battleMusic');
+ // Create and position crystal
+ crystal = new Crystal();
+ crystal.x = gameWidth / 2;
+ crystal.y = gameHeight / 2;
+ game.addChild(crystal);
+ // Create guardian
+ guardian = new Guardian();
+ guardian.x = gameWidth / 2;
+ guardian.y = gameHeight / 2 + 300;
+ game.addChild(guardian);
+ // Create UI elements
+ createUI();
+ // Reset game state
+ wave = 1;
+ score = 0;
+ LK.setScore(score);
+ spawnTimer = 60; // Start first wave after 1 second
+ waveInProgress = false;
+ updateUI();
+}
+function createUI() {
+ // Score text
+ scoreTxt = new Text2('Score: 0', {
+ size: 60,
+ fill: 0xFFFFFF
+ });
+ scoreTxt.anchor.set(1, 0); // Right aligned
+ LK.gui.topRight.addChild(scoreTxt);
+ // Wave text
+ waveTxt = new Text2('Wave: 1', {
+ size: 60,
+ fill: 0xFFFFFF
+ });
+ waveTxt.anchor.set(0, 0); // Left aligned
+ // Make sure this doesn't overlap with the top left corner menu icon
+ waveTxt.x = 120; // Add some padding from the left edge
+ LK.gui.top.addChild(waveTxt);
+ // Health text
+ healthTxt = new Text2('Crystal: 100/100', {
+ size: 60,
+ fill: 0xFFFFFF
+ });
+ healthTxt.anchor.set(0.5, 0);
+ LK.gui.top.addChild(healthTxt);
+ // Area spell ready indicator
+ areaSpellReadyTxt = new Text2('Area Spell: Ready!', {
+ size: 50,
+ fill: 0xFFFF00
+ });
+ areaSpellReadyTxt.anchor.set(0.5, 0);
+ areaSpellReadyTxt.y = 70;
+ areaSpellReadyTxt.visible = false;
+ LK.gui.top.addChild(areaSpellReadyTxt);
+}
+function updateUI() {
+ scoreTxt.setText('Score: ' + score);
+ waveTxt.setText('Wave: ' + wave);
+ healthTxt.setText('Crystal: ' + crystal.health + '/' + crystal.maxHealth);
+ // Show/hide area spell indicator
+ areaSpellReadyTxt.visible = crystal.canUseAreaSpell;
+}
+function startWave() {
+ waveInProgress = true;
+ enemiesInWave = 5 + wave * 2;
+ enemiesSpawned = 0;
+ spawnTimer = 0;
+ // Determine if this wave has a mini-boss
+ hasMiniBase = wave % 3 === 0;
+ if (hasMiniBase) {
+ enemiesInWave += 1; // Add mini-boss to count
+ }
+}
+function spawnEnemy() {
+ // Determine enemy type based on wave and some randomness
+ var enemyType = 'basic';
+ var randomValue = Math.random();
+ if (wave >= 3 && enemiesSpawned === enemiesInWave - 1 && wave % 3 === 0) {
+ // Spawn mini-boss as last enemy in every third wave
+ enemyType = 'miniBoss';
+ } else if (wave >= 2 && randomValue < 0.3) {
+ enemyType = 'fast';
+ } else if (wave >= 4 && randomValue < 0.5) {
+ enemyType = 'tough';
+ }
+ var enemy = new Enemy(enemyType);
+ // Spawn at random position around the edges
+ var side = Math.floor(Math.random() * 4);
+ switch (side) {
+ case 0:
+ // Top
+ enemy.x = Math.random() * gameWidth;
+ enemy.y = -50;
+ break;
+ case 1:
+ // Right
+ enemy.x = gameWidth + 50;
+ enemy.y = Math.random() * gameHeight;
+ break;
+ case 2:
+ // Bottom
+ enemy.x = Math.random() * gameWidth;
+ enemy.y = gameHeight + 50;
+ break;
+ case 3:
+ // Left
+ enemy.x = -50;
+ enemy.y = Math.random() * gameHeight;
+ break;
+ }
+ enemies.push(enemy);
+ game.addChild(enemy);
+ enemiesSpawned++;
+ // Check if we've spawned all enemies for this wave
+ if (enemiesSpawned >= enemiesInWave) {
+ waveInProgress = false;
+ }
+}
+function checkWaveCompletion() {
+ if (!waveInProgress && enemies.length === 0 && enemiesSpawned >= enemiesInWave && enemiesInWave > 0) {
+ // Wave completed
+ wave++;
+ LK.getSound('waveComplete').play();
+ // Add bonus points for completing wave
+ score += wave * 50;
+ LK.setScore(score);
+ // Update high score if needed
+ if (score > storage.highScore) {
+ storage.highScore = score;
+ }
+ updateUI();
+ // Short delay before next wave
+ spawnTimer = 180; // 3 seconds at 60fps
+ enemiesInWave = 0;
+ }
+}
+function castAreaSpell() {
+ if (!crystal.canUseAreaSpell) {
+ return;
+ }
+ var areaSpell = new AreaSpell();
+ areaSpell.x = crystal.x;
+ areaSpell.y = crystal.y;
+ areaSpells.push(areaSpell);
+ game.addChild(areaSpell);
+ LK.getSound('areaSpell').play();
+ // Use the crystal's area spell ability
+ crystal.useAreaSpell();
+ updateUI();
+}
+// Event handlers
+game.down = function (x, y, obj) {
+ // Check if clicking on crystal and area spell is ready
+ if (crystal.canUseAreaSpell) {
+ var crystalGlobalPos = game.toLocal(crystal.position);
+ var dx = x - crystalGlobalPos.x;
+ var dy = y - crystalGlobalPos.y;
+ var distanceSquared = dx * dx + dy * dy;
+ if (distanceSquared <= 120 * 120) {
+ castAreaSpell();
+ return;
+ }
+ }
+ // Start barrier creation
+ dragBarrierStart = {
+ x: x,
+ y: y
+ };
+ // Set guardian target
+ guardian.targetX = x;
+ guardian.targetY = y;
+};
+game.move = function (x, y, obj) {
+ if (dragBarrierStart) {
+ // Visual feedback for barrier drawing
+ // In a real implementation, we'd show a preview of the barrier
+ }
+};
+game.up = function (x, y, obj) {
+ if (dragBarrierStart) {
+ // Calculate barrier properties
+ var dx = x - dragBarrierStart.x;
+ var dy = y - dragBarrierStart.y;
+ var length = Math.sqrt(dx * dx + dy * dy);
+ // Only create barrier if drag distance is sufficient
+ if (length > 100) {
+ // Calculate barrier position (middle of the line)
+ var barrierX = dragBarrierStart.x + dx / 2;
+ var barrierY = dragBarrierStart.y + dy / 2;
+ // Calculate rotation angle
+ var rotation = Math.atan2(dy, dx);
+ var barrier = guardian.createBarrier(barrierX, barrierY, rotation);
+ barriers.push(barrier);
+ game.addChild(barrier);
+ LK.getSound('barrierCreate').play();
+ } else {
+ // If not creating a barrier, cast a spell instead
+ var spell = guardian.castSpell(x, y, 'basic');
+ spells.push(spell);
+ game.addChild(spell);
+ LK.getSound('spellCast').play();
+ }
+ dragBarrierStart = null;
+ }
+};
+// Game update loop
+game.update = function () {
+ // Initialize game on first update
+ if (!crystal) {
+ initGame();
+ return;
+ }
+ // Update guardian
+ guardian.update();
+ // Spawn enemies
+ if (spawnTimer <= 0) {
+ if (!waveInProgress && enemiesInWave === 0) {
+ startWave();
+ } else if (waveInProgress && enemiesSpawned < enemiesInWave) {
+ spawnEnemy();
+ spawnTimer = spawnDelay - wave * 5; // Decrease spawn delay as waves progress
+ if (spawnTimer < 30) {
+ spawnTimer = 30;
+ } // Minimum spawn delay
+ }
+ } else {
+ spawnTimer--;
+ }
+ // Update spells and check for collisions
+ for (var i = spells.length - 1; i >= 0; i--) {
+ var removeSpell = spells[i].update();
+ // Check for collisions with enemies
+ for (var j = enemies.length - 1; j >= 0; j--) {
+ if (spells[i].intersects(enemies[j])) {
+ removeSpell = true;
+ // Apply damage to enemy
+ if (enemies[j].takeDamage(spells[i].damage)) {
+ // Enemy defeated
+ score += enemies[j].scoreValue;
+ LK.setScore(score);
+ enemies[j].destroy();
+ enemies.splice(j, 1);
+ }
+ break;
+ }
+ }
+ // Remove spell if needed
+ if (removeSpell) {
+ spells[i].destroy();
+ spells.splice(i, 1);
+ }
+ }
+ // Update barriers
+ for (var i = barriers.length - 1; i >= 0; i--) {
+ if (barriers[i].update()) {
+ barriers[i].destroy();
+ barriers.splice(i, 1);
+ continue;
+ }
+ }
+ // Update enemies
+ for (var i = enemies.length - 1; i >= 0; i--) {
+ var removeEnemy = enemies[i].update();
+ // Check for collisions with barriers
+ for (var j = barriers.length - 1; j >= 0; j--) {
+ if (enemies[i].intersects(barriers[j])) {
+ // Enemy hits barrier
+ if (barriers[j].takeDamage(1)) {
+ barriers[j].destroy();
+ barriers.splice(j, 1);
+ }
+ // Slow down or push back enemy slightly (simplified physics)
+ var dx = enemies[i].x - barriers[j].x;
+ var dy = enemies[i].y - barriers[j].y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist > 0) {
+ enemies[i].x += dx / dist * 2;
+ enemies[i].y += dy / dist * 2;
+ }
+ break;
+ }
+ }
+ // Remove enemy if needed
+ if (removeEnemy) {
+ enemies[i].destroy();
+ enemies.splice(i, 1);
+ }
+ }
+ // Update area spells
+ for (var i = areaSpells.length - 1; i >= 0; i--) {
+ if (areaSpells[i].update()) {
+ areaSpells[i].destroy();
+ areaSpells.splice(i, 1);
+ continue;
+ }
+ // Check for enemy collisions
+ for (var j = enemies.length - 1; j >= 0; j--) {
+ if (areaSpells[i].intersects(enemies[j])) {
+ // Apply damage to enemy
+ if (enemies[j].takeDamage(areaSpells[i].damage)) {
+ // Enemy defeated
+ score += enemies[j].scoreValue;
+ LK.setScore(score);
+ enemies[j].destroy();
+ enemies.splice(j, 1);
+ }
+ }
+ }
+ }
+ // Check if wave is complete
+ checkWaveCompletion();
+ // Update UI
+ updateUI();
+};
\ No newline at end of file
blue light crystal. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
top down anime mage character. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
red orc scarry taunt put hands up Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
top down temple floor. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
angry running green orc reptile taunting scarry. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
blue light of snow crystall. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
orang fire ball light. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
2d png black fanta bull orc anger taunt. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
full body angry taunt orc grim reaper style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows