浏览代码

- Refactored victory loss condition checks
- Added toString() method to EVictoryLossCheckResult enum class to improve debugging
- Removed mostly unused CFunctionList2
- Added missing header files for vcmiclient project to CMakeLists
- Tweaked SDL suggests bpp message a bit
- Added showInfoDialogAndWait (info dialog and waits, used from client thread) and showOkDialog (callback to ok click, used from GUI thread) to player interface
- Added showOkDialog method to CInfoWindow (unused for now, but may be used later)

beegee1 12 年之前
父节点
当前提交
c786a3076a

+ 2 - 2
client/CMT.cpp

@@ -752,10 +752,10 @@ static void setScreenRes(int w, int h, int bpp, bool fullscreen, bool resetVideo
 	}
 
 	bool bufOnScreen = (screenBuf == screen);
-	
+
 	if(suggestedBpp != bpp)
 	{
-        logGlobal->warnStream() << "Note: SDL suggests to use " << suggestedBpp << " bpp instead of"  << bpp << " bpp ";
+		logGlobal->infoStream() << boost::format("Using %s bpp (bits per pixel) for the video mode. Default or overriden setting was %s bpp.") % suggestedBpp % bpp;
 	}
 
 	//For some reason changing fullscreen via config window checkbox result in SDL_Quit event

+ 19 - 10
client/CMakeLists.txt

@@ -6,17 +6,13 @@ include_directories(${SDL_INCLUDE_DIR} ${SDLIMAGE_INCLUDE_DIR} ${SDLMIXER_INCLUD
 include_directories(${Boost_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR} ${FFMPEG_INCLUDE_DIRS})
 
 set(client_SRCS
-        CPreGame.cpp
-        Client.cpp
-        CPlayerInterface.cpp
-        CMT.cpp
-        GUIClasses.cpp
-        battle/CBattleInterface.cpp
-
         ../CCallback.cpp
+
+		battle/CBattleInterface.cpp
         battle/CBattleAnimations.cpp
         battle/CBattleInterfaceClasses.cpp
         battle/CCreatureAnimation.cpp
+
         gui/CGuiHandler.cpp
         gui/CIntObject.cpp
         gui/CIntObjectClasses.cpp
@@ -24,6 +20,12 @@ set(client_SRCS
         gui/Geometries.cpp
         gui/CCursorHandler.cpp
         gui/SDL_Extensions.cpp
+
+		CPreGame.cpp
+		Client.cpp
+		CPlayerInterface.cpp
+		CMT.cpp
+		GUIClasses.cpp
         AdventureMapClasses.cpp
         CAdvmapInterface.cpp
         CAnimation.cpp
@@ -44,15 +46,22 @@ set(client_SRCS
         NetPacksClient.cpp
 )
 
+set(client_HEADERS
+		CSoundBase.h
+		FunctionList.h
+
+		gui/SDL_Pixels.h
+)
+
 if(WIN32)
-    add_executable(vcmiclient WIN32 ${client_SRCS})
+	add_executable(vcmiclient WIN32 ${client_SRCS} ${client_HEADERS})
 elseif(APPLE)
     # OS X specific includes
     include_directories(${SPARKLE_INCLUDE_DIR})
 
     # OS X specific source files
     set(client_SRCS ${client_SRCS} SDLMain.m OSX.mm Info.plist vcmi.icns ../osx/vcmi_dsa_public.pem)
-    add_executable(vcmiclient MACOSX_BUNDLE ${client_SRCS})
+	add_executable(vcmiclient MACOSX_BUNDLE ${client_SRCS} ${client_HEADERS})
 
     # OS X specific libraries
     target_link_libraries(vcmiclient ${SPARKLE_FRAMEWORK})
@@ -93,7 +102,7 @@ elseif(APPLE)
         
     add_custom_command(TARGET vcmiclient POST_BUILD COMMAND ${MakeVCMIBundle})
 else()
-    add_executable(vcmiclient ${client_SRCS})
+	add_executable(vcmiclient ${client_SRCS} ${client_HEADERS})
 endif()
 
 target_link_libraries(vcmiclient vcmi ${Boost_LIBRARIES} ${SDL_LIBRARY} ${SDLIMAGE_LIBRARY} ${SDLMIXER_LIBRARY} ${SDLTTF_LIBRARY} ${ZLIB_LIBRARIES} ${FFMPEG_LIBRARIES} ${RT_LIB} ${DL_LIB})

+ 61 - 0
client/CPlayerInterface.cpp

@@ -1022,6 +1022,21 @@ void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector
 	}
 }
 
+void CPlayerInterface::showInfoDialogAndWait(std::vector<Component> & components, const MetaString & text)
+{
+	EVENT_HANDLER_CALLED_BY_CLIENT;
+	std::vector<Component*> comps;
+	for(auto & elem : components)
+	{
+		comps.push_back(&elem);
+	}
+	std::string str;
+	text.toString(str);
+
+	showInfoDialog(str,comps, 0);
+	waitWhileDialog();
+}
+
 void CPlayerInterface::showYesNoDialog(const std::string &text, CFunctionList<void()> onYes, CFunctionList<void()> onNo, bool DelComps, const std::vector<CComponent*> & components)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
@@ -1031,6 +1046,27 @@ void CPlayerInterface::showYesNoDialog(const std::string &text, CFunctionList<vo
 	CInfoWindow::showYesNoDialog(text, &components, onYes, onNo, DelComps, playerID);
 }
 
+void CPlayerInterface::showOkDialog(std::vector<Component> & components, const MetaString & text, const boost::function<void()> & onOk)
+{
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
+
+	std::vector<Component*> comps;
+	for(auto & elem : components)
+	{
+		comps.push_back(&elem);
+	}
+	std::string str;
+	text.toString(str);
+
+	stopMovement();
+	showingDialog->setn(true);
+
+	std::vector<CComponent*> intComps;
+	for(auto & component : comps)
+		intComps.push_back(new CComponent(*component));
+	CInfoWindow::showOkDialog(str, &intComps, onOk, true, playerID);
+}
+
 void CPlayerInterface::showBlockingDialog( const std::string &text, const std::vector<Component> &components, QueryID askID, int soundID, bool selection, bool cancel )
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
@@ -2251,6 +2287,31 @@ void CPlayerInterface::acceptTurn()
 
 		adventureInt->endTurn.callback();
 	}
+
+	// warn player if he has no town
+	if(cb->howManyTowns() == 0)
+	{
+		auto playerColor = *cb->getPlayerID();
+
+		std::vector<Component> components;
+		components.push_back(Component(Component::FLAG, playerColor.getNum(), 0, 0));
+		MetaString text;
+
+		auto daysWithoutCastle = cb->getPlayer(playerColor)->daysWithoutCastle;
+		if(daysWithoutCastle == 6)
+		{
+			text.addTxt(MetaString::ARRAY_TXT,129); //%s, this is your last day to capture a town or you will be banished from this land.
+			text.addReplacement(MetaString::COLOR, playerColor.getNum());
+		}
+		else
+		{
+			text.addTxt(MetaString::ARRAY_TXT,128); //%s, you only have %d days left to capture a town or you will be banished from this land.
+			text.addReplacement(MetaString::COLOR, playerColor.getNum());
+			text.addReplacement(7 - daysWithoutCastle);
+		}
+
+		showInfoDialogAndWait(components, text);
+	}
 }
 
 void CPlayerInterface::tryDiggging(const CGHeroInstance *h)

+ 5 - 0
client/CPlayerInterface.h

@@ -229,9 +229,14 @@ public:
 	void updateInfo(const CGObjectInstance * specific);
 	void init(shared_ptr<CCallback> CB);
 	int3 repairScreenPos(int3 pos); //returns position closest to pos we can center screen on
+
+	// show dialogs
 	void showInfoDialog(const std::string &text, CComponent * component);
 	void showInfoDialog(const std::string &text, const std::vector<CComponent*> & components = std::vector<CComponent*>(), int soundID = 0, bool delComps = false);
+	void showInfoDialogAndWait(std::vector<Component> & components, const MetaString & text);
+	void showOkDialog(std::vector<Component> & components, const MetaString & text, const boost::function<void()> & onOk);
 	void showYesNoDialog(const std::string &text, CFunctionList<void()> onYes, CFunctionList<void()> onNo, bool DelComps = false, const std::vector<CComponent*> & components = std::vector<CComponent*>()); //deactivateCur - whether current main interface should be deactivated; delComps - if components will be deleted on window close
+
 	void stopMovement();
 	bool moveHero(const CGHeroInstance *h, CGPath path);
 	void initMovement(const TryMoveHero &details, const CGHeroInstance * ho, const int3 &hp );//initializing objects and performing first step of move

+ 0 - 45
client/FunctionList.h

@@ -83,48 +83,3 @@ public:
 		}
 	}
 };
-
-template<typename Signature>
-class CFunctionList2
-{
-public:
-	std::vector<boost::function<Signature> > funcs;
-
-	CFunctionList2(int){};
-	CFunctionList2(){};
-	template <typename Functor> 
-	CFunctionList2(const Functor &f)
-	{
-		funcs.push_back(boost::function<Signature>(f));
-	}
-	CFunctionList2(const boost::function<Signature> &first)
-	{
-		funcs.push_back(first);
-	}
-	CFunctionList2(boost::function<Signature> &first)
-	{
-		funcs.push_back(first);
-	}
-	CFunctionList2 & operator+=(const boost::function<Signature> &first)
-	{
-		funcs.push_back(first);
-		return *this;
-	}
-	void clear()
-	{
-		funcs.clear();
-	}
-	operator bool() const
-	{
-		return funcs.size();
-	}
-	template <typename Arg> 
-	void operator()(const Arg & a) const
-	{
-		std::vector<boost::function<Signature> > funcs2 = funcs; //backup
-		for(size_t i=0;i<funcs2.size(); ++i) 
-		{
-			funcs2[i](a);
-		}
-	}
-};

+ 10 - 0
client/GUIClasses.cpp

@@ -742,6 +742,16 @@ void CInfoWindow::showYesNoDialog(const std::string & text, const std::vector<CC
 	GH.pushInt(temp);
 }
 
+void CInfoWindow::showOkDialog(const std::string & text, const std::vector<CComponent*> *components, const boost::function<void()> & onOk, bool delComps, PlayerColor player)
+{
+	std::vector<std::pair<std::string,CFunctionList<void()> > > pom;
+	pom.push_back(std::pair<std::string,CFunctionList<void()> >("IOKAY.DEF",0));
+	CInfoWindow * temp = new CInfoWindow(text, player, *components, pom, delComps);
+	temp->buttons[0]->callback += onOk;
+
+	GH.pushInt(temp);
+}
+
 CInfoWindow * CInfoWindow::create(const std::string &text, PlayerColor playerID /*= 1*/, const std::vector<CComponent*> *components /*= nullptr*/, bool DelComps)
 {
 	std::vector<std::pair<std::string,CFunctionList<void()> > > pom;

+ 1 - 0
client/GUIClasses.h

@@ -106,6 +106,7 @@ public:
 
 	//use only before the game starts! (showYesNoDialog in LOCPLINT must be used then)
 	static void showInfoDialog( const std::string & text, const std::vector<CComponent*> *components, bool DelComps = true, PlayerColor player = PlayerColor(1));
+	static void showOkDialog(const std::string & text, const std::vector<CComponent*> *components, const boost::function<void()> & onOk, bool delComps = true, PlayerColor player = PlayerColor(1));
 	static void showYesNoDialog( const std::string & text, const std::vector<CComponent*> *components, const CFunctionList<void( ) > &onYes, const CFunctionList<void()> &onNo, bool DelComps = true, PlayerColor player = PlayerColor(1));
 	static CInfoWindow *create(const std::string &text, PlayerColor playerID = PlayerColor(1), const std::vector<CComponent*> *components = nullptr, bool DelComps = false);
 

+ 1 - 1
client/gui/CIntObjectClasses.cpp

@@ -534,7 +534,7 @@ void CHighlightableButtonsGroup::addButton(const std::map<int,std::string> &tool
 	buttons.push_back(bt);
 }
 
-CHighlightableButtonsGroup::CHighlightableButtonsGroup(const CFunctionList2<void(int)> &OnChange, bool musicLikeButtons)
+CHighlightableButtonsGroup::CHighlightableButtonsGroup(const CFunctionList<void(int)> &OnChange, bool musicLikeButtons)
 : onChange(OnChange), musicLike(musicLikeButtons)
 {}
 

+ 2 - 2
client/gui/CIntObjectClasses.h

@@ -171,14 +171,14 @@ public:
 class CHighlightableButtonsGroup : public CIntObject
 {
 public:
-	CFunctionList2<void(int)> onChange; //called when changing selected button with new button's id
+	CFunctionList<void(int)> onChange; //called when changing selected button with new button's id
 	std::vector<CHighlightableButton*> buttons;
 	bool musicLike; //determines the behaviour of this group
 
 	//void addButton(const std::map<int,std::string> &tooltip, const std::string &HelpBox, const std::string &defName, int x, int y, int uid);
 	void addButton(CHighlightableButton* bt);//add existing button, it'll be deleted by CHighlightableButtonsGroup destructor
 	void addButton(const std::map<int,std::string> &tooltip, const std::string &HelpBox, const std::string &defName, int x, int y, int uid, const CFunctionList<void()> &OnSelect=0, int key=0); //creates new button
-	CHighlightableButtonsGroup(const CFunctionList2<void(int)> &OnChange, bool musicLikeButtons = false);
+	CHighlightableButtonsGroup(const CFunctionList<void(int)> & OnChange, bool musicLikeButtons = false);
 	~CHighlightableButtonsGroup();
 	void select(int id, bool mode); //mode==0: id is serial; mode==1: id is unique button id
 	void selectionChanged(int to);

+ 12 - 8
lib/CGameState.cpp

@@ -3327,15 +3327,19 @@ bool EVictoryLossCheckResult::loss() const
 	return !victory();
 }
 
-std::ostream & operator<<(std::ostream & os, const EVictoryLossCheckResult & victoryLossCheckResult)
+std::string EVictoryLossCheckResult::toString() const
 {
-	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";
+	if(*this == EVictoryLossCheckResult::NO_VICTORY_OR_LOSS) return "No victory or loss";
+	else if(*this == EVictoryLossCheckResult::VICTORY_STANDARD) return "Victory standard";
+	else if(*this == EVictoryLossCheckResult::VICTORY_SPECIAL) return "Victory special";
+	else if(*this == EVictoryLossCheckResult::LOSS_STANDARD_HEROES_AND_TOWNS) return "Loss standard heroes and towns";
+	else if(*this == EVictoryLossCheckResult::LOSS_STANDARD_TOWNS_AND_TIME_OVER) return "Loss standard towns and time over";
+	else if(*this == EVictoryLossCheckResult::LOSS_SPECIAL) return "Loss special";
+	else return "Unknown type";
+}
 
+std::ostream & operator<<(std::ostream & os, const EVictoryLossCheckResult & victoryLossCheckResult)
+{
+	os << victoryLossCheckResult.toString();
 	return os;
 }

+ 1 - 0
lib/CGameState.h

@@ -371,6 +371,7 @@ public:
 	bool operator!=(EVictoryLossCheckResult const & other) const;
 	bool victory() const;
 	bool loss() const;
+	std::string toString() const;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{

+ 0 - 1
lib/IGameCallback.h

@@ -2,7 +2,6 @@
 
 
 #include "BattleHex.h"
-#include "../client/FunctionList.h"
 #include "ResourceSet.h"
 #include "int3.h"
 #include "GameConstants.h"

+ 47 - 68
server/CGameHandler.cpp

@@ -685,7 +685,9 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result )
 	}
 	visitObjectAfterVictory = false;
 
-	winLoseHandle(1<<finishingBattle->loser.getNum() | 1<<finishingBattle->victor.getNum()); //handle victory/loss of engaged players
+	//handle victory/loss of engaged players
+	std::set<PlayerColor> playerColors = boost::assign::list_of(finishingBattle->loser)(finishingBattle->victor);
+	checkVictoryLossConditions(playerColors);
 
 	if(result.result == BattleResult::SURRENDER || result.result == BattleResult::ESCAPE) //loser has escaped or surrendered
 	{
@@ -1411,39 +1413,7 @@ void CGameHandler::newTurn()
 			elem->newTurn();
 	}
 
-	winLoseHandle(0xff);
-
-	//warn players without town
-	if(gs->day)
-	{
-		for (auto i=gs->players.cbegin() ; i!=gs->players.cend();i++)
-		{
-			if(i->second.status || i->second.towns.size() || i->second.color >= PlayerColor::PLAYER_LIMIT)
-				continue;
-
-			InfoWindow iw;
-			iw.player = i->first;
-			iw.components.push_back(Component(Component::FLAG, i->first.getNum(), 0, 0));
-
-			if(!i->second.daysWithoutCastle)
-			{
-				iw.text.addTxt(MetaString::GENERAL_TXT,6); //%s, you have lost your last town.  If you do not conquer another town in the next week, you will be eliminated.
-				iw.text.addReplacement(MetaString::COLOR, i->first.getNum());
-			}
-			else if(i->second.daysWithoutCastle == 6)
-			{
-				iw.text.addTxt(MetaString::ARRAY_TXT,129); //%s, this is your last day to capture a town or you will be banished from this land.
-				iw.text.addReplacement(MetaString::COLOR, i->first.getNum());
-			}
-			else
-			{
-				iw.text.addTxt(MetaString::ARRAY_TXT,128); //%s, you only have %d days left to capture a town or you will be banished from this land.
-				iw.text.addReplacement(MetaString::COLOR, i->first.getNum());
-				iw.text.addReplacement(7 - i->second.daysWithoutCastle);
-			}
-			sendAndApply(&iw);
-		}
-	}
+	checkVictoryLossConditionsForAll();
 
 	synchronizeArtifactHandlerLists(); //new day events may have changed them. TODO better of managing that
 }
@@ -1515,27 +1485,21 @@ void CGameHandler::run(bool resume)
 		resume = false;
 		for(; i != gs->players.end(); i++)
 		{
-			if((i->second.towns.size()==0 && i->second.heroes.size()==0)
-				|| i->first>=PlayerColor::PLAYER_LIMIT
-				|| i->second.status)
+			if(i->second.status == EPlayerStatus::INGAME)
 			{
-				continue;
-			}
-			states.setFlag(i->first,&PlayerStatus::makingTurn,true);
+				states.setFlag(i->first,&PlayerStatus::makingTurn,true);
 
-			{
 				YourTurn yt;
 				yt.player = i->first;
 				applyAndSend(&yt);
-			}
-
-			//wait till turn is done
-			boost::unique_lock<boost::mutex> lock(states.mx);
-			while(states.players.at(i->first).makingTurn && !end2)
-			{
-				static time_duration p = milliseconds(200);
-				states.cv.timed_wait(lock,p);
 
+				//wait till turn is done
+				boost::unique_lock<boost::mutex> lock(states.mx);
+				while(states.players.at(i->first).makingTurn && !end2)
+				{
+					static time_duration p = milliseconds(200);
+					states.cv.timed_wait(lock,p);
+				}
 			}
 		}
 	}
@@ -1608,7 +1572,7 @@ bool CGameHandler::removeObject( const CGObjectInstance * obj )
 	ro.id = obj->id;
 	sendAndApply(&ro);
 
-	winLoseHandle(255); //eg if monster escaped (removing objs after battle is done dircetly by endBattle, not this function)
+	checkVictoryLossConditionsForAll(); //eg if monster escaped (removing objs after battle is done dircetly by endBattle, not this function)
 	return true;
 }
 
@@ -1811,7 +1775,9 @@ void CGameHandler::setOwner(const CGObjectInstance * obj, PlayerColor owner)
 	SetObjectProperty sop(obj->id, 1, owner.getNum());
 	sendAndApply(&sop);
 
-	winLoseHandle(1<<owner.getNum() | 1<<oldOwner.getNum());
+	std::set<PlayerColor> playerColors = boost::assign::list_of(owner)(oldOwner);
+	checkVictoryLossConditions(playerColors);
+
 	if(owner < PlayerColor::PLAYER_LIMIT && dynamic_cast<const CGTownInstance *>(obj)) //town captured
 	{
 		const CGTownInstance * town = dynamic_cast<const CGTownInstance *>(obj);
@@ -1933,7 +1899,7 @@ void CGameHandler::heroVisitCastle(const CGTownInstance * obj, const CGHeroInsta
 	giveSpells (obj, hero);
 
 	if(gs->map->victoryCondition.condition == EVictoryConditionType::TRANSPORTITEM)
-		checkLossVictory(hero->tempOwner); //transported artifact?
+		checkVictoryLossConditionsForPlayer(hero->tempOwner); //transported artifact?
 }
 
 void CGameHandler::vistiCastleObjects (const CGTownInstance *t, const CGHeroInstance *h)
@@ -2178,28 +2144,28 @@ void CGameHandler::sendAndApply(CGarrisonOperationPack * info)
 {
 	sendAndApply(static_cast<CPackForClient*>(info));
 	if(gs->map->victoryCondition.condition == EVictoryConditionType::GATHERTROOP)
-		winLoseHandle();
+		checkVictoryLossConditionsForAll();
 }
 
 void CGameHandler::sendAndApply( SetResource * info )
 {
 	sendAndApply(static_cast<CPackForClient*>(info));
 	if(gs->map->victoryCondition.condition == EVictoryConditionType::GATHERRESOURCE)
-		checkLossVictory(info->player);
+		checkVictoryLossConditionsForPlayer(info->player);
 }
 
 void CGameHandler::sendAndApply( SetResources * info )
 {
 	sendAndApply(static_cast<CPackForClient*>(info));
 	if(gs->map->victoryCondition.condition == EVictoryConditionType::GATHERRESOURCE)
-		checkLossVictory(info->player);
+		checkVictoryLossConditionsForPlayer(info->player);
 }
 
 void CGameHandler::sendAndApply( NewStructures * info )
 {
 	sendAndApply(static_cast<CPackForClient*>(info));
 	if(gs->map->victoryCondition.condition == EVictoryConditionType::BUILDCITY)
-		checkLossVictory(getTown(info->tid)->tempOwner);
+		checkVictoryLossConditionsForPlayer(getTown(info->tid)->tempOwner);
 }
 
 void CGameHandler::save(const std::string & filename )
@@ -2525,7 +2491,7 @@ bool CGameHandler::buildStructure( ObjectInstanceID tid, BuildingID requestedID,
 	if(t->garrisonHero)
 		vistiCastleObjects (t, t->garrisonHero);
 
-	checkLossVictory(t->tempOwner);
+	checkVictoryLossConditionsForPlayer(t->tempOwner);
 	return true;
 }
 bool CGameHandler::razeStructure (ObjectInstanceID tid, BuildingID bid)
@@ -3927,7 +3893,7 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message
 	{
 		SystemMessage temp_message(VLC->generaltexth->allTexts.at(260));
 		sendAndApply(&temp_message);
-		checkLossVictory(player);//Player enter win code or got required art\creature
+		checkVictoryLossConditionsForPlayer(player);//Player enter win code or got required art\creature
 	}
 }
 
@@ -5055,21 +5021,28 @@ void CGameHandler::engageIntoBattle( PlayerColor player )
 	sendAndApply(&pb);
 }
 
-void CGameHandler::winLoseHandle(ui8 players )
+void CGameHandler::checkVictoryLossConditions(const std::set<PlayerColor> & playerColors)
 {
-	for(size_t i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++)
+	for(auto playerColor : playerColors)
 	{
-		if(players & 1<<i  &&  gs->getPlayer(PlayerColor(i)))
-		{
-			checkLossVictory(PlayerColor(i));
-		}
+		if(gs->getPlayer(playerColor)) checkVictoryLossConditionsForPlayer(playerColor);
 	}
 }
 
-void CGameHandler::checkLossVictory( PlayerColor player )
+void CGameHandler::checkVictoryLossConditionsForAll()
+{
+	std::set<PlayerColor> playerColors;
+	for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; ++i)
+	{
+		playerColors.insert(PlayerColor(i));
+	}
+	checkVictoryLossConditions(playerColors);
+}
+
+void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
 {
 	const PlayerState *p = gs->getPlayer(player);
-	if(p->status == EPlayerStatus::WINNER || p->status == EPlayerStatus::LOSER || p->status == EPlayerStatus::WRONG) return;
+	if(p->status != EPlayerStatus::INGAME) return;
 
 	auto victoryLossCheckResult = gs->checkForVictoryAndLoss(player);
 
@@ -5091,7 +5064,7 @@ void CGameHandler::checkLossVictory( PlayerColor player )
 
 			for (auto i = gs->players.cbegin(); i!=gs->players.cend(); i++)
 			{
-				if(i->first < PlayerColor::PLAYER_LIMIT && i->first != player)//FIXME: skip already eliminated players?
+				if(i->first != player && gs->getPlayer(i->first)->status == EPlayerStatus::INGAME)
 				{
 					iw.player = i->first;
 					sendAndApply(&iw);
@@ -5150,7 +5123,13 @@ void CGameHandler::checkLossVictory( PlayerColor player )
 			}
 
 			//eliminating one player may cause victory of another:
-			winLoseHandle(GameConstants::ALL_PLAYERS & ~(1<<player.getNum()));
+			std::set<PlayerColor> playerColors;
+			for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; ++i)
+			{
+				if(player.getNum() != i) playerColors.insert(PlayerColor(i));
+			}
+
+			checkVictoryLossConditions(playerColors);
 		}
 
 		//If player making turn has lost his turn must be over as well

+ 5 - 2
server/CGameHandler.h

@@ -106,8 +106,6 @@ public:
 	void giveSpells(const CGTownInstance *t, const CGHeroInstance *h);
 	int moveStack(int stack, BattleHex dest); //returned value - travelled distance
 	void runBattle();
-	void checkLossVictory(PlayerColor player);
-	void winLoseHandle(ui8 players=255); //players: bit field - colours of players to be checked; default: all
 
 	////used only in endBattle - don't touch elsewhere
 	bool visitObjectAfterVictory;
@@ -293,6 +291,11 @@ public:
 private:
 	void makeStackDoNothing(const CStack * next);
 	void getVictoryLossMessage(PlayerColor player, EVictoryLossCheckResult victoryLossCheckResult, InfoWindow & out) const;
+
+	// Check for victory and loss conditions
+	void checkVictoryLossConditionsForPlayer(PlayerColor player);
+	void checkVictoryLossConditions(const std::set<PlayerColor> & playerColors);
+	void checkVictoryLossConditionsForAll();
 };
 
 void makeStackDoNothing();