User prompt
projectiles should record in a map which enemies it hit (by the enemy id) and the last tick they were hit. Enemies cannot get hit more than once every 10 ticks
Code edit (11 edits merged)
Please save this source code
User prompt
instead of generating a unique id, use an integer which is incremented
User prompt
Fix Bug: 'TypeError: LK.generateUniqueId is not a function' in this line: 'enemies.push(new BasicEnemy(self, x, y, {' Line Number: 704
User prompt
Fix Bug: 'TypeError: LK.generateUniqueId is not a function' in this line: 'enemies.push(new BasicEnemy(self, x, y, {' Line Number: 704
User prompt
add a unique id to enemies, passed through in the constructor args object
Code edit (6 edits merged)
Please save this source code
User prompt
Fix Bug: 'TypeError: t.setStageReference is not a function' in this line: 'parent.addChild(self);' Line Number: 138
Code edit (1 edits merged)
Please save this source code
Code edit (3 edits merged)
Please save this source code
User prompt
in the CrossWeapon's launch function, create additional projectiles based on the hero's "Extra" major boon. These additional projectiles should have their angle uniformally distributed in a radius around the hero
Code edit (3 edits merged)
Please save this source code
User prompt
make the screen flash red when a basicenemy deals damage to the hero
Code edit (4 edits merged)
Please save this source code
User prompt
Fix Bug: 'ReferenceError: updateButton is not defined' in this line: 'updateButton.visible = bool;' Line Number: 595
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (6 edits merged)
Please save this source code
User prompt
Fix Bug: 'TypeError: availableBoonsTxt.anchor is undefined' in this line: 'availableBoonsTxt.anchor.set(.5, .5);' Line Number: 389
Code edit (1 edits merged)
Please save this source code
Code edit (5 edits merged)
Please save this source code
User prompt
Fix Bug: 'ReferenceError: setting is not defined' in this line: 'if (setting.anchor) {' Line Number: 55
Code edit (2 edits merged)
Please save this source code
User prompt
Fix Bug: 'ReferenceError: setting is not defined' in this line: 'text.anchor.set(settings.anchor.x, setting.anchor.y);' Line Number: 51
function updateIteration(container, vars) {
for (var i = 0; i < container.length; i++) {
if (container[i].update(vars)) {
container.splice(i, 1);
i--;
}
}
}
var CountdownTimer = Container.expand(function (initialCountdown) {
var self = Container.call(this);
var countdown = initialCountdown;
var ticker = 60;
var countdownTxt = new BorderedText('', {
size: 60,
fill: '#ffffff',
font: 'bold monospace',
anchor: {
x: .5,
y: 0
}
});
self.addChild(countdownTxt);
adjustLabel();
function update() {
ticker -= 1;
if (ticker <= 0) {
ticker = 60;
countdown--;
if (countdown >= 0) {
adjustLabel();
}
}
return 1 - countdown / initialCountdown;
}
function adjustLabel() {
var minutes = Math.floor(countdown / 60);
var seconds = countdown % 60;
var minutesString = (minutes < 10 ? '0' : '') + minutes;
var secondsString = (seconds < 10 ? '0' : '') + seconds;
countdownTxt.setText(minutesString + ':' + secondsString);
}
});
var BorderedText = Container.expand(function (string, settings) {
var self = Container.call(this);
var textList = [];
var offsets = [[-2, -2], [-2, 2], [2, 2], [2, -2], [0, 0]];
for (var i = 0; i < offsets.length; i++) {
var text = new Text2(string, settings);
text.x += offsets[i][0];
text.y += offsets[i][1];
text.anchor.set(settings.anchor.x, settings.anchor.y);
textList.push(text);
self.addChild(text);
}
self.setText = function (string) {
for (var i = 0; i < textList.length; i++) {
textList[i].setText(string);
}
};
});
var ExperiencePickup = Container.expand(function (hero) {
var self = Container.call(this);
var pickupGraphics = self.createAsset('experiencePickup', 'Experience Pickup', .5, .5);
self.active = false;
self.move = function () {
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 400) {
self.active = true;
}
if (self.active) {
self.x += dx / distance * 20;
self.y += dy / distance * 20;
}
};
});
var CrucifixPickup = Container.expand(function () {
var self = Container.call(this);
var pickupGraphics = self.createAsset('crucifixPickup', 'Crucifix Pickup', .5, .5);
});
var HealingPickup = Container.expand(function (hero) {
var self = Container.call(this);
var pickupGraphics = self.createAsset('healingPickup', 'Healing Pickup', .5, .5);
var healPercentage = 0.1;
self.update = update;
function update() {
if (self.intersects(hero)) {
hero.health = Math.min(hero.healthMax, hero.health + hero.healthMax * healPercentage);
return true;
}
}
});
var BasicBloodSplatter = Container.expand(function () {
var self = Container.call(this);
var splatterGraphics = self.createAsset('bloodSplatter', 'Blood Splatter', .5, .5);
var initialLifetime = Math.floor(0.2 * 60);
var remainingLifetime = initialLifetime;
LK.setInterval(function () {
remainingLifetime -= 1;
var lifetime = remainingLifetime / initialLifetime;
splatterGraphics.alpha = lifetime;
splatterGraphics.scale = {
x: 2 - lifetime,
y: 2 - lifetime
};
if (remainingLifetime <= 0) {
self.destroy();
}
}, 0);
});
var BasicEnemy = Container.expand(function (difficultyScale) {
var self = Container.call(this);
var enemyGraphics = self.createAsset('enemy', 'Enemy character', .5, .5);
var damageDistance = 100;
var speed = 1.8;
var damage = 5;
var initialCooldown = 20;
var cooldown = initialCooldown;
var bobMagnitude = 2;
var bobPeriod = 10;
var creationTick = LK.ticks;
var health = 5 + 25 * difficultyScale;
self.update = function (vars) {
if (health <= 0) {}
var hero = vars.hero;
var projectiles = vars.projectiles;
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > damageDistance) {
self.x += dx / distance * speed;
self.y += dy / distance * speed + Math.sin((LK.ticks - creationTick) / bobPeriod) * bobMagnitude;
} else {
cooldown -= 1;
if (cooldown <= 0) {
cooldown = cooldownInitial;
hero.health -= damage;
}
}
for (var j = 0; j < projectiles.length; j++) {
var projectile = hero.projectiles[j];
if (self.intersects(projectile)) {
self.health -= 10 + 5 * hero.minorBoonLevels['Damage'];
if (self.health.health <= 0) {
if (crucifixPickups.length < 1 && Math.random() < Math.sqrt(hero.minorBoonLevels['Luck']) / 100) {
var crucifixPickup = new CrucifixPickup();
crucifixPickup.x = enemies[i].x;
crucifixPickup.y = enemies[i].y;
self.addChild(crucifixPickup);
crucifixPickups.push(crucifixPickup);
} else if (healthPickups.length < 5) {
for (var k = 0; k <= hero.minorBoonLevels['Luck']; k++) {
if (Math.random() < 0.05) {
var pickup = new HealingPickup();
pickup.x = enemies[i].x;
pickup.y = enemies[i].y;
self.addChild(pickup);
healthPickups.push(pickup);
break;
}
}
}
for (var k = 0; k <= hero.minorBoonLevels['Luck']; k++) {
if (Math.random() < 0.5) {
var experiencePickup = new ExperiencePickup(hero);
experiencePickup.x = enemies[i].x + Math.random() * 100 - 50;
experiencePickup.y = enemies[i].y + Math.random() * 100 - 50;
self.addChild(experiencePickup);
experiencePickups.push(experiencePickup);
break;
}
}
var bloodSplatter = new BloodSplatter();
bloodSplatter.x = enemies[i].x;
bloodSplatter.y = enemies[i].y;
self.addChild(bloodSplatter);
enemies[i].destroy();
enemies.splice(i, 1);
i--;
break;
}
}
}
};
});
var CrossProjectile = Container.expand(function (hero) {
var self = Container.call(this);
var bulletGraphics = self.createAsset('crossProjectile', 'Bullet Graphics', .5, .5);
self.speed = 30;
self.range = null;
self.destroyRange = -1200;
self.travelDistance = 200;
self.update = function () {
self.x += self.speed * Math.cos(self.angle);
self.y += self.speed * Math.sin(self.angle);
self.rotation += 0.2;
self.travelDistance -= Math.abs(self.speed);
if (self.travelDistance <= 0 && self.speed > -30) {
self.speed -= 1;
}
if ((self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) && self.travelDistance < -self.destroyRange || self.intersects(hero) && projectile.speed < 0) {
heroBullet.destroy();
hero.projectiles.splice(index, 1);
i--;
}
};
});
var CrossWeapon = Container.expand(function (hero) {
var self = Container.call(this);
self.initialCooldown = 120;
self.cooldown = self.initialCooldown;
self.launch = function () {
var bullet = new Bullet(hero);
var scale = 1 + 0.25 * hero.minorBoonLevels['Scale'];
var range = 200 + 50 * hero.minorBoonLevels['Range'];
var dx = hero.targetPos.x - hero.x;
var dy = hero.targetPos.y - hero.y;
bullet.x = hero.x;
bullet.y = hero.y;
bullet.angle = Math.atan2(dy, dx);
bullet.range = range;
bullet.initialRange = range;
hero.projectiles.push(bullet);
self.addChild(bullet);
};
self.update = function () {
if (self.cooldown > 0) {
self.cooldown--;
} else {
self.launch();
var attackSpeed = 1 + 0.25 * hero.minorBoonLevels['Rearm'];
self.cooldown = self.initialCooldown / attackSpeed;
}
};
});
var Hero = Container.expand(function () {
var self = Container.call(this);
var heroGraphics = self.createAsset('hero', 'Hero character', .5, .5);
self.minorBoonLevels = {
'Luck': 0,
'Scale': 0,
'Range': 0,
'Damage': 0,
'Rearm': 0
};
self.majorBoonLevels = {
'Split': 0,
'Multi': 0
};
self.shootPos = {
x: 2048 / 2,
y: 2732 / 2
};
self.weapons = [];
self.projectiles = [];
self.crossWeapon = new CrossWeapon(self);
self.weapons.push(self.crossWeapon);
self.addChild(self.crossWeapon);
self.health = 100;
self.healthBarBorder = self.createAsset('healthBarBorder', 'Health Bar Border', 0.5, 1);
self.healthBarBorder.y = -115;
self.healthBar = self.createAsset('healthBar', 'Health Bar', 0.5, 1);
self.healthBar.y = -120;
self.healthBarBorder.x = self.healthBar.x;
self.healthBarBorder.width = self.healthBar.width + 10;
self.healthBarBorder.height = self.healthBar.height + 10;
self.update = function (enemies) {
self.healthBar.scale.x = Math.max(0, self.health / self.healthMax);
if (self.health <= 0) {
LK.showGameOver();
}
if (self.targetPos) {
var dx = self.targetPos.x - self.x;
var dy = self.targetPos.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * 10;
self.y += dy / distance * 10;
} else {
self.x = self.targetPos.x;
self.y = self.targetPos.y;
self.targetPos = null;
}
}
if (self.shootPos.x < self.x) {
self.scale.x = -1;
} else {
self.scale.x = 1;
}
iterateContainer(self.weapons);
};
});
var BoonSelection = Container.expand(function (boons, type, count, callback) {
var self = Container.call(this);
var availableBoons = Object.keys(boons).filter(function (boon) {
return boons[boon] < 5;
});
if (!availableBoons.length) {
self.destroy();
callback();
}
var selectedBoons = [];
while (selectedBoons.length < 3 && availableBoons.length > 0) {
var boonIndex = Math.floor(Math.random() * availableBoons.length);
selectedBoons.push(availableBoons.splice(boonIndex, 1)[0]);
}
var background = self.createAsset('boonBackground', 'Boon Selection Popup', 0.5, 0.5);
var boonMessageTitle = new Text2('Choose ' + count, {
size: 60,
fill: '#ffffff',
font: 'bold monospace'
});
background.y = 50;
boonMessageTitle.anchor.set(.5, 0);
boonMessageTitle.y = -220;
self.addChild(boonMessageTitle);
var boonMessageSubtitle = new Text2(type + ' Boon', {
size: 50,
fill: '#ffffff',
font: 'bold monospace'
});
boonMessageSubtitle.anchor.set(.5, 0);
boonMessageSubtitle.y = -160;
self.addChild(boonMessageSubtitle);
for (var i = 0; i < selectedBoons.length; i++) {
var boon = selectedBoons[i];
var boonButton = self.createAsset('boonButton', boon + ' Boon Button', 0.5, 0.5);
boonButton.y = i * 120;
boonButton.x = -100;
boonButton.boon = boon;
boonButton.on('down', function () {
self.destroy();
callback(this.boon);
});
var boonLevel = new Text2(boons[boon], {
size: 50,
fill: '#ffffff',
font: 'bold monospace'
});
boonLevel.anchor.set(.5, .5);
boonLevel.y = boonButton.y;
boonLevel.x = boonButton.x;
self.addChild(boonLevel);
var boonName = new Text2(boon, {
size: 50,
fill: '#ffffff',
font: 'bold monospace'
});
boonName.anchor.set(0, .5);
boonName.y = boonButton.y;
boonName.x = boonButton.x + 60;
self.addChild(boonName);
}
});
var Game = Container.expand(function () {
var self = Container.call(this);
var isPaused = true;
var grass = self.createAsset('grass', 'Grass Background', 0, 0);
var hero = new Hero();
var canMove = false;
var difficultyScale = 0;
var experience = 0;
var level = 1;
var levelRequirement = 20;
var minorBoonCount = 0;
var majorBoonCount = 0;
var availableBoonsTxt = new Text2('0', {
size: 50,
fill: '#ff0000',
font: 'bold monospace'
});
var updateButton = LK.gui.topRight.createAsset('updateButton', 'Update Button', 0.5, 0.5);
LK.gui.topRight.addChild(availableBoonsTxt);
grass.width = 2048;
grass.height = 2732;
self.addChild(grass);
updateButton.x = -70;
updateButton.y = 70;
availableBoonsTxt.anchor.set(.5, .5);
availableBoonsTxt.x = -70;
availableBoonsTxt.y = 70;
updateButton.on('down', function () {
if (minorBoonCount + majorBoonCount > 0) {
showBoonSelection();
}
});
var levelTxt = new Text2('Level ' + level + ' • XP', {
size: 80,
fill: '#ffffff',
font: 'bold monospace'
});
var selectingBoon = false;
var progressBarBorder = LK.getAsset('progressBarBorder', 'Progress Bar Border', 0, .5);
var progressBar = LK.getAsset('progressBar', 'Progress Bar', 0, .5);
var enemyProjectiles = [];
var enemies = [];
var healthPickups = [];
var crucifixPickups = [];
var experiencePickups = [];
var countdownTimer = new CountdownTimer(300);
LK.gui.topCenter.addChild(countdownTimer);
LK.gui.topCenter.addChild(progressBarBorder);
LK.gui.topCenter.addChild(progressBar);
LK.gui.topCenter.addChild(levelTxt);
countdownTimer.y = levelTxt.height + 10;
progressBarBorder.x = 20;
progressBarBorder.y = levelTxt.y + levelTxt.height / 2;
progressBar.x = 24;
progressBar.y = levelTxt.y + levelTxt.height / 2;
progressBar.scale.x = 0;
levelTxt.anchor.set(1.0, 0);
hero.x = 2048 / 2;
hero.y = 2732 / 2;
self.targetPos = null;
self.addChild(hero);
refreshBoonUpgradeButton();
stage.on('down', function (obj) {
canMove = true;
hero.targetPos = obj.event.getLocalPosition(self);
});
stage.on('up', function (obj) {
canMove = false;
hero.targetPos = null;
});
stage.on('move', function (obj) {
if (!isPaused) {
if (canMove) {
hero.targetPos = obj.event.getLocalPosition(self);
}
hero.shootPos = obj.event.getLocalPosition(self);
if (hero.shootPos.x < hero.x) {
hero.scale.x = -1;
} else {
hero.scale.x = 1;
}
}
});
LK.on('tick', function () {
if (!isPaused) {
difficultyScale = countdownTimer.update();
hero.update();
updateContainer(healthPickups);
updateContainer(enemyProjectiles);
updateContainer(enemies, {
projectiles: hero.projectiles
});
for (var i = 0; i < crucifixPickups.length; i++) {
if (hero.intersects(crucifixPickups[i])) {
for (var j = 0; j < enemies.length; j++) {
enemies[j].destroy();
}
enemies = [];
for (var j = 0; j < enemyBullets.length; j++) {
enemyBullets[j].destroy();
}
enemyBullets = [];
for (var j = 0; j < healthPickups.length; j++) {
healthPickups[j].destroy();
}
healthPickups = [];
for (var j = 0; j < crucifixPickups.length; j++) {
crucifixPickups[j].destroy();
}
crucifixPickups = [];
for (var j = 0; j < experiencePickups.length; j++) {
experiencePickups[j].active = true;
}
LK.effects.flashScreen(0xffffff, 1000);
break;
}
}
for (var i = 0; i < experiencePickups.length; i++) {
var experiencePickup = experiencePickups[i];
experiencePickup.move();
if (experiencePickup.intersects(hero)) {
experiencePickup.destroy();
experiencePickups.splice(i, 1);
experience++;
if (experience >= levelRequirement) {
level++;
experience -= levelRequirement;
levelRequirement = Math.floor(levelRequirement * 1.2);
levelTxt.setText('Level ' + level + ' • XP');
if (level % 5) {
minorBoonCount++;
} else {
majorBoonCount++;
}
refreshBoonUpgradeButton();
}
progressBar.scale.x = experience / levelRequirement;
i--;
}
}
if (LK.ticks % (60 - Math.floor(55 * difficultyScale)) === 0) {
spawnEnemy();
}
}
});
function refreshBoonUpgradeButton() {
var bool = minorBoonCount + majorBoonCount > 0;
updateButton.visible = bool;
availableBoonsTxt.visible = bool;
availableBoonsTxt.setText(minorBoonCount + majorBoonCount);
}
function showBoonSelection() {
if (!selectingBoon) {
var boonSelection;
if (minorBoonCount) {
selectingBoon = true;
boonSelection = new BoonSelection(hero.minorBoonLevels, 'Minor', minorBoonCount, function (boon) {
if (boon) {
hero.minorBoonLevels[boon]++;
minorBoonCount--;
selectingBoon = false;
refreshBoonUpgradeButton();
showBoonSelection();
}
});
} else if (majorBoonCount) {
selectingBoon = true;
boonSelection = new BoonSelection(hero.majorBoonLevels, 'Major', majorBoonCount, function (boon) {
if (boon) {
hero.majorBoonLevels[boon]++;
majorBoonCount--;
selectingBoon = false;
refreshBoonUpgradeButton();
showBoonSelection();
}
});
}
if (boonSelection) {
isPaused = true;
LK.gui.center.addChild(boonSelection);
} else {
isPaused = false;
}
}
}
function spawnEnemy() {
var side = Math.floor(Math.random() * 4);
var enemy = new Enemy(hero);
switch (side) {
case 0:
enemy.x = Math.random() * 2048;
enemy.y = 0;
break;
case 1:
enemy.x = 2048;
enemy.y = Math.random() * 2732;
break;
case 2:
enemy.x = Math.random() * 2048;
enemy.y = 2732;
break;
case 3:
enemy.x = 0;
enemy.y = Math.random() * 2732;
break;
}
enemies.push(enemy);
self.addChild(enemy);
}
});
pixel art cross with blue accents Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a white orb. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a white orb with a halo. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a pulsating white heart with a halo. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a dark goo projectile with red highlights. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art tall blue fireball. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of an evil fantasy sword facing downward. Minor red details. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
backgroundAmbient
Sound effect
heroHealed
Sound effect
pickupExperience
Sound effect
heroLeveled
Sound effect
weaponCrossImpact
Sound effect
heroImpact
Sound effect
enemyDeath
Sound effect
pickupWeapon
Sound effect
pickupCrucifix
Sound effect
weaponCrossLaunch
Sound effect
heroDeath
Sound effect
enemyRoar
Sound effect
clockChime
Sound effect