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:
parent
f13a53c1d9
commit
4b1224ec8c
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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()
|
||||||
|
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
|
const CGObjectInstance * CGameInfoCallback::getObjByQuestIdentifier(int identifier) const
|
||||||
{
|
{
|
||||||
if(gs->map->questIdentifierToId.empty())
|
if(gs->map->questIdentifierToId.empty())
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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) {}
|
||||||
|
@ -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;
|
||||||
|
@ -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())
|
||||||
|
@ -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>();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user