diff --git a/.DS_Store b/.DS_Store index 4cb7f5b..039b8fb 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/js/bullet.js b/js/bullet.js index bd80b96..5b978e2 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -326,11 +326,12 @@ const b = { //player damage if (Vector.magnitude(Vector.sub(where, player.position)) < radius) { - const drain = (tech.isExplosionHarm ? 0.5 : 0.25) * (tech.isImmuneExplosion ? Math.min(1, Math.max(1 - m.energy * 0.7, 0)) : 1) - m.energy -= drain + const DRAIN = (tech.isExplosionHarm ? 0.5 : 0.25) * (tech.isRadioactiveResistance ? 0.25 : 1) + // * (tech.isImmuneExplosion ? Math.min(1, Math.max(1 - m.energy * 0.7, 0)) : 1) + m.energy -= DRAIN if (m.energy < 0) { m.energy = 0 - m.damage(0.03); + m.damage(0.03 * (tech.isRadioactiveResistance ? 0.25 : 1)); } } @@ -922,11 +923,10 @@ const b = { bullet[me].restitution = 0; bullet[me].minDmgSpeed = 0; bullet[me].damageRadius = 100; - bullet[me].maxDamageRadius = 450 + 130 * tech.isNeutronSlow + 130 * tech.isNeutronImmune //+ 150 * Math.random() - bullet[me].radiusDecay = (0.81 + 0.15 * tech.isNeutronSlow + 0.15 * tech.isNeutronImmune) / tech.isBulletsLastLonger + bullet[me].maxDamageRadius = 450 + 130 * tech.isNeutronSlow //+ 150 * Math.random() + bullet[me].radiusDecay = (0.81 + 0.15 * tech.isNeutronSlow) / tech.isBulletsLastLonger bullet[me].stuckTo = null; bullet[me].stuckToRelativePosition = null; - bullet[me].vacuumSlow = 0.97; if (tech.isRPG) { const SCALE = 2 @@ -1030,27 +1030,27 @@ const b = { this.endCycle = 0; } else { //aoe damage to player - if (!tech.isNeutronImmune && Vector.magnitude(Vector.sub(player.position, this.position)) < this.damageRadius) { - const DRAIN = 0.0023 + if (Vector.magnitude(Vector.sub(player.position, this.position)) < this.damageRadius) { + const DRAIN = tech.isRadioactiveResistance ? 0.0025 * 0.25 : 0.0025 if (m.energy > DRAIN) { m.energy -= DRAIN } else { m.energy = 0; - m.damage(0.00015) + m.damage(tech.isRadioactiveResistance ? 0.00016 * 0.25 : 0.00016) //0.00015 } } //aoe damage to mobs for (let i = 0, len = mob.length; i < len; i++) { if (Vector.magnitude(Vector.sub(mob[i].position, this.position)) < this.damageRadius) { - let dmg = b.dmgScale * 0.082 + let dmg = b.dmgScale * 0.09 if (Matter.Query.ray(map, mob[i].position, this.position).length > 0) dmg *= 0.25 //reduce damage if a wall is in the way if (mob[i].shield) dmg *= 4 //x5 to make up for the /5 that shields normally take mob[i].damage(dmg); mob[i].locatePlayer(); if (tech.isNeutronSlow) { Matter.Body.setVelocity(mob[i], { - x: mob[i].velocity.x * this.vacuumSlow, - y: mob[i].velocity.y * this.vacuumSlow + x: mob[i].velocity.x * 0.97, + y: mob[i].velocity.y * 0.97 }); } } @@ -1062,22 +1062,21 @@ const b = { ctx.fill(); ctx.globalCompositeOperation = "source-over" if (tech.isNeutronSlow) { - const that = this - function slow(who, radius = that.explodeRad * 3.2) { + let slow = (who, radius = this.explodeRad * 3.2) => { for (i = 0, len = who.length; i < len; i++) { - const sub = Vector.sub(that.position, who[i].position); + const sub = Vector.sub(this.position, who[i].position); const dist = Vector.magnitude(sub); if (dist < radius) { Matter.Body.setVelocity(who[i], { - x: who[i].velocity.x * that.vacuumSlow, - y: who[i].velocity.y * that.vacuumSlow + x: who[i].velocity.x * 0.975, + y: who[i].velocity.y * 0.975 }); } } } slow(body, this.damageRadius) - if (!tech.isNeutronImmune) slow([player], this.damageRadius) + slow([player], this.damageRadius) } } } @@ -2210,6 +2209,242 @@ const b = { y: speed * Math.sin(dir) }); }, + droneRadioactive(where = { x: m.pos.x + 30 * Math.cos(m.angle) + 20 * (Math.random() - 0.5), y: m.pos.y + 30 * Math.sin(m.angle) + 20 * (Math.random() - 0.5) }, speed = 1) { + const me = bullet.length; + const THRUST = tech.isFastDrones ? 0.002 : 0.0012 + const dir = m.angle + 0.4 * (Math.random() - 0.5); + const RADIUS = (4 + 1 * Math.random()) + bullet[me] = Bodies.polygon(where.x, where.y, 8, RADIUS, { + angle: dir, + inertia: Infinity, + friction: 0.05, + frictionAir: 0, + restitution: 1, + dmg: 0.24, //damage done in addition to the damage from momentum + lookFrequency: 120 + Math.floor(23 * Math.random()), + endCycle: simulation.cycle + Math.floor((900 + 400 * Math.random()) * tech.isBulletsLastLonger) + 140 + RADIUS * 5, + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield //self collide + }, + minDmgSpeed: 0, + lockedOn: null, + isFollowMouse: true, + deathCycles: 110 + RADIUS * 5, + isImproved: false, + radioRadius: 0, + maxRadioRadius: 400 + Math.floor(75 * Math.random()) + 80 * tech.isNeutronSlow, + beforeDmg(who) { + const unit = Vector.mult(Vector.normalise(Vector.sub(this.position, who.position)), -20) //move away from target after hitting + Matter.Body.setVelocity(this, { + x: unit.x, + y: unit.y + }); + this.lockedOn = null + if (this.endCycle > simulation.cycle + this.deathCycles) { + this.endCycle -= 60 + if (simulation.cycle + this.deathCycles > this.endCycle) this.endCycle = simulation.cycle + this.deathCycles + } + }, + onEnd() { + if (tech.isDroneRespawn) { + const who = b.guns[b.activeGun] + if (who.name === "drones" && who.ammo > 0 && mob.length) { + b.droneRadioactive({ x: this.position.x, y: this.position.y }, 0) + if (Math.random() < 0.33) { + b.guns[b.activeGun].ammo--; + simulation.updateGunHUD(); + } + } + } + }, + do() { + //radioactive zone + this.radioRadius = this.radioRadius * 0.993 + 0.007 * this.maxRadioRadius //smooth radius towards max + //aoe damage to player + if (Vector.magnitude(Vector.sub(player.position, this.position)) < this.radioRadius) { + const DRAIN = tech.isRadioactiveResistance ? 0.0023 * 0.25 : 0.0023 + if (m.energy > DRAIN) { + m.energy -= DRAIN + } else { + m.energy = 0; + m.damage(tech.isRadioactiveResistance ? 0.00015 * 0.25 : 0.00015) //0.00015 + } + } + //aoe damage to mobs + for (let i = 0, len = mob.length; i < len; i++) { + if (Vector.magnitude(Vector.sub(mob[i].position, this.position)) < this.radioRadius) { + let dmg = b.dmgScale * 0.035 //neutron bombs dmg = 0.09 + if (Matter.Query.ray(map, mob[i].position, this.position).length > 0) dmg *= 0.25 //reduce damage if a wall is in the way + if (mob[i].shield) dmg *= 4 //x5 to make up for the /5 that shields normally take + mob[i].damage(dmg); + mob[i].locatePlayer(); + if (tech.isNeutronSlow) { + Matter.Body.setVelocity(mob[i], { + x: mob[i].velocity.x * 0.97, + y: mob[i].velocity.y * 0.97 + }); + } + } + } + //draw + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.radioRadius, 0, 2 * Math.PI); + ctx.globalCompositeOperation = "lighter" + ctx.fillStyle = `rgba(25,139,170,${0.1+0.05*Math.random()})`; + ctx.fill(); + ctx.globalCompositeOperation = "source-over" + + if (tech.isNeutronSlow) { + const slow = (who, radius = this.radioRadius * 3.2) => { + for (i = 0, len = who.length; i < len; i++) { + const sub = Vector.sub(this.position, who[i].position); + const dist = Vector.magnitude(sub); + if (dist < radius) { + Matter.Body.setVelocity(who[i], { + x: who[i].velocity.x * 0.975, + y: who[i].velocity.y * 0.975 + }); + } + } + } + slow(body, this.radioRadius) + slow([player], this.radioRadius) + } + + + //normal drone actions + if (simulation.cycle + this.deathCycles > this.endCycle) { //fall shrink and die + this.force.y += this.mass * 0.0012; + this.restitution = 0.2; + const scale = 0.995; + Matter.Body.scale(this, scale, scale); + this.maxRadioRadius = 0 + this.radioRadius = this.radioRadius * 0.98 //let radioactivity decrease + } else { + this.force.y += this.mass * 0.0002; //gravity + + if (!(simulation.cycle % this.lookFrequency)) { + //find mob targets + this.lockedOn = null; + let closeDist = Infinity; + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + !mob[i].isBadTarget && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0 + ) { + const TARGET_VECTOR = Vector.sub(this.position, mob[i].position) + const DIST = Vector.magnitude(TARGET_VECTOR); + if (DIST < closeDist) { + closeDist = DIST; + this.lockedOn = mob[i] + } + } + } + //power ups + if (!this.isImproved && !simulation.isChoosing && !tech.isExtraMaxEnergy) { + if (this.lockedOn) { + //grab, but don't lock onto nearby power up + for (let i = 0, len = powerUp.length; i < len; ++i) { + if ( + (powerUp[i].name !== "heal" || m.health < 0.9 * m.maxHealth || tech.isDroneGrab) && + (powerUp[i].name !== "field" || !tech.isDeterminism) && + Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 + ) { + //draw pickup for a single cycle + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(powerUp[i].position.x, powerUp[i].position.y); + ctx.strokeStyle = "#000" + ctx.lineWidth = 4 + ctx.stroke(); + //pick up nearby power ups + powerUps.onPickUp(powerUp[i]); + powerUp[i].effect(); + Matter.World.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + if (tech.isDroneGrab) { + this.isImproved = true; + const SCALE = 2.25 + Matter.Body.scale(this, SCALE, SCALE); + this.lookFrequency = 30 + Math.floor(11 * Math.random()); + this.endCycle += 1000 * tech.isBulletsLastLonger + this.maxRadioRadius *= 1.25 + } + break; + } + } + } else { + //look for power ups to lock onto + let closeDist = Infinity; + for (let i = 0, len = powerUp.length; i < len; ++i) { + if ( + (powerUp[i].name !== "heal" || m.health < 0.9 * m.maxHealth || tech.isDroneGrab) && + (powerUp[i].name !== "field" || !tech.isDeterminism) + ) { + if (Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 && !simulation.isChoosing) { + //draw pickup for a single cycle + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(powerUp[i].position.x, powerUp[i].position.y); + ctx.strokeStyle = "#000" + ctx.lineWidth = 4 + ctx.stroke(); + //pick up nearby power ups + powerUps.onPickUp(powerUp[i]); + powerUp[i].effect(); + Matter.World.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + if (tech.isDroneGrab) { + this.isImproved = true; + const SCALE = 2.25 + Matter.Body.scale(this, SCALE, SCALE); + this.lookFrequency = 30 + Math.floor(11 * Math.random()); + this.endCycle += 1000 * tech.isBulletsLastLonger + this.maxRadioRadius *= 1.25 + } + break; + } + //look for power ups to lock onto + if ( + Matter.Query.ray(map, this.position, powerUp[i].position).length === 0 && + Matter.Query.ray(body, this.position, powerUp[i].position).length === 0 + ) { + const TARGET_VECTOR = Vector.sub(this.position, powerUp[i].position) + const DIST = Vector.magnitude(TARGET_VECTOR); + if (DIST < closeDist) { + closeDist = DIST; + this.lockedOn = powerUp[i] + } + } + } + } + } + } + } + if (this.lockedOn) { //accelerate towards mobs + this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, this.lockedOn.position)), -this.mass * THRUST) + } else { //accelerate towards mouse + this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, simulation.mouseInGame)), -this.mass * THRUST) + } + // speed cap instead of friction to give more agility + if (this.speed > 6) { + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.97, + y: this.velocity.y * 0.97 + }); + } + } + } + }) + World.add(engine.world, bullet[me]); //add bullet to world + Matter.Body.setVelocity(bullet[me], { + x: speed * Math.cos(dir), + y: speed * Math.sin(dir) + }); + }, foam(position, velocity, radius) { // radius *= Math.sqrt(tech.bulletSize) const me = bullet.length; @@ -3323,7 +3558,7 @@ const b = { if (tech.isNailRadiation) { mobs.statusDoT(who, tech.isFastRadiation ? 12 : 3, tech.isSlowRadiation ? 240 : (tech.isFastRadiation ? 30 : 120)) // one tick every 30 cycles } else { - let dmg = b.dmgScale * 5 + let dmg = b.dmgScale * 5.5 if (tech.isCrit && who.isStunned) dmg *= 4 who.damage(dmg, tech.isNeedleShieldPierce); simulation.drawList.push({ //add dmg to draw queue @@ -3356,18 +3591,18 @@ const b = { x: m.Vx / 2 + SPEED * Math.cos(angle), y: m.Vy / 2 + SPEED * Math.sin(angle) }); - Matter.Body.setDensity(bullet[me], 0.00001); + // Matter.Body.setDensity(bullet[me], 0.00001); World.add(engine.world, bullet[me]); //add bullet to world } if (m.crouch) { - m.fireCDcycle = m.cycle + 50 * b.fireCD; // cool down + m.fireCDcycle = m.cycle + 45 * b.fireCD; // cool down makeNeedle() for (let i = 1; i < 4; i++) { //4 total needles setTimeout(() => { if (!simulation.paused) makeNeedle() }, 60 * i); } } else { - m.fireCDcycle = m.cycle + 30 * b.fireCD; // cool down + m.fireCDcycle = m.cycle + 25 * b.fireCD; // cool down makeNeedle() for (let i = 1; i < 3; i++) { //3 total needles setTimeout(() => { if (!simulation.paused) makeNeedle() }, 60 * i); @@ -4145,12 +4380,22 @@ const b = { have: false, do() {}, fire() { - if (m.crouch) { - b.drone({ x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) }, 45) - m.fireCDcycle = m.cycle + Math.floor(13 * b.fireCD); // cool down + if (tech.isDroneRadioactive) { + if (m.crouch) { + b.droneRadioactive({ x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) }, 45) + m.fireCDcycle = m.cycle + Math.floor(7 * 13 * b.fireCD); // cool down + } else { + b.droneRadioactive({ x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) }, 10) + m.fireCDcycle = m.cycle + Math.floor(7 * 6 * b.fireCD); // cool down + } } else { - b.drone() - m.fireCDcycle = m.cycle + Math.floor(6 * b.fireCD); // cool down + if (m.crouch) { + b.drone({ x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) }, 45) + m.fireCDcycle = m.cycle + Math.floor(13 * b.fireCD); // cool down + } else { + b.drone() + m.fireCDcycle = m.cycle + Math.floor(6 * b.fireCD); // cool down + } } } }, diff --git a/js/level.js b/js/level.js index 1e16ceb..768f834 100644 --- a/js/level.js +++ b/js/level.js @@ -16,12 +16,15 @@ const level = { // simulation.difficulty = 20 // simulation.isHorizontalFlipped = true // level.difficultyIncrease(99) - // m.setField("standing wave harmonics") - // tech.giveTech("spherical harmonics") - // for (let i = 0; i < 9; i++) tech.giveTech("spherical harmonics") + // m.setField("nano-scale manufacturing") // b.giveGuns("grenades") + // tech.giveTech("neutron bomb") + // b.giveGuns("drones") + // tech.giveTech("radioactive drones") + // tech.isRadioactiveResistance = true + // for (let i = 0; i < 9; i++) tech.giveTech("spherical harmonics") // tech.isExplodeRadio = true - // tech.giveTech("chain reaction") + // tech.giveTech("vacuum permittivity") // tech.giveTech("quenching") // tech.giveTech("decoherence") // tech.giveTech("supertemporal") @@ -96,7 +99,7 @@ const level = { powerUps.spawn(player.position.x + Math.random() * 50, player.position.y - Math.random() * 50, "tech", false); } if (tech.isHealLowHealth) { - const len = Math.ceil((m.maxHealth - m.health) / 0.33) + const len = Math.ceil((m.maxHealth - m.health) / 0.25) for (let i = 0; i < len; i++) powerUps.spawn(player.position.x + 90 * (Math.random() - 0.5), player.position.y + 90 * (Math.random() - 0.5), "heal", false); } if (tech.isMACHO) spawn.MACHO() @@ -965,23 +968,12 @@ const level = { ctx.fillRect(this.min.x, this.min.y + offset, this.width, this.height - offset) if (this.height > 0 && Matter.Query.region([player], this).length) { - const drain = 0.003 + m.fieldRegen - if (m.energy > drain) { - m.energy -= drain + const DRAIN = 0.003 * (tech.isRadioactiveResistance ? 0.25 : 1) + m.fieldRegen + console.log(DRAIN) + if (m.energy > DRAIN) { + m.energy -= DRAIN } else { - if (damage < 0.02) { - m.damage(damage) - } else if (m.immuneCycle < m.cycle) { - m.immuneCycle = m.cycle + tech.collisionImmuneCycles; - m.damage(damage) - simulation.drawList.push({ //add dmg to draw queue - x: player.position.x, - y: player.position.y, - radius: damage * 1500, - color: simulation.mobDmgColor, - time: 20 - }); - } + m.damage(damage * (tech.isRadioactiveResistance ? 0.25 : 1)) } //float if (player.velocity.y > 5) player.force.y -= 0.95 * player.mass * simulation.g @@ -1926,7 +1918,7 @@ const level = { const doorWidth2 = 15 + Math.floor(100 * Math.random() * Math.random()) spawn.bodyRect(offset.x + 2050 - doorWidth2 / 2, offset.y - 175, doorWidth2, 165); //block door } else { - spawn.mapRect(offset.x + 2000, offset.y - 2800, 100, 2800); //right wall + spawn.mapRect(offset.x + 2000, offset.y - 2800, 100, 2900); //right wall } } @@ -2086,7 +2078,7 @@ const level = { }, testing() { // const button = level.button(200, -700) - const toggle = level.toggle(200, -700) + // const toggle = level.toggle(200, -700) level.custom = () => { // button.draw(); ctx.fillStyle = "rgba(0,255,255,0.1)"; @@ -2096,7 +2088,7 @@ const level = { level.enter.draw(); }; level.customTopLayer = () => { - toggle.query(); + // toggle.query(); }; level.setPosToSpawn(0, -750); //normal spawn @@ -2127,11 +2119,9 @@ const level = { function blockDoor(x, y, blockSize = 58) { spawn.mapRect(x, y - 290, 40, 60); // door lip spawn.mapRect(x, y, 40, 50); // door lip - for (let i = 0; i < 4; ++i) { - spawn.bodyRect(x + 5, y - 260 + i * blockSize, 30, blockSize); - } + for (let i = 0; i < 4; ++i) spawn.bodyRect(x + 5, y - 260 + i * blockSize, 30, blockSize); } - blockDoor(710, -710); + // blockDoor(710, -710); // for (let i = 0; i < 30; i++) powerUps.directSpawn(710, -710, "tech"); spawn.mapRect(2500, -1200, 200, 750); //right wall @@ -2151,10 +2141,10 @@ const level = { // spawn.shooterBoss(1900, -500) // spawn.historyBoss(1200, -500) // spawn.laserTargetingBoss(1600, -400) - // spawn.striker(1600, -500) + spawn.hopper(1600, -500) // spawn.laserTargetingBoss(1700, -120) // spawn.bomberBoss(1400, -500) - // spawn.ghoster(1800, -120) + spawn.hopBoss(1800, -120) // spawn.streamBoss(1600, -500) // spawn.orbitalBoss(1600, -500) // spawn.cellBossCulture(1600, -500) @@ -2962,8 +2952,6 @@ const level = { button1.query(); button1.draw(); - hazard.query(); - hazard.level(button1.isUp) rotor.rotate(); ctx.fillStyle = "hsl(175, 15%, 76%)" @@ -4017,43 +4005,54 @@ const level = { level.enter.draw(); }; + + // simulation.draw.mapPath = new Path2D(); + // for (let i = 0, len = map.length; i < len; ++i) { + // let vertices = map[i].vertices; + // simulation.draw.mapPath.moveTo(vertices[0].x, vertices[0].y); + // for (let j = 1; j < vertices.length; j += 1) { + // simulation.draw.mapPath.lineTo(vertices[j].x, vertices[j].y); + // } + // simulation.draw.mapPath.lineTo(vertices[0].x, vertices[0].y); + // } + const lightingPath = new Path2D() //pre-draw the complex lighting path to save processing + lightingPath.moveTo(-1800, -500) + lightingPath.lineTo(-910, -500) //3rd floor light + lightingPath.lineTo(-1300, 0) + lightingPath.lineTo(-500, 0) + lightingPath.lineTo(-890, -500) + lightingPath.lineTo(-175, -500) + lightingPath.lineTo(-175, -250) + lightingPath.lineTo(175, -250) + lightingPath.lineTo(175, 0) + lightingPath.lineTo(-910, 100) //2nd floor light left + lightingPath.lineTo(-1300, 600) + lightingPath.lineTo(-500, 600) + lightingPath.lineTo(-890, 100) + lightingPath.lineTo(190, 100) //2nd floor light right + lightingPath.lineTo(-200, 600) + lightingPath.lineTo(600, 600) + lightingPath.lineTo(210, 100) + lightingPath.lineTo(1100, 100) + lightingPath.lineTo(1100, 1400) + lightingPath.lineTo(600, 1400) //1st floor light right + lightingPath.lineTo(10, 700) + lightingPath.lineTo(-10, 700) + lightingPath.lineTo(-600, 1400) + lightingPath.lineTo(-1950, 1400) //1st floor light left + lightingPath.lineTo(-2290, 950) + lightingPath.lineTo(-2310, 950) + lightingPath.lineTo(-2650, 1400) + lightingPath.lineTo(-3025, 1400) + lightingPath.lineTo(-3025, 150) + lightingPath.lineTo(-2590, 150) + lightingPath.lineTo(-2600, -150) + lightingPath.lineTo(-1800, -150) + lightingPath.lineTo(-1800, -500) //top left end/start of path + level.customTopLayer = () => { ctx.fillStyle = "rgba(0,0,0,0.15)"; //shadows and lights - ctx.beginPath() - ctx.moveTo(-1800, -500) - ctx.lineTo(-910, -500) //3rd floor light - ctx.lineTo(-1300, 0) - ctx.lineTo(-500, 0) - ctx.lineTo(-890, -500) - ctx.lineTo(-175, -500) - ctx.lineTo(-175, -250) - ctx.lineTo(175, -250) - ctx.lineTo(175, 0) - ctx.lineTo(-910, 100) //2nd floor light left - ctx.lineTo(-1300, 600) - ctx.lineTo(-500, 600) - ctx.lineTo(-890, 100) - ctx.lineTo(190, 100) //2nd floor light right - ctx.lineTo(-200, 600) - ctx.lineTo(600, 600) - ctx.lineTo(210, 100) - ctx.lineTo(1100, 100) - ctx.lineTo(1100, 1400) - ctx.lineTo(600, 1400) //1st floor light right - ctx.lineTo(10, 700) - ctx.lineTo(-10, 700) - ctx.lineTo(-600, 1400) - ctx.lineTo(-1950, 1400) //1st floor light left - ctx.lineTo(-2290, 950) - ctx.lineTo(-2310, 950) - ctx.lineTo(-2650, 1400) - ctx.lineTo(-3025, 1400) - ctx.lineTo(-3025, 150) - ctx.lineTo(-2590, 150) - ctx.lineTo(-2600, -150) - ctx.lineTo(-1800, -150) - ctx.lineTo(-1800, -500) //top left end/start of path - ctx.fill() + ctx.fill(lightingPath); }; level.setPosToSpawn(25, -55); //normal spawn diff --git a/js/player.js b/js/player.js index 4b9d2e4..8000520 100644 --- a/js/player.js +++ b/js/player.js @@ -495,7 +495,7 @@ const m = { if (tech.isSlowFPS) dmg *= 0.8 // if (tech.isPiezo) dmg *= 0.85 if (tech.isHarmReduce && input.field && m.fieldCDcycle < m.cycle) dmg *= 0.4 - if (tech.isBotArmor) dmg *= 0.94 ** b.totalBots() + if (tech.isBotArmor) dmg *= 0.93 ** b.totalBots() if (tech.isHarmArmor && m.lastHarmCycle + 600 > m.cycle) dmg *= 0.33; if (tech.isNoFireDefense && m.cycle > m.fireCDcycle + 120) dmg *= 0.34 if (tech.energyRegen === 0) dmg *= 0.34 @@ -1682,6 +1682,9 @@ const m = { } else if (tech.isIceField) { m.energy -= 0.04; b.iceIX(1) + } else if (tech.isDroneRadioactive) { + m.energy -= 1; + b.droneRadioactive({ x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) }, 25) } else { m.energy -= 0.45 * tech.droneEnergyReduction; b.drone() diff --git a/js/powerup.js b/js/powerup.js index 2fee77a..a84de0c 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -736,7 +736,11 @@ const powerUps = { //bonus power ups for clearing runs in the last game if (level.levelsCleared === 0 && !simulation.isCheating && localSettings.levelsClearedLastGame > 1) { - for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++) powerUps.spawn(m.pos.x, m.pos.y, "tech", false); //spawn a tech for levels cleared in last game + for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++) { + powerUps.spawn(m.pos.x, m.pos.y, "tech", false); //spawn a tech for levels cleared in last game + simulation.makeTextLog(`for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++)`); + simulation.makeTextLog(`{ powerUps.spawn(m.pos.x, m.pos.y, "tech") //simulation superposition }`); + } localSettings.levelsClearedLastGame = 0 //after getting bonus power ups reset run history localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } diff --git a/js/spawn.js b/js/spawn.js index c4c39a4..de4f64b 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -170,13 +170,13 @@ const spawn = { Matter.Body.setVelocity(this, { x: 0, y: 0 }); //aoe damage to player - if (m.immuneCycle < m.cycle && Vector.magnitude(Vector.sub(player.position, this.position)) < this.radius && !tech.isNeutronImmune) { - const DRAIN = 0.07 + if (m.immuneCycle < m.cycle && Vector.magnitude(Vector.sub(player.position, this.position)) < this.radius) { + const DRAIN = tech.isRadioactiveResistance ? 0.07 * 0.25 : 0.07 if (m.energy > DRAIN) { m.energy -= DRAIN } else { m.energy = 0; - m.damage(0.007 * simulation.dmgScale) + m.damage((tech.isRadioactiveResistance ? 0.007 * 0.25 : 0.007) * simulation.dmgScale) simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, @@ -233,7 +233,7 @@ const spawn = { y: me.position.y }, bodyB: me, - stiffness: 0.001, + stiffness: 1, damping: 1 }); World.add(engine.world, me.constraint); @@ -244,7 +244,7 @@ const spawn = { me.memory = Infinity; me.hasRunDeathScript = false me.locatePlayer(); - const density = 0.25 + const density = 0.23 Matter.Body.setDensity(me, density); //extra dense //normal is 0.001 //makes effective life much larger // spawn.shield(me, x, y, 1); me.onDeath = function() { @@ -416,7 +416,7 @@ const spawn = { } } else if (this.mode !== 3) { //all three modes at once this.cycle = 0; - Matter.Body.setDensity(me, density); //extra dense //normal is 0.001 //makes effective life much larger + Matter.Body.setDensity(me, 10 * density); //extra dense //normal is 0.001 //makes effective life much larger if (this.mode === 2) { Matter.Body.scale(this, 5, 5); } else { @@ -1017,6 +1017,8 @@ const spawn = { me.accelMag = 0.04; me.g = 0.0017; //required if using 'gravity' me.frictionAir = 0.01; + me.friction = 1 + me.frictionStatic = 1 me.restitution = 0; me.delay = 120 * simulation.CDScale; me.randomHopFrequency = 200 + Math.floor(Math.random() * 150); @@ -1032,7 +1034,7 @@ const spawn = { const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass; const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); this.force.x += forceMag * Math.cos(angle); - this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.07 + 0.02) * this.mass; //antigravity + this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.07 + 0.06) * this.mass; //antigravity } } else { //randomly hob if not aware of player @@ -1048,6 +1050,96 @@ const spawn = { } }; }, + hopBoss(x, y, radius = 90) { + mobs.spawn(x, y, 5, radius, "rgb(0,200,180)"); + let me = mob[mob.length - 1]; + me.isBoss = true; + me.g = 0.005; //required if using 'gravity' + me.frictionAir = 0.01; + me.friction = 1 + me.frictionStatic = 1 + me.restitution = 0; + me.accelMag = 0.07; + me.delay = 120 * simulation.CDScale; + me.randomHopFrequency = 200 + me.randomHopCD = simulation.cycle + me.randomHopFrequency; + // me.memory = 420; + me.isInAir = false + Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger + spawn.shield(me, x, y, 1); + spawn.spawnOrbitals(me, radius + 60, 1) + me.onDeath = function() { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + }; + me.lastSpeed = me.speed + me.do = function() { + this.gravity(); + this.seePlayerCheck(); + this.checkStatus(); + if (this.seePlayer.recall) { + const deltaSpeed = this.lastSpeed - this.speed + this.lastSpeed = this.speed + if (deltaSpeed > 13 && this.speed < 5) { //if the player slows down greatly in one cycle + //damage and push player away, push away blocks + const range = 800 //Math.min(800, 50 * deltaSpeed) + for (let i = body.length - 1; i > -1; i--) { + if (!body[i].isNotHoldable) { + sub = Vector.sub(body[i].position, this.position); + dist = Vector.magnitude(sub); + if (dist < range) { + knock = Vector.mult(Vector.normalise(sub), Math.min(20, 50 * body[i].mass / dist)); + body[i].force.x += knock.x; + body[i].force.y += knock.y; + } + } + } + + simulation.drawList.push({ //draw radius + x: this.position.x, + y: this.position.y, + radius: range, + color: "rgba(0,200,180,0.6)", + time: 4 + }); + } + + if (this.isInAir) { + if (this.velocity.y > -0.01 && Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length) { //not moving up, and has hit the map or a body + this.isInAir = false //landing + this.cd = simulation.cycle + this.delay + + } + } else { //on ground + if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { //jump + this.isInAir = true + const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass; + const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + this.force.x += forceMag * Math.cos(angle); + this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.05 + 0.04) * this.mass; //antigravity + } + } + + // if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { + // this.cd = simulation.cycle + this.delay; + // const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass; + // const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + // this.force.x += forceMag * Math.cos(angle); + // this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.05 + 0.04) * this.mass; //antigravity + // } + } else { + //randomly hob if not aware of player + if (this.randomHopCD < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { + this.randomHopCD = simulation.cycle + this.randomHopFrequency; + //slowly change randomHopFrequency after each hop + this.randomHopFrequency = Math.max(100, this.randomHopFrequency + 200 * (0.5 - Math.random())); + const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass * (0.5 + Math.random() * 0.2); + const angle = -Math.PI / 2 + (Math.random() - 0.5) * Math.PI; + this.force.x += forceMag * Math.cos(angle); + this.force.y += forceMag * Math.sin(angle) - (0.1 + 0.08 * Math.random()) * this.mass; //antigravity + } + } + }; + }, spinner(x, y, radius = 30 + Math.ceil(Math.random() * 35)) { mobs.spawn(x, y, 5, radius, "#000000"); let me = mob[mob.length - 1]; @@ -2063,7 +2155,7 @@ const spawn = { y: me.position.y }, bodyB: me, - stiffness: 0.001, + stiffness: 1, damping: 1 }); World.add(engine.world, me.constraint); @@ -2575,8 +2667,8 @@ const spawn = { y: me.position.y }, bodyB: me, - stiffness: 0.0002, - damping: 1 + stiffness: 0.00004, + damping: 0.1 }); World.add(engine.world, me.constraint); }, 2000); //add in a delay in case the level gets flipped left right @@ -2912,7 +3004,7 @@ const spawn = { y: me.position.y }, bodyB: me, - stiffness: 0.001, + stiffness: 0.0001, damping: 1 }); World.add(engine.world, me.constraint); diff --git a/js/tech.js b/js/tech.js index d8caa4d..b554971 100644 --- a/js/tech.js +++ b/js/tech.js @@ -177,7 +177,7 @@ if (tech.isOneGun && b.inventory.length < 2) dmg *= 1.22 if (tech.isNoFireDamage && m.cycle > m.fireCDcycle + 120) dmg *= 1.9 if (tech.isSpeedDamage) dmg *= 1 + Math.min(0.43, player.speed * 0.015) - if (tech.isBotDamage) dmg *= 1 + 0.05 * b.totalBots() + if (tech.isBotDamage) dmg *= 1 + 0.06 * b.totalBots() return dmg * tech.slowFire * tech.aimDamage }, duplicationChance() { @@ -716,7 +716,7 @@ }, requires: "an explosive damage source, not ammonium nitrate or nitroglycerin", effect: () => { - tech.isExplodeRadio = true; + tech.isExplodeRadio = true; //iridium-192 }, remove() { tech.isExplodeRadio = false; @@ -823,9 +823,9 @@ frequency: 2, frequencyDefault: 2, allowed() { - return tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("vacuum bomb") || tech.isMissileField || tech.isExplodeMob || tech.isPulseLaser || tech.isBlockExplosion + return !tech.isExplodeRadio && (tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("vacuum bomb") || tech.isMissileField || tech.isExplodeMob || tech.isPulseLaser || tech.isBlockExplosion) }, - requires: "an explosive damage source", + requires: "an explosive damage source, not iridium-192", effect: () => { tech.isImmuneExplosion = true; }, @@ -841,9 +841,9 @@ frequency: 2, frequencyDefault: 2, allowed() { - return ((m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isSporeField || tech.isMissileField || tech.isIceField)) || tech.haveGunCheck("drones") || tech.haveGunCheck("super balls") || tech.haveGunCheck("shotgun")) && !tech.isNailShot + return ((m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isDroneRadioactive || tech.isSporeField || tech.isMissileField || tech.isIceField)) || (tech.haveGunCheck("drones") && !tech.isDroneRadioactive) || tech.haveGunCheck("super balls") || tech.haveGunCheck("shotgun")) && !tech.isNailShot }, - requires: "drones, super balls, shotgun", + requires: "super balls, shotgun, drones, not radioactive drones", effect() { tech.isIncendiary = true }, @@ -1376,7 +1376,7 @@ }, { name: "perimeter defense", - description: "reduce harm by 6%
for each of your permanent bots", + description: "reduce harm by 7%
for each of your permanent bots", maxCount: 1, count: 0, frequency: 2, @@ -1394,7 +1394,7 @@ }, { name: "network effect", - description: "increase damage by 5%
for each of your permanent bots", + description: "increase damage by 6%
for each of your permanent bots", maxCount: 1, count: 0, frequency: 2, @@ -2509,7 +2509,7 @@ }, { name: "negentropy", - description: `at the start of each level
spawn a heal for every 33 missing health`, + description: `at the start of each level
spawn a heal for every 25 missing health`, maxCount: 1, count: 0, frequency: 1, @@ -4039,7 +4039,7 @@ }, { name: "neutron bomb", - description: "grenades are irradiated with Cf-252
does damage, harm, and drains energy", + description: "grenades are irradiated with Cf-252
does damage, harm, and drains energy", isGunTech: true, maxCount: 1, count: 0, @@ -4060,35 +4060,35 @@ }, { name: "water shielding", - description: "increase neutron bomb's range by 20%
you are immune to its harmful effects", + description: "radioactive effects on you are reduced by 75%
neutron bomb, drones, explosions, slime", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return tech.isNeutronBomb + return tech.isNeutronBomb || tech.isDroneRadioactive }, - requires: "neutron bomb", + requires: "neutron bomb or radioactive drones", effect() { - tech.isNeutronImmune = true + tech.isRadioactiveResistance = true }, remove() { - tech.isNeutronImmune = false + tech.isRadioactiveResistance = false } }, { name: "vacuum permittivity", - description: "increase neutron bomb's range by 20%
objects in range of the bomb are slowed", + description: "increase radioactive range by 20%
objects in range of the bomb are slowed", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return tech.isNeutronBomb + return tech.isNeutronBomb || tech.isDroneRadioactive }, - requires: "neutron bomb", + requires: "neutron bomb or radioactive drones", effect() { tech.isNeutronSlow = true }, @@ -4270,6 +4270,39 @@ tech.isMutualism = false } }, + { + name: "radioisotope generator", + description: "irradiate drones, reduce ammo by 75%
does damage, harm, and drains energy", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("drones") && tech.droneCycleReduction === 1 && !tech.isIncendiary + }, + requires: "drone gun, not reduced tolerances or incendiary", + effect() { + tech.isDroneRadioactive = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "drones") { + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * 0.25 + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.25) + } + } + }, + remove() { + if (tech.isDroneRadioactive) { + tech.isDroneRadioactive = false + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "drones") { + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack + b.guns[i].ammo = b.guns[i].ammo * 4 + } + } + } + } + }, { name: "brushless motor", description: "drones accelerate 50% faster", @@ -4308,33 +4341,6 @@ tech.isDroneGrab = false } }, - { - name: "reduced tolerances", - description: "increase drone ammo/efficiency by 66%
reduce the average drone lifetime by 40%", - isGunTech: true, - maxCount: 3, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("drones") || (m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isSporeField || tech.isMissileField || tech.isIceField)) - }, - requires: "drones", - effect() { - tech.droneCycleReduction = Math.pow(0.6, 1 + this.count) - tech.droneEnergyReduction = Math.pow(0.333, 1 + this.count) - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "drones") b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * Math.pow(3, this.count) - } - }, - remove() { - tech.droneCycleReduction = 1 - tech.droneEnergyReduction = 1 - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "drones") b.guns[i].ammoPack = b.guns[i].defaultAmmoPack - } - } - }, { name: "drone repair", description: "broken drones repair if the drone gun is active
repairing has a 33% chance to use 1 ammo", @@ -4354,6 +4360,36 @@ tech.isDroneRespawn = false } }, + { + name: "reduced tolerances", + description: "increase drone ammo/efficiency by 66%
reduce the average drone lifetime by 40%", + isGunTech: true, + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isDroneRadioactive && (tech.haveGunCheck("drones") || (m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isSporeField || tech.isMissileField || tech.isIceField))) + }, + requires: "drones", + effect() { + tech.droneCycleReduction = Math.pow(0.6, 1 + this.count) + tech.droneEnergyReduction = Math.pow(0.333, 1 + this.count) + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "drones") { + const scale = Math.pow(3, this.count + 1) + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * scale + } + } + }, + remove() { + tech.droneCycleReduction = 1 + tech.droneEnergyReduction = 1 + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "drones") b.guns[i].ammoPack = b.guns[i].defaultAmmoPack + } + } + }, { name: "necrophoresis", description: "foam bubbles grow and split into 3 copies
when the mob they are stuck to dies", @@ -7209,5 +7245,6 @@ isBlockExplosion: null, superBallDelay: null, isBlockExplode: null, - isOverHeal: null + isOverHeal: null, + isDroneRadioactive: null } \ No newline at end of file diff --git a/todo.txt b/todo.txt index 1ab5963..9fecfab 100644 --- a/todo.txt +++ b/todo.txt @@ -1,20 +1,22 @@ ******************************************************** NEXT PATCH ******************************************************** -new level labs is done (it's kinda randomized, so expect it to feel different each run) - (I know there are tons of bugs, but I figure we can find them together :) +tech - radioisotope generator - drones have the effect of neutron bomb, but you get less ammo +bug fix - reduced tolerances now properly gives extra ammo +water shielding now protects you from all radioactivity, but only gives 75% protection + (drones, neutron bomb, radioactive explosions, and even slime) -foam gun now gets about 20% less ammo -new junk tech: "emergency broadcasting" - plays some fun sounds and gives you health +final boss has more durability in it's final phase +final boss no longers gets knocked around as much from explosions -updated matter.js to a newer build matter-js 0.17.1 by @liabru - (was matter-js 0.14.2 by @liabru 2018-06-11) - matter.js patch notes suggest a possible 30% physics performance increase - decomp.min.js was removed because I don't think it does anything anymore - I did get one error message about it being missing, but there were other bugs too +level.warehouse predraw lighting for performance +bug fix on sewers where slime was doing too much harm ******************************************************** BUGS ******************************************************** -Final boss acts super weird when hit by explosions +slime does extra damage on flipped levels? + +using time dilatation breaks constraints + on map n-gon, toggles player can become crouched while not touching the ground if they exit the ground while crouched @@ -38,9 +40,18 @@ is there a way to check if the player is stuck inside the map or block ******************************************************** LEVELS ******************************************************** +make a power up you can carry around like the heal power up when at full health + drop it on a map element to turn it on? + use it in combat? + labs - procedural generation bugs mob spawns shouldn't be based on probability? + style + graphics look too bright? + add shadows and lighting and graphic details? + what about performance? + with the mobs staggered spawning it should be fine feel disrupt the flat ground less platforming / easier platforming @@ -49,7 +60,13 @@ labs - procedural generation is it laggy? in loot room, spawn mobs after power up is grabbed more background graphics, better colors - science theme, with a cool technology showcased + loot room: + make it more interesting to get the power up + slow player and reduce damage in region + increase the size of the region + don't have space for much + make graphics more unique + push player away, so that normal pick up methods don't work, but add a button to disable region room ideas - portal room endlessly falling blocks down a slide, that the player has to climb up @@ -57,18 +74,95 @@ labs - procedural generation slime room sound room, with buttons to control sound color room with r,g,b buttons to control color - test fire room, button fires blocks at a wall mob buff zone: Map element: "Orbital Pickup Zone": Mobs that enter a specific area of the map gain +1 orbital per second, or a shield could put in the loot room +buttons can now on/off boosts + +repeat map in vertical when you fall teleport to above the mab, as if the map repeats + camera looks strange when you teleport player with a high velocity + +map element - player rotates a rotor that makes a platform go up or down + +level element: a zone with wind, anti-gravity, extra gravity + control with button + +map: observatory + button controls rotation of telescope + laser beam shoots out of telescope + button opens the dome + +map: prison + doors linked to buttons + mobs inside the doors? + +boss levels - small levels just a boss, and maybe a few mobs + boss level for timeSkipBoss because of game instability for boss on normal levels + this might not fix issues + +******************************************************** MOBS ******************************************************** + +mob mechanics + use the force at a location effect, like the plasma field + +mob - after taking damage + release seekers + teleports + +hop boss: + AoE damage when landing + pull in player? and blocks? + extra gravity on falling? + immune to damage while falling? + +mob: molecule shapes - 2 separate mobs joined by a bond + use constraints: just spawn 2x or 3x groupings + low friction so they can spin around + spin when attacking player? + increase constraint length when attacking + +mob vision: look at player history + build a new type of attraction for mobs + if mobs can't see player, they check to see if they can see where the player was in the history + if mobs can't see player, they could check to see if they can find player in the past + https://abitawake.com/news/articles/enemy-ai-chasing-a-player-without-navigation2d-or-a-star-pathfinding + write find in spawn undo exploder, but commented out + +Mob: "Tentacle": Sits on wall. Is a black blob. When you get near it, reaches out and grabs you, similar to wires. Does not deal damage. + maybe it could be immune to damage? but it is spawned by an actual mob + +level Boss: fractal Sierpiński triangle + https://en.wikipedia.org/wiki/Sierpi%C5%84ski_triangle + spawns a 1/2 size version of the boss, this version can also spawn a smaller version, but it is capped at some size level + they spawn once at the start of the level + if a version dies, one can be replaced every ten seconds by the largest version + +give mobs more animal-like behaviors like rain world + mobs play, look for food, explore + mobs some times aren't aggressive + when low on life or after taking a large hit + mobs can fight each other + this might be hard to code + isolated mobs try to group up + +mob: wall mounted guns / lasers + not part of randomized mob pool, customized to each level + +level boss: fires a line intersection in a random direction every few seconds. + the last two intersections have a destructive laser between them. + ******************************************************** TODO ******************************************************** +tech foam teleports around, like a quantum wave function collapse + +should ammo apply to all guns, or just one of your guns? + if one gun only, it would make multi-gun builds weaker + +nail bots should benefit from nail gun tech + tech: picking up heal power ups at max health does harm, but increases max health scales with heal value -does catabolism give too much ammo? - synergy with shotgun harm immunity - let standing wave harmonics get tech decorrelation tech: cloaking field - decrease/increase cooldown on sneak attack? @@ -88,24 +182,12 @@ tech plasma : plasma length increases then decreases as you hold down the field tech: at the start of a new level remove 5 research and spawn a second boss -what about the single axis graphic? (find the code in standing wave harmonic) - maybe just save it for a mob - maybe use it on lore - -buttons can now on/off boosts - energy conservation 6% damage recovered as energy add a negative effect: junk tech Weak Anthropic Principle: you get a second chance at life, but .... -mob: molecule shapes - 2 separate mobs joined by a bond - use constraints: just spawn 2x or 3x groupings - low friction so they can spin around - spin when attacking player? - increase constraint length when attacking - tech: use the ability for power ups to have custom code (note: this code is half way done, it just needs to be completed) attracted to player @@ -137,31 +219,12 @@ apply the new gun.do functions to other guns crouching missile? works similar to foam performance issues? - -tech plasma field - plasma field becomes an aoe damage field with the same radius - 200% more energy drain, 100% more damage - draw a square (or two) that rapidly spins look into improving mouse lag with pointer lock? https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API https://www.vsynctester.com/game.html https://news.ycombinator.com/item?id=26530272 -mob vision: look at player history - build a new type of attraction for mobs - if mobs can't see player, they check to see if they can see where the player was in the history - if mobs can't see player, they could check to see if they can find player in the past - https://abitawake.com/news/articles/enemy-ai-chasing-a-player-without-navigation2d-or-a-star-pathfinding - write find in spawn undo exploder, but commented out - -Mob: "Tentacle": Sits on wall. Is a black blob. When you get near it, reaches out and grabs you, similar to wires. Does not deal damage. - maybe it could be immune to damage? but it is spawned by an actual mob - -mob - after taking damage - attack outwardly - grows - teleports - mobile requirements: detect mobile, flip to landscape detect no keyboard, no mouse @@ -170,20 +233,12 @@ mobile requirements: tap screen regions to move (WASD) reduce font size -lore: a tutorial / lore intro - needs to be optional so it doesn't slow experienced players - put something on the intro map - maybe a button triggers something - add back in gamepad support but does anyone care? https://github.com/landgreen/landgreen.github.io/search?q=gamepadconnected rename intro level to something lore related -rename ? - health -> integrity, unity - heal -> also integrity, unity RPG default or tech: grenades detonate on your cursor / where your cursor was when they were fired @@ -219,18 +274,6 @@ tech pilot wave: antigravity - blocks have no gravity for a few seconds after ex wormhole - make it clear when the wormhole can and can't teleport to a location before the player clicks -flavor - your bullets destroy blocks - this isn't really a bonus, so maybe just add this as flavor to another tech field/gun - a chance for destroyed blocks to drop stuff - power ups - spores - -using a reroll gives 3 options for tech, and 3 options for guns/fields/tech - or 6 options for tech (rewrite tech selection to work with 1-6 options) - the second stack of 3 tech could have repeats, so you don't have to write new tech code - adjust css to make 2 columns of 3 - can't use with cardinality - new power up - increase damage and fire speed, for 15 seconds named boost? enabled by a tech @@ -242,9 +285,6 @@ tech- do 50% more damage in close, but 50% less at a distance code it like techisFarAwayDmg have these tech disable each other -repeat map in vertical and horizontal space - or at least vertical space - camera looks strange when you teleport player with a high velocity new status effect: weakness, mobs do 75% les damage graphic indication? @@ -267,45 +307,8 @@ look for tech that could update description text with count and tech is informat can only use variables that change in effect() and remove() this.description = `8% chance to duplicate spawned power ups
chance to duplicate = ${techduplicateChance}` -map element - player rotates a rotor that makes a platform go up or down - use mac automator to speed up your n-gon -> git sync -level Boss: fractal Sierpiński triangle - https://en.wikipedia.org/wiki/Sierpi%C5%84ski_triangle - spawns a 1/2 size version of the boss, this version can also spawn a smaller version, but it is capped at some size level - they spawn once at the start of the level - if a version dies, one can be replaced every ten seconds by the largest version - -level element: a zone with wind, anti-gravity, extra gravity - control with button - -give mobs more animal-like behaviors like rain world - mobs play, look for food, explore - mobs some times aren't aggressive - when low on life or after taking a large hit - mobs can fight each other - this might be hard to code - isolated mobs try to group up - -mob: wall mounted guns / lasers - not part of randomized mob pool, customized to each level - -level boss: fires a line intersection in a random direction every few seconds. - the last two intersections have a destructive laser between them. - -map: observatory - button controls rotation of telescope - laser beam shoots out of telescope - button opens the dome - -map: prison - doors linked to buttons - mobs inside the doors? - -graphic idea: bezier curve that moves smoothly from mob to mob - loops around player - movement fluidity let legs jump on mobs, but player will still take damage like: ori and the blind forest, celeste @@ -328,14 +331,6 @@ have a mob apply a positive status effect on other mobs, damage bonus, but how? possible balance issues -boss levels - small levels just a boss, and maybe a few mobs - boss level for timeSkipBoss because of game instability for boss on normal levels - this might not fix issues - -an effect when canceling a power up - ammo? heals? - 50% chance for a tech 25% heal, 25% ammo - css transition for pause menu animate new level spawn by having the map aspects randomly fly into place @@ -344,6 +339,7 @@ n-gon outreach ideas blips - errant signal on youtube reddit - r/IndieGaming hacker news - show hacker news post + twitch - lets play ******************************************************** LORE ******************************************************** @@ -369,6 +365,17 @@ possible names for tech phlogiston theory is a superseded scientific theory that postulated the existence of a fire-like element called phlogiston Laplace's demon was a notable published articulation of causal determinism on a scientific basis by Pierre-Simon Laplace in 1814.[1] According to determinism, if someone (the demon) knows the precise location and momentum of every atom in the universe, their past and future values for any given time are entailed; they can be calculated from the laws of classical mechanics. Degenerate matter is a highly dense state of fermionic matter in which the Pauli exclusion principle exerts significant pressure in addition to, or in lieu of thermal pressure. + evolutionary cosmology + eternal inflation + +a tutorial / lore intro + needs to be optional so it doesn't slow experienced players + put something on the intro map + maybe a button triggers something + +rename ? + health -> integrity, unity + heal -> also integrity, unity plot script: