rot-js
Version:
A roguelike toolkit in JavaScript
98 lines (97 loc) • 3.39 kB
JavaScript
import FOV from "./fov.js";
/**
* @class Discrete shadowcasting algorithm. Obsoleted by Precise shadowcasting.
* @augments ROT.FOV
*/
export default class DiscreteShadowcasting extends FOV {
compute(x, y, R, callback) {
/* this place is always visible */
callback(x, y, 0, 1);
/* standing in a dark place. FIXME is this a good idea? */
if (!this._lightPasses(x, y)) {
return;
}
/* start and end angles */
let DATA = [];
let A, B, cx, cy, blocks;
/* analyze surrounding cells in concentric rings, starting from the center */
for (let r = 1; r <= R; r++) {
let neighbors = this._getCircle(x, y, r);
let angle = 360 / neighbors.length;
for (let i = 0; i < neighbors.length; i++) {
cx = neighbors[i][0];
cy = neighbors[i][1];
A = angle * (i - 0.5);
B = A + angle;
blocks = !this._lightPasses(cx, cy);
if (this._visibleCoords(Math.floor(A), Math.ceil(B), blocks, DATA)) {
callback(cx, cy, r, 1);
}
if (DATA.length == 2 && DATA[0] == 0 && DATA[1] == 360) {
return;
} /* cutoff? */
} /* for all cells in this ring */
} /* for all rings */
}
/**
* @param {int} A start angle
* @param {int} B end angle
* @param {bool} blocks Does current cell block visibility?
* @param {int[][]} DATA shadowed angle pairs
*/
_visibleCoords(A, B, blocks, DATA) {
if (A < 0) {
let v1 = this._visibleCoords(0, B, blocks, DATA);
let v2 = this._visibleCoords(360 + A, 360, blocks, DATA);
return v1 || v2;
}
let index = 0;
while (index < DATA.length && DATA[index] < A) {
index++;
}
if (index == DATA.length) { /* completely new shadow */
if (blocks) {
DATA.push(A, B);
}
return true;
}
let count = 0;
if (index % 2) { /* this shadow starts in an existing shadow, or within its ending boundary */
while (index < DATA.length && DATA[index] < B) {
index++;
count++;
}
if (count == 0) {
return false;
}
if (blocks) {
if (count % 2) {
DATA.splice(index - count, count, B);
}
else {
DATA.splice(index - count, count);
}
}
return true;
}
else { /* this shadow starts outside an existing shadow, or within a starting boundary */
while (index < DATA.length && DATA[index] < B) {
index++;
count++;
}
/* visible when outside an existing shadow, or when overlapping */
if (A == DATA[index - count] && count == 1) {
return false;
}
if (blocks) {
if (count % 2) {
DATA.splice(index - count, count, A);
}
else {
DATA.splice(index - count, count, A, B);
}
}
return true;
}
}
}