diff --git a/js/bullet.js b/js/bullet.js
index 1ad5f6e..8ace739 100644
--- a/js/bullet.js
+++ b/js/bullet.js
@@ -5433,8 +5433,8 @@ const b = {
}
if (tech.isShotgunReversed) {
- player.force.x += 4 * knock * Math.cos(m.angle)
- player.force.y += 4 * knock * Math.sin(m.angle) - 6 * player.mass * simulation.g
+ player.force.x += 1.6 * knock * Math.cos(m.angle)
+ player.force.y += 1.6 * knock * Math.sin(m.angle) - 3 * player.mass * simulation.g
} else if (tech.isShotgunRecoil) {
m.fireCDcycle -= 0.66 * (56 * b.fireCDscale)
player.force.x -= 2 * knock * Math.cos(m.angle)
@@ -5585,16 +5585,16 @@ const b = {
b.iceIX(25 + 20 * Math.random(), m.angle + spread * (Math.random() - 0.5))
}
} else if (tech.isFoamShot) {
- const spread = (input.down ? 0.2 : 0.6)
+ const spread = (input.down ? 0.15 : 0.4)
const where = {
x: m.pos.x + 25 * Math.cos(m.angle),
y: m.pos.y + 25 * Math.sin(m.angle)
}
- const number = 15 * (tech.isShotgunReversed ? 1.6 : 1)
+ const number = 16 * (tech.isShotgunReversed ? 1.6 : 1)
for (let i = 0; i < number; i++) {
- const SPEED = 13 + 4 * Math.random();
+ const SPEED = 11 + 4 * Math.random();
const angle = m.angle + spread * (Math.random() - 0.5)
- b.foam(where, { x: SPEED * Math.cos(angle), y: SPEED * Math.sin(angle) }, 6 + 8 * Math.random())
+ b.foam(where, { x: SPEED * Math.cos(angle), y: SPEED * Math.sin(angle) }, 8 + 7 * Math.random())
}
} else if (tech.isNeedles) {
const number = 9 * (tech.isShotgunReversed ? 1.6 : 1)
diff --git a/js/level.js b/js/level.js
index e8381e2..dd46a4b 100644
--- a/js/level.js
+++ b/js/level.js
@@ -16,7 +16,7 @@ const level = {
start() {
if (level.levelsCleared === 0) { //this code only runs on the first level
// simulation.enableConstructMode() //used to build maps in testing mode
- // level.difficultyIncrease(8 * 4) //30 is near max on hard //60 is near max on why
+ // level.difficultyIncrease(2 * 4) //30 is near max on hard //60 is near max on why
// simulation.isHorizontalFlipped = true
// m.maxHealth = m.health = 100
// powerUps.research.changeRerolls(100000)
@@ -24,8 +24,9 @@ const level = {
// powerUps.research.changeRerolls(100)
// tech.tech[297].frequency = 100
// b.guns[0].ammo = 10000
+
// m.setField("time dilation") //molecular assembler time dilation perfect diamagnetism metamaterial cloaking wormhole negative mass
- // b.giveGuns("laser") //0 nail gun 1 shotgun 2 super balls 3 matter wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
+ // b.giveGuns("shotgun") //0 nail gun 1 shotgun 2 super balls 3 matter wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// tech.giveTech("infrared diode");
// tech.giveTech("active cooling");
// tech.giveTech("pulse")
@@ -35,10 +36,12 @@ const level = {
// for (let i = 0; i < 1; i++) tech.giveTech("dynamic equilibrium")
// for (let i = 0; i < 10; i++) powerUps.directSpawn(450, -50, "tech");
// for (let i = 0; i < 10; i++) powerUps.directSpawn(450, -50, "research");
+
// spawn.starter(1900, -500, 200)
// spawn.starter(1900, -500, 50)
+ // spawn.dragonFlyBoss(1900, -400)
// spawn.beetleBoss(1900, -400)
- // for (let i = 0; i < 10; ++i) spawn.flutter(1900 + 300 * Math.random(), -500 + 300 * Math.random())
+ // for (let i = 0; i < 2; ++i) spawn.flutter(1900 + 300 * Math.random(), -500 + 300 * Math.random())
// level.testing(); //not in rotation, used for testing
// for (let i = 0; i < 7; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "research");
// for (let i = 0; i < 4; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "tech");
@@ -2805,7 +2808,7 @@ const level = {
// spawn.laserTargetingBoss(1700, -500)
// spawn.powerUpBoss(1900, -500)
// spawn.powerUpBossBaby(3200, -500)
- // spawn.snakeBoss(1700, -500)
+ // spawn.dragonFlyBoss(1700, -500)
// spawn.streamBoss(3200, -500)
// spawn.pulsarBoss(1700, -500)
// spawn.spawnerBossCulture(3200, -500)
@@ -5863,10 +5866,10 @@ const level = {
spawn.randomSmallMob(-900, 825);
if (simulation.difficulty > 1) {
- if (Math.random() < 0.70) {
+ if (Math.random() < 0.80) {
spawn.randomLevelBoss(-800, -1300)
} else {
- spawn.snakeBoss(-1000 + Math.random() * 2500, -1300); //boss snake with head
+ spawn.dragonFlyBoss(-1000 + Math.random() * 2500, -1300); //boss snake with head
}
}
powerUps.addResearchToLevel() //needs to run after mobs are spawned
@@ -6546,7 +6549,7 @@ const level = {
spawn.tetherBoss(2300, -1300, { x: 2300, y: -1750 })
if (simulation.difficulty > 4) spawn.nodeGroup(2350, -1300, "spawns", 8, 20, 105);
} else {
- spawn.randomLevelBoss(2300, -1400, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "spiderBoss", "laserBoss", "snakeBoss", "pulsarBoss"]);
+ spawn.randomLevelBoss(2300, -1400, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "spiderBoss", "laserBoss", "dragonFlyBoss", "pulsarBoss"]);
}
}
}
@@ -7394,7 +7397,7 @@ const level = {
spawn.tetherBoss(3380, -1775, { x: 3775, y: -1775 })
if (simulation.difficulty > 4) spawn.nodeGroup(3380, -1775, "spawns", 8, 20, 105); //chance to spawn a ring of exploding mobs around this boss
} else {
- spawn.randomLevelBoss(3100, -1850, ["shooterBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "snakeBoss", "laserBoss"]);
+ spawn.randomLevelBoss(3100, -1850, ["shooterBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "dragonFlyBoss", "laserBoss"]);
}
}
},
@@ -7843,7 +7846,7 @@ const level = {
// spawn.randomGroup(7700, -1100, 0.5);
spawn.randomGroup(9800, -1100, 0.5);
- if (simulation.difficulty > 3) spawn.randomLevelBoss(8600, -600, ["powerUpBoss", "bomberBoss", "snakeBoss", "spiderBoss", "historyBoss"])
+ if (simulation.difficulty > 3) spawn.randomLevelBoss(8600, -600, ["powerUpBoss", "bomberBoss", "dragonFlyBoss", "spiderBoss", "historyBoss"])
spawn.secondaryBossChance(7900, -400)
//Boss Spawning
diff --git a/js/mob.js b/js/mob.js
index 54f277f..3e6a57e 100644
--- a/js/mob.js
+++ b/js/mob.js
@@ -589,7 +589,7 @@ const mobs = {
ctx.setLineDash([]);
}
},
- wing(a, radius = 250, ellipticity = 0.4) {
+ wing(a, radius = 250, ellipticity = 0.4, dmg = 0.0004) {
const minorRadius = radius * ellipticity
const perp = { x: Math.cos(a), y: Math.sin(a) } //
const where = Vector.add(this.position, Vector.mult(perp, radius + 0.8 * this.radius))
@@ -601,8 +601,8 @@ const mobs = {
//check for wing -> player damage
const hitPlayer = Matter.Query.ray([player], this.position, Vector.add(this.position, Vector.mult(perp, radius * 2.05)), minorRadius)
if (hitPlayer.length && m.immuneCycle < m.cycle) {
- m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage
- m.damage(0.00008 * radius * simulation.dmgScale);
+ m.damage(dmg * simulation.dmgScale);
+ // m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage
}
},
searchSpring() {
diff --git a/js/spawn.js b/js/spawn.js
index adb3294..e7a2572 100644
--- a/js/spawn.js
+++ b/js/spawn.js
@@ -5,8 +5,8 @@ const spawn = {
randomBossList: [
"orbitalBoss", "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss",
"powerUpBoss", "powerUpBossBaby", "streamBoss", "pulsarBoss", "spawnerBossCulture", "grenadierBoss", "growBossCulture", "blinkBoss",
- "snakeBoss", "snakeSpitBoss", "laserBombingBoss", "blockBoss", "revolutionBoss", "slashBoss", "shieldingBoss", "timeSkipBoss",
- "beetleBoss"
+ "snakeSpitBoss", "laserBombingBoss", "blockBoss", "revolutionBoss", "slashBoss", "shieldingBoss", "timeSkipBoss",
+ "dragonFlyBoss", "beetleBoss"
],
bossTypeSpawnOrder: [], //preset list of boss names calculated at the start of a run by the randomSeed
bossTypeSpawnIndex: 0, //increases as the boss type cycles
@@ -21,11 +21,11 @@ const spawn = {
},
pickList: ["starter", "starter"],
fullPickList: [
+ "flutter", "flutter", "flutter",
"hopper", "hopper", "hopper",
"slasher", "slasher", "slasher",
"stabber", "stabber", "stabber",
"springer", "springer", "springer",
- "flutter", "flutter",
"shooter", "shooter",
"grenadier", "grenadier",
"striker", "striker",
@@ -2696,7 +2696,7 @@ const spawn = {
mobs.spawn(x, y, 7, radius, '#16576b');
let me = mob[mob.length - 1];
me.isBoss = true;
- Matter.Body.setDensity(me, 0.002); //extra dense //normal is 0.001 //makes effective life much larger
+ Matter.Body.setDensity(me, 0.0016); //extra dense //normal is 0.001 //makes effective life much larger
// me.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front
@@ -2714,7 +2714,7 @@ const spawn = {
// me.onDeath = function() {};
me.flapRate = 0.3 + Math.floor(3 * Math.random()) / 10 + 100 * me.accelMag
- me.flapRadius = 100 + 75 * Math.random() + radius * 2
+ me.flapRadius = 75 + 50 * Math.random() + radius * 2
me.do = function() {
this.seePlayerByHistory()
this.checkStatus();
@@ -2764,9 +2764,12 @@ const spawn = {
me.nextHealthThreshold = 0.75
me.invulnerableCount = 0
+ me.flapRate = 0.25
+ me.wingSize = 0
+ me.wingGoal = 250
me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front
Matter.Body.rotate(me, Math.random() * Math.PI * 2);
- me.accelMag = 0.0006 + 0.0006 * Math.sqrt(simulation.accelScale);
+ me.accelMag = 0.0005 + 0.0005 * Math.sqrt(simulation.accelScale);
me.frictionAir = 0.05;
me.seePlayerFreq = 30
me.memory = 420;
@@ -2777,22 +2780,53 @@ const spawn = {
me.fireDir = { x: 0, y: 0 }
spawn.shield(me, x, y);
- // for (let i = 0, len = 4 + 0.2 * simulation.difficulty; i < len; ++i) {
- // const phase = i / len * 2 * Math.PI
- // const where = Vector.add(me.position, Vector.mult({ x: Math.cos(phase), y: Math.sin(phase) }, radius * 1.5))
- // spawn.flutter(where.x, where.y)
+ // len = 0.3 * simulation.difficulty //spawn some baby flutters
+ // if (len > 3) {
+ // for (let i = 0; i < len; ++i) {
+ // const phase = i / len * 2 * Math.PI
+ // const where = Vector.add(me.position, Vector.mult({ x: Math.cos(phase), y: Math.sin(phase) }, radius * 1.5))
+ // spawn.flutter(where.x, where.y)
+ // }
// }
+ if (Math.random() < 0.3) {
+ const len = 0.1 * simulation.difficulty //spawn some baby flutters
+ let i = 0
+ let spawnFlutters = () => {
+ if (i < len) {
+ if (!(simulation.cycle % 30) && !simulation.paused && !simulation.isChoosing) {
+ const phase = i / len * 2 * Math.PI
+ const where = Vector.add(me.position, Vector.mult({ x: Math.cos(phase), y: Math.sin(phase) }, radius * 1.5))
+ spawn.flutter(where.x, where.y)
+ i++
+ }
+ requestAnimationFrame(spawnFlutters);
+ }
+ }
+ requestAnimationFrame(spawnFlutters);
+ me.isAlreadyHadBabies = true
+ }
+
+
+
me.onDeath = function() {
powerUps.spawnBossPowerUp(this.position.x, this.position.y)
- len = 0.2 * simulation.difficulty
- if (len > 3) {
- for (let i = 0; i < len; ++i) {
- const phase = i / len * 2 * Math.PI
- const where = Vector.add(this.position, Vector.mult({ x: Math.cos(phase), y: Math.sin(phase) }, radius * 1.5))
- spawn.flutter(where.x, where.y)
+ const len = 0.1 * simulation.difficulty //spawn some baby flutters
+ if (len > 3 && !this.isAlreadyHadBabies) {
+ let i = 0
+ let spawnFlutters = () => {
+ if (i < len) {
+ if (!(simulation.cycle % 20) && !simulation.paused && !simulation.isChoosing) {
+ const phase = i / len * 2 * Math.PI
+ const where = Vector.add(me.position, Vector.mult({ x: Math.cos(phase), y: Math.sin(phase) }, radius * 1.5))
+ spawn.flutter(where.x, where.y)
+ i++
+ }
+ requestAnimationFrame(spawnFlutters);
+ }
}
+ requestAnimationFrame(spawnFlutters);
}
};
me.onDamage = function() {
@@ -2803,6 +2837,10 @@ const spawn = {
this.isInvulnerable = true
this.damageReduction = 0
this.frictionAir = 0
+ this.wingGoal = 0
+ this.wingSize = 0
+ this.flapRate += 0.1
+ this.accelMag *= 1.25
}
};
me.do = function() {
@@ -2814,6 +2852,7 @@ const spawn = {
this.isInvulnerable = false
this.damageReduction = this.startingDamageReduction
this.frictionAir = 0.05
+ this.wingGoal = 250
}
// //draw wings as if they are protecting
// const wingShield = (a, size) => {
@@ -2868,11 +2907,13 @@ const spawn = {
} else if (c < -threshold) {
this.torque -= turn;
}
- const flapRate = 0.5
const flapArc = 0.7 //don't go past 1.57 for normal flaps
+ this.wingSize = 0.98 * this.wingSize + 0.02 * this.wingGoal
ctx.fillStyle = this.fill = `hsla(${160+40*Math.random()}, 100%, ${25 + 25*Math.random()*Math.random()}%, 0.9)`; //"rgba(0,235,255,0.3)"; // ctx.fillStyle = `hsla(44, 79%, 31%,0.4)`; //"rgba(0,235,255,0.3)";
- this.wing(this.angle + Math.PI / 2 + flapArc * Math.sin(simulation.cycle * flapRate), 250, 0.5)
- this.wing(this.angle - Math.PI / 2 - flapArc * Math.sin(simulation.cycle * flapRate), 250, 0.5)
+ this.wing(this.angle + Math.PI / 2 + flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingSize, 0.5, 0.0008)
+ this.wing(this.angle - Math.PI / 2 - flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingSize, 0.5, 0.0008)
+ } else {
+ this.wingSize = 0.96 * this.wingSize + 0 //shrink while stunned
}
};
},
@@ -5944,13 +5985,13 @@ const spawn = {
Composite.add(engine.world, consBB[consBB.length - 1]);
// spawn.shield(me, x, y, 1);
},
- snakeBoss(x, y, radius = 50) { //snake boss with a laser head
+ dragonFlyBoss(x, y, radius = 42) { //snake boss with a laser head
const nodes = Math.min(8 + Math.ceil(0.5 * simulation.difficulty), 40)
let angle = Math.PI
let mag = 300
- const color1 = "#f27"
- mobs.spawn(x + mag * Math.cos(angle), y + mag * Math.sin(angle), 8, radius, color1); //"rgb(55,170,170)"
+ const color1 = "#00bfd9" //"#f27"
+ mobs.spawn(x + mag * Math.cos(angle), y + mag * Math.sin(angle), 8, radius, "#000"); //"rgb(55,170,170)"
let me = mob[mob.length - 1];
me.isBoss = true;
me.accelMag = 0.0009 + 0.0002 * Math.sqrt(simulation.accelScale)
@@ -5971,18 +6012,34 @@ const spawn = {
this.seePlayerByHistory()
this.checkStatus();
this.attraction();
- this.harmZone();
+
+ let a //used to set the angle of wings
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);
- ctx.lineWidth = 20;
- ctx.strokeStyle = "rgba(255,255,255,0.7)";
+ ctx.lineWidth = 12;
+ ctx.strokeStyle = "rgba(255,255,255,0.9)";
ctx.stroke();
+ const sub = Vector.sub(this.position, this.snakeBody1.position)
+ a = Math.atan2(sub.y, sub.x)
+ } else {
+ a = Math.atan2(this.velocity.y, this.velocity.x)
}
+
+ ctx.fillStyle = `hsla(${160+40*Math.random()}, 100%, ${25 + 25*Math.random()*Math.random()}%, 0.9)`; //"rgba(0,235,255,0.3)"; // ctx.fillStyle = `hsla(44, 79%, 31%,0.4)`; //"rgba(0,235,255,0.3)";
+ this.wing(a + Math.PI / 2 + this.angleOff + this.flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingLength, this.ellipticity)
+ this.wing(a - Math.PI / 2 - this.angleOff - this.flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingLength, this.ellipticity)
+ this.wing(a - Math.PI / 2 + this.angleOff + this.flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingLength, this.ellipticity)
+ this.wing(a + Math.PI / 2 - this.angleOff - this.flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingLength, this.ellipticity)
};
+ me.flapRate = 0.4
+ me.flapArc = 0.2 //don't go past 1.57 for normal flaps
+ me.wingLength = 150
+ me.ellipticity = 0.3
+ me.angleOff = 0.4
//extra space to give head room
angle -= 0.1
mag -= 10
@@ -5992,16 +6049,13 @@ const spawn = {
mag -= (i < 2) ? -15 : 5
spawn.snakeBody(x + mag * Math.cos(angle), y + mag * Math.sin(angle), i === 0 ? 25 : 20);
if (i < 3) mob[mob.length - 1].snakeHeadID = me.id
+ if (i === 0) me.snakeBody1 = mob[mob.length - 1] //track this segment, so the difference in position between this segment and the head can be used to angle the wings
mob[mob.length - 1].previousTailID = previousTailID
previousTailID = mob[mob.length - 1].id
}
this.constrain2AdjacentMobs(nodes, Math.random() * 0.06 + 0.01);
for (let i = mob.length - 1, len = i - nodes; i > len; i--) { //set alternating colors
- if (i % 2) {
- mob[i].fill = "#333"
- } else {
- mob[i].fill = color1
- }
+ mob[i].fill = `hsla(${160+40*Math.random()}, 100%, ${5 + 25*Math.random()*Math.random()}%, 0.9)`
}
//constraint with first 3 mobs in line
consBB[consBB.length] = Constraint.create({
@@ -6028,7 +6082,7 @@ const spawn = {
mobs.spawn(x, y, 8, radius, "rgba(0,180,180,0.4)");
let me = mob[mob.length - 1];
me.collisionFilter.mask = cat.bullet | cat.player | cat.mob //| cat.body
- me.damageReduction = 0.2
+ me.damageReduction = 0.23
Matter.Body.setDensity(me, 0.005); //normal is 0.001
// me.accelMag = 0.0007 * simulation.accelScale;
diff --git a/js/tech.js b/js/tech.js
index c5c094d..f55feb3 100644
--- a/js/tech.js
+++ b/js/tech.js
@@ -4164,7 +4164,7 @@ const tech = {
{
name: "Noether violation",
link: `Noether violation`,
- description: "+60% shotgun damage
shotgun recoil is increased and reversed",
+ description: "+60% shotgun damage
shotgun recoil is reversed",
isGunTech: true,
maxCount: 1,
count: 0,
diff --git a/todo.txt b/todo.txt
index 8ab7dbc..1d32582 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,27 +1,18 @@
******************************************************** NEXT PATCH **************************************************
-mob: beetleBoss
-mob: flutter
+snakeBoss is now dragonFlyBoss
+ snakeSpitBoss till exists, don't worry
-balance: all mobs at every level are about 2%-3% harder to kill
+flutter and beetle mobs changes
+
+Noether violation has 50% less forward recoil for shotgun
*********************************************************** TODO *****************************************************
-wings
- put in mob as a method?
- add parameter for ellipticity
- give snakeBoss wings?
-
-
-
mob mechanics
bullets hit player and stay attached for 4-5 seconds, slowing player
hopperBullets?
black hole sucker effect on tail
- flickering wings
- draw pair of circles at 2 different locations, like beetle wings flapping
- draw as ellipse
- damage player if caught in wings
vines
attached to map and grows, a series of spheres connected by vines
if node dies it's removed from tree