瀏覽代碼

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.
Arseniy Shestakov 9 年之前
父節點
當前提交
33f8686ca6
共有 5 個文件被更改,包括 113 次插入77 次删除
  1. 50 12
      lib/CGameState.cpp
  2. 5 0
      lib/CGameState.h
  3. 2 2
      lib/IGameCallback.cpp
  4. 56 48
      lib/NetPacksLib.cpp
  5. 0 15
      server/CGameHandler.cpp

+ 50 - 12
lib/CGameState.cpp

@@ -746,6 +746,7 @@ void CGameState::init(StartInfo * si, bool allowSavingRandomMap)
 	buildBonusSystemTree();
 	buildBonusSystemTree();
 	initVisitingAndGarrisonedHeroes();
 	initVisitingAndGarrisonedHeroes();
 	initFogOfWar();
 	initFogOfWar();
+	initSightMap();
 
 
 	// Explicitly initialize static variables
 	// Explicitly initialize static variables
 	for(auto & elem : players)
 	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()
 void CGameState::initFogOfWar()
 {
 {
 	logGlobal->debug("\tFog of war"); //FIXME: should be initialized after all bonuses are set
 	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 h=0; h<map->height; ++h)
 				for(int v = 0; v < (map->twoLevel ? 2 : 1); ++v)
 				for(int v = 0; v < (map->twoLevel ? 2 : 1); ++v)
 					elem.second.fogOfWarMap[g][h][v] = 0;
 					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;
-			}
-		}
 	}
 	}
 }
 }
 
 

+ 5 - 0
lib/CGameState.h

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

+ 2 - 2
lib/IGameCallback.cpp

@@ -78,8 +78,8 @@ void CPrivilagedInfoCallback::getTilesInRange(std::unordered_set<int3, ShashInt3
 				if(distance <= radious)
 				if(distance <= radious)
 				{
 				{
 					if(!player
 					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));
 						tiles.insert(int3(xd,yd,pos.z));
 				}
 				}

+ 56 - 48
lib/NetPacksLib.cpp

@@ -220,29 +220,13 @@ DLL_LINKAGE void FoWChange::applyGs(CGameState *gs)
 {
 {
 	TeamState * team = gs->getPlayerTeam(player);
 	TeamState * team = gs->getPlayerTeam(player);
 	for(int3 t : tiles)
 	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());
 		logNetwork->error("Wrong ChangeObjPos: object %d doesn't exist!", objid.getNum());
 		return;
 		return;
 	}
 	}
+	gs->removeSightnObj(obj);
 	gs->map->removeBlockVisTiles(obj);
 	gs->map->removeBlockVisTiles(obj);
 	obj->pos = nPos;
 	obj->pos = nPos;
 	gs->map->addBlockVisTiles(obj);
 	gs->map->addBlockVisTiles(obj);
+	gs->addSightObj(obj);
 }
 }
 
 
 DLL_LINKAGE void ChangeObjectVisitors::applyGs(CGameState *gs)
 DLL_LINKAGE void ChangeObjectVisitors::applyGs(CGameState *gs)
@@ -386,6 +372,7 @@ DLL_LINKAGE void RemoveObject::applyGs(CGameState *gs)
 	CGObjectInstance *obj = gs->getObjInstance(id);
 	CGObjectInstance *obj = gs->getObjInstance(id);
 	logGlobal->debug("removing object id=%d; address=%x; name=%s", id, (intptr_t)obj, obj->getObjectName());
 	logGlobal->debug("removing object id=%d; address=%x; name=%s", id, (intptr_t)obj, obj->getObjectName());
 	//unblock tiles
 	//unblock tiles
+	gs->removeSightnObj(obj);
 	gs->map->removeBlockVisTiles(obj);
 	gs->map->removeBlockVisTiles(obj);
 
 
 	if(obj->ID==Obj::HERO)
 	if(obj->ID==Obj::HERO)
@@ -516,6 +503,8 @@ void TryMoveHero::applyGs(CGameState *gs)
 		logGlobal->error("Attempt ot move unavailable hero %d", id.getNum());
 		logGlobal->error("Attempt ot move unavailable hero %d", id.getNum());
 		return;
 		return;
 	}
 	}
+	if(start != end)
+		gs->removeSightnObj(h);
 
 
 	h->movement = movePoints;
 	h->movement = movePoints;
 
 
@@ -547,17 +536,19 @@ void TryMoveHero::applyGs(CGameState *gs)
 		h->boat = nullptr;
 		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)
 DLL_LINKAGE void NewStructures::applyGs(CGameState *gs)
@@ -571,7 +562,9 @@ DLL_LINKAGE void NewStructures::applyGs(CGameState *gs)
 		t->updateAppearance();
 		t->updateAppearance();
 	}
 	}
 	t->builded = builded;
 	t->builded = builded;
+	gs->removeSightnObj(t);
 	t->recreateBuildingsBonuses();
 	t->recreateBuildingsBonuses();
+	gs->addSightObj(t);
 }
 }
 DLL_LINKAGE void RazeStructures::applyGs(CGameState *gs)
 DLL_LINKAGE void RazeStructures::applyGs(CGameState *gs)
 {
 {
@@ -583,7 +576,9 @@ DLL_LINKAGE void RazeStructures::applyGs(CGameState *gs)
 		t->updateAppearance();
 		t->updateAppearance();
 	}
 	}
 	t->destroyed = destroyed; //yeaha
 	t->destroyed = destroyed; //yeaha
+	gs->removeSightnObj(t);
 	t->recreateBuildingsBonuses();
 	t->recreateBuildingsBonuses();
+	gs->addSightObj(t);
 }
 }
 
 
 DLL_LINKAGE void SetAvailableCreatures::applyGs(CGameState *gs)
 DLL_LINKAGE void SetAvailableCreatures::applyGs(CGameState *gs)
@@ -661,6 +656,7 @@ DLL_LINKAGE void HeroRecruited::applyGs(CGameState *gs)
 	{
 	{
 		t->setVisitingHero(h);
 		t->setVisitingHero(h);
 	}
 	}
+	gs->addSightObj(h);
 }
 }
 
 
 DLL_LINKAGE void GiveHero::applyGs(CGameState *gs)
 DLL_LINKAGE void GiveHero::applyGs(CGameState *gs)
@@ -720,6 +716,7 @@ DLL_LINKAGE void NewObject::applyGs(CGameState *gs)
 	gs->map->addBlockVisTiles(o);
 	gs->map->addBlockVisTiles(o);
 	o->initObj(gs->getRandomGenerator());
 	o->initObj(gs->getRandomGenerator());
 	gs->map->calculateGuardingGreaturePositions();
 	gs->map->calculateGuardingGreaturePositions();
+	gs->addSightObj(o);
 
 
 	logGlobal->debug("Added object id=%d; address=%x; name=%s", id, (intptr_t)o, o->getObjectName());
 	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;
 		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
 	else //not an armed instance
 	{
 	{

+ 0 - 15
server/CGameHandler.cpp

@@ -6291,21 +6291,6 @@ void CGameHandler::changeFogOfWar(int3 center, ui32 radius, PlayerColor player,
 {
 {
 	std::unordered_set<int3, ShashInt3> tiles;
 	std::unordered_set<int3, ShashInt3> tiles;
 	getTilesInRange(tiles, center, radius, player, hide? -1 : 1);
 	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);
 	changeFogOfWar(tiles, player, hide);
 }
 }