diff --git a/img/parasitism.webp b/img/parasitism.webp
deleted file mode 100644
index 3db9826..0000000
Binary files a/img/parasitism.webp and /dev/null differ
diff --git a/js/bullet.js b/js/bullet.js
index 08ffc80..9c00921 100644
--- a/js/bullet.js
+++ b/js/bullet.js
@@ -3160,7 +3160,7 @@ const b = {
if (
Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 &&
!(
- (m.health > 0.93 * m.maxHealth && !tech.isDroneGrab && powerUp[i].name === "heal") ||
+ (m.health > 0.94 * m.maxHealth && !tech.isOverHeal && !tech.isDroneGrab && powerUp[i].name === "heal") ||
(tech.isSuperDeterminism && powerUp[i].name === "field") ||
((tech.isEnergyNoAmmo || b.inventory.length === 0) && powerUp[i].name === "ammo")
)
@@ -3192,7 +3192,7 @@ const b = {
let closeDist = Infinity;
for (let i = 0, len = powerUp.length; i < len; ++i) {
if (!(
- (m.health > 0.93 * m.maxHealth && !tech.isDroneGrab && powerUp[i].name === "heal") ||
+ (m.health > 0.94 * m.maxHealth && !tech.isOverHeal && !tech.isDroneGrab && powerUp[i].name === "heal") ||
(tech.isSuperDeterminism && powerUp[i].name === "field") ||
((tech.isEnergyNoAmmo || b.inventory.length === 0) && powerUp[i].name === "ammo")
)) {
diff --git a/js/index.js b/js/index.js
index e36a298..768bd67 100644
--- a/js/index.js
+++ b/js/index.js
@@ -28,11 +28,11 @@ Math.seededRandom = function (min = 0, max = 1) { // in order to work 'Math.seed
// console.log(Math.seed)
-function shuffle(array) {
+function seededShuffle(array) {
var currentIndex = array.length,
temporaryValue,
randomIndex;
- // While there remain elements to shuffle...
+ // While there remain elements
while (0 !== currentIndex) {
// Pick a remaining element...
// randomIndex = Math.floor(Math.random() * currentIndex);
@@ -542,7 +542,7 @@ ${simulation.difficultyMode > 4 ? `
console log
-
${document.getElementById("text-log").innerHTML}
+
${document.getElementById("text-log").innerHTML}
`
@@ -1205,6 +1205,7 @@ const input = {
left: false,
right: false,
isPauseKeyReady: true,
+ // lastDown: null,
key: {
fire: "KeyF",
field: "Space",
@@ -1379,6 +1380,7 @@ window.addEventListener("keyup", function (event) {
});
window.addEventListener("keydown", function (event) {
+ // input.lastDown = event.code
// console.log(event.code)
switch (event.code) {
case input.key.right:
diff --git a/js/level.js b/js/level.js
index 85eb04b..7c70f51 100644
--- a/js/level.js
+++ b/js/level.js
@@ -22,7 +22,7 @@ const level = {
// simulation.isHorizontalFlipped = true
// spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns
// spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns
- // level.levelsCleared = 10
+ // level.levelsCleared = 9
// level.updateDifficulty()
// tech.giveTech("performance")
// m.maxHealth = m.health = 100000000
@@ -33,7 +33,7 @@ const level = {
// tech.tech[297].frequency = 100
// tech.addJunkTechToPool(0.5)
// m.couplingChange(10)
- // m.setField("negative mass") //1 standing wave 2 perfect diamagnetism 3 negative mass 4 molecular assembler 5 plasma torch 6 time dilation 7 metamaterial cloaking 8 pilot wave 9 wormhole 10 grappling hook
+ // m.setField("wormhole") //1 standing wave 2 perfect diamagnetism 3 negative mass 4 molecular assembler 5 plasma torch 6 time dilation 7 metamaterial cloaking 8 pilot wave 9 wormhole 10 grappling hook
// m.energy = 0
// powerUps.research.count = 3
// tech.isHookWire = true
@@ -58,7 +58,7 @@ const level = {
// requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("interest") });
// for (let i = 0; i < 1; i++) tech.giveTech("interest")
// m.lastKillCycle = m.cycle
- // for (let i = 0; i < 7; i++) powerUps.directSpawn(450, -50, "tech");
+ // for (let i = 0; i < 7; i++) powerUps.directSpawn(450, -50, "field");
// for (let i = 0; i < 7; i++) powerUps.directSpawn(m.pos.x + 200, m.pos.y - 250, "research", false);
// spawn.bodyRect(575, -700, 150, 150); //block mob line of site on testing
// level.testing();
@@ -810,14 +810,14 @@ const level = {
if (document.getElementById("seed").value) { //check for player entered seed in settings
Math.initialSeed = String(document.getElementById("seed").value)
- Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed in case the player changed it
}
+ Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed
if (simulation.isTraining) {
simulation.isHorizontalFlipped = false
level.levels = level.trainingLevels.slice(0) //copy array, not by just by assignment
if (simulation.isCommunityMaps) level.trainingLevels.push("diamagnetism")
- } else { //add remove and shuffle levels for the normal game (not training levels)
+ } else {
level.levels = level.playableLevels.slice(0) //copy array, not by just by assignment
if (simulation.isCommunityMaps) {
level.levels = level.levels.concat(level.communityLevels)
@@ -825,7 +825,7 @@ const level = {
} else {
simulation.isHorizontalFlipped = (Math.seededRandom() < 0.5) ? true : false //if true, some maps are flipped horizontally
}
- level.levels = shuffle(level.levels); //shuffles order of maps with seeded random
+ level.levels = seededShuffle(level.levels); //shuffles order of maps with seeded random
level.levels.length = 9 //remove any extra levels past 9
pick = ["interferometer", "factory", "reservoir"]
level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.6, level.levels.length)), 0, pick[Math.floor(Math.random() * pick.length)]); //add level to the back half of the randomized levels list
@@ -3533,7 +3533,7 @@ const level = {
const stationList = [] //use to randomize station order
for (let i = 1, totalNumberOfStations = 10; i < totalNumberOfStations; ++i) stationList.push(i) //!!!! update station number when you add a new station
- shuffle(stationList);
+ stationList.sort(() => Math.random() - 0.5);
stationList.splice(0, 3); //remove some stations to keep it to 4 stations
stationList.unshift(0) //add index zero to the front of the array
@@ -6690,14 +6690,14 @@ const level = {
//3x2: 4 short rooms (3000x1500), 1 double tall room (3000x3000)
//rooms
let rooms = ["exit", "loot", "enter", "empty"]
- rooms = shuffle(rooms); //shuffles array order
+ rooms.sort(() => Math.random() - 0.5);
//look... you and I both know there is a better way to do this, but it works so I'm gonna focus on other things
while ( //makes sure that the exit and entrance aren't both on the same floor
(rooms[0] === "enter" && rooms[2] === "exit") ||
(rooms[2] === "enter" && rooms[0] === "exit") ||
(rooms[1] === "enter" && rooms[3] === "exit") ||
(rooms[3] === "enter" && rooms[1] === "exit")
- ) rooms = shuffle(rooms); //shuffles array order
+ ) rooms.sort(() => Math.random() - 0.5);
for (let i = 0; i < rooms.length; i++) {
if (rooms[i] === "enter") rooms[i] = enter
if (rooms[i] === "exit") rooms[i] = exit
@@ -6764,7 +6764,7 @@ const level = {
rooms[3]()
},
]
- columns = shuffle(columns) //********************************* RUN THIS LINE IN THE FINAL VERSION ***************************************
+ columns.sort(() => Math.random() - 0.5);
for (let i = 0; i < 3; i++) {
if (i === 0) {
isDoorLeft = false
@@ -7054,7 +7054,7 @@ const level = {
};
powerUps.spawnStartingPowerUps(1875, -3075);
- const powerUpPos = shuffle([{ //no debris on this level but 2 random spawn instead
+ const powerUpPos = [{ //no debris on this level but 2 random spawn instead
x: -150,
y: -1775
}, {
@@ -7066,7 +7066,8 @@ const level = {
}, {
x: 1325,
y: -150
- }]);
+ }];
+ powerUpPos.sort(() => Math.random() - 0.5);
powerUps.chooseRandomPowerUp(powerUpPos[0].x, powerUpPos[0].y);
powerUps.chooseRandomPowerUp(powerUpPos[1].x, powerUpPos[1].y);
//outer wall
@@ -8858,7 +8859,8 @@ const level = {
// spawn.bodyRect(312, -100, 25, 100);
spawn.bodyRect(1450, -300, 150, 50);
- const xPos = shuffle([600, 1250, 2000]);
+ const xPos = [600, 1250, 2000];
+ xPos.sort(() => Math.random() - 0.5);
spawn.mapRect(xPos[0], -200, 300, 100);
spawn.mapRect(xPos[1], -250, 300, 300);
spawn.mapRect(xPos[2], -150, 300, 200);
diff --git a/js/mob.js b/js/mob.js
index df145f9..9027446 100644
--- a/js/mob.js
+++ b/js/mob.js
@@ -1069,7 +1069,11 @@ const mobs = {
if (tech.isFarAwayDmg) dmg *= 1 + Math.sqrt(Math.max(500, Math.min(3000, this.distanceToPlayer())) - 500) * 0.0067 //up to 33% dmg at max range of 3000
dmg *= this.damageReduction
//energy and heal drain should be calculated after damage boosts
- if (tech.energySiphon && dmg !== Infinity && this.isDropPowerUp && m.immuneCycle < m.cycle) m.energy += Math.min(this.health, dmg) * tech.energySiphon * level.isReducedRegen
+ if (tech.energySiphon && this.isDropPowerUp && m.immuneCycle < m.cycle) {
+ //dmg !== Infinity &&
+ const regen = Math.min(this.health, dmg) * tech.energySiphon * level.isReducedRegen
+ if (!isNaN(regen) && regen !== Infinity) m.energy += regen
+ }
dmg /= Math.sqrt(this.mass)
}
@@ -1129,7 +1133,7 @@ const mobs = {
for (let i = 0; i < mob.length; i++) {
if (Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)) < 500000 && mob[i].alive) { //700
if (mob[i].health < 1) {
- mob[i].health += 0.33 + this.isBoss
+ mob[i].health += 0.33
if (mob[i].health > 1) mob[i].health = 1
simulation.drawList.push({
x: mob[i].position.x,
diff --git a/js/player.js b/js/player.js
index 39a41bc..1484d82 100644
--- a/js/player.js
+++ b/js/player.js
@@ -1226,11 +1226,7 @@ const m = {
m.isAltSkin = true
m.yOffWhen.stand = 52
m.yOffWhen.jump = 72
- m.coyoteCycles = 11
- m.hardLandCDScale = 0.5
- m.hardLanding = 160
- m.squirrelFx = 1.4;
- m.squirrelJump = 1.16;
+ m.squirrelJump = 1.15;
m.setMovement()
m.draw = function () {
@@ -3631,6 +3627,9 @@ const m = {
// if ((m.fieldMode === 0 || m.fieldMode === 9) && !build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.4);
},
setField(index) {
+ // console.log("field mode: ", index)
+ window.removeEventListener("keydown", m.fieldEvent);
+
if (isNaN(index)) { //find index by name
let found = false
for (let i = 0; i < m.fieldUpgrades.length; i++) {
@@ -3647,1520 +3646,684 @@ const m = {
m.setHoldDefaults();
m.fieldUpgrades[index].effect();
simulation.inGameConsole(`
m.setField("${m.fieldUpgrades[m.fieldMode].name}")
input.key.field: ["MouseRight"]`);
+ if (m.fieldMode === 4) simulation.inGameConsole(`simulation.molecularMode = ${m.fieldUpgrades[4].modeText()} ↓↘→↓↙←↑↑↓`);
},
- fieldUpgrades: [{
- name: "field emitter",
- imageNumber: Math.floor(Math.random() * 26), //pick one of the 25 field emitter image files at random
- description: `initial field
use energy to deflect mobs and throw blocks
+ fieldEvent: null,
+ fieldUpgrades: [
+ {
+ name: "field emitter",
+ imageNumber: Math.floor(Math.random() * 26), //pick one of the 25 field emitter image files at random
+ description: `initial field
use energy to deflect mobs and throw blocks
4 energy per second`, //
100 max energy
- effect: () => {
- m.hold = function () {
- if (m.isHolding) {
- m.drawHold(m.holdingTarget);
- m.holding();
- m.throwBlock();
- } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed
- if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
- m.grabPowerUp();
- m.lookForPickUp();
- if (m.energy > m.minEnergyToDeflect) {
- m.drawField();
- m.pushMobsFacing();
- }
- } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
- m.pickUp();
- } else {
- m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
- }
- m.drawRegenEnergy()
- }
- }
- },
- {
- name: "standing wave",
- //deflecting protects you in every direction
- description: `3 oscillating shields are permanently active
-
+150 max energy
-
6 energy per second`,
- drainCD: 0,
- effect: () => {
- m.fieldBlockCD = 0;
- m.blockingRecoil = 1 //4 is normal
- m.fieldRange = 185
- m.fieldShieldingScale = 1.6 * Math.pow(0.5, (tech.harmonics - 2))
- // m.fieldHarmReduction = 0.66; //33% reduction
-
- m.harmonic3Phase = () => { //normal standard 3 different 2-d circles
- const fieldRange1 = (0.75 + 0.3 * Math.sin(m.cycle / 23)) * m.fieldRange * m.harmonicRadius
- const fieldRange2 = (0.68 + 0.37 * Math.sin(m.cycle / 37)) * m.fieldRange * m.harmonicRadius
- const fieldRange3 = (0.7 + 0.35 * Math.sin(m.cycle / 47)) * m.fieldRange * m.harmonicRadius
- const netFieldRange = Math.max(fieldRange1, fieldRange2, fieldRange3)
- ctx.fillStyle = "rgba(110,170,200," + Math.min(0.6, (0.04 + 0.7 * m.energy * (0.1 + 0.11 * Math.random()))) + ")";
- ctx.beginPath();
- ctx.arc(m.pos.x, m.pos.y, fieldRange1, 0, 2 * Math.PI);
- ctx.fill();
- ctx.beginPath();
- ctx.arc(m.pos.x, m.pos.y, fieldRange2, 0, 2 * Math.PI);
- ctx.fill();
- ctx.beginPath();
- ctx.arc(m.pos.x, m.pos.y, fieldRange3, 0, 2 * Math.PI);
- ctx.fill();
- //360 block
- for (let i = 0, len = mob.length; i < len; ++i) {
- if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < netFieldRange && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0
- mob[i].locatePlayer();
- if (this.drainCD > m.cycle) {
- m.pushMass(mob[i], 0);
- } else {
- m.pushMass(mob[i]);
- this.drainCD = m.cycle + 15
- }
- }
- }
- }
- m.harmonicRadius = 1 //for smoothing function when player holds mouse (for harmonicAtomic)
- m.harmonicAtomic = () => { //several ellipses spinning about different axises
- const rotation = simulation.cycle * 0.0031
- const phase = simulation.cycle * 0.023
- const radius = m.fieldRange * m.harmonicRadius
- ctx.lineWidth = 1;
- ctx.strokeStyle = "rgba(110,170,200,0.8)"
- ctx.fillStyle = "rgba(110,170,200," + Math.min(0.6, 0.7 * m.energy * (0.11 + 0.1 * Math.random()) * (3 / tech.harmonics)) + ")";
- // ctx.fillStyle = "rgba(110,170,200," + Math.min(0.7, m.energy * (0.22 - 0.01 * tech.harmonics) * (0.5 + 0.5 * Math.random())) + ")";
- for (let i = 0; i < tech.harmonics; i++) {
- ctx.beginPath();
- ctx.ellipse(m.pos.x, m.pos.y, radius * Math.abs(Math.sin(phase + i / tech.harmonics * Math.PI)), radius, rotation + i / tech.harmonics * Math.PI, 0, 2 * Math.PI);
- ctx.fill();
- ctx.stroke();
- }
- //360 block
- for (let i = 0, len = mob.length; i < len; ++i) {
- if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < radius && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0
- mob[i].locatePlayer();
- if (this.drainCD > m.cycle) {
- m.pushMass(mob[i], 0);
- } else {
- m.pushMass(mob[i]);
- this.drainCD = m.cycle + 15
- }
- }
- }
- }
- if (tech.harmonics === 2) {
- m.harmonicShield = m.harmonic3Phase
- } else {
- m.harmonicShield = m.harmonicAtomic
- }
- m.hold = function () {
- if (m.isHolding) {
- m.drawHold(m.holdingTarget);
- m.holding();
- m.throwBlock();
- } else if ((input.field) && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed
- if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
- m.grabPowerUp();
- m.lookForPickUp();
- } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
- m.pickUp();
- } else {
- m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
- }
- if (m.energy > m.minEnergyToDeflect && m.fieldCDcycle < m.cycle) {
- if (tech.isStandingWaveExpand) {
- if (input.field) {
- // const oldHarmonicRadius = m.harmonicRadius
- m.harmonicRadius = 0.99 * m.harmonicRadius + 0.01 * 4
- // m.energy -= 0.1 * (m.harmonicRadius - oldHarmonicRadius)
- } else {
- m.harmonicRadius = 0.994 * m.harmonicRadius + 0.006
- }
- }
- if (!simulation.isTimeSkipping) m.harmonicShield()
- }
- m.drawRegenEnergy()
- }
- }
- },
- {
- name: "perfect diamagnetism",
- description: `deflecting does not drain energy
shield maintains functionality while inactive
5 energy per second`,
- effect: () => {
- m.fieldMeterColor = "#48f" //"#0c5"
- m.eyeFillColor = m.fieldMeterColor
- m.fieldShieldingScale = 0;
- m.fieldBlockCD = 3;
- m.grabPowerUpRange2 = 10000000
- m.fieldPosition = { x: m.pos.x, y: m.pos.y }
- m.fieldAngle = m.angle
- m.perfectPush = (isFree = false) => {
- if (m.fieldCDcycle < m.cycle) {
- for (let i = 0, len = mob.length; i < len; ++i) {
- if (
- Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) - mob[i].radius < m.fieldRange &&
- !mob[i].isUnblockable &&
- Vector.dot({ x: Math.cos(m.fieldAngle), y: Math.sin(m.fieldAngle) }, Vector.normalise(Vector.sub(mob[i].position, m.fieldPosition))) > m.fieldThreshold &&
- Matter.Query.ray(map, mob[i].position, m.fieldPosition).length === 0
- ) {
- mob[i].locatePlayer();
- const unit = Vector.normalise(Vector.sub(m.fieldPosition, mob[i].position))
- m.fieldCDcycle = m.cycle + m.fieldBlockCD + (mob[i].isShielded ? 10 : 0);
- if (!mob[i].isInvulnerable && bullet.length < 250) {
- for (let i = 0; i < m.coupling; i++) {
- if (0.1 * m.coupling - i > Math.random()) {
- const angle = m.fieldAngle + 4 * m.fieldArc * (Math.random() - 0.5)
- const radius = m.fieldRange * (0.6 + 0.3 * Math.random())
- b.iceIX(6 + 6 * Math.random(), angle, Vector.add(m.fieldPosition, {
- x: radius * Math.cos(angle),
- y: radius * Math.sin(angle)
- }))
- }
- }
- }
- if (tech.blockDmg) { //electricity
- Matter.Body.setVelocity(mob[i], { x: 0.5 * mob[i].velocity.x, y: 0.5 * mob[i].velocity.y });
- if (mob[i].isShielded) {
- for (let j = 0, len = mob.length; j < len; j++) {
- if (mob[j].id === mob[i].shieldID) mob[j].damage(tech.blockDmg * m.dmgScale * (tech.isBlockRadiation ? 6 : 2), true)
- }
- } else if (tech.isBlockRadiation) {
- if (mob[i].isMobBullet) {
- mob[i].damage(tech.blockDmg * m.dmgScale * 3, true)
- } else {
- mobs.statusDoT(mob[i], tech.blockDmg * m.dmgScale * 0.42, 180) //200% increase -> x (1+2) //over 7s -> 360/30 = 12 half seconds -> 3/12
- }
- } else {
- mob[i].damage(tech.blockDmg * m.dmgScale, true)
- }
- // if (mob[i].isShielded) {
- // for (let j = 0, len = mob.length; j < len; j++) {
- // if (mob[j].id === mob[i].shieldID) mob[j].damage(tech.blockDmg * m.dmgScale * (tech.isBlockRadiation ? 3 : 1), true)
- // }
- // } else {
- // if (tech.isBlockRadiation && !mob[i].isMobBullet) {
- // mobs.statusDoT(mob[i], tech.blockDmg * m.dmgScale * 4 / 12, 360) //200% increase -> x (1+2) //over 7s -> 360/30 = 12 half seconds -> 3/12
- // } else {
- // mob[i].damage(tech.blockDmg * m.dmgScale)
- // }
- // }
- const step = 40
- ctx.beginPath();
- for (let i = 0, len = 0.5 * tech.blockDmg; i < len; i++) {
- let x = m.fieldPosition.x - 20 * unit.x;
- let y = m.fieldPosition.y - 20 * unit.y;
- ctx.moveTo(x, y);
- for (let i = 0; i < 8; i++) {
- x += step * (-unit.x + 1.5 * (Math.random() - 0.5))
- y += step * (-unit.y + 1.5 * (Math.random() - 0.5))
- ctx.lineTo(x, y);
- }
- }
- ctx.lineWidth = 3;
- ctx.strokeStyle = "#f0f";
- ctx.stroke();
- } else if (isFree) {
- ctx.lineWidth = 2; //when blocking draw this graphic
- ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})`
- ctx.strokeStyle = "#000";
- const len = mob[i].vertices.length - 1;
- const mag = mob[i].radius
- ctx.beginPath();
- ctx.moveTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5))
- for (let j = 0; j < len; j++) {
- ctx.lineTo(mob[i].vertices[j].x + mag * (Math.random() - 0.5), mob[i].vertices[j].y + mag * (Math.random() - 0.5));
- }
- ctx.lineTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5))
- ctx.fill();
- ctx.stroke();
- } else {
-
- const eye = 15; //when blocking draw this graphic
- const len = mob[i].vertices.length - 1;
- ctx.lineWidth = 1;
- ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})`
- ctx.strokeStyle = "#000";
- ctx.beginPath();
- ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle));
- ctx.lineTo(mob[i].vertices[len].x, mob[i].vertices[len].y);
- ctx.lineTo(mob[i].vertices[0].x, mob[i].vertices[0].y);
- ctx.fill();
- ctx.stroke();
- for (let j = 0; j < len; j++) {
- ctx.beginPath();
- ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle));
- ctx.lineTo(mob[i].vertices[j].x, mob[i].vertices[j].y);
- ctx.lineTo(mob[i].vertices[j + 1].x, mob[i].vertices[j + 1].y);
- ctx.fill();
- ctx.stroke();
- }
- }
- m.bulletsToBlocks(mob[i])
- if (tech.isStunField) mobs.statusStun(mob[i], tech.isStunField)
- //mob knock backs
- const massRoot = Math.sqrt(Math.max(1, mob[i].mass));
- Matter.Body.setVelocity(mob[i], {
- x: player.velocity.x - (30 * unit.x) / massRoot,
- y: player.velocity.y - (30 * unit.y) / massRoot
- });
- if (mob[i].isUnstable) {
- if (m.fieldCDcycle < m.cycle + 10) m.fieldCDcycle = m.cycle + 6
- mob[i].death();
- }
- if (!isFree) { //player knock backs
- if (mob[i].isDropPowerUp && player.speed < 12) {
- const massRootCap = Math.sqrt(Math.min(10, Math.max(0.2, mob[i].mass)));
- Matter.Body.setVelocity(player, {
- x: 0.9 * player.velocity.x + 0.6 * unit.x * massRootCap,
- y: 0.9 * player.velocity.y + 0.6 * unit.y * massRootCap
- });
- }
- }
- }
- }
- }
- }
- m.hold = function () {
- const wave = Math.sin(m.cycle * 0.022);
- m.fieldRange = 180 + 12 * wave + 100 * tech.isBigField
- m.fieldArc = 0.35 + 0.045 * wave + 0.065 * tech.isBigField //run calculateFieldThreshold after setting fieldArc, used for powerUp grab and mobPush with lookingAt(mob)
- m.calculateFieldThreshold();
- if (m.isHolding) {
- m.drawHold(m.holdingTarget);
- m.holding();
- m.throwBlock();
- } else if (input.field) { //not hold but field button is pressed
- //float while field is on
- const angleReduction = 0.5 + 0.7 * (Math.PI / 2 - Math.min(Math.PI / 2, Math.abs(m.angle + Math.PI / 2)))
- // console.log(angleReduction)
- if (player.velocity.y > 1) {
- player.force.y -= angleReduction * (tech.isBigField ? 0.95 : 0.5) * player.mass * simulation.g;
- Matter.Body.setVelocity(player, {
- x: player.velocity.x,
- y: 0.98 * player.velocity.y
- }); //set velocity to cap, but keep the direction
- }
-
- // go invulnerable while field is active, but also drain energy
- // if (true && m.energy > 2 * m.fieldRegen && m.immuneCycle < m.cycle + tech.cyclicImmunity) {
- // m.immuneCycle = m.cycle + 1; //player is immune to damage for 60 cycles
- // m.energy -= 2 * m.fieldRegen
- // if (m.energy < m.fieldRegen) m.fieldCDcycle = m.cycle + 90;
- // }
-
- if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
- m.grabPowerUp();
- m.lookForPickUp();
- m.fieldPosition = { x: m.pos.x, y: m.pos.y }
- m.fieldAngle = m.angle
- //draw field attached to player
- if (m.holdingTarget) {
- ctx.fillStyle = `rgba(110,150,220, ${0.06 + 0.03 * Math.random()})`
- ctx.strokeStyle = `rgba(110,150,220, ${0.35 + 0.05 * Math.random()})`
- } else {
- ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})`
- ctx.strokeStyle = `rgba(110,150,220, ${0.4 + 0.5 * Math.random()})`
- }
- ctx.beginPath();
- ctx.arc(m.pos.x, m.pos.y, m.fieldRange, m.angle - Math.PI * m.fieldArc, m.angle + Math.PI * m.fieldArc, false);
- ctx.lineWidth = 2.5 - 1.5 * wave;
- ctx.stroke();
- const curve = 0.57 + 0.04 * wave
- const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc
- let a = m.angle + aMag
- let cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a)
- let cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a)
- ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 30 * Math.cos(m.angle), m.pos.y + 30 * Math.sin(m.angle))
- a = m.angle - aMag
- cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a)
- cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a)
- ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 1 * m.fieldRange * Math.cos(m.angle - Math.PI * m.fieldArc), m.pos.y + 1 * m.fieldRange * Math.sin(m.angle - Math.PI * m.fieldArc))
- ctx.fill();
- m.perfectPush();
- } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
- m.pickUp();
- } else {
- m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
- if (!input.field) { //&& tech.isFieldFree
- //draw field free of player
- ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})`
- ctx.strokeStyle = `rgba(110,180,255, ${0.4 + 0.5 * Math.random()})`
- ctx.beginPath();
- ctx.arc(m.fieldPosition.x, m.fieldPosition.y, m.fieldRange, m.fieldAngle - Math.PI * m.fieldArc, m.fieldAngle + Math.PI * m.fieldArc, false);
- ctx.lineWidth = 2.5 - 1.5 * wave;
- ctx.stroke();
- const curve = 0.8 + 0.06 * wave
- const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc
- let a = m.fieldAngle + aMag
- ctx.quadraticCurveTo(m.fieldPosition.x + curve * m.fieldRange * Math.cos(a), m.fieldPosition.y + curve * m.fieldRange * Math.sin(a), m.fieldPosition.x + 1 * m.fieldRange * Math.cos(m.fieldAngle - Math.PI * m.fieldArc), m.fieldPosition.y + 1 * m.fieldRange * Math.sin(m.fieldAngle - Math.PI * m.fieldArc))
- ctx.fill();
- m.perfectPush(true);
- }
- }
- // m.drawRegenEnergy()
- m.drawRegenEnergy("rgba(0,0,0,0.2)")
- if (tech.isPerfectBrake) { //cap mob speed around player
- const range = 200 + 140 * wave + 150 * m.energy
- for (let i = 0; i < mob.length; i++) {
- const distance = Vector.magnitude(Vector.sub(m.pos, mob[i].position))
- if (distance < range) {
- const cap = mob[i].isShielded ? 8 : 4
- 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, range, 0, 2 * Math.PI);
- ctx.fillStyle = "hsla(200,50%,61%,0.08)";
- ctx.fill();
- }
- }
- }
- },
- {
- name: "negative mass",
- //
hold blocks as if they have a lower mass
- description: `use energy to nullify gravity
0.5x damage taken
6 energy per second`,
- fieldDrawRadius: 0,
- effect: () => {
- m.fieldFire = true;
- m.holdingMassScale = 0.01; //can hold heavier blocks with lower cost to jumping
- m.fieldMeterColor = "#333"
- m.eyeFillColor = m.fieldMeterColor
- m.fieldHarmReduction = 0.5;
- m.fieldDrawRadius = 0;
-
- m.hold = function () {
- m.airSpeedLimit = 125 //5 * player.mass * player.mass
- m.FxAir = 0.016
- if (m.isHolding) {
- m.drawHold(m.holdingTarget);
- m.holding();
- m.throwBlock();
- } else if (input.field) { //push away
- if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
- m.grabPowerUp();
- m.lookForPickUp();
- if (m.energy > tech.negativeMassCost && m.fieldCDcycle < m.cycle) {
- if (tech.isFlyFaster) {
- //look for nearby objects to make zero-g
- function moveThis(who, range, mag = 1.06) {
- for (let i = 0, len = who.length; i < len; ++i) {
- sub = Vector.sub(who[i].position, m.pos);
- dist = Vector.magnitude(sub);
- if (dist < range) {
- who[i].force.y -= who[i].mass * (simulation.g * mag); //add a bit more then standard gravity
- if (input.left) { //blocks move horizontally with the same force as the player
- who[i].force.x -= m.FxAir * who[i].mass / 10; // move player left / a
- } else if (input.right) {
- who[i].force.x += m.FxAir * who[i].mass / 10; //move player right / d
- }
- //loose attraction to player
- // const sub = Vector.sub(m.pos, body[i].position)
- // const unit = Vector.mult(Vector.normalise(sub), who[i].mass * 0.0000002 * Vector.magnitude(sub))
- // body[i].force.x += unit.x
- // body[i].force.y += unit.y
- }
- }
- }
- //control horizontal acceleration
- m.airSpeedLimit = 1000 // 7* player.mass * player.mass
- m.FxAir = 0.01
- //control vertical acceleration
- if (input.down) { //down
- player.force.y += 0.5 * player.mass * simulation.g;
- this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 500 * 0.03;
- moveThis(powerUp, this.fieldDrawRadius, 0);
- moveThis(body, this.fieldDrawRadius, 0);
- } else if (input.up) { //up
- m.energy -= 5 * tech.negativeMassCost;
- this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 1100 * 0.03;
- player.force.y -= 2.25 * player.mass * simulation.g;
- moveThis(powerUp, this.fieldDrawRadius, 1.8);
- moveThis(body, this.fieldDrawRadius, 1.8);
- } else {
- m.energy -= tech.negativeMassCost;
- this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 800 * 0.03;
- player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift
- moveThis(powerUp, this.fieldDrawRadius);
- moveThis(body, this.fieldDrawRadius);
- }
- } else {
- //look for nearby objects to make zero-g
- function verticalForce(who, range, mag = 1.06) {
- for (let i = 0, len = who.length; i < len; ++i) {
- sub = Vector.sub(who[i].position, m.pos);
- dist = Vector.magnitude(sub);
- if (dist < range) {
- who[i].force.y -= who[i].mass * (simulation.g * mag); //add a bit more then standard gravity
- if (input.left) { //blocks move horizontally with the same force as the player
- who[i].force.x -= m.FxAir * who[i].mass / 10; // move player left / a
- } else if (input.right) {
- who[i].force.x += m.FxAir * who[i].mass / 10; //move player right / d
- }
- }
-
-
-
- // sub = Vector.sub(who[i].position, m.pos);
- // dist = Vector.magnitude(sub);
- // if (dist < range) who[i].force.y -= who[i].mass * (simulation.g * mag);
- }
- }
- //control horizontal acceleration
- m.airSpeedLimit = 400 // 7* player.mass * player.mass
- m.FxAir = 0.005
- //control vertical acceleration
- if (input.down) { //down
- player.force.y -= 0.5 * player.mass * simulation.g;
- this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 400 * 0.03;
- verticalForce(powerUp, this.fieldDrawRadius, 0.7);
- verticalForce(body, this.fieldDrawRadius, 0.7);
- } else if (input.up) { //up
- m.energy -= 5 * tech.negativeMassCost;
- this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 850 * 0.03;
- player.force.y -= 1.45 * player.mass * simulation.g;
- verticalForce(powerUp, this.fieldDrawRadius, 1.38);
- verticalForce(body, this.fieldDrawRadius, 1.38);
- } else {
- m.energy -= tech.negativeMassCost;
- this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 650 * 0.03;
- player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift
- verticalForce(powerUp, this.fieldDrawRadius);
- verticalForce(body, this.fieldDrawRadius);
- }
- }
-
- if (m.energy < 0.001) {
- m.fieldCDcycle = m.cycle + 120;
- m.energy = 0;
- }
- //add extra friction for horizontal motion
- if (input.down || input.up || input.left || input.right) {
- Matter.Body.setVelocity(player, { x: player.velocity.x * 0.99, y: player.velocity.y * 0.98 });
- } else { //slow rise and fall
- Matter.Body.setVelocity(player, { x: player.velocity.x * 0.99, y: player.velocity.y * 0.98 });
- }
- // if (tech.isFreezeMobs) {
- // const ICE_DRAIN = 0.0005
- // for (let i = 0, len = mob.length; i < len; i++) {
- // if (!mob[i].isMobBullet && !mob[i].shield && !mob[i].isShielded && ((mob[i].distanceToPlayer() + mob[i].radius) < this.fieldDrawRadius)) {
- // if (m.energy > ICE_DRAIN * 2) {
- // m.energy -= ICE_DRAIN;
- // this.fieldDrawRadius -= 2;
- // mobs.statusSlow(mob[i], 60)
- // } else {
- // break;
- // }
- // }
- // }
- // }
- //draw zero-G range
- if (!simulation.isTimeSkipping) {
- ctx.beginPath();
- ctx.arc(m.pos.x, m.pos.y, this.fieldDrawRadius, 0, 2 * Math.PI);
- ctx.fillStyle = "#f5f5ff";
- ctx.globalCompositeOperation = "difference";
- ctx.fill();
- ctx.globalCompositeOperation = "source-over";
- }
- }
- } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
- m.pickUp();
- this.fieldDrawRadius = 0
- } else {
- m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
- this.fieldDrawRadius = 0
- }
- m.drawRegenEnergy("rgba(0,0,0,0.2)")
-
-
- // if (tech.isHealAttract) {
- // for (let i = 0; i < powerUp.length; i++) {
- // if (powerUp[i].name === "heal") {
- // //&& Vector.magnitudeSquared(Vector.sub(powerUp[i].position, m.pos)) < 500000
- // let attract = Vector.mult(Vector.normalise(Vector.sub(m.pos, powerUp[i].position)), 0.01 * 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));
- // }
- // }
- // }
-
-
- // powerUp[i].force.x += 0.05 * (dxP / Math.sqrt(dist2)) * powerUp[i].mass;
- // powerUp[i].force.y += 0.05 * (dyP / Math.sqrt(dist2)) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity
- // //extra friction
- // Matter.Body.setVelocity(powerUp[i], {
- // x: powerUp[i].velocity.x * 0.11,
- // y: powerUp[i].velocity.y * 0.11
- // });
-
- }
- }
- },
- {
- name: "molecular assembler",
- description: `use energy to deflect mobs
excess energy used to print ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
12 energy per second`,
- setDescription() {
- return `use energy to deflect mobs
excess energy used to print ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
12 energy per second`
- },
-
- effect: () => {
- m.fieldMeterColor = "#ff0"
- m.eyeFillColor = m.fieldMeterColor
- m.hold = function () {
- if (m.energy > m.maxEnergy - 0.02 && m.fieldCDcycle < m.cycle && !input.field && bullet.length < 300 && (m.cycle % 2)) {
- if (simulation.molecularMode === 0) {
- if (tech.isSporeFlea) {
- const drain = 0.18 + (Math.max(bullet.length, 130) - 130) * 0.02
- if (m.energy > drain) {
- m.energy -= drain
- const speed = m.crouch ? 20 + 8 * Math.random() : 10 + 3 * Math.random()
- b.flea({
- x: m.pos.x + 35 * Math.cos(m.angle),
- y: m.pos.y + 35 * Math.sin(m.angle)
- }, {
- x: speed * Math.cos(m.angle),
- y: speed * Math.sin(m.angle)
- })
- }
- } else if (tech.isSporeWorm) {
- const drain = 0.18 + (Math.max(bullet.length, 130) - 130) * 0.02
- if (m.energy > drain) {
- m.energy -= drain
- b.worm({
- x: m.pos.x + 35 * Math.cos(m.angle),
- y: m.pos.y + 35 * Math.sin(m.angle)
- })
- const SPEED = 2 + 1 * Math.random();
- Matter.Body.setVelocity(bullet[bullet.length - 1], {
- x: SPEED * Math.cos(m.angle),
- y: SPEED * Math.sin(m.angle)
- });
- }
- } else {
- const drain = 0.095 + (Math.max(bullet.length, 130) - 130) * 0.01
- for (let i = 0, len = Math.random() * 20; i < len; i++) {
- if (m.energy > 3 * drain) {
- m.energy -= drain
- b.spore(m.pos)
- } else {
- break
- }
- }
- }
- } else if (simulation.molecularMode === 1) {
- m.energy -= 0.33;
- const direction = { x: Math.cos(m.angle), y: Math.sin(m.angle) }
- const push = Vector.mult(Vector.perp(direction), 0.08)
- b.missile({ x: m.pos.x + 30 * direction.x, y: m.pos.y + 30 * direction.y }, m.angle, -15)
- bullet[bullet.length - 1].force.x += push.x * (Math.random() - 0.5)
- bullet[bullet.length - 1].force.y += 0.005 + push.y * (Math.random() - 0.5)
- // b.missile({ x: m.pos.x, y: m.pos.y - 40 }, -Math.PI / 2 + 0.5 * (Math.random() - 0.5), 0, 1)
- } else if (simulation.molecularMode === 2) {
- m.energy -= 0.044;
- b.iceIX(1)
- } else if (simulation.molecularMode === 3) {
- if (tech.isDroneRadioactive) {
- const drain = 0.8 + (Math.max(bullet.length, 50) - 50) * 0.01
- if (m.energy > drain) {
- m.energy -= drain
- b.droneRadioactive({
- x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5),
- y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5)
- }, 25)
- }
- } else {
- //every bullet above 100 adds 0.005 to the energy cost per drone
- //at 200 bullets the energy cost is 0.45 + 100*0.006 = 1.05
- const drain = (0.45 + (Math.max(bullet.length, 100) - 100) * 0.006) * tech.droneEnergyReduction
- if (m.energy > drain) {
- m.energy -= drain
- b.drone()
- }
- }
- }
- }
- if (m.isHolding) {
- m.drawHold(m.holdingTarget);
- m.holding();
- if (tech.isPrinter && m.holdingTarget.isPrinted && input.field) {
- // if (Math.random() < 0.004 && m.holdingTarget.vertices.length < 12) m.holdingTarget.vertices.push({ x: 0, y: 0 }) //small chance to increase the number of vertices
- m.holdingTarget.radius += Math.min(1.1, 1.3 / m.holdingTarget.mass) //grow up to a limit
- const r1 = m.holdingTarget.radius * (1 + 0.12 * Math.sin(m.cycle * 0.11))
- const r2 = m.holdingTarget.radius * (1 + 0.12 * Math.cos(m.cycle * 0.11))
- let angle = (m.cycle * 0.01) % (2 * Math.PI) //rotate the object
- let vertices = []
- for (let i = 0, len = m.holdingTarget.vertices.length; i < len; i++) {
- angle += 2 * Math.PI / len
- vertices.push({ x: m.holdingTarget.position.x + r1 * Math.cos(angle), y: m.holdingTarget.position.y + r2 * Math.sin(angle) })
- }
- Matter.Body.setVertices(m.holdingTarget, vertices)
- m.definePlayerMass(m.defaultMass + m.holdingTarget.mass * m.holdingMassScale)
- }
- m.throwBlock();
- } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed
- if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
- m.grabPowerUp();
- m.lookForPickUp();
- if (tech.isPrinter && input.down) {
- m.printBlock();
- } else if (m.energy > m.minEnergyToDeflect) {
- m.drawField();
- m.pushMobsFacing();
- }
- } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
- m.pickUp();
- } else {
- m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
- }
- m.drawRegenEnergy()
- }
- }
- },
- {
- name: "plasma torch",
- description: "use energy to emit short range plasma
1.5x damage
10 energy per second",
- set() {
- b.isExtruderOn = false
- m.fieldDamage = 1.5
- // m.fieldCDcycleAlternate = 0
-
- if (m.plasmaBall) {
- m.plasmaBall.reset()
- Matter.Composite.remove(engine.world, m.plasmaBall);
- }
- if (tech.isPlasmaBall) {
- const circleRadiusScale = 2
- m.plasmaBall = Bodies.circle(m.pos.x + 10 * Math.cos(m.angle), m.pos.y + 10 * Math.sin(m.angle), 1, {
- // collisionFilter: {
- // group: 0,
- // category: 0,
- // mask: 0 //cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield
- // },
- isSensor: true,
- frictionAir: 0,
- alpha: 0.7,
- isPopping: false,
- isAttached: false,
- isOn: false,
- drain: 0.0017,
- radiusLimit: 10,
- damage: 0.8,
- setPositionToNose() {
- const nose = { x: m.pos.x + 10 * Math.cos(m.angle), y: m.pos.y + 10 * Math.sin(m.angle) }
- Matter.Body.setPosition(this, Vector.add(nose, Vector.mult(Vector.normalise(Vector.sub(nose, m.pos)), circleRadiusScale * this.circleRadius)));
- },
- fire() {
- this.isAttached = false;
- const speed = 10 //scale with mass?
- Matter.Body.setVelocity(this, {
- x: player.velocity.x * 0.4 + speed * Math.cos(m.angle),
- y: speed * Math.sin(m.angle)
- });
- m.plasmaBall.setPositionToNose()
- if (this.circleRadius < 10) this.isPopping = true
- },
- scale(scale) {
- Matter.Body.scale(m.plasmaBall, scale, scale); //shrink fast
- if (this.circleRadius < this.radiusLimit) this.reset()
- },
- reset() {
- // console.log(this.circleRadius)
- const scale = 1 / m.plasmaBall.circleRadius
- Matter.Body.scale(m.plasmaBall, scale, scale); //grow
- // console.log(this.circleRadius)
- // this.circleRadius = 0
- this.alpha = 0.7
- this.isOn = false
- this.isPopping = false
- // this.isAttached = true;
- },
- do() {
- if (this.isOn) {
- //collisions with map
- if (Matter.Query.collides(this, map).length > 0) {
- if (this.isAttached) {
- this.scale(Math.max(0.9, 0.998 - 0.1 / m.plasmaBall.circleRadius))
- } else {
- this.isPopping = true
- }
- }
- if (this.isPopping) {
- this.alpha -= 0.03
- if (this.alpha < 0.1) {
- this.reset()
- } else {
- const scale = 1.04 + 4 / Math.max(1, m.plasmaBall.circleRadius)
- Matter.Body.scale(m.plasmaBall, scale, scale); //grow
- }
- // if (this.speed > 2.5) {
- // const slow = 0.9
- // Matter.Body.setVelocity(this, {
- // x: slow * this.velocity.x,
- // y: slow * this.velocity.y
- // });
- // }
- }
- //collisions with mobs
- // const whom = Matter.Query.collides(this, mob)
- // const dmg = this.damage * m.dmgScale
- // for (let i = 0, len = whom.length; i < len; i++) {
- // const mobHit = (who) => {
- // if (who.alive) {
- // if (!this.isAttached && !who.isMobBullet) this.isPopping = true
- // who.damage(dmg);
- // // if (who.shield) this.scale(Math.max(0.9, 0.99 - 0.5 / m.plasmaBall.circleRadius))
- // if (who.speed > 5) {
- // Matter.Body.setVelocity(who, { //friction
- // x: who.velocity.x * 0.6,
- // y: who.velocity.y * 0.6
- // });
- // } else {
- // Matter.Body.setVelocity(who, { //friction
- // x: who.velocity.x * 0.93,
- // y: who.velocity.y * 0.93
- // });
- // }
- // }
- // }
- // mobHit(whom[i].bodyA)
- // mobHit(whom[i].bodyB)
- // }
-
- //damage nearby mobs
- const dmg = this.damage * m.dmgScale
- const arcList = []
- const damageRadius = circleRadiusScale * this.circleRadius
- const dischargeRange = 150 + 1600 * tech.plasmaDischarge + 1.3 * damageRadius
- for (let i = 0, len = mob.length; i < len; i++) {
- if (mob[i].alive && (!mob[i].isBadTarget || mob[i].isMobBullet) && !mob[i].isInvulnerable) {
- const sub = Vector.magnitude(Vector.sub(this.position, mob[i].position))
- if (sub < damageRadius + mob[i].radius) {
- // if (!this.isAttached && !mob[i].isMobBullet) this.isPopping = true
- mob[i].damage(dmg);
- if (mob[i].speed > 5) {
- Matter.Body.setVelocity(mob[i], { x: mob[i].velocity.x * 0.6, y: mob[i].velocity.y * 0.6 });
- } else {
- Matter.Body.setVelocity(mob[i], { x: mob[i].velocity.x * 0.93, y: mob[i].velocity.y * 0.93 });
- }
- } else if (sub < dischargeRange + mob[i].radius && Matter.Query.ray(map, mob[i].position, this.position).length === 0) {
- arcList.push(mob[i]) //populate electrical arc list
- }
- }
- }
- for (let i = 0; i < arcList.length; i++) {
- if (tech.plasmaDischarge > Math.random()) {
- const who = arcList[Math.floor(Math.random() * arcList.length)]
- who.damage(dmg * 4);
- //draw arcs
- const sub = Vector.sub(who.position, this.position)
- const unit = Vector.normalise(sub)
- let len = 12
- const step = Vector.magnitude(sub) / (len + 2)
- let x = this.position.x
- let y = this.position.y
- ctx.beginPath();
- ctx.moveTo(x, y);
- for (let i = 0; i < len; i++) {
- x += step * (unit.x + (Math.random() - 0.5))
- y += step * (unit.y + (Math.random() - 0.5))
- ctx.lineTo(x, y);
- }
- ctx.lineTo(who.position.x, who.position.y);
- ctx.strokeStyle = "#88f";
- ctx.lineWidth = 4 + 3 * Math.random();
- ctx.stroke();
- if (who.damageReduction) {
- simulation.drawList.push({
- x: who.position.x,
- y: who.position.y,
- radius: 15,
- color: "rgba(150,150,255,0.4)",
- time: 15
- });
- }
- }
- }
-
-
- //slowly slow down if too fast
- if (this.speed > 10) {
- const scale = 0.998
- Matter.Body.setVelocity(this, { x: scale * this.velocity.x, y: scale * this.velocity.y });
- }
-
- //graphics
- const radius = circleRadiusScale * this.circleRadius * (0.99 + 0.02 * Math.random()) + 3 * Math.random()
- const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 0, this.position.x, this.position.y, radius);
- const alpha = this.alpha + 0.1 * Math.random()
- gradient.addColorStop(0, `rgba(255,255,255,${alpha})`);
- gradient.addColorStop(0.35 + 0.1 * Math.random(), `rgba(255,150,255,${alpha})`);
- gradient.addColorStop(1, `rgba(255,0,255,${alpha})`);
- // gradient.addColorStop(1, `rgba(255,150,255,${alpha})`);
- ctx.fillStyle = gradient
- ctx.beginPath();
- ctx.arc(this.position.x, this.position.y, radius, 0, 2 * Math.PI);
- ctx.fill();
- //draw arcs
- const unit = Vector.rotate({ x: 1, y: 0 }, Math.random() * 6.28)
- let len = 8
- const step = this.circleRadius / len
- let x = this.position.x
- let y = this.position.y
- ctx.beginPath();
- if (Math.random() < 0.5) {
- x += step * (unit.x + 6 * (Math.random() - 0.5))
- y += step * (unit.y + 6 * (Math.random() - 0.5))
- len -= 2
- }
- if (Math.random() < 0.5) {
- x += step * (unit.x + 6 * (Math.random() - 0.5))
- y += step * (unit.y + 6 * (Math.random() - 0.5))
- len -= 2
- }
- ctx.moveTo(x, y);
-
- for (let i = 0; i < len; i++) {
- x += step * (unit.x + 1.9 * (Math.random() - 0.5))
- y += step * (unit.y + 1.9 * (Math.random() - 0.5))
- ctx.lineTo(x, y);
- }
- ctx.strokeStyle = "#88f";
- ctx.lineWidth = 2 * Math.random();
- ctx.stroke();
- }
- },
- });
-
- Composite.add(engine.world, m.plasmaBall);
- // m.plasmaBall.startingVertices = m.plasmaBall.vertices.slice();
+ effect: () => {
m.hold = function () {
if (m.isHolding) {
m.drawHold(m.holdingTarget);
m.holding();
m.throwBlock();
- } else if (input.field) { //not hold but field button is pressed
+ } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed
if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
m.grabPowerUp();
m.lookForPickUp();
- if (m.fieldCDcycle < m.cycle) {
- //field is active
- if (!m.plasmaBall.isAttached) { //return ball to player
- if (m.plasmaBall.isOn) {
- m.plasmaBall.isPopping = true
- } else {
- m.plasmaBall.isAttached = true
- m.plasmaBall.isOn = true
- m.plasmaBall.isPopping = false
- m.plasmaBall.alpha = 0.7
- m.plasmaBall.setPositionToNose()
- // m.plasmaBall.reset()
-
- }
- } else if (m.energy > m.plasmaBall.drain) { //charge up when attached
- if (tech.isCapacitor) {
- m.energy -= m.plasmaBall.drain * 2;
- const scale = 1 + 48 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8)
- Matter.Body.scale(m.plasmaBall, scale, scale); //grow
- } else {
- m.energy -= m.plasmaBall.drain;
- const scale = 1 + 16 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8)
- Matter.Body.scale(m.plasmaBall, scale, scale); //grow
- }
- if (m.energy > m.maxEnergy) {
- m.energy -= m.plasmaBall.drain * 2;
- const scale = 1 + 16 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8)
- Matter.Body.scale(m.plasmaBall, scale, scale); //grow
- }
- m.plasmaBall.setPositionToNose()
-
- //add friction for player when holding ball, more friction in vertical
- // const floatScale = Math.sqrt(m.plasmaBall.circleRadius)
- // const friction = 0.0002 * floatScale
- // const slowY = (player.velocity.y > 0) ? Math.max(0.8, 1 - friction * player.velocity.y * player.velocity.y) : Math.max(0.98, 1 - friction * Math.abs(player.velocity.y)) //down : up
- // Matter.Body.setVelocity(player, {
- // x: Math.max(0.95, 1 - friction * Math.abs(player.velocity.x)) * player.velocity.x,
- // y: slowY * player.velocity.y
- // });
-
- // if (player.velocity.y > 7) player.force.y -= 0.95 * player.mass * simulation.g //less gravity when falling fast
- // player.force.y -= Math.min(0.95, 0.05 * floatScale) * player.mass * simulation.g; //undo some gravity on up or down
-
- //float
- const slowY = (player.velocity.y > 0) ? Math.max(0.8, 1 - 0.002 * player.velocity.y * player.velocity.y) : Math.max(0.98, 1 - 0.001 * Math.abs(player.velocity.y)) //down : up
- Matter.Body.setVelocity(player, {
- x: Math.max(0.95, 1 - 0.003 * Math.abs(player.velocity.x)) * player.velocity.x,
- y: slowY * player.velocity.y
- });
- if (player.velocity.y > 5) {
- player.force.y -= 0.9 * player.mass * simulation.g //less gravity when falling fast
- } else {
- player.force.y -= 0.5 * player.mass * simulation.g;
- }
- } else {
- m.fieldCDcycle = m.cycle + 90;
- m.plasmaBall.fire()
- }
+ if (m.energy > m.minEnergyToDeflect) {
+ m.drawField();
+ m.pushMobsFacing();
}
} else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
m.pickUp();
- if (m.plasmaBall.isAttached) {
- m.fieldCDcycle = m.cycle + 30;
- m.plasmaBall.fire()
- }
} else {
m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
- if (m.plasmaBall.isAttached) {
- m.fieldCDcycle = m.cycle + 30;
- m.plasmaBall.fire()
- }
- }
- m.drawRegenEnergy("rgba(0, 0, 0, 0.2)")
- m.plasmaBall.do()
- }
- } else if (tech.isExtruder) {
- m.hold = function () {
- b.isExtruderOn = false
- if (m.isHolding) {
- m.drawHold(m.holdingTarget);
- m.holding();
- m.throwBlock();
- } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed
- if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
- m.grabPowerUp();
- m.lookForPickUp();
- b.extruder();
- } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
- m.pickUp();
- } else {
- m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
- }
- m.drawRegenEnergy("rgba(0, 0, 0, 0.2)")
- if (input.field) {
- b.wasExtruderOn = true
- } else {
- b.wasExtruderOn = false
- b.canExtruderFire = true
- }
- ctx.beginPath(); //draw all the wave bullets
- for (let i = 1, len = bullet.length; i < len; i++) { //skip the first bullet (which is is oldest bullet)
- if (bullet[i].isWave) {
- if (bullet[i].isBranch || bullet[i - 1].isBranch) {
- ctx.moveTo(bullet[i].position.x, bullet[i].position.y)
- } else {
- ctx.lineTo(bullet[i].position.x, bullet[i].position.y)
- }
- }
- }
- if (b.wasExtruderOn && b.isExtruderOn) ctx.lineTo(m.pos.x + 15 * Math.cos(m.angle), m.pos.y + 15 * Math.sin(m.angle))
- ctx.lineWidth = 4;
- ctx.strokeStyle = "#f07"
- ctx.stroke();
- ctx.lineWidth = tech.extruderRange;
- ctx.strokeStyle = "rgba(255,0,110,0.06)"
- ctx.stroke();
- }
- // } else if (true) { //plasma sword slash
- // const plasmaSweepCycles = 30
- // m.plasmaSweep = 0
- // m.plasmaSlashDirection = m.flipLegs//Math.random() > 0.5 ? 1 : -1;
- // m.hold = function () {
- // if (m.isHolding) {
- // m.drawHold(m.holdingTarget);
- // m.holding();
- // m.throwBlock();
- // m.plasmaSweep = 0
- // // } else if (true) { //not hold but field button is pressed
- // } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed
- // if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
- // m.grabPowerUp();
- // m.lookForPickUp();
-
- // //graphics
- // if (m.plasmaSweep === 0) m.plasmaSlashDirection = m.flipLegs //Math.random() > 0.5 ? 1 : -1;
- // const angle = m.angle //+ 1 * (m.plasmaSweep - plasmaSweepCycles / 2) / plasmaSweepCycles * m.plasmaSlashDirection
- // const plasmaSweepCapped = Math.min(m.plasmaSweep, plasmaSweepCycles - 8) / plasmaSweepCycles
- // const range = 100 * plasmaSweepCapped
- // const arc = 1.3
- // const A = { x: m.pos.x + range * Math.cos(angle - arc), y: m.pos.y + range * Math.sin(angle - arc) }
- // const B = { x: m.pos.x + range * Math.cos(angle + arc), y: m.pos.y + range * Math.sin(angle + arc) }
- // const controlRange = 500 * plasmaSweepCapped
- // const AC = { x: m.pos.x + controlRange * Math.cos(angle - arc / 2), y: m.pos.y + controlRange * Math.sin(angle - arc / 2) }
- // const BC = { x: m.pos.x + controlRange * Math.cos(angle + arc / 2), y: m.pos.y + controlRange * Math.sin(angle + arc / 2) }
- // const innerControlRange = 300 * plasmaSweepCapped
- // const ACinner = { x: m.pos.x + innerControlRange * Math.cos(angle - arc / 2), y: m.pos.y + innerControlRange * Math.sin(angle - arc / 2) }
- // const BCinner = { x: m.pos.x + innerControlRange * Math.cos(angle + arc / 2), y: m.pos.y + innerControlRange * Math.sin(angle + arc / 2) }
- // ctx.beginPath();
- // ctx.moveTo(A.x, A.y)
- // ctx.bezierCurveTo(AC.x, AC.y, BC.x, BC.y, B.x, B.y); //outer curve
- // ctx.bezierCurveTo(BCinner.x, BCinner.y, ACinner.x, ACinner.y, A.x, A.y); //inner curve
- // // ctx.strokeStyle = "#000"
- // // ctx.stroke();
- // ctx.fillStyle = "rgba(255,0,255,0.5)"
- // ctx.fill();
-
- // //draw control points for graphics reference
- // ctx.lineWidth = '0.5'
- // ctx.beginPath();
- // ctx.arc(A.x, A.y, 5, 0, 2 * Math.PI);
- // ctx.stroke();
- // ctx.beginPath();
- // ctx.arc(B.x, B.y, 5, 0, 2 * Math.PI);
- // ctx.stroke();
- // ctx.beginPath();
- // ctx.arc(AC.x, AC.y, 5, 0, 2 * Math.PI);
- // ctx.stroke();
- // ctx.beginPath();
- // ctx.arc(BC.x, BC.y, 5, 0, 2 * Math.PI);
- // ctx.stroke();
- // ctx.beginPath();
- // ctx.arc(ACinner.x, ACinner.y, 5, 0, 2 * Math.PI);
- // ctx.stroke();
- // ctx.beginPath();
- // ctx.arc(BCinner.x, BCinner.y, 5, 0, 2 * Math.PI);
- // ctx.stroke();
-
- // //mob collision detection
- // collideRange = 160
- // const collideCenter = {
- // x: m.pos.x + collideRange * Math.cos(angle),
- // y: m.pos.y + collideRange * Math.sin(angle)
- // }
- // ctx.beginPath();
- // ctx.arc(collideCenter.x, collideCenter.y, 140, 0, 2 * Math.PI);
- // ctx.stroke();
-
- // //push mob away and slow them?
-
-
- // //sweeping motion and cooldown
- // m.plasmaSweep++
- // if (m.plasmaSweep > plasmaSweepCycles) {
- // m.plasmaSweep = 0
- // if (m.fireCDcycle < m.cycle + 30) m.fieldCDcycle = m.cycle + 30
- // }
- // } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
- // m.pickUp();
- // m.plasmaSweep = 0
- // } else {
- // m.plasmaSweep = 0
- // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
- // }
- // m.drawRegenEnergy("rgba(0, 0, 0, 0.2)")
- // }
- } else {
- m.hold = function () {
- if (m.isHolding) {
- m.drawHold(m.holdingTarget);
- m.holding();
- m.throwBlock();
- } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed
- if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
- m.grabPowerUp();
- m.lookForPickUp();
- b.plasma();
- } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
- m.pickUp();
- } else {
- m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
- }
- m.drawRegenEnergy("rgba(0, 0, 0, 0.2)")
- }
- }
- },
- effect() {
- m.fieldMeterColor = "#f0f"
- m.eyeFillColor = m.fieldMeterColor
- this.set();
- }
- },
- {
- name: "time dilation",
- description: `use energy to stop time
1.2x movement and fire rate
12 energy per second`,
- set() {
- // m.fieldMeterColor = "#0fc"
- // m.fieldMeterColor = "#ff0"
- m.fieldMeterColor = "#3fe"
- m.eyeFillColor = m.fieldMeterColor
- m.fieldFx = 1.25
- // m.fieldJump = 1.09
- m.setMovement();
- b.setFireCD()
- const timeStop = () => {
- m.immuneCycle = m.cycle + 10; //invulnerable to harm while time is stopped, this also disables regen
- //draw field everywhere
- ctx.globalCompositeOperation = "saturation"
- ctx.fillStyle = "#ccc";
- ctx.fillRect(-50000, -50000, 100000, 100000)
- ctx.globalCompositeOperation = "source-over"
- //stop time
- m.isTimeDilated = true;
-
- function sleep(who) {
- for (let i = 0, len = who.length; i < len; ++i) {
- if (!who[i].isSleeping) {
- who[i].storeVelocity = who[i].velocity
- who[i].storeAngularVelocity = who[i].angularVelocity
- }
- Matter.Sleeping.set(who[i], true)
- }
- }
- sleep(mob);
- sleep(body);
- sleep(bullet);
- simulation.cycle--; //pause all functions that depend on game cycle increasing
- }
- if (tech.isRewindField) {
- this.rewindCount = 0
- m.grabPowerUpRange2 = 300000// m.grabPowerUpRange2 = 200000;
-
- m.hold = function () {
- // console.log(m.fieldCDcycle)
- m.grabPowerUp();
- // //grab power ups
- // for (let i = 0, len = powerUp.length; i < len; ++i) {
- // if (
- // Vector.magnitudeSquared(Vector.sub(m.pos, powerUp[i].position)) < 100000 &&
- // !simulation.isChoosing &&
- // (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal)
- // ) {
- // powerUps.onPickUp(powerUp[i]);
- // powerUp[i].effect();
- // Matter.Composite.remove(engine.world, powerUp[i]);
- // powerUp.splice(i, 1);
- // break; //because the array order is messed up after splice
- // }
- // }
- if (m.isHolding) {
- m.drawHold(m.holdingTarget);
- m.holding();
- m.throwBlock();
- m.wakeCheck();
- } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed
- const drain = 0.0014 / (1 + 0.05 * m.coupling)
- if (m.energy > drain) m.energy -= drain
- m.grabPowerUp();
- if (this.rewindCount === 0) {
- m.lookForPickUp();
- }
-
- if (!m.holdingTarget) {
- if (this.rewindCount === 0) { //large upfront energy cost to enter rewind mode
- if (m.energy > 0.3) {
- m.energy -= 0.3
- } else {
- this.rewindCount = 0;
- m.resetHistory();
- if (m.fireCDcycle < m.cycle + 60) m.fieldCDcycle = m.cycle + 60
- m.immuneCycle = m.cycle //if you reach the end of the history disable harm immunity
- }
- }
- this.rewindCount += 6;
- const DRAIN = 0.003
- let history = m.history[(m.cycle - this.rewindCount) % 600]
- if (this.rewindCount > 599 || m.energy < DRAIN) {
- this.rewindCount = 0;
- m.resetHistory();
- if (m.fireCDcycle < m.cycle + 60) m.fieldCDcycle = m.cycle + 60
- m.immuneCycle = m.cycle //if you reach the end of the history disable harm immunity
- } else {
- //draw field everywhere
- ctx.globalCompositeOperation = "saturation"
- ctx.fillStyle = "#ccc";
- ctx.fillRect(-100000, -100000, 200000, 200000)
- ctx.globalCompositeOperation = "source-over"
- // m.grabPowerUp(); //a second grab power up to make the power ups easier to grab, and they more fast which matches the time theme
- m.energy -= DRAIN
- if (m.immuneCycle < m.cycle + 5) m.immuneCycle = m.cycle + 5; //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) {
- m.health = history.health
- if (m.health > m.maxHealth) m.health = m.maxHealth
- m.displayHealth();
- }
- m.yOff = history.yOff
- if (m.yOff < 48) {
- m.doCrouch()
- } else {
- m.undoCrouch()
- }
- if (tech.isRewindBot && !(this.rewindCount % 60)) {
- for (let i = 0; i < tech.isRewindBot; i++) {
- b.randomBot(m.pos, false, false)
- bullet[bullet.length - 1].endCycle = simulation.cycle + 300 + Math.floor(180 * Math.random()) //8-9 seconds
- }
- }
- if (tech.isRewindGrenade && !(this.rewindCount % 30)) {
- b.grenade(m.pos, this.rewindCount) //Math.PI / 2
- const who = bullet[bullet.length - 1]
- who.endCycle = simulation.cycle + 120
- }
- }
- }
- m.wakeCheck();
- } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
- m.pickUp();
- this.rewindCount = 0;
- m.wakeCheck();
- } else if (tech.isTimeStop && player.speed < 1 && m.onGround && !input.fire) {
- timeStop();
- this.rewindCount = 0;
- } else {
- m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
- this.rewindCount = 0;
- m.wakeCheck();
- }
- m.drawRegenEnergy() // this calls m.regenEnergy(); also
- }
- } else {
- m.fieldFire = true;
- m.isTimeDilated = false;
- m.hold = function () {
- if (m.isHolding) {
- m.wakeCheck();
- m.drawHold(m.holdingTarget);
- m.holding();
- m.throwBlock();
- } else if (input.field && m.fieldCDcycle < m.cycle) {
- const drain = 0.0026 / (1 + 0.03 * m.coupling)
- if (m.energy > drain) m.energy -= drain
- m.grabPowerUp();
- m.lookForPickUp(); //this drains energy 0.001
- if (m.energy > drain) {
- timeStop();
- } else { //holding, but field button is released
- m.fieldCDcycle = m.cycle + 120;
- m.energy = 0;
- m.wakeCheck();
- m.wakeCheck();
- }
- } else if (tech.isTimeStop && player.speed < 1 && m.onGround && m.fireCDcycle < m.cycle && !input.fire) {
- timeStop();
- //makes things move at 1/5 time rate, but has an annoying flicker for mob graphics, and other minor bugs
- // if (!(m.cycle % 4)) {
- // // requestAnimationFrame(() => {
- // m.wakeCheck();
- // // simulation.timePlayerSkip(1)
- // // }); //wrapping in animation frame prevents errors, probably
- // ctx.globalCompositeOperation = "saturation"
- // ctx.fillStyle = "#ccc";
- // ctx.fillRect(-100000, -100000, 200000, 200000)
- // ctx.globalCompositeOperation = "source-over"
- // } else {
- // timeStop();
- // }
- } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
- m.wakeCheck();
- m.pickUp();
- } else {
- m.wakeCheck();
- m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
}
m.drawRegenEnergy()
}
}
},
- effect() {
- if (tech.isTimeStop) {
- m.fieldHarmReduction = 0.6;
- } else {
- m.fieldHarmReduction = 1;
- }
- this.set();
- }
- },
- {
- name: "metamaterial cloaking",
- description: `0.3x damage taken while cloaked
after decloaking 4.5x damage for 2 s
6 energy per second`,
- effect: () => {
- m.fieldFire = true;
- m.fieldMeterColor = "#333";
- m.eyeFillColor = m.fieldMeterColor
- m.fieldPhase = 0;
- m.isCloak = false
- m.fieldDrawRadius = 0
- m.isSneakAttack = true;
- m.sneakAttackCycle = 0;
- m.enterCloakCycle = 0;
- m.drawCloakedM = function () {
- m.walk_cycle -= m.flipLegs * m.Vx;
- m.pos.x += 4
- m.draw();
- }
- m.drawCloak = function () {
- m.fieldPhase += 0.007
- const wiggle = 0.15 * Math.sin(m.fieldPhase * 0.5)
- ctx.beginPath();
- ctx.ellipse(m.pos.x, m.pos.y, m.fieldDrawRadius * (1 - wiggle), m.fieldDrawRadius * (1 + wiggle), m.fieldPhase, 0, 2 * Math.PI);
- ctx.fillStyle = "#fff"
- ctx.lineWidth = 2;
- ctx.strokeStyle = "#000"
- // ctx.stroke()
- ctx.globalCompositeOperation = "destination-in";
- ctx.fill();
- ctx.globalCompositeOperation = "source-over";
- ctx.clip();
- }
- m.hold = function () {
- if (m.isHolding) {
- m.drawHold(m.holdingTarget);
- m.holding();
- m.throwBlock();
- } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold and field button is pressed
- if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
- m.grabPowerUp();
- m.lookForPickUp();
- } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding target exists, and field button is not pressed
- m.pickUp();
- } else {
- m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
- }
- //not shooting (or using field) enable cloak
- if (m.energy < 0.05 && m.fireCDcycle < m.cycle && !input.fire) m.fireCDcycle = m.cycle
- if (m.fireCDcycle + 10 < m.cycle && !input.fire) { //automatically cloak if not firing
- // const drain = 0.02
- if (!m.isCloak) { //&& m.energy > drain + 0.03
- // m.energy -= drain
- m.isCloak = true //enter cloak
- m.fieldHarmReduction = 0.3;
- m.enterCloakCycle = m.cycle
- if (tech.isCloakHealLastHit && m.lastHit > 0) {
- const heal = Math.min(0.75 * m.lastHit, m.energy)
- m.addHealth(heal); //heal from last hit
- m.lastHit = 0
- simulation.drawList.push({ //add dmg to draw queue
- x: m.pos.x,
- y: m.pos.y,
- radius: Math.sqrt(heal) * 200,
- color: "rgba(0,255,200,0.6)",
- time: 16
- });
- }
- if (tech.isIntangible) {
- for (let i = 0; i < bullet.length; i++) {
- if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.bullet | cat.mobBullet | cat.mobShield
- }
- }
- }
- } else if (m.isCloak) { //exit cloak
- m.sneakAttackCycle = m.cycle
- m.isCloak = false
- m.fieldHarmReduction = 1
-
- if (tech.isIntangible) {
- for (let i = 0; i < bullet.length; i++) {
- if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield
- }
- }
- if (tech.isCloakStun) { //stun nearby mobs after exiting cloak
- // let isMobsAround = false
- const stunRange = m.fieldDrawRadius * 1.25
- // const drain = 0.01
- // if (m.energy > drain) {
- for (let i = 0, len = mob.length; i < len; ++i) {
- if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) < stunRange && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 && !mob[i].isBadTarget) {
- isMobsAround = true
- mobs.statusStun(mob[i], 120)
- }
- }
- // if (isMobsAround) {
- // m.energy -= drain
- // simulation.drawList.push({
- // x: m.pos.x,
- // y: m.pos.y,
- // radius: stunRange,
- // color: "hsla(0,50%,100%,0.7)",
- // time: 7
- // });
- // }
- // }
- }
- }
-
- if (m.isCloak) {
- m.fieldRange = m.fieldRange * 0.85 + 130
- m.fieldDrawRadius = m.fieldRange * 1.1 //* 0.88 //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy));
- m.drawCloak()
- // ctx.globalCompositeOperation = "lighter";
- // m.drawCloakedM()
- // ctx.globalCompositeOperation = "source-over";
+ {
+ name: "standing wave",
+ //deflecting protects you in every direction
+ description: `3 oscillating shields are permanently active
+
+150 max energy
+
6 energy per second`,
+ drainCD: 0,
+ effect: () => {
+ m.fieldBlockCD = 0;
+ m.blockingRecoil = 1 //4 is normal
+ m.fieldRange = 185
+ m.fieldShieldingScale = 1.6 * Math.pow(0.5, (tech.harmonics - 2))
+ // m.fieldHarmReduction = 0.66; //33% reduction
+ m.harmonic3Phase = () => { //normal standard 3 different 2-d circles
+ const fieldRange1 = (0.75 + 0.3 * Math.sin(m.cycle / 23)) * m.fieldRange * m.harmonicRadius
+ const fieldRange2 = (0.68 + 0.37 * Math.sin(m.cycle / 37)) * m.fieldRange * m.harmonicRadius
+ const fieldRange3 = (0.7 + 0.35 * Math.sin(m.cycle / 47)) * m.fieldRange * m.harmonicRadius
+ const netFieldRange = Math.max(fieldRange1, fieldRange2, fieldRange3)
+ ctx.fillStyle = "rgba(110,170,200," + Math.min(0.6, (0.04 + 0.7 * m.energy * (0.1 + 0.11 * Math.random()))) + ")";
ctx.beginPath();
- ctx.arc(m.pos.x, m.pos.y, 35, 0, 2 * Math.PI);
- ctx.strokeStyle = "rgba(255,255,255,0.25)";//"rgba(0,0,0,0.7)";//"rgba(255,255,255,0.7)";//"rgba(255,0,100,0.7)";
- ctx.lineWidth = 10
- ctx.stroke();
-
- } else if (m.fieldRange < 4000) {
- m.fieldRange += 90
- m.fieldDrawRadius = m.fieldRange //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy));
- m.drawCloak()
+ ctx.arc(m.pos.x, m.pos.y, fieldRange1, 0, 2 * Math.PI);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.arc(m.pos.x, m.pos.y, fieldRange2, 0, 2 * Math.PI);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.arc(m.pos.x, m.pos.y, fieldRange3, 0, 2 * Math.PI);
+ ctx.fill();
+ //360 block
+ for (let i = 0, len = mob.length; i < len; ++i) {
+ if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < netFieldRange && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0
+ mob[i].locatePlayer();
+ if (this.drainCD > m.cycle) {
+ m.pushMass(mob[i], 0);
+ } else {
+ m.pushMass(mob[i]);
+ this.drainCD = m.cycle + 15
+ }
+ }
+ }
}
- if (tech.isIntangible) {
- if (m.isCloak) {
- player.collisionFilter.mask = cat.map
- let inPlayer = Matter.Query.region(mob, player.bounds)
- if (inPlayer.length > 0) {
- for (let i = 0; i < inPlayer.length; i++) {
- if (m.energy > 0) {
- if (!inPlayer[i].isUnblockable) m.energy -= 0.003;
- if (inPlayer[i].shield) m.energy -= 0.011;
+ m.harmonicRadius = 1 //for smoothing function when player holds mouse (for harmonicAtomic)
+ m.harmonicAtomic = () => { //several ellipses spinning about different axises
+ const rotation = simulation.cycle * 0.0031
+ const phase = simulation.cycle * 0.023
+ const radius = m.fieldRange * m.harmonicRadius
+ ctx.lineWidth = 1;
+ ctx.strokeStyle = "rgba(110,170,200,0.8)"
+ ctx.fillStyle = "rgba(110,170,200," + Math.min(0.6, 0.7 * m.energy * (0.11 + 0.1 * Math.random()) * (3 / tech.harmonics)) + ")";
+ // ctx.fillStyle = "rgba(110,170,200," + Math.min(0.7, m.energy * (0.22 - 0.01 * tech.harmonics) * (0.5 + 0.5 * Math.random())) + ")";
+ for (let i = 0; i < tech.harmonics; i++) {
+ ctx.beginPath();
+ ctx.ellipse(m.pos.x, m.pos.y, radius * Math.abs(Math.sin(phase + i / tech.harmonics * Math.PI)), radius, rotation + i / tech.harmonics * Math.PI, 0, 2 * Math.PI);
+ ctx.fill();
+ ctx.stroke();
+ }
+ //360 block
+ for (let i = 0, len = mob.length; i < len; ++i) {
+ if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < radius && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0
+ mob[i].locatePlayer();
+ if (this.drainCD > m.cycle) {
+ m.pushMass(mob[i], 0);
+ } else {
+ m.pushMass(mob[i]);
+ this.drainCD = m.cycle + 15
+ }
+ }
+ }
+ }
+ if (tech.harmonics === 2) {
+ m.harmonicShield = m.harmonic3Phase
+ } else {
+ m.harmonicShield = m.harmonicAtomic
+ }
+ m.hold = function () {
+ if (m.isHolding) {
+ m.drawHold(m.holdingTarget);
+ m.holding();
+ m.throwBlock();
+ } else if ((input.field) && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed
+ if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
+ m.grabPowerUp();
+ m.lookForPickUp();
+ } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
+ m.pickUp();
+ } else {
+ m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
+ }
+ if (m.energy > m.minEnergyToDeflect && m.fieldCDcycle < m.cycle) {
+ if (tech.isStandingWaveExpand) {
+ if (input.field) {
+ // const oldHarmonicRadius = m.harmonicRadius
+ m.harmonicRadius = 0.99 * m.harmonicRadius + 0.01 * 4
+ // m.energy -= 0.1 * (m.harmonicRadius - oldHarmonicRadius)
+ } else {
+ m.harmonicRadius = 0.994 * m.harmonicRadius + 0.006
+ }
+ }
+ if (!simulation.isTimeSkipping) m.harmonicShield()
+ }
+ m.drawRegenEnergy()
+ }
+ }
+ },
+ {
+ name: "perfect diamagnetism",
+ description: `deflecting does not drain energy
shield maintains functionality while inactive
5 energy per second`,
+ effect: () => {
+ m.fieldMeterColor = "#48f" //"#0c5"
+ m.eyeFillColor = m.fieldMeterColor
+ m.fieldShieldingScale = 0;
+ m.fieldBlockCD = 3;
+ m.grabPowerUpRange2 = 10000000
+ m.fieldPosition = { x: m.pos.x, y: m.pos.y }
+ m.fieldAngle = m.angle
+ m.perfectPush = (isFree = false) => {
+ if (m.fieldCDcycle < m.cycle) {
+ for (let i = 0, len = mob.length; i < len; ++i) {
+ if (
+ Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) - mob[i].radius < m.fieldRange &&
+ !mob[i].isUnblockable &&
+ Vector.dot({ x: Math.cos(m.fieldAngle), y: Math.sin(m.fieldAngle) }, Vector.normalise(Vector.sub(mob[i].position, m.fieldPosition))) > m.fieldThreshold &&
+ Matter.Query.ray(map, mob[i].position, m.fieldPosition).length === 0
+ ) {
+ mob[i].locatePlayer();
+ const unit = Vector.normalise(Vector.sub(m.fieldPosition, mob[i].position))
+ m.fieldCDcycle = m.cycle + m.fieldBlockCD + (mob[i].isShielded ? 10 : 0);
+ if (!mob[i].isInvulnerable && bullet.length < 250) {
+ for (let i = 0; i < m.coupling; i++) {
+ if (0.1 * m.coupling - i > Math.random()) {
+ const angle = m.fieldAngle + 4 * m.fieldArc * (Math.random() - 0.5)
+ const radius = m.fieldRange * (0.6 + 0.3 * Math.random())
+ b.iceIX(6 + 6 * Math.random(), angle, Vector.add(m.fieldPosition, {
+ x: radius * Math.cos(angle),
+ y: radius * Math.sin(angle)
+ }))
+ }
+ }
+ }
+ if (tech.blockDmg) { //electricity
+ Matter.Body.setVelocity(mob[i], { x: 0.5 * mob[i].velocity.x, y: 0.5 * mob[i].velocity.y });
+ if (mob[i].isShielded) {
+ for (let j = 0, len = mob.length; j < len; j++) {
+ if (mob[j].id === mob[i].shieldID) mob[j].damage(tech.blockDmg * m.dmgScale * (tech.isBlockRadiation ? 6 : 2), true)
+ }
+ } else if (tech.isBlockRadiation) {
+ if (mob[i].isMobBullet) {
+ mob[i].damage(tech.blockDmg * m.dmgScale * 3, true)
+ } else {
+ mobs.statusDoT(mob[i], tech.blockDmg * m.dmgScale * 0.42, 180) //200% increase -> x (1+2) //over 7s -> 360/30 = 12 half seconds -> 3/12
+ }
+ } else {
+ mob[i].damage(tech.blockDmg * m.dmgScale, true)
+ }
+ // if (mob[i].isShielded) {
+ // for (let j = 0, len = mob.length; j < len; j++) {
+ // if (mob[j].id === mob[i].shieldID) mob[j].damage(tech.blockDmg * m.dmgScale * (tech.isBlockRadiation ? 3 : 1), true)
+ // }
+ // } else {
+ // if (tech.isBlockRadiation && !mob[i].isMobBullet) {
+ // mobs.statusDoT(mob[i], tech.blockDmg * m.dmgScale * 4 / 12, 360) //200% increase -> x (1+2) //over 7s -> 360/30 = 12 half seconds -> 3/12
+ // } else {
+ // mob[i].damage(tech.blockDmg * m.dmgScale)
+ // }
+ // }
+ const step = 40
+ ctx.beginPath();
+ for (let i = 0, len = 0.5 * tech.blockDmg; i < len; i++) {
+ let x = m.fieldPosition.x - 20 * unit.x;
+ let y = m.fieldPosition.y - 20 * unit.y;
+ ctx.moveTo(x, y);
+ for (let i = 0; i < 8; i++) {
+ x += step * (-unit.x + 1.5 * (Math.random() - 0.5))
+ y += step * (-unit.y + 1.5 * (Math.random() - 0.5))
+ ctx.lineTo(x, y);
+ }
+ }
+ ctx.lineWidth = 3;
+ ctx.strokeStyle = "#f0f";
+ ctx.stroke();
+ } else if (isFree) {
+ ctx.lineWidth = 2; //when blocking draw this graphic
+ ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})`
+ ctx.strokeStyle = "#000";
+ const len = mob[i].vertices.length - 1;
+ const mag = mob[i].radius
+ ctx.beginPath();
+ ctx.moveTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5))
+ for (let j = 0; j < len; j++) {
+ ctx.lineTo(mob[i].vertices[j].x + mag * (Math.random() - 0.5), mob[i].vertices[j].y + mag * (Math.random() - 0.5));
+ }
+ ctx.lineTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5))
+ ctx.fill();
+ ctx.stroke();
+ } else {
+
+ const eye = 15; //when blocking draw this graphic
+ const len = mob[i].vertices.length - 1;
+ ctx.lineWidth = 1;
+ ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})`
+ ctx.strokeStyle = "#000";
+ ctx.beginPath();
+ ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle));
+ ctx.lineTo(mob[i].vertices[len].x, mob[i].vertices[len].y);
+ ctx.lineTo(mob[i].vertices[0].x, mob[i].vertices[0].y);
+ ctx.fill();
+ ctx.stroke();
+ for (let j = 0; j < len; j++) {
+ ctx.beginPath();
+ ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle));
+ ctx.lineTo(mob[i].vertices[j].x, mob[i].vertices[j].y);
+ ctx.lineTo(mob[i].vertices[j + 1].x, mob[i].vertices[j + 1].y);
+ ctx.fill();
+ ctx.stroke();
+ }
+ }
+ m.bulletsToBlocks(mob[i])
+ if (tech.isStunField) mobs.statusStun(mob[i], tech.isStunField)
+ //mob knock backs
+ const massRoot = Math.sqrt(Math.max(1, mob[i].mass));
+ Matter.Body.setVelocity(mob[i], {
+ x: player.velocity.x - (30 * unit.x) / massRoot,
+ y: player.velocity.y - (30 * unit.y) / massRoot
+ });
+ if (mob[i].isUnstable) {
+ if (m.fieldCDcycle < m.cycle + 10) m.fieldCDcycle = m.cycle + 6
+ mob[i].death();
+ }
+ if (!isFree) { //player knock backs
+ if (mob[i].isDropPowerUp && player.speed < 12) {
+ const massRootCap = Math.sqrt(Math.min(10, Math.max(0.2, mob[i].mass)));
+ Matter.Body.setVelocity(player, {
+ x: 0.9 * player.velocity.x + 0.6 * unit.x * massRootCap,
+ y: 0.9 * player.velocity.y + 0.6 * unit.y * massRootCap
+ });
+ }
}
}
}
- } else {
- player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions
}
}
- this.drawRegenEnergyCloaking()
- if (m.isSneakAttack && m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) > m.cycle) { //show sneak attack status
- m.fieldDamage = 4.5 * (1 + 0.05 * m.coupling)
- const timeLeft = (m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) - m.cycle) * 0.5
- ctx.beginPath();
- ctx.arc(m.pos.x, m.pos.y, 32, 0, 2 * Math.PI);
- ctx.strokeStyle = "rgba(180,30,70,0.5)";//"rgba(0,0,0,0.7)";//"rgba(255,255,255,0.7)";//"rgba(255,0,100,0.7)";
- ctx.lineWidth = Math.max(Math.min(10, timeLeft), 3);
- ctx.stroke();
- // ctx.globalCompositeOperation = "multiply";
- // m.drawCloakedM()
- // ctx.globalCompositeOperation = "source-over";
- } else {
- m.fieldDamage = 1
- }
- }
- }
- },
- {
- name: "pilot wave",
- description: `use energy to guide blocks
, , and have +3 choices
10 energy per second`,
- effect: () => {
- m.fieldMeterColor = "#333"
- m.eyeFillColor = m.fieldMeterColor
+ m.hold = function () {
+ const wave = Math.sin(m.cycle * 0.022);
+ m.fieldRange = 180 + 12 * wave + 100 * tech.isBigField
+ m.fieldArc = 0.35 + 0.045 * wave + 0.065 * tech.isBigField //run calculateFieldThreshold after setting fieldArc, used for powerUp grab and mobPush with lookingAt(mob)
+ m.calculateFieldThreshold();
+ if (m.isHolding) {
+ m.drawHold(m.holdingTarget);
+ m.holding();
+ m.throwBlock();
+ } else if (input.field) { //not hold but field button is pressed
+ //float while field is on
+ const angleReduction = 0.5 + 0.7 * (Math.PI / 2 - Math.min(Math.PI / 2, Math.abs(m.angle + Math.PI / 2)))
+ // console.log(angleReduction)
+ if (player.velocity.y > 1) {
+ player.force.y -= angleReduction * (tech.isBigField ? 0.95 : 0.5) * player.mass * simulation.g;
+ Matter.Body.setVelocity(player, {
+ x: player.velocity.x,
+ y: 0.98 * player.velocity.y
+ }); //set velocity to cap, but keep the direction
+ }
- m.fieldPhase = 0;
- m.fieldPosition = {
- x: simulation.mouseInGame.x,
- y: simulation.mouseInGame.y
- }
- m.lastFieldPosition = {
- x: simulation.mouseInGame.x,
- y: simulation.mouseInGame.y
- }
- m.fieldOn = false;
- m.fieldRadius = 0;
- m.drop();
- m.hold = function () {
- if (tech.isPrinter) {
- //spawn blocks if field and crouch
- if (input.field && m.fieldCDcycle < m.cycle && input.down && !m.isHolding) {
- m.printBlock()
+ // go invulnerable while field is active, but also drain energy
+ // if (true && m.energy > 2 * m.fieldRegen && m.immuneCycle < m.cycle + tech.cyclicImmunity) {
+ // m.immuneCycle = m.cycle + 1; //player is immune to damage for 60 cycles
+ // m.energy -= 2 * m.fieldRegen
+ // if (m.energy < m.fieldRegen) m.fieldCDcycle = m.cycle + 90;
+ // }
+
+ if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
+ m.grabPowerUp();
+ m.lookForPickUp();
+ m.fieldPosition = { x: m.pos.x, y: m.pos.y }
+ m.fieldAngle = m.angle
+ //draw field attached to player
+ if (m.holdingTarget) {
+ ctx.fillStyle = `rgba(110,150,220, ${0.06 + 0.03 * Math.random()})`
+ ctx.strokeStyle = `rgba(110,150,220, ${0.35 + 0.05 * Math.random()})`
+ } else {
+ ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})`
+ ctx.strokeStyle = `rgba(110,150,220, ${0.4 + 0.5 * Math.random()})`
+ }
+ ctx.beginPath();
+ ctx.arc(m.pos.x, m.pos.y, m.fieldRange, m.angle - Math.PI * m.fieldArc, m.angle + Math.PI * m.fieldArc, false);
+ ctx.lineWidth = 2.5 - 1.5 * wave;
+ ctx.stroke();
+ const curve = 0.57 + 0.04 * wave
+ const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc
+ let a = m.angle + aMag
+ let cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a)
+ let cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a)
+ ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 30 * Math.cos(m.angle), m.pos.y + 30 * Math.sin(m.angle))
+ a = m.angle - aMag
+ cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a)
+ cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a)
+ ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 1 * m.fieldRange * Math.cos(m.angle - Math.PI * m.fieldArc), m.pos.y + 1 * m.fieldRange * Math.sin(m.angle - Math.PI * m.fieldArc))
+ ctx.fill();
+ m.perfectPush();
+ } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
+ m.pickUp();
+ } else {
+ m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
+ if (!input.field) { //&& tech.isFieldFree
+ //draw field free of player
+ ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})`
+ ctx.strokeStyle = `rgba(110,180,255, ${0.4 + 0.5 * Math.random()})`
+ ctx.beginPath();
+ ctx.arc(m.fieldPosition.x, m.fieldPosition.y, m.fieldRange, m.fieldAngle - Math.PI * m.fieldArc, m.fieldAngle + Math.PI * m.fieldArc, false);
+ ctx.lineWidth = 2.5 - 1.5 * wave;
+ ctx.stroke();
+ const curve = 0.8 + 0.06 * wave
+ const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc
+ let a = m.fieldAngle + aMag
+ ctx.quadraticCurveTo(m.fieldPosition.x + curve * m.fieldRange * Math.cos(a), m.fieldPosition.y + curve * m.fieldRange * Math.sin(a), m.fieldPosition.x + 1 * m.fieldRange * Math.cos(m.fieldAngle - Math.PI * m.fieldArc), m.fieldPosition.y + 1 * m.fieldRange * Math.sin(m.fieldAngle - Math.PI * m.fieldArc))
+ ctx.fill();
+ m.perfectPush(true);
+ }
+ }
+ // m.drawRegenEnergy()
+ m.drawRegenEnergy("rgba(0,0,0,0.2)")
+ if (tech.isPerfectBrake) { //cap mob speed around player
+ const range = 200 + 140 * wave + 150 * m.energy
+ for (let i = 0; i < mob.length; i++) {
+ const distance = Vector.magnitude(Vector.sub(m.pos, mob[i].position))
+ if (distance < range) {
+ const cap = mob[i].isShielded ? 8 : 4
+ 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, range, 0, 2 * Math.PI);
+ ctx.fillStyle = "hsla(200,50%,61%,0.08)";
+ ctx.fill();
+ }
+ }
+ }
+ },
+ {
+ name: "negative mass",
+ //
hold blocks as if they have a lower mass
+ description: `use energy to nullify gravity
0.5x damage taken
6 energy per second`,
+ fieldDrawRadius: 0,
+ effect: () => {
+ m.fieldFire = true;
+ m.holdingMassScale = 0.01; //can hold heavier blocks with lower cost to jumping
+ m.fieldMeterColor = "#333"
+ m.eyeFillColor = m.fieldMeterColor
+ m.fieldHarmReduction = 0.5;
+ m.fieldDrawRadius = 0;
+
+ m.hold = function () {
+ m.airSpeedLimit = 125 //5 * player.mass * player.mass
+ m.FxAir = 0.016
+ if (m.isHolding) {
+ m.drawHold(m.holdingTarget);
+ m.holding();
+ m.throwBlock();
+ } else if (input.field) { //push away
+ if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
+ m.grabPowerUp();
+ m.lookForPickUp();
+ if (m.energy > tech.negativeMassCost && m.fieldCDcycle < m.cycle) {
+ if (tech.isFlyFaster) {
+ //look for nearby objects to make zero-g
+ function moveThis(who, range, mag = 1.06) {
+ for (let i = 0, len = who.length; i < len; ++i) {
+ sub = Vector.sub(who[i].position, m.pos);
+ dist = Vector.magnitude(sub);
+ if (dist < range) {
+ who[i].force.y -= who[i].mass * (simulation.g * mag); //add a bit more then standard gravity
+ if (input.left) { //blocks move horizontally with the same force as the player
+ who[i].force.x -= m.FxAir * who[i].mass / 10; // move player left / a
+ } else if (input.right) {
+ who[i].force.x += m.FxAir * who[i].mass / 10; //move player right / d
+ }
+ //loose attraction to player
+ // const sub = Vector.sub(m.pos, body[i].position)
+ // const unit = Vector.mult(Vector.normalise(sub), who[i].mass * 0.0000002 * Vector.magnitude(sub))
+ // body[i].force.x += unit.x
+ // body[i].force.y += unit.y
+ }
+ }
+ }
+ //control horizontal acceleration
+ m.airSpeedLimit = 1000 // 7* player.mass * player.mass
+ m.FxAir = 0.01
+ //control vertical acceleration
+ if (input.down) { //down
+ player.force.y += 0.5 * player.mass * simulation.g;
+ this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 500 * 0.03;
+ moveThis(powerUp, this.fieldDrawRadius, 0);
+ moveThis(body, this.fieldDrawRadius, 0);
+ } else if (input.up) { //up
+ m.energy -= 5 * tech.negativeMassCost;
+ this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 1100 * 0.03;
+ player.force.y -= 2.25 * player.mass * simulation.g;
+ moveThis(powerUp, this.fieldDrawRadius, 1.8);
+ moveThis(body, this.fieldDrawRadius, 1.8);
+ } else {
+ m.energy -= tech.negativeMassCost;
+ this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 800 * 0.03;
+ player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift
+ moveThis(powerUp, this.fieldDrawRadius);
+ moveThis(body, this.fieldDrawRadius);
+ }
+ } else {
+ //look for nearby objects to make zero-g
+ function verticalForce(who, range, mag = 1.06) {
+ for (let i = 0, len = who.length; i < len; ++i) {
+ sub = Vector.sub(who[i].position, m.pos);
+ dist = Vector.magnitude(sub);
+ if (dist < range) {
+ who[i].force.y -= who[i].mass * (simulation.g * mag); //add a bit more then standard gravity
+ if (input.left) { //blocks move horizontally with the same force as the player
+ who[i].force.x -= m.FxAir * who[i].mass / 10; // move player left / a
+ } else if (input.right) {
+ who[i].force.x += m.FxAir * who[i].mass / 10; //move player right / d
+ }
+ }
+
+
+
+ // sub = Vector.sub(who[i].position, m.pos);
+ // dist = Vector.magnitude(sub);
+ // if (dist < range) who[i].force.y -= who[i].mass * (simulation.g * mag);
+ }
+ }
+ //control horizontal acceleration
+ m.airSpeedLimit = 400 // 7* player.mass * player.mass
+ m.FxAir = 0.005
+ //control vertical acceleration
+ if (input.down) { //down
+ player.force.y -= 0.5 * player.mass * simulation.g;
+ this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 400 * 0.03;
+ verticalForce(powerUp, this.fieldDrawRadius, 0.7);
+ verticalForce(body, this.fieldDrawRadius, 0.7);
+ } else if (input.up) { //up
+ m.energy -= 5 * tech.negativeMassCost;
+ this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 850 * 0.03;
+ player.force.y -= 1.45 * player.mass * simulation.g;
+ verticalForce(powerUp, this.fieldDrawRadius, 1.38);
+ verticalForce(body, this.fieldDrawRadius, 1.38);
+ } else {
+ m.energy -= tech.negativeMassCost;
+ this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 650 * 0.03;
+ player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift
+ verticalForce(powerUp, this.fieldDrawRadius);
+ verticalForce(body, this.fieldDrawRadius);
+ }
+ }
+
+ if (m.energy < 0.001) {
+ m.fieldCDcycle = m.cycle + 120;
+ m.energy = 0;
+ }
+ //add extra friction for horizontal motion
+ if (input.down || input.up || input.left || input.right) {
+ Matter.Body.setVelocity(player, { x: player.velocity.x * 0.99, y: player.velocity.y * 0.98 });
+ } else { //slow rise and fall
+ Matter.Body.setVelocity(player, { x: player.velocity.x * 0.99, y: player.velocity.y * 0.98 });
+ }
+ // if (tech.isFreezeMobs) {
+ // const ICE_DRAIN = 0.0005
+ // for (let i = 0, len = mob.length; i < len; i++) {
+ // if (!mob[i].isMobBullet && !mob[i].shield && !mob[i].isShielded && ((mob[i].distanceToPlayer() + mob[i].radius) < this.fieldDrawRadius)) {
+ // if (m.energy > ICE_DRAIN * 2) {
+ // m.energy -= ICE_DRAIN;
+ // this.fieldDrawRadius -= 2;
+ // mobs.statusSlow(mob[i], 60)
+ // } else {
+ // break;
+ // }
+ // }
+ // }
+ // }
+ //draw zero-G range
+ if (!simulation.isTimeSkipping) {
+ ctx.beginPath();
+ ctx.arc(m.pos.x, m.pos.y, this.fieldDrawRadius, 0, 2 * Math.PI);
+ ctx.fillStyle = "#f5f5ff";
+ ctx.globalCompositeOperation = "difference";
+ ctx.fill();
+ ctx.globalCompositeOperation = "source-over";
+ }
+ }
+ } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
+ m.pickUp();
+ this.fieldDrawRadius = 0
+ } else {
+ m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
+ this.fieldDrawRadius = 0
+ }
+ m.drawRegenEnergy("rgba(0,0,0,0.2)")
+
+
+ // if (tech.isHealAttract) {
+ // for (let i = 0; i < powerUp.length; i++) {
+ // if (powerUp[i].name === "heal") {
+ // //&& Vector.magnitudeSquared(Vector.sub(powerUp[i].position, m.pos)) < 500000
+ // let attract = Vector.mult(Vector.normalise(Vector.sub(m.pos, powerUp[i].position)), 0.01 * 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));
+ // }
+ // }
+ // }
+
+
+ // powerUp[i].force.x += 0.05 * (dxP / Math.sqrt(dist2)) * powerUp[i].mass;
+ // powerUp[i].force.y += 0.05 * (dyP / Math.sqrt(dist2)) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity
+ // //extra friction
+ // Matter.Body.setVelocity(powerUp[i], {
+ // x: powerUp[i].velocity.x * 0.11,
+ // y: powerUp[i].velocity.y * 0.11
+ // });
+
+ }
+ }
+ },
+ {
+ name: "molecular assembler",
+ modeText() {
+ return `${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}`
+ },
+ description: `use energy to deflect mobs
excess energy used to print ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
12 energy per second ↓↘→↓↙←↑↑↓`,
+ setDescription() {
+ return `use energy to deflect mobs
excess energy used to print ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
12 energy per second ↓↘→↓↙←↑↑↓`
+ },
+ keyLog: [],
+ effect: () => {
+ //store event function so it can be found and removed in m.setField()
+ m.fieldEvent = function (event) {
+ m.fieldUpgrades[4].keyLog.push(event.code)
+
+
+ // Helper function to compare arrays
+ function arraysEqual(arr1, arr2) {
+ if (arr1.length !== arr2.length) return false;
+ for (let i = 0; i < arr1.length; i++) {
+ if (arr1[i] !== arr2[i]) return false;
+ }
+ return true;
+ }
+
+ const pattern = [input.key.down, input.key.right, input.key.down, input.key.left, input.key.up, input.key.up, input.key.down]
+ //check if the newest key press is correct
+ if (event.code !== pattern[m.fieldUpgrades[4].keyLog.length - 1]) {
+ m.fieldUpgrades[4].keyLog = [] //pattern is wrong, reset log
+ } else if (arraysEqual(m.fieldUpgrades[4].keyLog, pattern)) { //pattern is complete
+ //cycle to next molecular mode
+ m.fieldUpgrades[4].keyLog = []
+ const energy = m.energy //save current energy
+ if (simulation.molecularMode < 3) {
+ simulation.molecularMode++
+ } else {
+ simulation.molecularMode = 0
+ }
+ // m.setField((m.fieldMode === m.fieldUpgrades.length - 1) ? 1 : m.fieldMode + 1) //cycle to next field, skip field emitter
+ m.fieldUpgrades[4].description = m.fieldUpgrades[4].setDescription()
+ m.energy = energy //return to current energy
+
+ const name = `${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}`
+ simulation.inGameConsole(`simulation.molecularMode = ${simulation.molecularMode} // ${name} ↓↘→↓↙←↑↑↓`);
+ }
+ // console.log(m.fieldUpgrades[4].keyLog)
+ }
+ window.addEventListener("keydown", m.fieldEvent);
+
+ m.fieldMeterColor = "#ff0"
+ m.eyeFillColor = m.fieldMeterColor
+ m.hold = function () {
+ if (m.energy > m.maxEnergy - 0.02 && m.fieldCDcycle < m.cycle && !input.field && bullet.length < 300 && (m.cycle % 2)) {
+ if (simulation.molecularMode === 0) {
+ if (tech.isSporeFlea) {
+ const drain = 0.18 + (Math.max(bullet.length, 130) - 130) * 0.02
+ if (m.energy > drain) {
+ m.energy -= drain
+ const speed = m.crouch ? 20 + 8 * Math.random() : 10 + 3 * Math.random()
+ b.flea({
+ x: m.pos.x + 35 * Math.cos(m.angle),
+ y: m.pos.y + 35 * Math.sin(m.angle)
+ }, {
+ x: speed * Math.cos(m.angle),
+ y: speed * Math.sin(m.angle)
+ })
+ }
+ } else if (tech.isSporeWorm) {
+ const drain = 0.18 + (Math.max(bullet.length, 130) - 130) * 0.02
+ if (m.energy > drain) {
+ m.energy -= drain
+ b.worm({
+ x: m.pos.x + 35 * Math.cos(m.angle),
+ y: m.pos.y + 35 * Math.sin(m.angle)
+ })
+ const SPEED = 2 + 1 * Math.random();
+ Matter.Body.setVelocity(bullet[bullet.length - 1], {
+ x: SPEED * Math.cos(m.angle),
+ y: SPEED * Math.sin(m.angle)
+ });
+ }
+ } else {
+ const drain = 0.095 + (Math.max(bullet.length, 130) - 130) * 0.01
+ for (let i = 0, len = 5; i < len; i++) {
+ if (m.energy > 3 * drain) {
+ m.energy -= drain
+ b.spore(m.pos)
+ } else {
+ break
+ }
+ }
+ }
+ } else if (simulation.molecularMode === 1) {
+ m.energy -= 0.33;
+ const direction = { x: Math.cos(m.angle), y: Math.sin(m.angle) }
+ const push = Vector.mult(Vector.perp(direction), 0.08)
+ b.missile({ x: m.pos.x + 30 * direction.x, y: m.pos.y + 30 * direction.y }, m.angle, -15)
+ bullet[bullet.length - 1].force.x += push.x * (Math.random() - 0.5)
+ bullet[bullet.length - 1].force.y += 0.005 + push.y * (Math.random() - 0.5)
+ // b.missile({ x: m.pos.x, y: m.pos.y - 40 }, -Math.PI / 2 + 0.5 * (Math.random() - 0.5), 0, 1)
+ } else if (simulation.molecularMode === 2) {
+ m.energy -= 0.044;
+ b.iceIX(1)
+ } else if (simulation.molecularMode === 3) {
+ if (tech.isDroneRadioactive) {
+ const drain = 0.8 + (Math.max(bullet.length, 50) - 50) * 0.01
+ if (m.energy > drain) {
+ m.energy -= drain
+ b.droneRadioactive({
+ x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5),
+ y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5)
+ }, 25)
+ }
+ } else {
+ //every bullet above 100 adds 0.005 to the energy cost per drone
+ //at 200 bullets the energy cost is 0.45 + 100*0.006 = 1.05
+ const drain = (0.45 + (Math.max(bullet.length, 100) - 100) * 0.006) * tech.droneEnergyReduction
+ if (m.energy > drain) {
+ m.energy -= drain
+ b.drone()
+ }
+ }
+ }
}
- //if holding block grow it
if (m.isHolding) {
m.drawHold(m.holdingTarget);
m.holding();
@@ -5178,365 +4341,1245 @@ const m = {
Matter.Body.setVertices(m.holdingTarget, vertices)
m.definePlayerMass(m.defaultMass + m.holdingTarget.mass * m.holdingMassScale)
}
- m.throwBlock()
+ m.throwBlock();
+ } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed
+ if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
+ m.grabPowerUp();
+ m.lookForPickUp();
+ if (tech.isPrinter && input.down) {
+ m.printBlock();
+ } else if (m.energy > m.minEnergyToDeflect) {
+ m.drawField();
+ m.pushMobsFacing();
+ }
+ } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
+ m.pickUp();
} else {
m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
}
- //if releasing field throw it
-
+ m.drawRegenEnergy()
}
- if (input.field) {
- if (m.fieldCDcycle < m.cycle) {
- const scale = 25
- const bounds = {
- min: {
- x: m.fieldPosition.x - scale,
- y: m.fieldPosition.y - scale
- },
- max: {
- x: m.fieldPosition.x + scale,
- y: m.fieldPosition.y + scale
+ }
+ },
+ {
+ name: "plasma torch",
+ description: "use energy to emit short range plasma
1.5x damage
10 energy per second",
+ set() {
+ b.isExtruderOn = false
+ m.fieldDamage = 1.5
+ // m.fieldCDcycleAlternate = 0
+
+ if (m.plasmaBall) {
+ m.plasmaBall.reset()
+ Matter.Composite.remove(engine.world, m.plasmaBall);
+ }
+ if (tech.isPlasmaBall) {
+ const circleRadiusScale = 2
+ m.plasmaBall = Bodies.circle(m.pos.x + 10 * Math.cos(m.angle), m.pos.y + 10 * Math.sin(m.angle), 1, {
+ // collisionFilter: {
+ // group: 0,
+ // category: 0,
+ // mask: 0 //cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield
+ // },
+ isSensor: true,
+ frictionAir: 0,
+ alpha: 0.7,
+ isPopping: false,
+ isAttached: false,
+ isOn: false,
+ drain: 0.0017,
+ radiusLimit: 10,
+ damage: 0.8,
+ setPositionToNose() {
+ const nose = { x: m.pos.x + 10 * Math.cos(m.angle), y: m.pos.y + 10 * Math.sin(m.angle) }
+ Matter.Body.setPosition(this, Vector.add(nose, Vector.mult(Vector.normalise(Vector.sub(nose, m.pos)), circleRadiusScale * this.circleRadius)));
+ },
+ fire() {
+ this.isAttached = false;
+ const speed = 10 //scale with mass?
+ Matter.Body.setVelocity(this, {
+ x: player.velocity.x * 0.4 + speed * Math.cos(m.angle),
+ y: speed * Math.sin(m.angle)
+ });
+ m.plasmaBall.setPositionToNose()
+ if (this.circleRadius < 10) this.isPopping = true
+ },
+ scale(scale) {
+ Matter.Body.scale(m.plasmaBall, scale, scale); //shrink fast
+ if (this.circleRadius < this.radiusLimit) this.reset()
+ },
+ reset() {
+ // console.log(this.circleRadius)
+ const scale = 1 / m.plasmaBall.circleRadius
+ Matter.Body.scale(m.plasmaBall, scale, scale); //grow
+ // console.log(this.circleRadius)
+ // this.circleRadius = 0
+ this.alpha = 0.7
+ this.isOn = false
+ this.isPopping = false
+ // this.isAttached = true;
+ },
+ do() {
+ if (this.isOn) {
+ //collisions with map
+ if (Matter.Query.collides(this, map).length > 0) {
+ if (this.isAttached) {
+ this.scale(Math.max(0.9, 0.998 - 0.1 / m.plasmaBall.circleRadius))
+ } else {
+ this.isPopping = true
+ }
+ }
+ if (this.isPopping) {
+ this.alpha -= 0.03
+ if (this.alpha < 0.1) {
+ this.reset()
+ } else {
+ const scale = 1.04 + 4 / Math.max(1, m.plasmaBall.circleRadius)
+ Matter.Body.scale(m.plasmaBall, scale, scale); //grow
+ }
+ // if (this.speed > 2.5) {
+ // const slow = 0.9
+ // Matter.Body.setVelocity(this, {
+ // x: slow * this.velocity.x,
+ // y: slow * this.velocity.y
+ // });
+ // }
+ }
+ //collisions with mobs
+ // const whom = Matter.Query.collides(this, mob)
+ // const dmg = this.damage * m.dmgScale
+ // for (let i = 0, len = whom.length; i < len; i++) {
+ // const mobHit = (who) => {
+ // if (who.alive) {
+ // if (!this.isAttached && !who.isMobBullet) this.isPopping = true
+ // who.damage(dmg);
+ // // if (who.shield) this.scale(Math.max(0.9, 0.99 - 0.5 / m.plasmaBall.circleRadius))
+ // if (who.speed > 5) {
+ // Matter.Body.setVelocity(who, { //friction
+ // x: who.velocity.x * 0.6,
+ // y: who.velocity.y * 0.6
+ // });
+ // } else {
+ // Matter.Body.setVelocity(who, { //friction
+ // x: who.velocity.x * 0.93,
+ // y: who.velocity.y * 0.93
+ // });
+ // }
+ // }
+ // }
+ // mobHit(whom[i].bodyA)
+ // mobHit(whom[i].bodyB)
+ // }
+
+ //damage nearby mobs
+ const dmg = this.damage * m.dmgScale
+ const arcList = []
+ const damageRadius = circleRadiusScale * this.circleRadius
+ const dischargeRange = 150 + 1600 * tech.plasmaDischarge + 1.3 * damageRadius
+ for (let i = 0, len = mob.length; i < len; i++) {
+ if (mob[i].alive && (!mob[i].isBadTarget || mob[i].isMobBullet) && !mob[i].isInvulnerable) {
+ const sub = Vector.magnitude(Vector.sub(this.position, mob[i].position))
+ if (sub < damageRadius + mob[i].radius) {
+ // if (!this.isAttached && !mob[i].isMobBullet) this.isPopping = true
+ mob[i].damage(dmg);
+ if (mob[i].speed > 5) {
+ Matter.Body.setVelocity(mob[i], { x: mob[i].velocity.x * 0.6, y: mob[i].velocity.y * 0.6 });
+ } else {
+ Matter.Body.setVelocity(mob[i], { x: mob[i].velocity.x * 0.93, y: mob[i].velocity.y * 0.93 });
+ }
+ } else if (sub < dischargeRange + mob[i].radius && Matter.Query.ray(map, mob[i].position, this.position).length === 0) {
+ arcList.push(mob[i]) //populate electrical arc list
+ }
+ }
+ }
+ for (let i = 0; i < arcList.length; i++) {
+ if (tech.plasmaDischarge > Math.random()) {
+ const who = arcList[Math.floor(Math.random() * arcList.length)]
+ who.damage(dmg * 4);
+ //draw arcs
+ const sub = Vector.sub(who.position, this.position)
+ const unit = Vector.normalise(sub)
+ let len = 12
+ const step = Vector.magnitude(sub) / (len + 2)
+ let x = this.position.x
+ let y = this.position.y
+ ctx.beginPath();
+ ctx.moveTo(x, y);
+ for (let i = 0; i < len; i++) {
+ x += step * (unit.x + (Math.random() - 0.5))
+ y += step * (unit.y + (Math.random() - 0.5))
+ ctx.lineTo(x, y);
+ }
+ ctx.lineTo(who.position.x, who.position.y);
+ ctx.strokeStyle = "#88f";
+ ctx.lineWidth = 4 + 3 * Math.random();
+ ctx.stroke();
+ if (who.damageReduction) {
+ simulation.drawList.push({
+ x: who.position.x,
+ y: who.position.y,
+ radius: 15,
+ color: "rgba(150,150,255,0.4)",
+ time: 15
+ });
+ }
+ }
+ }
+
+
+ //slowly slow down if too fast
+ if (this.speed > 10) {
+ const scale = 0.998
+ Matter.Body.setVelocity(this, { x: scale * this.velocity.x, y: scale * this.velocity.y });
+ }
+
+ //graphics
+ const radius = circleRadiusScale * this.circleRadius * (0.99 + 0.02 * Math.random()) + 3 * Math.random()
+ const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 0, this.position.x, this.position.y, radius);
+ const alpha = this.alpha + 0.1 * Math.random()
+ gradient.addColorStop(0, `rgba(255,255,255,${alpha})`);
+ gradient.addColorStop(0.35 + 0.1 * Math.random(), `rgba(255,150,255,${alpha})`);
+ gradient.addColorStop(1, `rgba(255,0,255,${alpha})`);
+ // gradient.addColorStop(1, `rgba(255,150,255,${alpha})`);
+ ctx.fillStyle = gradient
+ ctx.beginPath();
+ ctx.arc(this.position.x, this.position.y, radius, 0, 2 * Math.PI);
+ ctx.fill();
+ //draw arcs
+ const unit = Vector.rotate({ x: 1, y: 0 }, Math.random() * 6.28)
+ let len = 8
+ const step = this.circleRadius / len
+ let x = this.position.x
+ let y = this.position.y
+ ctx.beginPath();
+ if (Math.random() < 0.5) {
+ x += step * (unit.x + 6 * (Math.random() - 0.5))
+ y += step * (unit.y + 6 * (Math.random() - 0.5))
+ len -= 2
+ }
+ if (Math.random() < 0.5) {
+ x += step * (unit.x + 6 * (Math.random() - 0.5))
+ y += step * (unit.y + 6 * (Math.random() - 0.5))
+ len -= 2
+ }
+ ctx.moveTo(x, y);
+
+ for (let i = 0; i < len; i++) {
+ x += step * (unit.x + 1.9 * (Math.random() - 0.5))
+ y += step * (unit.y + 1.9 * (Math.random() - 0.5))
+ ctx.lineTo(x, y);
+ }
+ ctx.strokeStyle = "#88f";
+ ctx.lineWidth = 2 * Math.random();
+ ctx.stroke();
+ }
+ },
+ });
+
+ Composite.add(engine.world, m.plasmaBall);
+ // m.plasmaBall.startingVertices = m.plasmaBall.vertices.slice();
+ m.hold = function () {
+ if (m.isHolding) {
+ m.drawHold(m.holdingTarget);
+ m.holding();
+ m.throwBlock();
+ } else if (input.field) { //not hold but field button is pressed
+ if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
+ m.grabPowerUp();
+ m.lookForPickUp();
+ if (m.fieldCDcycle < m.cycle) {
+ //field is active
+ if (!m.plasmaBall.isAttached) { //return ball to player
+ if (m.plasmaBall.isOn) {
+ m.plasmaBall.isPopping = true
+ } else {
+ m.plasmaBall.isAttached = true
+ m.plasmaBall.isOn = true
+ m.plasmaBall.isPopping = false
+ m.plasmaBall.alpha = 0.7
+ m.plasmaBall.setPositionToNose()
+ // m.plasmaBall.reset()
+
+ }
+ } else if (m.energy > m.plasmaBall.drain) { //charge up when attached
+ if (tech.isCapacitor) {
+ m.energy -= m.plasmaBall.drain * 2;
+ const scale = 1 + 48 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8)
+ Matter.Body.scale(m.plasmaBall, scale, scale); //grow
+ } else {
+ m.energy -= m.plasmaBall.drain;
+ const scale = 1 + 16 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8)
+ Matter.Body.scale(m.plasmaBall, scale, scale); //grow
+ }
+ if (m.energy > m.maxEnergy) {
+ m.energy -= m.plasmaBall.drain * 2;
+ const scale = 1 + 16 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8)
+ Matter.Body.scale(m.plasmaBall, scale, scale); //grow
+ }
+ m.plasmaBall.setPositionToNose()
+
+ //add friction for player when holding ball, more friction in vertical
+ // const floatScale = Math.sqrt(m.plasmaBall.circleRadius)
+ // const friction = 0.0002 * floatScale
+ // const slowY = (player.velocity.y > 0) ? Math.max(0.8, 1 - friction * player.velocity.y * player.velocity.y) : Math.max(0.98, 1 - friction * Math.abs(player.velocity.y)) //down : up
+ // Matter.Body.setVelocity(player, {
+ // x: Math.max(0.95, 1 - friction * Math.abs(player.velocity.x)) * player.velocity.x,
+ // y: slowY * player.velocity.y
+ // });
+
+ // if (player.velocity.y > 7) player.force.y -= 0.95 * player.mass * simulation.g //less gravity when falling fast
+ // player.force.y -= Math.min(0.95, 0.05 * floatScale) * player.mass * simulation.g; //undo some gravity on up or down
+
+ //float
+ const slowY = (player.velocity.y > 0) ? Math.max(0.8, 1 - 0.002 * player.velocity.y * player.velocity.y) : Math.max(0.98, 1 - 0.001 * Math.abs(player.velocity.y)) //down : up
+ Matter.Body.setVelocity(player, {
+ x: Math.max(0.95, 1 - 0.003 * Math.abs(player.velocity.x)) * player.velocity.x,
+ y: slowY * player.velocity.y
+ });
+ if (player.velocity.y > 5) {
+ player.force.y -= 0.9 * player.mass * simulation.g //less gravity when falling fast
+ } else {
+ player.force.y -= 0.5 * player.mass * simulation.g;
+ }
+ } else {
+ m.fieldCDcycle = m.cycle + 90;
+ m.plasmaBall.fire()
+ }
+ }
+ } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
+ m.pickUp();
+ if (m.plasmaBall.isAttached) {
+ m.fieldCDcycle = m.cycle + 30;
+ m.plasmaBall.fire()
+ }
+ } else {
+ m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
+ if (m.plasmaBall.isAttached) {
+ m.fieldCDcycle = m.cycle + 30;
+ m.plasmaBall.fire()
}
}
- const isInMap = Matter.Query.region(map, bounds).length
- // const isInMap = Matter.Query.point(map, m.fieldPosition).length
+ m.drawRegenEnergy("rgba(0, 0, 0, 0.2)")
+ m.plasmaBall.do()
+ }
+ } else if (tech.isExtruder) {
+ m.hold = function () {
+ b.isExtruderOn = false
+ if (m.isHolding) {
+ m.drawHold(m.holdingTarget);
+ m.holding();
+ m.throwBlock();
+ } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed
+ if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
+ m.grabPowerUp();
+ m.lookForPickUp();
+ b.extruder();
+ } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
+ m.pickUp();
+ } else {
+ m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
+ }
+ m.drawRegenEnergy("rgba(0, 0, 0, 0.2)")
+ if (input.field) {
+ b.wasExtruderOn = true
+ } else {
+ b.wasExtruderOn = false
+ b.canExtruderFire = true
+ }
+ ctx.beginPath(); //draw all the wave bullets
+ for (let i = 1, len = bullet.length; i < len; i++) { //skip the first bullet (which is is oldest bullet)
+ if (bullet[i].isWave) {
+ if (bullet[i].isBranch || bullet[i - 1].isBranch) {
+ ctx.moveTo(bullet[i].position.x, bullet[i].position.y)
+ } else {
+ ctx.lineTo(bullet[i].position.x, bullet[i].position.y)
+ }
+ }
+ }
+ if (b.wasExtruderOn && b.isExtruderOn) ctx.lineTo(m.pos.x + 15 * Math.cos(m.angle), m.pos.y + 15 * Math.sin(m.angle))
+ ctx.lineWidth = 4;
+ ctx.strokeStyle = "#f07"
+ ctx.stroke();
+ ctx.lineWidth = tech.extruderRange;
+ ctx.strokeStyle = "rgba(255,0,110,0.06)"
+ ctx.stroke();
+ }
+ // } else if (true) { //plasma sword slash
+ // const plasmaSweepCycles = 30
+ // m.plasmaSweep = 0
+ // m.plasmaSlashDirection = m.flipLegs//Math.random() > 0.5 ? 1 : -1;
+ // m.hold = function () {
+ // if (m.isHolding) {
+ // m.drawHold(m.holdingTarget);
+ // m.holding();
+ // m.throwBlock();
+ // m.plasmaSweep = 0
+ // // } else if (true) { //not hold but field button is pressed
+ // } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed
+ // if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
+ // m.grabPowerUp();
+ // m.lookForPickUp();
- if (!m.fieldOn) { // if field was off, and it starting up, teleport to new mouse location
- m.fieldOn = true;
- // m.fieldPosition = { //smooth the mouse position, set to starting at player
- // x: m.pos.x,
- // y: m.pos.y
+ // //graphics
+ // if (m.plasmaSweep === 0) m.plasmaSlashDirection = m.flipLegs //Math.random() > 0.5 ? 1 : -1;
+ // const angle = m.angle //+ 1 * (m.plasmaSweep - plasmaSweepCycles / 2) / plasmaSweepCycles * m.plasmaSlashDirection
+ // const plasmaSweepCapped = Math.min(m.plasmaSweep, plasmaSweepCycles - 8) / plasmaSweepCycles
+ // const range = 100 * plasmaSweepCapped
+ // const arc = 1.3
+ // const A = { x: m.pos.x + range * Math.cos(angle - arc), y: m.pos.y + range * Math.sin(angle - arc) }
+ // const B = { x: m.pos.x + range * Math.cos(angle + arc), y: m.pos.y + range * Math.sin(angle + arc) }
+ // const controlRange = 500 * plasmaSweepCapped
+ // const AC = { x: m.pos.x + controlRange * Math.cos(angle - arc / 2), y: m.pos.y + controlRange * Math.sin(angle - arc / 2) }
+ // const BC = { x: m.pos.x + controlRange * Math.cos(angle + arc / 2), y: m.pos.y + controlRange * Math.sin(angle + arc / 2) }
+ // const innerControlRange = 300 * plasmaSweepCapped
+ // const ACinner = { x: m.pos.x + innerControlRange * Math.cos(angle - arc / 2), y: m.pos.y + innerControlRange * Math.sin(angle - arc / 2) }
+ // const BCinner = { x: m.pos.x + innerControlRange * Math.cos(angle + arc / 2), y: m.pos.y + innerControlRange * Math.sin(angle + arc / 2) }
+ // ctx.beginPath();
+ // ctx.moveTo(A.x, A.y)
+ // ctx.bezierCurveTo(AC.x, AC.y, BC.x, BC.y, B.x, B.y); //outer curve
+ // ctx.bezierCurveTo(BCinner.x, BCinner.y, ACinner.x, ACinner.y, A.x, A.y); //inner curve
+ // // ctx.strokeStyle = "#000"
+ // // ctx.stroke();
+ // ctx.fillStyle = "rgba(255,0,255,0.5)"
+ // ctx.fill();
+
+ // //draw control points for graphics reference
+ // ctx.lineWidth = '0.5'
+ // ctx.beginPath();
+ // ctx.arc(A.x, A.y, 5, 0, 2 * Math.PI);
+ // ctx.stroke();
+ // ctx.beginPath();
+ // ctx.arc(B.x, B.y, 5, 0, 2 * Math.PI);
+ // ctx.stroke();
+ // ctx.beginPath();
+ // ctx.arc(AC.x, AC.y, 5, 0, 2 * Math.PI);
+ // ctx.stroke();
+ // ctx.beginPath();
+ // ctx.arc(BC.x, BC.y, 5, 0, 2 * Math.PI);
+ // ctx.stroke();
+ // ctx.beginPath();
+ // ctx.arc(ACinner.x, ACinner.y, 5, 0, 2 * Math.PI);
+ // ctx.stroke();
+ // ctx.beginPath();
+ // ctx.arc(BCinner.x, BCinner.y, 5, 0, 2 * Math.PI);
+ // ctx.stroke();
+
+ // //mob collision detection
+ // collideRange = 160
+ // const collideCenter = {
+ // x: m.pos.x + collideRange * Math.cos(angle),
+ // y: m.pos.y + collideRange * Math.sin(angle)
+ // }
+ // ctx.beginPath();
+ // ctx.arc(collideCenter.x, collideCenter.y, 140, 0, 2 * Math.PI);
+ // ctx.stroke();
+
+ // //push mob away and slow them?
+
+
+ // //sweeping motion and cooldown
+ // m.plasmaSweep++
+ // if (m.plasmaSweep > plasmaSweepCycles) {
+ // m.plasmaSweep = 0
+ // if (m.fireCDcycle < m.cycle + 30) m.fieldCDcycle = m.cycle + 30
+ // }
+ // } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
+ // m.pickUp();
+ // m.plasmaSweep = 0
+ // } else {
+ // m.plasmaSweep = 0
+ // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
+ // }
+ // m.drawRegenEnergy("rgba(0, 0, 0, 0.2)")
+ // }
+ } else {
+ m.hold = function () {
+ if (m.isHolding) {
+ m.drawHold(m.holdingTarget);
+ m.holding();
+ m.throwBlock();
+ } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed
+ if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
+ m.grabPowerUp();
+ m.lookForPickUp();
+ b.plasma();
+ } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
+ m.pickUp();
+ } else {
+ m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
+ }
+ m.drawRegenEnergy("rgba(0, 0, 0, 0.2)")
+ }
+ }
+ },
+ effect() {
+ m.fieldMeterColor = "#f0f"
+ m.eyeFillColor = m.fieldMeterColor
+ this.set();
+ }
+ },
+ {
+ name: "time dilation",
+ description: `use energy to stop time
1.2x movement and fire rate
12 energy per second`,
+ set() {
+ // m.fieldMeterColor = "#0fc"
+ // m.fieldMeterColor = "#ff0"
+ m.fieldMeterColor = "#3fe"
+ m.eyeFillColor = m.fieldMeterColor
+ m.fieldFx = 1.25
+ // m.fieldJump = 1.09
+ m.setMovement();
+ b.setFireCD()
+ const timeStop = () => {
+ m.immuneCycle = m.cycle + 10; //invulnerable to harm while time is stopped, this also disables regen
+ //draw field everywhere
+ ctx.globalCompositeOperation = "saturation"
+ ctx.fillStyle = "#ccc";
+ ctx.fillRect(-50000, -50000, 100000, 100000)
+ ctx.globalCompositeOperation = "source-over"
+ //stop time
+ m.isTimeDilated = true;
+
+ function sleep(who) {
+ for (let i = 0, len = who.length; i < len; ++i) {
+ if (!who[i].isSleeping) {
+ who[i].storeVelocity = who[i].velocity
+ who[i].storeAngularVelocity = who[i].angularVelocity
+ }
+ Matter.Sleeping.set(who[i], true)
+ }
+ }
+ sleep(mob);
+ sleep(body);
+ sleep(bullet);
+ simulation.cycle--; //pause all functions that depend on game cycle increasing
+ }
+ if (tech.isRewindField) {
+ this.rewindCount = 0
+ m.grabPowerUpRange2 = 300000// m.grabPowerUpRange2 = 200000;
+
+ m.hold = function () {
+ // console.log(m.fieldCDcycle)
+ m.grabPowerUp();
+ // //grab power ups
+ // for (let i = 0, len = powerUp.length; i < len; ++i) {
+ // if (
+ // Vector.magnitudeSquared(Vector.sub(m.pos, powerUp[i].position)) < 100000 &&
+ // !simulation.isChoosing &&
+ // (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal)
+ // ) {
+ // powerUps.onPickUp(powerUp[i]);
+ // powerUp[i].effect();
+ // Matter.Composite.remove(engine.world, powerUp[i]);
+ // powerUp.splice(i, 1);
+ // break; //because the array order is messed up after splice
+ // }
+ // }
+ if (m.isHolding) {
+ m.drawHold(m.holdingTarget);
+ m.holding();
+ m.throwBlock();
+ m.wakeCheck();
+ } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed
+ const drain = 0.0014 / (1 + 0.05 * m.coupling)
+ if (m.energy > drain) m.energy -= drain
+ m.grabPowerUp();
+ if (this.rewindCount === 0) {
+ m.lookForPickUp();
+ }
+
+ if (!m.holdingTarget) {
+ if (this.rewindCount === 0) { //large upfront energy cost to enter rewind mode
+ if (m.energy > 0.3) {
+ m.energy -= 0.3
+ } else {
+ this.rewindCount = 0;
+ m.resetHistory();
+ if (m.fireCDcycle < m.cycle + 60) m.fieldCDcycle = m.cycle + 60
+ m.immuneCycle = m.cycle //if you reach the end of the history disable harm immunity
+ }
+ }
+ this.rewindCount += 6;
+ const DRAIN = 0.003
+ let history = m.history[(m.cycle - this.rewindCount) % 600]
+ if (this.rewindCount > 599 || m.energy < DRAIN) {
+ this.rewindCount = 0;
+ m.resetHistory();
+ if (m.fireCDcycle < m.cycle + 60) m.fieldCDcycle = m.cycle + 60
+ m.immuneCycle = m.cycle //if you reach the end of the history disable harm immunity
+ } else {
+ //draw field everywhere
+ ctx.globalCompositeOperation = "saturation"
+ ctx.fillStyle = "#ccc";
+ ctx.fillRect(-100000, -100000, 200000, 200000)
+ ctx.globalCompositeOperation = "source-over"
+ // m.grabPowerUp(); //a second grab power up to make the power ups easier to grab, and they more fast which matches the time theme
+ m.energy -= DRAIN
+ if (m.immuneCycle < m.cycle + 5) m.immuneCycle = m.cycle + 5; //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) {
+ m.health = history.health
+ if (m.health > m.maxHealth) m.health = m.maxHealth
+ m.displayHealth();
+ }
+ m.yOff = history.yOff
+ if (m.yOff < 48) {
+ m.doCrouch()
+ } else {
+ m.undoCrouch()
+ }
+ if (tech.isRewindBot && !(this.rewindCount % 60)) {
+ for (let i = 0; i < tech.isRewindBot; i++) {
+ b.randomBot(m.pos, false, false)
+ bullet[bullet.length - 1].endCycle = simulation.cycle + 300 + Math.floor(180 * Math.random()) //8-9 seconds
+ }
+ }
+ if (tech.isRewindGrenade && !(this.rewindCount % 30)) {
+ b.grenade(m.pos, this.rewindCount) //Math.PI / 2
+ const who = bullet[bullet.length - 1]
+ who.endCycle = simulation.cycle + 120
+ }
+ }
+ }
+ m.wakeCheck();
+ } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
+ m.pickUp();
+ this.rewindCount = 0;
+ m.wakeCheck();
+ } else if (tech.isTimeStop && player.speed < 1 && m.onGround && !input.fire) {
+ timeStop();
+ this.rewindCount = 0;
+ } else {
+ m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
+ this.rewindCount = 0;
+ m.wakeCheck();
+ }
+ m.drawRegenEnergy() // this calls m.regenEnergy(); also
+ }
+ } else {
+ m.fieldFire = true;
+ m.isTimeDilated = false;
+ m.hold = function () {
+ if (m.isHolding) {
+ m.wakeCheck();
+ m.drawHold(m.holdingTarget);
+ m.holding();
+ m.throwBlock();
+ } else if (input.field && m.fieldCDcycle < m.cycle) {
+ const drain = 0.0026 / (1 + 0.03 * m.coupling)
+ if (m.energy > drain) m.energy -= drain
+ m.grabPowerUp();
+ m.lookForPickUp(); //this drains energy 0.001
+ if (m.energy > drain) {
+ timeStop();
+ } else { //holding, but field button is released
+ m.fieldCDcycle = m.cycle + 120;
+ m.energy = 0;
+ m.wakeCheck();
+ m.wakeCheck();
+ }
+ } else if (tech.isTimeStop && player.speed < 1 && m.onGround && m.fireCDcycle < m.cycle && !input.fire) {
+ timeStop();
+ //makes things move at 1/5 time rate, but has an annoying flicker for mob graphics, and other minor bugs
+ // if (!(m.cycle % 4)) {
+ // // requestAnimationFrame(() => {
+ // m.wakeCheck();
+ // // simulation.timePlayerSkip(1)
+ // // }); //wrapping in animation frame prevents errors, probably
+ // ctx.globalCompositeOperation = "saturation"
+ // ctx.fillStyle = "#ccc";
+ // ctx.fillRect(-100000, -100000, 200000, 200000)
+ // ctx.globalCompositeOperation = "source-over"
+ // } else {
+ // timeStop();
// }
- m.fieldPosition = { //smooth the mouse position, set to mouse's current location
- x: simulation.mouseInGame.x,
- y: simulation.mouseInGame.y
+ } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
+ m.wakeCheck();
+ m.pickUp();
+ } else {
+ m.wakeCheck();
+ m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
+ }
+ m.drawRegenEnergy()
+ }
+ }
+ },
+ effect() {
+ if (tech.isTimeStop) {
+ m.fieldHarmReduction = 0.6;
+ } else {
+ m.fieldHarmReduction = 1;
+ }
+ this.set();
+ }
+ },
+ {
+ name: "metamaterial cloaking",
+ description: `0.3x damage taken while cloaked
after decloaking 4.5x damage for 2 s
6 energy per second`,
+ effect: () => {
+ m.fieldFire = true;
+ m.fieldMeterColor = "#333";
+ m.eyeFillColor = m.fieldMeterColor
+ m.fieldPhase = 0;
+ m.isCloak = false
+ m.fieldDrawRadius = 0
+ m.isSneakAttack = true;
+ m.sneakAttackCycle = 0;
+ m.enterCloakCycle = 0;
+ m.drawCloakedM = function () {
+ m.walk_cycle -= m.flipLegs * m.Vx;
+ m.pos.x += 4
+ m.draw();
+ }
+ m.drawCloak = function () {
+ m.fieldPhase += 0.007
+ const wiggle = 0.15 * Math.sin(m.fieldPhase * 0.5)
+ ctx.beginPath();
+ ctx.ellipse(m.pos.x, m.pos.y, m.fieldDrawRadius * (1 - wiggle), m.fieldDrawRadius * (1 + wiggle), m.fieldPhase, 0, 2 * Math.PI);
+ ctx.fillStyle = "#fff"
+ ctx.lineWidth = 2;
+ ctx.strokeStyle = "#000"
+ // ctx.stroke()
+ ctx.globalCompositeOperation = "destination-in";
+ ctx.fill();
+ ctx.globalCompositeOperation = "source-over";
+ ctx.clip();
+ }
+ m.hold = function () {
+ if (m.isHolding) {
+ m.drawHold(m.holdingTarget);
+ m.holding();
+ m.throwBlock();
+ } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold and field button is pressed
+ if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen
+ m.grabPowerUp();
+ m.lookForPickUp();
+ } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding target exists, and field button is not pressed
+ m.pickUp();
+ } else {
+ m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
+ }
+ //not shooting (or using field) enable cloak
+ if (m.energy < 0.05 && m.fireCDcycle < m.cycle && !input.fire) m.fireCDcycle = m.cycle
+ if (m.fireCDcycle + 10 < m.cycle && !input.fire) { //automatically cloak if not firing
+ // const drain = 0.02
+ if (!m.isCloak) { //&& m.energy > drain + 0.03
+ // m.energy -= drain
+ m.isCloak = true //enter cloak
+ m.fieldHarmReduction = 0.3;
+ m.enterCloakCycle = m.cycle
+ if (tech.isCloakHealLastHit && m.lastHit > 0) {
+ const heal = Math.min(0.75 * m.lastHit, m.energy)
+ m.addHealth(heal); //heal from last hit
+ m.lastHit = 0
+ simulation.drawList.push({ //add dmg to draw queue
+ x: m.pos.x,
+ y: m.pos.y,
+ radius: Math.sqrt(heal) * 200,
+ color: "rgba(0,255,200,0.6)",
+ time: 16
+ });
}
- m.lastFieldPosition = { //used to find velocity of field changes
- x: m.fieldPosition.x,
- y: m.fieldPosition.y
- }
- } else { //when field is on it smoothly moves towards the mouse
- m.lastFieldPosition = { //used to find velocity of field changes
- x: m.fieldPosition.x,
- y: m.fieldPosition.y
- }
- const smooth = isInMap ? 0.985 : 0.96;
- m.fieldPosition = { //smooth the mouse position
- x: m.fieldPosition.x * smooth + simulation.mouseInGame.x * (1 - smooth),
- y: m.fieldPosition.y * smooth + simulation.mouseInGame.y * (1 - smooth),
+ if (tech.isIntangible) {
+ for (let i = 0; i < bullet.length; i++) {
+ if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.bullet | cat.mobBullet | cat.mobShield
+ }
}
}
+ } else if (m.isCloak) { //exit cloak
+ m.sneakAttackCycle = m.cycle
+ m.isCloak = false
+ m.fieldHarmReduction = 1
- //grab power ups into the field
+ if (tech.isIntangible) {
+ for (let i = 0; i < bullet.length; i++) {
+ if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield
+ }
+ }
+ if (tech.isCloakStun) { //stun nearby mobs after exiting cloak
+ // let isMobsAround = false
+ const stunRange = m.fieldDrawRadius * 1.25
+ // const drain = 0.01
+ // if (m.energy > drain) {
+ for (let i = 0, len = mob.length; i < len; ++i) {
+ if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) < stunRange && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 && !mob[i].isBadTarget) {
+ isMobsAround = true
+ mobs.statusStun(mob[i], 120)
+ }
+ }
+ // if (isMobsAround) {
+ // m.energy -= drain
+ // simulation.drawList.push({
+ // x: m.pos.x,
+ // y: m.pos.y,
+ // radius: stunRange,
+ // color: "hsla(0,50%,100%,0.7)",
+ // time: 7
+ // });
+ // }
+ // }
+ }
+ }
+
+ if (m.isCloak) {
+ m.fieldRange = m.fieldRange * 0.85 + 130
+ m.fieldDrawRadius = m.fieldRange * 1.1 //* 0.88 //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy));
+ m.drawCloak()
+ // ctx.globalCompositeOperation = "lighter";
+ // m.drawCloakedM()
+ // ctx.globalCompositeOperation = "source-over";
+
+ ctx.beginPath();
+ ctx.arc(m.pos.x, m.pos.y, 35, 0, 2 * Math.PI);
+ ctx.strokeStyle = "rgba(255,255,255,0.25)";//"rgba(0,0,0,0.7)";//"rgba(255,255,255,0.7)";//"rgba(255,0,100,0.7)";
+ ctx.lineWidth = 10
+ ctx.stroke();
+
+ } else if (m.fieldRange < 4000) {
+ m.fieldRange += 90
+ m.fieldDrawRadius = m.fieldRange //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy));
+ m.drawCloak()
+ }
+ if (tech.isIntangible) {
+ if (m.isCloak) {
+ player.collisionFilter.mask = cat.map
+ let inPlayer = Matter.Query.region(mob, player.bounds)
+ if (inPlayer.length > 0) {
+ for (let i = 0; i < inPlayer.length; i++) {
+ if (m.energy > 0) {
+ if (!inPlayer[i].isUnblockable) m.energy -= 0.003;
+ if (inPlayer[i].shield) m.energy -= 0.011;
+ }
+ }
+ }
+ } else {
+ player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions
+ }
+ }
+ this.drawRegenEnergyCloaking()
+ if (m.isSneakAttack && m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) > m.cycle) { //show sneak attack status
+ m.fieldDamage = 4.5 * (1 + 0.05 * m.coupling)
+ const timeLeft = (m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) - m.cycle) * 0.5
+ ctx.beginPath();
+ ctx.arc(m.pos.x, m.pos.y, 32, 0, 2 * Math.PI);
+ ctx.strokeStyle = "rgba(180,30,70,0.5)";//"rgba(0,0,0,0.7)";//"rgba(255,255,255,0.7)";//"rgba(255,0,100,0.7)";
+ ctx.lineWidth = Math.max(Math.min(10, timeLeft), 3);
+ ctx.stroke();
+ // ctx.globalCompositeOperation = "multiply";
+ // m.drawCloakedM()
+ // ctx.globalCompositeOperation = "source-over";
+ } else {
+ m.fieldDamage = 1
+ }
+ }
+ }
+ },
+ {
+ name: "pilot wave",
+ description: `use energy to guide blocks
, , and have +3 choices
10 energy per second`,
+ effect: () => {
+ m.fieldMeterColor = "#333"
+ m.eyeFillColor = m.fieldMeterColor
+
+ m.fieldPhase = 0;
+ m.fieldPosition = {
+ x: simulation.mouseInGame.x,
+ y: simulation.mouseInGame.y
+ }
+ m.lastFieldPosition = {
+ x: simulation.mouseInGame.x,
+ y: simulation.mouseInGame.y
+ }
+ m.fieldOn = false;
+ m.fieldRadius = 0;
+ m.drop();
+ m.hold = function () {
+ if (tech.isPrinter) {
+ //spawn blocks if field and crouch
+ if (input.field && m.fieldCDcycle < m.cycle && input.down && !m.isHolding) {
+ m.printBlock()
+ }
+ //if holding block grow it
+ if (m.isHolding) {
+ m.drawHold(m.holdingTarget);
+ m.holding();
+ if (tech.isPrinter && m.holdingTarget.isPrinted && input.field) {
+ // if (Math.random() < 0.004 && m.holdingTarget.vertices.length < 12) m.holdingTarget.vertices.push({ x: 0, y: 0 }) //small chance to increase the number of vertices
+ m.holdingTarget.radius += Math.min(1.1, 1.3 / m.holdingTarget.mass) //grow up to a limit
+ const r1 = m.holdingTarget.radius * (1 + 0.12 * Math.sin(m.cycle * 0.11))
+ const r2 = m.holdingTarget.radius * (1 + 0.12 * Math.cos(m.cycle * 0.11))
+ let angle = (m.cycle * 0.01) % (2 * Math.PI) //rotate the object
+ let vertices = []
+ for (let i = 0, len = m.holdingTarget.vertices.length; i < len; i++) {
+ angle += 2 * Math.PI / len
+ vertices.push({ x: m.holdingTarget.position.x + r1 * Math.cos(angle), y: m.holdingTarget.position.y + r2 * Math.sin(angle) })
+ }
+ Matter.Body.setVertices(m.holdingTarget, vertices)
+ m.definePlayerMass(m.defaultMass + m.holdingTarget.mass * m.holdingMassScale)
+ }
+ m.throwBlock()
+ } else {
+ m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
+ }
+ //if releasing field throw it
+
+ }
+ if (input.field) {
+ if (m.fieldCDcycle < m.cycle) {
+ const scale = 25
+ const bounds = {
+ min: {
+ x: m.fieldPosition.x - scale,
+ y: m.fieldPosition.y - scale
+ },
+ max: {
+ x: m.fieldPosition.x + scale,
+ y: m.fieldPosition.y + scale
+ }
+ }
+ const isInMap = Matter.Query.region(map, bounds).length
+ // const isInMap = Matter.Query.point(map, m.fieldPosition).length
+
+ if (!m.fieldOn) { // if field was off, and it starting up, teleport to new mouse location
+ m.fieldOn = true;
+ // m.fieldPosition = { //smooth the mouse position, set to starting at player
+ // x: m.pos.x,
+ // y: m.pos.y
+ // }
+ m.fieldPosition = { //smooth the mouse position, set to mouse's current location
+ x: simulation.mouseInGame.x,
+ y: simulation.mouseInGame.y
+ }
+ m.lastFieldPosition = { //used to find velocity of field changes
+ x: m.fieldPosition.x,
+ y: m.fieldPosition.y
+ }
+ } else { //when field is on it smoothly moves towards the mouse
+ m.lastFieldPosition = { //used to find velocity of field changes
+ x: m.fieldPosition.x,
+ y: m.fieldPosition.y
+ }
+ const smooth = isInMap ? 0.985 : 0.96;
+ m.fieldPosition = { //smooth the mouse position
+ x: m.fieldPosition.x * smooth + simulation.mouseInGame.x * (1 - smooth),
+ y: m.fieldPosition.y * smooth + simulation.mouseInGame.y * (1 - smooth),
+ }
+ }
+
+ //grab power ups into the field
+ for (let i = 0, len = powerUp.length; i < len; ++i) {
+ if (tech.isEnergyNoAmmo && powerUp[i].name === "ammo") continue
+
+ const dxP = m.fieldPosition.x - powerUp[i].position.x;
+ const dyP = m.fieldPosition.y - powerUp[i].position.y;
+ const dist2 = dxP * dxP + dyP * dyP + 200;
+ // float towards field if looking at and in range or if very close to player
+ if (
+ dist2 < m.fieldRadius * m.fieldRadius &&
+ (m.lookingAt(powerUp[i]) || dist2 < 16000)
+ ) {
+ powerUp[i].force.x += 0.05 * (dxP / Math.sqrt(dist2)) * powerUp[i].mass;
+ powerUp[i].force.y += 0.05 * (dyP / Math.sqrt(dist2)) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity
+ //extra friction
+ Matter.Body.setVelocity(powerUp[i], { x: powerUp[i].velocity.x * 0.11, y: powerUp[i].velocity.y * 0.11 });
+ if (
+ dist2 < 5000 &&
+ !simulation.isChoosing &&
+ (tech.isOverHeal || powerUp[i].name !== "heal" || m.maxHealth - m.health > 0.01)
+ // (powerUp[i].name !== "heal" || m.health < 0.94 * m.maxHealth)
+ // (powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity)
+ ) { //use power up if it is close enough
+ powerUps.onPickUp(powerUp[i]);
+ powerUp[i].effect();
+ Matter.Composite.remove(engine.world, powerUp[i]);
+ powerUp.splice(i, 1);
+ // m.fieldRadius += 50
+ break; //because the array order is messed up after splice
+ }
+ }
+ }
+ //grab power ups normally too
+ m.grabPowerUp();
+
+ if (m.energy > 0.01) {
+ //find mouse velocity
+ const diff = Vector.sub(m.fieldPosition, m.lastFieldPosition)
+ const speed = Vector.magnitude(diff)
+ const velocity = Vector.mult(Vector.normalise(diff), Math.min(speed, 60)) //limit velocity
+ let radius, radiusSmooth
+ if (Matter.Query.ray(map, m.fieldPosition, player.position).length) { //is there something block the player's view of the field
+ radius = 0
+ radiusSmooth = Math.max(0, isInMap ? 0.96 - 0.02 * speed : 0.995); //0.99
+ } else {
+ radius = Math.max(50, 250 - 2 * speed)
+ radiusSmooth = 0.97
+ }
+ m.fieldRadius = m.fieldRadius * radiusSmooth + radius * (1 - radiusSmooth)
+
+ for (let i = 0, len = body.length; i < len; ++i) {
+ if (Vector.magnitude(Vector.sub(body[i].position, m.fieldPosition)) < m.fieldRadius && !body[i].isNotHoldable) {
+ const DRAIN = speed * body[i].mass * 0.0000035 // * (1 + m.energy * m.energy) //drain more energy when you have more energy
+ if (m.energy > DRAIN) {
+ m.energy -= DRAIN;
+ Matter.Body.setVelocity(body[i], velocity); //give block mouse velocity
+ Matter.Body.setAngularVelocity(body[i], body[i].angularVelocity * 0.8)
+ // body[i].force.y -= body[i].mass * simulation.g; //remove gravity effects
+ //blocks drift towards center of pilot wave
+ const sub = Vector.sub(m.fieldPosition, body[i].position)
+ const push = Vector.mult(Vector.normalise(sub), 0.0001 * body[i].mass * Vector.magnitude(sub))
+ body[i].force.x += push.x
+ body[i].force.y += push.y - body[i].mass * simulation.g //remove gravity effects
+ // if (body[i].collisionFilter.category !== cat.bullet) {
+ // body[i].collisionFilter.category = cat.bullet;
+ // }
+ } else {
+ m.fieldCDcycle = m.cycle + 120;
+ m.fieldOn = false
+ m.fieldRadius = 0
+ break
+ }
+ }
+ }
+
+
+ // m.holdingTarget.collisionFilter.category = cat.bullet;
+ // m.holdingTarget.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield;
+ // //check every second to see if player is away from thrown body, and make solid
+ // const solid = function(that) {
+ // const dx = that.position.x - player.position.x;
+ // const dy = that.position.y - player.position.y;
+ // if (that.speed < 3 && dx * dx + dy * dy > 10000 && that !== m.holdingTarget) {
+ // that.collisionFilter.category = cat.body; //make solid
+ // that.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; //can hit player now
+ // } else {
+ // setTimeout(solid, 40, that);
+ // }
+ // };
+ // setTimeout(solid, 200, m.holdingTarget);
+
+
+
+ // if (tech.isFreezeMobs) {
+ // for (let i = 0, len = mob.length; i < len; ++i) {
+ // if (!mob[i].isMobBullet && !mob[i].shield && !mob[i].isShielded && Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) < m.fieldRadius + mob[i].radius) {
+ // const ICE_DRAIN = 0.0005
+ // if (m.energy > ICE_DRAIN) m.energy -= ICE_DRAIN;
+ // mobs.statusSlow(mob[i], 180)
+ // }
+ // }
+ // }
+
+ ctx.beginPath();
+ const rotate = m.cycle * 0.008;
+ m.fieldPhase += 0.2 // - 0.5 * Math.sqrt(Math.min(m.energy, 1));
+ const off1 = 1 + 0.06 * Math.sin(m.fieldPhase);
+ const off2 = 1 - 0.06 * Math.sin(m.fieldPhase);
+ ctx.beginPath();
+ ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI);
+ ctx.globalCompositeOperation = "exclusion"; //"exclusion" "difference";
+ ctx.fillStyle = "#fff"; //"#eef";
+ ctx.fill();
+ ctx.globalCompositeOperation = "source-over";
+ ctx.beginPath();
+ ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI * m.energy / m.maxEnergy);
+ ctx.strokeStyle = "#000";
+ ctx.lineWidth = 4;
+ ctx.stroke();
+ } else {
+ m.fieldCDcycle = m.cycle + 120;
+ m.fieldOn = false
+ m.fieldRadius = 0
+ }
+ } else {
+ m.grabPowerUp();
+ }
+ } else {
+ m.fieldOn = false
+ m.fieldRadius = 0
+ }
+ m.drawRegenEnergy("rgba(0,0,0,0.2)")
+ }
+ }
+ },
+ {
+ name: "wormhole",
+ //wormholes attract blocks and power ups
+ description: "use energy to tunnel through a wormhole
+8% chance to duplicate spawned power ups
8 energy per second", //
bullets may also traverse wormholes
+ drain: 0,
+ effect: function () {
+ m.fieldMeterColor = "#bbf" //"#0c5"
+ m.eyeFillColor = m.fieldMeterColor
+
+ m.duplicateChance = 0.08
+ m.fieldRange = 0
+ powerUps.setPowerUpMode(); //needed after adjusting duplication chance
+
+ m.hold = function () {
+ // m.hole = { //this is reset with each new field, but I'm leaving it here for reference
+ // isOn: false,
+ // isReady: true,
+ // pos1: {x: 0,y: 0},
+ // pos2: {x: 0,y: 0},
+ // angle: 0,
+ // unit:{x:0,y:0},
+ // }
+ if (m.hole.isOn) {
+ // draw holes
+ m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025))
+ const semiMajorAxis = m.fieldRange + 30
+ const edge1a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos1)
+ const edge1b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos1)
+ const edge2a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos2)
+ const edge2b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos2)
+ ctx.beginPath();
+ ctx.moveTo(edge1a.x, edge1a.y)
+ ctx.bezierCurveTo(m.hole.pos1.x, m.hole.pos1.y, m.hole.pos2.x, m.hole.pos2.y, edge2a.x, edge2a.y);
+ ctx.lineTo(edge2b.x, edge2b.y)
+ ctx.bezierCurveTo(m.hole.pos2.x, m.hole.pos2.y, m.hole.pos1.x, m.hole.pos1.y, edge1b.x, edge1b.y);
+ ctx.fillStyle = `rgba(255,255,255,${200 / m.fieldRange / m.fieldRange})` //"rgba(0,0,0,0.1)"
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(m.hole.pos1.x, m.hole.pos1.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI)
+ ctx.ellipse(m.hole.pos2.x, m.hole.pos2.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI)
+ ctx.fillStyle = `rgba(255,255,255,${32 / m.fieldRange})`
+ ctx.fill();
+
+ //suck power ups
for (let i = 0, len = powerUp.length; i < len; ++i) {
if (tech.isEnergyNoAmmo && powerUp[i].name === "ammo") continue
-
- const dxP = m.fieldPosition.x - powerUp[i].position.x;
- const dyP = m.fieldPosition.y - powerUp[i].position.y;
- const dist2 = dxP * dxP + dyP * dyP + 200;
- // float towards field if looking at and in range or if very close to player
- if (
- dist2 < m.fieldRadius * m.fieldRadius &&
- (m.lookingAt(powerUp[i]) || dist2 < 16000)
- ) {
- powerUp[i].force.x += 0.05 * (dxP / Math.sqrt(dist2)) * powerUp[i].mass;
- powerUp[i].force.y += 0.05 * (dyP / Math.sqrt(dist2)) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity
- //extra friction
- Matter.Body.setVelocity(powerUp[i], { x: powerUp[i].velocity.x * 0.11, y: powerUp[i].velocity.y * 0.11 });
- if (
- dist2 < 5000 &&
- !simulation.isChoosing &&
- (tech.isOverHeal || powerUp[i].name !== "heal" || m.maxHealth - m.health > 0.01)
- // (powerUp[i].name !== "heal" || m.health < 0.94 * m.maxHealth)
- // (powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity)
- ) { //use power up if it is close enough
+ //which hole is closer
+ const dxP1 = m.hole.pos1.x - powerUp[i].position.x;
+ const dyP1 = m.hole.pos1.y - powerUp[i].position.y;
+ const dxP2 = m.hole.pos2.x - powerUp[i].position.x;
+ const dyP2 = m.hole.pos2.y - powerUp[i].position.y;
+ let dxP, dyP, dist2
+ if (dxP1 * dxP1 + dyP1 * dyP1 < dxP2 * dxP2 + dyP2 * dyP2) {
+ dxP = dxP1
+ dyP = dyP1
+ } else {
+ dxP = dxP2
+ dyP = dyP2
+ }
+ dist2 = dxP * dxP + dyP * dyP;
+ if (dist2 < 600000) { //&& !(m.health === m.maxHealth && powerUp[i].name === "heal")
+ powerUp[i].force.x += 4 * (dxP / dist2) * powerUp[i].mass; // float towards hole
+ powerUp[i].force.y += 4 * (dyP / dist2) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity
+ Matter.Body.setVelocity(powerUp[i], { x: powerUp[i].velocity.x * 0.05, y: powerUp[i].velocity.y * 0.05 });
+ if (dist2 < 1000 && !simulation.isChoosing) { //use power up if it is close enough
+ m.fieldRange *= 0.8
powerUps.onPickUp(powerUp[i]);
powerUp[i].effect();
Matter.Composite.remove(engine.world, powerUp[i]);
powerUp.splice(i, 1);
- // m.fieldRadius += 50
break; //because the array order is messed up after splice
}
}
}
- //grab power ups normally too
- m.grabPowerUp();
-
- if (m.energy > 0.01) {
- //find mouse velocity
- const diff = Vector.sub(m.fieldPosition, m.lastFieldPosition)
- const speed = Vector.magnitude(diff)
- const velocity = Vector.mult(Vector.normalise(diff), Math.min(speed, 60)) //limit velocity
- let radius, radiusSmooth
- if (Matter.Query.ray(map, m.fieldPosition, player.position).length) { //is there something block the player's view of the field
- radius = 0
- radiusSmooth = Math.max(0, isInMap ? 0.96 - 0.02 * speed : 0.995); //0.99
- } else {
- radius = Math.max(50, 250 - 2 * speed)
- radiusSmooth = 0.97
- }
- m.fieldRadius = m.fieldRadius * radiusSmooth + radius * (1 - radiusSmooth)
-
- for (let i = 0, len = body.length; i < len; ++i) {
- if (Vector.magnitude(Vector.sub(body[i].position, m.fieldPosition)) < m.fieldRadius && !body[i].isNotHoldable) {
- const DRAIN = speed * body[i].mass * 0.0000035 // * (1 + m.energy * m.energy) //drain more energy when you have more energy
- if (m.energy > DRAIN) {
- m.energy -= DRAIN;
- Matter.Body.setVelocity(body[i], velocity); //give block mouse velocity
- Matter.Body.setAngularVelocity(body[i], body[i].angularVelocity * 0.8)
- // body[i].force.y -= body[i].mass * simulation.g; //remove gravity effects
- //blocks drift towards center of pilot wave
- const sub = Vector.sub(m.fieldPosition, body[i].position)
- const push = Vector.mult(Vector.normalise(sub), 0.0001 * body[i].mass * Vector.magnitude(sub))
- body[i].force.x += push.x
- body[i].force.y += push.y - body[i].mass * simulation.g //remove gravity effects
- // if (body[i].collisionFilter.category !== cat.bullet) {
- // body[i].collisionFilter.category = cat.bullet;
- // }
- } else {
- m.fieldCDcycle = m.cycle + 120;
- m.fieldOn = false
- m.fieldRadius = 0
- break
+ //suck and shrink blocks
+ const suckRange = 500
+ const shrinkRange = 100
+ const shrinkScale = 0.97;
+ const slowScale = 0.9
+ for (let i = 0, len = body.length; i < len; i++) {
+ if (!body[i].isNotHoldable) {
+ const dist1 = Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position))
+ const dist2 = Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position))
+ if (dist1 < dist2) {
+ if (dist1 < suckRange) {
+ const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, body[i].position)), 1)
+ const slow = Vector.mult(body[i].velocity, slowScale)
+ Matter.Body.setVelocity(body[i], Vector.add(slow, pull));
+ //shrink
+ if (Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) < shrinkRange) {
+ Matter.Body.scale(body[i], shrinkScale, shrinkScale);
+ if (body[i].mass < 0.05) {
+ Matter.Composite.remove(engine.world, body[i]);
+ body.splice(i, 1);
+ m.fieldRange *= 0.8
+ if ((m.fieldMode === 0 || m.fieldMode === 9) && m.immuneCycle < m.cycle) m.energy += 0.02 * m.coupling * level.isReducedRegen
+ if (tech.isWormholeWorms) { //pandimensional spermia
+ b.worm(Vector.add(m.hole.pos2, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random())))
+ Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), -10));
+ if (Math.random() < 0.5) { //chance for a second worm
+ b.worm(Vector.add(m.hole.pos2, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random())))
+ Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), -10));
+ }
+ }
+ break
+ }
+ }
}
- }
- }
-
-
- // m.holdingTarget.collisionFilter.category = cat.bullet;
- // m.holdingTarget.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield;
- // //check every second to see if player is away from thrown body, and make solid
- // const solid = function(that) {
- // const dx = that.position.x - player.position.x;
- // const dy = that.position.y - player.position.y;
- // if (that.speed < 3 && dx * dx + dy * dy > 10000 && that !== m.holdingTarget) {
- // that.collisionFilter.category = cat.body; //make solid
- // that.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; //can hit player now
- // } else {
- // setTimeout(solid, 40, that);
- // }
- // };
- // setTimeout(solid, 200, m.holdingTarget);
-
-
-
- // if (tech.isFreezeMobs) {
- // for (let i = 0, len = mob.length; i < len; ++i) {
- // if (!mob[i].isMobBullet && !mob[i].shield && !mob[i].isShielded && Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) < m.fieldRadius + mob[i].radius) {
- // const ICE_DRAIN = 0.0005
- // if (m.energy > ICE_DRAIN) m.energy -= ICE_DRAIN;
- // mobs.statusSlow(mob[i], 180)
- // }
- // }
- // }
-
- ctx.beginPath();
- const rotate = m.cycle * 0.008;
- m.fieldPhase += 0.2 // - 0.5 * Math.sqrt(Math.min(m.energy, 1));
- const off1 = 1 + 0.06 * Math.sin(m.fieldPhase);
- const off2 = 1 - 0.06 * Math.sin(m.fieldPhase);
- ctx.beginPath();
- ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI);
- ctx.globalCompositeOperation = "exclusion"; //"exclusion" "difference";
- ctx.fillStyle = "#fff"; //"#eef";
- ctx.fill();
- ctx.globalCompositeOperation = "source-over";
- ctx.beginPath();
- ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI * m.energy / m.maxEnergy);
- ctx.strokeStyle = "#000";
- ctx.lineWidth = 4;
- ctx.stroke();
- } else {
- m.fieldCDcycle = m.cycle + 120;
- m.fieldOn = false
- m.fieldRadius = 0
- }
- } else {
- m.grabPowerUp();
- }
- } else {
- m.fieldOn = false
- m.fieldRadius = 0
- }
- m.drawRegenEnergy("rgba(0,0,0,0.2)")
- }
- }
- },
- {
- name: "wormhole",
- //wormholes attract blocks and power ups
- description: "use energy to tunnel through a wormhole
+8% chance to duplicate spawned power ups
8 energy per second", //
bullets may also traverse wormholes
- drain: 0,
- effect: function () {
- m.fieldMeterColor = "#bbf" //"#0c5"
- m.eyeFillColor = m.fieldMeterColor
-
- m.duplicateChance = 0.08
- m.fieldRange = 0
- powerUps.setPowerUpMode(); //needed after adjusting duplication chance
-
- m.hold = function () {
- // m.hole = { //this is reset with each new field, but I'm leaving it here for reference
- // isOn: false,
- // isReady: true,
- // pos1: {x: 0,y: 0},
- // pos2: {x: 0,y: 0},
- // angle: 0,
- // unit:{x:0,y:0},
- // }
- if (m.hole.isOn) {
- // draw holes
- m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025))
- const semiMajorAxis = m.fieldRange + 30
- const edge1a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos1)
- const edge1b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos1)
- const edge2a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos2)
- const edge2b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos2)
- ctx.beginPath();
- ctx.moveTo(edge1a.x, edge1a.y)
- ctx.bezierCurveTo(m.hole.pos1.x, m.hole.pos1.y, m.hole.pos2.x, m.hole.pos2.y, edge2a.x, edge2a.y);
- ctx.lineTo(edge2b.x, edge2b.y)
- ctx.bezierCurveTo(m.hole.pos2.x, m.hole.pos2.y, m.hole.pos1.x, m.hole.pos1.y, edge1b.x, edge1b.y);
- ctx.fillStyle = `rgba(255,255,255,${200 / m.fieldRange / m.fieldRange})` //"rgba(0,0,0,0.1)"
- ctx.fill();
- ctx.beginPath();
- ctx.ellipse(m.hole.pos1.x, m.hole.pos1.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI)
- ctx.ellipse(m.hole.pos2.x, m.hole.pos2.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI)
- ctx.fillStyle = `rgba(255,255,255,${32 / m.fieldRange})`
- ctx.fill();
-
- //suck power ups
- for (let i = 0, len = powerUp.length; i < len; ++i) {
- if (tech.isEnergyNoAmmo && powerUp[i].name === "ammo") continue
- //which hole is closer
- const dxP1 = m.hole.pos1.x - powerUp[i].position.x;
- const dyP1 = m.hole.pos1.y - powerUp[i].position.y;
- const dxP2 = m.hole.pos2.x - powerUp[i].position.x;
- const dyP2 = m.hole.pos2.y - powerUp[i].position.y;
- let dxP, dyP, dist2
- if (dxP1 * dxP1 + dyP1 * dyP1 < dxP2 * dxP2 + dyP2 * dyP2) {
- dxP = dxP1
- dyP = dyP1
- } else {
- dxP = dxP2
- dyP = dyP2
- }
- dist2 = dxP * dxP + dyP * dyP;
- if (dist2 < 600000) { //&& !(m.health === m.maxHealth && powerUp[i].name === "heal")
- powerUp[i].force.x += 4 * (dxP / dist2) * powerUp[i].mass; // float towards hole
- powerUp[i].force.y += 4 * (dyP / dist2) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity
- Matter.Body.setVelocity(powerUp[i], { x: powerUp[i].velocity.x * 0.05, y: powerUp[i].velocity.y * 0.05 });
- if (dist2 < 1000 && !simulation.isChoosing) { //use power up if it is close enough
- m.fieldRange *= 0.8
- powerUps.onPickUp(powerUp[i]);
- powerUp[i].effect();
- Matter.Composite.remove(engine.world, powerUp[i]);
- powerUp.splice(i, 1);
- break; //because the array order is messed up after splice
- }
- }
- }
- //suck and shrink blocks
- const suckRange = 500
- const shrinkRange = 100
- const shrinkScale = 0.97;
- const slowScale = 0.9
- for (let i = 0, len = body.length; i < len; i++) {
- if (!body[i].isNotHoldable) {
- const dist1 = Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position))
- const dist2 = Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position))
- if (dist1 < dist2) {
- if (dist1 < suckRange) {
- const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, body[i].position)), 1)
+ } else if (dist2 < suckRange) {
+ const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, body[i].position)), 1)
const slow = Vector.mult(body[i].velocity, slowScale)
Matter.Body.setVelocity(body[i], Vector.add(slow, pull));
//shrink
- if (Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) < shrinkRange) {
+ if (Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) < shrinkRange) {
Matter.Body.scale(body[i], shrinkScale, shrinkScale);
if (body[i].mass < 0.05) {
Matter.Composite.remove(engine.world, body[i]);
body.splice(i, 1);
m.fieldRange *= 0.8
if ((m.fieldMode === 0 || m.fieldMode === 9) && m.immuneCycle < m.cycle) m.energy += 0.02 * m.coupling * level.isReducedRegen
+ if (m.fieldMode === 0 || m.fieldMode === 9) m.energy += 0.02 * m.coupling * level.isReducedRegen
if (tech.isWormholeWorms) { //pandimensional spermia
- b.worm(Vector.add(m.hole.pos2, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random())))
- Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), -10));
+ b.worm(Vector.add(m.hole.pos1, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random())))
+ Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), 5));
if (Math.random() < 0.5) { //chance for a second worm
- b.worm(Vector.add(m.hole.pos2, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random())))
- Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), -10));
+ b.worm(Vector.add(m.hole.pos1, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random())))
+ Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), 5));
}
}
break
}
}
}
- } else if (dist2 < suckRange) {
- const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, body[i].position)), 1)
- const slow = Vector.mult(body[i].velocity, slowScale)
- Matter.Body.setVelocity(body[i], Vector.add(slow, pull));
- //shrink
- if (Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) < shrinkRange) {
- Matter.Body.scale(body[i], shrinkScale, shrinkScale);
- if (body[i].mass < 0.05) {
- Matter.Composite.remove(engine.world, body[i]);
- body.splice(i, 1);
- m.fieldRange *= 0.8
- if ((m.fieldMode === 0 || m.fieldMode === 9) && m.immuneCycle < m.cycle) m.energy += 0.02 * m.coupling * level.isReducedRegen
- if (m.fieldMode === 0 || m.fieldMode === 9) m.energy += 0.02 * m.coupling * level.isReducedRegen
- if (tech.isWormholeWorms) { //pandimensional spermia
- b.worm(Vector.add(m.hole.pos1, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random())))
- Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), 5));
- if (Math.random() < 0.5) { //chance for a second worm
- b.worm(Vector.add(m.hole.pos1, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random())))
- Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), 5));
- }
- }
- break
+ }
+ }
+ if (tech.isWormHoleBullets) {
+ //teleport bullets
+ for (let i = 0, len = bullet.length; i < len; ++i) { //teleport bullets from hole1 to hole2
+ if (!bullet[i].botType && !bullet[i].isInHole) { //don't teleport bots
+ if (Vector.magnitude(Vector.sub(m.hole.pos1, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1
+ Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos2, Vector.sub(m.hole.pos1, bullet[i].position)));
+ m.fieldRange += 5
+ bullet[i].isInHole = true
+ } else if (Vector.magnitude(Vector.sub(m.hole.pos2, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1
+ Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos1, Vector.sub(m.hole.pos2, bullet[i].position)));
+ m.fieldRange += 5
+ bullet[i].isInHole = true
}
}
}
- }
- }
- if (tech.isWormHoleBullets) {
- //teleport bullets
- for (let i = 0, len = bullet.length; i < len; ++i) { //teleport bullets from hole1 to hole2
- if (!bullet[i].botType && !bullet[i].isInHole) { //don't teleport bots
- if (Vector.magnitude(Vector.sub(m.hole.pos1, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1
- Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos2, Vector.sub(m.hole.pos1, bullet[i].position)));
- m.fieldRange += 5
- bullet[i].isInHole = true
- } else if (Vector.magnitude(Vector.sub(m.hole.pos2, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1
- Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos1, Vector.sub(m.hole.pos2, bullet[i].position)));
- m.fieldRange += 5
- bullet[i].isInHole = true
+ // mobs get pushed away
+ for (let i = 0, len = mob.length; i < len; i++) {
+ if (Vector.magnitude(Vector.sub(m.hole.pos1, mob[i].position)) < 200) {
+ const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, mob[i].position)), -0.07)
+ Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull));
+ }
+ if (Vector.magnitude(Vector.sub(m.hole.pos2, mob[i].position)) < 200) {
+ const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, mob[i].position)), -0.07)
+ Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull));
}
}
}
- // mobs get pushed away
- for (let i = 0, len = mob.length; i < len; i++) {
- if (Vector.magnitude(Vector.sub(m.hole.pos1, mob[i].position)) < 200) {
- const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, mob[i].position)), -0.07)
- Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull));
- }
- if (Vector.magnitude(Vector.sub(m.hole.pos2, mob[i].position)) < 200) {
- const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, mob[i].position)), -0.07)
- Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull));
- }
- }
}
- }
- if (m.fieldCDcycle < m.cycle) {
- const scale = 60
- const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame)
- const sub = Vector.sub(simulation.mouseInGame, m.pos)
- const mag = Vector.magnitude(sub)
+ if (m.fieldCDcycle < m.cycle) {
+ const scale = 60
+ const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame)
+ const sub = Vector.sub(simulation.mouseInGame, m.pos)
+ const mag = Vector.magnitude(sub)
- if (input.field) {
- if (tech.isWormHolePause) {
- const drain = m.fieldRegen + 0.000035
- if (m.energy > drain) {
- m.energy -= drain
+ if (input.field) {
+ if (tech.isWormHolePause) {
+ // const drain = m.fieldRegen + 0.000035
+ // if (m.energy > drain) {
+ // m.energy -= drain
if (m.immuneCycle < m.cycle + 1) m.immuneCycle = m.cycle + 1; //player is immune to damage for 1 cycle
m.isTimeDilated = true;
@@ -5559,399 +5602,395 @@ const m = {
});
player.force.x = 0
player.force.y = 0
+ // } else {
+ // m.wakeCheck();
+ // m.energy = 0;
+ // }
+ }
+
+ m.grabPowerUp();
+ //draw possible wormhole
+ if (tech.isWormholeMapIgnore && Matter.Query.ray(map, m.pos, justPastMouse).length !== 0) {
+ this.drain = (0.05 + 0.005 * Math.sqrt(mag)) * 2
} else {
- m.wakeCheck();
- m.energy = 0;
+ this.drain = tech.isFreeWormHole ? 0 : 0.05 + 0.005 * Math.sqrt(mag)
}
- }
-
- m.grabPowerUp();
- //draw possible wormhole
- if (tech.isWormholeMapIgnore && Matter.Query.ray(map, m.pos, justPastMouse).length !== 0) {
- this.drain = (0.05 + 0.005 * Math.sqrt(mag)) * 2
- } else {
- this.drain = tech.isFreeWormHole ? 0 : 0.05 + 0.005 * Math.sqrt(mag)
- }
- const unit = Vector.perp(Vector.normalise(sub))
- const where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle) }
- m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025))
- const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame)
- const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame)
- ctx.beginPath();
- ctx.moveTo(where.x, where.y)
- ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y);
- ctx.moveTo(where.x, where.y)
- ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2b.x, edge2b.y);
- if (
- mag > 250 && m.energy > this.drain &&
- (tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) &&
- Matter.Query.region(map, {
- min: {
- x: simulation.mouseInGame.x - scale,
- y: simulation.mouseInGame.y - scale
- },
- max: {
- x: simulation.mouseInGame.x + scale,
- y: simulation.mouseInGame.y + scale
- }
- }).length === 0
- ) {
- m.hole.isReady = true;
- // ctx.fillStyle = "rgba(255,255,255,0.5)"
- // ctx.fill();
- ctx.lineWidth = 1
- ctx.strokeStyle = "#000"
- ctx.stroke();
- } else {
- m.hole.isReady = false;
- ctx.lineWidth = 1
- ctx.strokeStyle = "#000"
- ctx.lineDashOffset = 30 * Math.random()
- ctx.setLineDash([20, 40]);
- ctx.stroke();
- ctx.setLineDash([]);
- }
- } else {
- if (tech.isWormHolePause && m.isTimeDilated) m.wakeCheck();
-
- //make new wormhole
- if (
- m.hole.isReady && mag > 250 && m.energy > this.drain &&
- (tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) &&
- Matter.Query.region(map, {
- min: {
- x: simulation.mouseInGame.x - scale,
- y: simulation.mouseInGame.y - scale
- },
- max: {
- x: simulation.mouseInGame.x + scale,
- y: simulation.mouseInGame.y + scale
- }
- }).length === 0
- ) {
- m.energy -= this.drain
- m.hole.isReady = false;
- m.fieldRange = 0
- Matter.Body.setPosition(player, simulation.mouseInGame);
- // simulation.translatePlayerAndCamera(simulation.mouseInGame) //too jerky
- m.buttonCD_jump = 0 //this might fix a bug with jumping
- const velocity = Vector.mult(Vector.normalise(sub), 20)
- Matter.Body.setVelocity(player, {
- x: velocity.x,
- y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer
- });
- if (m.immuneCycle < m.cycle + 5) m.immuneCycle = m.cycle + 5; //player is immune to damage for 1/4 seconds
- // move bots to player
- for (let i = 0; i < bullet.length; i++) {
- if (bullet[i].botType) {
- Matter.Body.setPosition(bullet[i], Vector.add(player.position, {
- x: 250 * (Math.random() - 0.5),
- y: 250 * (Math.random() - 0.5)
- }));
- Matter.Body.setVelocity(bullet[i], {
- x: 0,
- y: 0
- });
- }
+ const unit = Vector.perp(Vector.normalise(sub))
+ const where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle) }
+ m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025))
+ const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame)
+ const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame)
+ ctx.beginPath();
+ ctx.moveTo(where.x, where.y)
+ ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y);
+ ctx.moveTo(where.x, where.y)
+ ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2b.x, edge2b.y);
+ if (
+ mag > 250 && m.energy > this.drain &&
+ (tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) &&
+ Matter.Query.region(map, {
+ min: {
+ x: simulation.mouseInGame.x - scale,
+ y: simulation.mouseInGame.y - scale
+ },
+ max: {
+ x: simulation.mouseInGame.x + scale,
+ y: simulation.mouseInGame.y + scale
+ }
+ }).length === 0
+ ) {
+ m.hole.isReady = true;
+ // ctx.fillStyle = "rgba(255,255,255,0.5)"
+ // ctx.fill();
+ ctx.lineWidth = 1
+ ctx.strokeStyle = "#000"
+ ctx.stroke();
+ } else {
+ m.hole.isReady = false;
+ ctx.lineWidth = 1
+ ctx.strokeStyle = "#000"
+ ctx.lineDashOffset = 30 * Math.random()
+ ctx.setLineDash([20, 40]);
+ ctx.stroke();
+ ctx.setLineDash([]);
}
+ } else {
+ if (tech.isWormHolePause && m.isTimeDilated) m.wakeCheck();
- //set holes
- m.hole.isOn = true;
- m.hole.pos1.x = m.pos.x
- m.hole.pos1.y = m.pos.y
- m.hole.pos2.x = player.position.x
- m.hole.pos2.y = player.position.y
- m.hole.angle = Math.atan2(sub.y, sub.x)
- m.hole.unit = Vector.perp(Vector.normalise(sub))
+ //make new wormhole
+ if (
+ m.hole.isReady && mag > 250 && m.energy > this.drain &&
+ (tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) &&
+ Matter.Query.region(map, {
+ min: { x: simulation.mouseInGame.x - scale, y: simulation.mouseInGame.y - scale },
+ max: { x: simulation.mouseInGame.x + scale, y: simulation.mouseInGame.y + scale }
+ }).length === 0
+ ) {
+ //tech.isWormHolePause
+ // console.log(this.drain)
+ m.energy -= this.drain
+ m.hole.isReady = false;
+ m.fieldRange = 0
+ Matter.Body.setPosition(player, simulation.mouseInGame);
+ // simulation.translatePlayerAndCamera(simulation.mouseInGame) //too jerky
+ m.buttonCD_jump = 0 //this might fix a bug with jumping
+ const velocity = Vector.mult(Vector.normalise(sub), 20)
+ Matter.Body.setVelocity(player, {
+ x: velocity.x,
+ y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer
+ });
+ if (m.immuneCycle < m.cycle + 5) m.immuneCycle = m.cycle + 5; //player is immune to damage for 1/4 seconds
+ // move bots to player
+ for (let i = 0; i < bullet.length; i++) {
+ if (bullet[i].botType) {
+ Matter.Body.setPosition(bullet[i], Vector.add(player.position, {
+ x: 250 * (Math.random() - 0.5),
+ y: 250 * (Math.random() - 0.5)
+ }));
+ Matter.Body.setVelocity(bullet[i], {
+ x: 0,
+ y: 0
+ });
+ }
+ }
- if (tech.isWormholeDamage) {
- 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, 1, 420)
- mobs.statusStun(who[i].body, 360)
+ //set holes
+ m.hole.isOn = true;
+ m.hole.pos1.x = m.pos.x
+ m.hole.pos1.y = m.pos.y
+ m.hole.pos2.x = player.position.x
+ m.hole.pos2.y = player.position.y
+ m.hole.angle = Math.atan2(sub.y, sub.x)
+ m.hole.unit = Vector.perp(Vector.normalise(sub))
+
+ if (tech.isWormholeDamage) {
+ 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, 1, 420)
+ mobs.statusStun(who[i].body, 360)
+ }
}
}
}
}
+
+ // if (true && m.energy > 0.5) { //teleport away low mass mobs
+ // // && !(m.cycle % 1)
+ // const hit = Matter.Query.region(mob, {
+ // min: {
+ // x: m.pos.x - 80,
+ // y: m.pos.y - 80
+ // },
+ // max: {
+ // x: m.pos.x + 80,
+ // y: m.pos.y + 160
+ // }
+ // })
+
+ // // find incoming mob with low mass
+ // for (let i = 0; i < hit.length; i++) {
+ // if (hit[i].mass < 4 && m.energy > hit[i].mass * 0.06) {
+ // //is the mob moving towards the player?
+
+ // // console.log('found one', hit[i].mass)
+ // const unit = Vector.normalise(hit[i].velocity)
+ // const jump = Vector.mult(unit, 200)
+ // const where = Vector.add(hit[i].position, jump)
+ // if (Matter.Query.ray(map, hit[i].position, where).length === 0) { // check if space 180 from mob is clear of body and map
+ // // m.energy -= hit[i].mass * 0.06
+ // // m.fieldCDcycle = m.cycle + 30;
+ // simulation.drawList.push({ x: hit[i].position.x, y: hit[i].position.y, radius: 20, color: "#fff", time: 16 });
+ // Matter.Body.setPosition(hit[i], where);
+ // simulation.drawList.push({ x: hit[i].position.x, y: hit[i].position.y, radius: 20, color: "#fff", time: 16 });
+ // }
+ // // break
+ // }
+ // }
+ // }
}
-
- // if (true && m.energy > 0.5) { //teleport away low mass mobs
- // // && !(m.cycle % 1)
- // const hit = Matter.Query.region(mob, {
- // min: {
- // x: m.pos.x - 80,
- // y: m.pos.y - 80
- // },
- // max: {
- // x: m.pos.x + 80,
- // y: m.pos.y + 160
- // }
- // })
-
- // // find incoming mob with low mass
- // for (let i = 0; i < hit.length; i++) {
- // if (hit[i].mass < 4 && m.energy > hit[i].mass * 0.06) {
- // //is the mob moving towards the player?
-
- // // console.log('found one', hit[i].mass)
- // const unit = Vector.normalise(hit[i].velocity)
- // const jump = Vector.mult(unit, 200)
- // const where = Vector.add(hit[i].position, jump)
- // if (Matter.Query.ray(map, hit[i].position, where).length === 0) { // check if space 180 from mob is clear of body and map
- // // m.energy -= hit[i].mass * 0.06
- // // m.fieldCDcycle = m.cycle + 30;
- // simulation.drawList.push({ x: hit[i].position.x, y: hit[i].position.y, radius: 20, color: "#fff", time: 16 });
- // Matter.Body.setPosition(hit[i], where);
- // simulation.drawList.push({ x: hit[i].position.x, y: hit[i].position.y, radius: 20, color: "#fff", time: 16 });
+ // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed
+ // const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame)
+ // const scale = 60
+ // const sub = Vector.sub(simulation.mouseInGame, m.pos)
+ // const mag = Vector.magnitude(sub)
+ // const drain = tech.isFreeWormHole ? 0 : 0.06 + 0.006 * Math.sqrt(mag)
+ // if (m.hole.isReady && mag > 250 && m.energy > drain) {
+ // if (
+ // Matter.Query.region(map, {
+ // min: {
+ // x: simulation.mouseInGame.x - scale,
+ // y: simulation.mouseInGame.y - scale
+ // },
+ // max: {
+ // x: simulation.mouseInGame.x + scale,
+ // y: simulation.mouseInGame.y + scale
+ // }
+ // }).length === 0 &&
+ // Matter.Query.ray(map, m.pos, justPastMouse).length === 0
+ // // Matter.Query.ray(map, m.pos, simulation.mouseInGame).length === 0 &&
+ // // Matter.Query.ray(map, player.position, simulation.mouseInGame).length === 0 &&
+ // // Matter.Query.ray(map, player.position, justPastMouse).length === 0
+ // ) {
+ // m.energy -= drain
+ // m.hole.isReady = false;
+ // m.fieldRange = 0
+ // Matter.Body.setPosition(player, simulation.mouseInGame);
+ // m.buttonCD_jump = 0 //this might fix a bug with jumping
+ // const velocity = Vector.mult(Vector.normalise(sub), 20)
+ // Matter.Body.setVelocity(player, {
+ // x: velocity.x,
+ // y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer
+ // });
+ // if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 15; //player is immune to damage for 1/4 seconds
+ // // move bots to player
+ // for (let i = 0; i < bullet.length; i++) {
+ // if (bullet[i].botType) {
+ // Matter.Body.setPosition(bullet[i], Vector.add(player.position, {
+ // x: 250 * (Math.random() - 0.5),
+ // y: 250 * (Math.random() - 0.5)
+ // }));
+ // Matter.Body.setVelocity(bullet[i], {
+ // x: 0,
+ // y: 0
+ // });
+ // }
// }
- // // break
+
+ // //set holes
+ // m.hole.isOn = true;
+ // m.hole.pos1.x = m.pos.x
+ // m.hole.pos1.y = m.pos.y
+ // m.hole.pos2.x = player.position.x
+ // m.hole.pos2.y = player.position.y
+ // m.hole.angle = Math.atan2(sub.y, sub.x)
+ // m.hole.unit = Vector.perp(Vector.normalise(sub))
+
+ // if (tech.isWormholeDamage) {
+ // 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, 1, 420)
+ // mobs.statusStun(who[i].body, 360)
+ // }
+ // }
+ // }
+ // } else {
+ // //draw failed wormhole
+ // const unit = Vector.perp(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)))
+ // const where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle), }
+ // m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025))
+ // const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame)
+ // const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame)
+ // ctx.beginPath();
+ // ctx.moveTo(where.x, where.y)
+ // ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y);
+ // ctx.lineTo(edge2b.x, edge2b.y)
+ // ctx.bezierCurveTo(simulation.mouseInGame.x, simulation.mouseInGame.y, where.x, where.y, where.x, where.y);
+ // // ctx.fillStyle = "rgba(255,255,255,0.5)"
+ // // ctx.fill();
+ // ctx.lineWidth = 1
+ // ctx.strokeStyle = "#000"
+ // ctx.lineDashOffset = 30 * Math.random()
+ // ctx.setLineDash([20, 40]);
+ // ctx.stroke();
+ // ctx.setLineDash([]);
// }
// }
+ // m.grabPowerUp();
+ // } else {
+ // m.hole.isReady = true;
// }
+ m.drawRegenEnergy()
}
- // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed
- // const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame)
- // const scale = 60
- // const sub = Vector.sub(simulation.mouseInGame, m.pos)
- // const mag = Vector.magnitude(sub)
- // const drain = tech.isFreeWormHole ? 0 : 0.06 + 0.006 * Math.sqrt(mag)
- // if (m.hole.isReady && mag > 250 && m.energy > drain) {
- // if (
- // Matter.Query.region(map, {
- // min: {
- // x: simulation.mouseInGame.x - scale,
- // y: simulation.mouseInGame.y - scale
- // },
- // max: {
- // x: simulation.mouseInGame.x + scale,
- // y: simulation.mouseInGame.y + scale
- // }
- // }).length === 0 &&
- // Matter.Query.ray(map, m.pos, justPastMouse).length === 0
- // // Matter.Query.ray(map, m.pos, simulation.mouseInGame).length === 0 &&
- // // Matter.Query.ray(map, player.position, simulation.mouseInGame).length === 0 &&
- // // Matter.Query.ray(map, player.position, justPastMouse).length === 0
- // ) {
- // m.energy -= drain
- // m.hole.isReady = false;
- // m.fieldRange = 0
- // Matter.Body.setPosition(player, simulation.mouseInGame);
- // m.buttonCD_jump = 0 //this might fix a bug with jumping
- // const velocity = Vector.mult(Vector.normalise(sub), 20)
- // Matter.Body.setVelocity(player, {
- // x: velocity.x,
- // y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer
- // });
- // if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 15; //player is immune to damage for 1/4 seconds
- // // move bots to player
- // for (let i = 0; i < bullet.length; i++) {
- // if (bullet[i].botType) {
- // Matter.Body.setPosition(bullet[i], Vector.add(player.position, {
- // x: 250 * (Math.random() - 0.5),
- // y: 250 * (Math.random() - 0.5)
- // }));
- // Matter.Body.setVelocity(bullet[i], {
- // x: 0,
- // y: 0
- // });
- // }
- // }
+ },
- // //set holes
- // m.hole.isOn = true;
- // m.hole.pos1.x = m.pos.x
- // m.hole.pos1.y = m.pos.y
- // m.hole.pos2.x = player.position.x
- // m.hole.pos2.y = player.position.y
- // m.hole.angle = Math.atan2(sub.y, sub.x)
- // m.hole.unit = Vector.perp(Vector.normalise(sub))
+ // rewind: function() {
+ // if (input.down) {
+ // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed
+ // const DRAIN = 0.01
+ // if (this.rewindCount < 289 && m.energy > DRAIN) {
+ // m.energy -= DRAIN
- // if (tech.isWormholeDamage) {
- // 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, 1, 420)
- // mobs.statusStun(who[i].body, 360)
- // }
- // }
- // }
- // } else {
- // //draw failed wormhole
- // const unit = Vector.perp(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)))
- // const where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle), }
- // m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025))
- // const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame)
- // const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame)
- // ctx.beginPath();
- // ctx.moveTo(where.x, where.y)
- // ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y);
- // ctx.lineTo(edge2b.x, edge2b.y)
- // ctx.bezierCurveTo(simulation.mouseInGame.x, simulation.mouseInGame.y, where.x, where.y, where.x, where.y);
- // // ctx.fillStyle = "rgba(255,255,255,0.5)"
- // // ctx.fill();
- // ctx.lineWidth = 1
- // ctx.strokeStyle = "#000"
- // ctx.lineDashOffset = 30 * Math.random()
- // ctx.setLineDash([20, 40]);
- // ctx.stroke();
- // ctx.setLineDash([]);
- // }
- // }
- // m.grabPowerUp();
- // } else {
- // m.hole.isReady = true;
- // }
- m.drawRegenEnergy()
+
+ // if (this.rewindCount === 0) {
+ // const shortPause = function() {
+ // if (m.defaultFPSCycle < m.cycle) { //back to default values
+ // simulation.fpsCap = simulation.fpsCapDefault
+ // simulation.fpsInterval = 1000 / simulation.fpsCap;
+ // // document.getElementById("dmg").style.transition = "opacity 1s";
+ // // document.getElementById("dmg").style.opacity = "0";
+ // } else {
+ // requestAnimationFrame(shortPause);
+ // }
+ // };
+ // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause);
+ // simulation.fpsCap = 4 //1 is longest pause, 4 is standard
+ // simulation.fpsInterval = 1000 / simulation.fpsCap;
+ // m.defaultFPSCycle = m.cycle
+ // }
+
+
+ // this.rewindCount += 10;
+ // simulation.wipe = function() { //set wipe to have trails
+ // // ctx.fillStyle = "rgba(255,255,255,0)";
+ // ctx.fillStyle = `rgba(221,221,221,${0.004})`;
+ // ctx.fillRect(0, 0, canvas.width, canvas.height);
+ // }
+ // let history = m.history[(m.cycle - this.rewindCount) % 300]
+ // Matter.Body.setPosition(player, history.position);
+ // Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y });
+ // if (history.health > m.health) {
+ // m.health = history.health
+ // m.displayHealth();
+ // }
+ // //grab power ups
+ // for (let i = 0, len = powerUp.length; i < len; ++i) {
+ // const dxP = player.position.x - powerUp[i].position.x;
+ // const dyP = player.position.y - powerUp[i].position.y;
+ // if (dxP * dxP + dyP * dyP < 50000 && !simulation.isChoosing && !(m.health === m.maxHealth && powerUp[i].name === "heal")) {
+ // powerUps.onPickUp(player.position);
+ // powerUp[i].effect();
+ // Matter.Composite.remove(engine.world, powerUp[i]);
+ // powerUp.splice(i, 1);
+ // const shortPause = function() {
+ // if (m.defaultFPSCycle < m.cycle) { //back to default values
+ // simulation.fpsCap = simulation.fpsCapDefault
+ // simulation.fpsInterval = 1000 / simulation.fpsCap;
+ // // document.getElementById("dmg").style.transition = "opacity 1s";
+ // // document.getElementById("dmg").style.opacity = "0";
+ // } else {
+ // requestAnimationFrame(shortPause);
+ // }
+ // };
+ // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause);
+ // simulation.fpsCap = 3 //1 is longest pause, 4 is standard
+ // simulation.fpsInterval = 1000 / simulation.fpsCap;
+ // m.defaultFPSCycle = m.cycle
+ // break; //because the array order is messed up after splice
+ // }
+ // }
+ // m.immuneCycle = m.cycle + 5; //player is immune to damage for 30 cycles
+ // } else {
+ // m.fieldCDcycle = m.cycle + 30;
+ // // m.resetHistory();
+ // }
+ // } else {
+ // if (this.rewindCount !== 0) {
+ // m.fieldCDcycle = m.cycle + 30;
+ // m.resetHistory();
+ // this.rewindCount = 0;
+ // simulation.wipe = function() { //set wipe to normal
+ // ctx.clearRect(0, 0, canvas.width, canvas.height);
+ // }
+ // }
+ // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
+ // }
+ // }
+ // m.drawRegenEnergy()
+ // },
+ },
+ {
+ name: "grappling hook",
+ description: `use energy to fire a hook that pulls you
0.5x damage taken
9 energy per second`,
+ effect: () => {
+ m.fieldFire = true;
+ // m.holdingMassScale = 0.01; //can hold heavier blocks with lower cost to jumping
+ // m.fieldMeterColor = "#789"//"#456"
+ m.eyeFillColor = m.fieldMeterColor
+ m.fieldHarmReduction = 0.5; //40% reduction
+ m.grabPowerUpRange2 = 300000 //m.grabPowerUpRange2 = 200000;
+
+ m.hold = function () {
+ if (m.isHolding) {
+ m.drawHold(m.holdingTarget);
+ m.holding();
+ m.throwBlock();
+ } else if (input.field) {
+ m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
+ if (m.fieldCDcycle < m.cycle) {
+ if (m.energy > 0.02) m.energy -= 0.02
+ b.grapple({ x: m.pos.x + 40 * Math.cos(m.angle), y: m.pos.y + 40 * Math.sin(m.angle) }, m.angle)
+ if (m.fieldCDcycle < m.cycle + 20) m.fieldCDcycle = m.cycle + 20
+ }
+ m.grabPowerUp();
+ } else {
+ m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
+ if (tech.isHookDefense && m.energy > 0.15 && m.fieldCDcycle < m.cycle) {
+ const range = 300
+ for (let i = 0; i < mob.length; i++) {
+ if (!mob[i].isBadTarget &&
+ !mob[i].isInvulnerable &&
+ Vector.magnitude(Vector.sub(m.pos, mob[i].position)) < range &&
+ Matter.Query.ray(map, m.pos, mob[i].position).length === 0
+ ) {
+ m.energy -= 0.1
+ if (m.fieldCDcycle < m.cycle + 30) m.fieldCDcycle = m.cycle + 30
+ const angle = Math.atan2(mob[i].position.y - player.position.y, mob[i].position.x - player.position.x);
+ b.harpoon(m.pos, mob[i], angle, 0.75, true, 20) // harpoon(where, target, angle = m.angle, harpoonSize = 1, isReturn = false, totalCycles = 35, isReturnAmmo = true, thrust = 0.1) {
+ bullet[bullet.length - 1].drain = 0
+ const maxCount = 6
+ for (let j = maxCount - 1; j > 0; j--) {
+ b.harpoon(m.pos, mob[i], angle + j * 2 * Math.PI / maxCount, 0.75, true, 10)
+ bullet[bullet.length - 1].drain = 0
+ }
+ break
+ }
+ }
+ ctx.beginPath();
+ ctx.arc(m.pos.x, m.pos.y, range, 0, 2 * Math.PI);
+ ctx.strokeStyle = "#000";
+ ctx.lineWidth = 0.25;
+ ctx.setLineDash([10, 30]);
+ ctx.stroke();
+ ctx.setLineDash([]);
+ }
+ }
+ m.drawRegenEnergy()
+ //look for nearby mobs and fire harpoons at them
+ }
}
},
-
- // rewind: function() {
- // if (input.down) {
- // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed
- // const DRAIN = 0.01
- // if (this.rewindCount < 289 && m.energy > DRAIN) {
- // m.energy -= DRAIN
-
-
- // if (this.rewindCount === 0) {
- // const shortPause = function() {
- // if (m.defaultFPSCycle < m.cycle) { //back to default values
- // simulation.fpsCap = simulation.fpsCapDefault
- // simulation.fpsInterval = 1000 / simulation.fpsCap;
- // // document.getElementById("dmg").style.transition = "opacity 1s";
- // // document.getElementById("dmg").style.opacity = "0";
- // } else {
- // requestAnimationFrame(shortPause);
- // }
- // };
- // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause);
- // simulation.fpsCap = 4 //1 is longest pause, 4 is standard
- // simulation.fpsInterval = 1000 / simulation.fpsCap;
- // m.defaultFPSCycle = m.cycle
- // }
-
-
- // this.rewindCount += 10;
- // simulation.wipe = function() { //set wipe to have trails
- // // ctx.fillStyle = "rgba(255,255,255,0)";
- // ctx.fillStyle = `rgba(221,221,221,${0.004})`;
- // ctx.fillRect(0, 0, canvas.width, canvas.height);
- // }
- // let history = m.history[(m.cycle - this.rewindCount) % 300]
- // Matter.Body.setPosition(player, history.position);
- // Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y });
- // if (history.health > m.health) {
- // m.health = history.health
- // m.displayHealth();
- // }
- // //grab power ups
- // for (let i = 0, len = powerUp.length; i < len; ++i) {
- // const dxP = player.position.x - powerUp[i].position.x;
- // const dyP = player.position.y - powerUp[i].position.y;
- // if (dxP * dxP + dyP * dyP < 50000 && !simulation.isChoosing && !(m.health === m.maxHealth && powerUp[i].name === "heal")) {
- // powerUps.onPickUp(player.position);
- // powerUp[i].effect();
- // Matter.Composite.remove(engine.world, powerUp[i]);
- // powerUp.splice(i, 1);
- // const shortPause = function() {
- // if (m.defaultFPSCycle < m.cycle) { //back to default values
- // simulation.fpsCap = simulation.fpsCapDefault
- // simulation.fpsInterval = 1000 / simulation.fpsCap;
- // // document.getElementById("dmg").style.transition = "opacity 1s";
- // // document.getElementById("dmg").style.opacity = "0";
- // } else {
- // requestAnimationFrame(shortPause);
- // }
- // };
- // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause);
- // simulation.fpsCap = 3 //1 is longest pause, 4 is standard
- // simulation.fpsInterval = 1000 / simulation.fpsCap;
- // m.defaultFPSCycle = m.cycle
- // break; //because the array order is messed up after splice
- // }
- // }
- // m.immuneCycle = m.cycle + 5; //player is immune to damage for 30 cycles
- // } else {
- // m.fieldCDcycle = m.cycle + 30;
- // // m.resetHistory();
- // }
- // } else {
- // if (this.rewindCount !== 0) {
- // m.fieldCDcycle = m.cycle + 30;
- // m.resetHistory();
- // this.rewindCount = 0;
- // simulation.wipe = function() { //set wipe to normal
- // ctx.clearRect(0, 0, canvas.width, canvas.height);
- // }
- // }
- // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
- // }
- // }
- // m.drawRegenEnergy()
- // },
- },
- {
- name: "grappling hook",
- description: `use energy to fire a hook that pulls you
0.5x damage taken
9 energy per second`,
- effect: () => {
- m.fieldFire = true;
- // m.holdingMassScale = 0.01; //can hold heavier blocks with lower cost to jumping
- // m.fieldMeterColor = "#789"//"#456"
- m.eyeFillColor = m.fieldMeterColor
- m.fieldHarmReduction = 0.5; //40% reduction
- m.grabPowerUpRange2 = 300000 //m.grabPowerUpRange2 = 200000;
-
- m.hold = function () {
- if (m.isHolding) {
- m.drawHold(m.holdingTarget);
- m.holding();
- m.throwBlock();
- } else if (input.field) {
- m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
- if (m.fieldCDcycle < m.cycle) {
- if (m.energy > 0.02) m.energy -= 0.02
- b.grapple({ x: m.pos.x + 40 * Math.cos(m.angle), y: m.pos.y + 40 * Math.sin(m.angle) }, m.angle)
- if (m.fieldCDcycle < m.cycle + 20) m.fieldCDcycle = m.cycle + 20
- }
- m.grabPowerUp();
- } else {
- m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
- if (tech.isHookDefense && m.energy > 0.15 && m.fieldCDcycle < m.cycle) {
- const range = 300
- for (let i = 0; i < mob.length; i++) {
- if (!mob[i].isBadTarget &&
- !mob[i].isInvulnerable &&
- Vector.magnitude(Vector.sub(m.pos, mob[i].position)) < range &&
- Matter.Query.ray(map, m.pos, mob[i].position).length === 0
- ) {
- m.energy -= 0.1
- if (m.fieldCDcycle < m.cycle + 30) m.fieldCDcycle = m.cycle + 30
- const angle = Math.atan2(mob[i].position.y - player.position.y, mob[i].position.x - player.position.x);
- b.harpoon(m.pos, mob[i], angle, 0.75, true, 20) // harpoon(where, target, angle = m.angle, harpoonSize = 1, isReturn = false, totalCycles = 35, isReturnAmmo = true, thrust = 0.1) {
- bullet[bullet.length - 1].drain = 0
- const maxCount = 6
- for (let j = maxCount - 1; j > 0; j--) {
- b.harpoon(m.pos, mob[i], angle + j * 2 * Math.PI / maxCount, 0.75, true, 10)
- bullet[bullet.length - 1].drain = 0
- }
- break
- }
- }
- ctx.beginPath();
- ctx.arc(m.pos.x, m.pos.y, range, 0, 2 * Math.PI);
- ctx.strokeStyle = "#000";
- ctx.lineWidth = 0.25;
- ctx.setLineDash([10, 30]);
- ctx.stroke();
- ctx.setLineDash([]);
- }
- }
- m.drawRegenEnergy()
- //look for nearby mobs and fire harpoons at them
- }
- }
- },
],
//************************************************************************************
//************************************************************************************
diff --git a/js/powerup.js b/js/powerup.js
index b7722bc..559f5a7 100644
--- a/js/powerup.js
+++ b/js/powerup.js
@@ -1653,6 +1653,7 @@ const powerUps = {
if (b.inventory.length === 0) {
powerUps.spawn(x, y, "gun", false); //first gun
} else if (tech.totalCount === 0) { //first tech
+ powerUps.spawn(x - 22, y - 50, "ammo", false); //some ammo
powerUps.spawn(x, y, "tech", false);
} else if (b.inventory.length === 1) { //second gun or extra ammo
if (Math.random() < 0.4) {
diff --git a/js/simulation.js b/js/simulation.js
index df22584..ae76c3c 100644
--- a/js/simulation.js
+++ b/js/simulation.js
@@ -987,7 +987,7 @@ const simulation = {
} else {
Composite.add(engine.world, [player])
}
- shuffle(level.constraint)
+ seededShuffle(level.constraint)
level.populateLevels()
input.endKeySensing();
simulation.ephemera = []
diff --git a/js/spawn.js b/js/spawn.js
index 4f743bc..0aa972a 100644
--- a/js/spawn.js
+++ b/js/spawn.js
@@ -745,7 +745,7 @@ const spawn = {
// exit() { },
// },
]
- shuffle(me.mode); //THIS SHOULD NOT BE COMMENTED OUT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ me.mode.sort(() => Math.random() - 0.5);
me.do = function () {
this.fill = `hsl(${360 * Math.sin(this.cycle * 0.011)},${80 + 20 * Math.sin(this.cycle * 0.004)}%,${60 + 20 * Math.sin(this.cycle * 0.009)}%)`
if (this.health < 1) {
@@ -1771,6 +1771,7 @@ const spawn = {
powerUps.spawn(me.position.x, me.position.y, "heal");
powerUps.spawn(me.position.x, me.position.y, "ammo");
powerUps.spawn(me.position.x, me.position.y, "ammo");
+ powerUps.spawn(me.position.x, me.position.y, "ammo");
} else if (!m.isCloak) {
me.foundPlayer();
}
@@ -2683,22 +2684,24 @@ const spawn = {
this.seePlayerByHistory()
this.invulnerabilityCountDown--
if (this.isInvulnerable) {
- ctx.beginPath();
- let vertices = this.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);
- for (let i = 0; i < this.babyList.length; i++) {
- if (this.babyList[i].alive) {
- let vertices = this.babyList[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);
+ if (this.invulnerabilityCountDown > 90 || this.invulnerabilityCountDown % 20 > 10) {
+ ctx.beginPath();
+ let vertices = this.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);
+ for (let i = 0; i < this.babyList.length; i++) {
+ if (this.babyList[i].alive) {
+ let vertices = this.babyList[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);
+ }
}
+ ctx.lineWidth = 3 + 0.2 * Math.min(this.invulnerabilityCountDown, 90) + 5 * Math.random()
+ ctx.strokeStyle = `rgba(255,255,255,${0.4 + 0.4 * Math.random()})`;
+ ctx.stroke();
}
- ctx.lineWidth = 13 + 5 * Math.random();
- ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`;
- ctx.stroke();
if (this.invulnerabilityCountDown < 0) {
this.invulnerabilityCountDown = 110
this.isInvulnerable = false
@@ -2711,7 +2714,7 @@ const spawn = {
}
}
} else if (this.invulnerabilityCountDown < 0) {
- this.invulnerabilityCountDown = 120 + 9 * simulation.difficulty
+ this.invulnerabilityCountDown = 240 + 5 * simulation.difficulty
this.isInvulnerable = true
if (this.damageReduction) this.startingDamageReduction = this.damageReduction
this.damageReduction = 0
diff --git a/js/tech.js b/js/tech.js
index 954987c..59d5ea4 100644
--- a/js/tech.js
+++ b/js/tech.js
@@ -271,7 +271,6 @@ const tech = {
if (tech.isImmunityDamage && m.immuneCycle > m.cycle) dmg *= 3
if (tech.isPowerUpDamage) dmg *= 1 + 0.07 * powerUp.length
if (tech.isDamageCooldown) dmg *= m.lastKillCycle + tech.isDamageCooldownTime > m.cycle ? 0.4 : 4
- if (tech.isDamageAfterKillNoRegen && m.lastKillCycle + 300 > m.cycle) dmg *= 2
if (tech.isDivisor && b.activeGun !== undefined && b.activeGun !== null && b.guns[b.activeGun].ammo % 3 === 0) dmg *= 1.9
if (tech.offGroundDamage && !m.onGround) dmg *= tech.offGroundDamage
if (tech.isDilate) dmg *= 1.9 + 1.1 * Math.sin(m.cycle * 0.01)
@@ -1018,7 +1017,7 @@ const tech = {
{
name: "Pareto efficiency",
descriptionFunction() {
- return `all you ${powerUps.orb.gun()} randomly get
5x or 0.2x ammo per ${powerUps.orb.ammo(1)}`
+ return `all your ${powerUps.orb.gun()} randomly get
5x or 0.2x ammo per ${powerUps.orb.ammo(1)}`
},
maxCount: 1,
count: 0,
@@ -1033,7 +1032,7 @@ const tech = {
let options = []
for (let i = 0; i < b.inventory.length; i++) options.push(b.inventory[i])
- options = shuffle(options)
+ options.sort(() => Math.random() - 0.5);
for (let i = 0; i < options.length; i++) {
const index = options[i]
const scale = (i < options.length / 2) ? 4 : 0.25
@@ -2758,7 +2757,7 @@ const tech = {
},
{
name: "Pauli exclusion",
- description: `for 7 seconds after mob collisions
become invulnerable and inhibit energy regen`,
+ description: `for 7 seconds after mob collisions
gain invulnerbility and blocked energy regen`,
maxCount: 9,
count: 0,
frequency: 1,
@@ -2777,7 +2776,7 @@ const tech = {
},
{
name: "spin-statistics theorem",
- description: `for 1.9 seconds out of every 7 seconds
become invulnerable and inhibit energy regen`,
+ description: `for 1.9 seconds out of every 7 seconds
gain invulnerbility and blocked energy regen`,
maxCount: 3,
count: 0,
frequency: 1,
@@ -2795,7 +2794,7 @@ const tech = {
},
{
name: "fermion",
- description: `for 5 seconds after mobs die
become invulnerable and inhibit energy regen`,
+ description: `if a mob has died in the last 5 seconds
gain invulnerbility and blocked energy regen`,
maxCount: 1,
count: 0,
frequency: 1,
@@ -3009,9 +3008,9 @@ const tech = {
frequency: 1,
frequencyDefault: 1,
allowed() {
- return !tech.isDamageAfterKillNoRegen
+ return true
},
- requires: "not parasitism",
+ requires: "",
effect() {
tech.isCrouchRegen = true; //only used to check for requirements
m.regenEnergy = function () {
@@ -3042,29 +3041,6 @@ const tech = {
tech.energySiphon = 0;
}
},
- {
- name: "parasitism",
- description: "if a mob has died in the last 5 seconds
2x damage, no passive energy generation",
- maxCount: 1,
- count: 0,
- frequency: 1,
- frequencyDefault: 1,
- allowed() {
- return !tech.isCrouchRegen
- },
- requires: "not inductive charging",
- effect() {
- tech.isDamageAfterKillNoRegen = true;
- m.regenEnergy = function () {
- if (m.immuneCycle < m.cycle && (m.lastKillCycle + 300 < m.cycle) && m.fieldCDcycle < m.cycle) m.energy += m.fieldRegen * level.isReducedRegen;
- if (m.energy < 0) m.energy = 0
- }
- },
- remove() {
- if (this.count) m.regenEnergy = m.regenEnergyDefault
- tech.isDamageAfterKillNoRegen = false;
- }
- },
{
name: "waste heat recovery",
description: "if a mob has died in the last 5 seconds
generate 0.05x maximum energy every second",
@@ -4769,7 +4745,7 @@ const tech = {
for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups
if (tech.tech[i].count && !tech.tech[i].isInstant) pool.push(i)
}
- pool = shuffle(pool); //shuffles order of maps
+ pool.sort(() => Math.random() - 0.5);
let removeCount = 0
for (let i = 0, len = pool.length * 0.5; i < len; i++) removeCount += tech.removeTech(pool[i])
this.damage = this.damagePerRemoved * removeCount
@@ -8965,22 +8941,31 @@ const tech = {
},
{
name: "invariant",
- description: "while placing your wormhole
use energy to pause time",
+ cost: 1,
+ descriptionFunction() {
+ return `use ${powerUps.orb.research(this.cost)}
pause time while placing your wormhole`
+ },
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
- return m.fieldMode === 9 && !tech.isNoDraftPause
+ return m.fieldMode === 9 && !tech.isNoDraftPause && (build.isExperimentSelection || powerUps.research.count > this.cost - 1)
},
requires: "wormhole, not eternalism",
effect() {
tech.isWormHolePause = true
+ for (let i = 0; i < this.cost; i++) {
+ if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1)
+ }
},
remove() {
if (tech.isWormHolePause && m.isTimeDilated) m.wakeCheck();
tech.isWormHolePause = false
+ if (this.count) {
+ powerUps.research.changeRerolls(this.cost)
+ }
}
},
{
@@ -9031,7 +9016,7 @@ const tech = {
allowed() {
return m.fieldMode === 9 && !tech.isFreeWormHole
},
- requires: "wormhole, not charmed baryons",
+ requires: "wormhole, not holographic principle",
effect() {
tech.isWormholeMapIgnore = true
},
@@ -10044,8 +10029,8 @@ const tech = {
},
requires: "",
effect() {
- // const colors = shuffle(["#f7b", "#0eb", "#467", "#0cf", "hsl(246,100%,77%)", "#26a"])
- const colors = shuffle([powerUps.research.color, powerUps.heal.color, powerUps.ammo.color, powerUps.ammo.color, powerUps.field.color, powerUps.gun.color])
+ const colors = [powerUps.research.color, powerUps.heal.color, powerUps.ammo.color, powerUps.ammo.color, powerUps.field.color, powerUps.gun.color]
+ colors.sort(() => Math.random() - 0.5);
powerUps.research.color = colors[0]
powerUps.heal.color = colors[1]
powerUps.ammo.color = colors[2]
@@ -10075,37 +10060,7 @@ const tech = {
}
}
},
- remove() {
- // const colors = ["#f7b", "#0eb", "#467", "#0cf", "hsl(246,100%,77%)", "#26a"] //no shuffle
- // powerUps.research.color = colors[0]
- // powerUps.heal.color = colors[1]
- // powerUps.ammo.color = colors[2]
- // powerUps.field.color = colors[3]
- // powerUps.tech.color = colors[4]
- // powerUps.gun.color = colors[5]
- // for (let i = 0; i < powerUp.length; i++) {
- // switch (powerUp[i].name) {
- // case "research":
- // powerUp[i].color = colors[0]
- // break;
- // case "heal":
- // powerUp[i].color = colors[1]
- // break;
- // case "ammo":
- // powerUp[i].color = colors[2]
- // break;
- // case "field":
- // powerUp[i].color = colors[3]
- // break;
- // case "tech":
- // powerUp[i].color = colors[4]
- // break;
- // case "gun":
- // powerUp[i].color = colors[5]
- // break;
- // }
- // }
- }
+ remove() { }
},
{
name: "emergency broadcasting",
@@ -10519,7 +10474,7 @@ const tech = {
},
requires: "",
effect() {
- String.prototype.shuffle = function () {
+ String.prototype.shuf = function () {
var a = this.split(""),
n = a.length;
@@ -10532,7 +10487,7 @@ const tech = {
return a.join("");
}
- for (let i = 0, len = tech.tech.length; i < len; i++) tech.tech[i].name = tech.tech[i].name.shuffle()
+ for (let i = 0, len = tech.tech.length; i < len; i++) tech.tech[i].name = tech.tech[i].name.shuf()
},
remove() { }
},
@@ -12190,7 +12145,6 @@ const tech = {
isDuplicateMobs: null,
isDynamoBotUpgrade: null,
isBlockPowerUps: null,
- isDamageAfterKillNoRegen: null,
isHarmReduceNoKill: null,
isSwitchReality: null,
isResearchReality: null,
diff --git a/todo.txt b/todo.txt
index 2802010..4dc0196 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,98 +1,77 @@
******************************************************** NEXT PATCH **************************************************
-tech: demineralization - after mobs die gain 0.85x damage taken, effect stacks, but fades 10% every second
-tech: remineralization - after mobs die gain 1.08x damage, effect stacks, but fades 10% every second
-tech: equivalence principle - negative mass field doesn't cost energy
-new JUNK tech: aerodynamics
-
-interferometer
- slower elevator and lasers
- wider side ledges
- large laser blocking blocks
-flocculation
- fewer mobs
- it's easier to get out of the slime
-pavilion
- move vanish elements
- easier traversal
- secret tunnel
- removed debris, but added power ups and blocks
-corridor
- limited to bosses that don't interact with the movers poorly
-gravitron, substructure, corridor, interferometer
- added more heal and ammo power ups to match other levels
-because some newer levels are zoomed out more
- laser max range is 3000->5000
- nails last 1/3 of a second longer
-bosses spawn an extra ammo power up
- and 2 extra ammo on the hardest 2 difficulties
-slasher mob's laserSwords will now damage a cloaked player
-constraint announcement text looks more like computer code style to match game theme
-
-foam recoil is back: 1->0.7x horizontal force and 2->4.3x vertical up force
- this makes it less annoying to horizontally and easier to kinda fly/float
-negative mass field damage reduction 0.4->0.5x
-holographic principle no longer slows player movement
- added 2 research cost
-fermion gives 6->5 seconds of invulnerability after mobs die
-stability 0.2->0.1x damage taken at max health
-non-Newtonian armor 0.3->0.4x damage taken after collisions
-Zeno's paradox 0.15->0.2x damage taken
-annihilation energy cost 10->8 to destroy mobs after collisions
-radiative equilibrium damage is 3->4x for 8->4 seconds
-aerostat can be taken 1->3 times
-dynamic equilibrium damage increased by 6->8x damage per last damage taken
-aerostat no longer has 0.9x damage for being on the ground
-launch system 1.2->1.3x ammo for missiles
-research says that repeatedly entering alternate realities builds up some positive effects
- Hilbert space 4x->3x damage
- Ψ(t) collapse 6->4 research on boss death
-transdimensional worms: 50% chance for a second worm per block in wormhole
-wormhole 7->8 energy regen per second
-hidden-variable theory 1.15->1.2 damage after choosing a field tech
-ghoster mobs are less likely to get knocked far away from the player for long periods of time
+mantisBoss flashes for a second before it drops invulnerability
+removed parasitism - it's too similar to invulnerability tech
+invariant no longer drains energy while wormhole time is paused
+ added 1 research cost
+added secret combo to change molecular assembler mode
bug fixes
- dynamic equilibrium was set to 100 times higher frequency then normal
- when constraints hide health bar's it's now hidden in the pause menu
- mobs aiming at cloaked player
- snakeBoss more intelligently chases player for a few seconds
- pulsarBoss aims at player's history 3 seconds in past
- pulsar will not stop firing
- but it will still not fire at cloaked player
+ issue with constraint: "mob death heals mobs"
+ mob health was becoming NaN, this was infecting other values like player energy
+ entering a seed in settings wasn't giving the same results as a randomly generated seeds
+ also removed some random code that was using seeded shuffle, but didn't need to
+ converted it to non seeded random shuffle with .sort(() => Math.random() - 0.5);
******************************************************** BUGS ********************************************************
-figure out why seeded random isn't making runs the same:
- shuffle is being used for a wide variety of things that don't need a seeded random
- make two shuffle functions?
- check which of these is working
- level order
- is flipped
- constraint order
- mob type order
- boss order
- gun, field, tech options
- make field options offered precalculated so it doesn't depend on player choices?
- generate all constraints at the start of the game
- doesn't seem to be determined by the seed...
- display future constraints in pause menu?
-
player can become crouched while not touching the ground if they exit the ground while crouched
*********************************************************** TODO *****************************************************
+
+use ←↑→↓↖↗↘↙ combos to allow fields to have special actions
+ !!should this be wasd, arrows, or both?
+ how to limit spam?
+ on cooldown
+ timer or once per level
+ energy cost
+ research cost
+ toggle modes
+ combos are listed in white text in field description, and console message when getting field
+ shows up on highlight, and mouse over in experiment mode
+ field ideas:
+ standing wave
+ push mobs away are any distance
+ pull mobs towards you
+ perfect diamagnetism
+ negative mass
+ molecular assembler - done
+ plasma torch
+ time dilation
+ metamaterial cloaking
+ pilot wave
+ spawn blocks
+ wormhole
+ shoot out all the blocks that were sucked in this level (maybe cap at like 10?, cap with energy spent to fire)
+ are block sizes stored properly? because they shrink before they get eaten...
+ store vertices on the body object if one does already exists
+ after eating store the saved vertices
+ where to fire blocks from and in what direction?
+ target mobs?
+ fire from player (and draw a wormhole looking graphic)
+ grappling hook
+
+new level idea: escort mission
+ player has to stay near something that moves slowly through the level
+ maybe only a zone around the escort is safe
+ safe from?
+ lasers
+ slime
+ radiation
+ use line of site vision?
+ is it going to feel too slow?
+ where to put in level order?
+ random option instead of reactor?
+ normal level?
+ 4th hard level?
+ already too many hard options
+ you could put in 2 hard levels in the level list
+ make the style look like substructure, that level looks great
+ use the motion sense lasers
+
tech synergy ideas
a tech that spawns mobs that the player can use to trigger mob death tech
-
-wormhole field needs a buff
- it's good on wide open maps, but it needs a defensive effect on more closed maps
- fix?
- increase energy regen?
- new tech?
- a way to make more blocks
- the block can feed into worms/coupling energy
-
tech: - remove the research costs of all tech
there's about 15 tech with a research cost