v0.6.3: significant refactoring and performance improvements to fog of war

This commit is contained in:
Evan Debenham 2017-12-26 16:46:44 -05:00
parent acc9fd33d2
commit c290e5fe47
6 changed files with 324 additions and 204 deletions

View File

@ -99,6 +99,15 @@ public class Rect {
return result;
}
public Rect union( Rect other ){
Rect result = new Rect();
result.left = Math.min( left, other.left );
result.right = Math.max( right, other.right );
result.top = Math.min( top, other.top );
result.bottom = Math.max( bottom, other.bottom );
return result;
}
public Rect union( int x, int y ) {
if (isEmpty()) {
return set( x, y, x + 1, y + 1 );

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 B

After

Width:  |  Height:  |  Size: 162 B

View File

@ -29,12 +29,14 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Light;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MindVision;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Blacksmith;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Ghost;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Imp;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Wandmaker;
import com.shatteredpixel.shatteredpixeldungeon.items.Ankh;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.DriedRose;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.Potion;
@ -744,30 +746,45 @@ public class Dungeon {
level.updateFieldOfView(hero, level.heroFOV);
if (hero.buff(MindVision.class) != null || hero.buff(Awareness.class) != null) {
BArray.or( level.visited, level.heroFOV, 0, level.heroFOV.length, level.visited );
GameScene.updateFog();
} else {
int cx = hero.pos % level.width();
int cy = hero.pos / level.width();
int ax = Math.max( 0, cx - dist );
int bx = Math.min( cx + dist, level.width() - 1 );
int ay = Math.max( 0, cy - dist );
int by = Math.min( cy + dist, level.height() - 1 );
int len = bx - ax + 1;
int pos = ax + ay * level.width();
for (int y = ay; y <= by; y++, pos+=level.width()) {
BArray.or( level.visited, level.heroFOV, pos, len, level.visited );
}
GameScene.updateFog(ax, ay, len, by-ay);
int x = hero.pos % level.width();
int y = hero.pos / level.width();
//left, right, top, bottom
int l = Math.max( 0, x - dist );
int r = Math.min( x + dist, level.width() - 1 );
int t = Math.max( 0, y - dist );
int b = Math.min( y + dist, level.height() - 1 );
int length = r - l + 1;
int width = t - b + 1;
int pos = l + t * level.width();
for (int i = t; i <= b; i++) {
BArray.or( level.visited, level.heroFOV, pos, length, level.visited );
pos+=level.width();
}
GameScene.updateFog(l, t, length, width);
if (hero.buff(MindVision.class) != null){
for (Mob m : level.mobs.toArray(new Mob[0])){
BArray.or( level.visited, level.heroFOV, m.pos - 1 - level.width(), 3, level.visited );
BArray.or( level.visited, level.heroFOV, m.pos, 3, level.visited );
BArray.or( level.visited, level.heroFOV, m.pos - 1 + level.width(), 3, level.visited );
//updates adjacent cells too
GameScene.updateFog(m.pos, 2);
}
}
if (hero.buff(Awareness.class) != null){
for (Heap h : level.heaps.values()){
BArray.or( level.visited, level.heroFOV, h.pos - 1 - level.width(), 3, level.visited );
BArray.or( level.visited, level.heroFOV, h.pos - 1, 3, level.visited );
BArray.or( level.visited, level.heroFOV, h.pos - 1 + level.width(), 3, level.visited );
GameScene.updateFog(h.pos, 2);
}
}
GameScene.afterObserve();
}

View File

@ -105,7 +105,6 @@ import com.watabou.noosa.audio.Music;
import com.watabou.noosa.audio.Sample;
import com.watabou.noosa.particles.Emitter;
import com.watabou.utils.GameMath;
import com.watabou.utils.PathFinder;
import com.watabou.utils.Random;
import java.io.IOException;
@ -777,7 +776,8 @@ public class GameScene extends PixelScene {
scene.visualGrid.updateMapCell( cell );
scene.terrainFeatures.updateMapCell( cell );
scene.walls.updateMapCell( cell );
updateFog( cell );
//update adjacent cells too
updateFog( cell, 1 );
}
}
@ -813,21 +813,11 @@ public class GameScene extends PixelScene {
scene.wallBlocking.updateArea(x, y, w, h);
}
}
public static void updateFog( int cell ){
public static void updateFog( int cell, int radius ){
if (scene != null) {
//update in a 3x3 grid to account for neighbours which might also be affected
if (Dungeon.level.insideMap(cell)) {
for (int i : PathFinder.NEIGHBOURS9) {
scene.fog.updateFogCell( cell + i );
scene.wallBlocking.updateMapCell( cell + i );
}
//unless we're at the level's edge, then just do the one tile.
} else {
scene.fog.updateFogCell( cell );
scene.wallBlocking.updateMapCell( cell );
}
scene.fog.updateFog( cell, radius );
scene.wallBlocking.updateArea( cell, radius );
}
}

View File

@ -36,6 +36,7 @@ import com.watabou.utils.Rect;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.ArrayList;
public class FogOfWar extends Image {
@ -77,11 +78,19 @@ public class FogOfWar extends Image {
private int width2;
private int height2;
private volatile Rect updated;
private Rect updating;
private volatile ArrayList<Rect> toUpdate;
private volatile ArrayList<Rect> updating;
//should be divisible by 2
private static final int PIX_PER_TILE = 2;
/*
TODO currently the center of each fox pixel is aligned with the inside of a cell
might be possible to create a better fog effect by aligning them with edges of a cell,
similar to the existing fog effect in vanilla (although probably with more precision)
the advantage here is that it may be possible to totally eliminate the tile blocking map
*/
public FogOfWar( int mapWidth, int mapHeight ) {
super();
@ -113,26 +122,47 @@ public class FogOfWar extends Image {
DungeonTilemap.SIZE / PIX_PER_TILE,
DungeonTilemap.SIZE / PIX_PER_TILE);
updated = new Rect(0, 0, mapWidth, mapHeight);
toUpdate = new ArrayList<>();
toUpdate.add(new Rect(0, 0, mapWidth, mapHeight));
}
public synchronized void updateFog(){
updated.set( 0, 0, mapWidth, mapHeight );
toUpdate.clear();
toUpdate.add(new Rect(0, 0, mapWidth, mapHeight));
}
public synchronized void updateFog(Rect update){
for (Rect r : toUpdate.toArray(new Rect[0])){
if (!r.intersect(update).isEmpty()){
toUpdate.remove(r);
toUpdate.add(r.union(update));
return;
}
}
toUpdate.add(update);
}
public synchronized void updateFogCell( int cell ){
updateFogArea( cell % mapWidth , cell / mapWidth, 1, 1 );
public synchronized void updateFog( int cell, int radius ){
Rect update = new Rect(
(cell % mapWidth) - radius,
(cell / mapWidth) - radius,
(cell % mapWidth) - radius + 1 + 2*radius,
(cell / mapWidth) - radius + 1 + 2*radius);
update.left = Math.max(0, update.left);
update.top = Math.max(0, update.top);
update.right = Math.min(mapWidth, update.right);
update.bottom = Math.min(mapHeight, update.bottom);
if (update.isEmpty()) return;
updateFog( update );
}
public synchronized void updateFogArea(int x, int y, int w, int h){
updated.union(x, y);
updated.union(x + w, y + h);
updated = updated.intersect( new Rect(0, 0, mapWidth, mapHeight) );
updateFog(new Rect(x, y, x + w, y + h));
}
public synchronized void moveToUpdating(){
updating = new Rect(updated);
updated.setEmpty();
private synchronized void moveToUpdating(){
updating = toUpdate;
toUpdate = new ArrayList<>();
}
private boolean[] visible;
@ -147,101 +177,116 @@ public class FogOfWar extends Image {
this.brightness = ShatteredPixelDungeon.brightness() + 2;
moveToUpdating();
boolean fullUpdate = updating.height() == mapHeight && updating.width() == mapWidth;
boolean fullUpdate = false;
if (updating.size() == 1){
Rect update = updating.get(0);
if (update.height() == mapHeight && update.width() == mapWidth){
fullUpdate = true;
}
}
FogTexture fog = (FogTexture)texture;
int cell;
int[] colorArray = new int[PIX_PER_TILE*PIX_PER_TILE];
for (int i=updating.top; i < updating.bottom; i++) {
cell = mapWidth * i + updating.left;
for (int j=updating.left; j < updating.right; j++) {
if (cell >= Dungeon.level.length()) continue; //do nothing
if (!Dungeon.level.discoverable[cell]
|| (!visible[cell] && !visited[cell] && !mapped[cell])){
//we skip filling cells here if it isn't a full update
// because they must already be dark
if (fullUpdate)
fillCell(j, i, FOG_COLORS[INVISIBLE][brightness]);
cell++;
continue;
}
//wall tiles
if (DungeonTileSheet.wallStitcheable(Dungeon.level.map[cell])){
//internal wall tiles
if (cell + mapWidth >= mapLength){
fillCell(j, i, FOG_COLORS[INVISIBLE][brightness]);
} else if (DungeonTileSheet.wallStitcheable(Dungeon.level.map[cell + mapWidth])){
//these tiles need to check both the left and right side, to account for only one half of them being seen
if (cell % mapWidth != 0){
//picks the darkest fog between current tile, left, and below-left(if left is a wall).
if (cell + mapWidth < mapLength && DungeonTileSheet.wallStitcheable(Dungeon.level.map[cell - 1])){
//if below-left is also a wall, then we should be dark no matter what.
if (DungeonTileSheet.wallStitcheable(Dungeon.level.map[cell + mapWidth - 1])){
colorArray[0] = colorArray[2] = FOG_COLORS[INVISIBLE][brightness];
} else {
colorArray[0] = colorArray[2] = FOG_COLORS[Math.max(getCellFog(cell), Math.max(getCellFog(cell + mapWidth - 1), getCellFog(cell - 1)))][brightness];
}
} else {
colorArray[0] = colorArray[2] = FOG_COLORS[Math.max(getCellFog(cell), getCellFog(cell - 1))][brightness];
}
} else {
colorArray[0] = colorArray [2] = FOG_COLORS[INVISIBLE][brightness];
}
if ((cell+1) % mapWidth != 0){
//picks the darkest fog between current tile, right, and below-right(if right is a wall).
if (cell + mapWidth < mapLength && DungeonTileSheet.wallStitcheable(Dungeon.level.map[cell + 1])){
//if below-right is also a wall, then we should be dark no matter what.
if (DungeonTileSheet.wallStitcheable(Dungeon.level.map[cell + mapWidth + 1])){
colorArray[1] = colorArray[3] = FOG_COLORS[INVISIBLE][brightness];
} else {
colorArray[1] = colorArray[3] = FOG_COLORS[Math.max(getCellFog(cell), Math.max(getCellFog(cell + mapWidth + 1), getCellFog(cell + 1)))][brightness];
}
} else {
colorArray[1] = colorArray[3] =
FOG_COLORS[Math.max(getCellFog(cell), getCellFog(cell + 1))][brightness];
}
} else {
colorArray[1] = colorArray [3] = FOG_COLORS[INVISIBLE][brightness];
}
fillCell(j, i, colorArray);
//camera-facing wall tiles
} else {
fillCell(j, i, FOG_COLORS[Math.max(getCellFog(cell), getCellFog(cell + mapWidth))][brightness]);
for (Rect update : updating) {
for (int i = update.top; i <= update.bottom; i++) {
cell = mapWidth * i + update.left;
for (int j = update.left; j <= update.right; j++) {
if (cell >= Dungeon.level.length()) continue; //do nothing
if (!Dungeon.level.discoverable[cell]
|| (!visible[cell] && !visited[cell] && !mapped[cell])) {
//we skip filling cells here if it isn't a full update
// because they must already be dark
if (fullUpdate)
fillCell(j, i, FOG_COLORS[INVISIBLE][brightness]);
cell++;
continue;
}
//other tiles
} else {
fillCell(j, i, FOG_COLORS[getCellFog(cell)][brightness]);
//wall tiles
if (wall(cell)) {
//always dark if nothing is beneath them
if (cell + mapWidth >= mapLength) {
fillCell(j, i, FOG_COLORS[INVISIBLE][brightness]);
//internal wall tiles, need to check both the left and right side,
// to account for only one half of them being seen
} else if (wall(cell + mapWidth)) {
//left side
if (cell % mapWidth != 0) {
//picks the darkest fog between current tile, left, and below-left(if left is a wall).
if (wall(cell - 1)) {
//if below-left is also a wall, then we should be dark no matter what.
if (wall(cell + mapWidth - 1)) {
fillLeft(j, i, FOG_COLORS[INVISIBLE][brightness]);
} else {
fillLeft(j, i, FOG_COLORS[Math.max(getCellFog(cell), Math.max(getCellFog(cell + mapWidth - 1), getCellFog(cell - 1)))][brightness]);
}
} else {
fillLeft(j, i, FOG_COLORS[Math.max(getCellFog(cell), getCellFog(cell - 1))][brightness]);
}
} else {
fillLeft(j, i, FOG_COLORS[INVISIBLE][brightness]);
}
//right side
if ((cell + 1) % mapWidth != 0) {
//picks the darkest fog between current tile, right, and below-right(if right is a wall).
if (wall(cell + 1)) {
//if below-right is also a wall, then we should be dark no matter what.
if (wall(cell + mapWidth + 1)) {
fillRight(j, i, FOG_COLORS[INVISIBLE][brightness]);
} else {
fillRight(j, i, FOG_COLORS[Math.max(getCellFog(cell), Math.max(getCellFog(cell + mapWidth + 1), getCellFog(cell + 1)))][brightness]);
}
} else {
fillRight(j, i, FOG_COLORS[Math.max(getCellFog(cell), getCellFog(cell + 1))][brightness]);
}
} else {
fillRight(j, i, FOG_COLORS[INVISIBLE][brightness]);
}
//camera-facing wall tiles
//darkest between themselves and the tile below them
} else {
fillCell(j, i, FOG_COLORS[Math.max(getCellFog(cell), getCellFog(cell + mapWidth))][brightness]);
}
//other tiles, just their direct value
} else {
fillCell(j, i, FOG_COLORS[getCellFog(cell)][brightness]);
}
cell++;
}
cell++;
}
}
if (updating.size() == 1 && !fullUpdate){
fog.update(updating.get(0).top * PIX_PER_TILE, updating.get(0).bottom * PIX_PER_TILE);
} else {
fog.update();
}
if (updating.width() == mapWidth && updating.height() == mapHeight)
fog.update();
else
fog.update(updating.top * PIX_PER_TILE, updating.bottom * PIX_PER_TILE);
}
private boolean wall(int cell) {
return DungeonTileSheet.wallStitcheable(Dungeon.level.map[cell]);
}
private int getCellFog( int cell ){
@ -256,13 +301,23 @@ public class FogOfWar extends Image {
return INVISIBLE;
}
}
private void fillCell( int x, int y, int[] colors){
private void fillLeft( int x, int y, int color){
FogTexture fog = (FogTexture)texture;
for (int i = 0; i < PIX_PER_TILE; i++){
fog.pixels.position(((y * PIX_PER_TILE)+i)*width2 + x * PIX_PER_TILE);
for (int j = 0; j < PIX_PER_TILE; j++) {
fog.pixels.put(colors[i*PIX_PER_TILE + j]);
for (int j = 0; j < PIX_PER_TILE/2; j++) {
fog.pixels.put(color);
}
}
}
private void fillRight( int x, int y, int color){
FogTexture fog = (FogTexture)texture;
for (int i = 0; i < PIX_PER_TILE; i++){
fog.pixels.position(((y * PIX_PER_TILE)+i)*width2 + x * PIX_PER_TILE + PIX_PER_TILE/2);
for (int j = PIX_PER_TILE/2; j < PIX_PER_TILE; j++) {
fog.pixels.put(color);
}
}
}
@ -356,9 +411,8 @@ public class FogOfWar extends Image {
@Override
public void draw() {
if (!updated.isEmpty()){
if (!toUpdate.isEmpty()){
updateTexture(Dungeon.level.heroFOV, Dungeon.level.visited, Dungeon.level.mapped);
updating.setEmpty();
}
super.draw();

View File

@ -30,11 +30,12 @@ public class WallBlockingTilemap extends Tilemap {
public static final int SIZE = 16;
private static final int CLEARED = -1;
private static final int BLOCK_NONE = 0;
private static final int BLOCK_RIGHT = 1;
private static final int BLOCK_LEFT = 2;
private static final int BLOCK_ALL = 3;
private static final int CLEARED = -2;
private static final int BLOCK_NONE = -1;
private static final int BLOCK_RIGHT = 0;
private static final int BLOCK_LEFT = 1;
private static final int BLOCK_ALL = 2;
private static final int BLOCK_BELOW = 3;
public WallBlockingTilemap() {
super("wall_blocking.png", new TextureFilm( "wall_blocking.png", SIZE, SIZE ) );
@ -45,109 +46,158 @@ public class WallBlockingTilemap extends Tilemap {
public synchronized void updateMap() {
super.updateMap();
data = new int[size]; //clears all values, including cleared tiles
for (int i = 0; i < data.length; i++)
updateMapCell(i);
for (int cell = 0; cell < data.length; cell++) {
//force all none-discoverable and border cells to cleared
if (!Dungeon.level.discoverable[cell] ||
!Dungeon.level.insideMap(cell)){
data[cell] = CLEARED;
} else {
updateMapCell(cell);
}
}
}
private int curr;
@Override
public synchronized void updateMapCell(int cell) {
int prev = data[cell];
int curr;
//TODO should doors be considered? currently the blocking is a bit permissive around doors
if (prev == CLEARED){
return;
//non-wall tiles
if (!wall(cell)) {
} else if (!Dungeon.level.discoverable[cell]) {
curr = CLEARED;
//handles blocking wall overhang (which is technically on a none wall tile)
} else if (!wall(cell)) {
if (!fogHidden(cell)) {
//clear empty floor tiles and cells which are visible
if (!fogHidden(cell) || !wall(cell + mapWidth)) {
curr = CLEARED;
} else if ( wall(cell + mapWidth) && !fogHidden(cell + mapWidth)
&& fogHidden(cell - 1) && fogHidden(cell + 1)) {
curr = BLOCK_ALL;
//block wall overhang if:
//- The cell below is a wall and visible
//- All of left, below-left, right, below-right is either a wall or hidden
} else if ( !fogHidden(cell + mapWidth)
&& (fogHidden(cell - 1) || wall(cell - 1))
&& (fogHidden(cell + 1) || wall(cell + 1))
&& (fogHidden(cell - 1 + mapWidth) || wall(cell - 1 + mapWidth))
&& (fogHidden(cell + 1 + mapWidth) || wall(cell + 1 + mapWidth))) {
curr = BLOCK_BELOW;
} else {
curr = BLOCK_NONE;
}
//wall tiles
} else {
if (fogHidden(cell - mapWidth) && fogHidden(cell) && fogHidden(cell + mapWidth)) {
curr = BLOCK_NONE;
//camera-facing wall tiles
if (!wall(cell + mapWidth)) {
//camera-facing wall tiles
} else if (!wall(cell + mapWidth)) {
if (!fogHidden(cell + mapWidth)){
//Block a camera-facing wall if:
//- the cell above, above-left, or above-right is not a wall, visible, and has a wall below
//- none of the remaining 5 neighbour cells are both not a wall and visible
//if all 3 above are wall we can shortcut and just clear the cell
if (wall(cell - 1 - mapWidth) && wall(cell - mapWidth) && wall(cell + 1 - mapWidth)){
curr = CLEARED;
} else if ((cell + 1) % mapWidth != 0 && !fogHidden(cell + 1)
&& !door(cell + 1) && !(wall(cell + 1) && wall(cell + 1 + mapWidth))){
curr = CLEARED;
} else if (cell % mapWidth != 0 && !fogHidden(cell - 1)
&& !door(cell - 1) && !(wall(cell - 1) && wall(cell - 1 + mapWidth))){
curr = CLEARED;
} else if ((!wall(cell - 1 - mapWidth) && !fogHidden(cell - 1 - mapWidth) && wall(cell - 1)) ||
(!wall(cell - mapWidth) && !fogHidden(cell - mapWidth)) ||
(!wall(cell + 1 - mapWidth) && !fogHidden(cell + 1 - mapWidth) && wall(cell+1))){
if ( !fogHidden( cell + mapWidth) ||
(!wall(cell - 1) && !fogHidden(cell - 1)) ||
(!wall(cell - 1 + mapWidth) && !fogHidden(cell - 1 + mapWidth)) ||
(!wall(cell + 1) && !fogHidden(cell + 1)) ||
(!wall(cell + 1 + mapWidth) && !fogHidden(cell + 1 + mapWidth))){
curr = CLEARED;
} else {
curr = BLOCK_ALL;
}
} else {
curr = BLOCK_ALL;
curr = BLOCK_NONE;
}
//internal wall tiles
//internal wall tiles
} else {
//Block the side of an internal wall if:
//- the cell above, below, or the cell itself is visible
//and all of the following are NOT true:
//- the top-side neighbour is visible and the side neighbour isn't a wall.
//- the side neighbour is both not a wall and visible
//- the bottom-side neighbour is both not a wall and visible
curr = BLOCK_NONE;
if ((cell + 1) % mapWidth != 0) {
if ((wall(cell + 1) || fogHidden(cell + 1 - mapWidth))
&& fogHidden(cell + 1)
&& (wall(cell + 1 + mapWidth) || fogHidden(cell + 1 + mapWidth))){
if (!fogHidden(cell - mapWidth)
|| !fogHidden(cell)
|| !fogHidden(cell + mapWidth)) {
//right side
if ((!wall(cell + 1) && !fogHidden(cell + 1 - mapWidth)) ||
(!wall(cell + 1) && !fogHidden(cell + 1)) ||
(!wall(cell + 1 + mapWidth) && !fogHidden(cell + 1 + mapWidth))
){
//do nothing
} else {
curr += 1;
}
}
if (cell % mapWidth != 0) {
if ((wall(cell - 1) || fogHidden(cell - 1 - mapWidth))
&& fogHidden(cell - 1)
&& (wall(cell - 1 + mapWidth) || fogHidden(cell - 1 + mapWidth))){
//left side
if ((!wall(cell - 1) && !fogHidden(cell - 1 - mapWidth)) ||
(!wall(cell - 1) && !fogHidden(cell - 1)) ||
(!wall(cell - 1 + mapWidth) && !fogHidden(cell - 1 + mapWidth))
){
//do nothing
} else {
curr += 2;
}
}
if (curr == BLOCK_NONE) {
curr = CLEARED;
if (curr == BLOCK_NONE) {
curr = CLEARED;
}
}
}
}
if (prev != curr){
if (data[cell] != curr){
data[cell] = curr;
super.updateMapCell(cell);
}
}
private boolean fogHidden(int cell){
if (cell < 0 || cell >= Dungeon.level.length()) return false;
if (!Dungeon.level.visited[cell] && !Dungeon.level.mapped[cell]) return true;
if (wall(cell) && cell + mapWidth < Dungeon.level.length() && !wall(cell + mapWidth) &&
!Dungeon.level.visited[cell + mapWidth] && !Dungeon.level.mapped[cell + mapWidth])
if (!Dungeon.level.visited[cell] && !Dungeon.level.mapped[cell]) {
return true;
} else if (wall(cell) && !wall(cell + mapWidth) &&
!Dungeon.level.visited[cell + mapWidth] && !Dungeon.level.mapped[cell + mapWidth]) {
return true;
}
return false;
}
//for the purposes of wall stitching, tiles below the map count as walls
private boolean wall(int cell) {
return cell >= 0 && (cell >= size || DungeonTileSheet.wallStitcheable(Dungeon.level.map[cell]));
return DungeonTileSheet.wallStitcheable(Dungeon.level.map[cell]);
}
private boolean door(int cell) {
return cell >= 0 && cell < size && DungeonTileSheet.doorTile(Dungeon.level.map[cell]);
return DungeonTileSheet.doorTile(Dungeon.level.map[cell]);
}
public synchronized void updateArea(int cell, int radius){
int l = cell%mapWidth - radius;
int t = cell/mapWidth - radius;
int r = cell%mapWidth + radius;
int b = cell/mapWidth + radius;
updateArea(
Math.max(0, l),
Math.max(0, t),
Math.min(mapWidth-1, r - l),
Math.min(mapHeight-1, b - t)
);
}
public synchronized void updateArea(int x, int y, int w, int h) {