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
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; |
|
|
|
namespace { |
|
|
|
/* |
|
* 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 |
|
}; |
|
|
|
uint8_t lightradius[16][128]; |
|
bool dovision; |
|
uint8_t lightblock[64][16][16]; |
|
|
|
/** RadiusAdj maps from VisionCrawlTable index to lighting vision radius adjustment. */ |
|
const uint8_t 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) { |
|
uint8_t 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] = static_cast<uint8_t>(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 - static_cast<uint8_t>(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] - static_cast<uint8_t>((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
|
|
|