diff --git a/js/bullet.js b/js/bullet.js index cceb11c..ce339fb 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -198,7 +198,7 @@ const b = { } if (gunTechPool.length) { const index = Math.floor(Math.random() * gunTechPool.length) - simulation.makeTextLog(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`) + simulation.makeTextLog(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`) tech.giveTech(gunTechPool[index]) // choose from the gun pool } else { tech.giveTech() //get normal tech if you can't find any gun tech @@ -370,7 +370,7 @@ const b = { } }, explosionRange() { - return tech.explosiveRadius * (tech.isExplosionHarm ? 1.7 : 1) * (tech.isSmallExplosion ? 0.66 : 1) * (tech.isExplodeRadio ? 1.25 : 1) + return tech.explosiveRadius * (tech.isExplosionHarm ? 1.7 : 1) * (tech.isSmallExplosion ? 0.7 : 1) * (tech.isExplodeRadio ? 1.25 : 1) }, explosion(where, radius, color = "rgba(255,25,0,0.6)") { // typically explode is used for some bullets with .onEnd radius *= tech.explosiveRadius @@ -380,8 +380,8 @@ const b = { if (tech.isExplosionHarm) radius *= 1.7 // 1/sqrt(2) radius -> area if (tech.isSmallExplosion) { // color = "rgba(255,0,30,0.7)" - radius *= 0.66 - dmg *= 1.66 + radius *= 0.7 + dmg *= 1.7 } if (tech.isExplodeRadio) { //radiation explosion @@ -2374,22 +2374,8 @@ const b = { }, dmg = tech.laserDamage, reflections = tech.laserReflections, isThickBeam = false, push = 1) { const reflectivity = 1 - 1 / (reflections * 3) let damage = m.dmgScale * dmg - - let best = { - x: 1, - y: 1, - dist2: Infinity, - who: null, - v1: 1, - v2: 1 - }; - const path = [{ - x: where.x, - y: where.y - }, { - x: whereEnd.x, - y: whereEnd.y - }]; + let best = { x: 1, y: 1, dist2: Infinity, who: null, v1: 1, v2: 1 }; + const path = [{ x: where.x, y: where.y }, { x: whereEnd.x, y: whereEnd.y }]; const checkForCollisions = function () { best = vertexCollision(path[path.length - 2], path[path.length - 1], [mob, map, body]); @@ -4760,7 +4746,6 @@ const b = { } }, dynamoBot(position = player.position, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.dynamoBot()`); const me = bullet.length; bullet[me] = Bodies.polygon(position.x, position.y, 5, 10, { isUpgraded: tech.isDynamoBotUpgrade, @@ -4862,7 +4847,6 @@ const b = { b.setDynamoBotDelay() }, nailBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.nailBot()`); const me = bullet.length; const dir = m.angle; const RADIUS = (12 + 4 * Math.random()) @@ -4878,7 +4862,7 @@ const b = { minDmgSpeed: 2, // lookFrequency: 56 + Math.floor(17 * Math.random()) - isUpgraded * 20, lastLookCycle: simulation.cycle + 60 * Math.random(), - delay: Math.floor((tech.isNailBotUpgrade ? 18 : 85)), + delay: Math.floor((tech.isNailBotUpgrade ? 22 : 85)), acceleration: (isKeep ? 0.005 : 0.001) * (1 + 0.5 * Math.random()), range: 60 * (1 + 0.3 * Math.random()) + 3 * b.totalBots() + !isKeep * 100, endCycle: Infinity, @@ -4927,7 +4911,6 @@ const b = { Composite.add(engine.world, bullet[me]); //add bullet to world }, missileBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.missileBot()`); const me = bullet.length; bullet[me] = Bodies.rectangle(position.x, position.y, 28, 11, { botType: "missile", @@ -4998,7 +4981,6 @@ const b = { Composite.add(engine.world, bullet[me]); //add bullet to world }, foamBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.foamBot()`); const me = bullet.length; const dir = m.angle; const RADIUS = (10 + 5 * Math.random()) @@ -5016,7 +4998,7 @@ const b = { cd: 0, fireCount: 0, fireLimit: 5 + 2 * tech.isFoamBotUpgrade, - delay: Math.floor((145 + (tech.isFoamBotUpgrade ? 0 : 230))),// + 30 - 20 * tech.isFoamBotUpgrade,//20 + Math.floor(85 * b.fireCDscale) - 20 * tech.isFoamBotUpgrade, + delay: Math.floor((200 + (tech.isFoamBotUpgrade ? 0 : 200))),// + 30 - 20 * tech.isFoamBotUpgrade,//20 + Math.floor(85 * b.fireCDscale) - 20 * tech.isFoamBotUpgrade, acceleration: (isKeep ? 0.005 : 0.001) * (1 + 0.5 * Math.random()), range: 60 * (1 + 0.3 * Math.random()) + 3 * b.totalBots() + !isKeep * 100, //how far from the player the bot will move endCycle: Infinity, @@ -5038,7 +5020,7 @@ const b = { const radius = 5 + 3 * Math.random() const SPEED = Math.max(5, 25 - radius * 0.4); //(m.crouch ? 32 : 20) - radius * 0.7; const velocity = Vector.mult(Vector.normalise(Vector.sub(this.fireTarget, this.position)), SPEED) - b.foam(this.position, Vector.rotate(velocity, 0.07 * (Math.random() - 0.5)), radius + 6 * this.isUpgraded) + b.foam(this.position, Vector.rotate(velocity, 0.07 * (Math.random() - 0.5)), radius + 5 * this.isUpgraded) //recoil // const force = Vector.mult(Vector.normalise(velocity), 0.005 * this.mass * (tech.isFoamCavitation ? 2 : 1)) @@ -5068,58 +5050,11 @@ const b = { } else { //fire mode: quickly fire at targets and doesn't follow player this.fire() } - - - - - - - - - - // const distanceToPlayer = Vector.magnitude(Vector.sub(this.position, m.pos)) - // if (distanceToPlayer > this.range) { //if far away move towards player - // this.force = Vector.mult(Vector.normalise(Vector.sub(m.pos, this.position)), this.mass * this.acceleration) - // } else { //close to player - // Matter.Body.setVelocity(this, Vector.add(Vector.mult(this.velocity, 0.90), Vector.mult(player.velocity, 0.17))); //add player's velocity - - // //&& !(simulation.cycle % this.lookFrequency) - // if (this.cd < simulation.cycle && !m.isCloak) { - // let target - // for (let i = 0, len = mob.length; i < len; i++) { - // const dist2 = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)); - // if (dist2 < 2000000 && !mob[i].isBadTarget && Matter.Query.ray(map, this.position, mob[i].position).length === 0 && !mob[i].isInvulnerable) { - - // this.fireCount++ - // if (this.fireCount > 5) { - // this.fireCount = 0 - // this.cd = simulation.cycle + this.delay; - // } else { - // // this.cd = simulation.cycle + 1; - // } - - // target = Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist2) / 60)) - // const radius = 6 + 7 * Math.random() - // const SPEED = Math.max(5, 25 - radius * 0.4); //(m.crouch ? 32 : 20) - radius * 0.7; - // const velocity = Vector.mult(Vector.normalise(Vector.sub(target, this.position)), SPEED) - // b.foam(this.position, velocity, radius + 7.5 * this.isUpgraded) - - // //recoil - // // const force = Vector.mult(Vector.normalise(velocity), 0.005 * this.mass * (tech.isFoamCavitation ? 2 : 1)) - // const force = Vector.mult(velocity, 0.0003 * this.mass * (tech.isFoamCavitation ? 2 : 1)) - // this.force.x -= force.x - // this.force.y -= force.y - // break; - // } - // } - // } - // } } }) Composite.add(engine.world, bullet[me]); //add bullet to world }, soundBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.soundBot()`); const me = bullet.length; const dir = m.angle; bullet[me] = Bodies.rectangle(position.x, position.y, 12, 30, { @@ -5132,11 +5067,11 @@ const b = { restitution: 0.6 * (1 + 0.5 * Math.random()), dmg: 0, // 0.14 //damage done in addition to the damage from momentum minDmgSpeed: 2, - lookFrequency: 17 + Math.floor(7 * Math.random()) - 5 * tech.isSoundBotUpgrade, + lookFrequency: 17 + Math.floor(7 * Math.random()) - 3 * tech.isSoundBotUpgrade, cd: 0, fireCount: 0, - fireLimit: 5 + 2 * tech.isSoundBotUpgrade, - delay: Math.floor((120 + (tech.isSoundBotUpgrade ? 0 : 70))),// + 30 - 20 * tech.isFoamBotUpgrade,//20 + Math.floor(85 * b.fireCDscale) - 20 * tech.isFoamBotUpgrade, + fireLimit: 5 + 3 * tech.isSoundBotUpgrade, + delay: Math.floor((140 + (tech.isSoundBotUpgrade ? 0 : 50))),// + 30 - 20 * tech.isFoamBotUpgrade,//20 + Math.floor(85 * b.fireCDscale) - 20 * tech.isFoamBotUpgrade, acceleration: (isKeep ? 0.005 : 0.001) * (1 + 0.5 * Math.random()), range: 60 * (1 + 0.3 * Math.random()) + 3 * b.totalBots() + !isKeep * 100, //how far from the player the bot will move endCycle: Infinity, @@ -5151,7 +5086,7 @@ const b = { waves: [], phononWaveCD: 0, addWave(where, angle) { - const halfArc = 0.2 * (tech.isBulletTeleport ? 0.66 + (Math.random() - 0.5) : 1) + 0.05 * tech.isSoundBotUpgrade //6.28 is a full circle, but these arcs needs to stay small because we are using small angle linear approximation, for collisions + const halfArc = 0.2 * (tech.isBulletTeleport ? 0.66 + (Math.random() - 0.5) : 1) + 0.04 * tech.isSoundBotUpgrade //6.28 is a full circle, but these arcs needs to stay small because we are using small angle linear approximation, for collisions this.waves.push({ position: where, angle: angle - halfArc, //used in drawing ctx.arc @@ -5160,7 +5095,7 @@ const b = { arc: halfArc * 2, radius: 25, resonanceCount: 0, - dmg: (tech.isUpgraded ? 4 : 1.5) * m.dmgScale * tech.wavePacketDamage * tech.waveBeamDamage * (tech.isBulletTeleport ? 1.5 : 1), + dmg: (tech.isUpgraded ? 3.5 : 1.5) * m.dmgScale * tech.wavePacketDamage * tech.waveBeamDamage * (tech.isBulletTeleport ? 1.5 : 1), }) }, fire() { @@ -5285,7 +5220,6 @@ const b = { Composite.add(engine.world, bullet[me]); //add bullet to world }, laserBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.laserBot()`); const me = bullet.length; const dir = m.angle; const RADIUS = (14 + 6 * Math.random()) @@ -5468,7 +5402,6 @@ const b = { Composite.add(engine.world, bullet[me]); //add bullet to world }, boomBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.boomBot()`); const me = bullet.length; const dir = m.angle; const RADIUS = (7 + 2 * Math.random()) @@ -5484,8 +5417,8 @@ const b = { minDmgSpeed: 0, lookFrequency: 43 + Math.floor(7 * Math.random()) - 13 * tech.isBoomBotUpgrade, acceleration: (isKeep ? 0.005 : 0.001) * (1 + 0.5 * Math.random()), - attackAcceleration: 0.012 + 0.006 * tech.isBoomBotUpgrade, - range: 500 * (1 + 0.1 * Math.random()) + 350 * tech.isBoomBotUpgrade + !isKeep * 100, + attackAcceleration: 0.012 + 0.005 * tech.isBoomBotUpgrade, + range: 500 * (1 + 0.1 * Math.random()) + 250 * tech.isBoomBotUpgrade + !isKeep * 100, endCycle: Infinity, classType: "bullet", collisionFilter: { @@ -5556,7 +5489,6 @@ const b = { Composite.add(engine.world, bullet[me]); //add bullet to world }, plasmaBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.plasmaBot()`); const me = bullet.length; const dir = m.angle; const RADIUS = 21 @@ -5694,7 +5626,6 @@ const b = { Composite.add(engine.world, bullet[me]); //add bullet to world }, orbitBot(position = player.position, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.orbitBot()`); const me = bullet.length; bullet[me] = Bodies.polygon(position.x, position.y, 9, 12, { isUpgraded: tech.isOrbitBotUpgrade, @@ -5729,7 +5660,7 @@ const b = { } } }, - range: 190 + 130 * tech.isOrbitBotUpgrade + !isKeep * 60 * (0.5 - Math.random()), //range is set in bot upgrade too! + range: 190 + 170 * tech.isOrbitBotUpgrade + !isKeep * 60 * (0.5 - Math.random()), //range is set in bot upgrade too! orbitalSpeed: 0, phase: 2 * Math.PI * Math.random(), do() { @@ -6125,7 +6056,7 @@ const b = { name: "shotgun", //1 // description: `fire a wide burst of short range bullets
with a low fire rate
3-4 nails per ${powerUps.orb.ammo()}`, descriptionFunction() { - return `fire a wide burst of short range bullets
has a slow fire rate
${this.ammoPack.toFixed(1)} shots per ${powerUps.orb.ammo()}` + return `fire a wide burst of short range pellets
has a slow fire rate
${this.ammoPack.toFixed(1)} shots per ${powerUps.orb.ammo()}` }, ammo: 0, ammoPack: 1.6, @@ -6414,9 +6345,8 @@ const b = { } }, { name: "super balls", //2 - // description: `fire 3 balls in a wide arc
balls bounce with no momentum loss
9 balls per ${powerUps.orb.ammo()}`, descriptionFunction() { - return `fire 3 balls in a wide arc
balls bounce with no momentum loss
${this.ammoPack.toFixed(0)} balls per ${powerUps.orb.ammo()}` + return `fire 3 balls that retain
momentum and kinetic energy after collisions
${this.ammoPack.toFixed(0)} balls per ${powerUps.orb.ammo()}` }, ammo: 0, ammoPack: 4.05, @@ -6974,7 +6904,7 @@ const b = { name: "grenades", //5 // description: `lob a single bouncy projectile
explodes on contact or after one second
7 grenades per ${powerUps.orb.ammo()}`, descriptionFunction() { - return `lob a single bouncy projectile
explodes on contact or after one second
${this.ammoPack.toFixed(0)} grenades per ${powerUps.orb.ammo()}` + return `lob a single bouncy projectile
explodes on contact or after 1.5 seconds
${this.ammoPack.toFixed(0)} grenades per ${powerUps.orb.ammo()}` }, ammo: 0, ammoPack: 3.2, @@ -7357,7 +7287,7 @@ const b = { name: "harpoon", //9 // description: `throw a self-steering harpoon that uses energy
to retract and refund its ammo cost
1-2 harpoons per ${powerUps.orb.ammo()}`, descriptionFunction() { - return `throw a self-steering harpoon that uses energy
to retract and refund its ammo cost
${this.ammoPack.toFixed(1)} harpoons per ${powerUps.orb.ammo()}` + return `throw a harpoon that uses energy to retract
harpoons refund ammo
${this.ammoPack.toFixed(1)} harpoons per ${powerUps.orb.ammo()}` }, ammo: 0, ammoPack: 0.77, //update this in railgun tech @@ -7767,7 +7697,7 @@ const b = { { name: "laser", //11 descriptionFunction() { - return `emit a beam of collimated coherent light
costs ${(tech.laserDrain * 6000).toFixed(1)} energy per second
doesn't use ammo` + return `emit a beam of collimated coherent light
reflects off map, blocks, and mobs ${(tech.isWideLaser || tech.isPulseLaser) ? 0 : tech.laserReflections} times
costs ${(tech.laserDrain * 6000).toFixed(1)} energy per second and 0 ammo` }, ammo: 0, ammoPack: Infinity, @@ -7902,79 +7832,6 @@ const b = { } // this.fire = this.firePhoton }, - // fireLaser() { - // // console.log('hi') - // const drain = tech.laserDrain / b.fireCDscale - // if (m.energy < drain) { - // m.fireCDcycle = m.cycle + 100; // cool down if out of energy - // } else { - // m.fireCDcycle = m.cycle - // m.energy -= drain - // const range = { - // x: 5000 * Math.cos(m.angle), - // y: 5000 * Math.sin(m.angle) - // } - // const laserSeparation = 3 - // const rangeOffPlus = { - // x: laserSeparation * Math.cos(m.angle + Math.PI / 2), - // y: laserSeparation * Math.sin(m.angle + Math.PI / 2) - // } - // const rangeOffMinus = { - // x: laserSeparation * Math.cos(m.angle - Math.PI / 2), - // y: laserSeparation * Math.sin(m.angle - Math.PI / 2) - // } - // const dmg = 0.70 * tech.laserDamage / b.fireCDscale * this.lensDamage // 3.5 * 0.55 = 200% more damage - // const where = { - // x: m.pos.x + 30 * Math.cos(m.angle), - // y: m.pos.y + 30 * Math.sin(m.angle) - // } - // const eye = { - // x: m.pos.x + 15 * Math.cos(m.angle), - // y: m.pos.y + 15 * Math.sin(m.angle) - // } - // // ctx.strokeStyle = tech.laserColor; - // // ctx.lineWidth = 8 - // // ctx.beginPath(); - // if (Matter.Query.ray(map, eye, where).length === 0 && Matter.Query.ray(body, eye, where).length === 0) { - // b.laser(eye, { - // x: eye.x + range.x, - // y: eye.y + range.y - // }, dmg) - // } - // for (let i = 1; i < 2; i++) { - // let whereOff = Vector.add(where, { - // x: i * rangeOffPlus.x, - // y: i * rangeOffPlus.y - // }) - // if (Matter.Query.ray(map, eye, whereOff).length === 0 && Matter.Query.ray(body, eye, whereOff).length === 0) { - // ctx.moveTo(eye.x, eye.y) - // ctx.lineTo(whereOff.x, whereOff.y) - // b.laser(whereOff, { - // x: whereOff.x + range.x, - // y: whereOff.y + range.y - // }, dmg) - // } - // whereOff = Vector.add(where, { - // x: i * rangeOffMinus.x, - // y: i * rangeOffMinus.y - // }) - // if (Matter.Query.ray(map, eye, whereOff).length === 0 && Matter.Query.ray(body, eye, whereOff).length === 0) { - // ctx.moveTo(eye.x, eye.y) - // ctx.lineTo(whereOff.x, whereOff.y) - // b.laser(whereOff, { - // x: whereOff.x + range.x, - // y: whereOff.y + range.y - // }, dmg) - // } - // } - // // ctx.stroke(); - // // if (tech.isLaserLens && b.guns[11].lensDamage !== 1) { - // // ctx.lineWidth = 20 + 3 * b.guns[11].lensDamageOn - // // ctx.globalAlpha = 0.3 - // // ctx.stroke(); - // // } - // } - // }, fireLaser() { const drain = tech.laserDrain / b.fireCDscale if (m.energy < drain) { @@ -8004,10 +7861,7 @@ const b = { // const scale = Math.pow(0.9, tech.beamSplitter) // const pushScale = scale * scale let dmg = tech.laserDamage / b.fireCDscale * this.lensDamage // * scale //Math.pow(0.9, tech.laserDamage) - const where = { - x: m.pos.x + 20 * Math.cos(m.angle), - y: m.pos.y + 20 * Math.sin(m.angle) - } + const where = { x: m.pos.x + 20 * Math.cos(m.angle), y: m.pos.y + 20 * Math.sin(m.angle) } const divergence = m.crouch ? 0.15 : 0.35 const angle = m.angle - tech.beamSplitter * divergence / 2 for (let i = 0; i < 1 + tech.beamSplitter; i++) { @@ -8098,8 +7952,8 @@ const b = { } else { m.fireCDcycle = m.cycle m.energy -= drain - const dmg = 0.5 * tech.laserDamage / b.fireCDscale * this.lensDamage // 3.5 * 0.55 = 200% more damage - const spacing = Math.ceil(10 - 0.4 * tech.historyLaser) + const dmg = tech.laserDamage / b.fireCDscale * this.lensDamage + const spacing = Math.ceil(23 - tech.historyLaser) ctx.beginPath(); b.laser({ x: m.pos.x + 20 * Math.cos(m.angle), @@ -8107,18 +7961,21 @@ const b = { }, { x: m.pos.x + 3000 * Math.cos(m.angle), y: m.pos.y + 3000 * Math.sin(m.angle) - }, dmg, 0, true, 0.2); - for (let i = 1, len = 3 + tech.historyLaser * 3; i < len; i++) { + }, dmg); + + for (let i = 1, len = 1 + tech.historyLaser; i < len; i++) { const history = m.history[(m.cycle - i * spacing) % 600] - const off = history.yOff - 24.2859 + const off = history.yOff - 24.2859 + 2 * i + // ctx.globalAlpha = 0.13 b.laser({ x: history.position.x + 20 * Math.cos(history.angle), y: history.position.y + 20 * Math.sin(history.angle) - off }, { x: history.position.x + 3000 * Math.cos(history.angle), y: history.position.y + 3000 * Math.sin(history.angle) - off - }, dmg, 0, true, 0.2); + }, 0.7 * dmg, tech.laserReflections, true); } + // ctx.globalAlpha = 1 ctx.strokeStyle = tech.laserColor; ctx.lineWidth = 1 ctx.stroke(); diff --git a/js/engine.js b/js/engine.js index fb7c481..1090afb 100644 --- a/js/engine.js +++ b/js/engine.js @@ -117,7 +117,7 @@ function collisionChecks(event) { if (tech.isCollisionRealitySwitch && m.alive) { m.switchWorlds() - simulation.trails() + simulation.trails(90) simulation.makeTextLog(`simulation.amplitude = ${Math.random()}`); } if (tech.isPiezo) m.energy += 20.48; diff --git a/js/index.js b/js/index.js index fa71cdc..8b3213d 100644 --- a/js/index.js +++ b/js/index.js @@ -464,12 +464,12 @@ const build = { let text = `
PAUSED -press ${input.key.pause} to resume +press ${input.key.pause} to resume

- +
@@ -503,18 +503,18 @@ ${tech.junkChance ? `
JUNK: ${(100 * tech
difficulty parameters
- ${simulation.difficultyMode > 0 ? `
0.84x damage done per level
1.23x damage taken per level
` : " "} + ${simulation.difficultyMode > 0 ? `
0.85x damage done per level
1.23x damage taken per level
` : " "} ${simulation.difficultyMode > 1 ? `
-5 initial power ups
faster and more mobs per level
` : " "} - ${simulation.difficultyMode > 2 ? `
0.84x damage done per level
1.23x damage taken per level
` : " "} - ${simulation.difficultyMode > 3 ? `
+1 boss per level, -1 tech per boss
-1 ${powerUps.orb.research()} per level
` : " "} - ${simulation.difficultyMode > 4 ? `
0.84x damage done per level
1.23x damage taken per level
` : " "} + ${simulation.difficultyMode > 2 ? `
0.85x damage done per level
1.23x damage taken per level
` : " "} + ${simulation.difficultyMode > 3 ? `
+1 boss per level
-1 tech per boss
` : " "} + ${simulation.difficultyMode > 4 ? `
0.85x damage done per level
1.23x damage taken per level
` : " "} ${simulation.difficultyMode > 5 ? `
3x chance for shielded mobs
-3 initial power ups
` : " "}
` if (!localSettings.isHideHUD) text += `
-console messages +console log
${document.getElementById("text-log").innerHTML}
diff --git a/js/level.js b/js/level.js index 51e091b..4f5082b 100644 --- a/js/level.js +++ b/js/level.js @@ -10,7 +10,7 @@ const level = { levelsCleared: 0, //see level.populateLevels: (initial, ... , reservoir or factory, reactor, ... , subway, final) added later playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock", "towers"], - communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever", "tlinat", "ruins", "ace", "crimsonTowers", "LaunchSite", "shipwreck", "unchartedCave", "dojo", "arena", "soft"], + communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever", "tlinat", "ruins", "ace", "crimsonTowers", "LaunchSite", "shipwreck", "unchartedCave", "dojo", "arena", "soft", "flappyGon", "rings", "trial"], trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon"], levels: [], start() { @@ -26,7 +26,7 @@ const level = { // tech.tech[297].frequency = 100 // tech.addJunkTechToPool(0.5) // m.couplingChange(10) - // m.setField("molecular assembler") //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("time dilation") //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 @@ -42,13 +42,13 @@ const level = { // b.guns[8].ammo = 100000000 // requestAnimationFrame(() => { tech.giveTech("optical amplifier") }); - // for (let i = 0; i < 1; ++i) tech.giveTech("combinatorial optimization") - // tech.giveTech("Newtons 2nd law") - // for (let i = 0; i < 1; ++i) tech.giveTech("tungsten carbide") - // for (let i = 0; i < 1; ++i) tech.giveTech("working mass") - // for (let i = 0; i < 1; ++i) tech.giveTech("buckling") + // tech.giveTech("1st ionization energy") + // for (let i = 0; i < 1; ++i) tech.giveTech("negative feedback") + // for (let i = 0; i < 2; ++i) tech.giveTech("delayed-choice") + // for (let i = 0; i < 1; ++i) tech.giveTech("pulse") + // for (let i = 0; i < 1; ++i) tech.giveTech("mass-energy equivalence") // requestAnimationFrame(() => { for (let i = 0; i < 10; i++) b.orbitBot(m.pos, false) }); - // requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("ersatz bots") }); + // requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("1st ionization energy") }); // for (let i = 0; i < 1; i++) tech.giveTech("tungsten carbide") // m.lastKillCycle = m.cycle // for (let i = 0; i < 1; ++i) tech.giveTech("compression engine") @@ -56,7 +56,7 @@ const level = { // for (let i = 0; i < 1; i++) powerUps.directSpawn(-50, -70, "difficulty", false); // for (let i = 0; i < 100; i++) powerUps.directSpawn(1750, -500, "coupling"); // spawn.mapRect(575, -700, 25, 425); //block mob line of site on testing - // level.testing(); + // level.arena(); // for (let i = 0; i < 1; ++i) spawn.snakeSpitBoss(1400, -500) // Matter.Body.setPosition(player, { x: -200, y: -3330 }); @@ -64,7 +64,6 @@ const level = { // spawn.hopper(1900, -500) // spawn.zombie(-3000, -500 + 300 * Math.random(), 30, 5, "white") // zombie(x, y, radius, sides, color) // for (let i = 0; i < 5; ++i) spawn.starter(1000 + 1000 * Math.random(), -500 + 300 * Math.random()) - // tech.addJunkTechToPool(2) // tech.tech[322].frequency = 100 // spawn.tetherBoss(1900, -500, { x: 1900, y: -500 }) // for (let i = 0; i < 40; ++i) tech.giveTech() @@ -78,7 +77,6 @@ const level = { // simulation.isAutoZoom = false; //look in close // simulation.zoomScale *= 0.5; // simulation.setZoom(); - // tech.addJunkTechToPool(0.7) // for (let i = 0; i < 1; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "entanglement"); // for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 450, m.pos.y + 50 * Math.random(), "boost"); @@ -243,7 +241,7 @@ const level = { } else if (simulation.difficultyMode > 1) { scale = 2 } - m.dmgScale = Math.pow(0.84, level.levelsCleared * scale) + m.dmgScale = Math.pow(0.85, level.levelsCleared * scale) simulation.dmgScale = Math.max(0.1, 0.23 * level.levelsCleared * scale) //damage done by mobs scales with total levels // @@ -2453,7 +2451,10 @@ const level = { powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2100, "heal", false); powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2060, "research", false); } - // if (level.levelsCleared === 0) powerUps.directSpawn(-50, -70, "difficulty", false); + //spin the power ups to prevent them from stacking awkwardly + for (let i = 0; i < powerUp.length; i++) { + Matter.Body.setAngularVelocity(powerUp[i], 5 * (Math.random() - 0.5)) + } } else { requestAnimationFrame(cycle); } @@ -2581,6 +2582,14 @@ const level = { wires.lineTo(2600, -690) level.custom = () => { + //working on a message using text + // ctx.font = "50px Arial"; + // ctx.fillStyle = "rgba(0,0,0,0.3)" + // for (let i = 0; i < 5; i++) { + // const wiggle = 10 + // ctx.fillText("move", 500 + wiggle * Math.random(), -500 + wiggle * Math.random()); + // } + //push around power ups stuck in the tube wall if (!(simulation.cycle % 30)) { for (let i = 0, len = powerUp.length; i < len; i++) { @@ -11342,14 +11351,6 @@ const level = { anotherBoss = (x, y) => { if (tech.isDuplicateMobs && Math.random() < tech.duplicationChance()) { spawn.randomLevelBoss(x, y, ["historyBoss"]); - } else if (tech.isResearchBoss) { - if (powerUps.research.count > 2) { - powerUps.research.changeRerolls(-3) - simulation.makeTextLog(`m.research -= 3
${powerUps.research.count}`) - } else { - tech.addJunkTechToPool(0.49) - } - spawn.randomLevelBoss(x, y, ["historyBoss"]); } } @@ -14281,16 +14282,6 @@ const level = { const anotherBoss = (x, y) => { if (tech.isDuplicateMobs && Math.random() < tech.duplicationChance()) { spawn.historyBoss(x, y) - } else if (tech.isResearchBoss) { - if (powerUps.research.count > 2) { - powerUps.research.changeRerolls(-3) - simulation.makeTextLog( - `m.research -= 3
${powerUps.research.count}` - ) - } else { - tech.addJunkTechToPool(0.49) - } - spawn.historyBoss(x, y) } } @@ -15102,9 +15093,6 @@ const level = { me.collisionFilter.mask = cat.player | cat.mob | cat.bullet; }, 1000); } - me.onDeath = function () { - tech.addJunkTechToPool(0.1) - } Composite.add(engine.world, me.constraint); } @@ -31372,7 +31360,687 @@ const level = { powerUps.addResearchToLevel() //needs to run after mobs are spawned }, arena() { - simulation.makeTextLog(`arena by Richard0820`) + simulation.makeTextLog(`arena by Whyisthisnotavalable`) + let genisis, genisisJumpSensor, genisisBody, genisisHead, genisisHeadSensor, genisisBodySensor; + let control = { left: false, right: false, up: false, down: false }; + const g = { + spawn() { + //load genisis in matter.js physic engine + // let vector = Vertices.fromPath("0 40 50 40 50 115 0 115 30 130 20 130"); //genisis as a series of vertices + let vertices = Vertices.fromPath("0,40, 50,40, 50,115, 30,130, 20,130, 0,115, 0,40"); //genisis as a series of vertices + genisisBody = Bodies.fromVertices(0, 0, vertices); + genisisJumpSensor = Bodies.rectangle(0, 46, 36, 6, { + //this sensor check if the genisis is on the ground to enable jumping + sleepThreshold: 99999999999, + isSensor: true + }); + vertices = Vertices.fromPath("16 -82 2 -66 2 -37 43 -37 43 -66 30 -82"); + genisisHead = Bodies.fromVertices(0, -55, vertices); //this part of the genisis lowers on crouch + genisisHeadSensor = Bodies.rectangle(0, -57, 48, 45, { + //senses if the genisis's head is empty and can return after crouching + sleepThreshold: 99999999999, + isSensor: true + }); + genisisBodySensor = Bodies.rectangle(0, 0, 70, 45, { + sleepThreshold: 99999999999, + isSensor: true + }); + genisis = Body.create({ + //combine genisisJumpSensor and genisisBody + parts: [genisisBody, genisisHead, genisisJumpSensor, genisisHeadSensor, genisisBodySensor], + inertia: Infinity, //prevents genisis rotation + friction: 0.002, + frictionAir: 0.001, + //frictionStatic: 0.5, + restitution: 0, + sleepThreshold: Infinity, + collisionFilter: { + group: 0, + category: cat.mob, + mask: cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield | cat.player | cat.bullet + }, + // death() { + // g.death(); + // } + }); + Matter.Body.setMass(genisis, g.mass); + Composite.add(engine.world, [genisis]); + }, + health: 1000, + maxHealth: 1000, //set in simulation.reset() + cycle: 600, //starts at 600 cycles instead of 0 to prevent bugs with g.history + lastKillCycle: 0, + lastHarmCycle: 0, + width: 50, + radius: 30, + eyeFillColor: null, + fillColor: null, //set by setFillColors + fillColorDark: null, //set by setFillColors + bodyGradient: null, //set by setFillColors + color: { + hue: 0, + sat: 0, + light: 50, + }, + setFillColors() { + g.fillColor = `hsl(${g.color.hue},${g.color.sat}%,${g.color.light}%)` + g.fillColorDark = `hsl(${g.color.hue},${g.color.sat}%,${g.color.light - 25}%)` + let grd = ctx.createLinearGradient(-30, 0, 30, 0); + grd.addColorStop(0, g.fillColorDark); + grd.addColorStop(1, g.fillColor); + g.bodyGradient = grd + }, + setFillColorsAlpha(alpha = 0.5) { + g.fillColor = `hsla(${g.color.hue},${g.color.sat}%,${g.color.light}%,${alpha})` + g.fillColorDark = `hsla(${g.color.hue},${g.color.sat}%,${g.color.light - 25}%,${alpha})` + let grd = ctx.createLinearGradient(-30, 0, 30, 0); + grd.addColorStop(0, g.fillColorDark); + grd.addColorStop(1, g.fillColor); + g.bodyGradient = grd + }, + height: 42, + yOffWhen: { + crouch: 22, + stand: 49, + jump: 70 + }, + defaultMass: 5, + mass: 5, + FxNotHolding: 0.015, + Fx: 0.016, //run Force on ground // + jumpForce: 0.64, + setMovement() { + // g.Fx = 0.08 / mass * tech.squirrelFx + // g.FxAir = 0.4 / mass / mass + g.Fx = tech.baseFx * g.fieldFx * tech.squirrelFx * (tech.isFastTime ? 1.5 : 1) / genisis.mass //base genisis mass is 5 + g.jumpForce = tech.baseJumpForce * g.fieldJump * tech.squirrelJump * (tech.isFastTime ? 1.13 : 1) / genisis.mass / genisis.mass //base genisis mass is 5 + }, + FxAir: 0.032, // 0.4/5/5 run Force in Air + yOff: 70, + yOffGoal: 70, + onGround: false, //checks if on ground or in air + lastOnGroundCycle: 0, //use to calculate coyote time + standingOn: undefined, + numTouching: 0, + crouch: false, + // isHeadClear: true, + spawnPos: { + x: 0, + y: 0 + }, + spawnVel: { + x: 0, + y: 0 + }, + pos: { + x: 0, + y: 0 + }, + yPosDifference: 24.2859, //genisis.position.y - g.pos.y //24.285923217549026 + // yPosDifferenceCrouched: -2.7140767824453604, + Sy: 0, //adds a smoothing effect to vertical only + Vx: 0, + Vy: 0, + friction: { + ground: 0.01, + air: 0.0025 + }, + airSpeedLimit: 125, // 125/mass/mass = 5 + angle: 0, + walk_cycle: 0, + stepSize: 0, + flipLegs: -1, + hip: { + x: 12, + y: 24 + }, + knee: { + x: 0, + y: 0, + x2: 0, + y2: 0 + }, + foot: { + x: 0, + y: 0 + }, + legLength1: 55, + legLength2: 45, + transX: 0, + transY: 0, + history: new Array(600), //[], //tracks the last second of genisis position + rewindCount: 0, //used with CPT + resetHistory() { + const set = { + position: { + x: genisis.position.x, + y: genisis.position.y, + }, + velocity: { + x: genisis.velocity.x, + y: genisis.velocity.y + }, + yOff: g.yOff, + angle: g.angle, + health: g.health, + energy: g.energy, + activeGun: b.activeGun + } + for (let i = 0; i < 600; i++) { //reset history + g.history[i] = set + } + }, + move() { + g.pos.x = genisis.position.x; + g.pos.y = genisisBody.position.y - g.yOff; + g.Vx = genisis.velocity.x; + g.Vy = genisis.velocity.y; + + //tracks the last 10s of genisis information + g.history.splice(g.cycle % 600, 1, { + position: { + x: genisis.position.x, + y: genisis.position.y, + }, + velocity: { + x: genisis.velocity.x, + y: genisis.velocity.y + }, + yOff: g.yOff, + angle: g.angle, + health: g.health, + energy: g.energy, + activeGun: b.activeGun + }); + // const back = 59 // 59 looks at 1 second ago //29 looks at 1/2 a second ago + // historyIndex = (g.cycle - back) % 600 + }, + targetX: 0, + targetY: 0, + transSmoothX: 0, + transSmoothY: 0, + lastGroundedPositionY: 0, + // mouseZoom: 0, + lookSmoothing: 0.07, //1 is instant jerky, 0.001 is slow smooth zoom, 0.07 is standard + look() { }, //set to lookDefault() + lookDefault() { + g.angle = Math.atan2( + g.targetY - g.pos.y, + g.targetX - g.pos.x + ); + // //smoothed mouse look translations + const scale = 0.8; + g.transSmoothX = canvas.width2 - g.pos.x - (simulation.mouse.x - canvas.width2) * scale; + g.transSmoothY = canvas.height2 - g.pos.y - (simulation.mouse.y - canvas.height2) * scale; + + g.transX += (g.transSmoothX - g.transX) * g.lookSmoothing; + g.transY += (g.transSmoothY - g.transY) * g.lookSmoothing; + }, + doCrouch() { + if (!g.crouch) { + g.crouch = true; + g.yOffGoal = g.yOffWhen.crouch; + if ((genisisHead.position.y - genisis.position.y) < 0) { + Matter.Body.setPosition(genisisHead, { + x: genisis.position.x, + y: genisis.position.y + 9.1740767 + }) + } + } + }, + undoCrouch() { + if (g.crouch) { + g.crouch = false; + g.yOffGoal = g.yOffWhen.stand; + if ((genisisHead.position.y - genisis.position.y) > 0) { + Matter.Body.setPosition(genisisHead, { + x: genisis.position.x, + y: genisis.position.y - 30.28592321 + }) + } + } + }, + hardLandCD: 0, + checkHeadClear() { + if (Matter.Query.collides(headSensor, map).length > 0) { + return false + } else { + return true + } + }, + buttonCD_jump: 0, //cool down for genisis buttons + jump() { + // if (!g.onGround) g.lastOnGroundCycle = 0 //g.cycle - tech.coyoteTime + g.buttonCD_jump = g.cycle + 20; //can't jump again until 20 cycles pass + //apply a fraction of the jump force to the body the genisis is jumping off of + Matter.Body.applyForce(g.standingOn, g.pos, { + x: 0, + y: g.jumpForce * 0.12 * Math.min(g.standingOn.mass, 5) + }); + + genisis.force.y = -g.jumpForce; //genisis jump force + Matter.Body.setVelocity(genisis, { //zero genisis y-velocity for consistent jumps + x: genisis.velocity.x, + y: Math.max(-10, Math.min(g.standingOn.velocity.y, 10)) //cap velocity contribution from blocks you are standing on to 10 in the vertical + }); + }, + groundControl() { + //check for crouch or jump + if (g.crouch) { + if (!(control.down) && g.checkHeadClear() && g.hardLandCD < g.cycle) g.undoCrouch(); + } else if (control.down || g.hardLandCD > g.cycle) { + g.doCrouch(); //on ground && not crouched and pressing s or down + } else if (control.up && g.buttonCD_jump + 20 < g.cycle && g.yOffWhen.stand > 23) { + g.jump() + } + + if (control.left) { + if (genisis.velocity.x > -2) { + genisis.force.x -= g.Fx * 1.5 + } else { + genisis.force.x -= g.Fx + } + // } + } else if (control.right) { + if (genisis.velocity.x < 2) { + genisis.force.x += g.Fx * 1.5 + } else { + genisis.force.x += g.Fx + } + } else { + const stoppingFriction = 0.92; //come to a stop if no move key is pressed + Matter.Body.setVelocity(genisis, { + x: genisis.velocity.x * stoppingFriction, + y: genisis.velocity.y * stoppingFriction + }); + } + //come to a stop if fast + if (genisis.speed > 4) { + const stoppingFriction = (g.crouch) ? 0.65 : 0.89; // this controls speed when crouched + Matter.Body.setVelocity(genisis, { + x: genisis.velocity.x * stoppingFriction, + y: genisis.velocity.y * stoppingFriction + }); + } + }, + airControl() { + //check for coyote time jump + // if (control.up && g.buttonCD_jump + 20 + tech.coyoteTime < g.cycle && g.yOffWhen.stand > 23 && g.lastOnGroundCycle + tech.coyoteTime > g.cycle) g.jump() + if (control.up && g.buttonCD_jump + 20 < g.cycle && g.yOffWhen.stand > 23 && g.lastOnGroundCycle + 5 > g.cycle) g.jump() + + //check for short jumps //moving up //recently pressed jump //but not pressing jump key now + if (g.buttonCD_jump + 60 > g.cycle && !(control.up) && g.Vy < 0) { + Matter.Body.setVelocity(genisis, { + //reduce genisis y-velocity every cycle + x: genisis.velocity.x, + y: genisis.velocity.y * 0.94 + }); + } + + if (control.left) { + if (genisis.velocity.x > -g.airSpeedLimit / genisis.mass / genisis.mass) genisis.force.x -= g.FxAir; // move genisis left / a + } else if (control.right) { + if (genisis.velocity.x < g.airSpeedLimit / genisis.mass / genisis.mass) genisis.force.x += g.FxAir; //move genisis right / d + } + }, + alive: true, + dmgScale: 1, //scales all damage, but not raw .dmg //set in levels.setDifficulty + defaultFPSCycle: 0, //tracks when to return to normal fps + immuneCycle: 0, //used in engine + collisionImmuneCycles: 30, + buttonCD: 0, //cool down for genisis buttons + drawLeg(stroke) { + // if (simulation.mouseInGame.x > g.pos.x) { + if (g.angle > -Math.PI / 2 && g.angle < Math.PI / 2) { + g.flipLegs = 1; + } else { + g.flipLegs = -1; + } + ctx.save(); + ctx.scale(g.flipLegs, 1); //leg lines + ctx.beginPath(); + ctx.moveTo(g.hip.x, g.hip.y); + ctx.lineTo(g.knee.x, g.knee.y); + ctx.lineTo(g.foot.x, g.foot.y); + ctx.strokeStyle = stroke; + ctx.lineWidth = 7; + ctx.stroke(); + + //toe lines + ctx.beginPath(); + ctx.moveTo(g.foot.x, g.foot.y); + ctx.lineTo(g.foot.x - 15, g.foot.y + 5); + ctx.moveTo(g.foot.x, g.foot.y); + ctx.lineTo(g.foot.x + 15, g.foot.y + 5); + ctx.lineWidth = 4; + ctx.stroke(); + + //hip joint + ctx.beginPath(); + ctx.arc(g.hip.x, g.hip.y, 11, 0, 2 * Math.PI); + //knee joint + ctx.moveTo(g.knee.x + 7, g.knee.y); + ctx.arc(g.knee.x, g.knee.y, 7, 0, 2 * Math.PI); + //foot joint + ctx.moveTo(g.foot.x + 6, g.foot.y); + ctx.arc(g.foot.x, g.foot.y, 6, 0, 2 * Math.PI); + ctx.fillStyle = g.fillColor; + ctx.fill(); + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + }, + calcLeg(cycle_offset, offset) { + g.hip.x = 12 + offset; + g.hip.y = 24 + offset; + //stepSize goes to zero if Vx is zero or not on ground (make m transition cleaner) + g.stepSize = 0.8 * g.stepSize + 0.2 * (7 * Math.sqrt(Math.min(9, Math.abs(g.Vx))) * g.onGround); + //changes to stepsize are smoothed by adding only a percent of the new value each cycle + const stepAngle = 0.034 * g.walk_cycle + cycle_offset; + g.foot.x = 2.2 * g.stepSize * Math.cos(stepAngle) + offset; + g.foot.y = offset + 1.2 * g.stepSize * Math.sin(stepAngle) + g.yOff + g.height; + const Ymax = g.yOff + g.height; + if (g.foot.y > Ymax) g.foot.y = Ymax; + + //calculate knee position as intersection of circle from hip and foot + const d = Math.sqrt((g.hip.x - g.foot.x) * (g.hip.x - g.foot.x) + (g.hip.y - g.foot.y) * (g.hip.y - g.foot.y)); + const l = (g.legLength1 * g.legLength1 - g.legLength2 * g.legLength2 + d * d) / (2 * d); + const h = Math.sqrt(g.legLength1 * g.legLength1 - l * l); + g.knee.x = (l / d) * (g.foot.x - g.hip.x) - (h / d) * (g.foot.y - g.hip.y) + g.hip.x + offset; + g.knee.y = (l / d) * (g.foot.y - g.hip.y) + (h / d) * (g.foot.x - g.hip.x) + g.hip.y; + }, + draw() { }, + drawDefault() { + ctx.fillStyle = g.fillColor; + g.walk_cycle += g.flipLegs * g.Vx; + ctx.save(); + ctx.globalAlpha = (g.immuneCycle < g.cycle) ? 1 : 0.5 //|| (g.cycle % 40 > 20) + ctx.translate(g.pos.x, g.pos.y); + g.calcLeg(Math.PI, -3); + g.drawLeg("#FFFFFF"); + g.calcLeg(0, 0); + g.drawLeg("#FFFFFF"); + ctx.rotate(g.angle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = g.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#FFFFFF"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + g.yOff = g.yOff * 0.85 + g.yOffGoal * 0.15; //smoothly move leg height towards height goal + }, + drawDamage() { + ctx.fillStyle = "red"; + g.walk_cycle += g.flipLegs * g.Vx; + ctx.save(); + ctx.globalAlpha = 0.7; + ctx.translate(g.pos.x, g.pos.y); + g.calcLeg(Math.PI, -3); + g.drawLeg("#FF0000"); + g.calcLeg(0, 0); + g.drawLeg("#FF0000"); + ctx.rotate(g.angle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = g.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#FF0000"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + g.yOff = g.yOff * 0.85 + g.yOffGoal * 0.15; //smoothly move leg height towards height goal + }, + damage(dmg) { + g.health -= dmg; + }, + rebirth() { + g.health = g.maxHealth; + genisis.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield | cat.player | cat.bullet + }, + lastHealth: 1000, + drawHealth() { + let interpolated = this.lastHealth + (g.health - this.lastHealth) * 0.1; + + ctx.save(); + ctx.setTransform(1, 0, 0.2, 1, 0, 0); //slanted + ctx.fillStyle = "rgba(250, 100, 100, 0.3)"; + ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2, 30); + const grad = ctx.createLinearGradient(0, 0, canvas.width2, 0); + grad.addColorStop(0, "lightblue"); + grad.addColorStop(1, "crimson"); + + ctx.fillStyle = grad; + ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2 * interpolated / 1000, 30); + ctx.restore(); + + this.lastHealth = interpolated; + }, + genisisOnGroundCheck(event) { + //runs on collisions events + function enter() { + g.numTouching++; + if (!g.onGround) { + g.onGround = true; + if (g.crouch) { + if (g.checkHeadClear()) { + g.undoCrouch(); + } else { + g.yOffGoal = g.yOffWhen.crouch; + } + } else { + //sets a hard land where genisis stays in a crouch for a bit and can't jump + //crouch is forced in groundControl below + const momentum = genisis.velocity.y * genisis.mass //genisis mass is 5 so this triggers at 26 down velocity, unless the genisis is holding something + if (momentum > 130) { + g.doCrouch(); + g.yOff = g.yOffWhen.jump; + g.hardLandCD = g.cycle + Math.min(momentum / 6.5 - 6, 40) + //falling damage + if (tech.isFallingDamage && g.immuneCycle < g.cycle && momentum > 150) { + g.damage(Math.min(Math.sqrt(momentum - 133) * 0.01, 0.25)); + if (g.immuneCycle < g.cycle + g.collisionImmuneCycles) g.immuneCycle = g.cycle + g.collisionImmuneCycles; //genisis is immune to damage for 30 cycles + } + } else { + g.yOffGoal = g.yOffWhen.stand; + } + } + } + } + const pairs = event.pairs; + for (let i = 0, j = pairs.length; i != j; ++i) { + let pair = pairs[i]; + if (pair.bodyA === genisisJumpSensor) { + g.standingOn = pair.bodyB; //keeping track to correctly provide recoil on jump + if (g.standingOn.alive !== true) enter(); + } else if (pair.bodyB === genisisJumpSensor) { + g.standingOn = pair.bodyA; //keeping track to correctly provide recoil on jump + if (g.standingOn.alive !== true) enter(); + } + } + g.numTouching = 0; + }, + genisisOffGroundCheck(event) { + //runs on collisions events + const pairs = event.pairs; + for (let i = 0, j = pairs.length; i != j; ++i) { + if (pairs[i].bodyA === genisisJumpSensor || pairs[i].bodyB === genisisJumpSensor) { + if (g.onGround && g.numTouching === 0) { + g.onGround = false; + g.lastOnGroundCycle = g.cycle; + g.hardLandCD = 0 // disable hard landing + if (g.checkHeadClear()) { + if (g.crouch) { + g.undoCrouch(); + } + g.yOffGoal = g.yOffWhen.jump; + } + } + } + } + } + }; + function GenisisCollisionChecks(event) { + const pairs = event.pairs; + for (let i = 0, j = pairs.length; i != j; i++) { + for (let k = 0; k < bullet.length; k++) { + if (pairs[i].bodyA === bullet[k]) { + collideBullet(pairs[i].bodyB); + break; + } else if (pairs[i].bodyB === bullet[k]) { + collideBullet(pairs[i].bodyA); + break; + } + + function collideBullet(obj) { + if ( + g.immuneCycle < g.cycle && + (obj === genisisBody || obj === genisisHead) + ) { + let dmg = Math.sqrt(Math.abs(0.000025 * Math.sqrt((bullet[k].mass + Math.sqrt(Vector.magnitude(bullet[k].velocity)) * 0.0000125)))); + g.damage(dmg); + return; + } + } + } + for (let k = 0; k < mob.length; k++) { + if (mob[k].alive) { + if (pairs[i].bodyA === mob[k]) { + collideMob(pairs[i].bodyB); + break; + } else if (pairs[i].bodyB === mob[k]) { + collideMob(pairs[i].bodyA); + break; + } + + function collideMob(obj) { + //genisis + mob collision + if ( + g.immuneCycle < g.cycle && + (obj === genisisBody || obj === genisisHead) && + !mob[k].isSlowed && !mob[k].isStunned + ) { + let dmg = Math.max(0.025 * Math.sqrt(mob[k].mass), 0.05); + // if (g.isCloak) dmg *= 0.5 + if (tech.isRewindAvoidDeath && g.energy > 0.85 * Math.min(1, g.maxEnergy) && dmg > 0.01) { //CPT reversal runs in g.damage, but it stops the rest of the collision code here too + g.damage(dmg); + return + } + g.damage(dmg); + + if (tech.isCollisionRealitySwitch && g.alive) { + g.switchWorlds() + simulation.trails() + simulation.makeTextLog(`simulation.amplitude = ${Math.random()}`); + } + if (tech.isPiezo) g.energy += 20.48; + if (tech.isCouplingNoHit && g.coupling > 0) { + g.couplingChange(-5) + + const unit = Vector.rotate({ x: 1, y: 0 }, 6.28 * Math.random()) + let where = Vector.add(g.pos, Vector.mult(unit, 17)) + simulation.drawList.push({ //add dmg to draw queue + x: where.x, + y: where.y, + radius: 22, + color: 'rgba(0, 171, 238, 0.33)', + time: 8 + }); + where = Vector.add(g.pos, Vector.mult(unit, 60)) + simulation.drawList.push({ //add dmg to draw queue + x: where.x, + y: where.y, + radius: 18, + color: 'rgba(0, 171, 238, 0.5)', + time: 16 + }); + where = Vector.add(g.pos, Vector.mult(unit, 100)) + simulation.drawList.push({ //add dmg to draw queue + x: where.x, + y: where.y, + radius: 14, + color: 'rgba(0, 171, 238, 0.6)', + time: 24 + }); + where = Vector.add(g.pos, Vector.mult(unit, 135)) + simulation.drawList.push({ //add dmg to draw queue + x: where.x, + y: where.y, + radius: 10, + color: 'rgba(0, 171, 238, 0.7)', + time: 32 + }); + // simulation.drawList.push({ //add dmg to draw queue + // x: g.pos.x, + // y: g.pos.y, + // radius: 150, + // color: 'rgba(0, 171, 238, 0.33)', + // time: 6 + // }); + // simulation.drawList.push({ //add dmg to draw queue + // x: g.pos.x, + // y: g.pos.y, + // radius: 75, + // color: 'rgba(0, 171, 238, 0.5)', + // time: 16 + // }); + // simulation.drawList.push({ //add dmg to draw queue + // x: g.pos.x, + // y: g.pos.y, + // radius: 25, + // color: 'rgba(0, 171, 238, 0.75)', + // time: 25 + // }); + } + if (mob[k].onHit) mob[k].onHit(); + if (g.immuneCycle < g.cycle + g.collisionImmuneCycles) g.immuneCycle = g.cycle + g.collisionImmuneCycles; //genisis is immune to damage for 30 cycles + //extra kick between genisis and mob //this section would be better with forces but they don't work... + let angle = Math.atan2(genisis.position.y - mob[k].position.y, genisis.position.x - mob[k].position.x); + Matter.Body.setVelocity(genisis, { + x: genisis.velocity.x + 8 * Math.cos(angle), + y: genisis.velocity.y + 8 * Math.sin(angle) + }); + Matter.Body.setVelocity(mob[k], { + x: mob[k].velocity.x - 8 * Math.cos(angle), + y: mob[k].velocity.y - 8 * Math.sin(angle) + }); + + if (tech.isAnnihilation && !mob[k].shield && !mob[k].isShielded && !mob[k].isBoss && mob[k].isDropPowerUp && g.energy > 0.1 && mob[k].damageReduction > 0) { + g.energy -= 0.1 //* Math.max(g.maxEnergy, g.energy) //0.33 * g.energy + if (g.immuneCycle === g.cycle + g.collisionImmuneCycles) g.immuneCycle = 0; //genisis doesn't go immune to collision damage + mob[k].death(); + simulation.drawList.push({ //add dmg to draw queue + x: pairs[i].activeContacts[0].vertex.x, + y: pairs[i].activeContacts[0].vertex.y, + radius: Math.sqrt(dmg) * 500, + color: "rgba(255,0,255,0.2)", + time: simulation.drawTime + }); + } else { + simulation.drawList.push({ //add dmg to draw queue + x: pairs[i].activeContacts[0].vertex.x, + y: pairs[i].activeContacts[0].vertex.y, + radius: Math.sqrt(dmg) * 200, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + } + // return; + // } + } + } + } + } + } + } + g.spawn(); + Matter.Body.setPosition(genisis, { + x: 7875, + y: -2530 + }) let isUsingSwordMod = false; for (let i = 0; i < tech.tech.length; i++) { if (tech.tech[i].name === 'size-weight illusion') { //to detect if the player is using the sword mod so that it won't mess up their mod. The sword mod adds this tech so if it is detected then the sword won't be removed from the gun array //Landgreen, don't add a tech with the same name @@ -31380,40 +32048,47 @@ const level = { } } if (!isUsingSwordMod) { - (function () { - const e = { - name: "sword", - descriptionFunction() { return `swing a sword that lifesteals health
drains health instead of ammunition
doesn't use ammo` }, - ammo: Infinity, - ammoPack: Infinity, - defaultAmmoPack: Infinity, - have: false, - cycle: 0, - sword: undefined, - bladeSegments: undefined, - bladeTrails: [], - angle: 0, - constraint: undefined, - do() { - if (input.fire && m.fireCDcycle > m.cycle) { + const e = { + name: "sword", + descriptionFunction() { return `swing a sword that lifesteals health
drains health instead of ammunition
doesn't use ammo` }, + ammo: Infinity, + ammoPack: Infinity, + defaultAmmoPack: Infinity, + have: false, + cycle: 0, + sword: undefined, + swordArray: [], + bladeSegments: undefined, + bladeTrails: [], + angle: 0, + constraint: undefined, + charge: 0, + angle2: 0, + fire() { }, + do() { + if (this.sword && this.cycle < 1) { + this.angle2 = Math.atan2(this.sword.position.y - m.pos.y, this.sword.position.x - m.pos.x); + } + if (this.sword) { + this.cycle++; + } + this.normalFire(); + this.renderDefault(); + this.collision(); + }, + normalFire() { + if (this.constraint) { + this.constraint.pointA = player.position; + } + if (tech.isStabSword && !m.crouch && this.cycle > 0 && this.stabStatus) { + if (this.sword) { + this.stabStatus = false; if (tech.isEnergyHealth) { - m.energy -= 0.004; - } else { - m.health -= 0.001; - m.displayHealth(); + m.energy = 0.01; + m.immuneCycle = m.cycle + 30; } - } - if (b.activeGun !== null && input.fire && (tech.isEnergyHealth ? m.energy >= 0.11 : m.health >= 0.11)) { - if (!this.sword && b.guns[b.activeGun].name === 'sword') { - Matter.Body.setMass(player, 1000); - ({ sword: this.sword, bladeSegments: this.bladeSegments } = this.createAndSwingSword()); - this.angle = m.angle; - } - } - if (this.sword && !input.fire) { this.cycle = 0; Matter.Body.setAngularVelocity(this.sword, 0); - Matter.Body.setMass(player, 5) Composite.remove(engine.world, this.sword); this.sword.parts.forEach(part => { Composite.remove(engine.world, part); @@ -31428,137 +32103,273 @@ const level = { this.constraint = undefined; } this.bladeTrails = []; - m.fireCDcycle = m.cycle + 10; + m.fireCDcycle = 0; + } + } + + if (input.fire && (tech.isEnergyHealth ? m.energy >= 0.11 : m.health >= 0.11)) { + if (tech.isEnergyHealth) { + m.energy -= 0.004; } else { - if (this.sword && (tech.isEnergyHealth ? m.energy >= 0.11 : m.health >= 0.11)) { - let handle; - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].customName == "handle") { - handle = bullet[i]; - } + m.health -= 0.001; + m.displayHealth(); + } + } + if (input.fire && (tech.isEnergyHealth ? m.energy >= 0.11 : m.health >= 0.11)) { + if (!this.sword && b.guns[b.activeGun].name === 'sword') { + ({ sword: this.sword, bladeSegments: this.bladeSegments } = this.createAndSwingSword()); + this.angle = m.angle; + } + } + if (this.sword && !input.fire) { + this.cycle = 0; + Matter.Body.setAngularVelocity(this.sword, 0); + player.force.x *= 0.01; + player.force.y *= 0.01; + Composite.remove(engine.world, this.sword); + this.sword.parts.forEach(part => { + Composite.remove(engine.world, part); + const index = bullet.indexOf(part); + if (index !== -1) { + bullet.splice(index, 1); + } + }); + this.sword = undefined; + if (this.constraint) { + Composite.remove(engine.world, this.constraint); + this.constraint = undefined; + } + this.bladeTrails = []; + m.fireCDcycle = m.cycle + 10; + } else { + if (this.sword && (tech.isEnergyHealth ? m.energy >= 0.11 : m.health >= 0.11)) { + let handle; + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].customName == "handle") { + handle = bullet[i]; } - if (!(this.angle > -Math.PI / 2 && this.angle < Math.PI / 2)) { - Matter.Body.setAngularVelocity(this.sword, -Math.PI * 0.1); - } else { - Matter.Body.setAngularVelocity(this.sword, Math.PI * 0.1); - } - if (!this.constraint && (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2)) { - this.constraint = Constraint.create({ - bodyA: player, - bodyB: this.sword, - pointB: { x: -9, y: ((handle.position.y - this.sword.position.y)) }, - stiffness: 0.1, - damping: 0.0001815, - length: 0, + } + if (tech.infinityEdge) { + const newSize = Math.sqrt(0.5 * m.health) + 1; + Matter.Body.scale(this.sword, newSize * (1 / (this.sword.scale == undefined ? 1 : this.sword.scale)), newSize * (1 / (this.sword.scale == undefined ? 1 : this.sword.scale)), handle.position); + this.sword.scale = newSize; + } + if (!(this.angle > -Math.PI / 2 && this.angle < Math.PI / 2)) { + Matter.Body.setAngularVelocity(this.sword, -Math.PI * 0.1); + } else { + Matter.Body.setAngularVelocity(this.sword, Math.PI * 0.1); + } + if (tech.sizeIllusion) { + player.force.x += Math.cos(m.angle) * player.mass / 500; + player.force.y += Math.sin(m.angle) * player.mass / 500; + } + if (!this.constraint && (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2)) { + this.constraint = Constraint.create({ + pointA: player.position, + bodyB: this.sword, + pointB: { x: -9, y: ((handle.position.y - this.sword.position.y)) }, + stiffness: (tech.infinityEdge ? 0.05 : 0.1), + damping: 0.001815, + length: 0, - }); - Composite.add(engine.world, this.constraint); - } else if (!this.constraint) { - this.constraint = Constraint.create({ - bodyA: player, - bodyB: this.sword, - pointB: { x: 9, y: ((handle.position.y - this.sword.position.y)) }, - stiffness: 0.1, - damping: 0.0001815, - length: 0, - }); - Composite.add(engine.world, this.constraint); - } - } else if (this.sword) { - if (tech.isEnergyHealth) { - m.energy = 0.01; - m.immuneCycle = m.cycle + 30; - } - this.cycle = 0; - Matter.Body.setAngularVelocity(this.sword, 0); - Matter.Body.setMass(player, 5) - Composite.remove(engine.world, this.sword); - this.sword.parts.forEach(part => { - Composite.remove(engine.world, part); - const index = bullet.indexOf(part); - if (index !== -1) { - bullet.splice(index, 1); - } }); - this.sword = undefined; - if (this.constraint) { - Composite.remove(engine.world, this.constraint); - this.constraint = undefined; - } - this.bladeTrails = []; - m.fireCDcycle = 0; + Composite.add(engine.world, this.constraint); + } else if (!this.constraint) { + this.constraint = Constraint.create({ + pointA: player.position, + bodyB: this.sword, + pointB: { x: 9, y: ((handle.position.y - this.sword.position.y)) }, + stiffness: (tech.infinityEdge ? 0.05 : 0.1), + damping: 0.001815, + length: 0, + }); + Composite.add(engine.world, this.constraint); } + } else if (this.sword) { + if (tech.isEnergyHealth) { + m.energy = 0.01; + m.immuneCycle = m.cycle + 30; + } + this.cycle = 0; + Matter.Body.setAngularVelocity(this.sword, 0); + player.force.x *= 0.01; + player.force.y *= 0.01; + Composite.remove(engine.world, this.sword); + this.sword.parts.forEach(part => { + Composite.remove(engine.world, part); + const index = bullet.indexOf(part); + if (index !== -1) { + bullet.splice(index, 1); + } + }); + this.sword = undefined; + if (this.constraint) { + Composite.remove(engine.world, this.constraint); + this.constraint = undefined; + } + this.bladeTrails = []; + m.fireCDcycle = 0; } - if (this.sword) { - for (let i = 0; i < this.bladeSegments.length; i++) { - const blade = this.bladeSegments[i]; - const trail = this.bladeTrails[i] || []; - const vertices = blade.vertices.map(vertex => ({ x: vertex.x, y: vertex.y })); - trail.push(vertices); - if (trail.length > 10) { - trail.shift(); - } - this.bladeTrails[i] = trail; + } + }, + createAndSwingSword(x = player.position.x, y = player.position.y, angle = m.angle) { + const handleWidth = 20; + const handleHeight = 150; + const handle = Bodies.rectangle(x, y, handleWidth, handleHeight, spawn.propsIsNotHoldable); + bullet[bullet.length] = handle; + handle.customName = "handle"; + bullet[bullet.length - 1].do = () => { }; + const pommelWidth = 30; + const pommelHeight = 40; + const pommelVertices = [ + { x: x, y: y + handleHeight / 2 + pommelHeight / 2 }, + { x: x + pommelWidth / 2, y: y + handleHeight / 2 }, + { x: x, y: y + handleHeight / 2 - pommelHeight / 2 }, + { x: x - pommelWidth / 2, y: y + handleHeight / 2 }, + ]; + const pommel = Bodies.fromVertices(x, y + handleHeight / 2, pommelVertices, spawn.propsIsNotHoldable); + bullet[bullet.length] = pommel; + bullet[bullet.length - 1].do = () => { }; + if (tech.soundSword) { + bullet[bullet.length - 1].draw = () => { }; + } + // Blade setup + const bladeWidth = 100 * (tech.soundSword ? 3 : 1); + const bladeHeight = 20 * (tech.soundSword ? 3 : 1); + const numBlades = 15; + const extensionFactor = 5; + const bladeSegments = []; + + if ((angle > -Math.PI / 2 && angle < Math.PI / 2)) { + for (let i = 0; i < numBlades; i++) { + const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; + const bladeX = x + i * (bladeWidth / 20); + const bladeY = y - handleHeight / 2 - i * (bladeHeight / 4.5) * extensionFactor; + + const vertices = [ + { x: bladeX, y: bladeY - bladeHeight / 2 }, + { x: bladeX + bladeWidth / 2, y: bladeY + bladeHeight / 2 }, + { x: bladeX - bladeWidth / 2, y: bladeY + bladeHeight / 2 }, + { x: bladeX, y: bladeY - bladeHeight / 2 + 10 }, + ]; + + const blade = Bodies.fromVertices(bladeX, bladeY, vertices, spawn.propsIsNotHoldable); + bullet[bullet.length] = blade; + bullet[bullet.length - 1].do = () => { }; + if (tech.soundSword) { + bullet[bullet.length - 1].draw = () => { }; } - - for (let i = 0; i < this.bladeTrails.length; i++) { - const trail = this.bladeTrails[i]; - - const alphaStep = 1 / trail.length; - let alpha = 0; - - for (let j = 0; j < trail.length; j++) { - const vertices = trail[j]; - ctx.beginPath(); - ctx.moveTo(vertices[0].x, vertices[0].y); - - for (let k = 1; k < vertices.length; k++) { - ctx.lineTo(vertices[k].x, vertices[k].y); - }; - - alpha += alphaStep; - ctx.closePath(); - if (tech.isEnergyHealth) { - const eyeColor = m.fieldMeterColor; - const r = eyeColor[1]; - const g = eyeColor[2]; - const b = eyeColor[3]; - const color = `#${r}${r}${g}${g}${b}${b}${Math.round(alpha * 255).toString(16).padStart(2, '0')}`; - ctx.fillStyle = color; - } else { - ctx.fillStyle = `rgba(220, 20, 60, ${alpha})`; - } - ctx.fill(); - } + Matter.Body.rotate(blade, -Math.sin(i * (Math.PI / 270) * 15)); + bladeSegments.push(blade); + } + } else { + for (let i = 0; i < numBlades; i++) { + const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; + const mirroredBladeX = x - i * (bladeWidth / 20); + const mirroredBladeY = y - handleHeight / 2 - i * (bladeHeight / 4.5) * extensionFactor; + const mirroredVertices = [ + { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 }, + { x: mirroredBladeX + bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, + { x: mirroredBladeX - bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, + { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 + 10 }, + ]; + const mirroredBlade = Bodies.fromVertices(mirroredBladeX, mirroredBladeY, mirroredVertices, spawn.propsIsNotHoldable); + bullet[bullet.length] = mirroredBlade; + bullet[bullet.length - 1].do = () => { }; + if (tech.soundSword) { + bullet[bullet.length - 1].draw = () => { }; } - for (let i = 0; i < this.bladeSegments.length; i++) { + Matter.Body.rotate(mirroredBlade, Math.sin(i * (Math.PI / 270) * 15)); + bladeSegments.push(mirroredBlade); + } + } + bladeSegments.push(pommel); + const sword = Body.create({ + parts: [handle, ...bladeSegments], + }); + + Composite.add(engine.world, sword); + Matter.Body.setPosition(sword, { x, y }); + + sword.collisionFilter.category = cat.bullet; + sword.collisionFilter.mask = cat.mobBullet | cat.powerup | cat.mob; + Body.scale(sword, -1, 1, { x, y }); + + return { sword, bladeSegments }; + }, + renderDefault() { + if (this.sword) { + for (let i = 0; i < this.bladeSegments.length; i++) { + const blade = this.bladeSegments[i]; + const trail = this.bladeTrails[i] || []; + const vertices = blade.vertices.map(vertex => ({ x: vertex.x, y: vertex.y })); + trail.push(vertices); + if (trail.length > 10) { + trail.shift(); + } + this.bladeTrails[i] = trail; + } + + for (let i = 0; i < this.bladeTrails.length; i++) { + const trail = this.bladeTrails[i]; + + const alphaStep = 1 / trail.length; + let alpha = 0; + + for (let j = 0; j < trail.length; j++) { + const vertices = trail[j]; ctx.beginPath(); - ctx.lineJoin = "miter"; - ctx.miterLimit = 100; - ctx.strokeStyle = tech.isEnergyHealth ? m.fieldMeterColor : tech.isAmmoSword ? "#c0c0c0" : "crimson"; - ctx.lineWidth = 5; - ctx.fillStyle = "black"; - ctx.moveTo(this.bladeSegments[i].vertices[0].x, this.bladeSegments[i].vertices[0].y); - for (let j = 0; j < this.bladeSegments[i].vertices.length; j++) { - ctx.lineTo(this.bladeSegments[i].vertices[j].x, this.bladeSegments[i].vertices[j].y) + ctx.moveTo(vertices[0].x, vertices[0].y); + + for (let k = 1; k < vertices.length; k++) { + ctx.lineTo(vertices[k].x, vertices[k].y); }; + + alpha += alphaStep; ctx.closePath(); - ctx.stroke(); + if (tech.isEnergyHealth) { + const eyeColor = m.fieldMeterColor; + const r = eyeColor[1]; + const g = eyeColor[2]; + const b = eyeColor[3]; + const color = `#${r}${r}${g}${g}${b}${b}${Math.round(alpha * 255).toString(16).padStart(2, '0')}`; + ctx.fillStyle = color; + } else { + ctx.fillStyle = `rgba(220, 20, 60, ${alpha})`; + } ctx.fill(); - ctx.lineJoin = "round"; - ctx.miterLimit = 10; } } - if (this.sword) { - for (let i = 0; i < mob.length; i++) { - if (Matter.Query.collides(this.sword, [mob[i]]).length > 0) { - const dmg = m.dmgScale * 6 * Math.sqrt(this.sword.speed); - if (m.health < 0.9) { + for (let i = 0; i < this.bladeSegments.length; i++) { + ctx.beginPath(); + ctx.lineJoin = "miter"; + ctx.miterLimit = 100; + ctx.strokeStyle = tech.isEnergyHealth ? m.fieldMeterColor : tech.isAmmoSword ? "#c0c0c0" : "crimson"; + ctx.lineWidth = 5; + ctx.fillStyle = "black"; + ctx.moveTo(this.bladeSegments[i].vertices[0].x, this.bladeSegments[i].vertices[0].y); + for (let j = 0; j < this.bladeSegments[i].vertices.length; j++) { + ctx.lineTo(this.bladeSegments[i].vertices[j].x, this.bladeSegments[i].vertices[j].y) + }; + ctx.closePath(); + ctx.stroke(); + ctx.fill(); + ctx.lineJoin = "round"; + ctx.miterLimit = 10; + } + } + }, + collision() { + if (this.sword) { + for (let i = 0; i < mob.length; i++) { + if (Matter.Query.collides(this.sword, [mob[i]]).length > 0) { + const dmg = m.dmgScale * Math.sqrt(this.sword.speed) * (tech.sizeIllusion ? 1.1 : 1) * (tech.isStabSword ? 1.5 : 1) * (tech.infinityEdge ? 1.1 : 1); + if (!tech.soundSword) { + if (m.health < m.maxHealth) { if (tech.isEnergyHealth) { m.energy += 0.04; } else { - m.health += 0.001 * (dmg - mob[i].health); - if (m.health > m.maxHealth) m.health = m.maxHealth; + m.health += 0.01 * (dmg - mob[i].health); m.displayHealth(); } } else { @@ -31569,98 +32380,33 @@ const level = { m.displayHealth(); } } - mob[i].damage(dmg, true); - simulation.drawList.push({ - x: mob[i].position.x, - y: mob[i].position.y, - radius: Math.sqrt(dmg / this.sword.speed) * 50, - color: simulation.mobDmgColor, - time: simulation.drawTime - }); - const angle = Math.atan2(mob[i].position.y - this.sword.position.y, mob[i].position.x - this.sword.position.x); - this.sword.force.x -= Math.cos(angle) * 5; - this.sword.force.y -= Math.sin(angle) * 5; - break } + mob[i].damage(dmg, true); + simulation.drawList.push({ + x: mob[i].position.x, + y: mob[i].position.y, + radius: Math.abs(Math.log(dmg * this.sword.speed) * 40 * mob[i].damageReduction + 3), + color: (tech.soundSword ? "rgba(0, 0, 0, 0.3)" : simulation.mobDmgColor), + time: simulation.drawTime + }); + break } } - }, - createAndSwingSword(x = player.position.x, y = player.position.y, angle = m.angle) { - if (this.cycle < m.cycle) { - this.cycle = Infinity; - m.fireCDcycle = Infinity; - const handleWidth = 20; - const handleHeight = 150; - const handle = Bodies.rectangle(x, y, handleWidth, handleHeight, spawn.propsIsNotHoldable); - bullet[bullet.length] = handle; - handle.customName = "handle"; - bullet[bullet.length - 1].do = () => { }; - const bladeWidth = 100; - const bladeHeight = 20; - const numBlades = 15; - const extensionFactor = 5; - const bladeSegments = []; - if ((angle > -Math.PI / 2 && angle < Math.PI / 2)) { - for (let i = 0; i < numBlades; i++) { - const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; - const bladeX = x + i * (bladeWidth / 20); - const bladeY = y - handleHeight / 2 - i * (bladeHeight / 4.5) * extensionFactor; - - const vertices = [ - { x: bladeX, y: bladeY - bladeHeight / 2 }, - { x: bladeX + bladeWidth / 2, y: bladeY + bladeHeight / 2 }, - { x: bladeX - bladeWidth / 2, y: bladeY + bladeHeight / 2 }, - { x: bladeX, y: bladeY - bladeHeight / 2 + 10 }, - ]; - - const blade = Bodies.fromVertices(bladeX, bladeY, vertices, spawn.propsIsNotHoldable); - bullet[bullet.length] = blade; - bullet[bullet.length - 1].do = () => { }; - Matter.Body.rotate(blade, -Math.sin(i * (Math.PI / 270) * 15)); - bladeSegments.push(blade); - } - } else { - for (let i = 0; i < numBlades; i++) { - const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; - const mirroredBladeX = x - i * (bladeWidth / 20); - const mirroredBladeY = y - handleHeight / 2 - i * (bladeHeight / 4.5) * extensionFactor; - const mirroredVertices = [ - { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 }, - { x: mirroredBladeX + bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, - { x: mirroredBladeX - bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, - { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 + 10 }, - ]; - const mirroredBlade = Bodies.fromVertices(mirroredBladeX, mirroredBladeY, mirroredVertices, spawn.propsIsNotHoldable); - bullet[bullet.length] = mirroredBlade; - bullet[bullet.length - 1].do = () => { }; - Matter.Body.rotate(mirroredBlade, Math.sin(i * (Math.PI / 270) * 15)); - bladeSegments.push(mirroredBlade); - } - } - const sword = Body.create({ - parts: [handle, ...bladeSegments], - }); - - Composite.add(engine.world, sword); - Matter.Body.setPosition(sword, { x, y }); - - sword.collisionFilter.category = cat.bullet; - sword.collisionFilter.mask = cat.mobBullet | cat.mob; - Body.scale(sword, -1, 1, { x, y }); - // sword.frictionAir -= 0.01; - - return { sword, bladeSegments }; + if (Matter.Query.collides(this.sword, [genisis]).length > 0) { + m.damage(-0.0142) //balanced! } - }, - fire() { } - }; - b.guns.push(e); - const gunArray = b.guns.filter( - (obj, index, self) => - index === self.findIndex((item) => item.name === obj.name) - ); - b.guns = gunArray; - })(); + } + } + }; + b.guns.push(e); + const gunArray = b.guns.filter( + (obj, index, self) => + index === self.findIndex((item) => item.name === obj.name) + ); + b.guns = gunArray; + } else { + simulation.makeTextLog(`Thank you for using my sword mod
I'll save you the trouble of killing genisis
g.damage(Infinity)
`); + g.damage(Infinity); } simulation.makeTextLog(`arena by Richard0820`); let index = 0; @@ -31673,21 +32419,13 @@ const level = { level.exit.y = -2530; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit - level.defaultZoom = 8000 + level.defaultZoom = 2000 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#987654"; color.map = "#765432" //custom map color color.block = "#876543"; door.isClosing = true; door2.isClosing = true; - spawnStuff(1000, -3100, 4450, 3125, 50 / simulation.difficultyMode); - spawnStuff(5400, -2425, 200, 2250, 5 / simulation.difficultyMode); - spawnStuff(5625, -2425, 2000, 275, 5 / simulation.difficultyMode); - spawnStuff(5625, -2125, 850, 1125, 5 / simulation.difficultyMode); - spawnStuff(6500, -2150, 475, 650, 5 / simulation.difficultyMode); - spawnStuff(7000, -2125, 325, 275, 5 / simulation.difficultyMode); - spawnStuff(5650, -950, 300, 450, 5 / simulation.difficultyMode); - spawn.randomLevelBoss(4225, -575); for (let i = 0; i < 5; i++) { powerUps.spawn(-6075, -2000, "heal"); } @@ -31727,7 +32465,7 @@ const level = { this.maxLife = this.life; } - draw(ctx) { + draw() { ctx.beginPath(); ctx.fillStyle = `rgba(255, 255, 255, ${this.alpha})`; ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); @@ -31786,7 +32524,10 @@ const level = { system.update(); } function draw() { - system.particles.forEach(particle => particle.draw(ctx)); + system.particles.forEach(particle => particle.draw()); + } + for (let i = 0; i < system.particles.length; i++) { + system.particles[i].life = 0; } level.custom = () => { update(); @@ -31856,7 +32597,7 @@ const level = { } } index2++; - simulation.makeTextLog(`If you want to keep this sword, visit https://github.com/Whyisthisnotavalable/n-scythe. The sword is there.`) + setTimeout(() => { simulation.makeTextLog(`If you want to keep this sword, visit https://github.com/Whyisthisnotavalable/n-scythe. The sword is there.`) }, 1000) } for (let i = 0; i < bladeSegments.length; i++) { const blade = bladeSegments[i]; @@ -31934,7 +32675,419 @@ const level = { } }; level.customTopLayer = () => { }; + simulation.ephemera.push({ + name: "genesis", + death: false, + pwuspawn: 0, + do() { + if (this.death === true) { + b.explosion(g.pos, 200 * Math.random(), "#000000") + setTimeout(() => { + if (this.pwuspawn === 0) { + powerUps.spawnBossPowerUp(g.pos.x, g.pos.y) + this.pwuspawn++; + } + simulation.removeEphemera(this.name); + simulation.removeEphemera("genisisScythe"); + }, 1000); + } + if (g.health >= 0) { + if (g.health < g.maxHealth) { + g.health++; + } + const dist = Matter.Vector.magnitudeSquared(Matter.Vector.sub(genisis.position, player.position)); + const time = Math.sqrt(dist) / 60; + g.alive = true; + g.targetX = m.pos.x + player.velocity.x * time; + g.targetY = m.pos.y + player.velocity.y * time; + } else { + this.death = true; + g.alive = false; + } + if (g.alive) { + g.cycle++; + g.move() + g.lookDefault(); + g.drawDefault(); + g.drawHealth(); + genisis.force.y += g.mass * simulation.g + g.setFillColors(); + control.right = g.angle > -Math.PI * 2 / 5 && g.angle < Math.PI * 2 / 5; + control.left = g.angle > Math.PI * 3 / 5 || g.angle < -Math.PI * 3 / 5; + control.down = g.angle > Math.PI * 3 / 10 && g.angle < Math.PI * 7 / 10; + if (Matter.Query.collides(genisis, body).length || Matter.Query.collides(genisisHead, map).length || Matter.Query.collides(genisisBodySensor, map).length && !control.down) { + if (g.buttonCD_jump < g.cycle) { + g.jump(); + } + } + control.up = g.angle > -Math.PI * 6 / 10 && g.angle < -Math.PI * 4 / 10; + if (g.onGround) { + g.groundControl() + } else { + g.airControl() + } + + if (g.pos.y > simulation.fallHeight) { + Matter.Body.setPosition(genisis, { + x: level.exit.x, + y: level.exit.y + }) + } + } else { + genisis.collisionFilter.mask = cat.map | cat.body; + Matter.Body.setPosition(genisis, { + x: 0, + y: 0 + }) + } + + if (simulation.testing) { + ctx.beginPath(); + let bodyDraw = genisisJumpSensor.vertices; + ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); + for (let j = 1; j < bodyDraw.length; ++j) { + ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); + } + ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); + ctx.fillStyle = "rgba(255, 0, 0, 0.5)"; + ctx.fill(); + + ctx.beginPath(); + bodyDraw = genisisBody.vertices; + ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); + for (let j = 1; j < bodyDraw.length; ++j) { + ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); + } + ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); + ctx.fillStyle = "rgba(0, 255, 255, 0.25)"; + ctx.fill(); + + ctx.beginPath(); + bodyDraw = genisisHead.vertices; + ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); + for (let j = 1; j < bodyDraw.length; ++j) { + ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); + } + ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); + ctx.fillStyle = "rgba(255, 255, 0, 0.4)"; + ctx.fill(); + + ctx.beginPath(); + bodyDraw = genisisHeadSensor.vertices; + ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); + for (let j = 1; j < bodyDraw.length; ++j) { + ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); + } + ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); + ctx.fillStyle = "rgba(0, 0, 255, 0.25)"; + ctx.fill(); + + ctx.beginPath(); + bodyDraw = genisisBodySensor.vertices; + ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); + for (let j = 1; j < bodyDraw.length; ++j) { + ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); + } + ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); + ctx.fillStyle = "rgba(255, 0, 255, 0.25)"; + ctx.fill(); + } + + Events.on(engine, "collisionStart", function (event) { + g.genisisOnGroundCheck(event); + GenisisCollisionChecks(event); + }); + Events.on(engine, "collisionActive", function (event) { + g.genisisOnGroundCheck(event); + }); + Events.on(engine, "collisionEnd", function (event) { + g.genisisOffGroundCheck(event); + }); + } + }) + let evo = { + isLongBlade: true, + isScytheRange: true, + scytheRange: 3, + isScytheRad: false, + scytheRad: 0, + isDoubleScythe: false, + isPhaseScythe: false, + isMeleeScythe: false, + isStunScythe: false, + }; + simulation.ephemera.push({ + name: "genisisScythe", + cycle: 0, + scythe: undefined, + bladeSegments: undefined, + bladeTrails: [], + angle: 0, + constraint: undefined, + fireCD: 0, + do() { + if (isOwned) { + if (g.health < 500 && g.health > 200) { + evo = { + isLongBlade: true, + isScytheRange: true, + scytheRange: 3, + isScytheRad: false, + scytheRad: 1, + isDoubleScythe: true, + isPhaseScythe: false, + isMeleeScythe: false, + isStunScythe: false, + }; + } else if (g.health < 200 && g.health > 50) { + evo = { + isLongBlade: true, + isScytheRange: true, + scytheRange: 3, + isScytheRad: true, + scytheRad: 1, + isDoubleScythe: true, + isPhaseScythe: true, + isMeleeScythe: true, + isStunScythe: true, + }; + } else if (g.health < 50) { + evo = { + isLongBlade: true, + isScytheRange: true, + scytheRange: 9, + isScytheRad: true, + scytheRad: 6, + isDoubleScythe: true, + isPhaseScythe: true, + isMeleeScythe: true, + isStunScythe: true, + }; + } + if (g.cycle > this.fireCD && !this.scythe) { + this.fireCD = g.cycle + 30; + if (!this.scythe) { + ({ scythe: this.scythe, bladeSegments: this.bladeSegments } = this.createAndSwingScythe()); + this.angle = g.angle; + } + } + if (this.scythe && g.cycle > this.cycle + 30 || !g.alive && this.scythe) { + Composite.remove(engine.world, this.scythe); + this.scythe.parts.forEach(part => { + Composite.remove(engine.world, part); + const index = bullet.indexOf(part); + if (index !== -1) { + bullet.splice(index, 1); + } + }); + this.scythe = undefined; + this.bladeTrails = []; + } else { + if (this.scythe && !evo.isMeleeScythe) { + if (!(this.angle > -Math.PI / 2 && this.angle < Math.PI / 2)) { + Matter.Body.setAngularVelocity(this.scythe, -Math.PI * 0.15 - (evo.scytheRad ? evo.scytheRad * 0.1 : 0)); + } else { + Matter.Body.setAngularVelocity(this.scythe, Math.PI * 0.15 + (evo.scytheRad ? evo.scytheRad * 0.1 : 0)); + } + Matter.Body.setVelocity(this.scythe, { + x: Math.cos(this.angle) * 30, + y: Math.sin(this.angle) * 30 + }); + } else if (this.scythe && evo.isMeleeScythe) { + if (!(this.angle > -Math.PI / 2 && this.angle < Math.PI / 2)) { + Matter.Body.setAngularVelocity(this.scythe, -Math.PI * 0.1 + (evo.isStunScythe ? 0.1 : 0)); + } else { + Matter.Body.setAngularVelocity(this.scythe, Math.PI * 0.1 - (evo.isStunScythe ? 0.1 : 0)); + } + Matter.Body.setPosition(this.scythe, genisis.position); + } + } + if (this.scythe) { + for (let i = 0; i < this.bladeSegments.length; i++) { + const blade = this.bladeSegments[i]; + const trail = this.bladeTrails[i] || []; + const vertices = blade.vertices.map(vertex => ({ x: vertex.x, y: vertex.y })); + trail.push(vertices); + if (trail.length > 10) { + trail.shift(); + } + this.bladeTrails[i] = trail; + } + for (let i = 0; i < this.bladeTrails.length; i++) { + const trail = this.bladeTrails[i]; + + const alphaStep = 1 / trail.length; + let alpha = 0; + + for (let j = 0; j < trail.length; j++) { + const vertices = trail[j]; + ctx.beginPath(); + ctx.moveTo(vertices[0].x, vertices[0].y); + + for (let k = 1; k < vertices.length; k++) { + ctx.lineTo(vertices[k].x, vertices[k].y); + }; + + alpha += alphaStep; + ctx.closePath(); + ctx.fillStyle = `rgba(100, 20, 255, ${alpha})`; + ctx.fill(); + } + } + for (let i = 0; i < this.bladeSegments.length; i++) { + ctx.beginPath(); + ctx.lineJoin = "miter"; + ctx.miterLimit = 100; + ctx.strokeStyle = `rgb(100, 20, 255)`; + ctx.lineWidth = 5; + ctx.fillStyle = "black"; + ctx.moveTo(this.bladeSegments[i].vertices[0].x, this.bladeSegments[i].vertices[0].y); + for (let j = 0; j < this.bladeSegments[i].vertices.length; j++) { + ctx.lineTo(this.bladeSegments[i].vertices[j].x, this.bladeSegments[i].vertices[j].y) + }; + ctx.closePath(); + ctx.stroke(); + ctx.fill(); + ctx.lineJoin = "round"; + ctx.miterLimit = 10; + } + } + if (this.scythe) { + for (let i = 0; i < mob.length; i++) { + if (Matter.Query.collides(this.scythe, [mob[i]]).length > 0) { + const dmg = m.dmgScale * 0.12 * 2.73 * (evo.isLongBlade ? 1.3 : 1) * (evo.scytheRange ? evo.scytheRange * 1.15 : 1) * (evo.isDoubleScythe ? 0.9 : 1) * (evo.scytheRad ? evo.scytheRad * 1.5 : 1); + mob[i].damage(dmg, true); + simulation.drawList.push({ + x: mob[i].position.x, + y: mob[i].position.y, + radius: Math.sqrt(dmg) * 50, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + if (!evo.isMeleeScythe) { + const angle = Math.atan2(mob[i].position.y - this.scythe.position.y, mob[i].position.x - this.scythe.position.x); + this.scythe.force.x += Math.cos(angle) * 2; + this.scythe.force.y += Math.sin(angle) * 2; + } + if (evo.isStunScythe) { + mobs.statusStun(mob[i], 90); + } + break + } + } + if (Matter.Query.collides(this.scythe, [player]).length > 0 && m.immuneCycle < m.cycle) { + const dmg = 0.02 * (evo.isLongBlade ? 1.3 : 1) * (evo.scytheRange ? evo.scytheRange * 1.15 : 1) * (evo.isDoubleScythe ? 0.9 : 1) * (evo.scytheRad ? evo.scytheRad * 1.5 : 1); // actual scythe scallings one tap the player so this is nerfed for genisis + m.damage(dmg); + m.immuneCycle = m.cycle + 10; + simulation.drawList.push({ + x: player.position.x, + y: player.position.y, + radius: Math.sqrt(dmg) * 50, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + if (!evo.isMeleeScythe) { + const angle = Math.atan2(player.position.y - this.scythe.position.y, player.position.x - this.scythe.position.x); + this.scythe.force.x += Math.cos(angle) * 2; + this.scythe.force.y += Math.sin(angle) * 2; + } + } + } + } + }, + createAndSwingScythe(x = genisis.position.x, y = genisis.position.y, angle = g.angle) { + this.cycle = g.cycle + 60 + (evo.scytheRange * 6); + const handleWidth = 20; + const handleHeight = 200 + (evo.isLongBlade ? 30 : 0) + (evo.isMeleeScythe ? 200 : 0); + const handle = Bodies.rectangle(x, y, handleWidth, handleHeight, spawn.propsIsNotHoldable); + bullet[bullet.length] = handle; + bullet[bullet.length - 1].do = () => { }; + const bladeWidth = 100; + const bladeHeight = 20; + const numBlades = 10 + (evo.isLongBlade ? 1 : 0) + (evo.isMeleeScythe ? 3 : 0); + const extensionFactor = 5.5; + const bladeSegments = []; + if (!evo.isDoubleScythe) { + for (let i = 0; i < numBlades; i++) { + const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; + const bladeX = x - handleWidth / 2 + i * (bladeWidth / 2) - extensionFactorFraction * (bladeWidth / 2); + const bladeY = y + handleHeight / 2 - i * (bladeHeight / (3 ** i)); + + const vertices = [ + { x: bladeX, y: bladeY - bladeHeight / 2 }, + { x: bladeX + bladeWidth / 2, y: bladeY + bladeHeight / 2 }, + { x: bladeX - bladeWidth / 2, y: bladeY + bladeHeight / 2 }, + { x: bladeX, y: bladeY - bladeHeight / 2 + 10 }, + ]; + + const blade = Bodies.fromVertices(bladeX, bladeY, vertices, spawn.propsIsNotHoldable); + bullet[bullet.length] = blade; + bullet[bullet.length - 1].do = () => { }; + Matter.Body.rotate(blade, -Math.sin(i * (Math.PI / 180) * 5)); + bladeSegments.push(blade); + } + } else { + for (let i = 0; i < numBlades; i++) { + const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; + const bladeX = x - handleWidth / 2 + i * (bladeWidth / 2) - extensionFactorFraction * (bladeWidth / 2); + const bladeY = y + handleHeight / 2 - i * (bladeHeight / (3 ** i)); + + const vertices = [ + { x: bladeX, y: bladeY - bladeHeight / 2 }, + { x: bladeX + bladeWidth / 2, y: bladeY + bladeHeight / 2 }, + { x: bladeX - bladeWidth / 2, y: bladeY + bladeHeight / 2 }, + { x: bladeX, y: bladeY - bladeHeight / 2 + 10 }, + ]; + + const blade = Bodies.fromVertices(bladeX, bladeY, vertices, spawn.propsIsNotHoldable); + bullet[bullet.length] = blade; + bullet[bullet.length - 1].do = () => { }; + Matter.Body.rotate(blade, -Math.sin(i * (Math.PI / 180) * 5)); + bladeSegments.push(blade); + } + + for (let i = 0; i < numBlades; i++) { + const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; + const bladeX = x + handleWidth / 2 - i * (bladeWidth / 2) + extensionFactorFraction * (bladeWidth / 2); + const bladeY = y - handleHeight / 2 - i * (bladeHeight / (3 ** i)); + + const vertices = [ + { x: bladeX, y: bladeY - bladeHeight / 2 }, + { x: bladeX + bladeWidth / 2, y: bladeY + bladeHeight / 2 }, + { x: bladeX - bladeWidth / 2, y: bladeY + bladeHeight / 2 }, + { x: bladeX, y: bladeY - bladeHeight / 2 + 10 }, + ]; + + const blade = Bodies.fromVertices(bladeX, bladeY, vertices, spawn.propsIsNotHoldable); + bullet[bullet.length] = blade; + bullet[bullet.length - 1].do = () => { }; + Matter.Body.rotate(blade, -Math.sin(i * (Math.PI / 180) * 5) + Math.PI); + bladeSegments.push(blade); + } + } + const scythe = Body.create({ + parts: [handle, ...bladeSegments], + }); + Composite.add(engine.world, scythe); + Matter.Body.setPosition(scythe, { x, y }); + + scythe.collisionFilter.category = cat.body; + scythe.collisionFilter.mask = cat.mobBullet | cat.player; + if (!evo.isMeleeScythe) { + setTimeout(() => { + scythe.collisionFilter.mask = cat.mobBullet | cat.mob | cat.player; + }, 1000) + } + if ((angle > -Math.PI / 2 && angle < Math.PI / 2)) { + Body.scale(scythe, -1, 1, { x, y }); + } + + scythe.frictionAir -= 0.01; + + return { scythe, bladeSegments }; + }, + }) spawn.mapRect(-10000, 0, 20000, 2000); spawn.mapRect(-10000, -10000, 2000, 10000); spawn.mapRect(8000, -10000, 2000, 10000); @@ -31946,56 +33099,54 @@ const level = { spawn.mapRect(4000, -10, 100, 20); spawn.mapRect(-1000, -10000, 2000, 8000); - spawn.mapRect(-500, -10000, 1000, 9700); - function createSword(x = 0, y = 0, angle = 0) { //sword asthetic + // spawn.mapRect(-500, -10000, 1000, 9700); + function createSword(x = 0, y = 0) { //sword asthetic const handleWidth = 20; const handleHeight = 150; const handle = Bodies.rectangle(x, y, handleWidth, handleHeight, spawn.propsIsNotHoldable); bullet[bullet.length] = handle; handle.customName = "handle"; bullet[bullet.length - 1].do = () => { }; - const bladeWidth = 100; - const bladeHeight = 20; + const pommelWidth = 30; + const pommelHeight = 40; + const pommelVertices = [ + { x: x, y: y + handleHeight / 2 + pommelHeight / 2 }, + { x: x + pommelWidth / 2, y: y + handleHeight / 2 }, + { x: x, y: y + handleHeight / 2 - pommelHeight / 2 }, + { x: x - pommelWidth / 2, y: y + handleHeight / 2 }, + ]; + const pommel = Bodies.fromVertices(x, y + handleHeight / 2, pommelVertices, spawn.propsIsNotHoldable); + bullet[bullet.length] = pommel; + bullet[bullet.length - 1].do = () => { }; + if (tech.soundSword) { + bullet[bullet.length - 1].draw = () => { }; + } + // Blade setup + const bladeWidth = 100 * (tech.soundSword ? 3 : 1); + const bladeHeight = 20 * (tech.soundSword ? 3 : 1); const numBlades = 15; const extensionFactor = 5; const bladeSegments = []; - if ((angle > -Math.PI / 2 && angle < Math.PI / 2)) { - for (let i = 0; i < numBlades; i++) { - const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; - const bladeX = x + i * (bladeWidth / 20); - const bladeY = y - handleHeight / 2 - i * (bladeHeight / 4.5) * extensionFactor; - - const vertices = [ - { x: bladeX, y: bladeY - bladeHeight / 2 }, - { x: bladeX + bladeWidth / 2, y: bladeY + bladeHeight / 2 }, - { x: bladeX - bladeWidth / 2, y: bladeY + bladeHeight / 2 }, - { x: bladeX, y: bladeY - bladeHeight / 2 + 10 }, - ]; - - const blade = Bodies.fromVertices(bladeX, bladeY, vertices, spawn.propsIsNotHoldable); - bullet[bullet.length] = blade; - bullet[bullet.length - 1].do = () => { }; - Matter.Body.rotate(blade, -Math.sin(i * (Math.PI / 270) * 15)); - bladeSegments.push(blade); - } - } else { - for (let i = 0; i < numBlades; i++) { - const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; - const mirroredBladeX = x - i * (bladeWidth / 20); - const mirroredBladeY = y - handleHeight / 2 - i * (bladeHeight / 4.5) * extensionFactor; - const mirroredVertices = [ - { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 }, - { x: mirroredBladeX + bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, - { x: mirroredBladeX - bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, - { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 + 10 }, - ]; - const mirroredBlade = Bodies.fromVertices(mirroredBladeX, mirroredBladeY, mirroredVertices, spawn.propsIsNotHoldable); - bullet[bullet.length] = mirroredBlade; - bullet[bullet.length - 1].do = () => { }; - Matter.Body.rotate(mirroredBlade, Math.sin(i * (Math.PI / 270) * 15)); - bladeSegments.push(mirroredBlade); + for (let i = 0; i < numBlades; i++) { + const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; + const mirroredBladeX = x - i * (bladeWidth / 20); + const mirroredBladeY = y - handleHeight / 2 - i * (bladeHeight / 4.5) * extensionFactor; + const mirroredVertices = [ + { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 }, + { x: mirroredBladeX + bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, + { x: mirroredBladeX - bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, + { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 + 10 }, + ]; + const mirroredBlade = Bodies.fromVertices(mirroredBladeX, mirroredBladeY, mirroredVertices, spawn.propsIsNotHoldable); + bullet[bullet.length] = mirroredBlade; + bullet[bullet.length - 1].do = () => { }; + if (tech.soundSword) { + bullet[bullet.length - 1].draw = () => { }; } + Matter.Body.rotate(mirroredBlade, Math.sin(i * (Math.PI / 270) * 15)); + bladeSegments.push(mirroredBlade); } + bladeSegments.push(pommel); const sword = Body.create({ parts: [handle, ...bladeSegments], }); @@ -32004,28 +33155,12 @@ const level = { Matter.Body.setPosition(sword, { x, y }); sword.collisionFilter.category = cat.bullet; - sword.collisionFilter.mask = cat.bullet; + sword.collisionFilter.mask = cat.mobBullet | cat.powerup | cat.mob; Body.scale(sword, -1, 1, { x, y }); - Body.rotate(sword, Math.PI / 1.05) - sword.frictionAir = -0.01; + Body.rotate(sword, Math.PI + Math.PI / 15) return { sword, bladeSegments }; } - function spawnStuff(x, y, width, height, num) { - for (let i = 0; i < num; i++) { - randomMob(x + width * Math.random(), y + height * Math.random(), Infinity) - } - } - function randomMob(x, y, chance = 1) { - if (spawn.spawnChance(chance) || chance === Infinity) { - const pick = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]; - spawn[pick](x, y); - } - if (tech.isDuplicateMobs && Math.random() < tech.duplicationChance()) { - const pick = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]; - spawn[pick](x, y); - } - } }, soft() { simulation.makeTextLog(``); @@ -32583,6 +33718,386 @@ const level = { let hardBody = body[body.length - 1]; hardBody.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.powerUp }, + flappyGon() { //community map by digin + level.announceMobTypes(); + simulation.makeTextLog(`flappy n-gon by Digin`); + setTimeout(() => { simulation.makeTextLog("gravity is a choice"); }, 1000); + setTimeout(() => { simulation.makeTextLog("everyone will fly"); }, 2000); + setTimeout(() => { simulation.makeTextLog("jump from the post and find out"); }, 3000); + level.setPosToSpawn(0, -50); //normal spawn + level.exit.x = 8600; + level.exit.y = -1100; + level.defaultZoom = 1800; + simulation.zoomTransition(level.defaultZoom); + document.body.style.backgroundColor = "#55FF55"; + + var slimey = level.hazard(-200, -10, 9000, 10); + + // allow "flight" + + const old_playerOffGroundCheck = playerOffGroundCheck; + + playerOffGroundCheck = (event) => { + old_playerOffGroundCheck(event); + if (player.position.y < -300) { + m.onGround = true; + } + }; + + const oldNextLevel = level.nextLevel; + level.nextLevel = () => { // clear the flappy effects, because apparently there's no established api for this + playerOffGroundCheck = old_playerOffGroundCheck; + + level.nextLevel = oldNextLevel; + oldNextLevel(); + }; + + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); // standard bumps + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); + spawn.mapRect(level.exit.x - 100, level.exit.y + 40, 200, 100); + + // room basis + spawn.mapRect(-200, 0, 9000, 100); + spawn.mapRect(-200, -1500, 9000, 100); + spawn.mapRect(-200, -1500, 100, 1500); + spawn.mapRect(8700, -1500, 100, 1500); + + // somewhat randomized flappy pylons + const pylon = 1500; // height of the entire pylon assembly + for (var i = 0; i < 10; i++) { + var xbasis = 700 + i * 750; + var window = 300 + (10 - i) * 50; + var toph = pylon - window - 400 + (Math.random() - 0.5) * 400 - i * 50; + if (i == 0) { // on the first one, the lower pile will always have a height of 300 + toph = pylon - window - 300; + } + spawn.mapRect(xbasis, -1500, 100, toph); + spawn.mapRect(xbasis, toph + window - pylon, 100, pylon - toph - window); + if (i < 9) { + spawn.randomMob(xbasis + 300, Math.random() * -1400); + } + else { + spawn.randomLevelBoss(xbasis + 300, Math.random() * -1400); + } + if (i == 5) { + spawn.secondaryBossChance(xbasis + 300, Math.random() * -1400); + } + } + + level.custom = () => { + level.exit.drawAndCheck(); + player.onGround = true; + level.enter.draw(); + }; + const slimeRise = 0.15; + level.customTopLayer = () => { + slimey.height += slimeRise; + slimey.min.y -= slimeRise; + slimey.query(); + }; + powerUps.addResearchToLevel(); + }, + rings() { + level.announceMobTypes(); + simulation.makeTextLog(`rings by ThatLittleFrog`); + setTimeout(() => { + simulation.makeTextLog("go up"); + }, 2000); + level.setPosToSpawn(0, -2000); // spawn high up so you can go to the bottom of the lowest ring without tripping the too-low reset + level.exit.x = 0; + level.exit.y = -6400; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit + level.defaultZoom = 1800; + simulation.zoomTransition(level.defaultZoom); + document.body.style.backgroundColor = "#d8dadf"; + + function mkrect(x, y, w, h) { + let who = body[body.length] = Bodies.rectangle(x, y, w, h, { + collisionFilter: { + category: cat.map, + mask: cat.body | cat.player | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + inertia: Infinity, //prevents rotation + isNotHoldable: true, + friction: 1, + frictionStatic: 1, + restitution: 0, + frictionAir: 1, + isStatic: true + }); + Composite.add(engine.world, who); + who.classType = "body"; + return body[body.length - 1]; + } + + function makeRing(x, y, linegth, thicc = 200) { // I don't feel like doing trigonometry, so linegth is slightly different from the radius + var _shape = [undefined, undefined, undefined, undefined]; + _shape[0] = mkrect(x - linegth / 2 - thicc, y - linegth / 2 - thicc, linegth, thicc); + _shape[1] = mkrect(x - linegth / 2 - thicc, y - linegth / 2, thicc, linegth); + _shape[2] = mkrect(x - linegth / 2, y + linegth / 2 - thicc, linegth, thicc); + _shape[3] = mkrect(x + linegth / 2 - thicc, y - linegth / 2 - thicc, thicc, linegth - thicc * 2); + let ret = { + shape: _shape, + x: x, + y: y, + r: 0, + rot(ang) { + this.r = ang; + let offs = 0; + for (let shape of this.shape) { + offs += Math.PI / 2; + Matter.Body.setAngle(shape, ang); + if (shape == this.shape[3]) { + Matter.Body.setPosition(shape, { + x: this.x + Math.cos(ang + offs) * (linegth / 2 - thicc / 2) - Math.cos(ang + offs + Math.PI / 2) * thicc, + y: this.y + Math.sin(ang + offs) * (linegth / 2 - thicc / 2) - Math.sin(ang + offs + Math.PI / 2) * thicc + }); + } + else { + Matter.Body.setPosition(shape, { + x: this.x + Math.cos(ang + offs) * (linegth / 2 - thicc / 2), + y: this.y + Math.sin(ang + offs) * (linegth / 2 - thicc / 2) + }); + } + } + }, + rotBy(ang) { + this.rot(this.r + ang); + } + }; + ret.rot(0); + return ret; + } + + var inner = makeRing(level.enter.x, level.enter.y, 1000); + var mid = makeRing(level.enter.x, level.enter.y, 2500); + var mid2 = makeRing(level.enter.x, level.enter.y, 4000); + var out = makeRing(level.enter.x, level.enter.y, 6000); + + spawn.randomMob(level.enter.x + 250, level.enter.y); + + spawn.randomMob(level.enter.x + 1250, level.enter.y); + spawn.randomMob(level.enter.x - 1250, level.enter.y); + spawn.randomMob(level.enter.x, level.enter.y + 1250); + spawn.randomMob(level.enter.x, level.enter.y - 1250); + spawn.randomMob(level.enter.x + 1250, level.enter.y + 500); + spawn.randomMob(level.enter.x - 1250, level.enter.y + 500); + spawn.randomMob(level.enter.x + 500, level.enter.y + 1250); + spawn.randomMob(level.enter.x + 500, level.enter.y - 1250); + + spawn.randomMob(level.enter.x + 2750, level.enter.y); + spawn.randomMob(level.enter.x - 2750, level.enter.y); + spawn.randomMob(level.enter.x, level.enter.y + 2750); + spawn.randomMob(level.enter.x, level.enter.y - 2750); + spawn.randomMob(level.enter.x + 2750, level.enter.y + 500); + spawn.randomMob(level.enter.x - 2750, level.enter.y + 500); + spawn.randomMob(level.enter.x + 500, level.enter.y + 2750); + spawn.randomMob(level.enter.x + 500, level.enter.y - 2750); + + spawn.randomLevelBoss(level.enter.x, level.enter.y - 4250); + spawn.secondaryBossChance(level.enter.x, level.enter.y + 4250); + + level.custom = () => { + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.customTopLayer = () => { + inner.rotBy(0.01); + mid.rotBy(-0.005); + mid2.rotBy(0.003); + out.rotBy(-0.002); + }; + + powerUps.addResearchToLevel(); + }, + trial() { // trial, collab between Cirryn and Tarantula Hawk + simulation.makeTextLog(`trial by Cirryn and Tarantula Hawk`); + level.setPosToSpawn(0, -50); + level.exit.x = 4150; + level.exit.y = -30; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit + level.defaultZoom = 1800; + simulation.zoomTransition(level.defaultZoom); + document.body.style.backgroundColor = "#d8dadf"; + const button = level.button(2000, 0); + const door = level.door(3930, -300, 40, 300, 300, 10); + door.isClosing = false; + var didTrialBegin = false; + + const customMob = { + assassin(x, y) { // modified slasher + mobs.spawn(x, y, 3, 30, "black"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + me.accelMag = 0.0008 * simulation.accelScale; + me.torqueMagnitude = 0.00002 * me.inertia * (Math.random() > 0.5 ? -1 : 1); + me.frictionStatic = 0; + me.frictionAir = 0.08; + me.delay = 120 * simulation.CDScale; + me.cd = 0; + spawn.shield(me, x, y); + me.damageReduction = 0; + const start = window.performance.now(); // they only last ~ten seconds + const lifespan = 15000 + 700 * (Math.random() - 0.5); + me.onDamage = function () { + Matter.Body.setAngularVelocity(me, me.angularVelocity + 1); + }; + me.do = function () { + this.checkStatus(); + this.alwaysSeePlayer(); + this.attraction(); + this.health = 1 - (window.performance.now() - start) / lifespan; + if (this.health < 0) { + this.death(); + } + Matter.Body.setAngularVelocity(me, me.angularVelocity + 0.05); + }; + }, + mercenary(x, y) { // fast boi + mobs.spawn(x, y, 3, 60, "white"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + me.accelMag = 0.001 * simulation.accelScale; + me.torqueMagnitude = 0.00001 * me.inertia * (Math.random() > 0.5 ? -1 : 1); + me.frictionStatic = 0; + me.frictionAir = 0.03; + me.delay = 120 * simulation.CDScale; + me.cd = 0; + spawn.shield(me, x, y); + me.damageReduction = 0; + const start = window.performance.now(); // they only last ~ten seconds + const lifespan = 25000 + 700 * (Math.random() - 0.5); + me.onDamage = function () { + Matter.Body.setAngularVelocity(me, me.angularVelocity + 1); + }; + me.do = function () { + this.checkStatus(); + this.attraction(); + this.health = 1 - (window.performance.now() - start) / lifespan; + if (this.health < 0) { + this.death(); + } + this.alwaysSeePlayer(); + }; + } + // eventually maybe add more custom mob types + }; + + function randomWave(count, source) { // generates a wave list from a source + // checks in spawn first, then customMob, for the sources + var ret = []; + for (var i = 0; i < count; i++) { + var pick = source[Math.floor(Math.random() * source.length)]; + if (spawn[pick]) { + ret.push(spawn[pick]); + } + else if (customMob[pick]) { + ret.push(customMob[pick]); + } + } + return ret; + } + + function wave(mobs) { // takes a list of functions that accept x,y coordinates to spawn a mob and spawns them in the ceiling + for (var i = 0; i < mobs.length; i++) { + var x = 1000 + 2400 * i / mobs.length + 200 * (Math.random() - 0.5); + var y = -950 - 100 * Math.random(); + mobs[i](x, y); + } + const ammoCount = Math.random() * (10 - simulation.difficulty / 4); + for (var i = 0; i < ammoCount; i++) { + powerUps.spawn(3300, -1000, "ammo"); + } + } + + level.custom = () => { + door.openClose(); + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.customTopLayer = () => { + button.query(); + button.draw(); + door.draw(); + if (!button.isUp && !didTrialBegin) { + didTrialBegin = true; + simulation.makeTextLog('The Trial has begun.'); + + setTimeout(() => { + simulation.makeTextLog('first wave (domitable)'); + wave(randomWave(2 + simulation.difficulty * 0.1, spawn.fullPickList)); + }, 3000); + + setTimeout(() => { + simulation.makeTextLog('second wave (domitable)'); + wave(randomWave(2 + simulation.difficulty * 0.1, spawn.fullPickList)); + }, 13000); + + setTimeout(() => { + simulation.makeTextLog('third wave (indomitable)'); + wave(randomWave(4, ["assassin"])); + }, 23000); + + setTimeout(() => { + simulation.makeTextLog('fourth wave (domitable)'); + wave(randomWave(4 + simulation.difficulty / 2, spawn.fullPickList)); + }, 39000); + + setTimeout(() => { + simulation.makeTextLog('fifth wave (domitable)'); + wave(randomWave(4 + simulation.difficulty / 2, spawn.fullPickList)); + }, 49000); + + setTimeout(() => { + simulation.makeTextLog('sixth wave (indomitable)'); + wave(randomWave(7, ["mercenary"])); + }, 59000); + + setTimeout(() => { + simulation.makeTextLog('seventh wave (boss)'); + spawn.randomLevelBoss(700, -1000); + var mainBoss = mob[mob.length - 1]; + mainBoss.oldOnDeath = mainBoss.onDeath; + mainBoss.onDeath = () => { + door.isClosing = false; + powerUps.spawn(4150, -30, "tech"); + powerUps.spawn(4150, -30, "tech"); + mainBoss.oldOnDeath(); + } + spawn.secondaryBossChance(3500, -1000); + }, 86000); + + door.isClosing = true; + } + }; + + spawn.mapRect(-100, 0, 10000, 10000); // the left half of the room + spawn.mapRect(-10000, -300, 9900, 10000); + spawn.mapRect(-100, -300, 400, 100); + spawn.mapRect(200, -800, 100, 500); + spawn.mapRect(200, -800, 500, 100); + spawn.mapRect(600, -1000, 100, 200); + + spawn.mapRect(600, -1100, 3000, 100); // the ceiling + + spawn.mapRect(3500, -1000, 100, 200); // the right half of the room + spawn.mapRect(3500, -800, 500, 100); + spawn.mapRect(3900, -800, 100, 500); + spawn.mapRect(3900, -300, 400, 100); + spawn.mapRect(4300, -300, 10000, 10000); + + for (var i = 0; i < 4; i++) { // "door" at the entrance + spawn.bodyRect(200, -200 + i * 50, 20, 50); + } + + for (var i = 0; i < 5; i++) { // some random rubble in the first half of the room + spawn.bodyRect(400 + Math.random() * 1000, -200, 40 + Math.random() * 40, 40 + Math.random() * 40); + } + + powerUps.addResearchToLevel(); //needs to run after mobs are spawneds + }, // ******************************************************************************************************** // ******************************************************************************************************** // ***************************************** training levels ********************************************** diff --git a/js/player.js b/js/player.js index 6e83d3a..2df51df 100644 --- a/js/player.js +++ b/js/player.js @@ -553,17 +553,18 @@ const m = { if (tech.isHarmMACHO) dmg *= tech.isMoveMACHO ? 0.3 : 0.4 if (tech.isImmortal) dmg *= 0.7 if (m.fieldMode === 0 || m.fieldMode === 3) dmg *= 0.973 ** m.coupling - if (tech.isLowHealthDefense) dmg *= 1 - Math.max(0, 1 - m.health) * 0.8 if (tech.isHarmReduceNoKill && m.lastKillCycle + 300 < m.cycle) dmg *= 0.3 if (tech.isAddBlockMass && m.isHolding) dmg *= 0.1 if (tech.isSpeedHarm && (tech.speedAdded + player.speed) > 0.1) dmg *= 1 - Math.min((tech.speedAdded + player.speed) * 0.0193, 0.8) //capped at speed of 55 if (tech.isHarmReduce && input.field) dmg *= 0.1 if (tech.isNeutronium && input.field && m.fieldCDcycle < m.cycle) dmg *= 0.05 - if (tech.isBotArmor) dmg *= 0.95 ** b.totalBots() + if (tech.isBotArmor) dmg *= 0.96 ** b.totalBots() if (tech.isHarmArmor && m.lastHarmCycle + 600 > m.cycle) dmg *= 0.3; if (tech.isNoFireDefense && m.cycle > m.fireCDcycle + 120) dmg *= 0.3 if (tech.isTurret && m.crouch) dmg *= 0.3; if (tech.isFirstDer && b.inventory[0] === b.activeGun) dmg *= 0.85 ** b.inventory.length + // if (tech.isLowHealthDefense) dmg *= Math.pow(0.3, Math.max(0, (tech.isEnergyHealth ? m.maxEnergy - m.energy : m.maxHealth - m.health))) + if (tech.isLowHealthDefense) dmg *= Math.pow(0.2, Math.max(0, 1 - (tech.isEnergyHealth ? m.energy / m.maxEnergy : m.health / m.maxHealth))) // return tech.isEnergyHealth ? Math.pow(dmg, 0.7) : dmg //defense has less effect // dmg *= m.fieldHarmReduction return dmg * m.fieldHarmReduction @@ -2115,7 +2116,7 @@ const m = { isFieldActive: false, fieldRange: 155, fieldShieldingScale: 1, - // fieldDamage: 1, + fieldDamage: 1, isSneakAttack: false, lastHit: 0, //stores value of last damage player took above a threshold, in m.damage sneakAttackCycle: 0, @@ -2152,6 +2153,7 @@ const m = { m.eyeFillColor = m.fieldMeterColor m.fieldShieldingScale = 1; m.fieldBlockCD = 10; + m.fieldDamage = 1 m.fieldHarmReduction = 1; m.lastHit = 0 m.isSneakAttack = false @@ -2241,7 +2243,7 @@ const m = { } else { m.fieldRegen = 0.001 //6 energy per second } - if (m.fieldMode === 0 || m.fieldMode === 4) m.fieldRegen += 0.000133 * m.coupling + if (m.fieldMode === 0 || m.fieldMode === 4) m.fieldRegen += 0.0001 * m.coupling if (tech.isTimeCrystals) { m.fieldRegen *= 2.5 } else if (tech.isGroundState) { @@ -2867,7 +2869,7 @@ const m = { case 3: //negative mass return `${(0.973 ** couple).toFixed(2)}x damage taken` case 4: //assembler - return `+${(0.8 * couple).toFixed(1)} energy per second` + return `+${(0.6 * couple).toFixed(1)} energy per second` case 5: //plasma return `${(1 + 0.015 * couple).toFixed(3)}x damage` case 6: //time dilation @@ -2883,7 +2885,7 @@ const m = { } }, couplingChange(change = 0) { - if (change > 0 && level.onLevel !== -1) simulation.makeTextLog(`m.coupling += ${change}`, 60); //level.onLevel !== -1 means not on lore level + if (change > 0 && level.onLevel !== -1) simulation.makeTextLog(`
m.coupling += ${change}`, 60); //level.onLevel !== -1 means not on lore level m.coupling += change if (m.coupling < 0) { //look for coupling power ups on this level and remove them to prevent exploiting tech ejections @@ -2921,7 +2923,7 @@ const m = { document.getElementById("field").innerHTML = m.fieldUpgrades[index].name m.setHoldDefaults(); m.fieldUpgrades[index].effect(); - simulation.makeTextLog(`m.setField("${m.fieldUpgrades[m.fieldMode].name}")`); + simulation.makeTextLog(`
  m.setField("${m.fieldUpgrades[m.fieldMode].name}")
input.key.field: ["MouseRight"]`); }, fieldUpgrades: [{ name: "field emitter", @@ -3058,7 +3060,7 @@ const m = { }, { name: "perfect diamagnetism", - description: "deflecting does not drain energy
maintains functionality while inactive
5 energy per second", + description: "deflecting does not drain energy
shield maintains functionality while inactive
5 energy per second", effect: () => { m.fieldMeterColor = "#48f" //"#0c5" m.eyeFillColor = m.fieldMeterColor @@ -3481,11 +3483,11 @@ const m = { }, { name: "molecular assembler", - description: `excess energy used to print ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
use energy to deflect mobs
12 energy per second`, + 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 `excess energy used to print ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
use energy to deflect mobs
12 energy per second` + 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` }, - blockJumpPhase: 0, + effect: () => { m.fieldMeterColor = "#ff0" m.eyeFillColor = m.fieldMeterColor @@ -3596,115 +3598,15 @@ const m = { 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() - - if (tech.isBlockJump) { - if (m.onGround && m.buttonCD_jump + 10 < m.cycle) this.blockJumpPhase = 0 //reset after touching ground or block - if (this.blockJumpPhase === 0 && !m.onGround) { //1st jump or fall - this.blockJumpPhase = 1 - } else if (this.blockJumpPhase === 1 && !input.up && m.buttonCD_jump + 10 < m.cycle) { //not pressing jump - this.blockJumpPhase = 2 - } else if (this.blockJumpPhase === 2 && input.up && m.buttonCD_jump + 10 < m.cycle) { //2nd jump - this.blockJumpPhase = 3 - - //make a block - const radius = 25 + Math.floor(15 * Math.random()) - body[body.length] = Matter.Bodies.polygon(m.pos.x, m.pos.y + 65 + radius, 4, radius, { - friction: 0.05, - frictionAir: 0.001, - collisionFilter: { - category: cat.body, - mask: cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet - }, - classType: "body", - }); - const block = body[body.length - 1] - //mess with the block shape (this code is horrible) - Composite.add(engine.world, block); //add to world - const r1 = radius * (1 + 0.4 * Math.random()) - const r2 = radius * (1 + 0.4 * Math.random()) - let angle = 0 - const vertices = [] - for (let i = 0, len = block.vertices.length; i < len; i++) { - angle += 2 * Math.PI / len - vertices.push({ x: block.position.x + r1 * Math.cos(angle), y: block.position.y + r2 * Math.sin(angle) }) - } - Matter.Body.setVertices(block, vertices) - Matter.Body.setAngle(block, Math.PI / 4) - Matter.Body.setVelocity(block, { x: 0.9 * player.velocity.x, y: 10 }); - Matter.Body.applyForce(block, m.pos, { x: 0, y: m.jumpForce * 0.12 * Math.min(m.standingOn.mass, 5) }); - if (tech.isBlockRestitution) { - block.restitution = 0.999 //extra bouncy - block.friction = block.frictionStatic = block.frictionAir = 0.001 - } - if (tech.isAddBlockMass) { - const expand = function (that, massLimit) { - if (that.mass < massLimit) { - const scale = 1.04; - Matter.Body.scale(that, scale, scale); - setTimeout(expand, 20, that, massLimit); - } - }; - expand(block, Math.min(20, block.mass * 3)) - } - //jump - m.buttonCD_jump = m.cycle; //can't jump again until 20 cycles pass - let horizontalVelocity = 8 * (- input.left + input.right) - Matter.Body.setVelocity(player, { x: player.velocity.x + horizontalVelocity, y: -7.5 + 0.25 * player.velocity.y }); - player.force.y = -m.jumpForce; //player jump force - } else if (this.blockJumpPhase === 3 && m.onGround && m.buttonCD_jump + 10 < m.cycle) { - //reset - this.blockJumpPhase = 0 //reset - } - } - - - // if (tech.isBlockJump) { - // //make sure only 1 ephemera is running - // simulation.ephemera.push({ - // name: "2 jump", - // mode: 0, - // do() { - // // console.log('hi') - // if (m.buttonCD_jump + 20 < m.cycle && m.onGround) simulation.removeEphemera(this.name) - // if (this.mode === 0) { - // if (!input.up) this.mode = 1 - // } else if (this.mode === 1) { - // if (input.up && m.buttonCD_jump + 20 < m.cycle) { - // simulation.removeEphemera(this.name) - // //make a block - // body[body.length] = Matter.Bodies.polygon(m.pos.x, m.pos.y + 80, 4, 30 + Math.floor(10 * Math.random()), { - // friction: 0.05, - // frictionAir: 0.001, - // collisionFilter: { - // category: cat.body, - // mask: cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet - // }, - // classType: "body", - // // isPrinted: true, - // }); - // const who = body[body.length - 1] - // Composite.add(engine.world, who); //add to world - // Matter.Body.setVelocity(who, { x: player.velocity.x * 0.5, y: +30 }); - // Matter.Body.applyForce(who, m.pos, { x: 0, y: m.jumpForce * 0.12 * Math.min(m.standingOn.mass, 5) }); - - // //jump again - // m.buttonCD_jump = m.cycle; //can't jump again until 20 cycles pass - // player.force.y = -m.jumpForce; //player jump force - // Matter.Body.setVelocity(player, { x: player.velocity.x, y: -7.5 + 0.25 * player.velocity.y }); - // } - // } - // }, - // }) - - // } } } }, { name: "plasma torch", - description: "use energy to emit short range plasma
damages and pushes mobs away
10 energy per second", + 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) { @@ -4500,16 +4402,18 @@ const m = { } 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.033 * 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 } } } @@ -4742,66 +4646,6 @@ const m = { m.fieldRadius = 0 } m.drawRegenEnergy("rgba(0,0,0,0.2)") - - if (tech.isBlockJump) { - if (m.onGround && m.buttonCD_jump + 10 < m.cycle) this.blockJumpPhase = 0 //reset after touching ground or block - if (this.blockJumpPhase === 0 && !m.onGround) { //1st jump or fall - this.blockJumpPhase = 1 - } else if (this.blockJumpPhase === 1 && !input.up && m.buttonCD_jump + 10 < m.cycle) { //not pressing jump - this.blockJumpPhase = 2 - } else if (this.blockJumpPhase === 2 && input.up && m.buttonCD_jump + 10 < m.cycle) { //2nd jump - this.blockJumpPhase = 3 - - //make a block - const radius = 25 + Math.floor(15 * Math.random()) - body[body.length] = Matter.Bodies.polygon(m.pos.x, m.pos.y + 60 + radius, 4, radius, { - friction: 0.05, - frictionAir: 0.001, - collisionFilter: { - category: cat.body, - mask: cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet - }, - classType: "body", - }); - const block = body[body.length - 1] - //mess with the block shape (this code is horrible) - Composite.add(engine.world, block); //add to world - const r1 = radius * (1 + 0.4 * Math.random()) - const r2 = radius * (1 + 0.4 * Math.random()) - let angle = 0 - const vertices = [] - for (let i = 0, len = block.vertices.length; i < len; i++) { - angle += 2 * Math.PI / len - vertices.push({ x: block.position.x + r1 * Math.cos(angle), y: block.position.y + r2 * Math.sin(angle) }) - } - Matter.Body.setVertices(block, vertices) - Matter.Body.setAngle(block, Math.PI / 4) - Matter.Body.setVelocity(block, { x: 0.9 * player.velocity.x, y: 10 }); - Matter.Body.applyForce(block, m.pos, { x: 0, y: m.jumpForce * 0.12 * Math.min(m.standingOn.mass, 5) }); - if (tech.isBlockRestitution) { - block.restitution = 0.999 //extra bouncy - block.friction = block.frictionStatic = block.frictionAir = 0.001 - } - if (tech.isAddBlockMass) { - const expand = function (that, massLimit) { - if (that.mass < massLimit) { - const scale = 1.04; - Matter.Body.scale(that, scale, scale); - setTimeout(expand, 20, that, massLimit); - } - }; - expand(block, Math.min(20, block.mass * 3)) - } - //jump - m.buttonCD_jump = m.cycle; //can't jump again until 20 cycles pass - let horizontalVelocity = 8 * (- input.left + input.right) - Matter.Body.setVelocity(player, { x: player.velocity.x + horizontalVelocity, y: -7.5 + 0.25 * player.velocity.y }); - player.force.y = -m.jumpForce; //player jump force - } else if (this.blockJumpPhase === 3 && m.onGround && m.buttonCD_jump + 10 < m.cycle) { - //reset - this.blockJumpPhase = 0 //reset - } - } } } }, diff --git a/js/powerup.js b/js/powerup.js index d96f70e..0074835 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -273,7 +273,7 @@ const powerUps = { choose(type, index) { if (type === "gun") { b.giveGuns(index) - let text = `b.giveGuns("${b.guns[index].name}")` + let text = `
  b.giveGuns("${b.guns[index].name}")` if (b.inventory.length === 1) text += `
input.key.gun: ["MouseLeft"]` if (b.inventory.length === 2) text += `
input.key.nextGun: ["${input.key.nextGun}","MouseWheel"] @@ -282,8 +282,8 @@ const powerUps = { } else if (type === "field") { m.setField(index) } else if (type === "tech") { - // if (tech.isBanish && tech.tech[index].isBanished) tech.tech[index].isBanished = false - simulation.makeTextLog(`tech.giveTech("${tech.tech[index].name}")`); + // if (tech.isBanish && tech.tech[index].isBanished) tech.tech[index].isBanished = false + simulation.makeTextLog(`
  tech.giveTech("${tech.tech[index].name}")`); tech.giveTech(index) } powerUps.endDraft(type); @@ -421,11 +421,11 @@ const powerUps = {
-
0.84x damage done per level
1.23x damage taken per level
+
0.85x damage done per level
1.23x damage taken per level
-5 initial power ups
faster and more mobs per level
-
0.84x damage done per level
1.23x damage taken per level
-
+1 boss per level, -1 tech per boss
-1 ${powerUps.orb.research()} per level
-
0.84x damage done per level
1.23x damage taken per level
+
0.85x damage done per level
1.23x damage taken per level
+
+1 boss per level
-1 tech per boss
+
0.85x damage done per level
1.23x damage taken per level
3x chance for shielded mobs
-3 initial power ups
@@ -648,7 +648,7 @@ const powerUps = { let overHeal = m.health + heal * simulation.healScale - m.maxHealth //used with tech.isOverHeal const healOutput = Math.min(m.maxHealth - m.health, heal) * simulation.healScale m.addHealth(heal); - if (healOutput > 0) simulation.makeTextLog(`m.health += ${(healOutput).toFixed(3)}`) //
${m.health.toFixed(3)} + if (healOutput > 0) simulation.makeTextLog(`
  m.health += ${(healOutput).toFixed(3)}`) //
${m.health.toFixed(3)} if (tech.isOverHeal && overHeal > 0) { //tech quenching overHeal *= 2 //double the over heal converted to max health //make sure overHeal doesn't kill player @@ -658,7 +658,7 @@ const powerUps = { m.setMaxHealth(); m.damage(overHeal); overHeal *= m.defense() // account for defense after m.damage() so the text log is accurate - simulation.makeTextLog(`m.health -= ${(overHeal).toFixed(3)}`) //
${m.health.toFixed(3)} + simulation.makeTextLog(`
  m.health -= ${(overHeal).toFixed(3)}`) //
${m.health.toFixed(3)} simulation.drawList.push({ //add dmg to draw queue x: m.pos.x, y: m.pos.y, @@ -1218,8 +1218,7 @@ const powerUps = { for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].name === "decoherence") powerUps.ejectTech(i, true) } - simulation.makeTextLog(`decoherence tech ejected`) - simulation.makeTextLog(`options reset`) + simulation.makeTextLog(`decoherence tech ejected
options reset`) } } if (tech.tooManyTechChoices) { @@ -1244,7 +1243,7 @@ const powerUps = { const choose = options[Math.floor(Math.seededRandom(0, options.length))] //pick an element from the array of options if (tech.isBanish) { tech.tech[choose].isBanished = true - if (i === 0) simulation.makeTextLog(`options.length = ${optionLengthNoDuplicates} //tech removed from pool by decoherence`) + if (i === 0) simulation.makeTextLog(`options.length = ${optionLengthNoDuplicates} //tech removed from pool by decoherence`) } removeOption(choose) //move from future options pool to avoid repeats on this selection tech.tech[choose].isRecentlyShown = true //this flag prevents this option from being shown the next time you pick up a tech power up @@ -1391,10 +1390,8 @@ const powerUps = { if (!alreadyHasGun) text += powerUps.gunText(choose, `powerUps.choose('gun',${choose})`) } for (let i = 0; i < localSettings.entanglement.techIndexes.length; i++) { //add tech - let found = false; let choose = undefined - console.log(localSettings.entanglement.techIndexes[i]) for (let j = 0; j < tech.tech.length; j++) { if (localSettings.entanglement.techIndexes[i] === tech.tech[j].name) { choose = j; @@ -1402,12 +1399,9 @@ const powerUps = { break; } } - // let choose = localSettings.entanglement.techIndexes[i] - console.log(choose) if (found && tech.tech[choose]) { const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; if (choose === null || tech.tech[choose].count + 1 > tech.tech[choose].maxCount || !tech.tech[choose].allowed()) { - // text += `
${tech.tech[choose].name} - incoherent
` text += powerUps.incoherentTechText(choose) } else { if (tech.tech[choose].isFieldTech) { @@ -1428,7 +1422,6 @@ const powerUps = { } } } - // document.getElementById("choose-grid").classList.add("flipX"); document.getElementById("choose-grid").innerHTML = text powerUps.showDraft(); localSettings.entanglement = undefined @@ -1557,7 +1550,8 @@ const powerUps = { } }, addResearchToLevel() { //add a random power up to a location that has a mob, mostly used to give each level a research - if (simulation.difficultyMode < 4 && mob.length) { //don't spawn on higher difficulty settings + // if (simulation.difficultyMode < 4 && mob.length) { //don't spawn on higher difficulty settings + if (level.levelsCleared < 13 - simulation.difficultyMode * 2 && mob.length) { //don't spawn late game const index = Math.floor(Math.random() * mob.length) powerUps.spawn(mob[index].position.x, mob[index].position.y, "research"); } @@ -1599,7 +1593,7 @@ const powerUps = { if (have.length) { choose = have[Math.floor(Math.random() * have.length)] - simulation.makeTextLog(`tech.remove("${tech.tech[choose].name}")`) + simulation.makeTextLog(`tech.remove("${tech.tech[choose].name}")`) for (let i = 0; i < tech.tech[choose].count; i++) { powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); @@ -1617,7 +1611,7 @@ const powerUps = { return false } } else if (tech.tech[choose].count && !tech.tech[choose].isInstant) { - simulation.makeTextLog(`tech.remove("${tech.tech[choose].name}")`) + simulation.makeTextLog(`tech.remove("${tech.tech[choose].name}")`) for (let i = 0; i < tech.tech[choose].count; i++) { powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); diff --git a/js/spawn.js b/js/spawn.js index 5759357..1a94832 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -114,7 +114,7 @@ const spawn = { } }, secondaryBossChance(x, y) { - if (simulation.difficultyMode > 2 && level.levelsCleared > 1) { + if (simulation.difficultyMode > 2 && level.levelsCleared > 2) { spawn.randomLevelBoss(x, y); } else { return false @@ -1428,7 +1428,7 @@ const spawn = { me.seeAtDistance2 = 1400000; me.cellMassMax = 70 me.collisionFilter.mask = cat.player | cat.bullet | cat.body// | cat.map - Matter.Body.setDensity(me, 0.0001 + 0.00002 * simulation.difficulty) // normal density is 0.001 + Matter.Body.setDensity(me, 0.00012 + 0.00001 * simulation.difficulty) // normal density is 0.001 me.damageReduction = 0.17 const k = 642 //k=r^2/m diff --git a/js/tech.js b/js/tech.js index e5014ea..d10f69e 100644 --- a/js/tech.js +++ b/js/tech.js @@ -73,9 +73,13 @@ const tech = { }, junkChance: 0, addJunkTechToPool(percent) { //percent is number between 0-1 - simulation.makeTextLog(`+${(100 * percent).toFixed(0)}% JUNKtech chance`) - tech.junkChance += (1 - tech.junkChance) * percent + simulation.makeTextLog(`+${(100 * percent).toFixed(0)}% JUNKtech chance (${100 * tech.junkChance.toFixed(0)} total chance)`) + // tech.junkChance += (1 - tech.junkChance) * percent + tech.junkChance += percent + if (tech.junkChance < 0.001 || tech.junkChance === undefined) tech.junkChance = 0 + if (tech.junkChance > 1) tech.junkChance = 1 return percent + //make an array for possible junk tech to add // let options = []; // for (let i = 0; i < tech.tech.length; i++) { @@ -103,12 +107,15 @@ const tech = { // } // } // } - if (percent > 0) { - tech.junkChance = (tech.junkChance - percent) / (1 - percent) - if (tech.junkChance < 0.001 || tech.junkChance === undefined) tech.junkChance = 0 - } - }, + // if (percent > 0) { + // tech.junkChance = (tech.junkChance - percent) / (1 - percent) + // if (tech.junkChance < 0.001 || tech.junkChance === undefined) tech.junkChance = 0 + // } + tech.junkChance -= percent + if (tech.junkChance < 0.001 || tech.junkChance === undefined) tech.junkChance = 0 + if (tech.junkChance > 1) tech.junkChance = 1 + }, giveTech(index = 'random') { if (index === 'random') { let options = []; @@ -118,7 +125,7 @@ const tech = { // give a random tech from the tech I don't have if (options.length > 0) { let newTech = options[Math.floor(Math.random() * options.length)] - simulation.makeTextLog(`tech.giveTech("${tech.tech[newTech].name}") //random tech`); + simulation.makeTextLog(`tech.giveTech("${tech.tech[newTech].name}") //random tech`); tech.giveTech(newTech) } } else { @@ -199,7 +206,7 @@ const tech = { }, damage: 1, //used for tech changes to player damage that don't have complex conditions damageFromTech() { - let dmg = tech.damage //m.fieldDamage + let dmg = tech.damage * m.fieldDamage if (tech.isImmunityDamage && m.immuneCycle > m.cycle) dmg *= 4 if (tech.isPowerUpDamage) dmg *= 1 + 0.05 * powerUp.length if (tech.isDamageCooldown) dmg *= m.lastKillCycle + tech.isDamageCooldownTime > m.cycle ? 0.5 : 4 @@ -210,7 +217,6 @@ const tech = { if (tech.isGunChoice && tech.buffedGun === b.inventoryGun) dmg *= 1 + 0.3 * b.inventory.length if (powerUps.boost.endCycle > m.cycle) dmg *= 1 + powerUps.boost.damage if (m.coupling && (m.fieldMode === 0 || m.fieldMode === 5)) dmg *= 1 + 0.015 * m.coupling - if (m.isSneakAttack && m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) > m.cycle) dmg *= 4.5 * (1 + 0.033 * m.coupling) if (tech.deathSkipTime) dmg *= 1 + 0.6 * tech.deathSkipTime if (tech.isTechDebt) dmg *= tech.totalCount > 20 ? Math.pow(0.85, tech.totalCount - 20) : 4 - 0.15 * tech.totalCount if (tech.isAnthropicDamage && tech.isDeathAvoidedThisLevel) dmg *= 2.71828 @@ -219,7 +225,7 @@ const tech = { if (tech.isOneGun && b.inventory.length < 2) dmg *= 1.3 if (tech.isAcidDmg && m.health > 1) dmg *= 1.35; if (tech.isRerollDamage) dmg *= 1 + Math.max(0, 0.05 * powerUps.research.count) - if (tech.isBotDamage) dmg *= 1 + 0.05 * b.totalBots() + if (tech.isBotDamage) dmg *= 1 + 0.04 * b.totalBots() if (tech.restDamage > 1 && player.speed < 1) dmg *= tech.restDamage if (tech.isLowEnergyDamage) dmg *= 1 + Math.max(0, 1 - m.energy) if (tech.energyDamage) dmg *= 1 + m.energy * 0.23 * tech.energyDamage; @@ -229,7 +235,8 @@ const tech = { if (tech.isAxion && tech.isHarmMACHO) dmg *= (tech.isMoveMACHO ? 3 : 2) if (tech.isHarmDamage && m.lastHarmCycle + 480 > m.cycle) dmg *= 3; if (tech.lastHitDamage && m.lastHit) dmg *= 1 + tech.lastHitDamage * m.lastHit - if (tech.isLowHealthDmg) dmg *= 1 + 0.7 * Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health)) + // if (tech.isLowHealthDmg) dmg *= 1 + 0.6 * Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health)) + if (tech.isLowHealthDmg) dmg *= 1 + 0.6 * Math.max(0, (tech.isEnergyHealth ? m.maxEnergy - m.energy : m.maxHealth - m.health)) if (tech.isJunkDNA) dmg *= 1 + 2 * tech.junkChance return dmg }, @@ -628,7 +635,7 @@ const tech = { frequencyDefault: 1, isInstant: true, isBadRandomOption: true, - allowed: () => true, + allowed: () => tech.junkChance < 1, requires: "", effect() { powerUps.spawn(m.pos.x, m.pos.y, "gun"); @@ -648,7 +655,7 @@ const tech = { { name: "arsenal", descriptionFunction() { - return `1.25x damage per unequipped gun
(${(1 + 0.25 * Math.max(0, b.inventory.length - 1)).toFixed(2)}x)` + return `1.25x damage per unequipped gun
(${(1 + 0.25 * Math.max(0, b.inventory.length - 1)).toFixed(2)}x)` }, maxCount: 1, count: 0, @@ -666,7 +673,7 @@ const tech = { { name: "active cooling", descriptionFunction() { - return `1.25x fire rate per unequipped gun
(${(1 / Math.pow(0.8, Math.max(0, b.inventory.length - 1))).toFixed(2)}x)` + return `1.25x fire rate per unequipped gun
(${(1 / Math.pow(0.8, Math.max(0, b.inventory.length - 1))).toFixed(2)}x)` }, //
but not including your equipped gun` }, maxCount: 1, count: 0, @@ -815,7 +822,7 @@ const tech = { { name: "supply chain", descriptionFunction() { - return `spawn a gun
spawn ${powerUps.orb.ammo(1)} equal to all your active gun's ammo` + return `spawn a gun
spawn ${powerUps.orb.ammo(1)} that will 2x your gun's ammo` }, maxCount: 9, count: 0, @@ -1038,7 +1045,7 @@ const tech = { { name: "Newtons 1st law", descriptionFunction() { - return `damage taken is proportional to your speed
up to 0.2x damage taken at 55 speed (${(1 - Math.min((tech.speedAdded + player.speed) * 0.0193, 0.8)).toFixed(2)}x)` + return `damage taken is proportional to your speed
up to 0.2x damage taken at 55 speed (${(1 - Math.min((tech.speedAdded + player.speed) * 0.0193, 0.8)).toFixed(2)}x)` }, description: "", maxCount: 1, @@ -1059,7 +1066,7 @@ const tech = { { name: "Newtons 2nd law", descriptionFunction() { - return `damage is proportional to your speed
up to 2x damage at 55 speed (${(1 + Math.min(1, ((tech.speedAdded + player.speed) * 0.0193))).toFixed(2)}x)` + return `damage is proportional to your speed
up to 2x damage at 55 speed (${(1 + Math.min(1, ((tech.speedAdded + player.speed) * 0.0193))).toFixed(2)}x)` }, maxCount: 1, count: 0, @@ -1143,7 +1150,7 @@ const tech = { }, { name: "regression", - description: "bullet collisions increase vulnerability to
damage by 1.05x for mobs (+1.025x for bosses)", + description: "bullet collisions increase vulnerability to
damage by 1.05x for mobs and +1.025x for bosses", maxCount: 1, count: 0, frequency: 1, @@ -1207,7 +1214,7 @@ const tech = { let damageTotal = 1 for (let i = 0; i < this.damageSoFar.length; i++) damageTotal *= this.damageSoFar[i] let currentDamage = "" - if (this.count) currentDamage = `
(${(damageTotal).toFixed(2)}x)` + if (this.count) currentDamage = `
(${(damageTotal).toFixed(2)}x)` return `randomly gain between 1x and 1.3x damage` + currentDamage }, maxCount: 9, @@ -1374,6 +1381,46 @@ const tech = { tech.deathSkipTime = 0 } }, + { + name: "exciton", + descriptionFunction() { + return `after mobs die they have a 14% chance to
spawn ${powerUps.orb.boost(1)} that give ${(1 + powerUps.boost.damage).toFixed(2)}x damage for ${(powerUps.boost.duration / 60).toFixed(0)} seconds
` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + effect() { + tech.isBoostPowerUps = true + }, + remove() { + tech.isBoostPowerUps = false + } + }, + { + name: "band gap", + descriptionFunction() { + return `${powerUps.orb.boost(1)} give 1.77x damage
but their duration is reduced by 1 second` + }, + maxCount: 9, + count: 1, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isBoostPowerUps || tech.isBoostReplaceAmmo + }, + requires: "exciton, quasiparticles", + effect() { + powerUps.boost.duration -= 60 + powerUps.boost.damage += 0.77 + }, + remove() { + powerUps.boost.duration = 600 + powerUps.boost.damage = 1.25 + } + }, { name: "collider", descriptionFunction() { @@ -1397,7 +1444,7 @@ const tech = { { name: "bubble fusion", descriptionFunction() { - return `after destroying a mob's shield
spawn 1-2 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)} (once per mob)` + return `after destroying a mob's shield
spawn 1-2 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)} (once per mob)` }, maxCount: 1, count: 0, @@ -1546,7 +1593,7 @@ const tech = { { name: "nail-bot upgrade", link: `nail-bot upgrade`, - description: "convert your bots to nail-bots
5x fire rate and 1.4x nail velocity", + description: "convert your bots to nail-bots
4x fire rate and 1.4x nail velocity", maxCount: 1, count: 0, frequency: 3, @@ -1604,7 +1651,7 @@ const tech = { { name: "foam-bot upgrade", link: `foam-bot upgrade`, - description: "convert your bots to foam-bots
3x foam size and fire rate", + description: "convert your bots to foam-bots
2.5x foam size and fire rate", maxCount: 1, count: 0, frequency: 3, @@ -1660,7 +1707,7 @@ const tech = { { name: "sound-bot upgrade", link: `sound-bot upgrade`, - description: "convert your bots to sound-bots
2.5x wave fire rate and 2.5x damage", + description: "convert your bots to sound-bots
2x wave fire rate, damage, and duration", maxCount: 1, count: 0, frequency: 3, @@ -1776,7 +1823,7 @@ const tech = { { name: "laser-bot upgrade", link: `laser-bot upgrade`, - description: "convert your bots to laser-bots
2.00x damage, efficiency, and range", + description: "convert your bots to laser-bots
2x damage, efficiency, and range", maxCount: 1, count: 0, frequency: 3, @@ -1834,7 +1881,7 @@ const tech = { { name: "orbital-bot upgrade", link: `orbital-bot upgrade`, - description: "convert your bots to orbital-bots
4x orbital damage and 1.5x radius", + description: "convert your bots to orbital-bots
4x orbital damage and 2x radius", maxCount: 1, count: 0, frequency: 3, @@ -1932,7 +1979,7 @@ const tech = { }, { name: "perimeter defense", - description: "for each permanent bot
0.95x damage taken", + description: "for each permanent bot
0.96x damage taken", maxCount: 1, count: 0, frequency: 2, @@ -1951,7 +1998,7 @@ const tech = { }, { name: "network effect", - description: "for each permanent bot
1.05x damage", + description: "for each permanent bot
1.04x damage", maxCount: 1, count: 0, frequency: 2, @@ -1972,7 +2019,7 @@ const tech = { name: "bot fabrication", link: `bot fabrication`, descriptionFunction() { - return `after you collect ${powerUps.orb.research(2 + Math.floor(0.1666 * b.totalBots()))}use them to build a
random bot (+1 cost every 5 bots)` + return `after you collect ${powerUps.orb.research(2 + Math.floor(0.1666 * b.totalBots()))}use them to build a
random bot (+1 cost every 5 bots)` }, // description: `if you collect ${powerUps.orb.research(2)}use them to build a
random bot (+1 cost every 5 bots)`, maxCount: 1, @@ -2351,7 +2398,7 @@ const tech = { { name: "first derivative", descriptionFunction() { - return `while your first gun is equipped
0.85x damage taken per gun (${(0.85 ** b.inventory.length).toFixed(2)}x)` + return `while your first gun is equipped
0.85x damage taken per gun (${(0.85 ** b.inventory.length).toFixed(2)}x)` }, maxCount: 1, count: 0, @@ -2682,7 +2729,7 @@ const tech = { { name: "Gibbs free energy", descriptionFunction() { - return `use ${powerUps.orb.research(2)}
1.01x damage per energy below 100 (${(1 + Math.max(0, 1 - m.energy)).toFixed(2)}x)` + return `use ${powerUps.orb.research(2)}
1.01x damage per energy below 100 (${(1 + Math.max(0, 1 - m.energy)).toFixed(2)}x)` }, maxCount: 1, count: 0, @@ -2707,19 +2754,19 @@ const tech = { }, { name: "overcharge", - description: "+88 maximum energy
+5% JUNKtech chance", + description: "+88 maximum energy
+4% JUNKtech chance", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return true + return tech.junkChance < 1 }, requires: "", effect() { tech.bonusEnergy += 0.88 m.setMaxEnergy() - this.refundAmount += tech.addJunkTechToPool(0.05) + this.refundAmount += tech.addJunkTechToPool(0.04) }, refundAmount: 0, remove() { @@ -2739,7 +2786,7 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return m.energy > m.maxEnergy || build.isExperimentSelection + return (m.energy > m.maxEnergy || build.isExperimentSelection) && tech.junkChance < 1 }, requires: "energy above your max", effect() { @@ -2780,7 +2827,7 @@ const tech = { }, { name: "energy conservation", - description: "1.04x of damage done recovered as energy", + description: "doing damage to mobs generates energy", maxCount: 9, count: 0, frequency: 1, @@ -2821,7 +2868,7 @@ const tech = { }, { name: "waste heat recovery", - description: "if a mob has died in the last 5 seconds
generate 0.05x max energy per second", + description: "if a mob has died in the last 5 seconds
generate 0.05x max energy every second", maxCount: 1, count: 0, frequency: 1, @@ -2840,7 +2887,7 @@ const tech = { { name: "recycling", descriptionFunction() { - return `if a mob has died in the last 5 seconds
recover 0.005x max ${tech.isEnergyHealth ? "energy" : "health"} per second` + return `if a mob has died in the last 5 seconds
recover 0.005x max ${tech.isEnergyHealth ? "energy" : "health"} every second` }, description: "", maxCount: 1, @@ -2880,14 +2927,16 @@ const tech = { { name: "homeostasis", descriptionFunction() { - return `0.8x damage taken per health below 100
(${(1 - Math.max(0, 1 - m.health) * 0.8).toFixed(2)}x)` + // return `0.9x damage taken for each ${name} missing
(${(Math.pow(0.1 * max, Math.max(0, max - h))).toFixed(2)}x)` + const scale = 0.2 //adjust this to control the strength of this effect + return `reduce damage taken for each missing ${tech.isEnergyHealth ? "energy" : "health"}
down to a limit of ${scale}x at 0 ${tech.isEnergyHealth ? "energy" : "health"}(${(Math.pow(scale, Math.max(0, 1 - (tech.isEnergyHealth ? m.energy / m.maxEnergy : m.health / m.maxHealth)))).toFixed(2)}x)` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return m.health < 0.6 || build.isExperimentSelection + return m.health < 0.9 || build.isExperimentSelection }, requires: "health below 60", effect() { @@ -2900,16 +2949,16 @@ const tech = { { name: "negative feedback", descriptionFunction() { - return `1.007x damage per ${tech.isEnergyHealth ? "energy" : "health"} below 100
(${(1 + 0.7 * Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health))).toFixed(2)}x)` + return `1.006x damage for each missing ${tech.isEnergyHealth ? "energy" : "health"}
(${(1 + 0.6 * Math.max(0, (tech.isEnergyHealth ? m.maxEnergy - m.energy : m.maxHealth - m.health))).toFixed(2)}x)` //1 + 0.6 * Math.max(0, (tech.isEnergyHealth ? m.maxEnergy - m.energy : m.maxHealth - m.health)) }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return m.health < 0.6 || build.isExperimentSelection + return m.health < 0.9 || build.isExperimentSelection }, - requires: "health below 60", + requires: "health below 90", effect() { tech.isLowHealthDmg = true; //used in mob.damage() }, @@ -2946,6 +2995,7 @@ const tech = { count: 0, frequency: 1, frequencyDefault: 1, + isBadRandomOption: true, allowed() { return true }, @@ -3048,7 +3098,7 @@ const tech = { frequencyDefault: 1, isHealTech: true, allowed() { - return (m.health / m.maxHealth) < 0.7 || build.isExperimentSelection + return ((m.health / m.maxHealth) < 0.7 || build.isExperimentSelection) && tech.junkChance < 1 }, requires: "under 70% health", effect() { @@ -3128,7 +3178,7 @@ const tech = { { name: "accretion disk", descriptionFunction() { - return `1.05x damage (${(1 + 0.05 * powerUp.length).toFixed(2)}x)
for each power up that exists on this level` + return `1.05x damage for each power up on this level
(${(1 + 0.05 * powerUp.length).toFixed(2)}x)` }, maxCount: 1, count: 0, @@ -3301,7 +3351,7 @@ const tech = { { name: "many-worlds", // description: "each level is an alternate reality, where you
find a tech at the start of each level", - description: `on each new level spawn a tech power up
and enter an alternate reality`, + description: `at the start of each level spawn a tech
and enter an alternate reality`, maxCount: 1, count: 0, frequency: 1, @@ -3321,7 +3371,7 @@ const tech = { { name: "Ψ(t) collapse", link: `Ψ(t) collapse`, - description: `after a boss dies spawn ${powerUps.orb.research(5)}
after you research enter an alternate reality`, + description: `after a boss dies spawn ${powerUps.orb.research(5)}
if you research enter an alternate reality`, maxCount: 1, count: 0, frequency: 1, @@ -3375,9 +3425,9 @@ const tech = { frequency: 1, frequencyDefault: 1, allowed() { - return (powerUps.research.count > 1 || build.isExperimentSelection) && !tech.isSuperDeterminism + return (powerUps.research.count > 0 || build.isExperimentSelection) && !tech.isSuperDeterminism }, - requires: "at least 2 research, not superdeterminism", + requires: "at least 1 research, not superdeterminism", effect() { tech.isResearchDamage = true; }, @@ -3405,13 +3455,13 @@ const tech = { }, { name: "renormalization", - description: `+5% JUNKtech chance
47% chance to spawn ${powerUps.orb.research(1)} after consuming ${powerUps.orb.research(1)}`, + description: `47% chance to spawn ${powerUps.orb.research(1)} after consuming ${powerUps.orb.research(1)}
+5% JUNKtech chance`, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return (powerUps.research.count > 3 || build.isExperimentSelection) && !tech.isSuperDeterminism + return (powerUps.research.count > 3 || build.isExperimentSelection) && !tech.isSuperDeterminism && tech.junkChance < 1 }, requires: "at least 4 research, not superdeterminism", effect() { @@ -3453,7 +3503,7 @@ const tech = { { name: "Bayesian statistics", descriptionFunction() { - return `1.05x damage per ${powerUps.orb.research(1)} in your inventory
(${(1 + Math.max(0, 0.05 * powerUps.research.count)).toFixed(2)}x)` + return `1.05x damage per ${powerUps.orb.research(1)} in your inventory
(${(1 + Math.max(0, 0.05 * powerUps.research.count)).toFixed(2)}x)` }, maxCount: 1, count: 0, @@ -3602,7 +3652,7 @@ const tech = { { name: "path integral", link: `path integral`, - description: "your next tech choice has all possible options
+5% JUNKtech chance", + description: "your next tech choice has all possible options
+4% JUNKtech chance", maxCount: 1, count: 0, frequency: 1, @@ -3610,13 +3660,13 @@ const tech = { isInstant: true, // isJunk: true, allowed() { - return !tech.isDeterminism && !tech.isBrainstorm + return !tech.isDeterminism && !tech.isBrainstorm && tech.junkChance < 1 }, requires: "not determinism, brainstorm", effect() { tech.tooManyTechChoices = 1 // for (let i = 0; i < this.bonusResearch; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); - this.refundAmount += tech.addJunkTechToPool(0.05) + this.refundAmount += tech.addJunkTechToPool(0.04) }, refundAmount: 0, remove() { @@ -3674,7 +3724,7 @@ const tech = { { name: "technical debt", descriptionFunction() { - return `4x damage but lose 0.15x damage
for each tech you have learned (${(tech.totalCount > 20 ? (Math.pow(0.85, tech.totalCount - 20)) : (4 - 0.15 * tech.totalCount)).toFixed(2)}x)` + return `4x damage but lose 0.15x damage
for each tech you have learned (${(tech.totalCount > 20 ? (Math.pow(0.85, tech.totalCount - 20)) : (4 - 0.15 * tech.totalCount)).toFixed(2)}x)` }, maxCount: 1, count: 0, @@ -3712,19 +3762,19 @@ const tech = { }, { name: "dark patterns", - description: "1.3x damage
+17% JUNKtech chance", + description: "1.3x damage
+15% JUNKtech chance", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return true + return tech.junkChance < 1 }, requires: "", damage: 1.3, effect() { tech.damage *= this.damage - this.refundAmount += tech.addJunkTechToPool(0.17) + this.refundAmount += tech.addJunkTechToPool(0.15) }, refundAmount: 0, remove() { @@ -3737,7 +3787,7 @@ const tech = { { name: "junk DNA", descriptionFunction() { - return `increase damage by twice the
JUNKtech chance (${(1 + 2 * tech.junkChance).toFixed(2)}x)` + return `increase damage by twice the
JUNKtech chance (${(1 + 2 * tech.junkChance).toFixed(2)}x)` }, maxCount: 1, count: 0, @@ -3754,46 +3804,6 @@ const tech = { tech.isJunkDNA = false } }, - { - name: "exciton", - descriptionFunction() { - return `after mobs die they have a 14% chance to
spawn ${powerUps.orb.boost(1)} that give ${(1 + powerUps.boost.damage).toFixed(2)}x damage for ${(powerUps.boost.duration / 60).toFixed(0)} seconds
` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed: () => true, - requires: "", - effect() { - tech.isBoostPowerUps = true - }, - remove() { - tech.isBoostPowerUps = false - } - }, - { - name: "band gap", - descriptionFunction() { - return `${powerUps.orb.boost(1)} give 1.77x damage
but their duration is reduced by 1 second` - }, - maxCount: 9, - count: 1, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isBoostPowerUps || tech.isBoostReplaceAmmo - }, - requires: "exciton, quasiparticles", - effect() { - powerUps.boost.duration -= 60 - powerUps.boost.damage += 0.77 - }, - remove() { - powerUps.boost.duration = 600 - powerUps.boost.damage = 1.25 - } - }, { name: "mass production", descriptionFunction() { @@ -4091,20 +4101,20 @@ const tech = { }, { name: "replication", - description: "+10% chance to duplicate spawned power ups
+22% JUNKtech chance", + description: "+10% chance to duplicate spawned power ups
+15% JUNKtech chance", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return tech.duplicationChance() < 1. + return tech.duplicationChance() < 1 && tech.junkChance < 1 }, requires: "below 100% duplication chance", effect() { tech.duplicateChance += 0.1 powerUps.setPowerUpMode(); //needed after adjusting duplication chance if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.1); - this.refundAmount += tech.addJunkTechToPool(0.22) + this.refundAmount += tech.addJunkTechToPool(0.15) }, refundAmount: 0, remove() { @@ -4140,7 +4150,7 @@ const tech = { { name: "correlated damage", descriptionFunction() { - return `duplication increases damage
(${(1 + Math.min(1, tech.duplicationChance())).toFixed(2)}x)` + return `duplication increases damage
(${(1 + Math.min(1, tech.duplicationChance())).toFixed(2)}x)` }, maxCount: 1, count: 0, @@ -4165,7 +4175,7 @@ const tech = { frequency: 1, frequencyDefault: 1, allowed() { - return tech.duplicationChance() > 0// && !tech.isResearchBoss + return tech.duplicationChance() > 0 }, requires: "some duplication chance", effect() { @@ -4287,7 +4297,7 @@ const tech = { { name: "martingale", descriptionFunction() { - return `${(1 + this.damage).toFixed(1)}x damage. removing this has a 50%
chance return with 2x its damage (${(1 + this.damage).toFixed(1)}x→${(1 + 2 * this.damage).toFixed(1)}x)
` + return `${(1 + this.damage).toFixed(1)}x damage. removing this has a 50%
chance return with 2x its damage (${(1 + this.damage).toFixed(1)}x→${(1 + 2 * this.damage).toFixed(1)}x)
` }, maxCount: 1, count: 0, @@ -4346,7 +4356,7 @@ const tech = { name: "deprecated", scale: 0.07, descriptionFunction() { - return `after removing this gain
${1 + this.scale}x damage per removed tech(${(1 + this.scale * ((this.frequency === 0 ? 0 : 1) + tech.removeCount)).toFixed(2)}x)` + return `after removing this gain
${1 + this.scale}x damage per removed tech(${(1 + this.scale * ((this.frequency === 0 ? 0 : 1) + tech.removeCount)).toFixed(2)}x)` }, maxCount: 1, count: 0, @@ -4371,7 +4381,7 @@ const tech = { { name: "paradigm shift", descriptionFunction() { - return `when paused clicking a tech ejects it
–${tech.pauseEjectTech.toFixed(1)} ${tech.isEnergyHealth ? "energy" : "health"} cost (1.3x cost each use)` + return `when paused clicking a tech ejects it
–${tech.pauseEjectTech.toFixed(1)} ${tech.isEnergyHealth ? "energy" : "health"} cost (1.3x cost each use)` }, maxCount: 1, count: 0, @@ -4439,7 +4449,7 @@ const tech = { { name: "Occams razor", descriptionFunction() { - return `randomly remove half your tech
for each removed ${(1 + this.damagePerRemoved).toFixed(2)}x damage (~${((this.count === 0) ? 1 + this.damagePerRemoved * 0.5 * tech.totalCount : this.damage).toFixed(2)}x)` + return `randomly remove half your tech
for each removed ${(1 + this.damagePerRemoved).toFixed(2)}x damage (~${((this.count === 0) ? 1 + this.damagePerRemoved * 0.5 * tech.totalCount : this.damage).toFixed(2)}x)` }, maxCount: 1, count: 0, @@ -4617,7 +4627,7 @@ const tech = { }, { name: "stress concentration", - description: "mobs below 50% durability die after you shoot
them near their center with needles or rivets", + description: "mobs below half durability die after you shoot
them near their center with needles or rivets", isGunTech: true, maxCount: 1, count: 0, @@ -5344,7 +5354,7 @@ const tech = { }, { name: "bound state", - description: "wave packets reflect backwards 2 times
0.66x wave range", + description: "wave packets reflect backwards 2 times
0.7x wave range", isGunTech: true, maxCount: 9, count: 0, @@ -5655,7 +5665,7 @@ const tech = { }, { name: "nitroglycerin", - description: "1.66x explosive damage
0.66x smaller explosive radius", + description: "1.7x explosive damage
0.7x smaller explosive radius", isGunTech: true, maxCount: 1, count: 0, @@ -5799,7 +5809,7 @@ const tech = { }, { name: "chain reaction", - description: "1.33x grenade radius and damage
blocks caught in explosions also explode", + description: "1.3x grenade radius and damage
blocks caught in explosions also explode", isGunTech: true, maxCount: 1, count: 0, @@ -5994,20 +6004,20 @@ const tech = { }, { name: "booby trap", - description: "50% chance to drop a mine from power ups
+30% JUNKtech chance", + description: "50% chance to drop a mine from power ups
+15% JUNKtech chance", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return tech.haveGunCheck("mine") + return tech.haveGunCheck("mine") && tech.junkChance < 1 }, requires: "mines", effect() { tech.isMineDrop = true; if (tech.isMineDrop) b.mine(m.pos, { x: 0, y: 0 }, 0) - this.refundAmount += tech.addJunkTechToPool(0.30) + this.refundAmount += tech.addJunkTechToPool(0.15) }, refundAmount: 0, remove() { @@ -7289,19 +7299,18 @@ const tech = { } }, { - name: "slow light", - description: "laser gun beam is spread into your recent past
4x full beam damage", + name: "delayed-choice", + description: "laser gun fires a 0.4 second delayed beam
delayed beams do 0.7x damage", isGunTech: true, maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isWideLaser + return tech.haveGunCheck("laser") && !tech.beamSplitter && !tech.isWideLaser }, - requires: "laser gun, not specular reflection, diffraction grating, diffuse beam", + requires: "laser gun, diffraction grating, diffuse beam", effect() { - // this.description = `add 5 more laser beams into into your past` tech.historyLaser++ b.guns[11].chooseFireMethod() }, @@ -7506,7 +7515,7 @@ const tech = { { name: "electronegativity", descriptionFunction() { - return `1.0023x damage per energy
(${(1 + 0.23 * m.maxEnergy).toFixed(2)}x damage at max energy)` + return `1.0023x damage per energy
(${(1 + 0.23 * m.maxEnergy).toFixed(2)}x damage at max energy)` }, // description: "+1% damage per 8 stored energy", isFieldTech: true, @@ -7644,7 +7653,7 @@ const tech = { { name: "dynamic equilibrium", descriptionFunction() { - return `increase damage by your last ${tech.isEnergyHealth ? "energy" : "health"} loss
(${(1 + tech.lastHitDamage * m.lastHit).toFixed(2)}x damage)` + return `increase damage by your last ${tech.isEnergyHealth ? "energy" : "health"} loss
style ="float: right;">(${(1 + tech.lastHitDamage * m.lastHit).toFixed(2)}x damage)` }, isFieldTech: true, maxCount: 9, @@ -7873,7 +7882,8 @@ const tech = { // description: "molecular assembler prints one block
to jump off while midair", descriptionFunction() { const fieldName = m.fieldMode === 8 ? "pilot wave" : "molecular assembler" - return `${fieldName} prints a block
to jump off while midair a second time` + return `a second jump in midair
will print a block to jump off` + // return `${fieldName} prints a block
to jump off while midair` }, isFieldTech: true, maxCount: 1, @@ -7885,10 +7895,66 @@ const tech = { }, requires: "molecular assembler, pilot wave", effect() { - tech.isBlockJump = true + simulation.ephemera.push({ + name: "blockJump", + blockJumpPhase: 0, + do() { + if (m.onGround && m.buttonCD_jump + 10 < m.cycle && !(m.lastOnGroundCycle + m.coyoteCycles > m.cycle)) this.blockJumpPhase = 0 //reset after touching ground or block + if (this.blockJumpPhase === 0 && !m.onGround && !input.up && m.buttonCD_jump + 10 < m.cycle) { //not pressing jump + this.blockJumpPhase = 1 + } else if (this.blockJumpPhase === 1 && input.up && m.buttonCD_jump + 10 < m.cycle) { //2nd jump + this.blockJumpPhase = 2 + let horizontalVelocity = 8 * (- input.left + input.right) //ive player and block horizontal momentum + + const radius = 25 + Math.floor(15 * Math.random()) + body[body.length] = Matter.Bodies.polygon(m.pos.x, m.pos.y + 60 + radius, 4, radius, { + friction: 0.05, + frictionAir: 0.001, + collisionFilter: { + category: cat.body, + mask: cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet + }, + classType: "body", + }); + const block = body[body.length - 1] + //mess with the block shape (this code is horrible) + Composite.add(engine.world, block); //add to world + const r1 = radius * (1 + 0.4 * Math.random()) + const r2 = radius * (1 + 0.4 * Math.random()) + let angle = Math.PI / 4 + const vertices = [] + for (let i = 0, len = block.vertices.length; i < len; i++) { + angle += 2 * Math.PI / len + vertices.push({ x: block.position.x + r1 * Math.cos(angle), y: block.position.y + r2 * Math.sin(angle) }) + } + Matter.Body.setVertices(block, vertices) + // Matter.Body.setAngle(block, Math.PI / 4) + Matter.Body.setVelocity(block, { x: 0.9 * player.velocity.x - horizontalVelocity, y: 10 }); + Matter.Body.applyForce(block, m.pos, { x: 0, y: m.jumpForce * 0.12 * Math.min(m.standingOn.mass, 5) }); + if (tech.isBlockRestitution) { + block.restitution = 0.999 //extra bouncy + block.friction = block.frictionStatic = block.frictionAir = 0.001 + } + if (tech.isAddBlockMass) { + const expand = function (that, massLimit) { + if (that.mass < massLimit) { + const scale = 1.04; + Matter.Body.scale(that, scale, scale); + setTimeout(expand, 20, that, massLimit); + } + }; + expand(block, Math.min(20, block.mass * 3)) + } + //jump + m.buttonCD_jump = m.cycle; //can't jump again until 20 cycles pass + Matter.Body.setVelocity(player, { x: player.velocity.x + horizontalVelocity, y: -7.5 + 0.25 * player.velocity.y }); + player.force.y = -m.jumpForce; //player jump force + } + }, + }) }, remove() { - tech.isBlockJump = false + if (this.count) simulation.removeEphemera("blockJump") } }, { @@ -8213,7 +8279,7 @@ const tech = { { name: "time crystals", descriptionFunction() { - return `2.5x passive energy generation
(+${(150 * m.fieldRegen * 60).toFixed(1)} energy per second)` + return `2.5x passive energy generation
(+${(150 * m.fieldRegen * 60).toFixed(1)} energy per second)` }, isFieldTech: true, maxCount: 1, @@ -8228,14 +8294,14 @@ const tech = { tech.isTimeCrystals = true m.setFieldRegen() this.descriptionFunction = function () { - return `2.5x passive energy generation
(+${(60 * m.fieldRegen * 60).toFixed(1)} energy per second)` + return `2.5x passive energy generation
(+${(60 * m.fieldRegen * 60).toFixed(1)} energy per second)` } }, remove() { tech.isTimeCrystals = false m.setFieldRegen() this.descriptionFunction = function () { - return `2.5x passive energy generation
(+${(150 * m.fieldRegen * 60).toFixed(1)} energy per second)` + return `2.5x passive energy generation
(+${(150 * m.fieldRegen * 60).toFixed(1)} energy per second)` } } }, @@ -8394,9 +8460,9 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return m.fieldMode === 9 || m.fieldMode === 8 || m.fieldMode === 6 + return m.fieldMode === 9 || m.fieldMode === 8 }, - requires: "wormhole, pilot wave, time dilation", + requires: "wormhole, pilot wave", effect() { tech.wimpCount++ spawn.WIMP() @@ -9267,7 +9333,7 @@ const tech = { if (tech.tech[i].isJunk) list.push(tech.tech[i].name) } let name = list[Math.floor(Math.random() * list.length)] - simulation.makeTextLog(`tech.giveTech("${name}")`); + simulation.makeTextLog(`tech.giveTech("${name}")`); tech.giveTech(name) } }, @@ -10477,19 +10543,19 @@ const tech = { }, { name: "expert system", - description: "spawn a tech power up
+64% JUNKtech chance", + description: "spawn a tech power up
+50% JUNKtech chance", maxCount: 9, count: 0, frequency: 0, isInstant: true, isJunk: true, allowed() { - return true + return tech.junkChance < 1 }, requires: "", effect() { powerUps.spawn(m.pos.x, m.pos.y, "tech"); - tech.addJunkTechToPool(0.64) + tech.addJunkTechToPool(0.5) }, remove() { } }, @@ -11723,7 +11789,6 @@ const tech = { isDroneTeleport: null, isDroneFastLook: null, isBulletTeleport: null, - isResearchBoss: null, isJunkResearch: null, laserColor: null, laserColorAlpha: null, @@ -11844,5 +11909,4 @@ const tech = { interestRate: null, isImmunityDamage: null, isMobDeathImmunity: null, - isBlockJump: null, } \ No newline at end of file diff --git a/style.css b/style.css index 7855b46..e63679f 100644 --- a/style.css +++ b/style.css @@ -193,8 +193,6 @@ summary { gap: 10px; background-color: #444; /* padding: 10px 1px; */ - - position: absolute; top: 50%; left: 50%; @@ -212,6 +210,7 @@ summary { overflow: auto; -ms-overflow-style: none; scrollbar-width: none; + /* box-shadow: 7px 7px 6px rgba(0, 0, 0, 0.13); */ } @@ -394,7 +393,7 @@ summary { border-top-right-radius: 0px; padding: 0.2em 0px; /* height: 210px; */ - box-shadow: 8px 8px 7px rgba(0, 0, 50, 0.15); + box-shadow: 8px 8px 6px rgba(0, 0, 50, 0.15); } .card-background { @@ -589,6 +588,7 @@ summary { border-radius: 8px; border: 2px #333 solid; background-color: #fff; + /* box-shadow: 8px 8px 6px rgba(0, 0, 60, 0.11); */ } @@ -1575,9 +1575,10 @@ summary { } .pause-difficulty-row { - line-height: 140%; + line-height: 130%; font-size: 1em; - padding: 10px; + padding: 7px 0px 7px 0px; + /* top | right | bottom | left */ margin: -5px 0; } @@ -1656,6 +1657,7 @@ summary { background-color: hsl(240, 18%, 93%); border: 1px solid #333; border-radius: 5px; - padding: 5px; + padding: 7px; margin-bottom: 10px; + /* box-shadow: 4px 4px 4px hsla(240, 0%, 17%, 0.14); */ } \ No newline at end of file diff --git a/todo.txt b/todo.txt index 74d161d..947e8a8 100644 --- a/todo.txt +++ b/todo.txt @@ -1,17 +1,37 @@ ******************************************************** NEXT PATCH ************************************************** -tech: working mass - in midair molecular assembler or pilot wave prints a block to jump off +new community levels: (you can enable community maps in the settings) + rings by thatLittleFrog + flappyGon by Digin + trial by Cirryn and Tarantula Hawk + arena level updated by Whyisthisnotavalable -buckling: 100%->50% chance to spawn a power up from any block that kills a mob - no longer requires the block to be thrown -mobs: powerUpBoss, snakeBoss tails, cellBoss, ghoster, sucker - all collide with blocks now -added a few more blocks to towers level -deprecated: 1.05->1.07 damage per removed tech -laserLayerBoss has 33% less health and has fewer lasers at higher difficulties +difficulty scaling: 0.84->0.85x player damage per level +research spawn per level is no longer in the difficulty settings + instead players get 1 research for only the first few levels +cell boss has less health at high difficulty -bugfix - disabled spawnDelay stopping on damage because it had too many negative tech interactions - metastability + paradigm shift no longer makes all ejected tech explode +converted JUNK tech to additive, instead of the multiplicative + makes the first JUNK you take do the same, but if you take too much you can get to 100% +right aligned some text in tech descriptions +added some circles to the in-game console messages + +renamed slow light -> delayed-choice - a single 0.4 second delayed 0.7x damage laser beam + also now works with reflection +plasma torch field gets 1.5x damage by default +molecular assembler coupling 0.8->0.6 energy per second +nail-bot upgrade 5->4x fire rate +foam-bot upgrade 3->2.5x size and fire rate +sound-bot upgrade 2.5->2x fire rate, damage, 1->2x wave packet length +boom-bot upgrade reduced range, bot acceleration +orbital-bot upgrade 1.5->2x radius +perimeter defense 0.95->0.96x damage taken per bot +network effect 1.05->1.04x damage per bot +tech: working mass - cleaned up physics and logic a bit +negative feedback scales with health below maxHealth, not health below 100 + 1.007->1.006x damage per missing health +homeostasis scales with missing health, not health below 100 + limit of 0.2x at 0 health ******************************************************* DESIGN ****************************************************** @@ -39,6 +59,17 @@ list of powerful synergies *********************************************************** TODO ***************************************************** +figure out how to put controls in background on initial level + mouse smooth makes the text position jitter when it moves sub pixels + hide the jitter with artificial jitter to make it seem intentional + make it look like the instructions are on a fuzzy TV screen + when player presses move buttons highlight the box/letter for those buttons + +a few bosses have too much health + probably the ones that scale with simulation.difficulty + Matter.Body.setDensity(me, 0.00012 + 0.00001 * simulation.difficulty) // normal density is 0.001 + + make player mass an adjustable var in the skin does this mess with jump height or air control? increase mass and movement speed at the same time @@ -48,27 +79,20 @@ increase mass and movement speed at the same time possible player.mass bad interactions grapple +tech: - if health === maxHealth take 0.6x damage + do 1.5x damage? + JUNK tech - player takes damage from block collisions is this gonna contribute to lag? -player damage seems low -player damage taken seems fine, or maybe increase - -difficulty rework: explicit changes to the game to increase difficulty - UI - - add a wire attached to difficulty power up - like the one attached to player, but thinner - -laser: slow light is the least fun laser tech, make it more fun +tech: anthropic principle - cost 1 research to let you not die once per level + another tech that allows it to trigger multiple times tokamak synergy tech tech: stellarator - after firing a block with tokamak, heal (scale heal amount with block mass?) tech: inertial confinement - while charging tokamak you can fly, and invulnerable but energy drains -after getting a new tech,gun,field draw that tech where it would be in a pause menu for a second seconds - this makes it easier for people to see what's going on - bullets should trigger shrinking platforms level element? level element - player activated elevators