User prompt
Agac 2 ve desdroyhomeyi sil
User prompt
Delorasyonlari haritanin her yerine ekle karakterin yuruduyu yerde ortaya cikmasin
User prompt
Bu modeledim agaci haritanin rasgele yerlerine ekle
User prompt
Agac 2 yi modelle
User prompt
Haritadaki dekorasyonlari tum harita boyunca olsun yakin olmasin evler random yerlerde ağaclar random yerlerde olsun cimpar tum harita boyunca olsun
User prompt
Dekorasyonlar yakin yakin olmasin uzak uzak rasgele olsun tum haritayi kapsasin
User prompt
Haritayi sonsuz yap dusmanlarda karakterin yakinda dosun
User prompt
Haritaya ağaclar cimler yikilmis evler kamp atesi rasgele yerlere yerlesdir
User prompt
Bos ve dusmanlar hitboxun disindan vurmasinlar
User prompt
Karakterin hitboxunu yeniden yap
User prompt
Bosun saldirilarindan biride havadan yere kirmizi iz birakib mermi atsin
User prompt
Boosun en cok kullandi saldirisi mermi atmak olsun
User prompt
Boos saldirilarindan biride yerde kirmizi iz birakim oraya ziplasin
User prompt
Kasadan 10sansla level artilan esya ciksin
User prompt
Sandiklarin icinden 50sansla xb ciksin
User prompt
Kutulari modelle
User prompt
Haritada kutular olusun bu kutulari kirinci icinde can ciksin
User prompt
Fasterenemey olunce patlasin
User prompt
Simsek buyucusunun mermilerini modele
User prompt
UI secdiklerimizi buyut ve alt alta koy
User prompt
Her level atlayinca 3ozelik ciksin ve biz 1ini secelim
User prompt
Bosun mermilerini modelle
User prompt
Elektirik buyucusunun atti mermileri modelle ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Ozelik secerken oyun dursun ve secdiyimiz ozelikler alt alta dursunlar
User prompt
Her level atladimizda rasgele 3ozeliyimizden birini secib artira bilelim
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BasicEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('basicEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 25;
self.maxHealth = 25;
self.damage = 15;
self.speed = 3;
self.scoreValue = 10;
// Enemy hitbox properties
self.width = 240; // Enemy visual width
self.height = 240; // Enemy visual height
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
// Drop XP orb
var xpOrb = new XPOrb();
xpOrb.x = self.x;
xpOrb.y = self.y;
xpOrbs.push(xpOrb);
game.addChild(xpOrb);
self.destroy();
LK.setScore(LK.getScore() + self.scoreValue);
scoreTxt.setText(LK.getScore());
LK.getSound('enemyDestroy').play();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
}
};
self.update = function () {
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
return self;
});
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('wikingboss', {
anchorX: 0.5,
anchorY: 0.5
});
bossGraphics.tint = 0x8B4513; // Brown viking color
bossGraphics.scaleX = 4.0; // Much larger viking boss model
bossGraphics.scaleY = 4.0; // Much larger viking boss model
// Add viking boss aura effect
var auraGraphics = self.attachAsset('wikingboss', {
anchorX: 0.5,
anchorY: 0.5
});
auraGraphics.tint = 0xDAA520; // Golden viking aura
auraGraphics.alpha = 0.4;
auraGraphics.scaleX = 4.5; // Larger aura to match viking boss size
auraGraphics.scaleY = 4.5; // Larger aura to match viking boss size
// Add pulsing viking aura animation
tween(auraGraphics, {
scaleX: 5.0,
scaleY: 5.0,
alpha: 0.2
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(auraGraphics, {
scaleX: 4.5,
scaleY: 4.5,
alpha: 0.4
}, {
duration: 1500,
easing: tween.easeInOut
});
}
});
self.health = 400 + (currentWave - 3) * 85; // Much higher health for viking boss
self.maxHealth = self.health;
self.damage = 70; // Higher damage for intimidating viking boss
self.speed = 1.1; // Slightly slower due to massive viking size
self.scoreValue = 250; // Higher reward for defeating viking boss
// Boss hitbox properties for accurate collision
self.width = 760; // Boss visual width (190 * 4 scale)
self.height = 760; // Boss visual height (190 * 4 scale)
self.lastLightningTime = 0;
self.lightningCooldown = 60; // 1 second at 60fps - much shorter for primary attack
self.lastChargeTime = 0;
self.chargeCooldown = 360; // 6 seconds at 60fps - less frequent
self.isCharging = false;
self.chargeSpeed = 8;
self.chargeTargetX = 0;
self.chargeTargetY = 0;
self.lastJumpTime = 0;
self.jumpCooldown = 300; // 5 seconds at 60fps - less frequent
self.isJumping = false;
self.jumpMarker = null;
self.lastAerialTime = 0;
self.aerialCooldown = 240; // 4 seconds at 60fps
self.isAerial = false;
self.aerialMarkers = [];
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
// Drop multiple XP orbs
for (var orbCount = 0; orbCount < 5; orbCount++) {
var xpOrb = new XPOrb();
xpOrb.x = self.x + (Math.random() - 0.5) * 100;
xpOrb.y = self.y + (Math.random() - 0.5) * 100;
xpOrbs.push(xpOrb);
game.addChild(xpOrb);
}
self.destroy();
LK.setScore(LK.getScore() + self.scoreValue);
scoreTxt.setText(LK.getScore());
LK.getSound('enemyDestroy').play();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
}
};
self.shootBigBullets = function () {
// Create multiple big bullets - increased from 5 to 7 for more coverage
for (var bulletCount = 0; bulletCount < 7; bulletCount++) {
var bigBullet = new BossBullet();
bigBullet.x = self.x;
bigBullet.y = self.y;
// Spread bullets in a wider arc for better coverage
var angleOffset = (bulletCount - 3) * Math.PI / 10;
var targetX = hero.x + Math.cos(angleOffset) * 200;
var targetY = hero.y + Math.sin(angleOffset) * 200;
bigBullet.setTarget(targetX, targetY);
bossBullets.push(bigBullet);
game.addChild(bigBullet);
}
// More dramatic visual effect for primary attack
LK.effects.flashObject(self, 0xFF4500, 600);
LK.effects.flashScreen(0xFF4500, 100);
};
self.startCharge = function () {
self.isCharging = true;
self.chargeTargetX = hero.x;
self.chargeTargetY = hero.y;
LK.effects.flashObject(self, 0xFF4500, 500);
// Viking boss grows larger during charge
tween(bossGraphics, {
scaleX: 4.5,
scaleY: 4.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Charge for 1 second then return to normal
LK.setTimeout(function () {
self.isCharging = false;
tween(bossGraphics, {
scaleX: 4.0,
scaleY: 4.0
}, {
duration: 200,
easing: tween.easeIn
});
}, 1000);
}
});
};
self.startJumpAttack = function () {
self.isJumping = true;
// Create red marker at hero's position
self.jumpMarker = LK.getAsset('jumpMarker', {
anchorX: 0.5,
anchorY: 0.5
});
self.jumpMarker.x = hero.x;
self.jumpMarker.y = hero.y;
self.jumpMarker.alpha = 0.6;
game.addChild(self.jumpMarker);
// Pulse the marker
tween(self.jumpMarker, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self.jumpMarker, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 0.6
}, {
duration: 500,
easing: tween.easeInOut
});
}
});
// Boss prepares to jump - crouch animation
tween(bossGraphics, {
scaleY: 3.5
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
// Jump to marker position
var jumpX = self.jumpMarker.x;
var jumpY = self.jumpMarker.y;
// Jump animation
tween(self, {
x: jumpX,
y: jumpY
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
// Landing impact
LK.effects.flashScreen(0xFF0000, 200);
tween(bossGraphics, {
scaleY: 4.0
}, {
duration: 200,
easing: tween.easeOut
});
// Deal area damage at landing position
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy !== self) {
var impactDx = enemy.x - self.x;
var impactDy = enemy.y - self.y;
var impactDistance = Math.sqrt(impactDx * impactDx + impactDy * impactDy);
if (impactDistance < 250) {
enemy.takeDamage(40);
}
}
}
// Check if hero is in impact area
var heroDx = hero.x - self.x;
var heroDy = hero.y - self.y;
var heroDistance = Math.sqrt(heroDx * heroDx + heroDy * heroDy);
if (heroDistance < 250) {
hero.takeDamage(60);
}
// Remove marker
if (self.jumpMarker) {
self.jumpMarker.destroy();
self.jumpMarker = null;
}
self.isJumping = false;
}
});
}
});
};
self.startAerialAttack = function () {
self.isAerial = true;
// Create multiple red markers in a pattern around hero
var markerCount = 5;
for (var i = 0; i < markerCount; i++) {
var angle = Math.PI * 2 / markerCount * i;
var markerDistance = 150;
var marker = LK.getAsset('jumpMarker', {
anchorX: 0.5,
anchorY: 0.5
});
marker.x = hero.x + Math.cos(angle) * markerDistance;
marker.y = hero.y + Math.sin(angle) * markerDistance;
marker.alpha = 0.8;
marker.tint = 0xFF0000;
game.addChild(marker);
self.aerialMarkers.push(marker);
// Pulse animation for markers
tween(marker, {
scaleX: 1.3,
scaleY: 1.3,
alpha: 0.4
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(marker, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 0.8
}, {
duration: 400,
easing: tween.easeInOut
});
}
});
}
// Boss rises up animation
tween(bossGraphics, {
scaleX: 3.5,
scaleY: 3.5,
alpha: 0.5
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// After delay, rain bullets from sky
LK.setTimeout(function () {
// Create bullets at each marker position
for (var j = 0; j < self.aerialMarkers.length; j++) {
var targetMarker = self.aerialMarkers[j];
// Create bullet high above
var skyBullet = new BossBullet();
skyBullet.x = targetMarker.x;
skyBullet.y = targetMarker.y - 800; // Start high above
skyBullet.setTarget(targetMarker.x, targetMarker.y);
bossBullets.push(skyBullet);
game.addChild(skyBullet);
}
// Boss returns to normal
tween(bossGraphics, {
scaleX: 4.0,
scaleY: 4.0,
alpha: 1.0
}, {
duration: 300,
easing: tween.easeIn
});
// Remove markers after bullets are fired
for (var k = 0; k < self.aerialMarkers.length; k++) {
self.aerialMarkers[k].destroy();
}
self.aerialMarkers = [];
self.isAerial = false;
}, 800);
}
});
};
self.update = function () {
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var currentTime = LK.ticks;
// Big bullet attack - primary attack with extended range
if (distance < 900 && currentTime - self.lastLightningTime > self.lightningCooldown) {
self.shootBigBullets();
self.lastLightningTime = currentTime;
}
// Charge attack - secondary attack, less frequent
if (distance > 250 && distance < 600 && currentTime - self.lastChargeTime > self.chargeCooldown && !self.isCharging && !self.isJumping) {
self.startCharge();
self.lastChargeTime = currentTime;
}
// Jump attack - tertiary attack, least frequent
if (distance < 700 && currentTime - self.lastJumpTime > self.jumpCooldown && !self.isJumping && !self.isCharging && !self.isAerial) {
self.startJumpAttack();
self.lastJumpTime = currentTime;
}
// Aerial attack - quaternary attack
if (distance < 600 && currentTime - self.lastAerialTime > self.aerialCooldown && !self.isJumping && !self.isCharging && !self.isAerial) {
self.startAerialAttack();
self.lastAerialTime = currentTime;
}
// Movement behavior
if (self.isCharging) {
// Charge towards target position
var chargeDx = self.chargeTargetX - self.x;
var chargeDy = self.chargeTargetY - self.y;
var chargeDistance = Math.sqrt(chargeDx * chargeDx + chargeDy * chargeDy);
if (chargeDistance > 0) {
self.x += chargeDx / chargeDistance * self.chargeSpeed;
self.y += chargeDy / chargeDistance * self.chargeSpeed;
}
} else if (self.isJumping || self.isAerial) {
// Don't move while jumping or aerial attack
} else {
// Normal movement - maintain distance from hero
if (distance > 300) {
// Move closer
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
} else if (distance < 150) {
// Move away
if (distance > 0) {
self.x -= dx / distance * self.speed;
self.y -= dy / distance * self.speed;
}
}
}
};
return self;
});
var BossBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bossBullet', {
anchorX: 0.5,
anchorY: 0.5
});
bulletGraphics.scaleX = 1.5; // Scale for appropriate size
bulletGraphics.scaleY = 1.5; // Scale for appropriate size
// Add glowing effect to boss bullets
var glowGraphics = self.attachAsset('bossBullet', {
anchorX: 0.5,
anchorY: 0.5
});
glowGraphics.tint = 0xFF6600; // Orange glow
glowGraphics.alpha = 0.4;
glowGraphics.scaleX = 2;
glowGraphics.scaleY = 2;
// Pulsing glow animation
tween(glowGraphics, {
scaleX: 2.5,
scaleY: 2.5,
alpha: 0.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(glowGraphics, {
scaleX: 2,
scaleY: 2,
alpha: 0.4
}, {
duration: 500,
easing: tween.easeInOut
});
}
});
self.speed = 6;
self.damage = 50;
// Boss bullet hitbox properties
self.width = 150; // Boss bullet visual width (100 * 1.5 scale)
self.height = 150; // Boss bullet visual height (100 * 1.5 scale)
self.targetX = 0;
self.targetY = 0;
self.directionX = 0;
self.directionY = 0;
self.setTarget = function (x, y) {
self.targetX = x;
self.targetY = y;
var dx = x - self.x;
var dy = y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
// Rotate bullet to face direction
bulletGraphics.rotation = Math.atan2(dy, dx) + Math.PI / 2;
};
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
// Rotate the boss bullet for visual effect
self.rotation += 0.1;
// Remove bullet if it goes off screen
if (self.x < -50 || self.x > 8242 || self.y < -50 || self.y > 10978) {
self.destroy();
for (var i = bossBullets.length - 1; i >= 0; i--) {
if (bossBullets[i] === self) {
bossBullets.splice(i, 1);
break;
}
}
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = Bullet.prototype.baseSpeed || 8;
self.damage = 25;
self.targetX = 0;
self.targetY = 0;
self.directionX = 0;
self.directionY = 0;
self.setTarget = function (x, y) {
self.targetX = x;
self.targetY = y;
var dx = x - self.x;
var dy = y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
// Rotate bullet to face direction
bulletGraphics.rotation = Math.atan2(dy, dx) + Math.PI / 2;
};
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
// Remove bullet if it goes off screen
if (self.x < -50 || self.x > 8242 || self.y < -50 || self.y > 10978) {
self.destroy();
for (var i = bullets.length - 1; i >= 0; i--) {
if (bullets[i] === self) {
bullets.splice(i, 1);
break;
}
}
}
};
return self;
});
var FastEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('fastEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 15;
self.maxHealth = 15;
self.damage = 10;
self.speed = 4.5;
self.scoreValue = 15;
// Enemy hitbox properties
self.width = 150; // Enemy visual width
self.height = 150; // Enemy visual height
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
// Create explosion effect
LK.effects.flashScreen(0xFFA500, 300); // Orange flash
// Deal damage to nearby enemies
for (var j = 0; j < enemies.length; j++) {
var otherEnemy = enemies[j];
if (otherEnemy !== self) {
var explodeDx = otherEnemy.x - self.x;
var explodeDy = otherEnemy.y - self.y;
var explodeDistance = Math.sqrt(explodeDx * explodeDx + explodeDy * explodeDy);
// Damage enemies within 150 pixel radius
if (explodeDistance < 150) {
otherEnemy.takeDamage(20);
// Push enemies away from explosion
if (explodeDistance > 0) {
otherEnemy.x += explodeDx / explodeDistance * 50;
otherEnemy.y += explodeDy / explodeDistance * 50;
}
}
}
}
// Check if hero is in explosion radius
var heroDx = hero.x - self.x;
var heroDy = hero.y - self.y;
var heroDistance = Math.sqrt(heroDx * heroDx + heroDy * heroDy);
if (heroDistance < 150) {
hero.takeDamage(15);
}
// Drop XP orb
var xpOrb = new XPOrb();
xpOrb.x = self.x;
xpOrb.y = self.y;
xpOrbs.push(xpOrb);
game.addChild(xpOrb);
self.destroy();
LK.setScore(LK.getScore() + self.scoreValue);
scoreTxt.setText(LK.getScore());
LK.getSound('enemyDestroy').play();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
}
};
self.update = function () {
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
return self;
});
var HealthCrate = Container.expand(function () {
var self = Container.call(this);
var crateGraphics = self.attachAsset('crate', {
anchorX: 0.5,
anchorY: 0.5
});
crateGraphics.scaleX = 0.8;
crateGraphics.scaleY = 0.8;
self.health = 1;
self.takeDamage = function (damage) {
self.health -= damage;
// Shake effect when hit
LK.effects.flashObject(self, 0xFFFFFF, 100);
tween(self, {
rotation: self.rotation + 0.1
}, {
duration: 50,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
rotation: self.rotation - 0.2
}, {
duration: 50,
easing: tween.easeIn,
onFinish: function onFinish() {
tween(self, {
rotation: self.rotation + 0.1
}, {
duration: 50,
easing: tween.easeOut
});
}
});
}
});
if (self.health <= 0) {
var dropChance = Math.random();
if (dropChance < 0.1) {
// 10% chance to drop level up item
var levelPickup = new LevelPickup();
levelPickup.x = self.x;
levelPickup.y = self.y;
levelPickups.push(levelPickup);
game.addChild(levelPickup);
} else if (dropChance < 0.55) {
// 45% chance to drop XP orb
var xpOrb = new XPOrb();
xpOrb.x = self.x;
xpOrb.y = self.y;
xpOrbs.push(xpOrb);
game.addChild(xpOrb);
} else {
// 45% chance to drop health pickup
var healthPickup = new HealthPickup();
healthPickup.x = self.x;
healthPickup.y = self.y;
healthPickups.push(healthPickup);
game.addChild(healthPickup);
}
self.destroy();
LK.getSound('enemyDestroy').play();
for (var i = healthCrates.length - 1; i >= 0; i--) {
if (healthCrates[i] === self) {
healthCrates.splice(i, 1);
break;
}
}
}
};
return self;
});
var HealthPickup = Container.expand(function () {
var self = Container.call(this);
var pickupGraphics = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
pickupGraphics.scaleX = 0.3;
pickupGraphics.scaleY = 0.3;
pickupGraphics.tint = 0xFF0000; // Red color for health
// Add pulsing animation
tween(pickupGraphics, {
scaleX: 0.35,
scaleY: 0.35
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(pickupGraphics, {
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 800,
easing: tween.easeInOut
});
}
});
self.healAmount = 15;
self.update = function () {
// Check if collected by hero
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 40) {
// Heal player
if (hero.health < hero.maxHealth) {
hero.health = Math.min(hero.health + self.healAmount, hero.maxHealth);
updateHealthBar();
LK.getSound('xpCollect').play();
LK.effects.flashObject(hero, 0x00FF00, 300); // Green flash
}
// Remove from healthPickups array
for (var i = healthPickups.length - 1; i >= 0; i--) {
if (healthPickups[i] === self) {
healthPickups.splice(i, 1);
break;
}
}
self.destroy();
}
};
return self;
});
var Hero = Container.expand(function () {
var self = Container.call(this);
var heroGraphics = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
var weaponGraphics = self.attachAsset('weapon', {
anchorX: 0.5,
anchorY: 0.9
});
weaponGraphics.x = 25;
weaponGraphics.y = -10;
self.maxHealth = 100;
self.health = self.maxHealth;
self.attackDamage = 25;
self.isInvulnerable = false;
// Custom hitbox properties
self.hitboxWidth = 80; // Smaller than visual size for better gameplay
self.hitboxHeight = 100; // Adjusted for character proportions
self.hitboxOffsetX = 0; // Center aligned
self.hitboxOffsetY = 0; // Center aligned
self.takeDamage = function (damage) {
if (self.isInvulnerable) return;
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
LK.showGameOver();
return;
}
// Flash red when taking damage
LK.effects.flashObject(self, 0xFF0000, 300);
LK.getSound('playerHit').play();
// Brief invulnerability
self.isInvulnerable = true;
LK.setTimeout(function () {
self.isInvulnerable = false;
}, 500);
};
self.attack = function (enemy) {
enemy.takeDamage(self.attackDamage);
LK.getSound('enemyHit').play();
// Weapon swing animation
tween(weaponGraphics, {
rotation: weaponGraphics.rotation + Math.PI / 3
}, {
duration: 100
});
};
self.updateWeapon = function (targetX, targetY) {
// Calculate angle to target
var dx = targetX - self.x;
var dy = targetY - self.y;
var angle = Math.atan2(dy, dx);
weaponGraphics.rotation = angle + Math.PI / 2;
};
self.shoot = function (targetX, targetY) {
var bullet = new Bullet();
bullet.x = self.x + Math.cos(weaponGraphics.rotation - Math.PI / 2) * 40;
bullet.y = self.y + Math.sin(weaponGraphics.rotation - Math.PI / 2) * 40;
bullet.setTarget(targetX, targetY);
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('weaponShoot').play();
// Weapon recoil animation
tween(weaponGraphics, {
y: weaponGraphics.y + 5
}, {
duration: 50,
onComplete: function onComplete() {
tween(weaponGraphics, {
y: weaponGraphics.y - 5
}, {
duration: 50
});
}
});
};
self.isWalking = false;
self.moveSpeed = 4;
self.analogMove = function (deltaX, deltaY) {
// Analog movement without bounds for infinite world
self.x += deltaX * self.moveSpeed;
self.y += deltaY * self.moveSpeed;
};
self.walkTo = function (targetX, targetY) {
// Don't start new walk if already walking
if (self.isWalking) {
tween.stop(self, {
x: true,
y: true
});
}
self.isWalking = true;
// No bounds checking for infinite world
// Calculate distance for duration
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var duration = distance * 2; // Adjust speed by changing multiplier
// Walking animation with slight bounce
tween(heroGraphics, {
scaleY: 0.9
}, {
duration: duration / 4,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(heroGraphics, {
scaleY: 1.1
}, {
duration: duration / 4,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(heroGraphics, {
scaleY: 1.0
}, {
duration: duration / 2,
easing: tween.easeOut
});
}
});
}
});
// Move to target position
tween(self, {
x: targetX,
y: targetY
}, {
duration: duration,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isWalking = false;
}
});
};
// Custom collision check method using hitbox
self.checkCollision = function (other) {
// Calculate hitbox bounds for hero
var heroLeft = self.x - self.hitboxWidth / 2 + self.hitboxOffsetX;
var heroRight = self.x + self.hitboxWidth / 2 + self.hitboxOffsetX;
var heroTop = self.y - self.hitboxHeight / 2 + self.hitboxOffsetY;
var heroBottom = self.y + self.hitboxHeight / 2 + self.hitboxOffsetY;
// Get other object bounds (assuming standard bounds)
var otherLeft = other.x - other.width / 2;
var otherRight = other.x + other.width / 2;
var otherTop = other.y - other.height / 2;
var otherBottom = other.y + other.height / 2;
// Check for overlap
return heroLeft < otherRight && heroRight > otherLeft && heroTop < otherBottom && heroBottom > otherTop;
};
// Debug method to visualize hitbox (can be called for testing)
self.showHitbox = function () {
if (!self.hitboxVisual) {
self.hitboxVisual = LK.getAsset('attackRange', {
anchorX: 0.5,
anchorY: 0.5
});
self.hitboxVisual.width = self.hitboxWidth;
self.hitboxVisual.height = self.hitboxHeight;
self.hitboxVisual.x = self.hitboxOffsetX;
self.hitboxVisual.y = self.hitboxOffsetY;
self.hitboxVisual.alpha = 0.3;
self.hitboxVisual.tint = 0x00FF00;
self.addChild(self.hitboxVisual);
}
};
return self;
});
var LevelPickup = Container.expand(function () {
var self = Container.call(this);
var pickupGraphics = self.attachAsset('levelBar', {
anchorX: 0.5,
anchorY: 0.5
});
pickupGraphics.scaleX = 0.4;
pickupGraphics.scaleY = 0.4;
pickupGraphics.tint = 0xFFD700; // Gold color for level up
// Add glowing effect
var glowGraphics = self.attachAsset('levelBar', {
anchorX: 0.5,
anchorY: 0.5
});
glowGraphics.tint = 0xFFFF00; // Yellow glow
glowGraphics.alpha = 0.3;
glowGraphics.scaleX = 0.5;
glowGraphics.scaleY = 0.5;
// Add pulsing animation
tween(pickupGraphics, {
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(pickupGraphics, {
scaleX: 0.4,
scaleY: 0.4
}, {
duration: 600,
easing: tween.easeInOut
});
}
});
// Rotating glow effect
tween(glowGraphics, {
rotation: Math.PI * 2
}, {
duration: 2000,
easing: tween.linear,
onFinish: function onFinish() {
tween(glowGraphics, {
rotation: 0
}, {
duration: 2000,
easing: tween.linear
});
}
});
self.update = function () {
// Check if collected by hero
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 40) {
// Level up player
playerLevel++;
currentXP = 0;
xpToNextLevel = playerLevel * 30;
levelText.setText('Level ' + playerLevel);
// Level up effects
LK.effects.flashScreen(0xFFD700, 500);
hero.maxHealth += 3;
hero.health = hero.maxHealth; // Full heal on level up
updateHealthBar();
updateLevelBar();
LK.getSound('xpCollect').play();
LK.effects.flashObject(hero, 0xFFD700, 500); // Gold flash
// Show upgrade selection
showUpgradeSelection();
// Remove from levelPickups array
for (var i = levelPickups.length - 1; i >= 0; i--) {
if (levelPickups[i] === self) {
levelPickups.splice(i, 1);
break;
}
}
self.destroy();
}
};
return self;
});
var Lightning = Container.expand(function () {
var self = Container.call(this);
var lightningGraphics = self.attachAsset('lightningBolt', {
anchorX: 0.5,
anchorY: 0.5
});
// Add glowing effect to lightning bolt
var glowGraphics = self.attachAsset('lightningBolt', {
anchorX: 0.5,
anchorY: 0.5
});
glowGraphics.tint = 0xFFFF00; // Yellow glow
glowGraphics.alpha = 0.5;
glowGraphics.scaleX = 1.3;
glowGraphics.scaleY = 1.3;
self.speed = 7;
self.damage = 30;
// Lightning bolt hitbox properties
self.width = 80; // Lightning visual width
self.height = 200; // Lightning visual height
self.targetX = 0;
self.targetY = 0;
self.directionX = 0;
self.directionY = 0;
self.lifetime = 0;
self.maxLifetime = 120; // 2 seconds at 60fps
// Lightning crackling effect
tween(lightningGraphics, {
alpha: 0.3
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(lightningGraphics, {
alpha: 1.0
}, {
duration: 100,
easing: tween.easeInOut
});
}
});
self.setTarget = function (x, y) {
self.targetX = x;
self.targetY = y;
var dx = x - self.x;
var dy = y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
// Rotate lightning to face direction
lightningGraphics.rotation = Math.atan2(dy, dx) + Math.PI / 2;
glowGraphics.rotation = Math.atan2(dy, dx) + Math.PI / 2;
};
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
self.lifetime++;
// Random flickering effect
if (Math.random() < 0.3) {
lightningGraphics.alpha = Math.random() * 0.5 + 0.5;
}
// Remove lightning after lifetime expires or goes off screen
if (self.lifetime > self.maxLifetime || self.x < -50 || self.x > 8242 || self.y < -50 || self.y > 10978) {
self.destroy();
for (var i = lightningBolts.length - 1; i >= 0; i--) {
if (lightningBolts[i] === self) {
lightningBolts.splice(i, 1);
break;
}
}
}
};
return self;
});
var MapDecoration = Container.expand(function () {
var self = Container.call(this);
self.decorationType = '';
self.setDecoration = function (type) {
self.decorationType = type;
var decorationGraphics;
switch (type) {
case 'tree':
decorationGraphics = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 1.0
});
decorationGraphics.tint = 0x228B22;
// Add slight random rotation for variety
decorationGraphics.rotation = (Math.random() - 0.5) * 0.3;
break;
case 'agac2':
decorationGraphics = self.attachAsset('Agac2', {
anchorX: 0.5,
anchorY: 1.0
});
decorationGraphics.tint = 0x2F4F2F; // Dark forest green for variety
// Add slight random rotation for variety
decorationGraphics.rotation = (Math.random() - 0.5) * 0.4;
// Random scale for more variety
decorationGraphics.scaleX = 0.8 + Math.random() * 0.6;
decorationGraphics.scaleY = 0.8 + Math.random() * 0.6;
break;
case 'grass':
decorationGraphics = self.attachAsset('grass', {
anchorX: 0.5,
anchorY: 0.5
});
decorationGraphics.tint = 0x32CD32;
decorationGraphics.scaleX = 0.8 + Math.random() * 0.4;
decorationGraphics.scaleY = 0.8 + Math.random() * 0.4;
break;
case 'destroyedHouse':
decorationGraphics = self.attachAsset('destroyedHouse', {
anchorX: 0.5,
anchorY: 1.0
});
decorationGraphics.tint = 0x696969;
decorationGraphics.rotation = (Math.random() - 0.5) * 0.2;
break;
case 'campfire':
decorationGraphics = self.attachAsset('campfire', {
anchorX: 0.5,
anchorY: 0.5
});
decorationGraphics.tint = 0xFF4500;
// Add flickering animation to campfire
tween(decorationGraphics, {
scaleX: 1.1,
scaleY: 1.1,
alpha: 0.8
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(decorationGraphics, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 1.0
}, {
duration: 800,
easing: tween.easeInOut
});
}
});
break;
}
// Make decorations non-interactive and lower layer
self.alpha = 0.7;
};
return self;
});
var StrongEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('strongEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 50;
self.maxHealth = 50;
self.damage = 25;
self.speed = 2.8;
self.scoreValue = 25;
// Enemy hitbox properties
self.width = 300; // Enemy visual width
self.height = 300; // Enemy visual height
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
// Drop XP orb
var xpOrb = new XPOrb();
xpOrb.x = self.x;
xpOrb.y = self.y;
xpOrbs.push(xpOrb);
game.addChild(xpOrb);
self.destroy();
LK.setScore(LK.getScore() + self.scoreValue);
scoreTxt.setText(LK.getScore());
LK.getSound('enemyDestroy').play();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
}
};
self.update = function () {
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
return self;
});
var UpgradeOption = Container.expand(function () {
var self = Container.call(this);
// Background for upgrade option - made larger
var bgGraphics = self.attachAsset('levelBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
bgGraphics.scaleX = 3.5;
bgGraphics.scaleY = 4;
bgGraphics.tint = 0x2c3e50;
// Title text - made larger
self.titleText = new Text2('', {
size: 80,
fill: 0xFFD700
});
self.titleText.anchor.set(0.5, 0.5);
self.titleText.y = -100;
self.addChild(self.titleText);
// Description text - made larger
self.descText = new Text2('', {
size: 60,
fill: 0xFFFFFF
});
self.descText.anchor.set(0.5, 0.5);
self.descText.y = 0;
self.addChild(self.descText);
// Upgrade type
self.upgradeType = '';
self.setUpgrade = function (type, title, description) {
self.upgradeType = type;
self.titleText.setText(title);
self.descText.setText(description);
};
// Hover effect
self.down = function (x, y, obj) {
tween(bgGraphics, {
scaleX: 3.8,
scaleY: 4.3
}, {
duration: 100,
easing: tween.easeOut
});
};
self.up = function (x, y, obj) {
tween(bgGraphics, {
scaleX: 3.5,
scaleY: 4
}, {
duration: 100,
easing: tween.easeIn
});
// Apply the upgrade
applyUpgrade(self.upgradeType);
};
return self;
});
var Wizard = Container.expand(function () {
var self = Container.call(this);
var wizardGraphics = self.attachAsset('Wizard', {
anchorX: 0.5,
anchorY: 0.5
});
// Add magical aura effect using the wizard asset
var auraGraphics = self.attachAsset('Wizard', {
anchorX: 0.5,
anchorY: 0.5
});
auraGraphics.tint = 0x9370DB; // Purple aura
auraGraphics.alpha = 0.3;
auraGraphics.scaleX = 1.3;
auraGraphics.scaleY = 1.3;
// Add pulsing magical aura animation
tween(auraGraphics, {
scaleX: 1.8,
scaleY: 1.8,
alpha: 0.1
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(auraGraphics, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.3
}, {
duration: 1000,
easing: tween.easeInOut
});
}
});
self.health = 60;
self.maxHealth = 60;
self.damage = 20;
self.speed = 2.5;
self.scoreValue = 30;
// Wizard hitbox properties
self.width = 310; // Wizard visual width
self.height = 310; // Wizard visual height
self.lastLightningTime = 0;
self.lightningCooldown = 180; // 3 seconds at 60fps
self.lightningRange = 400;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
// Drop XP orb
var xpOrb = new XPOrb();
xpOrb.x = self.x;
xpOrb.y = self.y;
xpOrbs.push(xpOrb);
game.addChild(xpOrb);
self.destroy();
LK.setScore(LK.getScore() + self.scoreValue);
scoreTxt.setText(LK.getScore());
LK.getSound('enemyDestroy').play();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
}
};
self.shootLightning = function () {
// Create lightning bolt
var lightning = new Lightning();
lightning.x = self.x;
lightning.y = self.y;
lightning.setTarget(hero.x, hero.y);
lightningBolts.push(lightning);
game.addChild(lightning);
// Enhanced lightning casting effects
LK.effects.flashObject(self, 0xFFFF00, 300);
LK.effects.flashObject(auraGraphics, 0xFFFFFF, 400);
// Dramatic casting animation - wizard grows larger then returns
tween(wizardGraphics, {
scaleX: 1.5,
scaleY: 1.5,
rotation: wizardGraphics.rotation + Math.PI / 6
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(wizardGraphics, {
scaleX: 1.2,
scaleY: 1.2,
rotation: wizardGraphics.rotation - Math.PI / 6
}, {
duration: 250,
easing: tween.easeIn
});
}
});
// Aura intensifies during casting
tween(auraGraphics, {
scaleX: 2.2,
scaleY: 2.2,
alpha: 0.6
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(auraGraphics, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.3
}, {
duration: 300,
easing: tween.easeIn
});
}
});
};
self.update = function () {
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Keep distance and shoot lightning
if (distance < self.lightningRange && distance > 150) {
// Stop moving and prepare to shoot
var currentTime = LK.ticks;
if (currentTime - self.lastLightningTime > self.lightningCooldown) {
self.shootLightning();
self.lastLightningTime = currentTime;
}
} else if (distance > self.lightningRange) {
// Move closer to hero
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
} else if (distance < 150) {
// Move away from hero
if (distance > 0) {
self.x -= dx / distance * self.speed;
self.y -= dy / distance * self.speed;
}
}
};
return self;
});
var XPOrb = Container.expand(function () {
var self = Container.call(this);
var orbGraphics = self.attachAsset('xpOrb', {
anchorX: 0.5,
anchorY: 0.5
});
self.xpValue = 5;
self.moveSpeed = 2;
self.attractRange = 150;
// Add pulsing animation to make XP orbs more attractive
tween(orbGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(orbGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Restart the pulsing animation
tween(orbGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.easeInOut
});
}
});
}
});
self.update = function () {
// XP orb magnetic attraction to other nearby orbs
for (var o = 0; o < xpOrbs.length; o++) {
var otherOrb = xpOrbs[o];
if (otherOrb !== self) {
var orbDx = otherOrb.x - self.x;
var orbDy = otherOrb.y - self.y;
var orbDistance = Math.sqrt(orbDx * orbDx + orbDy * orbDy);
// Attract to nearby orbs within 80 pixels
if (orbDistance < 80 && orbDistance > 0) {
var attractForce = 0.5;
self.x += orbDx / orbDistance * attractForce;
self.y += orbDy / orbDistance * attractForce;
}
}
}
// Move toward hero if within attract range
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.attractRange) {
if (distance > 0) {
self.x += dx / distance * self.moveSpeed;
self.y += dy / distance * self.moveSpeed;
}
}
// Check if collected by hero
if (distance < 30) {
// Give XP to player
currentXP += self.xpValue;
LK.setScore(LK.getScore() + self.xpValue);
scoreTxt.setText(LK.getScore());
LK.getSound('xpCollect').play();
// Remove from xpOrbs array
for (var i = xpOrbs.length - 1; i >= 0; i--) {
if (xpOrbs[i] === self) {
xpOrbs.splice(i, 1);
break;
}
}
self.destroy();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
var hero;
var enemies = [];
var bullets = [];
var xpOrbs = [];
var lightningBolts = [];
var bossBullets = [];
var healthCrates = [];
var healthPickups = [];
var levelPickups = [];
var spawnTimer = 0;
var difficultyLevel = 1;
var scoreTxt;
var healthBar;
var healthBarBg;
var attackIndicator;
var lastShootTime = 0;
var joystickBase;
var joystickKnob;
var isJoystickActive = false;
var joystickStartX = 0;
var joystickStartY = 0;
var joystickRadius = 80;
var movementX = 0;
var movementY = 0;
var healthText;
var levelBar;
var levelBarBg;
var levelText;
var playerLevel = 1;
var currentXP = 0;
var xpToNextLevel = 30;
var currentWave = 1;
var waveStartTime = 0;
var waveDuration = 60000; // 1 minute in milliseconds
var waveEnemyCount = 0;
var baseEnemiesPerWave = 20;
var waveText;
var waveTimer;
var bossSpawned = false;
var upgradeSelectionContainer;
var isSelectingUpgrade = false;
var availableUpgrades = [{
type: 'health',
title: 'Health Boost',
description: '+20 Max Health'
}, {
type: 'damage',
title: 'Damage Up',
description: '+10 Attack Damage'
}, {
type: 'speed',
title: 'Speed Boost',
description: '+1 Movement Speed'
}, {
type: 'attackSpeed',
title: 'Attack Speed',
description: 'Faster Shooting'
}, {
type: 'bulletSpeed',
title: 'Bullet Speed',
description: 'Faster Bullets'
}, {
type: 'regen',
title: 'Regeneration',
description: 'Heal 1 HP/sec'
}];
var lastRegenTime = 0;
var hasRegen = false;
var mapDecorations = [];
function generateMapDecorations() {
var decorationTypes = ['tree', 'agac2', 'grass', 'destroyedHouse', 'campfire'];
// Different distribution strategies for each decoration type
var decorationStrategies = {
tree: {
count: 25,
minDistance: 200,
maxDistance: 2000,
spacing: 180,
heroAvoidDistance: 150
},
// Trees distributed across entire map
agac2: {
count: 20,
minDistance: 150,
maxDistance: 1800,
spacing: 140,
heroAvoidDistance: 120
},
// Agac 2 trees scattered across entire map
grass: {
count: 100,
minDistance: 50,
maxDistance: 1500,
spacing: 40,
heroAvoidDistance: 80
},
// Grass truly everywhere across map
destroyedHouse: {
count: 8,
minDistance: 300,
maxDistance: 1600,
spacing: 350,
heroAvoidDistance: 200
},
// Houses distributed across map
campfire: {
count: 6,
minDistance: 250,
maxDistance: 1200,
spacing: 250,
heroAvoidDistance: 100
} // Campfires spread across map
};
// Generate decorations across the entire infinite map area
for (var type in decorationStrategies) {
var strategy = decorationStrategies[type];
var count = strategy.count;
for (var i = 0; i < count; i++) {
var decoration = new MapDecoration();
decoration.setDecoration(type);
var validPosition = false;
var attempts = 0;
var maxAttempts = 50;
// Try to find a valid position that doesn't conflict with hero or existing decorations
while (!validPosition && attempts < maxAttempts) {
// Generate completely random positions across very large area
var randomAngle = Math.random() * Math.PI * 2;
var randomDistance = strategy.minDistance + Math.random() * (strategy.maxDistance - strategy.minDistance);
// Generate positions in all directions from hero across entire map
decoration.x = hero.x + Math.cos(randomAngle) * randomDistance;
decoration.y = hero.y + Math.sin(randomAngle) * randomDistance;
// For grass, add extra scatter to make it truly everywhere
if (type === 'grass') {
decoration.x += (Math.random() - 0.5) * 800;
decoration.y += (Math.random() - 0.5) * 800;
}
// Check distance from hero - avoid spawning too close to hero's current position
var heroDistance = Math.sqrt((decoration.x - hero.x) * (decoration.x - hero.x) + (decoration.y - hero.y) * (decoration.y - hero.y));
if (heroDistance < strategy.heroAvoidDistance) {
attempts++;
continue; // Try again if too close to hero
}
// Check for minimum spacing between decorations of same type to avoid clustering
var tooClose = false;
for (var j = 0; j < mapDecorations.length; j++) {
var existingDecoration = mapDecorations[j];
if (existingDecoration.decorationType === type) {
var spacingDx = decoration.x - existingDecoration.x;
var spacingDy = decoration.y - existingDecoration.y;
var spacingDistance = Math.sqrt(spacingDx * spacingDx + spacingDy * spacingDy);
if (spacingDistance < strategy.spacing) {
tooClose = true;
break;
}
}
}
if (!tooClose) {
validPosition = true;
}
attempts++;
}
// Only add decoration if we found a valid position
if (validPosition) {
mapDecorations.push(decoration);
game.addChild(decoration);
}
}
}
}
// Create score display
scoreTxt = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create health bar background
healthBarBg = LK.getAsset('healthBarBg', {
anchorX: 0,
anchorY: 0
});
healthBarBg.x = 150;
healthBarBg.y = 50;
LK.gui.topLeft.addChild(healthBarBg);
// Create health bar
healthBar = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0
});
healthBar.x = 150;
healthBar.y = 50;
LK.gui.topLeft.addChild(healthBar);
// Create health text
healthText = new Text2('100/100', {
size: 40,
fill: 0xFFFFFF
});
healthText.anchor.set(0, 0.5);
healthText.x = 370;
healthText.y = 60;
LK.gui.topLeft.addChild(healthText);
// Create level bar background at bottom
levelBarBg = LK.getAsset('levelBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
levelBarBg.x = 2048 / 2;
levelBarBg.y = 2732 - 60;
game.addChild(levelBarBg);
// Create level bar fill
levelBar = LK.getAsset('levelBar', {
anchorX: 0,
anchorY: 0.5
});
levelBar.x = levelBarBg.x - levelBarBg.width / 2;
levelBar.y = levelBarBg.y;
game.addChild(levelBar);
// Create level text
levelText = new Text2('Level 1', {
size: 50,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = levelBarBg.x;
levelText.y = levelBarBg.y - 50;
game.addChild(levelText);
// Create wave display
waveText = new Text2('Wave 1', {
size: 60,
fill: 0xFFD700
});
waveText.anchor.set(1, 0);
waveText.x = 2048 - 20;
waveText.y = 20;
LK.gui.topRight.addChild(waveText);
// Create wave timer display
waveTimer = new Text2('2:00', {
size: 50,
fill: 0xFFFFFF
});
waveTimer.anchor.set(0.5, 0);
waveTimer.x = 0;
waveTimer.y = 80;
LK.gui.top.addChild(waveTimer);
// Create upgrade selection container
upgradeSelectionContainer = new Container();
upgradeSelectionContainer.visible = false;
LK.gui.center.addChild(upgradeSelectionContainer);
// Create semi-transparent background
var upgradeBg = LK.getAsset('wallHorizontal', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeBg.scaleX = 8;
upgradeBg.scaleY = 8;
upgradeBg.tint = 0x000000;
upgradeBg.alpha = 0.8;
upgradeSelectionContainer.addChild(upgradeBg);
// Create "Choose Upgrade" title
var upgradeTitle = new Text2('Choose Your Upgrade!', {
size: 80,
fill: 0xFFD700
});
upgradeTitle.anchor.set(0.5, 0.5);
upgradeTitle.y = -200;
upgradeSelectionContainer.addChild(upgradeTitle);
// Create boss health bar background
var bossHealthBarBg = LK.getAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0
});
bossHealthBarBg.x = 0;
bossHealthBarBg.y = 140;
bossHealthBarBg.visible = false;
bossHealthBarBg.scaleX = 2;
LK.gui.top.addChild(bossHealthBarBg);
// Create boss health bar
var bossHealthBar = LK.getAsset('healthBar', {
anchorX: 0.5,
anchorY: 0
});
bossHealthBar.x = 0;
bossHealthBar.y = 140;
bossHealthBar.visible = false;
bossHealthBar.scaleX = 2;
bossHealthBar.tint = 0xFF4444; // Red color for boss health
LK.gui.top.addChild(bossHealthBar);
// Create boss health text
var bossHealthText = new Text2('', {
size: 40,
fill: 0xFFFFFF
});
bossHealthText.anchor.set(0.5, 0);
bossHealthText.x = 0;
bossHealthText.y = 180;
bossHealthText.visible = false;
LK.gui.top.addChild(bossHealthText);
// Create hero
hero = game.addChild(new Hero());
hero.x = 4096;
hero.y = 5464;
// Camera following variables
var cameraX = 0;
var cameraY = 0;
var cameraFollowSpeed = 0.1;
var cameraSmoothing = true;
// Create attack range indicator (initially hidden)
attackIndicator = LK.getAsset('attackRange', {
anchorX: 0.5,
anchorY: 0.5
});
attackIndicator.alpha = 0.3;
attackIndicator.visible = false;
game.addChild(attackIndicator);
// Create analog joystick base
joystickBase = LK.getAsset('attackRange', {
anchorX: 0.5,
anchorY: 0.5
});
joystickBase.alpha = 0.2;
joystickBase.visible = false;
joystickBase.scaleX = 1.5;
joystickBase.scaleY = 1.5;
game.addChild(joystickBase);
// Create analog joystick knob
joystickKnob = LK.getAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
joystickKnob.alpha = 0.7;
joystickKnob.visible = false;
joystickKnob.scaleX = 0.6;
joystickKnob.scaleY = 0.6;
game.addChild(joystickKnob);
// No static walls needed for infinite world
// Generate map decorations
generateMapDecorations();
function spawnEnemy() {
var enemyType = Math.random();
var enemy;
if (enemyType < 0.5) {
enemy = new BasicEnemy();
} else if (enemyType < 0.75) {
enemy = new FastEnemy();
} else if (enemyType < 0.95) {
enemy = new StrongEnemy();
} else {
enemy = new Wizard();
}
// Spawn enemies near hero's current position for infinite world
var spawnDistance = 400 + Math.random() * 200; // 400-600 pixels from hero
var angle = Math.random() * Math.PI * 2; // Random angle around hero
enemy.x = hero.x + Math.cos(angle) * spawnDistance;
enemy.y = hero.y + Math.sin(angle) * spawnDistance;
enemies.push(enemy);
game.addChild(enemy);
}
function spawnHealthCrate() {
var crate = new HealthCrate();
// Spawn near hero in infinite world
var crateDistance = 200 + Math.random() * 300; // 200-500 pixels from hero
var crateAngle = Math.random() * Math.PI * 2; // Random angle around hero
crate.x = hero.x + Math.cos(crateAngle) * crateDistance;
crate.y = hero.y + Math.sin(crateAngle) * crateDistance;
healthCrates.push(crate);
game.addChild(crate);
}
function updateHealthBar() {
var healthPercent = hero.health / hero.maxHealth;
healthBar.scaleX = healthPercent;
if (healthPercent > 0.5) {
healthBar.tint = 0x4CAF50; // Green
} else if (healthPercent > 0.25) {
healthBar.tint = 0xFFC107; // Yellow
} else {
healthBar.tint = 0xF44336; // Red
}
// Update health text
healthText.setText(Math.ceil(hero.health) + '/' + hero.maxHealth);
}
function updateLevelBar() {
var xpPercent = currentXP / xpToNextLevel;
levelBar.scaleX = xpPercent;
// Check for level up
if (currentXP >= xpToNextLevel) {
playerLevel++;
currentXP = 0;
xpToNextLevel = playerLevel * 30; // Increase XP requirement
levelText.setText('Level ' + playerLevel);
// Level up effects
LK.effects.flashScreen(0xFFD700, 500);
hero.maxHealth += 3;
hero.health = hero.maxHealth; // Full heal on level up
// Show upgrade selection
showUpgradeSelection();
}
}
function showUpgradeSelection() {
isSelectingUpgrade = true;
upgradeSelectionContainer.visible = true;
// Remove any existing upgrade options
for (var i = upgradeSelectionContainer.children.length - 1; i >= 0; i--) {
var child = upgradeSelectionContainer.children[i];
if (child instanceof UpgradeOption) {
child.destroy();
}
}
// Randomly select 3 different upgrades
var selectedUpgrades = [];
var tempUpgrades = availableUpgrades.slice(); // Copy array
for (var j = 0; j < 3 && tempUpgrades.length > 0; j++) {
var randomIndex = Math.floor(Math.random() * tempUpgrades.length);
selectedUpgrades.push(tempUpgrades[randomIndex]);
tempUpgrades.splice(randomIndex, 1);
}
// Create upgrade option buttons - vertical layout
for (var k = 0; k < selectedUpgrades.length; k++) {
var option = new UpgradeOption();
var upgrade = selectedUpgrades[k];
option.setUpgrade(upgrade.type, upgrade.title, upgrade.description);
option.x = 0; // Center horizontally
option.y = (k - 1) * 300; // Position vertically: -300, 0, 300
upgradeSelectionContainer.addChild(option);
}
}
function applyUpgrade(upgradeType) {
// Mark upgrade as applied
for (var i = 0; i < availableUpgrades.length; i++) {
if (availableUpgrades[i].type === upgradeType) {
availableUpgrades[i].applied = true;
break;
}
}
switch (upgradeType) {
case 'health':
hero.maxHealth += 20;
hero.health += 20;
updateHealthBar();
break;
case 'damage':
hero.attackDamage += 10;
break;
case 'speed':
hero.moveSpeed += 1;
break;
case 'attackSpeed':
// Handled in auto-shoot logic
break;
case 'bulletSpeed':
// Increase all future bullet speeds
Bullet.prototype.baseSpeed = 12; // Default is 8
break;
case 'regen':
hasRegen = true;
break;
}
// Hide upgrade selection
isSelectingUpgrade = false;
upgradeSelectionContainer.visible = false;
}
function updateBossHealthBar() {
// Find if there's a boss enemy alive
var currentBoss = null;
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] instanceof Boss) {
currentBoss = enemies[i];
break;
}
}
// Show/hide boss health bar based on boss presence
if (currentBoss) {
bossHealthBarBg.visible = true;
bossHealthBar.visible = true;
bossHealthText.visible = true;
// Update boss health bar
var bossHealthPercent = currentBoss.health / currentBoss.maxHealth;
bossHealthBar.scaleX = bossHealthPercent * 2; // Scale to match background
// Update boss health text
bossHealthText.setText('Boss: ' + Math.ceil(currentBoss.health) + '/' + currentBoss.maxHealth);
} else {
bossHealthBarBg.visible = false;
bossHealthBar.visible = false;
bossHealthText.visible = false;
}
}
function startNewWave() {
currentWave++;
waveStartTime = Date.now();
waveEnemyCount = 0;
bossSpawned = false;
waveText.setText('Wave ' + currentWave);
// Wave start effects
LK.effects.flashScreen(0x00FF00, 800);
// Increase difficulty each wave
difficultyLevel = currentWave;
}
function updateWaveTimer() {
var currentTime = Date.now();
var timeElapsed = currentTime - waveStartTime;
var timeRemaining = Math.max(0, waveDuration - timeElapsed);
var minutes = Math.floor(timeRemaining / 60000);
var seconds = Math.floor(timeRemaining % 60000 / 1000);
var timeText = minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
waveTimer.setText(timeText);
// Check if wave time is up
if (timeRemaining <= 0) {
startNewWave();
}
}
function updateCamera() {
// Calculate target camera position (center hero on screen)
var targetCameraX = -(hero.x - 2048 / 2);
var targetCameraY = -(hero.y - 2732 / 2);
// No camera bounds for infinite world
if (cameraSmoothing) {
// Smooth camera following using interpolation
cameraX += (targetCameraX - cameraX) * cameraFollowSpeed;
cameraY += (targetCameraY - cameraY) * cameraFollowSpeed;
} else {
// Direct camera following
cameraX = targetCameraX;
cameraY = targetCameraY;
}
// Apply camera position to game container
game.x = cameraX;
game.y = cameraY;
// Update level bar position to follow hero
levelBarBg.x = hero.x;
levelBarBg.y = hero.y + 300; // Position below hero
levelBar.x = levelBarBg.x - levelBarBg.width / 2;
levelBar.y = levelBarBg.y;
levelText.x = levelBarBg.x;
levelText.y = levelBarBg.y - 50;
}
function handleMove(x, y, obj) {
// Handle analog joystick movement
if (isJoystickActive) {
var deltaX = x - joystickStartX;
var deltaY = y - joystickStartY;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
// Limit joystick range
if (distance > joystickRadius) {
deltaX = deltaX / distance * joystickRadius;
deltaY = deltaY / distance * joystickRadius;
}
// Update joystick knob position
joystickKnob.x = joystickStartX + deltaX;
joystickKnob.y = joystickStartY + deltaY;
// Calculate movement values (-1 to 1)
movementX = deltaX / joystickRadius;
movementY = deltaY / joystickRadius;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Make hero walk to touch position
hero.walkTo(x, y);
};
game.up = function (x, y, obj) {
// Deactivate analog joystick
if (isJoystickActive) {
isJoystickActive = false;
joystickBase.visible = false;
joystickKnob.visible = false;
movementX = 0;
movementY = 0;
}
attackIndicator.visible = false;
};
game.update = function () {
// Initialize wave system on first update
if (waveStartTime === 0) {
waveStartTime = Date.now();
}
// Pause game logic during upgrade selection
if (isSelectingUpgrade) {
return;
}
// Update wave timer
updateWaveTimer();
// Apply regeneration if unlocked
if (hasRegen && hero.health < hero.maxHealth) {
var currentTime = LK.ticks;
if (currentTime - lastRegenTime > 60) {
// 60 ticks = 1 second
hero.health = Math.min(hero.health + 1, hero.maxHealth);
lastRegenTime = currentTime;
updateHealthBar();
}
}
// Process analog movement
if (isJoystickActive && (Math.abs(movementX) > 0.1 || Math.abs(movementY) > 0.1)) {
hero.analogMove(movementX, movementY);
}
spawnTimer++;
// Wave-based enemy spawning
var maxEnemiesThisWave = baseEnemiesPerWave + (currentWave - 1) * 5;
var spawnRate = Math.max(60 - currentWave * 3, 15);
// Only spawn if we haven't reached the wave enemy limit
if (spawnTimer >= spawnRate && waveEnemyCount < maxEnemiesThisWave) {
spawnEnemy();
waveEnemyCount++;
spawnTimer = 0;
}
// Boss spawning for wave 1 and above
if (currentWave >= 1 && !bossSpawned && waveEnemyCount >= maxEnemiesThisWave / 2) {
var boss = new Boss();
// Spawn boss near hero in infinite world
var bossDistance = 600 + Math.random() * 200; // 600-800 pixels from hero
var bossAngle = Math.random() * Math.PI * 2; // Random angle around hero
boss.x = hero.x + Math.cos(bossAngle) * bossDistance;
boss.y = hero.y + Math.sin(bossAngle) * bossDistance;
enemies.push(boss);
game.addChild(boss);
bossSpawned = true;
// Boss spawn effects
LK.effects.flashScreen(0xFF0000, 1000);
}
// Update weapon to point towards nearest enemy
var nearestEnemy = null;
var nearestDistance = Infinity;
for (var j = 0; j < enemies.length; j++) {
var tempEnemy = enemies[j];
var tempDx = hero.x - tempEnemy.x;
var tempDy = hero.y - tempEnemy.y;
var tempDistance = Math.sqrt(tempDx * tempDx + tempDy * tempDy);
if (tempDistance < nearestDistance) {
nearestEnemy = tempEnemy;
nearestDistance = tempDistance;
}
}
if (nearestEnemy) {
hero.updateWeapon(nearestEnemy.x, nearestEnemy.y);
// Auto-shoot while moving - shoot at nearest enemy every 40 ticks
var currentTime = LK.ticks;
var shootCooldown = 80;
// Check if attack speed upgrade was selected
for (var u = 0; u < availableUpgrades.length; u++) {
if (availableUpgrades[u].type === 'attackSpeed' && availableUpgrades[u].applied) {
shootCooldown = 40; // Faster shooting
break;
}
}
if (currentTime - lastShootTime > shootCooldown && nearestDistance < 800) {
hero.shoot(nearestEnemy.x, nearestEnemy.y);
lastShootTime = currentTime;
}
}
// Check bullet collisions with enemies
for (var b = bullets.length - 1; b >= 0; b--) {
var bullet = bullets[b];
var bulletHit = false;
for (var e = enemies.length - 1; e >= 0; e--) {
var enemy = enemies[e];
if (bullet.intersects(enemy)) {
// Bullet hits enemy
enemy.takeDamage(bullet.damage);
bullet.destroy();
bullets.splice(b, 1);
bulletHit = true;
break;
}
}
}
// Check lightning bolt collisions with hero
for (var l = lightningBolts.length - 1; l >= 0; l--) {
var lightning = lightningBolts[l];
if (hero.checkCollision(lightning)) {
// Lightning hits hero
hero.takeDamage(lightning.damage);
LK.effects.flashObject(hero, 0xFFFF00, 300);
lightning.destroy();
lightningBolts.splice(l, 1);
}
}
// Check boss bullet collisions with hero
for (var bb = bossBullets.length - 1; bb >= 0; bb--) {
var bossBullet = bossBullets[bb];
if (hero.checkCollision(bossBullet)) {
// Boss bullet hits hero
hero.takeDamage(bossBullet.damage);
LK.effects.flashObject(hero, 0xFF4500, 300);
bossBullet.destroy();
bossBullets.splice(bb, 1);
}
}
// Check collisions and close combat between hero and enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
var dx = hero.x - enemy.x;
var dy = hero.y - enemy.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Auto-attack when very close (melee range)
if (distance < 60 && !enemy.justAttacked) {
hero.attack(enemy);
enemy.justAttacked = true;
// Reset attack flag after short delay
LK.setTimeout(function () {
if (enemy && !enemy.destroyed) {
enemy.justAttacked = false;
}
}, 300);
} else if (hero.checkCollision(enemy) && !enemy.justHitHero) {
// Take damage from collision but don't destroy enemy
hero.takeDamage(enemy.damage);
enemy.justHitHero = true;
// Reset hit flag after short delay to prevent rapid damage
LK.setTimeout(function () {
if (enemy && !enemy.destroyed) {
enemy.justHitHero = false;
}
}, 1000); // 1 second cooldown between hits
}
// Remove enemies that are too far from hero in infinite world
var enemyDistanceFromHero = Math.sqrt((enemy.x - hero.x) * (enemy.x - hero.x) + (enemy.y - hero.y) * (enemy.y - hero.y));
if (enemyDistanceFromHero > 1200) {
// Remove enemies more than 1200 pixels from hero
enemy.destroy();
enemies.splice(i, 1);
}
}
// Update health bar
updateHealthBar();
// Update level bar
updateLevelBar();
// Update boss health bar
updateBossHealthBar();
// Spawn health crates occasionally
if (LK.ticks % 600 === 0) {
// Every 10 seconds
spawnHealthCrate();
}
// Check bullet collisions with health crates
for (var bc = bullets.length - 1; bc >= 0; bc--) {
var bulletCrate = bullets[bc];
var crateHit = false;
for (var hc = healthCrates.length - 1; hc >= 0; hc--) {
var healthCrate = healthCrates[hc];
if (bulletCrate.intersects(healthCrate)) {
// Bullet hits crate
healthCrate.takeDamage(bulletCrate.damage);
bulletCrate.destroy();
bullets.splice(bc, 1);
crateHit = true;
break;
}
}
}
// Check hero melee attacks on crates
for (var cr = healthCrates.length - 1; cr >= 0; cr--) {
var crate = healthCrates[cr];
var crateDx = hero.x - crate.x;
var crateDy = hero.y - crate.y;
var crateDistance = Math.sqrt(crateDx * crateDx + crateDy * crateDy);
// Auto-attack crates when very close
if (crateDistance < 60 && !crate.justAttacked) {
crate.takeDamage(hero.attackDamage);
crate.justAttacked = true;
// Reset attack flag after short delay
LK.setTimeout(function () {
if (crate && !crate.destroyed) {
crate.justAttacked = false;
}
}, 300);
}
}
// Clean up decorations that are too far from hero
for (var d = mapDecorations.length - 1; d >= 0; d--) {
var decoration = mapDecorations[d];
var decorationDistance = Math.sqrt((decoration.x - hero.x) * (decoration.x - hero.x) + (decoration.y - hero.y) * (decoration.y - hero.y));
if (decorationDistance > 1500) {
// Remove decorations more than 1500 pixels from hero
decoration.destroy();
mapDecorations.splice(d, 1);
}
}
// Dynamically generate new decorations as hero moves
if (LK.ticks % 300 === 0) {
// Every 5 seconds
generateMapDecorations();
}
// Update camera to follow hero
updateCamera();
scoreTxt.setText(LK.getScore());
}; ===================================================================
--- original.js
+++ change.js
@@ -1533,76 +1533,97 @@
var decorationTypes = ['tree', 'agac2', 'grass', 'destroyedHouse', 'campfire'];
// Different distribution strategies for each decoration type
var decorationStrategies = {
tree: {
- count: 20,
- minDistance: 800,
- maxDistance: 1500,
- spacing: 150
+ count: 25,
+ minDistance: 200,
+ maxDistance: 2000,
+ spacing: 180,
+ heroAvoidDistance: 150
},
- // Original trees scattered far
+ // Trees distributed across entire map
agac2: {
- count: 15,
- minDistance: 600,
- maxDistance: 1400,
- spacing: 120
+ count: 20,
+ minDistance: 150,
+ maxDistance: 1800,
+ spacing: 140,
+ heroAvoidDistance: 120
},
- // Agac 2 trees scattered across map
+ // Agac 2 trees scattered across entire map
grass: {
- count: 80,
- minDistance: 100,
- maxDistance: 1200,
- spacing: 60
+ count: 100,
+ minDistance: 50,
+ maxDistance: 1500,
+ spacing: 40,
+ heroAvoidDistance: 80
},
- // Grass everywhere
+ // Grass truly everywhere across map
destroyedHouse: {
- count: 6,
- minDistance: 600,
- maxDistance: 1000,
- spacing: 300
+ count: 8,
+ minDistance: 300,
+ maxDistance: 1600,
+ spacing: 350,
+ heroAvoidDistance: 200
},
- // Houses random locations
+ // Houses distributed across map
campfire: {
- count: 4,
- minDistance: 400,
- maxDistance: 800,
- spacing: 200
- } // Campfires spread out
+ count: 6,
+ minDistance: 250,
+ maxDistance: 1200,
+ spacing: 250,
+ heroAvoidDistance: 100
+ } // Campfires spread across map
};
- // Generate decorations across the entire map area around hero
+ // Generate decorations across the entire infinite map area
for (var type in decorationStrategies) {
var strategy = decorationStrategies[type];
var count = strategy.count;
for (var i = 0; i < count; i++) {
var decoration = new MapDecoration();
decoration.setDecoration(type);
- // Generate completely random positions across large area
- var randomAngle = Math.random() * Math.PI * 2;
- var randomDistance = strategy.minDistance + Math.random() * (strategy.maxDistance - strategy.minDistance);
- // Base position around hero but spread much wider
- decoration.x = hero.x + Math.cos(randomAngle) * randomDistance;
- decoration.y = hero.y + Math.sin(randomAngle) * randomDistance;
- // For grass, make it truly everywhere by adding extra randomness
- if (type === 'grass') {
- decoration.x += (Math.random() - 0.5) * 400;
- decoration.y += (Math.random() - 0.5) * 400;
- }
- // Check for minimum spacing between decorations of same type to avoid clustering
- var tooClose = false;
- for (var j = 0; j < mapDecorations.length; j++) {
- var existingDecoration = mapDecorations[j];
- if (existingDecoration.decorationType === type) {
- var spacingDx = decoration.x - existingDecoration.x;
- var spacingDy = decoration.y - existingDecoration.y;
- var spacingDistance = Math.sqrt(spacingDx * spacingDx + spacingDy * spacingDy);
- if (spacingDistance < strategy.spacing) {
- tooClose = true;
- break;
+ var validPosition = false;
+ var attempts = 0;
+ var maxAttempts = 50;
+ // Try to find a valid position that doesn't conflict with hero or existing decorations
+ while (!validPosition && attempts < maxAttempts) {
+ // Generate completely random positions across very large area
+ var randomAngle = Math.random() * Math.PI * 2;
+ var randomDistance = strategy.minDistance + Math.random() * (strategy.maxDistance - strategy.minDistance);
+ // Generate positions in all directions from hero across entire map
+ decoration.x = hero.x + Math.cos(randomAngle) * randomDistance;
+ decoration.y = hero.y + Math.sin(randomAngle) * randomDistance;
+ // For grass, add extra scatter to make it truly everywhere
+ if (type === 'grass') {
+ decoration.x += (Math.random() - 0.5) * 800;
+ decoration.y += (Math.random() - 0.5) * 800;
+ }
+ // Check distance from hero - avoid spawning too close to hero's current position
+ var heroDistance = Math.sqrt((decoration.x - hero.x) * (decoration.x - hero.x) + (decoration.y - hero.y) * (decoration.y - hero.y));
+ if (heroDistance < strategy.heroAvoidDistance) {
+ attempts++;
+ continue; // Try again if too close to hero
+ }
+ // Check for minimum spacing between decorations of same type to avoid clustering
+ var tooClose = false;
+ for (var j = 0; j < mapDecorations.length; j++) {
+ var existingDecoration = mapDecorations[j];
+ if (existingDecoration.decorationType === type) {
+ var spacingDx = decoration.x - existingDecoration.x;
+ var spacingDy = decoration.y - existingDecoration.y;
+ var spacingDistance = Math.sqrt(spacingDx * spacingDx + spacingDy * spacingDy);
+ if (spacingDistance < strategy.spacing) {
+ tooClose = true;
+ break;
+ }
}
}
+ if (!tooClose) {
+ validPosition = true;
+ }
+ attempts++;
}
- // Only add decoration if it's not too close to others of same type
- if (!tooClose) {
+ // Only add decoration if we found a valid position
+ if (validPosition) {
mapDecorations.push(decoration);
game.addChild(decoration);
}
}
Ucan bomba. In-Game asset. 2d. High contrast. No shadows
Qanli iskelet. In-Game asset. 2d. High contrast. No shadows
Beyaz zirhli sovalye. In-Game asset. 2d. High contrast. No shadows
Iskeleten Elektirik buycusu. In-Game asset. 2d. High contrast. No shadows
Wiking boss. In-Game asset. 2d. High contrast. No shadows
Mavi tatli kucuk slime gozleri parildasin. In-Game asset. 2d. High contrast. No shadows
Yesil top halinde tukuruk. In-Game asset. 2d. High contrast. No shadows
Simsek topu. In-Game asset. 2d. High contrast. No shadows
Sandik. In-Game asset. 2d. High contrast. No shadows
Ağac. In-Game asset. 2d. High contrast. No shadows
Cim yesilik. In-Game asset. 2d. High contrast. No shadows
Kamp atesi. In-Game asset. 2d. High contrast. No shadows
Madeni para. In-Game asset. 2d. High contrast. No shadows
Bullet. In-Game asset. 2d. High contrast. No shadows
Lasergun. In-Game asset. 2d. High contrast. No shadows