gravityObservatory

new level: gravityObservatory

level: testChamber2 renamed gravityInterferometer
  it has been added to the levels that are extra hard and only show up late game
  it also got a bit hard with the addition of 1 more laser

deflected mob bullets are converted into small blocks
ablative drones is now a gun tech for drones
  it makes ~33% more drones
1.033->1.05x sneak attack damage per coupling for cloaking field

bug fixes
  extended vertical flip to edge cases:
    trail left by snakeBoss
    laser array from boss and mobs
    springer,spiderBoss,mantisBoss constraints
  subway: dark matter no longer removed if it gets too far from the player
  training: fixed potential lock out from running out of ammo
  training: fixed accidental difficulty increase
This commit is contained in:
landgreen
2024-10-05 17:11:06 -07:00
parent cea1c64c6e
commit a47ef97dbc
9 changed files with 628 additions and 187 deletions

View File

@@ -474,13 +474,14 @@ const build = {
// <strong class='color-g'>${b.activeGun === null || b.activeGun === undefined ? "undefined" : b.guns[b.activeGun].name}</strong> (${b.activeGun === null || b.activeGun === undefined ? "0" : b.guns[b.activeGun].ammo}) // <strong class='color-g'>${b.activeGun === null || b.activeGun === undefined ? "undefined" : b.guns[b.activeGun].name}</strong> (${b.activeGun === null || b.activeGun === undefined ? "0" : b.guns[b.activeGun].ammo})
// <br>
// <input onclick="build.showImages('pause')" type="checkbox" id="hide-images-pause" name="hide-images-pause" ${localSettings.isHideImages ? "checked" : ""}>
// <label for="hide-images-pause" title="hide images for fields, guns, and tech" style="font-size:1.15em;" >hide images</label>
let text = `<div class="pause-grid-module" style="padding: 8px;"> let text = `<div class="pause-grid-module" style="padding: 8px;">
<span style="font-size:1.4em;font-weight: 600; float: left;">PAUSED</span> <span style="font-size:1.4em;font-weight: 600; float: left;">PAUSED</span>
<em style="float: right;color:#ccc;">press ${input.key.pause} to resume</em> <em style="float: right;color:#ccc;">press ${input.key.pause} to resume</em>
<br> <br>
<input onclick="build.showImages('pause')" type="checkbox" id="hide-images-pause" name="hide-images-pause" ${localSettings.isHideImages ? "checked" : ""}>
<label for="hide-images-pause" title="hide images for fields, guns, and tech" style="font-size:1.15em;" >hide images</label>
<br>
<button onclick="build.shareURL(false)" class='sort-button' style="font-size:1em;float: right;">copy build URL</button> <button onclick="build.shareURL(false)" class='sort-button' style="font-size:1em;float: right;">copy build URL</button>
<input onclick="build.hideHUD('settings')" type="checkbox" id="hide-hud" name="hide-hud" ${localSettings.isHideHUD ? "checked" : ""}> <input onclick="build.hideHUD('settings')" type="checkbox" id="hide-hud" name="hide-hud" ${localSettings.isHideHUD ? "checked" : ""}>
<label for="hide-hud" title="hide: tech, damage taken, damage, in game console" style="font-size:1.15em;">minimal HUD</label> <label for="hide-hud" title="hide: tech, damage taken, damage, in game console" style="font-size:1.15em;">minimal HUD</label>

View File

@@ -8,8 +8,8 @@ const level = {
defaultZoom: 1400, defaultZoom: 1400,
onLevel: -1, onLevel: -1,
levelsCleared: 0, levelsCleared: 0,
//see level.populateLevels: (initial, ... , reservoir or factory, reactor, ... , subway, final) added later //see level.populateLevels: (initial, ... , (reservoir, factory, or gravityInterferometer), reactor, ... , subway, final) added later
playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock", "towers", "flocculation", "testChamber2"], playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock", "towers", "flocculation", "gravityObservatory"],
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"], 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"], trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon"],
levels: [], levels: [],
@@ -30,14 +30,14 @@ const level = {
// tech.tech[297].frequency = 100 // tech.tech[297].frequency = 100
// tech.addJunkTechToPool(0.5) // tech.addJunkTechToPool(0.5)
// m.couplingChange(10) // m.couplingChange(10)
// 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.setField("standing wave") //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 // m.energy = 0
// powerUps.research.count = 3 // powerUps.research.count = 3
// tech.isHookWire = true // tech.isHookWire = true
// m.energy = 0 // m.energy = 0
// simulation.molecularMode = 2 // simulation.molecularMode = 2
// m.damage(0.1); // m.damage(0.1);
// b.giveGuns("super balls") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser // b.giveGuns("nail gun") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// b.giveGuns("spores") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser // b.giveGuns("spores") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// b.giveGuns("laser") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser // b.giveGuns("laser") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// tech.laserColor = "#fff" // tech.laserColor = "#fff"
@@ -45,9 +45,9 @@ const level = {
// b.guns[8].ammo = 100000000 // b.guns[8].ammo = 100000000
// requestAnimationFrame(() => { tech.giveTech("stimulated emission") }); // requestAnimationFrame(() => { tech.giveTech("stimulated emission") });
// tech.giveTech("mass-energy equivalence") // tech.giveTech("dark matter")
// tech.addJunkTechToPool(0.5) // tech.addJunkTechToPool(0.5)
// for (let i = 0; i < 1; ++i) tech.giveTech("the upside down") // for (let i = 0; i < 1; ++i) tech.giveTech("entropic gravity")
// for (let i = 0; i < 1; ++i) tech.giveTech("nitinol") // for (let i = 0; i < 1; ++i) tech.giveTech("nitinol")
// m.skin.egg(); // m.skin.egg();
@@ -59,13 +59,13 @@ const level = {
// for (let i = 0; i < 4; i++) powerUps.directSpawn(450, -50, "tech"); // for (let i = 0; i < 4; i++) powerUps.directSpawn(450, -50, "tech");
// for (let i = 0; i < 7; i++) powerUps.directSpawn(m.pos.x + 200, m.pos.y - 250, "research", false); // for (let i = 0; i < 7; i++) powerUps.directSpawn(m.pos.x + 200, m.pos.y - 250, "research", false);
// spawn.bodyRect(575, -700, 150, 150); //block mob line of site on testing // spawn.bodyRect(575, -700, 150, 150); //block mob line of site on testing
// level.testChamber2(); // level.gravityInterferometer();
level[simulation.isTraining ? "walk" : "initial"]() //normal starting level ************************************************** level[simulation.isTraining ? "walk" : "initial"]() //normal starting level **************************************************
// for (let i = 0; i < 1; ++i) spawn.revolutionBoss(1900, -500) // for (let i = 0; i < 1; ++i) spawn.snakeBoss(1900, -500)
// for (let i = 0; i < 1; i++) spawn.starter(1900, -500, 20) // for (let i = 0; i < 1; i++) spawn.mantisBoss(1900, -500)
// 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 < 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"); // for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 450, m.pos.y + 50 * Math.random(), "boost");
@@ -137,7 +137,7 @@ const level = {
} }
level.newLevelOrPhase() level.newLevelOrPhase()
if (simulation.isTraining) { if (simulation.isTraining) {
simulation.difficultyMode = 2 simulation.difficultyMode = 1
} else { } else {
simulation.inGameConsole(`<span class='color-var'>level</span>.onLevel <span class='color-symbol'>=</span> "<span class='color-text'>${level.levels[level.onLevel]}</span>"`); simulation.inGameConsole(`<span class='color-var'>level</span>.onLevel <span class='color-symbol'>=</span> "<span class='color-text'>${level.levels[level.onLevel]}</span>"`);
document.title = "n-gon: " + level.levelAnnounce(); document.title = "n-gon: " + level.levelAnnounce();
@@ -256,7 +256,12 @@ const level = {
customTopLayer() { }, customTopLayer() { },
updateDifficulty() { updateDifficulty() {
simulation.difficulty = level.levelsCleared * simulation.difficultyMode simulation.difficulty = level.levelsCleared * simulation.difficultyMode
if (simulation.isTraining) simulation.difficulty = 1 if (simulation.isTraining) {
simulation.difficulty = 1
simulation.difficultyMode = 1
m.dmgScale = 1
simulation.dmgScale = 1//Math.max(0.1, 0.25 * level.levelsCleared * scale) //damage done by mobs scales with total levels //a bigger number means the player takes more damage
} else {
const scale = simulation.difficultyMode > 3 ? 2 : 1 const scale = simulation.difficultyMode > 3 ? 2 : 1
m.dmgScale = Math.pow(0.85, level.levelsCleared * scale) m.dmgScale = Math.pow(0.85, level.levelsCleared * scale)
simulation.dmgScale = Math.max(0.1, 0.25 * level.levelsCleared * scale) //damage done by mobs scales with total levels //a bigger number means the player takes more damage simulation.dmgScale = Math.max(0.1, 0.25 * level.levelsCleared * scale) //damage done by mobs scales with total levels //a bigger number means the player takes more damage
@@ -264,6 +269,7 @@ const level = {
m.dmgScale *= 0.5 m.dmgScale *= 0.5
simulation.dmgScale *= 2 simulation.dmgScale *= 2
} }
}
simulation.healScale = 1 / (1 + simulation.difficulty * 0.043) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale; simulation.healScale = 1 / (1 + simulation.difficulty * 0.043) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale;
if (simulation.difficultyMode === 1) { if (simulation.difficultyMode === 1) {
simulation.accelScale = 1.1 simulation.accelScale = 1.1
@@ -772,7 +778,8 @@ const level = {
} }
level.levels = shuffle(level.levels); //shuffles order of maps with seeded random level.levels = shuffle(level.levels); //shuffles order of maps with seeded random
level.levels.length = 9 //remove any extra levels past 9 level.levels.length = 9 //remove any extra levels past 9
level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.6, level.levels.length)), 0, Math.random() < 0.5 ? "factory" : "reservoir"); //add level to the back half of the randomized levels list pick = ["gravityInterferometer", "factory", "reservoir"]
level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.6, level.levels.length)), 0, pick[Math.floor(Math.random() * pick.length)]); //add level to the back half of the randomized levels list
level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.6, level.levels.length)), 0, "reactor"); //add level to the back half of the randomized levels list level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.6, level.levels.length)), 0, "reactor"); //add level to the back half of the randomized levels list
if (!build.isExperimentSelection || (build.hasExperimentalMode && !simulation.isCheating)) { //experimental mode is endless, unless you only have an experiment Tech if (!build.isExperimentSelection || (build.hasExperimentalMode && !simulation.isCheating)) { //experimental mode is endless, unless you only have an experiment Tech
level.levels.unshift("initial"); //add level to the start of the randomized levels list level.levels.unshift("initial"); //add level to the start of the randomized levels list
@@ -1548,7 +1555,7 @@ const level = {
}, },
} }
}, },
button(x, y, width = 126, isSpawnBase = true, isInvertedVertical = false) { button(x, y, width = 126, isSpawnBase = true, isInvertedVertical = false, color = "hsl(0, 100%, 70%)") {
if (isSpawnBase) { if (isSpawnBase) {
if (isInvertedVertical) { if (isInvertedVertical) {
spawn.mapVertex(x + 65, y - 3, "100 -10 -100 -10 -70 10 70 10"); spawn.mapVertex(x + 65, y - 3, "100 -10 -100 -10 -70 10 70 10");
@@ -1594,18 +1601,19 @@ const level = {
this.isUp = false; this.isUp = false;
} }
}, },
query() { queryRemove() {
if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) { if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) {
this.isUp = true; this.isUp = true;
} else { } else {
if (this.isUp === true) { if (this.isUp === true) {
const list = Matter.Query.region(body, this) //are any blocks colliding with this const list = Matter.Query.region(body, this) //are any blocks colliding with this
if (list.length > 0) { if (list.length > 0) {
if (list[0].bounds.max.x - list[0].bounds.min.x < 150 && list[0].bounds.max.y - list[0].bounds.min.y < 150) { //not too big of a block Matter.Composite.remove(engine.world, list[0]);
Matter.Body.setPosition(list[0], { //teleport block to the center of the button for (let i = 0; i < body.length; i++) {
x: this.min.x + width / 2, if (body[i] === list[0]) {
y: list[0].position.y body.splice(i, 1);
}) break
}
} }
Matter.Body.setVelocity(list[0], { x: 0, y: 0 }); Matter.Body.setVelocity(list[0], { x: 0, y: 0 });
} }
@@ -1613,8 +1621,15 @@ const level = {
this.isUp = false; this.isUp = false;
} }
}, },
queryPlayer() {
if (Matter.Query.region([player], this).length === 0) {
this.isUp = true;
} else {
this.isUp = false;
}
},
draw() { draw() {
ctx.fillStyle = "hsl(0, 100%, 70%)" ctx.fillStyle = color
if (this.isUp) { if (this.isUp) {
ctx.fillRect(this.min.x, this.min.y, this.width, 20) ctx.fillRect(this.min.x, this.min.y, this.width, 20)
} else { } else {
@@ -1654,18 +1669,20 @@ const level = {
this.isUp = false; this.isUp = false;
} }
}, },
query() { queryRemove() {
if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) { if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) {
this.isUp = true; this.isUp = true;
} else { } else {
if (this.isUp === true) { if (this.isUp === true) {
const list = Matter.Query.region(body, this) //are any blocks colliding with this const list = Matter.Query.region(body, this) //are any blocks colliding with this
if (list.length > 0) { if (list.length > 0) {
if (list[0].bounds.max.x - list[0].bounds.min.x < 150 && list[0].bounds.max.y - list[0].bounds.min.y < 150) { //not too big of a block //delete triggering block
Matter.Body.setPosition(list[0], { //teleport block to the center of the button Matter.Composite.remove(engine.world, list[0]);
x: this.min.x + width / 2, for (let i = 0; i < body.length; i++) {
y: list[0].position.y if (body[i] === list[0]) {
}) body.splice(i, 1);
break
}
} }
Matter.Body.setVelocity(list[0], { x: 0, y: 0 }); Matter.Body.setVelocity(list[0], { x: 0, y: 0 });
} }
@@ -1673,8 +1690,15 @@ const level = {
this.isUp = false; this.isUp = false;
} }
}, },
queryPlayer() {
if (Matter.Query.region([player], this).length === 0) {
this.isUp = true;
} else {
this.isUp = false;
}
},
draw() { draw() {
ctx.fillStyle = "hsl(0, 100%, 70%)" ctx.fillStyle = color
if (this.isUp) { if (this.isUp) {
ctx.fillRect(this.min.x, this.min.y - 10, this.width, 20) ctx.fillRect(this.min.x, this.min.y - 10, this.width, 20)
} else { } else {
@@ -3376,7 +3400,7 @@ const level = {
} }
//remove any mob that is too far from player //remove any mob that is too far from player
for (let i = 0; i < mob.length; ++i) { for (let i = 0; i < mob.length; ++i) {
if (Vector.magnitudeSquared(Vector.sub(player.position, mob[i].position)) > 4000000) { //remove any mob farther then 2000 pixels from player if (Vector.magnitudeSquared(Vector.sub(player.position, mob[i].position)) > 4000000 && !mob[i].isDarkMatter) { //remove any mob farther then 2000 pixels from player
mob[i].removeConsBB() mob[i].removeConsBB()
mob[i].removeCons() mob[i].removeCons()
mob[i].leaveBody = false mob[i].leaveBody = false
@@ -7025,7 +7049,8 @@ const level = {
} }
}, },
testChamber2() { gravityInterferometer() {
level.isVerticalFLipLevel = true
mobs.maxMobBody = 20 //normally 40, but set to 10 to avoid too much clutter mobs.maxMobBody = 20 //normally 40, but set to 10 to avoid too much clutter
simulation.fallHeight = 4000 simulation.fallHeight = 4000
level.announceMobTypes() level.announceMobTypes()
@@ -7133,13 +7158,13 @@ const level = {
spawn.mapRect(-4000, 2000, 8000, 3000); //floor spawn.mapRect(-4000, 2000, 8000, 3000); //floor
} }
let buildNormalMap = function () { let buildNormalMap = function () {
buttons.push(level.button(-1895, -1600)) buttons.push(level.button(-1895, -1600, 126, true, false, "hsl(330, 100%, 50%)"))
buttons[buttons.length - 1].isUp = false buttons[buttons.length - 1].isUp = false
spawn.mapRect(-1675, -2025, 50, 250); spawn.mapRect(-1675, -2025, 50, 250);
simulation.ephemera.push({ simulation.ephemera.push({
name: "buttons up", name: "buttons up",
count: flipAnimationCycles, count: flipAnimationCycles + 30,
do() { do() {
this.count-- this.count--
if (this.count < 0) { if (this.count < 0) {
@@ -7161,6 +7186,10 @@ const level = {
balance.push(level.rotor(-750, 1700, 400, 25, 0.01, Math.PI / 2, 0.5)) balance.push(level.rotor(-750, 1700, 400, 25, 0.01, Math.PI / 2, 0.5))
balance.push(level.rotor(-275, 1650, 550, 32, 0.01, 0, 0.5)) balance.push(level.rotor(-275, 1650, 550, 32, 0.01, 0, 0.5))
lasers.push(level.laser({ x: -1625, y: -850 }, { x: 1980, y: -850 })) ////x, y, width, height, damage = 0.002)
spawn.mapRect(1980, -862, 25, 25); //laser entrance
balance.push(level.rotor(1000, -910, 550, 32, 0.01, 0, 0.5))
//left side //left side
//level entrance //level entrance
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
@@ -7214,13 +7243,13 @@ const level = {
} }
let buildVerticalFLippedMap = function () { // flip Y with this -> spawn.mapRect(x, -y - h, w, h); let buildVerticalFLippedMap = function () { // flip Y with this -> spawn.mapRect(x, -y - h, w, h);
buttons.push(level.button(-1895, 1600, 126, true, true)) buttons.push(level.button(-1895, 1600, 126, true, true, "hsl(330, 100%, 50%)"))
buttons[buttons.length - 1].isUp = false buttons[buttons.length - 1].isUp = false
spawn.mapRect(-1675, 2025 - 250, 50, 250); spawn.mapRect(-1675, 2025 - 250, 50, 250);
simulation.ephemera.push({ simulation.ephemera.push({
name: "buttons up", name: "buttons up",
count: flipAnimationCycles, count: flipAnimationCycles + 30,
do() { do() {
this.count-- this.count--
if (this.count < 0) { if (this.count < 0) {
@@ -7242,6 +7271,11 @@ const level = {
balance.push(level.rotor(-750, -1700 - 25, 400, 25, 0.01, Math.PI / 2, 0.5)) balance.push(level.rotor(-750, -1700 - 25, 400, 25, 0.01, Math.PI / 2, 0.5))
balance.push(level.rotor(-250, -1650 - 32, 500, 32, 0.01, 0, 0.5)) balance.push(level.rotor(-250, -1650 - 32, 500, 32, 0.01, 0, 0.5))
lasers.push(level.laser({ x: -1625, y: 850 }, { x: 1980, y: 850 })) ////x, y, width, height, damage = 0.002)
spawn.mapRect(1980, 862 - 25, 25, 25); //laser entrance
balance.push(level.rotor(1000, 910 - 32, 550, 32, 0.01, 0, 0.5))
//left side //left side
//level entrance //level entrance
spawn.mapRect(level.enter.x, level.enter.y - 20 - 20, 100, 20); spawn.mapRect(level.enter.x, level.enter.y - 20 - 20, 100, 20);
@@ -7329,8 +7363,24 @@ const level = {
m.history[i].angle *= -1 m.history[i].angle *= -1
m.history[i].velocity.y *= -1 m.history[i].velocity.y *= -1
} }
for (let i = 0; i < mob.length; i++) {
//stun to wipe history of all mobs, so they don't get confused about player position vertical swap //stun to wipe history of all mobs, so they don't get confused about player position vertical swap
for (let i = 0; i < mob.length; i++) mobs.statusStun(mob[i], 1) mobs.statusStun(mob[i], 1)
//edge cases
if (mob[i].history) {
for (let j = 0; j < mob[i].history.length; j++) mob[i].history[j].y *= -1
}
if (mob[i].laserArray) {
for (let j = 0; j < mob[i].laserArray.length; j++) {
mob[i].laserArray[j].a.y *= -1
mob[i].laserArray[j].b.y *= -1
}
}
if (mob[i].springTarget2) {
mob[i].springTarget.y *= -1
mob[i].springTarget2.y *= -1
}
}
} }
buildMapOutline() buildMapOutline()
buildNormalMap() buildNormalMap()
@@ -7339,7 +7389,8 @@ const level = {
for (let i = 0; i < buttons.length; i++) { for (let i = 0; i < buttons.length; i++) {
buttons[i].draw() buttons[i].draw()
if (buttons[i].isUp && !isFlipping) { if (buttons[i].isUp && !isFlipping) {
buttons[i].query(); // buttons[i].query();
buttons[i].queryPlayer();
if (!buttons[i].isUp) { if (!buttons[i].isUp) {
isFlipping = true isFlipping = true
if (isFlipped) { if (isFlipped) {
@@ -7444,7 +7495,7 @@ const level = {
} }
} }
ctx.fill(); ctx.fill();
ctx.fillStyle = `rgba(255,255,255,${0 + 0.3 * Math.random()})` //balances center dot ctx.fillStyle = `rgba(255,255,255,${0 + 0.3 * Math.random()})`
if (isFlipped) { if (isFlipped) {
ctx.fillRect(-2025, 2025 - 450, 400, 450); ctx.fillRect(-2025, 2025 - 450, 400, 450);
//shadows //shadows
@@ -7518,6 +7569,351 @@ const level = {
spawn.randomLevelBoss(-875, -200); spawn.randomLevelBoss(-875, -200);
powerUps.addResearchToLevel() //needs to run after mobs are spawned powerUps.addResearchToLevel() //needs to run after mobs are spawned
}, },
gravityObservatory() {
mobs.maxMobBody = 25 //normally 40, but set lower to avoid too much clutter
level.isVerticalFLipLevel = true
simulation.fallHeight = 4000
level.announceMobTypes()
level.setPosToSpawn(-2375, 950);
level.exit.x = 3750
level.exit.y = 165
level.defaultZoom = 2600
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#c3d6e1";
color.map = "#444"
let buttons = []
let isFlipped = false;
let isFlipping = false;
const flipAnimationCycles = 60
let buildMapOutline = function () {
//boxes center on zero,zero with deep walls to hide background
spawn.mapRect(4000, -2000, 2000, 4000); //right map wall
spawn.mapRect(-6000, -2000, 2000, 4000); //left map wall
spawn.mapRect(-6000, -4000, 12000, 3000); //map ceiling
spawn.mapRect(-6000, 1000, 12000, 3000); //floor
}
let buildNormalMap = function () {
buttons.push(level.button(-3350, 985, 126, true, false, "hsl(330, 100%, 50%)"))
buttons.push(level.button(-3350, -985, 126, true, true, "hsl(330, 100%, 50%)"))
buttons.push(level.button(150, 985, 126, true, false, "hsl(330, 100%, 50%)"))
buttons.push(level.button(150, -985, 126, true, true, "hsl(330, 100%, 50%)"))
buttons.push(level.button(3725, 985, 126, true, false, "hsl(330, 100%, 50%)"))
buttons.push(level.button(3725, -985, 126, true, true, "hsl(330, 100%, 50%)"))
for (let i = 0; i < buttons.length; i++) buttons[i].isUp = false
simulation.ephemera.push({
name: "buttons up",
count: flipAnimationCycles,
do() {
this.count--
if (this.count < 0) {
for (let i = 0; i < buttons.length; i++) buttons[i].isUp = true
simulation.removeEphemera(this.name);
isFlipping = false
}
},
})
//far left zone
spawn.mapRect(-2575, 987, 375, 100);
spawn.mapRect(-2575, -600, 600, 1325);
spawn.mapRect(-2200, 650, 225, 475);
spawn.mapRect(-2575, 700, 35, 125);
spawn.mapRect(-3500, -1050, 425, 63);
spawn.mapRect(-3500, 987, 425, 50);
spawn.mapVertex(-2275, -1000, "-400 0 -300 150 300 150 400 0");
spawn.mapVertex(-3287, 0, "-213 -500 0 -550 213 -500 213 500 0 550 -213 500");
spawn.mapVertex(-3750, -100, "-100 -200 -50 -250 50 -250 100 -200 100 200 50 250 -50 250 -100 200");
spawn.mapVertex(-2825, 0, "-100 -400 -50 -450 50 -450 100 -400 100 400 50 450 -50 450 -100 400");
//dense center left zone
spawn.mapVertex(-1150, -750, "400 -75 425 0 400 75 -400 75 -425 0 -400 -75");
spawn.mapVertex(-550, -450, "400 -75 425 0 400 75 -400 75 -425 0 -400 -75");
spawn.mapVertex(-1685, 153, "-150 -500 0 -550 150 -500 150 750 -150 450");
spawn.mapVertex(-1106, 707, "500 -150 550 0 500 150 -500 150 -800 -150");
spawn.mapRect(-1645, 470, 200, 200);
Matter.Body.setAngle(map[map.length - 1], Math.PI / 4)
spawn.mapRect(-2085, 910, 200, 200);
Matter.Body.setAngle(map[map.length - 1], Math.PI / 4)
//open center right area with both bosses
// spawn.mapRect(0, -450, 425, 1100);
spawn.mapVertex(213, 0, "-213 -700 0 -650 213 -600 213 700 0 650 -213 600");
spawn.mapRect(0, -1050, 425, 63);
spawn.mapRect(0, 987, 425, 50);
spawn.mapVertex(1700, -1000, "-600 0 -400 400 400 400 600 0");
spawn.mapVertex(2800, -1000, "-500 0 -400 150 400 150 500 0");
spawn.mapVertex(1700, 700, "-400 -100 -450 0 -400 100 400 100 450 0 400 -100");
spawn.mapVertex(2800, 375, "-400 -100 -450 0 -400 100 400 100 450 0 400 -100");
//far right exit structure
spawn.mapRect(3575, -1050, 425, 63);
spawn.mapRect(3575, 987, 425, 50);
spawn.mapVertex(3840, 450, "-250 -300 250 -300 250 300 -250 100");
spawn.mapVertex(3840, -450, "-250 300 250 300 250 -300 -250 -100");
spawn.mapRect(3750, 185, 100, 25);
}
let buildVerticalFLippedMap = function () { // flip Y with this -> spawn.mapRect(x, -y - h, w, h);
buttons.push(level.button(-3350, 985, 126, true, false, "hsl(330, 100%, 50%)"))
buttons.push(level.button(-3350, -985, 126, true, true, "hsl(330, 100%, 50%)"))
buttons.push(level.button(150, 985, 126, true, false, "hsl(330, 100%, 50%)"))
buttons.push(level.button(150, -985, 126, true, true, "hsl(330, 100%, 50%)"))
buttons.push(level.button(3725, 985, 126, true, false, "hsl(330, 100%, 50%)"))
buttons.push(level.button(3725, -985, 126, true, true, "hsl(330, 100%, 50%)"))
for (let i = 0; i < buttons.length; i++) buttons[i].isUp = false
simulation.ephemera.push({
name: "buttons up",
count: flipAnimationCycles,
do() {
this.count--
if (this.count < 0) {
for (let i = 0; i < buttons.length; i++) buttons[i].isUp = true
simulation.removeEphemera(this.name);
isFlipping = false
}
},
})
//far left zone
spawn.mapRect(-2575, -1087, 375, 100);
spawn.mapRect(-2575, 600 - 1325, 600, 1325);
spawn.mapRect(-2200, -650 - 475, 225, 475);
spawn.mapRect(-2575, -700 - 125, 35, 125);
spawn.mapRect(-3500, 1050 - 63, 425, 63);
spawn.mapRect(-3500, -987 - 50, 425, 50);
spawn.mapVertex(-2275, 1000, "-300 0 -400 150 400 150 300 0");
spawn.mapVertex(-3287, 0, "-213 -500 0 -550 213 -500 213 500 0 550 -213 500");
spawn.mapVertex(-3750, 100, "-100 -200 -50 -250 50 -250 100 -200 100 200 50 250 -50 250 -100 200");
spawn.mapVertex(-2825, 0, "-100 -400 -50 -450 50 -450 100 -400 100 400 50 450 -50 450 -100 400");
//dense center left zone
spawn.mapVertex(-1150, 750, "400 -75 425 0 400 75 -400 75 -425 0 -400 -75");
spawn.mapVertex(-550, 450, "400 -75 425 0 400 75 -400 75 -425 0 -400 -75");
spawn.mapVertex(-1685, -153, "-150 500 0 550 150 500 150 -750 -150 -450");
spawn.mapVertex(-1106, -707, "500 150 550 0 500 -150 -500 -150 -800 150");
spawn.mapRect(-1645, -470 - 200, 200, 200);
Matter.Body.setAngle(map[map.length - 1], Math.PI / 4)
spawn.mapRect(-2085, -910 - 200, 200, 200);
Matter.Body.setAngle(map[map.length - 1], Math.PI / 4)
//open center right area with both bosses
spawn.mapVertex(213, 0, "-213 -600 0 -650 213 -700 213 600 0 650 -213 700");
spawn.mapRect(0, 1050 - 63, 425, 63);
spawn.mapRect(0, -987 - 50, 425, 50);
spawn.mapVertex(1700, 1000, "-400 0 -600 400 600 400 400 0");
spawn.mapVertex(2800, 1000, "-400 0 -500 150 500 150 400 0");
spawn.mapVertex(1700, -700, "-400 -100 -450 0 -400 100 400 100 450 0 400 -100");
spawn.mapVertex(2800, -375, "-400 -100 -450 0 -400 100 400 100 450 0 400 -100");
//far right building like exit structure
spawn.mapRect(3575, 1050 - 63, 425, 63);
spawn.mapRect(3575, -987 - 50, 425, 50);
spawn.mapVertex(3840, 450, "-250 -300 250 -300 250 300 -250 100");
spawn.mapVertex(3840, -450, "-250 300 250 300 250 -300 -250 -100");
spawn.mapRect(3750, -210, 100, 25);
}
let flipAndRemove = function () {
simulation.translatePlayerAndCamera({ x: player.position.x, y: -player.position.y })
level.enter.y = -level.enter.y
level.exit.y = -level.exit.y
for (let i = body.length - 1; i > -1; i--) {
if (body[i].isRotor) body.splice(i, 1);
}
function removeAll(array) {
for (let i = 0; i < array.length; ++i) Matter.Composite.remove(engine.world, array[i]);
}
removeAll(map);
map = [];
removeAll(buttons);
buttons = []
function invertVertical(array) {
for (let i = 0; i < array.length; ++i) {
Matter.Body.setPosition(array[i], { x: array[i].position.x, y: -array[i].position.y })
}
}
invertVertical(body);
invertVertical(powerUp);
invertVertical(bullet);
invertVertical(mob);
//fields
if (m.fieldMode === 9 && m.hole.isOn) {
m.hole.pos1.y *= -1
m.hole.pos2.y *= -1
} else if (m.fieldMode === 2) {
m.fieldPosition.y *= -1
m.fieldAngle *= -1
}
//history
for (let i = 0; i < m.history.length; i++) {
m.history[i].position.y *= -1
m.history[i].angle *= -1
m.history[i].velocity.y *= -1
}
for (let i = 0; i < mob.length; i++) {
//stun to wipe history of all mobs, so they don't get confused about player position vertical swap
mobs.statusStun(mob[i], 1)
//edge cases
if (mob[i].history) {
for (let j = 0; j < mob[i].history.length; j++) mob[i].history[j].y *= -1
}
if (mob[i].laserArray) {
for (let j = 0; j < mob[i].laserArray.length; j++) {
mob[i].laserArray[j].a.y *= -1
mob[i].laserArray[j].b.y *= -1
}
}
if (mob[i].springTarget2) {
mob[i].springTarget.y *= -1
mob[i].springTarget2.y *= -1
}
}
}
buildMapOutline()
buildNormalMap()
level.custom = () => {
//stuff floats near buttons
if ((player.position.x > -3505 && player.position.x < -3075) ||
(player.position.x > 0 && player.position.x < 425) ||
(player.position.x > 3575)) {
if (player.position.y > 0) {
player.force.y -= 0.8 * simulation.g * player.mass
}
}
for (let i = 0; i < body.length; i++) {
if ((body[i].position.x > -3505 && body[i].position.x < -3075) ||
(body[i].position.x > 0 && body[i].position.x < 425) ||
(body[i].position.x > 3575)
) {
if (body[i].position.y > 0) {
body[i].force.y -= 1.04 * simulation.g * body[i].mass
} else {
body[i].force.y += 1.04 * simulation.g * body[i].mass
}
}
}
for (let i = 0; i < powerUp.length; i++) {
if ((powerUp[i].position.x > -3505 && powerUp[i].position.x < -3075) ||
(powerUp[i].position.x > 0 && powerUp[i].position.x < 425) ||
(powerUp[i].position.x > 3575)
) {
if (powerUp[i].position.y > 0) {
powerUp[i].force.y -= 1.04 * simulation.g * powerUp[i].mass
} else {
powerUp[i].force.y += 1.04 * simulation.g * powerUp[i].mass
}
}
}
for (let i = 0; i < buttons.length; i++) {
buttons[i].draw()
if (buttons[i].isUp && !isFlipping) {
// buttons[i].query();
buttons[i].queryPlayer();
if (!buttons[i].isUp) {
isFlipping = true
if (isFlipped) {
const normalMap = function () {
isFlipped = false
flipAndRemove()
buildMapOutline()
buildNormalMap(); //rewrite flipped version of map
simulation.draw.setPaths() //update map graphics
level.addToWorld()
}
simulation.unFlipCameraVertical(flipAnimationCycles, normalMap)
} else {
const flipMap = function () {
isFlipped = true
flipAndRemove()
buildMapOutline()
buildVerticalFLippedMap(); //rewrite flipped version of map
simulation.draw.setPaths() //update map graphics
level.addToWorld()
}
simulation.flipCameraVertical(flipAnimationCycles, flipMap)
}
break
}
}
}
ctx.fillStyle = "#d4f4f4"
ctx.fillRect(3575, -300, 475, 575);
if (isFlipped) {
//draw flipped entrance
ctx.beginPath();
ctx.moveTo(level.enter.x, level.enter.y - 30);
ctx.lineTo(level.enter.x, level.enter.y + 80);
ctx.bezierCurveTo(level.enter.x, level.enter.y + 170, level.enter.x + 100, level.enter.y + 170, level.enter.x + 100, level.enter.y + 80);
ctx.lineTo(level.enter.x + 100, level.enter.y - 30);
ctx.lineTo(level.enter.x, level.enter.y - 30);
ctx.fillStyle = "#ccc";
ctx.fill();
//draw flipped exit
ctx.fillStyle = "#d4f4f4"
// ctx.fillRect(-2000, 1325, 375, 350)
ctx.beginPath();
ctx.moveTo(level.exit.x, level.exit.y - 30);
ctx.lineTo(level.exit.x, level.exit.y + 80);
ctx.bezierCurveTo(level.exit.x, level.exit.y + 170, level.exit.x + 100, level.exit.y + 170, level.exit.x + 100, level.exit.y + 80);
ctx.lineTo(level.exit.x + 100, level.exit.y - 30);
ctx.lineTo(level.exit.x, level.exit.y - 30);
ctx.fillStyle = "#0ff";
ctx.fill();
} else {
level.exit.drawAndCheck();
level.enter.draw();
}
};
level.customTopLayer = () => {
ctx.fillStyle = `rgba(255,255,255,${0 + 0.3 * Math.random()})`
ctx.fillRect(-3500, -1075, 425, 2100);
ctx.fillRect(0, -1075, 425, 2100);
ctx.fillRect(3575, -1075, 425, 2100);
ctx.fillStyle = "rgba(0,0,0,0.08)"
ctx.fillRect(-2575, -1025, 600, 2050);
ctx.fillRect(1300, -1050, 800, 2100);
ctx.fillRect(2400, -1050, 800, 2100);
};
// spawn.bodyRect(1900, 1875, 100, 125, 0.5);
spawn.bodyRect(-2569, 825, 25, 165);
spawn.randomMob(-3750, -400, 0.2);
spawn.randomMob(-3425, -600, 0.2);
spawn.randomMob(-3225, -625, 0.3);
spawn.randomMob(-2850, -500, 0.3);
spawn.randomMob(-2450, -675, 0.3);
spawn.randomMob(-2150, -650, 0.4);
spawn.randomMob(-1650, -500, 0.4);
spawn.randomMob(-1325, 275, 0.4);
spawn.randomMob(-825, 425, 0.5);
spawn.randomMob(-400, -575, 0.5);
spawn.randomMob(-1275, -900, 0.5);
spawn.randomMob(-675, -775, 0.6);
spawn.randomMob(150, -725, 0.6);
spawn.randomMob(475, 925, 0.7);
spawn.randomMob(1500, 550, 0.7);
spawn.randomMob(1850, 500, 0.7);
spawn.randomMob(2025, 925, 0.8);
spawn.randomMob(1575, 875, 0.8);
spawn.randomMob(2650, 650, 0.9);
spawn.randomMob(3100, 700, 0.9);
spawn.randomMob(3050, 100, 1);
spawn.randomMob(2350, 100, 1);
spawn.randomMob(3400, 875, 1);
spawn.randomMob(3375, -725, 1);
spawn.randomMob(3925, 100, 1);
powerUps.spawnStartingPowerUps(-825, -600);
spawn.randomLevelBoss(1550, 200);
spawn.secondaryBossChance(2675, -125)
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
lock() { lock() {
level.announceMobTypes() level.announceMobTypes()
level.setPosToSpawn(0, -65); //lower start level.setPosToSpawn(0, -65); //lower start
@@ -18624,7 +19020,7 @@ const level = {
const door3 = level.door(20238, -781.4, 88, 452, 412) const door3 = level.door(20238, -781.4, 88, 452, 412)
const hazard2 = level.hazard(2550, -150, 10, 0.4) //y=-1485 const hazard2 = level.hazard(2550, -150, 10, 0.4) //y=-1485
simulation.enableConstructMode() // simulation.enableConstructMode()
level.setPosToSpawn(0, -50); //normal spawn level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 15316; level.exit.x = 15316;
level.exit.y = -30; level.exit.y = -30;
@@ -21089,7 +21485,7 @@ const level = {
ctx.fillRect(1675, -2325, 250, 75); ctx.fillRect(1675, -2325, 250, 75);
ctx.fillRect(2700, -2525, 25, 150); ctx.fillRect(2700, -2525, 25, 150);
}; };
simulation.enableConstructMode() // simulation.enableConstructMode()
level.setPosToSpawn(0, -50); //normal spawn level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 23885; level.exit.x = 23885;
level.exit.y = 800; level.exit.y = 800;
@@ -21221,7 +21617,7 @@ const level = {
const slime2 = level.hazard(2400, -2100, 200, 2100); const slime2 = level.hazard(2400, -2100, 200, 2100);
const slime3 = level.hazard(2600, -2100, 3600, 200); const slime3 = level.hazard(2600, -2100, 3600, 200);
const slime4 = level.hazard(6400, -2100, 3600, 200); const slime4 = level.hazard(6400, -2100, 3600, 200);
simulation.enableConstructMode() // simulation.enableConstructMode()
level.setPosToSpawn(0, -50); //normal spawn level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 13130.3; level.exit.x = 13130.3;
let rainCount = 1 let rainCount = 1
@@ -24060,7 +24456,7 @@ const level = {
} }
} }
}; };
simulation.enableConstructMode() //landgreen if you see this can you remove im probably gonna forget // simulation.enableConstructMode() //landgreen if you see this can you remove im probably gonna forget
for (let i = 0; i < spawn.bossTypeSpawnOrder.length * Math.random(); i++) { for (let i = 0; i < spawn.bossTypeSpawnOrder.length * Math.random(); i++) {
spawn.bossTypeSpawnOrder.splice(i * Math.floor(Math.random() * spawn.bossTypeSpawnOrder.length), 1, "restoreBoss") //meh good enough spawn.bossTypeSpawnOrder.splice(i * Math.floor(Math.random() * spawn.bossTypeSpawnOrder.length), 1, "restoreBoss") //meh good enough
} }
@@ -24212,7 +24608,7 @@ const level = {
} }
Composite.add(engine.world, me.constraint); Composite.add(engine.world, me.constraint);
} }
simulation.enableConstructMode() // simulation.enableConstructMode()
let firstMobsSpawned = 1 let firstMobsSpawned = 1
let secondMobsSpawned = 0 let secondMobsSpawned = 0
let thirdMobsSpawned = 0 let thirdMobsSpawned = 0
@@ -27153,7 +27549,7 @@ const level = {
const door3 = level.door(20238, -781.4, 88, 452, 412) const door3 = level.door(20238, -781.4, 88, 452, 412)
//y=-1485 //y=-1485
simulation.enableConstructMode() // simulation.enableConstructMode()
level.setPosToSpawn(0, -50); //normal spawn level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 15316; level.exit.x = 15316;
level.exit.y = -84; level.exit.y = -84;
@@ -36285,10 +36681,9 @@ const level = {
} }
for (let i = 0; i < 2; i++) { for (let i = 0; i < 2; i++) {
spawn.spinner(1300 + i, -3000 - 200 * i, 25 + 5 * i) spawn.spinner(1300 + i, -3000 - 200 * i, 25 + 5 * i)
Matter.Body.setVelocity(mob[mob.length - 1], { const who = mob[mob.length - 1]
x: 0, Matter.Body.setVelocity(who, { x: 0, y: 62 });
y: 62 who.isDropPowerUp = false
});
} }
spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
@@ -36373,6 +36768,7 @@ const level = {
} }
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
spawn.hopper(1300 + i, -3000 - 2000 * i, 25 + 5 * i) spawn.hopper(1300 + i, -3000 - 2000 * i, 25 + 5 * i)
mob[mob.length - 1].isDropPowerUp = false
// Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 }); // Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 });
} }
spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
@@ -36455,6 +36851,7 @@ const level = {
} }
for (let i = 0; i < 6; i++) { for (let i = 0; i < 6; i++) {
spawn.spawner(i * 230, -800) spawn.spawner(i * 230, -800)
mob[mob.length - 1].isDropPowerUp = false
// Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 }); // Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 });
} }
spawn.mapVertex(510, -430, "725 0 725 80 -650 80 -650 -80 650 -80"); //upper room with mobs spawn.mapVertex(510, -430, "725 0 725 80 -650 80 -650 -80 650 -80"); //upper room with mobs
@@ -36538,6 +36935,7 @@ const level = {
} }
for (let i = 0; i < 6; i++) { for (let i = 0; i < 6; i++) {
spawn.springer(i * 200, -800) spawn.springer(i * 200, -800)
mob[mob.length - 1].isDropPowerUp = false
// Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 }); // Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 });
} }
spawn.springer(1825, -330, 20); spawn.springer(1825, -330, 20);
@@ -36627,6 +37025,7 @@ const level = {
} }
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
spawn.springer(2100 + i * 100, -250) spawn.springer(2100 + i * 100, -250)
mob[mob.length - 1].isDropPowerUp = false
// Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 }); // Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 });
} }

View File

@@ -658,29 +658,6 @@ const mobs = {
this.cons.length = 100 + 1.5 * this.radius; this.cons.length = 100 + 1.5 * this.radius;
this.cons2.length = 100 + 1.5 * this.radius; this.cons2.length = 100 + 1.5 * this.radius;
} }
// if (!(simulation.cycle % (this.seePlayerFreq * 2))) {
// const unit = Vector.normalise(Vector.sub(this.seePlayer.position, this.position))
// const goal = Vector.add(this.position, Vector.mult(unit, stepRange))
// this.springTarget.x = goal.x;
// this.springTarget.y = goal.y;
// // this.springTarget.x = this.seePlayer.position.x;
// // this.springTarget.y = this.seePlayer.position.y;
// this.cons.length = -200;
// this.cons2.length = 100 + 1.5 * this.radius;
// } else if (!(simulation.cycle % this.seePlayerFreq)) {
// const unit = Vector.normalise(Vector.sub(this.seePlayer.position, this.position))
// const goal = Vector.add(this.position, Vector.mult(unit, stepRange))
// this.springTarget2.x = goal.x;
// this.springTarget2.y = goal.y;
// // this.springTarget2.x = this.seePlayer.position.x;
// // this.springTarget2.y = this.seePlayer.position.y;
// this.cons.length = 100 + 1.5 * this.radius;
// this.cons2.length = -200;
// }
} }
} }
} }

View File

@@ -691,8 +691,8 @@ const m = {
return return
} }
m.lastHarmCycle = m.cycle m.lastHarmCycle = m.cycle
if (tech.isDroneOnDamage && bullet.length < 150) { //chance to build a drone on damage from tech if (tech.isDroneOnDamage && bullet.length < 180) { //chance to build a drone on damage from tech
const len = Math.min((dmg - 0.06 * Math.random()) * 80, 60) / tech.droneEnergyReduction * (tech.isEnergyHealth ? 0.5 : 1) const len = Math.min((dmg - 0.045 * Math.random()) * 95, 65) / tech.droneEnergyReduction * (tech.isEnergyHealth ? 0.5 : 1)
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
if (Math.random() < 0.5) b.drone({ if (Math.random() < 0.5) b.drone({
x: m.pos.x + 30 * Math.cos(m.angle) + 100 * (Math.random() - 0.5), x: m.pos.x + 30 * Math.cos(m.angle) + 100 * (Math.random() - 0.5),
@@ -3074,6 +3074,51 @@ const m = {
} }
}, },
minEnergyToDeflect: 0.05, minEnergyToDeflect: 0.05,
bulletsToBlocks(who) {
if (who.isMobBullet && !who.isInvulnerable && who.mass < 10 && body.length < mobs.maxMobBody) {
// spawn block
body[body.length] = Matter.Bodies.polygon(who.position.x, who.position.y, who.vertices.length, who.radius, {
friction: 0.05,
frictionAir: 0.001,
collisionFilter: {
category: cat.bullet,
mask: cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield
},
classType: "body",
isPrinted: true,
radius: 10, //used to grow and warp the shape of the block
density: 0.002, //double density for 2x damage
});
const block = body[body.length - 1]
Composite.add(engine.world, block); //add to world
//reverse velocity and make sure it's above 40
const unit = Vector.mult(Vector.normalise(who.velocity), -Math.max(40, who.speed))
Matter.Body.setVelocity(block, unit);
simulation.ephemera.push({
name: "remove block",
count: 120, //cycles before it self removes
do() {
this.count--
if (this.count < 0) {
simulation.removeEphemera(this.name)
Matter.Composite.remove(engine.world, block);
//find block
for (let i = 0; i < body.length; i++) {
if (body[i] === block) {
body.splice(i, 1);
break
}
}
}
},
})
//remove mob bullet
Matter.Composite.remove(engine.world, who); //remove from physics early to avoid collisions with block
who.alive = false
}
},
pushMass(who, fieldBlockCost = (0.025 + Math.sqrt(who.mass) * Vector.magnitude(Vector.sub(who.velocity, player.velocity)) * 0.002) * m.fieldShieldingScale) { pushMass(who, fieldBlockCost = (0.025 + Math.sqrt(who.mass) * Vector.magnitude(Vector.sub(who.velocity, player.velocity)) * 0.002) * m.fieldShieldingScale) {
if (m.energy > m.minEnergyToDeflect) { //shield needs at least some of the cost to block if (m.energy > m.minEnergyToDeflect) { //shield needs at least some of the cost to block
if (who.isShielded) fieldBlockCost *= 2; //shielded mobs take more energy to block if (who.isShielded) fieldBlockCost *= 2; //shielded mobs take more energy to block
@@ -3108,6 +3153,7 @@ const m = {
} }
} }
} }
m.bulletsToBlocks(who)
const unit = Vector.normalise(Vector.sub(player.position, who.position)) const unit = Vector.normalise(Vector.sub(player.position, who.position))
if (tech.blockDmg) { if (tech.blockDmg) {
Matter.Body.setVelocity(who, { x: 0.5 * who.velocity.x, y: 0.5 * who.velocity.y }); Matter.Body.setVelocity(who, { x: 0.5 * who.velocity.x, y: 0.5 * who.velocity.y });
@@ -3282,7 +3328,8 @@ const m = {
case 6: //time dilation case 6: //time dilation
return `<strong>+${(1 + 0.05 * couple).toFixed(2)}x</strong> longer <strong style='letter-spacing: 2px;'>stopped time</strong>` //<strong>movement</strong>, <strong>jumping</strong>, and return `<strong>+${(1 + 0.05 * couple).toFixed(2)}x</strong> longer <strong style='letter-spacing: 2px;'>stopped time</strong>` //<strong>movement</strong>, <strong>jumping</strong>, and
case 7: //cloaking case 7: //cloaking
return `<strong>${(1 + 3.3 * couple).toFixed(3)}x</strong> ambush <strong class='color-d'>damage</strong>` // return `<strong>${(1 + 3.3 * couple).toFixed(3)}x</strong> ambush <strong class='color-d'>damage</strong>`
return `<strong>${(1 + 0.05 * couple).toFixed(3)}x</strong> ambush <strong class='color-d'>damage</strong>`
case 8: //pilot wave case 8: //pilot wave
return `<strong>${(1 + 0.05 * couple).toFixed(2)}x</strong> <strong class='color-block'>block</strong> collision <strong class='color-d'>damage</strong>` return `<strong>${(1 + 0.05 * couple).toFixed(2)}x</strong> <strong class='color-block'>block</strong> collision <strong class='color-d'>damage</strong>`
case 9: //wormhole case 9: //wormhole
@@ -3577,6 +3624,7 @@ const m = {
ctx.stroke(); ctx.stroke();
} }
} }
m.bulletsToBlocks(mob[i])
if (tech.isStunField) mobs.statusStun(mob[i], tech.isStunField) if (tech.isStunField) mobs.statusStun(mob[i], tech.isStunField)
//mob knock backs //mob knock backs
const massRoot = Math.sqrt(Math.max(1, mob[i].mass)); const massRoot = Math.sqrt(Math.max(1, mob[i].mass));
@@ -4809,7 +4857,7 @@ const m = {
} }
this.drawRegenEnergyCloaking() this.drawRegenEnergyCloaking()
if (m.isSneakAttack && m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) > m.cycle) { //show sneak attack status 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) m.fieldDamage = 4.5 * (1 + 0.05 * m.coupling)
const timeLeft = (m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) - m.cycle) * 0.5 const timeLeft = (m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) - m.cycle) * 0.5
ctx.beginPath(); ctx.beginPath();
ctx.arc(m.pos.x, m.pos.y, 32, 0, 2 * Math.PI); ctx.arc(m.pos.x, m.pos.y, 32, 0, 2 * Math.PI);

View File

@@ -351,9 +351,10 @@ const powerUps = {
name: "instructions", name: "instructions",
color: "rgba(100,125,140,0.35)", color: "rgba(100,125,140,0.35)",
size() { size() {
return 150 return 130
}, },
effect() { effect() {
Matter.Body.setVelocity(player, { x: 0, y: 0 });//power up is so big it launches the player, this stops that
requestAnimationFrame(() => { //add a background behind the power up menu requestAnimationFrame(() => { //add a background behind the power up menu
ctx.fillStyle = `rgba(150,150,150,0.9)`; ctx.fillStyle = `rgba(150,150,150,0.9)`;
ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillRect(0, 0, canvas.width, canvas.height);
@@ -372,7 +373,7 @@ const powerUps = {
document.getElementById("choose-grid").classList.remove('choose-grid'); document.getElementById("choose-grid").classList.remove('choose-grid');
document.getElementById("choose-grid").style.gridTemplateColumns = "800px"//adjust this to increase the width of the whole menu, but mostly the center column document.getElementById("choose-grid").style.gridTemplateColumns = "800px"//adjust this to increase the width of the whole menu, but mostly the center column
let lore = localSettings.loreCount > 0 ? "lore.unlockTesting() //press T to enter testing" : "" let lore = localSettings.loreCount > 0 ? "lore.unlockTesting() //press T to enter testing" : ""
let text = `<div class="grid-container"><pre> <strong>//console commands</strong> let text = `<div class="grid-container" style = "font-size:1rem;"><pre> <strong>//console commands</strong>
powerUps.instructions.effect() //reproduce this message powerUps.instructions.effect() //reproduce this message
tech.giveTech("name") //replace "name" with tech name tech.giveTech("name") //replace "name" with tech name
m.setField("name") //standing wave perfect diamagnetism negative mass molecular assembler plasma torch time dilation metamaterial cloaking pilot wave wormhole grappling hook m.setField("name") //standing wave perfect diamagnetism negative mass molecular assembler plasma torch time dilation metamaterial cloaking pilot wave wormhole grappling hook
@@ -382,17 +383,13 @@ const powerUps = {
m.energy = 0 //set energy m.energy = 0 //set energy
m.health = 1 //set health m.health = 1 //set health
m.maxHealth = 1 //set max health m.maxHealth = 1 //set max health
m.energy = 1 //set energy
m.maxEnergy = 1 //set max energy m.maxEnergy = 1 //set max energy
simulation.enableConstructMode() //press T to build with mouse simulation.enableConstructMode() //press T to build with mouse
${lore} ${lore}
powerUps.spawn(m.pos.x, m.pos.y, "name") //tech gun field heal ammo research coupling boost instructions entanglement
//tech gun field heal ammo research coupling boost instructions entanglement
powerUps.spawn(m.pos.x, m.pos.y, "name")
Matter.Body.setPosition(player, simulation.mouseInGame); Matter.Body.setPosition(player, simulation.mouseInGame);
spawn.bodyRect(simulation.mouseInGame.x, simulation.mouseInGame.y, 50, 50) spawn.bodyRect(simulation.mouseInGame.x, simulation.mouseInGame.y, 50, 50)
spawn.randomLevelBoss(simulation.mouseInGame.x, simulation.mouseInGame.y) spawn.randomLevelBoss(simulation.mouseInGame.x, simulation.mouseInGame.y)
<strong>chrome</strong> <strong>firefox</strong> <strong>chrome</strong> <strong>firefox</strong>
<strong>Win/Linux:</strong> Ctrl + Shift + J Ctrl + Shift + J <strong>Win/Linux:</strong> Ctrl + Shift + J Ctrl + Shift + J
<strong>Mac:</strong> Cmd + Option + J Cmd + Shift + J</pre></div> <strong>Mac:</strong> Cmd + Option + J Cmd + Shift + J</pre></div>

View File

@@ -935,6 +935,7 @@ const simulation = {
m.onGround = false m.onGround = false
m.lastOnGroundCycle = 0 m.lastOnGroundCycle = 0
m.health = 0; m.health = 0;
level.isNoHeal = false
m.addHealth(0.25) m.addHealth(0.25)
m.drop(); m.drop();
m.holdingTarget = null m.holdingTarget = null
@@ -1185,6 +1186,7 @@ const simulation = {
}, },
clearNow: false, clearNow: false,
clearMap() { clearMap() {
level.isVerticalFLipLevel = false
level.isProcedural = false; level.isProcedural = false;
level.fallMode = ""; level.fallMode = "";
simulation.unFlipCameraVertical() simulation.unFlipCameraVertical()
@@ -1957,6 +1959,7 @@ const simulation = {
}, },
enableConstructMode() { enableConstructMode() {
level.isProcedural = false //this is set to be true in levels like labs that need x+ and y+ in front of positions level.isProcedural = false //this is set to be true in levels like labs that need x+ and y+ in front of positions
level.isVerticalFLipLevel = false
simulation.isConstructionMode = true; simulation.isConstructionMode = true;
simulation.isHorizontalFlipped = false; simulation.isHorizontalFlipped = false;
simulation.isAutoZoom = false; simulation.isAutoZoom = false;
@@ -1983,8 +1986,17 @@ const simulation = {
simulation.outputMapString(`${Math.floor(simulation.constructMouseDownPosition.x)}, ${Math.floor(simulation.constructMouseDownPosition.y)}`); simulation.outputMapString(`${Math.floor(simulation.constructMouseDownPosition.x)}, ${Math.floor(simulation.constructMouseDownPosition.y)}`);
} else if (simulation.mouseInGame.x > simulation.constructMouseDownPosition.x && simulation.mouseInGame.y > simulation.constructMouseDownPosition.y) { //make sure that the width and height are positive } else if (simulation.mouseInGame.x > simulation.constructMouseDownPosition.x && simulation.mouseInGame.y > simulation.constructMouseDownPosition.y) { //make sure that the width and height are positive
if (e.button === 0) { //add map if (e.button === 0) { //add map
// if (level.isProcedural) {
// simulation.outputMapString(`spawn.mapRect(x+${x}, ${y}, ${dx}, ${dy});\n`);
// } else {
// simulation.outputMapString(`spawn.mapRect(${x}, ${y}, ${dx}, ${dy});\n`);
// }
if (level.isProcedural) { if (level.isProcedural) {
simulation.outputMapString(`spawn.mapRect(x+${x}, ${y}, ${dx}, ${dy});\n`); simulation.outputMapString(`spawn.mapRect(x+${x}, ${y}, ${dx}, ${dy});\n`);
} else if (level.isVerticalFLipLevel) {
console.log('hi')
simulation.outputMapString(`spawn.mapRect(${x}, ${y}, ${dx}, ${dy});\n`);
simulation.outputMapString(`//spawn.mapRect(${x}, ${-y - dy}, ${dx}, ${dy});\n`);
} else { } else {
simulation.outputMapString(`spawn.mapRect(${x}, ${y}, ${dx}, ${dy});\n`); simulation.outputMapString(`spawn.mapRect(${x}, ${y}, ${dx}, ${dy});\n`);
} }

View File

@@ -2559,10 +2559,7 @@ const spawn = {
const springStiffness = 0.00014; const springStiffness = 0.00014;
const springDampening = 0.0005; const springDampening = 0.0005;
me.springTarget = { me.springTarget = { x: me.position.x, y: me.position.y };
x: me.position.x,
y: me.position.y
};
const len = cons.length; const len = cons.length;
cons[len] = Constraint.create({ cons[len] = Constraint.create({
pointA: me.springTarget, pointA: me.springTarget,
@@ -4322,7 +4319,7 @@ const spawn = {
me.accelMag = 0.0002 * simulation.accelScale; me.accelMag = 0.0002 * simulation.accelScale;
spawn.shield(me, x, y); spawn.shield(me, x, y);
me.lasers = [] //keeps track of static laser beams me.laserArray = [] //keeps track of static laser beams
me.laserLimit = simulation.difficultyMode < 3 ? 1 : 2 me.laserLimit = simulation.difficultyMode < 3 ? 1 : 2
me.fireDelay = Math.max(75, 140 - simulation.difficulty * 0.5) me.fireDelay = Math.max(75, 140 - simulation.difficulty * 0.5)
me.cycle = 0 me.cycle = 0
@@ -4354,14 +4351,14 @@ const spawn = {
best2.y = save1Y best2.y = save1Y
} }
this.lasers.push({ a: { x: best1.x, y: best1.y }, b: { x: best2.x, y: best2.y }, fade: 0 }) this.laserArray.push({ a: { x: best1.x, y: best1.y }, b: { x: best2.x, y: best2.y }, fade: 0 })
//friction to animate the mob dropping something //friction to animate the mob dropping something
Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.05)); Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.05));
Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.05) Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.05)
// simulation.drawList.push({ x: best1.x, y: best1.y, radius: 10, color: "rgba(255,0,100,0.3)", time: simulation.drawTime * 2 }); // simulation.drawList.push({ x: best1.x, y: best1.y, radius: 10, color: "rgba(255,0,100,0.3)", time: simulation.drawTime * 2 });
// simulation.drawList.push({ x: best2.x, y: best2.y, radius: 10, color: "rgba(255,0,100,0.3)", time: simulation.drawTime * 2 }); // simulation.drawList.push({ x: best2.x, y: best2.y, radius: 10, color: "rgba(255,0,100,0.3)", time: simulation.drawTime * 2 });
if (this.lasers.length > this.laserLimit) this.lasers.shift() //cap total lasers if (this.laserArray.length > this.laserLimit) this.laserArray.shift() //cap total laserArray
if (!this.seePlayer.recall && (Vector.magnitude(Vector.sub(this.position, this.driftGoal)) < 200 || 0.3 > Math.random())) { if (!this.seePlayer.recall && (Vector.magnitude(Vector.sub(this.position, this.driftGoal)) < 200 || 0.3 > Math.random())) {
//used in drift when can't find player //used in drift when can't find player
const radius = Math.random() * 1000; const radius = Math.random() * 1000;
@@ -4371,9 +4368,9 @@ const spawn = {
} }
} }
me.fireLaser = function () { me.fireLaser = function () {
for (let i = 0; i < this.lasers.length; i++) { //fire all lasers in the array for (let i = 0; i < this.laserArray.length; i++) { //fire all lasers in the array
let best = vertexCollision(this.lasers[i].a, this.lasers[i].b, m.isCloak ? [body] : [body, [playerBody, playerHead]]); //not checking map to fix not hitting player bug, this might make some lasers look strange when the map changes let best = vertexCollision(this.laserArray[i].a, this.laserArray[i].b, m.isCloak ? [body] : [body, [playerBody, playerHead]]); //not checking map to fix not hitting player bug, this might make some lasers look strange when the map changes
if (this.lasers[i].fade > 0.99) { if (this.laserArray[i].fade > 0.99) {
if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { // hitting player if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { // hitting player
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage after getting hit m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage after getting hit
const dmg = 0.03 * simulation.dmgScale; const dmg = 0.03 * simulation.dmgScale;
@@ -4385,7 +4382,7 @@ const spawn = {
color: "rgba(255,0,100,0.5)", color: "rgba(255,0,100,0.5)",
time: 20 time: 20
}); });
this.lasers.splice(i, 1) //remove this laser node this.laserArray.splice(i, 1) //remove this laser node
if (this.distanceToPlayer < 1000) { //mob jumps away from player if (this.distanceToPlayer < 1000) { //mob jumps away from player
const forceMag = 0.03 * this.mass; const forceMag = 0.03 * this.mass;
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
@@ -4395,7 +4392,7 @@ const spawn = {
} else if (best.who && best.who.classType === "body") { //hitting block } else if (best.who && best.who.classType === "body") { //hitting block
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(best.x, best.y); ctx.moveTo(best.x, best.y);
ctx.lineTo(this.lasers[i].a.x, this.lasers[i].a.y); ctx.lineTo(this.laserArray[i].a.x, this.laserArray[i].a.y);
ctx.strokeStyle = `rgb(255,0,100)`; ctx.strokeStyle = `rgb(255,0,100)`;
ctx.lineWidth = 2; ctx.lineWidth = 2;
ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]);
@@ -4403,8 +4400,8 @@ const spawn = {
ctx.setLineDash([]); ctx.setLineDash([]);
} else { //hitting nothing } else { //hitting nothing
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(this.lasers[i].b.x, this.lasers[i].b.y); ctx.moveTo(this.laserArray[i].b.x, this.laserArray[i].b.y);
ctx.lineTo(this.lasers[i].a.x, this.lasers[i].a.y); ctx.lineTo(this.laserArray[i].a.x, this.laserArray[i].a.y);
ctx.strokeStyle = `rgb(255,0,100)`; ctx.strokeStyle = `rgb(255,0,100)`;
ctx.lineWidth = 2; ctx.lineWidth = 2;
ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]);
@@ -4412,12 +4409,12 @@ const spawn = {
ctx.setLineDash([]); ctx.setLineDash([]);
} }
} else {//fade in warning } else {//fade in warning
this.lasers[i].fade += 0.01 this.laserArray[i].fade += 0.01
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(this.lasers[i].a.x, this.lasers[i].a.y); ctx.moveTo(this.laserArray[i].a.x, this.laserArray[i].a.y);
ctx.lineTo(this.lasers[i].b.x, this.lasers[i].b.y); ctx.lineTo(this.laserArray[i].b.x, this.laserArray[i].b.y);
ctx.lineWidth = 2 + 40 - 40 * this.lasers[i].fade; ctx.lineWidth = 2 + 40 - 40 * this.laserArray[i].fade;
ctx.strokeStyle = `rgba(255,0,100,${0.02 + 0.1 * this.lasers[i].fade})`; ctx.strokeStyle = `rgba(255,0,100,${0.02 + 0.1 * this.laserArray[i].fade})`;
ctx.stroke(); ctx.stroke();
} }
} }
@@ -4486,7 +4483,7 @@ const spawn = {
this.laserDelay = 130 this.laserDelay = 130
} }
}; };
me.lasers = [] //keeps track of static laser beams me.laserArray = [] //keeps track of static laser beams
me.laserLimit = 2 + (simulation.difficultyMode > 2) + (simulation.difficultyMode > 4) me.laserLimit = 2 + (simulation.difficultyMode > 2) + (simulation.difficultyMode > 4)
me.fireDelay = Math.max(75, 140 - simulation.difficulty * 0.5) me.fireDelay = Math.max(75, 140 - simulation.difficulty * 0.5)
me.cycle = 0 me.cycle = 0
@@ -4517,7 +4514,7 @@ const spawn = {
best2.x = save1X best2.x = save1X
best2.y = save1Y best2.y = save1Y
} }
this.lasers.push({ a: { x: best1.x, y: best1.y }, b: { x: best2.x, y: best2.y }, fade: 0 }) this.laserArray.push({ a: { x: best1.x, y: best1.y }, b: { x: best2.x, y: best2.y }, fade: 0 })
} }
// add(m.pos, m.angle) // add(m.pos, m.angle)
add(m.pos, this.angle + Math.PI / 4 + Math.PI / 2) add(m.pos, this.angle + Math.PI / 4 + Math.PI / 2)
@@ -4534,9 +4531,9 @@ const spawn = {
} }
} }
me.fireLaser = function () { me.fireLaser = function () {
for (let i = 0; i < this.lasers.length; i++) { //fire all lasers in the array for (let i = 0; i < this.laserArray.length; i++) { //fire all laserArray in the array
let best = vertexCollision(this.lasers[i].a, this.lasers[i].b, m.isCloak ? [body] : [body, [playerBody, playerHead]]); //not checking map to fix not hitting player bug, this might make some lasers look strange when the map changes let best = vertexCollision(this.laserArray[i].a, this.laserArray[i].b, m.isCloak ? [body] : [body, [playerBody, playerHead]]); //not checking map to fix not hitting player bug, this might make some lasers look strange when the map changes
if (this.lasers[i].fade > 0.99) { if (this.laserArray[i].fade > 0.99) {
if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { // hitting player if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { // hitting player
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage after getting hit m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage after getting hit
const dmg = 0.03 * simulation.dmgScale; const dmg = 0.03 * simulation.dmgScale;
@@ -4548,7 +4545,7 @@ const spawn = {
color: "rgba(255,0,100,0.5)", color: "rgba(255,0,100,0.5)",
time: 20 time: 20
}); });
this.lasers.splice(i, 1) //remove this laser node this.laserArray.splice(i, 1) //remove this laser node
if (this.distanceToPlayer < 1000) { //mob jumps away from player if (this.distanceToPlayer < 1000) { //mob jumps away from player
const forceMag = 0.03 * this.mass; const forceMag = 0.03 * this.mass;
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
@@ -4558,7 +4555,7 @@ const spawn = {
} else if (best.who && best.who.classType === "body") { //hitting block } else if (best.who && best.who.classType === "body") { //hitting block
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(best.x, best.y); ctx.moveTo(best.x, best.y);
ctx.lineTo(this.lasers[i].a.x, this.lasers[i].a.y); ctx.lineTo(this.laserArray[i].a.x, this.laserArray[i].a.y);
ctx.strokeStyle = `rgb(255,0,100)`; ctx.strokeStyle = `rgb(255,0,100)`;
ctx.lineWidth = 2; ctx.lineWidth = 2;
ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]);
@@ -4566,8 +4563,8 @@ const spawn = {
ctx.setLineDash([]); ctx.setLineDash([]);
} else { //hitting nothing } else { //hitting nothing
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(this.lasers[i].b.x, this.lasers[i].b.y); ctx.moveTo(this.laserArray[i].b.x, this.laserArray[i].b.y);
ctx.lineTo(this.lasers[i].a.x, this.lasers[i].a.y); ctx.lineTo(this.laserArray[i].a.x, this.laserArray[i].a.y);
ctx.strokeStyle = `rgb(255,0,100)`; ctx.strokeStyle = `rgb(255,0,100)`;
ctx.lineWidth = 2; ctx.lineWidth = 2;
ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]);
@@ -4575,16 +4572,16 @@ const spawn = {
ctx.setLineDash([]); ctx.setLineDash([]);
} }
} else {//fade in warning } else {//fade in warning
this.lasers[i].fade += 0.007 this.laserArray[i].fade += 0.007
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(this.lasers[i].a.x, this.lasers[i].a.y); ctx.moveTo(this.laserArray[i].a.x, this.laserArray[i].a.y);
ctx.lineTo(this.lasers[i].b.x, this.lasers[i].b.y); ctx.lineTo(this.laserArray[i].b.x, this.laserArray[i].b.y);
ctx.lineWidth = 2 + 40 - 40 * this.lasers[i].fade; ctx.lineWidth = 2 + 40 - 40 * this.laserArray[i].fade;
ctx.strokeStyle = `rgba(255,0,100,${0.02 + 0.1 * this.lasers[i].fade})`; ctx.strokeStyle = `rgba(255,0,100,${0.02 + 0.1 * this.laserArray[i].fade})`;
ctx.stroke(); ctx.stroke();
if (this.lasers[i].fade > 0.99) { if (this.laserArray[i].fade > 0.99) {
this.lasers[i].fade = 1; this.laserArray[i].fade = 1;
if (this.lasers.length > this.laserLimit) this.lasers.shift() //cap total lasers if (this.laserArray.length > this.laserLimit) this.laserArray.shift() //cap total lasers
break break
} }
} }
@@ -4744,9 +4741,9 @@ const spawn = {
Matter.Body.setAngularVelocity(this, 0) Matter.Body.setAngularVelocity(this, 0)
} }
ctx.beginPath(); ctx.beginPath();
this.lasers(this.vertices[0], this.angle + Math.PI / 3); this.laserArray(this.vertices[0], this.angle + Math.PI / 3);
this.lasers(this.vertices[1], this.angle + Math.PI); this.laserArray(this.vertices[1], this.angle + Math.PI);
this.lasers(this.vertices[2], this.angle - Math.PI / 3); this.laserArray(this.vertices[2], this.angle - Math.PI / 3);
ctx.strokeStyle = "#50f"; ctx.strokeStyle = "#50f";
ctx.lineWidth = 1.5; ctx.lineWidth = 1.5;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
@@ -4757,7 +4754,7 @@ const spawn = {
ctx.stroke(); // Draw it ctx.stroke(); // Draw it
} }
}; };
me.lasers = function (where, angle) { me.laserArray = function (where, angle) {
const seeRange = 7000; const seeRange = 7000;
best = { best = {
x: null, x: null,

View File

@@ -2634,25 +2634,6 @@ const tech = {
tech.isDarkStar = false tech.isDarkStar = false
} }
}, },
{
name: "ablative drones",
descriptionFunction() {
return `after losing ${tech.isEnergyHealth ? "<strong class='color-f'>energy</strong>" : "<strong class='color-h'>health</strong>"} there is a chance<br>to rebuild your broken parts as <strong>drones</strong>`
},
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed: () => true,
requires: "",
effect() {
tech.isDroneOnDamage = true;
// for (let i = 0; i < 4; i++) b.drone()
},
remove() {
tech.isDroneOnDamage = false;
}
},
{ {
name: "non-Newtonian armor", name: "non-Newtonian armor",
link: `<a target="_blank" href='https://en.wikipedia.org/wiki/Non-Newtonian_fluid' class="link">non-Newtonian armor</a>`, link: `<a target="_blank" href='https://en.wikipedia.org/wiki/Non-Newtonian_fluid' class="link">non-Newtonian armor</a>`,
@@ -4574,7 +4555,7 @@ const tech = {
name: "deprecated", name: "deprecated",
scale: 0.08, scale: 0.08,
descriptionFunction() { descriptionFunction() {
return `after <span class='color-remove'>removing</span> this gain<br><strong>${1 + this.scale}x</strong> <strong class='color-d'>damage</strong> per <span class='color-remove'>removed</span> ${powerUps.orb.tech()}<em style ="float: right;">(${(1 + this.scale * ((this.frequency === 0 ? 0 : 1) + tech.removeCount)).toFixed(2)}x)</em>` return `after <span class='color-remove'>removing</span> this gain <strong>${1 + this.scale}x</strong> <strong class='color-d'>damage</strong><br>per ${powerUps.orb.tech()} <span class='color-remove'>removed</span> this game<em style ="float: right;">(${(1 + this.scale * ((this.frequency === 0 ? 0 : 1) + tech.removeCount)).toFixed(2)}x)</em>`
}, },
maxCount: 1, maxCount: 1,
count: 0, count: 0,
@@ -6607,7 +6588,7 @@ const tech = {
allowed() { allowed() {
return tech.haveGunCheck("drones", false) && !tech.isDroneRespawn && tech.bulletsLastLonger === 1 && !tech.isDronesTravel && (build.isExperimentSelection || powerUps.research.count > 1) return tech.haveGunCheck("drones", false) && !tech.isDroneRespawn && tech.bulletsLastLonger === 1 && !tech.isDronesTravel && (build.isExperimentSelection || powerUps.research.count > 1)
}, },
requires: "drones, not drone repair, anti-shear topology, autonomous navigation", requires: "drones, not drone repair, anti-shear topology, autonomous navigation, ",
effect() { effect() {
const num = 5 const num = 5
tech.isForeverDrones += num tech.isForeverDrones += num
@@ -6640,6 +6621,27 @@ const tech = {
// if (this.count > 0) powerUps.research.changeRerolls(2) // if (this.count > 0) powerUps.research.changeRerolls(2)
} }
}, },
{
name: "ablative drones",
descriptionFunction() {
return `after losing ${tech.isEnergyHealth ? "<strong class='color-f'>energy</strong>" : "<strong class='color-h'>health</strong>"} there is a chance<br>to rebuild your broken parts as <strong>drones</strong>`
},
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return (tech.haveGunCheck("drones") && !tech.isForeverDrones) || (m.fieldMode === 4 && simulation.molecularMode === 3)
},
requires: "drones, not fault tolerance",
effect() {
tech.isDroneOnDamage = true;
},
remove() {
tech.isDroneOnDamage = false;
}
},
{ {
name: "reduced tolerances", name: "reduced tolerances",
link: `<a target="_blank" href='https://en.wikipedia.org/wiki/Engineering_tolerance' class="link">reduced tolerances</a>`, link: `<a target="_blank" href='https://en.wikipedia.org/wiki/Engineering_tolerance' class="link">reduced tolerances</a>`,

View File

@@ -1,32 +1,27 @@
******************************************************** NEXT PATCH ************************************************** ******************************************************** NEXT PATCH **************************************************
new level testChamber2 new level: gravityObservatory
New camera flip effect
new laser level element now has collisions with blocks
elevators are less deadly to mobs at low speeds
difficulty level progression reworked level: testChamber2 renamed gravityInterferometer
no constraints on final boss it has been added to the levels that are extra hard and only show up late game
new constraint - healing disabled it also got a bit hard with the addition of 1 more laser
quenching 0.3->0.4x overheal converted to max health deflected mob bullets are converted into small blocks
tungsten carbide 400->500 extra max health ablative drones is now a gun tech for drones
paradigm shift's health loss is no longer reduced by damage taken reduction it makes ~33% more drones
coherence no longer remembers tech that is set to zero frequency, like removed tech 1.033->1.05x sneak attack damage per coupling for cloaking field
JUNK tech: pet the bot - lets you pet your bots bug fixes
JUNK tech: the upside down - flip everything extended vertical flip to edge cases:
trail left by snakeBoss
bug laser array from boss and mobs
prevented possible duplicate choices with coherence tech springer,spiderBoss,mantisBoss constraints
fixed issues with showing and hiding health bars on that constraint subway: dark matter no longer removed if it gets too far from the player
fixed crash from autonomous defense training: fixed potential lock out from running out of ammo
mass-energy mode wasn't getting any benefit from damage taken reduction training: fixed accidental difficulty increase
it now gets square root of damage taken reduction
******************************************************** BUGS ******************************************************** ******************************************************** BUGS ********************************************************
figure out why seeded random isn't making runs the same: figure out why seeded random isn't making runs the same:
shuffle is being used for a wide variety of things that don't need a seeded random shuffle is being used for a wide variety of things that don't need a seeded random
make two shuffle functions? make two shuffle functions?
@@ -55,26 +50,37 @@ player can become crouched while not touching the ground if they exit the ground
*********************************************************** TODO ***************************************************** *********************************************************** TODO *****************************************************
considering removing generative AI images from n-gon
pros: (of removing images)
the novelty of the images has worn off
cleaner UI without them
reduce the total size of n-gon by about 1/3
reduce the complexity of the code
avoid ire of people that hate generative AI
cons: (of removing images)
some people might like them
make a level selector power up
it shows up when you enter testing mode on the initial level
check box for community maps?
deflecting with field converts mob bullets to blocks that despawn after a few seconds
default for all fields that can deflect
add more tips:
download latest version of n-gon
https://codeload.github.com/landgreen/n-gon/zip/refs/heads/master
new level based laser element new level based laser element
!!update new version into other levels !!update new version into other levels
level technique: pairs of touch activated elevators jump on one to get high enough to jump on the next one level technique: pairs of touch activated elevators jump on one to get high enough to jump on the next one
flip player upside down new vertical flip level
fieldTech: negative mass? long horizontal
JUNK tech? several buttons
final boss effect shorten flip time?
maybe just flip player camera and mouse
level effect flip map vertically also
new subway station
new full level
needs a device that will flip gravity
update to old level so it is more surreal
levels with few effects, just blocks and map
levels with a roof or infinite fall
towers, lab
@@ -1343,6 +1349,8 @@ possible names for tech
cork - used as a heat shield for rockets cork - used as a heat shield for rockets
P = NP - something with speeding up calculation times P = NP - something with speeding up calculation times
transistivity - something where a>b and b>c -> a>c transistivity - something where a>b and b>c -> a>c
lenticular - looks different from different angles (lasers?)
De Sitter space - simple model of universe related to general relativity (mass-energy?)
******************************************************* DESIGN ****************************************************** ******************************************************* DESIGN ******************************************************