/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Artifact = Container.expand(function (artifactType) { var self = Container.call(this); self.artifactType = artifactType || 'projectileSpeed'; var artifactGraphics = self.attachAsset('artifact', { anchorX: 0.5, anchorY: 0.5 }); // Set color based on artifact type if (self.artifactType === 'projectileSpeed') { artifactGraphics.tint = 0x00ffff; // Cyan for projectile speed } else if (self.artifactType === 'shootingSpeed') { artifactGraphics.tint = 0xffff00; // Yellow for shooting speed } else if (self.artifactType === 'damage') { artifactGraphics.tint = 0x800080; // Purple for damage } self.collected = false; self.lifeTime = 0; self.collect = function () { if (!self.collected) { self.collected = true; LK.getSound('powerUp').play(); LK.setScore(LK.getScore() + 100); // Apply different effects based on artifact type if (!hero.collectedArtifacts) hero.collectedArtifacts = []; if (!hero.activeBuffs) hero.activeBuffs = []; var artifactLabel = ''; var buffDuration = 900; // default 15 seconds var buffMultiplier = 1; if (self.artifactType === 'projectileSpeed') { artifactLabel = 'Projectile Speed Artifact'; buffDuration = 900; buffMultiplier = 2; // Add a new buff instance hero.activeBuffs.push({ type: 'projectileSpeed', multiplier: 2, duration: buffDuration }); } else if (self.artifactType === 'shootingSpeed') { artifactLabel = 'Shooting Speed Artifact'; buffDuration = 900; buffMultiplier = 2; hero.activeBuffs.push({ type: 'shootingSpeed', multiplier: 2, duration: buffDuration }); } else if (self.artifactType === 'damage') { artifactLabel = 'Damage Artifact'; buffDuration = 900; buffMultiplier = 3; hero.activeBuffs.push({ type: 'damage', multiplier: 3, duration: buffDuration }); } if (artifactLabel) { hero.collectedArtifacts.push(artifactLabel); if (!hero.artifactTimers) hero.artifactTimers = []; hero.artifactTimers.push({ label: artifactLabel, duration: buffDuration }); } // Remove from artifacts array for (var i = artifacts.length - 1; i >= 0; i--) { if (artifacts[i] === self) { artifacts.splice(i, 1); break; } } tween(self, { scaleX: 0, scaleY: 0 }, { duration: 200, onFinish: function onFinish() { self.destroy(); } }); } }; self.update = function () { self.lifeTime++; self.rotation += 0.1; // Pulse effect var pulse = Math.sin(self.lifeTime * 0.2) * 0.2 + 1; self.scaleX = pulse; self.scaleY = pulse; // Despawn after 10 seconds if (self.lifeTime > 600) { for (var i = artifacts.length - 1; i >= 0; i--) { if (artifacts[i] === self) { artifacts.splice(i, 1); break; } } self.destroy(); } }; return self; }); var BuffChoice = Container.expand(function (buffType, onSelect) { var self = Container.call(this); var color = 0xFFFFFF; var label = ''; if (buffType === 'health') { color = 0xFF4444; label = '+20 Max Health'; } else if (buffType === 'damage') { color = 0x800080; label = '+1 Damage'; } else if (buffType === 'shootingSpeed') { color = 0xFFFF00; label = 'Faster Shooting'; } var icon = self.attachAsset('artifact', { anchorX: 0.5, anchorY: 0.5 }); icon.tint = color; icon.scaleX = 1.2; icon.scaleY = 1.2; var txt = new Text2(label, { size: 60, fill: color }); txt.anchor.set(0.5, 0); txt.y = 90; self.addChild(txt); self.buffType = buffType; self.onSelect = onSelect; self.interactive = true; self.down = function (x, y, obj) { if (typeof self.onSelect === 'function') { self.onSelect(self.buffType); } }; return self; }); var Chest = Container.expand(function () { var self = Container.call(this); var chestGraphics = self.attachAsset('Chest', { anchorX: 0.5, anchorY: 0.5 }); self.scaleX = 1.5; self.scaleY = 1.5; self.glow = true; self.opened = false; self.update = function () { // Glow effect var pulse = Math.sin(LK.ticks * 0.15) * 0.2 + 1.2; self.scaleX = pulse; self.scaleY = pulse; }; self.open = function () { if (self.opened) return; self.opened = true; // Animate chest opening tween(self, { scaleX: 2, scaleY: 2 }, { duration: 200, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); var Enemy = Container.expand(function (enemyType) { var self = Container.call(this); self.enemyType = enemyType || 'minotaur'; self.health = 2; self.damage = 10; self.speed = 2; self.points = 10; var enemyGraphics; if (self.enemyType === 'minotaur') { enemyGraphics = self.attachAsset('minotaur', { anchorX: 0.5, anchorY: 0.5 }); self.health = 4; self.speed = 3; self.points = 15; } else if (self.enemyType === 'harpy') { enemyGraphics = self.attachAsset('harpy', { anchorX: 0.5, anchorY: 0.5 }); self.health = 2; self.speed = 6; self.points = 20; } else if (self.enemyType === 'cyclops') { enemyGraphics = self.attachAsset('cyclops', { anchorX: 0.5, anchorY: 0.5 }); self.health = 8; self.speed = 2; self.damage = 20; self.points = 50; } self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xFFFFFF, 200); if (self.health <= 0) { self.die(); } }; self.die = function () { LK.getSound('enemyHit').play(); LK.setScore(LK.getScore() + self.points); // Chance to drop artifact if (Math.random() < 0.15) { var artifactTypes = ['health', 'projectileSpeed', 'shootingSpeed', 'damage']; var randomType = artifactTypes[Math.floor(Math.random() * artifactTypes.length)]; var artifact; if (randomType === 'health') { artifact = new Potion(); } else { artifact = new Artifact(randomType); } artifact.x = self.x; artifact.y = self.y; artifacts.push(artifact); game.addChild(artifact); } // Remove from enemies array for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } self.destroy(); }; self.update = function () { // Move towards hero var dx = hero.x - self.x; var dy = hero.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } }; return self; }); var Hero = Container.expand(function () { var self = Container.call(this); var heroGraphics = self.attachAsset('hero', { anchorX: 0.5, anchorY: 0.5 }); self.health = 100; self.maxHealth = 100; self.shootCooldown = 0; self.speed = 8; // Boost properties self.projectileSpeedBoost = 0; self.projectileSpeedMultiplier = 1; self.shootingSpeedBoost = 0; self.shootingSpeedMultiplier = 1; self.damageBoost = 0; self.damageMultiplier = 1; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xFF0000, 300); if (self.health <= 0) { gameOver = true; } }; self.shoot = function () { if (self.shootCooldown <= 0) { var bullet = new HeroBullet(); bullet.x = self.x; bullet.y = self.y - 40; // Apply projectile speed buffs (always use multiplier) bullet.speed = 4 * (self.projectileSpeedMultiplier || 1); // Apply damage buffs (always use multiplier) bullet.damage = 1 * (self.damageMultiplier || 1); // Find nearest enemy for auto-aim var nearestEnemy = null; var nearestDistance = Infinity; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < nearestDistance) { nearestDistance = distance; nearestEnemy = enemy; } } // Set bullet direction towards nearest enemy if (nearestEnemy) { var dx = nearestEnemy.x - self.x; var dy = nearestEnemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { bullet.directionX = dx / distance; bullet.directionY = dy / distance; } } else { // Default upward direction if no enemies bullet.directionX = 0; bullet.directionY = -1; } heroBullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); // Apply shooting speed buffs (always use multiplier) var cooldownTime = 10; if (self.shootingSpeedMultiplier && self.shootingSpeedMultiplier > 1) { cooldownTime = Math.floor(cooldownTime / self.shootingSpeedMultiplier); } self.shootCooldown = cooldownTime; } }; self.update = function () { if (self.shootCooldown > 0) { self.shootCooldown--; } // Stackable buffs logic if (!self.activeBuffs) self.activeBuffs = []; // Reset multipliers self.projectileSpeedMultiplier = 1; self.shootingSpeedMultiplier = 1; self.damageMultiplier = 1; // Remove expired buffs and apply active ones for (var i = self.activeBuffs.length - 1; i >= 0; i--) { var buff = self.activeBuffs[i]; if (buff.duration > 0) { if (buff.type === 'projectileSpeed') { self.projectileSpeedMultiplier *= buff.multiplier; } else if (buff.type === 'shootingSpeed') { self.shootingSpeedMultiplier *= buff.multiplier; } else if (buff.type === 'damage') { self.damageMultiplier *= buff.multiplier; } buff.duration--; } if (buff.duration <= 0) { self.activeBuffs.splice(i, 1); } } }; return self; }); var HeroBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('heroBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 4; self.damage = 1; self.directionX = 0; self.directionY = -1; self.update = function () { self.x += self.directionX * self.speed; self.y += self.directionY * self.speed; // Rotate bullet to match shooting direction self.rotation = Math.atan2(self.directionY, self.directionX) + Math.PI / 2; }; return self; }); // Potion class for health artifact var Potion = Container.expand(function () { var self = Container.call(this); self.artifactType = 'health'; // Use Hpot asset for potion var potionGraphics = self.attachAsset('Hpot', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; self.lifeTime = 0; self.collect = function () { if (!self.collected) { self.collected = true; LK.getSound('powerUp').play(); LK.setScore(LK.getScore() + 100); // Heal hero hero.health = Math.min(hero.maxHealth, hero.health + 20); var artifactLabel = 'Health Potion'; var buffDuration = 900; // 15 seconds if (!hero.collectedArtifacts) hero.collectedArtifacts = []; if (!hero.artifactTimers) hero.artifactTimers = []; hero.collectedArtifacts.push(artifactLabel); hero.artifactTimers.push({ label: artifactLabel, duration: buffDuration }); // Remove from artifacts array for (var i = artifacts.length - 1; i >= 0; i--) { if (artifacts[i] === self) { artifacts.splice(i, 1); break; } } tween(self, { scaleX: 0, scaleY: 0 }, { duration: 200, onFinish: function onFinish() { self.destroy(); } }); } }; self.update = function () { self.lifeTime++; self.rotation += 0.1; // Pulse effect var pulse = Math.sin(self.lifeTime * 0.2) * 0.2 + 1; self.scaleX = pulse; self.scaleY = pulse; // Despawn after 10 seconds if (self.lifeTime > 600) { for (var i = artifacts.length - 1; i >= 0; i--) { if (artifacts[i] === self) { artifacts.splice(i, 1); break; } } self.destroy(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x8B4513 }); /**** * Game Code ****/ // Game variables var hero; var heroBullets = []; var enemies = []; var artifacts = []; var enemySpawnTimer = 0; var gameOver = false; var dragActive = false; var waveLevel = 1; // UI var scoreTxt = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(0, 0); scoreTxt.x = 120; scoreTxt.y = 50; LK.gui.topLeft.addChild(scoreTxt); var healthTxt = new Text2('Health: 100', { size: 60, fill: 0xFF4444 }); healthTxt.anchor.set(1, 0); LK.gui.topRight.addChild(healthTxt); // Artifacts Tab (top-right, under health) var artifactsTabBg = LK.getAsset('artifact', { anchorX: 1, anchorY: 0, scaleX: 2.2, scaleY: 2.2, x: 0, y: 0 }); artifactsTabBg.tint = 0x442222; artifactsTabBg.alpha = 0.7; artifactsTabBg.x = 0; artifactsTabBg.y = 180; LK.gui.topRight.addChild(artifactsTabBg); var artifactsTabTitle = new Text2('Artifacts', { size: 40, fill: 0xFFAAAA }); artifactsTabTitle.anchor.set(1, 0); artifactsTabTitle.x = -30; artifactsTabTitle.y = 200; LK.gui.topRight.addChild(artifactsTabTitle); var artifactsTabTxt = new Text2('', { size: 36, fill: 0xFFFFFF }); artifactsTabTxt.anchor.set(1, 0); artifactsTabTxt.x = -30; artifactsTabTxt.y = 250; LK.gui.topRight.addChild(artifactsTabTxt); var waveTxt = new Text2('Wave: 1', { size: 50, fill: 0x44FF44 }); waveTxt.anchor.set(0.5, 0); LK.gui.top.addChild(waveTxt); // Weapons Tab var weaponsTabBg = LK.getAsset('weapon', { anchorX: 0, anchorY: 0, scaleX: 2.2, scaleY: 2.2, x: 0, y: 0 }); weaponsTabBg.tint = 0x222244; weaponsTabBg.alpha = 0.7; weaponsTabBg.x = 0; weaponsTabBg.y = 180; LK.gui.topLeft.addChild(weaponsTabBg); var weaponsTabTitle = new Text2('Weapons', { size: 40, fill: 0xFFFFAA }); weaponsTabTitle.anchor.set(0, 0); weaponsTabTitle.x = 30; weaponsTabTitle.y = 200; LK.gui.topLeft.addChild(weaponsTabTitle); var weaponsTabTxt = new Text2('', { size: 36, fill: 0xFFFFFF }); weaponsTabTxt.anchor.set(0, 0); weaponsTabTxt.x = 30; weaponsTabTxt.y = 250; LK.gui.topLeft.addChild(weaponsTabTxt); // Add background image var background = LK.getAsset('bakground', { anchorX: 0, anchorY: 0, scaleX: 20.48, // Scale to fit 2048 width (2048/100) scaleY: 27.32 // Scale to fit 2732 height (2732/100) }); background.x = 0; background.y = 0; game.addChild(background); // Initialize hero hero = new Hero(); hero.x = 1024; hero.y = 2000; game.addChild(hero); // Spawn enemy function function spawnEnemy() { var enemyTypes = ['minotaur', 'harpy', 'cyclops']; var type = enemyTypes[Math.floor(Math.random() * enemyTypes.length)]; // Cyclops only spawn in later waves if (type === 'cyclops' && waveLevel < 3) { type = 'minotaur'; } var enemy = new Enemy(type); // Spawn from random edge var side = Math.floor(Math.random() * 4); if (side === 0) { // Top enemy.x = Math.random() * 2048; enemy.y = -100; } else if (side === 1) { // Right enemy.x = 2148; enemy.y = Math.random() * 2732; } else if (side === 2) { // Bottom enemy.x = Math.random() * 2048; enemy.y = 2832; } else { // Left enemy.x = -100; enemy.y = Math.random() * 2732; } enemies.push(enemy); game.addChild(enemy); } // Touch controls game.down = function (x, y, obj) { // If chest or buff choices are active, do not move hero or shoot if (game.chest || game.buffChoices) return; dragActive = true; hero.x = x; hero.y = y; hero.shoot(); }; game.move = function (x, y, obj) { // If chest or buff choices are active, do not move hero if (game.chest || game.buffChoices) return; if (dragActive) { hero.x = x; hero.y = y; } }; game.up = function (x, y, obj) { dragActive = false; }; // Main game loop game.update = function () { if (gameOver) { LK.showGameOver(); return; } // If chest or buff choices are active, pause gameplay except for chest/buff UI if (game.chest || game.buffChoices) { // Only update chest and buff choices if (game.chest) game.chest.update(); if (game.buffChoices) { for (var i = 0; i < game.buffChoices.length; i++) { game.buffChoices[i].update && game.buffChoices[i].update(); } } // Update UI scoreTxt.setText('Score: ' + LK.getScore()); healthTxt.setText('Health: ' + hero.health); waveTxt.setText('Wave: ' + waveLevel); return; } // Update hero hero.update(); // Keep hero in bounds hero.x = Math.max(40, Math.min(2008, hero.x)); hero.y = Math.max(40, Math.min(2692, hero.y)); // Auto-shoot var autoShootRate = 30; if (hero.shootingSpeedMultiplier && hero.shootingSpeedMultiplier > 1) { autoShootRate = Math.floor(autoShootRate / hero.shootingSpeedMultiplier); } if (LK.ticks % autoShootRate === 0) { hero.shoot(); } // Spawn enemies enemySpawnTimer++; var spawnRate = Math.max(30, 120 - waveLevel * 10); if (enemySpawnTimer >= spawnRate) { spawnEnemy(); enemySpawnTimer = 0; } // Update bullets for (var i = heroBullets.length - 1; i >= 0; i--) { var bullet = heroBullets[i]; bullet.update(); // Remove bullets that are off-screen if (bullet.y < -50) { bullet.destroy(); heroBullets.splice(i, 1); continue; } // Check bullet vs enemy collisions for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (bullet.intersects(enemy)) { enemy.takeDamage(bullet.damage); bullet.destroy(); heroBullets.splice(i, 1); break; } } } // Update enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; enemy.update(); // Check hero vs enemy collision if (enemy.intersects(hero)) { hero.takeDamage(enemy.damage); enemy.die(); } // Remove enemies that are too far off-screen if (enemy.x < -200 || enemy.x > 2248 || enemy.y < -200 || enemy.y > 2932) { enemies.splice(i, 1); enemy.destroy(); } } // Update artifacts for (var i = artifacts.length - 1; i >= 0; i--) { var artifact = artifacts[i]; artifact.update(); // Check hero vs artifact collision if (artifact.intersects(hero)) { artifact.collect(); } } // Update wave level based on score var newWaveLevel = Math.floor(LK.getScore() / 500) + 1; if (newWaveLevel > waveLevel) { waveLevel = newWaveLevel; LK.effects.flashScreen(0x00FF00, 500); // Pause enemy spawns and show chest enemySpawnTimer = -9999; // Prevent spawns // Remove all remaining enemies for (var i = enemies.length - 1; i >= 0; i--) { enemies[i].destroy(); enemies.splice(i, 1); } // Spawn chest in center if (!game.chest) { var chest = new Chest(); chest.x = 1024; chest.y = 1200; game.addChild(chest); game.chest = chest; } // Show buff choices after chest is opened game.showBuffChoices = function () { if (game.buffChoices) return; var buffs = ['health', 'damage', 'shootingSpeed']; // Shuffle and pick 3 for (var i = buffs.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var t = buffs[i]; buffs[i] = buffs[j]; buffs[j] = t; } var selectedBuffs = buffs.slice(0, 3); game.buffChoices = []; if (!hero.weapons) hero.weapons = []; for (var i = 0; i < 3; i++) { (function (buffType, idx) { var choice = new BuffChoice(buffType, function (chosenBuff) { // Apply permanent weapon (buff) if (chosenBuff === 'health') { hero.maxHealth += 20; hero.health += 20; hero.weapons.push('+20 Max Health'); } else if (chosenBuff === 'damage') { hero.damageMultiplier = (hero.damageMultiplier || 1) + 1; hero.weapons.push('+1 Damage'); } else if (chosenBuff === 'shootingSpeed') { hero.shootingSpeedMultiplier = (hero.shootingSpeedMultiplier || 1) + 0.5; hero.weapons.push('Faster Shooting'); } // Remove buff choices for (var k = 0; k < game.buffChoices.length; k++) { game.buffChoices[k].destroy(); } game.buffChoices = null; // Resume enemy spawn enemySpawnTimer = 0; }); choice.x = 512 + idx * 512; choice.y = 1500; game.addChild(choice); game.buffChoices.push(choice); })(selectedBuffs[i], i); } }; // Listen for chest open game.chest.down = function (x, y, obj) { if (game.chest.opened) return; game.chest.open(); LK.setTimeout(function () { if (game.chest) { game.chest.destroy(); game.chest = null; } game.showBuffChoices(); }, 300); }; } // Update UI scoreTxt.setText('Score: ' + LK.getScore()); healthTxt.setText('Health: ' + hero.health); waveTxt.setText('Wave: ' + waveLevel); // Update weapons tab var weaponsList = []; if (hero.weapons && hero.weapons.length > 0) { // Count stackable buffs var buffCounts = {}; for (var i = 0; i < hero.weapons.length; i++) { var buff = hero.weapons[i]; if (!buffCounts[buff]) buffCounts[buff] = 0; buffCounts[buff]++; } // Show each buff with stack count if more than 1 for (var buff in buffCounts) { if (buffCounts[buff] > 1) { weaponsList.push(buff + " x" + buffCounts[buff]); } else { weaponsList.push(buff); } } } weaponsTabTxt.setText(weaponsList.length ? weaponsList.join('\n') : 'None'); // Update artifacts tab // Remove expired artifacts (duration > 15 seconds) if (!hero.artifactTimers) hero.artifactTimers = []; if (hero.collectedArtifacts && hero.collectedArtifacts.length > 0) { for (var i = hero.artifactTimers.length - 1; i >= 0; i--) { hero.artifactTimers[i].duration--; if (hero.artifactTimers[i].duration <= 0) { // Remove from collectedArtifacts and artifactTimers hero.collectedArtifacts.splice(i, 1); hero.artifactTimers.splice(i, 1); } } } var artifactsList = []; if (hero.collectedArtifacts && hero.collectedArtifacts.length > 0 && hero.artifactTimers && hero.artifactTimers.length > 0) { for (var i = 0; i < hero.collectedArtifacts.length; i++) { var label = hero.collectedArtifacts[i]; var timer = hero.artifactTimers[i]; // Show seconds left, rounded up var secondsLeft = Math.ceil((timer && timer.duration ? timer.duration : 0) / 60); artifactsList.push(label + (secondsLeft > 0 ? " (" + secondsLeft + "s)" : "")); } } artifactsTabTxt.setText(artifactsList.length ? artifactsList.join('\n') : 'None'); }; // Start background music LK.playMusic('dungeonMusic');
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Artifact = Container.expand(function (artifactType) {
var self = Container.call(this);
self.artifactType = artifactType || 'projectileSpeed';
var artifactGraphics = self.attachAsset('artifact', {
anchorX: 0.5,
anchorY: 0.5
});
// Set color based on artifact type
if (self.artifactType === 'projectileSpeed') {
artifactGraphics.tint = 0x00ffff; // Cyan for projectile speed
} else if (self.artifactType === 'shootingSpeed') {
artifactGraphics.tint = 0xffff00; // Yellow for shooting speed
} else if (self.artifactType === 'damage') {
artifactGraphics.tint = 0x800080; // Purple for damage
}
self.collected = false;
self.lifeTime = 0;
self.collect = function () {
if (!self.collected) {
self.collected = true;
LK.getSound('powerUp').play();
LK.setScore(LK.getScore() + 100);
// Apply different effects based on artifact type
if (!hero.collectedArtifacts) hero.collectedArtifacts = [];
if (!hero.activeBuffs) hero.activeBuffs = [];
var artifactLabel = '';
var buffDuration = 900; // default 15 seconds
var buffMultiplier = 1;
if (self.artifactType === 'projectileSpeed') {
artifactLabel = 'Projectile Speed Artifact';
buffDuration = 900;
buffMultiplier = 2;
// Add a new buff instance
hero.activeBuffs.push({
type: 'projectileSpeed',
multiplier: 2,
duration: buffDuration
});
} else if (self.artifactType === 'shootingSpeed') {
artifactLabel = 'Shooting Speed Artifact';
buffDuration = 900;
buffMultiplier = 2;
hero.activeBuffs.push({
type: 'shootingSpeed',
multiplier: 2,
duration: buffDuration
});
} else if (self.artifactType === 'damage') {
artifactLabel = 'Damage Artifact';
buffDuration = 900;
buffMultiplier = 3;
hero.activeBuffs.push({
type: 'damage',
multiplier: 3,
duration: buffDuration
});
}
if (artifactLabel) {
hero.collectedArtifacts.push(artifactLabel);
if (!hero.artifactTimers) hero.artifactTimers = [];
hero.artifactTimers.push({
label: artifactLabel,
duration: buffDuration
});
}
// Remove from artifacts array
for (var i = artifacts.length - 1; i >= 0; i--) {
if (artifacts[i] === self) {
artifacts.splice(i, 1);
break;
}
}
tween(self, {
scaleX: 0,
scaleY: 0
}, {
duration: 200,
onFinish: function onFinish() {
self.destroy();
}
});
}
};
self.update = function () {
self.lifeTime++;
self.rotation += 0.1;
// Pulse effect
var pulse = Math.sin(self.lifeTime * 0.2) * 0.2 + 1;
self.scaleX = pulse;
self.scaleY = pulse;
// Despawn after 10 seconds
if (self.lifeTime > 600) {
for (var i = artifacts.length - 1; i >= 0; i--) {
if (artifacts[i] === self) {
artifacts.splice(i, 1);
break;
}
}
self.destroy();
}
};
return self;
});
var BuffChoice = Container.expand(function (buffType, onSelect) {
var self = Container.call(this);
var color = 0xFFFFFF;
var label = '';
if (buffType === 'health') {
color = 0xFF4444;
label = '+20 Max Health';
} else if (buffType === 'damage') {
color = 0x800080;
label = '+1 Damage';
} else if (buffType === 'shootingSpeed') {
color = 0xFFFF00;
label = 'Faster Shooting';
}
var icon = self.attachAsset('artifact', {
anchorX: 0.5,
anchorY: 0.5
});
icon.tint = color;
icon.scaleX = 1.2;
icon.scaleY = 1.2;
var txt = new Text2(label, {
size: 60,
fill: color
});
txt.anchor.set(0.5, 0);
txt.y = 90;
self.addChild(txt);
self.buffType = buffType;
self.onSelect = onSelect;
self.interactive = true;
self.down = function (x, y, obj) {
if (typeof self.onSelect === 'function') {
self.onSelect(self.buffType);
}
};
return self;
});
var Chest = Container.expand(function () {
var self = Container.call(this);
var chestGraphics = self.attachAsset('Chest', {
anchorX: 0.5,
anchorY: 0.5
});
self.scaleX = 1.5;
self.scaleY = 1.5;
self.glow = true;
self.opened = false;
self.update = function () {
// Glow effect
var pulse = Math.sin(LK.ticks * 0.15) * 0.2 + 1.2;
self.scaleX = pulse;
self.scaleY = pulse;
};
self.open = function () {
if (self.opened) return;
self.opened = true;
// Animate chest opening
tween(self, {
scaleX: 2,
scaleY: 2
}, {
duration: 200,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var Enemy = Container.expand(function (enemyType) {
var self = Container.call(this);
self.enemyType = enemyType || 'minotaur';
self.health = 2;
self.damage = 10;
self.speed = 2;
self.points = 10;
var enemyGraphics;
if (self.enemyType === 'minotaur') {
enemyGraphics = self.attachAsset('minotaur', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 4;
self.speed = 3;
self.points = 15;
} else if (self.enemyType === 'harpy') {
enemyGraphics = self.attachAsset('harpy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 2;
self.speed = 6;
self.points = 20;
} else if (self.enemyType === 'cyclops') {
enemyGraphics = self.attachAsset('cyclops', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 8;
self.speed = 2;
self.damage = 20;
self.points = 50;
}
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
LK.getSound('enemyHit').play();
LK.setScore(LK.getScore() + self.points);
// Chance to drop artifact
if (Math.random() < 0.15) {
var artifactTypes = ['health', 'projectileSpeed', 'shootingSpeed', 'damage'];
var randomType = artifactTypes[Math.floor(Math.random() * artifactTypes.length)];
var artifact;
if (randomType === 'health') {
artifact = new Potion();
} else {
artifact = new Artifact(randomType);
}
artifact.x = self.x;
artifact.y = self.y;
artifacts.push(artifact);
game.addChild(artifact);
}
// Remove from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
};
self.update = function () {
// Move towards hero
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
return self;
});
var Hero = Container.expand(function () {
var self = Container.call(this);
var heroGraphics = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.shootCooldown = 0;
self.speed = 8;
// Boost properties
self.projectileSpeedBoost = 0;
self.projectileSpeedMultiplier = 1;
self.shootingSpeedBoost = 0;
self.shootingSpeedMultiplier = 1;
self.damageBoost = 0;
self.damageMultiplier = 1;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFF0000, 300);
if (self.health <= 0) {
gameOver = true;
}
};
self.shoot = function () {
if (self.shootCooldown <= 0) {
var bullet = new HeroBullet();
bullet.x = self.x;
bullet.y = self.y - 40;
// Apply projectile speed buffs (always use multiplier)
bullet.speed = 4 * (self.projectileSpeedMultiplier || 1);
// Apply damage buffs (always use multiplier)
bullet.damage = 1 * (self.damageMultiplier || 1);
// Find nearest enemy for auto-aim
var nearestEnemy = null;
var nearestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestEnemy = enemy;
}
}
// Set bullet direction towards nearest enemy
if (nearestEnemy) {
var dx = nearestEnemy.x - self.x;
var dy = nearestEnemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
bullet.directionX = dx / distance;
bullet.directionY = dy / distance;
}
} else {
// Default upward direction if no enemies
bullet.directionX = 0;
bullet.directionY = -1;
}
heroBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
// Apply shooting speed buffs (always use multiplier)
var cooldownTime = 10;
if (self.shootingSpeedMultiplier && self.shootingSpeedMultiplier > 1) {
cooldownTime = Math.floor(cooldownTime / self.shootingSpeedMultiplier);
}
self.shootCooldown = cooldownTime;
}
};
self.update = function () {
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
// Stackable buffs logic
if (!self.activeBuffs) self.activeBuffs = [];
// Reset multipliers
self.projectileSpeedMultiplier = 1;
self.shootingSpeedMultiplier = 1;
self.damageMultiplier = 1;
// Remove expired buffs and apply active ones
for (var i = self.activeBuffs.length - 1; i >= 0; i--) {
var buff = self.activeBuffs[i];
if (buff.duration > 0) {
if (buff.type === 'projectileSpeed') {
self.projectileSpeedMultiplier *= buff.multiplier;
} else if (buff.type === 'shootingSpeed') {
self.shootingSpeedMultiplier *= buff.multiplier;
} else if (buff.type === 'damage') {
self.damageMultiplier *= buff.multiplier;
}
buff.duration--;
}
if (buff.duration <= 0) {
self.activeBuffs.splice(i, 1);
}
}
};
return self;
});
var HeroBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('heroBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.damage = 1;
self.directionX = 0;
self.directionY = -1;
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
// Rotate bullet to match shooting direction
self.rotation = Math.atan2(self.directionY, self.directionX) + Math.PI / 2;
};
return self;
});
// Potion class for health artifact
var Potion = Container.expand(function () {
var self = Container.call(this);
self.artifactType = 'health';
// Use Hpot asset for potion
var potionGraphics = self.attachAsset('Hpot', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.lifeTime = 0;
self.collect = function () {
if (!self.collected) {
self.collected = true;
LK.getSound('powerUp').play();
LK.setScore(LK.getScore() + 100);
// Heal hero
hero.health = Math.min(hero.maxHealth, hero.health + 20);
var artifactLabel = 'Health Potion';
var buffDuration = 900; // 15 seconds
if (!hero.collectedArtifacts) hero.collectedArtifacts = [];
if (!hero.artifactTimers) hero.artifactTimers = [];
hero.collectedArtifacts.push(artifactLabel);
hero.artifactTimers.push({
label: artifactLabel,
duration: buffDuration
});
// Remove from artifacts array
for (var i = artifacts.length - 1; i >= 0; i--) {
if (artifacts[i] === self) {
artifacts.splice(i, 1);
break;
}
}
tween(self, {
scaleX: 0,
scaleY: 0
}, {
duration: 200,
onFinish: function onFinish() {
self.destroy();
}
});
}
};
self.update = function () {
self.lifeTime++;
self.rotation += 0.1;
// Pulse effect
var pulse = Math.sin(self.lifeTime * 0.2) * 0.2 + 1;
self.scaleX = pulse;
self.scaleY = pulse;
// Despawn after 10 seconds
if (self.lifeTime > 600) {
for (var i = artifacts.length - 1; i >= 0; i--) {
if (artifacts[i] === self) {
artifacts.splice(i, 1);
break;
}
}
self.destroy();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x8B4513
});
/****
* Game Code
****/
// Game variables
var hero;
var heroBullets = [];
var enemies = [];
var artifacts = [];
var enemySpawnTimer = 0;
var gameOver = false;
var dragActive = false;
var waveLevel = 1;
// UI
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 120;
scoreTxt.y = 50;
LK.gui.topLeft.addChild(scoreTxt);
var healthTxt = new Text2('Health: 100', {
size: 60,
fill: 0xFF4444
});
healthTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(healthTxt);
// Artifacts Tab (top-right, under health)
var artifactsTabBg = LK.getAsset('artifact', {
anchorX: 1,
anchorY: 0,
scaleX: 2.2,
scaleY: 2.2,
x: 0,
y: 0
});
artifactsTabBg.tint = 0x442222;
artifactsTabBg.alpha = 0.7;
artifactsTabBg.x = 0;
artifactsTabBg.y = 180;
LK.gui.topRight.addChild(artifactsTabBg);
var artifactsTabTitle = new Text2('Artifacts', {
size: 40,
fill: 0xFFAAAA
});
artifactsTabTitle.anchor.set(1, 0);
artifactsTabTitle.x = -30;
artifactsTabTitle.y = 200;
LK.gui.topRight.addChild(artifactsTabTitle);
var artifactsTabTxt = new Text2('', {
size: 36,
fill: 0xFFFFFF
});
artifactsTabTxt.anchor.set(1, 0);
artifactsTabTxt.x = -30;
artifactsTabTxt.y = 250;
LK.gui.topRight.addChild(artifactsTabTxt);
var waveTxt = new Text2('Wave: 1', {
size: 50,
fill: 0x44FF44
});
waveTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(waveTxt);
// Weapons Tab
var weaponsTabBg = LK.getAsset('weapon', {
anchorX: 0,
anchorY: 0,
scaleX: 2.2,
scaleY: 2.2,
x: 0,
y: 0
});
weaponsTabBg.tint = 0x222244;
weaponsTabBg.alpha = 0.7;
weaponsTabBg.x = 0;
weaponsTabBg.y = 180;
LK.gui.topLeft.addChild(weaponsTabBg);
var weaponsTabTitle = new Text2('Weapons', {
size: 40,
fill: 0xFFFFAA
});
weaponsTabTitle.anchor.set(0, 0);
weaponsTabTitle.x = 30;
weaponsTabTitle.y = 200;
LK.gui.topLeft.addChild(weaponsTabTitle);
var weaponsTabTxt = new Text2('', {
size: 36,
fill: 0xFFFFFF
});
weaponsTabTxt.anchor.set(0, 0);
weaponsTabTxt.x = 30;
weaponsTabTxt.y = 250;
LK.gui.topLeft.addChild(weaponsTabTxt);
// Add background image
var background = LK.getAsset('bakground', {
anchorX: 0,
anchorY: 0,
scaleX: 20.48,
// Scale to fit 2048 width (2048/100)
scaleY: 27.32 // Scale to fit 2732 height (2732/100)
});
background.x = 0;
background.y = 0;
game.addChild(background);
// Initialize hero
hero = new Hero();
hero.x = 1024;
hero.y = 2000;
game.addChild(hero);
// Spawn enemy function
function spawnEnemy() {
var enemyTypes = ['minotaur', 'harpy', 'cyclops'];
var type = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
// Cyclops only spawn in later waves
if (type === 'cyclops' && waveLevel < 3) {
type = 'minotaur';
}
var enemy = new Enemy(type);
// Spawn from random edge
var side = Math.floor(Math.random() * 4);
if (side === 0) {
// Top
enemy.x = Math.random() * 2048;
enemy.y = -100;
} else if (side === 1) {
// Right
enemy.x = 2148;
enemy.y = Math.random() * 2732;
} else if (side === 2) {
// Bottom
enemy.x = Math.random() * 2048;
enemy.y = 2832;
} else {
// Left
enemy.x = -100;
enemy.y = Math.random() * 2732;
}
enemies.push(enemy);
game.addChild(enemy);
}
// Touch controls
game.down = function (x, y, obj) {
// If chest or buff choices are active, do not move hero or shoot
if (game.chest || game.buffChoices) return;
dragActive = true;
hero.x = x;
hero.y = y;
hero.shoot();
};
game.move = function (x, y, obj) {
// If chest or buff choices are active, do not move hero
if (game.chest || game.buffChoices) return;
if (dragActive) {
hero.x = x;
hero.y = y;
}
};
game.up = function (x, y, obj) {
dragActive = false;
};
// Main game loop
game.update = function () {
if (gameOver) {
LK.showGameOver();
return;
}
// If chest or buff choices are active, pause gameplay except for chest/buff UI
if (game.chest || game.buffChoices) {
// Only update chest and buff choices
if (game.chest) game.chest.update();
if (game.buffChoices) {
for (var i = 0; i < game.buffChoices.length; i++) {
game.buffChoices[i].update && game.buffChoices[i].update();
}
}
// Update UI
scoreTxt.setText('Score: ' + LK.getScore());
healthTxt.setText('Health: ' + hero.health);
waveTxt.setText('Wave: ' + waveLevel);
return;
}
// Update hero
hero.update();
// Keep hero in bounds
hero.x = Math.max(40, Math.min(2008, hero.x));
hero.y = Math.max(40, Math.min(2692, hero.y));
// Auto-shoot
var autoShootRate = 30;
if (hero.shootingSpeedMultiplier && hero.shootingSpeedMultiplier > 1) {
autoShootRate = Math.floor(autoShootRate / hero.shootingSpeedMultiplier);
}
if (LK.ticks % autoShootRate === 0) {
hero.shoot();
}
// Spawn enemies
enemySpawnTimer++;
var spawnRate = Math.max(30, 120 - waveLevel * 10);
if (enemySpawnTimer >= spawnRate) {
spawnEnemy();
enemySpawnTimer = 0;
}
// Update bullets
for (var i = heroBullets.length - 1; i >= 0; i--) {
var bullet = heroBullets[i];
bullet.update();
// Remove bullets that are off-screen
if (bullet.y < -50) {
bullet.destroy();
heroBullets.splice(i, 1);
continue;
}
// Check bullet vs enemy collisions
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
enemy.takeDamage(bullet.damage);
bullet.destroy();
heroBullets.splice(i, 1);
break;
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
// Check hero vs enemy collision
if (enemy.intersects(hero)) {
hero.takeDamage(enemy.damage);
enemy.die();
}
// Remove enemies that are too far off-screen
if (enemy.x < -200 || enemy.x > 2248 || enemy.y < -200 || enemy.y > 2932) {
enemies.splice(i, 1);
enemy.destroy();
}
}
// Update artifacts
for (var i = artifacts.length - 1; i >= 0; i--) {
var artifact = artifacts[i];
artifact.update();
// Check hero vs artifact collision
if (artifact.intersects(hero)) {
artifact.collect();
}
}
// Update wave level based on score
var newWaveLevel = Math.floor(LK.getScore() / 500) + 1;
if (newWaveLevel > waveLevel) {
waveLevel = newWaveLevel;
LK.effects.flashScreen(0x00FF00, 500);
// Pause enemy spawns and show chest
enemySpawnTimer = -9999; // Prevent spawns
// Remove all remaining enemies
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
enemies.splice(i, 1);
}
// Spawn chest in center
if (!game.chest) {
var chest = new Chest();
chest.x = 1024;
chest.y = 1200;
game.addChild(chest);
game.chest = chest;
}
// Show buff choices after chest is opened
game.showBuffChoices = function () {
if (game.buffChoices) return;
var buffs = ['health', 'damage', 'shootingSpeed'];
// Shuffle and pick 3
for (var i = buffs.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var t = buffs[i];
buffs[i] = buffs[j];
buffs[j] = t;
}
var selectedBuffs = buffs.slice(0, 3);
game.buffChoices = [];
if (!hero.weapons) hero.weapons = [];
for (var i = 0; i < 3; i++) {
(function (buffType, idx) {
var choice = new BuffChoice(buffType, function (chosenBuff) {
// Apply permanent weapon (buff)
if (chosenBuff === 'health') {
hero.maxHealth += 20;
hero.health += 20;
hero.weapons.push('+20 Max Health');
} else if (chosenBuff === 'damage') {
hero.damageMultiplier = (hero.damageMultiplier || 1) + 1;
hero.weapons.push('+1 Damage');
} else if (chosenBuff === 'shootingSpeed') {
hero.shootingSpeedMultiplier = (hero.shootingSpeedMultiplier || 1) + 0.5;
hero.weapons.push('Faster Shooting');
}
// Remove buff choices
for (var k = 0; k < game.buffChoices.length; k++) {
game.buffChoices[k].destroy();
}
game.buffChoices = null;
// Resume enemy spawn
enemySpawnTimer = 0;
});
choice.x = 512 + idx * 512;
choice.y = 1500;
game.addChild(choice);
game.buffChoices.push(choice);
})(selectedBuffs[i], i);
}
};
// Listen for chest open
game.chest.down = function (x, y, obj) {
if (game.chest.opened) return;
game.chest.open();
LK.setTimeout(function () {
if (game.chest) {
game.chest.destroy();
game.chest = null;
}
game.showBuffChoices();
}, 300);
};
}
// Update UI
scoreTxt.setText('Score: ' + LK.getScore());
healthTxt.setText('Health: ' + hero.health);
waveTxt.setText('Wave: ' + waveLevel);
// Update weapons tab
var weaponsList = [];
if (hero.weapons && hero.weapons.length > 0) {
// Count stackable buffs
var buffCounts = {};
for (var i = 0; i < hero.weapons.length; i++) {
var buff = hero.weapons[i];
if (!buffCounts[buff]) buffCounts[buff] = 0;
buffCounts[buff]++;
}
// Show each buff with stack count if more than 1
for (var buff in buffCounts) {
if (buffCounts[buff] > 1) {
weaponsList.push(buff + " x" + buffCounts[buff]);
} else {
weaponsList.push(buff);
}
}
}
weaponsTabTxt.setText(weaponsList.length ? weaponsList.join('\n') : 'None');
// Update artifacts tab
// Remove expired artifacts (duration > 15 seconds)
if (!hero.artifactTimers) hero.artifactTimers = [];
if (hero.collectedArtifacts && hero.collectedArtifacts.length > 0) {
for (var i = hero.artifactTimers.length - 1; i >= 0; i--) {
hero.artifactTimers[i].duration--;
if (hero.artifactTimers[i].duration <= 0) {
// Remove from collectedArtifacts and artifactTimers
hero.collectedArtifacts.splice(i, 1);
hero.artifactTimers.splice(i, 1);
}
}
}
var artifactsList = [];
if (hero.collectedArtifacts && hero.collectedArtifacts.length > 0 && hero.artifactTimers && hero.artifactTimers.length > 0) {
for (var i = 0; i < hero.collectedArtifacts.length; i++) {
var label = hero.collectedArtifacts[i];
var timer = hero.artifactTimers[i];
// Show seconds left, rounded up
var secondsLeft = Math.ceil((timer && timer.duration ? timer.duration : 0) / 60);
artifactsList.push(label + (secondsLeft > 0 ? " (" + secondsLeft + "s)" : ""));
}
}
artifactsTabTxt.setText(artifactsList.length ? artifactsList.join('\n') : 'None');
};
// Start background music
LK.playMusic('dungeonMusic');
a realistic cyclops face. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a woman who has wings instead of arms and talons instead of legs. realistic
a realistic angry bull head. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a realistic gold chalice. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a realistic face of a spartan warrior. In-Game asset. 2d. High contrast. No shadows
make this photo up to down
a darker green
a medieval chest. In-Game asset. 2d. High contrast. No shadows
a mediaval sword. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a classic health potion, a bottle of translucent red liquid. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat