/****
* Plugins
****/
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Boss class: fires directly at wizard, uses boss asset and BossBullet
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossSprite = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 30;
self.lastX = 0;
self.lastY = 0;
self.fireCooldown = 0;
self.fireInterval = 200; // frames (~3.3s at 60fps) - boss fires even less often
self.speed = 6;
self.state = "enter"; // "enter" or "fight"
self.update = function () {
// Enter from top, then stop and start fighting
if (self.state === "enter") {
self.y += self.speed;
if (self.y >= 400) {
self.y = 400;
self.state = "fight";
// Initialize orbit parameters
self.orbitAngle = 0;
self.orbitRadius = 850; // Even wider orbit
self.orbitSpeed = 0.006 + Math.random() * 0.003; // 3te1 daha yavaş (1/3 speed)
self.orbitDirection = Math.random() < 0.5 ? 1 : -1; // random direction
self.orbitDirChangeTimer = 0; // For smooth direction change
self.orbitDirTarget = self.orbitDirection;
}
} else if (self.state === "fight") {
// Orbit around wizard
if (typeof self.orbitAngle === "undefined") self.orbitAngle = 0;
if (typeof self.orbitRadius === "undefined") self.orbitRadius = 650; // Match new default
if (typeof self.orbitSpeed === "undefined") self.orbitSpeed = 0.012 + Math.random() * 0.008; // Match new default
if (typeof self.orbitDirection === "undefined") self.orbitDirection = 1;
// Smoothly change orbit direction every 2-4 seconds
if (typeof self.orbitDirChangeTimer === "undefined") self.orbitDirChangeTimer = 0;
if (typeof self.orbitDirTarget === "undefined") self.orbitDirTarget = self.orbitDirection;
self.orbitDirChangeTimer--;
if (self.orbitDirChangeTimer <= 0) {
// Pick a new direction: -1, 1, or a random value in between for smoothness
var newTarget = Math.random() < 0.5 ? -1 : 1;
// Occasionally pick a value in between for smoothness
if (Math.random() < 0.4) newTarget = (Math.random() - 0.5) * 2;
self.orbitDirTarget = newTarget;
self.orbitDirChangeTimer = 120 + Math.floor(Math.random() * 120); // 2-4 seconds
}
// Smoothly interpolate orbitDirection toward orbitDirTarget
self.orbitDirection += (self.orbitDirTarget - self.orbitDirection) * 0.04;
// Update orbit angle
self.orbitAngle += self.orbitSpeed * self.orbitDirection;
// Orbit center is wizard's current position
var centerX = wizard.x;
var centerY = wizard.y;
// Calculate boss position on orbit
self.x = centerX + Math.cos(self.orbitAngle) * self.orbitRadius;
self.y = centerY + Math.sin(self.orbitAngle) * self.orbitRadius;
// Always face wizard (for firing), but keep boss sprite visually fixed
var dx = wizard.x - self.x;
var dy = wizard.y - self.y;
var angle = Math.atan2(dy, dx);
// Do NOT rotate boss sprite visually; keep it fixed
// (But still use 'angle' for boss bullet firing below)
// Fire at wizard every interval
self.fireCooldown--;
if (self.fireCooldown <= 0) {
// Fire BossBullet at wizard
var bullet = new BossBullet();
bullet.x = self.x;
bullet.y = self.y;
// Calculate direction to wizard
var dx = wizard.x - self.x;
var dy = wizard.y - self.y;
var len = Math.sqrt(dx * dx + dy * dy);
var speed = 5; // 300 units/sec at 60fps
if (len > 0) {
bullet.vx = dx / len * speed;
bullet.vy = dy / len * speed;
}
// Rotate bullet sprite to face direction
if (bullet.children && bullet.children.length > 0) {
bullet.children[0].rotation = Math.atan2(bullet.vy, bullet.vx);
}
if (typeof bossBullets !== "undefined") {
bossBullets.push(bullet);
game.addChild(bullet);
}
self.fireCooldown = self.fireInterval;
}
}
// Animate boss (pulse)
var t = LK.ticks || Date.now();
var scale = 1 + 0.18 * Math.sin((t + self.x + self.y) * 0.06);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// BossBullet: fired by boss, moves toward wizard, 2 health, can be destroyed by fireballs
var BossBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('bossbullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.health = 2;
self.lastX = 0;
self.lastY = 0;
self.lastWasIntersecting = false;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
// Remove if off screen
if (self.x < -120 || self.x > 2048 + 120 || self.y < -120 || self.y > 2732 + 120) {
self.destroy();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Game update: update wizard, fireballs, enemies, handle collisions, and scoring
// Coin class: represents coins dropped by enemies when destroyed
var Coin = Container.expand(function () {
var self = Container.call(this);
// Create a gold coin visual (using a yellow circle)
var coinSprite = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
// Coin movement properties
self.vx = 0;
self.vy = 0;
self.gravity = 0;
self.collected = false;
// Track last position for event triggers
self.lastX = 0;
self.lastY = 0;
self.lastWasIntersecting = false;
self.update = function () {
// No gravity or bounce logic needed
// Remove coin if it leaves the screen horizontally
if (self.x < -50 || self.x > 2048 + 50) {
self.destroy();
}
// Pulsate slightly for visual effect
var t = LK.ticks || Date.now();
var scale = 1 + 0.1 * Math.sin(t * 0.1);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Enemy class: represents an enemy that moves toward the wizard
var Enemy = Container.expand(function () {
var self = Container.call(this);
// Attach a unique enemy asset (pixel blue monster)
var enemySprite = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Enemy speed and direction
self.vx = 0;
self.vy = 0;
// Track last position for event triggers
self.lastX = 0;
self.lastY = 0;
self.health = 1; // Enemy1: 1 hit to kill
self.type = 1;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
// Pulsate scale for movement effect
var t = LK.ticks || Date.now(); // fallback for safety
var scale = 1 + 0.15 * Math.sin((t + self.x + self.y) * 0.05);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
// Remove enemy if it leaves the screen
if (self.x < -100 || self.x > 2048 + 100 || self.y < -100 || self.y > 2732 + 100) {
self.destroy();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Enemy2: needs 2 fireballs to die
var Enemy2 = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.lastX = 0;
self.lastY = 0;
self.health = 2;
self.type = 2;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
var t = LK.ticks || Date.now();
var scale = 1 + 0.18 * Math.sin((t + self.x + self.y) * 0.06);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
if (self.x < -100 || self.x > 2048 + 100 || self.y < -100 || self.y > 2732 + 100) {
self.destroy();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Enemy3: needs 3 fireballs to die
var Enemy3 = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy3', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.lastX = 0;
self.lastY = 0;
self.health = 3;
self.type = 3;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
var t = LK.ticks || Date.now();
var scale = 1 + 0.22 * Math.sin((t + self.x + self.y) * 0.07);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
if (self.x < -100 || self.x > 2048 + 100 || self.y < -100 || self.y > 2732 + 100) {
self.destroy();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Enemy4: needs 4 fireballs to die
var Enemy4 = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy4', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.lastX = 0;
self.lastY = 0;
self.health = 4;
self.type = 4;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
var t = LK.ticks || Date.now();
var scale = 1 + 0.26 * Math.sin((t + self.x + self.y) * 0.08);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
if (self.x < -100 || self.x > 2048 + 100 || self.y < -100 || self.y > 2732 + 100) {
self.destroy();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Enemy5: Goblin, appears at score 5, moves freely but never enters the center area
var Enemy5 = Container.expand(function () {
var self = Container.call(this);
var goblinSprite = self.attachAsset('goblin', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 20;
self.type = 5;
self.lastX = 0;
self.lastY = 0;
// --- Free movement variables ---
self.vx = (Math.random() - 0.5) * 13 + (Math.random() < 0.5 ? -7 : 7); // random speed, avoid 0
self.vy = (Math.random() - 0.5) * 13 + (Math.random() < 0.5 ? -7 : 7);
self.changeDirCooldown = 0;
// Start at a random edge
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// top
self.x = Math.random() * 2048;
self.y = -90;
} else if (edge === 1) {
// bottom
self.x = Math.random() * 2048;
self.y = 2732 + 90;
} else if (edge === 2) {
// left
self.x = -90;
self.y = Math.random() * 2732;
} else {
// right
self.x = 2048 + 90;
self.y = Math.random() * 2732;
}
// --- Center area to avoid ---
var centerAreaX = 2048 / 2;
var centerAreaY = 2732 / 2;
var centerRadius = 420; // px, forbidden zone
// --- Timed self-destruction after 15 seconds ---
self.spawnTick = typeof LK.ticks !== "undefined" ? LK.ticks : 0;
self.smokeShown = false;
self.update = function () {
// Move freely
self.x += self.vx;
self.y += self.vy;
// Bounce off screen edges
if (self.x < 0 + 90 && self.vx < 0) self.vx = Math.abs(self.vx);
if (self.x > 2048 - 90 && self.vx > 0) self.vx = -Math.abs(self.vx);
if (self.y < 0 + 90 && self.vy < 0) self.vy = Math.abs(self.vy);
if (self.y > 2732 - 90 && self.vy > 0) self.vy = -Math.abs(self.vy);
// Never enter the center forbidden area
var dx = self.x - centerAreaX;
var dy = self.y - centerAreaY;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < centerRadius) {
// Push goblin out of the center area
var angle = Math.atan2(dy, dx);
self.x = centerAreaX + Math.cos(angle) * (centerRadius + 2);
self.y = centerAreaY + Math.sin(angle) * (centerRadius + 2);
// Bounce away
self.vx = Math.cos(angle) * (8 + Math.random() * 5);
self.vy = Math.sin(angle) * (8 + Math.random() * 5);
}
// Occasionally change direction randomly
self.changeDirCooldown--;
if (self.changeDirCooldown <= 0) {
var angle = Math.atan2(self.vy, self.vx) + (Math.random() - 0.5) * 0.7;
var speed = 7 + Math.random() * 6;
self.vx = Math.cos(angle) * speed;
self.vy = Math.sin(angle) * speed;
self.changeDirCooldown = 60 + Math.floor(Math.random() * 60); // 1-2 seconds
}
// Animate goblin (pulse)
var t = LK.ticks || Date.now();
var scale = 1 + 0.18 * Math.sin((t + self.x + self.y) * 0.09);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
// --- Timed self-destruction after 15 seconds ---
if (!self.smokeShown && typeof LK.ticks !== "undefined" && LK.ticks - self.spawnTick >= 900) {
// 15s at 60fps
// Show smoke at goblin's position
var smoke = LK.getAsset('Smoke', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
scaleX: 1.0,
scaleY: 1.0,
alpha: 1
});
game.addChild(smoke);
// Animate smoke: fade out and scale up over 0.7s (42 frames)
(function (smokeObj) {
var frames = 42,
frame = 0;
smokeObj.update = function () {
frame++;
smokeObj.scale.x = 1.0 + 0.5 * (frame / frames);
smokeObj.scale.y = 1.0 + 0.5 * (frame / frames);
smokeObj.alpha = 1 - frame / frames;
if (frame >= frames) {
smokeObj.destroy();
}
};
})(smoke);
self.smokeShown = true;
// Mark goblin as permanently killed (so it never respawns)
enemy5EverKilled = true;
// Remove goblin from game
self.destroy();
// Remove from enemies array in game.update (handled by main loop)
// enemy5 reference will be cleared in main loop on destroy
if (typeof enemy5 !== "undefined" && enemy5 === self) {
enemy5 = null;
enemy5Spawned = false;
}
return;
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Enemy6: needs 6 fireballs to die, drops healthpack on death
var Enemy6 = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy6', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.lastX = 0;
self.lastY = 0;
self.health = 2;
self.type = 6;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
var t = LK.ticks || Date.now();
var scale = 1 + 0.18 * Math.sin((t + self.x + self.y) * 0.09);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
if (self.x < -100 || self.x > 2048 + 100 || self.y < -100 || self.y > 2732 + 100) {
self.destroy();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Explosion class: shows a quick explosion effect at (x, y)
var Explosion = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
// Initial scale and alpha
sprite.scale.x = 0.5;
sprite.scale.y = 0.5;
sprite.alpha = 1;
// Animate explosion: grow and fade out
var duration = 18; // frames (~0.3s)
var frame = 0;
self.update = function () {
frame++;
// Grow and fade
var progress = frame / duration;
sprite.scale.x = 0.5 + 1.2 * progress;
sprite.scale.y = 0.5 + 1.2 * progress;
sprite.alpha = 1 - progress;
if (frame >= duration) {
self.destroy();
}
};
return self;
});
// FireCircle: orbiting fire damage area after boss reward
var FireCircle = Container.expand(function () {
var self = Container.call(this);
// Attach firecircle asset
var sprite = self.attachAsset('firecircle', {
anchorX: 0.5,
anchorY: 0.5,
rotation: -Math.PI / 2 // 90 derece yukarıya döndür (saat yönünün tersine)
});
// Orbit parameters
self.orbitRadius = 850;
self.orbitAngle = 0;
self.orbitSpeed = 0.009; // Yarı yarıya yavaşlatıldı (half speed)
self.centerX = 2048 / 2;
self.centerY = 2732 / 2;
self.damage = 2; // FireCircle now deals 2 damage per hit
self.enemiesHit = {}; // Track which enemies have been hit this frame
self.update = function () {
// Orbit around wizard (or last known wizard position)
if (typeof wizard !== "undefined" && wizard && !wizard.destroyed) {
self.centerX = wizard.x;
self.centerY = wizard.y;
}
self.orbitAngle += self.orbitSpeed;
self.x = self.centerX + Math.cos(self.orbitAngle) * self.orbitRadius;
self.y = self.centerY + Math.sin(self.orbitAngle) * self.orbitRadius;
// Animate (pulse + spin)
var t = LK.ticks || Date.now();
// Make pulse 3 times slower: divide frequency by 3 (multiply period by 3)
var scale = 1 + 0.08 * Math.sin((t + self.x + self.y) * (0.09 / 3));
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
// Add 360-degree spin effect (rotate asset as it orbits)
self.children[0].rotation = self.orbitAngle;
}
// Damage enemies that intersect, every frame (permanent, not once per activation)
if (typeof enemies !== "undefined") {
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (!enemy || enemy.destroyed) continue;
// Assign a unique id if not present
if (typeof enemy._fireCircleId === "undefined") {
enemy._fireCircleId = Math.random().toString(36).substr(2, 9) + Date.now();
}
// Only damage once per frame
if (!self.enemiesHit[enemy._fireCircleId] && self.intersects(enemy)) {
// If enemy has 2 or 1 health, instantly kill
if (enemy.health === 2 || enemy.health === 1) {
enemy.health = 0;
} else {
enemy.health -= self.damage;
}
self.enemiesHit[enemy._fireCircleId] = true;
// Flash enemy for feedback
if (enemy.children && enemy.children.length > 0) {
LK.effects.flashObject(enemy.children[0], 0xff6600, 200);
}
// If enemy dies, handle in main game.update
}
}
}
// Reset hit tracking for next frame
self.enemiesHit = {};
};
return self;
});
// Fireball class: represents a fireball shot by the wizard
var Fireball = Container.expand(function () {
var self = Container.call(this);
// Attach fireball asset (pixel fireball)
var fireballSprite = self.attachAsset('fireball', {
anchorX: 0.5,
anchorY: 0.5
});
// Fireball speed and direction
self.vx = 0;
self.vy = 0;
// Track last position for event triggers
self.lastX = 0;
self.lastY = 0;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
// Remove fireball if it leaves the screen
if (self.x < -100 || self.x > 2048 + 100 || self.y < -100 || self.y > 2732 + 100) {
self.destroy();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Healthpack class: dropped by Enemy6, restores 2 wizard health when collected
var Healthpack = Container.expand(function () {
var self = Container.call(this);
// Attach healthpack asset
var sprite = self.attachAsset('healthpack', {
anchorX: 0.5,
anchorY: 0.5
});
// Track last position for event triggers
self.lastX = 0;
self.lastY = 0;
self.lastWasIntersecting = false;
self.update = function () {
// Move toward wizard like coin
if (typeof wizard !== "undefined" && wizard) {
var dx = wizard.x - self.x;
var dy = wizard.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var speed = 22;
if (dist > 1) {
self.x += dx / dist * Math.min(speed, dist);
self.y += dy / dist * Math.min(speed, dist);
} else {
self.x = wizard.x;
self.y = wizard.y;
}
}
// Pulsate for effect
var t = LK.ticks || Date.now();
var scale = 1 + 0.12 * Math.sin((t + self.x + self.y) * 0.13);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// InfernoBullet: InfernoMonster'ın ateşlediği mermi
var InfernoBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('infernobullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.lastX = 0;
self.lastY = 0;
self.lastWasIntersecting = false;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
// Ekran dışına çıkarsa yok et
if (self.x < -120 || self.x > 2048 + 120 || self.y < -120 || self.y > 2732 + 120) {
self.destroy();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// InfernoMonster: orbits at 850 radius, uses 'inferno' asset, appears at score 5
var InfernoMonster = Container.expand(function () {
var self = Container.call(this);
// Attach inferno asset
var infernoSprite = self.attachAsset('inferno', {
anchorX: 0.5,
anchorY: 0.5
});
// Orbit parameters
self.orbitRadius = 850;
self.orbitAngle = 0;
self.orbitSpeed = (0.012 + Math.random() * 0.008) * 0.5;
self.orbitDirection = Math.random() < 0.5 ? 1 : -1;
self.lastX = 0;
self.lastY = 0;
self.health = 50; // 50 health as required
self.type = 99; // Unique type for inferno
// Add: timer for direction change (başlangıç 3 saniye, sonra 4-5-6-7)
self._reverseDirTimer = 180; // 3 seconds at 60fps
self._reverseDirStage = 0; // 0:3s, 1:4s, 2:5s, 3:6s, 4+:7s
// --- InfernoMonster Fire Logic ---
self.fireCooldown = 0;
self.fireInterval = 90 + Math.floor(Math.random() * 60); // Ateş aralığı: 1.5-2.5 saniye (90-150 frame)
// --- InfernoMonster ateşlerini globalde tut ---
if (typeof infernoBullets === "undefined") infernoBullets = [];
self.update = function () {
// Orbit around wizard
if (typeof wizard !== "undefined" && wizard && !wizard.destroyed) {
// Reverse direction every N seconds (3,4,5,6,7)
self._reverseDirTimer--;
if (self._reverseDirTimer <= 0) {
self.orbitDirection *= -1;
// Timer sequence: 3s, 4s, 5s, 6s, 7s, 7s, 7s...
if (typeof self._reverseDirStage === "undefined") self._reverseDirStage = 0;
self._reverseDirStage++;
var nextTimes = [240, 300, 360, 420, 420]; // 4s, 5s, 6s, 7s, 7s...
var idx = self._reverseDirStage - 1;
if (idx < nextTimes.length) {
self._reverseDirTimer = nextTimes[idx];
} else {
self._reverseDirTimer = 420; // Stay at 7s
}
}
self.orbitAngle += self.orbitSpeed * self.orbitDirection;
self.x = wizard.x + Math.cos(self.orbitAngle) * self.orbitRadius;
self.y = wizard.y + Math.sin(self.orbitAngle) * self.orbitRadius;
// --- InfernoMonster ateş etme mantığı ---
self.fireCooldown--;
if (self.fireCooldown <= 0) {
// Wizard'a doğru bir mermi fırlat
var bullet = new InfernoBullet();
bullet.x = self.x;
bullet.y = self.y;
// Wizard'a doğru yön
var dx = wizard.x - self.x;
var dy = wizard.y - self.y;
var len = Math.sqrt(dx * dx + dy * dy);
var speed = 10; // Inferno mermisi hızı
if (len > 0) {
bullet.vx = dx / len * speed;
bullet.vy = dy / len * speed;
}
// Sprite'ı yönüne döndür
if (bullet.children && bullet.children.length > 0) {
bullet.children[0].rotation = Math.atan2(bullet.vy, bullet.vx);
}
infernoBullets.push(bullet);
game.addChild(bullet);
// Sonraki ateş aralığı rastgele belirle
self.fireCooldown = self.fireInterval + Math.floor(Math.random() * 60);
}
}
// Animate (pulse)
var t = LK.ticks || Date.now();
// Reduce pulse amplitude to one third (0.13 / 3)
var scale = 1 + 0.13 / 3 * Math.sin((t + self.x + self.y) * 0.07);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Wizard class: represents the player character
var Wizard = Container.expand(function () {
var self = Container.call(this);
// Attach wizard asset (blue hat, pixel style)
var wizardSprite = self.attachAsset('wizard', {
anchorX: 0.5,
anchorY: 0.5
});
// Track last position for event triggers
self.lastX = 0;
self.lastY = 0;
// Update method (called every tick)
self.update = function () {
// No movement logic here; wizard is moved by mouse/touch
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Explosion asset for enemy death effect
;
// Add background image to the game scene
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
game.addChild(background);
// --- Game Code for Pixel Wizard: Blue Hat Adventure ---
// Create wizard instance and add to game
var wizard = new Wizard();
game.addChild(wizard);
// Center wizard on screen at start
wizard.x = 2048 / 2;
wizard.y = 2732 / 2;
// --- START & UPGRADE BUTTON UI LOGIC ---
var gameStarted = false;
// UI container for start and upgrade buttons
var startUiContainer = new Container();
// Create start button asset (simple box with text)
var startButtonWidth = 420;
var startButtonHeight = 140;
var startButton = new Container();
var startButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: startButtonWidth,
height: startButtonHeight
});
startButtonBg.alpha = 0.92;
startButton.addChild(startButtonBg);
var startButtonText = new Text2("START GAME", {
size: 220,
fill: "#fff"
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 0;
startButtonText.y = -120;
startButton.addChild(startButtonText);
// Create upgrade button below start button
var upgradeButtonWidth = 420;
var upgradeButtonHeight = 110;
var upgradeButton = new Container();
var upgradeButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: upgradeButtonWidth,
height: upgradeButtonHeight
});
upgradeButtonBg.alpha = 0.92;
upgradeButton.addChild(upgradeButtonBg);
var upgradeButtonText = new Text2("UPGRADE", {
size: 110,
// Half of START GAME (220)
fill: "#fff"
});
upgradeButtonText.anchor.set(0.5, 0.5);
upgradeButtonText.x = 0;
upgradeButtonText.y = 0;
upgradeButton.addChild(upgradeButtonText);
// Position start, upgrade, and shop buttons in the UI container
startButton.x = 0;
startButton.y = -400; //{36} // Moved much higher
upgradeButton.x = 0;
upgradeButton.y = startButton.y + startButtonHeight / 2 + 55 + upgradeButtonHeight / 2; //{38} // Increased spacing by 15px
// --- SHOP BUTTON UI LOGIC ---
var shopButtonWidth = 420;
var shopButtonHeight = 110;
var shopButton = new Container();
var shopButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: shopButtonWidth,
height: shopButtonHeight
});
shopButtonBg.alpha = 0.92;
shopButton.addChild(shopButtonBg);
var shopButtonText = new Text2("SHOP", {
size: 110,
fill: "#fff"
});
shopButtonText.anchor.set(0.5, 0.5);
shopButtonText.x = 0;
shopButtonText.y = 0;
shopButton.addChild(shopButtonText);
// Position shop button below upgrade button
shopButton.x = 0;
shopButton.y = upgradeButton.y + upgradeButtonHeight / 2 + 40 + shopButtonHeight / 2;
// Add all three buttons to the UI container
startUiContainer.addChild(startButton);
startUiContainer.addChild(upgradeButton);
startUiContainer.addChild(shopButton);
// --- RESET BUTTON UI LOGIC ---
var resetButtonWidth = 420;
var resetButtonHeight = 110;
var resetButton = new Container();
var resetButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: resetButtonWidth,
height: resetButtonHeight
});
resetButtonBg.alpha = 0.92;
resetButton.addChild(resetButtonBg);
var resetButtonText = new Text2("RESET", {
size: 110,
fill: "#fff"
});
resetButtonText.anchor.set(0.5, 0.5);
resetButtonText.x = 0;
resetButtonText.y = 0;
resetButton.addChild(resetButtonText);
// Position reset button below shop button
resetButton.x = 0;
resetButton.y = shopButton.y + shopButtonHeight / 2 + 40 + resetButtonHeight / 2;
startUiContainer.addChild(resetButton);
// --- RESET POPUP LOGIC ---
var resetPopup = null;
function showResetPopup() {
if (resetPopup && !resetPopup.destroyed) return;
resetPopup = new Container();
// Popup background
var popupW = 900;
var popupH = 600;
var popupBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: popupW,
height: popupH
});
popupBg.alpha = 0.98;
resetPopup.addChild(popupBg);
// Popup text
var popupText = new Text2("Are you sure you want to reset all progress?", {
size: 80,
fill: "#fff"
});
popupText.anchor.set(0.5, 0.5);
popupText.x = 0;
popupText.y = -100;
resetPopup.addChild(popupText);
// Yes button
var yesBtn = new Container();
var yesBtnBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: 320,
height: 120
});
yesBtnBg.alpha = 0.95;
yesBtn.addChild(yesBtnBg);
var yesBtnText = new Text2("YES", {
size: 90,
fill: 0x2ECC40
});
yesBtnText.anchor.set(0.5, 0.5);
yesBtnText.x = 0;
yesBtnText.y = 0;
yesBtn.addChild(yesBtnText);
yesBtn.x = -180;
yesBtn.y = 120;
// No button
var noBtn = new Container();
var noBtnBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: 320,
height: 120
});
noBtnBg.alpha = 0.95;
noBtn.addChild(noBtnBg);
var noBtnText = new Text2("NO", {
size: 90,
fill: 0xE74C3C
});
noBtnText.anchor.set(0.5, 0.5);
noBtnText.x = 0;
noBtnText.y = 0;
noBtn.addChild(noBtnText);
noBtn.x = 180;
noBtn.y = 120;
// Add buttons to popup
resetPopup.addChild(yesBtn);
resetPopup.addChild(noBtn);
// Center popup
resetPopup.x = 2048 / 2;
resetPopup.y = 2732 / 2;
game.addChild(resetPopup);
// Yes: reset all progress and reload game state
yesBtn.down = function () {
// Clear all persistent storage
for (var key in storage) {
if (storage.hasOwnProperty(key)) {
delete storage[key];
}
}
// Remove all weapon upgrades from storage
for (var key in storage) {
if (storage.hasOwnProperty(key) && key.indexOf("weaponUpgrades_") === 0) {
delete storage[key];
}
}
// Also clear all in-memory weapon upgrades for all weapons and set to first stage
if (typeof weaponUpgradeDefaults !== "undefined") {
var weaponAssets = ["wizard", "submachine", "MP5", "shuriken", "shotgun", "minigun", "Lasergun"];
for (var i = 0; i < weaponAssets.length; i++) {
var key = getWeaponUpgradeKey(weaponAssets[i]);
// Set all upgrades to first stage (level 0, cost default, health 5, etc)
var upgrades = {};
for (var k in weaponUpgradeDefaults) upgrades[k] = weaponUpgradeDefaults[k];
storage[key] = upgrades;
}
}
// Optionally, force reload by reloading the page, but here we just reset all variables and UI
// Remove popup
if (resetPopup && !resetPopup.destroyed) {
resetPopup.destroy();
resetPopup = null;
}
// Remove start UI if present
if (startUiContainer && !startUiContainer.destroyed) {
startUiContainer.destroy();
}
// Remove all enemies, fireballs, coins
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] && !enemies[i].destroyed) enemies[i].destroy();
enemies.splice(i, 1);
}
for (var i = fireballs.length - 1; i >= 0; i--) {
if (fireballs[i] && !fireballs[i].destroyed) fireballs[i].destroy();
fireballs.splice(i, 1);
}
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i] && !coins[i].destroyed) coins[i].destroy();
coins.splice(i, 1);
}
// Reset all progress variables
score = 0;
coinCount = 0;
storage.coinCount = 0;
speedUpgradeLevel = 0;
speedUpgradeCost = 50;
// (damageUpgradeLevel and damageUpgradeCost removed)
healthUpgradeLevel = 0;
healthUpgradeCost = 25;
maxWizardHealth = 5;
wizardHealth = 5;
fireballDamage = 1;
// Reset shop purchases and upgrades
shopPurchases = {};
storage.shopPurchases = {};
wizardAsset = "wizard";
storage.wizardAsset = "wizard"; // persist default asset
storage.speedUpgradeLevel = 0;
storage.speedUpgradeCost = 50;
// (storage.damageUpgradeLevel and storage.damageUpgradeCost removed)
storage.healthUpgradeLevel = 0;
storage.healthUpgradeCost = 25;
storage.maxWizardHealth = 5;
storage.wizardHealth = 5;
storage.fireballDamage = 1;
// Update UI
scoreText.setText("Score: 0");
coinText.setText("" + coinCount);
// Reset wizard asset to default and ensure only one asset exists
// Remove all children
for (var i = wizard.children.length - 1; i >= 0; i--) {
wizard.removeChild(wizard.children[i]);
}
// Attach new wizard asset as the only sprite
var newSprite = wizard.attachAsset('wizard', {
anchorX: 0.5,
anchorY: 0.5
});
// Reset health bar (no-op)
updateHealthBar();
// Center wizard
wizard.x = 2048 / 2;
wizard.y = 2732 / 2;
if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = Math.PI;
}
// Recreate start UI
startUiContainer = new Container();
startButton = new Container();
startButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: startButtonWidth,
height: startButtonHeight
});
startButtonBg.alpha = 0.92;
startButton.addChild(startButtonBg);
startButtonText = new Text2("START GAME", {
size: 220,
fill: "#fff"
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 0;
startButtonText.y = -120;
startButton.addChild(startButtonText);
upgradeButton = new Container();
upgradeButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: upgradeButtonWidth,
height: upgradeButtonHeight
});
upgradeButtonBg.alpha = 0.92;
upgradeButton.addChild(upgradeButtonBg);
upgradeButtonText = new Text2("UPGRADE", {
size: 110,
fill: "#fff"
});
upgradeButtonText.anchor.set(0.5, 0.5);
upgradeButtonText.x = 0;
upgradeButtonText.y = 0;
upgradeButton.addChild(upgradeButtonText);
shopButton = new Container();
shopButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: shopButtonWidth,
height: shopButtonHeight
});
shopButtonBg.alpha = 0.92;
shopButton.addChild(shopButtonBg);
shopButtonText = new Text2("SHOP", {
size: 110,
fill: "#fff"
});
shopButtonText.anchor.set(0.5, 0.5);
shopButtonText.x = 0;
shopButtonText.y = 0;
shopButton.addChild(shopButtonText);
// Re-add reset button
resetButton = new Container();
resetButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: resetButtonWidth,
height: resetButtonHeight
});
resetButtonBg.alpha = 0.92;
resetButton.addChild(resetButtonBg);
resetButtonText = new Text2("RESET", {
size: 110,
fill: "#fff"
});
resetButtonText.anchor.set(0.5, 0.5);
resetButtonText.x = 0;
resetButtonText.y = 0;
resetButton.addChild(resetButtonText);
// Position all buttons
startButton.x = 0;
startButton.y = -400;
upgradeButton.x = 0;
upgradeButton.y = startButton.y + startButtonHeight / 2 + 55 + upgradeButtonHeight / 2;
shopButton.x = 0;
shopButton.y = upgradeButton.y + upgradeButtonHeight / 2 + 40 + shopButtonHeight / 2;
resetButton.x = 0;
resetButton.y = shopButton.y + shopButtonHeight / 2 + 40 + resetButtonHeight / 2;
// Add to container
startUiContainer.addChild(startButton);
startUiContainer.addChild(upgradeButton);
startUiContainer.addChild(shopButton);
startUiContainer.addChild(resetButton);
startUiContainer.x = 2048 / 2;
startUiContainer.y = 2732 / 2;
game.addChild(startUiContainer);
// Re-attach handlers
upgradeButton.down = function (x, y, obj) {
if (upgradeWindow && !upgradeWindow.destroyed) return;
showUpgradeWindow();
};
shopButton.down = function (x, y, obj) {
if (shopWindow && !shopWindow.destroyed) return;
showShopWindow();
};
startButton.down = function (x, y, obj) {
if (gameStarted) return;
gameStarted = true;
if (startUiContainer && !startUiContainer.destroyed) {
startUiContainer.destroy();
}
// Remove all enemies, fireballs, coins
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] && !enemies[i].destroyed) enemies[i].destroy();
enemies.splice(i, 1);
}
for (var i = fireballs.length - 1; i >= 0; i--) {
if (fireballs[i] && !fireballs[i].destroyed) fireballs[i].destroy();
fireballs.splice(i, 1);
}
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i] && !coins[i].destroyed) coins[i].destroy();
coins.splice(i, 1);
}
score = 0;
scoreText.setText("Score: 0");
coinText.setText("" + coinCount);
wizard.x = 2048 / 2;
wizard.y = 2732 / 2;
if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = Math.PI;
}
// --- Reset magazine system ---
// If minigun is selected, always set to 350 ammo at new game start
if (wizardAsset === "minigun") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("minigun");
wizardAmmo = wizardMaxAmmo;
tapFireCooldown = 500;
} else if (wizardAsset === "submachine") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("submachine");
wizardAmmo = wizardMaxAmmo;
tapFireCooldown = 100;
wizardReloadTime = 500;
} else if (wizardAsset === "Lasergun") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("Lasergun");
wizardAmmo = wizardMaxAmmo;
tapFireCooldown = 300;
} else if (wizardAsset === "shotgun") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("shotgun");
wizardAmmo = wizardMaxAmmo;
tapFireCooldown = 200;
} else {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo(wizardAsset);
wizardAmmo = wizardMaxAmmo;
}
wizardReloading = false;
if (wizardReloadTimeout) {
LK.clearTimeout(wizardReloadTimeout);
wizardReloadTimeout = null;
}
// Health bar is now fixed in the GUI, no need to reposition here
wizardHealth = maxWizardHealth;
updateHealthBar();
game.move = originalGameMove;
game.down = originalGameDown;
game.up = originalGameUp;
};
resetButton.down = function (x, y, obj) {
showResetPopup();
};
// Block input until start is pressed
blockGameInput();
gameStarted = false;
};
// No: close popup
noBtn.down = function () {
if (resetPopup && !resetPopup.destroyed) {
resetPopup.destroy();
resetPopup = null;
}
};
}
// Reset button interaction
resetButton.down = function (x, y, obj) {
showResetPopup();
};
// Position the UI container: horizontally centered, vertically centered
startUiContainer.x = 2048 / 2;
startUiContainer.y = 2732 / 2;
// Add to game
game.addChild(startUiContainer);
// Block input until start is pressed
function blockGameInput() {
game.move = function () {};
game.down = function () {};
game.up = function () {};
isFiring = false;
if (fireInterval) {
LK.clearInterval(fireInterval);
fireInterval = null;
}
}
blockGameInput();
// --- UPGRADE WINDOW LOGIC ---
var upgradeWindow = null;
function showUpgradeWindow() {
if (upgradeWindow && !upgradeWindow.destroyed) return;
upgradeWindow = new Container();
// Window background (main)
var winW = 1200 + 500 - 250,
// Decrease width by 250px
winH = 1100 + 500 + 500; // Increase height by 500px again, width unchanged
var winBg = LK.getAsset('upgradescreen', {
anchorX: 0.5,
anchorY: 0.5,
width: winW,
height: winH
});
winBg.alpha = 0.97;
upgradeWindow.addChild(winBg);
// Title removed: no upgrade window title text
// Button helper with icon
function makeUpgradeBtn(label, y, color, iconAsset, iconColor) {
var btn = new Container();
// No background for speed, power, health, or back buttons
// Icon asset (left side of button)
var icon = null;
if (iconAsset) {
// If this is the health asset, increase its size by 50px
if (iconAsset === 'health') {
icon = LK.getAsset(iconAsset, {
anchorX: 0.5,
anchorY: 0.5,
x: -360,
// 100px more to the left
y: 0,
width: 150,
height: 150
});
} else {
icon = LK.getAsset(iconAsset, {
anchorX: 0.5,
anchorY: 0.5,
x: -360,
// 100px more to the left
y: 0,
scaleX: 1.2,
scaleY: 1.2
});
}
// Remove any filter/overlay in front of the icon (if any exist)
if (icon.filters && icon.filters.length > 0) {
icon.filters = [];
}
btn.addChild(icon);
}
var btnText = new Text2(label, {
size: 100,
fill: "#fff"
});
btnText.anchor.set(0.5, 0.5);
btnText.x = 60;
btnText.y = 0;
btn.addChild(btnText);
btn.x = 0;
btn.y = y;
return btn;
}
// Hız, Health, Ammo, Geri buttons with icons and unique colors
var btnSpacing = 220; // Slightly reduced to fit 3 upgrades
var btns = [];
// Use fast, background assets for each upgrade (background as health icon)
var btnLabels = [{
label: "SPEED",
iconAsset: 'fast'
}, {
label: "HEALTH",
iconAsset: 'health'
}, {
label: "AMMO",
iconAsset: 'ammoupgrade'
}];
for (var i = 0; i < btnLabels.length; i++) {
var btn = makeUpgradeBtn(btnLabels[i].label, -180 + i * btnSpacing, btnLabels[i].color, btnLabels[i].iconAsset, btnLabels[i].iconColor);
upgradeWindow.addChild(btn);
btns.push(btn);
}
// --- SPEED UPGRADE LOGIC (5 seviye, her silah için ayrı ayrı, atış ve mermi hızı) ---
if (typeof speedUpgradeBtnText === "undefined") speedUpgradeBtnText = null;
var upgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = upgrades.speedUpgradeLevel;
// Set cost to 100 * (level+1) for each upgrade, max 5 upgrades
speedUpgradeCost = speedUpgradeLevel < 5 ? 100 * (speedUpgradeLevel + 1) : "-";
upgrades.speedUpgradeCost = speedUpgradeCost;
btns[0].down = function () {
var upgrades = getWeaponUpgrades(wizardAsset);
// Always set cost to 100 * (level+1) for each upgrade, max 5 upgrades
upgrades.speedUpgradeCost = upgrades.speedUpgradeLevel < 5 ? 100 * (upgrades.speedUpgradeLevel + 1) : "-";
if (coinCount >= upgrades.speedUpgradeCost && upgrades.speedUpgradeLevel < 5) {
coinCount -= upgrades.speedUpgradeCost;
coinText.setText("" + coinCount);
upgrades.speedUpgradeLevel += 1;
// Her seviye için maliyet: 100, 200, 300, 400, 500 coin
upgrades.speedUpgradeCost = upgrades.speedUpgradeLevel < 5 ? 100 * (upgrades.speedUpgradeLevel + 1) : "-";
saveWeaponUpgrades(wizardAsset, upgrades);
speedUpgradeLevel = upgrades.speedUpgradeLevel;
speedUpgradeCost = upgrades.speedUpgradeCost;
// Atış hızı: tapFireCooldown'u azalt (daha hızlı ateş)
// Mermi hızı: shootFireballAt fonksiyonunda kullanılacak
tapFireCooldown = Math.max(80, 850 - speedUpgradeLevel * 140);
// Buton label güncelle
if (speedUpgradeBtnText) {
if (btns[0].coinIcon && btns[0].coinIcon.parent) {
btns[0].coinIcon.parent.removeChild(btns[0].coinIcon);
btns[0].coinIcon = null;
}
if (speedUpgradeLevel < 5) {
speedUpgradeBtnText.setText("SPEED | " + speedUpgradeCost);
var coinAsset = LK.getAsset('coin', {
anchorX: 0.0,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
coinAsset.x = speedUpgradeBtnText.x + speedUpgradeBtnText.width / 2 + coinAsset.width * 0.6;
coinAsset.y = speedUpgradeBtnText.y;
btns[0].addChild(coinAsset);
btns[0].coinIcon = coinAsset;
} else {
speedUpgradeBtnText.setText("SPEED | MAX");
}
}
LK.effects.flashObject(btns[0], 0x2ecc40, 400);
} else if (speedUpgradeLevel >= 5) {
LK.effects.flashObject(btns[0], 0x888888, 400);
} else {
LK.effects.flashObject(coinText, 0xe74c3c, 600);
}
};
// Always set speedUpgradeBtnText to the SPEED button label when opening the window
speedUpgradeBtnText = btns[0].children[1];
if (speedUpgradeBtnText) {
if (btns[0].coinIcon && btns[0].coinIcon.parent) {
btns[0].coinIcon.parent.removeChild(btns[0].coinIcon);
btns[0].coinIcon = null;
}
if (speedUpgradeLevel < 5) {
speedUpgradeBtnText.setText("SPEED | " + speedUpgradeCost);
var coinAsset = LK.getAsset('coin', {
anchorX: 0.0,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
coinAsset.x = speedUpgradeBtnText.x + speedUpgradeBtnText.width / 2 + coinAsset.width * 0.6;
coinAsset.y = speedUpgradeBtnText.y;
btns[0].addChild(coinAsset);
btns[0].coinIcon = coinAsset;
} else {
speedUpgradeBtnText.setText("SPEED | MAX");
}
}
// HEALTH upgrade logic: cost increases after each purchase, +2 max health, +2 current health, max 3 upgrades: 25, 50, 75 coin
healthUpgradeLevel = upgrades.healthUpgradeLevel;
healthUpgradeCost = upgrades.healthUpgradeLevel < 3 ? 25 * (upgrades.healthUpgradeLevel + 1) : "-";
upgrades.healthUpgradeCost = healthUpgradeCost;
maxWizardHealth = upgrades.maxWizardHealth;
wizardHealth = upgrades.wizardHealth;
btns[1].down = function () {
var upgrades = getWeaponUpgrades(wizardAsset);
upgrades.healthUpgradeCost = upgrades.healthUpgradeLevel < 3 ? 25 * (upgrades.healthUpgradeLevel + 1) : "-";
if (coinCount >= upgrades.healthUpgradeCost && upgrades.healthUpgradeLevel < 3) {
coinCount -= upgrades.healthUpgradeCost;
coinText.setText("" + coinCount);
upgrades.maxWizardHealth += 2;
upgrades.wizardHealth += 2;
updateHealthBar();
upgrades.healthUpgradeLevel += 1;
upgrades.healthUpgradeCost = upgrades.healthUpgradeLevel < 3 ? 25 * (upgrades.healthUpgradeLevel + 1) : "-";
saveWeaponUpgrades(wizardAsset, upgrades);
healthUpgradeLevel = upgrades.healthUpgradeLevel;
healthUpgradeCost = upgrades.healthUpgradeCost;
maxWizardHealth = upgrades.maxWizardHealth;
wizardHealth = upgrades.wizardHealth;
// Update button label to show new cost or disable if maxed
if (healthUpgradeBtnText) {
if (btns[1].coinIcon && btns[1].coinIcon.parent) {
btns[1].coinIcon.parent.removeChild(btns[1].coinIcon);
btns[1].coinIcon = null;
}
if (healthUpgradeLevel < 3) {
healthUpgradeBtnText.setText("HEALTH | " + healthUpgradeCost);
var coinAsset = LK.getAsset('coin', {
anchorX: 0.0,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
coinAsset.x = healthUpgradeBtnText.x + healthUpgradeBtnText.width / 2 + coinAsset.width * 0.6;
coinAsset.y = healthUpgradeBtnText.y;
btns[1].addChild(coinAsset);
btns[1].coinIcon = coinAsset;
} else {
healthUpgradeBtnText.setText("HEALTH | MAX");
}
}
LK.effects.flashObject(btns[1], 0x2ecc40, 400);
} else if (healthUpgradeLevel >= 3) {
LK.effects.flashObject(btns[1], 0x888888, 400);
} else {
LK.effects.flashObject(coinText, 0xe74c3c, 600);
}
};
// Always set healthUpgradeBtnText to the HEALTH button label when opening the window
healthUpgradeBtnText = btns[1].children[1];
if (healthUpgradeBtnText) {
if (btns[1].coinIcon && btns[1].coinIcon.parent) {
btns[1].coinIcon.parent.removeChild(btns[1].coinIcon);
btns[1].coinIcon = null;
}
if (healthUpgradeLevel < 3) {
healthUpgradeBtnText.setText("HEALTH | " + healthUpgradeCost);
var coinAsset = LK.getAsset('coin', {
anchorX: 0.0,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
coinAsset.x = healthUpgradeBtnText.x + healthUpgradeBtnText.width / 2 + coinAsset.width * 0.6;
coinAsset.y = healthUpgradeBtnText.y;
btns[1].addChild(coinAsset);
btns[1].coinIcon = coinAsset;
} else {
healthUpgradeBtnText.setText("HEALTH | MAX");
}
}
// --- AMMO UPGRADE LOGIC (max 5 upgrades, +5 max ammo per upgrade, cost 80, 160, 240, 320, 400) ---
if (typeof ammoUpgradeBtnText === "undefined") ammoUpgradeBtnText = null;
if (typeof upgrades.ammoUpgradeLevel === "undefined") upgrades.ammoUpgradeLevel = 0;
if (typeof upgrades.ammoUpgradeCost === "undefined") upgrades.ammoUpgradeCost = 80;
if (typeof upgrades.ammoUpgradeAmount === "undefined") upgrades.ammoUpgradeAmount = 0;
var ammoUpgradeLevel = upgrades.ammoUpgradeLevel;
var ammoUpgradeCost = ammoUpgradeLevel < 5 ? 80 * (ammoUpgradeLevel + 1) : "-";
upgrades.ammoUpgradeCost = ammoUpgradeCost;
btns[2].down = function () {
var upgrades = getWeaponUpgrades(wizardAsset);
if (typeof upgrades.ammoUpgradeLevel === "undefined") upgrades.ammoUpgradeLevel = 0;
if (typeof upgrades.ammoUpgradeCost === "undefined") upgrades.ammoUpgradeCost = 80;
if (typeof upgrades.ammoUpgradeAmount === "undefined") upgrades.ammoUpgradeAmount = 0;
upgrades.ammoUpgradeCost = upgrades.ammoUpgradeLevel < 5 ? 80 * (upgrades.ammoUpgradeLevel + 1) : "-";
if (coinCount >= upgrades.ammoUpgradeCost && upgrades.ammoUpgradeLevel < 5) {
coinCount -= upgrades.ammoUpgradeCost;
coinText.setText("" + coinCount);
upgrades.ammoUpgradeLevel += 1;
upgrades.ammoUpgradeAmount = (upgrades.ammoUpgradeAmount || 0) + 5;
upgrades.ammoUpgradeCost = upgrades.ammoUpgradeLevel < 5 ? 80 * (upgrades.ammoUpgradeLevel + 1) : "-";
saveWeaponUpgrades(wizardAsset, upgrades);
ammoUpgradeLevel = upgrades.ammoUpgradeLevel;
ammoUpgradeCost = upgrades.ammoUpgradeCost;
// Increase wizardMaxAmmo for current weapon
if (typeof wizardMaxAmmo !== "undefined") {
wizardMaxAmmo += 5;
wizardAmmo = wizardMaxAmmo;
}
// Update button label
if (ammoUpgradeBtnText) {
if (btns[2].coinIcon && btns[2].coinIcon.parent) {
btns[2].coinIcon.parent.removeChild(btns[2].coinIcon);
btns[2].coinIcon = null;
}
if (ammoUpgradeLevel < 5) {
ammoUpgradeBtnText.setText("AMMO | " + ammoUpgradeCost);
var coinAsset = LK.getAsset('coin', {
anchorX: 0.0,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
coinAsset.x = ammoUpgradeBtnText.x + ammoUpgradeBtnText.width / 2 + coinAsset.width * 0.6;
coinAsset.y = ammoUpgradeBtnText.y;
btns[2].addChild(coinAsset);
btns[2].coinIcon = coinAsset;
} else {
ammoUpgradeBtnText.setText("AMMO | MAX");
}
}
LK.effects.flashObject(btns[2], 0x2ecc40, 400);
} else if (ammoUpgradeLevel >= 5) {
LK.effects.flashObject(btns[2], 0x888888, 400);
} else {
LK.effects.flashObject(coinText, 0xe74c3c, 600);
}
};
// Always set ammoUpgradeBtnText to the AMMO button label when opening the window
ammoUpgradeBtnText = btns[2].children[1];
if (ammoUpgradeBtnText) {
if (btns[2].coinIcon && btns[2].coinIcon.parent) {
btns[2].coinIcon.parent.removeChild(btns[2].coinIcon);
btns[2].coinIcon = null;
}
if (ammoUpgradeLevel < 5) {
ammoUpgradeBtnText.setText("AMMO | " + ammoUpgradeCost);
var coinAsset = LK.getAsset('coin', {
anchorX: 0.0,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
coinAsset.x = ammoUpgradeBtnText.x + ammoUpgradeBtnText.width / 2 + coinAsset.width * 0.6;
coinAsset.y = ammoUpgradeBtnText.y;
btns[2].addChild(coinAsset);
btns[2].coinIcon = coinAsset;
} else {
ammoUpgradeBtnText.setText("AMMO | MAX");
}
}
// BACK button without coin asset
// Place it 500 units further from AMMO (btnSpacing = 220, so AMMO is at -180 + 3*220 = 480, BACK at 480+400=880)
var geriBtn = makeUpgradeBtn("BACK", 880, undefined, null, undefined);
upgradeWindow.addChild(geriBtn);
// BACK closes window
geriBtn.down = function () {
if (upgradeWindow && !upgradeWindow.destroyed) {
upgradeWindow.destroy();
upgradeWindow = null;
}
};
// Center window
upgradeWindow.x = 2048 / 2;
upgradeWindow.y = 2732 / 2;
game.addChild(upgradeWindow);
}
// Upgrade button interaction
upgradeButton.down = function (x, y, obj) {
if (upgradeWindow && !upgradeWindow.destroyed) return;
showUpgradeWindow();
};
// --- SHOP WINDOW LOGIC ---
var shopWindow = null;
function showShopWindow() {
if (shopWindow && !shopWindow.destroyed) return;
shopWindow = new Container();
// Window background
var winW = 1500;
var winH = 2000;
var winBg = LK.getAsset('shop', {
anchorX: 0.5,
anchorY: 0.5,
width: winW,
height: winH
});
winBg.alpha = 0.97;
shopWindow.addChild(winBg);
// Title text removed
// Weapon shop items
var weaponList = [{
name: "SMG",
asset: "submachine",
price: 1
}, {
name: "Shuriken",
asset: "shuriken",
price: 150
}, {
name: "Shotgun",
asset: "shotgun",
price: 250
}, {
name: "Minigun",
asset: "minigun",
price: 350
}, {
name: "Laser Gun",
asset: "Lasergun",
price: 750
}];
var itemStartY = -350;
var itemSpacing = 220;
var shopItemButtons = [];
var _loop = function _loop() {
weapon = weaponList[i];
itemY = itemStartY + i * itemSpacing;
shopItem = new Container(); // Icon (left)
itemIcon = LK.getAsset(weapon.asset, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0,
x: -420,
y: 0
});
shopItem.addChild(itemIcon);
// "SMG | x" text (centered)
itemText = new Text2(weapon.name + " | " + weapon.price, {
size: 90,
fill: "#fff"
});
itemText.anchor.set(0, 0.5);
itemText.x = -180;
itemText.y = 0;
shopItem.addChild(itemText);
// Coin asset (right of price)
priceCoin = LK.getAsset('coin', {
anchorX: 0.0,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7,
x: itemText.x + itemText.width + 30,
y: itemText.y
});
shopItem.addChild(priceCoin);
// Helper to update purchased/selected state visuals
function updateShopItemState() {
var isPurchased = shopPurchases && shopPurchases[weapon.asset];
var isSelected = wizardAsset === weapon.asset;
// Only update priceCoin and itemText, no labels
if (!!isPurchased && isSelected) {
priceCoin.visible = false;
itemText.setText(weapon.name);
if (typeof itemText.setFill === "function") {
itemText.setFill("#2ecc40"); // green
}
} else {
priceCoin.visible = !isPurchased;
itemText.setText(weapon.name + (isPurchased ? "" : " | " + weapon.price));
if (typeof itemText.setFill === "function") {
itemText.setFill("#fff"); // white
}
}
}
updateShopItemState();
// Add down handler for all shop items
(function (weapon, shopItem, priceCoin, itemText, updateShopItemState) {
shopItem.down = function () {
var isPurchased = shopPurchases && shopPurchases[weapon.asset];
var isSelected = wizardAsset === weapon.asset;
// If not purchased, buy and select immediately
if (!isPurchased && coinCount >= weapon.price) {
coinCount -= weapon.price;
coinText.setText("" + coinCount);
if (!shopPurchases) shopPurchases = {};
shopPurchases[weapon.asset] = true;
storage.shopPurchases = shopPurchases;
// Remove all children to ensure only one wizard asset
for (var i = wizard.children.length - 1; i >= 0; i--) {
wizard.removeChild(wizard.children[i]);
}
// Add new asset as wizard's appearance
var newSprite = wizard.attachAsset(weapon.asset, {
anchorX: 0.5,
anchorY: 0.5
});
// Save wizard asset and select it
storage.wizardAsset = weapon.asset;
wizardAsset = weapon.asset;
// Switch upgrades to selected weapon
currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
wizardHealth = currentWeaponUpgrades.wizardHealth;
fireballDamage = currentWeaponUpgrades.fireballDamage;
// --- Minigun ayarları ---
if (weapon.asset === "minigun") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("minigun");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 2000;
tapFireCooldown = 500;
wizardBulletType = "minigunbullet";
} else if (weapon.asset === "shuriken") {
wizardBulletType = "shurikenbullet";
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("shuriken");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 2000;
tapFireCooldown = 750;
} else if (weapon.asset === "shotgun") {
wizardBulletType = null;
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("shotgun");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 1000;
tapFireCooldown = 200;
} else if (weapon.asset === "submachine") {
wizardBulletType = null;
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("submachine");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 500;
tapFireCooldown = 100;
} else if (weapon.asset === "Lasergun") {
wizardBulletType = "lasergunbullet";
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("Lasergun");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 1200;
tapFireCooldown = 300;
} else {
wizardBulletType = null;
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo(wizardAsset);
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 2000;
tapFireCooldown = Math.max(100, 850 - speedUpgradeLevel * 150);
}
LK.effects.flashObject(wizard, 0x2ecc40, 400);
// Update visuals for all shop items
for (var si = 0; si < shopItemButtons.length; si++) {
if (shopItemButtons[si].updateShopItemState) shopItemButtons[si].updateShopItemState();
}
return;
}
// If purchased, select it
if (isPurchased && !isSelected) {
// Remove all children to ensure only one wizard asset
for (var i = wizard.children.length - 1; i >= 0; i--) {
wizard.removeChild(wizard.children[i]);
}
// Add new asset as wizard's appearance
var newSprite = wizard.attachAsset(weapon.asset, {
anchorX: 0.5,
anchorY: 0.5
});
// Save wizard asset
storage.wizardAsset = weapon.asset;
wizardAsset = weapon.asset;
// Switch upgrades to selected weapon
currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
wizardHealth = currentWeaponUpgrades.wizardHealth;
fireballDamage = currentWeaponUpgrades.fireballDamage;
// --- Minigun ayarları ---
if (weapon.asset === "minigun") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("minigun");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 2000;
tapFireCooldown = 500;
wizardBulletType = "minigunbullet";
} else if (weapon.asset === "shuriken") {
wizardBulletType = "shurikenbullet";
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("shuriken");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 2000;
tapFireCooldown = 750;
} else if (weapon.asset === "shotgun") {
wizardBulletType = null;
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("shotgun");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 1000;
tapFireCooldown = 200;
} else if (weapon.asset === "submachine") {
wizardBulletType = null;
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("submachine");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 500;
tapFireCooldown = 100;
} else if (weapon.asset === "Lasergun") {
wizardBulletType = "lasergunbullet";
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("Lasergun");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 1200;
tapFireCooldown = 300;
} else {
wizardBulletType = null;
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo(wizardAsset);
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 2000;
tapFireCooldown = Math.max(100, 850 - speedUpgradeLevel * 150);
}
LK.effects.flashObject(wizard, 0x2ecc40, 400);
// Update visuals for all shop items
for (var si = 0; si < shopItemButtons.length; si++) {
if (shopItemButtons[si].updateShopItemState) shopItemButtons[si].updateShopItemState();
}
return;
}
// If not enough coins, flash coinText
if (!isPurchased && coinCount < weapon.price) {
LK.effects.flashObject(coinText, 0xe74c3c, 600);
}
};
// Attach state update helper for global update
shopItem.updateShopItemState = updateShopItemState;
})(weapon, shopItem, priceCoin, itemText, updateShopItemState);
shopItem.x = 0;
shopItem.y = itemY;
shopWindow.addChild(shopItem);
shopItemButtons.push(shopItem);
},
weapon,
itemY,
shopItem,
itemIcon,
itemText,
priceCoin,
purchasedLabel,
selectedLabel;
for (var i = 0; i < weaponList.length; i++) {
_loop();
}
// BACK button
var backBtn = new Container();
// No background for back button
var backBtnText = new Text2("BACK", {
size: 90,
fill: "#fff"
});
backBtnText.anchor.set(0.5, 0.5);
backBtnText.x = 0;
backBtnText.y = 0;
backBtn.addChild(backBtnText);
backBtn.x = 0;
backBtn.y = itemStartY + weaponList.length * itemSpacing + 80;
shopWindow.addChild(backBtn);
backBtn.down = function () {
if (shopWindow && !shopWindow.destroyed) {
shopWindow.destroy();
shopWindow = null;
}
};
// Center window
shopWindow.x = 2048 / 2;
shopWindow.y = 2732 / 2;
game.addChild(shopWindow);
}
// Shop button interaction
shopButton.down = function (x, y, obj) {
if (shopWindow && !shopWindow.destroyed) return;
showShopWindow();
};
// Start button interaction
startButton.down = function (x, y, obj) {
if (gameStarted) return;
gameStarted = true;
// Remove start UI container (removes both start and upgrade buttons)
if (startUiContainer && !startUiContainer.destroyed) {
startUiContainer.destroy();
}
// --- RESET GAME STATE ---
// Remove all enemies, fireballs, coins from game and arrays
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] && !enemies[i].destroyed) enemies[i].destroy();
enemies.splice(i, 1);
}
for (var i = fireballs.length - 1; i >= 0; i--) {
if (fireballs[i] && !fireballs[i].destroyed) fireballs[i].destroy();
fireballs.splice(i, 1);
}
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i] && !coins[i].destroyed) coins[i].destroy();
coins.splice(i, 1);
}
// Reset score and coin count
score = 0;
// coinCount is NOT reset here, so coins are preserved when starting a new game
scoreText.setText("Score: 0");
coinText.setText("" + coinCount);
// Center wizard
wizard.x = 2048 / 2;
wizard.y = 2732 / 2;
// Remove all children to ensure only one wizard asset
for (var i = wizard.children.length - 1; i >= 0; i--) {
wizard.removeChild(wizard.children[i]);
}
// Attach wizard asset if not present
var hasWizardSprite = false;
for (var i = 0; i < wizard.children.length; i++) {
hasWizardSprite = true;
break;
}
if (!hasWizardSprite) {
var newSprite = wizard.attachAsset(wizardAsset, {
anchorX: 0.5,
anchorY: 0.5
});
if (newSprite) {
newSprite.rotation = Math.PI;
}
} else if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = Math.PI; // face left
}
// Reset health
wizardHealth = maxWizardHealth;
updateHealthBar();
// Enable game input
game.move = originalGameMove;
game.down = originalGameDown;
game.up = originalGameUp;
};
// Save original handlers for restoration
var originalGameMove = function originalGameMove(x, y, obj) {
// Wizard is fixed; do nothing
// Calculate angle from wizard to pointer/touch and rotate wizard sprite
var dx = x - wizard.x;
var dy = y - wizard.y;
var angle = Math.atan2(dy, dx);
// Wizard's default asset faces left, so add Math.PI to rotate to correct direction
if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = angle + Math.PI;
}
// If firing, update direction for continuous fire
if (isFiring) {
lastFireX = x;
lastFireY = y;
}
};
var originalGameDown = function originalGameDown(x, y, obj) {
var now = Date.now();
if (now - lastTapFireTime < tapFireCooldown) {
// Too soon, ignore this tap for firing
return;
}
lastTapFireTime = now;
isFiring = true;
lastFireX = x;
lastFireY = y;
shootFireballAt(x, y);
if (fireInterval) LK.clearInterval(fireInterval);
fireInterval = LK.setInterval(function () {
if (isFiring) {
shootFireballAt(lastFireX, lastFireY);
}
}, tapFireCooldown); // fire every tapFireCooldown ms when holding down
};
var originalGameUp = function originalGameUp(x, y, obj) {
isFiring = false;
if (fireInterval) {
LK.clearInterval(fireInterval);
fireInterval = null;
}
};
// Array to hold all fireballs
var fireballs = [];
// Array to hold all boss bullets
var bossBullets = [];
// Helper: get direction vector from wizard to (x, y)
function getDirection(fromX, fromY, toX, toY, speed) {
var dx = toX - fromX;
var dy = toY - fromY;
var len = Math.sqrt(dx * dx + dy * dy);
if (len === 0) return {
vx: 0,
vy: 0
};
return {
vx: dx / len * speed,
vy: dy / len * speed
};
}
// Dragging wizard with mouse/touch (disabled)
var dragging = false;
// Move handler: wizard does not move
game.move = function (x, y, obj) {
if (!gameStarted) return; // Only rotate wizard during gameplay
// Calculate angle from wizard to pointer/touch and rotate wizard sprite
var dx = x - wizard.x;
var dy = y - wizard.y;
var angle = Math.atan2(dy, dx);
// Wizard's default asset faces left, so add Math.PI to rotate to correct direction
if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = angle + Math.PI;
}
// If firing, update direction for continuous fire
if (isFiring) {
lastFireX = x;
lastFireY = y;
}
};
// Track if fire is being held
var isFiring = false;
var fireInterval = null;
var lastFireX = 0;
// --- Magazine system for wizard weapon ---
var wizardMaxAmmo = 7;
var wizardAmmo = wizardMaxAmmo;
var wizardReloading = false;
var wizardReloadTime = 2000; // ms
var wizardReloadTimeout = null;
// Hangi mermi asseti kullanılacak? (null: default, "shurikenbullet", "minigunbullet")
var wizardBulletType = null;
// Helper: apply ammo upgrade to wizardMaxAmmo for current weapon
function applyAmmoUpgradeToMaxAmmo(weaponAsset) {
var upgrades = getWeaponUpgrades(weaponAsset);
var baseMaxAmmo = 7;
if (weaponAsset === "minigun") baseMaxAmmo = 150;else if (weaponAsset === "shuriken") baseMaxAmmo = 7;else if (weaponAsset === "shotgun") baseMaxAmmo = 2;else if (weaponAsset === "submachine") baseMaxAmmo = 3;else if (weaponAsset === "Lasergun") baseMaxAmmo = 10;
// Add ammoUpgradeAmount if present
if (typeof upgrades.ammoUpgradeAmount === "number") {
return baseMaxAmmo + upgrades.ammoUpgradeAmount;
}
return baseMaxAmmo;
}
// Cooldown for tap-to-fire (now 850ms)
var lastTapFireTime = 0;
var tapFireCooldown = 850;
function shootFireballAt(x, y) {
// --- Magazine system: block fire if out of ammo or reloading ---
// For shuriken, skip ammo/reload logic (infinite ammo)
if (wizardAsset !== "shuriken") {
if (wizardReloading) {
// Can't shoot while reloading
return;
}
if (wizardAmmo <= 0) {
// Start reload if not already reloading
if (!wizardReloading) {
wizardReloading = true;
wizardReloadTimeout = LK.setTimeout(function () {
wizardAmmo = wizardMaxAmmo;
wizardReloading = false;
wizardReloadTimeout = null;
}, wizardReloadTime);
}
return;
}
// Decrease ammo
wizardAmmo--;
}
// Eğer wizardAsset shuriken ise shurikenbullet fırlat
if (wizardAsset === "shuriken") {
// SPEED upgrade seviyesine göre shuriken için özel ateş aralığı ve mermi hızı uygulanacak
var upgrades = getWeaponUpgrades(wizardAsset);
var speedLevel = upgrades.speedUpgradeLevel || 0;
// Shuriken için yeni seviye tablosu:
// | Seviye | Ateş Aralığı (ms) | Mermi Hızı (birim/frame) |
// | 0 | 850 | 18 |
// | 1 | 762 | 19.5 |
// | 2 | 675 | 21 |
// | 3 | 587 | 22.5 |
// | 4 | 500 | 24 |
var shurikenFireIntervals = [850, 762, 675, 587, 500];
var shurikenBulletSpeeds = [18, 19.5, 21, 22.5, 24];
var fireInterval = shurikenFireIntervals[Math.min(speedLevel, 4)];
var bulletSpeed = shurikenBulletSpeeds[Math.min(speedLevel, 4)];
tapFireCooldown = fireInterval;
// Fire 3 shurikenbullets at -15, 0, +15 degrees from the main direction
var baseDir = getDirection(wizard.x, wizard.y, x, y, bulletSpeed);
var baseAngle = Math.atan2(baseDir.vy, baseDir.vx);
var angles = [-15, 0, 15];
for (var i = 0; i < angles.length; i++) {
var angleRad = baseAngle + angles[i] * Math.PI / 180;
var vx = Math.cos(angleRad) * bulletSpeed;
var vy = Math.sin(angleRad) * bulletSpeed;
var fireball = new Fireball();
// Remove default fireball sprite, add shurikenbullet
if (fireball.children && fireball.children.length > 0) {
var oldSprite = fireball.children[0];
fireball.removeChild(oldSprite);
}
var shurikenBulletSprite = fireball.attachAsset('shurikenbullet', {
anchorX: 0.5,
anchorY: 0.5
});
fireball.x = wizard.x;
fireball.y = wizard.y;
fireball.vx = vx;
fireball.vy = vy;
if (fireball.children && fireball.children.length > 0) {
fireball.children[0].rotation = angleRad;
}
if (wizard.children && wizard.children.length > 0 && angles[i] === 0) {
wizard.children[0].rotation = baseAngle + Math.PI;
}
fireballs.push(fireball);
game.addChild(fireball);
}
LK.getSound('shuriken').play();
} else {
// SMG logic
if (wizardAsset === "submachine") {
tapFireCooldown = 100;
wizardReloadTime = 500;
var baseDir = getDirection(wizard.x, wizard.y, x, y, 8);
var baseAngle = Math.atan2(baseDir.vy, baseDir.vx);
// Fire only 1 bullet per shot (no in-a-row)
var fireball = new Fireball();
fireball.x = wizard.x;
fireball.y = wizard.y;
fireball.vx = Math.cos(baseAngle) * 8;
fireball.vy = Math.sin(baseAngle) * 8;
if (fireball.children && fireball.children.length > 0) {
fireball.children[0].rotation = baseAngle;
}
fireballs.push(fireball);
game.addChild(fireball);
if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = baseAngle + Math.PI;
}
// Play SMG sound (use fire sound as placeholder if SMG sound not available)
if (LK.getSound('SMG')) {
LK.getSound('SMG').play();
} else {
LK.getSound('fire').play();
}
// Start reload if out of ammo after this shot
if (wizardAmmo <= 0 && !wizardReloading) {
wizardReloading = true;
wizardReloadTimeout = LK.setTimeout(function () {
wizardAmmo = wizardMaxAmmo;
wizardReloading = false;
wizardReloadTimeout = null;
}, wizardReloadTime);
}
} else
// Shotgun logic
if (wizardAsset === "shotgun") {
// --- SPEED UPGRADE: Shotgun için özel ateş ve mermi hızları (800ms -> 500ms) ---
var upgrades = getWeaponUpgrades("shotgun");
var speedLevel = upgrades.speedUpgradeLevel || 0;
// Yeni seviye tablosu:
// | Seviye | Ateş Aralığı (ms) | Mermi Hızı (birim/frame) |
// | 0 | 800 | 10 |
// | 1 | 725 | 11 |
// | 2 | 650 | 12 |
// | 3 | 575 | 13 |
// | 4 | 500 | 14 |
var shotgunFireIntervals = [800, 725, 650, 575, 500];
var shotgunBulletSpeeds = [10, 11, 12, 13, 14];
var fireInterval = shotgunFireIntervals[Math.min(speedLevel, 4)];
var bulletSpeed = shotgunBulletSpeeds[Math.min(speedLevel, 4)];
tapFireCooldown = fireInterval;
wizardReloadTime = 1000;
// 5 bullets, 75-degree random spread
var baseDir = getDirection(wizard.x, wizard.y, x, y, bulletSpeed);
var baseAngle = Math.atan2(baseDir.vy, baseDir.vx);
for (var i = 0; i < 5; i++) {
var spreadDeg = Math.random() * 75 - 37.5;
var angleRad = baseAngle + spreadDeg * Math.PI / 180;
var vx = Math.cos(angleRad) * bulletSpeed;
var vy = Math.sin(angleRad) * bulletSpeed;
var fireball = new Fireball();
fireball.x = wizard.x;
fireball.y = wizard.y;
fireball.vx = vx;
fireball.vy = vy;
if (fireball.children && fireball.children.length > 0) {
fireball.children[0].rotation = angleRad;
}
fireballs.push(fireball);
game.addChild(fireball);
}
if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = baseAngle + Math.PI;
}
// Play shotgun sound (use fire sound as placeholder if shotgun sound not available)
if (LK.getSound('shotgun')) {
LK.getSound('shotgun').play();
} else {
LK.getSound('fire').play();
}
// Start reload if out of ammo after this shot
if (wizardAmmo <= 0 && !wizardReloading) {
wizardReloading = true;
wizardReloadTimeout = LK.setTimeout(function () {
wizardAmmo = wizardMaxAmmo;
wizardReloading = false;
wizardReloadTimeout = null;
}, wizardReloadTime);
}
} else
// Minigun için özel asset ve ayarlar
if (wizardAsset === "minigun" || wizardBulletType === "minigunbullet") {
// --- SPEED UPGRADE: Minigun için özel ateş ve mermi hızları ---
var upgrades = getWeaponUpgrades("minigun");
var speedLevel = upgrades.speedUpgradeLevel || 0;
// Seviye tablosu:
// | Seviye | Ateş Aralığı (ms) | Mermi Hızı (birim/frame) |
// | 0 | 500 | 18 |
// | 1 | 360 | 19.5 |
// | 2 | 220 | 21 |
// | 3 | 150 | 22.5 |
// | 4 | 80 | 24 |
var minigunFireIntervals = [500, 360, 220, 150, 80];
var minigunBulletSpeeds = [18, 19.5, 21, 22.5, 24];
var fireInterval = minigunFireIntervals[Math.min(speedLevel, 4)];
var bulletSpeed = minigunBulletSpeeds[Math.min(speedLevel, 4)];
tapFireCooldown = fireInterval;
// Fire 1 bullet per tap, but with random scatter in a 30-degree cone
var baseDir = getDirection(wizard.x, wizard.y, x, y, bulletSpeed);
var baseAngle = Math.atan2(baseDir.vy, baseDir.vx);
// 30-degree cone: -15 to +15 deg, random within
var scatterDeg = Math.random() * 30 - 15;
var scatterRad = scatterDeg * Math.PI / 180;
var angle = baseAngle + scatterRad;
var vx = Math.cos(angle) * bulletSpeed;
var vy = Math.sin(angle) * bulletSpeed;
var fireball = new Fireball();
fireball.x = wizard.x;
fireball.y = wizard.y;
fireball.vx = vx;
fireball.vy = vy;
// Default fireball asseti kaldır, minigunbullet ekle
if (fireball.children && fireball.children.length > 0) {
var oldSprite = fireball.children[0];
fireball.removeChild(oldSprite);
}
var minigunBulletSprite = fireball.attachAsset('minigunbullet', {
anchorX: 0.5,
anchorY: 0.5
});
if (fireball.children && fireball.children.length > 0) {
fireball.children[0].rotation = angle;
}
if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = Math.atan2(y - wizard.y, x - wizard.x) + Math.PI;
}
fireballs.push(fireball);
game.addChild(fireball);
LK.getSound('Minigun').play();
// Start reload if out of ammo after this shot
if (wizardAmmo <= 0 && !wizardReloading) {
wizardReloading = true;
wizardReloadTimeout = LK.setTimeout(function () {
wizardAmmo = wizardMaxAmmo;
wizardReloading = false;
wizardReloadTimeout = null;
}, wizardReloadTime);
}
} else if (wizardAsset === "Lasergun" || wizardBulletType === "lasergunbullet") {
// --- SPEED UPGRADE: Lasergun için yeni ateş ve mermi hızları (750ms -> 200ms) ---
var upgrades = getWeaponUpgrades("Lasergun");
var speedLevel = upgrades.speedUpgradeLevel || 0;
// Seviye tablosu:
// | Seviye | Ateş Aralığı (ms) | Mermi Hızı (birim/frame) |
// | 0 | 750 | 18 |
// | 1 | 550 | 19.5 |
// | 2 | 400 | 21 |
// | 3 | 300 | 22.5 |
// | 4 | 200 | 24 |
var lasergunFireIntervals = [750, 550, 400, 300, 200];
var lasergunBulletSpeeds = [18, 19.5, 21, 22.5, 24];
var fireInterval = lasergunFireIntervals[Math.min(speedLevel, 4)];
var bulletSpeed = lasergunBulletSpeeds[Math.min(speedLevel, 4)];
tapFireCooldown = fireInterval;
var baseDir = getDirection(wizard.x, wizard.y, x, y, bulletSpeed);
var baseAngle = Math.atan2(baseDir.vy, baseDir.vx);
// Fire 3 lasergun bullets in a row, no spread
for (var i = 0; i < 3; i++) {
var fireball = new Fireball();
fireball.x = wizard.x;
fireball.y = wizard.y;
fireball.vx = Math.cos(baseAngle) * bulletSpeed;
fireball.vy = Math.sin(baseAngle) * bulletSpeed;
// Remove default fireball sprite, add lasergunbullet
if (fireball.children && fireball.children.length > 0) {
var oldSprite = fireball.children[0];
fireball.removeChild(oldSprite);
}
var laserBulletSprite = fireball.attachAsset('lasergunbullet', {
anchorX: 0.5,
anchorY: 0.5
});
if (fireball.children && fireball.children.length > 0) {
fireball.children[0].rotation = baseAngle;
}
// Only rotate wizard sprite for the center bullet
if (wizard.children && wizard.children.length > 0 && i === 1) {
wizard.children[0].rotation = Math.atan2(y - wizard.y, x - wizard.x) + Math.PI;
}
fireballs.push(fireball);
game.addChild(fireball);
}
LK.getSound('plasma').play();
// Start reload if out of ammo after this shot
if (wizardAmmo <= 0 && !wizardReloading) {
wizardReloading = true;
wizardReloadTimeout = LK.setTimeout(function () {
wizardAmmo = wizardMaxAmmo;
wizardReloading = false;
wizardReloadTimeout = null;
}, wizardReloadTime);
}
} else {
// Restore tapFireCooldown to upgrade-based value for non-shuriken weapons
// SPEED upgrade seviyesine göre mermi hızı artırımı
var upgrades = getWeaponUpgrades(wizardAsset);
var speedLevel = upgrades.speedUpgradeLevel || 0;
var bulletSpeed = 8 + speedLevel * 1.5; // her seviye +1.5 hız
tapFireCooldown = Math.max(80, 850 - speedLevel * 140);
var fireball = new Fireball();
fireball.x = wizard.x;
fireball.y = wizard.y;
var dir = getDirection(wizard.x, wizard.y, x, y, bulletSpeed);
fireball.vx = dir.vx;
fireball.vy = dir.vy;
var angle = Math.atan2(dir.vy, dir.vx);
if (fireball.children && fireball.children.length > 0) {
fireball.children[0].rotation = angle;
}
if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = Math.atan2(y - wizard.y, x - wizard.x) + Math.PI;
}
fireballs.push(fireball);
game.addChild(fireball);
LK.getSound('fire').play();
// Start reload if out of ammo after this shot
if (wizardAmmo <= 0 && !wizardReloading) {
wizardReloading = true;
wizardReloadTimeout = LK.setTimeout(function () {
wizardAmmo = wizardMaxAmmo;
wizardReloading = false;
wizardReloadTimeout = null;
}, wizardReloadTime);
}
}
}
}
// Down handler: start firing repeatedly only if gameStarted
game.down = function (x, y, obj) {
if (!gameStarted) return;
var now = Date.now();
if (now - lastTapFireTime < tapFireCooldown) {
// Too soon, ignore this tap for firing
return;
}
lastTapFireTime = now;
isFiring = true;
lastFireX = x;
lastFireY = y;
shootFireballAt(x, y);
if (fireInterval) LK.clearInterval(fireInterval);
fireInterval = LK.setInterval(function () {
if (isFiring) {
shootFireballAt(lastFireX, lastFireY);
}
}, tapFireCooldown); // fire every tapFireCooldown ms when holding down
};
// Up handler: stop firing only if gameStarted
game.up = function (x, y, obj) {
if (!gameStarted) return;
isFiring = false;
if (fireInterval) {
LK.clearInterval(fireInterval);
fireInterval = null;
}
};
// Array to hold all enemies
var enemies = [];
// Import storage plugin for persistent coin saving
// Array to hold all coins
var coins = [];
// Wizard health system
var wizardHealth = 5;
var maxWizardHealth = 5;
// Health upgrade cost system
var healthUpgradeCost = 25;
var healthUpgradeBtnText = null;
var healthUpgradeLevel = 0;
// --- Per-weapon upgrade system ---
var weaponUpgradeDefaults = {
speedUpgradeLevel: 0,
speedUpgradeCost: 100,
// ilk seviye 100 coin
healthUpgradeLevel: 0,
healthUpgradeCost: 25,
maxWizardHealth: 5,
wizardHealth: 5,
fireballDamage: 1,
// --- Ammo upgrade fields ---
ammoUpgradeLevel: 0,
ammoUpgradeCost: 80,
ammoUpgradeAmount: 0
};
// Helper to get upgrade key for a weapon
function getWeaponUpgradeKey(weaponAsset) {
return "weaponUpgrades_" + weaponAsset;
}
// Helper to get upgrades for a weapon (returns object)
function getWeaponUpgrades(weaponAsset) {
var key = getWeaponUpgradeKey(weaponAsset);
var upgrades = storage[key];
if (!upgrades) {
upgrades = {};
// Copy defaults
for (var k in weaponUpgradeDefaults) upgrades[k] = weaponUpgradeDefaults[k];
storage[key] = upgrades;
} else {
// Fill in any missing defaults
for (var k in weaponUpgradeDefaults) {
if (typeof upgrades[k] === "undefined") upgrades[k] = weaponUpgradeDefaults[k];
}
}
return upgrades;
}
// Helper to save upgrades for a weapon
function saveWeaponUpgrades(weaponAsset, upgrades) {
var key = getWeaponUpgradeKey(weaponAsset);
storage[key] = upgrades;
}
// --- Current weapon upgrades (populated on load and on weapon change) ---
var currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
var speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
var speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
// (damageUpgradeLevel and damageUpgradeCost removed)
var healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
var healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
var maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
var wizardHealth = currentWeaponUpgrades.wizardHealth;
var fireballDamage = currentWeaponUpgrades.fireballDamage;
// Health bar UI update
function updateHealthBar() {
if (typeof healthBarFill === "undefined" || typeof healthBarBg === "undefined") return;
var ratio = Math.max(0, Math.min(1, wizardHealth / maxWizardHealth));
healthBarFill.width = (healthBarBg.width - 8) * ratio;
// Keep bar anchored left
healthBarFill.x = healthBarBg.x - (healthBarBg.width - 8) * (1 - ratio) / 2;
}
// Score variable
var score = 0;
// Persistent total coins (stacked across games)
var totalCoins = storage.totalCoins || 0;
// --- Persistent Progress: Restore on load ---
var coinCount = typeof storage.coinCount === "number" ? storage.coinCount : 0;
// No automatic coin grant on reload
var speedUpgradeLevel = typeof storage.speedUpgradeLevel === "number" ? storage.speedUpgradeLevel : 0;
var speedUpgradeCost = typeof storage.speedUpgradeCost === "number" ? storage.speedUpgradeCost : 50;
// (damageUpgradeLevel and damageUpgradeCost removed)
var healthUpgradeLevel = typeof storage.healthUpgradeLevel === "number" ? storage.healthUpgradeLevel : 0;
var healthUpgradeCost = typeof storage.healthUpgradeCost === "number" ? storage.healthUpgradeCost : 25;
var maxWizardHealth = typeof storage.maxWizardHealth === "number" ? storage.maxWizardHealth : 5;
var wizardHealth = typeof storage.wizardHealth === "number" ? storage.wizardHealth : maxWizardHealth;
var fireballDamage = typeof storage.fireballDamage === "number" ? storage.fireballDamage : 1;
var shopPurchases = storage.shopPurchases || {};
var wizardAsset = storage.wizardAsset || "wizard";
// Score text
var scoreText = new Text2("Score: 0", {
size: 100,
fill: "#fff"
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
// After scoreText and wizard are positioned, update startUiContainer position
if (typeof startUiContainer !== "undefined" && startUiContainer && typeof wizard !== "undefined" && wizard) {
// Place UI container between score and wizard, but not overlapping either
startUiContainer.x = 2048 / 2;
startUiContainer.y = wizard.y - 180;
}
// Coin icon and coin count text
var coinIcon = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
var coinText = new Text2("" + coinCount, {
size: 90,
fill: 0xFFD700
});
coinText.anchor.set(0.5, 0);
// Add coin icon and text to the GUI, but position them below the score text
LK.gui.top.addChild(coinIcon);
LK.gui.top.addChild(coinText);
// --- Health Bar UI under coin asset ---
var healthBarWidth = 245;
var healthBarHeight = 57;
var healthBarBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: healthBarWidth,
height: healthBarHeight
});
healthBarBg.alpha = 0.35;
var healthBarFill = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: healthBarWidth - 8,
height: healthBarHeight - 8
});
healthBarFill.alpha = 0.85;
healthBarFill.tint = 0x2ecc40;
// Add white "HEALTH" label centered in the bar
var healthBarLabel = new Text2("HEALTH", {
size: 64,
// Increased font size
fill: "#fff"
});
healthBarLabel.anchor.set(0.5, 0.5);
LK.gui.top.addChild(healthBarBg);
LK.gui.top.addChild(healthBarFill);
LK.gui.top.addChild(healthBarLabel);
// --- Ammo Bar UI below health bar ---
var ammoBarWidth = healthBarWidth;
var ammoBarHeight = 36;
var ammoBarBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: ammoBarWidth,
height: ammoBarHeight
});
ammoBarBg.alpha = 0.28;
var ammoBarFill = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: ammoBarWidth - 8,
height: ammoBarHeight - 8
});
ammoBarFill.alpha = 0.92;
ammoBarFill.tint = 0x3498db; // blue for ammo
// Add dynamic ammo label centered in the bar
var ammoBarLabel = new Text2("", {
size: 36,
fill: "#fff"
});
ammoBarLabel.anchor.set(0.5, 0.5);
LK.gui.top.addChild(ammoBarBg);
LK.gui.top.addChild(ammoBarFill);
LK.gui.top.addChild(ammoBarLabel);
// --- Reload Button below ammo bar ---
var reloadButtonSize = 180;
var reloadButton = new Container();
var reloadButtonBg = LK.getAsset('reload', {
anchorX: 0.5,
anchorY: 0.5,
width: reloadButtonSize,
height: reloadButtonSize
});
reloadButtonBg.alpha = 0.95;
reloadButton.addChild(reloadButtonBg);
LK.gui.top.addChild(reloadButton);
// Position reload button below ammo bar (centered), moved 850px further down (250 + 750 - 100 - 50)
reloadButton.x = ammoBarBg.x;
reloadButton.y = ammoBarBg.y + ammoBarBg.height / 2 + reloadButtonSize / 2 + 18 + 850;
// Reload logic on click/tap
reloadButton.down = function (x, y, obj) {
// Only reload if not already reloading, not shuriken, and not full
if (wizardAsset !== "shuriken" && !wizardReloading && typeof wizardAmmo !== "undefined" && typeof wizardMaxAmmo !== "undefined" && wizardAmmo < wizardMaxAmmo) {
wizardReloading = true;
if (wizardReloadTimeout) {
LK.clearTimeout(wizardReloadTimeout);
wizardReloadTimeout = null;
}
wizardReloadTimeout = LK.setTimeout(function () {
wizardAmmo = wizardMaxAmmo;
wizardReloading = false;
wizardReloadTimeout = null;
}, wizardReloadTime);
}
};
// Position coin icon and text below the score text in the start screen
coinIcon.x = scoreText.x;
coinIcon.y = scoreText.y + scoreText.height + coinIcon.height * 0.6;
// Place coinText 100px to the right and 50px above the coin icon
coinText.x = coinIcon.x + 100;
coinText.y = coinIcon.y - 50;
// Position health bar under coin asset
healthBarBg.x = coinIcon.x;
healthBarBg.y = coinIcon.y + coinIcon.height * 0.7 + healthBarHeight / 2 + 8;
healthBarFill.x = healthBarBg.x;
healthBarFill.y = healthBarBg.y;
// Center the label inside the health bar
if (typeof healthBarLabel !== "undefined") {
healthBarLabel.x = healthBarBg.x;
healthBarLabel.y = healthBarBg.y;
}
// Health bar update function
updateHealthBar();
// --- Restore wizard asset if changed (e.g. shuriken) ---
if (wizardAsset !== "wizard") {
// Remove all children to ensure only one wizard asset
for (var i = wizard.children.length - 1; i >= 0; i--) {
wizard.removeChild(wizard.children[i]);
}
var newSprite = wizard.attachAsset(wizardAsset, {
anchorX: 0.5,
anchorY: 0.5
});
}
// Helper: spawn an enemy at a random edge, moving toward wizard
function spawnEnemy() {
// Limit the maximum number of active enemies to 20
if (typeof enemies !== "undefined" && enemies.length >= 20) {
return;
}
// Only allow certain types after certain scores
var canEnemy2 = score >= 10;
var canEnemy3 = score >= 20;
var canEnemy4 = score >= 30;
var canEnemy6 = score >= 20;
// Weighted random: 30% enemy1, 25% enemy2, 15% enemy3, 10% enemy4, 20% enemy6
// If a type is not available, its weight is 0 and weights are renormalized
var weights = [30,
// enemy1
canEnemy2 ? 25 : 0,
// enemy2
canEnemy3 ? 15 : 0,
// enemy3
canEnemy4 ? 10 : 0,
// enemy4
canEnemy6 ? 5 : 0 // enemy6 (was 20, now 5 for 5% chance)
];
var totalWeight = 0;
for (var i = 0; i < weights.length; i++) totalWeight += weights[i];
var r = Math.random() * totalWeight;
var acc = 0;
var enemyType = 1;
for (var i = 0; i < weights.length; i++) {
acc += weights[i];
if (r < acc) {
enemyType = i + 1;
break;
}
}
var enemy;
if (enemyType === 1) {
enemy = new Enemy();
} else if (enemyType === 2) {
enemy = new Enemy2();
} else if (enemyType === 3) {
enemy = new Enemy3();
} else if (enemyType === 4) {
enemy = new Enemy4();
} else if (enemyType === 5) {
enemy = new Enemy6();
}
// Randomly pick an edge: 0=top, 1=bottom, 2=left, 3=right
var edge = Math.floor(Math.random() * 4);
var ex, ey;
if (edge === 0) {
ex = Math.random() * 2048;
ey = -50;
} else if (edge === 1) {
ex = Math.random() * 2048;
ey = 2732 + 50;
} else if (edge === 2) {
ex = -50;
ey = Math.random() * 2732;
} else {
ex = 2048 + 50;
ey = Math.random() * 2732;
}
enemy.x = ex;
enemy.y = ey;
// Move toward wizard
var dir = getDirection(ex, ey, wizard.x, wizard.y, 2.5 + Math.random() * 1.5); // randomize speed a bit
enemy.vx = dir.vx;
enemy.vy = dir.vy;
// Apply slow effect if slow timer is currently active
if (typeof slowTimerTimeout !== "undefined" && slowTimerTimeout && typeof enemies !== "undefined") {
// If slowTimerTimeout is running, slow effect is active
if (!enemy._isSlowed) {
enemy._origVx = typeof enemy._origVx === "number" ? enemy._origVx : enemy.vx;
enemy._origVy = typeof enemy._origVy === "number" ? enemy._origVy : enemy.vy;
enemy.vx = enemy.vx * (1 / 3);
enemy.vy = enemy.vy * (1 / 3);
enemy._isSlowed = true;
}
}
enemies.push(enemy);
game.addChild(enemy);
}
// Enemy spawn timer
var enemySpawnStopped = false;
var bossSpawned = false;
var boss = null;
var enemy5Spawned = false;
var enemy5 = null;
var infernoMonsterSpawned = false;
var infernoMonster = null;
var enemySpawnTimer = LK.setInterval(function () {
// Do not spawn any enemies if game is not started (main menu)
if (!gameStarted) {
return;
}
// Pause enemy spawn if boss is present and alive
if (boss && !boss.destroyed) {
return;
}
// Pause enemy spawn if goblin (Enemy5) is present and alive
// Only pause if goblin is present, not destroyed, and has NOT shown smoke (i.e. not timed out)
if (enemy5 && !enemy5.destroyed && !enemy5.smokeShown) {
return;
}
// If goblin has disappeared with smoke, clear enemy5 reference and allow normal enemies to spawn
if (enemy5 && enemy5.smokeShown) {
enemy5 = null;
enemy5Spawned = false;
}
// Spawn InfernoMonster at score 160, only once
if (!infernoMonsterSpawned && score >= 160) {
infernoMonster = new InfernoMonster();
// Place initially at correct orbit position
infernoMonster.orbitAngle = Math.random() * Math.PI * 2;
infernoMonster.x = wizard.x + Math.cos(infernoMonster.orbitAngle) * 850;
infernoMonster.y = wizard.y + Math.sin(infernoMonster.orbitAngle) * 850;
enemies.push(infernoMonster);
game.addChild(infernoMonster);
infernoMonsterSpawned = true;
}
// Spawn boss at score 110
if (!bossSpawned && score >= 110) {
boss = new Boss();
boss.x = 2048 / 2;
boss.y = -120;
game.addChild(boss);
bossSpawned = true;
return;
}
// Stop enemy spawn at score 75 and spawn goblin
if (!enemy5Spawned && score >= 75 && (typeof enemy5EverKilled === "undefined" || !enemy5EverKilled)) {
// Spawn goblin (Enemy5) at score 75, only once
enemy5 = new Enemy5();
// Start at a random angle on the orbit
enemy5.orbitAngle = Math.random() * Math.PI * 2;
// Place initially at correct orbit position
enemy5.x = wizard.x + Math.cos(enemy5.orbitAngle) * (enemy5.orbitRadius || 850);
enemy5.y = wizard.y + Math.sin(enemy5.orbitAngle) * (enemy5.orbitRadius || 850);
enemies.push(enemy5);
game.addChild(enemy5);
enemy5Spawned = true;
// Play goblin spawn sound
if (LK.getSound('Goblinl')) {
LK.getSound('Goblinl').play();
}
// Do not return; allow normal enemies to keep spawning
}
// Only spawn normal enemies if goblin is not present
if (!enemy5 || enemy5.destroyed) {
spawnEnemy();
}
}, 1200);
// Game update: update wizard, fireballs, enemies, handle collisions, and scoring
game.update = function () {
// If game not started, only update startButton and wizard visuals
if (!gameStarted) {
// Stop slow timer effect if active
if (typeof stopSlowTimerEffect === "function") {
stopSlowTimerEffect();
}
// Clear fireCircle timers if present
if (typeof fireCircleInterval !== "undefined" && fireCircleInterval) {
LK.clearInterval(fireCircleInterval);
fireCircleInterval = null;
}
if (typeof fireCircleTimeout !== "undefined" && fireCircleTimeout) {
LK.clearTimeout(fireCircleTimeout);
fireCircleTimeout = null;
}
// Clear permanent fireCircles if present
if (typeof fireCircles !== "undefined") {
for (var i = fireCircles.length - 1; i >= 0; i--) {
if (fireCircles[i] && !fireCircles[i].destroyed) fireCircles[i].destroy();
}
fireCircles = [];
}
// --- Reset magazine system on game over/start screen ---
// If minigun is selected, always set to 350 ammo at new game start
if (wizardAsset === "minigun") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("minigun");
wizardAmmo = wizardMaxAmmo;
tapFireCooldown = 500;
} else if (wizardAsset === "Lasergun") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("Lasergun");
wizardAmmo = wizardMaxAmmo;
tapFireCooldown = 300;
} else if (wizardAsset === "shotgun") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("shotgun");
wizardAmmo = wizardMaxAmmo;
tapFireCooldown = 200;
} else {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo(wizardAsset);
wizardAmmo = wizardMaxAmmo;
}
wizardReloading = false;
if (wizardReloadTimeout) {
LK.clearTimeout(wizardReloadTimeout);
wizardReloadTimeout = null;
}
if (wizard.update) wizard.update();
// Animate start button (pulse effect)
if (typeof startButton !== "undefined" && startButton && startButton.children && startButton.children.length > 0) {
var t = LK.ticks || Date.now();
var scale = 1 + 0.06 * Math.sin(t * 0.12);
startButton.scale.x = scale;
startButton.scale.y = scale;
}
return;
}
// Update wizard
if (wizard.update) wizard.update();
// Update fireballs and remove destroyed ones
for (var i = fireballs.length - 1; i >= 0; i--) {
var fb = fireballs[i];
if (fb.update) fb.update();
if (fb.destroyed) {
fireballs.splice(i, 1);
}
}
// Update permanent fireCircles (if any)
if (typeof fireCircles !== "undefined") {
for (var i = fireCircles.length - 1; i >= 0; i--) {
var fc = fireCircles[i];
if (fc && fc.update) fc.update();
if (fc && fc.destroyed) {
fireCircles.splice(i, 1);
continue;
}
// --- FireCircle damage to enemies ---
if (fc && typeof enemies !== "undefined") {
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (!enemy || enemy.destroyed) continue;
// If enemy health is 0 or below, destroy enemy and handle rewards
if (enemy.health <= 0) {
// Spawn explosion at enemy position
var explosion = new Explosion();
explosion.x = enemy.x;
explosion.y = enemy.y;
game.addChild(explosion);
// Drop healthpack if Enemy6
if (enemy.type === 6) {
var healthpack = new Healthpack();
healthpack.x = enemy.x;
healthpack.y = enemy.y;
game.addChild(healthpack);
if (typeof healthpacks === "undefined") healthpacks = [];
healthpacks.push(healthpack);
}
// Determine coin count and plus text based on enemy type
var coinAmount = 1;
if (enemy.type === 2) coinAmount = 2;
if (enemy.type === 3) coinAmount = 3;
if (enemy.type === 4) coinAmount = 4;
if (enemy.type === 5) coinAmount = 5;
// Drop the correct number of coins
if (enemy.type === 5) {
// Drop 20 coins at goblin's position when killed
for (var cc = 0; cc < 20; cc++) {
var coin = new Coin();
coin.x = enemy.x;
coin.y = enemy.y;
coin.vx = 0;
coin.vy = 0;
coin.gravity = 0;
if (cc === 0) coin.amount = 20;
coins.push(coin);
game.addChild(coin);
}
} else {
for (var cc = 0; cc < coinAmount; cc++) {
var coin = new Coin();
coin.x = enemy.x;
coin.y = enemy.y;
coin.vx = 0;
coin.vy = 0;
coin.gravity = 0;
if (cc === 0) {
coin.amount = coinAmount;
}
coins.push(coin);
game.addChild(coin);
}
}
// Play dead sound
LK.getSound('dead').play();
// If this is Enemy5, also clear enemy5 reference so it never respawns
if (enemy.type === 5) {
// Show goblingain effect at goblin's position
var goblingain = LK.getAsset('goblingain', {
anchorX: 0.5,
anchorY: 0.5,
x: enemy.x,
y: enemy.y,
scaleX: 1.0,
scaleY: 1.0,
alpha: 1
});
game.addChild(goblingain);
// Animate goblingain: scale up and fade out over 0.7s (42 frames)
(function (gobObj) {
var frames = 42,
frame = 0;
gobObj.update = function () {
frame++;
gobObj.scale.x = 1.0 + 0.5 * (frame / frames);
gobObj.scale.y = 1.0 + 0.5 * (frame / frames);
gobObj.alpha = 1 - frame / frames;
if (frame >= frames) {
gobObj.destroy();
}
};
})(goblingain);
// Mark goblin as permanently killed
enemy5EverKilled = true;
if (typeof enemy5 !== "undefined" && enemy5 === enemy) {
enemy5 = null;
enemy5Spawned = false; // Allow normal enemies to spawn again
}
// Resume normal enemy spawn if it was paused for goblin
// (Handled by enemySpawnTimer logic: normal enemies spawn if enemy5 is null or destroyed)
}
enemy.destroy();
enemies.splice(j, 1);
score += 1;
scoreText.setText("Score: " + score);
}
}
}
}
}
// Update coins and remove destroyed ones
for (var c = coins.length - 1; c >= 0; c--) {
var coin = coins[c];
// Move coin directly toward wizard
var dx = wizard.x - coin.x;
var dy = wizard.y - coin.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var speed = 22; // Fast enough to feel instant, but visible
if (dist > 1) {
coin.x += dx / dist * Math.min(speed, dist);
coin.y += dy / dist * Math.min(speed, dist);
} else {
coin.x = wizard.x;
coin.y = wizard.y;
}
// Check collision with wizard (collect coin)
if (coin.lastWasIntersecting === undefined) coin.lastWasIntersecting = false;
var coinIntersecting = coin.intersects(wizard);
if (!coin.lastWasIntersecting && coinIntersecting) {
// Do not add to score when collecting coin
coinCount += 1;
coinText.setText("" + coinCount);
// Save coin count persistently
storage.coinCount = coinCount;
// Create gain effect (use gain asset instead of explosion)
var gainEffect = LK.getAsset('gain', {
anchorX: 0.5,
anchorY: 0.5,
x: coin.x,
y: coin.y,
scaleX: 1,
scaleY: 1,
alpha: 1
});
game.addChild(gainEffect);
// Animate gain effect: scale up and fade out, then destroy
(function (effect) {
var frames = 24;
var frame = 0;
effect.update = function () {
frame++;
effect.scale.x = 1 + 0.5 * (frame / frames);
effect.scale.y = 1 + 0.5 * (frame / frames);
effect.alpha = 1 - frame / frames;
if (frame >= frames) {
effect.destroy();
}
};
})(gainEffect);
// Create +N asset effect at wizard position
var plusAmount = 1;
if (coin.hasOwnProperty('amount')) {
plusAmount = coin.amount;
}
var plusText = new Text2("+" + plusAmount, {
size: 90,
fill: 0xFFD700
});
plusText.anchor.set(0.5, 0.5);
plusText.x = wizard.x;
plusText.y = wizard.y;
game.addChild(plusText);
// Animate +N asset: move up and fade out, then destroy
(function (txt) {
var startY = txt.y;
var frames = 36;
var frame = 0;
txt.alpha = 1;
txt.update = function () {
frame++;
txt.y = startY - frame * 2.2;
txt.alpha = 1 - frame / frames;
if (frame >= frames) {
txt.destroy();
}
};
})(plusText);
// Remove coin
coin.destroy();
coins.splice(c, 1);
continue;
}
coin.lastWasIntersecting = coinIntersecting;
}
// --- Healthpack update and collection ---
if (typeof healthpacks === "undefined") healthpacks = [];
for (var h = healthpacks.length - 1; h >= 0; h--) {
var hp = healthpacks[h];
if (hp.update) hp.update();
if (hp.destroyed) {
healthpacks.splice(h, 1);
continue;
}
if (hp.lastWasIntersecting === undefined) hp.lastWasIntersecting = false;
var hpIntersecting = hp.intersects(wizard);
if (!hp.lastWasIntersecting && hpIntersecting) {
// Restore 2 wizard health, up to max
wizardHealth = Math.min(maxWizardHealth, wizardHealth + 2);
storage.wizardHealth = wizardHealth;
updateHealthBar();
// Flash wizard green for feedback
if (wizard.children && wizard.children.length > 0) {
LK.effects.flashObject(wizard.children[0], 0x2ecc40, 400);
}
// Show health gain effect
var healthGain = LK.getAsset('health', {
anchorX: 0.5,
anchorY: 0.5,
x: wizard.x,
y: wizard.y,
scaleX: 1,
scaleY: 1,
alpha: 1
});
game.addChild(healthGain);
(function (effect) {
var frames = 24;
var frame = 0;
effect.update = function () {
frame++;
effect.scale.x = 1 + 0.5 * (frame / frames);
effect.scale.y = 1 + 0.5 * (frame / frames);
effect.alpha = 1 - frame / frames;
if (frame >= frames) {
effect.destroy();
}
};
})(healthGain);
// Show "+2" text
var plus2 = new Text2("+2", {
size: 90,
fill: 0x2ecc40
});
plus2.anchor.set(0.5, 0.5);
plus2.x = wizard.x;
plus2.y = wizard.y;
game.addChild(plus2);
(function (txt) {
var startY = txt.y;
var frames = 36;
var frame = 0;
txt.alpha = 1;
txt.update = function () {
frame++;
txt.y = startY - frame * 2.2;
txt.alpha = 1 - frame / frames;
if (frame >= frames) {
txt.destroy();
}
};
})(plus2);
// Remove healthpack
hp.destroy();
healthpacks.splice(h, 1);
continue;
}
hp.lastWasIntersecting = hpIntersecting;
}
// Position coin icon and text below the score text (centered)
if (scoreText.parent && coinIcon.parent && coinText.parent) {
// Place coin icon below the score text, centered
coinIcon.x = scoreText.x;
coinIcon.y = scoreText.y + scoreText.height + coinIcon.height * 0.6;
// Place coinText 100px to the right and 50px above the coin icon
coinText.x = coinIcon.x + 100;
coinText.y = coinIcon.y - 50;
// Position health bar under coin asset
if (typeof healthBarBg !== "undefined" && typeof healthBarFill !== "undefined") {
healthBarBg.x = coinIcon.x;
healthBarBg.y = coinIcon.y + coinIcon.height * 0.7 + healthBarBg.height / 2 + 8;
healthBarFill.x = healthBarBg.x - (healthBarBg.width - 8) * (1 - Math.max(0, Math.min(1, wizardHealth / maxWizardHealth))) / 2;
healthBarFill.y = healthBarBg.y;
// Keep label centered in bar
if (typeof healthBarLabel !== "undefined") {
healthBarLabel.x = healthBarBg.x;
healthBarLabel.y = healthBarBg.y;
}
}
updateHealthBar();
// --- Position and update ammo bar below health bar ---
if (typeof ammoBarBg !== "undefined" && typeof ammoBarFill !== "undefined") {
ammoBarBg.x = healthBarBg.x;
ammoBarBg.y = healthBarBg.y + healthBarBg.height / 2 + ammoBarBg.height / 2 + 10;
ammoBarFill.x = ammoBarBg.x;
ammoBarFill.y = ammoBarBg.y;
if (typeof ammoBarLabel !== "undefined") {
ammoBarLabel.x = ammoBarBg.x;
ammoBarLabel.y = ammoBarBg.y;
// Update ammo label text
if (wizardAsset === "shuriken") {
ammoBarLabel.setText("∞/∞");
} else if (typeof wizardAmmo !== "undefined" && typeof wizardMaxAmmo !== "undefined") {
ammoBarLabel.setText(wizardAmmo + "/" + wizardMaxAmmo);
} else {
ammoBarLabel.setText("");
}
}
// Update ammo bar fill width
var ammoRatio = 1;
if (wizardAsset === "shuriken") {
ammoRatio = 1; // always full for shuriken
} else if (typeof wizardAmmo !== "undefined" && typeof wizardMaxAmmo !== "undefined" && wizardMaxAmmo > 0) {
ammoRatio = Math.max(0, Math.min(1, wizardAmmo / wizardMaxAmmo));
}
ammoBarFill.width = (ammoBarBg.width - 8) * ammoRatio;
ammoBarFill.x = ammoBarBg.x - (ammoBarBg.width - 8) * (1 - ammoRatio) / 2;
}
}
// Update enemies and remove destroyed ones
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (enemy.update) enemy.update();
if (enemy.destroyed) {
enemies.splice(j, 1);
continue;
}
// ... (rest of enemy collision code unchanged)
// Check collision with wizard (health system)
if (enemy.lastWasIntersecting === undefined) enemy.lastWasIntersecting = false;
var nowIntersecting = enemy.intersects(wizard);
if (!enemy.lastWasIntersecting && nowIntersecting) {
// Only decrease health if not already at 0
if (wizardHealth > 0) {
wizardHealth -= 1;
// Save wizard health
storage.wizardHealth = wizardHealth;
// Flash wizard red
if (wizard.children && wizard.children.length > 0) {
LK.effects.flashObject(wizard.children[0], 0xff0000, 400);
}
// Update health bar (no-op)
updateHealthBar();
// Remove enemy after hit
// If this is Enemy5, also clear enemy5 reference so it never respawns
if (enemy.type === 5) {
if (typeof enemy5 !== "undefined" && enemy5 === enemy) {
enemy5 = null;
enemy5Spawned = false;
}
}
enemy.destroy();
enemies.splice(j, 1);
// If health reaches 0, trigger game over
if (wizardHealth <= 0) {
totalCoins += coinCount;
storage.totalCoins = totalCoins;
LK.effects.flashScreen(0xff0000, 1000);
// Instead of game over, reset to start screen
gameStarted = false;
// Recreate start UI if needed
if (!startUiContainer || startUiContainer.destroyed) {
startUiContainer = new Container();
startButton = new Container();
startButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: startButtonWidth,
height: startButtonHeight,
color: 0x1e90ff
});
startButtonBg.alpha = 0.92;
startButton.addChild(startButtonBg);
startButtonText = new Text2("START GAME", {
size: 220,
fill: "#fff"
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 0;
startButtonText.y = -120;
startButton.addChild(startButtonText);
upgradeButton = new Container();
upgradeButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: upgradeButtonWidth,
height: upgradeButtonHeight,
color: 0x2ecc71
});
upgradeButtonBg.alpha = 0.92;
upgradeButton.addChild(upgradeButtonBg);
upgradeButtonText = new Text2("UPGRADE", {
size: 110,
// Half of recreated START GAME (220)
fill: "#fff"
});
upgradeButtonText.anchor.set(0.5, 0.5);
upgradeButtonText.x = 0;
upgradeButtonText.y = 0;
upgradeButton.addChild(upgradeButtonText);
startButton.x = 0;
startButton.y = -400;
upgradeButton.x = 0;
upgradeButton.y = startButton.y + startButtonHeight / 2 + 55 + upgradeButtonHeight / 2;
startUiContainer.addChild(startButton);
startUiContainer.addChild(upgradeButton);
startUiContainer.x = 2048 / 2;
startUiContainer.y = 2732 / 2;
game.addChild(startUiContainer);
// Re-attach handlers
upgradeButton.down = function (x, y, obj) {
if (upgradeWindow && !upgradeWindow.destroyed) return;
showUpgradeWindow();
};
startButton.down = function (x, y, obj) {
var now = Date.now();
if (now - lastTapFireTime < tapFireCooldown) {
// Too soon, ignore this tap for firing
return;
}
lastTapFireTime = now;
if (gameStarted) return;
gameStarted = true;
if (startUiContainer && !startUiContainer.destroyed) {
startUiContainer.destroy();
}
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] && !enemies[i].destroyed) enemies[i].destroy();
enemies.splice(i, 1);
}
for (var i = fireballs.length - 1; i >= 0; i--) {
if (fireballs[i] && !fireballs[i].destroyed) fireballs[i].destroy();
fireballs.splice(i, 1);
}
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i] && !coins[i].destroyed) coins[i].destroy();
coins.splice(i, 1);
}
score = 0;
// coinCount is NOT reset here, so coins are preserved when starting a new game
scoreText.setText("Score: 0");
coinText.setText("" + coinCount);
wizard.x = 2048 / 2;
wizard.y = 2732 / 2;
// Load upgrades for selected weapon
currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
// (damageUpgradeLevel and damageUpgradeCost removed)
healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
wizardHealth = currentWeaponUpgrades.wizardHealth;
fireballDamage = currentWeaponUpgrades.fireballDamage;
// Remove all children to ensure only one wizard asset
for (var i = wizard.children.length - 1; i >= 0; i--) {
wizard.removeChild(wizard.children[i]);
}
// Attach wizard asset if not present
var hasWizardSprite = false;
for (var i = 0; i < wizard.children.length; i++) {
hasWizardSprite = true;
break;
}
if (!hasWizardSprite) {
var newSprite = wizard.attachAsset(wizardAsset, {
anchorX: 0.5,
anchorY: 0.5
});
if (newSprite) {
newSprite.rotation = Math.PI;
}
} else if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = Math.PI;
}
// Switch upgrades to selected weapon on restart
currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
// (damageUpgradeLevel and damageUpgradeCost removed)
healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
wizardHealth = currentWeaponUpgrades.wizardHealth;
fireballDamage = currentWeaponUpgrades.fireballDamage;
wizardHealth = maxWizardHealth;
storage.wizardHealth = wizardHealth;
storage.maxWizardHealth = maxWizardHealth;
storage.wizardHealth = wizardHealth;
storage.maxWizardHealth = maxWizardHealth;
updateHealthBar();
game.move = originalGameMove;
game.down = originalGameDown;
game.up = originalGameUp;
};
} else {
// If UI exists, just re-add it
if (startUiContainer.parent !== game) {
game.addChild(startUiContainer);
}
// Recreate shop button and handlers if needed
if (typeof shopButton === "undefined" || !shopButton || shopButton.destroyed) {
// --- SHOP BUTTON UI LOGIC (recreate) ---
shopButton = new Container();
shopButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: shopButtonWidth,
height: shopButtonHeight
});
shopButtonBg.alpha = 0.92;
shopButton.addChild(shopButtonBg);
shopButtonText = new Text2("SHOP", {
size: 110,
fill: "#fff"
});
shopButtonText.anchor.set(0.5, 0.5);
shopButtonText.x = 0;
shopButtonText.y = 0;
shopButton.addChild(shopButtonText);
shopButton.x = 0;
shopButton.y = upgradeButton.y + upgradeButtonHeight / 2 + 40 + shopButtonHeight / 2;
startUiContainer.addChild(shopButton);
shopButton.down = function (x, y, obj) {
if (shopWindow && !shopWindow.destroyed) return;
showShopWindow();
};
}
}
// Block input until start is pressed
blockGameInput();
return;
}
}
}
enemy.lastWasIntersecting = nowIntersecting;
// Check collision with fireballs
for (var k = fireballs.length - 1; k >= 0; k--) {
var fb = fireballs[k];
if (enemy['fb' + k + '_lastIntersecting'] === undefined) enemy['fb' + k + '_lastIntersecting'] = false;
var fbIntersect = enemy.intersects(fb);
// Only allow collision if fireball is inside the visible screen
var fireballOnScreen = fb.x >= 0 && fb.x <= 2048 && fb.y >= 0 && fb.y <= 2732;
if (!enemy['fb' + k + '_lastIntersecting'] && fbIntersect && fireballOnScreen) {
// Decrease enemy health by fireballDamage (default 1, upgrades increase)
// Award 1 coin for each fireball hit on goblin (not killed)
if (enemy.type === 5 && enemy.health > 0) {
// Drop 1 coin at goblin's position
var coin = new Coin();
coin.x = enemy.x;
coin.y = enemy.y;
coin.vx = 0;
coin.vy = 0;
coin.gravity = 0;
coin.amount = 1;
coins.push(coin);
game.addChild(coin);
}
enemy.health -= fireballDamage;
// Destroy fireball
fb.destroy();
fireballs.splice(k, 1);
// If enemy health reaches 0, destroy enemy and increase score
if (enemy.health <= 0) {
// Spawn explosion at enemy position
var explosion = new Explosion();
explosion.x = enemy.x;
explosion.y = enemy.y;
game.addChild(explosion);
// Drop healthpack if Enemy6
if (enemy.type === 6) {
var healthpack = new Healthpack();
healthpack.x = enemy.x;
healthpack.y = enemy.y;
game.addChild(healthpack);
if (typeof healthpacks === "undefined") healthpacks = [];
healthpacks.push(healthpack);
}
// Determine coin count and plus text based on enemy type
var coinAmount = 1;
if (enemy.type === 2) coinAmount = 2;
if (enemy.type === 3) coinAmount = 3;
if (enemy.type === 4) coinAmount = 4;
if (enemy.type === 5) coinAmount = 5;
// Drop the correct number of coins
if (enemy.type === 5) {
// Drop 20 coins at goblin's position when killed
for (var cc = 0; cc < 20; cc++) {
var coin = new Coin();
coin.x = enemy.x;
coin.y = enemy.y;
coin.vx = 0;
coin.vy = 0;
coin.gravity = 0;
if (cc === 0) coin.amount = 20;
coins.push(coin);
game.addChild(coin);
}
} else {
for (var cc = 0; cc < coinAmount; cc++) {
var coin = new Coin();
coin.x = enemy.x;
coin.y = enemy.y;
coin.vx = 0;
coin.vy = 0;
coin.gravity = 0;
// For multi-coin drops, only the first coin will show +N at wizard
if (cc === 0) {
coin.amount = coinAmount;
}
coins.push(coin);
game.addChild(coin);
}
}
// Play dead sound
LK.getSound('dead').play();
// If this is Enemy5, also clear enemy5 reference so it never respawns
if (enemy.type === 5) {
// Show goblingain effect at goblin's position
var goblingain = LK.getAsset('goblingain', {
anchorX: 0.5,
anchorY: 0.5,
x: enemy.x,
y: enemy.y,
scaleX: 1.0,
scaleY: 1.0,
alpha: 1
});
game.addChild(goblingain);
// Animate goblingain: scale up and fade out over 0.7s (42 frames)
(function (gobObj) {
var frames = 42,
frame = 0;
gobObj.update = function () {
frame++;
gobObj.scale.x = 1.0 + 0.5 * (frame / frames);
gobObj.scale.y = 1.0 + 0.5 * (frame / frames);
gobObj.alpha = 1 - frame / frames;
if (frame >= frames) {
gobObj.destroy();
}
};
})(goblingain);
// Mark goblin as permanently killed
enemy5EverKilled = true;
if (typeof enemy5 !== "undefined" && enemy5 === enemy) {
enemy5 = null;
enemy5Spawned = false; // Allow normal enemies to spawn again
}
// Resume normal enemy spawn if it was paused for goblin
// (Handled by enemySpawnTimer logic: normal enemies spawn if enemy5 is null or destroyed)
}
// --- INFERNOMONSTER SPLIT LOGIC ---
if (enemy.type === 99 && typeof InfernoMonster !== "undefined") {
// Split into two smaller InfernoMonsters if not already small
// Only split if not already a "split" (use a flag or reduced health)
if (!enemy._wasSplit && enemy.orbitRadius > 400) {
for (var splitIdx = 0; splitIdx < 2; splitIdx++) {
var splitMonster = new InfernoMonster();
// Place at enemy's position, but offset a bit
var angle = Math.random() * Math.PI * 2;
splitMonster.x = enemy.x + Math.cos(angle) * 80 * (splitIdx === 0 ? 1 : -1);
splitMonster.y = enemy.y + Math.sin(angle) * 80 * (splitIdx === 0 ? 1 : -1);
// Make smaller: half orbit radius, half health, mark as split
splitMonster.orbitRadius = Math.max(400, enemy.orbitRadius * 0.5);
splitMonster.health = Math.max(3, Math.floor(enemy.health * 0.5));
splitMonster._wasSplit = true;
// Randomize orbit direction and angle
splitMonster.orbitAngle = Math.random() * Math.PI * 2;
splitMonster.orbitDirection = Math.random() < 0.5 ? 1 : -1;
// Add to enemies and game
enemies.push(splitMonster);
game.addChild(splitMonster);
}
}
}
enemy.destroy();
enemies.splice(j, 1);
score += 1;
scoreText.setText("Score: " + score);
// (Removed: +N effect at enemy death. Now shown at wizard when coin is collected)
break;
}
}
enemy['fb' + k + '_lastIntersecting'] = fbIntersect;
}
}
// --- Boss update and boss fireball collision ---
if (boss && !boss.destroyed) {
if (boss.update) boss.update();
// Boss hit by fireballs
for (var k = fireballs.length - 1; k >= 0; k--) {
var fb = fireballs[k];
if (boss['fb' + k + '_lastIntersecting'] === undefined) boss['fb' + k + '_lastIntersecting'] = false;
var fbIntersect = boss.intersects(fb);
// Only allow collision if fireball is inside the visible screen
var fireballOnScreen = fb.x >= 0 && fb.x <= 2048 && fb.y >= 0 && fb.y <= 2732;
if (!boss['fb' + k + '_lastIntersecting'] && fbIntersect && fireballOnScreen) {
boss.health -= fireballDamage;
fb.destroy();
fireballs.splice(k, 1);
// Flash boss on hit
if (boss.children && boss.children.length > 0) {
LK.effects.flashObject(boss.children[0], 0xff0000, 200);
}
// Boss death
if (boss.health <= 0) {
// Spawn explosion at boss position
var explosion = new Explosion();
explosion.x = boss.x;
explosion.y = boss.y;
game.addChild(explosion);
// Drop 10 coins
for (var cc = 0; cc < 10; cc++) {
var coin = new Coin();
coin.x = boss.x;
coin.y = boss.y;
coin.vx = 0;
coin.vy = 0;
coin.gravity = 0;
if (cc === 0) coin.amount = 10;
coins.push(coin);
game.addChild(coin);
}
LK.getSound('dead').play();
boss.destroy();
boss = null;
// Clean up all bossBullets after boss death
for (var bb = bossBullets.length - 1; bb >= 0; bb--) {
if (bossBullets[bb] && !bossBullets[bb].destroyed) bossBullets[bb].destroy();
bossBullets.splice(bb, 1);
}
// Award 10 score for boss
score += 10;
scoreText.setText("Score: " + score);
// --- Show UI with two buttons: Slow Timer and Fireball ---
if (typeof bossRewardUi !== "undefined" && bossRewardUi && !bossRewardUi.destroyed) {
bossRewardUi.destroy();
}
bossRewardUi = new Container();
// Background
var rewardBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: 900,
height: 600
});
rewardBg.alpha = 0.98;
bossRewardUi.addChild(rewardBg);
// Title
var rewardTitle = new Text2("Choose Your Reward!", {
size: 110,
fill: "#fff"
});
rewardTitle.anchor.set(0.5, 0.5);
rewardTitle.x = 0;
rewardTitle.y = -180;
bossRewardUi.addChild(rewardTitle);
// Slow Timer Button
var slowBtn = new Container();
var slowBtnBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: 340,
height: 140
});
slowBtnBg.alpha = 0.93;
slowBtn.addChild(slowBtnBg);
// Use timer asset instead of text
var slowBtnIcon = LK.getAsset('timer', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.1,
scaleY: 1.1,
x: 0,
y: 0
});
slowBtn.addChild(slowBtnIcon);
slowBtn.x = -200;
slowBtn.y = 80;
bossRewardUi.addChild(slowBtn);
// Fireball Button
var fireballBtn = new Container();
var fireballBtnBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: 340,
height: 140
});
fireballBtnBg.alpha = 0.93;
fireballBtn.addChild(fireballBtnBg);
// Use firecircle asset instead of text
var fireballBtnIcon = LK.getAsset('firecircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.1,
scaleY: 1.1,
x: 0,
y: 0
});
fireballBtn.addChild(fireballBtnIcon);
fireballBtn.x = 200;
fireballBtn.y = 80;
bossRewardUi.addChild(fireballBtn);
// Center UI
bossRewardUi.x = 2048 / 2;
bossRewardUi.y = 2732 / 2;
game.addChild(bossRewardUi);
// Pause enemy spawn until a button is pressed
enemySpawnStopped = true;
if (enemySpawnTimer) LK.clearInterval(enemySpawnTimer);
// Button handlers: resume game and remove UI
var resumeAfterReward = function resumeAfterReward() {
if (bossRewardUi && !bossRewardUi.destroyed) {
bossRewardUi.destroy();
bossRewardUi = null;
}
// Resume enemy spawn: restore normal spawn logic after boss (no restriction)
enemySpawnStopped = false;
if (enemySpawnTimer) LK.clearInterval(enemySpawnTimer);
enemySpawnTimer = LK.setInterval(function () {
// Pause enemy spawn if boss is present and alive
if (boss && !boss.destroyed) {
return;
}
if (!enemySpawnStopped) {
// After boss, spawn enemies with normal logic but at 0.8s interval
spawnEnemy();
}
}, 800); // spawn every 0.8s after boss
};
slowBtn.down = function (x, y, obj) {
// Start slow timer effect: every 10s, for 2s, slow all enemies by 1/3 speed
resumeAfterReward();
// Defensive: clear any previous intervals/timeouts
if (typeof slowTimerInterval !== "undefined" && slowTimerInterval) {
LK.clearInterval(slowTimerInterval);
slowTimerInterval = null;
}
if (typeof slowTimerTimeout !== "undefined" && slowTimerTimeout) {
LK.clearTimeout(slowTimerTimeout);
slowTimerTimeout = null;
}
// Helper: mark enemies as slowed and restore after 2s
function applySlowToEnemies() {
if (typeof enemies === "undefined") return;
for (var i = 0; i < enemies.length; i++) {
var e = enemies[i];
if (!e || e.destroyed) continue;
// Only slow if not already slowed
if (!e._isSlowed) {
e._origVx = typeof e._origVx === "number" ? e._origVx : e.vx;
e._origVy = typeof e._origVy === "number" ? e._origVy : e.vy;
e.vx = e.vx * (1 / 3);
e.vy = e.vy * (1 / 3);
e._isSlowed = true;
}
}
}
function restoreEnemiesSpeed() {
if (typeof enemies === "undefined") return;
for (var i = 0; i < enemies.length; i++) {
var e = enemies[i];
if (!e || e.destroyed) continue;
if (e._isSlowed) {
// Restore only if we have original values
if (typeof e._origVx === "number") e.vx = e._origVx;
if (typeof e._origVy === "number") e.vy = e._origVy;
e._isSlowed = false;
}
}
}
// Start the repeating slow effect
slowTimerInterval = LK.setInterval(function () {
applySlowToEnemies();
// After 4s, restore speed
slowTimerTimeout = LK.setTimeout(function () {
restoreEnemiesSpeed();
}, 4000);
}, 10000);
// Defensive: also restore on game over or restart
if (typeof stopSlowTimerEffect === "undefined") {
stopSlowTimerEffect = function stopSlowTimerEffect() {
if (typeof slowTimerInterval !== "undefined" && slowTimerInterval) {
LK.clearInterval(slowTimerInterval);
slowTimerInterval = null;
}
if (typeof slowTimerTimeout !== "undefined" && slowTimerTimeout) {
LK.clearTimeout(slowTimerTimeout);
slowTimerTimeout = null;
}
restoreEnemiesSpeed();
};
}
};
fireballBtn.down = function (x, y, obj) {
// FireCircle reward: spawn a single permanent, rotating firecircle
resumeAfterReward();
// Remove any previous firecircle timers
if (typeof fireCircleInterval !== "undefined" && fireCircleInterval) {
LK.clearInterval(fireCircleInterval);
fireCircleInterval = null;
}
if (typeof fireCircleTimeout !== "undefined" && fireCircleTimeout) {
LK.clearTimeout(fireCircleTimeout);
fireCircleTimeout = null;
}
// Remove any existing firecircles
if (typeof fireCircles === "undefined") fireCircles = [];
for (var i = fireCircles.length - 1; i >= 0; i--) {
if (fireCircles[i] && !fireCircles[i].destroyed) fireCircles[i].destroy();
fireCircles.splice(i, 1);
}
// Spawn a single permanent FireCircle
var fc = new FireCircle();
fc.orbitAngle = Math.random() * Math.PI * 2;
if (typeof wizard !== "undefined" && wizard && !wizard.destroyed) {
fc.centerX = wizard.x;
fc.centerY = wizard.y;
}
game.addChild(fc);
fireCircles.push(fc);
};
break;
}
}
boss['fb' + k + '_lastIntersecting'] = fbIntersect;
}
// --- BossBullet update, collision with wizard and fireballs ---
}
// Update bossBullets
for (var b = bossBullets.length - 1; b >= 0; b--) {
var bullet = bossBullets[b];
if (bullet.update) bullet.update();
if (bullet.destroyed) {
bossBullets.splice(b, 1);
continue;
}
// Collision with wizard
if (bullet.lastWasIntersecting === undefined) bullet.lastWasIntersecting = false;
var bulletIntersecting = bullet.intersects(wizard);
if (!bullet.lastWasIntersecting && bulletIntersecting) {
// Wizard takes 1 damage
if (wizardHealth > 0) {
wizardHealth -= 1;
storage.wizardHealth = wizardHealth;
if (wizard.children && wizard.children.length > 0) {
LK.effects.flashObject(wizard.children[0], 0xff0000, 400);
}
updateHealthBar();
// If health reaches 0, trigger game over and return to main screen
if (wizardHealth <= 0) {
totalCoins += coinCount;
storage.totalCoins = totalCoins;
LK.effects.flashScreen(0xff0000, 1000);
gameStarted = false;
// Recreate start UI if needed
if (!startUiContainer || startUiContainer.destroyed) {
startUiContainer = new Container();
startButton = new Container();
startButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: startButtonWidth,
height: startButtonHeight,
color: 0x1e90ff
});
startButtonBg.alpha = 0.92;
startButton.addChild(startButtonBg);
startButtonText = new Text2("START GAME", {
size: 220,
fill: "#fff"
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 0;
startButtonText.y = -120;
startButton.addChild(startButtonText);
upgradeButton = new Container();
upgradeButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: upgradeButtonWidth,
height: upgradeButtonHeight,
color: 0x2ecc71
});
upgradeButtonBg.alpha = 0.92;
upgradeButton.addChild(upgradeButtonBg);
upgradeButtonText = new Text2("UPGRADE", {
size: 110,
// Half of recreated START GAME (220)
fill: "#fff"
});
upgradeButtonText.anchor.set(0.5, 0.5);
upgradeButtonText.x = 0;
upgradeButtonText.y = 0;
upgradeButton.addChild(upgradeButtonText);
startButton.x = 0;
startButton.y = -400;
upgradeButton.x = 0;
upgradeButton.y = startButton.y + startButtonHeight / 2 + 55 + upgradeButtonHeight / 2;
startUiContainer.addChild(startButton);
startUiContainer.addChild(upgradeButton);
startUiContainer.x = 2048 / 2;
startUiContainer.y = 2732 / 2;
game.addChild(startUiContainer);
// Re-attach handlers
upgradeButton.down = function (x, y, obj) {
if (upgradeWindow && !upgradeWindow.destroyed) return;
showUpgradeWindow();
};
startButton.down = function (x, y, obj) {
var now = Date.now();
if (now - lastTapFireTime < tapFireCooldown) {
// Too soon, ignore this tap for firing
return;
}
lastTapFireTime = now;
if (gameStarted) return;
gameStarted = true;
if (startUiContainer && !startUiContainer.destroyed) {
startUiContainer.destroy();
}
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] && !enemies[i].destroyed) enemies[i].destroy();
enemies.splice(i, 1);
}
for (var i = fireballs.length - 1; i >= 0; i--) {
if (fireballs[i] && !fireballs[i].destroyed) fireballs[i].destroy();
fireballs.splice(i, 1);
}
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i] && !coins[i].destroyed) coins[i].destroy();
coins.splice(i, 1);
}
score = 0;
// coinCount is NOT reset here, so coins are preserved when starting a new game
scoreText.setText("Score: 0");
coinText.setText("" + coinCount);
wizard.x = 2048 / 2;
wizard.y = 2732 / 2;
// Load upgrades for selected weapon
currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
// (damageUpgradeLevel and damageUpgradeCost removed)
healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
wizardHealth = currentWeaponUpgrades.wizardHealth;
fireballDamage = currentWeaponUpgrades.fireballDamage;
// Remove all children to ensure only one wizard asset
for (var i = wizard.children.length - 1; i >= 0; i--) {
wizard.removeChild(wizard.children[i]);
}
// Attach wizard asset if not present
var hasWizardSprite = false;
for (var i = 0; i < wizard.children.length; i++) {
hasWizardSprite = true;
break;
}
if (!hasWizardSprite) {
var newSprite = wizard.attachAsset(wizardAsset, {
anchorX: 0.5,
anchorY: 0.5
});
if (newSprite) {
newSprite.rotation = Math.PI;
}
} else if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = Math.PI;
}
// Switch upgrades to selected weapon on restart
currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
// (damageUpgradeLevel and damageUpgradeCost removed)
healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
wizardHealth = currentWeaponUpgrades.wizardHealth;
fireballDamage = currentWeaponUpgrades.fireballDamage;
wizardHealth = maxWizardHealth;
storage.wizardHealth = wizardHealth;
storage.maxWizardHealth = maxWizardHealth;
storage.wizardHealth = wizardHealth;
storage.maxWizardHealth = maxWizardHealth;
updateHealthBar();
game.move = originalGameMove;
game.down = originalGameDown;
game.up = originalGameUp;
};
} else {
// If UI exists, just re-add it
if (startUiContainer.parent !== game) {
game.addChild(startUiContainer);
}
// Recreate shop button and handlers if needed
if (typeof shopButton === "undefined" || !shopButton || shopButton.destroyed) {
// --- SHOP BUTTON UI LOGIC (recreate) ---
shopButton = new Container();
shopButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: shopButtonWidth,
height: shopButtonHeight
});
shopButtonBg.alpha = 0.92;
shopButton.addChild(shopButtonBg);
shopButtonText = new Text2("SHOP", {
size: 110,
fill: "#fff"
});
shopButtonText.anchor.set(0.5, 0.5);
shopButtonText.x = 0;
shopButtonText.y = 0;
shopButton.addChild(shopButtonText);
shopButton.x = 0;
shopButton.y = upgradeButton.y + upgradeButtonHeight / 2 + 40 + shopButtonHeight / 2;
startUiContainer.addChild(shopButton);
shopButton.down = function (x, y, obj) {
if (shopWindow && !shopWindow.destroyed) return;
showShopWindow();
};
}
}
// Block input until start is pressed
blockGameInput();
return;
}
}
// Destroy bullet
bullet.destroy();
bossBullets.splice(b, 1);
// If health reaches 0, trigger game over (handled above)
continue;
}
bullet.lastWasIntersecting = bulletIntersecting;
// Collision with fireballs
for (var k = fireballs.length - 1; k >= 0; k--) {
var fb = fireballs[k];
if (bullet['fb' + k + '_lastIntersecting'] === undefined) bullet['fb' + k + '_lastIntersecting'] = false;
var fbIntersect = bullet.intersects(fb);
var fireballOnScreen = fb.x >= 0 && fb.x <= 2048 && fb.y >= 0 && fb.y <= 2732;
if (!bullet['fb' + k + '_lastIntersecting'] && fbIntersect && fireballOnScreen) {
bullet.health -= fireballDamage;
fb.destroy();
fireballs.splice(k, 1);
// Flash bullet on hit
if (bullet.children && bullet.children.length > 0) {
LK.effects.flashObject(bullet.children[0], 0xff0000, 200);
}
// If bullet destroyed, spawn explosion and remove
if (bullet.health <= 0) {
var explosion = new Explosion();
explosion.x = bullet.x;
explosion.y = bullet.y;
game.addChild(explosion);
bullet.destroy();
bossBullets.splice(b, 1);
break;
}
}
bullet['fb' + k + '_lastIntersecting'] = fbIntersect;
}
}
// --- InfernoBullet update, collision with wizard and fireballs ---
if (typeof infernoBullets !== "undefined") {
for (var ib = infernoBullets.length - 1; ib >= 0; ib--) {
var ibullet = infernoBullets[ib];
if (ibullet.update) ibullet.update();
if (ibullet.destroyed) {
infernoBullets.splice(ib, 1);
continue;
}
// Collision with wizard
if (ibullet.lastWasIntersecting === undefined) ibullet.lastWasIntersecting = false;
var ibulletIntersecting = ibullet.intersects(wizard);
if (!ibullet.lastWasIntersecting && ibulletIntersecting) {
// Wizard takes 1 damage
if (wizardHealth > 0) {
wizardHealth -= 1;
storage.wizardHealth = wizardHealth;
if (wizard.children && wizard.children.length > 0) {
LK.effects.flashObject(wizard.children[0], 0xff0000, 400);
}
updateHealthBar();
// If health reaches 0, trigger game over and return to main screen
if (wizardHealth <= 0) {
totalCoins += coinCount;
storage.totalCoins = totalCoins;
LK.effects.flashScreen(0xff0000, 1000);
gameStarted = false;
// Recreate start UI if needed
if (!startUiContainer || startUiContainer.destroyed) {
startUiContainer = new Container();
startButton = new Container();
startButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: startButtonWidth,
height: startButtonHeight,
color: 0x1e90ff
});
startButtonBg.alpha = 0.92;
startButton.addChild(startButtonBg);
startButtonText = new Text2("START GAME", {
size: 220,
fill: "#fff"
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 0;
startButtonText.y = -120;
startButton.addChild(startButtonText);
upgradeButton = new Container();
upgradeButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: upgradeButtonWidth,
height: upgradeButtonHeight,
color: 0x2ecc71
});
upgradeButtonBg.alpha = 0.92;
upgradeButton.addChild(upgradeButtonBg);
upgradeButtonText = new Text2("UPGRADE", {
size: 110,
fill: "#fff"
});
upgradeButtonText.anchor.set(0.5, 0.5);
upgradeButtonText.x = 0;
upgradeButtonText.y = 0;
upgradeButton.addChild(upgradeButtonText);
startButton.x = 0;
startButton.y = -400;
upgradeButton.x = 0;
upgradeButton.y = startButton.y + startButtonHeight / 2 + 55 + upgradeButtonHeight / 2;
startUiContainer.addChild(startButton);
startUiContainer.addChild(upgradeButton);
startUiContainer.x = 2048 / 2;
startUiContainer.y = 2732 / 2;
game.addChild(startUiContainer);
// Re-attach handlers
upgradeButton.down = function (x, y, obj) {
if (upgradeWindow && !upgradeWindow.destroyed) return;
showUpgradeWindow();
};
startButton.down = function (x, y, obj) {
var now = Date.now();
if (now - lastTapFireTime < tapFireCooldown) {
// Too soon, ignore this tap for firing
return;
}
lastTapFireTime = now;
if (gameStarted) return;
gameStarted = true;
if (startUiContainer && !startUiContainer.destroyed) {
startUiContainer.destroy();
}
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] && !enemies[i].destroyed) enemies[i].destroy();
enemies.splice(i, 1);
}
for (var i = fireballs.length - 1; i >= 0; i--) {
if (fireballs[i] && !fireballs[i].destroyed) fireballs[i].destroy();
fireballs.splice(i, 1);
}
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i] && !coins[i].destroyed) coins[i].destroy();
coins.splice(i, 1);
}
score = 0;
// coinCount is NOT reset here, so coins are preserved when starting a new game
scoreText.setText("Score: 0");
coinText.setText("" + coinCount);
wizard.x = 2048 / 2;
wizard.y = 2732 / 2;
// Load upgrades for selected weapon
currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
// (damageUpgradeLevel and damageUpgradeCost removed)
healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
wizardHealth = currentWeaponUpgrades.wizardHealth;
fireballDamage = currentWeaponUpgrades.fireballDamage;
// Remove all children to ensure only one wizard asset
for (var i = wizard.children.length - 1; i >= 0; i--) {
wizard.removeChild(wizard.children[i]);
}
// Attach wizard asset if not present
var hasWizardSprite = false;
for (var i = 0; i < wizard.children.length; i++) {
hasWizardSprite = true;
break;
}
if (!hasWizardSprite) {
var newSprite = wizard.attachAsset(wizardAsset, {
anchorX: 0.5,
anchorY: 0.5
});
if (newSprite) {
newSprite.rotation = Math.PI;
}
} else if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = Math.PI;
}
// Switch upgrades to selected weapon on restart
currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
// (damageUpgradeLevel and damageUpgradeCost removed)
healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
wizardHealth = currentWeaponUpgrades.wizardHealth;
fireballDamage = currentWeaponUpgrades.fireballDamage;
wizardHealth = maxWizardHealth;
storage.wizardHealth = wizardHealth;
storage.maxWizardHealth = maxWizardHealth;
storage.wizardHealth = wizardHealth;
storage.maxWizardHealth = maxWizardHealth;
updateHealthBar();
game.move = originalGameMove;
game.down = originalGameDown;
game.up = originalGameUp;
};
} else {
// If UI exists, just re-add it
if (startUiContainer.parent !== game) {
game.addChild(startUiContainer);
}
// Recreate shop button and handlers if needed
if (typeof shopButton === "undefined" || !shopButton || shopButton.destroyed) {
// --- SHOP BUTTON UI LOGIC (recreate) ---
shopButton = new Container();
shopButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: shopButtonWidth,
height: shopButtonHeight
});
shopButtonBg.alpha = 0.92;
shopButton.addChild(shopButtonBg);
shopButtonText = new Text2("SHOP", {
size: 110,
fill: "#fff"
});
shopButtonText.anchor.set(0.5, 0.5);
shopButtonText.x = 0;
shopButtonText.y = 0;
shopButton.addChild(shopButtonText);
shopButton.x = 0;
shopButton.y = upgradeButton.y + upgradeButtonHeight / 2 + 40 + shopButtonHeight / 2;
startUiContainer.addChild(shopButton);
shopButton.down = function (x, y, obj) {
if (shopWindow && !shopWindow.destroyed) return;
showShopWindow();
};
}
}
// Block input until start is pressed
blockGameInput();
return;
}
}
// Destroy bullet
ibullet.destroy();
infernoBullets.splice(ib, 1);
continue;
}
ibullet.lastWasIntersecting = ibulletIntersecting;
// Collision with fireballs
for (var k = fireballs.length - 1; k >= 0; k--) {
var fb = fireballs[k];
if (ibullet['fb' + k + '_lastIntersecting'] === undefined) ibullet['fb' + k + '_lastIntersecting'] = false;
var fbIntersect = ibullet.intersects(fb);
var fireballOnScreen = fb.x >= 0 && fb.x <= 2048 && fb.y >= 0 && fb.y <= 2732;
if (!ibullet['fb' + k + '_lastIntersecting'] && fbIntersect && fireballOnScreen) {
// InfernoBullet yok olur, fireball da yok olur
fb.destroy();
fireballs.splice(k, 1);
// Flash inferno bullet on hit
if (ibullet.children && ibullet.children.length > 0) {
LK.effects.flashObject(ibullet.children[0], 0xff0000, 200);
}
// Destroy inferno bullet
ibullet.destroy();
infernoBullets.splice(ib, 1);
break;
}
ibullet['fb' + k + '_lastIntersecting'] = fbIntersect;
}
}
}
}; /****
* Plugins
****/
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Boss class: fires directly at wizard, uses boss asset and BossBullet
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossSprite = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 30;
self.lastX = 0;
self.lastY = 0;
self.fireCooldown = 0;
self.fireInterval = 200; // frames (~3.3s at 60fps) - boss fires even less often
self.speed = 6;
self.state = "enter"; // "enter" or "fight"
self.update = function () {
// Enter from top, then stop and start fighting
if (self.state === "enter") {
self.y += self.speed;
if (self.y >= 400) {
self.y = 400;
self.state = "fight";
// Initialize orbit parameters
self.orbitAngle = 0;
self.orbitRadius = 850; // Even wider orbit
self.orbitSpeed = 0.006 + Math.random() * 0.003; // 3te1 daha yavaş (1/3 speed)
self.orbitDirection = Math.random() < 0.5 ? 1 : -1; // random direction
self.orbitDirChangeTimer = 0; // For smooth direction change
self.orbitDirTarget = self.orbitDirection;
}
} else if (self.state === "fight") {
// Orbit around wizard
if (typeof self.orbitAngle === "undefined") self.orbitAngle = 0;
if (typeof self.orbitRadius === "undefined") self.orbitRadius = 650; // Match new default
if (typeof self.orbitSpeed === "undefined") self.orbitSpeed = 0.012 + Math.random() * 0.008; // Match new default
if (typeof self.orbitDirection === "undefined") self.orbitDirection = 1;
// Smoothly change orbit direction every 2-4 seconds
if (typeof self.orbitDirChangeTimer === "undefined") self.orbitDirChangeTimer = 0;
if (typeof self.orbitDirTarget === "undefined") self.orbitDirTarget = self.orbitDirection;
self.orbitDirChangeTimer--;
if (self.orbitDirChangeTimer <= 0) {
// Pick a new direction: -1, 1, or a random value in between for smoothness
var newTarget = Math.random() < 0.5 ? -1 : 1;
// Occasionally pick a value in between for smoothness
if (Math.random() < 0.4) newTarget = (Math.random() - 0.5) * 2;
self.orbitDirTarget = newTarget;
self.orbitDirChangeTimer = 120 + Math.floor(Math.random() * 120); // 2-4 seconds
}
// Smoothly interpolate orbitDirection toward orbitDirTarget
self.orbitDirection += (self.orbitDirTarget - self.orbitDirection) * 0.04;
// Update orbit angle
self.orbitAngle += self.orbitSpeed * self.orbitDirection;
// Orbit center is wizard's current position
var centerX = wizard.x;
var centerY = wizard.y;
// Calculate boss position on orbit
self.x = centerX + Math.cos(self.orbitAngle) * self.orbitRadius;
self.y = centerY + Math.sin(self.orbitAngle) * self.orbitRadius;
// Always face wizard (for firing), but keep boss sprite visually fixed
var dx = wizard.x - self.x;
var dy = wizard.y - self.y;
var angle = Math.atan2(dy, dx);
// Do NOT rotate boss sprite visually; keep it fixed
// (But still use 'angle' for boss bullet firing below)
// Fire at wizard every interval
self.fireCooldown--;
if (self.fireCooldown <= 0) {
// Fire BossBullet at wizard
var bullet = new BossBullet();
bullet.x = self.x;
bullet.y = self.y;
// Calculate direction to wizard
var dx = wizard.x - self.x;
var dy = wizard.y - self.y;
var len = Math.sqrt(dx * dx + dy * dy);
var speed = 5; // 300 units/sec at 60fps
if (len > 0) {
bullet.vx = dx / len * speed;
bullet.vy = dy / len * speed;
}
// Rotate bullet sprite to face direction
if (bullet.children && bullet.children.length > 0) {
bullet.children[0].rotation = Math.atan2(bullet.vy, bullet.vx);
}
if (typeof bossBullets !== "undefined") {
bossBullets.push(bullet);
game.addChild(bullet);
}
self.fireCooldown = self.fireInterval;
}
}
// Animate boss (pulse)
var t = LK.ticks || Date.now();
var scale = 1 + 0.18 * Math.sin((t + self.x + self.y) * 0.06);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// BossBullet: fired by boss, moves toward wizard, 2 health, can be destroyed by fireballs
var BossBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('bossbullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.health = 2;
self.lastX = 0;
self.lastY = 0;
self.lastWasIntersecting = false;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
// Remove if off screen
if (self.x < -120 || self.x > 2048 + 120 || self.y < -120 || self.y > 2732 + 120) {
self.destroy();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Game update: update wizard, fireballs, enemies, handle collisions, and scoring
// Coin class: represents coins dropped by enemies when destroyed
var Coin = Container.expand(function () {
var self = Container.call(this);
// Create a gold coin visual (using a yellow circle)
var coinSprite = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
// Coin movement properties
self.vx = 0;
self.vy = 0;
self.gravity = 0;
self.collected = false;
// Track last position for event triggers
self.lastX = 0;
self.lastY = 0;
self.lastWasIntersecting = false;
self.update = function () {
// No gravity or bounce logic needed
// Remove coin if it leaves the screen horizontally
if (self.x < -50 || self.x > 2048 + 50) {
self.destroy();
}
// Pulsate slightly for visual effect
var t = LK.ticks || Date.now();
var scale = 1 + 0.1 * Math.sin(t * 0.1);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Enemy class: represents an enemy that moves toward the wizard
var Enemy = Container.expand(function () {
var self = Container.call(this);
// Attach a unique enemy asset (pixel blue monster)
var enemySprite = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Enemy speed and direction
self.vx = 0;
self.vy = 0;
// Track last position for event triggers
self.lastX = 0;
self.lastY = 0;
self.health = 1; // Enemy1: 1 hit to kill
self.type = 1;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
// Pulsate scale for movement effect
var t = LK.ticks || Date.now(); // fallback for safety
var scale = 1 + 0.15 * Math.sin((t + self.x + self.y) * 0.05);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
// Remove enemy if it leaves the screen
if (self.x < -100 || self.x > 2048 + 100 || self.y < -100 || self.y > 2732 + 100) {
self.destroy();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Enemy2: needs 2 fireballs to die
var Enemy2 = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.lastX = 0;
self.lastY = 0;
self.health = 2;
self.type = 2;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
var t = LK.ticks || Date.now();
var scale = 1 + 0.18 * Math.sin((t + self.x + self.y) * 0.06);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
if (self.x < -100 || self.x > 2048 + 100 || self.y < -100 || self.y > 2732 + 100) {
self.destroy();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Enemy3: needs 3 fireballs to die
var Enemy3 = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy3', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.lastX = 0;
self.lastY = 0;
self.health = 3;
self.type = 3;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
var t = LK.ticks || Date.now();
var scale = 1 + 0.22 * Math.sin((t + self.x + self.y) * 0.07);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
if (self.x < -100 || self.x > 2048 + 100 || self.y < -100 || self.y > 2732 + 100) {
self.destroy();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Enemy4: needs 4 fireballs to die
var Enemy4 = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy4', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.lastX = 0;
self.lastY = 0;
self.health = 4;
self.type = 4;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
var t = LK.ticks || Date.now();
var scale = 1 + 0.26 * Math.sin((t + self.x + self.y) * 0.08);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
if (self.x < -100 || self.x > 2048 + 100 || self.y < -100 || self.y > 2732 + 100) {
self.destroy();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Enemy5: Goblin, appears at score 5, moves freely but never enters the center area
var Enemy5 = Container.expand(function () {
var self = Container.call(this);
var goblinSprite = self.attachAsset('goblin', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 20;
self.type = 5;
self.lastX = 0;
self.lastY = 0;
// --- Free movement variables ---
self.vx = (Math.random() - 0.5) * 13 + (Math.random() < 0.5 ? -7 : 7); // random speed, avoid 0
self.vy = (Math.random() - 0.5) * 13 + (Math.random() < 0.5 ? -7 : 7);
self.changeDirCooldown = 0;
// Start at a random edge
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// top
self.x = Math.random() * 2048;
self.y = -90;
} else if (edge === 1) {
// bottom
self.x = Math.random() * 2048;
self.y = 2732 + 90;
} else if (edge === 2) {
// left
self.x = -90;
self.y = Math.random() * 2732;
} else {
// right
self.x = 2048 + 90;
self.y = Math.random() * 2732;
}
// --- Center area to avoid ---
var centerAreaX = 2048 / 2;
var centerAreaY = 2732 / 2;
var centerRadius = 420; // px, forbidden zone
// --- Timed self-destruction after 15 seconds ---
self.spawnTick = typeof LK.ticks !== "undefined" ? LK.ticks : 0;
self.smokeShown = false;
self.update = function () {
// Move freely
self.x += self.vx;
self.y += self.vy;
// Bounce off screen edges
if (self.x < 0 + 90 && self.vx < 0) self.vx = Math.abs(self.vx);
if (self.x > 2048 - 90 && self.vx > 0) self.vx = -Math.abs(self.vx);
if (self.y < 0 + 90 && self.vy < 0) self.vy = Math.abs(self.vy);
if (self.y > 2732 - 90 && self.vy > 0) self.vy = -Math.abs(self.vy);
// Never enter the center forbidden area
var dx = self.x - centerAreaX;
var dy = self.y - centerAreaY;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < centerRadius) {
// Push goblin out of the center area
var angle = Math.atan2(dy, dx);
self.x = centerAreaX + Math.cos(angle) * (centerRadius + 2);
self.y = centerAreaY + Math.sin(angle) * (centerRadius + 2);
// Bounce away
self.vx = Math.cos(angle) * (8 + Math.random() * 5);
self.vy = Math.sin(angle) * (8 + Math.random() * 5);
}
// Occasionally change direction randomly
self.changeDirCooldown--;
if (self.changeDirCooldown <= 0) {
var angle = Math.atan2(self.vy, self.vx) + (Math.random() - 0.5) * 0.7;
var speed = 7 + Math.random() * 6;
self.vx = Math.cos(angle) * speed;
self.vy = Math.sin(angle) * speed;
self.changeDirCooldown = 60 + Math.floor(Math.random() * 60); // 1-2 seconds
}
// Animate goblin (pulse)
var t = LK.ticks || Date.now();
var scale = 1 + 0.18 * Math.sin((t + self.x + self.y) * 0.09);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
// --- Timed self-destruction after 15 seconds ---
if (!self.smokeShown && typeof LK.ticks !== "undefined" && LK.ticks - self.spawnTick >= 900) {
// 15s at 60fps
// Show smoke at goblin's position
var smoke = LK.getAsset('Smoke', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
scaleX: 1.0,
scaleY: 1.0,
alpha: 1
});
game.addChild(smoke);
// Animate smoke: fade out and scale up over 0.7s (42 frames)
(function (smokeObj) {
var frames = 42,
frame = 0;
smokeObj.update = function () {
frame++;
smokeObj.scale.x = 1.0 + 0.5 * (frame / frames);
smokeObj.scale.y = 1.0 + 0.5 * (frame / frames);
smokeObj.alpha = 1 - frame / frames;
if (frame >= frames) {
smokeObj.destroy();
}
};
})(smoke);
self.smokeShown = true;
// Mark goblin as permanently killed (so it never respawns)
enemy5EverKilled = true;
// Remove goblin from game
self.destroy();
// Remove from enemies array in game.update (handled by main loop)
// enemy5 reference will be cleared in main loop on destroy
if (typeof enemy5 !== "undefined" && enemy5 === self) {
enemy5 = null;
enemy5Spawned = false;
}
return;
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Enemy6: needs 6 fireballs to die, drops healthpack on death
var Enemy6 = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy6', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.lastX = 0;
self.lastY = 0;
self.health = 2;
self.type = 6;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
var t = LK.ticks || Date.now();
var scale = 1 + 0.18 * Math.sin((t + self.x + self.y) * 0.09);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
if (self.x < -100 || self.x > 2048 + 100 || self.y < -100 || self.y > 2732 + 100) {
self.destroy();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Explosion class: shows a quick explosion effect at (x, y)
var Explosion = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
// Initial scale and alpha
sprite.scale.x = 0.5;
sprite.scale.y = 0.5;
sprite.alpha = 1;
// Animate explosion: grow and fade out
var duration = 18; // frames (~0.3s)
var frame = 0;
self.update = function () {
frame++;
// Grow and fade
var progress = frame / duration;
sprite.scale.x = 0.5 + 1.2 * progress;
sprite.scale.y = 0.5 + 1.2 * progress;
sprite.alpha = 1 - progress;
if (frame >= duration) {
self.destroy();
}
};
return self;
});
// FireCircle: orbiting fire damage area after boss reward
var FireCircle = Container.expand(function () {
var self = Container.call(this);
// Attach firecircle asset
var sprite = self.attachAsset('firecircle', {
anchorX: 0.5,
anchorY: 0.5,
rotation: -Math.PI / 2 // 90 derece yukarıya döndür (saat yönünün tersine)
});
// Orbit parameters
self.orbitRadius = 850;
self.orbitAngle = 0;
self.orbitSpeed = 0.009; // Yarı yarıya yavaşlatıldı (half speed)
self.centerX = 2048 / 2;
self.centerY = 2732 / 2;
self.damage = 2; // FireCircle now deals 2 damage per hit
self.enemiesHit = {}; // Track which enemies have been hit this frame
self.update = function () {
// Orbit around wizard (or last known wizard position)
if (typeof wizard !== "undefined" && wizard && !wizard.destroyed) {
self.centerX = wizard.x;
self.centerY = wizard.y;
}
self.orbitAngle += self.orbitSpeed;
self.x = self.centerX + Math.cos(self.orbitAngle) * self.orbitRadius;
self.y = self.centerY + Math.sin(self.orbitAngle) * self.orbitRadius;
// Animate (pulse + spin)
var t = LK.ticks || Date.now();
// Make pulse 3 times slower: divide frequency by 3 (multiply period by 3)
var scale = 1 + 0.08 * Math.sin((t + self.x + self.y) * (0.09 / 3));
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
// Add 360-degree spin effect (rotate asset as it orbits)
self.children[0].rotation = self.orbitAngle;
}
// Damage enemies that intersect, every frame (permanent, not once per activation)
if (typeof enemies !== "undefined") {
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (!enemy || enemy.destroyed) continue;
// Assign a unique id if not present
if (typeof enemy._fireCircleId === "undefined") {
enemy._fireCircleId = Math.random().toString(36).substr(2, 9) + Date.now();
}
// Only damage once per frame
if (!self.enemiesHit[enemy._fireCircleId] && self.intersects(enemy)) {
// If enemy has 2 or 1 health, instantly kill
if (enemy.health === 2 || enemy.health === 1) {
enemy.health = 0;
} else {
enemy.health -= self.damage;
}
self.enemiesHit[enemy._fireCircleId] = true;
// Flash enemy for feedback
if (enemy.children && enemy.children.length > 0) {
LK.effects.flashObject(enemy.children[0], 0xff6600, 200);
}
// If enemy dies, handle in main game.update
}
}
}
// Reset hit tracking for next frame
self.enemiesHit = {};
};
return self;
});
// Fireball class: represents a fireball shot by the wizard
var Fireball = Container.expand(function () {
var self = Container.call(this);
// Attach fireball asset (pixel fireball)
var fireballSprite = self.attachAsset('fireball', {
anchorX: 0.5,
anchorY: 0.5
});
// Fireball speed and direction
self.vx = 0;
self.vy = 0;
// Track last position for event triggers
self.lastX = 0;
self.lastY = 0;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
// Remove fireball if it leaves the screen
if (self.x < -100 || self.x > 2048 + 100 || self.y < -100 || self.y > 2732 + 100) {
self.destroy();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Healthpack class: dropped by Enemy6, restores 2 wizard health when collected
var Healthpack = Container.expand(function () {
var self = Container.call(this);
// Attach healthpack asset
var sprite = self.attachAsset('healthpack', {
anchorX: 0.5,
anchorY: 0.5
});
// Track last position for event triggers
self.lastX = 0;
self.lastY = 0;
self.lastWasIntersecting = false;
self.update = function () {
// Move toward wizard like coin
if (typeof wizard !== "undefined" && wizard) {
var dx = wizard.x - self.x;
var dy = wizard.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var speed = 22;
if (dist > 1) {
self.x += dx / dist * Math.min(speed, dist);
self.y += dy / dist * Math.min(speed, dist);
} else {
self.x = wizard.x;
self.y = wizard.y;
}
}
// Pulsate for effect
var t = LK.ticks || Date.now();
var scale = 1 + 0.12 * Math.sin((t + self.x + self.y) * 0.13);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// InfernoBullet: InfernoMonster'ın ateşlediği mermi
var InfernoBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('infernobullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.lastX = 0;
self.lastY = 0;
self.lastWasIntersecting = false;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
// Ekran dışına çıkarsa yok et
if (self.x < -120 || self.x > 2048 + 120 || self.y < -120 || self.y > 2732 + 120) {
self.destroy();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// InfernoMonster: orbits at 850 radius, uses 'inferno' asset, appears at score 5
var InfernoMonster = Container.expand(function () {
var self = Container.call(this);
// Attach inferno asset
var infernoSprite = self.attachAsset('inferno', {
anchorX: 0.5,
anchorY: 0.5
});
// Orbit parameters
self.orbitRadius = 850;
self.orbitAngle = 0;
self.orbitSpeed = (0.012 + Math.random() * 0.008) * 0.5;
self.orbitDirection = Math.random() < 0.5 ? 1 : -1;
self.lastX = 0;
self.lastY = 0;
self.health = 50; // 50 health as required
self.type = 99; // Unique type for inferno
// Add: timer for direction change (başlangıç 3 saniye, sonra 4-5-6-7)
self._reverseDirTimer = 180; // 3 seconds at 60fps
self._reverseDirStage = 0; // 0:3s, 1:4s, 2:5s, 3:6s, 4+:7s
// --- InfernoMonster Fire Logic ---
self.fireCooldown = 0;
self.fireInterval = 90 + Math.floor(Math.random() * 60); // Ateş aralığı: 1.5-2.5 saniye (90-150 frame)
// --- InfernoMonster ateşlerini globalde tut ---
if (typeof infernoBullets === "undefined") infernoBullets = [];
self.update = function () {
// Orbit around wizard
if (typeof wizard !== "undefined" && wizard && !wizard.destroyed) {
// Reverse direction every N seconds (3,4,5,6,7)
self._reverseDirTimer--;
if (self._reverseDirTimer <= 0) {
self.orbitDirection *= -1;
// Timer sequence: 3s, 4s, 5s, 6s, 7s, 7s, 7s...
if (typeof self._reverseDirStage === "undefined") self._reverseDirStage = 0;
self._reverseDirStage++;
var nextTimes = [240, 300, 360, 420, 420]; // 4s, 5s, 6s, 7s, 7s...
var idx = self._reverseDirStage - 1;
if (idx < nextTimes.length) {
self._reverseDirTimer = nextTimes[idx];
} else {
self._reverseDirTimer = 420; // Stay at 7s
}
}
self.orbitAngle += self.orbitSpeed * self.orbitDirection;
self.x = wizard.x + Math.cos(self.orbitAngle) * self.orbitRadius;
self.y = wizard.y + Math.sin(self.orbitAngle) * self.orbitRadius;
// --- InfernoMonster ateş etme mantığı ---
self.fireCooldown--;
if (self.fireCooldown <= 0) {
// Wizard'a doğru bir mermi fırlat
var bullet = new InfernoBullet();
bullet.x = self.x;
bullet.y = self.y;
// Wizard'a doğru yön
var dx = wizard.x - self.x;
var dy = wizard.y - self.y;
var len = Math.sqrt(dx * dx + dy * dy);
var speed = 10; // Inferno mermisi hızı
if (len > 0) {
bullet.vx = dx / len * speed;
bullet.vy = dy / len * speed;
}
// Sprite'ı yönüne döndür
if (bullet.children && bullet.children.length > 0) {
bullet.children[0].rotation = Math.atan2(bullet.vy, bullet.vx);
}
infernoBullets.push(bullet);
game.addChild(bullet);
// Sonraki ateş aralığı rastgele belirle
self.fireCooldown = self.fireInterval + Math.floor(Math.random() * 60);
}
}
// Animate (pulse)
var t = LK.ticks || Date.now();
// Reduce pulse amplitude to one third (0.13 / 3)
var scale = 1 + 0.13 / 3 * Math.sin((t + self.x + self.y) * 0.07);
if (self.children && self.children.length > 0) {
self.children[0].scale.x = scale;
self.children[0].scale.y = scale;
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Wizard class: represents the player character
var Wizard = Container.expand(function () {
var self = Container.call(this);
// Attach wizard asset (blue hat, pixel style)
var wizardSprite = self.attachAsset('wizard', {
anchorX: 0.5,
anchorY: 0.5
});
// Track last position for event triggers
self.lastX = 0;
self.lastY = 0;
// Update method (called every tick)
self.update = function () {
// No movement logic here; wizard is moved by mouse/touch
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Explosion asset for enemy death effect
;
// Add background image to the game scene
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
game.addChild(background);
// --- Game Code for Pixel Wizard: Blue Hat Adventure ---
// Create wizard instance and add to game
var wizard = new Wizard();
game.addChild(wizard);
// Center wizard on screen at start
wizard.x = 2048 / 2;
wizard.y = 2732 / 2;
// --- START & UPGRADE BUTTON UI LOGIC ---
var gameStarted = false;
// UI container for start and upgrade buttons
var startUiContainer = new Container();
// Create start button asset (simple box with text)
var startButtonWidth = 420;
var startButtonHeight = 140;
var startButton = new Container();
var startButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: startButtonWidth,
height: startButtonHeight
});
startButtonBg.alpha = 0.92;
startButton.addChild(startButtonBg);
var startButtonText = new Text2("START GAME", {
size: 220,
fill: "#fff"
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 0;
startButtonText.y = -120;
startButton.addChild(startButtonText);
// Create upgrade button below start button
var upgradeButtonWidth = 420;
var upgradeButtonHeight = 110;
var upgradeButton = new Container();
var upgradeButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: upgradeButtonWidth,
height: upgradeButtonHeight
});
upgradeButtonBg.alpha = 0.92;
upgradeButton.addChild(upgradeButtonBg);
var upgradeButtonText = new Text2("UPGRADE", {
size: 110,
// Half of START GAME (220)
fill: "#fff"
});
upgradeButtonText.anchor.set(0.5, 0.5);
upgradeButtonText.x = 0;
upgradeButtonText.y = 0;
upgradeButton.addChild(upgradeButtonText);
// Position start, upgrade, and shop buttons in the UI container
startButton.x = 0;
startButton.y = -400; //{36} // Moved much higher
upgradeButton.x = 0;
upgradeButton.y = startButton.y + startButtonHeight / 2 + 55 + upgradeButtonHeight / 2; //{38} // Increased spacing by 15px
// --- SHOP BUTTON UI LOGIC ---
var shopButtonWidth = 420;
var shopButtonHeight = 110;
var shopButton = new Container();
var shopButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: shopButtonWidth,
height: shopButtonHeight
});
shopButtonBg.alpha = 0.92;
shopButton.addChild(shopButtonBg);
var shopButtonText = new Text2("SHOP", {
size: 110,
fill: "#fff"
});
shopButtonText.anchor.set(0.5, 0.5);
shopButtonText.x = 0;
shopButtonText.y = 0;
shopButton.addChild(shopButtonText);
// Position shop button below upgrade button
shopButton.x = 0;
shopButton.y = upgradeButton.y + upgradeButtonHeight / 2 + 40 + shopButtonHeight / 2;
// Add all three buttons to the UI container
startUiContainer.addChild(startButton);
startUiContainer.addChild(upgradeButton);
startUiContainer.addChild(shopButton);
// --- RESET BUTTON UI LOGIC ---
var resetButtonWidth = 420;
var resetButtonHeight = 110;
var resetButton = new Container();
var resetButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: resetButtonWidth,
height: resetButtonHeight
});
resetButtonBg.alpha = 0.92;
resetButton.addChild(resetButtonBg);
var resetButtonText = new Text2("RESET", {
size: 110,
fill: "#fff"
});
resetButtonText.anchor.set(0.5, 0.5);
resetButtonText.x = 0;
resetButtonText.y = 0;
resetButton.addChild(resetButtonText);
// Position reset button below shop button
resetButton.x = 0;
resetButton.y = shopButton.y + shopButtonHeight / 2 + 40 + resetButtonHeight / 2;
startUiContainer.addChild(resetButton);
// --- RESET POPUP LOGIC ---
var resetPopup = null;
function showResetPopup() {
if (resetPopup && !resetPopup.destroyed) return;
resetPopup = new Container();
// Popup background
var popupW = 900;
var popupH = 600;
var popupBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: popupW,
height: popupH
});
popupBg.alpha = 0.98;
resetPopup.addChild(popupBg);
// Popup text
var popupText = new Text2("Are you sure you want to reset all progress?", {
size: 80,
fill: "#fff"
});
popupText.anchor.set(0.5, 0.5);
popupText.x = 0;
popupText.y = -100;
resetPopup.addChild(popupText);
// Yes button
var yesBtn = new Container();
var yesBtnBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: 320,
height: 120
});
yesBtnBg.alpha = 0.95;
yesBtn.addChild(yesBtnBg);
var yesBtnText = new Text2("YES", {
size: 90,
fill: 0x2ECC40
});
yesBtnText.anchor.set(0.5, 0.5);
yesBtnText.x = 0;
yesBtnText.y = 0;
yesBtn.addChild(yesBtnText);
yesBtn.x = -180;
yesBtn.y = 120;
// No button
var noBtn = new Container();
var noBtnBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: 320,
height: 120
});
noBtnBg.alpha = 0.95;
noBtn.addChild(noBtnBg);
var noBtnText = new Text2("NO", {
size: 90,
fill: 0xE74C3C
});
noBtnText.anchor.set(0.5, 0.5);
noBtnText.x = 0;
noBtnText.y = 0;
noBtn.addChild(noBtnText);
noBtn.x = 180;
noBtn.y = 120;
// Add buttons to popup
resetPopup.addChild(yesBtn);
resetPopup.addChild(noBtn);
// Center popup
resetPopup.x = 2048 / 2;
resetPopup.y = 2732 / 2;
game.addChild(resetPopup);
// Yes: reset all progress and reload game state
yesBtn.down = function () {
// Clear all persistent storage
for (var key in storage) {
if (storage.hasOwnProperty(key)) {
delete storage[key];
}
}
// Remove all weapon upgrades from storage
for (var key in storage) {
if (storage.hasOwnProperty(key) && key.indexOf("weaponUpgrades_") === 0) {
delete storage[key];
}
}
// Also clear all in-memory weapon upgrades for all weapons and set to first stage
if (typeof weaponUpgradeDefaults !== "undefined") {
var weaponAssets = ["wizard", "submachine", "MP5", "shuriken", "shotgun", "minigun", "Lasergun"];
for (var i = 0; i < weaponAssets.length; i++) {
var key = getWeaponUpgradeKey(weaponAssets[i]);
// Set all upgrades to first stage (level 0, cost default, health 5, etc)
var upgrades = {};
for (var k in weaponUpgradeDefaults) upgrades[k] = weaponUpgradeDefaults[k];
storage[key] = upgrades;
}
}
// Optionally, force reload by reloading the page, but here we just reset all variables and UI
// Remove popup
if (resetPopup && !resetPopup.destroyed) {
resetPopup.destroy();
resetPopup = null;
}
// Remove start UI if present
if (startUiContainer && !startUiContainer.destroyed) {
startUiContainer.destroy();
}
// Remove all enemies, fireballs, coins
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] && !enemies[i].destroyed) enemies[i].destroy();
enemies.splice(i, 1);
}
for (var i = fireballs.length - 1; i >= 0; i--) {
if (fireballs[i] && !fireballs[i].destroyed) fireballs[i].destroy();
fireballs.splice(i, 1);
}
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i] && !coins[i].destroyed) coins[i].destroy();
coins.splice(i, 1);
}
// Reset all progress variables
score = 0;
coinCount = 0;
storage.coinCount = 0;
speedUpgradeLevel = 0;
speedUpgradeCost = 50;
// (damageUpgradeLevel and damageUpgradeCost removed)
healthUpgradeLevel = 0;
healthUpgradeCost = 25;
maxWizardHealth = 5;
wizardHealth = 5;
fireballDamage = 1;
// Reset shop purchases and upgrades
shopPurchases = {};
storage.shopPurchases = {};
wizardAsset = "wizard";
storage.wizardAsset = "wizard"; // persist default asset
storage.speedUpgradeLevel = 0;
storage.speedUpgradeCost = 50;
// (storage.damageUpgradeLevel and storage.damageUpgradeCost removed)
storage.healthUpgradeLevel = 0;
storage.healthUpgradeCost = 25;
storage.maxWizardHealth = 5;
storage.wizardHealth = 5;
storage.fireballDamage = 1;
// Update UI
scoreText.setText("Score: 0");
coinText.setText("" + coinCount);
// Reset wizard asset to default and ensure only one asset exists
// Remove all children
for (var i = wizard.children.length - 1; i >= 0; i--) {
wizard.removeChild(wizard.children[i]);
}
// Attach new wizard asset as the only sprite
var newSprite = wizard.attachAsset('wizard', {
anchorX: 0.5,
anchorY: 0.5
});
// Reset health bar (no-op)
updateHealthBar();
// Center wizard
wizard.x = 2048 / 2;
wizard.y = 2732 / 2;
if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = Math.PI;
}
// Recreate start UI
startUiContainer = new Container();
startButton = new Container();
startButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: startButtonWidth,
height: startButtonHeight
});
startButtonBg.alpha = 0.92;
startButton.addChild(startButtonBg);
startButtonText = new Text2("START GAME", {
size: 220,
fill: "#fff"
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 0;
startButtonText.y = -120;
startButton.addChild(startButtonText);
upgradeButton = new Container();
upgradeButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: upgradeButtonWidth,
height: upgradeButtonHeight
});
upgradeButtonBg.alpha = 0.92;
upgradeButton.addChild(upgradeButtonBg);
upgradeButtonText = new Text2("UPGRADE", {
size: 110,
fill: "#fff"
});
upgradeButtonText.anchor.set(0.5, 0.5);
upgradeButtonText.x = 0;
upgradeButtonText.y = 0;
upgradeButton.addChild(upgradeButtonText);
shopButton = new Container();
shopButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: shopButtonWidth,
height: shopButtonHeight
});
shopButtonBg.alpha = 0.92;
shopButton.addChild(shopButtonBg);
shopButtonText = new Text2("SHOP", {
size: 110,
fill: "#fff"
});
shopButtonText.anchor.set(0.5, 0.5);
shopButtonText.x = 0;
shopButtonText.y = 0;
shopButton.addChild(shopButtonText);
// Re-add reset button
resetButton = new Container();
resetButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: resetButtonWidth,
height: resetButtonHeight
});
resetButtonBg.alpha = 0.92;
resetButton.addChild(resetButtonBg);
resetButtonText = new Text2("RESET", {
size: 110,
fill: "#fff"
});
resetButtonText.anchor.set(0.5, 0.5);
resetButtonText.x = 0;
resetButtonText.y = 0;
resetButton.addChild(resetButtonText);
// Position all buttons
startButton.x = 0;
startButton.y = -400;
upgradeButton.x = 0;
upgradeButton.y = startButton.y + startButtonHeight / 2 + 55 + upgradeButtonHeight / 2;
shopButton.x = 0;
shopButton.y = upgradeButton.y + upgradeButtonHeight / 2 + 40 + shopButtonHeight / 2;
resetButton.x = 0;
resetButton.y = shopButton.y + shopButtonHeight / 2 + 40 + resetButtonHeight / 2;
// Add to container
startUiContainer.addChild(startButton);
startUiContainer.addChild(upgradeButton);
startUiContainer.addChild(shopButton);
startUiContainer.addChild(resetButton);
startUiContainer.x = 2048 / 2;
startUiContainer.y = 2732 / 2;
game.addChild(startUiContainer);
// Re-attach handlers
upgradeButton.down = function (x, y, obj) {
if (upgradeWindow && !upgradeWindow.destroyed) return;
showUpgradeWindow();
};
shopButton.down = function (x, y, obj) {
if (shopWindow && !shopWindow.destroyed) return;
showShopWindow();
};
startButton.down = function (x, y, obj) {
if (gameStarted) return;
gameStarted = true;
if (startUiContainer && !startUiContainer.destroyed) {
startUiContainer.destroy();
}
// Remove all enemies, fireballs, coins
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] && !enemies[i].destroyed) enemies[i].destroy();
enemies.splice(i, 1);
}
for (var i = fireballs.length - 1; i >= 0; i--) {
if (fireballs[i] && !fireballs[i].destroyed) fireballs[i].destroy();
fireballs.splice(i, 1);
}
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i] && !coins[i].destroyed) coins[i].destroy();
coins.splice(i, 1);
}
score = 0;
scoreText.setText("Score: 0");
coinText.setText("" + coinCount);
wizard.x = 2048 / 2;
wizard.y = 2732 / 2;
if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = Math.PI;
}
// --- Reset magazine system ---
// If minigun is selected, always set to 350 ammo at new game start
if (wizardAsset === "minigun") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("minigun");
wizardAmmo = wizardMaxAmmo;
tapFireCooldown = 500;
} else if (wizardAsset === "submachine") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("submachine");
wizardAmmo = wizardMaxAmmo;
tapFireCooldown = 100;
wizardReloadTime = 500;
} else if (wizardAsset === "Lasergun") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("Lasergun");
wizardAmmo = wizardMaxAmmo;
tapFireCooldown = 300;
} else if (wizardAsset === "shotgun") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("shotgun");
wizardAmmo = wizardMaxAmmo;
tapFireCooldown = 200;
} else {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo(wizardAsset);
wizardAmmo = wizardMaxAmmo;
}
wizardReloading = false;
if (wizardReloadTimeout) {
LK.clearTimeout(wizardReloadTimeout);
wizardReloadTimeout = null;
}
// Health bar is now fixed in the GUI, no need to reposition here
wizardHealth = maxWizardHealth;
updateHealthBar();
game.move = originalGameMove;
game.down = originalGameDown;
game.up = originalGameUp;
};
resetButton.down = function (x, y, obj) {
showResetPopup();
};
// Block input until start is pressed
blockGameInput();
gameStarted = false;
};
// No: close popup
noBtn.down = function () {
if (resetPopup && !resetPopup.destroyed) {
resetPopup.destroy();
resetPopup = null;
}
};
}
// Reset button interaction
resetButton.down = function (x, y, obj) {
showResetPopup();
};
// Position the UI container: horizontally centered, vertically centered
startUiContainer.x = 2048 / 2;
startUiContainer.y = 2732 / 2;
// Add to game
game.addChild(startUiContainer);
// Block input until start is pressed
function blockGameInput() {
game.move = function () {};
game.down = function () {};
game.up = function () {};
isFiring = false;
if (fireInterval) {
LK.clearInterval(fireInterval);
fireInterval = null;
}
}
blockGameInput();
// --- UPGRADE WINDOW LOGIC ---
var upgradeWindow = null;
function showUpgradeWindow() {
if (upgradeWindow && !upgradeWindow.destroyed) return;
upgradeWindow = new Container();
// Window background (main)
var winW = 1200 + 500 - 250,
// Decrease width by 250px
winH = 1100 + 500 + 500; // Increase height by 500px again, width unchanged
var winBg = LK.getAsset('upgradescreen', {
anchorX: 0.5,
anchorY: 0.5,
width: winW,
height: winH
});
winBg.alpha = 0.97;
upgradeWindow.addChild(winBg);
// Title removed: no upgrade window title text
// Button helper with icon
function makeUpgradeBtn(label, y, color, iconAsset, iconColor) {
var btn = new Container();
// No background for speed, power, health, or back buttons
// Icon asset (left side of button)
var icon = null;
if (iconAsset) {
// If this is the health asset, increase its size by 50px
if (iconAsset === 'health') {
icon = LK.getAsset(iconAsset, {
anchorX: 0.5,
anchorY: 0.5,
x: -360,
// 100px more to the left
y: 0,
width: 150,
height: 150
});
} else {
icon = LK.getAsset(iconAsset, {
anchorX: 0.5,
anchorY: 0.5,
x: -360,
// 100px more to the left
y: 0,
scaleX: 1.2,
scaleY: 1.2
});
}
// Remove any filter/overlay in front of the icon (if any exist)
if (icon.filters && icon.filters.length > 0) {
icon.filters = [];
}
btn.addChild(icon);
}
var btnText = new Text2(label, {
size: 100,
fill: "#fff"
});
btnText.anchor.set(0.5, 0.5);
btnText.x = 60;
btnText.y = 0;
btn.addChild(btnText);
btn.x = 0;
btn.y = y;
return btn;
}
// Hız, Health, Ammo, Geri buttons with icons and unique colors
var btnSpacing = 220; // Slightly reduced to fit 3 upgrades
var btns = [];
// Use fast, background assets for each upgrade (background as health icon)
var btnLabels = [{
label: "SPEED",
iconAsset: 'fast'
}, {
label: "HEALTH",
iconAsset: 'health'
}, {
label: "AMMO",
iconAsset: 'ammoupgrade'
}];
for (var i = 0; i < btnLabels.length; i++) {
var btn = makeUpgradeBtn(btnLabels[i].label, -180 + i * btnSpacing, btnLabels[i].color, btnLabels[i].iconAsset, btnLabels[i].iconColor);
upgradeWindow.addChild(btn);
btns.push(btn);
}
// --- SPEED UPGRADE LOGIC (5 seviye, her silah için ayrı ayrı, atış ve mermi hızı) ---
if (typeof speedUpgradeBtnText === "undefined") speedUpgradeBtnText = null;
var upgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = upgrades.speedUpgradeLevel;
// Set cost to 100 * (level+1) for each upgrade, max 5 upgrades
speedUpgradeCost = speedUpgradeLevel < 5 ? 100 * (speedUpgradeLevel + 1) : "-";
upgrades.speedUpgradeCost = speedUpgradeCost;
btns[0].down = function () {
var upgrades = getWeaponUpgrades(wizardAsset);
// Always set cost to 100 * (level+1) for each upgrade, max 5 upgrades
upgrades.speedUpgradeCost = upgrades.speedUpgradeLevel < 5 ? 100 * (upgrades.speedUpgradeLevel + 1) : "-";
if (coinCount >= upgrades.speedUpgradeCost && upgrades.speedUpgradeLevel < 5) {
coinCount -= upgrades.speedUpgradeCost;
coinText.setText("" + coinCount);
upgrades.speedUpgradeLevel += 1;
// Her seviye için maliyet: 100, 200, 300, 400, 500 coin
upgrades.speedUpgradeCost = upgrades.speedUpgradeLevel < 5 ? 100 * (upgrades.speedUpgradeLevel + 1) : "-";
saveWeaponUpgrades(wizardAsset, upgrades);
speedUpgradeLevel = upgrades.speedUpgradeLevel;
speedUpgradeCost = upgrades.speedUpgradeCost;
// Atış hızı: tapFireCooldown'u azalt (daha hızlı ateş)
// Mermi hızı: shootFireballAt fonksiyonunda kullanılacak
tapFireCooldown = Math.max(80, 850 - speedUpgradeLevel * 140);
// Buton label güncelle
if (speedUpgradeBtnText) {
if (btns[0].coinIcon && btns[0].coinIcon.parent) {
btns[0].coinIcon.parent.removeChild(btns[0].coinIcon);
btns[0].coinIcon = null;
}
if (speedUpgradeLevel < 5) {
speedUpgradeBtnText.setText("SPEED | " + speedUpgradeCost);
var coinAsset = LK.getAsset('coin', {
anchorX: 0.0,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
coinAsset.x = speedUpgradeBtnText.x + speedUpgradeBtnText.width / 2 + coinAsset.width * 0.6;
coinAsset.y = speedUpgradeBtnText.y;
btns[0].addChild(coinAsset);
btns[0].coinIcon = coinAsset;
} else {
speedUpgradeBtnText.setText("SPEED | MAX");
}
}
LK.effects.flashObject(btns[0], 0x2ecc40, 400);
} else if (speedUpgradeLevel >= 5) {
LK.effects.flashObject(btns[0], 0x888888, 400);
} else {
LK.effects.flashObject(coinText, 0xe74c3c, 600);
}
};
// Always set speedUpgradeBtnText to the SPEED button label when opening the window
speedUpgradeBtnText = btns[0].children[1];
if (speedUpgradeBtnText) {
if (btns[0].coinIcon && btns[0].coinIcon.parent) {
btns[0].coinIcon.parent.removeChild(btns[0].coinIcon);
btns[0].coinIcon = null;
}
if (speedUpgradeLevel < 5) {
speedUpgradeBtnText.setText("SPEED | " + speedUpgradeCost);
var coinAsset = LK.getAsset('coin', {
anchorX: 0.0,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
coinAsset.x = speedUpgradeBtnText.x + speedUpgradeBtnText.width / 2 + coinAsset.width * 0.6;
coinAsset.y = speedUpgradeBtnText.y;
btns[0].addChild(coinAsset);
btns[0].coinIcon = coinAsset;
} else {
speedUpgradeBtnText.setText("SPEED | MAX");
}
}
// HEALTH upgrade logic: cost increases after each purchase, +2 max health, +2 current health, max 3 upgrades: 25, 50, 75 coin
healthUpgradeLevel = upgrades.healthUpgradeLevel;
healthUpgradeCost = upgrades.healthUpgradeLevel < 3 ? 25 * (upgrades.healthUpgradeLevel + 1) : "-";
upgrades.healthUpgradeCost = healthUpgradeCost;
maxWizardHealth = upgrades.maxWizardHealth;
wizardHealth = upgrades.wizardHealth;
btns[1].down = function () {
var upgrades = getWeaponUpgrades(wizardAsset);
upgrades.healthUpgradeCost = upgrades.healthUpgradeLevel < 3 ? 25 * (upgrades.healthUpgradeLevel + 1) : "-";
if (coinCount >= upgrades.healthUpgradeCost && upgrades.healthUpgradeLevel < 3) {
coinCount -= upgrades.healthUpgradeCost;
coinText.setText("" + coinCount);
upgrades.maxWizardHealth += 2;
upgrades.wizardHealth += 2;
updateHealthBar();
upgrades.healthUpgradeLevel += 1;
upgrades.healthUpgradeCost = upgrades.healthUpgradeLevel < 3 ? 25 * (upgrades.healthUpgradeLevel + 1) : "-";
saveWeaponUpgrades(wizardAsset, upgrades);
healthUpgradeLevel = upgrades.healthUpgradeLevel;
healthUpgradeCost = upgrades.healthUpgradeCost;
maxWizardHealth = upgrades.maxWizardHealth;
wizardHealth = upgrades.wizardHealth;
// Update button label to show new cost or disable if maxed
if (healthUpgradeBtnText) {
if (btns[1].coinIcon && btns[1].coinIcon.parent) {
btns[1].coinIcon.parent.removeChild(btns[1].coinIcon);
btns[1].coinIcon = null;
}
if (healthUpgradeLevel < 3) {
healthUpgradeBtnText.setText("HEALTH | " + healthUpgradeCost);
var coinAsset = LK.getAsset('coin', {
anchorX: 0.0,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
coinAsset.x = healthUpgradeBtnText.x + healthUpgradeBtnText.width / 2 + coinAsset.width * 0.6;
coinAsset.y = healthUpgradeBtnText.y;
btns[1].addChild(coinAsset);
btns[1].coinIcon = coinAsset;
} else {
healthUpgradeBtnText.setText("HEALTH | MAX");
}
}
LK.effects.flashObject(btns[1], 0x2ecc40, 400);
} else if (healthUpgradeLevel >= 3) {
LK.effects.flashObject(btns[1], 0x888888, 400);
} else {
LK.effects.flashObject(coinText, 0xe74c3c, 600);
}
};
// Always set healthUpgradeBtnText to the HEALTH button label when opening the window
healthUpgradeBtnText = btns[1].children[1];
if (healthUpgradeBtnText) {
if (btns[1].coinIcon && btns[1].coinIcon.parent) {
btns[1].coinIcon.parent.removeChild(btns[1].coinIcon);
btns[1].coinIcon = null;
}
if (healthUpgradeLevel < 3) {
healthUpgradeBtnText.setText("HEALTH | " + healthUpgradeCost);
var coinAsset = LK.getAsset('coin', {
anchorX: 0.0,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
coinAsset.x = healthUpgradeBtnText.x + healthUpgradeBtnText.width / 2 + coinAsset.width * 0.6;
coinAsset.y = healthUpgradeBtnText.y;
btns[1].addChild(coinAsset);
btns[1].coinIcon = coinAsset;
} else {
healthUpgradeBtnText.setText("HEALTH | MAX");
}
}
// --- AMMO UPGRADE LOGIC (max 5 upgrades, +5 max ammo per upgrade, cost 80, 160, 240, 320, 400) ---
if (typeof ammoUpgradeBtnText === "undefined") ammoUpgradeBtnText = null;
if (typeof upgrades.ammoUpgradeLevel === "undefined") upgrades.ammoUpgradeLevel = 0;
if (typeof upgrades.ammoUpgradeCost === "undefined") upgrades.ammoUpgradeCost = 80;
if (typeof upgrades.ammoUpgradeAmount === "undefined") upgrades.ammoUpgradeAmount = 0;
var ammoUpgradeLevel = upgrades.ammoUpgradeLevel;
var ammoUpgradeCost = ammoUpgradeLevel < 5 ? 80 * (ammoUpgradeLevel + 1) : "-";
upgrades.ammoUpgradeCost = ammoUpgradeCost;
btns[2].down = function () {
var upgrades = getWeaponUpgrades(wizardAsset);
if (typeof upgrades.ammoUpgradeLevel === "undefined") upgrades.ammoUpgradeLevel = 0;
if (typeof upgrades.ammoUpgradeCost === "undefined") upgrades.ammoUpgradeCost = 80;
if (typeof upgrades.ammoUpgradeAmount === "undefined") upgrades.ammoUpgradeAmount = 0;
upgrades.ammoUpgradeCost = upgrades.ammoUpgradeLevel < 5 ? 80 * (upgrades.ammoUpgradeLevel + 1) : "-";
if (coinCount >= upgrades.ammoUpgradeCost && upgrades.ammoUpgradeLevel < 5) {
coinCount -= upgrades.ammoUpgradeCost;
coinText.setText("" + coinCount);
upgrades.ammoUpgradeLevel += 1;
upgrades.ammoUpgradeAmount = (upgrades.ammoUpgradeAmount || 0) + 5;
upgrades.ammoUpgradeCost = upgrades.ammoUpgradeLevel < 5 ? 80 * (upgrades.ammoUpgradeLevel + 1) : "-";
saveWeaponUpgrades(wizardAsset, upgrades);
ammoUpgradeLevel = upgrades.ammoUpgradeLevel;
ammoUpgradeCost = upgrades.ammoUpgradeCost;
// Increase wizardMaxAmmo for current weapon
if (typeof wizardMaxAmmo !== "undefined") {
wizardMaxAmmo += 5;
wizardAmmo = wizardMaxAmmo;
}
// Update button label
if (ammoUpgradeBtnText) {
if (btns[2].coinIcon && btns[2].coinIcon.parent) {
btns[2].coinIcon.parent.removeChild(btns[2].coinIcon);
btns[2].coinIcon = null;
}
if (ammoUpgradeLevel < 5) {
ammoUpgradeBtnText.setText("AMMO | " + ammoUpgradeCost);
var coinAsset = LK.getAsset('coin', {
anchorX: 0.0,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
coinAsset.x = ammoUpgradeBtnText.x + ammoUpgradeBtnText.width / 2 + coinAsset.width * 0.6;
coinAsset.y = ammoUpgradeBtnText.y;
btns[2].addChild(coinAsset);
btns[2].coinIcon = coinAsset;
} else {
ammoUpgradeBtnText.setText("AMMO | MAX");
}
}
LK.effects.flashObject(btns[2], 0x2ecc40, 400);
} else if (ammoUpgradeLevel >= 5) {
LK.effects.flashObject(btns[2], 0x888888, 400);
} else {
LK.effects.flashObject(coinText, 0xe74c3c, 600);
}
};
// Always set ammoUpgradeBtnText to the AMMO button label when opening the window
ammoUpgradeBtnText = btns[2].children[1];
if (ammoUpgradeBtnText) {
if (btns[2].coinIcon && btns[2].coinIcon.parent) {
btns[2].coinIcon.parent.removeChild(btns[2].coinIcon);
btns[2].coinIcon = null;
}
if (ammoUpgradeLevel < 5) {
ammoUpgradeBtnText.setText("AMMO | " + ammoUpgradeCost);
var coinAsset = LK.getAsset('coin', {
anchorX: 0.0,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
coinAsset.x = ammoUpgradeBtnText.x + ammoUpgradeBtnText.width / 2 + coinAsset.width * 0.6;
coinAsset.y = ammoUpgradeBtnText.y;
btns[2].addChild(coinAsset);
btns[2].coinIcon = coinAsset;
} else {
ammoUpgradeBtnText.setText("AMMO | MAX");
}
}
// BACK button without coin asset
// Place it 500 units further from AMMO (btnSpacing = 220, so AMMO is at -180 + 3*220 = 480, BACK at 480+400=880)
var geriBtn = makeUpgradeBtn("BACK", 880, undefined, null, undefined);
upgradeWindow.addChild(geriBtn);
// BACK closes window
geriBtn.down = function () {
if (upgradeWindow && !upgradeWindow.destroyed) {
upgradeWindow.destroy();
upgradeWindow = null;
}
};
// Center window
upgradeWindow.x = 2048 / 2;
upgradeWindow.y = 2732 / 2;
game.addChild(upgradeWindow);
}
// Upgrade button interaction
upgradeButton.down = function (x, y, obj) {
if (upgradeWindow && !upgradeWindow.destroyed) return;
showUpgradeWindow();
};
// --- SHOP WINDOW LOGIC ---
var shopWindow = null;
function showShopWindow() {
if (shopWindow && !shopWindow.destroyed) return;
shopWindow = new Container();
// Window background
var winW = 1500;
var winH = 2000;
var winBg = LK.getAsset('shop', {
anchorX: 0.5,
anchorY: 0.5,
width: winW,
height: winH
});
winBg.alpha = 0.97;
shopWindow.addChild(winBg);
// Title text removed
// Weapon shop items
var weaponList = [{
name: "SMG",
asset: "submachine",
price: 1
}, {
name: "Shuriken",
asset: "shuriken",
price: 150
}, {
name: "Shotgun",
asset: "shotgun",
price: 250
}, {
name: "Minigun",
asset: "minigun",
price: 350
}, {
name: "Laser Gun",
asset: "Lasergun",
price: 750
}];
var itemStartY = -350;
var itemSpacing = 220;
var shopItemButtons = [];
var _loop = function _loop() {
weapon = weaponList[i];
itemY = itemStartY + i * itemSpacing;
shopItem = new Container(); // Icon (left)
itemIcon = LK.getAsset(weapon.asset, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0,
x: -420,
y: 0
});
shopItem.addChild(itemIcon);
// "SMG | x" text (centered)
itemText = new Text2(weapon.name + " | " + weapon.price, {
size: 90,
fill: "#fff"
});
itemText.anchor.set(0, 0.5);
itemText.x = -180;
itemText.y = 0;
shopItem.addChild(itemText);
// Coin asset (right of price)
priceCoin = LK.getAsset('coin', {
anchorX: 0.0,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7,
x: itemText.x + itemText.width + 30,
y: itemText.y
});
shopItem.addChild(priceCoin);
// Helper to update purchased/selected state visuals
function updateShopItemState() {
var isPurchased = shopPurchases && shopPurchases[weapon.asset];
var isSelected = wizardAsset === weapon.asset;
// Only update priceCoin and itemText, no labels
if (!!isPurchased && isSelected) {
priceCoin.visible = false;
itemText.setText(weapon.name);
if (typeof itemText.setFill === "function") {
itemText.setFill("#2ecc40"); // green
}
} else {
priceCoin.visible = !isPurchased;
itemText.setText(weapon.name + (isPurchased ? "" : " | " + weapon.price));
if (typeof itemText.setFill === "function") {
itemText.setFill("#fff"); // white
}
}
}
updateShopItemState();
// Add down handler for all shop items
(function (weapon, shopItem, priceCoin, itemText, updateShopItemState) {
shopItem.down = function () {
var isPurchased = shopPurchases && shopPurchases[weapon.asset];
var isSelected = wizardAsset === weapon.asset;
// If not purchased, buy and select immediately
if (!isPurchased && coinCount >= weapon.price) {
coinCount -= weapon.price;
coinText.setText("" + coinCount);
if (!shopPurchases) shopPurchases = {};
shopPurchases[weapon.asset] = true;
storage.shopPurchases = shopPurchases;
// Remove all children to ensure only one wizard asset
for (var i = wizard.children.length - 1; i >= 0; i--) {
wizard.removeChild(wizard.children[i]);
}
// Add new asset as wizard's appearance
var newSprite = wizard.attachAsset(weapon.asset, {
anchorX: 0.5,
anchorY: 0.5
});
// Save wizard asset and select it
storage.wizardAsset = weapon.asset;
wizardAsset = weapon.asset;
// Switch upgrades to selected weapon
currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
wizardHealth = currentWeaponUpgrades.wizardHealth;
fireballDamage = currentWeaponUpgrades.fireballDamage;
// --- Minigun ayarları ---
if (weapon.asset === "minigun") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("minigun");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 2000;
tapFireCooldown = 500;
wizardBulletType = "minigunbullet";
} else if (weapon.asset === "shuriken") {
wizardBulletType = "shurikenbullet";
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("shuriken");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 2000;
tapFireCooldown = 750;
} else if (weapon.asset === "shotgun") {
wizardBulletType = null;
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("shotgun");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 1000;
tapFireCooldown = 200;
} else if (weapon.asset === "submachine") {
wizardBulletType = null;
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("submachine");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 500;
tapFireCooldown = 100;
} else if (weapon.asset === "Lasergun") {
wizardBulletType = "lasergunbullet";
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("Lasergun");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 1200;
tapFireCooldown = 300;
} else {
wizardBulletType = null;
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo(wizardAsset);
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 2000;
tapFireCooldown = Math.max(100, 850 - speedUpgradeLevel * 150);
}
LK.effects.flashObject(wizard, 0x2ecc40, 400);
// Update visuals for all shop items
for (var si = 0; si < shopItemButtons.length; si++) {
if (shopItemButtons[si].updateShopItemState) shopItemButtons[si].updateShopItemState();
}
return;
}
// If purchased, select it
if (isPurchased && !isSelected) {
// Remove all children to ensure only one wizard asset
for (var i = wizard.children.length - 1; i >= 0; i--) {
wizard.removeChild(wizard.children[i]);
}
// Add new asset as wizard's appearance
var newSprite = wizard.attachAsset(weapon.asset, {
anchorX: 0.5,
anchorY: 0.5
});
// Save wizard asset
storage.wizardAsset = weapon.asset;
wizardAsset = weapon.asset;
// Switch upgrades to selected weapon
currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
wizardHealth = currentWeaponUpgrades.wizardHealth;
fireballDamage = currentWeaponUpgrades.fireballDamage;
// --- Minigun ayarları ---
if (weapon.asset === "minigun") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("minigun");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 2000;
tapFireCooldown = 500;
wizardBulletType = "minigunbullet";
} else if (weapon.asset === "shuriken") {
wizardBulletType = "shurikenbullet";
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("shuriken");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 2000;
tapFireCooldown = 750;
} else if (weapon.asset === "shotgun") {
wizardBulletType = null;
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("shotgun");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 1000;
tapFireCooldown = 200;
} else if (weapon.asset === "submachine") {
wizardBulletType = null;
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("submachine");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 500;
tapFireCooldown = 100;
} else if (weapon.asset === "Lasergun") {
wizardBulletType = "lasergunbullet";
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("Lasergun");
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 1200;
tapFireCooldown = 300;
} else {
wizardBulletType = null;
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo(wizardAsset);
wizardAmmo = wizardMaxAmmo;
wizardReloadTime = 2000;
tapFireCooldown = Math.max(100, 850 - speedUpgradeLevel * 150);
}
LK.effects.flashObject(wizard, 0x2ecc40, 400);
// Update visuals for all shop items
for (var si = 0; si < shopItemButtons.length; si++) {
if (shopItemButtons[si].updateShopItemState) shopItemButtons[si].updateShopItemState();
}
return;
}
// If not enough coins, flash coinText
if (!isPurchased && coinCount < weapon.price) {
LK.effects.flashObject(coinText, 0xe74c3c, 600);
}
};
// Attach state update helper for global update
shopItem.updateShopItemState = updateShopItemState;
})(weapon, shopItem, priceCoin, itemText, updateShopItemState);
shopItem.x = 0;
shopItem.y = itemY;
shopWindow.addChild(shopItem);
shopItemButtons.push(shopItem);
},
weapon,
itemY,
shopItem,
itemIcon,
itemText,
priceCoin,
purchasedLabel,
selectedLabel;
for (var i = 0; i < weaponList.length; i++) {
_loop();
}
// BACK button
var backBtn = new Container();
// No background for back button
var backBtnText = new Text2("BACK", {
size: 90,
fill: "#fff"
});
backBtnText.anchor.set(0.5, 0.5);
backBtnText.x = 0;
backBtnText.y = 0;
backBtn.addChild(backBtnText);
backBtn.x = 0;
backBtn.y = itemStartY + weaponList.length * itemSpacing + 80;
shopWindow.addChild(backBtn);
backBtn.down = function () {
if (shopWindow && !shopWindow.destroyed) {
shopWindow.destroy();
shopWindow = null;
}
};
// Center window
shopWindow.x = 2048 / 2;
shopWindow.y = 2732 / 2;
game.addChild(shopWindow);
}
// Shop button interaction
shopButton.down = function (x, y, obj) {
if (shopWindow && !shopWindow.destroyed) return;
showShopWindow();
};
// Start button interaction
startButton.down = function (x, y, obj) {
if (gameStarted) return;
gameStarted = true;
// Remove start UI container (removes both start and upgrade buttons)
if (startUiContainer && !startUiContainer.destroyed) {
startUiContainer.destroy();
}
// --- RESET GAME STATE ---
// Remove all enemies, fireballs, coins from game and arrays
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] && !enemies[i].destroyed) enemies[i].destroy();
enemies.splice(i, 1);
}
for (var i = fireballs.length - 1; i >= 0; i--) {
if (fireballs[i] && !fireballs[i].destroyed) fireballs[i].destroy();
fireballs.splice(i, 1);
}
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i] && !coins[i].destroyed) coins[i].destroy();
coins.splice(i, 1);
}
// Reset score and coin count
score = 0;
// coinCount is NOT reset here, so coins are preserved when starting a new game
scoreText.setText("Score: 0");
coinText.setText("" + coinCount);
// Center wizard
wizard.x = 2048 / 2;
wizard.y = 2732 / 2;
// Remove all children to ensure only one wizard asset
for (var i = wizard.children.length - 1; i >= 0; i--) {
wizard.removeChild(wizard.children[i]);
}
// Attach wizard asset if not present
var hasWizardSprite = false;
for (var i = 0; i < wizard.children.length; i++) {
hasWizardSprite = true;
break;
}
if (!hasWizardSprite) {
var newSprite = wizard.attachAsset(wizardAsset, {
anchorX: 0.5,
anchorY: 0.5
});
if (newSprite) {
newSprite.rotation = Math.PI;
}
} else if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = Math.PI; // face left
}
// Reset health
wizardHealth = maxWizardHealth;
updateHealthBar();
// Enable game input
game.move = originalGameMove;
game.down = originalGameDown;
game.up = originalGameUp;
};
// Save original handlers for restoration
var originalGameMove = function originalGameMove(x, y, obj) {
// Wizard is fixed; do nothing
// Calculate angle from wizard to pointer/touch and rotate wizard sprite
var dx = x - wizard.x;
var dy = y - wizard.y;
var angle = Math.atan2(dy, dx);
// Wizard's default asset faces left, so add Math.PI to rotate to correct direction
if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = angle + Math.PI;
}
// If firing, update direction for continuous fire
if (isFiring) {
lastFireX = x;
lastFireY = y;
}
};
var originalGameDown = function originalGameDown(x, y, obj) {
var now = Date.now();
if (now - lastTapFireTime < tapFireCooldown) {
// Too soon, ignore this tap for firing
return;
}
lastTapFireTime = now;
isFiring = true;
lastFireX = x;
lastFireY = y;
shootFireballAt(x, y);
if (fireInterval) LK.clearInterval(fireInterval);
fireInterval = LK.setInterval(function () {
if (isFiring) {
shootFireballAt(lastFireX, lastFireY);
}
}, tapFireCooldown); // fire every tapFireCooldown ms when holding down
};
var originalGameUp = function originalGameUp(x, y, obj) {
isFiring = false;
if (fireInterval) {
LK.clearInterval(fireInterval);
fireInterval = null;
}
};
// Array to hold all fireballs
var fireballs = [];
// Array to hold all boss bullets
var bossBullets = [];
// Helper: get direction vector from wizard to (x, y)
function getDirection(fromX, fromY, toX, toY, speed) {
var dx = toX - fromX;
var dy = toY - fromY;
var len = Math.sqrt(dx * dx + dy * dy);
if (len === 0) return {
vx: 0,
vy: 0
};
return {
vx: dx / len * speed,
vy: dy / len * speed
};
}
// Dragging wizard with mouse/touch (disabled)
var dragging = false;
// Move handler: wizard does not move
game.move = function (x, y, obj) {
if (!gameStarted) return; // Only rotate wizard during gameplay
// Calculate angle from wizard to pointer/touch and rotate wizard sprite
var dx = x - wizard.x;
var dy = y - wizard.y;
var angle = Math.atan2(dy, dx);
// Wizard's default asset faces left, so add Math.PI to rotate to correct direction
if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = angle + Math.PI;
}
// If firing, update direction for continuous fire
if (isFiring) {
lastFireX = x;
lastFireY = y;
}
};
// Track if fire is being held
var isFiring = false;
var fireInterval = null;
var lastFireX = 0;
// --- Magazine system for wizard weapon ---
var wizardMaxAmmo = 7;
var wizardAmmo = wizardMaxAmmo;
var wizardReloading = false;
var wizardReloadTime = 2000; // ms
var wizardReloadTimeout = null;
// Hangi mermi asseti kullanılacak? (null: default, "shurikenbullet", "minigunbullet")
var wizardBulletType = null;
// Helper: apply ammo upgrade to wizardMaxAmmo for current weapon
function applyAmmoUpgradeToMaxAmmo(weaponAsset) {
var upgrades = getWeaponUpgrades(weaponAsset);
var baseMaxAmmo = 7;
if (weaponAsset === "minigun") baseMaxAmmo = 150;else if (weaponAsset === "shuriken") baseMaxAmmo = 7;else if (weaponAsset === "shotgun") baseMaxAmmo = 2;else if (weaponAsset === "submachine") baseMaxAmmo = 3;else if (weaponAsset === "Lasergun") baseMaxAmmo = 10;
// Add ammoUpgradeAmount if present
if (typeof upgrades.ammoUpgradeAmount === "number") {
return baseMaxAmmo + upgrades.ammoUpgradeAmount;
}
return baseMaxAmmo;
}
// Cooldown for tap-to-fire (now 850ms)
var lastTapFireTime = 0;
var tapFireCooldown = 850;
function shootFireballAt(x, y) {
// --- Magazine system: block fire if out of ammo or reloading ---
// For shuriken, skip ammo/reload logic (infinite ammo)
if (wizardAsset !== "shuriken") {
if (wizardReloading) {
// Can't shoot while reloading
return;
}
if (wizardAmmo <= 0) {
// Start reload if not already reloading
if (!wizardReloading) {
wizardReloading = true;
wizardReloadTimeout = LK.setTimeout(function () {
wizardAmmo = wizardMaxAmmo;
wizardReloading = false;
wizardReloadTimeout = null;
}, wizardReloadTime);
}
return;
}
// Decrease ammo
wizardAmmo--;
}
// Eğer wizardAsset shuriken ise shurikenbullet fırlat
if (wizardAsset === "shuriken") {
// SPEED upgrade seviyesine göre shuriken için özel ateş aralığı ve mermi hızı uygulanacak
var upgrades = getWeaponUpgrades(wizardAsset);
var speedLevel = upgrades.speedUpgradeLevel || 0;
// Shuriken için yeni seviye tablosu:
// | Seviye | Ateş Aralığı (ms) | Mermi Hızı (birim/frame) |
// | 0 | 850 | 18 |
// | 1 | 762 | 19.5 |
// | 2 | 675 | 21 |
// | 3 | 587 | 22.5 |
// | 4 | 500 | 24 |
var shurikenFireIntervals = [850, 762, 675, 587, 500];
var shurikenBulletSpeeds = [18, 19.5, 21, 22.5, 24];
var fireInterval = shurikenFireIntervals[Math.min(speedLevel, 4)];
var bulletSpeed = shurikenBulletSpeeds[Math.min(speedLevel, 4)];
tapFireCooldown = fireInterval;
// Fire 3 shurikenbullets at -15, 0, +15 degrees from the main direction
var baseDir = getDirection(wizard.x, wizard.y, x, y, bulletSpeed);
var baseAngle = Math.atan2(baseDir.vy, baseDir.vx);
var angles = [-15, 0, 15];
for (var i = 0; i < angles.length; i++) {
var angleRad = baseAngle + angles[i] * Math.PI / 180;
var vx = Math.cos(angleRad) * bulletSpeed;
var vy = Math.sin(angleRad) * bulletSpeed;
var fireball = new Fireball();
// Remove default fireball sprite, add shurikenbullet
if (fireball.children && fireball.children.length > 0) {
var oldSprite = fireball.children[0];
fireball.removeChild(oldSprite);
}
var shurikenBulletSprite = fireball.attachAsset('shurikenbullet', {
anchorX: 0.5,
anchorY: 0.5
});
fireball.x = wizard.x;
fireball.y = wizard.y;
fireball.vx = vx;
fireball.vy = vy;
if (fireball.children && fireball.children.length > 0) {
fireball.children[0].rotation = angleRad;
}
if (wizard.children && wizard.children.length > 0 && angles[i] === 0) {
wizard.children[0].rotation = baseAngle + Math.PI;
}
fireballs.push(fireball);
game.addChild(fireball);
}
LK.getSound('shuriken').play();
} else {
// SMG logic
if (wizardAsset === "submachine") {
tapFireCooldown = 100;
wizardReloadTime = 500;
var baseDir = getDirection(wizard.x, wizard.y, x, y, 8);
var baseAngle = Math.atan2(baseDir.vy, baseDir.vx);
// Fire only 1 bullet per shot (no in-a-row)
var fireball = new Fireball();
fireball.x = wizard.x;
fireball.y = wizard.y;
fireball.vx = Math.cos(baseAngle) * 8;
fireball.vy = Math.sin(baseAngle) * 8;
if (fireball.children && fireball.children.length > 0) {
fireball.children[0].rotation = baseAngle;
}
fireballs.push(fireball);
game.addChild(fireball);
if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = baseAngle + Math.PI;
}
// Play SMG sound (use fire sound as placeholder if SMG sound not available)
if (LK.getSound('SMG')) {
LK.getSound('SMG').play();
} else {
LK.getSound('fire').play();
}
// Start reload if out of ammo after this shot
if (wizardAmmo <= 0 && !wizardReloading) {
wizardReloading = true;
wizardReloadTimeout = LK.setTimeout(function () {
wizardAmmo = wizardMaxAmmo;
wizardReloading = false;
wizardReloadTimeout = null;
}, wizardReloadTime);
}
} else
// Shotgun logic
if (wizardAsset === "shotgun") {
// --- SPEED UPGRADE: Shotgun için özel ateş ve mermi hızları (800ms -> 500ms) ---
var upgrades = getWeaponUpgrades("shotgun");
var speedLevel = upgrades.speedUpgradeLevel || 0;
// Yeni seviye tablosu:
// | Seviye | Ateş Aralığı (ms) | Mermi Hızı (birim/frame) |
// | 0 | 800 | 10 |
// | 1 | 725 | 11 |
// | 2 | 650 | 12 |
// | 3 | 575 | 13 |
// | 4 | 500 | 14 |
var shotgunFireIntervals = [800, 725, 650, 575, 500];
var shotgunBulletSpeeds = [10, 11, 12, 13, 14];
var fireInterval = shotgunFireIntervals[Math.min(speedLevel, 4)];
var bulletSpeed = shotgunBulletSpeeds[Math.min(speedLevel, 4)];
tapFireCooldown = fireInterval;
wizardReloadTime = 1000;
// 5 bullets, 75-degree random spread
var baseDir = getDirection(wizard.x, wizard.y, x, y, bulletSpeed);
var baseAngle = Math.atan2(baseDir.vy, baseDir.vx);
for (var i = 0; i < 5; i++) {
var spreadDeg = Math.random() * 75 - 37.5;
var angleRad = baseAngle + spreadDeg * Math.PI / 180;
var vx = Math.cos(angleRad) * bulletSpeed;
var vy = Math.sin(angleRad) * bulletSpeed;
var fireball = new Fireball();
fireball.x = wizard.x;
fireball.y = wizard.y;
fireball.vx = vx;
fireball.vy = vy;
if (fireball.children && fireball.children.length > 0) {
fireball.children[0].rotation = angleRad;
}
fireballs.push(fireball);
game.addChild(fireball);
}
if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = baseAngle + Math.PI;
}
// Play shotgun sound (use fire sound as placeholder if shotgun sound not available)
if (LK.getSound('shotgun')) {
LK.getSound('shotgun').play();
} else {
LK.getSound('fire').play();
}
// Start reload if out of ammo after this shot
if (wizardAmmo <= 0 && !wizardReloading) {
wizardReloading = true;
wizardReloadTimeout = LK.setTimeout(function () {
wizardAmmo = wizardMaxAmmo;
wizardReloading = false;
wizardReloadTimeout = null;
}, wizardReloadTime);
}
} else
// Minigun için özel asset ve ayarlar
if (wizardAsset === "minigun" || wizardBulletType === "minigunbullet") {
// --- SPEED UPGRADE: Minigun için özel ateş ve mermi hızları ---
var upgrades = getWeaponUpgrades("minigun");
var speedLevel = upgrades.speedUpgradeLevel || 0;
// Seviye tablosu:
// | Seviye | Ateş Aralığı (ms) | Mermi Hızı (birim/frame) |
// | 0 | 500 | 18 |
// | 1 | 360 | 19.5 |
// | 2 | 220 | 21 |
// | 3 | 150 | 22.5 |
// | 4 | 80 | 24 |
var minigunFireIntervals = [500, 360, 220, 150, 80];
var minigunBulletSpeeds = [18, 19.5, 21, 22.5, 24];
var fireInterval = minigunFireIntervals[Math.min(speedLevel, 4)];
var bulletSpeed = minigunBulletSpeeds[Math.min(speedLevel, 4)];
tapFireCooldown = fireInterval;
// Fire 1 bullet per tap, but with random scatter in a 30-degree cone
var baseDir = getDirection(wizard.x, wizard.y, x, y, bulletSpeed);
var baseAngle = Math.atan2(baseDir.vy, baseDir.vx);
// 30-degree cone: -15 to +15 deg, random within
var scatterDeg = Math.random() * 30 - 15;
var scatterRad = scatterDeg * Math.PI / 180;
var angle = baseAngle + scatterRad;
var vx = Math.cos(angle) * bulletSpeed;
var vy = Math.sin(angle) * bulletSpeed;
var fireball = new Fireball();
fireball.x = wizard.x;
fireball.y = wizard.y;
fireball.vx = vx;
fireball.vy = vy;
// Default fireball asseti kaldır, minigunbullet ekle
if (fireball.children && fireball.children.length > 0) {
var oldSprite = fireball.children[0];
fireball.removeChild(oldSprite);
}
var minigunBulletSprite = fireball.attachAsset('minigunbullet', {
anchorX: 0.5,
anchorY: 0.5
});
if (fireball.children && fireball.children.length > 0) {
fireball.children[0].rotation = angle;
}
if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = Math.atan2(y - wizard.y, x - wizard.x) + Math.PI;
}
fireballs.push(fireball);
game.addChild(fireball);
LK.getSound('Minigun').play();
// Start reload if out of ammo after this shot
if (wizardAmmo <= 0 && !wizardReloading) {
wizardReloading = true;
wizardReloadTimeout = LK.setTimeout(function () {
wizardAmmo = wizardMaxAmmo;
wizardReloading = false;
wizardReloadTimeout = null;
}, wizardReloadTime);
}
} else if (wizardAsset === "Lasergun" || wizardBulletType === "lasergunbullet") {
// --- SPEED UPGRADE: Lasergun için yeni ateş ve mermi hızları (750ms -> 200ms) ---
var upgrades = getWeaponUpgrades("Lasergun");
var speedLevel = upgrades.speedUpgradeLevel || 0;
// Seviye tablosu:
// | Seviye | Ateş Aralığı (ms) | Mermi Hızı (birim/frame) |
// | 0 | 750 | 18 |
// | 1 | 550 | 19.5 |
// | 2 | 400 | 21 |
// | 3 | 300 | 22.5 |
// | 4 | 200 | 24 |
var lasergunFireIntervals = [750, 550, 400, 300, 200];
var lasergunBulletSpeeds = [18, 19.5, 21, 22.5, 24];
var fireInterval = lasergunFireIntervals[Math.min(speedLevel, 4)];
var bulletSpeed = lasergunBulletSpeeds[Math.min(speedLevel, 4)];
tapFireCooldown = fireInterval;
var baseDir = getDirection(wizard.x, wizard.y, x, y, bulletSpeed);
var baseAngle = Math.atan2(baseDir.vy, baseDir.vx);
// Fire 3 lasergun bullets in a row, no spread
for (var i = 0; i < 3; i++) {
var fireball = new Fireball();
fireball.x = wizard.x;
fireball.y = wizard.y;
fireball.vx = Math.cos(baseAngle) * bulletSpeed;
fireball.vy = Math.sin(baseAngle) * bulletSpeed;
// Remove default fireball sprite, add lasergunbullet
if (fireball.children && fireball.children.length > 0) {
var oldSprite = fireball.children[0];
fireball.removeChild(oldSprite);
}
var laserBulletSprite = fireball.attachAsset('lasergunbullet', {
anchorX: 0.5,
anchorY: 0.5
});
if (fireball.children && fireball.children.length > 0) {
fireball.children[0].rotation = baseAngle;
}
// Only rotate wizard sprite for the center bullet
if (wizard.children && wizard.children.length > 0 && i === 1) {
wizard.children[0].rotation = Math.atan2(y - wizard.y, x - wizard.x) + Math.PI;
}
fireballs.push(fireball);
game.addChild(fireball);
}
LK.getSound('plasma').play();
// Start reload if out of ammo after this shot
if (wizardAmmo <= 0 && !wizardReloading) {
wizardReloading = true;
wizardReloadTimeout = LK.setTimeout(function () {
wizardAmmo = wizardMaxAmmo;
wizardReloading = false;
wizardReloadTimeout = null;
}, wizardReloadTime);
}
} else {
// Restore tapFireCooldown to upgrade-based value for non-shuriken weapons
// SPEED upgrade seviyesine göre mermi hızı artırımı
var upgrades = getWeaponUpgrades(wizardAsset);
var speedLevel = upgrades.speedUpgradeLevel || 0;
var bulletSpeed = 8 + speedLevel * 1.5; // her seviye +1.5 hız
tapFireCooldown = Math.max(80, 850 - speedLevel * 140);
var fireball = new Fireball();
fireball.x = wizard.x;
fireball.y = wizard.y;
var dir = getDirection(wizard.x, wizard.y, x, y, bulletSpeed);
fireball.vx = dir.vx;
fireball.vy = dir.vy;
var angle = Math.atan2(dir.vy, dir.vx);
if (fireball.children && fireball.children.length > 0) {
fireball.children[0].rotation = angle;
}
if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = Math.atan2(y - wizard.y, x - wizard.x) + Math.PI;
}
fireballs.push(fireball);
game.addChild(fireball);
LK.getSound('fire').play();
// Start reload if out of ammo after this shot
if (wizardAmmo <= 0 && !wizardReloading) {
wizardReloading = true;
wizardReloadTimeout = LK.setTimeout(function () {
wizardAmmo = wizardMaxAmmo;
wizardReloading = false;
wizardReloadTimeout = null;
}, wizardReloadTime);
}
}
}
}
// Down handler: start firing repeatedly only if gameStarted
game.down = function (x, y, obj) {
if (!gameStarted) return;
var now = Date.now();
if (now - lastTapFireTime < tapFireCooldown) {
// Too soon, ignore this tap for firing
return;
}
lastTapFireTime = now;
isFiring = true;
lastFireX = x;
lastFireY = y;
shootFireballAt(x, y);
if (fireInterval) LK.clearInterval(fireInterval);
fireInterval = LK.setInterval(function () {
if (isFiring) {
shootFireballAt(lastFireX, lastFireY);
}
}, tapFireCooldown); // fire every tapFireCooldown ms when holding down
};
// Up handler: stop firing only if gameStarted
game.up = function (x, y, obj) {
if (!gameStarted) return;
isFiring = false;
if (fireInterval) {
LK.clearInterval(fireInterval);
fireInterval = null;
}
};
// Array to hold all enemies
var enemies = [];
// Import storage plugin for persistent coin saving
// Array to hold all coins
var coins = [];
// Wizard health system
var wizardHealth = 5;
var maxWizardHealth = 5;
// Health upgrade cost system
var healthUpgradeCost = 25;
var healthUpgradeBtnText = null;
var healthUpgradeLevel = 0;
// --- Per-weapon upgrade system ---
var weaponUpgradeDefaults = {
speedUpgradeLevel: 0,
speedUpgradeCost: 100,
// ilk seviye 100 coin
healthUpgradeLevel: 0,
healthUpgradeCost: 25,
maxWizardHealth: 5,
wizardHealth: 5,
fireballDamage: 1,
// --- Ammo upgrade fields ---
ammoUpgradeLevel: 0,
ammoUpgradeCost: 80,
ammoUpgradeAmount: 0
};
// Helper to get upgrade key for a weapon
function getWeaponUpgradeKey(weaponAsset) {
return "weaponUpgrades_" + weaponAsset;
}
// Helper to get upgrades for a weapon (returns object)
function getWeaponUpgrades(weaponAsset) {
var key = getWeaponUpgradeKey(weaponAsset);
var upgrades = storage[key];
if (!upgrades) {
upgrades = {};
// Copy defaults
for (var k in weaponUpgradeDefaults) upgrades[k] = weaponUpgradeDefaults[k];
storage[key] = upgrades;
} else {
// Fill in any missing defaults
for (var k in weaponUpgradeDefaults) {
if (typeof upgrades[k] === "undefined") upgrades[k] = weaponUpgradeDefaults[k];
}
}
return upgrades;
}
// Helper to save upgrades for a weapon
function saveWeaponUpgrades(weaponAsset, upgrades) {
var key = getWeaponUpgradeKey(weaponAsset);
storage[key] = upgrades;
}
// --- Current weapon upgrades (populated on load and on weapon change) ---
var currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
var speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
var speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
// (damageUpgradeLevel and damageUpgradeCost removed)
var healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
var healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
var maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
var wizardHealth = currentWeaponUpgrades.wizardHealth;
var fireballDamage = currentWeaponUpgrades.fireballDamage;
// Health bar UI update
function updateHealthBar() {
if (typeof healthBarFill === "undefined" || typeof healthBarBg === "undefined") return;
var ratio = Math.max(0, Math.min(1, wizardHealth / maxWizardHealth));
healthBarFill.width = (healthBarBg.width - 8) * ratio;
// Keep bar anchored left
healthBarFill.x = healthBarBg.x - (healthBarBg.width - 8) * (1 - ratio) / 2;
}
// Score variable
var score = 0;
// Persistent total coins (stacked across games)
var totalCoins = storage.totalCoins || 0;
// --- Persistent Progress: Restore on load ---
var coinCount = typeof storage.coinCount === "number" ? storage.coinCount : 0;
// No automatic coin grant on reload
var speedUpgradeLevel = typeof storage.speedUpgradeLevel === "number" ? storage.speedUpgradeLevel : 0;
var speedUpgradeCost = typeof storage.speedUpgradeCost === "number" ? storage.speedUpgradeCost : 50;
// (damageUpgradeLevel and damageUpgradeCost removed)
var healthUpgradeLevel = typeof storage.healthUpgradeLevel === "number" ? storage.healthUpgradeLevel : 0;
var healthUpgradeCost = typeof storage.healthUpgradeCost === "number" ? storage.healthUpgradeCost : 25;
var maxWizardHealth = typeof storage.maxWizardHealth === "number" ? storage.maxWizardHealth : 5;
var wizardHealth = typeof storage.wizardHealth === "number" ? storage.wizardHealth : maxWizardHealth;
var fireballDamage = typeof storage.fireballDamage === "number" ? storage.fireballDamage : 1;
var shopPurchases = storage.shopPurchases || {};
var wizardAsset = storage.wizardAsset || "wizard";
// Score text
var scoreText = new Text2("Score: 0", {
size: 100,
fill: "#fff"
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
// After scoreText and wizard are positioned, update startUiContainer position
if (typeof startUiContainer !== "undefined" && startUiContainer && typeof wizard !== "undefined" && wizard) {
// Place UI container between score and wizard, but not overlapping either
startUiContainer.x = 2048 / 2;
startUiContainer.y = wizard.y - 180;
}
// Coin icon and coin count text
var coinIcon = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
var coinText = new Text2("" + coinCount, {
size: 90,
fill: 0xFFD700
});
coinText.anchor.set(0.5, 0);
// Add coin icon and text to the GUI, but position them below the score text
LK.gui.top.addChild(coinIcon);
LK.gui.top.addChild(coinText);
// --- Health Bar UI under coin asset ---
var healthBarWidth = 245;
var healthBarHeight = 57;
var healthBarBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: healthBarWidth,
height: healthBarHeight
});
healthBarBg.alpha = 0.35;
var healthBarFill = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: healthBarWidth - 8,
height: healthBarHeight - 8
});
healthBarFill.alpha = 0.85;
healthBarFill.tint = 0x2ecc40;
// Add white "HEALTH" label centered in the bar
var healthBarLabel = new Text2("HEALTH", {
size: 64,
// Increased font size
fill: "#fff"
});
healthBarLabel.anchor.set(0.5, 0.5);
LK.gui.top.addChild(healthBarBg);
LK.gui.top.addChild(healthBarFill);
LK.gui.top.addChild(healthBarLabel);
// --- Ammo Bar UI below health bar ---
var ammoBarWidth = healthBarWidth;
var ammoBarHeight = 36;
var ammoBarBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: ammoBarWidth,
height: ammoBarHeight
});
ammoBarBg.alpha = 0.28;
var ammoBarFill = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: ammoBarWidth - 8,
height: ammoBarHeight - 8
});
ammoBarFill.alpha = 0.92;
ammoBarFill.tint = 0x3498db; // blue for ammo
// Add dynamic ammo label centered in the bar
var ammoBarLabel = new Text2("", {
size: 36,
fill: "#fff"
});
ammoBarLabel.anchor.set(0.5, 0.5);
LK.gui.top.addChild(ammoBarBg);
LK.gui.top.addChild(ammoBarFill);
LK.gui.top.addChild(ammoBarLabel);
// --- Reload Button below ammo bar ---
var reloadButtonSize = 180;
var reloadButton = new Container();
var reloadButtonBg = LK.getAsset('reload', {
anchorX: 0.5,
anchorY: 0.5,
width: reloadButtonSize,
height: reloadButtonSize
});
reloadButtonBg.alpha = 0.95;
reloadButton.addChild(reloadButtonBg);
LK.gui.top.addChild(reloadButton);
// Position reload button below ammo bar (centered), moved 850px further down (250 + 750 - 100 - 50)
reloadButton.x = ammoBarBg.x;
reloadButton.y = ammoBarBg.y + ammoBarBg.height / 2 + reloadButtonSize / 2 + 18 + 850;
// Reload logic on click/tap
reloadButton.down = function (x, y, obj) {
// Only reload if not already reloading, not shuriken, and not full
if (wizardAsset !== "shuriken" && !wizardReloading && typeof wizardAmmo !== "undefined" && typeof wizardMaxAmmo !== "undefined" && wizardAmmo < wizardMaxAmmo) {
wizardReloading = true;
if (wizardReloadTimeout) {
LK.clearTimeout(wizardReloadTimeout);
wizardReloadTimeout = null;
}
wizardReloadTimeout = LK.setTimeout(function () {
wizardAmmo = wizardMaxAmmo;
wizardReloading = false;
wizardReloadTimeout = null;
}, wizardReloadTime);
}
};
// Position coin icon and text below the score text in the start screen
coinIcon.x = scoreText.x;
coinIcon.y = scoreText.y + scoreText.height + coinIcon.height * 0.6;
// Place coinText 100px to the right and 50px above the coin icon
coinText.x = coinIcon.x + 100;
coinText.y = coinIcon.y - 50;
// Position health bar under coin asset
healthBarBg.x = coinIcon.x;
healthBarBg.y = coinIcon.y + coinIcon.height * 0.7 + healthBarHeight / 2 + 8;
healthBarFill.x = healthBarBg.x;
healthBarFill.y = healthBarBg.y;
// Center the label inside the health bar
if (typeof healthBarLabel !== "undefined") {
healthBarLabel.x = healthBarBg.x;
healthBarLabel.y = healthBarBg.y;
}
// Health bar update function
updateHealthBar();
// --- Restore wizard asset if changed (e.g. shuriken) ---
if (wizardAsset !== "wizard") {
// Remove all children to ensure only one wizard asset
for (var i = wizard.children.length - 1; i >= 0; i--) {
wizard.removeChild(wizard.children[i]);
}
var newSprite = wizard.attachAsset(wizardAsset, {
anchorX: 0.5,
anchorY: 0.5
});
}
// Helper: spawn an enemy at a random edge, moving toward wizard
function spawnEnemy() {
// Limit the maximum number of active enemies to 20
if (typeof enemies !== "undefined" && enemies.length >= 20) {
return;
}
// Only allow certain types after certain scores
var canEnemy2 = score >= 10;
var canEnemy3 = score >= 20;
var canEnemy4 = score >= 30;
var canEnemy6 = score >= 20;
// Weighted random: 30% enemy1, 25% enemy2, 15% enemy3, 10% enemy4, 20% enemy6
// If a type is not available, its weight is 0 and weights are renormalized
var weights = [30,
// enemy1
canEnemy2 ? 25 : 0,
// enemy2
canEnemy3 ? 15 : 0,
// enemy3
canEnemy4 ? 10 : 0,
// enemy4
canEnemy6 ? 5 : 0 // enemy6 (was 20, now 5 for 5% chance)
];
var totalWeight = 0;
for (var i = 0; i < weights.length; i++) totalWeight += weights[i];
var r = Math.random() * totalWeight;
var acc = 0;
var enemyType = 1;
for (var i = 0; i < weights.length; i++) {
acc += weights[i];
if (r < acc) {
enemyType = i + 1;
break;
}
}
var enemy;
if (enemyType === 1) {
enemy = new Enemy();
} else if (enemyType === 2) {
enemy = new Enemy2();
} else if (enemyType === 3) {
enemy = new Enemy3();
} else if (enemyType === 4) {
enemy = new Enemy4();
} else if (enemyType === 5) {
enemy = new Enemy6();
}
// Randomly pick an edge: 0=top, 1=bottom, 2=left, 3=right
var edge = Math.floor(Math.random() * 4);
var ex, ey;
if (edge === 0) {
ex = Math.random() * 2048;
ey = -50;
} else if (edge === 1) {
ex = Math.random() * 2048;
ey = 2732 + 50;
} else if (edge === 2) {
ex = -50;
ey = Math.random() * 2732;
} else {
ex = 2048 + 50;
ey = Math.random() * 2732;
}
enemy.x = ex;
enemy.y = ey;
// Move toward wizard
var dir = getDirection(ex, ey, wizard.x, wizard.y, 2.5 + Math.random() * 1.5); // randomize speed a bit
enemy.vx = dir.vx;
enemy.vy = dir.vy;
// Apply slow effect if slow timer is currently active
if (typeof slowTimerTimeout !== "undefined" && slowTimerTimeout && typeof enemies !== "undefined") {
// If slowTimerTimeout is running, slow effect is active
if (!enemy._isSlowed) {
enemy._origVx = typeof enemy._origVx === "number" ? enemy._origVx : enemy.vx;
enemy._origVy = typeof enemy._origVy === "number" ? enemy._origVy : enemy.vy;
enemy.vx = enemy.vx * (1 / 3);
enemy.vy = enemy.vy * (1 / 3);
enemy._isSlowed = true;
}
}
enemies.push(enemy);
game.addChild(enemy);
}
// Enemy spawn timer
var enemySpawnStopped = false;
var bossSpawned = false;
var boss = null;
var enemy5Spawned = false;
var enemy5 = null;
var infernoMonsterSpawned = false;
var infernoMonster = null;
var enemySpawnTimer = LK.setInterval(function () {
// Do not spawn any enemies if game is not started (main menu)
if (!gameStarted) {
return;
}
// Pause enemy spawn if boss is present and alive
if (boss && !boss.destroyed) {
return;
}
// Pause enemy spawn if goblin (Enemy5) is present and alive
// Only pause if goblin is present, not destroyed, and has NOT shown smoke (i.e. not timed out)
if (enemy5 && !enemy5.destroyed && !enemy5.smokeShown) {
return;
}
// If goblin has disappeared with smoke, clear enemy5 reference and allow normal enemies to spawn
if (enemy5 && enemy5.smokeShown) {
enemy5 = null;
enemy5Spawned = false;
}
// Spawn InfernoMonster at score 160, only once
if (!infernoMonsterSpawned && score >= 160) {
infernoMonster = new InfernoMonster();
// Place initially at correct orbit position
infernoMonster.orbitAngle = Math.random() * Math.PI * 2;
infernoMonster.x = wizard.x + Math.cos(infernoMonster.orbitAngle) * 850;
infernoMonster.y = wizard.y + Math.sin(infernoMonster.orbitAngle) * 850;
enemies.push(infernoMonster);
game.addChild(infernoMonster);
infernoMonsterSpawned = true;
}
// Spawn boss at score 110
if (!bossSpawned && score >= 110) {
boss = new Boss();
boss.x = 2048 / 2;
boss.y = -120;
game.addChild(boss);
bossSpawned = true;
return;
}
// Stop enemy spawn at score 75 and spawn goblin
if (!enemy5Spawned && score >= 75 && (typeof enemy5EverKilled === "undefined" || !enemy5EverKilled)) {
// Spawn goblin (Enemy5) at score 75, only once
enemy5 = new Enemy5();
// Start at a random angle on the orbit
enemy5.orbitAngle = Math.random() * Math.PI * 2;
// Place initially at correct orbit position
enemy5.x = wizard.x + Math.cos(enemy5.orbitAngle) * (enemy5.orbitRadius || 850);
enemy5.y = wizard.y + Math.sin(enemy5.orbitAngle) * (enemy5.orbitRadius || 850);
enemies.push(enemy5);
game.addChild(enemy5);
enemy5Spawned = true;
// Play goblin spawn sound
if (LK.getSound('Goblinl')) {
LK.getSound('Goblinl').play();
}
// Do not return; allow normal enemies to keep spawning
}
// Only spawn normal enemies if goblin is not present
if (!enemy5 || enemy5.destroyed) {
spawnEnemy();
}
}, 1200);
// Game update: update wizard, fireballs, enemies, handle collisions, and scoring
game.update = function () {
// If game not started, only update startButton and wizard visuals
if (!gameStarted) {
// Stop slow timer effect if active
if (typeof stopSlowTimerEffect === "function") {
stopSlowTimerEffect();
}
// Clear fireCircle timers if present
if (typeof fireCircleInterval !== "undefined" && fireCircleInterval) {
LK.clearInterval(fireCircleInterval);
fireCircleInterval = null;
}
if (typeof fireCircleTimeout !== "undefined" && fireCircleTimeout) {
LK.clearTimeout(fireCircleTimeout);
fireCircleTimeout = null;
}
// Clear permanent fireCircles if present
if (typeof fireCircles !== "undefined") {
for (var i = fireCircles.length - 1; i >= 0; i--) {
if (fireCircles[i] && !fireCircles[i].destroyed) fireCircles[i].destroy();
}
fireCircles = [];
}
// --- Reset magazine system on game over/start screen ---
// If minigun is selected, always set to 350 ammo at new game start
if (wizardAsset === "minigun") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("minigun");
wizardAmmo = wizardMaxAmmo;
tapFireCooldown = 500;
} else if (wizardAsset === "Lasergun") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("Lasergun");
wizardAmmo = wizardMaxAmmo;
tapFireCooldown = 300;
} else if (wizardAsset === "shotgun") {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo("shotgun");
wizardAmmo = wizardMaxAmmo;
tapFireCooldown = 200;
} else {
wizardMaxAmmo = applyAmmoUpgradeToMaxAmmo(wizardAsset);
wizardAmmo = wizardMaxAmmo;
}
wizardReloading = false;
if (wizardReloadTimeout) {
LK.clearTimeout(wizardReloadTimeout);
wizardReloadTimeout = null;
}
if (wizard.update) wizard.update();
// Animate start button (pulse effect)
if (typeof startButton !== "undefined" && startButton && startButton.children && startButton.children.length > 0) {
var t = LK.ticks || Date.now();
var scale = 1 + 0.06 * Math.sin(t * 0.12);
startButton.scale.x = scale;
startButton.scale.y = scale;
}
return;
}
// Update wizard
if (wizard.update) wizard.update();
// Update fireballs and remove destroyed ones
for (var i = fireballs.length - 1; i >= 0; i--) {
var fb = fireballs[i];
if (fb.update) fb.update();
if (fb.destroyed) {
fireballs.splice(i, 1);
}
}
// Update permanent fireCircles (if any)
if (typeof fireCircles !== "undefined") {
for (var i = fireCircles.length - 1; i >= 0; i--) {
var fc = fireCircles[i];
if (fc && fc.update) fc.update();
if (fc && fc.destroyed) {
fireCircles.splice(i, 1);
continue;
}
// --- FireCircle damage to enemies ---
if (fc && typeof enemies !== "undefined") {
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (!enemy || enemy.destroyed) continue;
// If enemy health is 0 or below, destroy enemy and handle rewards
if (enemy.health <= 0) {
// Spawn explosion at enemy position
var explosion = new Explosion();
explosion.x = enemy.x;
explosion.y = enemy.y;
game.addChild(explosion);
// Drop healthpack if Enemy6
if (enemy.type === 6) {
var healthpack = new Healthpack();
healthpack.x = enemy.x;
healthpack.y = enemy.y;
game.addChild(healthpack);
if (typeof healthpacks === "undefined") healthpacks = [];
healthpacks.push(healthpack);
}
// Determine coin count and plus text based on enemy type
var coinAmount = 1;
if (enemy.type === 2) coinAmount = 2;
if (enemy.type === 3) coinAmount = 3;
if (enemy.type === 4) coinAmount = 4;
if (enemy.type === 5) coinAmount = 5;
// Drop the correct number of coins
if (enemy.type === 5) {
// Drop 20 coins at goblin's position when killed
for (var cc = 0; cc < 20; cc++) {
var coin = new Coin();
coin.x = enemy.x;
coin.y = enemy.y;
coin.vx = 0;
coin.vy = 0;
coin.gravity = 0;
if (cc === 0) coin.amount = 20;
coins.push(coin);
game.addChild(coin);
}
} else {
for (var cc = 0; cc < coinAmount; cc++) {
var coin = new Coin();
coin.x = enemy.x;
coin.y = enemy.y;
coin.vx = 0;
coin.vy = 0;
coin.gravity = 0;
if (cc === 0) {
coin.amount = coinAmount;
}
coins.push(coin);
game.addChild(coin);
}
}
// Play dead sound
LK.getSound('dead').play();
// If this is Enemy5, also clear enemy5 reference so it never respawns
if (enemy.type === 5) {
// Show goblingain effect at goblin's position
var goblingain = LK.getAsset('goblingain', {
anchorX: 0.5,
anchorY: 0.5,
x: enemy.x,
y: enemy.y,
scaleX: 1.0,
scaleY: 1.0,
alpha: 1
});
game.addChild(goblingain);
// Animate goblingain: scale up and fade out over 0.7s (42 frames)
(function (gobObj) {
var frames = 42,
frame = 0;
gobObj.update = function () {
frame++;
gobObj.scale.x = 1.0 + 0.5 * (frame / frames);
gobObj.scale.y = 1.0 + 0.5 * (frame / frames);
gobObj.alpha = 1 - frame / frames;
if (frame >= frames) {
gobObj.destroy();
}
};
})(goblingain);
// Mark goblin as permanently killed
enemy5EverKilled = true;
if (typeof enemy5 !== "undefined" && enemy5 === enemy) {
enemy5 = null;
enemy5Spawned = false; // Allow normal enemies to spawn again
}
// Resume normal enemy spawn if it was paused for goblin
// (Handled by enemySpawnTimer logic: normal enemies spawn if enemy5 is null or destroyed)
}
enemy.destroy();
enemies.splice(j, 1);
score += 1;
scoreText.setText("Score: " + score);
}
}
}
}
}
// Update coins and remove destroyed ones
for (var c = coins.length - 1; c >= 0; c--) {
var coin = coins[c];
// Move coin directly toward wizard
var dx = wizard.x - coin.x;
var dy = wizard.y - coin.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var speed = 22; // Fast enough to feel instant, but visible
if (dist > 1) {
coin.x += dx / dist * Math.min(speed, dist);
coin.y += dy / dist * Math.min(speed, dist);
} else {
coin.x = wizard.x;
coin.y = wizard.y;
}
// Check collision with wizard (collect coin)
if (coin.lastWasIntersecting === undefined) coin.lastWasIntersecting = false;
var coinIntersecting = coin.intersects(wizard);
if (!coin.lastWasIntersecting && coinIntersecting) {
// Do not add to score when collecting coin
coinCount += 1;
coinText.setText("" + coinCount);
// Save coin count persistently
storage.coinCount = coinCount;
// Create gain effect (use gain asset instead of explosion)
var gainEffect = LK.getAsset('gain', {
anchorX: 0.5,
anchorY: 0.5,
x: coin.x,
y: coin.y,
scaleX: 1,
scaleY: 1,
alpha: 1
});
game.addChild(gainEffect);
// Animate gain effect: scale up and fade out, then destroy
(function (effect) {
var frames = 24;
var frame = 0;
effect.update = function () {
frame++;
effect.scale.x = 1 + 0.5 * (frame / frames);
effect.scale.y = 1 + 0.5 * (frame / frames);
effect.alpha = 1 - frame / frames;
if (frame >= frames) {
effect.destroy();
}
};
})(gainEffect);
// Create +N asset effect at wizard position
var plusAmount = 1;
if (coin.hasOwnProperty('amount')) {
plusAmount = coin.amount;
}
var plusText = new Text2("+" + plusAmount, {
size: 90,
fill: 0xFFD700
});
plusText.anchor.set(0.5, 0.5);
plusText.x = wizard.x;
plusText.y = wizard.y;
game.addChild(plusText);
// Animate +N asset: move up and fade out, then destroy
(function (txt) {
var startY = txt.y;
var frames = 36;
var frame = 0;
txt.alpha = 1;
txt.update = function () {
frame++;
txt.y = startY - frame * 2.2;
txt.alpha = 1 - frame / frames;
if (frame >= frames) {
txt.destroy();
}
};
})(plusText);
// Remove coin
coin.destroy();
coins.splice(c, 1);
continue;
}
coin.lastWasIntersecting = coinIntersecting;
}
// --- Healthpack update and collection ---
if (typeof healthpacks === "undefined") healthpacks = [];
for (var h = healthpacks.length - 1; h >= 0; h--) {
var hp = healthpacks[h];
if (hp.update) hp.update();
if (hp.destroyed) {
healthpacks.splice(h, 1);
continue;
}
if (hp.lastWasIntersecting === undefined) hp.lastWasIntersecting = false;
var hpIntersecting = hp.intersects(wizard);
if (!hp.lastWasIntersecting && hpIntersecting) {
// Restore 2 wizard health, up to max
wizardHealth = Math.min(maxWizardHealth, wizardHealth + 2);
storage.wizardHealth = wizardHealth;
updateHealthBar();
// Flash wizard green for feedback
if (wizard.children && wizard.children.length > 0) {
LK.effects.flashObject(wizard.children[0], 0x2ecc40, 400);
}
// Show health gain effect
var healthGain = LK.getAsset('health', {
anchorX: 0.5,
anchorY: 0.5,
x: wizard.x,
y: wizard.y,
scaleX: 1,
scaleY: 1,
alpha: 1
});
game.addChild(healthGain);
(function (effect) {
var frames = 24;
var frame = 0;
effect.update = function () {
frame++;
effect.scale.x = 1 + 0.5 * (frame / frames);
effect.scale.y = 1 + 0.5 * (frame / frames);
effect.alpha = 1 - frame / frames;
if (frame >= frames) {
effect.destroy();
}
};
})(healthGain);
// Show "+2" text
var plus2 = new Text2("+2", {
size: 90,
fill: 0x2ecc40
});
plus2.anchor.set(0.5, 0.5);
plus2.x = wizard.x;
plus2.y = wizard.y;
game.addChild(plus2);
(function (txt) {
var startY = txt.y;
var frames = 36;
var frame = 0;
txt.alpha = 1;
txt.update = function () {
frame++;
txt.y = startY - frame * 2.2;
txt.alpha = 1 - frame / frames;
if (frame >= frames) {
txt.destroy();
}
};
})(plus2);
// Remove healthpack
hp.destroy();
healthpacks.splice(h, 1);
continue;
}
hp.lastWasIntersecting = hpIntersecting;
}
// Position coin icon and text below the score text (centered)
if (scoreText.parent && coinIcon.parent && coinText.parent) {
// Place coin icon below the score text, centered
coinIcon.x = scoreText.x;
coinIcon.y = scoreText.y + scoreText.height + coinIcon.height * 0.6;
// Place coinText 100px to the right and 50px above the coin icon
coinText.x = coinIcon.x + 100;
coinText.y = coinIcon.y - 50;
// Position health bar under coin asset
if (typeof healthBarBg !== "undefined" && typeof healthBarFill !== "undefined") {
healthBarBg.x = coinIcon.x;
healthBarBg.y = coinIcon.y + coinIcon.height * 0.7 + healthBarBg.height / 2 + 8;
healthBarFill.x = healthBarBg.x - (healthBarBg.width - 8) * (1 - Math.max(0, Math.min(1, wizardHealth / maxWizardHealth))) / 2;
healthBarFill.y = healthBarBg.y;
// Keep label centered in bar
if (typeof healthBarLabel !== "undefined") {
healthBarLabel.x = healthBarBg.x;
healthBarLabel.y = healthBarBg.y;
}
}
updateHealthBar();
// --- Position and update ammo bar below health bar ---
if (typeof ammoBarBg !== "undefined" && typeof ammoBarFill !== "undefined") {
ammoBarBg.x = healthBarBg.x;
ammoBarBg.y = healthBarBg.y + healthBarBg.height / 2 + ammoBarBg.height / 2 + 10;
ammoBarFill.x = ammoBarBg.x;
ammoBarFill.y = ammoBarBg.y;
if (typeof ammoBarLabel !== "undefined") {
ammoBarLabel.x = ammoBarBg.x;
ammoBarLabel.y = ammoBarBg.y;
// Update ammo label text
if (wizardAsset === "shuriken") {
ammoBarLabel.setText("∞/∞");
} else if (typeof wizardAmmo !== "undefined" && typeof wizardMaxAmmo !== "undefined") {
ammoBarLabel.setText(wizardAmmo + "/" + wizardMaxAmmo);
} else {
ammoBarLabel.setText("");
}
}
// Update ammo bar fill width
var ammoRatio = 1;
if (wizardAsset === "shuriken") {
ammoRatio = 1; // always full for shuriken
} else if (typeof wizardAmmo !== "undefined" && typeof wizardMaxAmmo !== "undefined" && wizardMaxAmmo > 0) {
ammoRatio = Math.max(0, Math.min(1, wizardAmmo / wizardMaxAmmo));
}
ammoBarFill.width = (ammoBarBg.width - 8) * ammoRatio;
ammoBarFill.x = ammoBarBg.x - (ammoBarBg.width - 8) * (1 - ammoRatio) / 2;
}
}
// Update enemies and remove destroyed ones
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (enemy.update) enemy.update();
if (enemy.destroyed) {
enemies.splice(j, 1);
continue;
}
// ... (rest of enemy collision code unchanged)
// Check collision with wizard (health system)
if (enemy.lastWasIntersecting === undefined) enemy.lastWasIntersecting = false;
var nowIntersecting = enemy.intersects(wizard);
if (!enemy.lastWasIntersecting && nowIntersecting) {
// Only decrease health if not already at 0
if (wizardHealth > 0) {
wizardHealth -= 1;
// Save wizard health
storage.wizardHealth = wizardHealth;
// Flash wizard red
if (wizard.children && wizard.children.length > 0) {
LK.effects.flashObject(wizard.children[0], 0xff0000, 400);
}
// Update health bar (no-op)
updateHealthBar();
// Remove enemy after hit
// If this is Enemy5, also clear enemy5 reference so it never respawns
if (enemy.type === 5) {
if (typeof enemy5 !== "undefined" && enemy5 === enemy) {
enemy5 = null;
enemy5Spawned = false;
}
}
enemy.destroy();
enemies.splice(j, 1);
// If health reaches 0, trigger game over
if (wizardHealth <= 0) {
totalCoins += coinCount;
storage.totalCoins = totalCoins;
LK.effects.flashScreen(0xff0000, 1000);
// Instead of game over, reset to start screen
gameStarted = false;
// Recreate start UI if needed
if (!startUiContainer || startUiContainer.destroyed) {
startUiContainer = new Container();
startButton = new Container();
startButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: startButtonWidth,
height: startButtonHeight,
color: 0x1e90ff
});
startButtonBg.alpha = 0.92;
startButton.addChild(startButtonBg);
startButtonText = new Text2("START GAME", {
size: 220,
fill: "#fff"
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 0;
startButtonText.y = -120;
startButton.addChild(startButtonText);
upgradeButton = new Container();
upgradeButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: upgradeButtonWidth,
height: upgradeButtonHeight,
color: 0x2ecc71
});
upgradeButtonBg.alpha = 0.92;
upgradeButton.addChild(upgradeButtonBg);
upgradeButtonText = new Text2("UPGRADE", {
size: 110,
// Half of recreated START GAME (220)
fill: "#fff"
});
upgradeButtonText.anchor.set(0.5, 0.5);
upgradeButtonText.x = 0;
upgradeButtonText.y = 0;
upgradeButton.addChild(upgradeButtonText);
startButton.x = 0;
startButton.y = -400;
upgradeButton.x = 0;
upgradeButton.y = startButton.y + startButtonHeight / 2 + 55 + upgradeButtonHeight / 2;
startUiContainer.addChild(startButton);
startUiContainer.addChild(upgradeButton);
startUiContainer.x = 2048 / 2;
startUiContainer.y = 2732 / 2;
game.addChild(startUiContainer);
// Re-attach handlers
upgradeButton.down = function (x, y, obj) {
if (upgradeWindow && !upgradeWindow.destroyed) return;
showUpgradeWindow();
};
startButton.down = function (x, y, obj) {
var now = Date.now();
if (now - lastTapFireTime < tapFireCooldown) {
// Too soon, ignore this tap for firing
return;
}
lastTapFireTime = now;
if (gameStarted) return;
gameStarted = true;
if (startUiContainer && !startUiContainer.destroyed) {
startUiContainer.destroy();
}
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] && !enemies[i].destroyed) enemies[i].destroy();
enemies.splice(i, 1);
}
for (var i = fireballs.length - 1; i >= 0; i--) {
if (fireballs[i] && !fireballs[i].destroyed) fireballs[i].destroy();
fireballs.splice(i, 1);
}
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i] && !coins[i].destroyed) coins[i].destroy();
coins.splice(i, 1);
}
score = 0;
// coinCount is NOT reset here, so coins are preserved when starting a new game
scoreText.setText("Score: 0");
coinText.setText("" + coinCount);
wizard.x = 2048 / 2;
wizard.y = 2732 / 2;
// Load upgrades for selected weapon
currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
// (damageUpgradeLevel and damageUpgradeCost removed)
healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
wizardHealth = currentWeaponUpgrades.wizardHealth;
fireballDamage = currentWeaponUpgrades.fireballDamage;
// Remove all children to ensure only one wizard asset
for (var i = wizard.children.length - 1; i >= 0; i--) {
wizard.removeChild(wizard.children[i]);
}
// Attach wizard asset if not present
var hasWizardSprite = false;
for (var i = 0; i < wizard.children.length; i++) {
hasWizardSprite = true;
break;
}
if (!hasWizardSprite) {
var newSprite = wizard.attachAsset(wizardAsset, {
anchorX: 0.5,
anchorY: 0.5
});
if (newSprite) {
newSprite.rotation = Math.PI;
}
} else if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = Math.PI;
}
// Switch upgrades to selected weapon on restart
currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
// (damageUpgradeLevel and damageUpgradeCost removed)
healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
wizardHealth = currentWeaponUpgrades.wizardHealth;
fireballDamage = currentWeaponUpgrades.fireballDamage;
wizardHealth = maxWizardHealth;
storage.wizardHealth = wizardHealth;
storage.maxWizardHealth = maxWizardHealth;
storage.wizardHealth = wizardHealth;
storage.maxWizardHealth = maxWizardHealth;
updateHealthBar();
game.move = originalGameMove;
game.down = originalGameDown;
game.up = originalGameUp;
};
} else {
// If UI exists, just re-add it
if (startUiContainer.parent !== game) {
game.addChild(startUiContainer);
}
// Recreate shop button and handlers if needed
if (typeof shopButton === "undefined" || !shopButton || shopButton.destroyed) {
// --- SHOP BUTTON UI LOGIC (recreate) ---
shopButton = new Container();
shopButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: shopButtonWidth,
height: shopButtonHeight
});
shopButtonBg.alpha = 0.92;
shopButton.addChild(shopButtonBg);
shopButtonText = new Text2("SHOP", {
size: 110,
fill: "#fff"
});
shopButtonText.anchor.set(0.5, 0.5);
shopButtonText.x = 0;
shopButtonText.y = 0;
shopButton.addChild(shopButtonText);
shopButton.x = 0;
shopButton.y = upgradeButton.y + upgradeButtonHeight / 2 + 40 + shopButtonHeight / 2;
startUiContainer.addChild(shopButton);
shopButton.down = function (x, y, obj) {
if (shopWindow && !shopWindow.destroyed) return;
showShopWindow();
};
}
}
// Block input until start is pressed
blockGameInput();
return;
}
}
}
enemy.lastWasIntersecting = nowIntersecting;
// Check collision with fireballs
for (var k = fireballs.length - 1; k >= 0; k--) {
var fb = fireballs[k];
if (enemy['fb' + k + '_lastIntersecting'] === undefined) enemy['fb' + k + '_lastIntersecting'] = false;
var fbIntersect = enemy.intersects(fb);
// Only allow collision if fireball is inside the visible screen
var fireballOnScreen = fb.x >= 0 && fb.x <= 2048 && fb.y >= 0 && fb.y <= 2732;
if (!enemy['fb' + k + '_lastIntersecting'] && fbIntersect && fireballOnScreen) {
// Decrease enemy health by fireballDamage (default 1, upgrades increase)
// Award 1 coin for each fireball hit on goblin (not killed)
if (enemy.type === 5 && enemy.health > 0) {
// Drop 1 coin at goblin's position
var coin = new Coin();
coin.x = enemy.x;
coin.y = enemy.y;
coin.vx = 0;
coin.vy = 0;
coin.gravity = 0;
coin.amount = 1;
coins.push(coin);
game.addChild(coin);
}
enemy.health -= fireballDamage;
// Destroy fireball
fb.destroy();
fireballs.splice(k, 1);
// If enemy health reaches 0, destroy enemy and increase score
if (enemy.health <= 0) {
// Spawn explosion at enemy position
var explosion = new Explosion();
explosion.x = enemy.x;
explosion.y = enemy.y;
game.addChild(explosion);
// Drop healthpack if Enemy6
if (enemy.type === 6) {
var healthpack = new Healthpack();
healthpack.x = enemy.x;
healthpack.y = enemy.y;
game.addChild(healthpack);
if (typeof healthpacks === "undefined") healthpacks = [];
healthpacks.push(healthpack);
}
// Determine coin count and plus text based on enemy type
var coinAmount = 1;
if (enemy.type === 2) coinAmount = 2;
if (enemy.type === 3) coinAmount = 3;
if (enemy.type === 4) coinAmount = 4;
if (enemy.type === 5) coinAmount = 5;
// Drop the correct number of coins
if (enemy.type === 5) {
// Drop 20 coins at goblin's position when killed
for (var cc = 0; cc < 20; cc++) {
var coin = new Coin();
coin.x = enemy.x;
coin.y = enemy.y;
coin.vx = 0;
coin.vy = 0;
coin.gravity = 0;
if (cc === 0) coin.amount = 20;
coins.push(coin);
game.addChild(coin);
}
} else {
for (var cc = 0; cc < coinAmount; cc++) {
var coin = new Coin();
coin.x = enemy.x;
coin.y = enemy.y;
coin.vx = 0;
coin.vy = 0;
coin.gravity = 0;
// For multi-coin drops, only the first coin will show +N at wizard
if (cc === 0) {
coin.amount = coinAmount;
}
coins.push(coin);
game.addChild(coin);
}
}
// Play dead sound
LK.getSound('dead').play();
// If this is Enemy5, also clear enemy5 reference so it never respawns
if (enemy.type === 5) {
// Show goblingain effect at goblin's position
var goblingain = LK.getAsset('goblingain', {
anchorX: 0.5,
anchorY: 0.5,
x: enemy.x,
y: enemy.y,
scaleX: 1.0,
scaleY: 1.0,
alpha: 1
});
game.addChild(goblingain);
// Animate goblingain: scale up and fade out over 0.7s (42 frames)
(function (gobObj) {
var frames = 42,
frame = 0;
gobObj.update = function () {
frame++;
gobObj.scale.x = 1.0 + 0.5 * (frame / frames);
gobObj.scale.y = 1.0 + 0.5 * (frame / frames);
gobObj.alpha = 1 - frame / frames;
if (frame >= frames) {
gobObj.destroy();
}
};
})(goblingain);
// Mark goblin as permanently killed
enemy5EverKilled = true;
if (typeof enemy5 !== "undefined" && enemy5 === enemy) {
enemy5 = null;
enemy5Spawned = false; // Allow normal enemies to spawn again
}
// Resume normal enemy spawn if it was paused for goblin
// (Handled by enemySpawnTimer logic: normal enemies spawn if enemy5 is null or destroyed)
}
// --- INFERNOMONSTER SPLIT LOGIC ---
if (enemy.type === 99 && typeof InfernoMonster !== "undefined") {
// Split into two smaller InfernoMonsters if not already small
// Only split if not already a "split" (use a flag or reduced health)
if (!enemy._wasSplit && enemy.orbitRadius > 400) {
for (var splitIdx = 0; splitIdx < 2; splitIdx++) {
var splitMonster = new InfernoMonster();
// Place at enemy's position, but offset a bit
var angle = Math.random() * Math.PI * 2;
splitMonster.x = enemy.x + Math.cos(angle) * 80 * (splitIdx === 0 ? 1 : -1);
splitMonster.y = enemy.y + Math.sin(angle) * 80 * (splitIdx === 0 ? 1 : -1);
// Make smaller: half orbit radius, half health, mark as split
splitMonster.orbitRadius = Math.max(400, enemy.orbitRadius * 0.5);
splitMonster.health = Math.max(3, Math.floor(enemy.health * 0.5));
splitMonster._wasSplit = true;
// Randomize orbit direction and angle
splitMonster.orbitAngle = Math.random() * Math.PI * 2;
splitMonster.orbitDirection = Math.random() < 0.5 ? 1 : -1;
// Add to enemies and game
enemies.push(splitMonster);
game.addChild(splitMonster);
}
}
}
enemy.destroy();
enemies.splice(j, 1);
score += 1;
scoreText.setText("Score: " + score);
// (Removed: +N effect at enemy death. Now shown at wizard when coin is collected)
break;
}
}
enemy['fb' + k + '_lastIntersecting'] = fbIntersect;
}
}
// --- Boss update and boss fireball collision ---
if (boss && !boss.destroyed) {
if (boss.update) boss.update();
// Boss hit by fireballs
for (var k = fireballs.length - 1; k >= 0; k--) {
var fb = fireballs[k];
if (boss['fb' + k + '_lastIntersecting'] === undefined) boss['fb' + k + '_lastIntersecting'] = false;
var fbIntersect = boss.intersects(fb);
// Only allow collision if fireball is inside the visible screen
var fireballOnScreen = fb.x >= 0 && fb.x <= 2048 && fb.y >= 0 && fb.y <= 2732;
if (!boss['fb' + k + '_lastIntersecting'] && fbIntersect && fireballOnScreen) {
boss.health -= fireballDamage;
fb.destroy();
fireballs.splice(k, 1);
// Flash boss on hit
if (boss.children && boss.children.length > 0) {
LK.effects.flashObject(boss.children[0], 0xff0000, 200);
}
// Boss death
if (boss.health <= 0) {
// Spawn explosion at boss position
var explosion = new Explosion();
explosion.x = boss.x;
explosion.y = boss.y;
game.addChild(explosion);
// Drop 10 coins
for (var cc = 0; cc < 10; cc++) {
var coin = new Coin();
coin.x = boss.x;
coin.y = boss.y;
coin.vx = 0;
coin.vy = 0;
coin.gravity = 0;
if (cc === 0) coin.amount = 10;
coins.push(coin);
game.addChild(coin);
}
LK.getSound('dead').play();
boss.destroy();
boss = null;
// Clean up all bossBullets after boss death
for (var bb = bossBullets.length - 1; bb >= 0; bb--) {
if (bossBullets[bb] && !bossBullets[bb].destroyed) bossBullets[bb].destroy();
bossBullets.splice(bb, 1);
}
// Award 10 score for boss
score += 10;
scoreText.setText("Score: " + score);
// --- Show UI with two buttons: Slow Timer and Fireball ---
if (typeof bossRewardUi !== "undefined" && bossRewardUi && !bossRewardUi.destroyed) {
bossRewardUi.destroy();
}
bossRewardUi = new Container();
// Background
var rewardBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: 900,
height: 600
});
rewardBg.alpha = 0.98;
bossRewardUi.addChild(rewardBg);
// Title
var rewardTitle = new Text2("Choose Your Reward!", {
size: 110,
fill: "#fff"
});
rewardTitle.anchor.set(0.5, 0.5);
rewardTitle.x = 0;
rewardTitle.y = -180;
bossRewardUi.addChild(rewardTitle);
// Slow Timer Button
var slowBtn = new Container();
var slowBtnBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: 340,
height: 140
});
slowBtnBg.alpha = 0.93;
slowBtn.addChild(slowBtnBg);
// Use timer asset instead of text
var slowBtnIcon = LK.getAsset('timer', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.1,
scaleY: 1.1,
x: 0,
y: 0
});
slowBtn.addChild(slowBtnIcon);
slowBtn.x = -200;
slowBtn.y = 80;
bossRewardUi.addChild(slowBtn);
// Fireball Button
var fireballBtn = new Container();
var fireballBtnBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: 340,
height: 140
});
fireballBtnBg.alpha = 0.93;
fireballBtn.addChild(fireballBtnBg);
// Use firecircle asset instead of text
var fireballBtnIcon = LK.getAsset('firecircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.1,
scaleY: 1.1,
x: 0,
y: 0
});
fireballBtn.addChild(fireballBtnIcon);
fireballBtn.x = 200;
fireballBtn.y = 80;
bossRewardUi.addChild(fireballBtn);
// Center UI
bossRewardUi.x = 2048 / 2;
bossRewardUi.y = 2732 / 2;
game.addChild(bossRewardUi);
// Pause enemy spawn until a button is pressed
enemySpawnStopped = true;
if (enemySpawnTimer) LK.clearInterval(enemySpawnTimer);
// Button handlers: resume game and remove UI
var resumeAfterReward = function resumeAfterReward() {
if (bossRewardUi && !bossRewardUi.destroyed) {
bossRewardUi.destroy();
bossRewardUi = null;
}
// Resume enemy spawn: restore normal spawn logic after boss (no restriction)
enemySpawnStopped = false;
if (enemySpawnTimer) LK.clearInterval(enemySpawnTimer);
enemySpawnTimer = LK.setInterval(function () {
// Pause enemy spawn if boss is present and alive
if (boss && !boss.destroyed) {
return;
}
if (!enemySpawnStopped) {
// After boss, spawn enemies with normal logic but at 0.8s interval
spawnEnemy();
}
}, 800); // spawn every 0.8s after boss
};
slowBtn.down = function (x, y, obj) {
// Start slow timer effect: every 10s, for 2s, slow all enemies by 1/3 speed
resumeAfterReward();
// Defensive: clear any previous intervals/timeouts
if (typeof slowTimerInterval !== "undefined" && slowTimerInterval) {
LK.clearInterval(slowTimerInterval);
slowTimerInterval = null;
}
if (typeof slowTimerTimeout !== "undefined" && slowTimerTimeout) {
LK.clearTimeout(slowTimerTimeout);
slowTimerTimeout = null;
}
// Helper: mark enemies as slowed and restore after 2s
function applySlowToEnemies() {
if (typeof enemies === "undefined") return;
for (var i = 0; i < enemies.length; i++) {
var e = enemies[i];
if (!e || e.destroyed) continue;
// Only slow if not already slowed
if (!e._isSlowed) {
e._origVx = typeof e._origVx === "number" ? e._origVx : e.vx;
e._origVy = typeof e._origVy === "number" ? e._origVy : e.vy;
e.vx = e.vx * (1 / 3);
e.vy = e.vy * (1 / 3);
e._isSlowed = true;
}
}
}
function restoreEnemiesSpeed() {
if (typeof enemies === "undefined") return;
for (var i = 0; i < enemies.length; i++) {
var e = enemies[i];
if (!e || e.destroyed) continue;
if (e._isSlowed) {
// Restore only if we have original values
if (typeof e._origVx === "number") e.vx = e._origVx;
if (typeof e._origVy === "number") e.vy = e._origVy;
e._isSlowed = false;
}
}
}
// Start the repeating slow effect
slowTimerInterval = LK.setInterval(function () {
applySlowToEnemies();
// After 4s, restore speed
slowTimerTimeout = LK.setTimeout(function () {
restoreEnemiesSpeed();
}, 4000);
}, 10000);
// Defensive: also restore on game over or restart
if (typeof stopSlowTimerEffect === "undefined") {
stopSlowTimerEffect = function stopSlowTimerEffect() {
if (typeof slowTimerInterval !== "undefined" && slowTimerInterval) {
LK.clearInterval(slowTimerInterval);
slowTimerInterval = null;
}
if (typeof slowTimerTimeout !== "undefined" && slowTimerTimeout) {
LK.clearTimeout(slowTimerTimeout);
slowTimerTimeout = null;
}
restoreEnemiesSpeed();
};
}
};
fireballBtn.down = function (x, y, obj) {
// FireCircle reward: spawn a single permanent, rotating firecircle
resumeAfterReward();
// Remove any previous firecircle timers
if (typeof fireCircleInterval !== "undefined" && fireCircleInterval) {
LK.clearInterval(fireCircleInterval);
fireCircleInterval = null;
}
if (typeof fireCircleTimeout !== "undefined" && fireCircleTimeout) {
LK.clearTimeout(fireCircleTimeout);
fireCircleTimeout = null;
}
// Remove any existing firecircles
if (typeof fireCircles === "undefined") fireCircles = [];
for (var i = fireCircles.length - 1; i >= 0; i--) {
if (fireCircles[i] && !fireCircles[i].destroyed) fireCircles[i].destroy();
fireCircles.splice(i, 1);
}
// Spawn a single permanent FireCircle
var fc = new FireCircle();
fc.orbitAngle = Math.random() * Math.PI * 2;
if (typeof wizard !== "undefined" && wizard && !wizard.destroyed) {
fc.centerX = wizard.x;
fc.centerY = wizard.y;
}
game.addChild(fc);
fireCircles.push(fc);
};
break;
}
}
boss['fb' + k + '_lastIntersecting'] = fbIntersect;
}
// --- BossBullet update, collision with wizard and fireballs ---
}
// Update bossBullets
for (var b = bossBullets.length - 1; b >= 0; b--) {
var bullet = bossBullets[b];
if (bullet.update) bullet.update();
if (bullet.destroyed) {
bossBullets.splice(b, 1);
continue;
}
// Collision with wizard
if (bullet.lastWasIntersecting === undefined) bullet.lastWasIntersecting = false;
var bulletIntersecting = bullet.intersects(wizard);
if (!bullet.lastWasIntersecting && bulletIntersecting) {
// Wizard takes 1 damage
if (wizardHealth > 0) {
wizardHealth -= 1;
storage.wizardHealth = wizardHealth;
if (wizard.children && wizard.children.length > 0) {
LK.effects.flashObject(wizard.children[0], 0xff0000, 400);
}
updateHealthBar();
// If health reaches 0, trigger game over and return to main screen
if (wizardHealth <= 0) {
totalCoins += coinCount;
storage.totalCoins = totalCoins;
LK.effects.flashScreen(0xff0000, 1000);
gameStarted = false;
// Recreate start UI if needed
if (!startUiContainer || startUiContainer.destroyed) {
startUiContainer = new Container();
startButton = new Container();
startButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: startButtonWidth,
height: startButtonHeight,
color: 0x1e90ff
});
startButtonBg.alpha = 0.92;
startButton.addChild(startButtonBg);
startButtonText = new Text2("START GAME", {
size: 220,
fill: "#fff"
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 0;
startButtonText.y = -120;
startButton.addChild(startButtonText);
upgradeButton = new Container();
upgradeButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: upgradeButtonWidth,
height: upgradeButtonHeight,
color: 0x2ecc71
});
upgradeButtonBg.alpha = 0.92;
upgradeButton.addChild(upgradeButtonBg);
upgradeButtonText = new Text2("UPGRADE", {
size: 110,
// Half of recreated START GAME (220)
fill: "#fff"
});
upgradeButtonText.anchor.set(0.5, 0.5);
upgradeButtonText.x = 0;
upgradeButtonText.y = 0;
upgradeButton.addChild(upgradeButtonText);
startButton.x = 0;
startButton.y = -400;
upgradeButton.x = 0;
upgradeButton.y = startButton.y + startButtonHeight / 2 + 55 + upgradeButtonHeight / 2;
startUiContainer.addChild(startButton);
startUiContainer.addChild(upgradeButton);
startUiContainer.x = 2048 / 2;
startUiContainer.y = 2732 / 2;
game.addChild(startUiContainer);
// Re-attach handlers
upgradeButton.down = function (x, y, obj) {
if (upgradeWindow && !upgradeWindow.destroyed) return;
showUpgradeWindow();
};
startButton.down = function (x, y, obj) {
var now = Date.now();
if (now - lastTapFireTime < tapFireCooldown) {
// Too soon, ignore this tap for firing
return;
}
lastTapFireTime = now;
if (gameStarted) return;
gameStarted = true;
if (startUiContainer && !startUiContainer.destroyed) {
startUiContainer.destroy();
}
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] && !enemies[i].destroyed) enemies[i].destroy();
enemies.splice(i, 1);
}
for (var i = fireballs.length - 1; i >= 0; i--) {
if (fireballs[i] && !fireballs[i].destroyed) fireballs[i].destroy();
fireballs.splice(i, 1);
}
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i] && !coins[i].destroyed) coins[i].destroy();
coins.splice(i, 1);
}
score = 0;
// coinCount is NOT reset here, so coins are preserved when starting a new game
scoreText.setText("Score: 0");
coinText.setText("" + coinCount);
wizard.x = 2048 / 2;
wizard.y = 2732 / 2;
// Load upgrades for selected weapon
currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
// (damageUpgradeLevel and damageUpgradeCost removed)
healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
wizardHealth = currentWeaponUpgrades.wizardHealth;
fireballDamage = currentWeaponUpgrades.fireballDamage;
// Remove all children to ensure only one wizard asset
for (var i = wizard.children.length - 1; i >= 0; i--) {
wizard.removeChild(wizard.children[i]);
}
// Attach wizard asset if not present
var hasWizardSprite = false;
for (var i = 0; i < wizard.children.length; i++) {
hasWizardSprite = true;
break;
}
if (!hasWizardSprite) {
var newSprite = wizard.attachAsset(wizardAsset, {
anchorX: 0.5,
anchorY: 0.5
});
if (newSprite) {
newSprite.rotation = Math.PI;
}
} else if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = Math.PI;
}
// Switch upgrades to selected weapon on restart
currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
// (damageUpgradeLevel and damageUpgradeCost removed)
healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
wizardHealth = currentWeaponUpgrades.wizardHealth;
fireballDamage = currentWeaponUpgrades.fireballDamage;
wizardHealth = maxWizardHealth;
storage.wizardHealth = wizardHealth;
storage.maxWizardHealth = maxWizardHealth;
storage.wizardHealth = wizardHealth;
storage.maxWizardHealth = maxWizardHealth;
updateHealthBar();
game.move = originalGameMove;
game.down = originalGameDown;
game.up = originalGameUp;
};
} else {
// If UI exists, just re-add it
if (startUiContainer.parent !== game) {
game.addChild(startUiContainer);
}
// Recreate shop button and handlers if needed
if (typeof shopButton === "undefined" || !shopButton || shopButton.destroyed) {
// --- SHOP BUTTON UI LOGIC (recreate) ---
shopButton = new Container();
shopButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: shopButtonWidth,
height: shopButtonHeight
});
shopButtonBg.alpha = 0.92;
shopButton.addChild(shopButtonBg);
shopButtonText = new Text2("SHOP", {
size: 110,
fill: "#fff"
});
shopButtonText.anchor.set(0.5, 0.5);
shopButtonText.x = 0;
shopButtonText.y = 0;
shopButton.addChild(shopButtonText);
shopButton.x = 0;
shopButton.y = upgradeButton.y + upgradeButtonHeight / 2 + 40 + shopButtonHeight / 2;
startUiContainer.addChild(shopButton);
shopButton.down = function (x, y, obj) {
if (shopWindow && !shopWindow.destroyed) return;
showShopWindow();
};
}
}
// Block input until start is pressed
blockGameInput();
return;
}
}
// Destroy bullet
bullet.destroy();
bossBullets.splice(b, 1);
// If health reaches 0, trigger game over (handled above)
continue;
}
bullet.lastWasIntersecting = bulletIntersecting;
// Collision with fireballs
for (var k = fireballs.length - 1; k >= 0; k--) {
var fb = fireballs[k];
if (bullet['fb' + k + '_lastIntersecting'] === undefined) bullet['fb' + k + '_lastIntersecting'] = false;
var fbIntersect = bullet.intersects(fb);
var fireballOnScreen = fb.x >= 0 && fb.x <= 2048 && fb.y >= 0 && fb.y <= 2732;
if (!bullet['fb' + k + '_lastIntersecting'] && fbIntersect && fireballOnScreen) {
bullet.health -= fireballDamage;
fb.destroy();
fireballs.splice(k, 1);
// Flash bullet on hit
if (bullet.children && bullet.children.length > 0) {
LK.effects.flashObject(bullet.children[0], 0xff0000, 200);
}
// If bullet destroyed, spawn explosion and remove
if (bullet.health <= 0) {
var explosion = new Explosion();
explosion.x = bullet.x;
explosion.y = bullet.y;
game.addChild(explosion);
bullet.destroy();
bossBullets.splice(b, 1);
break;
}
}
bullet['fb' + k + '_lastIntersecting'] = fbIntersect;
}
}
// --- InfernoBullet update, collision with wizard and fireballs ---
if (typeof infernoBullets !== "undefined") {
for (var ib = infernoBullets.length - 1; ib >= 0; ib--) {
var ibullet = infernoBullets[ib];
if (ibullet.update) ibullet.update();
if (ibullet.destroyed) {
infernoBullets.splice(ib, 1);
continue;
}
// Collision with wizard
if (ibullet.lastWasIntersecting === undefined) ibullet.lastWasIntersecting = false;
var ibulletIntersecting = ibullet.intersects(wizard);
if (!ibullet.lastWasIntersecting && ibulletIntersecting) {
// Wizard takes 1 damage
if (wizardHealth > 0) {
wizardHealth -= 1;
storage.wizardHealth = wizardHealth;
if (wizard.children && wizard.children.length > 0) {
LK.effects.flashObject(wizard.children[0], 0xff0000, 400);
}
updateHealthBar();
// If health reaches 0, trigger game over and return to main screen
if (wizardHealth <= 0) {
totalCoins += coinCount;
storage.totalCoins = totalCoins;
LK.effects.flashScreen(0xff0000, 1000);
gameStarted = false;
// Recreate start UI if needed
if (!startUiContainer || startUiContainer.destroyed) {
startUiContainer = new Container();
startButton = new Container();
startButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: startButtonWidth,
height: startButtonHeight,
color: 0x1e90ff
});
startButtonBg.alpha = 0.92;
startButton.addChild(startButtonBg);
startButtonText = new Text2("START GAME", {
size: 220,
fill: "#fff"
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 0;
startButtonText.y = -120;
startButton.addChild(startButtonText);
upgradeButton = new Container();
upgradeButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: upgradeButtonWidth,
height: upgradeButtonHeight,
color: 0x2ecc71
});
upgradeButtonBg.alpha = 0.92;
upgradeButton.addChild(upgradeButtonBg);
upgradeButtonText = new Text2("UPGRADE", {
size: 110,
fill: "#fff"
});
upgradeButtonText.anchor.set(0.5, 0.5);
upgradeButtonText.x = 0;
upgradeButtonText.y = 0;
upgradeButton.addChild(upgradeButtonText);
startButton.x = 0;
startButton.y = -400;
upgradeButton.x = 0;
upgradeButton.y = startButton.y + startButtonHeight / 2 + 55 + upgradeButtonHeight / 2;
startUiContainer.addChild(startButton);
startUiContainer.addChild(upgradeButton);
startUiContainer.x = 2048 / 2;
startUiContainer.y = 2732 / 2;
game.addChild(startUiContainer);
// Re-attach handlers
upgradeButton.down = function (x, y, obj) {
if (upgradeWindow && !upgradeWindow.destroyed) return;
showUpgradeWindow();
};
startButton.down = function (x, y, obj) {
var now = Date.now();
if (now - lastTapFireTime < tapFireCooldown) {
// Too soon, ignore this tap for firing
return;
}
lastTapFireTime = now;
if (gameStarted) return;
gameStarted = true;
if (startUiContainer && !startUiContainer.destroyed) {
startUiContainer.destroy();
}
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] && !enemies[i].destroyed) enemies[i].destroy();
enemies.splice(i, 1);
}
for (var i = fireballs.length - 1; i >= 0; i--) {
if (fireballs[i] && !fireballs[i].destroyed) fireballs[i].destroy();
fireballs.splice(i, 1);
}
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i] && !coins[i].destroyed) coins[i].destroy();
coins.splice(i, 1);
}
score = 0;
// coinCount is NOT reset here, so coins are preserved when starting a new game
scoreText.setText("Score: 0");
coinText.setText("" + coinCount);
wizard.x = 2048 / 2;
wizard.y = 2732 / 2;
// Load upgrades for selected weapon
currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
// (damageUpgradeLevel and damageUpgradeCost removed)
healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
wizardHealth = currentWeaponUpgrades.wizardHealth;
fireballDamage = currentWeaponUpgrades.fireballDamage;
// Remove all children to ensure only one wizard asset
for (var i = wizard.children.length - 1; i >= 0; i--) {
wizard.removeChild(wizard.children[i]);
}
// Attach wizard asset if not present
var hasWizardSprite = false;
for (var i = 0; i < wizard.children.length; i++) {
hasWizardSprite = true;
break;
}
if (!hasWizardSprite) {
var newSprite = wizard.attachAsset(wizardAsset, {
anchorX: 0.5,
anchorY: 0.5
});
if (newSprite) {
newSprite.rotation = Math.PI;
}
} else if (wizard.children && wizard.children.length > 0) {
wizard.children[0].rotation = Math.PI;
}
// Switch upgrades to selected weapon on restart
currentWeaponUpgrades = getWeaponUpgrades(wizardAsset);
speedUpgradeLevel = currentWeaponUpgrades.speedUpgradeLevel;
speedUpgradeCost = currentWeaponUpgrades.speedUpgradeCost;
// (damageUpgradeLevel and damageUpgradeCost removed)
healthUpgradeLevel = currentWeaponUpgrades.healthUpgradeLevel;
healthUpgradeCost = currentWeaponUpgrades.healthUpgradeCost;
maxWizardHealth = currentWeaponUpgrades.maxWizardHealth;
wizardHealth = currentWeaponUpgrades.wizardHealth;
fireballDamage = currentWeaponUpgrades.fireballDamage;
wizardHealth = maxWizardHealth;
storage.wizardHealth = wizardHealth;
storage.maxWizardHealth = maxWizardHealth;
storage.wizardHealth = wizardHealth;
storage.maxWizardHealth = maxWizardHealth;
updateHealthBar();
game.move = originalGameMove;
game.down = originalGameDown;
game.up = originalGameUp;
};
} else {
// If UI exists, just re-add it
if (startUiContainer.parent !== game) {
game.addChild(startUiContainer);
}
// Recreate shop button and handlers if needed
if (typeof shopButton === "undefined" || !shopButton || shopButton.destroyed) {
// --- SHOP BUTTON UI LOGIC (recreate) ---
shopButton = new Container();
shopButtonBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: shopButtonWidth,
height: shopButtonHeight
});
shopButtonBg.alpha = 0.92;
shopButton.addChild(shopButtonBg);
shopButtonText = new Text2("SHOP", {
size: 110,
fill: "#fff"
});
shopButtonText.anchor.set(0.5, 0.5);
shopButtonText.x = 0;
shopButtonText.y = 0;
shopButton.addChild(shopButtonText);
shopButton.x = 0;
shopButton.y = upgradeButton.y + upgradeButtonHeight / 2 + 40 + shopButtonHeight / 2;
startUiContainer.addChild(shopButton);
shopButton.down = function (x, y, obj) {
if (shopWindow && !shopWindow.destroyed) return;
showShopWindow();
};
}
}
// Block input until start is pressed
blockGameInput();
return;
}
}
// Destroy bullet
ibullet.destroy();
infernoBullets.splice(ib, 1);
continue;
}
ibullet.lastWasIntersecting = ibulletIntersecting;
// Collision with fireballs
for (var k = fireballs.length - 1; k >= 0; k--) {
var fb = fireballs[k];
if (ibullet['fb' + k + '_lastIntersecting'] === undefined) ibullet['fb' + k + '_lastIntersecting'] = false;
var fbIntersect = ibullet.intersects(fb);
var fireballOnScreen = fb.x >= 0 && fb.x <= 2048 && fb.y >= 0 && fb.y <= 2732;
if (!ibullet['fb' + k + '_lastIntersecting'] && fbIntersect && fireballOnScreen) {
// InfernoBullet yok olur, fireball da yok olur
fb.destroy();
fireballs.splice(k, 1);
// Flash inferno bullet on hit
if (ibullet.children && ibullet.children.length > 0) {
LK.effects.flashObject(ibullet.children[0], 0xff0000, 200);
}
// Destroy inferno bullet
ibullet.destroy();
infernoBullets.splice(ib, 1);
break;
}
ibullet['fb' + k + '_lastIntersecting'] = fbIntersect;
}
}
}
};
get an enemy in the form of slime. In-Game asset. 2d. High contrast. No shadows
get an enemy in the form of slime. In-Game asset. 2d. High contrast. No shadows
Let there be a mini machine gun and let this gun be pixel shaped. In-Game asset. 2d. High contrast. No shadows
a bullet but yellow and pixel. In-Game asset. 2d. High contrast. No shadows
slime explosion. In-Game asset. 2d. High contrast. No shadows
Change eyes like red
+ gain coin effect. In-Game asset. 2d. High contrast. No shadows
Fast bullet upgrade. In-Game asset. 2d. High contrast. No shadows
Upgrade power bullet. In-Game asset. 2d. High contrast. No shadows
Health + icon pixels. In-Game asset. 2d. High contrast. No shadows
Handgun pixel its look left. In-Game asset. 2d. High contrast. No shadows
işaretli alanı siyaha boya
pixel shuriken but 8 edges. In-Game asset. 2d. High contrast. No shadows
shotgun pixel and look left side. In-Game asset. 2d. High contrast. No shadows
submachine gun look left. In-Game asset. 2d. High contrast. No shadows
mp5 gun pixel. In-Game asset. 2d. High contrast. No shadows
Minigun bullet pixel. In-Game asset. 2d. High contrast. No shadows
Eliptic neon laser bullet. In-Game asset. 2d. High contrast. No shadows. Pixel
slime but have metalic helmet. In-Game asset. 2d. High contrast. No shadows
a slime boss enemy very strict. In-Game asset. 2d. High contrast. No shadows
create mirror view a bit smaller
add a dragon baby on top of gun
a goblin slime which have backpack fully coins. In-Game asset. 2d. High contrast. No shadows
Disappear smoke pixel. In-Game asset. 2d. High contrast. No shadows
Coin pile pixel. In-Game asset. 2d. High contrast. No shadows
fire left to right pixel. In-Game asset. 2d. High contrast. No shadows
Slime enemy healer. In-Game asset. 2d. High contrast. No shadows
Healt restore pixel. In-Game asset. 2d. High contrast. No shadows
Ammo +1 upgrade. In-Game asset. 2d. High contrast. No shadows
Type SLOW bottom of the amblem
Fire ball pixel
boss slime but like fire and dangereous. In-Game asset. 2d. High contrast. No shadows
Add body of this slime