User prompt
make secret towers if you choose to not restart at the end of the game that are EX versions of each tower and make the towers the same sprites but tinted red ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
make the player bullet's fire faster and make the bullets do .5 dmage
User prompt
make the boss bullets take 2 hits to destroy with your bullets
User prompt
remove the attack interface
User prompt
not the attack interface! the thing i want to spawn in the same row as the heart is Sword_event
User prompt
make the attack button for the boss fights spawns on the row where player spawns
User prompt
make the bosses take damage from a thing that randomly appears during the boss battle that has a picture of a sword and when your heart touches it it does damage to the boss.
User prompt
make a hit sound when you hit the enemy with a bullet
User prompt
make a battle theme
User prompt
make the username of the person playing show on the bottom instead of the bullet
User prompt
make each character have different HP and each attack the enemy targets one of the heroes and make the bullets do .2 Damage
User prompt
remove the attack option and make the magic have a healing spell that gives you + 5 HP
User prompt
make the bullets do .5 damage every time the enemy collides, and make the enemies have more HP
User prompt
Please fix the bug: 'Uncaught ReferenceError: shopActive is not defined' in or related to this line: 'if (shopActive && shopMenu) {' Line Number: 1848
User prompt
Please fix the bug: 'Uncaught ReferenceError: playerTurnActive is not defined' in or related to this line: 'if (playerTurnActive && playerTurnMenu) {' Line Number: 1803
User prompt
make a few versions of the mimic that have a random chance to have a different sprite
User prompt
Please fix the bug: 'Uncaught ReferenceError: playerTurnActive is not defined' in or related to this line: 'if (playerTurnActive && playerTurnMenu) {' Line Number: 1797
User prompt
make images for each enemy and make it to where you choose what you do on your turn like, attack, items (So you'd have to add a shop) and magic
User prompt
can you make the heart shoot yellow bullets when dragged to destroy bullets the enemy shoots
User prompt
make a white box around the place where you can move the heart
User prompt
make it take 3 seconds before the enemy attacks ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
and let you fight the enemy first
User prompt
make a few minor enemies fights before the boss fight
User prompt
Please fix the bug: 'Uncaught TypeError: storage.set is not a function' in or related to this line: 'storage.set('selectedParty', selectedParty.slice());' Line Number: 496 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
make a knight, but keep it so you can only have 3 teammates but it shows the stats of each character on the bottom and make a reset game data button below the stats incase you want to play the game again ↪💡 Consider importing and using the following plugins: @upit/storage.v1
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Enemy Bullet
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
self.sprite = self.attachAsset('enemy_bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.radius = 30;
self.active = true;
self.init = function (x, y, vx, vy) {
self.x = x;
self.y = y;
self.vx = vx;
self.vy = vy;
};
self.update = function () {
if (!self.active) return;
self.x += self.vx;
self.y += self.vy;
// Offscreen check
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.active = false;
self.destroy();
}
};
return self;
});
// Tower 1 Boss: Gargoyle
var GargoyleBoss = Container.expand(function () {
var self = Container.call(this);
self.hp = 50;
self.maxHP = 50;
self.exp = 50;
self.sprite = self.attachAsset('enemy_bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.sprite.tint = 0x888888; // gray for gargoyle
self.sprite.scaleX = self.sprite.scaleY = 1.7;
self.update = function () {};
return self;
});
// Tower 1 Goblin Enemy
var GoblinEnemy = Container.expand(function () {
var self = Container.call(this);
self.hp = 10;
self.maxHP = 10;
self.exp = 3;
self.sprite = self.attachAsset('enemy_bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.sprite.tint = 0x3fa34d; // greenish for goblin
self.sprite.scaleX = self.sprite.scaleY = 1.2;
self.update = function () {};
return self;
});
// Heart (player dodge object)
var Heart = Container.expand(function () {
var self = Container.call(this);
self.sprite = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 40;
self.invincible = false;
self.invincibleTicks = 0;
self.setInvincible = function (ticks) {
self.invincible = true;
self.invincibleTicks = ticks;
self.sprite.alpha = 0.5;
};
self.update = function () {
if (self.invincible) {
self.invincibleTicks--;
if (self.invincibleTicks <= 0) {
self.invincible = false;
self.sprite.alpha = 1;
}
}
};
return self;
});
// Orb Icon
var OrbIcon = Container.expand(function () {
var self = Container.call(this);
self.orbId = 1;
self.collected = false;
self.icon = null;
self.setOrb = function (orbId, collected) {
self.orbId = orbId;
self.collected = collected;
if (self.icon) self.icon.destroy();
var assetId = 'orb' + orbId;
self.icon = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.icon.alpha = collected ? 1 : 0.3;
};
return self;
});
// Party Member Icon
var PartyIcon = Container.expand(function () {
var self = Container.call(this);
self.type = null;
self.selected = false;
self.icon = null;
self.setType = function (type) {
self.type = type;
if (self.icon) self.icon.destroy();
var assetId = '';
if (type === 'white_mage') assetId = 'icon_white_mage';
if (type === 'thief') assetId = 'icon_thief';
if (type === 'martial') assetId = 'icon_martial';
if (type === 'knight') assetId = 'icon_knight';
self.icon = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
};
self.setSelected = function (selected) {
self.selected = selected;
if (self.icon) {
self.icon.alpha = selected ? 1 : 0.6;
self.icon.scaleX = self.icon.scaleY = selected ? 1.15 : 1;
}
};
return self;
});
// Tower Icon
var TowerIcon = Container.expand(function () {
var self = Container.call(this);
self.towerId = 1;
self.locked = false;
self.icon = null;
self.setTower = function (towerId, locked) {
self.towerId = towerId;
self.locked = locked;
if (self.icon) self.icon.destroy();
var assetId = 'tower' + towerId;
self.icon = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 1
});
self.icon.alpha = locked ? 0.4 : 1;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222233
});
/****
* Game Code
****/
// Game state
// Party member icons
// Heart (player dodge object)
// Enemy bullet
// Tower icons
// Orb icons
// Sound effects
// Music
// --- STORAGE PLUGIN ---
var STATE_PARTY_SELECT = 0;
var STATE_TOWER_SELECT = 1;
var STATE_BATTLE = 2;
var STATE_BOSS_DEFEAT = 3;
var STATE_GAME_COMPLETE = 4;
var gameState = STATE_PARTY_SELECT;
// Party selection
var partyMembers = ['white_mage', 'thief', 'martial', 'knight'];
// Load persistent party and orbs if available
var selectedParty = [];
var collectedOrbs = [false, false, false, false];
(function () {
var savedParty = storage.selectedParty;
var savedOrbs = storage.collectedOrbs;
if (savedParty && Array.isArray(savedParty) && savedParty.length === 3) {
selectedParty = savedParty.slice();
}
if (savedOrbs && Array.isArray(savedOrbs) && savedOrbs.length === 4) {
collectedOrbs = savedOrbs.slice();
}
})();
var partyIcons = [];
var partySelectText = null;
// Tower selection
var towers = [{
id: 1,
name: 'Amethyst Tower',
orb: 1
}, {
id: 2,
name: 'Emerald Tower',
orb: 2
}, {
id: 3,
name: 'Amber Tower',
orb: 3
}, {
id: 4,
name: 'Crimson Tower',
orb: 4
}, {
id: 5,
name: 'Obsidian Tower',
orb: 0
}];
var towerIcons = [];
var orbIcons = [];
var towerSelectText = null;
// Battle
var heart = null;
var heartStartX = 0;
var heartStartY = 0;
var heartDragging = false;
var heartDragOffsetX = 0;
var heartDragOffsetY = 0;
var battleBullets = [];
var battleTimer = 0;
var battlePhase = 0;
var battleBoss = null;
var battleBossId = 0;
var battleBossHP = 0;
var battleBossMaxHP = 0;
var battleBossText = null;
var battleHPText = null;
var battleOrbsText = null;
var battleActive = false;
var battleWin = false;
var battleLose = false;
var battleEndTimeout = null;
// GUI
var guiParty = [];
var guiOrbs = [];
var guiHP = null;
// --- CHARACTER STATS ---
var characterStats = {
white_mage: {
name: "White Mage",
hp: 18,
atk: 6,
def: 8,
desc: "Heals and supports the party."
},
thief: {
name: "Thief",
hp: 14,
atk: 10,
def: 5,
desc: "Fast and nimble, high critical chance."
},
martial: {
name: "Martial Artist",
hp: 20,
atk: 8,
def: 7,
desc: "Balanced fighter with strong attacks."
},
knight: {
name: "Knight",
hp: 24,
atk: 7,
def: 12,
desc: "High defense, protects the party."
}
};
var statTextObjs = [];
var resetButton = null;
function showCharacterStatsAndReset() {
// Remove old stats if present
for (var i = 0; i < statTextObjs.length; i++) statTextObjs[i].destroy();
statTextObjs = [];
if (resetButton) {
resetButton.destroy();
resetButton = null;
}
var statsY = 2500;
var statsX = 200;
var spacing = 480;
for (var i = 0; i < partyMembers.length; i++) {
var type = partyMembers[i];
var stats = characterStats[type];
var txt = new Text2(stats.name + " HP:" + stats.hp + " ATK:" + stats.atk + " DEF:" + stats.def, {
size: 60,
fill: "#fff"
});
txt.anchor.set(0, 0);
txt.x = statsX + i * spacing;
txt.y = statsY;
LK.gui.bottom.addChild(txt);
statTextObjs.push(txt);
}
// Add reset button below stats
resetButton = new Text2("Reset Game Data", {
size: 70,
fill: 0xFF3366
});
resetButton.anchor.set(0.5, 0);
resetButton.x = 2048 / 2;
resetButton.y = statsY + 120;
resetButton.interactive = true;
resetButton.buttonMode = true;
resetButton._isResetButton = true;
LK.gui.bottom.addChild(resetButton);
}
function clearCharacterStatsAndReset() {
for (var i = 0; i < statTextObjs.length; i++) statTextObjs[i].destroy();
statTextObjs = [];
if (resetButton) {
resetButton.destroy();
resetButton = null;
}
}
// Utility
function resetGame() {
// Reset all state
gameState = STATE_PARTY_SELECT;
selectedParty = [];
collectedOrbs = [false, false, false, false];
for (var i = 0; i < partyIcons.length; i++) partyIcons[i].destroy();
partyIcons = [];
for (var i = 0; i < towerIcons.length; i++) towerIcons[i].destroy();
towerIcons = [];
for (var i = 0; i < orbIcons.length; i++) orbIcons[i].destroy();
orbIcons = [];
if (partySelectText) {
partySelectText.destroy();
partySelectText = null;
}
if (towerSelectText) {
towerSelectText.destroy();
towerSelectText = null;
}
if (heart) {
heart.destroy();
heart = null;
}
for (var i = 0; i < battleBullets.length; i++) battleBullets[i].destroy();
battleBullets = [];
if (battleBossText) {
battleBossText.destroy();
battleBossText = null;
}
if (battleHPText) {
battleHPText.destroy();
battleHPText = null;
}
if (battleOrbsText) {
battleOrbsText.destroy();
battleOrbsText = null;
}
if (battleEndTimeout) {
LK.clearTimeout(battleEndTimeout);
battleEndTimeout = null;
}
for (var i = 0; i < guiParty.length; i++) guiParty[i].destroy();
guiParty = [];
for (var i = 0; i < guiOrbs.length; i++) guiOrbs[i].destroy();
guiOrbs = [];
if (guiHP) {
guiHP.destroy();
guiHP = null;
}
LK.setScore(0);
// Clear persistent stats on full reset
delete storage.selectedParty;
delete storage.collectedOrbs;
startPartySelect();
}
// --- PARTY SELECT ---
function startPartySelect() {
clearCharacterStatsAndReset();
showCharacterStatsAndReset();
gameState = STATE_PARTY_SELECT;
selectedParty = [];
// Show party selection text
partySelectText = new Text2('Choose your party (tap to select 3):', {
size: 90,
fill: '#fff'
});
partySelectText.anchor.set(0.5, 0);
partySelectText.x = 2048 / 2;
partySelectText.y = 180;
game.addChild(partySelectText);
// Show icons for each member
var spacing = 400;
var startX = 2048 / 2 - spacing;
for (var i = 0; i < partyMembers.length; i++) {
var icon = new PartyIcon();
icon.setType(partyMembers[i]);
icon.x = startX + i * spacing;
icon.y = 600;
icon.setSelected(false);
icon.memberId = partyMembers[i];
icon.index = i;
partyIcons.push(icon);
game.addChild(icon);
}
}
// Handle party icon selection
function handlePartySelect(x, y, obj) {
if (gameState !== STATE_PARTY_SELECT) return;
for (var i = 0; i < partyIcons.length; i++) {
var icon = partyIcons[i];
var dx = x - icon.x;
var dy = y - icon.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 80) {
// Toggle selection
var idx = selectedParty.indexOf(icon.memberId);
if (idx === -1 && selectedParty.length < 3) {
selectedParty.push(icon.memberId);
icon.setSelected(true);
} else if (idx !== -1) {
selectedParty.splice(idx, 1);
icon.setSelected(false);
}
// If 3 selected, proceed
if (selectedParty.length === 3) {
// Save party selection persistently
storage.selectedParty = selectedParty.slice();
LK.setTimeout(function () {
for (var j = 0; j < partyIcons.length; j++) partyIcons[j].destroy();
partyIcons = [];
if (partySelectText) {
partySelectText.destroy();
partySelectText = null;
}
startTowerSelect();
}, 400);
}
break;
}
}
}
// --- TOWER SELECT ---
function startTowerSelect() {
clearCharacterStatsAndReset();
showCharacterStatsAndReset();
gameState = STATE_TOWER_SELECT;
// Show tower select text
towerSelectText = new Text2('Choose a tower to conquer:', {
size: 90,
fill: '#fff'
});
towerSelectText.anchor.set(0.5, 0);
towerSelectText.x = 2048 / 2;
towerSelectText.y = 180;
game.addChild(towerSelectText);
// Show towers
var spacing = 350;
var startX = 2048 / 2 - spacing * 2 + spacing / 2;
for (var i = 0; i < towers.length; i++) {
var locked = false;
if (i === 4) {
// Fifth tower locked until all orbs
locked = !(collectedOrbs[0] && collectedOrbs[1] && collectedOrbs[2] && collectedOrbs[3]);
}
var icon = new TowerIcon();
icon.setTower(towers[i].id, locked);
icon.x = startX + i * spacing;
icon.y = 700;
icon.towerIndex = i;
towerIcons.push(icon);
game.addChild(icon);
}
// Show orbs collected
for (var i = 0; i < 4; i++) {
var orb = new OrbIcon();
orb.setOrb(i + 1, collectedOrbs[i]);
orb.x = 2048 / 2 - 200 + i * 130;
orb.y = 1200;
orbIcons.push(orb);
game.addChild(orb);
}
}
// Handle tower icon selection
function handleTowerSelect(x, y, obj) {
if (gameState !== STATE_TOWER_SELECT) return;
for (var i = 0; i < towerIcons.length; i++) {
var icon = towerIcons[i];
var dx = x - icon.x;
var dy = y - (icon.y - 150);
if (dx > -90 && dx < 90 && dy > 0 && dy < 300) {
if (icon.locked) {
// Do nothing
return;
}
// Start battle for this tower
var towerIdx = icon.towerId - 1;
for (var j = 0; j < towerIcons.length; j++) towerIcons[j].destroy();
towerIcons = [];
for (var j = 0; j < orbIcons.length; j++) orbIcons[j].destroy();
orbIcons = [];
if (towerSelectText) {
towerSelectText.destroy();
towerSelectText = null;
}
startBattle(towerIdx);
break;
}
}
}
// --- BATTLE ---
function startBattle(towerIdx) {
clearCharacterStatsAndReset();
gameState = STATE_BATTLE;
battleActive = true;
battleWin = false;
battleLose = false;
battlePhase = 0;
battleTimer = 0;
battleBossId = towerIdx + 1;
battleBossHP = 10 + towerIdx * 5;
battleBossMaxHP = battleBossHP;
battleBullets = [];
// --- MINOR ENEMY FIGHTS SETUP ---
var minorEnemies = [];
var minorEnemyCount = 2 + towerIdx; // 2,3,4,5,6 for each tower
var minorEnemyHP = 4 + towerIdx * 2;
var minorEnemyIdx = 0;
var inMinorFight = true;
var minorEnemyObj = null;
var minorEnemyText = null;
var minorEnemyHPText = null;
var minorEnemyPatternTimer = 0;
var minorEnemyPatternId = 0;
var minorEnemyNames = ["Goblin", "Slime", "Bat", "Imp", "Wisp", "Rat", "Mimic", "Sprite"];
var minorEnemyColors = [0x3fa34d, 0x6ec6e6, 0x8888ff, 0x9e4b16, 0xf7e96b, 0xaaaaaa, 0x8b5c2a, 0xccccff];
var minorEnemyName = "";
var minorEnemyColor = 0x3fa34d;
// Start with minor enemy fight before boss
inMinorFight = true;
minorEnemyIdx = 0;
// Draw white box for heart movement area
if (typeof heartBox !== "undefined" && heartBox) {
heartBox.destroy();
heartBox = null;
}
var heartBox = new Container();
var boxMinX = 2048 / 2 - 350;
var boxMaxX = 2048 / 2 + 350;
var boxMinY = 1200;
var boxMaxY = 2200;
var boxW = boxMaxX - boxMinX;
var boxH = boxMaxY - boxMinY;
var borderW = 8;
var boxColor = 0xffffff;
// Top border
var topLine = LK.getAsset('bullet', {
width: boxW,
height: borderW,
color: boxColor,
shape: 'box',
anchorX: 0,
anchorY: 0
});
topLine.x = boxMinX;
topLine.y = boxMinY;
heartBox.addChild(topLine);
// Bottom border
var bottomLine = LK.getAsset('bullet', {
width: boxW,
height: borderW,
color: boxColor,
shape: 'box',
anchorX: 0,
anchorY: 0
});
bottomLine.x = boxMinX;
bottomLine.y = boxMaxY - borderW;
heartBox.addChild(bottomLine);
// Left border
var leftLine = LK.getAsset('bullet', {
width: borderW,
height: boxH,
color: boxColor,
shape: 'box',
anchorX: 0,
anchorY: 0
});
leftLine.x = boxMinX;
leftLine.y = boxMinY;
heartBox.addChild(leftLine);
// Right border
var rightLine = LK.getAsset('bullet', {
width: borderW,
height: boxH,
color: boxColor,
shape: 'box',
anchorX: 0,
anchorY: 0
});
rightLine.x = boxMaxX - borderW;
rightLine.y = boxMinY;
heartBox.addChild(rightLine);
game.addChild(heartBox);
// Show heart
heart = new Heart();
heart.x = 2048 / 2;
heart.y = 1800;
heartStartX = heart.x;
heartStartY = heart.y;
game.addChild(heart);
// Show party in GUI
for (var i = 0; i < selectedParty.length; i++) {
var icon = new PartyIcon();
icon.setType(selectedParty[i]);
icon.x = 200 + i * 150;
icon.y = 100;
icon.setSelected(true);
guiParty.push(icon);
LK.gui.top.addChild(icon);
}
// Show orbs in GUI
for (var i = 0; i < 4; i++) {
var orb = new OrbIcon();
orb.setOrb(i + 1, collectedOrbs[i]);
orb.x = 2048 - 200 - (3 - i) * 120;
orb.y = 100;
guiOrbs.push(orb);
LK.gui.top.addChild(orb);
}
// Show HP in GUI
guiHP = new Text2('❤', {
size: 80,
fill: '#ff3366'
});
guiHP.anchor.set(0, 0);
guiHP.x = 120;
guiHP.y = 220;
LK.gui.top.addChild(guiHP);
// Show orbs
battleOrbsText = new Text2('Orbs: ' + getOrbsCount() + '/4', {
size: 60,
fill: '#fff'
});
battleOrbsText.anchor.set(0.5, 0);
battleOrbsText.x = 2048 / 2;
battleOrbsText.y = 450;
game.addChild(battleOrbsText);
// --- ENEMY ATTACK DELAY STATE ---
var enemyAttackDelayTicks = 180; // 3 seconds at 60fps
var enemyAttackDelayActive = true;
// --- MINOR ENEMY FIGHT LOGIC ---
function startMinorEnemyFight(idx) {
// Clean up previous
if (minorEnemyObj) {
minorEnemyObj.destroy();
minorEnemyObj = null;
}
if (minorEnemyText) {
minorEnemyText.destroy();
minorEnemyText = null;
}
if (minorEnemyHPText) {
minorEnemyHPText.destroy();
minorEnemyHPText = null;
}
minorEnemyPatternTimer = 0;
minorEnemyPatternId = idx % 4;
minorEnemyName = minorEnemyNames[idx % minorEnemyNames.length];
minorEnemyColor = minorEnemyColors[idx % minorEnemyColors.length];
// Create enemy
minorEnemyObj = new GoblinEnemy();
minorEnemyObj.hp = minorEnemyHP;
minorEnemyObj.maxHP = minorEnemyHP;
minorEnemyObj.sprite.tint = minorEnemyColor;
minorEnemyObj.sprite.scaleX = minorEnemyObj.sprite.scaleY = 1.2 + 0.1 * (idx % 3);
minorEnemyObj.x = 2048 / 2;
minorEnemyObj.y = 700;
game.addChild(minorEnemyObj);
// Name
minorEnemyText = new Text2(minorEnemyName, {
size: 90,
fill: '#fff'
});
minorEnemyText.anchor.set(0.5, 0);
minorEnemyText.x = 2048 / 2;
minorEnemyText.y = 200;
game.addChild(minorEnemyText);
// HP
minorEnemyHPText = new Text2('HP: ' + minorEnemyObj.hp, {
size: 70,
fill: '#fff'
});
minorEnemyHPText.anchor.set(0.5, 0);
minorEnemyHPText.x = 2048 / 2;
minorEnemyHPText.y = 320;
game.addChild(minorEnemyHPText);
// Reset enemy attack delay for each new minor enemy
enemyAttackDelayTicks = 180;
enemyAttackDelayActive = true;
}
function cleanupMinorEnemyFight() {
if (minorEnemyObj) {
minorEnemyObj.destroy();
minorEnemyObj = null;
}
if (minorEnemyText) {
minorEnemyText.destroy();
minorEnemyText = null;
}
if (minorEnemyHPText) {
minorEnemyHPText.destroy();
minorEnemyHPText = null;
}
for (var i = battleBullets.length - 1; i >= 0; i--) {
battleBullets[i].destroy();
battleBullets.splice(i, 1);
}
LK.setScore(0);
}
function updateMinorEnemyFight() {
if (!minorEnemyObj) return;
// Update heart
if (heart) heart.update();
// Update bullets
for (var i = battleBullets.length - 1; i >= 0; i--) {
var b = battleBullets[i];
b.update();
if (!b.active) {
battleBullets.splice(i, 1);
continue;
}
// Collision with heart
if (!heart.invincible && circleIntersect(heart.x, heart.y, heart.radius, b.x, b.y, b.radius)) {
heart.setInvincible(40);
LK.effects.flashObject(heart, 0xff0000, 400);
LK.getSound('hit').play();
LK.setScore(LK.getScore() + 1);
// Lose if hit 2 times in minor fight
if (LK.getScore() >= 2) {
battleLose = true;
battleActive = false;
endBattle(false);
return;
}
}
}
// --- ENEMY ATTACK DELAY LOGIC ---
if (enemyAttackDelayActive) {
enemyAttackDelayTicks--;
if (enemyAttackDelayTicks <= 0) {
enemyAttackDelayActive = false;
}
return; // Don't attack until delay is over
}
// Enemy attack pattern
minorEnemyPatternTimer++;
if (minorEnemyPatternTimer % 40 === 0) {
spawnBulletPattern(minorEnemyPatternId);
// Minor enemy HP drops a bit each pattern
minorEnemyObj.hp--;
if (minorEnemyObj.hp < 0) minorEnemyObj.hp = 0;
if (minorEnemyHPText) minorEnemyHPText.setText('HP: ' + minorEnemyObj.hp);
}
// Win condition: survive or reduce enemy HP
if (minorEnemyObj.hp <= 0) {
// Next enemy or boss
cleanupMinorEnemyFight();
minorEnemyIdx++;
if (minorEnemyIdx < minorEnemyCount) {
startMinorEnemyFight(minorEnemyIdx);
} else {
// Proceed to boss
inMinorFight = false;
startBossFight();
}
}
}
function startBossFight() {
// Clean up minor enemy UI
if (minorEnemyObj) {
minorEnemyObj.destroy();
minorEnemyObj = null;
}
if (minorEnemyText) {
minorEnemyText.destroy();
minorEnemyText = null;
}
if (minorEnemyHPText) {
minorEnemyHPText.destroy();
minorEnemyHPText = null;
}
for (var i = battleBullets.length - 1; i >= 0; i--) {
battleBullets[i].destroy();
battleBullets.splice(i, 1);
}
LK.setScore(0);
// Show boss name
var bossNames = ['Amethyst Guardian', 'Emerald Warden', 'Amber Sentinel', 'Crimson Overlord', 'Obsidian Nemesis'];
battleBossText = new Text2(bossNames[towerIdx], {
size: 100,
fill: '#fff'
});
battleBossText.anchor.set(0.5, 0);
battleBossText.x = 2048 / 2;
battleBossText.y = 200;
game.addChild(battleBossText);
// Show HP
battleHPText = new Text2('HP: ' + battleBossHP, {
size: 80,
fill: '#fff'
});
battleHPText.anchor.set(0.5, 0);
battleHPText.x = 2048 / 2;
battleHPText.y = 350;
game.addChild(battleHPText);
LK.setScore(0);
battleTimer = 0;
battlePhase = 0;
battleActive = true;
// Reset enemy attack delay for boss
enemyAttackDelayTicks = 180;
enemyAttackDelayActive = true;
}
// Start music
LK.playMusic(towerIdx === 4 ? 'boss_theme' : 'battle_theme');
// Start first minor enemy
startMinorEnemyFight(0);
// Patch updateBattle to handle minor enemies before boss
var _originalUpdateBattle = updateBattle;
updateBattle = function updateBattle() {
if (gameState !== STATE_BATTLE || !battleActive) return;
if (inMinorFight) {
updateMinorEnemyFight();
return;
}
// Boss fight as before
// Update heart
if (heart) heart.update();
// Update bullets
for (var i = battleBullets.length - 1; i >= 0; i--) {
var b = battleBullets[i];
b.update();
if (!b.active) {
battleBullets.splice(i, 1);
continue;
}
// Collision with heart
if (!heart.invincible && circleIntersect(heart.x, heart.y, heart.radius, b.x, b.y, b.radius)) {
heart.setInvincible(40);
LK.effects.flashObject(heart, 0xff0000, 400);
LK.getSound('hit').play();
LK.setScore(LK.getScore() + 1);
// Lose if hit 3 times
if (LK.getScore() >= 3) {
battleLose = true;
battleActive = false;
endBattle(false);
return;
}
}
}
// --- ENEMY ATTACK DELAY LOGIC ---
if (enemyAttackDelayActive) {
enemyAttackDelayTicks--;
if (enemyAttackDelayTicks <= 0) {
enemyAttackDelayActive = false;
}
return; // Don't attack until delay is over
}
// Boss attack pattern
battleTimer++;
if (battleBossId === 5) {
// Final boss: mix of all patterns
if (battleTimer % 30 === 0) {
spawnBulletPattern(battleTimer / 300 % 4 | 0);
}
} else {
if (battleTimer % 40 === 0) {
spawnBulletPattern(battleBossId - 1);
}
}
// Win condition: survive for N ticks or reduce boss HP
if (battleBossHP <= 0) {
battleWin = true;
battleActive = false;
endBattle(true);
return;
}
};
}
// Get orbs count
function getOrbsCount() {
var c = 0;
for (var i = 0; i < collectedOrbs.length; i++) if (collectedOrbs[i]) c++;
return c;
}
// Handle heart drag
function handleHeartDrag(x, y, obj) {
if (gameState !== STATE_BATTLE || !battleActive) return;
if (!heart) return;
// Clamp to battle box
var minX = 2048 / 2 - 350,
maxX = 2048 / 2 + 350;
var minY = 1200,
maxY = 2200;
heart.x = Math.max(minX, Math.min(maxX, x));
heart.y = Math.max(minY, Math.min(maxY, y));
}
// Handle heart down
function handleHeartDown(x, y, obj) {
if (gameState !== STATE_BATTLE || !battleActive) return;
if (!heart) return;
var dx = x - heart.x;
var dy = y - heart.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < heart.radius + 10) {
heartDragging = true;
heartDragOffsetX = dx;
heartDragOffsetY = dy;
}
}
// Handle heart up
function handleHeartUp(x, y, obj) {
if (gameState !== STATE_BATTLE || !battleActive) return;
heartDragging = false;
}
// --- BATTLE UPDATE ---
function updateBattle() {
if (gameState !== STATE_BATTLE || !battleActive) return;
// Update heart
if (heart) heart.update();
// Update bullets
for (var i = battleBullets.length - 1; i >= 0; i--) {
var b = battleBullets[i];
b.update();
if (!b.active) {
battleBullets.splice(i, 1);
continue;
}
// Collision with heart
if (!heart.invincible && circleIntersect(heart.x, heart.y, heart.radius, b.x, b.y, b.radius)) {
// Hit!
heart.setInvincible(40);
LK.effects.flashObject(heart, 0xff0000, 400);
LK.getSound('hit').play();
LK.setScore(LK.getScore() + 1);
// Lose if hit 3 times
if (LK.getScore() >= 3) {
battleLose = true;
battleActive = false;
endBattle(false);
return;
}
}
}
// Boss attack pattern
battleTimer++;
if (battleBossId === 5) {
// Final boss: mix of all patterns
if (battleTimer % 30 === 0) {
spawnBulletPattern(battleTimer / 300 % 4 | 0);
}
} else {
if (battleTimer % 40 === 0) {
spawnBulletPattern(battleBossId - 1);
}
}
// Win condition: survive for N ticks or reduce boss HP
if (battleBossHP <= 0) {
battleWin = true;
battleActive = false;
endBattle(true);
return;
}
}
// --- BATTLE PATTERNS ---
function spawnBulletPattern(patternId) {
var cx = 2048 / 2,
cy = 1700;
if (patternId === 0) {
// Simple downward rain
for (var i = 0; i < 5; i++) {
var b = new EnemyBullet();
b.init(cx - 300 + 150 * i, 1200, 0, 12);
battleBullets.push(b);
game.addChild(b);
}
} else if (patternId === 1) {
// Sine wave
for (var i = 0; i < 4; i++) {
var b = new EnemyBullet();
b.init(cx - 200 + 200 * i, 1200, Math.sin(LK.ticks / 20 + i) * 6, 10);
battleBullets.push(b);
game.addChild(b);
}
} else if (patternId === 2) {
// Spiral
var angle = LK.ticks % 360 / 180 * Math.PI;
for (var i = 0; i < 6; i++) {
var a = angle + i * Math.PI / 3;
var b = new EnemyBullet();
b.init(cx, 1400, Math.cos(a) * 8, Math.sin(a) * 8 + 8);
battleBullets.push(b);
game.addChild(b);
}
} else if (patternId === 3) {
// Random bursts
for (var i = 0; i < 8; i++) {
var a = Math.random() * Math.PI * 2;
var b = new EnemyBullet();
b.init(cx, 1500, Math.cos(a) * 10, Math.sin(a) * 10 + 10);
battleBullets.push(b);
game.addChild(b);
}
}
// Boss HP drops a bit each pattern
battleBossHP--;
if (battleBossHP < 0) battleBossHP = 0;
if (battleHPText) battleHPText.setText('HP: ' + battleBossHP);
}
// --- END BATTLE ---
function endBattle(win) {
if (win) {
LK.getSound('boss_defeat').play();
// Collect orb if not final boss
if (battleBossId <= 4) {
collectedOrbs[battleBossId - 1] = true;
// Save orbs persistently
storage.collectedOrbs = collectedOrbs.slice();
LK.getSound('collect_orb').play();
}
// Show win text
var winText = new Text2('Victory!', {
size: 140,
fill: '#fff'
});
winText.anchor.set(0.5, 0.5);
winText.x = 2048 / 2;
winText.y = 1200;
game.addChild(winText);
LK.effects.flashScreen(0x00ff00, 800);
battleEndTimeout = LK.setTimeout(function () {
winText.destroy();
cleanupBattle();
if (battleBossId === 5) {
// Game complete
showGameComplete();
} else {
startTowerSelect();
}
}, 1800);
} else {
// Lose
var loseText = new Text2('Defeated!', {
size: 140,
fill: '#fff'
});
loseText.anchor.set(0.5, 0.5);
loseText.x = 2048 / 2;
loseText.y = 1200;
game.addChild(loseText);
LK.effects.flashScreen(0xff0000, 800);
battleEndTimeout = LK.setTimeout(function () {
loseText.destroy();
cleanupBattle();
startTowerSelect();
}, 1800);
}
}
// --- CLEANUP BATTLE ---
function cleanupBattle() {
if (heart) {
heart.destroy();
heart = null;
}
// Remove heartBox if present
if (typeof heartBox !== "undefined" && heartBox) {
heartBox.destroy();
heartBox = null;
}
for (var i = 0; i < battleBullets.length; i++) battleBullets[i].destroy();
battleBullets = [];
if (battleBossText) {
battleBossText.destroy();
battleBossText = null;
}
if (battleHPText) {
battleHPText.destroy();
battleHPText = null;
}
if (battleOrbsText) {
battleOrbsText.destroy();
battleOrbsText = null;
}
for (var i = 0; i < guiParty.length; i++) guiParty[i].destroy();
guiParty = [];
for (var i = 0; i < guiOrbs.length; i++) guiOrbs[i].destroy();
guiOrbs = [];
if (guiHP) {
guiHP.destroy();
guiHP = null;
}
LK.setScore(0);
LK.stopMusic();
}
// --- GAME COMPLETE ---
function showGameComplete() {
clearCharacterStatsAndReset();
gameState = STATE_GAME_COMPLETE;
var completeText = new Text2('You conquered all towers!\nQuest Complete!', {
size: 120,
fill: '#fff'
});
completeText.anchor.set(0.5, 0.5);
completeText.x = 2048 / 2;
completeText.y = 1200;
game.addChild(completeText);
LK.effects.flashScreen(0x00ffcc, 1200);
LK.setTimeout(function () {
completeText.destroy();
resetGame();
}, 3000);
}
// --- CIRCLE INTERSECT ---
function circleIntersect(x1, y1, r1, x2, y2, r2) {
var dx = x1 - x2,
dy = y1 - y2;
var dist = Math.sqrt(dx * dx + dy * dy);
return dist < r1 + r2 - 10;
}
// --- GAME EVENTS ---
game.down = function (x, y, obj) {
// Check for reset button tap
if (resetButton && resetButton.parent) {
var btn = resetButton;
var bx = btn.x - btn.width * btn.anchor.x;
var by = btn.y - btn.height * btn.anchor.y;
var bw = btn.width;
var bh = btn.height;
// Convert to LK.gui.bottom coordinates
var local = LK.gui.bottom.toLocal({
x: x,
y: y
});
if (local.x >= bx && local.x <= bx + bw && local.y >= by && local.y <= by + bh) {
resetGame();
return;
}
}
if (gameState === STATE_PARTY_SELECT) {
handlePartySelect(x, y, obj);
} else if (gameState === STATE_TOWER_SELECT) {
handleTowerSelect(x, y, obj);
} else if (gameState === STATE_BATTLE && battleActive) {
handleHeartDown(x, y, obj);
}
};
game.move = function (x, y, obj) {
if (gameState === STATE_BATTLE && battleActive && heartDragging) {
handleHeartDrag(x - heartDragOffsetX, y - heartDragOffsetY, obj);
}
};
game.up = function (x, y, obj) {
if (gameState === STATE_BATTLE && battleActive) {
handleHeartUp(x, y, obj);
}
};
// --- GAME UPDATE ---
game.update = function () {
if (gameState === STATE_BATTLE && battleActive) {
updateBattle();
}
};
// --- START GAME ---
LK.playMusic('main_theme');
resetGame(); ===================================================================
--- original.js
+++ change.js
@@ -549,8 +549,71 @@
var minorEnemyColor = 0x3fa34d;
// Start with minor enemy fight before boss
inMinorFight = true;
minorEnemyIdx = 0;
+ // Draw white box for heart movement area
+ if (typeof heartBox !== "undefined" && heartBox) {
+ heartBox.destroy();
+ heartBox = null;
+ }
+ var heartBox = new Container();
+ var boxMinX = 2048 / 2 - 350;
+ var boxMaxX = 2048 / 2 + 350;
+ var boxMinY = 1200;
+ var boxMaxY = 2200;
+ var boxW = boxMaxX - boxMinX;
+ var boxH = boxMaxY - boxMinY;
+ var borderW = 8;
+ var boxColor = 0xffffff;
+ // Top border
+ var topLine = LK.getAsset('bullet', {
+ width: boxW,
+ height: borderW,
+ color: boxColor,
+ shape: 'box',
+ anchorX: 0,
+ anchorY: 0
+ });
+ topLine.x = boxMinX;
+ topLine.y = boxMinY;
+ heartBox.addChild(topLine);
+ // Bottom border
+ var bottomLine = LK.getAsset('bullet', {
+ width: boxW,
+ height: borderW,
+ color: boxColor,
+ shape: 'box',
+ anchorX: 0,
+ anchorY: 0
+ });
+ bottomLine.x = boxMinX;
+ bottomLine.y = boxMaxY - borderW;
+ heartBox.addChild(bottomLine);
+ // Left border
+ var leftLine = LK.getAsset('bullet', {
+ width: borderW,
+ height: boxH,
+ color: boxColor,
+ shape: 'box',
+ anchorX: 0,
+ anchorY: 0
+ });
+ leftLine.x = boxMinX;
+ leftLine.y = boxMinY;
+ heartBox.addChild(leftLine);
+ // Right border
+ var rightLine = LK.getAsset('bullet', {
+ width: borderW,
+ height: boxH,
+ color: boxColor,
+ shape: 'box',
+ anchorX: 0,
+ anchorY: 0
+ });
+ rightLine.x = boxMaxX - borderW;
+ rightLine.y = boxMinY;
+ heartBox.addChild(rightLine);
+ game.addChild(heartBox);
// Show heart
heart = new Heart();
heart.x = 2048 / 2;
heart.y = 1800;
@@ -1021,8 +1084,13 @@
if (heart) {
heart.destroy();
heart = null;
}
+ // Remove heartBox if present
+ if (typeof heartBox !== "undefined" && heartBox) {
+ heartBox.destroy();
+ heartBox = null;
+ }
for (var i = 0; i < battleBullets.length; i++) battleBullets[i].destroy();
battleBullets = [];
if (battleBossText) {
battleBossText.destroy();
8-bit heart. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
martial artist. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
8- bit girl thief. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
White mage 8-bit girl. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A diamond shaped white bullet. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
purple orb. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
green emerald. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
orange diamond . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
red crystal. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
make a 8-bit knight. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
8-bit RPG tower. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Green Tower. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A tree with a door on top of a hill. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A castle surrounded by red hot lava. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a twisted castle with creepy trees on the ground. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a realistic whisp of blue fire. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a goblin with a dagger. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
realistic slime monster. In-Game asset. 2d. High contrast. No shadows
a realistic rat. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a red chest mimic. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Realistic Imp. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a pile of money that is also a mimic and realistic. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a brick mimic realistic. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a goblin with a dagger realistic. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a realistic bat. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a white bullet. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a giant realistic monster made out of obsidian. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a king made out of emerald. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a red ruby monster with a two handed greatsword. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
amethyst knight. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
final boss holding a white mage from the game and the knight from the game. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a sword on a yellow button. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat