diff --git a/src/fov/discrete-shadowcasting.js b/src/fov/discrete-shadowcasting.js index 1d573c31..056ea943 100644 --- a/src/fov/discrete-shadowcasting.js +++ b/src/fov/discrete-shadowcasting.js @@ -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 */ @@ -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; } /** @@ -52,38 +55,38 @@ 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 */ @@ -91,18 +94,18 @@ ROT.FOV.DiscreteShadowcasting.prototype._visibleCoords = function(A, B, blocks, 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; } } diff --git a/src/fov/fov.js b/src/fov/fov.js index e663e972..8912ecb4 100644 --- a/src/fov/fov.js +++ b/src/fov/fov.js @@ -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 diff --git a/src/fov/precise-shadowcasting.js b/src/fov/precise-shadowcasting.js index 12005e0e..046abaff 100644 --- a/src/fov/precise-shadowcasting.js +++ b/src/fov/precise-shadowcasting.js @@ -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 */ @@ -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; } /** @@ -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) */ diff --git a/src/fov/recursive-shadowcasting.js b/src/fov/recursive-shadowcasting.js index e0aa6ec2..cc5d50da 100644 --- a/src/fov/recursive-shadowcasting.js +++ b/src/fov/recursive-shadowcasting.js @@ -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; } /** @@ -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); } /** @@ -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); } /** @@ -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); } /** @@ -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; @@ -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 { @@ -143,7 +144,7 @@ ROT.FOV.RecursiveShadowcasting.prototype._castVisibility = function(startX, star newStart = slopeEnd; continue; } - + //Block has ended blocked = false; visSlopeStart = newStart; @@ -151,4 +152,5 @@ ROT.FOV.RecursiveShadowcasting.prototype._castVisibility = function(startX, star } if(blocked) { break; } } + return accumulator; } diff --git a/src/path/astar.js b/src/path/astar.js index bab0a629..d47b1e27 100644 --- a/src/path/astar.js +++ b/src/path/astar.js @@ -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; @@ -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) { @@ -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