Refactor of field of view
This commit is contained in:
parent
1effc1d224
commit
832702ce5c
2 changed files with 81 additions and 78 deletions
|
@ -52,8 +52,8 @@ public:
|
|||
}
|
||||
|
||||
/// Refresh field of view
|
||||
void Refresh(const CPlayer &player, const CUnit &unit, const Vec2i &pos, const short width,
|
||||
const short height, const short range, MapMarkerFunc *marker);
|
||||
void Refresh(const CPlayer &player, const CUnit &unit, const Vec2i &pos, const uint16_t width,
|
||||
const uint16_t height, const uint16_t range, MapMarkerFunc *marker);
|
||||
|
||||
bool SetType(const FieldOfViewTypes fov_type);
|
||||
FieldOfViewTypes GetType() const;
|
||||
|
@ -68,29 +68,30 @@ protected:
|
|||
private:
|
||||
/// Struct for portion of column. Used in FoV calculations
|
||||
struct SColumnPiece {
|
||||
SColumnPiece(short xValue, Vec2i top, Vec2i bottom) : col(xValue), TopVector(top), BottomVector(bottom) {}
|
||||
short col;
|
||||
SColumnPiece(int16_t xValue, Vec2i top, Vec2i bottom) : col(xValue), TopVector(top), BottomVector(bottom) {}
|
||||
int16_t col;
|
||||
Vec2i TopVector;
|
||||
Vec2i BottomVector;
|
||||
};
|
||||
|
||||
/// Calc whole simple radial field of view
|
||||
void ProceedSimpleRadial(const CPlayer &player, const Vec2i &pos, const int w, const int h, int range, MapMarkerFunc *marker) const;
|
||||
void ProceedSimpleRadial(const CPlayer &player, const Vec2i &pos, const int16_t w, const int16_t h,
|
||||
int16_t range, MapMarkerFunc *marker) const;
|
||||
/// Calc whole chadow casting field of view
|
||||
void ProceedShadowCasting(const Vec2i &spectatorPos, const short width, const short height, const short range);
|
||||
void ProceedShadowCasting(const Vec2i &spectatorPos, const uint16_t width, const uint16_t height, const uint16_t range);
|
||||
/// Calc field of view for set of lines along x or y.
|
||||
/// Used for calc part of FoV for assymetric (widht != height) spectators.
|
||||
void ProceedRaysCast(const char octant, const Vec2i &origin, const short width, const short range);
|
||||
void ProceedRaysCast(const uint8_t octant, const Vec2i &origin, const uint16_t width, const uint16_t range);
|
||||
/// Calc shadow casting field of view for single octant
|
||||
void RefreshOctant(const char octant, const Vec2i &origin, const short range);
|
||||
void RefreshOctant(const uint8_t octant, const Vec2i &origin, const uint16_t range);
|
||||
/// Calc shadow casting for portion of column
|
||||
void CalcFoVForColumnPiece(const short col, Vec2i &topVector, Vec2i &bottomVector,
|
||||
const short range, std::queue<SColumnPiece> &wrkQueue);
|
||||
void CalcFoVForColumnPiece(const int16_t col, Vec2i &topVector, Vec2i &bottomVector,
|
||||
const uint16_t range, std::queue<SColumnPiece> &wrkQueue);
|
||||
/// Recalculate top or bottom direction vectors
|
||||
short CalcRow_ByVector(const bool isTop, const short x, const Vec2i &vector) const;
|
||||
int16_t CalcRow_ByVector(const bool isTop, const int16_t x, const Vec2i &vector) const;
|
||||
|
||||
/// Recalculate coordinates and set current MapTile for [col, row]
|
||||
bool SetCurrentTile(const short col, const short row);
|
||||
bool SetCurrentTile(const int16_t col, const int16_t row);
|
||||
/// Check if current MapTile opaque
|
||||
bool IsTileOpaque() const;
|
||||
/// Mark current MapTile
|
||||
|
@ -99,13 +100,13 @@ private:
|
|||
/// Setup ShadowCaster for current refreshing of FoV
|
||||
void PrepareShadowCaster(const CPlayer &player, const CUnit &unit, const Vec2i &pos, MapMarkerFunc *marker);
|
||||
void ResetShadowCaster();
|
||||
void PrepareCache(const Vec2i pos, const short width, const short height, const short range);
|
||||
void PrepareCache(const Vec2i pos, const uint16_t width, const uint16_t height, const uint16_t range);
|
||||
|
||||
/// Update values of Octant and Origin for current working set
|
||||
void SetEnvironment(const char octant, const Vec2i &origin);
|
||||
void SetEnvironment(const uint8_t octant, const Vec2i &origin);
|
||||
void ResetEnvironment();
|
||||
/// Project [col,row] coordinates from current octant to global (Map) coordinate system and accordingly update position of current MapTile
|
||||
void ProjectCurrentTile(const short col, const short row);
|
||||
void ProjectCurrentTile(const int16_t col, const int16_t row);
|
||||
|
||||
private:
|
||||
struct FieldOfViewSettings
|
||||
|
@ -115,9 +116,9 @@ private:
|
|||
} Settings;
|
||||
|
||||
Vec2i currTilePos {0, 0}; /// Current work tile pos in global (Map) system coordinates
|
||||
char currOctant {0}; /// Current octant
|
||||
uint8_t currOctant {0}; /// Current octant
|
||||
Vec2i Origin {0, 0}; /// Position of the spectator in the global (Map) coordinate system
|
||||
unsigned short OpaqueFields {0}; /// Flags for opaque MapTiles for current calculation
|
||||
uint16_t OpaqueFields {0}; /// Flags for opaque MapTiles for current calculation
|
||||
|
||||
const CPlayer *Player {nullptr}; /// Pointer to player to set FoV for
|
||||
const CUnit *Unit {nullptr}; /// Pointer to unit to calculate FoV for
|
||||
|
@ -138,7 +139,7 @@ extern CFieldOfView FieldOfView;
|
|||
-- Functions
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
inline bool CFieldOfView::SetCurrentTile(const short col, const short row)
|
||||
inline bool CFieldOfView::SetCurrentTile(const int16_t col, const int16_t row)
|
||||
{
|
||||
ProjectCurrentTile(col, row);
|
||||
if (Map.Info.IsPointOnMap(currTilePos.x, currTilePos.y)) {
|
||||
|
@ -164,7 +165,7 @@ inline void CFieldOfView::MarkTile()
|
|||
}
|
||||
}
|
||||
|
||||
inline void CFieldOfView::ProjectCurrentTile(const short col, const short row)
|
||||
inline void CFieldOfView::ProjectCurrentTile(const int16_t col, const int16_t row)
|
||||
{
|
||||
switch (currOctant) {
|
||||
case 1: currTilePos.x = row; currTilePos.y = col; break;
|
||||
|
|
120
src/map/fov.cpp
120
src/map/fov.cpp
|
@ -38,7 +38,7 @@
|
|||
#include "unit_manager.h"
|
||||
#include "unittype.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
-- Variables
|
||||
----------------------------------------------------------------------------*/
|
||||
|
@ -127,8 +127,8 @@ void CFieldOfView::ResetAdditionalOpaqueFields()
|
|||
** @param range Radius to mark.
|
||||
** @param marker Function to mark or unmark sight
|
||||
*/
|
||||
void CFieldOfView::Refresh(const CPlayer &player, const CUnit &unit, const Vec2i &pos, const short width,
|
||||
const short height, const short range, MapMarkerFunc *marker)
|
||||
void CFieldOfView::Refresh(const CPlayer &player, const CUnit &unit, const Vec2i &pos, const uint16_t width,
|
||||
const uint16_t height, const uint16_t range, MapMarkerFunc *marker)
|
||||
{
|
||||
/// FIXME: sometimes when quit from game this assert is triggered
|
||||
if (!unit.Type) return;
|
||||
|
@ -137,8 +137,8 @@ void CFieldOfView::Refresh(const CPlayer &player, const CUnit &unit, const Vec2i
|
|||
if (!range) {
|
||||
return;
|
||||
}
|
||||
if (this->Settings.Type == FieldOfViewTypes::cShadowCasting && !unit.Type->AirUnit) {
|
||||
|
||||
if (this->Settings.Type == FieldOfViewTypes::cShadowCasting && !unit.Type->AirUnit) {
|
||||
/// FIXME: add high-/lowground
|
||||
OpaqueFields = unit.Type->BoolFlag[ELEVATED_INDEX].value ? 0 : this->Settings.OpaqueFields;
|
||||
if (GameSettings.Inside) {
|
||||
OpaqueFields &= ~(MapFieldRocks); /// because of rocks-flag is used as an obstacle for ranged attackers
|
||||
|
@ -162,41 +162,43 @@ void CFieldOfView::Refresh(const CPlayer &player, const CUnit &unit, const Vec2i
|
|||
** @param range Radius to mark (sight range)
|
||||
** @param marker Function to mark or unmark sight
|
||||
*/
|
||||
void CFieldOfView::ProceedSimpleRadial(const CPlayer &player, const Vec2i &pos, const int w, const int h, const int range, MapMarkerFunc *marker) const
|
||||
void CFieldOfView::ProceedSimpleRadial(const CPlayer &player, const Vec2i &pos,
|
||||
const int16_t w, const int16_t h, const int16_t range,
|
||||
MapMarkerFunc *marker) const
|
||||
{
|
||||
// Up hemi-cyle
|
||||
const int miny = std::max(-range, 0 - pos.y);
|
||||
for (int offsety = miny; offsety != 0; ++offsety) {
|
||||
const int offsetx = isqrt(square(range + 1) - square(-offsety) - 1);
|
||||
const int minx = std::max(0, pos.x - offsetx);
|
||||
const int maxx = std::min(Map.Info.MapWidth, pos.x + w + offsetx);
|
||||
const int16_t miny = std::max(-range, 0 - pos.y);
|
||||
for (int16_t offsety = miny; offsety != 0; offsety++) {
|
||||
const int16_t offsetx = isqrt(square(range + 1) - square(-offsety) - 1);
|
||||
const int16_t minx = std::max(0, pos.x - offsetx);
|
||||
const int16_t maxx = std::min(Map.Info.MapWidth, pos.x + w + offsetx);
|
||||
Vec2i mpos(minx, pos.y + offsety);
|
||||
const unsigned int index = mpos.y * Map.Info.MapWidth;
|
||||
const size_t index = mpos.y * Map.Info.MapWidth;
|
||||
|
||||
for (mpos.x = minx; mpos.x < maxx; ++mpos.x) {
|
||||
for (mpos.x = minx; mpos.x < maxx; mpos.x++) {
|
||||
marker(player, mpos.x + index);
|
||||
}
|
||||
}
|
||||
for (int offsety = 0; offsety < h; ++offsety) {
|
||||
const int minx = std::max(0, pos.x - range);
|
||||
const int maxx = std::min(Map.Info.MapWidth, pos.x + w + range);
|
||||
for (int16_t offsety = 0; offsety < h; offsety++) {
|
||||
const int16_t minx = std::max(0, pos.x - range);
|
||||
const int16_t maxx = std::min(Map.Info.MapWidth, pos.x + w + range);
|
||||
Vec2i mpos(minx, pos.y + offsety);
|
||||
const unsigned int index = mpos.y * Map.Info.MapWidth;
|
||||
const size_t index = mpos.y * Map.Info.MapWidth;
|
||||
|
||||
for (mpos.x = minx; mpos.x < maxx; ++mpos.x) {
|
||||
for (mpos.x = minx; mpos.x < maxx; mpos.x++) {
|
||||
marker(player, mpos.x + index);
|
||||
}
|
||||
}
|
||||
// bottom hemi-cycle
|
||||
const int maxy = std::min(range, Map.Info.MapHeight - pos.y - h);
|
||||
for (int offsety = 0; offsety < maxy; ++offsety) {
|
||||
const int offsetx = isqrt(square(range + 1) - square(offsety + 1) - 1);
|
||||
const int minx = std::max(0, pos.x - offsetx);
|
||||
const int maxx = std::min(Map.Info.MapWidth, pos.x + w + offsetx);
|
||||
const int16_t maxy = std::min(range, int16_t(Map.Info.MapHeight - pos.y - h));
|
||||
for (int16_t offsety = 0; offsety < maxy; offsety++) {
|
||||
const int16_t offsetx = isqrt(square(range + 1) - square(offsety + 1) - 1);
|
||||
const int16_t minx = std::max(0, pos.x - offsetx);
|
||||
const int16_t maxx = std::min(Map.Info.MapWidth, pos.x + w + offsetx);
|
||||
Vec2i mpos(minx, pos.y + h + offsety);
|
||||
const unsigned int index = mpos.y * Map.Info.MapWidth;
|
||||
const size_t index = mpos.y * Map.Info.MapWidth;
|
||||
|
||||
for (mpos.x = minx; mpos.x < maxx; ++mpos.x) {
|
||||
for (mpos.x = minx; mpos.x < maxx; mpos.x++) {
|
||||
marker(player, mpos.x + index);
|
||||
}
|
||||
}
|
||||
|
@ -210,33 +212,33 @@ void CFieldOfView::ProceedSimpleRadial(const CPlayer &player, const Vec2i &pos,
|
|||
** @param height Spectrator's height in tiles
|
||||
** @param range Spectrator's sight range in tiles
|
||||
*/
|
||||
void CFieldOfView::ProceedShadowCasting(const Vec2i &spectatorPos, const short width, const short height, const short range)
|
||||
void CFieldOfView::ProceedShadowCasting(const Vec2i &spectatorPos, const uint16_t width, const uint16_t height, const uint16_t range)
|
||||
{
|
||||
enum SpectatorGeometry {cOneTiled, cEven, cOdd, cTall, cWide} ;
|
||||
const int geometry = [width, height]{ if (width == height) {
|
||||
if (width == 1) { return cOneTiled; }
|
||||
if (width % 2) { return cOdd; }
|
||||
else { return cEven; }
|
||||
}
|
||||
if (width > height) { return cWide; }
|
||||
else { return cTall; }
|
||||
}();
|
||||
const uint8_t geometry = [width, height]{ if (width == height) {
|
||||
if (width == 1) { return cOneTiled; }
|
||||
if (width % 2) { return cOdd; }
|
||||
else { return cEven; }
|
||||
}
|
||||
if (width > height) { return cWide; }
|
||||
else { return cTall; }
|
||||
}();
|
||||
|
||||
Vec2i origin = {0, 0};
|
||||
int sightRange = range;
|
||||
uint16_t sightRange = range;
|
||||
|
||||
const bool isGeometrySymmetric = (geometry == cTall || geometry == cWide) ? false : true;
|
||||
|
||||
if (isGeometrySymmetric) {
|
||||
const short half = width >> 1;
|
||||
const int16_t half = width >> 1;
|
||||
origin.x = spectatorPos.x + half;
|
||||
origin.y = spectatorPos.y + half;
|
||||
sightRange += half - (geometry == cEven ? 1 : 0);
|
||||
} else {
|
||||
/// Fill spectator's tiles which not affected by RefreshOctant and ProceedRaysCast
|
||||
ResetEnvironment();
|
||||
for (short x = spectatorPos.x + 1; x < spectatorPos.x + width - 1; x++) {
|
||||
for (short y = spectatorPos.y + 1; y < spectatorPos.y + height - 1; y++) {
|
||||
for (int16_t x = spectatorPos.x + 1; x < spectatorPos.x + width - 1; x++) {
|
||||
for (int16_t y = spectatorPos.y + 1; y < spectatorPos.y + height - 1; y++) {
|
||||
if (SetCurrentTile(x, y)) {
|
||||
MarkTile();
|
||||
}
|
||||
|
@ -244,8 +246,8 @@ void CFieldOfView::ProceedShadowCasting(const Vec2i &spectatorPos, const short w
|
|||
}
|
||||
}
|
||||
|
||||
short rayWidth = 0;
|
||||
for (char quadrant = 0; quadrant < 4; ++quadrant) {
|
||||
int16_t rayWidth = 0;
|
||||
for (uint8_t quadrant = 0; quadrant < 4; quadrant++) {
|
||||
if (geometry == cEven) {
|
||||
/// recalculate center
|
||||
switch (quadrant) {
|
||||
|
@ -275,7 +277,7 @@ void CFieldOfView::ProceedShadowCasting(const Vec2i &spectatorPos, const short w
|
|||
break;
|
||||
}
|
||||
}
|
||||
const char octant = quadrant << 1;
|
||||
const uint8_t octant = quadrant << 1;
|
||||
/// First half-quadrant
|
||||
RefreshOctant(octant, origin, sightRange);
|
||||
/// Second half-quadrant
|
||||
|
@ -298,11 +300,11 @@ void CFieldOfView::ProceedShadowCasting(const Vec2i &spectatorPos, const short w
|
|||
** @param height Spectrator's height in tiles
|
||||
** @param range Spectrator's sight range in tiles
|
||||
*/
|
||||
void CFieldOfView::ProceedRaysCast(const char octant, const Vec2i &origin, const short width, const short range)
|
||||
void CFieldOfView::ProceedRaysCast(const uint8_t octant, const Vec2i &origin, const uint16_t width, const uint16_t range)
|
||||
{
|
||||
SetEnvironment(octant, origin);
|
||||
for (short col = -1; col >= -width; col--) {
|
||||
for (short row = 0; row < range; row++) {
|
||||
for (int16_t col = -1; col >= -width; col--) {
|
||||
for (int16_t row = 0; row < range; row++) {
|
||||
const bool isOnMap = SetCurrentTile(col, row);
|
||||
if (isOnMap) {
|
||||
MarkTile();
|
||||
|
@ -320,7 +322,7 @@ void CFieldOfView::ProceedRaysCast(const char octant, const Vec2i &origin, const
|
|||
** @param origin Tile position of the spectrator
|
||||
** @param range Spectrator's sight range in tiles
|
||||
*/
|
||||
void CFieldOfView::RefreshOctant(const char octant, const Vec2i &origin, const short range)
|
||||
void CFieldOfView::RefreshOctant(const uint8_t octant, const Vec2i &origin, const uint16_t range)
|
||||
{
|
||||
SetEnvironment(octant, origin);
|
||||
std::queue<SColumnPiece> wrkQueue;
|
||||
|
@ -345,16 +347,16 @@ void CFieldOfView::RefreshOctant(const char octant, const Vec2i &origin, const s
|
|||
** @param range Spectrator's sight range in tiles
|
||||
** @param wrkQueue Queue with all column pieces
|
||||
*/
|
||||
void CFieldOfView::CalcFoVForColumnPiece(const short col, Vec2i &topVector, Vec2i &bottomVector,
|
||||
const short range, std::queue<SColumnPiece> &wrkQueue)
|
||||
void CFieldOfView::CalcFoVForColumnPiece(const int16_t col, Vec2i &topVector, Vec2i &bottomVector,
|
||||
const uint16_t range, std::queue<SColumnPiece> &wrkQueue)
|
||||
{
|
||||
enum { cTop = true, cBottom = false };
|
||||
|
||||
short topRow = CalcRow_ByVector(cTop, col, topVector);
|
||||
short bottomRow = CalcRow_ByVector(cBottom, col, bottomVector);
|
||||
int16_t topRow = CalcRow_ByVector(cTop, col, topVector);
|
||||
int16_t bottomRow = CalcRow_ByVector(cBottom, col, bottomVector);
|
||||
|
||||
enum { cInit, cYes, cNo } wasLastTileOpaque = cInit;
|
||||
for (short row = topRow; row >= bottomRow; --row) {
|
||||
for (int16_t row = topRow; row >= bottomRow; --row) {
|
||||
const bool inRange = square(col) + square(row) < square(range);
|
||||
const bool isOnMap = SetCurrentTile(col, row);
|
||||
if (inRange && isOnMap) {
|
||||
|
@ -367,7 +369,7 @@ void CFieldOfView::CalcFoVForColumnPiece(const short col, Vec2i &topVector, Vec
|
|||
wrkQueue.push(SColumnPiece(col + 1, topVector, Vec2i(col * 2 - 1, row * 2 + 1)));
|
||||
}
|
||||
} else if (wasLastTileOpaque == cYes) {
|
||||
topVector = {short (col * 2 + 1), short (row * 2 + 1)};
|
||||
topVector = {int16_t(col * 2 + 1), int16_t(row * 2 + 1)};
|
||||
}
|
||||
}
|
||||
wasLastTileOpaque = isTileOpaque ? cYes : cNo;
|
||||
|
@ -385,17 +387,17 @@ void CFieldOfView::CalcFoVForColumnPiece(const short col, Vec2i &topVector, Vec
|
|||
** @param vector Current direction vector
|
||||
** @return Row (Y-value) for new direction vector
|
||||
*/
|
||||
short CFieldOfView::CalcRow_ByVector(const bool isTop, const short col, const Vec2i &vector) const
|
||||
int16_t CFieldOfView::CalcRow_ByVector(const bool isTop, const int16_t col, const Vec2i &vector) const
|
||||
{
|
||||
short row;
|
||||
int16_t row;
|
||||
if (col == 0) {
|
||||
row = 0;
|
||||
} else {
|
||||
// (col +|- 0.5) * (top|bot)_vector.y/(top|bot)_vector.x in integers
|
||||
const short devidend = (isTop ? (2 * col + 1) : (2 * col - 1)) * vector.y;
|
||||
const short devisor = 2 * vector.x;
|
||||
const short quotient = devidend / devisor;
|
||||
const short remainder = devidend % devisor;
|
||||
const int16_t devidend = (isTop ? (2 * col + 1) : (2 * col - 1)) * vector.y;
|
||||
const int16_t devisor = 2 * vector.x;
|
||||
const int16_t quotient = devidend / devisor;
|
||||
const int16_t remainder = devidend % devisor;
|
||||
// Round the result
|
||||
if (isTop ? remainder > vector.x
|
||||
: remainder >= vector.x) {
|
||||
|
@ -424,7 +426,7 @@ void CFieldOfView::ResetShadowCaster()
|
|||
ResetEnvironment();
|
||||
}
|
||||
|
||||
void CFieldOfView::PrepareCache(const Vec2i pos, const short width, const short height, const short range)
|
||||
void CFieldOfView::PrepareCache(const Vec2i pos, const uint16_t width, const uint16_t height, const uint16_t range)
|
||||
{
|
||||
/// Init cache table if it's uninitialized yet
|
||||
if (!MarkedTilesCache.size()) {
|
||||
|
@ -458,7 +460,7 @@ void CFieldOfView::PrepareCache(const Vec2i pos, const short width, const short
|
|||
** @param octant Octant to calc for
|
||||
** @param origin Tile position of the spectrator
|
||||
*/
|
||||
void CFieldOfView::SetEnvironment(const char octant, const Vec2i &origin)
|
||||
void CFieldOfView::SetEnvironment(const uint8_t octant, const Vec2i &origin)
|
||||
{
|
||||
Origin = origin;
|
||||
currOctant = octant;
|
||||
|
|
Loading…
Add table
Reference in a new issue