finalBoss rework

finalBoss rework
  (this is pretty raw, so expect a bug and balance patch soon)
  finalBoss goes invulnerable a few times as it loses health
  finalBoss damage reduction is higher
    finalBoss damage reduction slowly decays as you do any damage to the boss
      damage reduction resets to normal with each new invulnerability phase
  after each invulnerability phase it randomly adds 1 more attack mode
      lasers, black hole, mines, hoppers, seekers, mobs, orbiters, oscillation

mobs die below 0.05 -> 0.01 health
  might cause bugs, but testing this out

guns and field power ups show 3 -> 2 options

bug fixes:
This commit is contained in:
landgreen
2022-10-05 09:33:08 -07:00
parent d7c01ef92c
commit c162f1074e
9 changed files with 1103 additions and 552 deletions

View File

@@ -3931,6 +3931,10 @@ const b = {
if (!mob.shield && Vector.dot(Vector.normalise(Vector.sub(mob.position, bullet.position)), Vector.normalise(bullet.velocity)) > 0.99 - 4 / mob.radius) {
let cycle = () => { //makes this run after damage
if (mob.health < 0.5 && mob.damageReduction > 0 && mob.alive) {
// mob.death();
// mob.damage(this.health * Math.sqrt(this.mass) / this.damageReduction);
mob.damage(Infinity);
const color = 'rgb(255,255,255)'
simulation.drawList.push({
x: mob.position.x,
@@ -3953,7 +3957,6 @@ const b = {
color: color, //"rgb(0,0,0)",
time: 20
});
mob.death();
}
}
requestAnimationFrame(cycle);
@@ -5328,7 +5331,6 @@ const b = {
b.explosion(this.position, 300 + 40 * Math.random()); //makes bullet do explosive damage at end
}
} else if (tech.isCritKill) b.crit(who, this)
if (tech.isNailRadiation) mobs.statusDoT(who, 7 * (tech.isFastRadiation ? 0.7 : 0.24), tech.isSlowRadiation ? 360 : (tech.isFastRadiation ? 60 : 180)) // one tick every 30 cycles
if (this.speed > 4 && tech.fragments) {
b.targetedNail(this.position, 1.25 * tech.fragments * tech.bulletSize)
@@ -6595,7 +6597,6 @@ const b = {
ctx.fill();
if (this.isDischarge && m.cycle % 2) {
this.charge -= 0.75
const spread = (input.down ? 0.04 : 0.5) * (Math.random() - 0.5)
const radius = 5 + 8 * Math.random() + (tech.isAmmoFoamSize && this.ammo < 300) * 12
const SPEED = (input.down ? 1.2 : 1) * 10 - radius * 0.4 + Math.min(5, Math.sqrt(this.charge));
@@ -6609,6 +6610,7 @@ const b = {
y: m.pos.y + 30 * Math.sin(m.angle)
}
b.foam(position, Vector.rotate(velocity, spread), radius)
this.charge -= 0.75
m.fireCDcycle = m.cycle + 2; //disable firing and adding more charge until empty
} else if (!input.fire) {
this.isDischarge = true;

View File

@@ -16,37 +16,38 @@ const level = {
start() {
if (level.levelsCleared === 0) { //this code only runs on the first level
// simulation.enableConstructMode() //used to build maps in testing mode
// level.difficultyIncrease(12 * 4) //30 is near max on hard //60 is near max on why
// simulation.isHorizontalFlipped = true
// tech.giveTech("performance")
// level.difficultyIncrease(6 * 4) //30 is near max on hard //60 is near max on why
// m.maxHealth = m.health = 100
// tech.isRerollDamage = true
// powerUps.research.changeRerolls(100000)
// powerUps.research.changeRerolls(50)
// m.immuneCycle = Infinity //you can't take damage
// tech.tech[297].frequency = 100
// m.couplingChange(5)
// m.setField("metamaterial cloaking") //molecular assembler standing wave time dilation perfect diamagnetism metamaterial cloaking wormhole negative mass
// m.setField("pilot wave") //molecular assembler standing wave time dilation perfect diamagnetism metamaterial cloaking wormhole negative mass pilot wave
// simulation.molecularMode = 2
// m.damage(0.1);
// b.giveGuns("nail gun") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// b.guns[0].ammo = 1000000
// for (let i = 0; i < 1; ++i) tech.giveTech("mass-energy equivalence")
// tech.giveTech("Zeno's paradox")
// tech.giveTech("homeostasis")
// for (let i = 0; i < 1; ++i) tech.giveTech("1st ionization energy")
// for (let i = 0; i < 1; i++) tech.giveTech("negative feedback")
// for (let i = 0; i < 1; ++i) tech.giveTech("needle gun")
// tech.giveTech("pressure vessel")
// tech.giveTech("quintessence")
// for (let i = 0; i < 1; ++i) tech.giveTech("freezer burn")
// for (let i = 0; i < 1; i++) tech.giveTech("reaction inhibitor")
// for (let i = 0; i < 10; i++) powerUps.directSpawn(450, -50, "tech");
// for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "boost");
// for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "coupling");
// level.testing();
// level.final();
// spawn.starter(1900, -500)
// spawn.beetleBoss(2538, -1950)
// spawn.timeBoss(2538, -950)
// for (let i = 0; i < 33; ++i) spawn.sniper(1000 + 5000 * Math.random(), -500 + 300 * Math.random())
// tech.addJunkTechToPool(0.5)
// tech.tech[322].frequency = 100
// spawn.tetherBoss(1900, -500, { x: 1900, y: -500 })
// for (let i = 0; i < 36; ++i) tech.giveTech()
// for (let i = 0; i < 13; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "research");
if (simulation.isTraining) { level.walk(); } else { level.intro(); } //normal starting level ************************************************
// for (let i = 0; i < 4; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "tech");
@@ -3125,12 +3126,18 @@ const level = {
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
final() {
const slime = level.hazard(simulation.isHorizontalFlipped ? 150 - 860 : -150, -360, 880, 259) //x, y, width, height, damage = 0.002) {
slime.height -= slime.maxHeight - 150 //start slime at zero
slime.min.y += slime.maxHeight
slime.max.y = slime.min.y + slime.height
level.custom = () => {
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
slime.query();
slime.levelRise(0.1)
ctx.fillStyle = "rgba(0,255,255,0.1)"
ctx.fillRect(5400, -550, 300, 350)
};
@@ -3163,7 +3170,13 @@ const level = {
spawn.mapRect(-1950, -3300, 8200, 1800); //roof
spawn.mapRect(-250, -200, 1000, 300); // shelf
spawn.mapRect(-250, -1700, 1000, 1250); // shelf roof
spawn.blockDoor(710, -210);
// spawn.blockDoor(710, -210);
spawn.mapRect(705, -210, 25, 50);
spawn.mapRect(725, -220, 25, 50);
spawn.bodyRect(750, -125, 125, 125);
spawn.bodyRect(875, -50, 50, 50);
spawn.mapRect(5400, -1700, 400, 1150); //right wall
spawn.mapRect(5400, -300, 400, 400); //right wall
spawn.mapRect(5700, -3300, 1800, 5100); //right wall
@@ -3187,6 +3200,8 @@ const level = {
level.enter.draw();
};
level.customTopLayer = () => {
slime.query();
slime.levelRise(0.1)
ctx.fillStyle = "rgba(0,255,255,0.1)"
ctx.fillRect(-5400 - 300, -550, 300, 350)
};

View File

@@ -62,6 +62,8 @@ const mobs = {
if (!whom.shield && !whom.isShielded && whom.alive) {
if (tech.isIceMaxHealthLoss && whom.health > 0.65 && whom.damageReduction > 0) whom.health = 0.66
if (tech.isIceKill && whom.health < 0.34 && whom.damageReduction > 0 && whom.alive) {
// whom.death();
whom.damage(Infinity);
simulation.drawList.push({
x: whom.position.x,
y: whom.position.y,
@@ -83,7 +85,6 @@ const mobs = {
color: "rgb(0,100,255)",
time: 16
});
whom.death();
}
if (whom.isBoss) cycles = Math.floor(cycles * 0.25)
let i = whom.status.length
@@ -1136,20 +1137,22 @@ const mobs = {
},
damage(dmg, isBypassShield = false) {
if ((!this.isShielded || isBypassShield) && this.alive) {
dmg *= tech.damageFromTech()
//mobs specific damage changes
if (tech.isFarAwayDmg) dmg *= 1 + Math.sqrt(Math.max(500, Math.min(3000, this.distanceToPlayer())) - 500) * 0.0067 //up to 33% dmg at max range of 3000
dmg *= this.damageReduction
//energy and heal drain should be calculated after damage boosts
if (tech.energySiphon && dmg !== Infinity && this.isDropPowerUp && m.immuneCycle < m.cycle) m.energy += Math.min(this.health, dmg) * tech.energySiphon
if (tech.healthDrain && dmg !== Infinity && this.isDropPowerUp && Math.random() < tech.healthDrain * Math.min(this.health, dmg)) {
powerUps.spawn(m.pos.x + 20 * (Math.random() - 0.5), m.pos.y + 20 * (Math.random() - 0.5), "heal");
if (dmg !== Infinity) {
dmg *= tech.damageFromTech()
//mobs specific damage changes
if (tech.isFarAwayDmg) dmg *= 1 + Math.sqrt(Math.max(500, Math.min(3000, this.distanceToPlayer())) - 500) * 0.0067 //up to 33% dmg at max range of 3000
dmg *= this.damageReduction
//energy and heal drain should be calculated after damage boosts
if (tech.energySiphon && dmg !== Infinity && this.isDropPowerUp && m.immuneCycle < m.cycle) m.energy += Math.min(this.health, dmg) * tech.energySiphon
if (tech.healthDrain && dmg !== Infinity && this.isDropPowerUp && Math.random() < tech.healthDrain * Math.min(this.health, dmg)) {
powerUps.spawn(m.pos.x + 20 * (Math.random() - 0.5), m.pos.y + 20 * (Math.random() - 0.5), "heal");
}
dmg /= Math.sqrt(this.mass)
}
dmg /= Math.sqrt(this.mass)
this.health -= dmg
//this.fill = this.color + this.health + ')';
this.onDamage(dmg); //custom damage effects
if ((this.health < 0.05 || isNaN(this.health)) && this.alive) this.death();
if ((this.health < 0.01 || isNaN(this.health)) && this.alive) this.death();
}
},
onDamage() {

View File

@@ -2929,7 +2929,7 @@ const m = {
if (m.energy < 0.05 && m.fireCDcycle < m.cycle && !input.fire) m.fireCDcycle = m.cycle
if (m.fireCDcycle + 30 < m.cycle && !input.fire) { //automatically cloak if not firing
const drain = 0.1
if (!m.isCloak && m.energy > drain) {
if (!m.isCloak && m.energy > drain + 0.05) {
m.energy -= drain
m.isCloak = true //enter cloak
@@ -2945,28 +2945,18 @@ const m = {
m.enterCloakCycle = m.cycle
if (tech.isCloakHealLastHit && m.lastHit > 0) {
const heal = Math.min(0.75 * m.lastHit, m.energy)
m.energy -= heal
simulation.drawList.push({ //add dmg to draw queue
x: m.pos.x,
y: m.pos.y,
radius: Math.sqrt(heal) * 200,
color: "rgba(0,255,200,0.6)",
time: 16
});
m.addHealth(heal); //heal from last hit
// if (tech.isEnergyHealth) {
// simulation.drawList.push({ //add dmg to draw queue
// x: m.pos.x,
// y: m.pos.y,
// radius: Math.sqrt(heal) * 200,
// color: "#0ad", //simulation.mobDmgColor
// time: 16
// });
// m.energy += heal
// } else {
// }
m.lastHit = 0
// simulation.makeTextLog(`<span class='color-var'>m</span>.health <span class='color-symbol'>+=</span> ${(heal).toFixed(3)}`) // <br>${m.health.toFixed(3)}
if (m.energy > heal) {
m.energy -= heal
m.addHealth(heal); //heal from last hit
m.lastHit = 0
simulation.drawList.push({ //add dmg to draw queue
x: m.pos.x,
y: m.pos.y,
radius: Math.sqrt(heal) * 200,
color: "rgba(0,255,200,0.6)",
time: 16
});
}
}
if (tech.isIntangible) {
for (let i = 0; i < bullet.length; i++) {

View File

@@ -650,7 +650,7 @@ const powerUps = {
for (let i = 0; i < b.guns.length; i++) {
if (!b.guns[i].have) options.push(i);
}
let totalChoices = Math.min(options.length, tech.isDeterminism ? 1 : 3 + tech.extraChoices)
let totalChoices = Math.min(options.length, tech.isDeterminism ? 1 : 2 + tech.extraChoices)
if (tech.isFlipFlopChoices) totalChoices += tech.isRelay ? (tech.isFlipFlopOn ? -1 : 7) : (tech.isFlipFlopOn ? 7 : -1) //flip the order for relay
function removeOption(index) {
for (let i = 0; i < options.length; i++) {
@@ -787,7 +787,7 @@ const powerUps = {
for (let i = 1; i < m.fieldUpgrades.length; i++) { //skip field emitter
if (i !== m.fieldMode) options.push(i);
}
let totalChoices = Math.min(options.length, tech.isDeterminism ? 1 : 3 + tech.extraChoices)
let totalChoices = Math.min(options.length, tech.isDeterminism ? 1 : 2 + tech.extraChoices)
if (tech.isFlipFlopChoices) totalChoices += tech.isRelay ? (tech.isFlipFlopOn ? -1 : 7) : (tech.isFlipFlopOn ? 7 : -1) //flip the order for relay
function removeOption(index) {
@@ -953,7 +953,10 @@ const powerUps = {
totalChoices = optionLengthNoDuplicates
if (tech.isBanish) { //when you run out of options eject banish
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].name === "decoherence") powerUps.ejectTech(i, true)
if (tech.tech[i].name === "decoherence") {
// console.log(i)
powerUps.ejectTech(i, true)
}
}
simulation.makeTextLog(`decoherence <span class='color-var'>tech</span> ejected`)
simulation.makeTextLog(`options reset`)
@@ -1099,7 +1102,7 @@ const powerUps = {
let cycle = () => {
if (count > 0) {
requestAnimationFrame(cycle);
if (!simulation.paused && !simulation.isChoosing) { //&& !(simulation.cycle % 2)
if (!simulation.paused && !simulation.isChoosing && m.alive) { //&& !(simulation.cycle % 2)
count--
const where = { x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) }
powerUps.spawn(where.x, where.y, type);
@@ -1182,9 +1185,13 @@ const powerUps = {
// }
},
randomPowerUpCounter: 0,
isFieldSpawned: false, //makes it so a field spawns once but not more times
spawnBossPowerUp(x, y) { //boss spawns field and gun tech upgrades
if (level.levels[level.onLevel] !== "final") {
if (m.fieldMode === 0 && !m.coupling) {
// if (level.levelsCleared === 1) powerUps.spawn(x, y, "field")
// if (m.fieldMode === 0 && !m.coupling) {
if (!powerUps.isFieldSpawned) {
powerUps.isFieldSpawned = true
powerUps.spawn(x, y, "field")
} else {
powerUps.randomPowerUpCounter++;
@@ -1227,7 +1234,7 @@ const powerUps = {
}
},
spawnStartingPowerUps(x, y) { //used for map specific power ups, mostly to give player a starting gun
if (level.levelsCleared < 4) { //runs 4 times on all difficulty levels
if (level.levelsCleared < 4) { //runs on first 4 levels on all difficulties
if (level.levelsCleared > 1) powerUps.spawn(x, y, "tech")
if (b.inventory.length === 0) {
powerUps.spawn(x, y, "gun", false); //first gun
@@ -1242,23 +1249,24 @@ const powerUps = {
} else {
for (let i = 0; i < 4; i++) powerUps.spawnRandomPowerUp(x, y);
}
} else {
} else { //after the first 4 levels just spawn a random power up
for (let i = 0; i < 3; i++) powerUps.spawnRandomPowerUp(x, y);
}
},
ejectTech(choose = 'random', isOverride = false) {
if (!simulation.isChoosing || isOverride) {
// console.log(tech.tech[choose].name, tech.tech[choose].count, tech.tech[choose].isNonRefundable)
//find which tech you have
if (choose === 'random') {
const have = []
for (let i = 0; i < tech.tech.length; i++) {
if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) have.push(i)
}
if (have.length === 0) {
for (let i = 0; i < tech.tech.length; i++) {
if (tech.tech[i].count > 0) have.push(i)
}
}
// if (have.length === 0) {
// for (let i = 0; i < tech.tech.length; i++) {
// if (tech.tech[i].count > 0) have.push(i)
// }
// }
if (have.length) {
choose = have[Math.floor(Math.random() * have.length)]
@@ -1279,7 +1287,7 @@ const powerUps = {
} else {
return false
}
} else if (tech.tech[choose].count && tech.tech[choose].isNonRefundable) {
} else if (tech.tech[choose].count && !tech.tech[choose].isNonRefundable) {
// simulation.makeTextLog(`<div class='circle tech'></div> &nbsp; <strong>${tech.tech[choose].name}</strong> was ejected`, 600) //message about what tech was lost
simulation.makeTextLog(`<span class='color-var'>tech</span>.remove("<span class='color-text'>${tech.tech[choose].name}</span>")`)

View File

@@ -776,6 +776,7 @@ const simulation = {
powerUps.totalPowerUps = 0;
powerUps.research.count = 0;
powerUps.boost.endCycle = 0
powerUps.isFieldSpawned = false
m.setFillColors();
// m.maxHealth = 1
// m.maxEnergy = 1

File diff suppressed because it is too large Load Diff

View File

@@ -233,7 +233,7 @@ const tech = {
if (tech.isAxion && tech.isHarmMACHO) dmg *= 2 - m.harmReduction()
if (tech.isHarmDamage && m.lastHarmCycle + 600 > m.cycle) dmg *= 3;
if (tech.lastHitDamage && m.lastHit) dmg *= 1 + tech.lastHitDamage * m.lastHit * (2 - m.harmReduction()) // if (!simulation.paused) m.lastHit = 0
if (tech.isLowHealthDmg) dmg *= 1 + Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health))
if (tech.isLowHealthDmg) dmg *= 1 + 0.7 * Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health))
return dmg
},
duplicationChance() {
@@ -483,7 +483,7 @@ const tech = {
{
name: "supply chain",
junk: 0.05,
descriptionFunction() { return `for each <strong class='color-g'>gun</strong> in your inventory<br>double your current <strong class='color-ammo'>ammo</strong>` },
descriptionFunction() { return `for each <strong class='color-g'>gun</strong> in your inventory<br>double its <strong class='color-ammo'>ammo</strong>` },
maxCount: 9,
count: 0,
frequency: 1,
@@ -2627,7 +2627,7 @@ const tech = {
{
name: "negative feedback",
descriptionFunction() {
return `for each ${tech.isEnergyHealth ? "<strong class='color-f'>energy</strong>": "<strong class='color-h'>health</strong>"} below <strong>100</strong><br><strong>+0.7%</strong> <strong class='color-d'>damage</strong> <em>(${(100*Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health))).toFixed(0)}%)</em>`
return `for each ${tech.isEnergyHealth ? "<strong class='color-f'>energy</strong>": "<strong class='color-h'>health</strong>"} below <strong>100</strong><br><strong>+0.7%</strong> <strong class='color-d'>damage</strong> <em>(${(70*Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health))).toFixed(0)}%)</em>`
},
maxCount: 1,
count: 0,
@@ -3509,7 +3509,7 @@ const tech = {
couplingToResearch: 0.25,
effect() {
let count = 0
while (powerUps.research.count > 0) {
while (powerUps.research.count > 0 && powerUps.research.count !== Infinity) {
powerUps.research.changeRerolls(-1)
count += 2.5
this.researchUsed++