mirror of
https://github.com/diasurgical/DevilutionX.git
synced 2026-10-12 02:13:06 +00:00
We want to be able to use unpacked MPQs on low-end platforms (PS2/rg99/etc). This is tricky on case-sensitive filesystems. Avoids case issues by lowercasing all paths in the code (then we'll just need lowercased listfiles).
828 lines
21 KiB
C++
828 lines
21 KiB
C++
/**
|
|
* @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 DisplacementOf<int8_t> VisionCrawlTable[23][15] = {
|
|
// 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;
|
|
}
|
|
|
|
bool TileAllowsLight(Point position)
|
|
{
|
|
if (!InDungeonBounds(position))
|
|
return false;
|
|
return !TileHasAny(dPiece[position.x][position.y], TileProperties::BlockLight);
|
|
}
|
|
|
|
void DoVisionFlags(Point position, MapExplorationType doAutomap, bool visible)
|
|
{
|
|
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;
|
|
}
|
|
|
|
} // 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 radius, MapExplorationType doAutomap, bool visible)
|
|
{
|
|
DoVisionFlags(position, doAutomap, visible);
|
|
|
|
static const Displacement factors[] = { { 1, 1 }, { -1, 1 }, { 1, -1 }, { -1, -1 } };
|
|
for (auto factor : factors) {
|
|
for (int j = 0; j < 23; j++) {
|
|
int lineLen = radius - RadiusAdj[j];
|
|
for (int k = 0; k < lineLen; k++) {
|
|
Point crawl = position + VisionCrawlTable[j][k] * factor;
|
|
if (!InDungeonBounds(crawl))
|
|
break;
|
|
bool blockerFlag = TileHasAny(dPiece[crawl.x][crawl.y], TileProperties::BlockLight);
|
|
bool tileOK = !blockerFlag;
|
|
|
|
if (VisionCrawlTable[j][k].deltaX > 0 && VisionCrawlTable[j][k].deltaY > 0) {
|
|
tileOK = tileOK || TileAllowsLight(crawl + Displacement { -factor.deltaX, 0 });
|
|
tileOK = tileOK || TileAllowsLight(crawl + Displacement { 0, -factor.deltaY });
|
|
}
|
|
|
|
if (!tileOK)
|
|
break;
|
|
|
|
DoVisionFlags(crawl, doAutomap, visible);
|
|
|
|
if (blockerFlag)
|
|
break;
|
|
|
|
int8_t trans = dTransVal[crawl.x][crawl.y];
|
|
if (trans != 0)
|
|
TransList[trans] = 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
|