1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +02:00

Implement turn timer feature

This commit is contained in:
nordsoft 2023-08-13 14:06:35 +04:00
parent f13a53c1d9
commit 4b1224ec8c
16 changed files with 199 additions and 2 deletions

View File

@ -12,6 +12,7 @@ set(client_SRCS
adventureMap/CMinimap.cpp adventureMap/CMinimap.cpp
adventureMap/CResDataBar.cpp adventureMap/CResDataBar.cpp
adventureMap/MapAudioPlayer.cpp adventureMap/MapAudioPlayer.cpp
adventureMap/TurnTimerWidget.cpp
battle/BattleActionsController.cpp battle/BattleActionsController.cpp
battle/BattleAnimationClasses.cpp battle/BattleAnimationClasses.cpp
@ -157,6 +158,7 @@ set(client_HEADERS
adventureMap/CMinimap.h adventureMap/CMinimap.h
adventureMap/CResDataBar.h adventureMap/CResDataBar.h
adventureMap/MapAudioPlayer.h adventureMap/MapAudioPlayer.h
adventureMap/TurnTimerWidget.h
battle/BattleActionsController.h battle/BattleActionsController.h
battle/BattleAnimationClasses.h battle/BattleAnimationClasses.h

View File

@ -94,6 +94,7 @@ public:
void visitSystemMessage(SystemMessage & pack) override; void visitSystemMessage(SystemMessage & pack) override;
void visitPlayerBlocked(PlayerBlocked & pack) override; void visitPlayerBlocked(PlayerBlocked & pack) override;
void visitYourTurn(YourTurn & pack) override; void visitYourTurn(YourTurn & pack) override;
void visitTurnTimeUpdate(TurnTimeUpdate & pack) override;
void visitPlayerMessageClient(PlayerMessageClient & pack) override; void visitPlayerMessageClient(PlayerMessageClient & pack) override;
void visitAdvmapSpellCast(AdvmapSpellCast & pack) override; void visitAdvmapSpellCast(AdvmapSpellCast & pack) override;
void visitShowWorldViewEx(ShowWorldViewEx & pack) override; void visitShowWorldViewEx(ShowWorldViewEx & pack) override;

View File

@ -864,6 +864,11 @@ void ApplyClientNetPackVisitor::visitYourTurn(YourTurn & pack)
callOnlyThatInterface(cl, pack.player, &CGameInterface::yourTurn); 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) void ApplyClientNetPackVisitor::visitPlayerMessageClient(PlayerMessageClient & pack)
{ {
logNetwork->debug("pack.player %s sends a message: %s", pack.player.getStr(), pack.text); logNetwork->debug("pack.player %s sends a message: %s", pack.player.getStr(), pack.text);

View File

@ -17,6 +17,7 @@
#include "CList.h" #include "CList.h"
#include "CInfoBar.h" #include "CInfoBar.h"
#include "MapAudioPlayer.h" #include "MapAudioPlayer.h"
#include "TurnTimerWidget.h"
#include "AdventureMapWidget.h" #include "AdventureMapWidget.h"
#include "AdventureMapShortcuts.h" #include "AdventureMapShortcuts.h"
@ -35,6 +36,7 @@
#include "../../CCallback.h" #include "../../CCallback.h"
#include "../../lib/CConfigHandler.h" #include "../../lib/CConfigHandler.h"
#include "../../lib/StartInfo.h"
#include "../../lib/CGeneralTextHandler.h" #include "../../lib/CGeneralTextHandler.h"
#include "../../lib/spells/CSpellHandler.h" #include "../../lib/spells/CSpellHandler.h"
#include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapObjects/CGHeroInstance.h"
@ -61,6 +63,9 @@ AdventureMapInterface::AdventureMapInterface():
shortcuts->setState(EAdventureState::MAKING_TURN); shortcuts->setState(EAdventureState::MAKING_TURN);
widget->getMapView()->onViewMapActivated(); widget->getMapView()->onViewMapActivated();
if(LOCPLINT->cb->getStartInfo()->turnTime > 0)
watches = std::make_shared<TurnTimerWidget>();
addUsedEvents(KEYBOARD | TIME); addUsedEvents(KEYBOARD | TIME);
} }

View File

@ -39,6 +39,7 @@ class CTownList;
class CInfoBar; class CInfoBar;
class CMinimap; class CMinimap;
class MapAudioPlayer; class MapAudioPlayer;
class TurnTimerWidget;
enum class EAdventureState; enum class EAdventureState;
struct MapDrawingInfo; struct MapDrawingInfo;
@ -64,6 +65,7 @@ private:
std::shared_ptr<MapAudioPlayer> mapAudio; std::shared_ptr<MapAudioPlayer> mapAudio;
std::shared_ptr<AdventureMapWidget> widget; std::shared_ptr<AdventureMapWidget> widget;
std::shared_ptr<AdventureMapShortcuts> shortcuts; std::shared_ptr<AdventureMapShortcuts> shortcuts;
std::shared_ptr<TurnTimerWidget> watches;
private: private:
void setState(EAdventureState state); void setState(EAdventureState state);

View File

@ -314,7 +314,7 @@ void AdventureMapShortcuts::visitObject()
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
if(h) if(h)
LOCPLINT->cb->moveHero(h,h->pos); LOCPLINT->cb->moveHero(h, h->pos);
} }
void AdventureMapShortcuts::openObject() void AdventureMapShortcuts::openObject()

View 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);
}
}

View 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();
};

View File

@ -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 const CGObjectInstance * CGameInfoCallback::getObjByQuestIdentifier(int identifier) const
{ {
if(gs->map->questIdentifierToId.empty()) if(gs->map->questIdentifierToId.empty())

View File

@ -153,6 +153,7 @@ public:
virtual PlayerColor getCurrentPlayer() const; //player that currently makes move // TODO synchronous turns 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) 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 const PlayerSettings * getPlayerSettings(PlayerColor color) const;
virtual int getPlayerTurnTime(PlayerColor color) const;
//map //map
virtual bool isVisible(int3 pos, const std::optional<PlayerColor> & Player) const; virtual bool isVisible(int3 pos, const std::optional<PlayerColor> & Player) const;

View File

@ -39,6 +39,7 @@ public:
bool enteredWinningCheatCode, enteredLosingCheatCode; //if true, this player has entered cheat codes for loss / victory bool enteredWinningCheatCode, enteredLosingCheatCode; //if true, this player has entered cheat codes for loss / victory
EPlayerStatus::EStatus status; EPlayerStatus::EStatus status;
std::optional<ui8> daysWithoutCastle; std::optional<ui8> daysWithoutCastle;
int turnTime = 0;
PlayerState(); PlayerState();
PlayerState(PlayerState && other) noexcept; PlayerState(PlayerState && other) noexcept;
@ -71,6 +72,7 @@ public:
h & team; h & team;
h & resources; h & resources;
h & status; h & status;
h & turnTime;
h & heroes; h & heroes;
h & towns; h & towns;
h & dwellings; h & dwellings;

View File

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

View File

@ -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 struct DLL_LINKAGE YourTurn : public CPackForClient
{ {
void applyGs(CGameState * gs) const; void applyGs(CGameState * gs) const;

View File

@ -2513,6 +2513,12 @@ void YourTurn::applyGs(CGameState * gs) const
playerState.daysWithoutCastle = daysWithoutCastle; playerState.daysWithoutCastle = daysWithoutCastle;
} }
void TurnTimeUpdate::applyGs(CGameState *gs) const
{
auto & playerState = gs->players[player];
playerState.turnTime = turnTime;
}
Component::Component(const CStackBasicDescriptor & stack) Component::Component(const CStackBasicDescriptor & stack)
: id(EComponentType::CREATURE) : id(EComponentType::CREATURE)
, subtype(stack.type->getId()) , subtype(stack.type->getId())

View File

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

View File

@ -2067,6 +2067,14 @@ void CGameHandler::run(bool resume)
//Change local daysWithoutCastle counter for local interface message //TODO: needed? //Change local daysWithoutCastle counter for local interface message //TODO: needed?
yt.daysWithoutCastle = playerState->daysWithoutCastle; yt.daysWithoutCastle = playerState->daysWithoutCastle;
applyAndSend(&yt); 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) if(playerColor != PlayerColor::CANNOT_DETERMINE)
{ {
//wait till turn is done //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); boost::unique_lock<boost::mutex> lock(states.mx);
while(states.players.at(playerColor).makingTurn && lobby->state == EServerState::GAMEPLAY) 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); states.cv.timed_wait(lock, p);
} }
} }