You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

861 lines
21 KiB

/**
* @file lighting.cpp
*
* Implementation of light and vision.
*/
#include "lighting.h"
#include <algorithm>
#include "automap.h"
#include "diablo.h"
#include "engine/load_file.hpp"
#include "player.h"
namespace devilution {
Light VisionList[MAXVISION];
int VisionCount;
int VisionId;
Light Lights[MAXLIGHTS];
uint8_t ActiveLights[MAXLIGHTS];
int ActiveLightCount;
std::array<uint8_t, LIGHTSIZE> LightTables;
bool DisableLighting;
bool UpdateLighting;
/*
* X- Y-coordinate offsets of lighting visions.
* The last entry-pair is only for alignment.
*/
const uint8_t VisionCrawlTable[23][30] = {
// clang-format off
{ 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0 },
{ 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 1, 9, 1, 10, 1, 11, 1, 12, 1, 13, 1, 14, 1, 15, 1 },
{ 1, 0, 2, 0, 3, 0, 4, 1, 5, 1, 6, 1, 7, 1, 8, 1, 9, 1, 10, 1, 11, 1, 12, 2, 13, 2, 14, 2, 15, 2 },
{ 1, 0, 2, 0, 3, 1, 4, 1, 5, 1, 6, 1, 7, 1, 8, 2, 9, 2, 10, 2, 11, 2, 12, 2, 13, 3, 14, 3, 15, 3 },
{ 1, 0, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 7, 2, 8, 2, 9, 3, 10, 3, 11, 3, 12, 3, 13, 4, 14, 4, 0, 0 },
{ 1, 0, 2, 1, 3, 1, 4, 1, 5, 2, 6, 2, 7, 3, 8, 3, 9, 3, 10, 4, 11, 4, 12, 4, 13, 5, 14, 5, 0, 0 },
{ 1, 0, 2, 1, 3, 1, 4, 2, 5, 2, 6, 3, 7, 3, 8, 3, 9, 4, 10, 4, 11, 5, 12, 5, 13, 6, 14, 6, 0, 0 },
{ 1, 1, 2, 1, 3, 2, 4, 2, 5, 3, 6, 3, 7, 4, 8, 4, 9, 5, 10, 5, 11, 6, 12, 6, 13, 7, 0, 0, 0, 0 },
{ 1, 1, 2, 1, 3, 2, 4, 2, 5, 3, 6, 4, 7, 4, 8, 5, 9, 6, 10, 6, 11, 7, 12, 7, 12, 8, 13, 8, 0, 0 },
{ 1, 1, 2, 2, 3, 2, 4, 3, 5, 4, 6, 5, 7, 5, 8, 6, 9, 7, 10, 7, 10, 8, 11, 8, 12, 9, 0, 0, 0, 0 },
{ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 5, 7, 6, 8, 7, 9, 8, 10, 9, 11, 9, 11, 10, 0, 0, 0, 0, 0, 0 },
{ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 9, 11, 10, 11, 0, 0, 0, 0, 0, 0 },
{ 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 5, 7, 6, 8, 7, 9, 7, 10, 8, 10, 8, 11, 9, 12, 0, 0, 0, 0 },
{ 1, 1, 1, 2, 2, 3, 2, 4, 3, 5, 4, 6, 4, 7, 5, 8, 6, 9, 6, 10, 7, 11, 7, 12, 8, 12, 8, 13, 0, 0 },
{ 1, 1, 1, 2, 2, 3, 2, 4, 3, 5, 3, 6, 4, 7, 4, 8, 5, 9, 5, 10, 6, 11, 6, 12, 7, 13, 0, 0, 0, 0 },
{ 0, 1, 1, 2, 1, 3, 2, 4, 2, 5, 3, 6, 3, 7, 3, 8, 4, 9, 4, 10, 5, 11, 5, 12, 6, 13, 6, 14, 0, 0 },
{ 0, 1, 1, 2, 1, 3, 1, 4, 2, 5, 2, 6, 3, 7, 3, 8, 3, 9, 4, 10, 4, 11, 4, 12, 5, 13, 5, 14, 0, 0 },
{ 0, 1, 1, 2, 1, 3, 1, 4, 1, 5, 2, 6, 2, 7, 2, 8, 3, 9, 3, 10, 3, 11, 3, 12, 4, 13, 4, 14, 0, 0 },
{ 0, 1, 0, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, 2, 8, 2, 9, 2, 10, 2, 11, 2, 12, 3, 13, 3, 14, 3, 15 },
{ 0, 1, 0, 2, 0, 3, 1, 4, 1, 5, 1, 6, 1, 7, 1, 8, 1, 9, 1, 10, 1, 11, 2, 12, 2, 13, 2, 14, 2, 15 },
{ 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 1, 8, 1, 9, 1, 10, 1, 11, 1, 12, 1, 13, 1, 14, 1, 15 },
{ 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15 },
// clang-format on
};
namespace {
uint8_t lightradius[16][128];
bool dovision;
uint8_t lightblock[64][16][16];
/** RadiusAdj maps from VisionCrawlTable index to lighting vision radius adjustment. */
const BYTE RadiusAdj[23] = { 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 4, 3, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0 };
void RotateRadius(int *x, int *y, int *dx, int *dy, int *lx, int *ly, int *bx, int *by)
{
*bx = 0;
*by = 0;
int swap = *dx;
*dx = 7 - *dy;
*dy = swap;
swap = *lx;
*lx = 7 - *ly;
*ly = swap;
*x = *dx - *lx;
*y = *dy - *ly;
if (*x < 0) {
*x += 8;
*bx = 1;
}
if (*y < 0) {
*y += 8;
*by = 1;
}
}
void SetLight(Point position, char v)
{
if (LoadingMapObjects)
dPreLight[position.x][position.y] = v;
else
dLight[position.x][position.y] = v;
}
char GetLight(Point position)
{
if (LoadingMapObjects)
return dPreLight[position.x][position.y];
return dLight[position.x][position.y];
}
void DoUnLight(int nXPos, int nYPos, int nRadius)
{
nRadius++;
int minX = nXPos - nRadius;
int maxX = nXPos + nRadius;
int minY = nYPos - nRadius;
int maxY = nYPos + nRadius;
minX = std::max(minX, 0);
maxX = std::max(maxX, MAXDUNX);
minY = std::max(minY, 0);
maxY = std::max(maxY, MAXDUNY);
for (int y = minY; y < maxY; y++) {
for (int x = minX; x < maxX; x++) {
if (InDungeonBounds({ x, y }))
dLight[x][y] = dPreLight[x][y];
}
}
}
bool CrawlFlipsX(Displacement mirrored, tl::function_ref<bool(Displacement)> function)
{
for (const Displacement displacement : { mirrored.flipX(), mirrored }) {
if (!function(displacement))
return false;
}
return true;
}
bool CrawlFlipsY(Displacement mirrored, tl::function_ref<bool(Displacement)> function)
{
for (const Displacement displacement : { mirrored, mirrored.flipY() }) {
if (!function(displacement))
return false;
}
return true;
}
bool CrawlFlipsXY(Displacement mirrored, tl::function_ref<bool(Displacement)> function)
{
for (const Displacement displacement : { mirrored.flipX(), mirrored, mirrored.flipXY(), mirrored.flipY() }) {
if (!function(displacement))
return false;
}
return true;
}
} // namespace
bool DoCrawl(unsigned radius, tl::function_ref<bool(Displacement)> function)
{
if (radius == 0)
return function(Displacement { 0, 0 });
if (!CrawlFlipsY({ 0, static_cast<int>(radius) }, function))
return false;
for (unsigned i = 1; i < radius; i++) {
if (!CrawlFlipsXY({ static_cast<int>(i), static_cast<int>(radius) }, function))
return false;
}
if (radius > 1) {
if (!CrawlFlipsXY({ static_cast<int>(radius) - 1, static_cast<int>(radius) - 1 }, function))
return false;
}
if (!CrawlFlipsX({ static_cast<int>(radius), 0 }, function))
return false;
for (unsigned i = 1; i < radius; i++) {
if (!CrawlFlipsXY({ static_cast<int>(radius), static_cast<int>(i) }, function))
return false;
}
return true;
}
bool DoCrawl(unsigned minRadius, unsigned maxRadius, tl::function_ref<bool(Displacement)> function)
{
for (unsigned i = minRadius; i <= maxRadius; i++) {
if (!DoCrawl(i, function))
return false;
}
return true;
}
void DoLighting(Point position, int nRadius, int lnum)
{
int xoff = 0;
int yoff = 0;
int lightX = 0;
int lightY = 0;
int blockX = 0;
int blockY = 0;
if (lnum >= 0) {
Light &light = Lights[lnum];
xoff = light.position.offset.deltaX;
yoff = light.position.offset.deltaY;
if (xoff < 0) {
xoff += 8;
position -= { 1, 0 };
}
if (yoff < 0) {
yoff += 8;
position -= { 0, 1 };
}
}
int distX = xoff;
int distY = yoff;
int minX = 15;
if (position.x - 15 < 0) {
minX = position.x + 1;
}
int maxX = 15;
if (position.x + 15 > MAXDUNX) {
maxX = MAXDUNX - position.x;
}
int minY = 15;
if (position.y - 15 < 0) {
minY = position.y + 1;
}
int maxY = 15;
if (position.y + 15 > MAXDUNY) {
maxY = MAXDUNY - position.y;
}
if (InDungeonBounds(position)) {
if (IsNoneOf(leveltype, DTYPE_NEST, DTYPE_CRYPT)) {
SetLight(position, 0);
} else if (GetLight(position) > lightradius[nRadius][0]) {
SetLight(position, lightradius[nRadius][0]);
}
}
for (int i = 0; i < 4; i++) {
int mult = xoff + 8 * yoff;
int yBound = i > 0 && i < 3 ? maxY : minY;
int xBound = i < 2 ? maxX : minX;
for (int y = 0; y < yBound; y++) {
for (int x = 1; x < xBound; x++) {
int radiusBlock = lightblock[mult][y + blockY][x + blockX];
if (radiusBlock >= 128)
continue;
Point temp = position + (Displacement { x, y }).Rotate(-i);
int8_t v = lightradius[nRadius][radiusBlock];
if (!InDungeonBounds(temp))
continue;
if (v < GetLight(temp))
SetLight(temp, v);
}
}
RotateRadius(&xoff, &yoff, &distX, &distY, &lightX, &lightY, &blockX, &blockY);
}
}
void DoUnVision(Point position, int nRadius)
{
nRadius++;
nRadius++; // increasing the radius even further here prevents leaving stray vision tiles behind and doesn't seem to affect monster AI - applying new vision happens in the same tick
int x1 = std::max(position.x - nRadius, 0);
int y1 = std::max(position.y - nRadius, 0);
int x2 = std::min(position.x + nRadius, MAXDUNX);
int y2 = std::min(position.y + nRadius, MAXDUNY);
for (int i = x1; i < x2; i++) {
for (int j = y1; j < y2; j++) {
dFlags[i][j] &= ~(DungeonFlag::Visible | DungeonFlag::Lit);
}
}
}
void DoVision(Point position, int nRadius, MapExplorationType doautomap, bool visible)
{
if (InDungeonBounds(position)) {
if (doautomap != MAP_EXP_NONE) {
if (dFlags[position.x][position.y] != DungeonFlag::None) {
SetAutomapView(position, doautomap);
}
dFlags[position.x][position.y] |= DungeonFlag::Explored;
}
if (visible) {
dFlags[position.x][position.y] |= DungeonFlag::Lit;
}
dFlags[position.x][position.y] |= DungeonFlag::Visible;
}
for (int v = 0; v < 4; v++) {
for (int j = 0; j < 23; j++) {
bool nBlockerFlag = false;
int nLineLen = 2 * (nRadius - RadiusAdj[j]);
for (int k = 0; k < nLineLen && !nBlockerFlag; k += 2) {
int x1adj = 0;
int x2adj = 0;
int y1adj = 0;
int y2adj = 0;
int nCrawlX = 0;
int nCrawlY = 0;
switch (v) {
case 0:
nCrawlX = position.x + VisionCrawlTable[j][k];
nCrawlY = position.y + VisionCrawlTable[j][k + 1];
if (VisionCrawlTable[j][k] > 0 && VisionCrawlTable[j][k + 1] > 0) {
x1adj = -1;
y2adj = -1;
}
break;
case 1:
nCrawlX = position.x - VisionCrawlTable[j][k];
nCrawlY = position.y - VisionCrawlTable[j][k + 1];
if (VisionCrawlTable[j][k] > 0 && VisionCrawlTable[j][k + 1] > 0) {
y1adj = 1;
x2adj = 1;
}
break;
case 2:
nCrawlX = position.x + VisionCrawlTable[j][k];
nCrawlY = position.y - VisionCrawlTable[j][k + 1];
if (VisionCrawlTable[j][k] > 0 && VisionCrawlTable[j][k + 1] > 0) {
x1adj = -1;
y2adj = 1;
}
break;
case 3:
nCrawlX = position.x - VisionCrawlTable[j][k];
nCrawlY = position.y + VisionCrawlTable[j][k + 1];
if (VisionCrawlTable[j][k] > 0 && VisionCrawlTable[j][k + 1] > 0) {
y1adj = -1;
x2adj = 1;
}
break;
}
if (InDungeonBounds({ nCrawlX, nCrawlY })) {
nBlockerFlag = TileHasAny(dPiece[nCrawlX][nCrawlY], TileProperties::BlockLight);
if ((InDungeonBounds({ x1adj + nCrawlX, y1adj + nCrawlY })
&& !TileHasAny(dPiece[x1adj + nCrawlX][y1adj + nCrawlY], TileProperties::BlockLight))
|| (InDungeonBounds({ x2adj + nCrawlX, y2adj + nCrawlY })
&& !TileHasAny(dPiece[x2adj + nCrawlX][y2adj + nCrawlY], TileProperties::BlockLight))) {
if (doautomap != MAP_EXP_NONE) {
if (dFlags[nCrawlX][nCrawlY] != DungeonFlag::None) {
SetAutomapView({ nCrawlX, nCrawlY }, doautomap);
}
dFlags[nCrawlX][nCrawlY] |= DungeonFlag::Explored;
}
if (visible) {
dFlags[nCrawlX][nCrawlY] |= DungeonFlag::Lit;
}
dFlags[nCrawlX][nCrawlY] |= DungeonFlag::Visible;
if (!nBlockerFlag) {
int8_t nTrans = dTransVal[nCrawlX][nCrawlY];
if (nTrans != 0) {
TransList[nTrans] = true;
}
}
}
}
}
}
}
}
void MakeLightTable()
{
uint8_t *tbl = LightTables.data();
int shade = 0;
int lights = 15;
for (int i = 0; i < lights; i++) {
*tbl++ = 0;
for (int j = 0; j < 8; j++) {
uint8_t col = 16 * j + shade;
uint8_t max = 16 * j + 15;
for (int k = 0; k < 16; k++) {
if (k != 0 || j != 0) {
*tbl++ = col;
}
if (col < max) {
col++;
} else {
max = 0;
col = 0;
}
}
}
for (int j = 16; j < 20; j++) {
uint8_t col = 8 * j + (shade >> 1);
uint8_t max = 8 * j + 7;
for (int k = 0; k < 8; k++) {
*tbl++ = col;
if (col < max) {
col++;
} else {
max = 0;
col = 0;
}
}
}
for (int j = 10; j < 16; j++) {
uint8_t col = 16 * j + shade;
uint8_t max = 16 * j + 15;
for (int k = 0; k < 16; k++) {
*tbl++ = col;
if (col < max) {
col++;
} else {
max = 0;
col = 0;
}
if (col == 255) {
max = 0;
col = 0;
}
}
}
shade++;
}
for (int i = 0; i < 256; i++) {
*tbl++ = 0;
}
if (leveltype == DTYPE_HELL) {
BYTE blood[16];
tbl = LightTables.data();
for (int i = 0; i < lights; i++) {
int l1 = lights - i;
int l2 = l1;
int div = lights / l1;
int rem = lights % l1;
int cnt = 0;
blood[0] = 0;
uint8_t col = 1;
for (int j = 1; j < 16; j++) {
blood[j] = col;
l2 += rem;
if (l2 > l1 && j < 15) {
j++;
blood[j] = col;
l2 -= l1;
}
cnt++;
if (cnt == div) {
col++;
cnt = 0;
}
}
*tbl++ = 0;
for (int j = 1; j <= 15; j++) {
*tbl++ = blood[j];
}
for (int j = 15; j > 0; j--) {
*tbl++ = blood[j];
}
*tbl++ = 1;
tbl += 224;
}
*tbl++ = 0;
for (int j = 0; j < 31; j++) {
*tbl++ = 1;
}
tbl += 224;
}
if (IsAnyOf(leveltype, DTYPE_NEST, DTYPE_CRYPT)) {
tbl = LightTables.data();
for (int i = 0; i < lights; i++) {
*tbl++ = 0;
for (int j = 1; j < 16; j++)
*tbl++ = j;
tbl += 240;
}
*tbl++ = 0;
for (int j = 1; j < 16; j++)
*tbl++ = 1;
tbl += 240;
}
LoadFileInMem("PlrGFX\\Infra.TRN", tbl, 256);
tbl += 256;
LoadFileInMem("PlrGFX\\Stone.TRN", tbl, 256);
tbl += 256;
for (int i = 0; i < 8; i++) {
for (uint8_t col = 226; col < 239; col++) {
if (i != 0 || col != 226) {
*tbl++ = col;
} else {
*tbl++ = 0;
}
}
*tbl++ = 0;
*tbl++ = 0;
*tbl++ = 0;
}
for (int i = 0; i < 4; i++) {
uint8_t col = 224;
for (int j = 224; j < 239; j += 2) {
*tbl++ = col;
col += 2;
}
}
for (int i = 0; i < 6; i++) {
for (uint8_t col = 224; col < 239; col++) {
*tbl++ = col;
}
*tbl++ = 0;
}
for (int j = 0; j < 16; j++) {
for (int i = 0; i < 128; i++) {
if (i > (j + 1) * 8) {
lightradius[j][i] = 15;
} else {
double fs = (double)15 * i / ((double)8 * (j + 1));
lightradius[j][i] = (BYTE)(fs + 0.5);
}
}
}
if (IsAnyOf(leveltype, DTYPE_NEST, DTYPE_CRYPT)) {
for (int j = 0; j < 16; j++) {
double fa = (sqrt((double)(16 - j))) / 128;
fa *= fa;
for (int i = 0; i < 128; i++) {
lightradius[15 - j][i] = 15 - (BYTE)(fa * (double)((128 - i) * (128 - i)));
if (lightradius[15 - j][i] > 15)
lightradius[15 - j][i] = 0;
lightradius[15 - j][i] = lightradius[15 - j][i] - (BYTE)((15 - j) / 2);
if (lightradius[15 - j][i] > 15)
lightradius[15 - j][i] = 0;
}
}
}
for (int j = 0; j < 8; j++) {
for (int i = 0; i < 8; i++) {
for (int k = 0; k < 16; k++) {
for (int l = 0; l < 16; l++) {
int a = (8 * l - j);
int b = (8 * k - i);
lightblock[j * 8 + i][k][l] = static_cast<uint8_t>(sqrt(a * a + b * b));
}
}
}
}
}
#ifdef _DEBUG
void ToggleLighting()
{
DisableLighting = !DisableLighting;
if (DisableLighting) {
memset(dLight, 0, sizeof(dLight));
return;
}
memcpy(dLight, dPreLight, sizeof(dLight));
for (const Player &player : Players) {
if (player.plractive && player.isOnActiveLevel()) {
DoLighting(player.position.tile, player._pLightRad, -1);
}
}
}
#endif
void InitLighting()
{
ActiveLightCount = 0;
UpdateLighting = false;
DisableLighting = false;
for (int i = 0; i < MAXLIGHTS; i++) {
ActiveLights[i] = i;
}
}
int AddLight(Point position, int r)
{
int lid;
if (DisableLighting) {
return NO_LIGHT;
}
lid = NO_LIGHT;
if (ActiveLightCount < MAXLIGHTS) {
lid = ActiveLights[ActiveLightCount++];
Light &light = Lights[lid];
light.position.tile = position;
light._lradius = r;
light.position.offset = { 0, 0 };
light._ldel = false;
light._lunflag = false;
UpdateLighting = true;
}
return lid;
}
void AddUnLight(int i)
{
if (DisableLighting || i == NO_LIGHT) {
return;
}
Lights[i]._ldel = true;
UpdateLighting = true;
}
void ChangeLightRadius(int i, int r)
{
if (DisableLighting || i == NO_LIGHT) {
return;
}
Light &light = Lights[i];
light._lunflag = true;
light.position.old = light.position.tile;
light.oldRadius = light._lradius;
light._lradius = r;
UpdateLighting = true;
}
void ChangeLightXY(int i, Point position)
{
if (DisableLighting || i == NO_LIGHT) {
return;
}
Light &light = Lights[i];
light._lunflag = true;
light.position.old = light.position.tile;
light.oldRadius = light._lradius;
light.position.tile = position;
UpdateLighting = true;
}
void ChangeLightOffset(int i, Displacement offset)
{
if (DisableLighting || i == NO_LIGHT) {
return;
}
Light &light = Lights[i];
light._lunflag = true;
light.position.old = light.position.tile;
light.oldRadius = light._lradius;
light.position.offset = offset;
UpdateLighting = true;
}
void ChangeLight(int i, Point position, int r)
{
if (DisableLighting || i == NO_LIGHT) {
return;
}
Light &light = Lights[i];
light._lunflag = true;
light.position.old = light.position.tile;
light.oldRadius = light._lradius;
light.position.tile = position;
light._lradius = r;
UpdateLighting = true;
}
void ProcessLightList()
{
if (DisableLighting) {
return;
}
if (UpdateLighting) {
for (int i = 0; i < ActiveLightCount; i++) {
Light &light = Lights[ActiveLights[i]];
if (light._ldel) {
DoUnLight(light.position.tile.x, light.position.tile.y, light._lradius);
}
if (light._lunflag) {
DoUnLight(light.position.old.x, light.position.old.y, light.oldRadius);
light._lunflag = false;
}
}
for (int i = 0; i < ActiveLightCount; i++) {
int j = ActiveLights[i];
Light &light = Lights[j];
if (!light._ldel) {
DoLighting(light.position.tile, light._lradius, j);
}
}
int i = 0;
while (i < ActiveLightCount) {
if (Lights[ActiveLights[i]]._ldel) {
ActiveLightCount--;
std::swap(ActiveLights[ActiveLightCount], ActiveLights[i]);
} else {
i++;
}
}
}
UpdateLighting = false;
}
void SavePreLighting()
{
memcpy(dPreLight, dLight, sizeof(dPreLight));
}
void InitVision()
{
VisionCount = 0;
dovision = false;
VisionId = 1;
for (int i = 0; i < TransVal; i++) {
TransList[i] = false;
}
}
int AddVision(Point position, int r, bool mine)
{
if (VisionCount >= MAXVISION)
return -1;
auto &vision = VisionList[VisionCount];
vision.position.tile = position;
vision._lradius = r;
vision._lid = VisionId;
vision._ldel = false;
vision._lunflag = false;
vision._lflags = mine;
VisionId++;
VisionCount++;
dovision = true;
return vision._lid;
}
void ChangeVisionRadius(int id, int r)
{
for (int i = 0; i < VisionCount; i++) {
auto &vision = VisionList[i];
if (vision._lid != id)
continue;
vision._lunflag = true;
vision.position.old = vision.position.tile;
vision.oldRadius = vision._lradius;
vision._lradius = r;
dovision = true;
}
}
void ChangeVisionXY(int id, Point position)
{
for (int i = 0; i < VisionCount; i++) {
auto &vision = VisionList[i];
if (vision._lid != id)
continue;
vision._lunflag = true;
vision.position.old = vision.position.tile;
vision.oldRadius = vision._lradius;
vision.position.tile = position;
dovision = true;
}
}
void ProcessVisionList()
{
if (!dovision)
return;
for (int i = 0; i < VisionCount; i++) {
auto &vision = VisionList[i];
if (vision._ldel) {
DoUnVision(vision.position.tile, vision._lradius);
}
if (vision._lunflag) {
DoUnVision(vision.position.old, vision.oldRadius);
vision._lunflag = false;
}
}
for (int i = 0; i < TransVal; i++) {
TransList[i] = false;
}
for (int i = 0; i < VisionCount; i++) {
auto &vision = VisionList[i];
if (vision._ldel)
continue;
MapExplorationType doautomap = MAP_EXP_SELF;
if (!vision._lflags) {
doautomap = MAP_EXP_OTHERS;
for (const Player &player : Players) {
// Find player for this vision
if (!player.plractive || !player.isOnActiveLevel() || player._pvid != vision._lid)
continue;
// Check that player allows automap sharing
if (!player.friendlyMode)
doautomap = MAP_EXP_NONE;
break;
}
}
DoVision(
vision.position.tile,
vision._lradius,
doautomap,
vision._lflags);
}
bool delflag;
do {
delflag = false;
for (int i = 0; i < VisionCount; i++) {
auto &vision = VisionList[i];
if (!vision._ldel)
continue;
VisionCount--;
if (VisionCount > 0 && i != VisionCount) {
vision = VisionList[VisionCount];
}
delflag = true;
}
} while (delflag);
dovision = false;
}
void lighting_color_cycling()
{
if (leveltype != DTYPE_HELL) {
return;
}
uint8_t *tbl = LightTables.data();
for (int j = 0; j < 16; j++) {
tbl++;
uint8_t col = *tbl;
for (int i = 0; i < 30; i++) {
tbl[0] = tbl[1];
tbl++;
}
*tbl = col;
tbl += 225;
}
}
} // namespace devilution