mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Implement turn timer feature
This commit is contained in:
		| @@ -12,6 +12,7 @@ set(client_SRCS | ||||
| 	adventureMap/CMinimap.cpp | ||||
| 	adventureMap/CResDataBar.cpp | ||||
| 	adventureMap/MapAudioPlayer.cpp | ||||
| 	adventureMap/TurnTimerWidget.cpp | ||||
|  | ||||
| 	battle/BattleActionsController.cpp | ||||
| 	battle/BattleAnimationClasses.cpp | ||||
| @@ -157,6 +158,7 @@ set(client_HEADERS | ||||
| 	adventureMap/CMinimap.h | ||||
| 	adventureMap/CResDataBar.h | ||||
| 	adventureMap/MapAudioPlayer.h | ||||
| 	adventureMap/TurnTimerWidget.h | ||||
|  | ||||
| 	battle/BattleActionsController.h | ||||
| 	battle/BattleAnimationClasses.h | ||||
|   | ||||
| @@ -94,6 +94,7 @@ public: | ||||
| 	void visitSystemMessage(SystemMessage & pack) override; | ||||
| 	void visitPlayerBlocked(PlayerBlocked & pack) override; | ||||
| 	void visitYourTurn(YourTurn & pack) override; | ||||
| 	void visitTurnTimeUpdate(TurnTimeUpdate & pack) override; | ||||
| 	void visitPlayerMessageClient(PlayerMessageClient & pack) override; | ||||
| 	void visitAdvmapSpellCast(AdvmapSpellCast & pack) override; | ||||
| 	void visitShowWorldViewEx(ShowWorldViewEx & pack) override;	 | ||||
|   | ||||
| @@ -864,6 +864,11 @@ void ApplyClientNetPackVisitor::visitYourTurn(YourTurn & pack) | ||||
| 	callOnlyThatInterface(cl, pack.player, &CGameInterface::yourTurn); | ||||
| } | ||||
|  | ||||
| void ApplyClientNetPackVisitor::visitTurnTimeUpdate(TurnTimeUpdate & pack) | ||||
| { | ||||
| 	logNetwork->debug("Server sets %d turn time for %s", pack.turnTime, pack.player.getStr()); | ||||
| } | ||||
|  | ||||
| void ApplyClientNetPackVisitor::visitPlayerMessageClient(PlayerMessageClient & pack) | ||||
| { | ||||
| 	logNetwork->debug("pack.player %s sends a message: %s", pack.player.getStr(), pack.text); | ||||
|   | ||||
| @@ -17,6 +17,7 @@ | ||||
| #include "CList.h" | ||||
| #include "CInfoBar.h" | ||||
| #include "MapAudioPlayer.h" | ||||
| #include "TurnTimerWidget.h" | ||||
| #include "AdventureMapWidget.h" | ||||
| #include "AdventureMapShortcuts.h" | ||||
|  | ||||
| @@ -35,6 +36,7 @@ | ||||
|  | ||||
| #include "../../CCallback.h" | ||||
| #include "../../lib/CConfigHandler.h" | ||||
| #include "../../lib/StartInfo.h" | ||||
| #include "../../lib/CGeneralTextHandler.h" | ||||
| #include "../../lib/spells/CSpellHandler.h" | ||||
| #include "../../lib/mapObjects/CGHeroInstance.h" | ||||
| @@ -61,6 +63,9 @@ AdventureMapInterface::AdventureMapInterface(): | ||||
| 	shortcuts->setState(EAdventureState::MAKING_TURN); | ||||
| 	widget->getMapView()->onViewMapActivated(); | ||||
|  | ||||
| 	if(LOCPLINT->cb->getStartInfo()->turnTime > 0) | ||||
| 		watches = std::make_shared<TurnTimerWidget>(); | ||||
| 	 | ||||
| 	addUsedEvents(KEYBOARD | TIME); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -39,6 +39,7 @@ class CTownList; | ||||
| class CInfoBar; | ||||
| class CMinimap; | ||||
| class MapAudioPlayer; | ||||
| class TurnTimerWidget; | ||||
| enum class EAdventureState; | ||||
|  | ||||
| struct MapDrawingInfo; | ||||
| @@ -64,6 +65,7 @@ private: | ||||
| 	std::shared_ptr<MapAudioPlayer> mapAudio; | ||||
| 	std::shared_ptr<AdventureMapWidget> widget; | ||||
| 	std::shared_ptr<AdventureMapShortcuts> shortcuts; | ||||
| 	std::shared_ptr<TurnTimerWidget> watches; | ||||
|  | ||||
| private: | ||||
| 	void setState(EAdventureState state); | ||||
|   | ||||
| @@ -314,7 +314,7 @@ void AdventureMapShortcuts::visitObject() | ||||
| 	const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); | ||||
|  | ||||
| 	if(h) | ||||
| 		LOCPLINT->cb->moveHero(h,h->pos); | ||||
| 		LOCPLINT->cb->moveHero(h, h->pos); | ||||
| } | ||||
|  | ||||
| void AdventureMapShortcuts::openObject() | ||||
|   | ||||
							
								
								
									
										73
									
								
								client/adventureMap/TurnTimerWidget.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								client/adventureMap/TurnTimerWidget.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| /* | ||||
|  * TurnTimerWidget.cpp, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "TurnTimerWidget.h" | ||||
|  | ||||
| #include "../CPlayerInterface.h" | ||||
| #include "../render/Canvas.h" | ||||
| #include "../render/Colors.h" | ||||
| #include "../render/EFont.h" | ||||
| #include "../gui/CGuiHandler.h" | ||||
| #include "../gui/TextAlignment.h" | ||||
| #include "../widgets/Images.h" | ||||
| #include "../widgets/TextControls.h" | ||||
| #include "../../CCallback.h" | ||||
| #include "../../lib/CPlayerState.h" | ||||
|  | ||||
| #include <SDL_render.h> | ||||
|  | ||||
| TurnTimerWidget::TurnTimerWidget(): | ||||
| 	CIntObject(TIME), | ||||
| 	turnTime(0), lastTurnTime(0) | ||||
| { | ||||
| 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); | ||||
| 	 | ||||
| 	watches = std::make_shared<CAnimImage>("VCMI/BATTLEQUEUE/STATESSMALL", 1, 0, 4, 6); | ||||
| 	label = std::make_shared<CLabel>(24, 2, FONT_BIG, ETextAlignment::TOPLEFT, Colors::YELLOW, std::to_string(turnTime)); | ||||
| 	 | ||||
| 	recActions &= ~DEACTIVATE; | ||||
| } | ||||
|  | ||||
| void TurnTimerWidget::showAll(Canvas & to) | ||||
| { | ||||
| 	to.drawColor(Rect(4, 4, 68, 24), ColorRGBA(10, 10, 10, 255)); | ||||
| 	 | ||||
| 	CIntObject::showAll(to); | ||||
| } | ||||
|  | ||||
| void TurnTimerWidget::show(Canvas & to) | ||||
| { | ||||
| 	showAll(to); | ||||
| } | ||||
|  | ||||
| void TurnTimerWidget::setTime(int time) | ||||
| { | ||||
| 	turnTime = time / 1000; | ||||
| 	std::ostringstream oss; | ||||
| 	oss << turnTime / 60 << ":" << std::setw(2) << std::setfill('0') << turnTime % 60; | ||||
| 	label->setText(oss.str()); | ||||
| } | ||||
|  | ||||
| void TurnTimerWidget::tick(uint32_t msPassed) | ||||
| { | ||||
| 	if(LOCPLINT && LOCPLINT->cb && !LOCPLINT->battleInt) | ||||
| 	{ | ||||
| 		auto player = LOCPLINT->cb->getCurrentPlayer(); | ||||
| 		auto time = LOCPLINT->cb->getPlayerTurnTime(player); | ||||
| 		cachedTurnTime -= msPassed; | ||||
| 		if(time / 1000 != lastTurnTime / 1000) | ||||
| 		{ | ||||
| 			//do not update timer on this tick | ||||
| 			lastTurnTime = time; | ||||
| 			cachedTurnTime = time; | ||||
| 		} | ||||
| 		else setTime(cachedTurnTime); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										39
									
								
								client/adventureMap/TurnTimerWidget.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								client/adventureMap/TurnTimerWidget.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| /* | ||||
|  * TurnTimerWidget.hpp, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "../gui/CIntObject.h" | ||||
|  | ||||
| class CAnimImage; | ||||
| class CLabel; | ||||
|  | ||||
| class TurnTimerWidget : public CIntObject | ||||
| { | ||||
| private: | ||||
|  | ||||
| 	int turnTime; | ||||
| 	int lastTurnTime; | ||||
| 	int cachedTurnTime; | ||||
| 	 | ||||
| 	std::shared_ptr<CAnimImage> watches; | ||||
| 	std::shared_ptr<CLabel> label; | ||||
|  | ||||
| public: | ||||
|  | ||||
| 	//void tick(uint32_t msPassed) override; | ||||
| 	void show(Canvas & to) override; | ||||
| 	void showAll(Canvas & to) override; | ||||
| 	void tick(uint32_t msPassed) override; | ||||
| 	 | ||||
| 	void setTime(int time); | ||||
|  | ||||
| 	TurnTimerWidget(); | ||||
| }; | ||||
| @@ -99,6 +99,22 @@ const PlayerState * CGameInfoCallback::getPlayerState(PlayerColor color, bool ve | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int CGameInfoCallback::getPlayerTurnTime(PlayerColor color) const | ||||
| { | ||||
| 	if(!color.isValidPlayer()) | ||||
| 	{ | ||||
| 		return 0; | ||||
| 	} | ||||
| 	 | ||||
| 	auto player = gs->players.find(color); | ||||
| 	if(player != gs->players.end()) | ||||
| 	{ | ||||
| 		return player->second.turnTime; | ||||
| 	} | ||||
| 	 | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| const CGObjectInstance * CGameInfoCallback::getObjByQuestIdentifier(int identifier) const | ||||
| { | ||||
| 	if(gs->map->questIdentifierToId.empty()) | ||||
|   | ||||
| @@ -153,6 +153,7 @@ public: | ||||
| 	virtual PlayerColor getCurrentPlayer() const; //player that currently makes move // TODO synchronous turns | ||||
| 	PlayerColor getLocalPlayer() const override; //player that is currently owning given client (if not a client, then returns current player) | ||||
| 	virtual const PlayerSettings * getPlayerSettings(PlayerColor color) const; | ||||
| 	virtual int getPlayerTurnTime(PlayerColor color) const; | ||||
|  | ||||
| 	//map | ||||
| 	virtual bool isVisible(int3 pos, const std::optional<PlayerColor> & Player) const; | ||||
|   | ||||
| @@ -39,6 +39,7 @@ public: | ||||
| 	bool enteredWinningCheatCode, enteredLosingCheatCode; //if true, this player has entered cheat codes for loss / victory | ||||
| 	EPlayerStatus::EStatus status; | ||||
| 	std::optional<ui8> daysWithoutCastle; | ||||
| 	int turnTime = 0; | ||||
|  | ||||
| 	PlayerState(); | ||||
| 	PlayerState(PlayerState && other) noexcept; | ||||
| @@ -71,6 +72,7 @@ public: | ||||
| 		h & team; | ||||
| 		h & resources; | ||||
| 		h & status; | ||||
| 		h & turnTime; | ||||
| 		h & heroes; | ||||
| 		h & towns; | ||||
| 		h & dwellings; | ||||
|   | ||||
| @@ -27,6 +27,7 @@ public: | ||||
| 	virtual void visitPlayerBlocked(PlayerBlocked & pack) {} | ||||
| 	virtual void visitPlayerCheated(PlayerCheated & pack) {} | ||||
| 	virtual void visitYourTurn(YourTurn & pack) {} | ||||
| 	virtual void visitTurnTimeUpdate(TurnTimeUpdate & pack) {} | ||||
| 	virtual void visitEntitiesChanged(EntitiesChanged & pack) {} | ||||
| 	virtual void visitSetResources(SetResources & pack) {} | ||||
| 	virtual void visitSetPrimSkill(SetPrimSkill & pack) {} | ||||
|   | ||||
| @@ -147,6 +147,20 @@ struct DLL_LINKAGE PlayerCheated : public CPackForClient | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE TurnTimeUpdate : public CPackForClient | ||||
| { | ||||
| 	void applyGs(CGameState * gs) const; | ||||
| 	 | ||||
| 	PlayerColor player; | ||||
| 	int turnTime = 0; | ||||
| 		 | ||||
| 	template <typename Handler> void serialize(Handler & h, const int version) | ||||
| 	{ | ||||
| 		h & player; | ||||
| 		h & turnTime; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE YourTurn : public CPackForClient | ||||
| { | ||||
| 	void applyGs(CGameState * gs) const; | ||||
|   | ||||
| @@ -2513,6 +2513,12 @@ void YourTurn::applyGs(CGameState * gs) const | ||||
| 	playerState.daysWithoutCastle = daysWithoutCastle; | ||||
| } | ||||
|  | ||||
| void TurnTimeUpdate::applyGs(CGameState *gs) const | ||||
| { | ||||
| 	auto & playerState = gs->players[player]; | ||||
| 	playerState.turnTime = turnTime; | ||||
| } | ||||
|  | ||||
| Component::Component(const CStackBasicDescriptor & stack) | ||||
| 	: id(EComponentType::CREATURE) | ||||
| 	, subtype(stack.type->getId()) | ||||
|   | ||||
| @@ -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, TurnTimeUpdate>(); | ||||
| 	s.template registerType<CPackForClient, SetResources>(); | ||||
| 	s.template registerType<CPackForClient, SetPrimSkill>(); | ||||
| 	s.template registerType<CPackForClient, SetSecSkill>(); | ||||
|   | ||||
| @@ -2067,6 +2067,14 @@ void CGameHandler::run(bool resume) | ||||
| 					//Change local daysWithoutCastle counter for local interface message //TODO: needed? | ||||
| 					yt.daysWithoutCastle = playerState->daysWithoutCastle; | ||||
| 					applyAndSend(&yt); | ||||
| 					 | ||||
| 					if(gs->getStartInfo()->turnTime > 0) //turn timer check | ||||
| 					{ | ||||
| 						TurnTimeUpdate ttu; | ||||
| 						ttu.player = player; | ||||
| 						ttu.turnTime = gs->getStartInfo()->turnTime * 60 * 1000; //ms | ||||
| 						applyAndSend(&ttu); | ||||
| 					} | ||||
| 				} | ||||
| 			}; | ||||
|  | ||||
| @@ -2075,10 +2083,31 @@ void CGameHandler::run(bool resume) | ||||
| 			if(playerColor != PlayerColor::CANNOT_DETERMINE) | ||||
| 			{ | ||||
| 				//wait till turn is done | ||||
| 				const int waitTime = 100; //ms | ||||
| 				int turnTimePropagateFrequency = 5000; //do not send updates too frequently | ||||
| 				boost::unique_lock<boost::mutex> lock(states.mx); | ||||
| 				while(states.players.at(playerColor).makingTurn && lobby->state == EServerState::GAMEPLAY) | ||||
| 				{ | ||||
| 					static time_duration p = milliseconds(100); | ||||
| 					if(gs->getStartInfo()->turnTime > 0 && !gs->curB) //turn timer check | ||||
| 					{ | ||||
| 						if(gs->players[playerColor].turnTime > 0) | ||||
| 						{ | ||||
| 							gs->players[playerColor].turnTime -= waitTime; | ||||
| 							 | ||||
| 							if(gs->players[playerColor].status == EPlayerStatus::INGAME //do not send message if player is not active already | ||||
| 							   && gs->players[playerColor].turnTime % turnTimePropagateFrequency == 0) | ||||
| 							{ | ||||
| 								TurnTimeUpdate ttu; | ||||
| 								ttu.player = playerColor; | ||||
| 								ttu.turnTime = gs->players[playerColor].turnTime; | ||||
| 								applyAndSend(&ttu); | ||||
| 							} | ||||
| 						} | ||||
| 						else if(!queries.topQuery(playerColor)) //wait for replies to avoid pending queries | ||||
| 							states.players.at(playerColor).makingTurn = false; //force end turn | ||||
| 					} | ||||
| 					 | ||||
| 					static time_duration p = milliseconds(waitTime); | ||||
| 					states.cv.timed_wait(lock, p); | ||||
| 				} | ||||
| 			} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user