diff --git a/.DS_Store b/.DS_Store
index 3b660c6..83d170e 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/js/bullet.js b/js/bullet.js
index e17a5d3..7dea5e7 100644
--- a/js/bullet.js
+++ b/js/bullet.js
@@ -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) {
diff --git a/js/engine.js b/js/engine.js
index 55e33f3..a5c02d7 100644
--- a/js/engine.js
+++ b/js/engine.js
@@ -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, {
diff --git a/js/index.js b/js/index.js
index 57a3b5e..428ecf7 100644
--- a/js/index.js
+++ b/js/index.js
@@ -241,6 +241,8 @@ const build = {
text += `
${tech.tech[i].name} ${isCount}
${tech.tech[i].description}
`
}
countTech++
+ } else if (tech.tech[i].isLost) {
+ text += `${tech.tech[i].name}
${tech.tech[i].description}
`
}
}
el = document.getElementById("pause-grid-right")
diff --git a/js/level.js b/js/level.js
index c1a5df1..babfb06 100644
--- a/js/level.js
+++ b/js/level.js
@@ -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)
diff --git a/js/player.js b/js/player.js
index a8f171e..78b6258 100644
--- a/js/player.js
+++ b/js/player.js
@@ -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: "cloak after not using your gun or field
while cloaked mobs can't see you
increase damage by 133%",
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, {
diff --git a/js/powerup.js b/js/powerup.js
index 4dda1de..4f5ba6d 100644
--- a/js/powerup.js
+++ b/js/powerup.js
@@ -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 tech left
erased tech 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()
diff --git a/js/simulation.js b/js/simulation.js
index 4f1434c..dd0dc5c 100644
--- a/js/simulation.js
+++ b/js/simulation.js
@@ -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;
diff --git a/js/spawn.js b/js/spawn.js
index 82ca803..309fef4 100644
--- a/js/spawn.js
+++ b/js/spawn.js
@@ -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(`undefined = ${lore.techCount}/${lore.techGoal}
level.levels.push("null")`);
- //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(`undefined = ${lore.techCount}/${lore.techGoal}
level.levels.push("null")`);
+ 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 = Infinite`);
+ 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();
}
diff --git a/js/tech.js b/js/tech.js
index 92bce6e..e03d315 100644
--- a/js/tech.js
+++ b/js/tech.js
@@ -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: "double the frequency of finding gun tech
spawn a gun",
+ 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 gun in your inventory spawn a
heal, research, field, ammo, or tech",
@@ -483,7 +500,7 @@
},
{
name: "Newton's 1st law",
- description: "moving at high speeds reduces harm
by up to 50%",
+ description: "moving at high speeds reduces harm
by up to 60%",
maxCount: 1,
count: 0,
frequency: 1,
@@ -500,7 +517,7 @@
},
{
name: "Newton's 2nd law",
- description: "moving at high speeds increases damage
by up to 33%",
+ description: "moving at high speeds increases damage
by up to 43%",
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 harm by 6%
for each of your permanent bots",
@@ -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: "double the frequency of finding healing tech
spawn 12 heals",
+ 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 3 heals at the start of each level",
@@ -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: "20% chance to duplicate spawned power ups
after a collision, eject 1 tech",
+ description: "20% chance to duplicate spawned power ups
but, after a collision eject 1 tech",
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 difficulty by 1 level
add 18 junk tech 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 pause menu, change your field
by clicking on your field's box`,
@@ -2522,8 +2575,36 @@
remove() {
tech.isGunSwitchField = false;
}
- }, {
- name: "statistical ensemble",
+ },
+ {
+ name: "field technology",
+ description: "double the frequency of finding field tech
spawn a field",
+ 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 frequency of finding copies of
recursive tech you already have by 10000%",
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: "tech, fields, and guns have 5 choices",
@@ -2596,88 +2681,28 @@
remove() {
tech.isSuperDeterminism = false;
}
- }, {
- name: "gun technology",
- description: "double the frequency of finding gun tech
spawn a gun",
- 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: "double the frequency of finding gun tech
spawn 6 ammo",
- 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: "double the frequency of finding field tech
spawn a field",
- 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: "double the frequency of finding healing tech
spawn 12 heals",
- 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: "double the frequency of finding gun tech
spawn 6 ammo",
+ // 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 block falls into a wormhole
you gain 50 energy",
+ description: "after a block falls into a wormhole
you gain 63 energy",
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
diff --git a/todo.txt b/todo.txt
index 81bf5bd..4983842 100644
--- a/todo.txt
+++ b/todo.txt
@@ -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: