new mob: pulsar - aims at player and does damage in an circle
  (set to 3x chance to show up until the next patch)

several tech that were nonrefundable now can be removed and refunded
added several bug fixes
This commit is contained in:
landgreen
2021-03-07 05:59:10 -08:00
parent abed965f7b
commit 5f68bc687f
11 changed files with 462 additions and 156 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -574,6 +574,121 @@ const b = {
});
}
},
photon(where, angle = m.angle) {
let best;
const path = [{
x: m.pos.x + 20 * Math.cos(angle),
y: m.pos.y + 20 * Math.sin(angle)
},
{
x: m.pos.x + range * Math.cos(angle),
y: m.pos.y + range * Math.sin(angle)
}
];
const vertexCollision = function(v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let vertices = domain[i].vertices;
const len = vertices.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) {
best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[j],
v2: vertices[j + 1]
};
}
}
}
results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) {
best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[0],
v2: vertices[len]
};
}
}
}
};
//check for collisions
best = {
x: null,
y: null,
dist2: Infinity,
who: null,
v1: null,
v2: null
};
if (tech.isPulseAim) { //find mobs in line of sight
let dist = 2200
for (let i = 0, len = mob.length; i < len; i++) {
const newDist = Vector.magnitude(Vector.sub(path[0], mob[i].position))
if (explosionRadius < newDist &&
newDist < dist &&
Matter.Query.ray(map, path[0], mob[i].position).length === 0 &&
Matter.Query.ray(body, path[0], mob[i].position).length === 0) {
dist = newDist
best.who = mob[i]
path[path.length - 1] = mob[i].position
}
}
}
if (!best.who) {
vertexCollision(path[0], path[1], mob);
vertexCollision(path[0], path[1], map);
vertexCollision(path[0], path[1], body);
if (best.dist2 != Infinity) { //if hitting something
path[path.length - 1] = {
x: best.x,
y: best.y
};
}
}
if (best.who) b.explosion(path[1], explosionRadius, true)
//draw laser beam
ctx.beginPath();
ctx.moveTo(path[0].x, path[0].y);
ctx.lineTo(path[1].x, path[1].y);
ctx.strokeStyle = "rgba(255,0,0,0.13)"
ctx.lineWidth = 60 * energy / 0.2
ctx.stroke();
ctx.strokeStyle = "rgba(255,0,0,0.2)"
ctx.lineWidth = 18
ctx.stroke();
ctx.strokeStyle = "#f00";
ctx.lineWidth = 4
ctx.stroke();
//draw little dots along the laser path
const sub = Vector.sub(path[1], path[0])
const mag = Vector.magnitude(sub)
for (let i = 0, len = Math.floor(mag * 0.03 * energy / 0.2); i < len; i++) {
const dist = Math.random()
simulation.drawList.push({
x: path[0].x + sub.x * dist + 13 * (Math.random() - 0.5),
y: path[0].y + sub.y * dist + 13 * (Math.random() - 0.5),
radius: 1 + 4 * Math.random(),
color: "rgba(255,0,0,0.5)",
time: Math.floor(2 + 33 * Math.random() * Math.random())
});
}
},
grenade() {
},
@@ -3266,11 +3381,11 @@ const b = {
if (m.crouch) {
spread = 0.75
m.fireCDcycle = m.cycle + Math.floor(55 * b.fireCD); // cool down
if (tech.isShotgunImmune && m.immuneCycle < m.cycle + Math.floor(58 * b.fireCD)) m.immuneCycle = m.cycle + Math.floor(58 * b.fireCD); //player is immune to collision damage for 30 cycles
if (tech.isShotgunImmune && m.immuneCycle < m.cycle + Math.floor(58 * b.fireCD)) m.immuneCycle = m.cycle + Math.floor(58 * b.fireCD); //player is immune to damage for 30 cycles
knock = 0.01
} else {
m.fireCDcycle = m.cycle + Math.floor(45 * b.fireCD); // cool down
if (tech.isShotgunImmune && m.immuneCycle < m.cycle + Math.floor(47 * b.fireCD)) m.immuneCycle = m.cycle + Math.floor(47 * b.fireCD); //player is immune to collision damage for 30 cycles
if (tech.isShotgunImmune && m.immuneCycle < m.cycle + Math.floor(47 * b.fireCD)) m.immuneCycle = m.cycle + Math.floor(47 * b.fireCD); //player is immune to damage for 30 cycles
spread = 1.3
knock = 0.1
}
@@ -4319,6 +4434,12 @@ const b = {
} else {
this.fire = this.fireLaser
}
// this.fire = this.firePhoton
},
firePhoton() {
m.fireCDcycle = m.cycle + Math.floor((tech.isPulseAim ? 25 : 50) * b.fireCD); // cool down
b.photon({ x: m.pos.x + 23 * Math.cos(m.angle), y: m.pos.y + 23 * Math.sin(m.angle) }, m.angle)
},
fireLaser() {
if (m.energy < tech.laserFieldDrain) {
@@ -4329,7 +4450,6 @@ const b = {
b.laser();
}
},
// laser(where = {
// x: m.pos.x + 20 * Math.cos(m.angle),
// y: m.pos.y + 20 * Math.sin(m.angle)
@@ -4501,7 +4621,7 @@ const b = {
m.fireCDcycle = m.cycle + Math.floor(120 * b.fireCD); // cool down
} else {
m.energy -= DRAIN
if (m.immuneCycle < m.cycle + 30) m.immuneCycle = m.cycle + 30; //player is immune to collision damage for 5 cycles
if (m.immuneCycle < m.cycle + 30) m.immuneCycle = m.cycle + 30; //player is immune to damage for 5 cycles
Matter.Body.setPosition(player, history.position);
Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y });
if (m.health !== history.health) {

View File

@@ -126,7 +126,7 @@ function collisionChecks(event) {
if (tech.isPiezo) m.energy += 20.48;
if (tech.isBayesian) powerUps.ejectTech()
if (mob[k].onHit) mob[k].onHit(k);
m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to collision damage for 30 cycles
m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage for 30 cycles
//extra kick between player and mob //this section would be better with forces but they don't work...
let angle = Math.atan2(player.position.y - mob[k].position.y, player.position.x - mob[k].position.x);
Matter.Body.setVelocity(player, {

View File

@@ -241,6 +241,8 @@ const build = {
text += `<div class="pause-grid-module"><div class="grid-title"><div class="circle-grid tech"></div> &nbsp; ${tech.tech[i].name} ${isCount}</div>${tech.tech[i].description}</div></div>`
}
countTech++
} else if (tech.tech[i].isLost) {
text += `<div class="pause-grid-module" style="text-decoration: line-through;"><div class="grid-title">${tech.tech[i].name}</div>${tech.tech[i].description}</div></div>`
}
}
el = document.getElementById("pause-grid-right")

View File

@@ -16,7 +16,7 @@ const level = {
// simulation.zoomScale = 1000;
// simulation.setZoom();
// m.setField("nano-scale manufacturing")
// b.giveGuns("nail gun")
// b.giveGuns("laser")
// tech.isExplodeRadio = true
// for (let i = 0; i < 1; i++) tech.giveTech("dynamo-bot")
// tech.giveTech("supercritical fission")
@@ -1087,19 +1087,19 @@ const level = {
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump
// spawn.boost(1500, 0, 900);
// simulation.difficulty = 30
// spawn.starter(1900, -500, 200) //big boy
// spawn.starter(1900, -500)
spawn.pulsar(1900, -500)
// spawn.historyBoss(1900, -500)
// spawn.ghoster(2900, -500)
// spawn.launcherBoss(1200, -500)
// spawn.laserTargetingBoss(1600, -400)
// spawn.striker(1600, -500)
// spawn.shooter(1700, -120)
// spawn.laserTargetingBoss(1700, -120)
// spawn.bomberBoss(1400, -500)
// spawn.sniper(1800, -120)
// spawn.streamBoss(1600, -500)
simulation.difficulty = 30
spawn.orbitalBoss(1600, -500)
// spawn.orbitalBoss(1600, -500)
// spawn.cellBossCulture(1600, -500)
// spawn.shieldingBoss(1600, -500)
// spawn.beamer(1200, -500)

View File

@@ -321,7 +321,7 @@ const m = {
if (
!tech.tech[i].isNonRefundable &&
tech.tech[i].name !== "many-worlds" &&
tech.tech[i].name !== "decoherence"
tech.tech[i].name !== "Ψ(t) collapse"
) {
totalTech += tech.tech[i].count
tech.tech[i].remove();
@@ -509,7 +509,7 @@ const m = {
if (tech.healthDrain) dmg *= 1 + 2.667 * tech.healthDrain //tech.healthDrain = 0.03 at one stack //cause more damage
if (tech.squirrelFx !== 1) dmg *= 1 + (tech.squirrelFx - 1) / 5 //cause more damage
if (tech.isBlockHarm && m.isHolding) dmg *= 0.15
if (tech.isSpeedHarm) dmg *= 1 - Math.min(player.speed * 0.0185, 0.55)
if (tech.isSpeedHarm) dmg *= 1 - Math.min(player.speed * 0.019, 0.60)
if (tech.isSlowFPS) dmg *= 0.8
// if (tech.isPiezo) dmg *= 0.85
if (tech.isHarmReduce && m.fieldUpgrades[m.fieldMode].name === "negative mass field" && m.isFieldActive) dmg *= 0.5
@@ -581,7 +581,7 @@ const m = {
}
}
m.energy = Math.max(m.energy - steps / 136, 0.01)
if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to collision damage for 30 cycles
if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage for 30 cycles
let isDrawPlayer = true
const shortPause = function() {
@@ -834,7 +834,42 @@ const m = {
ctx.stroke();
// draw eye; used in flip-flop
// ctx.beginPath();
// ctx.arc(15, 0, 3, 0, 2 * Math.PI);
// ctx.arc(15, 0, 3.5, 0, 2 * Math.PI);
// ctx.fillStyle = m.eyeFillColor;
// ctx.fill()
ctx.restore();
m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal
},
drawDefault() {
ctx.fillStyle = m.fillColor;
m.walk_cycle += m.flipLegs * m.Vx;
//draw body
ctx.save();
ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5
ctx.translate(m.pos.x, m.pos.y);
m.calcLeg(Math.PI, -3);
m.drawLeg("#4a4a4a");
m.calcLeg(0, 0);
m.drawLeg("#333");
ctx.rotate(m.angle);
ctx.beginPath();
ctx.arc(0, 0, 30, 0, 2 * Math.PI);
let grd = ctx.createLinearGradient(-30, 0, 30, 0);
grd.addColorStop(0, m.fillColorDark);
grd.addColorStop(1, m.fillColor);
ctx.fillStyle = grd;
ctx.fill();
ctx.arc(15, 0, 4, 0, 2 * Math.PI);
ctx.strokeStyle = "#333";
ctx.lineWidth = 2;
ctx.stroke();
// draw eye; used in flip-flop
// ctx.beginPath();
// ctx.arc(15, 0, 3.5, 0, 2 * Math.PI);
// ctx.fillStyle = m.eyeFillColor;
// ctx.fill()
@@ -1571,7 +1606,7 @@ const m = {
effect: () => {
m.fieldFire = true;
m.holdingMassScale = 0.03; //can hold heavier blocks with lower cost to jumping
m.fieldMeterColor = "#000"
m.fieldMeterColor = "#333"
m.eyeFillColor = m.fieldMeterColor
m.fieldHarmReduction = 0.5;
m.fieldDrawRadius = 0;
@@ -1834,8 +1869,9 @@ const m = {
description: "<strong class='color-cloaked'>cloak</strong> after not using your gun or field<br>while <strong class='color-cloaked'>cloaked</strong> mobs can't see you<br>increase <strong class='color-d'>damage</strong> by <strong>133%</strong>",
effect: () => {
m.fieldFire = true;
m.fieldMeterColor = "#000";
m.fieldMeterColor = "#333";
m.eyeFillColor = m.fieldMeterColor
// m.eyeFillColor = '#333'
m.fieldPhase = 0;
m.isCloak = false
m.fieldDamage = 2.33 // 1 + 111/100
@@ -2344,7 +2380,7 @@ const m = {
// break; //because the array order is messed up after splice
// }
// }
// m.immuneCycle = m.cycle + 5; //player is immune to collision damage for 30 cycles
// m.immuneCycle = m.cycle + 5; //player is immune to damage for 30 cycles
// } else {
// m.fieldCDcycle = m.cycle + 30;
// // m.resetHistory();
@@ -2447,7 +2483,7 @@ const m = {
Matter.World.remove(engine.world, body[i]);
body.splice(i, 1);
m.fieldRange *= 0.8
if (tech.isWormholeEnergy) m.energy += 0.5
if (tech.isWormholeEnergy) m.energy += 0.63
if (tech.isWormSpores) { //pandimensionalspermia
for (let i = 0, len = Math.ceil(3 * Math.random()); i < len; i++) {
b.spore(Vector.add(m.hole.pos2, Vector.rotate({
@@ -2473,7 +2509,7 @@ const m = {
body.splice(i, 1);
m.fieldRange *= 0.8
// if (tech.isWormholeEnergy && m.energy < m.maxEnergy * 2) m.energy = m.maxEnergy * 2
if (tech.isWormholeEnergy) m.energy += 0.5
if (tech.isWormholeEnergy) m.energy += 0.63
if (tech.isWormSpores) { //pandimensionalspermia
for (let i = 0, len = Math.ceil(3 * Math.random()); i < len; i++) {
b.spore(Vector.add(m.hole.pos1, Vector.rotate({
@@ -2554,7 +2590,7 @@ const m = {
x: velocity.x,
y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer
});
if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to collision damage
if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage
// move bots to player
for (let i = 0; i < bullet.length; i++) {
if (bullet[i].botType) {
@@ -2579,11 +2615,11 @@ const m = {
m.hole.unit = Vector.perp(Vector.normalise(sub))
if (tech.isWormholeDamage) {
who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 80)
who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 100)
for (let i = 0; i < who.length; i++) {
if (who[i].body.alive) {
mobs.statusDoT(who[i].body, 0.6, 420)
mobs.statusStun(who[i].body, 240)
mobs.statusDoT(who[i].body, 1, 420)
mobs.statusStun(who[i].body, 360)
}
}
}
@@ -2830,7 +2866,7 @@ const m = {
if (tech.isPiezo) m.energy += 20.48;
if (tech.isBayesian) powerUps.ejectTech()
if (mob[k].onHit) mob[k].onHit(k);
if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to collision damage for 30 cycles
if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage for 30 cycles
//extra kick between player and mob //this section would be better with forces but they don't work...
let angle = Math.atan2(player.position.y - mob[k].position.y, player.position.x - mob[k].position.x);
Matter.Body.setVelocity(player, {

View File

@@ -70,7 +70,7 @@ const powerUps = {
document.body.style.overflow = "hidden"
simulation.paused = false;
simulation.isChoosing = false; //stops p from un pausing on key down
if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to collision damage for 30 cycles
if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage for 30 cycles
build.unPauseGrid()
requestAnimationFrame(cycle);
if (m.holdingTarget) m.drop();
@@ -379,11 +379,11 @@ const powerUps = {
} else {
if (tech.isBanish) {
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].name === "erase") powerUps.ejectTech(i)
if (tech.tech[i].name === "decoherence") powerUps.ejectTech(i)
}
// simulation.makeTextLog(`No <strong class='color-m'>tech</strong> left<br>erased <strong class='color-m'>tech</strong> have been recovered`)
simulation.makeTextLog(`powerUps.tech.length: ${Math.max(0,powerUps.tech.lastTotalChoices - powerUps.tech.banishLog.length)}`)
powerUps.spawn(m.pos.x, m.pos.y, "tech");
// powerUps.spawn(m.pos.x, m.pos.y, "tech");
powerUps.endDraft("tech");
} else {
powerUps.giveRandomAmmo()

View File

@@ -509,7 +509,11 @@ const simulation = {
document.getElementById("splash").style.display = "none"; //hides the element that spawned the function
document.getElementById("dmg").style.display = "inline";
document.getElementById("health-bg").style.display = "inline";
ctx.globalCompositeOperation = "source-over"
ctx.shadowBlur = 0;
// ctx.shadowColor = '#000';
if (!m.isShipMode) {
m.draw = m.drawDefault //set the play draw to normal, undoing some junk tech
m.spawn(); //spawns the player
} else {
World.add(engine.world, [player])
@@ -828,7 +832,7 @@ const simulation = {
if (!(simulation.cycle % 420)) { //once every 7 seconds
if (tech.cyclicImmunity && m.immuneCycle < m.cycle + tech.cyclicImmunity) m.immuneCycle = m.cycle + tech.cyclicImmunity; //player is immune to collision damage for 60 cycles
if (tech.cyclicImmunity && m.immuneCycle < m.cycle + tech.cyclicImmunity) m.immuneCycle = m.cycle + tech.cyclicImmunity; //player is immune to damage for 60 cycles
fallCheck = function(who, save = false) {
let i = who.length;

View File

@@ -11,6 +11,7 @@ const spawn = {
"launcher", "launcher",
"springer", "springer",
"sucker", "sucker",
"pulsar", "pulsar", "pulsar", "pulsar", "pulsar", //briefly high chance to show from a few days
"chaser",
"sniper",
"spinner",
@@ -21,7 +22,7 @@ const spawn = {
"ghoster",
"sneaker",
],
allowedGroupList: ["chaser", "spinner", "striker", "springer", "laser", "focuser", "beamer", "exploder", "spawner", "shooter", "launcher", "stabber", "sniper"],
allowedGroupList: ["chaser", "spinner", "striker", "springer", "laser", "focuser", "beamer", "exploder", "spawner", "shooter", "launcher", "stabber", "sniper", "pulsar"],
setSpawnList() { //this is run at the start of each new level to determine the possible mobs for the level
//each level has 2 mobs: one new mob and one from the last level
spawn.pickList.splice(0, 1);
@@ -100,16 +101,21 @@ const spawn = {
Matter.Body.setDensity(me, density); //extra dense //normal is 0.001 //makes effective life much larger
// spawn.shield(me, x, y, 1);
me.onDeath = function() {
//add lore level as next level if player took lore tech earlier in the game
if (lore.techCount > (lore.techGoal - 1) && !simulation.isCheating) {
level.levels.push("null")
function unlockExit() {
level.exit.x = 5500;
level.exit.y = -330;
simulation.makeTextLog(`<span class="lore-text">undefined</span> <span class='color-symbol'>=</span> ${lore.techCount}/${lore.techGoal}<br>level.levels.push("<span class='lore-text'>null</span>")`);
//remove block map element so exit is clear
Matter.World.remove(engine.world, map[map.length - 1]);
map.splice(map.length - 1, 1);
simulation.draw.setPaths(); //redraw map draw path
}
//add lore level as next level if player took lore tech earlier in the game
if (lore.techCount > (lore.techGoal - 1) && !simulation.isCheating) {
simulation.makeTextLog(`<span class="lore-text">undefined</span> <span class='color-symbol'>=</span> ${lore.techCount}/${lore.techGoal}<br>level.levels.push("<span class='lore-text'>null</span>")`);
level.levels.push("null")
//remove block map element so exit is clear
unlockExit()
} else { //reset game
let count = 0
@@ -130,7 +136,12 @@ const spawn = {
return
}
}
if (!simulation.testing) requestAnimationFrame(loop);
if (simulation.testing) {
simulation.makeTextLog(`level.levels.length <span class='color-symbol'>=</span> <strong>Infinite</strong>`);
unlockExit()
} else {
requestAnimationFrame(loop);
}
}
requestAnimationFrame(loop);
}
@@ -419,7 +430,7 @@ const spawn = {
vertexCollision(where, look, body);
if (!m.isCloak) vertexCollision(where, look, [player]);
if (best.who && best.who === player && m.immuneCycle < m.cycle) {
if (m.immuneCycle < m.cycle + 60 + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + 60 + tech.collisionImmuneCycles; //player is immune to collision damage extra time
if (m.immuneCycle < m.cycle + 60 + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + 60 + tech.collisionImmuneCycles; //player is immune to damage extra time
m.damage(dmg);
simulation.drawList.push({ //add dmg to draw queue
x: best.x,
@@ -1470,6 +1481,93 @@ const spawn = {
}
};
},
pulsar(x, y, radius = 30) {
mobs.spawn(x, y, 3, radius, "#f08");
let me = mob[mob.length - 1];
me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front
Matter.Body.rotate(me, Math.random() * Math.PI * 2);
me.radius *= 2
me.vertices[1].x = me.position.x + Math.cos(me.angle) * me.radius; //make one end of the triangle longer
me.vertices[1].y = me.position.y + Math.sin(me.angle) * me.radius;
me.fireCycle = 0
me.fireTarget = { x: 0, y: 0 }
me.pulseRadius = Math.min(500, 300 + simulation.difficulty)
me.frictionAir = 0.01;
me.fireDelay = Math.min(90, 210 - simulation.difficulty)
me.isFiring = false
me.onHit = function() {};
me.canSeeTarget = function() {
const diff = Vector.normalise(Vector.sub(this.fireTarget, this.position)); //make a vector for the mob's direction of length 1
const dot = Vector.dot({
x: Math.cos(this.angle),
y: Math.sin(this.angle)
}, diff); //the dot product of diff and dir will return how much over lap between the vectors
if (dot < 0.97 || Matter.Query.ray(map, this.fireTarget, this.position).length !== 0) { //if not looking at target
this.isFiring = false
return false
} else {
return true
}
}
me.do = function() {
this.seePlayerByLookingAt();
this.checkStatus();
if (!m.isBodiesAsleep && this.seePlayer.recall) {
if (this.isFiring) {
if (this.fireCycle > this.fireDelay) { //fire
if (!this.canSeeTarget()) return
this.isFiring = false
//damage player if in range
if (Vector.magnitude(Vector.sub(player.position, this.fireTarget)) < this.pulseRadius && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage
m.damage(0.03 * simulation.dmgScale);
}
simulation.drawList.push({ //add dmg to draw queue
x: this.fireTarget.x,
y: this.fireTarget.y,
radius: this.pulseRadius,
color: "rgba(255,0,100,0.8)",
time: simulation.drawTime
});
} else { //delay before firing
this.fireCycle++
if (!(simulation.cycle % 3)) {
if (!this.canSeeTarget()) return //if can't see stop firing
//draw explosion outline
ctx.beginPath();
ctx.arc(this.fireTarget.x, this.fireTarget.y, this.pulseRadius, 0, 2 * Math.PI);
ctx.fillStyle = "rgba(255,0,100,0.05)";
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = "rgba(255,0,100,0.5)";
ctx.stroke();
}
}
} else { //aim at player
this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); //set direction to turn to fire
//rotate towards fireAngle
const angle = this.angle + Math.PI / 2;
const c = Math.cos(angle) * this.fireDir.x + Math.sin(angle) * this.fireDir.y;
const threshold = 0.03;
if (c > threshold) {
this.torque += 0.000001 * this.inertia;
} else if (c < -threshold) {
this.torque -= 0.000001 * this.inertia;
} else { //fire
this.fireTarget = { x: player.position.x, y: player.position.y }
if (!this.canSeeTarget()) return
Matter.Body.setAngularVelocity(this, 0)
this.fireLockCount = 0
this.isFiring = true
this.fireCycle = 0
}
}
}
};
},
laser(x, y, radius = 30) {
mobs.spawn(x, y, 3, radius, "#f00");
let me = mob[mob.length - 1];
@@ -1603,7 +1701,7 @@ const spawn = {
vertexCollision(where, look, body);
if (!m.isCloak) vertexCollision(where, look, [player]);
if (best.who && best.who === player && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + tech.collisionImmuneCycles + 60; //player is immune to collision damage for an extra second
m.immuneCycle = m.cycle + tech.collisionImmuneCycles + 60; //player is immune to damage for an extra second
const dmg = 0.14 * simulation.dmgScale;
m.damage(dmg);
simulation.drawList.push({ //add dmg to draw queue
@@ -2758,7 +2856,8 @@ const spawn = {
}
Matter.Body.setPosition(this, Vector.add(who.position, Vector.mult(orbit, radius))) //bullets move with player
//damage player
if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible)) {
if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage for 30 cycles
m.damage(0.035 * simulation.dmgScale);
this.death();
}

View File

@@ -47,21 +47,16 @@
if (options.length) {
for (let i = 0; i < num; i++) tech.tech[options[Math.floor(Math.random() * options.length)]].frequency++
}
// for (let i = 0; i < num; i++) {
// // find an index that doesn't have dups first
// let index = null
// for (let i = 0; i < tech.junk.length; i++) {
// if (tech.junk[i].numberInPool === 0) {
// index = i
// break
// }
// }
// if (index === null) index = Math.floor(Math.random() * tech.junk.length) //or just pick a random junk tech to add
// tech.junk[index].numberInPool++
// tech.tech.push(Object.assign({}, tech.junk[index])) // push a "clone" of the tech.junk into the pool
// if (tech.junk[index].numberInPool > 1) tech.tech[tech.tech.length - 1].name += ` - ${(tech.junk[index].numberInPool + 9).toString(36)}` //give it a unique name so it can be found
// }
},
removeJunkTechFromPool(num = 1) {
for (let j = 0; j < num; j++) {
for (let i = 0; i < tech.tech.length; i++) {
if (tech.tech[i].isJunk && tech.tech[i].frequency > 0 && tech.tech[i].count < tech.tech[i].maxCount) {
tech.tech[i].frequency--
break
}
}
}
},
// removeJunkTechFromPool() {
// for (let i = tech.tech.length - 1; i > 0; i--) {
@@ -141,7 +136,7 @@
if (tech.isRerollDamage) dmg *= 1 + 0.039 * powerUps.research.count
if (tech.isOneGun && b.inventory.length < 2) dmg *= 1.25
if (tech.isNoFireDamage && m.cycle > m.fireCDcycle + 120) dmg *= 2
if (tech.isSpeedDamage) dmg *= 1 + Math.min(0.4, player.speed * 0.013)
if (tech.isSpeedDamage) dmg *= 1 + Math.min(0.43, player.speed * 0.015)
if (tech.isBotDamage) dmg *= 1 + 0.06 * b.totalBots()
return dmg * tech.slowFire * tech.aimDamage
},
@@ -264,6 +259,28 @@
tech.isGunCycle = false;
}
},
{
name: "gun technology",
description: "</strong>double</strong> the <strong class='flicker'>frequency</strong> of finding <strong class='color-g'>gun</strong> <strong class='color-m'>tech</strong><br>spawn a <strong class='color-g'>gun</strong>",
maxCount: 1,
count: 0,
frequency: 1,
isNonRefundable: true,
// isExperimentHide: true,
// isBadRandomOption: true,
allowed() {
return !tech.isSuperDeterminism
},
requires: "not superdeterminism",
effect() {
powerUps.spawn(m.pos.x, m.pos.y, "gun");
// this.count--
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].isGunTech) tech.tech[i].frequency *= 2
}
},
remove() {}
},
{
name: "specialist",
description: "for every <strong class='color-g'>gun</strong> in your inventory spawn a<br><strong class='color-h'>heal</strong>, <strong class='color-r'>research</strong>, <strong class='color-f'>field</strong>, <strong class='color-g'>ammo</strong>, or <strong class='color-m'>tech</strong>",
@@ -483,7 +500,7 @@
},
{
name: "Newton's 1st law",
description: "moving at high <strong>speeds</strong> reduces <strong class='color-harm'>harm</strong><br>by up to <strong>50%</strong>",
description: "moving at high <strong>speeds</strong> reduces <strong class='color-harm'>harm</strong><br>by up to <strong>60%</strong>",
maxCount: 1,
count: 0,
frequency: 1,
@@ -500,7 +517,7 @@
},
{
name: "Newton's 2nd law",
description: "moving at high <strong>speeds</strong> increases <strong class='color-d'>damage</strong><br> by up to <strong>33%</strong>",
description: "moving at high <strong>speeds</strong> increases <strong class='color-d'>damage</strong><br> by up to <strong>43%</strong>",
maxCount: 1,
count: 0,
frequency: 1,
@@ -1103,19 +1120,27 @@
count: 0,
frequency: 1,
isBotTech: true,
isNonRefundable: true,
// isNonRefundable: true,
allowed() {
return (b.totalBots() > 1 && powerUps.research.count > 0) || build.isExperimentSelection
},
requires: "at least 2 bots, 1 research",
effect: () => {
powerUps.research.changeRerolls(-1)
b.randomBot()
if (powerUps.research.count > 0) {
powerUps.research.changeRerolls(-1)
b.randomBot()
}
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].isBotTech) tech.tech[i].frequency *= 4
}
},
remove() {}
remove() {
if (this.count > 0) {
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].isBotTech) tech.tech[i].frequency /= 4
}
}
}
}, {
name: "perimeter defense",
description: "reduce <strong class='color-harm'>harm</strong> by <strong>6%</strong><br>for each of your permanent <strong class='color-bot'>bots</strong>",
@@ -1248,7 +1273,7 @@
requires: "",
effect() {
tech.collisionImmuneCycles += 45;
if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to collision damage for 30 cycles
if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage for 30 cycles
},
remove() {
tech.collisionImmuneCycles = 30;
@@ -1328,7 +1353,7 @@
ctx.stroke();
//draw eye
ctx.beginPath();
ctx.arc(15, 0, 3, 0, 2 * Math.PI);
ctx.arc(15, 0, 3.5, 0, 2 * Math.PI);
ctx.fillStyle = m.eyeFillColor;
ctx.fill()
ctx.restore();
@@ -2002,6 +2027,27 @@
tech.largerHeals = 1;
}
},
{
name: "healing technology",
description: "</strong>double</strong> the <strong class='flicker'>frequency</strong> of finding <strong class='color-h'>healing</strong> <strong class='color-m'>tech</strong><br>spawn <strong>12</strong> <strong class='color-h'>heals</strong>",
maxCount: 1,
count: 0,
frequency: 1,
isNonRefundable: true,
// isExperimentHide: true,
// isBadRandomOption: true,
allowed() {
return true
},
requires: "",
effect() {
for (let i = 0; i < 12; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "heal");
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].isHealTech) tech.tech[i].frequency *= 2
}
},
remove() {}
},
// {
// name: "perpetual heals",
// description: "find <strong>3</strong> <strong class='color-h'>heals</strong> at the start of each <strong>level</strong>",
@@ -2225,6 +2271,7 @@
if (tech.isSuperDeterminism) count -= 2 //remove the bonus tech
tech.setupAllTech(); // remove all tech
lore.techCount = 0;
// tech.addLoreTechToPool();
for (let i = 0; i < count; i++) { // spawn new tech power ups
powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech");
@@ -2268,7 +2315,7 @@
}
}, {
name: "stimulated emission",
description: "<strong>20%</strong> chance to <strong class='color-dup'>duplicate</strong> spawned <strong>power ups</strong><br>after a <strong>collision</strong>, eject <strong>1</strong> <strong class='color-m'>tech</strong>",
description: "<strong>20%</strong> chance to <strong class='color-dup'>duplicate</strong> spawned <strong>power ups</strong><br>but, after a <strong>collision</strong> eject <strong>1</strong> <strong class='color-m'>tech</strong>",
maxCount: 1,
count: 0,
frequency: 1,
@@ -2291,20 +2338,21 @@
maxCount: 9,
count: 0,
frequency: 1,
isNonRefundable: true,
// isNonRefundable: true,
allowed() {
return tech.duplicationChance() < 1
},
requires: "below 100% duplication chance",
effect() {
tech.duplicateChance += 0.075
tech.maxDuplicationEvent()
simulation.draw.powerUp = simulation.draw.powerUpBonus //change power up draw
tech.addJunkTechToPool(12)
tech.maxDuplicationEvent()
},
remove() {
tech.duplicateChance = 0
if (tech.duplicationChance() === 0) simulation.draw.powerUp = simulation.draw.powerUpNormal
if (this.count > 1) tech.removeJunkTechFromPool(12)
}
}, {
name: "futures exchange",
@@ -2491,7 +2539,7 @@
name: "dark patterns",
description: "reduce combat <strong>difficulty</strong> by <strong>1 level</strong><br>add <strong>18</strong> junk <strong class='color-m'>tech</strong> to the potential pool",
maxCount: 1,
isNonRefundable: true,
// isNonRefundable: true,
// isExperimentHide: true,
count: 0,
frequency: 1,
@@ -2505,7 +2553,12 @@
tech.addJunkTechToPool(18)
// for (let i = 0; i < tech.junk.length; i++) tech.tech.push(tech.junk[i])
},
remove() {}
remove() {
if (this.count > 0) {
tech.removeJunkTechFromPool(18)
level.difficultyIncrease(simulation.difficultyMode)
}
}
}, {
name: "unified field theory",
description: `in the <strong>pause</strong> menu, change your <strong class='color-f'>field</strong><br>by <strong>clicking</strong> on your <strong class='color-f'>field's</strong> box`,
@@ -2522,8 +2575,36 @@
remove() {
tech.isGunSwitchField = false;
}
}, {
name: "statistical ensemble",
},
{
name: "field technology",
description: "</strong>double</strong> the <strong class='flicker'>frequency</strong> of finding <strong class='color-f'>field</strong> <strong class='color-m'>tech</strong><br>spawn a <strong class='color-f'>field</strong>",
maxCount: 1,
count: 0,
frequency: 1,
// isNonRefundable: true,
// isExperimentHide: true,
// isBadRandomOption: true,
allowed() {
return !tech.isSuperDeterminism
},
requires: "not superdeterminism",
effect() {
powerUps.spawn(m.pos.x, m.pos.y, "field");
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].isFieldTech) tech.tech[i].frequency *= 2
}
},
remove() {
if (this.count > 1) {
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].isFieldTech) tech.tech[i].frequency /= 2
}
}
}
},
{
name: "reinforcement learning",
description: "increase the <strong class='flicker'>frequency</strong> of finding copies of<br>recursive <strong class='color-m'>tech</strong> you already have by <strong>10000%</strong>",
maxCount: 1,
count: 0,
@@ -2535,10 +2616,14 @@
requires: "at least 10 tech",
effect: () => {
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].count > 0) tech.tech[i].frequency += 100
if (tech.tech[i].count > 0) tech.tech[i].frequency *= 100
}
},
remove() {}
remove() {
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].count > 0) tech.tech[i].frequency /= 100
}
}
}, {
name: "cardinality",
description: "<strong class='color-m'>tech</strong>, <strong class='color-f'>fields</strong>, and <strong class='color-g'>guns</strong> have <strong>5</strong> <strong>choices</strong>",
@@ -2596,88 +2681,28 @@
remove() {
tech.isSuperDeterminism = false;
}
}, {
name: "gun technology",
description: "</strong>double</strong> the <strong class='flicker'>frequency</strong> of finding <strong class='color-g'>gun</strong> <strong class='color-m'>tech</strong><br>spawn a <strong class='color-g'>gun</strong>",
maxCount: 1,
count: 0,
frequency: 1,
isNonRefundable: true,
// isExperimentHide: true,
// isBadRandomOption: true,
allowed() {
return !tech.isSuperDeterminism
},
requires: "not superdeterminism",
effect() {
powerUps.spawn(m.pos.x, m.pos.y, "gun");
// this.count--
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].isGunTech) tech.tech[i].frequency *= 2
}
},
remove() {}
}, {
name: "ammo technology",
description: "</strong>double</strong> the <strong class='flicker'>frequency</strong> of finding <strong class='color-g'>gun</strong> <strong class='color-m'>tech</strong><br>spawn <strong>6</strong> <strong class='color-g'>ammo</strong>",
maxCount: 1,
count: 0,
frequency: 1,
isNonRefundable: true,
// isExperimentHide: true,
// isBadRandomOption: true,
allowed() {
return !tech.isEnergyNoAmmo
},
requires: "not exciton lattice",
effect() {
for (let i = 0; i < 6; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "ammo");
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].isGunTech) tech.tech[i].frequency *= 2
}
},
remove() {}
}, {
name: "field technology",
description: "</strong>double</strong> the <strong class='flicker'>frequency</strong> of finding <strong class='color-f'>field</strong> <strong class='color-m'>tech</strong><br>spawn a <strong class='color-f'>field</strong>",
maxCount: 1,
count: 0,
frequency: 1,
isNonRefundable: true,
// isExperimentHide: true,
// isBadRandomOption: true,
allowed() {
return !tech.isSuperDeterminism
},
requires: "not superdeterminism",
effect() {
powerUps.spawn(m.pos.x, m.pos.y, "field");
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].isFieldTech) tech.tech[i].frequency *= 2
}
},
remove() {}
}, {
name: "healing technology",
description: "</strong>double</strong> the <strong class='flicker'>frequency</strong> of finding <strong class='color-h'>healing</strong> <strong class='color-m'>tech</strong><br>spawn <strong>12</strong> <strong class='color-h'>heals</strong>",
maxCount: 1,
count: 0,
frequency: 1,
isNonRefundable: true,
// isExperimentHide: true,
// isBadRandomOption: true,
allowed() {
return true
},
requires: "",
effect() {
for (let i = 0; i < 12; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "heal");
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].isHealTech) tech.tech[i].frequency *= 200
}
},
remove() {}
},
// {
// name: "ammo technology",
// description: "</strong>double</strong> the <strong class='flicker'>frequency</strong> of finding <strong class='color-g'>gun</strong> <strong class='color-m'>tech</strong><br>spawn <strong>6</strong> <strong class='color-g'>ammo</strong>",
// maxCount: 1,
// count: 0,
// frequency: 1,
// isNonRefundable: true,
// // isExperimentHide: true,
// // isBadRandomOption: true,
// allowed() {
// return !tech.isEnergyNoAmmo
// },
// requires: "not exciton lattice",
// effect() {
// for (let i = 0; i < 6; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "ammo");
// for (let i = 0, len = tech.tech.length; i < len; i++) {
// if (tech.tech[i].isGunTech) tech.tech[i].frequency *= 2
// }
// },
// remove() {}
// },
//**************************************************
//************************************************** gun
//************************************************** tech
@@ -2834,7 +2859,7 @@
count: 0,
frequency: 1,
allowed() {
return tech.haveGunCheck("nail gun") && !tech.nailFireRate && !tech.isIceCrystals && !tech.isRivets && !tech.isNailRadiation
return tech.haveGunCheck("nail gun") && !tech.nailFireRate && !tech.isIceCrystals && !tech.isRivets
},
requires: "nail gun, not ice crystal, rivets, or pneumatic actuator",
effect() {
@@ -4513,7 +4538,7 @@
}
}, {
name: "Penrose process",
description: "after a <strong>block</strong> falls into a <strong class='color-worm'>wormhole</strong><br>you gain <strong>50</strong> <strong class='color-f'>energy</strong>",
description: "after a <strong>block</strong> falls into a <strong class='color-worm'>wormhole</strong><br>you gain <strong>63</strong> <strong class='color-f'>energy</strong>",
isFieldTech: true,
maxCount: 1,
count: 0,
@@ -4959,7 +4984,7 @@
},
requires: "",
effect() {
for (let i = 0, len = Math.floor(m.energy * 40); i < len; i++) {
for (let i = 0, len = 40; i < len; i++) {
setTimeout(() => {
m.energy -= 1 / len
const index = body.length

View File

@@ -1,8 +1,15 @@
******************************************************** NEXT PATCH ********************************************************
new mob: pulsar - aims at player and does damage in an circle
(set to 3x chance to show up until the next patch)
several tech that were nonrefundable now can be removed and refunded
added several bug fixes
******************************************************** BUGS ********************************************************
micro-extruder is laggy?
mouse event e.which is deprecated
fix door.isOpen actually meaning isClosed?
@@ -45,6 +52,16 @@ use the floor of portal sensor on the player? to unstuck player
******************************************************** TODO ********************************************************
remove pulsar from high chance to show on next patch
mob bullets that blow up
draw outline of exploded region, 2 seconds later damage player in region and remove bullet
mob sniper: draw aim graphics before fire
tech laser: photon - laser, but it can only move 100 pixels a cycle
mob - grows after taking damage
mob - attack outwardly after taking damage
@@ -328,7 +345,10 @@ possible names for tech
holonomy - parallel transport of a vector leads to movement (applies to curved space)
hypergolic - A hypergolic propellant combination used in a rocket engine is one whose components spontaneously ignite when they come into contact with each other.
uncertainty principle
swarm intelligence - for a drone tech
genetic algorithm
metaheuristic - is a higher-level procedure or heuristic designed to find, generate, or select a heuristic (partial search algorithm) that may provide a sufficiently good solution to an optimization problem, especially with incomplete or imperfect information or limited computation capacity
stochastic optimization
plot script: