sniper mob balance, bot replication mod

This commit is contained in:
landgreen
2020-05-24 18:45:53 -07:00
parent e265b0aade
commit f338184869
7 changed files with 254 additions and 49 deletions

View File

@@ -2077,7 +2077,7 @@ const b = {
isEasyToAim: true, isEasyToAim: true,
fire() { fire() {
if (mech.crouch) { if (mech.crouch) {
b.iceIX(20, 0.3) b.iceIX(10, 0.3)
mech.fireCDcycle = mech.cycle + Math.floor(10 * mod.fireRate); // cool down mech.fireCDcycle = mech.cycle + Math.floor(10 * mod.fireRate); // cool down
} else { } else {
b.iceIX(2) b.iceIX(2)
@@ -2160,13 +2160,23 @@ const b = {
bullet[me].endCycle = Infinity bullet[me].endCycle = Infinity
bullet[me].charge = 0; bullet[me].charge = 0;
bullet[me].do = function () { bullet[me].do = function () {
if ((!game.mouseDown && this.charge > 0.6)) { //fire on mouse release if ((!game.mouseDown && this.charge > 0.6) || mech.energy < 0.005) { //fire on mouse release
if (mech.energy < 0.005) {
this.charge = 0.1;
mech.fireCDcycle = mech.cycle + 120; // cool down if out of energy
//normal bullet behavior occurs after firing, overwrite this function
this.do = function () {
this.force.y += this.mass * 0.001; //normal gravity
}
} else {
mech.fireCDcycle = mech.cycle + 2; // set fire cool down
//normal bullet behavior occurs after firing, overwrite this function //normal bullet behavior occurs after firing, overwrite this function
this.do = function () { this.do = function () {
this.force.y += this.mass * 0.0003 / this.charge; // low gravity that scales with charge this.force.y += this.mass * 0.0003 / this.charge; // low gravity that scales with charge
} }
}
mech.fireCDcycle = mech.cycle + 2; // set fire cool down
Matter.Body.scale(this, 8000, 8000) // show the bullet by scaling it up (don't judge me... I know this is a bad way to do it) Matter.Body.scale(this, 8000, 8000) // show the bullet by scaling it up (don't judge me... I know this is a bad way to do it)
this.endCycle = game.cycle + 140 this.endCycle = game.cycle + 140
this.collisionFilter.category = cat.bullet this.collisionFilter.category = cat.bullet

View File

@@ -136,6 +136,24 @@ function collisionChecks(event) {
function collideMob(obj) { function collideMob(obj) {
//player + mob collision //player + mob collision
if (mech.immuneCycle < mech.cycle && (obj === playerBody || obj === playerHead)) { if (mech.immuneCycle < mech.cycle && (obj === playerBody || obj === playerHead)) {
// const a = Object.values(event.pairs[0].contacts)
// contains = Matter.Bounds.contains({
// max: {
// x: player.position.x + 60,
// y: player.position.y + 120
// },
// min: {
// x: player.position.x - 60,
// y: player.position.y + 40
// }
// }, {
// x: a[0].vertex.x,
// y: a[0].vertex.y
// })
// // Matter.Query.point([jumpSensor], point)
// console.log(contains)
// if (!contains) {
mech.immuneCycle = mech.cycle + mod.collisionImmuneCycles; //player is immune to collision damage for 30 cycles mech.immuneCycle = mech.cycle + mod.collisionImmuneCycles; //player is immune to collision damage for 30 cycles
mob[k].foundPlayer(); mob[k].foundPlayer();
let dmg = Math.min(Math.max(0.025 * Math.sqrt(mob[k].mass), 0.05), 0.3) * game.dmgScale; //player damage is capped at 0.3*dmgScale of 1.0 let dmg = Math.min(Math.max(0.025 * Math.sqrt(mob[k].mass), 0.05), 0.3) * game.dmgScale; //player damage is capped at 0.3*dmgScale of 1.0
@@ -179,6 +197,7 @@ function collisionChecks(event) {
} }
return; return;
// }
} }
//mob + bullet collisions //mob + bullet collisions
if (obj.classType === "bullet" && obj.speed > obj.minDmgSpeed) { if (obj.classType === "bullet" && obj.speed > obj.minDmgSpeed) {

View File

@@ -23,8 +23,8 @@ const level = {
// mech.setField("pilot wave") // mech.setField("pilot wave")
// mech.setField("phase decoherence field") // mech.setField("phase decoherence field")
level.intro(); //starting level // level.intro(); //starting level
// level.testing(); level.testing();
// level.stronghold() // level.stronghold()
// level.bosses(); // level.bosses();
// level.satellite(); // level.satellite();
@@ -183,9 +183,10 @@ const level = {
spawn.boost(1500, 0, 900); spawn.boost(1500, 0, 900);
// spawn.bomberBoss(2900, -500) // spawn.bomberBoss(2900, -500)
spawn.launcherBoss(1200, -500) // spawn.launcherBoss(1200, -500)
spawn.launcher(1600, -400) // spawn.sniper(1600, -400)
// spawn.spawner(1600, -500) // spawn.sneaker(1600, -500)
spawn.sniper(1700, -120)
// spawn.cellBossCulture(1600, -500) // spawn.cellBossCulture(1600, -500)
// spawn.shooter(1600, -500) // spawn.shooter(1600, -500)
// spawn.striker(1600, -500) // spawn.striker(1600, -500)

View File

@@ -330,7 +330,7 @@ const mod = {
maxCount: 6, maxCount: 6,
count: 0, count: 0,
allowed() { allowed() {
return mod.foamBotCount > 0 || mod.nailBotCount > 0 || mod.laserBotCount > 0 return mod.foamBotCount + mod.nailBotCount + mod.laserBotCount > 0
}, },
requires: "a bot", requires: "a bot",
effect() { effect() {
@@ -340,6 +340,38 @@ const mod = {
mod.isBotSpawner = 0; mod.isBotSpawner = 0;
} }
}, },
{
name: "self-replication",
description: "<strong>duplicate</strong> your permanent <strong>bots</strong><br>remove all your <strong>ammo</strong>",
maxCount: 1,
count: 0,
isNonRefundable: true,
allowed() {
return mod.foamBotCount + mod.nailBotCount + mod.laserBotCount > 1
},
requires: "2 or more bots",
effect() {
//remove ammo
for (let i = 0, len = b.guns.length; i < len; ++i) {
if (b.guns[i].ammo != Infinity) b.guns[i].ammo = 0;
}
//double bots
for (let i = 0; i < mod.nailBotCount; i++) {
b.nailBot();
}
mod.nailBotCount *= 2
for (let i = 0; i < mod.laserBotCount; i++) {
b.laserBot();
}
mod.laserBotCount *= 2
for (let i = 0; i < mod.foamBotCount; i++) {
b.foamBot();
}
mod.foamBotCount *= 2
},
remove() {}
},
{ {
name: "ablative mines", name: "ablative mines",
description: "rebuild your broken parts as a <strong>mine</strong><br>chance to occur after being <strong>harmed</strong>", description: "rebuild your broken parts as a <strong>mine</strong><br>chance to occur after being <strong>harmed</strong>",
@@ -769,6 +801,7 @@ const mod = {
description: "spawn <strong>5</strong> <strong class='color-m'>mods</strong><br><strong>power ups</strong> are limited to <strong>one choice</strong>", description: "spawn <strong>5</strong> <strong class='color-m'>mods</strong><br><strong>power ups</strong> are limited to <strong>one choice</strong>",
maxCount: 1, maxCount: 1,
count: 0, count: 0,
// isNonRefundable: true,
allowed() { allowed() {
return !mod.isExtraChoice return !mod.isExtraChoice
}, },
@@ -786,7 +819,7 @@ const mod = {
}, },
{ {
name: "many-worlds", name: "many-worlds",
description: "if you have <strong>zero</strong> <strong class='color-r'>rerolls</strong>, spawn a <strong class='color-r'>reroll</strong><br>after choosing a <strong>gun</strong>, <strong>field</strong>, or <strong class='color-m'>mod</strong>", description: "after choosing a <strong>gun</strong>, <strong>field</strong>, or <strong class='color-m'>mod</strong><br>spawn a <strong class='color-r'>reroll</strong>, if you have none",
maxCount: 1, maxCount: 1,
count: 0, count: 0,
allowed() { allowed() {
@@ -844,6 +877,7 @@ const mod = {
description: "<strong>remove</strong> all current <strong class='color-m'>mods</strong><br>spawn new <strong class='color-m'>mods</strong> to replace them", description: "<strong>remove</strong> all current <strong class='color-m'>mods</strong><br>spawn new <strong class='color-m'>mods</strong> to replace them",
maxCount: 1, maxCount: 1,
count: 0, count: 0,
isNonRefundable: true,
allowed() { allowed() {
return (mod.totalCount > 6) && !build.isCustomSelection return (mod.totalCount > 6) && !build.isCustomSelection
}, },
@@ -853,23 +887,22 @@ const mod = {
for (let i = 0; i < bullet.length; ++i) Matter.World.remove(engine.world, bullet[i]); for (let i = 0; i < bullet.length; ++i) Matter.World.remove(engine.world, bullet[i]);
bullet = []; bullet = [];
let count = mod.totalCount let count = mod.totalCount + 1
if (mod.isDeterminism) count -= 4 //remove the 5 bonus mods when getting rid of determinism if (mod.isDeterminism) count -= 5 //remove the 5 bonus mods when getting rid of determinism
for (let i = 0; i < count; i++) { // spawn new mods mod.setupAllMods(); // remove all mods
for (let i = 0; i < count; i++) { // spawn new mods power ups
powerUps.spawn(mech.pos.x, mech.pos.y, "mod"); powerUps.spawn(mech.pos.x, mech.pos.y, "mod");
} }
mod.setupAllMods(); // remove all mods
//have state is checked in mech.death() //have state is checked in mech.death()
}, },
remove() { remove() {}
//nothing to undo
}
}, },
{ {
name: "reallocation", name: "reallocation",
description: "convert <strong>1</strong> random <strong class='color-m'>mod</strong> into <strong>2</strong> new <strong>guns</strong><br><em>recursive mods lose all stacks</em>", description: "convert <strong>1</strong> random <strong class='color-m'>mod</strong> into <strong>2</strong> new <strong>guns</strong><br><em>recursive mods lose all stacks</em>",
maxCount: 1, maxCount: 1,
count: 0, count: 0,
isNonRefundable: true,
allowed() { allowed() {
return (mod.totalCount > 0) && !build.isCustomSelection return (mod.totalCount > 0) && !build.isCustomSelection
}, },
@@ -889,9 +922,7 @@ const mod = {
if (Math.random() < mod.bayesian) powerUps.spawn(mech.pos.x, mech.pos.y, "gun"); if (Math.random() < mod.bayesian) powerUps.spawn(mech.pos.x, mech.pos.y, "gun");
} }
}, },
remove() { remove() {}
//nothing to remove
}
}, },
//************************************************** //**************************************************
//************************************************** gun //************************************************** gun
@@ -1178,7 +1209,7 @@ const mod = {
} }
}, },
{ {
name: "self-replication", name: "recursion",
description: "after <strong>missiles</strong> <strong class='color-e'>explode</strong><br>they launch <strong>+1</strong> smaller <strong>missile</strong>", description: "after <strong>missiles</strong> <strong class='color-e'>explode</strong><br>they launch <strong>+1</strong> smaller <strong>missile</strong>",
maxCount: 9, maxCount: 9,
count: 0, count: 0,
@@ -1674,7 +1705,7 @@ const mod = {
}, },
{ {
name: "renormalization", name: "renormalization",
description: "<strong>phase decoherence</strong> has increased <strong>visibility</strong><br>and <strong>5x</strong> less <strong class='color-f'>energy</strong> drain when <strong>firing</strong>", description: "<strong>phase decoherence</strong> adds <strong>visibility</strong> to bullets<br><strong>5x</strong> less <strong class='color-f'>energy</strong> drain when <strong>firing</strong>",
maxCount: 1, maxCount: 1,
count: 0, count: 0,
allowed() { allowed() {
@@ -1691,7 +1722,7 @@ const mod = {
{ {
name: "superposition", name: "superposition",
// description: "<strong>phase decoherence field</strong> applies a <strong>stun</strong><br> to unshielded <strong>mobs</strong> for <strong>2</strong> seconds", // description: "<strong>phase decoherence field</strong> applies a <strong>stun</strong><br> to unshielded <strong>mobs</strong> for <strong>2</strong> seconds",
description: "apply a <strong>4</strong> second <strong>stun</strong> to unshielded <strong>mobs</strong><br>that <strong>overlap</strong> with <strong>phase decoherence field</strong>", description: "while <strong>phase decoherence field</strong> is active<br>mobs that <strong>overlap</strong> with the player are <strong>stunned</strong>",
maxCount: 1, maxCount: 1,
count: 0, count: 0,
allowed() { allowed() {

View File

@@ -372,11 +372,8 @@ const mech = {
let options = []; let options = [];
for (let i = 0, len = mod.mods.length; i < len; i++) { for (let i = 0, len = mod.mods.length; i < len; i++) {
if (mod.mods[i].count < mod.mods[i].maxCount && if (mod.mods[i].count < mod.mods[i].maxCount &&
!mod.mods[i].isNonRefundable &&
mod.mods[i].name !== "quantum immortality" && mod.mods[i].name !== "quantum immortality" &&
mod.mods[i].name !== "Born rule" &&
mod.mods[i].name !== "determinism" &&
mod.mods[i].name !== "reallocation" &&
mod.mods[i].name !== "many-worlds" &&
mod.mods[i].allowed() mod.mods[i].allowed()
) options.push(i); ) options.push(i);
} }

View File

@@ -2,25 +2,26 @@
const spawn = { const spawn = {
pickList: ["starter", "starter"], pickList: ["starter", "starter"],
fullPickList: [ fullPickList: [
"shooter", "shooter", "shooter", "shooter", // "hopper", "hopper", "hopper", "hopper",
"hopper", "hopper", "hopper", "hopper", // "shooter", "shooter", "shooter",
"chaser", "chaser", "chaser", // "chaser", "chaser",
"striker", "striker", // "striker", "striker",
"laser", "laser", // "laser", "laser",
"exploder", "exploder", // "exploder", "exploder",
"stabber", "stabber", // "stabber", "stabber",
"launcher", "launcher", // "launcher", "launcher",
"spinner", "sniper",
"grower", // "spinner",
"springer", // "grower",
"beamer", // "springer",
"focuser", // "beamer",
"sucker", // "focuser",
"spawner", // "sucker",
"ghoster", // "spawner",
"sneaker", // "ghoster",
// "sneaker",
], ],
allowedBossList: ["chaser", "spinner", "striker", "springer", "laser", "focuser", "beamer", "exploder", "spawner", "shooter", "launcher", "stabber"], allowedBossList: ["chaser", "spinner", "striker", "springer", "laser", "focuser", "beamer", "exploder", "spawner", "shooter", "launcher", "stabber", "sniper"],
setSpawnList() { //this is run at the start of each new level to determine the possible mobs for the level setSpawnList() { //this is run at the start of each new level to determine the possible mobs for the level
//each level has 2 mobs: one new mob and one from the last level //each level has 2 mobs: one new mob and one from the last level
spawn.pickList.splice(0, 1); spawn.pickList.splice(0, 1);
@@ -1353,7 +1354,8 @@ const spawn = {
shooter(x, y, radius = 25 + Math.ceil(Math.random() * 50)) { shooter(x, y, radius = 25 + Math.ceil(Math.random() * 50)) {
mobs.spawn(x, y, 3, radius, "rgb(255,100,150)"); mobs.spawn(x, y, 3, radius, "rgb(255,100,150)");
let me = mob[mob.length - 1]; let me = mob[mob.length - 1];
me.vertices = Matter.Vertices.clockwiseSort(Matter.Vertices.rotate(me.vertices, Math.PI, me.position)); //make the pointy side of triangle the front // me.vertices = Matter.Vertices.clockwiseSort(Matter.Vertices.rotate(me.vertices, Math.PI, me.position)); //make the pointy side of triangle the front
me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front
me.isVerticesChange = true me.isVerticesChange = true
// Matter.Body.rotate(me, Math.PI) // Matter.Body.rotate(me, Math.PI)
@@ -1437,6 +1439,146 @@ const spawn = {
this.timeLimit(); this.timeLimit();
}; };
}, },
sniper(x, y, radius = 35 + Math.ceil(Math.random() * 30)) {
mobs.spawn(x, y, 3, radius, "transparent"); //"rgb(25,0,50)")
let me = mob[mob.length - 1];
me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front
me.isVerticesChange = true
// Matter.Body.rotate(me, Math.PI)
me.stroke = "transparent"; //used for drawSneaker
me.alpha = 1; //used in drawSneaker
me.showHealthBar = false;
me.canTouchPlayer = false; //used in drawSneaker
me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player
me.memory = 60 //140;
me.fireFreq = 0.006 + Math.random() * 0.002;
me.noseLength = 0;
me.fireAngle = 0;
me.accelMag = 0.0005 * game.accelScale;
me.frictionAir = 0.05;
me.torque = 0.0001 * me.inertia;
me.fireDir = {
x: 0,
y: 0
};
me.onDeath = function () { //helps collisions functions work better after vertex have been changed
// this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices))
}
// spawn.shield(me, x, y);
me.do = function () {
// this.seePlayerByLookingAt();
this.seePlayerCheck();
this.checkStatus();
if (!mech.isBodiesAsleep) {
const setNoseShape = () => {
const mag = this.radius + this.radius * this.noseLength;
this.vertices[1].x = this.position.x + Math.cos(this.angle) * mag;
this.vertices[1].y = this.position.y + Math.sin(this.angle) * mag;
};
//throw a mob/bullet at player
if (this.seePlayer.recall) {
//set direction to turn to fire
if (!(game.cycle % this.seePlayerFreq)) {
this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position));
// this.fireDir.y -= Math.abs(this.seePlayer.position.x - this.position.x) / 1600; //gives the bullet an arc
}
//rotate towards fireAngle
const angle = this.angle + Math.PI / 2;
c = Math.cos(angle) * this.fireDir.x + Math.sin(angle) * this.fireDir.y;
const threshold = 0.2;
if (c > threshold) {
this.torque += 0.000004 * this.inertia;
} else if (c < -threshold) {
this.torque -= 0.000004 * this.inertia;
} else if (this.noseLength > 1.5) {
//fire
spawn.sniperBullet(this.vertices[1].x, this.vertices[1].y, 5 + Math.ceil(this.radius / 15), 4);
const v = 20 * game.accelScale;
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + this.fireDir.x * v + Math.random(),
y: this.velocity.y + this.fireDir.y * v + Math.random()
});
this.noseLength = 0;
// recoil
this.force.x -= 0.005 * this.fireDir.x * this.mass;
this.force.y -= 0.005 * this.fireDir.y * this.mass;
} else {
this.torque += 0.000001 * this.inertia; //
}
if (this.noseLength < 1.5) this.noseLength += this.fireFreq;
setNoseShape();
} else if (this.noseLength > 0.1) {
this.noseLength -= this.fireFreq / 2;
setNoseShape();
}
// else if (this.noseLength < -0.1) {
// this.noseLength += this.fireFreq / 4;
// setNoseShape();
// }
if (this.seePlayer.recall) {
if (this.alpha < 1) this.alpha += 0.01;
} else {
if (this.alpha > 0) this.alpha -= 0.03;
}
}
//draw
if (this.alpha > 0) {
if (this.alpha > 0.95) {
this.healthBar();
if (!this.canTouchPlayer) {
this.canTouchPlayer = true;
this.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob; //can touch player
}
}
//draw body
ctx.beginPath();
const vertices = this.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1, len = vertices.length; j < len; ++j) {
ctx.lineTo(vertices[j].x, vertices[j].y);
}
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.fillStyle = `rgba(25,0,50,${this.alpha * this.alpha})`;
ctx.fill();
} else if (this.canTouchPlayer) {
this.canTouchPlayer = false;
this.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player
}
};
},
sniperBullet(x, y, radius = 6, sides = 4) {
//bullets
mobs.spawn(x, y, sides, radius, "rgb(190,0,255)");
let me = mob[mob.length - 1];
me.stroke = "transparent";
me.onHit = function () {
this.explode(this.mass * 10);
};
Matter.Body.setDensity(me, 0.0001); //normal is 0.001
me.timeLeft = 240;
me.g = 0.001; //required if using 'gravity'
me.frictionAir = 0;
me.restitution = 0;
me.leaveBody = false;
me.dropPowerUp = false;
me.showHealthBar = false;
me.collisionFilter.category = cat.mobBullet;
me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet;
me.do = function () {
// this.gravity();
this.timeLimit();
if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0) {
// this.timeLeft = 0
this.dropPowerUp = false;
this.death(); //death with no power up
}
};
},
launcher(x, y, radius = 30 + Math.ceil(Math.random() * 40)) { launcher(x, y, radius = 30 + Math.ceil(Math.random() * 40)) {
mobs.spawn(x, y, 3, radius, "rgb(150,150,255)"); mobs.spawn(x, y, 3, radius, "rgb(150,150,255)");
let me = mob[mob.length - 1]; let me = mob[mob.length - 1];

View File

@@ -1,6 +1,11 @@
new mod: doubles current bots
************** TODO - n-gon ************** ************** TODO - n-gon **************
shielded mobs don't knock back the same as unshielded mobs
buff harmonic field to cover legs
movement fluidity movement fluidity
let legs jump on mobs, but player will still take damage let legs jump on mobs, but player will still take damage
like: ori and the blind forest, celeste like: ori and the blind forest, celeste