subway
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:
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
17
js/index.js
17
js/index.js
@@ -303,7 +303,13 @@ const build = {
|
||||
// console.log(localSettings.isHideImages, from)
|
||||
},
|
||||
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
|
||||
document.getElementById("hide-hud").checked = localSettings.isHideHUD
|
||||
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");
|
||||
break
|
||||
case "6":
|
||||
const index = body.length
|
||||
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
|
||||
case "7":
|
||||
const pick = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)];
|
||||
@@ -1452,7 +1453,7 @@ document.body.addEventListener("wheel", (e) => {
|
||||
//**********************************************************************
|
||||
let localSettings
|
||||
|
||||
function localstorageCheck() {
|
||||
function localStorageCheck() {
|
||||
try {
|
||||
return 'localStorage' in window && window['localStorage'] !== null;
|
||||
} catch (e) {
|
||||
@@ -1460,7 +1461,7 @@ function localstorageCheck() {
|
||||
}
|
||||
|
||||
}
|
||||
if (localstorageCheck()) {
|
||||
if (localStorageCheck()) {
|
||||
localSettings = JSON.parse(localStorage.getItem("localSettings"))
|
||||
if (localSettings) {
|
||||
console.log('localStorage is enabled')
|
||||
|
||||
1162
js/level.js
1162
js/level.js
File diff suppressed because it is too large
Load Diff
@@ -239,7 +239,7 @@ const mobs = {
|
||||
deathCount: 0,
|
||||
mobSpawnWithHealth: 1,
|
||||
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);
|
||||
body[len2].collisionFilter.category = cat.body;
|
||||
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";
|
||||
Composite.add(engine.world, body[len2]); //add to world
|
||||
|
||||
|
||||
@@ -2282,13 +2282,13 @@ const m = {
|
||||
if (tech.isLaserField) {
|
||||
simulation.ephemera.push({
|
||||
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() {
|
||||
this.count--
|
||||
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
|
||||
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 `+${(couple * 5).toFixed(0)} maximum <strong class='color-f'>energy</strong>`
|
||||
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>`
|
||||
case 3: //negative mass
|
||||
return `<strong>+${(100 * (1 - 0.973 ** couple)).toFixed(1)}%</strong> <strong class='color-defense'>defense</strong>`
|
||||
|
||||
@@ -664,7 +664,7 @@ const powerUps = {
|
||||
researchText(type) {
|
||||
let text = ""
|
||||
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) {
|
||||
text += `<div onclick="powerUps.research.use('${type}')" class='research-card'>` // style = "margin-left: 192px; margin-right: -192px;"
|
||||
tech.junkResearchNumber = Math.ceil(4 * Math.random())
|
||||
@@ -686,7 +686,7 @@ const powerUps = {
|
||||
researchAndCancelText(type) {
|
||||
let text = `<div class='research-cancel'>`
|
||||
if (type === "entanglement") {
|
||||
text += `<span class='research-card entanglement flipX' style="width: 275px;"><span style="letter-spacing: 6px;">entanglement</span></span>` //‌
|
||||
text += `<span class='research-card entanglement flipX' style="width: 275px;" onclick='powerUps.endDraft("${type}",true)'><span style="letter-spacing: 6px;">entanglement</span></span>` //‌
|
||||
} 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;"
|
||||
tech.junkResearchNumber = Math.ceil(4 * Math.random())
|
||||
@@ -1140,7 +1140,7 @@ const powerUps = {
|
||||
const cycle = (timestamp) => {
|
||||
// if (timeStart === undefined) timeStart = timestamp
|
||||
// console.log(timestamp, timeStart)
|
||||
if (timestamp - timeStart > tech.brainStormDelay * count) {
|
||||
if (timestamp - timeStart > tech.brainStormDelay * count && simulation.isChoosing) {
|
||||
count++
|
||||
powerUps.tech.effect();
|
||||
document.getElementById("choose-grid").style.pointerEvents = "auto"; //turn off the normal 500ms delay
|
||||
@@ -1201,18 +1201,19 @@ const powerUps = {
|
||||
// text += "<div></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')
|
||||
if (localSettings.entanglement.fieldIndex) {
|
||||
if (localSettings.entanglement.fieldIndex && localSettings.entanglement.fieldIndex !== m.fieldMode) {
|
||||
const choose = localSettings.entanglement.fieldIndex //add field
|
||||
text += powerUps.fieldText(choose, `powerUps.choose('field',${choose})`)
|
||||
}
|
||||
for (let i = 0; i < localSettings.entanglement.gunIndexes.length; i++) { //add guns
|
||||
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> ${b.guns[gun].name}</div> ${b.guns[gun].description}</div>`
|
||||
text += powerUps.gunText(choose, `powerUps.choose('gun',${choose})`)
|
||||
|
||||
//consider not adding guns the player currently has?
|
||||
|
||||
|
||||
if (!alreadyHasGun) text += powerUps.gunText(choose, `powerUps.choose('gun',${choose})`)
|
||||
}
|
||||
for (let i = 0; i < localSettings.entanglement.techIndexes.length; i++) { //add tech
|
||||
let choose = localSettings.entanglement.techIndexes[i]
|
||||
|
||||
489
js/simulation.js
489
js/simulation.js
@@ -388,213 +388,6 @@ const simulation = {
|
||||
}
|
||||
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() {
|
||||
if (b.inventory.length > 0) {
|
||||
for (let i = 0, len = b.inventory.length; i < len; ++i) {
|
||||
@@ -1287,6 +1080,7 @@ const simulation = {
|
||||
map: "#444",
|
||||
bullet: "#000"
|
||||
}
|
||||
simulation.draw.drawMapPath = simulation.draw.drawMapPathDefault
|
||||
m.fireCDcycle = 0
|
||||
m.drop();
|
||||
m.hole.isOn = false;
|
||||
@@ -1507,6 +1301,218 @@ const simulation = {
|
||||
ctx.textAlign = "center";
|
||||
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: {
|
||||
// powerUp() { //is set by Bayesian tech
|
||||
// // 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
|
||||
simulation.sight.intersectMap = [];
|
||||
for (var i = 0; i < map.length; i++) {
|
||||
@@ -1617,14 +1620,59 @@ const simulation = {
|
||||
// }
|
||||
simulation.sight.intersectMap.push({ vertices: newVertices });
|
||||
}
|
||||
|
||||
|
||||
|
||||
},
|
||||
drawMapPath() {
|
||||
drawMapPath() { },
|
||||
drawMapPathDefault() {
|
||||
ctx.fillStyle = color.map;
|
||||
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() {
|
||||
ctx.beginPath();
|
||||
for (let i = 0, len = body.length; i < len; ++i) {
|
||||
@@ -1792,10 +1840,10 @@ const simulation = {
|
||||
const y = round(simulation.constructMouseDownPosition.y)
|
||||
const dx = Math.max(25, round(simulation.mouseInGame.x) - x)
|
||||
const dy = Math.max(25, round(simulation.mouseInGame.y) - y)
|
||||
console.log(e.button)
|
||||
// console.log(e.button)
|
||||
if (e.button === 1) {
|
||||
if (level.isProcedural) {
|
||||
simulation.outputMapString(`spawn.randomMob(x+${x}, y+${y}, 0);\n`);
|
||||
simulation.outputMapString(`spawn.randomMob(x+${x}, ${y}, 0);\n`);
|
||||
} else {
|
||||
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
|
||||
if (e.button === 0) { //add map
|
||||
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 {
|
||||
simulation.outputMapString(`spawn.mapRect(${x}, ${y}, ${dx}, ${dy});\n`);
|
||||
}
|
||||
@@ -1818,17 +1866,12 @@ const simulation = {
|
||||
simulation.draw.setPaths() //update map graphics
|
||||
} else if (e.button === 2) { //add body
|
||||
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 {
|
||||
simulation.outputMapString(`spawn.bodyRect(${x}, ${y}, ${dx}, ${dy});\n`);
|
||||
}
|
||||
//see map in world
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
61
js/spawn.js
61
js/spawn.js
@@ -32,8 +32,7 @@ const spawn = {
|
||||
"laser", "laser",
|
||||
"pulsar", "pulsar",
|
||||
"sneaker", "sneaker",
|
||||
"launcher", "launcherOne", "exploder", "sucker", "sniper", "spinner", "grower", "beamer", "spawner", "ghoster",
|
||||
//, "focuser"
|
||||
"launcher", "launcherOne", "exploder", "sucker", "sniper", "spinner", "grower", "beamer", "spawner", "ghoster", "focuser"
|
||||
],
|
||||
mobTypeSpawnOrder: [], //preset list of mob names calculated at the start of a run by the randomSeed
|
||||
mobTypeSpawnIndex: 0, //increases as the mob type cycles
|
||||
@@ -5883,9 +5882,10 @@ const spawn = {
|
||||
mobs.spawn(x, y, 7, radius, "transparent");
|
||||
let me = mob[mob.length - 1];
|
||||
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
|
||||
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.alpha = 1; //used in drawGhost
|
||||
me.isNotCloaked = false; //used in drawGhost
|
||||
@@ -5893,9 +5893,9 @@ const spawn = {
|
||||
// me.leaveBody = false;
|
||||
me.collisionFilter.mask = cat.bullet //| cat.body
|
||||
me.showHealthBar = false;
|
||||
me.memory = 480;
|
||||
me.memory = 600;
|
||||
me.do = function () {
|
||||
//cap max speed
|
||||
//cap max speed to avoid getting launched by deflection, explosion
|
||||
if (this.speed > 7) {
|
||||
Matter.Body.setVelocity(this, {
|
||||
x: this.velocity.x * 0.8,
|
||||
@@ -5908,12 +5908,12 @@ const spawn = {
|
||||
this.search();
|
||||
//draw
|
||||
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 {
|
||||
if (this.alpha > 0) this.alpha -= 0.05; ///away from player, hide
|
||||
}
|
||||
if (this.alpha > 0) {
|
||||
if (this.alpha > 0.8 && this.seePlayer.recall) {
|
||||
if (this.alpha > 0.7 && this.seePlayer.recall) {
|
||||
this.healthBar();
|
||||
if (!this.isNotCloaked) {
|
||||
this.isNotCloaked = true;
|
||||
@@ -8165,14 +8165,29 @@ const spawn = {
|
||||
}
|
||||
}
|
||||
},
|
||||
bodyRect(x, y, width, height, chance = 1, properties = {
|
||||
friction: 0.05,
|
||||
frictionAir: 0.001,
|
||||
}) {
|
||||
if (Math.random() < chance) body[body.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, properties);
|
||||
// bodyRect(x, y, width, height, chance = 1, properties = { friction: 0.05, frictionAir: 0.001 }) {
|
||||
// if (Math.random() < chance) body[body.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, properties);
|
||||
// },
|
||||
// bodyVertex(x, y, vector, properties) { //adds shape to body array
|
||||
// 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);
|
||||
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
|
||||
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
|
||||
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
|
||||
spawnBuilding(x, y, w, h, leftDoor, rightDoor, walledSide) {
|
||||
this.mapRect(x, y, w, 25); //roof
|
||||
|
||||
26
js/tech.js
26
js/tech.js
@@ -33,6 +33,7 @@ const tech = {
|
||||
tech.totalCount = 0;
|
||||
tech.junkCount = 0 //tech.countJunkTech();
|
||||
simulation.updateTechHUD();
|
||||
simulation.updateGunHUD();
|
||||
},
|
||||
removeTech(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>",
|
||||
maxCount: 3,
|
||||
count: 0,
|
||||
@@ -1288,7 +1289,7 @@ const tech = {
|
||||
},
|
||||
{
|
||||
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,
|
||||
count: 0,
|
||||
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");
|
||||
},
|
||||
remove() {
|
||||
if (!this.count) tech.isDeterminism = false;
|
||||
tech.isDeterminism = false;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -8936,10 +8937,6 @@ const tech = {
|
||||
y: 10 * (Math.random() - 0.5)
|
||||
});
|
||||
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
|
||||
for (let i = 0; i < body.length; i++) {
|
||||
if (body[i] === bodyBullet) {
|
||||
@@ -10095,7 +10092,7 @@ const tech = {
|
||||
simulation.ephemera.push({
|
||||
name: "LoS", count: 0, do() {
|
||||
const pos = m.pos
|
||||
const radius = 5000
|
||||
const radius = 3000
|
||||
if (!simulation.isTimeSkipping) {
|
||||
const vertices = simulation.sight.circleLoS(pos, radius);
|
||||
if (vertices.length) {
|
||||
@@ -10121,10 +10118,11 @@ const tech = {
|
||||
} else {
|
||||
ctx.lineTo(vertices[0].x, vertices[0].y)
|
||||
}
|
||||
|
||||
//stroke the map, so it looks different form the line of sight
|
||||
ctx.strokeStyle = "#234";
|
||||
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.fillStyle = "#000";
|
||||
@@ -10235,16 +10233,8 @@ const tech = {
|
||||
for (let i = 0, len = 40; i < len; i++) {
|
||||
setTimeout(() => {
|
||||
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()));
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
86
todo.txt
86
todo.txt
@@ -1,34 +1,65 @@
|
||||
******************************************************** NEXT PATCH **************************************************
|
||||
|
||||
JUNK tech: flatland - draw line of sight
|
||||
credit to Cornbread for line of sight algorithm
|
||||
a preview of future line of site content
|
||||
try it out in console: tech.giveTech("flatland")
|
||||
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
|
||||
|
||||
new images
|
||||
bug fixes
|
||||
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
|
||||
|
||||
*********************************************************** TODO *****************************************************
|
||||
|
||||
LoS
|
||||
performance
|
||||
calculate things that don't change when the map ctx is done
|
||||
like the intersections?
|
||||
rewrite code for infinite range
|
||||
how to use this?
|
||||
JUNK tech?
|
||||
custom level?
|
||||
give it a dark back ground for contrast?
|
||||
game setting?
|
||||
applications
|
||||
level that is dark, and you can only see LoS
|
||||
boss that you need to avoid, probably requires a custom level
|
||||
boss is a source of light
|
||||
cloaking tech, or just replace cloaking effect
|
||||
explosion graphic
|
||||
mobs area of effect damage
|
||||
maybe make a shared mob AoE damage function
|
||||
standing wave graphic
|
||||
consider increasing the base player horizontal movement
|
||||
maybe only increase ground movement, air control seems fine
|
||||
|
||||
level: subway - a map that uses the train level element and line of sight graphics
|
||||
stations
|
||||
station theme ideas:
|
||||
portals
|
||||
teleport to far away rooms
|
||||
slime
|
||||
map elements that alternate between positions
|
||||
buttons and doors
|
||||
boss
|
||||
spawn at the exit station? or at a random station? or at the station before the exit?
|
||||
spawn on the station after enough mobs have been killed?
|
||||
type of boss?
|
||||
might need to make a new boss designed for this map: los and stations
|
||||
small, quick, sneaky
|
||||
do random bosses work?
|
||||
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/
|
||||
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
|
||||
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?
|
||||
is this more confusing because it contradicts text?
|
||||
|
||||
@@ -66,6 +94,8 @@ rework quantum eraser
|
||||
test bremsstrahlung damage
|
||||
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
|
||||
use modified tech selection code?
|
||||
this might be too much work without much reward
|
||||
|
||||
Reference in New Issue
Block a user