flatland
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")
new images
bug fixes
BIN
img/aerogel.webp
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 70 KiB |
@@ -6751,7 +6751,7 @@ const b = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "wave", //3
|
name: "wave", //3
|
||||||
description: "emit a <strong>wave packet</strong> of oscillating particles<br>that propagates through <strong>solids</strong>",
|
description: "emit <strong>wave packets</strong> that propagate through <strong>solids</strong><br>waves <strong class='color-s'>slow</strong> mobs", // of oscillating particles<br>
|
||||||
ammo: 0,
|
ammo: 0,
|
||||||
ammoPack: 115,
|
ammoPack: 115,
|
||||||
defaultAmmoPack: 115,
|
defaultAmmoPack: 115,
|
||||||
|
|||||||
3804
js/level.js
253
js/simulation.js
@@ -388,6 +388,213 @@ 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) {
|
||||||
@@ -398,23 +605,9 @@ const simulation = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (b.inventory.length > 0) {
|
|
||||||
// for (let i = 0, len = b.inventory.length; i < len; ++i) document.getElementById(b.inventory[i]).style.opacity = "0.3";
|
|
||||||
// // document.getElementById(b.activeGun).style.fontSize = "30px";
|
|
||||||
// if (document.getElementById(b.activeGun)) document.getElementById(b.activeGun).style.opacity = "1";
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
updateGunHUD() {
|
updateGunHUD() {
|
||||||
// for (let i = 0, len = b.inventory.length; i < len; ++i) {
|
|
||||||
// if (flashIndex === i) {
|
|
||||||
// document.getElementById(b.inventory[i]).innerHTML = b.guns[b.inventory[i]].name + " - " + b.guns[b.inventory[i]].ammo;
|
|
||||||
// } else {
|
|
||||||
// document.getElementById(b.inventory[i]).innerHTML = b.guns[b.inventory[i]].name + " - " + b.guns[b.inventory[i]].ammo;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
for (let i = 0, len = b.inventory.length; i < len; ++i) {
|
for (let i = 0, len = b.inventory.length; i < len; ++i) {
|
||||||
// document.getElementById(b.inventory[i]).innerHTML = b.guns[b.inventory[i]].name + " - " + b.guns[b.inventory[i]].ammo;
|
|
||||||
document.getElementById(b.inventory[i]).innerHTML = `${b.guns[b.inventory[i]].name} - ${b.guns[b.inventory[i]].ammo}`
|
document.getElementById(b.inventory[i]).innerHTML = `${b.guns[b.inventory[i]].name} - ${b.guns[b.inventory[i]].ammo}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1395,6 +1588,38 @@ const simulation = {
|
|||||||
}
|
}
|
||||||
simulation.draw.mapPath.lineTo(vertices[0].x, vertices[0].y);
|
simulation.draw.mapPath.lineTo(vertices[0].x, vertices[0].y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//store data for line of sight precalculation
|
||||||
|
simulation.sight.intersectMap = [];
|
||||||
|
for (var i = 0; i < map.length; i++) {
|
||||||
|
const obj = map[i];
|
||||||
|
const newVertices = [];
|
||||||
|
const restOfMap = [...map].slice(0, i).concat([...map].slice(i + 1))
|
||||||
|
for (var j = 0; j < obj.vertices.length - 1; j++) {
|
||||||
|
var intersections = simulation.sight.getIntersections(obj.vertices[j], obj.vertices[j + 1], restOfMap);
|
||||||
|
newVertices.push(obj.vertices[j]);
|
||||||
|
for (const vertex of intersections) newVertices.push({ x: vertex.x, y: vertex.y });
|
||||||
|
}
|
||||||
|
intersections = simulation.sight.getIntersections(obj.vertices[obj.vertices.length - 1], obj.vertices[0], restOfMap);
|
||||||
|
newVertices.push(obj.vertices[obj.vertices.length - 1]);
|
||||||
|
for (const vertex of intersections) newVertices.push({ x: vertex.x, y: vertex.y });
|
||||||
|
//draw the vertices as black circles for debugging
|
||||||
|
// for (const vertex of newVertices) {
|
||||||
|
// ctx.beginPath();
|
||||||
|
// ctx.moveTo(vertex.x, vertex.y);
|
||||||
|
// ctx.arc(vertex.x, vertex.y, 10, 0, 2 * Math.PI);
|
||||||
|
// ctx.fillStyle = '#000';
|
||||||
|
// ctx.fill()
|
||||||
|
// }
|
||||||
|
simulation.sight.intersectMap.push({ vertices: newVertices });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
drawMapPath() {
|
drawMapPath() {
|
||||||
ctx.fillStyle = color.map;
|
ctx.fillStyle = color.map;
|
||||||
|
|||||||
72
js/tech.js
@@ -523,7 +523,7 @@ const tech = {
|
|||||||
{
|
{
|
||||||
name: "causality bombs",
|
name: "causality bombs",
|
||||||
link: `<a target="_blank" href='https://en.wikipedia.org/wiki/Causality' class="link">causality bombs</a>`,
|
link: `<a target="_blank" href='https://en.wikipedia.org/wiki/Causality' class="link">causality bombs</a>`,
|
||||||
description: "when you <strong class='color-rewind'>rewind</strong> drop several <strong>grenades</strong><br>become <strong>invulnerable</strong> until they <strong class='color-e'>explode</strong>",
|
description: "when you <strong class='color-rewind'>rewind</strong> drop several <strong>grenades</strong>", //<br>become <strong>invulnerable</strong> until they <strong class='color-e'>explode</strong>
|
||||||
maxCount: 1,
|
maxCount: 1,
|
||||||
count: 0,
|
count: 0,
|
||||||
frequency: 2,
|
frequency: 2,
|
||||||
@@ -7257,9 +7257,9 @@ const tech = {
|
|||||||
frequency: 2,
|
frequency: 2,
|
||||||
frequencyDefault: 2,
|
frequencyDefault: 2,
|
||||||
allowed() {
|
allowed() {
|
||||||
return (m.fieldMode === 4 && tech.deflectEnergy === 0) || (m.fieldMode === 1 && tech.harmonics === 2)
|
return m.fieldMode === 1 && tech.harmonics === 2
|
||||||
},
|
},
|
||||||
requires: "molecular assembler, standing wave, not electric generator",
|
requires: "standing wave",
|
||||||
effect() {
|
effect() {
|
||||||
tech.isLaserField = true
|
tech.isLaserField = true
|
||||||
},
|
},
|
||||||
@@ -7836,9 +7836,9 @@ const tech = {
|
|||||||
frequency: 2,
|
frequency: 2,
|
||||||
frequencyDefault: 2,
|
frequencyDefault: 2,
|
||||||
allowed() {
|
allowed() {
|
||||||
return m.fieldMode === 4 && !tech.isLaserField
|
return m.fieldMode === 4
|
||||||
},
|
},
|
||||||
requires: "molecular assembler, not surface plasmon",
|
requires: "molecular assembler",
|
||||||
effect() {
|
effect() {
|
||||||
tech.deflectEnergy += 0.5;
|
tech.deflectEnergy += 0.5;
|
||||||
},
|
},
|
||||||
@@ -8360,7 +8360,7 @@ const tech = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "vacuum fluctuation",
|
name: "vacuum fluctuation",
|
||||||
description: `use ${powerUps.orb.research(3)}to exploit your <strong class='color-f'>field</strong> for a<br><strong>+11%</strong> chance to <strong class='color-dup'>duplicate</strong> spawned <strong>power ups</strong>`,
|
description: `use ${powerUps.orb.research(3)}<br><strong>+11%</strong> chance to <strong class='color-dup'>duplicate</strong> spawned <strong>power ups</strong>`,
|
||||||
isFieldTech: true,
|
isFieldTech: true,
|
||||||
maxCount: 1,
|
maxCount: 1,
|
||||||
count: 0,
|
count: 0,
|
||||||
@@ -10081,6 +10081,66 @@ const tech = {
|
|||||||
},
|
},
|
||||||
remove() { }
|
remove() { }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "flatland",
|
||||||
|
description: "map blocks line of sight",
|
||||||
|
maxCount: 1,
|
||||||
|
count: 0,
|
||||||
|
frequency: 0,
|
||||||
|
isNonRefundable: true,
|
||||||
|
isJunk: true,
|
||||||
|
allowed() { return true },
|
||||||
|
requires: "",
|
||||||
|
effect() {
|
||||||
|
simulation.ephemera.push({
|
||||||
|
name: "LoS", count: 0, do() {
|
||||||
|
const pos = m.pos
|
||||||
|
const radius = 5000
|
||||||
|
if (!simulation.isTimeSkipping) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
//stroke the map, so it looks different form the line of sight
|
||||||
|
ctx.strokeStyle = "#234";
|
||||||
|
ctx.lineWidth = 9;
|
||||||
|
ctx.stroke(simulation.draw.mapPath);
|
||||||
|
|
||||||
|
ctx.globalCompositeOperation = "destination-in";
|
||||||
|
ctx.fillStyle = "#000";
|
||||||
|
ctx.fill();
|
||||||
|
ctx.globalCompositeOperation = "source-over";
|
||||||
|
// also see the map
|
||||||
|
// ctx.fill(simulation.draw.mapPath);
|
||||||
|
// ctx.fillStyle = "#000";
|
||||||
|
ctx.clip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
remove() { }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "umbra",
|
name: "umbra",
|
||||||
description: "produce a blue glow around everything<br>and probably some simulation lag",
|
description: "produce a blue glow around everything<br>and probably some simulation lag",
|
||||||
|
|||||||
47
todo.txt
@@ -1,21 +1,48 @@
|
|||||||
******************************************************** NEXT PATCH **************************************************
|
******************************************************** NEXT PATCH **************************************************
|
||||||
|
|
||||||
factory: rewrote the end
|
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")
|
||||||
|
|
||||||
clock gating was removed because it's annoying
|
|
||||||
liquid cooling -> refrigerant - freezes mobs after losing at least 5% health
|
|
||||||
mass-energy gets more effect from defense (0.13 -> 0.19)
|
|
||||||
ternary 84 -> 77% damage
|
|
||||||
dark patterns 15 -> 17% damage and JUNK
|
|
||||||
Maxwell's demon 3% -> 1% energy loss above max
|
|
||||||
exciton 16 -> 14% chance to drop
|
|
||||||
|
|
||||||
10% increase in overall mob health
|
|
||||||
new images
|
new images
|
||||||
bug fixes
|
bug fixes
|
||||||
|
|
||||||
*********************************************************** TODO *****************************************************
|
*********************************************************** TODO *****************************************************
|
||||||
|
|
||||||
|
LoS
|
||||||
|
LoS clipping
|
||||||
|
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?
|
||||||
|
boss that you need to avoid, probably requires a custom level
|
||||||
|
boss is a source of light
|
||||||
|
level that is dark, and you can only see LoS
|
||||||
|
LoS with limited radius
|
||||||
|
explosion graphic
|
||||||
|
mobs area of effect damage
|
||||||
|
maybe make a shared mob AoE damage function
|
||||||
|
standing wave graphic
|
||||||
|
|
||||||
|
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
|
||||||
|
maybe the "snapshots" could work like this rather than downloading 8 versions of the game?
|
||||||
|
also you can play any version with https://kgurchiek.github.io/universal-n-gon-loader/?commitIndex=NUM
|
||||||
|
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?
|
||||||
|
|
||||||
use ephemera to replace things
|
use ephemera to replace things
|
||||||
JUNK?
|
JUNK?
|
||||||
request animation stuff
|
request animation stuff
|
||||||
|
|||||||