Browse Source

Eliminate CrawlTable

pull/4908/head
Vladimir Olteanu 4 years ago committed by Anders Jenbo
parent
commit
bed5bd9ec1
  1. 78
      Source/debug.cpp
  2. 65
      Source/lighting.cpp
  3. 108
      Source/lighting.h
  4. 142
      Source/missiles.cpp
  5. 14
      Source/utils/stdcompat/invoke_result_t.hpp
  6. 26
      test/lighting_test.cpp

78
Source/debug.cpp

@ -723,28 +723,30 @@ std::string DebugCmdSpawnUniqueMonster(const string_view parameter)
int spawnedMonster = 0;
for (const auto &table : CrawlTable) {
for (auto displacement : table) {
Point pos = myPlayer.position.tile + displacement;
if (dPlayer[pos.x][pos.y] != 0 || dMonster[pos.x][pos.y] != 0)
continue;
if (!IsTileWalkable(pos))
continue;
Monster *monster = AddMonster(pos, myPlayer._pdir, id, true);
if (monster == nullptr)
return fmt::format("I could only summon {} Monsters. The rest strike for shorter working hours.", spawnedMonster);
PrepareUniqueMonst(*monster, uniqueIndex, 0, 0, UniqueMonstersData[uniqueIndex]);
ActiveMonsterCount--;
monster->corpseId = 1;
spawnedMonster += 1;
if (spawnedMonster >= count)
return "Let the fighting begin!";
}
}
return fmt::format("I could only summon {} Monsters. The rest strike for shorter working hours.", spawnedMonster);
auto ret = Crawl(0, MaxCrawlRadius, [&](auto displacement) -> std::optional<std::string> {
Point pos = myPlayer.position.tile + displacement;
if (dPlayer[pos.x][pos.y] != 0 || dMonster[pos.x][pos.y] != 0)
return {};
if (!IsTileWalkable(pos))
return {};
Monster *monster = AddMonster(pos, myPlayer._pdir, id, true);
if (monster == nullptr)
return fmt::format("I could only summon {} Monsters. The rest strike for shorter working hours.", spawnedMonster);
PrepareUniqueMonst(*monster, uniqueIndex, 0, 0, UniqueMonstersData[uniqueIndex]);
ActiveMonsterCount--;
monster->corpseId = 1;
spawnedMonster += 1;
if (spawnedMonster >= count)
return "Let the fighting begin!";
return {};
});
if (!ret)
ret = fmt::format("I could only summon {} Monsters. The rest strike for shorter working hours.", spawnedMonster);
return *ret;
}
std::string DebugCmdSpawnMonster(const string_view parameter)
@ -807,24 +809,26 @@ std::string DebugCmdSpawnMonster(const string_view parameter)
int spawnedMonster = 0;
for (const auto &table : CrawlTable) {
for (auto displacement : table) {
Point pos = myPlayer.position.tile + displacement;
if (dPlayer[pos.x][pos.y] != 0 || dMonster[pos.x][pos.y] != 0)
continue;
if (!IsTileWalkable(pos))
continue;
auto ret = Crawl(0, MaxCrawlRadius, [&](auto displacement) -> std::optional<std::string> {
Point pos = myPlayer.position.tile + displacement;
if (dPlayer[pos.x][pos.y] != 0 || dMonster[pos.x][pos.y] != 0)
return {};
if (!IsTileWalkable(pos))
return {};
if (AddMonster(pos, myPlayer._pdir, id, true) == nullptr)
return fmt::format("I could only summon {} Monsters. The rest strike for shorter working hours.", spawnedMonster);
spawnedMonster += 1;
if (AddMonster(pos, myPlayer._pdir, id, true) == nullptr)
return fmt::format("I could only summon {} Monsters. The rest strike for shorter working hours.", spawnedMonster);
spawnedMonster += 1;
if (spawnedMonster >= count)
return "Let the fighting begin!";
}
}
if (spawnedMonster >= count)
return "Let the fighting begin!";
return {};
});
return fmt::format("I could only summon {} Monsters. The rest strike for shorter working hours.", spawnedMonster);
if (!ret)
ret = fmt::format("I could only summon {} Monsters. The rest strike for shorter working hours.", spawnedMonster);
return *ret;
}
std::string DebugCmdShowTileData(const string_view parameter)

65
Source/lighting.cpp

@ -24,71 +24,6 @@ std::array<uint8_t, LIGHTSIZE> LightTables;
bool DisableLighting;
bool UpdateLighting;
namespace {
std::vector<DisplacementOf<int8_t>> CrawlFlips(const DisplacementOf<int8_t> *begin, const DisplacementOf<int8_t> *end)
{
std::vector<DisplacementOf<int8_t>> ret;
for (; begin != end; ++begin) {
DisplacementOf<int8_t> displacement = *begin;
if (displacement.deltaX != 0)
ret.emplace_back(displacement.flipX());
ret.emplace_back(displacement);
if (displacement.deltaX != 0 && displacement.deltaY != 0)
ret.emplace_back(displacement.flipXY());
if (displacement.deltaY != 0)
ret.emplace_back(displacement.flipY());
}
return ret;
}
std::vector<DisplacementOf<int8_t>> CrawlRow(int8_t row)
{
StaticVector<DisplacementOf<int8_t>, (CrawlTableSize - 1) * 2 + 1> ret;
for (int8_t i = 0; i < row; i++)
ret.emplace_back(i, row);
if (row > 1)
ret.emplace_back(static_cast<int8_t>(row - 1), static_cast<int8_t>(row - 1));
for (int8_t i = 0; i < row; i++)
ret.emplace_back(row, i);
return CrawlFlips(ret.begin(), ret.end());
}
} // namespace
/**
* CrawlTable specifies X- and Y-coordinate deltas from a missile target coordinate.
*
* n=4
*
* y
* ^
* | 1
* | 3#4
* | 2
* +-----> x
*
* n=16
*
* y
* ^
* | 314
* | B7 8C
* | F # G
* | D9 AE
* | 526
* +-------> x
*/
const std::array<std::vector<DisplacementOf<int8_t>>, CrawlTableSize> CrawlTable = []() {
std::array<std::vector<DisplacementOf<int8_t>>, CrawlTableSize> ret;
ret[0].emplace_back(0, 0);
for (size_t row = 1; row < CrawlTableSize; ++row) {
ret[row] = CrawlRow(static_cast<int8_t>(row));
}
return ret;
}();
/*
* X- Y-coordinate offsets of lighting visions.
* The last entry-pair is only for alignment.

108
Source/lighting.h

@ -13,6 +13,7 @@
#include "engine/point.hpp"
#include "miniwin/miniwin.h"
#include "utils/attributes.h"
#include "utils/stdcompat/invoke_result_t.hpp"
namespace devilution {
@ -74,10 +75,113 @@ void ChangeVisionXY(int id, Point position);
void ProcessVisionList();
void lighting_color_cycling();
template <typename F>
auto CrawlFlipsX(Displacement mirrored, F function) -> invoke_result_t<decltype(function), Displacement>
{
const Displacement Flips[] = { mirrored.flipX(), mirrored };
for (auto displacement : Flips) {
auto ret = function(displacement);
if (ret)
return ret;
}
return {};
}
template <typename F>
auto CrawlFlipsY(Displacement mirrored, F function) -> invoke_result_t<decltype(function), Displacement>
{
const Displacement Flips[] = { mirrored, mirrored.flipY() };
for (auto displacement : Flips) {
auto ret = function(displacement);
if (ret)
return ret;
}
return {};
}
template <typename F>
auto CrawlFlipsXY(Displacement mirrored, F function) -> invoke_result_t<decltype(function), Displacement>
{
const Displacement Flips[] = { mirrored.flipX(), mirrored, mirrored.flipXY(), mirrored.flipY() };
for (auto displacement : Flips) {
auto ret = function(displacement);
if (ret)
return ret;
}
return {};
}
constexpr int MaxCrawlRadius = 18;
/**
* CrawlTable specifies X- and Y-coordinate deltas from a missile target coordinate.
*
* n=4
*
* y
* ^
* | 1
* | 3#4
* | 2
* +-----> x
*
* n=16
*
* y
* ^
* | 314
* | B7 8C
* | F # G
* | D9 AE
* | 526
* +-------> x
*/
template <typename F>
auto Crawl(unsigned radius, F function) -> invoke_result_t<decltype(function), Displacement>
{
assert(radius <= MaxCrawlRadius);
if (radius == 0)
return function(Displacement { 0, 0 });
auto ret = CrawlFlipsY({ 0, static_cast<int>(radius) }, function);
if (ret)
return ret;
for (unsigned i = 1; i < radius; i++) {
ret = CrawlFlipsXY({ static_cast<int>(i), static_cast<int>(radius) }, function);
if (ret)
return ret;
}
if (radius > 1) {
ret = CrawlFlipsXY({ static_cast<int>(radius) - 1, static_cast<int>(radius) - 1 }, function);
if (ret)
return ret;
}
ret = CrawlFlipsX({ static_cast<int>(radius), 0 }, function);
if (ret)
return ret;
for (unsigned i = 1; i < radius; i++) {
ret = CrawlFlipsXY({ static_cast<int>(radius), static_cast<int>(i) }, function);
if (ret)
return ret;
}
return {};
}
template <typename F>
auto Crawl(unsigned minRadius, unsigned maxRadius, F function) -> invoke_result_t<decltype(function), Displacement>
{
for (unsigned i = minRadius; i <= maxRadius; i++) {
auto displacement = Crawl(i, function);
if (displacement)
return displacement;
}
return {};
}
/* rdata */
constexpr size_t CrawlTableSize = 19;
extern DVL_API_FOR_TEST const std::array<std::vector<DisplacementOf<int8_t>>, CrawlTableSize> CrawlTable;
extern const uint8_t VisionCrawlTable[23][30];
} // namespace devilution

142
Source/missiles.cpp

@ -1218,69 +1218,69 @@ void AddJester(Missile &missile, const AddMissileParameter &parameter)
void AddStealPotions(Missile &missile, const AddMissileParameter & /*parameter*/)
{
for (int i = 0; i < 3; i++) {
for (auto displacement : CrawlTable[i]) {
Point target = missile.position.start + displacement;
if (!InDungeonBounds(target))
continue;
int8_t pnum = dPlayer[target.x][target.y];
if (pnum == 0)
continue;
Player &player = Players[abs(pnum) - 1];
bool hasPlayedSFX = false;
for (int si = 0; si < MaxBeltItems; si++) {
int ii = -1;
if (player.SpdList[si]._itype == ItemType::Misc) {
if (GenerateRnd(2) == 0)
continue;
switch (player.SpdList[si]._iMiscId) {
case IMISC_FULLHEAL:
ii = ItemMiscIdIdx(IMISC_HEAL);
break;
case IMISC_HEAL:
case IMISC_MANA:
player.RemoveSpdBarItem(si);
break;
case IMISC_FULLMANA:
Crawl(0, 2, [&](auto displacement) {
Point target = missile.position.start + displacement;
if (!InDungeonBounds(target))
return false;
int8_t pnum = dPlayer[target.x][target.y];
if (pnum == 0)
return false;
Player &player = Players[abs(pnum) - 1];
bool hasPlayedSFX = false;
for (int si = 0; si < MaxBeltItems; si++) {
int ii = -1;
if (player.SpdList[si]._itype == ItemType::Misc) {
if (GenerateRnd(2) == 0)
continue;
switch (player.SpdList[si]._iMiscId) {
case IMISC_FULLHEAL:
ii = ItemMiscIdIdx(IMISC_HEAL);
break;
case IMISC_HEAL:
case IMISC_MANA:
player.RemoveSpdBarItem(si);
break;
case IMISC_FULLMANA:
ii = ItemMiscIdIdx(IMISC_MANA);
break;
case IMISC_REJUV:
if (GenerateRnd(2) != 0) {
ii = ItemMiscIdIdx(IMISC_MANA);
} else {
ii = ItemMiscIdIdx(IMISC_HEAL);
}
break;
case IMISC_FULLREJUV:
switch (GenerateRnd(3)) {
case 0:
ii = ItemMiscIdIdx(IMISC_FULLMANA);
break;
case IMISC_REJUV:
if (GenerateRnd(2) != 0) {
ii = ItemMiscIdIdx(IMISC_MANA);
} else {
ii = ItemMiscIdIdx(IMISC_HEAL);
}
break;
case IMISC_FULLREJUV:
switch (GenerateRnd(3)) {
case 0:
ii = ItemMiscIdIdx(IMISC_FULLMANA);
break;
case 1:
ii = ItemMiscIdIdx(IMISC_FULLHEAL);
break;
default:
ii = ItemMiscIdIdx(IMISC_REJUV);
break;
}
case 1:
ii = ItemMiscIdIdx(IMISC_FULLHEAL);
break;
default:
continue;
ii = ItemMiscIdIdx(IMISC_REJUV);
break;
}
break;
default:
continue;
}
if (ii != -1) {
InitializeItem(player.SpdList[si], ii);
player.SpdList[si]._iStatFlag = true;
}
if (!hasPlayedSFX) {
PlaySfxLoc(IS_POPPOP2, target);
hasPlayedSFX = true;
}
}
force_redraw = 255;
if (ii != -1) {
InitializeItem(player.SpdList[si], ii);
player.SpdList[si]._iStatFlag = true;
}
if (!hasPlayedSFX) {
PlaySfxLoc(IS_POPPOP2, target);
hasPlayedSFX = true;
}
}
}
force_redraw = 255;
return false;
});
missile._miDelFlag = true;
}
@ -3020,24 +3020,25 @@ void MI_FireRing(Missile &missile)
if (missile.limitReached)
return;
for (auto displacement : CrawlTable[3]) {
Crawl(3, [&](auto displacement) {
Point target = Point { missile.var1, missile.var2 } + displacement;
if (!InDungeonBounds(target))
continue;
return false;
int dp = dPiece[target.x][target.y];
if (TileHasAny(dp, TileProperties::Solid))
continue;
return false;
if (IsObjectAtPosition(target))
continue;
return false;
if (!LineClearMissile(missile.position.tile, target))
continue;
return false;
if (TileHasAny(dp, TileProperties::BlockMissile)) {
missile.limitReached = true;
return;
return true;
}
AddMissile(target, target, Direction::South, MIS_FIREWALL, TARGET_BOTH, src, dmg, missile._mispllvl);
}
return false;
});
}
void MI_Search(Missile &missile)
@ -3374,16 +3375,15 @@ void MI_Chain(Missile &missile)
Point dst { missile.var1, missile.var2 };
Direction dir = GetDirection(position, dst);
AddMissile(position, dst, dir, MIS_LIGHTCTRL, TARGET_MONSTERS, id, 1, missile._mispllvl);
int rad = std::min<int>(missile._mispllvl + 3, CrawlTable.size() - 1);
for (int i = 1; i < rad; i++) {
for (auto displacement : CrawlTable[i]) {
Point target = position + displacement;
if (InDungeonBounds(target) && dMonster[target.x][target.y] > 0) {
dir = GetDirection(position, target);
AddMissile(position, target, dir, MIS_LIGHTCTRL, TARGET_MONSTERS, id, 1, missile._mispllvl);
}
int rad = std::min<int>(missile._mispllvl + 3, MaxCrawlRadius);
Crawl(1, rad, [&](auto displacement) {
Point target = position + displacement;
if (InDungeonBounds(target) && dMonster[target.x][target.y] > 0) {
dir = GetDirection(position, target);
AddMissile(position, target, dir, MIS_LIGHTCTRL, TARGET_MONSTERS, id, 1, missile._mispllvl);
}
}
return false;
});
missile._mirange--;
if (missile._mirange == 0)
missile._miDelFlag = true;

14
Source/utils/stdcompat/invoke_result_t.hpp

@ -0,0 +1,14 @@
#pragma once
#include <type_traits>
namespace devilution {
#if defined(__cplusplus) && __cplusplus >= 201703L
using std::invoke_result_t;
#else
template <typename F, typename... Args>
using invoke_result_t = typename std::result_of<F(Args...)>::type;
#endif
} // namespace devilution

26
test/lighting_test.cpp

@ -10,22 +10,22 @@ TEST(Lighting, CrawlTables)
bool added[40][40];
memset(added, 0, sizeof(added));
for (size_t j = 0; j < CrawlTable.size(); j++) {
int x = 20;
int y = 20;
for (unsigned i = 0; i < CrawlTable[j].size(); i++) {
int dx = x + CrawlTable[j][i].deltaX;
int dy = y + CrawlTable[j][i].deltaY;
EXPECT_EQ(added[dx][dy], false) << "location " << i << ":" << j << " added twice";
added[dx][dy] = true;
}
}
int x = 20;
int y = 20;
Crawl(0, MaxCrawlRadius, [&](auto displacement) {
int dx = x + displacement.deltaX;
int dy = y + displacement.deltaY;
EXPECT_EQ(added[dx][dy], false) << "displacement " << displacement.deltaX << ":" << displacement.deltaY << " added twice";
added[dx][dy] = true;
return false;
});
for (int i = -18; i <= 18; i++) {
for (int j = -18; j <= 18; j++) {
for (int i = -MaxCrawlRadius; i <= MaxCrawlRadius; i++) {
for (int j = -MaxCrawlRadius; j <= MaxCrawlRadius; j++) {
if (added[i + 20][j + 20])
continue;
if ((i == -18 && j == -18) || (i == -18 && j == 18) || (i == 18 && j == -18) || (i == 18 && j == 18))
if (abs(i) == MaxCrawlRadius && abs(j) == MaxCrawlRadius)
continue; // Limit of the crawl table rage
EXPECT_EQ(false, true) << "while checking location " << i << ":" << j;
}

Loading…
Cancel
Save