level: subway
replaces gauntlet just before the final boss
gauntlet moved to community map pool
subway todo:
add a few more stations
balance difficulty
find bugs
surface plasmons does 50% more damage
elasticity renamed nitinol because I bought some nitinol wire and it's neat
entanglement power up no longer shows guns or fields you already have
disabled minimal HUD for training levels
reaction inhibitor 12->11% mob max health reduction
spawn.bodyRect() now can add blocks mid level without any extra code
I think I found all the bugs this causes, but let me know if any blocks added mid game aren't colliding
community map - clock update:
visual overhaul,live lighting, remove pendulum overlap, move with pendulum if you stand in it, debris, trap exit only opens after completing the fight, moving elements now freeze while using time dilation
1571 lines
92 KiB
JavaScript
1571 lines
92 KiB
JavaScript
let powerUp = [];
|
|
|
|
const powerUps = {
|
|
ejectGraphic(color = "68, 102, 119") {
|
|
simulation.drawList.push({
|
|
x: m.pos.x,
|
|
y: m.pos.y,
|
|
radius: 100,
|
|
color: `rgba(${color}, 0.8)`,
|
|
time: 4
|
|
});
|
|
simulation.drawList.push({
|
|
x: m.pos.x,
|
|
y: m.pos.y,
|
|
radius: 75,
|
|
color: `rgba(${color}, 0.6)`,
|
|
time: 8
|
|
});
|
|
simulation.drawList.push({
|
|
x: m.pos.x,
|
|
y: m.pos.y,
|
|
radius: 50,
|
|
color: `rgba(${color}, 0.3)`,
|
|
time: 12
|
|
});
|
|
simulation.drawList.push({
|
|
x: m.pos.x,
|
|
y: m.pos.y,
|
|
radius: 25,
|
|
color: `rgba(${color}, 0.15)`,
|
|
time: 16
|
|
});
|
|
},
|
|
healGiveMaxEnergy: false, //for tech 1st ionization energy
|
|
orb: {
|
|
research(num = 1) {
|
|
switch (num) {
|
|
case 1:
|
|
return `<div class="research-circle"></div> `
|
|
case 2:
|
|
return `<span style="position:relative;">
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:0;"></div>
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:7px;"></div>
|
|
</span> `
|
|
case 3:
|
|
return `<span style="position:relative;">
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:0;"></div>
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:8px;"></div>
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:16px;"></div>
|
|
</span>   `
|
|
case 4:
|
|
return `<span style="position:relative;">
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:0;"></div>
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:8px;"></div>
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:16px;"></div>
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:24px;"></div>
|
|
</span> `
|
|
case 5:
|
|
return `<span style="position:relative;">
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:0;"></div>
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:8px;"></div>
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:16px;"></div>
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:24px;"></div>
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:32px;"></div>
|
|
</span> `
|
|
case 6:
|
|
return `<span style="position:relative;">
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:0;"></div>
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:8px;"></div>
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:16px;"></div>
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:24px;"></div>
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:32px;"></div>
|
|
<div class="research-circle" style="position:absolute; top:1.5px; left:40px;"></div>
|
|
</span> `
|
|
}
|
|
let text = '<span style="position:relative;">'
|
|
for (let i = 0; i < num; i++) {
|
|
text += `<div class="research-circle" style="position:absolute; top:1.5px; left:${i * 8}px;"></div>`
|
|
}
|
|
text += '</span> '
|
|
for (let i = 0; i < num; i++) {
|
|
text += ' '
|
|
}
|
|
return text
|
|
},
|
|
ammo(num = 1) {
|
|
switch (num) {
|
|
case 1:
|
|
return `<div class="ammo-circle"></div>`
|
|
}
|
|
let text = '<span style="position:relative;">'
|
|
for (let i = 0; i < num; i++) {
|
|
text += `<div class="ammo-circle" style="position:absolute; top:1.5px; left:${i * 8}px;"></div>`
|
|
}
|
|
text += '</span> '
|
|
for (let i = 0; i < num; i++) {
|
|
text += ' '
|
|
}
|
|
return text
|
|
},
|
|
heal(num = 1) {
|
|
if (powerUps.healGiveMaxEnergy) {
|
|
switch (num) {
|
|
case 1:
|
|
return `<div class="heal-circle-energy"></div>`
|
|
}
|
|
let text = '<span style="position:relative;">'
|
|
for (let i = 0; i < num; i++) {
|
|
text += `<div class="heal-circle-energy" style="position:absolute; top:1px; left:${i * 10}px;"></div>`
|
|
}
|
|
text += '</span> '
|
|
for (let i = 0; i < num; i++) {
|
|
text += ' '
|
|
}
|
|
return text
|
|
} else {
|
|
switch (num) {
|
|
case 1:
|
|
return `<div class="heal-circle"></div>`
|
|
}
|
|
let text = '<span style="position:relative;">'
|
|
for (let i = 0; i < num; i++) {
|
|
text += `<div class="heal-circle" style="position:absolute; top:1px; left:${i * 10}px;"></div>`
|
|
}
|
|
text += '</span> '
|
|
for (let i = 0; i < num; i++) {
|
|
text += ' '
|
|
}
|
|
return text
|
|
}
|
|
},
|
|
tech(num = 1) {
|
|
return `<div class="tech-circle"></div>`
|
|
},
|
|
coupling(num = 1) {
|
|
switch (num) {
|
|
case 1:
|
|
return `<div class="coupling-circle"></div>`
|
|
}
|
|
let text = '<span style="position:relative;">'
|
|
for (let i = 0; i < num; i++) {
|
|
text += `<div class="coupling-circle" style="position:absolute; top:1.5px; left:${i * 6}px;"></div>`
|
|
}
|
|
text += '</span> '
|
|
for (let i = 0; i < num; i++) {
|
|
text += '  '
|
|
}
|
|
return text
|
|
},
|
|
boost(num = 1) {
|
|
switch (num) {
|
|
case 1:
|
|
return `<div class="boost-circle"></div>`
|
|
}
|
|
let text = '<span style="position:relative;">'
|
|
for (let i = 0; i < num; i++) {
|
|
text += `<div class="boost-circle" style="position:absolute; top:1.5px; left:${i * 8}px;"></div>`
|
|
}
|
|
text += '</span> '
|
|
for (let i = 0; i < num; i++) {
|
|
text += ' '
|
|
}
|
|
return text
|
|
},
|
|
},
|
|
totalPowerUps: 0, //used for tech that count power ups at the end of a level
|
|
do() { },
|
|
setPowerUpMode() {
|
|
if (tech.duplicationChance() > 0 || tech.isAnthropicTech) {
|
|
powerUps.draw = powerUps.drawDup
|
|
if (tech.isPowerUpsVanish) {
|
|
if (tech.isHealAttract) {
|
|
powerUps.do = () => {
|
|
powerUps.dupExplode();
|
|
powerUps.draw();
|
|
powerUps.attractHeal();
|
|
}
|
|
} else {
|
|
powerUps.do = () => {
|
|
powerUps.dupExplode();
|
|
powerUps.draw();
|
|
}
|
|
}
|
|
} else if (tech.isHealAttract) {
|
|
powerUps.do = () => {
|
|
powerUps.draw();
|
|
powerUps.attractHeal();
|
|
}
|
|
} else {
|
|
powerUps.do = () => powerUps.draw();
|
|
}
|
|
tech.maxDuplicationEvent() //check to see if hitting 100% duplication
|
|
} else {
|
|
powerUps.draw = powerUps.drawCircle
|
|
if (tech.isHealAttract) {
|
|
powerUps.do = () => {
|
|
powerUps.draw();
|
|
powerUps.attractHeal();
|
|
}
|
|
} else {
|
|
powerUps.do = powerUps.draw
|
|
}
|
|
}
|
|
},
|
|
draw() { },
|
|
drawCircle() {
|
|
ctx.globalAlpha = 0.4 * Math.sin(simulation.cycle * 0.15) + 0.6;
|
|
for (let i = 0, len = powerUp.length; i < len; ++i) {
|
|
ctx.beginPath();
|
|
ctx.arc(powerUp[i].position.x, powerUp[i].position.y, powerUp[i].size, 0, 2 * Math.PI);
|
|
ctx.fillStyle = powerUp[i].color;
|
|
ctx.fill();
|
|
}
|
|
ctx.globalAlpha = 1;
|
|
},
|
|
drawDup() {
|
|
ctx.globalAlpha = 0.4 * Math.sin(simulation.cycle * 0.15) + 0.6;
|
|
for (let i = 0, len = powerUp.length; i < len; ++i) {
|
|
ctx.beginPath();
|
|
if (powerUp[i].isDuplicated) {
|
|
let vertices = powerUp[i].vertices;
|
|
ctx.moveTo(vertices[0].x, vertices[0].y);
|
|
for (let j = 1; j < vertices.length; j++) {
|
|
ctx.lineTo(vertices[j].x, vertices[j].y);
|
|
}
|
|
ctx.lineTo(vertices[0].x, vertices[0].y);
|
|
} else {
|
|
ctx.arc(powerUp[i].position.x, powerUp[i].position.y, powerUp[i].size, 0, 2 * Math.PI);
|
|
}
|
|
ctx.fillStyle = powerUp[i].color;
|
|
ctx.fill();
|
|
}
|
|
ctx.globalAlpha = 1;
|
|
},
|
|
attractHeal() {
|
|
for (let i = 0; i < powerUp.length; i++) { //attract heal power ups to player
|
|
if (powerUp[i].name === "heal") {
|
|
let attract = Vector.mult(Vector.normalise(Vector.sub(m.pos, powerUp[i].position)), 0.015 * powerUp[i].mass)
|
|
powerUp[i].force.x += attract.x;
|
|
powerUp[i].force.y += attract.y - powerUp[i].mass * simulation.g; //negate gravity
|
|
Matter.Body.setVelocity(powerUp[i], Vector.mult(powerUp[i].velocity, 0.7));
|
|
}
|
|
}
|
|
},
|
|
dupExplode() {
|
|
for (let i = 0, len = powerUp.length; i < len; ++i) {
|
|
if (powerUp[i].isDuplicated) {
|
|
if (Math.random() < 0.003) { // (1-0.003)^240 = chance to be removed after 4 seconds, 240 = 4 seconds * 60 cycles per second
|
|
b.explosion(powerUp[i].position, 175 + (11 + 3 * Math.random()) * powerUp[i].size);
|
|
Matter.Composite.remove(engine.world, powerUp[i]);
|
|
powerUp.splice(i, 1);
|
|
break
|
|
}
|
|
if (Math.random() < 0.3) { //draw electricity
|
|
const mag = 4 + powerUp[i].size / 5
|
|
let unit = Vector.rotate({ x: mag, y: mag }, 2 * Math.PI * Math.random())
|
|
let path = { x: powerUp[i].position.x + unit.x, y: powerUp[i].position.y + unit.y }
|
|
ctx.beginPath();
|
|
ctx.moveTo(path.x, path.y);
|
|
for (let i = 0; i < 6; i++) {
|
|
unit = Vector.rotate(unit, 4 * (Math.random() - 0.5))
|
|
path = Vector.add(path, unit)
|
|
ctx.lineTo(path.x, path.y);
|
|
}
|
|
ctx.lineWidth = 0.5 + 2 * Math.random();
|
|
ctx.strokeStyle = "#000"
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
choose(type, index) {
|
|
if (type === "gun") {
|
|
b.giveGuns(index)
|
|
let text = `b.giveGuns("<span class='color-text'>${b.guns[index].name}</span>")`
|
|
if (b.inventory.length === 1) text += `<br>input.key.gun<span class='color-symbol'>:</span> ["<span class='color-text'>MouseLeft</span>"]`
|
|
if (b.inventory.length === 2) text += `
|
|
<br>input.key.nextGun<span class='color-symbol'>:</span> ["<span class='color-text'>${input.key.nextGun}</span>","<span class='color-text'>MouseWheel</span>"]
|
|
<br>input.key.previousGun<span class='color-symbol'>:</span> ["<span class='color-text'>${input.key.previousGun}</span>","<span class='color-text'>MouseWheel</span>"]`
|
|
simulation.makeTextLog(text);
|
|
} else if (type === "field") {
|
|
m.setField(index)
|
|
} else if (type === "tech") {
|
|
// if (tech.isBanish && tech.tech[index].isBanished) tech.tech[index].isBanished = false
|
|
simulation.makeTextLog(`<span class='color-var'>tech</span>.giveTech("<span class='color-text'>${tech.tech[index].name}</span>")`);
|
|
tech.giveTech(index)
|
|
}
|
|
powerUps.endDraft(type);
|
|
},
|
|
showDraft() {
|
|
//disable clicking for 1/2 a second to prevent mistake clicks
|
|
document.getElementById("choose-grid").style.pointerEvents = "none";
|
|
document.body.style.cursor = "none";
|
|
setTimeout(() => {
|
|
// if (!tech.isNoDraftPause)
|
|
document.body.style.cursor = "auto";
|
|
document.getElementById("choose-grid").style.pointerEvents = "auto";
|
|
document.getElementById("choose-grid").style.transitionDuration = "0s";
|
|
}, 400);
|
|
simulation.isChoosing = true; //stops p from un pausing on key down
|
|
|
|
if (!simulation.paused) {
|
|
if (tech.isNoDraftPause) {
|
|
document.getElementById("choose-grid").style.opacity = "1"
|
|
} else {
|
|
simulation.paused = true;
|
|
document.getElementById("choose-grid").style.opacity = "1"
|
|
}
|
|
document.getElementById("choose-grid").style.transitionDuration = "0.5s"; //how long is the fade in on
|
|
document.getElementById("choose-grid").style.visibility = "visible"
|
|
|
|
requestAnimationFrame(() => {
|
|
ctx.fillStyle = `rgba(150,150,150,0.9)`; //`rgba(221,221,221,0.6)`;
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
});
|
|
// document.getElementById("pause-grid-right").style.opacity = "0.7"
|
|
// document.getElementById("pause-grid-left").style.opacity = "0.7"
|
|
}
|
|
// build.pauseGrid()
|
|
},
|
|
endDraft(type, isCanceled = false) { //type should be a gun, tech, or field
|
|
if (isCanceled) {
|
|
if (tech.isCancelTech && Math.random() < 0.85 && type !== "entanglement") {
|
|
// powerUps.research.use('tech')
|
|
powerUps[type].effect();
|
|
return
|
|
}
|
|
if (tech.isCancelDuplication) {
|
|
tech.duplication += 0.041
|
|
tech.maxDuplicationEvent()
|
|
simulation.makeTextLog(`tech.duplicationChance() <span class='color-symbol'>+=</span> ${0.043}`)
|
|
simulation.circleFlare(0.043);
|
|
}
|
|
if (tech.isCancelRerolls) {
|
|
for (let i = 0, len = 5 + 5 * Math.random(); i < len; i++) {
|
|
let spawnType
|
|
if (Math.random() < 0.4 && !tech.isEnergyNoAmmo) {
|
|
spawnType = "ammo"
|
|
} else if (Math.random() < 0.33 && !tech.isSuperDeterminism) {
|
|
spawnType = "research"
|
|
} else {
|
|
spawnType = "heal"
|
|
}
|
|
powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), spawnType, false);
|
|
}
|
|
}
|
|
if (tech.isCancelCouple) powerUps.spawnDelay("coupling", 5)
|
|
// if (tech.isCancelTech && Math.random() < 0.3) {
|
|
// powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "tech", false);
|
|
// simulation.makeTextLog(`<strong>options exchange</strong>: returns 1 <strong class='color-m'>tech</strong>`)
|
|
// }
|
|
// if (tech.isBanish && type === 'tech') { // banish researched tech by adding them to the list of banished tech
|
|
// const banishLength = tech.isDeterminism ? 1 : 3 + tech.extraChoices * 2
|
|
// for (let i = 0; i < banishLength; i++) {
|
|
// const index = powerUps.tech.choiceLog.length - i - 1
|
|
// if (powerUps.tech.choiceLog[index] && tech.tech[powerUps.tech.choiceLog[index]]) {
|
|
// tech.tech[powerUps.tech.choiceLog[index]].isBanished = true
|
|
// }
|
|
// }
|
|
// simulation.makeTextLog(`powerUps.tech.length: ${Math.max(0,powerUps.tech.lastTotalChoices - banishLength)}`)
|
|
// }
|
|
}
|
|
if (tech.isAnsatz && powerUps.research.count < 1) {
|
|
for (let i = 0; i < 2; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false);
|
|
}
|
|
// document.getElementById("choose-grid").style.display = "none"
|
|
document.getElementById("choose-grid").style.visibility = "hidden"
|
|
document.getElementById("choose-grid").style.opacity = "0"
|
|
|
|
document.body.style.cursor = "none";
|
|
// document.body.style.overflow = "hidden"
|
|
// if (m.alive){}
|
|
if (simulation.paused) requestAnimationFrame(cycle);
|
|
if (m.alive) simulation.paused = false;
|
|
simulation.isChoosing = false; //stops p from un pausing on key down
|
|
build.unPauseGrid()
|
|
if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 15; //player is immune to damage for 30 cycles
|
|
if (m.holdingTarget) m.drop();
|
|
},
|
|
coupling: {
|
|
name: "coupling",
|
|
color: "#0ae", //"#0cf",
|
|
size() {
|
|
return 13;
|
|
},
|
|
effect() {
|
|
m.couplingChange(1)
|
|
},
|
|
// spawnDelay(num) {
|
|
// let count = num
|
|
// let respawnDrones = () => {
|
|
// if (count > 0) {
|
|
// requestAnimationFrame(respawnDrones);
|
|
// if (!simulation.paused && !simulation.isChoosing) { //&& !(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, "coupling");
|
|
// }
|
|
// }
|
|
// }
|
|
// requestAnimationFrame(respawnDrones);
|
|
// }
|
|
},
|
|
boost: {
|
|
name: "boost",
|
|
color: "#f55", //"#0cf",
|
|
size() {
|
|
return 11;
|
|
},
|
|
endCycle: 0,
|
|
duration: null, //set by "tech: band gap"
|
|
damage: null, //set by "tech: band gap"
|
|
effect() {
|
|
powerUps.boost.endCycle = m.cycle + Math.floor(Math.max(0, powerUps.boost.endCycle - m.cycle) * 0.6) + powerUps.boost.duration //duration+seconds plus 2/3 of current time left
|
|
},
|
|
draw() {
|
|
// console.log(this.endCycle)
|
|
if (powerUps.boost.endCycle > m.cycle) {
|
|
ctx.strokeStyle = "rgba(255,0,0,0.8)" //m.fieldMeterColor; //"rgba(255,255,0,0.2)" //ctx.strokeStyle = `rgba(0,0,255,${0.5+0.5*Math.random()})`
|
|
ctx.beginPath();
|
|
const arc = (powerUps.boost.endCycle - m.cycle) / powerUps.boost.duration
|
|
ctx.arc(m.pos.x, m.pos.y, 28, m.angle - Math.PI * arc, m.angle + Math.PI * arc); //- Math.PI / 2
|
|
ctx.lineWidth = 4
|
|
ctx.stroke();
|
|
}
|
|
},
|
|
},
|
|
research: {
|
|
count: 0,
|
|
name: "research",
|
|
color: "#f7b",
|
|
size() {
|
|
return 20;
|
|
},
|
|
effect() {
|
|
powerUps.research.changeRerolls(1)
|
|
},
|
|
isMakingBots: false, //to prevent bot fabrication from running 2 sessions at once
|
|
changeRerolls(amount) {
|
|
if (amount !== 0) powerUps.research.count += amount
|
|
if (tech.isRerollBots && !this.isMakingBots) {
|
|
let cycle = () => {
|
|
const cost = 2 + Math.floor(0.2 * b.totalBots())
|
|
if (m.alive && powerUps.research.count >= cost) {
|
|
requestAnimationFrame(cycle);
|
|
this.isMakingBots = true
|
|
} else {
|
|
this.isMakingBots = false
|
|
}
|
|
if (!simulation.paused && !simulation.isChoosing && !(simulation.cycle % 60)) {
|
|
powerUps.research.count -= cost
|
|
b.randomBot()
|
|
if (tech.renormalization) {
|
|
for (let i = 0; i < cost; i++) {
|
|
if (Math.random() < 0.44) {
|
|
m.fieldCDcycle = m.cycle + 20;
|
|
powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "research");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
requestAnimationFrame(cycle);
|
|
}
|
|
if (tech.isDeathAvoid && document.getElementById("tech-anthropic")) {
|
|
document.getElementById("tech-anthropic").innerHTML = `-${powerUps.research.count}`
|
|
}
|
|
if (tech.renormalization && Math.random() < 0.44 && amount < 0) {
|
|
for (let i = 0, len = -amount; i < len; i++) powerUps.spawn(m.pos.x, m.pos.y, "research");
|
|
}
|
|
if (tech.isRerollHaste) {
|
|
if (powerUps.research.count === 0) {
|
|
tech.researchHaste = 0.66;
|
|
b.setFireCD();
|
|
} else {
|
|
tech.researchHaste = 1;
|
|
b.setFireCD();
|
|
}
|
|
}
|
|
},
|
|
currentRerollCount: 0,
|
|
use(type) { //runs when you actually research a list of selections, type can be field, gun, or tech
|
|
if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) {
|
|
tech.addJunkTechToPool(tech.junkResearchNumber * 0.01)
|
|
} else {
|
|
powerUps.research.changeRerolls(-1)
|
|
}
|
|
powerUps.research.currentRerollCount++
|
|
// if (tech.isBanish && type === 'tech') { // banish researched tech
|
|
// const banishLength = tech.isDeterminism ? 1 : 3 + tech.extraChoices * 2
|
|
// for (let i = 0; i < banishLength; i++) {
|
|
// const index = powerUps.tech.choiceLog.length - i - 1
|
|
// if (powerUps.tech.choiceLog[index] && tech.tech[powerUps.tech.choiceLog[index]]) tech.tech[powerUps.tech.choiceLog[index]].isBanished = true
|
|
// }
|
|
// simulation.makeTextLog(`powerUps.tech.length: ${Math.max(0,powerUps.tech.lastTotalChoices - banishLength)}`)
|
|
// }
|
|
if (tech.isResearchReality) {
|
|
m.switchWorlds()
|
|
simulation.trails()
|
|
simulation.makeTextLog(`simulation.amplitude <span class='color-symbol'>=</span> ${Math.random()}`);
|
|
}
|
|
powerUps[type].effect();
|
|
},
|
|
},
|
|
heal: {
|
|
name: "heal",
|
|
color: "#0eb",
|
|
size() {
|
|
return Math.sqrt(0.1 + 0.25) * 40 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1)) * (tech.isFlipFlopOn && tech.isFlipFlopHealth ? Math.sqrt(2) : 1); //(simulation.healScale ** 0.25) gives a smaller radius as heal scale goes down
|
|
},
|
|
effect() {
|
|
if (!tech.isEnergyHealth && m.alive) {
|
|
let heal = (this.size / 40 / (simulation.healScale ** 0.25)) ** 2 //simulation.healScale is undone here because heal scale is already properly affected on m.addHealth()
|
|
// console.log("size = " + this.size, "heal = " + heal)
|
|
if (heal > 0) {
|
|
const overHeal = m.health + heal * simulation.healScale - m.maxHealth //used with tech.isOverHeal
|
|
const healOutput = Math.min(m.maxHealth - m.health, heal) * simulation.healScale
|
|
m.addHealth(heal);
|
|
if (healOutput > 0) simulation.makeTextLog(`<span class='color-var'>m</span>.health <span class='color-symbol'>+=</span> ${(healOutput).toFixed(3)}`) // <br>${m.health.toFixed(3)}
|
|
if (tech.isOverHeal && overHeal > 0) { //tech quenching
|
|
const scaledOverHeal = overHeal * 0.9
|
|
m.damage(scaledOverHeal);
|
|
simulation.makeTextLog(`<span class='color-var'>m</span>.health <span class='color-symbol'>-=</span> ${(scaledOverHeal).toFixed(3)}`) // <br>${m.health.toFixed(3)}
|
|
simulation.drawList.push({ //add dmg to draw queue
|
|
x: m.pos.x,
|
|
y: m.pos.y,
|
|
radius: scaledOverHeal * 500 * simulation.healScale,
|
|
color: simulation.mobDmgColor,
|
|
time: simulation.drawTime
|
|
});
|
|
tech.extraMaxHealth += scaledOverHeal * simulation.healScale //increase max health
|
|
m.setMaxHealth();
|
|
} else if (overHeal > 0.1) {
|
|
requestAnimationFrame(() => {
|
|
powerUps.directSpawn(this.position.x, this.position.y, "heal", true, null, overHeal * 40 * (simulation.healScale ** 0.25))// directSpawn(x, y, target, moving = true, mode = null, size = powerUps[target].size()) {
|
|
});
|
|
}
|
|
if (tech.isHealBrake) {
|
|
const totalTime = 900
|
|
//check if you already have this effect
|
|
let foundActiveEffect = false
|
|
for (let i = 0; i < simulation.ephemera.length; i++) {
|
|
if (simulation.ephemera[i].name === "healPush") {
|
|
foundActiveEffect = true
|
|
simulation.ephemera[i].count = 0.5 * simulation.ephemera[i].count + totalTime //add time
|
|
simulation.ephemera[i].scale = 0.5 * (simulation.ephemera[i].scale + Math.min(Math.max(0.6, heal * 6), 2.3)) //take average of scale
|
|
}
|
|
}
|
|
if (!foundActiveEffect) {
|
|
simulation.ephemera.push({
|
|
name: "healPush",
|
|
count: totalTime, //cycles before it self removes
|
|
range: 0,
|
|
scale: Math.min(Math.max(0.7, heal * 4), 2.2), //typically heal is 0.35
|
|
do() {
|
|
this.count--
|
|
if (this.count < 0) simulation.removeEphemera(this.name)
|
|
this.range = this.range * 0.99 + 0.01 * (300 * this.scale + 100 * Math.sin(m.cycle * 0.022))
|
|
if (this.count < 120) this.range -= 5 * this.scale
|
|
this.range = Math.max(this.range, 1) //don't go negative
|
|
// const range = 300 + 100 * Math.sin(m.cycle * 0.022)
|
|
for (let i = 0; i < mob.length; i++) {
|
|
const distance = Vector.magnitude(Vector.sub(m.pos, mob[i].position))
|
|
if (distance < this.range) {
|
|
const cap = mob[i].isShielded ? 3 : 1
|
|
if (mob[i].speed > cap && Vector.dot(mob[i].velocity, Vector.sub(m.pos, mob[i].position)) > 0) { // if velocity is directed towards player
|
|
Matter.Body.setVelocity(mob[i], Vector.mult(Vector.normalise(mob[i].velocity), cap)); //set velocity to cap, but keep the direction
|
|
}
|
|
}
|
|
}
|
|
ctx.beginPath();
|
|
ctx.arc(m.pos.x, m.pos.y, this.range, 0, 2 * Math.PI);
|
|
ctx.fillStyle = "hsla(200,50%,61%,0.18)";
|
|
ctx.fill();
|
|
},
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (powerUps.healGiveMaxEnergy) {
|
|
tech.healMaxEnergyBonus += 0.08 * tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1)
|
|
m.setMaxEnergy();
|
|
}
|
|
},
|
|
spawn(x, y, size) { //used to spawn a heal with a specific size / heal amount, not normally used
|
|
powerUps.directSpawn(x, y, "heal", false, null, size)
|
|
if (Math.random() < tech.duplicationChance()) {
|
|
powerUps.directSpawn(x, y, "heal", false, null, size)
|
|
powerUp[powerUp.length - 1].isDuplicated = true
|
|
}
|
|
}
|
|
},
|
|
ammo: {
|
|
name: "ammo",
|
|
color: "#467",
|
|
size() {
|
|
return 17;
|
|
},
|
|
effect() {
|
|
if (b.inventory.length > 0) {
|
|
if (tech.isAmmoForGun && b.activeGun !== null) { //give extra ammo to one gun only with tech logistics
|
|
const target = b.guns[b.activeGun]
|
|
if (target.ammo !== Infinity) {
|
|
if (tech.ammoCap) {
|
|
const ammoAdded = Math.ceil(target.ammoPack * 0.7 * tech.ammoCap * 0.8) //0.7 is average
|
|
target.ammo = ammoAdded
|
|
// simulation.makeTextLog(`${target.name}.<span class='color-g'>ammo</span> <span class='color-symbol'>=</span> ${ammoAdded}`)
|
|
} else {
|
|
const ammoAdded = Math.ceil((0.7 * Math.random() + 0.7 * Math.random()) * target.ammoPack * 0.8)
|
|
target.ammo += ammoAdded
|
|
// simulation.makeTextLog(`${target.name}.<span class='color-g'>ammo</span> <span class='color-symbol'>+=</span> ${ammoAdded}`)
|
|
}
|
|
}
|
|
} else { //give ammo to all guns in inventory
|
|
// let textLog = ""
|
|
for (let i = 0, len = b.inventory.length; i < len; i++) {
|
|
const target = b.guns[b.inventory[i]]
|
|
if (target.ammo !== Infinity) {
|
|
if (tech.ammoCap) {
|
|
const ammoAdded = Math.ceil(target.ammoPack * 0.45 * tech.ammoCap) //0.45 is average
|
|
target.ammo = ammoAdded
|
|
// textLog += `${target.name}.<span class='color-g'>ammo</span> <span class='color-symbol'>=</span> ${ammoAdded}<br>`
|
|
} else {
|
|
const ammoAdded = Math.ceil((0.45 * Math.random() + 0.45 * Math.random()) * target.ammoPack) //Math.ceil(Math.random() * target.ammoPack)
|
|
target.ammo += ammoAdded
|
|
// textLog += `${target.name}.<span class='color-g'>ammo</span> <span class='color-symbol'>+=</span> ${ammoAdded}<br>`
|
|
}
|
|
}
|
|
}
|
|
// simulation.makeTextLog(textLog)
|
|
}
|
|
// } else { //give ammo to all guns in inventory
|
|
// for (let i = 0, len = b.inventory.length; i < len; i++) {
|
|
// const target = b.guns[b.inventory[i]]
|
|
// if (target.ammo !== Infinity) {
|
|
// if (tech.ammoCap) {
|
|
// const ammoAdded = Math.ceil(target.ammoPack * 0.45 * tech.ammoCap) //0.45 is average
|
|
// target.ammo = ammoAdded
|
|
// simulation.makeTextLog(`${target.name}.<span class='color-g'>ammo</span> <span class='color-symbol'>=</span> ${ammoAdded}`)
|
|
// } else {
|
|
// const ammoAdded = Math.ceil((0.45 * Math.random() + 0.45 * Math.random()) * target.ammoPack) //Math.ceil(Math.random() * target.ammoPack)
|
|
// target.ammo += ammoAdded
|
|
// simulation.makeTextLog(`${target.name}.<span class='color-g'>ammo</span> <span class='color-symbol'>+=</span> ${ammoAdded}`)
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
simulation.updateGunHUD();
|
|
}
|
|
}
|
|
},
|
|
cancelText(type) {
|
|
// if (localSettings.isHideImages) { }
|
|
|
|
if (tech.isSuperDeterminism) {
|
|
return `<div></div>`
|
|
} else if (tech.isCancelTech) {
|
|
return `<div class='cancel-card' onclick='powerUps.endDraft("${type}",true)' style="width: 115px;">randomize</div>`
|
|
} else {
|
|
return `<div class='cancel-card' onclick='powerUps.endDraft("${type}",true)' style="width: 85px;">cancel</div>`
|
|
}
|
|
},
|
|
researchText(type) {
|
|
let text = ""
|
|
if (type === "entanglement") {
|
|
text += `<div class='choose-grid-module entanglement flipX' onclick='powerUps.endDraft("${type}",true)'>entanglement</div>`
|
|
} else if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) {
|
|
text += `<div onclick="powerUps.research.use('${type}')" class='research-card'>` // style = "margin-left: 192px; margin-right: -192px;"
|
|
tech.junkResearchNumber = Math.ceil(4 * Math.random())
|
|
text += `<div><div> <span style="position:relative;">`
|
|
for (let i = 0; i < tech.junkResearchNumber; i++) {
|
|
text += `<div class="circle-grid junk" style="position:absolute; top:0; left:${15 * i}px ;opacity:0.8; border: 1px #fff solid;width: 1.15em;height: 1.15em;"></div>`
|
|
}
|
|
text += `</span> <span class='research-select'>pseudoscience</span></div></div></div>`
|
|
} else if (powerUps.research.count > 0) {
|
|
text += `<div onclick="powerUps.research.use('${type}')" class='research-card' >` // style = "margin-left: 192px; margin-right: -192px;"
|
|
text += `<div><div><span style="position:relative;">`
|
|
for (let i = 0, len = Math.min(powerUps.research.count, 30); i < len; i++) text += `<div class="circle-grid research" style="font-size:0.82em; position:absolute; top:0; left:${(18 - len * 0.21) * i}px ;opacity:0.8; border: 1px #fff solid;"></div>`
|
|
text += `</span> <span class='research-select'>${tech.isResearchReality ? "<span class='alt'>alternate reality</span>" : "research"}</span></div></div></div>`
|
|
} else {
|
|
text += `<div></div>`
|
|
}
|
|
return text
|
|
},
|
|
researchAndCancelText(type) {
|
|
let text = `<div class='research-cancel'>`
|
|
if (type === "entanglement") {
|
|
text += `<span class='research-card entanglement flipX' style="width: 275px;" onclick='powerUps.endDraft("${type}",true)'><span style="letter-spacing: 6px;">entanglement</span></span>` //‌
|
|
} else if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) {
|
|
text += `<span onclick="powerUps.research.use('${type}')" class='research-card' style="width: 275px;float: left;">` // style = "margin-left: 192px; margin-right: -192px;"
|
|
tech.junkResearchNumber = Math.ceil(4 * Math.random())
|
|
text += `<div><div><span style="position:relative;">`
|
|
for (let i = 0, len = tech.junkResearchNumber; i < len; i++) {
|
|
text += `<div class="circle-grid junk" style="position:absolute; top:0; left:${15 * i}px ;opacity:0.8; border: 1px #fff solid;width: 1.15em;height: 1.15em;"></div>`
|
|
}
|
|
text += `</span> <span class='research-select'>${tech.isResearchReality ? "<span class='alt'>alternate reality</span>" : "research"}</span></div></div></span>`
|
|
} else if (powerUps.research.count > 0) {
|
|
text += `<span onclick="powerUps.research.use('${type}')" class='research-card' style="width: 275px;float: left;">` // style = "margin-left: 192px; margin-right: -192px;"
|
|
text += `<div><div><span style="position:relative;">`
|
|
let researchCap = 18
|
|
if (tech.isCancelTech) researchCap -= 2
|
|
if (canvas.width < 1951) researchCap -= 3
|
|
if (canvas.width < 1711) researchCap -= 4
|
|
for (let i = 0, len = Math.min(powerUps.research.count, researchCap); i < len; i++) {
|
|
text += `<div class="circle-grid research" style="font-size:0.82em; position:absolute; top:0; left:${(18 - len * 0.21) * i}px ;opacity:0.8; border: 1px #fff solid;"></div>`
|
|
}
|
|
text += `</span> <span class='research-select'>${tech.isResearchReality ? "<span class='alt'>alternate reality</span>" : "research"}</span></div></div></span>`
|
|
} else {
|
|
text += `<span class='research-card' style="width: 275px;float: right; background-color: #aaa;color:#888;">research</span>` //‌
|
|
}
|
|
if (tech.isSuperDeterminism) {
|
|
text += `<span class='cancel-card' style="width: 95px;float: right;background-color: #aaa;color:#888;">cancel</span>`
|
|
} else if (tech.isCancelTech) {
|
|
text += `<span class='cancel-card' onclick='powerUps.endDraft("${type}",true)' style="width: 115px;float: right;font-size:0.9em;padding-top:5px">randomize</span>`
|
|
} else {
|
|
text += `<span class='cancel-card' onclick='powerUps.endDraft("${type}",true)' style="width: 95px;float: right;">cancel</span>`
|
|
}
|
|
|
|
return text + "</div>"
|
|
},
|
|
buildColumns(totalChoices, type) {
|
|
let width
|
|
if (canvas.width < 1710) {
|
|
width = "285px"
|
|
} else if (canvas.width < 1950) {
|
|
width = "340px"
|
|
} else {
|
|
width = "384px"
|
|
}
|
|
|
|
let text = ""
|
|
if (localSettings.isHideImages) {
|
|
document.getElementById("choose-grid").style.gridTemplateColumns = width
|
|
text += powerUps.researchAndCancelText(type)
|
|
} else if (totalChoices === 1 || canvas.width < 1200) {
|
|
document.getElementById("choose-grid").style.gridTemplateColumns = width
|
|
text += powerUps.researchAndCancelText(type)
|
|
// console.log('hi')
|
|
// text += powerUps.cancelText(type)
|
|
// text += powerUps.researchText(type)
|
|
} else if (totalChoices === 2) {
|
|
document.getElementById("choose-grid").style.gridTemplateColumns = `repeat(2, ${width})`
|
|
text += powerUps.researchText(type)
|
|
text += powerUps.cancelText(type)
|
|
} else {
|
|
document.getElementById("choose-grid").style.gridTemplateColumns = `repeat(3, ${width})`
|
|
text += "<div></div>"
|
|
text += powerUps.researchText(type)
|
|
text += powerUps.cancelText(type)
|
|
}
|
|
return text
|
|
},
|
|
// researchAndCancelText(type) {
|
|
// let text = "<div class= 'choose-grid-module'>"
|
|
// if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) {
|
|
// text += `<div onclick="powerUps.research.use('${type}')" class='choose-grid-module research-card'>` // style = "margin-left: 192px; margin-right: -192px;"
|
|
// tech.junkResearchNumber = Math.ceil(4 * Math.random())
|
|
// text += `<div><div> <span style="position:relative;">`
|
|
// for (let i = 0; i < tech.junkResearchNumber; i++) text += `<div class="circle-grid junk" style="position:absolute; top:0; left:${15*i}px ;opacity:0.8; border: 1px #fff solid;"></div>`
|
|
// text += `</span> <span class='research-select'>pseudoscience</span></div></div></div>`
|
|
// } else if (powerUps.research.count > 0) {
|
|
// text += `<div onclick="powerUps.research.use('${type}')" class='choose-grid-module research-card' >` // style = "margin-left: 192px; margin-right: -192px;"
|
|
// text += `<div><div><span style="position:relative;">`
|
|
// for (let i = 0, len = Math.min(powerUps.research.count, 30); i < len; i++) text += `<div class="circle-grid research" style="position:absolute; top:0; left:${(18 - len*0.21)*i}px ;opacity:0.8; border: 1px #fff solid;"></div>`
|
|
// text += `</span> <span class='research-select'>${tech.isResearchReality?"<span class='alt'>alternate reality</span>": "research"}</span></div></div></div>`
|
|
// } else {
|
|
// text += `<div></div>`
|
|
// }
|
|
// return text + '</div>'
|
|
// },
|
|
hideStyle: `style="height:auto; border: none; background-color: transparent;"`,
|
|
gunText(choose, click) {
|
|
const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/gun/${b.guns[choose].name}.webp');"`
|
|
return `<div class="choose-grid-module card-background" onclick="${click}" onauxclick="${click}" ${style}>
|
|
<div class="card-text">
|
|
<div class="grid-title"><div class="circle-grid gun"></div> ${b.guns[choose].name}</div>
|
|
${b.guns[choose].description}</div></div>`
|
|
},
|
|
fieldText(choose, click) {
|
|
const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/field/${m.fieldUpgrades[choose].name}${choose === 0 ? Math.floor(Math.random() * 10) : ""}.webp');"`
|
|
return `<div class="choose-grid-module card-background" onclick="${click}" onauxclick="${click}"${style}>
|
|
<div class="card-text">
|
|
<div class="grid-title"><div class="circle-grid field"></div> ${m.fieldUpgrades[choose].name}</div>
|
|
${m.fieldUpgrades[choose].description}</div></div>`
|
|
},
|
|
techText(choose, click) {
|
|
const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : "";
|
|
const style = localSettings.isHideImages || tech.tech[choose].isLore ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"`
|
|
return `<div class="choose-grid-module card-background" onclick="${click}" onauxclick="${click}"${style}>
|
|
<div class="card-text">
|
|
<div class="grid-title"><div class="circle-grid tech"></div> ${tech.tech[choose].name} ${techCountText}</div>
|
|
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}</div></div>`
|
|
},
|
|
skinTechText(choose, click) {
|
|
const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : "";
|
|
const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"`
|
|
return `<div class="choose-grid-module card-background" onclick="${click}" onauxclick="${click}"${style}>
|
|
<div class="card-text">
|
|
<div class="grid-title">
|
|
<span style="position:relative;">
|
|
<div class="circle-grid-skin"></div>
|
|
<div class="circle-grid-skin-eye"></div>
|
|
</span>
|
|
${tech.tech[choose].name} ${techCountText}</div>
|
|
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}</div></div>`
|
|
},
|
|
fieldTechText(choose, click) {
|
|
const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : "";
|
|
const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"`
|
|
return `<div class="choose-grid-module card-background" onclick="${click}" onauxclick="${click}"${style}>
|
|
<div class="card-text">
|
|
<div class="grid-title">
|
|
<span style="position:relative;">
|
|
<div class="circle-grid tech" style="position:absolute; top:0; left:0;opacity:0.8;"></div>
|
|
<div class="circle-grid field" style="position:absolute; top:0; left:10px;opacity:0.65;"></div>
|
|
</span>
|
|
${tech.tech[choose].name} ${techCountText}</div>
|
|
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}</div></div>`
|
|
},
|
|
gunTechText(choose, click) {
|
|
const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : "";
|
|
const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"`
|
|
return `<div class="choose-grid-module card-background" onclick="${click}" onauxclick="${click}"${style}>
|
|
<div class="card-text">
|
|
<div class="grid-title">
|
|
<span style="position:relative;">
|
|
<div class="circle-grid tech" style="position:absolute; top:0; left:0;opacity:0.8;"></div>
|
|
<div class="circle-grid gun" style="position:absolute; top:0; left:10px; opacity:0.65;"></div>
|
|
</span>
|
|
${tech.tech[choose].name} ${techCountText}</div>
|
|
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}</div></div>`
|
|
},
|
|
junkTechText(choose, click) {
|
|
const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : "";
|
|
const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-size: contain;background-repeat: no-repeat;background-image: url('img/junk.webp');"`
|
|
if (!localSettings.isHideImages) {
|
|
setTimeout(() => { //delay so that the html element exists
|
|
if (tech.tech[choose].url === undefined) { //if on url has been set yet
|
|
const url = "https://images.search.yahoo.com/search/images?p=" + tech.tech[choose].name;
|
|
fetch(url, { signal: AbortSignal.timeout(1000) }) //give up if it takes over 1 second
|
|
.then((response) => response.text())
|
|
.then((html) => {
|
|
const parser = new DOMParser();
|
|
const doc = parser.parseFromString(html, "text/html");
|
|
const elements = doc.getElementsByClassName("ld");
|
|
// console.log(i, elements[i].getAttribute("data"), JSON.parse(elements[i].getAttribute("data")).iurl)
|
|
const index = Math.floor(Math.random() * 4) //randomly choose from the first 4 images
|
|
if (parseInt(JSON.parse(elements[index].getAttribute("data")).s.slice(0, -2)) < 500) { //make sure it isn't too big
|
|
tech.tech[choose].url = JSON.parse(elements[index].getAttribute("data")).iurl //store the url
|
|
document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` //make the url the background image
|
|
} else if (parseInt(JSON.parse(elements[index + 1].getAttribute("data")).s.slice(0, -2)) < 500) { //try a different images and see if it is smaller
|
|
tech.tech[choose].url = JSON.parse(elements[index + 1].getAttribute("data")).iurl
|
|
document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')`
|
|
} else if (parseInt(JSON.parse(elements[index + 2].getAttribute("data")).s.slice(0, -2)) < 500) { //try a different images and see if it is smaller
|
|
tech.tech[choose].url = JSON.parse(elements[index + 2].getAttribute("data")).iurl
|
|
document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')`
|
|
}
|
|
});
|
|
} else {
|
|
document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')`
|
|
}
|
|
}, 1);
|
|
}
|
|
return `<div id = "junk-${choose}" class="choose-grid-module card-background" onclick="${click}" onauxclick="${click}"${style}>
|
|
<div class="card-text">
|
|
<div class="grid-title"><div class="circle-grid junk"></div> ${tech.tech[choose].name} ${techCountText}</div>
|
|
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}</div></div>`
|
|
},
|
|
incoherentTechText(choose, click) {
|
|
// text += `<div class="choose-grid-module" style = "background-color: #efeff5; border: 0px; opacity:0.5; font-size: 60%; line-height: 130%; margin: 1px; padding-top: 6px; padding-bottom: 6px;"><div class="grid-title">${tech.tech[choose].name} <span style = "color: #aaa;font-weight: normal;font-size:80%;">- incoherent</span></div></div>`
|
|
const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"`
|
|
return `<div class="choose-grid-module card-background" ${style}>
|
|
<div class="card-text" style = "background-color: #efeff5;">
|
|
<div class="grid-title" style = "color: #ddd;font-weight: normal;">incoherent</div> <br> <br>
|
|
</div></div>`
|
|
},
|
|
gun: {
|
|
name: "gun",
|
|
color: "#26a",
|
|
size() {
|
|
return 35;
|
|
},
|
|
effect() {
|
|
if (m.alive) {
|
|
let options = [];
|
|
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 : 2 + tech.extraChoices + 2 * (m.fieldMode === 8)))
|
|
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++) {
|
|
if (options[i] === index) {
|
|
options.splice(i, 1) //remove a previous choice from option pool
|
|
return
|
|
}
|
|
}
|
|
}
|
|
//check for guns that were a choice last time and remove them
|
|
for (let i = 0; i < b.guns.length; i++) {
|
|
if (options.length - 1 < totalChoices) break //you have to repeat choices if there are not enough choices left to display
|
|
if (b.guns[i].isRecentlyShown) removeOption(i)
|
|
}
|
|
for (let i = 0; i < b.guns.length; i++) b.guns[i].isRecentlyShown = false //reset recently shown back to zero
|
|
if (options.length > 0) {
|
|
let text = powerUps.buildColumns(totalChoices, "gun")
|
|
for (let i = 0; i < totalChoices; i++) {
|
|
const choose = options[Math.floor(Math.seededRandom(0, options.length))] //pick an element from the array of options
|
|
// text += `<div class="choose-grid-module" onclick="powerUps.choose('gun',${choose})"><div class="grid-title"><div class="circle-grid gun"></div> ${b.guns[choose].name}</div> ${b.guns[choose].description}</div>`
|
|
text += powerUps.gunText(choose, `powerUps.choose('gun',${choose})`)
|
|
|
|
b.guns[choose].isRecentlyShown = true
|
|
removeOption(choose)
|
|
if (options.length < 1) break
|
|
}
|
|
if (tech.isExtraBotOption) {
|
|
const botTech = [] //make an array of bot options
|
|
for (let i = 0, len = tech.tech.length; i < len; i++) {
|
|
if (tech.tech[i].isBotTech && tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed()) botTech.push(i)
|
|
}
|
|
if (botTech.length > 0) { //pick random bot tech
|
|
// const choose = botTech[Math.floor(Math.random() * botTech.length)];
|
|
// const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : "";
|
|
// text += `<div class="choose-grid-module" onclick="powerUps.choose('tech',${choose})"><div class="grid-title"> <span id = "cellular-rule-id${this.id}" style = "font-size: 150%;font-family: 'Courier New', monospace;">⭓▸●■</span> ${tech.tech[choose].name} ${isCount}</div>${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}</div>`
|
|
const choose = botTech[Math.floor(Math.random() * botTech.length)];
|
|
const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : "";
|
|
const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"`
|
|
text += `<div class="choose-grid-module card-background" onclick="powerUps.choose('tech',${choose})" ${style}>
|
|
<div class="card-text">
|
|
<div class="grid-title"><span id = "cellular-rule-id${this.id}" style = "font-size: 150%;font-family: 'Courier New', monospace;">⭓▸●■</span> ${tech.tech[choose].name} ${techCountText}</div>
|
|
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}</div></div>`
|
|
}
|
|
}
|
|
if (tech.isOneGun && b.inventory.length > 0) text += `<div style = "color: #f24">replaces your current gun</div>`
|
|
document.getElementById("choose-grid").innerHTML = text
|
|
powerUps.showDraft();
|
|
}
|
|
}
|
|
},
|
|
},
|
|
field: {
|
|
name: "field",
|
|
color: "#0cf",
|
|
size() {
|
|
return 45;
|
|
},
|
|
effect() {
|
|
if (m.alive) {
|
|
let options = [];
|
|
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 : 2 + tech.extraChoices + 2 * (m.fieldMode === 8)))
|
|
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++) {
|
|
if (options[i] === index) {
|
|
options.splice(i, 1) //remove a previous choice from option pool
|
|
return
|
|
}
|
|
}
|
|
}
|
|
//check for fields that were a choice last time and remove them
|
|
for (let i = 0; i < m.fieldUpgrades.length; i++) {
|
|
if (options.length - 1 < totalChoices) break //you have to repeat choices if there are not enough choices left to display
|
|
if (m.fieldUpgrades[i].isRecentlyShown) removeOption(i)
|
|
}
|
|
for (let i = 0; i < m.fieldUpgrades.length; i++) m.fieldUpgrades[i].isRecentlyShown = false //reset recently shown back to zero
|
|
|
|
if (options.length > 0 || tech.isExtraBotOption) {
|
|
let text = powerUps.buildColumns(totalChoices, "field")
|
|
for (let i = 0; i < totalChoices; i++) {
|
|
const choose = options[Math.floor(Math.seededRandom(0, options.length))] //pick an element from the array of options
|
|
//text += `<div class="choose-grid-module" onclick="powerUps.choose('field',${choose})"><div class="grid-title"><div class="circle-grid field"></div> ${m.fieldUpgrades[choose].name}</div> ${m.fieldUpgrades[choose].description}</div>` //default
|
|
text += powerUps.fieldText(choose, `powerUps.choose('field',${choose})`)
|
|
m.fieldUpgrades[choose].isRecentlyShown = true
|
|
removeOption(choose)
|
|
if (options.length < 1) break
|
|
}
|
|
if (tech.isExtraBotOption) {
|
|
const botTech = [] //make an array of bot options
|
|
for (let i = 0, len = tech.tech.length; i < len; i++) {
|
|
if (tech.tech[i].isBotTech && tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed()) botTech.push(i)
|
|
}
|
|
if (botTech.length > 0) { //pick random bot tech
|
|
// const choose = botTech[Math.floor(Math.random() * botTech.length)];
|
|
// const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : "";
|
|
// text += `<div class="choose-grid-module" onclick="powerUps.choose('tech',${choose})"><div class="grid-title"> <span id = "cellular-rule-id${this.id}" style = "font-size: 150%;font-family: 'Courier New', monospace;">⭓▸●■</span> ${tech.tech[choose].name} ${isCount}</div>${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}</div>`
|
|
const choose = botTech[Math.floor(Math.random() * botTech.length)];
|
|
const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : "";
|
|
const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"`
|
|
text += `<div class="choose-grid-module card-background" onclick="powerUps.choose('tech',${choose})" ${style}>
|
|
<div class="card-text">
|
|
<div class="grid-title"><span id = "cellular-rule-id${this.id}" style = "font-size: 150%;font-family: 'Courier New', monospace;">⭓▸●■</span> ${tech.tech[choose].name} ${techCountText}</div>
|
|
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}</div></div>`
|
|
}
|
|
}
|
|
document.getElementById("choose-grid").innerHTML = text
|
|
powerUps.showDraft();
|
|
}
|
|
}
|
|
},
|
|
},
|
|
tech: {
|
|
name: "tech",
|
|
color: "hsl(246,100%,77%)", //"#a8f",
|
|
size() {
|
|
return 42;
|
|
},
|
|
effect() {
|
|
if (m.alive) {
|
|
let junkCount = 0 //used for junk estimation
|
|
let totalCount = 0 //used for junk estimation
|
|
let options = []; //generate all options
|
|
optionLengthNoDuplicates = 0
|
|
for (let i = 0; i < tech.tech.length; i++) {
|
|
if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isBanished) {
|
|
totalCount += tech.tech[i].frequency
|
|
if (tech.tech[i].isJunk) junkCount += tech.tech[i].frequency
|
|
if (tech.tech[i].frequency > 0) optionLengthNoDuplicates++
|
|
for (let j = 0, len = tech.tech[i].frequency; j < len; j++) options.push(i);
|
|
}
|
|
}
|
|
function removeOption(index) {
|
|
for (let i = options.length - 1; i > -1; i--) {
|
|
if (index === options[i]) {
|
|
options.splice(i, 1) //remove all copies of that option form the options array (some tech are in the options array multiple times because of frequency)
|
|
optionLengthNoDuplicates--
|
|
}
|
|
if (options.length < 1) return;
|
|
}
|
|
}
|
|
//set total choices
|
|
let totalChoices = (tech.isDeterminism ? 1 : 3 + tech.extraChoices + 2 * (m.fieldMode === 8))
|
|
if (tech.isFlipFlopChoices) totalChoices += tech.isRelay ? (tech.isFlipFlopOn ? -1 : 7) : (tech.isFlipFlopOn ? 7 : -1) //flip the order for relay
|
|
if (optionLengthNoDuplicates < totalChoices + 1) { //if not enough options for all the choices
|
|
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)
|
|
}
|
|
simulation.makeTextLog(`decoherence <span class='color-var'>tech</span> ejected`)
|
|
simulation.makeTextLog(`options reset`)
|
|
}
|
|
}
|
|
if (tech.tooManyTechChoices) {
|
|
tech.tooManyTechChoices = false
|
|
totalChoices = optionLengthNoDuplicates
|
|
}
|
|
if (optionLengthNoDuplicates > totalChoices) { //check for tech that were a choice last time and remove them
|
|
for (let i = 0; i < tech.tech.length; i++) {
|
|
if (optionLengthNoDuplicates > totalChoices) {
|
|
if (tech.tech[i].isRecentlyShown) removeOption(i)
|
|
} else {
|
|
break //you have to repeat choices if there are not enough choices left to display
|
|
}
|
|
|
|
}
|
|
}
|
|
for (let i = 0; i < tech.tech.length; i++) tech.tech[i].isRecentlyShown = false //reset recently shown back to zero
|
|
if (options.length > 0) {
|
|
let text = powerUps.buildColumns(totalChoices, "tech")
|
|
for (let i = 0; i < totalChoices; i++) {
|
|
if (options.length < 1) break
|
|
const choose = options[Math.floor(Math.seededRandom(0, options.length))] //pick an element from the array of options
|
|
if (tech.isBanish) {
|
|
tech.tech[choose].isBanished = true
|
|
if (i === 0) simulation.makeTextLog(`options.length = ${optionLengthNoDuplicates}`)
|
|
}
|
|
removeOption(choose) //move from future options pool to avoid repeats on this selection
|
|
tech.tech[choose].isRecentlyShown = true //this flag prevents this option from being shown the next time you pick up a tech power up
|
|
const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : "";
|
|
if (tech.tech[choose].isFieldTech) {
|
|
text += powerUps.fieldTechText(choose, `powerUps.choose('tech',${choose})`)
|
|
} else if (tech.tech[choose].isGunTech) {
|
|
text += powerUps.gunTechText(choose, `powerUps.choose('tech',${choose})`)
|
|
} else if (tech.tech[choose].isJunk) {
|
|
text += powerUps.junkTechText(choose, `powerUps.choose('tech',${choose})`)
|
|
} else if (tech.tech[choose].isSkin) {
|
|
text += powerUps.skinTechText(choose, `powerUps.choose('tech',${choose})`)
|
|
} else { //normal tech
|
|
text += powerUps.techText(choose, `powerUps.choose('tech',${choose})`)
|
|
}
|
|
if (options.length < 1) break
|
|
}
|
|
if (tech.isExtraBotOption) {
|
|
const botTech = [] //make an array of bot options
|
|
for (let i = 0, len = tech.tech.length; i < len; i++) {
|
|
if (tech.tech[i].isBotTech && tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isRecentlyShown) botTech.push(i)
|
|
}
|
|
if (botTech.length > 0) { //pick random bot tech
|
|
// const choose = botTech[Math.floor(Math.random() * botTech.length)];
|
|
// const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : "";
|
|
// text += `<div class="choose-grid-module" onclick="powerUps.choose('tech',${choose})"><div class="grid-title"> <span id = "cellular-rule-id${this.id}" style = "font-size: 150%;font-family: 'Courier New', monospace;">⭓▸●■</span> ${tech.tech[choose].name} ${isCount}</div> ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}</div>`
|
|
const choose = botTech[Math.floor(Math.random() * botTech.length)];
|
|
const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : "";
|
|
const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"`
|
|
text += `<div class="choose-grid-module card-background" onclick="powerUps.choose('tech',${choose})" ${style}>
|
|
<div class="card-text">
|
|
<div class="grid-title"><span id = "cellular-rule-id${this.id}" style = "font-size: 150%;font-family: 'Courier New', monospace;">⭓▸●■</span> ${tech.tech[choose].name} ${techCountText}</div>
|
|
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}</div></div>`
|
|
}
|
|
}
|
|
|
|
if (tech.isExtraGunField) {
|
|
if (Math.random() > 0.5 && b.inventory.length < b.guns.length) {
|
|
let gunOptions = [];
|
|
for (let i = 0; i < b.guns.length; i++) {
|
|
if (!b.guns[i].have) gunOptions.push(i);
|
|
}
|
|
const pick = gunOptions[Math.floor(Math.seededRandom(0, gunOptions.length))] //pick an element from the array of options
|
|
// text += `<div class="choose-grid-module" onclick="powerUps.choose('gun',${pick})"><div class="grid-title"><div class="circle-grid gun"></div> ${b.guns[pick].name}</div> ${b.guns[pick].description}</div>`
|
|
text += powerUps.gunText(pick, `powerUps.choose('gun',${pick})`)
|
|
} else {
|
|
let fieldOptions = [];
|
|
for (let i = 1; i < m.fieldUpgrades.length; i++) { //skip field emitter
|
|
if (i !== m.fieldMode) fieldOptions.push(i);
|
|
}
|
|
const pick = fieldOptions[Math.floor(Math.seededRandom(0, fieldOptions.length))] //pick an element from the array of options
|
|
// text += `<div class="choose-grid-module" onclick="powerUps.choose('field',${pick})"><div class="grid-title"><div class="circle-grid field"></div> ${m.fieldUpgrades[pick].name}</div> ${m.fieldUpgrades[pick].description}</div>`
|
|
text += powerUps.fieldText(pick, `powerUps.choose('field',${pick})`)
|
|
}
|
|
}
|
|
// if (tech.isMicroTransactions && powerUps.research.count > 0) {
|
|
// const skins = [] //find skins
|
|
// for (let i = 0; i < tech.tech.length; i++) {
|
|
// if (tech.tech[i].isSkin) skins.push(i)
|
|
// }
|
|
// const choose = skins[Math.floor(Math.seededRandom(0, skins.length))] //pick an element from the array of options
|
|
|
|
// text += `<div class="choose-grid-module" onclick="tech.giveTech(${choose});powerUps.research.changeRerolls(-1);powerUps.endDraft('tech');powerUps.tech.effect();"><div class="grid-title"><div class="circle-grid research"></div> <span style = 'font-size:90%; font-weight: 100; letter-spacing: -1.5px;'>microtransaction:</span> ${tech.tech[choose].name}</div>${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}</div>`
|
|
// }
|
|
if (tech.isBrainstorm && !tech.isBrainstormActive && !simulation.isChoosing) {
|
|
tech.isBrainstormActive = true
|
|
|
|
let count = 1
|
|
let timeStart = performance.now()
|
|
const cycle = (timestamp) => {
|
|
// if (timeStart === undefined) timeStart = timestamp
|
|
// console.log(timestamp, timeStart)
|
|
if (timestamp - timeStart > tech.brainStormDelay * count && simulation.isChoosing) {
|
|
count++
|
|
powerUps.tech.effect();
|
|
document.getElementById("choose-grid").style.pointerEvents = "auto"; //turn off the normal 500ms delay
|
|
document.body.style.cursor = "auto";
|
|
document.getElementById("choose-grid").style.transitionDuration = "0s";
|
|
}
|
|
if (count < 5 && simulation.isChoosing) {
|
|
requestAnimationFrame(cycle);
|
|
} else {
|
|
tech.isBrainstormActive = false
|
|
}
|
|
}
|
|
requestAnimationFrame(cycle);
|
|
|
|
// count++
|
|
// if (count < tech.brainStormDelay * 5 && simulation.isChoosing) {
|
|
// if (!(count % tech.brainStormDelay)) {
|
|
// powerUps.tech.effect();
|
|
// document.getElementById("choose-grid").style.pointerEvents = "auto"; //turn off the normal 500ms delay
|
|
// document.body.style.cursor = "auto";
|
|
// document.getElementById("choose-grid").style.transitionDuration = "0s";
|
|
// }
|
|
// requestAnimationFrame(cycle);
|
|
// } else {
|
|
// tech.isBrainstormActive = false
|
|
// }
|
|
}
|
|
// if (localSettings.isHideImages) text += powerUps.researchText('tech')
|
|
document.getElementById("choose-grid").innerHTML = text
|
|
powerUps.showDraft();
|
|
|
|
//fade in all circles
|
|
// requestAnimationFrame(() => {
|
|
// var elements = document.getElementsByClassName('circle-grid');
|
|
// for (var i in elements) {
|
|
// if (elements.hasOwnProperty(i)) {
|
|
// elements[i].style.opacity = '1';
|
|
// }
|
|
// }
|
|
// });
|
|
}
|
|
}
|
|
},
|
|
},
|
|
entanglement: {
|
|
name: "entanglement",
|
|
color: "#fff", //"hsl(248,100%,65%)",
|
|
size() {
|
|
return 40
|
|
},
|
|
effect() {
|
|
if (m.alive && localSettings.entanglement) {
|
|
// let text = ""
|
|
// document.getElementById("choose-grid").style.gridTemplateColumns = "384px 384px 384px"
|
|
let text = powerUps.buildColumns(3, "entanglement")
|
|
|
|
// text += powerUps.researchText('tech')
|
|
// text += "<div></div>"
|
|
// text += "<div class='choose-grid-module entanglement flipX'>entanglement</div>"
|
|
// text += `<div class='choose-grid-module' onclick='powerUps.endDraft("tech",true)' style="width: 82px; text-align: center;font-size: 1.1em;font-weight: 100;justify-self: end;">cancel</div>` //powerUps.cancelText('tech')
|
|
if (localSettings.entanglement.fieldIndex && localSettings.entanglement.fieldIndex !== m.fieldMode) {
|
|
const choose = localSettings.entanglement.fieldIndex //add field
|
|
text += powerUps.fieldText(choose, `powerUps.choose('field',${choose})`)
|
|
}
|
|
for (let i = 0; i < localSettings.entanglement.gunIndexes.length; i++) { //add guns
|
|
const choose = localSettings.entanglement.gunIndexes[i]
|
|
//check if you always have this gun
|
|
let alreadyHasGun = false
|
|
for (let j = 0; j < b.inventory.length; j++) {
|
|
if (b.inventory[j] === choose) alreadyHasGun = true
|
|
}
|
|
// text += `<div class="choose-grid-module" onclick="powerUps.choose('gun',${gun})"><div class="grid-title"><div class="circle-grid gun"></div> ${b.guns[gun].name}</div> ${b.guns[gun].description}</div>`
|
|
if (!alreadyHasGun) text += powerUps.gunText(choose, `powerUps.choose('gun',${choose})`)
|
|
}
|
|
for (let i = 0; i < localSettings.entanglement.techIndexes.length; i++) { //add tech
|
|
let choose = localSettings.entanglement.techIndexes[i]
|
|
const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : "";
|
|
|
|
if (choose === null || tech.tech[choose].count + 1 > tech.tech[choose].maxCount || !tech.tech[choose].allowed()) {
|
|
// text += `<div class="choose-grid-module" style = "background-color: #efeff5; border: 0px; opacity:0.5; font-size: 60%; line-height: 130%; margin: 1px; padding-top: 6px; padding-bottom: 6px;"><div class="grid-title">${tech.tech[choose].name} <span style = "color: #aaa;font-weight: normal;font-size:80%;">- incoherent</span></div></div>`
|
|
text += powerUps.incoherentTechText(choose)
|
|
} else {
|
|
if (tech.tech[choose].isFieldTech) {
|
|
text += powerUps.fieldTechText(choose, `powerUps.choose('tech',${choose})`)
|
|
} else if (tech.tech[choose].isGunTech) {
|
|
text += powerUps.gunTechText(choose, `powerUps.choose('tech',${choose})`)
|
|
} else if (tech.tech[choose].isLore) {
|
|
text += `<div class="choose-grid-module" onclick="powerUps.choose('tech',${choose})"><div class="grid-title lore-text"><div class="circle-grid lore"></div> ${tech.tech[choose].name} ${isCount}</div>${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}</div>`
|
|
} else if (tech.tech[choose].isJunk) {
|
|
text += powerUps.junkTechText(choose, `powerUps.choose('tech',${choose})`)
|
|
} else if (tech.tech[choose].isSkin) {
|
|
text += powerUps.skinTechText(choose, `powerUps.choose('tech',${choose})`)
|
|
} else { //normal tech
|
|
text += powerUps.techText(choose, `powerUps.choose('tech',${choose})`)
|
|
}
|
|
}
|
|
}
|
|
// document.getElementById("choose-grid").classList.add("flipX");
|
|
document.getElementById("choose-grid").innerHTML = text
|
|
powerUps.showDraft();
|
|
localSettings.entanglement = undefined
|
|
if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
|
|
}
|
|
},
|
|
},
|
|
spawnDelay(type, num) {
|
|
let count = num
|
|
let cycle = () => {
|
|
if (count > 0) {
|
|
if (m.alive) requestAnimationFrame(cycle);
|
|
if (!simulation.paused && !simulation.isChoosing) { //&& !(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);
|
|
}
|
|
}
|
|
}
|
|
requestAnimationFrame(cycle);
|
|
},
|
|
onPickUp(who) {
|
|
powerUps.research.currentRerollCount = 0
|
|
if (tech.isTechDamage && who.name === "tech") m.damage(0.1)
|
|
if (tech.isMassEnergy) m.energy += 2;
|
|
if (tech.isMineDrop && bullet.length < 150 && Math.random() < 0.5) {
|
|
if (tech.isLaserMine && input.down) {
|
|
b.laserMine(who.position)
|
|
} else {
|
|
b.mine(who.position, { x: 0, y: 0 }, 0)
|
|
}
|
|
}
|
|
if (tech.isRelay) {
|
|
if (tech.isFlipFlopOn) {
|
|
tech.isFlipFlopOn = false
|
|
if (document.getElementById("tech-switch")) document.getElementById("tech-switch").innerHTML = ` = <strong>OFF</strong>`
|
|
m.eyeFillColor = 'transparent'
|
|
} else {
|
|
tech.isFlipFlopOn = true //immune to damage this hit, lose immunity for next hit
|
|
if (document.getElementById("tech-switch")) document.getElementById("tech-switch").innerHTML = ` = <strong>ON</strong>`
|
|
m.eyeFillColor = m.fieldMeterColor //'#0cf'
|
|
}
|
|
if (tech.isRelayEnergy) m.setMaxEnergy();
|
|
}
|
|
},
|
|
spawnRandomPowerUp(x, y) { //mostly used after mob dies, doesn't always return a power up
|
|
if (!tech.isEnergyHealth && (Math.random() * Math.random() - 0.3 > Math.sqrt(m.health)) || Math.random() < 0.04) { //spawn heal chance is higher at low health
|
|
powerUps.spawn(x, y, "heal");
|
|
return;
|
|
}
|
|
if (Math.random() < 0.15 && b.inventory.length > 0) {
|
|
powerUps.spawn(x, y, "ammo");
|
|
return;
|
|
}
|
|
if (Math.random() < 0.0007 * (3 - b.inventory.length)) { //a new gun has a low chance for each not acquired gun up to 3
|
|
powerUps.spawn(x, y, "gun");
|
|
return;
|
|
}
|
|
// if (Math.random() < 0.0027 * (22 - tech.totalCount)) { //a new tech has a low chance for each not acquired tech up to 25
|
|
if (Math.random() < 0.005 * (10 - level.levelsCleared)) { //a new tech has a low chance that decreases in later levels
|
|
powerUps.spawn(x, y, "tech");
|
|
return;
|
|
}
|
|
if (Math.random() < 0.0015) {
|
|
powerUps.spawn(x, y, "field");
|
|
return;
|
|
}
|
|
if (tech.isCouplingPowerUps && Math.random() < 0.17) {
|
|
powerUps.spawn(x, y, "coupling");
|
|
return;
|
|
}
|
|
if (tech.isBoostPowerUps && Math.random() < 0.14) {
|
|
powerUps.spawn(x, y, "boost");
|
|
return;
|
|
}
|
|
// if (Math.random() < 0.01) {
|
|
// powerUps.spawn(x, y, "research");
|
|
// return;
|
|
// }
|
|
},
|
|
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 (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++;
|
|
powerUpChance(Math.max(level.levelsCleared, 10) * 0.1)
|
|
}
|
|
powerUps.randomPowerUpCounter += 0.6;
|
|
powerUpChance(Math.max(level.levelsCleared, 6) * 0.1)
|
|
|
|
function powerUpChance(chanceToFail) {
|
|
if (Math.random() * chanceToFail < powerUps.randomPowerUpCounter) {
|
|
powerUps.randomPowerUpCounter = 0;
|
|
if (Math.random() < 0.97) {
|
|
powerUps.spawn(x, y, "tech")
|
|
} else {
|
|
powerUps.spawn(x, y, "gun")
|
|
}
|
|
} else {
|
|
if (m.health < 0.65 && !tech.isEnergyHealth) {
|
|
powerUps.spawn(x, y, "heal");
|
|
powerUps.spawn(x, y, "heal");
|
|
} else {
|
|
powerUps.spawn(x, y, "ammo");
|
|
powerUps.spawn(x, y, "ammo");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
chooseRandomPowerUp(x, y) { //100% chance to drop a random power up //used in spawn.debris
|
|
if (Math.random() < 0.5) {
|
|
powerUps.spawn(x, y, "heal", false);
|
|
} else {
|
|
powerUps.spawn(x, y, "ammo", false);
|
|
}
|
|
},
|
|
addResearchToLevel() { //add a random power up to a location that has a mob, mostly used to give each level one randomly placed research
|
|
if (mob.length && Math.random() < 0.45 - 0.3 * (simulation.difficultyMode > 5)) { //lower chance on why difficulty
|
|
const index = Math.floor(Math.random() * mob.length)
|
|
powerUps.spawn(mob[index].position.x, mob[index].position.y, "research");
|
|
}
|
|
},
|
|
spawnStartingPowerUps(x, y) { //used for map specific power ups, mostly to give player a starting gun
|
|
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
|
|
} else if (tech.totalCount === 0) { //first tech
|
|
powerUps.spawn(x, y, "tech", false);
|
|
} else if (b.inventory.length === 1) { //second gun or extra ammo
|
|
if (Math.random() < 0.4) {
|
|
powerUps.spawn(x, y, "gun", false);
|
|
} else {
|
|
for (let i = 0; i < 5; i++) powerUps.spawn(x, y, "ammo", false);
|
|
}
|
|
} else {
|
|
for (let i = 0; i < 4; i++) powerUps.spawnRandomPowerUp(x, y);
|
|
}
|
|
} 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) {
|
|
choose = have[Math.floor(Math.random() * have.length)]
|
|
simulation.makeTextLog(`<span class='color-var'>tech</span>.remove("<span class='color-text'>${tech.tech[choose].name}</span>")`)
|
|
|
|
for (let i = 0; i < tech.tech[choose].count; i++) {
|
|
powerUps.directSpawn(m.pos.x, m.pos.y, "tech");
|
|
// powerUp[powerUp.length - 1].isDuplicated = true
|
|
}
|
|
// remove a random tech from the list of tech you have
|
|
tech.tech[choose].remove();
|
|
tech.tech[choose].count = 0;
|
|
tech.tech[choose].isLost = true;
|
|
simulation.updateTechHUD();
|
|
m.fieldCDcycle = m.cycle + 30; //disable field so you can't pick up the ejected tech
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
} else if (tech.tech[choose].count && !tech.tech[choose].isNonRefundable) {
|
|
simulation.makeTextLog(`<span class='color-var'>tech</span>.remove("<span class='color-text'>${tech.tech[choose].name}</span>")`)
|
|
|
|
for (let i = 0; i < tech.tech[choose].count; i++) {
|
|
powerUps.directSpawn(m.pos.x, m.pos.y, "tech");
|
|
powerUp[powerUp.length - 1].isDuplicated = true
|
|
}
|
|
// remove a random tech from the list of tech you have
|
|
tech.tech[choose].remove();
|
|
tech.tech[choose].count = 0;
|
|
tech.tech[choose].isLost = true;
|
|
simulation.updateTechHUD();
|
|
m.fieldCDcycle = m.cycle + 30; //disable field so you can't pick up the ejected tech
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
},
|
|
pauseEjectTech(index) {
|
|
if ((tech.isPauseEjectTech || simulation.testing) && !simulation.isChoosing && !tech.tech[index].isNonRefundable) {
|
|
if (Math.random() < 0.2 || tech.tech[index].isFromAppliedScience || (tech.tech[index].bonusResearch !== undefined && tech.tech[index].bonusResearch > powerUps.research.count)) {
|
|
tech.removeTech(index)
|
|
// powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false);
|
|
} else {
|
|
powerUps.ejectTech(index)
|
|
}
|
|
document.getElementById(`${index}-pause-tech`).style.textDecoration = "line-through"
|
|
document.getElementById(`${index}-pause-tech`).style.animation = ""
|
|
document.getElementById(`${index}-pause-tech`).onclick = null
|
|
}
|
|
},
|
|
// removeRandomTech() {
|
|
// const have = [] //find which tech you have
|
|
// for (let i = 0; i < tech.tech.length; i++) {
|
|
// if (tech.tech[i].count > 0) have.push(i)
|
|
// }
|
|
// if (have.length) {
|
|
// const choose = have[Math.floor(Math.random() * have.length)]
|
|
// simulation.makeTextLog(`<span class='color-var'>tech</span>.removeTech("<span class='color-text'>${tech.tech[choose].name}</span>")`)
|
|
// const totalRemoved = tech.tech[choose].count
|
|
// tech.tech[choose].count = 0;
|
|
// tech.tech[choose].remove(); // remove a random tech form the list of tech you have
|
|
// tech.tech[choose].isLost = true
|
|
// simulation.updateTechHUD();
|
|
// return totalRemoved
|
|
// }
|
|
// return 0
|
|
// },
|
|
randomize(where) { //makes a random power up convert into a random different power up
|
|
//put 10 power ups close together
|
|
const len = Math.min(10, powerUp.length)
|
|
for (let i = 0; i < len; i++) { //collide the first 10 power ups
|
|
const unit = Vector.rotate({ x: 1, y: 0 }, 6.28 * Math.random())
|
|
Matter.Body.setPosition(powerUp[i], Vector.add(where, Vector.mult(unit, 20 + 25 * Math.random())));
|
|
Matter.Body.setVelocity(powerUp[i], Vector.mult(unit, 20));
|
|
}
|
|
|
|
//count big power ups and small power ups
|
|
let options = ["heal", "research", "ammo"]
|
|
if (m.coupling) options.push("coupling")
|
|
if (tech.isBoostPowerUps) options.push("boost")
|
|
let bigIndexes = []
|
|
let smallIndexes = []
|
|
for (let i = 0; i < powerUp.length; i++) {
|
|
if (powerUp[i].name === "tech" || powerUp[i].name === "gun" || powerUp[i].name === "field") {
|
|
bigIndexes.push(i)
|
|
} else {
|
|
smallIndexes.push(i)
|
|
}
|
|
}
|
|
if (bigIndexes.length > 0) {
|
|
// console.log("at least 1 big will always spilt")
|
|
const index = bigIndexes[Math.floor(Math.random() * bigIndexes.length)]
|
|
for (let i = 0; i < 3; i++) powerUps.directSpawn(where.x, where.y, options[Math.floor(Math.random() * options.length)], false)
|
|
|
|
Matter.Composite.remove(engine.world, powerUp[index]);
|
|
powerUp.splice(index, 1);
|
|
} else if (smallIndexes.length > 2 && Math.random() < 0.33) {
|
|
// console.log("no big, at least 3 small can combine")
|
|
for (let j = 0; j < 3; j++) {
|
|
for (let i = 0; i < powerUp.length; i++) {
|
|
if (powerUp[i].name === "heal" || powerUp[i].name === "research" || powerUp[i].name === "ammo" || powerUp[i].name === "coupling" || powerUp[i].name === "boost") {
|
|
Matter.Composite.remove(engine.world, powerUp[i]);
|
|
powerUp.splice(i, 1);
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
options = ["tech", "gun", "field"]
|
|
powerUps.directSpawn(where.x, where.y, options[Math.floor(Math.random() * options.length)], false)
|
|
} else if (smallIndexes.length > 0) {
|
|
// console.log("no big, at least 1 small will swap flavors")
|
|
const index = Math.floor(Math.random() * powerUp.length)
|
|
options = options.filter(e => e !== powerUp[index].name); //don't repeat the current power up type
|
|
powerUps.directSpawn(where.x, where.y, options[Math.floor(Math.random() * options.length)], false)
|
|
Matter.Composite.remove(engine.world, powerUp[index]);
|
|
powerUp.splice(index, 1);
|
|
}
|
|
},
|
|
directSpawn(x, y, target, moving = true, mode = null, size = powerUps[target].size(), isDuplicated = false) {
|
|
let index = powerUp.length;
|
|
let properties = {
|
|
density: 0.001,
|
|
frictionAir: 0.03,
|
|
restitution: 0.85,
|
|
collisionFilter: {
|
|
group: 0,
|
|
category: cat.powerUp,
|
|
mask: cat.map | cat.powerUp
|
|
},
|
|
color: powerUps[target].color,
|
|
effect: powerUps[target].effect,
|
|
name: powerUps[target].name,
|
|
size: size
|
|
}
|
|
let polygonSides
|
|
if (isDuplicated) {
|
|
polygonSides = tech.isPowerUpsVanish ? 3 : Math.floor(4 + 2 * Math.random())
|
|
properties.isDuplicated = true
|
|
} else {
|
|
properties.inertia = Infinity //prevents rotation for circles only
|
|
polygonSides = 0
|
|
}
|
|
powerUp[index] = Matter.Bodies.polygon(x, y, polygonSides, size, properties);
|
|
if (mode) powerUp[index].mode = mode
|
|
if (moving) Matter.Body.setVelocity(powerUp[index], { x: (Math.random() - 0.5) * 15, y: Math.random() * -9 - 3 });
|
|
Composite.add(engine.world, powerUp[index]);
|
|
},
|
|
spawn(x, y, target, moving = true, mode = null, size = powerUps[target].size()) {
|
|
if (
|
|
(!tech.isSuperDeterminism || (target !== 'research')) &&
|
|
!(tech.isEnergyNoAmmo && target === 'ammo')
|
|
) {
|
|
if (tech.isBoostReplaceAmmo && target === 'ammo') target = 'boost'
|
|
powerUps.directSpawn(x, y, target, moving, mode, size)
|
|
if (Math.random() < tech.duplicationChance()) {
|
|
powerUps.directSpawn(x, y, target, moving, mode, size, true)
|
|
powerUp[powerUp.length - 1].isDuplicated = true
|
|
// if (tech.isPowerUpsVanish) powerUp[powerUp.length - 1].endCycle = simulation.cycle + 300
|
|
}
|
|
}
|
|
},
|
|
}; |