/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Arrow class (player projectile) var Arrow = Container.expand(function () { var self = Container.call(this); var arrowSprite = self.attachAsset('arrow', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -32; // Upwards self.update = function () { self.y += self.speed; }; return self; }); // Dragon class (enemy) var Dragon = Container.expand(function () { var self = Container.call(this); var dragonSprite = self.attachAsset('dragon', { anchorX: 0.5, anchorY: 0.5 }); self.direction = 1; // 1: right, -1: left self.speed = 10; self.attackCooldown = 0; self.defeated = false; self.fallVy = 0; self.spinSpeed = 0; self.update = function () { if (self.defeated) { // Spinning fall animation self.y += self.fallVy; self.fallVy += 2.2; // gravity dragonSprite.rotation += self.spinSpeed; // Remove dragon if off screen if (self.y > 2732 + 300) { self.destroy(); } return; } // Move left/right self.x += self.speed * self.direction; // Bounce at screen edges if (self.x < 160) { self.x = 160; self.direction = 1; } if (self.x > 2048 - 160) { self.x = 2048 - 160; self.direction = -1; } // Attack cooldown if (self.attackCooldown > 0) { self.attackCooldown--; } }; // Dragon fires a fireball self.shoot = function () { if (self.defeated) return; if (self.attackCooldown === 0) { // Fire multiple fireballs in a spread, each targeting the fortress position var numFireballs = 3 + Math.floor(LK.getScore() / 10); // Increase number as score increases if (numFireballs > 5) numFireballs = 5; var spread = 220; // wider spread for more fireballs var startX = self.x - (numFireballs - 1) * spread / 2; for (var i = 0; i < numFireballs; i++) { var fireball = new Fireball(); fireball.x = startX + i * spread; fireball.y = self.y + 90; fireball.lastY = fireball.y; fireball.lastIntersecting = false; // Calculate direction vector from fireball to fortress at fire time var dx = fortress.x - fireball.x; var dy = fortress.y - 60 - fireball.y; // aim slightly above fortress center var dist = Math.sqrt(dx * dx + dy * dy); if (dist === 0) { fireball.vx = 0; fireball.vy = fireball.speed; } else { // Add a slight angle offset for spread effect var angleOffset = (i - (numFireballs - 1) / 2) * (Math.PI / 18); // up to +/-10deg var baseAngle = Math.atan2(dy, dx); var angle = baseAngle + angleOffset; fireball.vx = Math.cos(angle) * fireball.speed; fireball.vy = Math.sin(angle) * fireball.speed; } fireballs.push(fireball); game.addChild(fireball); } LK.getSound('fireball_shoot').play(); self.attackCooldown = Math.max(30, 120 - Math.floor(LK.getScore() / 5) * 10); // Faster as score increases } }; // Defeat animation trigger self.defeat = function () { if (self.defeated) return; self.defeated = true; self.fallVy = 18; self.spinSpeed = 0.28 + Math.random() * 0.15; // Optionally flash or play effect here LK.effects.flashObject(self, 0xFF6600, 600); }; return self; }); // Fireball class (enemy projectile) var Fireball = Container.expand(function () { var self = Container.call(this); var fireballSprite = self.attachAsset('fireball', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 18; self.update = function () { // If vx/vy are set, use them (targeted), else default straight down if (typeof self.vx === "number" && typeof self.vy === "number") { self.x += self.vx; self.y += self.vy; } else { self.y += self.speed; } }; return self; }); // Fortress class (player) var Fortress = Container.expand(function () { var self = Container.call(this); var fortressSprite = self.attachAsset('fortress', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () {}; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x0080ff }); /**** * Game Code ****/ // Music // Sound effects // Fortress health bar // Fireball (enemy projectile) // Dragon (enemy) // Shield (player deployable) // Arrow (player projectile) // Fortress (player) at the bottom // Game variables var arrows = []; var fireballs = []; var shields = []; var fortressHealth = 5; var fortressMaxHealth = 5; var canShoot = true; var shootCooldown = 0; var canShield = true; var shieldCooldown = 0; var dragNode = null; var lastFortressX = 0; // Add music LK.playMusic('castle_theme'); // Add background image behind all gameplay elements var background = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732 }); game.addChild(background); // Add fortress (player) at bottom center var fortress = new Fortress(); fortress.x = 2048 / 2; fortress.y = 2732 - 120; game.addChild(fortress); // Add dragon at top center var dragon = new Dragon(); dragon.x = 2048 / 2; dragon.y = 220; game.addChild(dragon); // (Health bar removed) // Score text var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Fortress health text var healthTxt = new Text2('♥♥♥♥♥', { size: 90, fill: 0x44DD44 }); healthTxt.anchor.set(0.5, 0); LK.gui.top.addChild(healthTxt); healthTxt.y = 120; healthTxt.x = 2048 / 2 / (2048 / LK.gui.top.width); // Helper: update health bar and text function updateHealthDisplay() { // (Health bar width update removed) // Health text var hearts = ''; for (var i = 0; i < fortressHealth; i++) hearts += '♥'; for (var i = fortressHealth; i < fortressMaxHealth; i++) hearts += '♡'; healthTxt.setText(hearts); if (fortressHealth <= 2) { healthTxt.setStyle({ fill: 0xFF4444 }); } else { healthTxt.setStyle({ fill: 0x44DD44 }); } } // Initial health display updateHealthDisplay(); // Touch/mouse controls game.down = function (x, y, obj) { // If touch is in lower 1/3, drag fortress if (y > 2732 * 2 / 3) { dragNode = fortress; lastFortressX = fortress.x; } }; game.move = function (x, y, obj) { if (dragNode === fortress) { // Move fortress horizontally, clamp to screen fortress.x = Math.max(160, Math.min(2048 - 160, x)); // Move shield with fortress for (var i = 0; i < shields.length; i++) { shields[i].x = fortress.x; } } }; game.up = function (x, y, obj) { dragNode = null; }; // Main game update loop game.update = function () { // Auto-shooting logic if (shootCooldown > 0) { shootCooldown--; } if (shootCooldown === 0) { // Auto-fire arrow var arrow = new Arrow(); arrow.x = fortress.x; arrow.y = fortress.y - 80; arrow.lastY = arrow.y; arrow.lastIntersecting = false; arrows.push(arrow); game.addChild(arrow); LK.getSound('arrow_shoot').play(); shootCooldown = 30; // 0.5s between shots } if (!canShield) { shieldCooldown--; if (shieldCooldown <= 0) { canShield = true; shieldCooldown = 0; } } // Update dragon dragon.update(); // Dragon attacks if (LK.ticks % Math.max(60, 180 - Math.floor(LK.getScore() / 5) * 10) === 0) { dragon.shoot(); } // Update arrows for (var i = arrows.length - 1; i >= 0; i--) { var arrow = arrows[i]; arrow.update(); // Remove if off screen if (arrow.y < -100) { arrow.destroy(); arrows.splice(i, 1); continue; } // Hit dragon? var hitDragon = arrow.intersects(dragon); if (!arrow.lastIntersecting && hitDragon) { // Score up LK.setScore(LK.getScore() + 1); scoreTxt.setText(LK.getScore()); // If this is the defeat shot (win condition), trigger dragon defeat if (LK.getScore() >= 30) { if (typeof dragon.defeat === "function") { dragon.defeat(); } } else { // Flash dragon LK.effects.flashObject(dragon, 0xFFFF00, 300); } arrow.destroy(); arrows.splice(i, 1); continue; } arrow.lastIntersecting = hitDragon; } // Update fireballs for (var j = fireballs.length - 1; j >= 0; j--) { var fireball = fireballs[j]; fireball.update(); // Remove if off screen if (fireball.y > 2732 + 100) { fireball.destroy(); fireballs.splice(j, 1); continue; } // Hit fortress? var hitFortress = fireball.intersects(fortress); if (!fireball.lastIntersecting && hitFortress) { fortressHealth--; updateHealthDisplay(); LK.effects.flashObject(fortress, 0xFF0000, 400); LK.getSound('fortress_hit').play(); fireball.destroy(); fireballs.splice(j, 1); if (fortressHealth <= 0) { LK.effects.flashScreen(0xFF0000, 1000); LK.showGameOver(); return; } continue; } fireball.lastIntersecting = hitFortress; } // Win condition: survive to score 30 if (LK.getScore() >= 30) { LK.showYouWin(); return; } }; // Initial score scoreTxt.setText(LK.getScore());
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Arrow class (player projectile)
var Arrow = Container.expand(function () {
var self = Container.call(this);
var arrowSprite = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -32; // Upwards
self.update = function () {
self.y += self.speed;
};
return self;
});
// Dragon class (enemy)
var Dragon = Container.expand(function () {
var self = Container.call(this);
var dragonSprite = self.attachAsset('dragon', {
anchorX: 0.5,
anchorY: 0.5
});
self.direction = 1; // 1: right, -1: left
self.speed = 10;
self.attackCooldown = 0;
self.defeated = false;
self.fallVy = 0;
self.spinSpeed = 0;
self.update = function () {
if (self.defeated) {
// Spinning fall animation
self.y += self.fallVy;
self.fallVy += 2.2; // gravity
dragonSprite.rotation += self.spinSpeed;
// Remove dragon if off screen
if (self.y > 2732 + 300) {
self.destroy();
}
return;
}
// Move left/right
self.x += self.speed * self.direction;
// Bounce at screen edges
if (self.x < 160) {
self.x = 160;
self.direction = 1;
}
if (self.x > 2048 - 160) {
self.x = 2048 - 160;
self.direction = -1;
}
// Attack cooldown
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
};
// Dragon fires a fireball
self.shoot = function () {
if (self.defeated) return;
if (self.attackCooldown === 0) {
// Fire multiple fireballs in a spread, each targeting the fortress position
var numFireballs = 3 + Math.floor(LK.getScore() / 10); // Increase number as score increases
if (numFireballs > 5) numFireballs = 5;
var spread = 220; // wider spread for more fireballs
var startX = self.x - (numFireballs - 1) * spread / 2;
for (var i = 0; i < numFireballs; i++) {
var fireball = new Fireball();
fireball.x = startX + i * spread;
fireball.y = self.y + 90;
fireball.lastY = fireball.y;
fireball.lastIntersecting = false;
// Calculate direction vector from fireball to fortress at fire time
var dx = fortress.x - fireball.x;
var dy = fortress.y - 60 - fireball.y; // aim slightly above fortress center
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist === 0) {
fireball.vx = 0;
fireball.vy = fireball.speed;
} else {
// Add a slight angle offset for spread effect
var angleOffset = (i - (numFireballs - 1) / 2) * (Math.PI / 18); // up to +/-10deg
var baseAngle = Math.atan2(dy, dx);
var angle = baseAngle + angleOffset;
fireball.vx = Math.cos(angle) * fireball.speed;
fireball.vy = Math.sin(angle) * fireball.speed;
}
fireballs.push(fireball);
game.addChild(fireball);
}
LK.getSound('fireball_shoot').play();
self.attackCooldown = Math.max(30, 120 - Math.floor(LK.getScore() / 5) * 10); // Faster as score increases
}
};
// Defeat animation trigger
self.defeat = function () {
if (self.defeated) return;
self.defeated = true;
self.fallVy = 18;
self.spinSpeed = 0.28 + Math.random() * 0.15;
// Optionally flash or play effect here
LK.effects.flashObject(self, 0xFF6600, 600);
};
return self;
});
// Fireball class (enemy projectile)
var Fireball = Container.expand(function () {
var self = Container.call(this);
var fireballSprite = self.attachAsset('fireball', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 18;
self.update = function () {
// If vx/vy are set, use them (targeted), else default straight down
if (typeof self.vx === "number" && typeof self.vy === "number") {
self.x += self.vx;
self.y += self.vy;
} else {
self.y += self.speed;
}
};
return self;
});
// Fortress class (player)
var Fortress = Container.expand(function () {
var self = Container.call(this);
var fortressSprite = self.attachAsset('fortress', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0080ff
});
/****
* Game Code
****/
// Music
// Sound effects
// Fortress health bar
// Fireball (enemy projectile)
// Dragon (enemy)
// Shield (player deployable)
// Arrow (player projectile)
// Fortress (player) at the bottom
// Game variables
var arrows = [];
var fireballs = [];
var shields = [];
var fortressHealth = 5;
var fortressMaxHealth = 5;
var canShoot = true;
var shootCooldown = 0;
var canShield = true;
var shieldCooldown = 0;
var dragNode = null;
var lastFortressX = 0;
// Add music
LK.playMusic('castle_theme');
// Add background image behind all gameplay elements
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
game.addChild(background);
// Add fortress (player) at bottom center
var fortress = new Fortress();
fortress.x = 2048 / 2;
fortress.y = 2732 - 120;
game.addChild(fortress);
// Add dragon at top center
var dragon = new Dragon();
dragon.x = 2048 / 2;
dragon.y = 220;
game.addChild(dragon);
// (Health bar removed)
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Fortress health text
var healthTxt = new Text2('♥♥♥♥♥', {
size: 90,
fill: 0x44DD44
});
healthTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(healthTxt);
healthTxt.y = 120;
healthTxt.x = 2048 / 2 / (2048 / LK.gui.top.width);
// Helper: update health bar and text
function updateHealthDisplay() {
// (Health bar width update removed)
// Health text
var hearts = '';
for (var i = 0; i < fortressHealth; i++) hearts += '♥';
for (var i = fortressHealth; i < fortressMaxHealth; i++) hearts += '♡';
healthTxt.setText(hearts);
if (fortressHealth <= 2) {
healthTxt.setStyle({
fill: 0xFF4444
});
} else {
healthTxt.setStyle({
fill: 0x44DD44
});
}
}
// Initial health display
updateHealthDisplay();
// Touch/mouse controls
game.down = function (x, y, obj) {
// If touch is in lower 1/3, drag fortress
if (y > 2732 * 2 / 3) {
dragNode = fortress;
lastFortressX = fortress.x;
}
};
game.move = function (x, y, obj) {
if (dragNode === fortress) {
// Move fortress horizontally, clamp to screen
fortress.x = Math.max(160, Math.min(2048 - 160, x));
// Move shield with fortress
for (var i = 0; i < shields.length; i++) {
shields[i].x = fortress.x;
}
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Main game update loop
game.update = function () {
// Auto-shooting logic
if (shootCooldown > 0) {
shootCooldown--;
}
if (shootCooldown === 0) {
// Auto-fire arrow
var arrow = new Arrow();
arrow.x = fortress.x;
arrow.y = fortress.y - 80;
arrow.lastY = arrow.y;
arrow.lastIntersecting = false;
arrows.push(arrow);
game.addChild(arrow);
LK.getSound('arrow_shoot').play();
shootCooldown = 30; // 0.5s between shots
}
if (!canShield) {
shieldCooldown--;
if (shieldCooldown <= 0) {
canShield = true;
shieldCooldown = 0;
}
}
// Update dragon
dragon.update();
// Dragon attacks
if (LK.ticks % Math.max(60, 180 - Math.floor(LK.getScore() / 5) * 10) === 0) {
dragon.shoot();
}
// Update arrows
for (var i = arrows.length - 1; i >= 0; i--) {
var arrow = arrows[i];
arrow.update();
// Remove if off screen
if (arrow.y < -100) {
arrow.destroy();
arrows.splice(i, 1);
continue;
}
// Hit dragon?
var hitDragon = arrow.intersects(dragon);
if (!arrow.lastIntersecting && hitDragon) {
// Score up
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
// If this is the defeat shot (win condition), trigger dragon defeat
if (LK.getScore() >= 30) {
if (typeof dragon.defeat === "function") {
dragon.defeat();
}
} else {
// Flash dragon
LK.effects.flashObject(dragon, 0xFFFF00, 300);
}
arrow.destroy();
arrows.splice(i, 1);
continue;
}
arrow.lastIntersecting = hitDragon;
}
// Update fireballs
for (var j = fireballs.length - 1; j >= 0; j--) {
var fireball = fireballs[j];
fireball.update();
// Remove if off screen
if (fireball.y > 2732 + 100) {
fireball.destroy();
fireballs.splice(j, 1);
continue;
}
// Hit fortress?
var hitFortress = fireball.intersects(fortress);
if (!fireball.lastIntersecting && hitFortress) {
fortressHealth--;
updateHealthDisplay();
LK.effects.flashObject(fortress, 0xFF0000, 400);
LK.getSound('fortress_hit').play();
fireball.destroy();
fireballs.splice(j, 1);
if (fortressHealth <= 0) {
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
return;
}
continue;
}
fireball.lastIntersecting = hitFortress;
}
// Win condition: survive to score 30
if (LK.getScore() >= 30) {
LK.showYouWin();
return;
}
};
// Initial score
scoreTxt.setText(LK.getScore());
vertical top down balista medieval. In-Game asset. 2d. High contrast. No shadows
big balista arrow vertical imagw. In-Game asset. 2d. High contrast. No shadows
fronf view flying image dragon with 3 In-Game asset. 2d. High contrast. No shadows
plain battlefield of defense burning medieval fortres at bottom anime image style. blue sky at upper. far distance a mountain In-Game asset. 2d. High contrast. No shadows