Browse Source

- Refactoring for checking victory/loss conditions (use enum class instead of magic numbers)
- Improved showing player lost message for one special case

beegee1 12 years ago
parent
commit
3e4407593f

+ 18 - 18
AI/VCAI/VCAI.cpp

@@ -192,14 +192,14 @@ void VCAI::showShipyardDialog(const IShipyard *obj)
 	NET_EVENT_HANDLER;
 }
 
-void VCAI::gameOver(PlayerColor player, bool victory)
+void VCAI::gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult)
 {
-	LOG_TRACE_PARAMS(logAi, "victory '%i'", victory);
+	LOG_TRACE_PARAMS(logAi, "victoryLossCheckResult '%s'", victoryLossCheckResult);
 	NET_EVENT_HANDLER;
-    logAi->debugStream() << boost::format("Player %d: I heard that player %d %s.") % playerID % player.getNum() % (victory ? "won" : "lost");
+	logAi->debugStream() << boost::format("Player %d: I heard that player %d %s.") % playerID % player.getNum() % (victoryLossCheckResult.victory() ? "won" : "lost");
 	if(player == playerID)
 	{
-		if(victory)
+		if(victoryLossCheckResult.victory())
 		{
             logAi->debugStream() << "VCAI: I won! Incredible!";
             logAi->debugStream() << "Turn nr " << myCb->getDate();
@@ -959,13 +959,13 @@ bool VCAI::tryBuildStructure(const CGTownInstance * t, BuildingID building, unsi
 	TResources currentRes = cb->getResourceAmount();
 	TResources income = estimateIncome();
 	//TODO: calculate if we have enough resources to build it in maxDays
-
-	for(const auto & buildID : toBuild)
-	{
-		const CBuilding *b = t->town->buildings.at(buildID);
-
-		EBuildingState::EBuildingState canBuild = cb->canBuildStructure(t, buildID);
-		if(canBuild == EBuildingState::ALLOWED)
+
+	for(const auto & buildID : toBuild)
+	{
+		const CBuilding *b = t->town->buildings.at(buildID);
+
+		EBuildingState::EBuildingState canBuild = cb->canBuildStructure(t, buildID);
+		if(canBuild == EBuildingState::ALLOWED)
 		{
 			if(!containsSavedRes(b->resources))
 			{
@@ -974,13 +974,13 @@ bool VCAI::tryBuildStructure(const CGTownInstance * t, BuildingID building, unsi
 				return true;
 			}
 			continue;
-		}
-		else if(canBuild == EBuildingState::NO_RESOURCES)
-		{
-			TResources cost = t->town->buildings.at(buildID)->resources;
-			for (int i = 0; i < GameConstants::RESOURCE_QUANTITY; i++)
-			{
-				int diff = currentRes[i] - cost[i] + income[i];
+		}
+		else if(canBuild == EBuildingState::NO_RESOURCES)
+		{
+			TResources cost = t->town->buildings.at(buildID)->resources;
+			for (int i = 0; i < GameConstants::RESOURCE_QUANTITY; i++)
+			{
+				int diff = currentRes[i] - cost[i] + income[i];
 				if(diff < 0)
 					saving[i] = 1;
 			}

+ 1 - 1
AI/VCAI/VCAI.h

@@ -188,7 +188,7 @@ public:
 	virtual void playerBlocked(int reason, bool start) override;
 	virtual void showPuzzleMap() override;
 	virtual void showShipyardDialog(const IShipyard *obj) override;
-	virtual void gameOver(PlayerColor player, bool victory) override;
+	virtual void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;
 	virtual void artifactPut(const ArtifactLocation &al) override;
 	virtual void artifactRemoved(const ArtifactLocation &al) override;
 	virtual void stacksErased(const StackLocation &location) override;

+ 13 - 4
client/CPlayerInterface.cpp

@@ -2069,13 +2069,13 @@ void CPlayerInterface::finishMovement( const TryMoveHero &details, const int3 &h
 	std::stable_sort(CGI->mh->ttiles[details.end.x][details.end.y][details.end.z].objects.begin(), CGI->mh->ttiles[details.end.x][details.end.y][details.end.z].objects.end(), ocmptwo_cgin);
 }
 
-void CPlayerInterface::gameOver(PlayerColor player, bool victory )
+void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult )
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 
 	if(player == playerID)
 	{
-		if(!victory)
+		if(victoryLossCheckResult.loss())
 			showInfoDialog(CGI->generaltexth->allTexts[95]);
 // 		else
 // 			showInfoDialog("Placeholder message: you won!");
@@ -2108,9 +2108,18 @@ void CPlayerInterface::gameOver(PlayerColor player, bool victory )
 
 	else
 	{
-		if(!victory && cb->getPlayerStatus(playerID) == EPlayerStatus::INGAME) //enemy has lost
+		if(victoryLossCheckResult.loss() && cb->getPlayerStatus(playerID) == EPlayerStatus::INGAME) //enemy has lost
 		{
-			std::string txt = CGI->generaltexth->allTexts[5]; //%s has been vanquished!
+			std::string txt;
+			if(victoryLossCheckResult == EVictoryLossCheckResult::LOSS_STANDARD_TOWNS_AND_TIME_OVER)
+			{
+				txt = CGI->generaltexth->allTexts[8]; // %s's heroes have abandoned him, and he is banished from this land.
+			}
+			else
+			{
+				txt = CGI->generaltexth->allTexts[5]; // %s has been vanquished!
+			}
+
 			boost::algorithm::replace_first(txt, "%s", CGI->generaltexth->capColors[player.getNum()]);
 			showInfoDialog(txt,std::vector<CComponent*>(1, new CComponent(CComponent::flag, player.getNum(), 0)));
 		}

+ 1 - 1
client/CPlayerInterface.h

@@ -184,7 +184,7 @@ public:
 	void centerView (int3 pos, int focusTime) override;
 	void objectPropertyChanged(const SetObjectProperty * sop) override;
 	void objectRemoved(const CGObjectInstance *obj) override;
-	void gameOver(PlayerColor player, bool victory) override;
+	void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;
 	void playerStartsTurn(PlayerColor player) override; //called before yourTurn on active itnerface
 	void showComp(const Component &comp, std::string message) override; //display component in the advmapint infobox
 	void saveGame(COSer<CSaveFile> &h, const int version) override; //saving

+ 47 - 47
client/NetPacksClient.cpp

@@ -292,7 +292,7 @@ void ChangeObjPos::applyCl( CClient *cl )
 
 void PlayerEndsGame::applyCl( CClient *cl )
 {
-	CALL_IN_ALL_INTERFACES(gameOver, player, victory);
+	CALL_IN_ALL_INTERFACES(gameOver, player, victoryLossCheckResult);
 }
 
 void RemoveBonus::applyCl( CClient *cl )
@@ -409,30 +409,30 @@ void NewStructures::applyCl( CClient *cl )
 {
 	CGTownInstance *town = GS(cl)->getTown(tid);
 	for(const auto & id : bid)
-	{
-		if(id == BuildingID::CAPITOL) //fort or capitol
-		{
-			town->defInfo = const_cast<CGDefInfo*>(CGI->dobjinfo->capitols.at(town->subID).get());
-		}
-		if(id == BuildingID::FORT)
-		{
-			town->defInfo = const_cast<CGDefInfo*>(CGI->dobjinfo->gobjs.at(Obj::TOWN).at(town->subID).get());
-		}
-		if(vstd::contains(cl->playerint,town->tempOwner))
-			cl->playerint[town->tempOwner]->buildChanged(town,id,1);
+	{
+		if(id == BuildingID::CAPITOL) //fort or capitol
+		{
+			town->defInfo = const_cast<CGDefInfo*>(CGI->dobjinfo->capitols.at(town->subID).get());
+		}
+		if(id == BuildingID::FORT)
+		{
+			town->defInfo = const_cast<CGDefInfo*>(CGI->dobjinfo->gobjs.at(Obj::TOWN).at(town->subID).get());
+		}
+		if(vstd::contains(cl->playerint,town->tempOwner))
+			cl->playerint[town->tempOwner]->buildChanged(town,id,1);
 	}
 }
 void RazeStructures::applyCl (CClient *cl)
 {
 	CGTownInstance *town = GS(cl)->getTown(tid);
 	for(const auto & id : bid)
-	{
-		if (id == BuildingID::CAPITOL) //fort or capitol
-		{
-			town->defInfo = const_cast<CGDefInfo*>(CGI->dobjinfo->gobjs.at(Obj::TOWN).at(town->subID).get());
-		}
-		if(vstd::contains (cl->playerint,town->tempOwner))
-			cl->playerint[town->tempOwner]->buildChanged (town,id,2);
+	{
+		if (id == BuildingID::CAPITOL) //fort or capitol
+		{
+			town->defInfo = const_cast<CGDefInfo*>(CGI->dobjinfo->gobjs.at(Obj::TOWN).at(town->subID).get());
+		}
+		if(vstd::contains (cl->playerint,town->tempOwner))
+			cl->playerint[town->tempOwner]->buildChanged (town,id,2);
 	}
 }
 
@@ -532,13 +532,13 @@ void InfoWindow::applyCl( CClient *cl )
 		comps.push_back(&elem);
 	}
 	std::string str;
-	text.toString(str);
-
-	if(vstd::contains(cl->playerint,player))
-		cl->playerint.at(player)->showInfoDialog(str,comps,(soundBase::soundID)soundID);
-	else
-        logNetwork->warnStream() << "We received InfoWindow for not our player...";
-}
+	text.toString(str);
+
+	if(vstd::contains(cl->playerint,player))
+		cl->playerint.at(player)->showInfoDialog(str,comps,(soundBase::soundID)soundID);
+	else
+        logNetwork->warnStream() << "We received InfoWindow for not our player...";
+}
 
 void SetObjectProperty::applyCl( CClient *cl )
 {
@@ -575,26 +575,26 @@ void CommanderLevelUp::applyCl( CClient *cl )
 void BlockingDialog::applyCl( CClient *cl )
 {
 	std::string str;
-	text.toString(str);
-
-	if(vstd::contains(cl->playerint,player))
-		cl->playerint.at(player)->showBlockingDialog(str,components,queryID,(soundBase::soundID)soundID,selection(),cancel());
-	else
-        logNetwork->warnStream() << "We received YesNoDialog for not our player...";
-}
+	text.toString(str);
+
+	if(vstd::contains(cl->playerint,player))
+		cl->playerint.at(player)->showBlockingDialog(str,components,queryID,(soundBase::soundID)soundID,selection(),cancel());
+	else
+        logNetwork->warnStream() << "We received YesNoDialog for not our player...";
+}
 
 void GarrisonDialog::applyCl(CClient *cl)
 {
 	const CGHeroInstance *h = cl->getHero(hid);
 	const CArmedInstance *obj = static_cast<const CArmedInstance*>(cl->getObj(objid));
 
-	if(!vstd::contains(cl->playerint,h->getOwner()))
-		return;
-
-	cl->playerint.at(h->getOwner())->showGarrisonDialog(obj,h,removableUnits,queryID);
-}
-
-void ExchangeDialog::applyCl(CClient *cl)
+	if(!vstd::contains(cl->playerint,h->getOwner()))
+		return;
+
+	cl->playerint.at(h->getOwner())->showGarrisonDialog(obj,h,removableUnits,queryID);
+}
+
+void ExchangeDialog::applyCl(CClient *cl)
 {
 	assert(heroes[0] && heroes[1]);
 	INTERFACE_CALL_IF_PRESENT(heroes[0]->tempOwner, heroExchangeStarted, heroes[0]->id, heroes[1]->id, queryID);
@@ -945,13 +945,13 @@ void SetAvailableArtifacts::applyCl(CClient *cl)
 		INTERFACE_CALL_IF_PRESENT(cl->getTile(bm->visitablePos())->visitableObjects.back()->tempOwner, availableArtifactsChanged, bm);
 	}
 }
-
-void TradeComponents::applyCl(CClient *cl)
-{///Shop handler
-	switch (CGI->mh->map->objects.at(objectid)->ID)
-	{
-	case Obj::BLACK_MARKET:
-		break;
+
+void TradeComponents::applyCl(CClient *cl)
+{///Shop handler
+	switch (CGI->mh->map->objects.at(objectid)->ID)
+	{
+	case Obj::BLACK_MARKET:
+		break;
 	case Obj::TAVERN:
 		break;
 	case Obj::DEN_OF_THIEVES:

+ 81 - 25
lib/CGameState.cpp

@@ -2032,23 +2032,29 @@ bool CGameState::checkForVisitableDir( const int3 & src, const TerrainTile *pom,
 	return true;
 }
 
+EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) const
+{
+	auto result = checkForVictory(player);
+	if (result == EVictoryLossCheckResult::NO_VICTORY_OR_LOSS) result = checkForLoss(player);
+	return result;
+}
 
-int CGameState::victoryCheck( PlayerColor player ) const
+EVictoryLossCheckResult CGameState::checkForVictory( PlayerColor player ) const
 {
 	const PlayerState *p = CGameInfoCallback::getPlayer(player);
 	if(map->victoryCondition.condition == EVictoryConditionType::WINSTANDARD  ||  map->victoryCondition.allowNormalVictory
 		|| (!p->human && !map->victoryCondition.appliesToAI)) //if the special victory condition applies only to human, AI has the standard)
 	{
 		if(player == checkForStandardWin())
-			return -1;
+			return EVictoryLossCheckResult::VICTORY_STANDARD;
 	}
 
 	if (p->enteredWinningCheatCode)
 	{ //cheater or tester, but has entered the code...
 		if(map->victoryCondition.condition == EVictoryConditionType::WINSTANDARD)
-			return -1;
+			return EVictoryLossCheckResult::VICTORY_STANDARD;
 		else
-			return 1;
+			return EVictoryLossCheckResult::VICTORY_SPECIAL;
 	}
 
 	if(p->human || map->victoryCondition.appliesToAI)
@@ -2059,7 +2065,7 @@ int CGameState::victoryCheck( PlayerColor player ) const
 			//check if any hero has winning artifact
 			for(auto & elem : p->heroes)
                 if(elem->hasArt(map->victoryCondition.objectId))
-					return 1;
+					return EVictoryLossCheckResult::VICTORY_SPECIAL;
 
 			break;
 
@@ -2081,13 +2087,13 @@ int CGameState::victoryCheck( PlayerColor player ) const
 				}
 
 				if(total >= map->victoryCondition.count)
-					return 1;
+					return EVictoryLossCheckResult::VICTORY_SPECIAL;
 			}
 			break;
 
 		case EVictoryConditionType::GATHERRESOURCE:
             if(p->resources[map->victoryCondition.objectId] >= map->victoryCondition.count)
-				return 1;
+				return EVictoryLossCheckResult::VICTORY_SPECIAL;
 
 			break;
 
@@ -2095,7 +2101,7 @@ int CGameState::victoryCheck( PlayerColor player ) const
 			{
 				const CGTownInstance *t = static_cast<const CGTownInstance *>(map->victoryCondition.obj);
                 if(t->tempOwner == player && t->fortLevel()-1 >= map->victoryCondition.objectId && t->hallLevel()-1 >= map->victoryCondition.count)
-					return 1;
+					return EVictoryLossCheckResult::VICTORY_SPECIAL;
 			}
 			break;
 
@@ -2104,22 +2110,22 @@ int CGameState::victoryCheck( PlayerColor player ) const
 				if((t == map->victoryCondition.obj || !map->victoryCondition.obj)
 					&& t->tempOwner == player
 					&& t->hasBuilt(BuildingID::GRAIL))
-					return 1;
+					return EVictoryLossCheckResult::VICTORY_SPECIAL;
 			break;
 
 		case EVictoryConditionType::BEATHERO:
 			if(map->victoryCondition.obj->tempOwner >= PlayerColor::PLAYER_LIMIT) //target hero not present on map
-				return 1;
+				return EVictoryLossCheckResult::VICTORY_SPECIAL;
 			break;
 		case EVictoryConditionType::CAPTURECITY:
 			{
 				if(map->victoryCondition.obj->tempOwner == player)
-					return 1;
+					return EVictoryLossCheckResult::VICTORY_SPECIAL;
 			}
 			break;
 		case EVictoryConditionType::BEATMONSTER:
 			if(!getObj(map->victoryCondition.obj->id)) //target monster not present on map
-				return 1;
+				return EVictoryLossCheckResult::VICTORY_SPECIAL;
 			break;
 		case EVictoryConditionType::TAKEDWELLINGS:
 			for(auto & elem : map->objects)
@@ -2131,11 +2137,11 @@ int CGameState::victoryCheck( PlayerColor player ) const
 					case Obj::CREATURE_GENERATOR1: case Obj::CREATURE_GENERATOR2:
 					case Obj::CREATURE_GENERATOR3: case Obj::CREATURE_GENERATOR4:
 					case Obj::RANDOM_DWELLING: case Obj::RANDOM_DWELLING_LVL: case Obj::RANDOM_DWELLING_FACTION:
-						return 0; //found not flagged dwelling - player not won
+						return EVictoryLossCheckResult::NO_VICTORY_OR_LOSS; //found not flagged dwelling - player not won
 					}
 				}
 			}
-			return 1;
+			return EVictoryLossCheckResult::VICTORY_SPECIAL;
 		case EVictoryConditionType::TAKEMINES:
 			for(auto & elem : map->objects)
 			{
@@ -2144,25 +2150,25 @@ int CGameState::victoryCheck( PlayerColor player ) const
 					switch(elem->ID)
 					{
 					case Obj::MINE: case Obj::ABANDONED_MINE:
-						return 0; //found not flagged mine - player not won
+						return EVictoryLossCheckResult::NO_VICTORY_OR_LOSS; //found not flagged mine - player not won
 					}
 				}
 			}
-			return 1;
+			return EVictoryLossCheckResult::VICTORY_SPECIAL;
 		case EVictoryConditionType::TRANSPORTITEM:
 			{
 				const CGTownInstance *t = static_cast<const CGTownInstance *>(map->victoryCondition.obj);
                 if((t->visitingHero && t->visitingHero->hasArt(map->victoryCondition.objectId))
                     || (t->garrisonHero && t->garrisonHero->hasArt(map->victoryCondition.objectId)))
 				{
-					return 1;
+					return EVictoryLossCheckResult::VICTORY_SPECIAL;
 				}
 			}
 			break;
  		}
 	}
 
-	return 0;
+	return EVictoryLossCheckResult::NO_VICTORY_OR_LOSS;
 }
 
 PlayerColor CGameState::checkForStandardWin() const
@@ -2395,16 +2401,16 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
 #undef FILL_FIELD
 }
 
-int CGameState::lossCheck( PlayerColor player ) const
+EVictoryLossCheckResult CGameState::checkForLoss( PlayerColor player ) const
 {
 	const PlayerState *p = CGameInfoCallback::getPlayer(player);
 	//if(map->lossCondition.typeOfLossCon == lossStandard)
 		if(checkForStandardLoss(player))
-			return -1;
+			return EVictoryLossCheckResult::LOSS_STANDARD_HEROES_AND_TOWNS;
 
 	if (p->enteredLosingCheatCode)
 	{
-		return 1;
+		return EVictoryLossCheckResult::LOSS_SPECIAL;
 	}
 
 	if(p->human) //special loss condition applies only to human player
@@ -2417,20 +2423,20 @@ int CGameState::lossCheck( PlayerColor player ) const
 				const CGObjectInstance *obj = map->lossCondition.obj;
 				assert(obj);
 				if(obj->tempOwner != player)
-					return 1;
+					return EVictoryLossCheckResult::LOSS_SPECIAL;
 			}
 			break;
 		case ELossConditionType::TIMEEXPIRES:
 			if(map->lossCondition.timeLimit < day)
-				return 1;
+				return EVictoryLossCheckResult::LOSS_SPECIAL;
 			break;
 		}
 	}
 
 	if(!p->towns.size() && p->daysWithoutCastle >= 7)
-		return 2;
+		return EVictoryLossCheckResult::LOSS_STANDARD_TOWNS_AND_TIME_OVER;
 
-	return false;
+	return EVictoryLossCheckResult::NO_VICTORY_OR_LOSS;
 }
 
 std::map<ui32, ConstTransitivePtr<CGHeroInstance> > CGameState::unusedHeroesFromPool()
@@ -3283,3 +3289,53 @@ CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance
 	useSubterraneanGates = true;
 	allowEmbarkAndDisembark = true;
 }
+
+const EVictoryLossCheckResult EVictoryLossCheckResult::NO_VICTORY_OR_LOSS = EVictoryLossCheckResult(0);
+const EVictoryLossCheckResult EVictoryLossCheckResult::VICTORY_STANDARD = EVictoryLossCheckResult(1);
+const EVictoryLossCheckResult EVictoryLossCheckResult::VICTORY_SPECIAL = EVictoryLossCheckResult(2);
+const EVictoryLossCheckResult EVictoryLossCheckResult::LOSS_STANDARD_HEROES_AND_TOWNS = EVictoryLossCheckResult(3);
+const EVictoryLossCheckResult EVictoryLossCheckResult::LOSS_STANDARD_TOWNS_AND_TIME_OVER = EVictoryLossCheckResult(4);
+const EVictoryLossCheckResult EVictoryLossCheckResult::LOSS_SPECIAL = EVictoryLossCheckResult(5);
+
+EVictoryLossCheckResult::EVictoryLossCheckResult() : intValue(0)
+{
+
+}
+
+EVictoryLossCheckResult::EVictoryLossCheckResult(si32 intValue) : intValue(intValue)
+{
+
+}
+
+bool EVictoryLossCheckResult::operator==(EVictoryLossCheckResult const & other) const
+{
+	return intValue == other.intValue;
+}
+
+bool EVictoryLossCheckResult::operator!=(EVictoryLossCheckResult const & other) const
+{
+	return intValue != other.intValue;
+}
+
+bool EVictoryLossCheckResult::victory() const
+{
+	return *this == VICTORY_STANDARD || *this == VICTORY_SPECIAL;
+}
+
+bool EVictoryLossCheckResult::loss() const
+{
+	return !victory();
+}
+
+std::ostream & operator<<(std::ostream & os, const EVictoryLossCheckResult & victoryLossCheckResult)
+{
+	if(victoryLossCheckResult == EVictoryLossCheckResult::NO_VICTORY_OR_LOSS) os << "No victory or loss";
+	else if(victoryLossCheckResult == EVictoryLossCheckResult::VICTORY_STANDARD) os << "Victory standard";
+	else if(victoryLossCheckResult == EVictoryLossCheckResult::VICTORY_SPECIAL) os << "Victory special";
+	else if(victoryLossCheckResult == EVictoryLossCheckResult::LOSS_STANDARD_HEROES_AND_TOWNS) os << "Loss standard heroes and towns";
+	else if(victoryLossCheckResult == EVictoryLossCheckResult::LOSS_STANDARD_TOWNS_AND_TIME_OVER) os << "Loss standard towns and time over";
+	else if(victoryLossCheckResult == EVictoryLossCheckResult::LOSS_SPECIAL) os << "Loss special";
+	else os << "Unknown type";
+
+	return os;
+}

+ 57 - 25
lib/CGameState.h

@@ -356,27 +356,55 @@ public:
 
 struct BattleInfo;
 
+class DLL_LINKAGE EVictoryLossCheckResult
+{
+public:
+	static const EVictoryLossCheckResult NO_VICTORY_OR_LOSS;
+	static const EVictoryLossCheckResult VICTORY_STANDARD;
+	static const EVictoryLossCheckResult VICTORY_SPECIAL;
+	static const EVictoryLossCheckResult LOSS_STANDARD_HEROES_AND_TOWNS;
+	static const EVictoryLossCheckResult LOSS_STANDARD_TOWNS_AND_TIME_OVER;
+	static const EVictoryLossCheckResult LOSS_SPECIAL;
+
+	EVictoryLossCheckResult();
+	bool operator==(EVictoryLossCheckResult const & other) const;
+	bool operator!=(EVictoryLossCheckResult const & other) const;
+	bool victory() const;
+	bool loss() const;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & intValue;
+	}
+
+private:
+	EVictoryLossCheckResult(si32 intValue);
+	si32 intValue;
+};
+
+DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EVictoryLossCheckResult & victoryLossCheckResult);
+
 class DLL_LINKAGE CGameState : public CNonConstInfoCallback
 {
 public:
 	ConstTransitivePtr<StartInfo> scenarioOps, initialOpts; //second one is a copy of settings received from pregame (not randomized)
 	PlayerColor currentPlayer; //ID of player currently having turn
-	ConstTransitivePtr<BattleInfo> curB; //current battle
-	ui32 day; //total number of days in game
-	ConstTransitivePtr<CMap> map;
-	std::map<PlayerColor, PlayerState> players;
-	std::map<TeamID, TeamState> teams;
-	CBonusSystemNode globalEffects;
-
-	struct DLL_LINKAGE HeroesPool
-	{
-		std::map<ui32, ConstTransitivePtr<CGHeroInstance> > heroesPool; //[subID] - heroes available to buy; nullptr if not available
-		std::map<ui32,ui8> pavailable; // [subid] -> which players can recruit hero (binary flags)
-
-		CGHeroInstance * pickHeroFor(bool native, PlayerColor player, const CTown *town, std::map<ui32, ConstTransitivePtr<CGHeroInstance> > &available, const CHeroClass *bannedClass = nullptr) const;
-
-		template <typename Handler> void serialize(Handler &h, const int version)
-		{
+	ConstTransitivePtr<BattleInfo> curB; //current battle
+	ui32 day; //total number of days in game
+	ConstTransitivePtr<CMap> map;
+	std::map<PlayerColor, PlayerState> players;
+	std::map<TeamID, TeamState> teams;
+	CBonusSystemNode globalEffects;
+
+	struct DLL_LINKAGE HeroesPool
+	{
+		std::map<ui32, ConstTransitivePtr<CGHeroInstance> > heroesPool; //[subID] - heroes available to buy; nullptr if not available
+		std::map<ui32,ui8> pavailable; // [subid] -> which players can recruit hero (binary flags)
+
+		CGHeroInstance * pickHeroFor(bool native, PlayerColor player, const CTown *town, std::map<ui32, ConstTransitivePtr<CGHeroInstance> > &available, const CHeroClass *bannedClass = nullptr) const;
+
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
 			h & heroesPool & pavailable;
 		}
 	} hpool; //we have here all heroes available on this map that are not hired
@@ -404,15 +432,13 @@ public:
 	void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src = int3(-1,-1,-1), int movement = -1); //calculates possible paths for hero, by default uses current hero position and movement left; returns pointer to newly allocated CPath or nullptr if path does not exists
 	int3 guardingCreaturePosition (int3 pos) const;
 	std::vector<CGObjectInstance*> guardingCreatures (int3 pos) const;
-	int victoryCheck(PlayerColor player) const; //checks if given player is winner; -1 if std victory, 1 if special victory, 0 else
-	int lossCheck(PlayerColor player) const; //checks if given player is loser;  -1 if std loss, 1 if special, 0 else
-	PlayerColor checkForStandardWin() const; //returns color of player that accomplished standard victory conditions or 255 (NEUTRAL) if no winner
-	bool checkForStandardLoss(PlayerColor player) const; //checks if given player lost the game
-	void obtainPlayersStats(SThievesGuildInfo & tgi, int level); //fills tgi with info about other players that is available at given level of thieves' guild
-	std::map<ui32, ConstTransitivePtr<CGHeroInstance> > unusedHeroesFromPool(); //heroes pool without heroes that are available in taverns
-	BattleInfo * setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town);
-
-	void buildBonusSystemTree();
+	EVictoryLossCheckResult checkForVictoryAndLoss(PlayerColor player) const;
+
+	void obtainPlayersStats(SThievesGuildInfo & tgi, int level); //fills tgi with info about other players that is available at given level of thieves' guild
+	std::map<ui32, ConstTransitivePtr<CGHeroInstance> > unusedHeroesFromPool(); //heroes pool without heroes that are available in taverns
+	BattleInfo * setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town);
+
+	void buildBonusSystemTree();
 	void attachArmedObjects();
 	void buildGlobalTeamPlayerTree();
 	void deserializationFix();
@@ -438,6 +464,12 @@ public:
 	friend class IGameCallback;
 	friend class CMapHandler;
 	friend class CGameHandler;
+
+private:
+	EVictoryLossCheckResult checkForVictory(PlayerColor player) const; //checks if given player is winner
+	EVictoryLossCheckResult checkForLoss(PlayerColor player) const; //checks if given player is loser
+	PlayerColor checkForStandardWin() const; //returns color of player that accomplished standard victory conditions or 255 (NEUTRAL) if no winner
+	bool checkForStandardLoss(PlayerColor player) const; //checks if given player lost the game
 };
 
 struct DLL_LINKAGE QuestInfo //universal interface for human and AI

+ 2 - 1
lib/IGameEventsReceiver.h

@@ -45,6 +45,7 @@ struct BattleTriggerEffect;
 class CComponent;
 struct CObstacleInstance;
 struct CPackForServer;
+class EVictoryLossCheckResult;
 
 class DLL_LINKAGE IBattleEventsReceiver
 {
@@ -128,7 +129,7 @@ public:
 	virtual void objectPropertyChanged(const SetObjectProperty * sop){}; //eg. mine has been flagged
 	virtual void objectRemoved(const CGObjectInstance *obj){}; //eg. collected resource, picked artifact, beaten hero
 	virtual void playerBlocked(int reason, bool start){}; //reason: 0 - upcoming battle
-	virtual void gameOver(PlayerColor player, bool victory){}; //player lost or won the game
+	virtual void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) {}; //player lost or won the game
 	virtual void playerStartsTurn(PlayerColor player){};
 	virtual void showComp(const Component &comp, std::string message) {}; //display component in the advmapint infobox
 

+ 2 - 2
lib/NetPacks.h

@@ -481,11 +481,11 @@ struct PlayerEndsGame : public CPackForClient //117
 	DLL_LINKAGE void applyGs(CGameState *gs);
 
 	PlayerColor player;
-	ui8 victory;
+	EVictoryLossCheckResult victoryLossCheckResult;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & player & victory;
+		h & player & victoryLossCheckResult;
 	}
 };
 

+ 2 - 1
lib/NetPacksLib.cpp

@@ -274,7 +274,8 @@ DLL_LINKAGE void ChangeObjPos::applyGs( CGameState *gs )
 DLL_LINKAGE void PlayerEndsGame::applyGs( CGameState *gs )
 {
 	PlayerState *p = gs->getPlayer(player);
-	p->status = victory ? EPlayerStatus::WINNER : EPlayerStatus::LOSER;
+	if(victoryLossCheckResult.victory()) p->status = EPlayerStatus::WINNER;
+	else p->status = EPlayerStatus::LOSER;
 }
 
 DLL_LINKAGE void RemoveBonus::applyGs( CGameState *gs )

File diff suppressed because it is too large
+ 400 - 400
server/CGameHandler.cpp


+ 4 - 3
server/CGameHandler.h

@@ -81,8 +81,6 @@ struct CasualtiesAfterBattle
 
 class CGameHandler : public IGameCallback, CBattleInfoCallback
 {
-private:
-	void makeStackDoNothing(const CStack * next);
 public:
 	//use enums as parameters, because doMove(sth, true, false, true) is not readable
 	enum EGuardLook {CHECK_FOR_GUARDS, IGNORE_GUARDS};
@@ -110,7 +108,6 @@ public:
 	void runBattle();
 	void checkLossVictory(PlayerColor player);
 	void winLoseHandle(ui8 players=255); //players: bit field - colours of players to be checked; default: all
-	void getLossVicMessage(PlayerColor player, si8 standard, bool victory, InfoWindow &out) const;
 
 	////used only in endBattle - don't touch elsewhere
 	bool visitObjectAfterVictory;
@@ -292,6 +289,10 @@ public:
 	void spawnWanderingMonsters(CreatureID creatureID);
 	friend class CVCMIServer;
 	friend class CScriptCallback;
+
+private:
+	void makeStackDoNothing(const CStack * next);
+	void getVictoryLossMessage(PlayerColor player, EVictoryLossCheckResult victoryLossCheckResult, InfoWindow & out) const;
 };
 
 void makeStackDoNothing();

Some files were not shown because too many files changed in this diff