void Compute(int octant, Vector2Int origin, int rangeLimit, int x, Slope top, Slope bottom) { /*foreach (var tile in tilesToSet) * { * MapManager.map[tile.x, tile.y].isVisible = false; * } * tilesToSet.Clear();*/ for (; (int)x <= (int)rangeLimit; x++) // rangeLimit < 0 || x <= rangeLimit { int topY; if (top.X == 1) { topY = x; } else { topY = ((x * 2 - 1) * top.Y + top.X) / (top.X * 2); // get the tile that the top vector enters from the left int ay = (topY * 2 + 1) * top.X; if (BlocksLight(x, topY, octant, origin)) // if the top tile is a wall... { if (top.GreaterOrEqual(ay, x * 2)) { topY++; // but the top vector misses the wall and passes into the tile above, move up } } else // the top tile is not a wall { if (top.Greater(ay, x * 2 + 1)) { topY++; // so if the top vector passes into the tile above, move up } } } int bottomY = bottom.Y == 0 ? 0 : ((x * 2 - 1) * bottom.Y + bottom.X) / (bottom.X * 2); int wasOpaque = -1; // 0:false, 1:true, -1:not applicable for (int y = topY; y >= bottomY; y--) { int tx = origin.x, ty = origin.y; switch (octant) // translate local coordinates to map coordinates { case 0: tx += x; ty -= y; break; case 1: tx += y; ty -= x; break; case 2: tx -= y; ty -= x; break; case 3: tx -= x; ty -= y; break; case 4: tx -= x; ty += y; break; case 5: tx -= y; ty += x; break; case 6: tx += y; ty += x; break; case 7: tx += x; ty += y; break; } bool inRange = rangeLimit < 0 || GetDistance(tx, ty) <= rangeLimit; // NOTE: use the following line instead to make the algorithm symmetrical // if(inRange && (y != topY || top.GreaterOrEqual(y, x)) && (y != bottomY || bottom.LessOrEqual(y, x))) SetVisible(tx, ty); //if (inRange) SetVisible(tx, ty); if (inRange && (y != topY || top.GreaterOrEqual(y, x)) && (y != bottomY || bottom.LessOrEqual(y, x))) { SetVisible(tx, ty, true); tilesToSet.Add(new Vector2Int(tx, ty)); } //else SetVisible(tx, ty, false); bool isOpaque = !inRange || _blocksLight(tx, ty); // if y == topY or y == bottomY, make sure the sector actually intersects the wall tile. if not, don't consider // it opaque to prevent the code below from moving the top vector up or the bottom vector down if (isOpaque && (y == topY && top.LessOrEqual(y * 2 - 1, x * 2) && !BlocksLight(x, y - 1, octant, origin) || y == bottomY && bottom.GreaterOrEqual(y * 2 + 1, x * 2) && !BlocksLight(x, y + 1, octant, origin))) { isOpaque = false; } if (x != rangeLimit) { if (isOpaque) { if (wasOpaque == 0) // if we found a transition from clear to opaque, this sector is done in this column, so { // adjust the bottom vector upwards and continue processing it in the next column. // (x*2-1, y*2+1) is a vector to the top-left corner of the opaque block if (!inRange || y == bottomY) { bottom = new Slope(y * 2 + 1, x * 2); break; } // don't recurse unless necessary else { Compute(octant, origin, rangeLimit, x + 1, top, new Slope(y * 2 + 1, x * 2)); } } wasOpaque = 1; } else // adjust the top vector downwards and continue if we found a transition from opaque to clear { // (x*2+1, y*2+1) is the top-right corner of the clear tile (i.e. the bottom-right of the opaque tile) if (wasOpaque > 0) { top = new Slope(y * 2 + 1, x * 2); } wasOpaque = 0; } } } if (wasOpaque != 0) { break; // if the column ended in a clear tile, continue processing the current sector } } }