/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Achievement = Container.expand(function (id, title, description, icon) {
var self = Container.call(this);
self.id = id;
self.title = title;
self.description = description;
self.icon = icon;
self.unlocked = false;
self.displayTime = 0;
// Create achievement notification display
var bg = LK.getAsset('achievement_bg', {
width: 600,
height: 120,
color: 0x2C3E50,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(bg);
var titleText = new Text2(title, {
size: 40,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0);
titleText.x = 0;
titleText.y = -30;
self.addChild(titleText);
var descText = new Text2(description, {
size: 30,
fill: 0xFFFFFF
});
descText.anchor.set(0.5, 1);
descText.x = 0;
descText.y = 30;
self.addChild(descText);
self.unlock = function () {
if (!self.unlocked) {
self.unlocked = true;
self.show();
// Save to storage
storage['achievement_' + self.id] = true;
}
};
self.show = function () {
self.x = 1024;
self.y = 200;
self.alpha = 0;
self.scaleX = 0.5;
self.scaleY = 0.5;
self.displayTime = 180; // 3 seconds
// Animate in
tween(self, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500
});
};
self.update = function () {
if (self.displayTime > 0) {
self.displayTime--;
if (self.displayTime <= 0) {
// Animate out
tween(self, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 500
});
}
}
};
return self;
});
var Dragon = Container.expand(function () {
var self = Container.call(this);
var dragonGraphics = self.attachAsset('dragon', {
anchorX: 0.5,
anchorY: 0.5
});
self.baseSpeed = 1.5;
self.currentSpeed = self.baseSpeed;
self.fireTimer = 0;
self.fireInterval = 120; // 2 seconds initially
self.health = 150;
self.maxHealth = 150;
// Create health bar
var dragonHealthBarBg = self.attachAsset('dragon_health_bar_bg', {
anchorX: 0.5,
anchorY: 0.5
});
dragonHealthBarBg.y = -180;
var dragonHealthBarFill = self.attachAsset('dragon_health_bar_fill', {
anchorX: 0,
anchorY: 0.5
});
dragonHealthBarFill.x = -60;
dragonHealthBarFill.y = -180;
self.update = function () {
// Follow player
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.currentSpeed;
self.y += dy / distance * self.currentSpeed;
}
// Fire breathing timer
self.fireTimer++;
if (self.fireTimer >= self.fireInterval) {
self.breatheFire();
self.fireTimer = 0;
}
// Adjust difficulty based on score
var difficulty = Math.floor(score / 10);
self.currentSpeed = self.baseSpeed + difficulty * 0.5;
self.fireInterval = Math.max(60, 120 - difficulty * 10);
// Update health bar
var healthPercent = self.health / self.maxHealth;
dragonHealthBarFill.width = 120 * healthPercent;
if (healthPercent > 0.6) {
dragonHealthBarFill.tint = 0xFF0000; // Red
} else if (healthPercent > 0.3) {
dragonHealthBarFill.tint = 0xFF4444; // Light Red
} else {
dragonHealthBarFill.tint = 0xFF8888; // Very Light Red
}
};
self.breatheFire = function () {
var fireball = new Fireball();
fireball.x = self.x;
fireball.y = self.y;
// Aim towards player
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
fireball.directionX = dx / distance;
fireball.directionY = dy / distance;
fireballs.push(fireball);
game.addChild(fireball);
LK.getSound('fire').play();
};
self.takeDamage = function (damage) {
self.health = Math.max(0, self.health - damage);
LK.effects.flashObject(self, 0xFF0000, 300);
if (self.health <= 0) {
// Dragon is defeated but doesn't die
LK.effects.flashObject(self, 0x000000, 1000);
self.baseSpeed = 0.5; // Reduced speed but still moves
self.currentSpeed = 0.5;
self.fireInterval = 999999; // Stop firing
}
};
return self;
});
var Fireball = Container.expand(function () {
var self = Container.call(this);
var fireballGraphics = self.attachAsset('fireball', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.directionX = 0;
self.directionY = 0;
self.lifetime = 300; // 5 seconds
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
fireballGraphics.rotation += 0.2;
fireballGraphics.alpha = Math.sin(LK.ticks * 0.5) * 0.3 + 0.7;
self.lifetime--;
if (self.lifetime <= 0) {
self.shouldDestroy = true;
}
// Check bounds
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.shouldDestroy = true;
}
};
return self;
});
var Gem = Container.expand(function () {
var self = Container.call(this);
var gemGraphics = self.attachAsset('gem', {
anchorX: 0.5,
anchorY: 0.5
});
self.points = 25;
self.floatOffset = Math.random() * Math.PI * 2;
self.update = function () {
gemGraphics.y = Math.sin(LK.ticks * 0.15 + self.floatOffset) * 3;
gemGraphics.rotation += 0.05;
gemGraphics.scaleX = Math.sin(LK.ticks * 0.1 + self.floatOffset) * 0.2 + 1;
gemGraphics.scaleY = Math.sin(LK.ticks * 0.1 + self.floatOffset) * 0.2 + 1;
};
return self;
});
var Luigi = Container.expand(function () {
var self = Container.call(this);
var luigiGraphics = self.attachAsset('luigi', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.jumpTimer = 0;
self.jumpInterval = 120; // 2 seconds
self.isJumping = false;
self.jumpHeight = 0;
self.maxJumpHeight = 100;
self.jumpSpeed = 8;
self.direction = 1; // 1 for right, -1 for left
self.update = function () {
// Move horizontally
self.x += self.speed * self.direction;
// Bounce off screen edges
if (self.x <= 100 || self.x >= 1948) {
self.direction *= -1;
luigiGraphics.scaleX = self.direction;
}
// Jump behavior
self.jumpTimer++;
if (self.jumpTimer >= self.jumpInterval && !self.isJumping) {
self.isJumping = true;
self.jumpHeight = 0;
self.jumpTimer = 0;
}
if (self.isJumping) {
self.jumpHeight += self.jumpSpeed;
if (self.jumpHeight >= self.maxJumpHeight) {
self.jumpSpeed = -8;
}
if (self.jumpHeight <= 0 && self.jumpSpeed < 0) {
self.jumpHeight = 0;
self.jumpSpeed = 8;
self.isJumping = false;
}
luigiGraphics.y = -self.jumpHeight;
}
// Check if player is close and doesn't have weapon
if (!playerWeapon) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 100) {
// Give weapon to player
playerWeapon = new Weapon();
playerWeapon.x = player.x;
playerWeapon.y = player.y - 50;
game.addChild(playerWeapon);
LK.effects.flashObject(self, 0x00FF00, 500);
updateScore(100); // Bonus for getting weapon
}
}
// Animate Luigi
luigiGraphics.rotation = Math.sin(LK.ticks * 0.1) * 0.1;
};
return self;
});
var Mario = Container.expand(function () {
var self = Container.call(this);
var marioGraphics = self.attachAsset('mario', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.targetSpaghetti = null;
self.searchTimer = 0;
self.searchInterval = 60; // 1 second
self.isAngry = false;
self.angerTimer = 0;
self.angerDuration = 300; // 5 seconds of anger
self.attackSpeed = 6;
self.health = 100;
self.maxHealth = 100;
// Create health bar
var healthBarBg = self.attachAsset('health_bar_bg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.y = -120;
var healthBarFill = self.attachAsset('health_bar_fill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -40;
healthBarFill.y = -120;
self.update = function () {
// Search for nearest spaghetti
self.searchTimer++;
if (self.searchTimer >= self.searchInterval) {
self.findNearestSpaghetti();
self.searchTimer = 0;
}
// Handle anger state
if (self.isAngry) {
self.angerTimer--;
if (self.angerTimer <= 0) {
self.isAngry = false;
marioGraphics.tint = 0xFFFFFF; // Return to normal color
} else {
// Attack player when angry
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.attackSpeed;
self.y += dy / distance * self.attackSpeed;
// Face the direction of movement
marioGraphics.scaleX = dx > 0 ? 1 : -1;
}
}
} else {
// Move towards target spaghetti
if (self.targetSpaghetti && spaghettis.indexOf(self.targetSpaghetti) !== -1) {
var dx = self.targetSpaghetti.x - self.x;
var dy = self.targetSpaghetti.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
// Face the direction of movement
marioGraphics.scaleX = dx > 0 ? 1 : -1;
}
} else {
// Random movement when no spaghetti found
self.x += (Math.random() - 0.5) * 2;
self.y += (Math.random() - 0.5) * 2;
}
}
// Keep Mario in bounds
self.x = Math.max(100, Math.min(1948, self.x));
self.y = Math.max(100, Math.min(2632, self.y));
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBarFill.width = 80 * healthPercent;
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00FF00; // Green
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xFFFF00; // Yellow
} else {
healthBarFill.tint = 0xFF0000; // Red
}
// Animate Mario
marioGraphics.rotation = Math.sin(LK.ticks * 0.1) * 0.05;
};
self.findNearestSpaghetti = function () {
var nearestDistance = Infinity;
var nearestSpaghetti = null;
for (var i = 0; i < spaghettis.length; i++) {
var spaghetti = spaghettis[i];
var dx = spaghetti.x - self.x;
var dy = spaghetti.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestSpaghetti = spaghetti;
}
}
self.targetSpaghetti = nearestSpaghetti;
};
self.becomeAngry = function () {
self.isAngry = true;
self.angerTimer = self.angerDuration;
marioGraphics.tint = 0xFF0000; // Turn red when angry
LK.effects.flashObject(self, 0xFF0000, 500);
};
self.takeDamage = function (damage) {
self.health = Math.max(0, self.health - damage);
LK.effects.flashObject(self, 0xFF0000, 300);
if (self.health <= 0) {
// Mario is defeated and disappears
LK.effects.flashObject(self, 0x000000, 1000);
self.shouldDestroy = true; // Mark Mario for destruction
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.invulnerable = false;
self.invulnerabilityTime = 0;
self.speedBoost = false;
self.speedBoostTime = 0;
self.update = function () {
if (self.invulnerabilityTime > 0) {
self.invulnerabilityTime--;
playerGraphics.alpha = Math.sin(LK.ticks * 0.3) * 0.5 + 0.5;
if (self.invulnerabilityTime <= 0) {
self.invulnerable = false;
playerGraphics.alpha = 1;
}
}
if (self.speedBoostTime > 0) {
self.speedBoostTime--;
if (self.speedBoostTime <= 0) {
self.speedBoost = false;
playerGraphics.tint = 0xFFFFFF;
}
}
};
self.activateInvulnerability = function () {
self.invulnerable = true;
self.invulnerabilityTime = 120; // 2 seconds at 60fps
};
self.activateSpeedBoost = function () {
self.speedBoost = true;
self.speedBoostTime = 300; // 5 seconds at 60fps
playerGraphics.tint = 0x00FFFF;
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = Math.random() < 0.5 ? 'speed' : 'invulnerable';
self.floatOffset = Math.random() * Math.PI * 2;
if (self.type === 'invulnerable') {
powerupGraphics.tint = 0xFFD700;
}
self.update = function () {
powerupGraphics.y = Math.sin(LK.ticks * 0.2 + self.floatOffset) * 8;
powerupGraphics.rotation += 0.1;
powerupGraphics.alpha = Math.sin(LK.ticks * 0.3) * 0.3 + 0.7;
};
return self;
});
var Sonic = Container.expand(function () {
var self = Container.call(this);
var sonicGraphics = self.attachAsset('sonic', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.targetMario = null;
self.searchTimer = 0;
self.searchInterval = 30; // 0.5 seconds
self.attackTimer = 0;
self.attackInterval = 120; // 2 seconds
self.spindashTimer = 0;
self.isSpinning = false;
self.direction = 1;
self.goldTimer = 30 * 60; // 30 seconds at 60fps
self.goldCollected = 0;
self.goldTarget = 3;
self.isAttackingPlayer = false;
self.targetPlayer = null;
self.health = 80;
self.maxHealth = 80;
// Create health bar
var healthBarBg = self.attachAsset('health_bar_bg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.y = -120;
var healthBarFill = self.attachAsset('health_bar_fill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -40;
healthBarFill.y = -120;
self.update = function () {
// Update gold collection timer
self.goldTimer--;
if (self.goldTimer <= 0 && self.goldCollected < self.goldTarget) {
self.isAttackingPlayer = true;
self.targetPlayer = player;
sonicGraphics.tint = 0xFF0000; // Turn red when attacking player
}
// Behavior based on current state
if (self.isAttackingPlayer && self.targetPlayer) {
// Attack player mode
var dx = self.targetPlayer.x - self.x;
var dy = self.targetPlayer.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * (self.speed * 1.5); // Faster when attacking player
self.y += dy / distance * (self.speed * 1.5);
sonicGraphics.scaleX = dx > 0 ? 1 : -1;
self.direction = dx > 0 ? 1 : -1;
}
} else if (self.goldCollected < self.goldTarget) {
// Gold collection mode - search for gems (gold)
self.searchTimer++;
if (self.searchTimer >= self.searchInterval) {
self.findNearestGem();
self.searchTimer = 0;
}
// Move towards nearest gem
if (self.targetGem && gems.indexOf(self.targetGem) !== -1) {
var dx = self.targetGem.x - self.x;
var dy = self.targetGem.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
sonicGraphics.scaleX = dx > 0 ? 1 : -1;
self.direction = dx > 0 ? 1 : -1;
}
} else {
// Search pattern movement
self.x += Math.sin(LK.ticks * 0.02) * 3;
self.y += Math.cos(LK.ticks * 0.015) * 2;
}
} else {
// Normal mode (after collecting enough gold) - but only attack Mario if dragon is alive
if (!dragon.isDead) {
self.searchTimer++;
if (self.searchTimer >= self.searchInterval) {
self.findMario();
self.searchTimer = 0;
}
// Move towards Mario for combat
if (self.targetMario && marios.indexOf(self.targetMario) !== -1) {
var dx = self.targetMario.x - self.x;
var dy = self.targetMario.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 80) {
// Move towards Mario
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
sonicGraphics.scaleX = dx > 0 ? 1 : -1;
self.direction = dx > 0 ? 1 : -1;
} else {
// Close enough to attack - start spindash
self.attackTimer++;
if (self.attackTimer >= self.attackInterval && !self.isSpinning) {
self.startSpindash();
self.attackTimer = 0;
}
}
} else {
// Search pattern movement
self.x += Math.sin(LK.ticks * 0.02) * 3;
self.y += Math.cos(LK.ticks * 0.015) * 2;
}
} else {
// Dragon is dead - peaceful mode, just wander around
self.x += Math.sin(LK.ticks * 0.01) * 2;
self.y += Math.cos(LK.ticks * 0.008) * 1.5;
}
}
// Handle spindash attack
if (self.isSpinning) {
self.spindashTimer++;
sonicGraphics.rotation += 0.5;
sonicGraphics.scaleX = Math.sin(LK.ticks * 0.3) * 0.2 + 0.8;
sonicGraphics.scaleY = Math.sin(LK.ticks * 0.3) * 0.2 + 0.8;
if (self.spindashTimer >= 60) {
self.isSpinning = false;
self.spindashTimer = 0;
sonicGraphics.rotation = 0;
sonicGraphics.scaleX = self.direction;
sonicGraphics.scaleY = 1;
}
}
// Keep Sonic in bounds
self.x = Math.max(100, Math.min(1948, self.x));
self.y = Math.max(100, Math.min(2632, self.y));
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBarFill.width = 80 * healthPercent;
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00FF00; // Green
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xFFFF00; // Yellow
} else {
healthBarFill.tint = 0xFF0000; // Red
}
// Idle animation when not spinning
if (!self.isSpinning) {
sonicGraphics.y = Math.sin(LK.ticks * 0.15) * 2;
}
};
self.findMario = function () {
var nearestDistance = Infinity;
var nearestMario = null;
for (var i = 0; i < marios.length; i++) {
var mario = marios[i];
var dx = mario.x - self.x;
var dy = mario.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestMario = mario;
}
}
self.targetMario = nearestMario;
};
self.findNearestGem = function () {
var nearestDistance = Infinity;
var nearestGem = null;
// First check for sonic gold (priority)
for (var i = 0; i < sonicGolds.length; i++) {
var sonicGold = sonicGolds[i];
var dx = sonicGold.x - self.x;
var dy = sonicGold.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestGem = sonicGold;
}
}
// Then check regular gems if no sonic gold found
if (!nearestGem) {
for (var i = 0; i < gems.length; i++) {
var gem = gems[i];
var dx = gem.x - self.x;
var dy = gem.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestGem = gem;
}
}
}
self.targetGem = nearestGem;
};
self.startSpindash = function () {
self.isSpinning = true;
self.spindashTimer = 0;
// Flash blue to indicate attack
LK.effects.flashObject(self, 0x0066FF, 200);
};
self.takeDamage = function (damage) {
self.health = Math.max(0, self.health - damage);
LK.effects.flashObject(self, 0xFF0000, 300);
if (self.health <= 0) {
// Sonic is defeated and disappears
LK.effects.flashObject(self, 0x000000, 1000);
self.shouldDestroy = true; // Mark Sonic for destruction
}
};
return self;
});
var SonicGold = Container.expand(function () {
var self = Container.call(this);
var sonicGoldGraphics = self.attachAsset('sonic_gold', {
anchorX: 0.5,
anchorY: 0.5
});
self.points = 30;
self.floatOffset = Math.random() * Math.PI * 2;
// Give it a special blue tint to distinguish from regular gems
sonicGoldGraphics.tint = 0x0066FF;
self.update = function () {
sonicGoldGraphics.y = Math.sin(LK.ticks * 0.18 + self.floatOffset) * 4;
sonicGoldGraphics.rotation += 0.08;
sonicGoldGraphics.scaleX = Math.sin(LK.ticks * 0.12 + self.floatOffset) * 0.25 + 1;
sonicGoldGraphics.scaleY = Math.sin(LK.ticks * 0.12 + self.floatOffset) * 0.25 + 1;
// Special pulsing effect
sonicGoldGraphics.alpha = Math.sin(LK.ticks * 0.2) * 0.3 + 0.7;
};
return self;
});
var Spaghetti = Container.expand(function () {
var self = Container.call(this);
var spaghettiGraphics = self.attachAsset('spaghetti', {
anchorX: 0.5,
anchorY: 0.5
});
self.points = 15;
self.floatOffset = Math.random() * Math.PI * 2;
self.update = function () {
spaghettiGraphics.y = Math.sin(LK.ticks * 0.12 + self.floatOffset) * 4;
spaghettiGraphics.rotation += 0.03;
spaghettiGraphics.scaleX = Math.sin(LK.ticks * 0.08 + self.floatOffset) * 0.1 + 1;
spaghettiGraphics.scaleY = Math.sin(LK.ticks * 0.08 + self.floatOffset) * 0.1 + 1;
};
return self;
});
var Treasure = Container.expand(function () {
var self = Container.call(this);
var treasureGraphics = self.attachAsset('treasure', {
anchorX: 0.5,
anchorY: 0.5
});
self.points = 10;
self.floatOffset = Math.random() * Math.PI * 2;
self.update = function () {
treasureGraphics.y = Math.sin(LK.ticks * 0.1 + self.floatOffset) * 5;
treasureGraphics.rotation += 0.02;
};
return self;
});
var Weapon = Container.expand(function () {
var self = Container.call(this);
var weaponGraphics = self.attachAsset('weapon', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 25;
self.cooldown = 0;
self.maxCooldown = 30; // 0.5 seconds
self.range = 150;
self.isActive = false;
self.update = function () {
if (self.cooldown > 0) {
self.cooldown--;
}
// Rotate weapon around player
weaponGraphics.rotation += 0.2;
// Pulse effect when ready to use
if (self.cooldown <= 0) {
weaponGraphics.alpha = Math.sin(LK.ticks * 0.3) * 0.3 + 0.7;
weaponGraphics.scaleX = Math.sin(LK.ticks * 0.2) * 0.2 + 1;
weaponGraphics.scaleY = Math.sin(LK.ticks * 0.2) * 0.2 + 1;
} else {
weaponGraphics.alpha = 0.5;
weaponGraphics.scaleX = 0.8;
weaponGraphics.scaleY = 0.8;
}
};
self.attack = function (target) {
if (self.cooldown <= 0) {
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.range) {
target.takeDamage(self.damage);
self.cooldown = self.maxCooldown;
LK.effects.flashObject(self, 0xFFFFFF, 300);
LK.getSound('weapon_hit').play();
return true;
}
}
return false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x8B4513
});
/****
* Game Code
****/
game.setBackgroundColor(0xDEB887);
// Game variables
var score = 0;
var health = 250;
var maxHealth = 250;
var gameTimer = 80 * 60; // 1 minute 20 seconds (80 seconds) at 60fps
var treasures = [];
var gems = [];
var powerups = [];
var fireballs = [];
var luigis = [];
var marios = [];
var spaghettis = [];
var sonics = [];
var sonicGolds = [];
var treasureSpawnTimer = 0;
var gemSpawnTimer = 0;
var powerupSpawnTimer = 0;
var luigiSpawnTimer = 0;
var spaghettiSpawnTimer = 0;
var sonicGoldSpawnTimer = 0;
var sonicGoldSpawnCount = 0;
var maxSonicGoldSpawns = 3;
var playerWeapon = null;
// Achievement system
var achievements = [];
var activeAchievements = [];
var achievementStats = {
treasuresCollected: 0,
gemsCollected: 0,
powerupsCollected: 0,
dragonHits: 0,
fireballsAvoided: 0,
survivalTime: 0,
maxScore: 0
};
// Initialize achievements
function initializeAchievements() {
achievements = [new Achievement('first_treasure', 'First Treasure!', 'Collect your first treasure', 'đ°'), new Achievement('treasure_hunter', 'Treasure Hunter', 'Collect 10 treasures', 'đ'), new Achievement('gem_collector', 'Gem Collector', 'Collect 5 gems', 'đ'), new Achievement('survivor', 'Survivor', 'Survive for 30 seconds', 'â°'), new Achievement('speed_demon', 'Speed Demon', 'Use 3 speed power-ups', 'âĄ'), new Achievement('invincible', 'Invincible', 'Use 3 invulnerability power-ups', 'đĄď¸'), new Achievement('score_master', 'Score Master', 'Reach 200 points', 'đŻ'), new Achievement('dragon_dancer', 'Dragon Dancer', 'Get hit by dragon 5 times and survive', 'đ'), new Achievement('high_scorer', 'High Scorer', 'Reach 300 points', 'đ'), new Achievement('ultimate_survivor', 'Ultimate Survivor', 'Survive for 60 seconds', 'đ')];
// Load unlocked achievements from storage
for (var i = 0; i < achievements.length; i++) {
var achievement = achievements[i];
if (storage['achievement_' + achievement.id]) {
achievement.unlocked = true;
}
}
}
function checkAchievements() {
for (var i = 0; i < achievements.length; i++) {
var achievement = achievements[i];
if (!achievement.unlocked) {
var shouldUnlock = false;
switch (achievement.id) {
case 'first_treasure':
shouldUnlock = achievementStats.treasuresCollected >= 1;
break;
case 'treasure_hunter':
shouldUnlock = achievementStats.treasuresCollected >= 10;
break;
case 'gem_collector':
shouldUnlock = achievementStats.gemsCollected >= 5;
break;
case 'survivor':
shouldUnlock = achievementStats.survivalTime >= 30 * 60;
break;
case 'speed_demon':
shouldUnlock = achievementStats.powerupsCollected >= 3;
break;
case 'invincible':
shouldUnlock = achievementStats.powerupsCollected >= 3;
break;
case 'score_master':
shouldUnlock = score >= 200;
break;
case 'dragon_dancer':
shouldUnlock = achievementStats.dragonHits >= 5;
break;
case 'high_scorer':
shouldUnlock = score >= 300;
break;
case 'ultimate_survivor':
shouldUnlock = achievementStats.survivalTime >= 60 * 60;
break;
}
if (shouldUnlock) {
achievement.unlock();
activeAchievements.push(achievement);
game.addChild(achievement);
}
}
}
}
// Initialize achievements
initializeAchievements();
// Create player
var player = game.addChild(new Player());
player.x = 1024;
player.y = 1366;
// Create dragon
var dragon = game.addChild(new Dragon());
dragon.x = 200;
dragon.y = 200;
// Create Luigi monster
var luigi = game.addChild(new Luigi());
luigi.x = 1500;
luigi.y = 1000;
luigis.push(luigi);
// Create Mario character
var mario = game.addChild(new Mario());
mario.x = 300;
mario.y = 800;
marios.push(mario);
// Create Sonic character
var sonic = game.addChild(new Sonic());
sonic.x = 1700;
sonic.y = 600;
sonics.push(sonic);
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var healthTxt = new Text2('Health: 250/250', {
size: 60,
fill: 0x00FF00
});
healthTxt.anchor.set(0, 0);
healthTxt.x = 120;
healthTxt.y = 20;
LK.gui.addChild(healthTxt);
var timerTxt = new Text2('Time: 1:20', {
size: 70,
fill: 0xFFFFFF
});
timerTxt.anchor.set(1, 0);
timerTxt.x = 2048 - 20;
timerTxt.y = 20;
LK.gui.addChild(timerTxt);
var instructionTxt = new Text2('Tap to move! Get close to Luigi for weapon to defeat dragon!', {
size: 50,
fill: 0xFFFFFF
});
instructionTxt.anchor.set(0.5, 1);
LK.gui.bottom.addChild(instructionTxt);
// Game logic functions
function spawnTreasure() {
var treasure = new Treasure();
treasure.x = Math.random() * 1800 + 124;
treasure.y = Math.random() * 2400 + 166;
treasures.push(treasure);
game.addChild(treasure);
}
function spawnGem() {
var gem = new Gem();
gem.x = Math.random() * 1800 + 124;
gem.y = Math.random() * 2400 + 166;
gems.push(gem);
game.addChild(gem);
}
function spawnPowerUp() {
var powerup = new PowerUp();
powerup.x = Math.random() * 1800 + 124;
powerup.y = Math.random() * 2400 + 166;
powerups.push(powerup);
game.addChild(powerup);
}
function spawnSpaghetti() {
var spaghetti = new Spaghetti();
spaghetti.x = Math.random() * 1800 + 124;
spaghetti.y = Math.random() * 2400 + 166;
spaghettis.push(spaghetti);
game.addChild(spaghetti);
}
function spawnSonicGold() {
if (sonicGoldSpawnCount < maxSonicGoldSpawns) {
var sonicGold = new SonicGold();
sonicGold.x = Math.random() * 1800 + 124;
sonicGold.y = Math.random() * 2400 + 166;
sonicGolds.push(sonicGold);
game.addChild(sonicGold);
}
}
function updateScore(points) {
score += points;
scoreTxt.setText('Score: ' + score);
LK.setScore(score);
}
function updateHealth(damage) {
health = Math.max(0, health - damage);
healthTxt.setText('Health: ' + health + '/' + maxHealth);
// Change color based on health percentage
var healthPercent = health / maxHealth;
if (healthPercent > 0.6) {
healthTxt.fill = 0x00FF00; // Green
} else if (healthPercent > 0.3) {
healthTxt.fill = 0xFFFF00; // Yellow
} else {
healthTxt.fill = 0xFF0000; // Red
}
if (health <= 0) {
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
}
}
function updateTimer() {
gameTimer = Math.max(0, gameTimer - 1);
var totalSeconds = Math.floor(gameTimer / 60);
var minutes = Math.floor(totalSeconds / 60);
var seconds = totalSeconds % 60;
var timeText = minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
timerTxt.setText('Time: ' + timeText);
// Change color based on remaining time
if (gameTimer <= 10 * 60) {
// Last 10 seconds
timerTxt.fill = 0xFF0000; // Red
} else if (gameTimer <= 30 * 60) {
// Last 30 seconds
timerTxt.fill = 0xFFFF00; // Yellow
} else {
timerTxt.fill = 0xFFFFFF; // White
}
if (gameTimer <= 0) {
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
}
}
// Touch controls
var targetX = player.x;
var targetY = player.y;
game.down = function (x, y, obj) {
targetX = x;
targetY = y;
// Attack dragon with weapon if player has one
if (playerWeapon) {
playerWeapon.attack(dragon);
// Also attack any Mario in range
for (var i = 0; i < marios.length; i++) {
var mario = marios[i];
if (mario.health > 0) {
playerWeapon.attack(mario);
}
}
// Also attack any Sonic in range
for (var i = 0; i < sonics.length; i++) {
var sonic = sonics[i];
playerWeapon.attack(sonic);
}
}
};
game.move = function (x, y, obj) {
targetX = x;
targetY = y;
};
// Game update loop
game.update = function () {
// Update timer
updateTimer();
// Move player towards target
var dx = targetX - player.x;
var dy = targetY - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
var speed = player.speedBoost ? player.speed * 1.5 : player.speed;
player.x += dx / distance * speed;
player.y += dy / distance * speed;
}
// Keep player in bounds
player.x = Math.max(40, Math.min(2008, player.x));
player.y = Math.max(40, Math.min(2692, player.y));
// Update weapon position if player has one
if (playerWeapon) {
playerWeapon.x = player.x;
playerWeapon.y = player.y - 50;
// Auto-attack dragon when in range
var dx = dragon.x - playerWeapon.x;
var dy = dragon.y - playerWeapon.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= playerWeapon.range) {
playerWeapon.attack(dragon);
}
// Auto-attack Mario when in range
for (var i = 0; i < marios.length; i++) {
var mario = marios[i];
if (mario.health > 0) {
var dx = mario.x - playerWeapon.x;
var dy = mario.y - playerWeapon.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= playerWeapon.range) {
playerWeapon.attack(mario);
}
}
}
// Auto-attack Sonic when in range
for (var i = 0; i < sonics.length; i++) {
var sonic = sonics[i];
var dx = sonic.x - playerWeapon.x;
var dy = sonic.y - playerWeapon.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= playerWeapon.range) {
playerWeapon.attack(sonic);
}
}
}
// Spawn treasures
treasureSpawnTimer++;
if (treasureSpawnTimer >= 90) {
// Every 1.5 seconds
spawnTreasure();
treasureSpawnTimer = 0;
}
// Spawn gems
gemSpawnTimer++;
if (gemSpawnTimer >= 180) {
// Every 3 seconds
spawnGem();
gemSpawnTimer = 0;
}
// Spawn power-ups
powerupSpawnTimer++;
if (powerupSpawnTimer >= 600) {
// Every 10 seconds
spawnPowerUp();
powerupSpawnTimer = 0;
}
// Spawn spaghetti
spaghettiSpawnTimer++;
if (spaghettiSpawnTimer >= 120) {
// Every 2 seconds
spawnSpaghetti();
spaghettiSpawnTimer = 0;
}
// Spawn sonic gold
sonicGoldSpawnTimer++;
if (sonicGoldSpawnTimer >= 240 && sonicGoldSpawnCount < maxSonicGoldSpawns) {
// Every 4 seconds, but only up to maximum allowed
spawnSonicGold();
sonicGoldSpawnTimer = 0;
sonicGoldSpawnCount++;
}
// Check treasure collection
for (var i = treasures.length - 1; i >= 0; i--) {
var treasure = treasures[i];
if (player.intersects(treasure)) {
updateScore(treasure.points);
achievementStats.treasuresCollected++;
LK.effects.flashObject(treasure, 0xFFFFFF, 300);
treasure.destroy();
treasures.splice(i, 1);
LK.getSound('collect').play();
}
}
// Check gem collection
for (var i = gems.length - 1; i >= 0; i--) {
var gem = gems[i];
if (player.intersects(gem)) {
updateScore(gem.points);
achievementStats.gemsCollected++;
LK.effects.flashObject(gem, 0xFFFFFF, 300);
gem.destroy();
gems.splice(i, 1);
LK.getSound('collect').play();
}
}
// Check power-up collection
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
if (player.intersects(powerup)) {
if (powerup.type === 'speed') {
player.activateSpeedBoost();
} else if (powerup.type === 'invulnerable') {
player.activateInvulnerability();
}
achievementStats.powerupsCollected++;
LK.effects.flashObject(powerup, 0xFFFFFF, 300);
powerup.destroy();
powerups.splice(i, 1);
LK.getSound('powerup').play();
}
}
// Check Mario spaghetti collection
for (var i = marios.length - 1; i >= 0; i--) {
var mario = marios[i];
for (var j = spaghettis.length - 1; j >= 0; j--) {
var spaghetti = spaghettis[j];
if (mario.intersects(spaghetti)) {
updateScore(spaghetti.points);
LK.effects.flashObject(spaghetti, 0xFFD700, 300);
spaghetti.destroy();
spaghettis.splice(j, 1);
LK.getSound('collect').play();
break;
}
}
}
// Check player spaghetti collection
for (var i = spaghettis.length - 1; i >= 0; i--) {
var spaghetti = spaghettis[i];
if (player.intersects(spaghetti)) {
updateScore(spaghetti.points);
LK.effects.flashObject(spaghetti, 0xFFD700, 300);
// Make all Marios angry when player steals spaghetti
for (var j = 0; j < marios.length; j++) {
var mario = marios[j];
if (!mario.isAngry) {
mario.becomeAngry();
}
}
spaghetti.destroy();
spaghettis.splice(i, 1);
LK.getSound('collect').play();
}
}
// Check if dragon is defeated and should disappear
if (dragon.health <= 0 && !dragon.isDead) {
dragon.isDead = true;
// Make all Marios angry when dragon is defeated
for (var j = 0; j < marios.length; j++) {
var mario = marios[j];
if (!mario.isAngry) {
mario.becomeAngry();
}
}
// Sonics remain in the game when dragon is defeated - they will no longer attack Mario
for (var k = 0; k < sonics.length; k++) {
var sonic = sonics[k];
sonic.isAttackingPlayer = false; // Stop attacking player
sonic.targetMario = null; // Clear Mario target
sonic.targetPlayer = null; // Clear player target
sonicGraphics = sonic.children[0]; // Get sonic graphics
if (sonicGraphics) {
sonicGraphics.tint = 0xFFFFFF; // Return to normal color
}
}
LK.effects.flashScreen(0x00FF00, 1000); // Green flash for dragon defeat
updateScore(200); // Bonus points for defeating dragon
// Dragon disappears from the game
dragon.destroy();
}
// Check if any Mario has died and make Sonics attack player
for (var i = marios.length - 1; i >= 0; i--) {
var mario = marios[i];
if (mario.shouldDestroy) {
// Mario is dead, make all Sonics attack player
for (var j = 0; j < sonics.length; j++) {
var sonic = sonics[j];
sonic.isAttackingPlayer = true;
sonic.targetPlayer = player;
var sonicGraphics = sonic.children[0];
if (sonicGraphics) {
sonicGraphics.tint = 0xFF0000; // Turn red when attacking player
}
}
// Remove Mario from game
mario.destroy();
marios.splice(i, 1);
}
}
// Check if any Sonic has died and remove them
for (var i = sonics.length - 1; i >= 0; i--) {
var sonic = sonics[i];
if (sonic.shouldDestroy) {
// Sonic died - game over
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
// Remove Sonic from game
sonic.destroy();
sonics.splice(i, 1);
}
}
// Game continues until timer runs out or player dies
// Check dragon collision
if (!player.invulnerable && player.intersects(dragon)) {
if (!player.lastDragonHit) {
updateHealth(50);
achievementStats.dragonHits++;
player.activateInvulnerability();
LK.effects.flashScreen(0xFF0000, 500);
player.lastDragonHit = true;
}
} else {
player.lastDragonHit = false;
}
// Check fireball collisions
for (var i = fireballs.length - 1; i >= 0; i--) {
var fireball = fireballs[i];
if (fireball.shouldDestroy) {
fireball.destroy();
fireballs.splice(i, 1);
continue;
}
if (!player.invulnerable && player.intersects(fireball)) {
if (!fireball.hasHit) {
updateHealth(25);
player.activateInvulnerability();
LK.effects.flashScreen(0xFF0000, 300);
fireball.hasHit = true;
fireball.shouldDestroy = true;
}
}
}
// Check Luigi collisions
for (var i = luigis.length - 1; i >= 0; i--) {
var luigi = luigis[i];
if (!player.invulnerable && player.intersects(luigi)) {
if (!luigi.lastHit) {
updateHealth(30);
player.activateInvulnerability();
LK.effects.flashScreen(0x00FF00, 400);
luigi.lastHit = true;
}
} else {
luigi.lastHit = false;
}
}
// Check angry Mario attacking player
for (var i = marios.length - 1; i >= 0; i--) {
var mario = marios[i];
if (mario.isAngry && !player.invulnerable && player.intersects(mario)) {
if (!mario.lastPlayerHit) {
updateHealth(40);
player.activateInvulnerability();
LK.effects.flashScreen(0xFF0000, 400);
mario.lastPlayerHit = true;
}
} else {
mario.lastPlayerHit = false;
}
}
// Check sonic gold collection (special gold for Sonic)
for (var i = sonicGolds.length - 1; i >= 0; i--) {
var sonicGold = sonicGolds[i];
if (player.intersects(sonicGold)) {
// Check if any Sonic needs gold
var goldGivenToSonic = false;
for (var j = 0; j < sonics.length; j++) {
var sonic = sonics[j];
if (sonic.goldCollected < sonic.goldTarget) {
sonic.goldCollected++;
goldGivenToSonic = true;
LK.effects.flashObject(sonic, 0x0066FF, 500); // Flash Sonic to show he received gold
break;
}
}
if (goldGivenToSonic) {
updateScore(sonicGold.points);
achievementStats.gemsCollected++;
LK.effects.flashObject(sonicGold, 0x0066FF, 300); // Blue flash for Sonic gold
sonicGold.destroy();
sonicGolds.splice(i, 1);
LK.getSound('collect').play();
} else {
// Normal collection if no Sonic needs gold
updateScore(sonicGold.points);
achievementStats.gemsCollected++;
LK.effects.flashObject(sonicGold, 0xFFD700, 300);
sonicGold.destroy();
sonicGolds.splice(i, 1);
LK.getSound('collect').play();
}
}
}
// Check if player collects gems for Sonic
for (var i = gems.length - 1; i >= 0; i--) {
var gem = gems[i];
if (player.intersects(gem)) {
// Check if any Sonic needs gold
var goldGivenToSonic = false;
for (var j = 0; j < sonics.length; j++) {
var sonic = sonics[j];
if (sonic.goldCollected < sonic.goldTarget) {
sonic.goldCollected++;
goldGivenToSonic = true;
LK.effects.flashObject(sonic, 0x0066FF, 300); // Flash Sonic to show he received gold
break;
}
}
if (goldGivenToSonic) {
updateScore(gem.points);
achievementStats.gemsCollected++;
LK.effects.flashObject(gem, 0xFFD700, 300); // Gold flash for Sonic collection
gem.destroy();
gems.splice(i, 1);
LK.getSound('collect').play();
} else {
// Normal gem collection if no Sonic needs gold
updateScore(gem.points);
achievementStats.gemsCollected++;
LK.effects.flashObject(gem, 0xFFFFFF, 300);
gem.destroy();
gems.splice(i, 1);
LK.getSound('collect').play();
}
}
}
// Check Sonic attacking player
for (var i = sonics.length - 1; i >= 0; i--) {
var sonic = sonics[i];
if (sonic.isAttackingPlayer && !player.invulnerable && player.intersects(sonic)) {
if (!sonic.lastPlayerHit) {
updateHealth(35);
player.activateInvulnerability();
LK.effects.flashScreen(0x0066FF, 400);
sonic.lastPlayerHit = true;
}
} else {
sonic.lastPlayerHit = false;
}
}
// Check Sonic vs Mario combat
for (var i = sonics.length - 1; i >= 0; i--) {
var sonic = sonics[i];
for (var j = marios.length - 1; j >= 0; j--) {
var mario = marios[j];
if (sonic.intersects(mario)) {
if (!sonic.lastMarioHit && sonic.isSpinning) {
// Sonic hits Mario during spindash
mario.takeDamage(20);
LK.effects.flashObject(mario, 0xFF0000, 500);
LK.effects.flashObject(sonic, 0x0066FF, 300);
updateScore(50); // Bonus points for combat
sonic.lastMarioHit = true;
} else if (!mario.lastSonicHit && !sonic.isSpinning) {
// Mario hits Sonic when not spinning
LK.effects.flashObject(sonic, 0xFF0000, 500);
LK.effects.flashObject(mario, 0x00FF00, 300);
updateScore(30); // Bonus points for combat
mario.lastSonicHit = true;
}
} else {
sonic.lastMarioHit = false;
mario.lastSonicHit = false;
}
}
}
// Spawn additional Luigis based on score
luigiSpawnTimer++;
if (luigiSpawnTimer >= 1800 && score >= 100) {
// Every 30 seconds after score 100
var newLuigi = new Luigi();
newLuigi.x = Math.random() * 1800 + 124;
newLuigi.y = Math.random() * 2000 + 500;
luigis.push(newLuigi);
game.addChild(newLuigi);
luigiSpawnTimer = 0;
}
// Update achievement stats
achievementStats.survivalTime++;
if (score > achievementStats.maxScore) {
achievementStats.maxScore = score;
}
// Check for new achievements
checkAchievements();
// Update active achievement displays
for (var i = activeAchievements.length - 1; i >= 0; i--) {
var achievement = activeAchievements[i];
if (achievement.displayTime <= 0 && achievement.alpha <= 0) {
achievement.destroy();
activeAchievements.splice(i, 1);
}
}
// Win condition
if (score >= 500) {
LK.showYouWin();
}
};
// Start background music
LK.playMusic('desert');
// Initial treasures
for (var i = 0; i < 3; i++) {
spawnTreasure();
}
// Initial spaghetti
for (var i = 0; i < 2; i++) {
spawnSpaghetti();
}
// Initial sonic gold - removed to respect 3 total limit
; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Achievement = Container.expand(function (id, title, description, icon) {
var self = Container.call(this);
self.id = id;
self.title = title;
self.description = description;
self.icon = icon;
self.unlocked = false;
self.displayTime = 0;
// Create achievement notification display
var bg = LK.getAsset('achievement_bg', {
width: 600,
height: 120,
color: 0x2C3E50,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(bg);
var titleText = new Text2(title, {
size: 40,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0);
titleText.x = 0;
titleText.y = -30;
self.addChild(titleText);
var descText = new Text2(description, {
size: 30,
fill: 0xFFFFFF
});
descText.anchor.set(0.5, 1);
descText.x = 0;
descText.y = 30;
self.addChild(descText);
self.unlock = function () {
if (!self.unlocked) {
self.unlocked = true;
self.show();
// Save to storage
storage['achievement_' + self.id] = true;
}
};
self.show = function () {
self.x = 1024;
self.y = 200;
self.alpha = 0;
self.scaleX = 0.5;
self.scaleY = 0.5;
self.displayTime = 180; // 3 seconds
// Animate in
tween(self, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500
});
};
self.update = function () {
if (self.displayTime > 0) {
self.displayTime--;
if (self.displayTime <= 0) {
// Animate out
tween(self, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 500
});
}
}
};
return self;
});
var Dragon = Container.expand(function () {
var self = Container.call(this);
var dragonGraphics = self.attachAsset('dragon', {
anchorX: 0.5,
anchorY: 0.5
});
self.baseSpeed = 1.5;
self.currentSpeed = self.baseSpeed;
self.fireTimer = 0;
self.fireInterval = 120; // 2 seconds initially
self.health = 150;
self.maxHealth = 150;
// Create health bar
var dragonHealthBarBg = self.attachAsset('dragon_health_bar_bg', {
anchorX: 0.5,
anchorY: 0.5
});
dragonHealthBarBg.y = -180;
var dragonHealthBarFill = self.attachAsset('dragon_health_bar_fill', {
anchorX: 0,
anchorY: 0.5
});
dragonHealthBarFill.x = -60;
dragonHealthBarFill.y = -180;
self.update = function () {
// Follow player
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.currentSpeed;
self.y += dy / distance * self.currentSpeed;
}
// Fire breathing timer
self.fireTimer++;
if (self.fireTimer >= self.fireInterval) {
self.breatheFire();
self.fireTimer = 0;
}
// Adjust difficulty based on score
var difficulty = Math.floor(score / 10);
self.currentSpeed = self.baseSpeed + difficulty * 0.5;
self.fireInterval = Math.max(60, 120 - difficulty * 10);
// Update health bar
var healthPercent = self.health / self.maxHealth;
dragonHealthBarFill.width = 120 * healthPercent;
if (healthPercent > 0.6) {
dragonHealthBarFill.tint = 0xFF0000; // Red
} else if (healthPercent > 0.3) {
dragonHealthBarFill.tint = 0xFF4444; // Light Red
} else {
dragonHealthBarFill.tint = 0xFF8888; // Very Light Red
}
};
self.breatheFire = function () {
var fireball = new Fireball();
fireball.x = self.x;
fireball.y = self.y;
// Aim towards player
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
fireball.directionX = dx / distance;
fireball.directionY = dy / distance;
fireballs.push(fireball);
game.addChild(fireball);
LK.getSound('fire').play();
};
self.takeDamage = function (damage) {
self.health = Math.max(0, self.health - damage);
LK.effects.flashObject(self, 0xFF0000, 300);
if (self.health <= 0) {
// Dragon is defeated but doesn't die
LK.effects.flashObject(self, 0x000000, 1000);
self.baseSpeed = 0.5; // Reduced speed but still moves
self.currentSpeed = 0.5;
self.fireInterval = 999999; // Stop firing
}
};
return self;
});
var Fireball = Container.expand(function () {
var self = Container.call(this);
var fireballGraphics = self.attachAsset('fireball', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.directionX = 0;
self.directionY = 0;
self.lifetime = 300; // 5 seconds
self.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
fireballGraphics.rotation += 0.2;
fireballGraphics.alpha = Math.sin(LK.ticks * 0.5) * 0.3 + 0.7;
self.lifetime--;
if (self.lifetime <= 0) {
self.shouldDestroy = true;
}
// Check bounds
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.shouldDestroy = true;
}
};
return self;
});
var Gem = Container.expand(function () {
var self = Container.call(this);
var gemGraphics = self.attachAsset('gem', {
anchorX: 0.5,
anchorY: 0.5
});
self.points = 25;
self.floatOffset = Math.random() * Math.PI * 2;
self.update = function () {
gemGraphics.y = Math.sin(LK.ticks * 0.15 + self.floatOffset) * 3;
gemGraphics.rotation += 0.05;
gemGraphics.scaleX = Math.sin(LK.ticks * 0.1 + self.floatOffset) * 0.2 + 1;
gemGraphics.scaleY = Math.sin(LK.ticks * 0.1 + self.floatOffset) * 0.2 + 1;
};
return self;
});
var Luigi = Container.expand(function () {
var self = Container.call(this);
var luigiGraphics = self.attachAsset('luigi', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.jumpTimer = 0;
self.jumpInterval = 120; // 2 seconds
self.isJumping = false;
self.jumpHeight = 0;
self.maxJumpHeight = 100;
self.jumpSpeed = 8;
self.direction = 1; // 1 for right, -1 for left
self.update = function () {
// Move horizontally
self.x += self.speed * self.direction;
// Bounce off screen edges
if (self.x <= 100 || self.x >= 1948) {
self.direction *= -1;
luigiGraphics.scaleX = self.direction;
}
// Jump behavior
self.jumpTimer++;
if (self.jumpTimer >= self.jumpInterval && !self.isJumping) {
self.isJumping = true;
self.jumpHeight = 0;
self.jumpTimer = 0;
}
if (self.isJumping) {
self.jumpHeight += self.jumpSpeed;
if (self.jumpHeight >= self.maxJumpHeight) {
self.jumpSpeed = -8;
}
if (self.jumpHeight <= 0 && self.jumpSpeed < 0) {
self.jumpHeight = 0;
self.jumpSpeed = 8;
self.isJumping = false;
}
luigiGraphics.y = -self.jumpHeight;
}
// Check if player is close and doesn't have weapon
if (!playerWeapon) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 100) {
// Give weapon to player
playerWeapon = new Weapon();
playerWeapon.x = player.x;
playerWeapon.y = player.y - 50;
game.addChild(playerWeapon);
LK.effects.flashObject(self, 0x00FF00, 500);
updateScore(100); // Bonus for getting weapon
}
}
// Animate Luigi
luigiGraphics.rotation = Math.sin(LK.ticks * 0.1) * 0.1;
};
return self;
});
var Mario = Container.expand(function () {
var self = Container.call(this);
var marioGraphics = self.attachAsset('mario', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.targetSpaghetti = null;
self.searchTimer = 0;
self.searchInterval = 60; // 1 second
self.isAngry = false;
self.angerTimer = 0;
self.angerDuration = 300; // 5 seconds of anger
self.attackSpeed = 6;
self.health = 100;
self.maxHealth = 100;
// Create health bar
var healthBarBg = self.attachAsset('health_bar_bg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.y = -120;
var healthBarFill = self.attachAsset('health_bar_fill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -40;
healthBarFill.y = -120;
self.update = function () {
// Search for nearest spaghetti
self.searchTimer++;
if (self.searchTimer >= self.searchInterval) {
self.findNearestSpaghetti();
self.searchTimer = 0;
}
// Handle anger state
if (self.isAngry) {
self.angerTimer--;
if (self.angerTimer <= 0) {
self.isAngry = false;
marioGraphics.tint = 0xFFFFFF; // Return to normal color
} else {
// Attack player when angry
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.attackSpeed;
self.y += dy / distance * self.attackSpeed;
// Face the direction of movement
marioGraphics.scaleX = dx > 0 ? 1 : -1;
}
}
} else {
// Move towards target spaghetti
if (self.targetSpaghetti && spaghettis.indexOf(self.targetSpaghetti) !== -1) {
var dx = self.targetSpaghetti.x - self.x;
var dy = self.targetSpaghetti.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
// Face the direction of movement
marioGraphics.scaleX = dx > 0 ? 1 : -1;
}
} else {
// Random movement when no spaghetti found
self.x += (Math.random() - 0.5) * 2;
self.y += (Math.random() - 0.5) * 2;
}
}
// Keep Mario in bounds
self.x = Math.max(100, Math.min(1948, self.x));
self.y = Math.max(100, Math.min(2632, self.y));
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBarFill.width = 80 * healthPercent;
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00FF00; // Green
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xFFFF00; // Yellow
} else {
healthBarFill.tint = 0xFF0000; // Red
}
// Animate Mario
marioGraphics.rotation = Math.sin(LK.ticks * 0.1) * 0.05;
};
self.findNearestSpaghetti = function () {
var nearestDistance = Infinity;
var nearestSpaghetti = null;
for (var i = 0; i < spaghettis.length; i++) {
var spaghetti = spaghettis[i];
var dx = spaghetti.x - self.x;
var dy = spaghetti.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestSpaghetti = spaghetti;
}
}
self.targetSpaghetti = nearestSpaghetti;
};
self.becomeAngry = function () {
self.isAngry = true;
self.angerTimer = self.angerDuration;
marioGraphics.tint = 0xFF0000; // Turn red when angry
LK.effects.flashObject(self, 0xFF0000, 500);
};
self.takeDamage = function (damage) {
self.health = Math.max(0, self.health - damage);
LK.effects.flashObject(self, 0xFF0000, 300);
if (self.health <= 0) {
// Mario is defeated and disappears
LK.effects.flashObject(self, 0x000000, 1000);
self.shouldDestroy = true; // Mark Mario for destruction
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.invulnerable = false;
self.invulnerabilityTime = 0;
self.speedBoost = false;
self.speedBoostTime = 0;
self.update = function () {
if (self.invulnerabilityTime > 0) {
self.invulnerabilityTime--;
playerGraphics.alpha = Math.sin(LK.ticks * 0.3) * 0.5 + 0.5;
if (self.invulnerabilityTime <= 0) {
self.invulnerable = false;
playerGraphics.alpha = 1;
}
}
if (self.speedBoostTime > 0) {
self.speedBoostTime--;
if (self.speedBoostTime <= 0) {
self.speedBoost = false;
playerGraphics.tint = 0xFFFFFF;
}
}
};
self.activateInvulnerability = function () {
self.invulnerable = true;
self.invulnerabilityTime = 120; // 2 seconds at 60fps
};
self.activateSpeedBoost = function () {
self.speedBoost = true;
self.speedBoostTime = 300; // 5 seconds at 60fps
playerGraphics.tint = 0x00FFFF;
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = Math.random() < 0.5 ? 'speed' : 'invulnerable';
self.floatOffset = Math.random() * Math.PI * 2;
if (self.type === 'invulnerable') {
powerupGraphics.tint = 0xFFD700;
}
self.update = function () {
powerupGraphics.y = Math.sin(LK.ticks * 0.2 + self.floatOffset) * 8;
powerupGraphics.rotation += 0.1;
powerupGraphics.alpha = Math.sin(LK.ticks * 0.3) * 0.3 + 0.7;
};
return self;
});
var Sonic = Container.expand(function () {
var self = Container.call(this);
var sonicGraphics = self.attachAsset('sonic', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.targetMario = null;
self.searchTimer = 0;
self.searchInterval = 30; // 0.5 seconds
self.attackTimer = 0;
self.attackInterval = 120; // 2 seconds
self.spindashTimer = 0;
self.isSpinning = false;
self.direction = 1;
self.goldTimer = 30 * 60; // 30 seconds at 60fps
self.goldCollected = 0;
self.goldTarget = 3;
self.isAttackingPlayer = false;
self.targetPlayer = null;
self.health = 80;
self.maxHealth = 80;
// Create health bar
var healthBarBg = self.attachAsset('health_bar_bg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.y = -120;
var healthBarFill = self.attachAsset('health_bar_fill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -40;
healthBarFill.y = -120;
self.update = function () {
// Update gold collection timer
self.goldTimer--;
if (self.goldTimer <= 0 && self.goldCollected < self.goldTarget) {
self.isAttackingPlayer = true;
self.targetPlayer = player;
sonicGraphics.tint = 0xFF0000; // Turn red when attacking player
}
// Behavior based on current state
if (self.isAttackingPlayer && self.targetPlayer) {
// Attack player mode
var dx = self.targetPlayer.x - self.x;
var dy = self.targetPlayer.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * (self.speed * 1.5); // Faster when attacking player
self.y += dy / distance * (self.speed * 1.5);
sonicGraphics.scaleX = dx > 0 ? 1 : -1;
self.direction = dx > 0 ? 1 : -1;
}
} else if (self.goldCollected < self.goldTarget) {
// Gold collection mode - search for gems (gold)
self.searchTimer++;
if (self.searchTimer >= self.searchInterval) {
self.findNearestGem();
self.searchTimer = 0;
}
// Move towards nearest gem
if (self.targetGem && gems.indexOf(self.targetGem) !== -1) {
var dx = self.targetGem.x - self.x;
var dy = self.targetGem.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
sonicGraphics.scaleX = dx > 0 ? 1 : -1;
self.direction = dx > 0 ? 1 : -1;
}
} else {
// Search pattern movement
self.x += Math.sin(LK.ticks * 0.02) * 3;
self.y += Math.cos(LK.ticks * 0.015) * 2;
}
} else {
// Normal mode (after collecting enough gold) - but only attack Mario if dragon is alive
if (!dragon.isDead) {
self.searchTimer++;
if (self.searchTimer >= self.searchInterval) {
self.findMario();
self.searchTimer = 0;
}
// Move towards Mario for combat
if (self.targetMario && marios.indexOf(self.targetMario) !== -1) {
var dx = self.targetMario.x - self.x;
var dy = self.targetMario.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 80) {
// Move towards Mario
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
sonicGraphics.scaleX = dx > 0 ? 1 : -1;
self.direction = dx > 0 ? 1 : -1;
} else {
// Close enough to attack - start spindash
self.attackTimer++;
if (self.attackTimer >= self.attackInterval && !self.isSpinning) {
self.startSpindash();
self.attackTimer = 0;
}
}
} else {
// Search pattern movement
self.x += Math.sin(LK.ticks * 0.02) * 3;
self.y += Math.cos(LK.ticks * 0.015) * 2;
}
} else {
// Dragon is dead - peaceful mode, just wander around
self.x += Math.sin(LK.ticks * 0.01) * 2;
self.y += Math.cos(LK.ticks * 0.008) * 1.5;
}
}
// Handle spindash attack
if (self.isSpinning) {
self.spindashTimer++;
sonicGraphics.rotation += 0.5;
sonicGraphics.scaleX = Math.sin(LK.ticks * 0.3) * 0.2 + 0.8;
sonicGraphics.scaleY = Math.sin(LK.ticks * 0.3) * 0.2 + 0.8;
if (self.spindashTimer >= 60) {
self.isSpinning = false;
self.spindashTimer = 0;
sonicGraphics.rotation = 0;
sonicGraphics.scaleX = self.direction;
sonicGraphics.scaleY = 1;
}
}
// Keep Sonic in bounds
self.x = Math.max(100, Math.min(1948, self.x));
self.y = Math.max(100, Math.min(2632, self.y));
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBarFill.width = 80 * healthPercent;
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00FF00; // Green
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xFFFF00; // Yellow
} else {
healthBarFill.tint = 0xFF0000; // Red
}
// Idle animation when not spinning
if (!self.isSpinning) {
sonicGraphics.y = Math.sin(LK.ticks * 0.15) * 2;
}
};
self.findMario = function () {
var nearestDistance = Infinity;
var nearestMario = null;
for (var i = 0; i < marios.length; i++) {
var mario = marios[i];
var dx = mario.x - self.x;
var dy = mario.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestMario = mario;
}
}
self.targetMario = nearestMario;
};
self.findNearestGem = function () {
var nearestDistance = Infinity;
var nearestGem = null;
// First check for sonic gold (priority)
for (var i = 0; i < sonicGolds.length; i++) {
var sonicGold = sonicGolds[i];
var dx = sonicGold.x - self.x;
var dy = sonicGold.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestGem = sonicGold;
}
}
// Then check regular gems if no sonic gold found
if (!nearestGem) {
for (var i = 0; i < gems.length; i++) {
var gem = gems[i];
var dx = gem.x - self.x;
var dy = gem.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestGem = gem;
}
}
}
self.targetGem = nearestGem;
};
self.startSpindash = function () {
self.isSpinning = true;
self.spindashTimer = 0;
// Flash blue to indicate attack
LK.effects.flashObject(self, 0x0066FF, 200);
};
self.takeDamage = function (damage) {
self.health = Math.max(0, self.health - damage);
LK.effects.flashObject(self, 0xFF0000, 300);
if (self.health <= 0) {
// Sonic is defeated and disappears
LK.effects.flashObject(self, 0x000000, 1000);
self.shouldDestroy = true; // Mark Sonic for destruction
}
};
return self;
});
var SonicGold = Container.expand(function () {
var self = Container.call(this);
var sonicGoldGraphics = self.attachAsset('sonic_gold', {
anchorX: 0.5,
anchorY: 0.5
});
self.points = 30;
self.floatOffset = Math.random() * Math.PI * 2;
// Give it a special blue tint to distinguish from regular gems
sonicGoldGraphics.tint = 0x0066FF;
self.update = function () {
sonicGoldGraphics.y = Math.sin(LK.ticks * 0.18 + self.floatOffset) * 4;
sonicGoldGraphics.rotation += 0.08;
sonicGoldGraphics.scaleX = Math.sin(LK.ticks * 0.12 + self.floatOffset) * 0.25 + 1;
sonicGoldGraphics.scaleY = Math.sin(LK.ticks * 0.12 + self.floatOffset) * 0.25 + 1;
// Special pulsing effect
sonicGoldGraphics.alpha = Math.sin(LK.ticks * 0.2) * 0.3 + 0.7;
};
return self;
});
var Spaghetti = Container.expand(function () {
var self = Container.call(this);
var spaghettiGraphics = self.attachAsset('spaghetti', {
anchorX: 0.5,
anchorY: 0.5
});
self.points = 15;
self.floatOffset = Math.random() * Math.PI * 2;
self.update = function () {
spaghettiGraphics.y = Math.sin(LK.ticks * 0.12 + self.floatOffset) * 4;
spaghettiGraphics.rotation += 0.03;
spaghettiGraphics.scaleX = Math.sin(LK.ticks * 0.08 + self.floatOffset) * 0.1 + 1;
spaghettiGraphics.scaleY = Math.sin(LK.ticks * 0.08 + self.floatOffset) * 0.1 + 1;
};
return self;
});
var Treasure = Container.expand(function () {
var self = Container.call(this);
var treasureGraphics = self.attachAsset('treasure', {
anchorX: 0.5,
anchorY: 0.5
});
self.points = 10;
self.floatOffset = Math.random() * Math.PI * 2;
self.update = function () {
treasureGraphics.y = Math.sin(LK.ticks * 0.1 + self.floatOffset) * 5;
treasureGraphics.rotation += 0.02;
};
return self;
});
var Weapon = Container.expand(function () {
var self = Container.call(this);
var weaponGraphics = self.attachAsset('weapon', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 25;
self.cooldown = 0;
self.maxCooldown = 30; // 0.5 seconds
self.range = 150;
self.isActive = false;
self.update = function () {
if (self.cooldown > 0) {
self.cooldown--;
}
// Rotate weapon around player
weaponGraphics.rotation += 0.2;
// Pulse effect when ready to use
if (self.cooldown <= 0) {
weaponGraphics.alpha = Math.sin(LK.ticks * 0.3) * 0.3 + 0.7;
weaponGraphics.scaleX = Math.sin(LK.ticks * 0.2) * 0.2 + 1;
weaponGraphics.scaleY = Math.sin(LK.ticks * 0.2) * 0.2 + 1;
} else {
weaponGraphics.alpha = 0.5;
weaponGraphics.scaleX = 0.8;
weaponGraphics.scaleY = 0.8;
}
};
self.attack = function (target) {
if (self.cooldown <= 0) {
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.range) {
target.takeDamage(self.damage);
self.cooldown = self.maxCooldown;
LK.effects.flashObject(self, 0xFFFFFF, 300);
LK.getSound('weapon_hit').play();
return true;
}
}
return false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x8B4513
});
/****
* Game Code
****/
game.setBackgroundColor(0xDEB887);
// Game variables
var score = 0;
var health = 250;
var maxHealth = 250;
var gameTimer = 80 * 60; // 1 minute 20 seconds (80 seconds) at 60fps
var treasures = [];
var gems = [];
var powerups = [];
var fireballs = [];
var luigis = [];
var marios = [];
var spaghettis = [];
var sonics = [];
var sonicGolds = [];
var treasureSpawnTimer = 0;
var gemSpawnTimer = 0;
var powerupSpawnTimer = 0;
var luigiSpawnTimer = 0;
var spaghettiSpawnTimer = 0;
var sonicGoldSpawnTimer = 0;
var sonicGoldSpawnCount = 0;
var maxSonicGoldSpawns = 3;
var playerWeapon = null;
// Achievement system
var achievements = [];
var activeAchievements = [];
var achievementStats = {
treasuresCollected: 0,
gemsCollected: 0,
powerupsCollected: 0,
dragonHits: 0,
fireballsAvoided: 0,
survivalTime: 0,
maxScore: 0
};
// Initialize achievements
function initializeAchievements() {
achievements = [new Achievement('first_treasure', 'First Treasure!', 'Collect your first treasure', 'đ°'), new Achievement('treasure_hunter', 'Treasure Hunter', 'Collect 10 treasures', 'đ'), new Achievement('gem_collector', 'Gem Collector', 'Collect 5 gems', 'đ'), new Achievement('survivor', 'Survivor', 'Survive for 30 seconds', 'â°'), new Achievement('speed_demon', 'Speed Demon', 'Use 3 speed power-ups', 'âĄ'), new Achievement('invincible', 'Invincible', 'Use 3 invulnerability power-ups', 'đĄď¸'), new Achievement('score_master', 'Score Master', 'Reach 200 points', 'đŻ'), new Achievement('dragon_dancer', 'Dragon Dancer', 'Get hit by dragon 5 times and survive', 'đ'), new Achievement('high_scorer', 'High Scorer', 'Reach 300 points', 'đ'), new Achievement('ultimate_survivor', 'Ultimate Survivor', 'Survive for 60 seconds', 'đ')];
// Load unlocked achievements from storage
for (var i = 0; i < achievements.length; i++) {
var achievement = achievements[i];
if (storage['achievement_' + achievement.id]) {
achievement.unlocked = true;
}
}
}
function checkAchievements() {
for (var i = 0; i < achievements.length; i++) {
var achievement = achievements[i];
if (!achievement.unlocked) {
var shouldUnlock = false;
switch (achievement.id) {
case 'first_treasure':
shouldUnlock = achievementStats.treasuresCollected >= 1;
break;
case 'treasure_hunter':
shouldUnlock = achievementStats.treasuresCollected >= 10;
break;
case 'gem_collector':
shouldUnlock = achievementStats.gemsCollected >= 5;
break;
case 'survivor':
shouldUnlock = achievementStats.survivalTime >= 30 * 60;
break;
case 'speed_demon':
shouldUnlock = achievementStats.powerupsCollected >= 3;
break;
case 'invincible':
shouldUnlock = achievementStats.powerupsCollected >= 3;
break;
case 'score_master':
shouldUnlock = score >= 200;
break;
case 'dragon_dancer':
shouldUnlock = achievementStats.dragonHits >= 5;
break;
case 'high_scorer':
shouldUnlock = score >= 300;
break;
case 'ultimate_survivor':
shouldUnlock = achievementStats.survivalTime >= 60 * 60;
break;
}
if (shouldUnlock) {
achievement.unlock();
activeAchievements.push(achievement);
game.addChild(achievement);
}
}
}
}
// Initialize achievements
initializeAchievements();
// Create player
var player = game.addChild(new Player());
player.x = 1024;
player.y = 1366;
// Create dragon
var dragon = game.addChild(new Dragon());
dragon.x = 200;
dragon.y = 200;
// Create Luigi monster
var luigi = game.addChild(new Luigi());
luigi.x = 1500;
luigi.y = 1000;
luigis.push(luigi);
// Create Mario character
var mario = game.addChild(new Mario());
mario.x = 300;
mario.y = 800;
marios.push(mario);
// Create Sonic character
var sonic = game.addChild(new Sonic());
sonic.x = 1700;
sonic.y = 600;
sonics.push(sonic);
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var healthTxt = new Text2('Health: 250/250', {
size: 60,
fill: 0x00FF00
});
healthTxt.anchor.set(0, 0);
healthTxt.x = 120;
healthTxt.y = 20;
LK.gui.addChild(healthTxt);
var timerTxt = new Text2('Time: 1:20', {
size: 70,
fill: 0xFFFFFF
});
timerTxt.anchor.set(1, 0);
timerTxt.x = 2048 - 20;
timerTxt.y = 20;
LK.gui.addChild(timerTxt);
var instructionTxt = new Text2('Tap to move! Get close to Luigi for weapon to defeat dragon!', {
size: 50,
fill: 0xFFFFFF
});
instructionTxt.anchor.set(0.5, 1);
LK.gui.bottom.addChild(instructionTxt);
// Game logic functions
function spawnTreasure() {
var treasure = new Treasure();
treasure.x = Math.random() * 1800 + 124;
treasure.y = Math.random() * 2400 + 166;
treasures.push(treasure);
game.addChild(treasure);
}
function spawnGem() {
var gem = new Gem();
gem.x = Math.random() * 1800 + 124;
gem.y = Math.random() * 2400 + 166;
gems.push(gem);
game.addChild(gem);
}
function spawnPowerUp() {
var powerup = new PowerUp();
powerup.x = Math.random() * 1800 + 124;
powerup.y = Math.random() * 2400 + 166;
powerups.push(powerup);
game.addChild(powerup);
}
function spawnSpaghetti() {
var spaghetti = new Spaghetti();
spaghetti.x = Math.random() * 1800 + 124;
spaghetti.y = Math.random() * 2400 + 166;
spaghettis.push(spaghetti);
game.addChild(spaghetti);
}
function spawnSonicGold() {
if (sonicGoldSpawnCount < maxSonicGoldSpawns) {
var sonicGold = new SonicGold();
sonicGold.x = Math.random() * 1800 + 124;
sonicGold.y = Math.random() * 2400 + 166;
sonicGolds.push(sonicGold);
game.addChild(sonicGold);
}
}
function updateScore(points) {
score += points;
scoreTxt.setText('Score: ' + score);
LK.setScore(score);
}
function updateHealth(damage) {
health = Math.max(0, health - damage);
healthTxt.setText('Health: ' + health + '/' + maxHealth);
// Change color based on health percentage
var healthPercent = health / maxHealth;
if (healthPercent > 0.6) {
healthTxt.fill = 0x00FF00; // Green
} else if (healthPercent > 0.3) {
healthTxt.fill = 0xFFFF00; // Yellow
} else {
healthTxt.fill = 0xFF0000; // Red
}
if (health <= 0) {
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
}
}
function updateTimer() {
gameTimer = Math.max(0, gameTimer - 1);
var totalSeconds = Math.floor(gameTimer / 60);
var minutes = Math.floor(totalSeconds / 60);
var seconds = totalSeconds % 60;
var timeText = minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
timerTxt.setText('Time: ' + timeText);
// Change color based on remaining time
if (gameTimer <= 10 * 60) {
// Last 10 seconds
timerTxt.fill = 0xFF0000; // Red
} else if (gameTimer <= 30 * 60) {
// Last 30 seconds
timerTxt.fill = 0xFFFF00; // Yellow
} else {
timerTxt.fill = 0xFFFFFF; // White
}
if (gameTimer <= 0) {
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
}
}
// Touch controls
var targetX = player.x;
var targetY = player.y;
game.down = function (x, y, obj) {
targetX = x;
targetY = y;
// Attack dragon with weapon if player has one
if (playerWeapon) {
playerWeapon.attack(dragon);
// Also attack any Mario in range
for (var i = 0; i < marios.length; i++) {
var mario = marios[i];
if (mario.health > 0) {
playerWeapon.attack(mario);
}
}
// Also attack any Sonic in range
for (var i = 0; i < sonics.length; i++) {
var sonic = sonics[i];
playerWeapon.attack(sonic);
}
}
};
game.move = function (x, y, obj) {
targetX = x;
targetY = y;
};
// Game update loop
game.update = function () {
// Update timer
updateTimer();
// Move player towards target
var dx = targetX - player.x;
var dy = targetY - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
var speed = player.speedBoost ? player.speed * 1.5 : player.speed;
player.x += dx / distance * speed;
player.y += dy / distance * speed;
}
// Keep player in bounds
player.x = Math.max(40, Math.min(2008, player.x));
player.y = Math.max(40, Math.min(2692, player.y));
// Update weapon position if player has one
if (playerWeapon) {
playerWeapon.x = player.x;
playerWeapon.y = player.y - 50;
// Auto-attack dragon when in range
var dx = dragon.x - playerWeapon.x;
var dy = dragon.y - playerWeapon.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= playerWeapon.range) {
playerWeapon.attack(dragon);
}
// Auto-attack Mario when in range
for (var i = 0; i < marios.length; i++) {
var mario = marios[i];
if (mario.health > 0) {
var dx = mario.x - playerWeapon.x;
var dy = mario.y - playerWeapon.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= playerWeapon.range) {
playerWeapon.attack(mario);
}
}
}
// Auto-attack Sonic when in range
for (var i = 0; i < sonics.length; i++) {
var sonic = sonics[i];
var dx = sonic.x - playerWeapon.x;
var dy = sonic.y - playerWeapon.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= playerWeapon.range) {
playerWeapon.attack(sonic);
}
}
}
// Spawn treasures
treasureSpawnTimer++;
if (treasureSpawnTimer >= 90) {
// Every 1.5 seconds
spawnTreasure();
treasureSpawnTimer = 0;
}
// Spawn gems
gemSpawnTimer++;
if (gemSpawnTimer >= 180) {
// Every 3 seconds
spawnGem();
gemSpawnTimer = 0;
}
// Spawn power-ups
powerupSpawnTimer++;
if (powerupSpawnTimer >= 600) {
// Every 10 seconds
spawnPowerUp();
powerupSpawnTimer = 0;
}
// Spawn spaghetti
spaghettiSpawnTimer++;
if (spaghettiSpawnTimer >= 120) {
// Every 2 seconds
spawnSpaghetti();
spaghettiSpawnTimer = 0;
}
// Spawn sonic gold
sonicGoldSpawnTimer++;
if (sonicGoldSpawnTimer >= 240 && sonicGoldSpawnCount < maxSonicGoldSpawns) {
// Every 4 seconds, but only up to maximum allowed
spawnSonicGold();
sonicGoldSpawnTimer = 0;
sonicGoldSpawnCount++;
}
// Check treasure collection
for (var i = treasures.length - 1; i >= 0; i--) {
var treasure = treasures[i];
if (player.intersects(treasure)) {
updateScore(treasure.points);
achievementStats.treasuresCollected++;
LK.effects.flashObject(treasure, 0xFFFFFF, 300);
treasure.destroy();
treasures.splice(i, 1);
LK.getSound('collect').play();
}
}
// Check gem collection
for (var i = gems.length - 1; i >= 0; i--) {
var gem = gems[i];
if (player.intersects(gem)) {
updateScore(gem.points);
achievementStats.gemsCollected++;
LK.effects.flashObject(gem, 0xFFFFFF, 300);
gem.destroy();
gems.splice(i, 1);
LK.getSound('collect').play();
}
}
// Check power-up collection
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
if (player.intersects(powerup)) {
if (powerup.type === 'speed') {
player.activateSpeedBoost();
} else if (powerup.type === 'invulnerable') {
player.activateInvulnerability();
}
achievementStats.powerupsCollected++;
LK.effects.flashObject(powerup, 0xFFFFFF, 300);
powerup.destroy();
powerups.splice(i, 1);
LK.getSound('powerup').play();
}
}
// Check Mario spaghetti collection
for (var i = marios.length - 1; i >= 0; i--) {
var mario = marios[i];
for (var j = spaghettis.length - 1; j >= 0; j--) {
var spaghetti = spaghettis[j];
if (mario.intersects(spaghetti)) {
updateScore(spaghetti.points);
LK.effects.flashObject(spaghetti, 0xFFD700, 300);
spaghetti.destroy();
spaghettis.splice(j, 1);
LK.getSound('collect').play();
break;
}
}
}
// Check player spaghetti collection
for (var i = spaghettis.length - 1; i >= 0; i--) {
var spaghetti = spaghettis[i];
if (player.intersects(spaghetti)) {
updateScore(spaghetti.points);
LK.effects.flashObject(spaghetti, 0xFFD700, 300);
// Make all Marios angry when player steals spaghetti
for (var j = 0; j < marios.length; j++) {
var mario = marios[j];
if (!mario.isAngry) {
mario.becomeAngry();
}
}
spaghetti.destroy();
spaghettis.splice(i, 1);
LK.getSound('collect').play();
}
}
// Check if dragon is defeated and should disappear
if (dragon.health <= 0 && !dragon.isDead) {
dragon.isDead = true;
// Make all Marios angry when dragon is defeated
for (var j = 0; j < marios.length; j++) {
var mario = marios[j];
if (!mario.isAngry) {
mario.becomeAngry();
}
}
// Sonics remain in the game when dragon is defeated - they will no longer attack Mario
for (var k = 0; k < sonics.length; k++) {
var sonic = sonics[k];
sonic.isAttackingPlayer = false; // Stop attacking player
sonic.targetMario = null; // Clear Mario target
sonic.targetPlayer = null; // Clear player target
sonicGraphics = sonic.children[0]; // Get sonic graphics
if (sonicGraphics) {
sonicGraphics.tint = 0xFFFFFF; // Return to normal color
}
}
LK.effects.flashScreen(0x00FF00, 1000); // Green flash for dragon defeat
updateScore(200); // Bonus points for defeating dragon
// Dragon disappears from the game
dragon.destroy();
}
// Check if any Mario has died and make Sonics attack player
for (var i = marios.length - 1; i >= 0; i--) {
var mario = marios[i];
if (mario.shouldDestroy) {
// Mario is dead, make all Sonics attack player
for (var j = 0; j < sonics.length; j++) {
var sonic = sonics[j];
sonic.isAttackingPlayer = true;
sonic.targetPlayer = player;
var sonicGraphics = sonic.children[0];
if (sonicGraphics) {
sonicGraphics.tint = 0xFF0000; // Turn red when attacking player
}
}
// Remove Mario from game
mario.destroy();
marios.splice(i, 1);
}
}
// Check if any Sonic has died and remove them
for (var i = sonics.length - 1; i >= 0; i--) {
var sonic = sonics[i];
if (sonic.shouldDestroy) {
// Sonic died - game over
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
// Remove Sonic from game
sonic.destroy();
sonics.splice(i, 1);
}
}
// Game continues until timer runs out or player dies
// Check dragon collision
if (!player.invulnerable && player.intersects(dragon)) {
if (!player.lastDragonHit) {
updateHealth(50);
achievementStats.dragonHits++;
player.activateInvulnerability();
LK.effects.flashScreen(0xFF0000, 500);
player.lastDragonHit = true;
}
} else {
player.lastDragonHit = false;
}
// Check fireball collisions
for (var i = fireballs.length - 1; i >= 0; i--) {
var fireball = fireballs[i];
if (fireball.shouldDestroy) {
fireball.destroy();
fireballs.splice(i, 1);
continue;
}
if (!player.invulnerable && player.intersects(fireball)) {
if (!fireball.hasHit) {
updateHealth(25);
player.activateInvulnerability();
LK.effects.flashScreen(0xFF0000, 300);
fireball.hasHit = true;
fireball.shouldDestroy = true;
}
}
}
// Check Luigi collisions
for (var i = luigis.length - 1; i >= 0; i--) {
var luigi = luigis[i];
if (!player.invulnerable && player.intersects(luigi)) {
if (!luigi.lastHit) {
updateHealth(30);
player.activateInvulnerability();
LK.effects.flashScreen(0x00FF00, 400);
luigi.lastHit = true;
}
} else {
luigi.lastHit = false;
}
}
// Check angry Mario attacking player
for (var i = marios.length - 1; i >= 0; i--) {
var mario = marios[i];
if (mario.isAngry && !player.invulnerable && player.intersects(mario)) {
if (!mario.lastPlayerHit) {
updateHealth(40);
player.activateInvulnerability();
LK.effects.flashScreen(0xFF0000, 400);
mario.lastPlayerHit = true;
}
} else {
mario.lastPlayerHit = false;
}
}
// Check sonic gold collection (special gold for Sonic)
for (var i = sonicGolds.length - 1; i >= 0; i--) {
var sonicGold = sonicGolds[i];
if (player.intersects(sonicGold)) {
// Check if any Sonic needs gold
var goldGivenToSonic = false;
for (var j = 0; j < sonics.length; j++) {
var sonic = sonics[j];
if (sonic.goldCollected < sonic.goldTarget) {
sonic.goldCollected++;
goldGivenToSonic = true;
LK.effects.flashObject(sonic, 0x0066FF, 500); // Flash Sonic to show he received gold
break;
}
}
if (goldGivenToSonic) {
updateScore(sonicGold.points);
achievementStats.gemsCollected++;
LK.effects.flashObject(sonicGold, 0x0066FF, 300); // Blue flash for Sonic gold
sonicGold.destroy();
sonicGolds.splice(i, 1);
LK.getSound('collect').play();
} else {
// Normal collection if no Sonic needs gold
updateScore(sonicGold.points);
achievementStats.gemsCollected++;
LK.effects.flashObject(sonicGold, 0xFFD700, 300);
sonicGold.destroy();
sonicGolds.splice(i, 1);
LK.getSound('collect').play();
}
}
}
// Check if player collects gems for Sonic
for (var i = gems.length - 1; i >= 0; i--) {
var gem = gems[i];
if (player.intersects(gem)) {
// Check if any Sonic needs gold
var goldGivenToSonic = false;
for (var j = 0; j < sonics.length; j++) {
var sonic = sonics[j];
if (sonic.goldCollected < sonic.goldTarget) {
sonic.goldCollected++;
goldGivenToSonic = true;
LK.effects.flashObject(sonic, 0x0066FF, 300); // Flash Sonic to show he received gold
break;
}
}
if (goldGivenToSonic) {
updateScore(gem.points);
achievementStats.gemsCollected++;
LK.effects.flashObject(gem, 0xFFD700, 300); // Gold flash for Sonic collection
gem.destroy();
gems.splice(i, 1);
LK.getSound('collect').play();
} else {
// Normal gem collection if no Sonic needs gold
updateScore(gem.points);
achievementStats.gemsCollected++;
LK.effects.flashObject(gem, 0xFFFFFF, 300);
gem.destroy();
gems.splice(i, 1);
LK.getSound('collect').play();
}
}
}
// Check Sonic attacking player
for (var i = sonics.length - 1; i >= 0; i--) {
var sonic = sonics[i];
if (sonic.isAttackingPlayer && !player.invulnerable && player.intersects(sonic)) {
if (!sonic.lastPlayerHit) {
updateHealth(35);
player.activateInvulnerability();
LK.effects.flashScreen(0x0066FF, 400);
sonic.lastPlayerHit = true;
}
} else {
sonic.lastPlayerHit = false;
}
}
// Check Sonic vs Mario combat
for (var i = sonics.length - 1; i >= 0; i--) {
var sonic = sonics[i];
for (var j = marios.length - 1; j >= 0; j--) {
var mario = marios[j];
if (sonic.intersects(mario)) {
if (!sonic.lastMarioHit && sonic.isSpinning) {
// Sonic hits Mario during spindash
mario.takeDamage(20);
LK.effects.flashObject(mario, 0xFF0000, 500);
LK.effects.flashObject(sonic, 0x0066FF, 300);
updateScore(50); // Bonus points for combat
sonic.lastMarioHit = true;
} else if (!mario.lastSonicHit && !sonic.isSpinning) {
// Mario hits Sonic when not spinning
LK.effects.flashObject(sonic, 0xFF0000, 500);
LK.effects.flashObject(mario, 0x00FF00, 300);
updateScore(30); // Bonus points for combat
mario.lastSonicHit = true;
}
} else {
sonic.lastMarioHit = false;
mario.lastSonicHit = false;
}
}
}
// Spawn additional Luigis based on score
luigiSpawnTimer++;
if (luigiSpawnTimer >= 1800 && score >= 100) {
// Every 30 seconds after score 100
var newLuigi = new Luigi();
newLuigi.x = Math.random() * 1800 + 124;
newLuigi.y = Math.random() * 2000 + 500;
luigis.push(newLuigi);
game.addChild(newLuigi);
luigiSpawnTimer = 0;
}
// Update achievement stats
achievementStats.survivalTime++;
if (score > achievementStats.maxScore) {
achievementStats.maxScore = score;
}
// Check for new achievements
checkAchievements();
// Update active achievement displays
for (var i = activeAchievements.length - 1; i >= 0; i--) {
var achievement = activeAchievements[i];
if (achievement.displayTime <= 0 && achievement.alpha <= 0) {
achievement.destroy();
activeAchievements.splice(i, 1);
}
}
// Win condition
if (score >= 500) {
LK.showYouWin();
}
};
// Start background music
LK.playMusic('desert');
// Initial treasures
for (var i = 0; i < 3; i++) {
spawnTreasure();
}
// Initial spaghetti
for (var i = 0; i < 2; i++) {
spawnSpaghetti();
}
// Initial sonic gold - removed to respect 3 total limit
;
CREATE THIS IMAGE. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
MAKE THIS PICTURE LIKE A FIREBALL. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
DESIGN THIS PICTURE AS AN ENERGY DRINK. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
LUIĚGIĚ KARAKTERIĚNIĚ OLUŞTUR. In-Game asset. 2d. High contrast. No shadows
KORKAK BIĚR ADAM. In-Game asset. 2d. High contrast. No shadows
BUĚYUĚK BIĚR EJDER. In-Game asset. 2d. High contrast. No shadows
MARIĚO. In-Game asset. 2d. High contrast. No shadows
SPAGHETTIĚ. In-Game asset. 2d. High contrast. No shadows
sonic. In-Game asset. 2d. High contrast. No shadows
silah. In-Game asset. 2d. High contrast. No shadows
sonic altÄąnÄą. In-Game asset. 2d. High contrast. No shadows