Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 26 additions & 23 deletions src/fov/discrete-shadowcasting.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ ROT.FOV.DiscreteShadowcasting.extend(ROT.FOV);
/**
* @see ROT.FOV#compute
*/
ROT.FOV.DiscreteShadowcasting.prototype.compute = function(x, y, R, callback) {
ROT.FOV.DiscreteShadowcasting.prototype.compute = function(x, y, R, callback, initialValue) {
var center = this._coords;
var map = this._map;

/* this place is always visible */
callback(x, y, 0, 1);
var accumulator = callback(x, y, 0, 1, initalValue);

/* standing in a dark place. FIXME is this a good idea? */
if (!this._lightPasses(x, y)) { return; }
if (!this._lightPasses(x, y)) { return accumulator; }

/* start and end angles */
var DATA = [];

var A, B, cx, cy, blocks;

/* analyze surrounding cells in concentric rings, starting from the center */
Expand All @@ -35,14 +35,17 @@ ROT.FOV.DiscreteShadowcasting.prototype.compute = function(x, y, R, callback) {
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? */
if (this._visibleCoords(Math.floor(A), Math.ceil(B), blocks, DATA)) {
accumulator = callback(cx, cy, r, 1, accumulator);
}

if (DATA.length == 2 && DATA[0] == 0 && DATA[1] == 360) { return accumulator; } /* cutoff? */

} /* for all cells in this ring */
} /* for all rings */
return accumulator;
}

/**
Expand All @@ -52,57 +55,57 @@ ROT.FOV.DiscreteShadowcasting.prototype.compute = function(x, y, R, callback) {
* @param {int[][]} DATA shadowed angle pairs
*/
ROT.FOV.DiscreteShadowcasting.prototype._visibleCoords = function(A, B, blocks, DATA) {
if (A < 0) {
if (A < 0) {
var v1 = arguments.callee(0, B, blocks, DATA);
var v2 = arguments.callee(360+A, 360, blocks, DATA);
return v1 || v2;
}

var index = 0;
while (index < DATA.length && DATA[index] < A) { index++; }

if (index == DATA.length) { /* completely new shadow */
if (blocks) { DATA.push(A, B); }
if (blocks) { DATA.push(A, B); }
return true;
}

var 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 (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 (blocks) {
if (count % 2) {
DATA.splice(index-count, count, A);
} else {
DATA.splice(index-count, count, A, B);
}
}

return true;
}
}
2 changes: 1 addition & 1 deletion src/fov/fov.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ ROT.FOV = function(lightPassesCallback, options) {
* @param {int} R Maximum visibility radius
* @param {function} callback
*/
ROT.FOV.prototype.compute = function(x, y, R, callback) {}
ROT.FOV.prototype.compute = function(x, y, R, callback, initialValue) {}

/**
* Return all neighbors in a concentric ring
Expand Down
25 changes: 13 additions & 12 deletions src/fov/precise-shadowcasting.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ ROT.FOV.PreciseShadowcasting.extend(ROT.FOV);
/**
* @see ROT.FOV#compute
*/
ROT.FOV.PreciseShadowcasting.prototype.compute = function(x, y, R, callback) {
ROT.FOV.PreciseShadowcasting.prototype.compute = function(x, y, R, callback, initialValue) {
/* this place is always visible */
callback(x, y, 0, 1);
var accumulator = callback(x, y, 0, 1, initialValue);

/* standing in a dark place. FIXME is this a good idea? */
if (!this._lightPasses(x, y)) { return; }
if (!this._lightPasses(x, y)) { return accumulator; }

/* list of all shadows */
var SHADOWS = [];

var cx, cy, blocks, A1, A2, visibility;

/* analyze surrounding cells in concentric rings, starting from the center */
Expand All @@ -32,16 +32,17 @@ ROT.FOV.PreciseShadowcasting.prototype.compute = function(x, y, R, callback) {
cy = neighbors[i][1];
/* shift half-an-angle backwards to maintain consistency of 0-th cells */
A1 = [i ? 2*i-1 : 2*neighborCount-1, 2*neighborCount];
A2 = [2*i+1, 2*neighborCount];
A2 = [2*i+1, 2*neighborCount];

blocks = !this._lightPasses(cx, cy);
visibility = this._checkVisibility(A1, A2, blocks, SHADOWS);
if (visibility) { callback(cx, cy, r, visibility); }
if (visibility) { accumulator = callback(cx, cy, r, visibility, accumulator); }

if (SHADOWS.length == 2 && SHADOWS[0][0] == 0 && SHADOWS[1][0] == SHADOWS[1][1]) { return; } /* cutoff? */
if (SHADOWS.length == 2 && SHADOWS[0][0] == 0 && SHADOWS[1][0] == SHADOWS[1][1]) { return accumulator; } /* cutoff? */

} /* for all cells in this ring */
} /* for all rings */
return accumulator;
}

/**
Expand Down Expand Up @@ -82,15 +83,15 @@ ROT.FOV.PreciseShadowcasting.prototype._checkVisibility = function(A1, A2, block

var visible = true;
if (index1 == index2 && (edge1 || edge2)) { /* subset of existing shadow, one of the edges match */
visible = false;
visible = false;
} else if (edge1 && edge2 && index1+1==index2 && (index2 % 2)) { /* completely equivalent with existing shadow */
visible = false;
} else if (index1 > index2 && (index1 % 2)) { /* subset of existing shadow, not touching */
visible = false;
}

if (!visible) { return 0; } /* fast case: not visible */

var visibleLength, P;

/* compute the length of visible arc, adjust list of shadows (if blocking) */
Expand Down
58 changes: 30 additions & 28 deletions src/fov/recursive-shadowcasting.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ ROT.FOV.RecursiveShadowcasting.OCTANTS = [
* @param {int} R Maximum visibility radius
* @param {function} callback
*/
ROT.FOV.RecursiveShadowcasting.prototype.compute = function(x, y, R, callback) {
ROT.FOV.RecursiveShadowcasting.prototype.compute = function(x, y, R, callback, initialValue) {
//You can always see your own tile
callback(x, y, 0, 1);
var accumulator = callback(x, y, 0, 1, initialValue);
for(var i = 0; i < ROT.FOV.RecursiveShadowcasting.OCTANTS.length; i++) {
this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[i], R, callback);
accumulator = this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[i], R, callback, accumulator);
}
return accumulator;
}

/**
Expand All @@ -44,16 +45,16 @@ ROT.FOV.RecursiveShadowcasting.prototype.compute = function(x, y, R, callback) {
* @param {int} dir Direction to look in (expressed in a ROT.DIRS value);
* @param {function} callback
*/
ROT.FOV.RecursiveShadowcasting.prototype.compute180 = function(x, y, R, dir, callback) {
ROT.FOV.RecursiveShadowcasting.prototype.compute180 = function(x, y, R, dir, callback, initialValue) {
//You can always see your own tile
callback(x, y, 0, 1);
accumulator = callback(x, y, 0, 1, initialValue);
var previousOctant = (dir - 1 + 8) % 8; //Need to retrieve the previous octant to render a full 180 degrees
var nextPreviousOctant = (dir - 2 + 8) % 8; //Need to retrieve the previous two octants to render a full 180 degrees
var nextOctant = (dir+ 1 + 8) % 8; //Need to grab to next octant to render a full 180 degrees
this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[nextPreviousOctant], R, callback);
this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[previousOctant], R, callback);
this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[dir], R, callback);
this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[nextOctant], R, callback);
accumulator = this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[nextPreviousOctant], R, callback, accumulator);
accumulator = this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[previousOctant], R, callback, accumulator);
accumulator = this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[dir], R, callback, accumulator);
return this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[nextOctant], R, callback, accumulator);
}

/**
Expand All @@ -64,12 +65,12 @@ ROT.FOV.RecursiveShadowcasting.prototype.compute180 = function(x, y, R, dir, cal
* @param {int} dir Direction to look in (expressed in a ROT.DIRS value);
* @param {function} callback
*/
ROT.FOV.RecursiveShadowcasting.prototype.compute90 = function(x, y, R, dir, callback) {
ROT.FOV.RecursiveShadowcasting.prototype.compute90 = function(x, y, R, dir, callback, initialValue) {
//You can always see your own tile
callback(x, y, 0, 1);
var accumulator = callback(x, y, 0, 1, initialValue);
var previousOctant = (dir - 1 + 8) % 8; //Need to retrieve the previous octant to render a full 90 degrees
this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[dir], R, callback);
this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[previousOctant], R, callback);
accumulator = this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[dir], R, callback, accumulator);
return this._renderOctant(x, y, ROT.FOV.RecursiveShadowcasting.OCTANTS[previousOctant], R, callback, accumulator);
}

/**
Expand All @@ -80,9 +81,9 @@ ROT.FOV.RecursiveShadowcasting.prototype.compute90 = function(x, y, R, dir, call
* @param {int} R Maximum visibility radius
* @param {function} callback
*/
ROT.FOV.RecursiveShadowcasting.prototype._renderOctant = function(x, y, octant, R, callback) {
ROT.FOV.RecursiveShadowcasting.prototype._renderOctant = function(x, y, octant, R, callback, accumulator) {
//Radius incremented by 1 to provide same coverage area as other shadowcasting radiuses
this._castVisibility(x, y, 1, 1.0, 0.0, R + 1, octant[0], octant[1], octant[2], octant[3], callback);
return this._castVisibility(x, y, 1, 1.0, 0.0, R + 1, octant[0], octant[1], octant[2], octant[3], callback, accumulator);
}

/**
Expand All @@ -93,14 +94,14 @@ ROT.FOV.RecursiveShadowcasting.prototype._renderOctant = function(x, y, octant,
* @param {float} visSlopeStart The slope to start at
* @param {float} visSlopeEnd The slope to end at
* @param {int} radius The radius to reach out to
* @param {int} xx
* @param {int} xy
* @param {int} yx
* @param {int} yy
* @param {int} xx
* @param {int} xy
* @param {int} yx
* @param {int} yy
* @param {function} callback The callback to use when we hit a block that is visible
*/
ROT.FOV.RecursiveShadowcasting.prototype._castVisibility = function(startX, startY, row, visSlopeStart, visSlopeEnd, radius, xx, xy, yx, yy, callback) {
if(visSlopeStart < visSlopeEnd) { return; }
ROT.FOV.RecursiveShadowcasting.prototype._castVisibility = function(startX, startY, row, visSlopeStart, visSlopeEnd, radius, xx, xy, yx, yy, callback, accumulator) {
if(visSlopeStart < visSlopeEnd) { return accumulator; }
for(var i = row; i <= radius; i++) {
var dx = -i - 1;
var dy = -i;
Expand All @@ -118,23 +119,23 @@ ROT.FOV.RecursiveShadowcasting.prototype._castVisibility = function(startX, star
//Range of the row
var slopeStart = (dx - 0.5) / (dy + 0.5);
var slopeEnd = (dx + 0.5) / (dy - 0.5);

//Ignore if not yet at left edge of Octant
if(slopeEnd > visSlopeStart) { continue; }

//Done if past right edge
if(slopeStart < visSlopeEnd) { break; }

//If it's in range, it's visible
if((dx * dx + dy * dy) < (radius * radius)) {
callback(mapX, mapY, i, 1);
accumulator = callback(mapX, mapY, i, 1, accumulator);
}

if(!blocked) {
//If tile is a blocking tile, cast around it
if(!this._lightPasses(mapX, mapY) && i < radius) {
blocked = true;
this._castVisibility(startX, startY, i + 1, visSlopeStart, slopeStart, radius, xx, xy, yx, yy, callback);
accumulator = this._castVisibility(startX, startY, i + 1, visSlopeStart, slopeStart, radius, xx, xy, yx, yy, callback, accumulator);
newStart = slopeEnd;
}
} else {
Expand All @@ -143,12 +144,13 @@ ROT.FOV.RecursiveShadowcasting.prototype._castVisibility = function(startX, star
newStart = slopeEnd;
continue;
}

//Block has ended
blocked = false;
visSlopeStart = newStart;
}
}
if(blocked) { break; }
}
return accumulator;
}
22 changes: 12 additions & 10 deletions src/path/astar.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ ROT.Path.AStar.extend(ROT.Path);
* Compute a path from a given point
* @see ROT.Path#compute
*/
ROT.Path.AStar.prototype.compute = function(fromX, fromY, callback) {
ROT.Path.AStar.prototype.compute = function(fromX, fromY, callback, initialValue) {
this._todo = [];
this._done = {};
this._fromX = fromX;
Expand All @@ -35,17 +35,19 @@ ROT.Path.AStar.prototype.compute = function(fromX, fromY, callback) {
var y = neighbor[1];
var id = x+","+y;
if (id in this._done) { continue; }
this._add(x, y, item);
this._add(x, y, item);
}
}

var item = this._done[fromX+","+fromY];
if (!item) { return; }

if (!item) { return initialValue; }

var accumulator = initialValue;
while (item) {
callback(item.x, item.y);
accumulator = callback(item.x, item.y, accumulator);
item = item.prev;
}
return accumulator;
}

ROT.Path.AStar.prototype._add = function(x, y, prev) {
Expand All @@ -58,9 +60,9 @@ ROT.Path.AStar.prototype._add = function(x, y, prev) {
h: h
}
this._done[x+","+y] = obj;

/* insert into priority queue */

var f = obj.g + obj.h;
for (var i=0;i<this._todo.length;i++) {
var item = this._todo[i];
Expand All @@ -70,7 +72,7 @@ ROT.Path.AStar.prototype._add = function(x, y, prev) {
return;
}
}

this._todo.push(obj);
}

Expand All @@ -86,7 +88,7 @@ ROT.Path.AStar.prototype._distance = function(x, y) {
return dy + Math.max(0, (dx-dy)/2);
break;

case 8:
case 8:
return Math.max(Math.abs(x-this._fromX), Math.abs(y-this._fromY));
break;
}
Expand Down
Loading