level: subway
  replaces gauntlet just before the final boss
    gauntlet moved to community map pool
  subway todo:
    add a few more stations
    balance difficulty
    find bugs

surface plasmons does 50% more damage
elasticity renamed nitinol because I bought some nitinol wire and it's neat
entanglement power up no longer shows guns or fields you already have
disabled minimal HUD for training levels
reaction inhibitor 12->11% mob max health reduction

spawn.bodyRect() now can add blocks mid level without any extra code
  I think I found all the bugs this causes, but let me know if any blocks added mid game aren't colliding

community map - clock update:
  visual overhaul,live lighting, remove pendulum overlap, move with pendulum if you stand in it, debris, trap exit only opens after completing the fight, moving elements now freeze while using time dilation
This commit is contained in:
landgreen
2023-06-03 13:56:18 -07:00
parent b3fa1bfc8a
commit 09c9e93fcf
10 changed files with 1428 additions and 445 deletions

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -303,7 +303,13 @@ const build = {
// console.log(localSettings.isHideImages, from) // console.log(localSettings.isHideImages, from)
}, },
hideHUD() { hideHUD() {
localSettings.isHideHUD = !localSettings.isHideHUD
if (simulation.isTraining) {
localSettings.isHideHUD = false
} else {
localSettings.isHideHUD = !localSettings.isHideHUD
}
if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
document.getElementById("hide-hud").checked = localSettings.isHideHUD document.getElementById("hide-hud").checked = localSettings.isHideHUD
document.getElementById("hide-hud").classList.toggle("ticked") document.getElementById("hide-hud").classList.toggle("ticked")
@@ -1299,12 +1305,7 @@ window.addEventListener("keydown", function (event) {
powerUps.directSpawn(simulation.mouseInGame.x, simulation.mouseInGame.y, "tech"); powerUps.directSpawn(simulation.mouseInGame.x, simulation.mouseInGame.y, "tech");
break break
case "6": case "6":
const index = body.length
spawn.bodyRect(simulation.mouseInGame.x, simulation.mouseInGame.y, 50, 50); spawn.bodyRect(simulation.mouseInGame.x, simulation.mouseInGame.y, 50, 50);
body[index].collisionFilter.category = cat.body;
body[index].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
body[index].classType = "body";
Composite.add(engine.world, body[index]); //add to world
break break
case "7": case "7":
const pick = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]; const pick = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)];
@@ -1452,7 +1453,7 @@ document.body.addEventListener("wheel", (e) => {
//********************************************************************** //**********************************************************************
let localSettings let localSettings
function localstorageCheck() { function localStorageCheck() {
try { try {
return 'localStorage' in window && window['localStorage'] !== null; return 'localStorage' in window && window['localStorage'] !== null;
} catch (e) { } catch (e) {
@@ -1460,7 +1461,7 @@ function localstorageCheck() {
} }
} }
if (localstorageCheck()) { if (localStorageCheck()) {
localSettings = JSON.parse(localStorage.getItem("localSettings")) localSettings = JSON.parse(localStorage.getItem("localSettings"))
if (localSettings) { if (localSettings) {
console.log('localStorage is enabled') console.log('localStorage is enabled')

File diff suppressed because it is too large Load Diff

View File

@@ -239,7 +239,7 @@ const mobs = {
deathCount: 0, deathCount: 0,
mobSpawnWithHealth: 1, mobSpawnWithHealth: 1,
setMobSpawnHealth() { setMobSpawnHealth() {
mobs.mobSpawnWithHealth = 0.88 ** (tech.mobSpawnWithHealth) mobs.mobSpawnWithHealth = 0.89 ** (tech.mobSpawnWithHealth)
}, },
//********************************************************************************************** //**********************************************************************************************
//********************************************************************************************** //**********************************************************************************************
@@ -1397,9 +1397,6 @@ const mobs = {
Matter.Body.setAngularVelocity(body[len2], this.angularVelocity); Matter.Body.setAngularVelocity(body[len2], this.angularVelocity);
body[len2].collisionFilter.category = cat.body; body[len2].collisionFilter.category = cat.body;
body[len2].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; body[len2].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet;
// if (body[len].mass > 10 || 45 + 10 * Math.random() < body.length) {
// body[len].collisionFilter.mask = cat.player | cat.bullet | cat.mob | cat.mobBullet;
// }
body[len2].classType = "body"; body[len2].classType = "body";
Composite.add(engine.world, body[len2]); //add to world Composite.add(engine.world, body[len2]); //add to world

View File

@@ -2282,13 +2282,13 @@ const m = {
if (tech.isLaserField) { if (tech.isLaserField) {
simulation.ephemera.push({ simulation.ephemera.push({
name: "laser field", //used to find this array element in simulation.removeEphemera() name: "laser field", //used to find this array element in simulation.removeEphemera()
count: 15 + Math.floor(m.maxEnergy * 30 * 0.0018 / tech.laserDrain), //how many cycles the ephemera lasts, scales with max energy count: 20 + Math.floor(m.maxEnergy * 30 * 0.0018 / tech.laserDrain), //how many cycles the ephemera lasts, scales with max energy
do() { do() {
this.count-- this.count--
if (this.count < 0) simulation.removeEphemera(this.name) if (this.count < 0) simulation.removeEphemera(this.name)
for (let i = 0, num = 20; i < num; i++) { //draw random lasers for (let i = 0, num = 12; i < num; i++) { //draw random lasers
const angle = 6.28 * i / num + m.cycle * 0.04 const angle = 6.28 * i / num + m.cycle * 0.04
b.laser({ x: m.pos.x + 30 * Math.cos(angle), y: m.pos.y + 30 * Math.sin(angle) }, { x: m.pos.x + 3000 * Math.cos(angle), y: m.pos.y + 3000 * Math.sin(angle) })//dmg = tech.laserDamage, reflections = tech.laserReflections, isThickBeam = false, push = 1 b.laser({ x: m.pos.x + 30 * Math.cos(angle), y: m.pos.y + 30 * Math.sin(angle) }, { x: m.pos.x + 3000 * Math.cos(angle), y: m.pos.y + 3000 * Math.sin(angle) }, tech.laserDamage * 2.5)//dmg = tech.laserDamage, reflections = tech.laserReflections, isThickBeam = false, push = 1
} }
}, },
}) })
@@ -2470,7 +2470,7 @@ const m = {
// return `<span style = 'font-size:95%;'><strong>deflecting</strong> condenses +${couple.toFixed(1)} <strong class='color-s'>ice IX</strong></span>` // return `<span style = 'font-size:95%;'><strong>deflecting</strong> condenses +${couple.toFixed(1)} <strong class='color-s'>ice IX</strong></span>`
return `+${(couple * 5).toFixed(0)} maximum <strong class='color-f'>energy</strong>` return `+${(couple * 5).toFixed(0)} maximum <strong class='color-f'>energy</strong>`
case 2: //perfect diamagnetism case 2: //perfect diamagnetism
return `<span style = 'font-size:95%;'><strong>deflecting</strong> condenses ${0.1 * couple.toFixed(2)} <strong class='color-s'>ice IX</strong></span>` return `<span style = 'font-size:95%;'><strong>deflecting</strong> condenses ${(0.1 * couple).toFixed(2)} <strong class='color-s'>ice IX</strong></span>`
// return `<span style = 'font-size:89%;'><strong>invulnerable</strong> <strong>+${2*couple}</strong> seconds post collision</span>` // return `<span style = 'font-size:89%;'><strong>invulnerable</strong> <strong>+${2*couple}</strong> seconds post collision</span>`
case 3: //negative mass case 3: //negative mass
return `<strong>+${(100 * (1 - 0.973 ** couple)).toFixed(1)}%</strong> <strong class='color-defense'>defense</strong>` return `<strong>+${(100 * (1 - 0.973 ** couple)).toFixed(1)}%</strong> <strong class='color-defense'>defense</strong>`

View File

@@ -664,7 +664,7 @@ const powerUps = {
researchText(type) { researchText(type) {
let text = "" let text = ""
if (type === "entanglement") { if (type === "entanglement") {
text += `<div class='choose-grid-module entanglement flipX'>entanglement</div>` text += `<div class='choose-grid-module entanglement flipX' onclick='powerUps.endDraft("${type}",true)'>entanglement</div>`
} else if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) { } else if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) {
text += `<div onclick="powerUps.research.use('${type}')" class='research-card'>` // style = "margin-left: 192px; margin-right: -192px;" text += `<div onclick="powerUps.research.use('${type}')" class='research-card'>` // style = "margin-left: 192px; margin-right: -192px;"
tech.junkResearchNumber = Math.ceil(4 * Math.random()) tech.junkResearchNumber = Math.ceil(4 * Math.random())
@@ -686,7 +686,7 @@ const powerUps = {
researchAndCancelText(type) { researchAndCancelText(type) {
let text = `<div class='research-cancel'>` let text = `<div class='research-cancel'>`
if (type === "entanglement") { if (type === "entanglement") {
text += `<span class='research-card entanglement flipX' style="width: 275px;"><span style="letter-spacing: 6px;">entanglement</span></span>` //&zwnj; text += `<span class='research-card entanglement flipX' style="width: 275px;" onclick='powerUps.endDraft("${type}",true)'><span style="letter-spacing: 6px;">entanglement</span></span>` //&zwnj;
} else if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) { } else if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) {
text += `<span onclick="powerUps.research.use('${type}')" class='research-card' style="width: 275px;float: left;">` // style = "margin-left: 192px; margin-right: -192px;" text += `<span onclick="powerUps.research.use('${type}')" class='research-card' style="width: 275px;float: left;">` // style = "margin-left: 192px; margin-right: -192px;"
tech.junkResearchNumber = Math.ceil(4 * Math.random()) tech.junkResearchNumber = Math.ceil(4 * Math.random())
@@ -1140,7 +1140,7 @@ const powerUps = {
const cycle = (timestamp) => { const cycle = (timestamp) => {
// if (timeStart === undefined) timeStart = timestamp // if (timeStart === undefined) timeStart = timestamp
// console.log(timestamp, timeStart) // console.log(timestamp, timeStart)
if (timestamp - timeStart > tech.brainStormDelay * count) { if (timestamp - timeStart > tech.brainStormDelay * count && simulation.isChoosing) {
count++ count++
powerUps.tech.effect(); powerUps.tech.effect();
document.getElementById("choose-grid").style.pointerEvents = "auto"; //turn off the normal 500ms delay document.getElementById("choose-grid").style.pointerEvents = "auto"; //turn off the normal 500ms delay
@@ -1201,18 +1201,19 @@ const powerUps = {
// text += "<div></div>" // text += "<div></div>"
// text += "<div class='choose-grid-module entanglement flipX'>entanglement</div>" // text += "<div class='choose-grid-module entanglement flipX'>entanglement</div>"
// text += `<div class='choose-grid-module' onclick='powerUps.endDraft("tech",true)' style="width: 82px; text-align: center;font-size: 1.1em;font-weight: 100;justify-self: end;">cancel</div>` //powerUps.cancelText('tech') // text += `<div class='choose-grid-module' onclick='powerUps.endDraft("tech",true)' style="width: 82px; text-align: center;font-size: 1.1em;font-weight: 100;justify-self: end;">cancel</div>` //powerUps.cancelText('tech')
if (localSettings.entanglement.fieldIndex) { if (localSettings.entanglement.fieldIndex && localSettings.entanglement.fieldIndex !== m.fieldMode) {
const choose = localSettings.entanglement.fieldIndex //add field const choose = localSettings.entanglement.fieldIndex //add field
text += powerUps.fieldText(choose, `powerUps.choose('field',${choose})`) text += powerUps.fieldText(choose, `powerUps.choose('field',${choose})`)
} }
for (let i = 0; i < localSettings.entanglement.gunIndexes.length; i++) { //add guns for (let i = 0; i < localSettings.entanglement.gunIndexes.length; i++) { //add guns
const choose = localSettings.entanglement.gunIndexes[i] const choose = localSettings.entanglement.gunIndexes[i]
//check if you always have this gun
let alreadyHasGun = false
for (let j = 0; j < b.inventory.length; j++) {
if (b.inventory[j] === choose) alreadyHasGun = true
}
// text += `<div class="choose-grid-module" onclick="powerUps.choose('gun',${gun})"><div class="grid-title"><div class="circle-grid gun"></div> &nbsp; ${b.guns[gun].name}</div> ${b.guns[gun].description}</div>` // text += `<div class="choose-grid-module" onclick="powerUps.choose('gun',${gun})"><div class="grid-title"><div class="circle-grid gun"></div> &nbsp; ${b.guns[gun].name}</div> ${b.guns[gun].description}</div>`
text += powerUps.gunText(choose, `powerUps.choose('gun',${choose})`) if (!alreadyHasGun) text += powerUps.gunText(choose, `powerUps.choose('gun',${choose})`)
//consider not adding guns the player currently has?
} }
for (let i = 0; i < localSettings.entanglement.techIndexes.length; i++) { //add tech for (let i = 0; i < localSettings.entanglement.techIndexes.length; i++) { //add tech
let choose = localSettings.entanglement.techIndexes[i] let choose = localSettings.entanglement.techIndexes[i]

View File

@@ -388,213 +388,6 @@ const simulation = {
} }
requestAnimationFrame(loop) requestAnimationFrame(loop)
}, },
sight: { //credit to Cornbread for adding this algorithm to n-gon
intersectMap: [], //this is precalculated in simulation.draw.setPaths() when the map changes
getIntersection(v1, v1End, domain) {
const intersections = simulation.sight.getIntersections(v1, v1End, domain);
var best = { x: v1End.x, y: v1End.y, dist: Math.sqrt((v1End.x - v1.x) ** 2 + (v1End.y - v1.y) ** 2) }
for (const intersection of intersections) {
const dist = Math.sqrt((intersection.x - v1.x) ** 2 + (intersection.y - v1.y) ** 2);
if (dist < best.dist) best = { x: intersection.x, y: intersection.y, dist: dist }
}
return best;
},
getIntersections(v1, v1End, domain) {
const intersections = [];
for (const obj of domain) {
for (var i = 0; i < obj.vertices.length - 1; i++) {
results = simulation.checkLineIntersection(v1, v1End, obj.vertices[i], obj.vertices[i + 1]);
if (results.onLine1 && results.onLine2) intersections.push({ x: results.x, y: results.y });
}
results = simulation.checkLineIntersection(v1, v1End, obj.vertices[obj.vertices.length - 1], obj.vertices[0]);
if (results.onLine1 && results.onLine2) intersections.push({ x: results.x, y: results.y });
}
return intersections;
},
circleLoS(pos, radius) {
function allCircleLineCollisions(c, radius, domain) {
var lines = [];
for (const obj of domain) {
for (var i = 0; i < obj.vertices.length - 1; i++) lines.push(circleLineCollisions(obj.vertices[i], obj.vertices[i + 1], c, radius));
lines.push(circleLineCollisions(obj.vertices[obj.vertices.length - 1], obj.vertices[0], c, radius));
}
const collisionLines = [];
for (const line of lines) {
if (line.length == 2) {
// const distance1 = Math.sqrt((line[0].x - c.x) ** 2 + (line[0].y - c.y) ** 2)
// const angle1 = Math.atan2(line[0].y - c.y, line[0].x - c.x);
// const queryPoint1 = {
// x: Math.cos(angle1) * (distance1 - 1) + c.x,
// y: Math.sin(angle1) * (distance1 - 1) + c.y
// }
// const distance2 = Math.sqrt((line[1].x - c.x) ** 2 + (line[1].y - c.y) ** 2)
// const angle2 = Math.atan2(line[1].y - c.y, line[1].x - c.x);
// const queryPoint2 = {
// x: Math.cos(angle2) * (distance2 - 1) + c.x,
// y: Math.sin(angle2) * (distance2 - 1) + c.y
// }
collisionLines.push(line)
}
}
return collisionLines;
}
function circleLineCollisions(a, b, c, radius) {
// calculate distances
const angleOffset = Math.atan2(b.y - a.y, b.x - a.x);
const sideB = Math.sqrt((a.x - c.x) ** 2 + (a.y - c.y) ** 2);
const sideC = Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2);
const sideA = Math.sqrt((c.x - b.x) ** 2 + (c.y - b.y) ** 2);
// calculate the closest point on line AB to point C
const angleA = Math.acos((sideB ** 2 + sideC ** 2 - sideA ** 2) / (2 * sideB * sideC)) * (a.x - c.x) / -Math.abs(a.x - c.x)
const sideAD = Math.cos(angleA) * sideB;
const d = { // closest point
x: Math.cos(angleOffset) * sideAD + a.x,
y: Math.sin(angleOffset) * sideAD + a.y
}
const distance = Math.sqrt((d.x - c.x) ** 2 + (d.y - c.y) ** 2);
if (distance == radius) {
// tangent
return [d];
} else if (distance < radius) {
// secant
const angleOffset = Math.atan2(d.y - c.y, d.x - c.x);
const innerAngle = Math.acos(distance / radius);
const intersection1 = {
x: Math.cos(angleOffset + innerAngle) * radius + c.x,
y: Math.sin(angleOffset + innerAngle) * radius + c.y
}
const intersection2 = {
x: Math.cos(angleOffset - innerAngle) * radius + c.x,
y: Math.sin(angleOffset - innerAngle) * radius + c.y
}
const distance1 = {
a: Math.sqrt((intersection1.x - a.x) ** 2 + (intersection1.y - a.y) ** 2),
b: Math.sqrt((intersection1.x - b.x) ** 2 + (intersection1.y - b.y) ** 2)
}
const distance2 = {
a: Math.sqrt((intersection2.x - a.x) ** 2 + (intersection2.y - a.y) ** 2),
b: Math.sqrt((intersection2.x - b.x) ** 2 + (intersection2.y - b.y) ** 2)
}
const result = [];
if (Math.abs(sideC - (distance1.a + distance1.b)) < 0.01) {
result.push(intersection1);
} else {
if (distance1.a < distance1.b) {
if (sideB <= radius) result.push(a);
} else {
if (sideA <= radius) result.push(b)
}
}
if (Math.abs(sideC - (distance2.a + distance2.b)) < 0.01) {
result.push(intersection2);
} else {
if (distance2.a <= distance2.b) {
if (sideB <= radius) result.push(a);
} else {
if (sideA <= radius) result.push(b)
}
}
return result;
} else {
// no intersection
return [];
}
}
var vertices = [];
for (const obj of simulation.sight.intersectMap) {
for (var i = 0; i < obj.vertices.length; i++) {
const vertex = obj.vertices[i];
const angleToVertex = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
const distanceToVertex = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2);
const queryPoint = { x: Math.cos(angleToVertex) * (distanceToVertex - 1) + pos.x, y: Math.sin(angleToVertex) * (distanceToVertex - 1) + pos.y }
if (Matter.Query.ray(map, pos, queryPoint).length == 0) {
var distance = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2);
var endPoint = { x: vertex.x, y: vertex.y }
if (distance > radius) {
const angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
endPoint = { x: Math.cos(angle) * radius + pos.x, y: Math.sin(angle) * radius + pos.y }
distance = radius
}
var best = simulation.sight.getIntersection(pos, endPoint, map);
if (best.dist >= distance) best = { x: endPoint.x, y: endPoint.y, dist: distance }
vertices.push(best)
var angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
endPoint = { x: Math.cos(angle + 0.001) * radius + pos.x, y: Math.sin(angle + 0.001) * radius + pos.y }
best = simulation.sight.getIntersection(pos, endPoint, map);
if (best.dist >= radius) best = { x: endPoint.x, y: endPoint.y, dist: radius }
vertices.push(best)
angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
endPoint = { x: Math.cos(angle - 0.001) * radius + pos.x, y: Math.sin(angle - 0.001) * radius + pos.y }
best = simulation.sight.getIntersection(pos, endPoint, map);
if (best.dist >= radius) best = { x: endPoint.x, y: endPoint.y, dist: radius }
vertices.push(best)
}
}
}
const outerCollisions = allCircleLineCollisions(pos, radius, map);
const circleCollisions = [];
for (const line of outerCollisions) {
for (const vertex of line) {
const distance = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2)
const angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
const queryPoint = {
x: Math.cos(angle) * (distance - 1) + pos.x,
y: Math.sin(angle) * (distance - 1) + pos.y
}
if (Math.abs(distance - radius) < 1 && Matter.Query.ray(map, pos, queryPoint).length == 0) circleCollisions.push(vertex)
}
}
for (var i = 0; i < circleCollisions.length; i++) {
const vertex = circleCollisions[i];
var nextIndex = i + 1;
if (nextIndex == circleCollisions.length) nextIndex = 0;
const nextVertex = circleCollisions[nextIndex];
const angle1 = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
const angle2 = Math.atan2(nextVertex.y - pos.y, nextVertex.x - pos.x);
var newAngle;
if (Math.abs(angle1) > Math.PI / 2 && Math.abs(angle2) > Math.PI / 2 && angle1 / Math.abs(angle1) != angle2 / Math.abs(angle2)) {
// if the arc between the to points crosses over the left side (+/- pi radians)
const newAngle1 = (Math.PI - Math.abs(angle1)) * (angle1 / Math.abs(angle1));
const newAngle2 = (Math.PI - Math.abs(angle2)) * (angle2 / Math.abs(angle2));
newAngle = (newAngle1 + newAngle2) / 2;
var multiplier;
if (newAngle == 0) {
multiplier = 1;
} else {
multiplier = newAngle / Math.abs(newAngle);
}
newAngle = Math.PI * multiplier - newAngle * multiplier;
test = true;
} else {
newAngle = (angle1 + angle2) / 2;
}
// shoot ray between them
var endPoint = { x: Math.cos(newAngle) * radius + pos.x, y: Math.sin(newAngle) * radius + pos.y }
var best = simulation.sight.getIntersection(pos, endPoint, map);
vertices.push(vertex);
if (best.dist <= radius) vertices.push({ x: best.x, y: best.y })
}
vertices.sort((a, b) => Math.atan2(a.y - pos.y, a.x - pos.x) - Math.atan2(b.y - pos.y, b.x - pos.x));
return vertices;
},
},
boldActiveGunHUD() { boldActiveGunHUD() {
if (b.inventory.length > 0) { if (b.inventory.length > 0) {
for (let i = 0, len = b.inventory.length; i < len; ++i) { for (let i = 0, len = b.inventory.length; i < len; ++i) {
@@ -1287,6 +1080,7 @@ const simulation = {
map: "#444", map: "#444",
bullet: "#000" bullet: "#000"
} }
simulation.draw.drawMapPath = simulation.draw.drawMapPathDefault
m.fireCDcycle = 0 m.fireCDcycle = 0
m.drop(); m.drop();
m.hole.isOn = false; m.hole.isOn = false;
@@ -1507,6 +1301,218 @@ const simulation = {
ctx.textAlign = "center"; ctx.textAlign = "center";
ctx.fillText(`(${simulation.mouseInGame.x.toFixed(1)}, ${simulation.mouseInGame.y.toFixed(1)})`, simulation.mouse.x, simulation.mouse.y - 20); ctx.fillText(`(${simulation.mouseInGame.x.toFixed(1)}, ${simulation.mouseInGame.y.toFixed(1)})`, simulation.mouse.x, simulation.mouse.y - 20);
}, },
sight: { //credit to Cornbread for adding this algorithm to n-gon
// square: 0,
intersectMap: [], //this is precalculated in simulation.draw.setPaths() when the map changes
getIntersection(v1, v1End, domain) {
const intersections = simulation.sight.getIntersections(v1, v1End, domain);
var best = { x: v1End.x, y: v1End.y, dist: (v1End.x - v1.x) ** 2 + (v1End.y - v1.y) ** 2 }
for (const intersection of intersections) {
const dist = (intersection.x - v1.x) ** 2 + (intersection.y - v1.y) ** 2;
if (dist < best.dist) best = { x: intersection.x, y: intersection.y, dist: dist }
}
best.dist = Math.sqrt(best.dist)
return best;
},
getIntersections(v1, v1End, domain) {
const intersections = [];
for (const obj of domain) {
for (var i = 0; i < obj.vertices.length - 1; i++) {
results = simulation.checkLineIntersection(v1, v1End, obj.vertices[i], obj.vertices[i + 1]);
if (results.onLine1 && results.onLine2) intersections.push({ x: results.x, y: results.y });
}
results = simulation.checkLineIntersection(v1, v1End, obj.vertices[obj.vertices.length - 1], obj.vertices[0]);
if (results.onLine1 && results.onLine2) intersections.push({ x: results.x, y: results.y });
}
return intersections;
},
circleLoS(pos, radius) {
function allCircleLineCollisions(c, radius, domain) {
var lines = [];
for (const obj of domain) {
for (var i = 0; i < obj.vertices.length - 1; i++) lines.push(circleLineCollisions(obj.vertices[i], obj.vertices[i + 1], c, radius));
lines.push(circleLineCollisions(obj.vertices[obj.vertices.length - 1], obj.vertices[0], c, radius));
}
const collisionLines = [];
for (const line of lines) {
if (line.length == 2) {
// const distance1 = Math.sqrt((line[0].x - c.x) ** 2 + (line[0].y - c.y) ** 2)
// const angle1 = Math.atan2(line[0].y - c.y, line[0].x - c.x);
// const queryPoint1 = {
// x: Math.cos(angle1) * (distance1 - 1) + c.x,
// y: Math.sin(angle1) * (distance1 - 1) + c.y
// }
// const distance2 = Math.sqrt((line[1].x - c.x) ** 2 + (line[1].y - c.y) ** 2)
// const angle2 = Math.atan2(line[1].y - c.y, line[1].x - c.x);
// const queryPoint2 = {
// x: Math.cos(angle2) * (distance2 - 1) + c.x,
// y: Math.sin(angle2) * (distance2 - 1) + c.y
// }
collisionLines.push(line)
}
}
return collisionLines;
}
function circleLineCollisions(a, b, c, radius) {
// calculate distances
const angleOffset = Math.atan2(b.y - a.y, b.x - a.x);
const sideB = Math.sqrt((a.x - c.x) ** 2 + (a.y - c.y) ** 2);
const sideC = Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2);
const sideA = Math.sqrt((c.x - b.x) ** 2 + (c.y - b.y) ** 2);
// calculate the closest point on line AB to point C
const angleA = Math.acos((sideB ** 2 + sideC ** 2 - sideA ** 2) / (2 * sideB * sideC)) * (a.x - c.x) / -Math.abs(a.x - c.x)
const sideAD = Math.cos(angleA) * sideB;
const d = { // closest point
x: Math.cos(angleOffset) * sideAD + a.x,
y: Math.sin(angleOffset) * sideAD + a.y
}
const distance = Math.sqrt((d.x - c.x) ** 2 + (d.y - c.y) ** 2);
if (distance == radius) {
// tangent
return [d];
} else if (distance < radius) {
// secant
const angleOffset = Math.atan2(d.y - c.y, d.x - c.x);
const innerAngle = Math.acos(distance / radius);
const intersection1 = {
x: Math.cos(angleOffset + innerAngle) * radius + c.x,
y: Math.sin(angleOffset + innerAngle) * radius + c.y
}
const intersection2 = {
x: Math.cos(angleOffset - innerAngle) * radius + c.x,
y: Math.sin(angleOffset - innerAngle) * radius + c.y
}
const distance1 = {
a: Math.sqrt((intersection1.x - a.x) ** 2 + (intersection1.y - a.y) ** 2),
b: Math.sqrt((intersection1.x - b.x) ** 2 + (intersection1.y - b.y) ** 2)
}
const distance2 = {
a: Math.sqrt((intersection2.x - a.x) ** 2 + (intersection2.y - a.y) ** 2),
b: Math.sqrt((intersection2.x - b.x) ** 2 + (intersection2.y - b.y) ** 2)
}
const result = [];
if (Math.abs(sideC - (distance1.a + distance1.b)) < 0.01) {
result.push(intersection1);
} else {
if (distance1.a < distance1.b) {
if (sideB <= radius) result.push(a);
} else {
if (sideA <= radius) result.push(b)
}
}
if (Math.abs(sideC - (distance2.a + distance2.b)) < 0.01) {
result.push(intersection2);
} else {
if (distance2.a <= distance2.b) {
if (sideB <= radius) result.push(a);
} else {
if (sideA <= radius) result.push(b)
}
}
return result;
} else {
// no intersection
return [];
}
}
var vertices = [];
for (const obj of simulation.sight.intersectMap) {
for (var i = 0; i < obj.vertices.length; i++) {
const vertex = obj.vertices[i];
const angleToVertex = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
// const distanceToVertex = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2);
// const queryPoint = { x: Math.cos(angleToVertex) * (distanceToVertex - 1) + pos.x, y: Math.sin(angleToVertex) * (distanceToVertex - 1) + pos.y }
const queryPoint = { x: Math.cos(angleToVertex + Math.PI) + vertex.x, y: Math.sin(angleToVertex + Math.PI) + vertex.y }
if (Matter.Query.ray(map, pos, queryPoint).length == 0) {
var distance = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2);
var endPoint = { x: vertex.x, y: vertex.y }
if (distance > radius) {
const angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
endPoint = { x: Math.cos(angle) * radius + pos.x, y: Math.sin(angle) * radius + pos.y }
distance = radius
}
var best = simulation.sight.getIntersection(pos, endPoint, map);
if (best.dist >= distance) best = { x: endPoint.x, y: endPoint.y, dist: distance }
vertices.push(best)
var angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
endPoint = { x: Math.cos(angle + 0.001) * radius + pos.x, y: Math.sin(angle + 0.001) * radius + pos.y }
best = simulation.sight.getIntersection(pos, endPoint, map);
if (best.dist >= radius) best = { x: endPoint.x, y: endPoint.y, dist: radius }
vertices.push(best)
angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
endPoint = { x: Math.cos(angle - 0.001) * radius + pos.x, y: Math.sin(angle - 0.001) * radius + pos.y }
best = simulation.sight.getIntersection(pos, endPoint, map);
if (best.dist >= radius) best = { x: endPoint.x, y: endPoint.y, dist: radius }
vertices.push(best)
}
}
}
const outerCollisions = allCircleLineCollisions(pos, radius, map);
const circleCollisions = [];
for (const line of outerCollisions) {
for (const vertex of line) {
// console.log('hi')
const distance = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2)
const angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
// const queryPoint = {
// x: Math.cos(angle) * (distance - 1) + pos.x,
// y: Math.sin(angle) * (distance - 1) + pos.y
// }
const queryPoint = { x: Math.cos(angle + Math.PI) + vertex.x, y: Math.sin(angle + Math.PI) + vertex.y }
if (Math.abs(distance - radius) < 1 && Matter.Query.ray(map, pos, queryPoint).length == 0) circleCollisions.push(vertex)
}
}
for (var i = 0; i < circleCollisions.length; i++) {
const vertex = circleCollisions[i];
var nextIndex = i + 1;
if (nextIndex == circleCollisions.length) nextIndex = 0;
const nextVertex = circleCollisions[nextIndex];
const angle1 = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
const angle2 = Math.atan2(nextVertex.y - pos.y, nextVertex.x - pos.x);
var newAngle;
if (Math.abs(angle1) > Math.PI / 2 && Math.abs(angle2) > Math.PI / 2 && angle1 / Math.abs(angle1) != angle2 / Math.abs(angle2)) {
// if the arc between the to points crosses over the left side (+/- pi radians)
const newAngle1 = (Math.PI - Math.abs(angle1)) * (angle1 / Math.abs(angle1));
const newAngle2 = (Math.PI - Math.abs(angle2)) * (angle2 / Math.abs(angle2));
newAngle = (newAngle1 + newAngle2) / 2;
var multiplier;
if (newAngle == 0) {
multiplier = 1;
} else {
multiplier = newAngle / Math.abs(newAngle);
}
newAngle = Math.PI * multiplier - newAngle * multiplier;
test = true;
} else {
newAngle = (angle1 + angle2) / 2;
}
// shoot ray between them
var endPoint = { x: Math.cos(newAngle) * radius + pos.x, y: Math.sin(newAngle) * radius + pos.y }
var best = simulation.sight.getIntersection(pos, endPoint, map);
vertices.push(vertex);
if (best.dist <= radius) vertices.push({ x: best.x, y: best.y })
}
vertices.sort((a, b) => Math.atan2(a.y - pos.y, a.x - pos.x) - Math.atan2(b.y - pos.y, b.x - pos.x));
return vertices;
},
},
draw: { draw: {
// powerUp() { //is set by Bayesian tech // powerUp() { //is set by Bayesian tech
// // ctx.globalAlpha = 0.4 * Math.sin(m.cycle * 0.15) + 0.6; // // ctx.globalAlpha = 0.4 * Math.sin(m.cycle * 0.15) + 0.6;
@@ -1590,9 +1596,6 @@ const simulation = {
} }
//store data for line of sight precalculation //store data for line of sight precalculation
simulation.sight.intersectMap = []; simulation.sight.intersectMap = [];
for (var i = 0; i < map.length; i++) { for (var i = 0; i < map.length; i++) {
@@ -1617,14 +1620,59 @@ const simulation = {
// } // }
simulation.sight.intersectMap.push({ vertices: newVertices }); simulation.sight.intersectMap.push({ vertices: newVertices });
} }
}, },
drawMapPath() { drawMapPath() { },
drawMapPathDefault() {
ctx.fillStyle = color.map; ctx.fillStyle = color.map;
ctx.fill(simulation.draw.mapPath); ctx.fill(simulation.draw.mapPath);
}, },
drawMapSight() {
if (!simulation.isTimeSkipping) {
const pos = m.pos
const radius = 4000
const vertices = simulation.sight.circleLoS(pos, radius);
if (vertices.length) {
ctx.beginPath();
ctx.moveTo(vertices[0].x, vertices[0].y);
for (var i = 1; i < vertices.length; i++) {
var currentDistance = Math.sqrt((vertices[i - 1].x - pos.x) ** 2 + (vertices[i - 1].y - pos.y) ** 2);
var newDistance = Math.sqrt((vertices[i].x - pos.x) ** 2 + (vertices[i].y - pos.y) ** 2);
if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) {
const currentAngle = Math.atan2(vertices[i - 1].y - pos.y, vertices[i - 1].x - pos.x);
const newAngle = Math.atan2(vertices[i].y - pos.y, vertices[i].x - pos.x);
ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle);
} else {
ctx.lineTo(vertices[i].x, vertices[i].y)
}
}
newDistance = Math.sqrt((vertices[0].x - pos.x) ** 2 + (vertices[0].y - pos.y) ** 2);
currentDistance = Math.sqrt((vertices[vertices.length - 1].x - pos.x) ** 2 + (vertices[vertices.length - 1].y - pos.y) ** 2);
if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) {
const currentAngle = Math.atan2(vertices[vertices.length - 1].y - pos.y, vertices[vertices.length - 1].x - pos.x);
const newAngle = Math.atan2(vertices[0].y - pos.y, vertices[0].x - pos.x);
ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle);
} else {
ctx.lineTo(vertices[0].x, vertices[0].y)
}
// outline map edges, best with lighter colored document.body.style.backgroundColor
ctx.strokeStyle = "#000";
ctx.lineWidth = 5;
ctx.stroke(simulation.draw.mapPath);
ctx.globalCompositeOperation = "destination-in";
ctx.fillStyle = "#000";
ctx.fill();
ctx.globalCompositeOperation = "source-over";
// make map visible
// ctx.fill(simulation.draw.mapPath);
// ctx.fillStyle = "#000";
ctx.clip(); //this doesn't seem to be required, but it helps with performance, probably stops the canvas context from drawing the whole map
}
}
},
body() { body() {
ctx.beginPath(); ctx.beginPath();
for (let i = 0, len = body.length; i < len; ++i) { for (let i = 0, len = body.length; i < len; ++i) {
@@ -1792,10 +1840,10 @@ const simulation = {
const y = round(simulation.constructMouseDownPosition.y) const y = round(simulation.constructMouseDownPosition.y)
const dx = Math.max(25, round(simulation.mouseInGame.x) - x) const dx = Math.max(25, round(simulation.mouseInGame.x) - x)
const dy = Math.max(25, round(simulation.mouseInGame.y) - y) const dy = Math.max(25, round(simulation.mouseInGame.y) - y)
console.log(e.button) // console.log(e.button)
if (e.button === 1) { if (e.button === 1) {
if (level.isProcedural) { if (level.isProcedural) {
simulation.outputMapString(`spawn.randomMob(x+${x}, y+${y}, 0);\n`); simulation.outputMapString(`spawn.randomMob(x+${x}, ${y}, 0);\n`);
} else { } else {
simulation.outputMapString(`spawn.randomMob(${x}, ${y}, 0);\n`); simulation.outputMapString(`spawn.randomMob(${x}, ${y}, 0);\n`);
} }
@@ -1804,7 +1852,7 @@ const simulation = {
} 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) { if (level.isProcedural) {
simulation.outputMapString(`spawn.mapRect(x+${x}, y+${y}, ${dx}, ${dy});\n`); simulation.outputMapString(`spawn.mapRect(x+${x}, ${y}, ${dx}, ${dy});\n`);
} else { } else {
simulation.outputMapString(`spawn.mapRect(${x}, ${y}, ${dx}, ${dy});\n`); simulation.outputMapString(`spawn.mapRect(${x}, ${y}, ${dx}, ${dy});\n`);
} }
@@ -1818,17 +1866,12 @@ const simulation = {
simulation.draw.setPaths() //update map graphics simulation.draw.setPaths() //update map graphics
} else if (e.button === 2) { //add body } else if (e.button === 2) { //add body
if (level.isProcedural) { if (level.isProcedural) {
simulation.outputMapString(`spawn.bodyRect(x+${x}, y+${y}, ${dx}, ${dy});\n`); simulation.outputMapString(`spawn.bodyRect(x+${x}, ${y}, ${dx}, ${dy});\n`);
} else { } else {
simulation.outputMapString(`spawn.bodyRect(${x}, ${y}, ${dx}, ${dy});\n`); simulation.outputMapString(`spawn.bodyRect(${x}, ${y}, ${dx}, ${dy});\n`);
} }
//see map in world //see map in world
spawn.bodyRect(x, y, dx, dy); spawn.bodyRect(x, y, dx, dy);
len = body.length - 1
body[len].collisionFilter.category = cat.body;
body[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
Composite.add(engine.world, body[len]); //add to world
body[len].classType = "body"
} }
} }
} }

View File

@@ -32,8 +32,7 @@ const spawn = {
"laser", "laser", "laser", "laser",
"pulsar", "pulsar", "pulsar", "pulsar",
"sneaker", "sneaker", "sneaker", "sneaker",
"launcher", "launcherOne", "exploder", "sucker", "sniper", "spinner", "grower", "beamer", "spawner", "ghoster", "launcher", "launcherOne", "exploder", "sucker", "sniper", "spinner", "grower", "beamer", "spawner", "ghoster", "focuser"
//, "focuser"
], ],
mobTypeSpawnOrder: [], //preset list of mob names calculated at the start of a run by the randomSeed mobTypeSpawnOrder: [], //preset list of mob names calculated at the start of a run by the randomSeed
mobTypeSpawnIndex: 0, //increases as the mob type cycles mobTypeSpawnIndex: 0, //increases as the mob type cycles
@@ -5883,9 +5882,10 @@ const spawn = {
mobs.spawn(x, y, 7, radius, "transparent"); mobs.spawn(x, y, 7, radius, "transparent");
let me = mob[mob.length - 1]; let me = mob[mob.length - 1];
me.seeAtDistance2 = 300000; me.seeAtDistance2 = 300000;
me.accelMag = 0.00015 * simulation.accelScale; me.accelMag = 0.00004 + 0.00015 * simulation.accelScale;
if (map.length) me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; //required for search if (map.length) me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; //required for search
Matter.Body.setDensity(me, 0.0015); //normal is 0.001 //makes effective life much lower // Matter.Body.setDensity(me, 0.0015); //normal is 0.001
me.damageReduction = 0.5
me.stroke = "transparent"; //used for drawGhost me.stroke = "transparent"; //used for drawGhost
me.alpha = 1; //used in drawGhost me.alpha = 1; //used in drawGhost
me.isNotCloaked = false; //used in drawGhost me.isNotCloaked = false; //used in drawGhost
@@ -5893,9 +5893,9 @@ const spawn = {
// me.leaveBody = false; // me.leaveBody = false;
me.collisionFilter.mask = cat.bullet //| cat.body me.collisionFilter.mask = cat.bullet //| cat.body
me.showHealthBar = false; me.showHealthBar = false;
me.memory = 480; me.memory = 600;
me.do = function () { me.do = function () {
//cap max speed //cap max speed to avoid getting launched by deflection, explosion
if (this.speed > 7) { if (this.speed > 7) {
Matter.Body.setVelocity(this, { Matter.Body.setVelocity(this, {
x: this.velocity.x * 0.8, x: this.velocity.x * 0.8,
@@ -5908,12 +5908,12 @@ const spawn = {
this.search(); this.search();
//draw //draw
if (this.distanceToPlayer2() < this.seeAtDistance2) { if (this.distanceToPlayer2() < this.seeAtDistance2) {
if (this.alpha < 1) this.alpha += 0.01 * simulation.CDScale; //near player go solid if (this.alpha < 1) this.alpha += 0.011 * simulation.CDScale; //near player go solid
} else { } else {
if (this.alpha > 0) this.alpha -= 0.05; ///away from player, hide if (this.alpha > 0) this.alpha -= 0.05; ///away from player, hide
} }
if (this.alpha > 0) { if (this.alpha > 0) {
if (this.alpha > 0.8 && this.seePlayer.recall) { if (this.alpha > 0.7 && this.seePlayer.recall) {
this.healthBar(); this.healthBar();
if (!this.isNotCloaked) { if (!this.isNotCloaked) {
this.isNotCloaked = true; this.isNotCloaked = true;
@@ -8165,14 +8165,29 @@ const spawn = {
} }
} }
}, },
bodyRect(x, y, width, height, chance = 1, properties = { // bodyRect(x, y, width, height, chance = 1, properties = { friction: 0.05, frictionAir: 0.001 }) {
friction: 0.05, // if (Math.random() < chance) body[body.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, properties);
frictionAir: 0.001, // },
}) { // bodyVertex(x, y, vector, properties) { //adds shape to body array
if (Math.random() < chance) body[body.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, properties); // body[body.length] = Matter.Bodies.fromVertices(x, y, Vertices.fromPath(vector), properties);
// },
bodyRect(x, y, width, height, chance = 1, properties = { friction: 0.05, frictionAir: 0.001 }) { //this is the command that adds blocks to the world in the middle of a level
if (Math.random() < chance) {
body[body.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, properties);
const who = body[body.length - 1]
who.collisionFilter.category = cat.body;
who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
Composite.add(engine.world, who); //add to world
who.classType = "body"
}
}, },
bodyVertex(x, y, vector, properties) { //adds shape to body array bodyVertex(x, y, vector, properties) { //this is the command that adds blocks to the world in the middle of a level
body[body.length] = Matter.Bodies.fromVertices(x, y, Vertices.fromPath(vector), properties); body[body.length] = Matter.Bodies.fromVertices(x, y, Vertices.fromPath(vector), properties);
const who = body[body.length - 1]
who.collisionFilter.category = cat.body;
who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
Composite.add(engine.world, who); //add to world
who.classType = "body"
}, },
mapRect(x, y, width, height, properties) { //adds rectangle to map array mapRect(x, y, width, height, properties) { //adds rectangle to map array
map[map.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, properties); map[map.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, properties);
@@ -8180,6 +8195,24 @@ const spawn = {
mapVertex(x, y, vector, properties) { //adds shape to map array mapVertex(x, y, vector, properties) { //adds shape to map array
map[map.length] = Matter.Bodies.fromVertices(x, y, Vertices.fromPath(vector), properties); map[map.length] = Matter.Bodies.fromVertices(x, y, Vertices.fromPath(vector), properties);
}, },
mapRectNow(x, y, width, height, properties, isRedrawMap = true) { //adds rectangle to map array in the middle of a level
map[map.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, properties);
const who = map[map.length - 1]
who.collisionFilter.category = cat.map;
who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
Matter.Body.setStatic(who, true); //make static
Composite.add(engine.world, who); //add to world
if (isRedrawMap) simulation.draw.setPaths()
},
mapVertexNow(x, y, vector, properties) { //adds shape to map array in the middle of a level
map[map.length] = Matter.Bodies.fromVertices(x, y, Vertices.fromPath(vector), properties);
const who = map[map.length - 1]
who.collisionFilter.category = cat.map;
who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
Matter.Body.setStatic(who, true); //make static
Composite.add(engine.world, who); //add to world
if (isRedrawMap) simulation.draw.setPaths() //this is a bit slow on processing so maybe it's better to run after you spawn several different shapes
},
//complex map templates //complex map templates
spawnBuilding(x, y, w, h, leftDoor, rightDoor, walledSide) { spawnBuilding(x, y, w, h, leftDoor, rightDoor, walledSide) {
this.mapRect(x, y, w, 25); //roof this.mapRect(x, y, w, 25); //roof

View File

@@ -33,6 +33,7 @@ const tech = {
tech.totalCount = 0; tech.totalCount = 0;
tech.junkCount = 0 //tech.countJunkTech(); tech.junkCount = 0 //tech.countJunkTech();
simulation.updateTechHUD(); simulation.updateTechHUD();
simulation.updateGunHUD();
}, },
removeTech(index = 'random') { removeTech(index = 'random') {
if (index === 'random') { if (index === 'random') {
@@ -335,7 +336,7 @@ const tech = {
} }
}, },
{ {
name: "elasticity", name: "nitinol",
description: "<strong>+33%</strong> <strong>movement</strong> and <strong>jumping</strong><br><strong>+30%</strong> <strong class='color-defense'>defense</strong>", description: "<strong>+33%</strong> <strong>movement</strong> and <strong>jumping</strong><br><strong>+30%</strong> <strong class='color-defense'>defense</strong>",
maxCount: 3, maxCount: 3,
count: 0, count: 0,
@@ -1288,7 +1289,7 @@ const tech = {
}, },
{ {
name: "reaction inhibitor", name: "reaction inhibitor",
description: "<strong>-12%</strong> maximum mob <strong>health</strong>", //<strong class='color-h'>health</strong> description: "<strong>-11%</strong> maximum mob <strong>health</strong>", //<strong class='color-h'>health</strong>
maxCount: 3, maxCount: 3,
count: 0, count: 0,
frequency: 1, frequency: 1,
@@ -3489,7 +3490,7 @@ const tech = {
for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech"); for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech");
}, },
remove() { remove() {
if (!this.count) tech.isDeterminism = false; tech.isDeterminism = false;
} }
}, },
{ {
@@ -8936,10 +8937,6 @@ const tech = {
y: 10 * (Math.random() - 0.5) y: 10 * (Math.random() - 0.5)
}); });
bodyBullet.isAboutToBeRemoved = true bodyBullet.isAboutToBeRemoved = true
bodyBullet.collisionFilter.category = cat.body;
bodyBullet.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
bodyBullet.classType = "body";
Composite.add(engine.world, bodyBullet); //add to world
setTimeout(() => { //remove block setTimeout(() => { //remove block
for (let i = 0; i < body.length; i++) { for (let i = 0; i < body.length; i++) {
if (body[i] === bodyBullet) { if (body[i] === bodyBullet) {
@@ -10095,7 +10092,7 @@ const tech = {
simulation.ephemera.push({ simulation.ephemera.push({
name: "LoS", count: 0, do() { name: "LoS", count: 0, do() {
const pos = m.pos const pos = m.pos
const radius = 5000 const radius = 3000
if (!simulation.isTimeSkipping) { if (!simulation.isTimeSkipping) {
const vertices = simulation.sight.circleLoS(pos, radius); const vertices = simulation.sight.circleLoS(pos, radius);
if (vertices.length) { if (vertices.length) {
@@ -10121,10 +10118,11 @@ const tech = {
} else { } else {
ctx.lineTo(vertices[0].x, vertices[0].y) ctx.lineTo(vertices[0].x, vertices[0].y)
} }
//stroke the map, so it looks different form the line of sight //stroke the map, so it looks different form the line of sight
ctx.strokeStyle = "#234"; ctx.strokeStyle = "#234";
ctx.lineWidth = 9; ctx.lineWidth = 9;
ctx.stroke(simulation.draw.mapPath); ctx.stroke(simulation.draw.mapPath); //this has a pretty large impact on performance, maybe 5% worse performance
ctx.globalCompositeOperation = "destination-in"; ctx.globalCompositeOperation = "destination-in";
ctx.fillStyle = "#000"; ctx.fillStyle = "#000";
@@ -10235,16 +10233,8 @@ const tech = {
for (let i = 0, len = 40; i < len; i++) { for (let i = 0, len = 40; i < len; i++) {
setTimeout(() => { setTimeout(() => {
m.energy -= 1 / len m.energy -= 1 / len
const index = body.length where = Vector.add(m.pos, { x: 400 * (Math.random() - 0.5), y: 400 * (Math.random() - 0.5) })
where = Vector.add(m.pos, {
x: 400 * (Math.random() - 0.5),
y: 400 * (Math.random() - 0.5)
})
spawn.bodyRect(where.x, where.y, Math.floor(15 + 100 * Math.random()), Math.floor(15 + 100 * Math.random())); spawn.bodyRect(where.x, where.y, Math.floor(15 + 100 * Math.random()), Math.floor(15 + 100 * Math.random()));
body[index].collisionFilter.category = cat.body;
body[index].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
body[index].classType = "body";
Composite.add(engine.world, body[index]); //add to world
}, i * 100); }, i * 100);
} }

View File

@@ -1,34 +1,65 @@
******************************************************** NEXT PATCH ************************************************** ******************************************************** NEXT PATCH **************************************************
JUNK tech: flatland - draw line of sight level: subway
credit to Cornbread for line of sight algorithm replaces gauntlet just before the final boss
a preview of future line of site content gauntlet moved to community map pool
try it out in console: tech.giveTech("flatland") subway todo:
add a few more stations
balance difficulty
find bugs
new images surface plasmons does 50% more damage
bug fixes elasticity renamed nitinol because I bought some nitinol wire and it's neat
entanglement power up no longer shows guns or fields you already have
disabled minimal HUD for training levels
reaction inhibitor 12->11% mob max health reduction
spawn.bodyRect() now can add blocks mid level without any extra code
I think I found all the bugs this causes, but let me know if any blocks added mid game aren't colliding
community map - clock update:
visual overhaul,live lighting, remove pendulum overlap, move with pendulum if you stand in it, debris, trap exit only opens after completing the fight, moving elements now freeze while using time dilation
*********************************************************** TODO ***************************************************** *********************************************************** TODO *****************************************************
LoS consider increasing the base player horizontal movement
performance maybe only increase ground movement, air control seems fine
calculate things that don't change when the map ctx is done
like the intersections? level: subway - a map that uses the train level element and line of sight graphics
rewrite code for infinite range stations
how to use this? station theme ideas:
JUNK tech? portals
custom level? teleport to far away rooms
give it a dark back ground for contrast? slime
game setting? map elements that alternate between positions
applications buttons and doors
level that is dark, and you can only see LoS boss
boss that you need to avoid, probably requires a custom level spawn at the exit station? or at a random station? or at the station before the exit?
boss is a source of light spawn on the station after enough mobs have been killed?
cloaking tech, or just replace cloaking effect type of boss?
explosion graphic might need to make a new boss designed for this map: los and stations
mobs area of effect damage small, quick, sneaky
maybe make a shared mob AoE damage function do random bosses work?
standing wave graphic I think
looks good with line of sight
background lighting for each room drawn in level.custom
no outdoors, no fall off the edge
slime
no small bumps
starting in a small room with a hole to the right with a short drop, like highrise or aerie
floating hexagons, like in reservoir, labs
ramping walls to jump over, like satellite
tech stubs should be a tech unlocked by skins
nitinol, tungsten?
maybe give another benefit?
defense?
make a shared variable for skin defense, since you can only have one skin
make a lemming that walks until it hits a wall and then turns around robotically
body or mob?
can't be killed?
Also another thing I made that could fit in-game: https://kgurchiek.github.io/universal-n-gon-loader/ Also another thing I made that could fit in-game: https://kgurchiek.github.io/universal-n-gon-loader/
by default it just plays a random version of n-gon downloaded from past github commits by default it just plays a random version of n-gon downloaded from past github commits
@@ -37,9 +68,6 @@ also you can play any version with https://kgurchiek.github.io/universal-n-gon-l
where setting "NUM" to 0 is the very first commit where setting "NUM" to 0 is the very first commit
here's the code if you want to check it out: https://github.com/kgurchiek/universal-n-gon-loader/blob/main/script.js here's the code if you want to check it out: https://github.com/kgurchiek/universal-n-gon-loader/blob/main/script.js
level - funicular
The system is characterized by two counterbalanced carriages (also called cars or trains) permanently attached to opposite ends of a haulage cable, which is looped over a pulley at the upper end of the track.[2][3] The result of such a configuration is that the two carriages move synchronously: as one ascends, the other descends at an equal speed.
missile bot and plasma bot don't get converted by bot upgrade tech? missile bot and plasma bot don't get converted by bot upgrade tech?
is this more confusing because it contradicts text? is this more confusing because it contradicts text?
@@ -66,6 +94,8 @@ rework quantum eraser
test bremsstrahlung damage test bremsstrahlung damage
and make sure it actually does more damage with the dot tech and make sure it actually does more damage with the dot tech
tech circular polarization - wave gun bullets move in a circle
tech: choose next map by name after exiting current map tech: choose next map by name after exiting current map
use modified tech selection code? use modified tech selection code?
this might be too much work without much reward this might be too much work without much reward