فهرست منبع

Fix handling of "7 days without town" loss condition

Ivan Savenko 2 سال پیش
والد
کامیت
f9410145d6

+ 0 - 1
client/ClientCommandManager.cpp

@@ -471,7 +471,6 @@ void ClientCommandManager::giveTurn(const PlayerColor &colorIdentifier)
 {
 	YourTurn yt;
 	yt.player = colorIdentifier;
-	yt.daysWithoutCastle = CSH->client->getPlayerState(colorIdentifier)->daysWithoutCastle;
 
 	ApplyClientNetPackVisitor visitor(*CSH->client, *CSH->client->gameState());
 	yt.visit(visitor);

+ 1 - 0
lib/NetPackVisitor.h

@@ -27,6 +27,7 @@ public:
 	virtual void visitPlayerBlocked(PlayerBlocked & pack) {}
 	virtual void visitPlayerCheated(PlayerCheated & pack) {}
 	virtual void visitYourTurn(YourTurn & pack) {}
+	virtual void visitYourTurn(DaysWithoutTown & pack) {}
 	virtual void visitTurnTimeUpdate(TurnTimeUpdate & pack) {}
 	virtual void visitEntitiesChanged(EntitiesChanged & pack) {}
 	virtual void visitSetResources(SetResources & pack) {}

+ 15 - 1
lib/NetPacks.h

@@ -167,7 +167,21 @@ struct DLL_LINKAGE YourTurn : public CPackForClient
 	void applyGs(CGameState * gs) const;
 
 	PlayerColor player;
-	std::optional<ui8> daysWithoutCastle;
+
+	virtual void visitTyped(ICPackVisitor & visitor) override;
+
+	template <typename Handler> void serialize(Handler & h, const int version)
+	{
+		h & player;
+	}
+};
+
+struct DLL_LINKAGE DaysWithoutTown : public CPackForClient
+{
+	void applyGs(CGameState * gs) const;
+
+	PlayerColor player;
+	std::optional<int32_t> daysWithoutCastle;
 
 	virtual void visitTyped(ICPackVisitor & visitor) override;
 

+ 18 - 22
lib/NetPacksLib.cpp

@@ -108,6 +108,11 @@ void YourTurn::visitTyped(ICPackVisitor & visitor)
 	visitor.visitYourTurn(*this);
 }
 
+void DaysWithoutTown::visitTyped(ICPackVisitor & visitor)
+{
+	visitor.visitYourTurn(*this);
+}
+
 void EntitiesChanged::visitTyped(ICPackVisitor & visitor)
 {
 	visitor.visitEntitiesChanged(*this);
@@ -2025,26 +2030,6 @@ void NewTurn::applyGs(CGameState *gs)
 
 	if(gs->getDate(Date::DAY_OF_WEEK) == 1)
 		gs->updateRumor();
-
-	//count days without town for all players, regardless of their turn order
-	for (auto &p : gs->players)
-	{
-		PlayerState & playerState = p.second;
-		if (playerState.status == EPlayerStatus::INGAME)
-		{
-			if (playerState.towns.empty())
-			{
-				if (playerState.daysWithoutCastle)
-					++(*playerState.daysWithoutCastle);
-				else
-					playerState.daysWithoutCastle = std::make_optional(0);
-			}
-			else
-			{
-				playerState.daysWithoutCastle = std::nullopt;
-			}
-		}
-	}
 }
 
 void SetObjectProperty::applyGs(CGameState * gs) const
@@ -2063,8 +2048,16 @@ void SetObjectProperty::applyGs(CGameState * gs) const
 		{
 			auto * t = dynamic_cast<CGTownInstance *>(obj);
 			assert(t);
-			if(t->tempOwner < PlayerColor::PLAYER_LIMIT)
-				gs->getPlayerState(t->tempOwner)->towns -= t;
+
+			PlayerColor oldOwner = t->tempOwner;
+			if(oldOwner.isValidPlayer())
+			{
+				auto * state = gs->getPlayerState(oldOwner);
+				state->towns -= t;
+
+				if(state->towns.empty())
+					*state->daysWithoutCastle = 0;
+			}
 			if(val < PlayerColor::PLAYER_LIMIT_I)
 			{
 				PlayerState * p = gs->getPlayerState(PlayerColor(val));
@@ -2509,7 +2502,10 @@ void PlayerCheated::applyGs(CGameState * gs) const
 void YourTurn::applyGs(CGameState * gs) const
 {
 	gs->currentPlayer = player;
+}
 
+void DaysWithoutTown::applyGs(CGameState * gs) const
+{
 	auto & playerState = gs->players[player];
 	playerState.daysWithoutCastle = daysWithoutCastle;
 }

+ 1 - 0
lib/registerTypes/RegisterTypes.h

@@ -232,6 +232,7 @@ void registerTypesClientPacks1(Serializer &s)
 	s.template registerType<CPackForClient, PlayerBlocked>();
 	s.template registerType<CPackForClient, PlayerCheated>();
 	s.template registerType<CPackForClient, YourTurn>();
+	s.template registerType<CPackForClient, DaysWithoutTown>();
 	s.template registerType<CPackForClient, TurnTimeUpdate>();
 	s.template registerType<CPackForClient, SetResources>();
 	s.template registerType<CPackForClient, SetPrimSkill>();

+ 22 - 1
server/CGameHandler.cpp

@@ -609,7 +609,28 @@ void CGameHandler::onPlayerTurnStarted(PlayerColor which)
 
 void CGameHandler::onPlayerTurnEnded(PlayerColor which)
 {
-	// 7 days without castle
+	const auto * playerState = gs->getPlayerState(which);
+	assert(playerState->status == EPlayerStatus::INGAME);
+
+	if (playerState->towns.empty())
+	{
+		DaysWithoutTown pack;
+		pack.player = which;
+		pack.daysWithoutCastle = playerState->daysWithoutCastle.value_or(0) + 1;
+		sendAndApply(&pack);
+	}
+	else
+	{
+		if (playerState->daysWithoutCastle.has_value())
+		{
+			DaysWithoutTown pack;
+			pack.player = which;
+			pack.daysWithoutCastle = std::nullopt;
+			sendAndApply(&pack);
+		}
+	}
+
+	// check for 7 days without castle
 	checkVictoryLossConditionsForPlayer(which);
 
 	bool newWeek = getDate(Date::DAY_OF_WEEK) == 7; // end of 7th day

+ 1 - 1
server/TurnTimerHandler.cpp

@@ -84,7 +84,7 @@ void TurnTimerHandler::onPlayerMakingTurn(PlayerState & state, int waitTime)
 			state.turnTimer.baseTimer = 0;
 			onPlayerMakingTurn(state, waitTime);
 		}
-		else if(!gameHandler.queries->topQuery(state.color))
+		else if(!gameHandler.queries->topQuery(state.color)) //wait for replies to avoid pending queries
 			gameHandler.turnOrder->onPlayerEndsTurn(state.color);
 	}
 }

+ 5 - 3
server/processors/TurnOrderProcessor.cpp

@@ -100,8 +100,6 @@ void TurnOrderProcessor::doStartPlayerTurn(PlayerColor which)
 
 	YourTurn yt;
 	yt.player = which;
-	//Change local daysWithoutCastle counter for local interface message //TODO: needed?
-	yt.daysWithoutCastle = gameHandler->getPlayerState(which)->daysWithoutCastle;
 	gameHandler->sendAndApply(&yt);
 
 	assert(actingPlayers.size() == 1); // No simturns yet :(
@@ -111,6 +109,7 @@ void TurnOrderProcessor::doStartPlayerTurn(PlayerColor which)
 void TurnOrderProcessor::doEndPlayerTurn(PlayerColor which)
 {
 	assert(playerMakingTurn(which));
+	assert(gameHandler->getPlayerStatus(which) == EPlayerStatus::INGAME);
 
 	actingPlayers.erase(which);
 	actedPlayers.insert(which);
@@ -170,7 +169,10 @@ bool TurnOrderProcessor::onPlayerEndsTurn(PlayerColor which)
 
 	gameHandler->onPlayerTurnEnded(which);
 
-	doEndPlayerTurn(which);
+	// it is possible that player have lost - e.g. spent 7 days without town
+	// in this case - don't call doEndPlayerTurn - turn transfer was already handled by onPlayerEndsGame
+	if(gameHandler->getPlayerStatus(which) == EPlayerStatus::INGAME)
+		doEndPlayerTurn(which);
 
 	return true;
 }

+ 7 - 10
server/processors/TurnOrderProcessor.h

@@ -37,6 +37,10 @@ class TurnOrderProcessor : boost::noncopyable
 	void doStartPlayerTurn(PlayerColor which);
 	void doEndPlayerTurn(PlayerColor which);
 
+	bool playerAwaitsTurn(PlayerColor which) const;
+	bool playerMakingTurn(PlayerColor which) const;
+	bool playerAwaitsNewDay(PlayerColor which) const;
+
 public:
 	TurnOrderProcessor(CGameHandler * owner);
 
@@ -46,21 +50,14 @@ public:
 	/// NetPack call-in
 	bool onPlayerEndsTurn(PlayerColor which);
 
+	/// Ends player turn and removes this player from turn order
 	void onPlayerEndsGame(PlayerColor which);
 
 	/// Start game (or resume from save) and send YourTurn pack to player(s)
 	void onGameStarted();
 
-	/// Returns true if player turn has not started today
-	bool playerAwaitsTurn(PlayerColor which) const;
-
-	/// Returns true if player is currently making his turn
-	bool playerMakingTurn(PlayerColor which) const;
-
-	/// Returns true if player has finished his turn and is waiting for new day
-	bool playerAwaitsNewDay(PlayerColor which) const;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
+	template<typename Handler>
+	void serialize(Handler & h, const int version)
 	{
 		h & awaitingPlayers;
 		h & actingPlayers;