1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-02-03 13:01:33 +02:00

Sight map: new version of FoW. Using TeamState fogOfWarMap as storage

Before FoW code was really messy and slow in cases when tiles are hidden.
Since we didn't knew what we shouldn't hide we had to re-check every object.

Instead we'll store how many team-owned objects have sight over every tile on map.
Now we have to be more careful when GS changes, but overall FoW code become much cleaner.
This commit is contained in:
Arseniy Shestakov 2016-09-22 07:56:52 +03:00
parent 1162923d93
commit 33f8686ca6
5 changed files with 113 additions and 77 deletions

View File

@ -746,6 +746,7 @@ void CGameState::init(StartInfo * si, bool allowSavingRandomMap)
buildBonusSystemTree();
initVisitingAndGarrisonedHeroes();
initFogOfWar();
initSightMap();
// Explicitly initialize static variables
for(auto & elem : players)
@ -1512,6 +1513,55 @@ void CGameState::giveCampaignBonusToHero(CGHeroInstance * hero)
}
}
void CGameState::removeSightnObj(const CGObjectInstance * obj)
{
addSightObj(obj, false);
}
void CGameState::addSightObj(const CGObjectInstance * obj, bool add)
{
if(!vstd::contains(players, obj->tempOwner))
return;
auto p = getPlayer(obj->tempOwner);
if(p->status != EPlayerStatus::INGAME)
return;
addSightObj(p->team, obj, add);
}
void CGameState::addSightObj(TeamID team, const CGObjectInstance * obj, bool add)
{
auto ts = getTeam(team);
std::unordered_set<int3, ShashInt3> tiles;
getTilesInRange(tiles, obj->getSightCenter(), obj->getSightRadius());
for(int3 t : tiles)
{
if(add)
{
if(ts->fogOfWarMap[t.x][t.y][t.z] == 0)
ts->fogOfWarMap[t.x][t.y][t.z] = 2;
else
ts->fogOfWarMap[t.x][t.y][t.z]++;
}
else
{
ts->fogOfWarMap[t.x][t.y][t.z]--;
}
}
}
void CGameState::initSightMap()
{
for(CGObjectInstance * obj : map->objects)
{
if(!obj)
continue; //not a flagged object
addSightObj(obj);
}
}
void CGameState::initFogOfWar()
{
logGlobal->debug("\tFog of war"); //FIXME: should be initialized after all bonuses are set
@ -1529,18 +1579,6 @@ void CGameState::initFogOfWar()
for(int h=0; h<map->height; ++h)
for(int v = 0; v < (map->twoLevel ? 2 : 1); ++v)
elem.second.fogOfWarMap[g][h][v] = 0;
for(CGObjectInstance *obj : map->objects)
{
if(!obj || !vstd::contains(elem.second.players, obj->tempOwner)) continue; //not a flagged object
std::unordered_set<int3, ShashInt3> tiles;
getTilesInRange(tiles, obj->getSightCenter(), obj->getSightRadius(), obj->tempOwner, 1);
for(int3 tile : tiles)
{
elem.second.fogOfWarMap[tile.x][tile.y][tile.z] = 1;
}
}
}
}

View File

@ -178,6 +178,10 @@ public:
std::vector<CGObjectInstance*> guardingCreatures (int3 pos) const;
void updateRumor();
void initSightMap();
void addSightObj(const CGObjectInstance * obj, bool add = true);
void removeSightnObj(const CGObjectInstance * obj);
// ----- victory, loss condition checks -----
EVictoryLossCheckResult checkForVictoryAndLoss(PlayerColor player) const;
@ -268,6 +272,7 @@ private:
void initStartingResources();
void initHeroes();
void giveCampaignBonusToHero(CGHeroInstance * hero);
void addSightObj(TeamID team, const CGObjectInstance * obj, bool add = true);
void initFogOfWar();
void initStartingBonus();
void initTowns();

View File

@ -78,8 +78,8 @@ void CPrivilagedInfoCallback::getTilesInRange(std::unordered_set<int3, ShashInt3
if(distance <= radious)
{
if(!player
|| (mode == 1 && team->fogOfWarMap[xd][yd][pos.z]==0)
|| (mode == -1 && team->fogOfWarMap[xd][yd][pos.z]==1)
|| (mode == 1 && team->fogOfWarMap[xd][yd][pos.z] == 0)
|| (mode == -1 && team->fogOfWarMap[xd][yd][pos.z] > 0)
)
tiles.insert(int3(xd,yd,pos.z));
}

View File

@ -220,29 +220,13 @@ DLL_LINKAGE void FoWChange::applyGs(CGameState *gs)
{
TeamState * team = gs->getPlayerTeam(player);
for(int3 t : tiles)
team->fogOfWarMap[t.x][t.y][t.z] = mode;
if (mode == 0) //do not hide too much
{
std::unordered_set<int3, ShashInt3> tilesRevealed;
for (auto & elem : gs->map->objects)
{
const CGObjectInstance *o = elem;
if (o)
{
switch(o->ID)
{
case Obj::HERO:
case Obj::MINE:
case Obj::TOWN:
case Obj::ABANDONED_MINE:
if(vstd::contains(team->players, o->tempOwner)) //check owned observators
gs->getTilesInRange(tilesRevealed, o->getSightCenter(), o->getSightRadius(), o->tempOwner, 1);
break;
}
}
}
for(int3 t : tilesRevealed) //probably not the most optimal solution ever
team->fogOfWarMap[t.x][t.y][t.z] = 1;
if(mode == 0 && team->fogOfWarMap[t.x][t.y][t.z] > 1)
continue;
else if(mode == 1 && team->fogOfWarMap[t.x][t.y][t.z])
continue;
team->fogOfWarMap[t.x][t.y][t.z] = mode;
}
}
@ -309,9 +293,11 @@ DLL_LINKAGE void ChangeObjPos::applyGs(CGameState *gs)
logNetwork->error("Wrong ChangeObjPos: object %d doesn't exist!", objid.getNum());
return;
}
gs->removeSightnObj(obj);
gs->map->removeBlockVisTiles(obj);
obj->pos = nPos;
gs->map->addBlockVisTiles(obj);
gs->addSightObj(obj);
}
DLL_LINKAGE void ChangeObjectVisitors::applyGs(CGameState *gs)
@ -386,6 +372,7 @@ DLL_LINKAGE void RemoveObject::applyGs(CGameState *gs)
CGObjectInstance *obj = gs->getObjInstance(id);
logGlobal->debug("removing object id=%d; address=%x; name=%s", id, (intptr_t)obj, obj->getObjectName());
//unblock tiles
gs->removeSightnObj(obj);
gs->map->removeBlockVisTiles(obj);
if(obj->ID==Obj::HERO)
@ -516,6 +503,8 @@ void TryMoveHero::applyGs(CGameState *gs)
logGlobal->error("Attempt ot move unavailable hero %d", id.getNum());
return;
}
if(start != end)
gs->removeSightnObj(h);
h->movement = movePoints;
@ -547,17 +536,19 @@ void TryMoveHero::applyGs(CGameState *gs)
h->boat = nullptr;
}
if(start!=end && (result == SUCCESS || result == TELEPORTATION || result == EMBARK || result == DISEMBARK))
if(start != end)
{
gs->map->removeBlockVisTiles(h);
h->pos = end;
if(CGBoat *b = const_cast<CGBoat *>(h->boat))
b->pos = end;
gs->map->addBlockVisTiles(h);
}
if(result == SUCCESS || result == TELEPORTATION || result == EMBARK || result == DISEMBARK)
{
gs->map->removeBlockVisTiles(h);
h->pos = end;
if(CGBoat *b = const_cast<CGBoat *>(h->boat))
b->pos = end;
gs->map->addBlockVisTiles(h);
}
for(int3 t : fowRevealed)
gs->getPlayerTeam(h->getOwner())->fogOfWarMap[t.x][t.y][t.z] = 1;
gs->addSightObj(h);
}
}
DLL_LINKAGE void NewStructures::applyGs(CGameState *gs)
@ -571,7 +562,9 @@ DLL_LINKAGE void NewStructures::applyGs(CGameState *gs)
t->updateAppearance();
}
t->builded = builded;
gs->removeSightnObj(t);
t->recreateBuildingsBonuses();
gs->addSightObj(t);
}
DLL_LINKAGE void RazeStructures::applyGs(CGameState *gs)
{
@ -583,7 +576,9 @@ DLL_LINKAGE void RazeStructures::applyGs(CGameState *gs)
t->updateAppearance();
}
t->destroyed = destroyed; //yeaha
gs->removeSightnObj(t);
t->recreateBuildingsBonuses();
gs->addSightObj(t);
}
DLL_LINKAGE void SetAvailableCreatures::applyGs(CGameState *gs)
@ -661,6 +656,7 @@ DLL_LINKAGE void HeroRecruited::applyGs(CGameState *gs)
{
t->setVisitingHero(h);
}
gs->addSightObj(h);
}
DLL_LINKAGE void GiveHero::applyGs(CGameState *gs)
@ -720,6 +716,7 @@ DLL_LINKAGE void NewObject::applyGs(CGameState *gs)
gs->map->addBlockVisTiles(o);
o->initObj(gs->getRandomGenerator());
gs->map->calculateGuardingGreaturePositions();
gs->addSightObj(o);
logGlobal->debug("Added object id=%d; address=%x; name=%s", id, (intptr_t)o, o->getObjectName());
}
@ -1166,29 +1163,40 @@ DLL_LINKAGE void SetObjectProperty::applyGs(CGameState *gs)
return;
}
CArmedInstance *cai = dynamic_cast<CArmedInstance *>(obj);
if(what == ObjProperty::OWNER && cai)
if(what == ObjProperty::OWNER)
{
if(obj->ID == Obj::TOWN)
CArmedInstance *cai = dynamic_cast<CArmedInstance *>(obj);
if(cai)
{
CGTownInstance *t = static_cast<CGTownInstance*>(obj);
if(t->tempOwner < PlayerColor::PLAYER_LIMIT)
gs->getPlayer(t->tempOwner)->towns -= t;
if(val < PlayerColor::PLAYER_LIMIT_I)
if(obj->ID == Obj::TOWN)
{
PlayerState * p = gs->getPlayer(PlayerColor(val));
p->towns.push_back(t);
CGTownInstance *t = static_cast<CGTownInstance*>(obj);
if(t->tempOwner < PlayerColor::PLAYER_LIMIT)
gs->getPlayer(t->tempOwner)->towns -= t;
if(val < PlayerColor::PLAYER_LIMIT_I)
{
PlayerState * p = gs->getPlayer(PlayerColor(val));
p->towns.push_back(t);
//reset counter before NewTurn to avoid no town message if game loaded at turn when one already captured
if(p->daysWithoutCastle)
p->daysWithoutCastle = boost::none;
//reset counter before NewTurn to avoid no town message if game loaded at turn when one already captured
if(p->daysWithoutCastle)
p->daysWithoutCastle = boost::none;
}
}
}
CBonusSystemNode *nodeToMove = cai->whatShouldBeAttached();
nodeToMove->detachFrom(cai->whereShouldBeAttached(gs));
obj->setProperty(what,val);
nodeToMove->attachTo(cai->whereShouldBeAttached(gs));
gs->removeSightnObj(obj);
CBonusSystemNode *nodeToMove = cai->whatShouldBeAttached();
nodeToMove->detachFrom(cai->whereShouldBeAttached(gs));
obj->setProperty(what,val);
nodeToMove->attachTo(cai->whereShouldBeAttached(gs));
gs->addSightObj(obj);
}
else
{
gs->removeSightnObj(obj);
obj->setProperty(what, val);
gs->addSightObj(obj);
}
}
else //not an armed instance
{

View File

@ -6291,21 +6291,6 @@ void CGameHandler::changeFogOfWar(int3 center, ui32 radius, PlayerColor player,
{
std::unordered_set<int3, ShashInt3> tiles;
getTilesInRange(tiles, center, radius, player, hide? -1 : 1);
if (hide)
{
std::unordered_set<int3, ShashInt3> observedTiles; //do not hide tiles observed by heroes. May lead to disastrous AI problems
auto p = getPlayer(player);
for (auto h : p->heroes)
{
getTilesInRange(observedTiles, h->getSightCenter(), h->getSightRadius(), h->tempOwner, -1);
}
for (auto t : p->towns)
{
getTilesInRange(observedTiles, t->getSightCenter(), t->getSightRadius(), t->tempOwner, -1);
}
for (auto tile : observedTiles)
vstd::erase_if_present (tiles, tile);
}
changeFogOfWar(tiles, player, hide);
}