mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-20 20:23:03 +02:00
commit
c45ab07d0b
@ -193,6 +193,15 @@
|
||||
"vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "Team Alignments",
|
||||
"vcmi.randomMapTab.widgets.roadTypesLabel" : "Road Types",
|
||||
|
||||
"vcmi.optionsTab.widgets.chessFieldBase.help" : "{Extra timer}\n\nStarts counting down when the {turn timer} reaches zero. It is set only once at the beginning of the game. When this timer reaches zero, the player's turn ends.",
|
||||
"vcmi.optionsTab.widgets.chessFieldTurn.help" : "{Turn timer}\n\nStarts counting down when the player starts their turn on the adventure map. It is reset to its initial value at the start of each turn. Any unused turn time will be added to the {Extra timer} if it is in use.",
|
||||
"vcmi.optionsTab.widgets.chessFieldBattle.help" : "{Battle timer}\n\nCounts down during battles when the {stack timer} reaches zero. It is reset to its initial value at the start of each battle. If the timer reaches zero, the currently active stack will defend.",
|
||||
"vcmi.optionsTab.widgets.chessFieldCreature.help" : "{Stack timer}\n\nStarts counting down when the player is selecting an action for the current stack during battle. It resets to its initial value after the stack's action is completed.",
|
||||
"vcmi.optionsTab.widgets.labelTimer" : "Timer",
|
||||
"vcmi.optionsTab.widgets.timerModeSwitch.classic" : "Classic timer",
|
||||
"vcmi.optionsTab.widgets.timerModeSwitch.chess" : "Chess timer",
|
||||
|
||||
|
||||
// Custom victory conditions for H3 campaigns and HotA maps
|
||||
"vcmi.map.victoryCondition.daysPassed.toOthers" : "The enemy has managed to survive till this day. Victory is theirs!",
|
||||
"vcmi.map.victoryCondition.daysPassed.toSelf" : "Congratulations! You have managed to survive. Victory is yours!",
|
||||
|
@ -140,7 +140,6 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player):
|
||||
firstCall = 1; //if loading will be overwritten in serialize
|
||||
autosaveCount = 0;
|
||||
isAutoFightOn = false;
|
||||
|
||||
duringMovement = false;
|
||||
ignoreEvents = false;
|
||||
numOfMovedArts = 0;
|
||||
|
@ -63,7 +63,7 @@ AdventureMapInterface::AdventureMapInterface():
|
||||
shortcuts->setState(EAdventureState::MAKING_TURN);
|
||||
widget->getMapView()->onViewMapActivated();
|
||||
|
||||
if(LOCPLINT->cb->getStartInfo()->turnTimerInfo.isEnabled())
|
||||
if(LOCPLINT->cb->getStartInfo()->turnTimerInfo.isEnabled() || LOCPLINT->cb->getStartInfo()->turnTimerInfo.isBattleEnabled())
|
||||
watches = std::make_shared<TurnTimerWidget>();
|
||||
|
||||
addUsedEvents(KEYBOARD | TIME);
|
||||
|
@ -97,58 +97,63 @@ void TurnTimerWidget::setTime(PlayerColor player, int time)
|
||||
}
|
||||
}
|
||||
|
||||
void TurnTimerWidget::updateTimer(PlayerColor player, uint32_t msPassed)
|
||||
{
|
||||
const auto & time = LOCPLINT->cb->getPlayerTurnTime(player);
|
||||
if(time.isActive)
|
||||
cachedTurnTime -= msPassed;
|
||||
|
||||
if(cachedTurnTime < 0)
|
||||
cachedTurnTime = 0; //do not go below zero
|
||||
|
||||
if(lastPlayer != player)
|
||||
{
|
||||
lastPlayer = player;
|
||||
lastTurnTime = 0;
|
||||
}
|
||||
|
||||
auto timeCheckAndUpdate = [&](int time)
|
||||
{
|
||||
if(time / 1000 != lastTurnTime / 1000)
|
||||
{
|
||||
//do not update timer on this tick
|
||||
lastTurnTime = time;
|
||||
cachedTurnTime = time;
|
||||
}
|
||||
else
|
||||
setTime(player, cachedTurnTime);
|
||||
};
|
||||
|
||||
auto * playerInfo = LOCPLINT->cb->getPlayer(player);
|
||||
if(player.isValidPlayer() || (playerInfo && playerInfo->isHuman()))
|
||||
{
|
||||
if(time.isBattle)
|
||||
timeCheckAndUpdate(time.creatureTimer);
|
||||
else
|
||||
timeCheckAndUpdate(time.turnTimer);
|
||||
}
|
||||
else
|
||||
timeCheckAndUpdate(0);
|
||||
}
|
||||
|
||||
void TurnTimerWidget::tick(uint32_t msPassed)
|
||||
{
|
||||
if(!LOCPLINT || !LOCPLINT->cb)
|
||||
return;
|
||||
|
||||
for (PlayerColor p(0); p < PlayerColor::PLAYER_LIMIT; ++p)
|
||||
if(LOCPLINT->battleInt)
|
||||
{
|
||||
auto player = p;
|
||||
if(LOCPLINT->battleInt)
|
||||
{
|
||||
if(auto * stack = LOCPLINT->battleInt->stacksController->getActiveStack())
|
||||
player = stack->getOwner();
|
||||
}
|
||||
else if (!LOCPLINT->cb->isPlayerMakingTurn(player))
|
||||
continue;
|
||||
|
||||
auto time = LOCPLINT->cb->getPlayerTurnTime(player);
|
||||
cachedTurnTime -= msPassed;
|
||||
if(cachedTurnTime < 0) cachedTurnTime = 0; //do not go below zero
|
||||
|
||||
if(lastPlayer != player)
|
||||
{
|
||||
lastPlayer = player;
|
||||
lastTurnTime = 0;
|
||||
}
|
||||
|
||||
auto timeCheckAndUpdate = [&](int time)
|
||||
{
|
||||
if(time / 1000 != lastTurnTime / 1000)
|
||||
{
|
||||
//do not update timer on this tick
|
||||
lastTurnTime = time;
|
||||
cachedTurnTime = time;
|
||||
}
|
||||
else
|
||||
setTime(player, cachedTurnTime);
|
||||
};
|
||||
|
||||
auto * playerInfo = LOCPLINT->cb->getPlayer(player);
|
||||
if(player.isValidPlayer() || (playerInfo && playerInfo->isHuman()))
|
||||
{
|
||||
if(LOCPLINT->battleInt)
|
||||
{
|
||||
if(time.isBattleEnabled())
|
||||
timeCheckAndUpdate(time.creatureTimer);
|
||||
}
|
||||
else
|
||||
{
|
||||
timeCheckAndUpdate(time.turnTimer);
|
||||
}
|
||||
}
|
||||
if(auto * stack = LOCPLINT->battleInt->stacksController->getActiveStack())
|
||||
updateTimer(stack->getOwner(), msPassed);
|
||||
else
|
||||
timeCheckAndUpdate(0);
|
||||
updateTimer(PlayerColor::NEUTRAL, msPassed);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(PlayerColor p(0); p < PlayerColor::PLAYER_LIMIT; ++p)
|
||||
{
|
||||
if(LOCPLINT->cb->isPlayerMakingTurn(p))
|
||||
updateTimer(p, msPassed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,8 @@ private:
|
||||
std::set<int> notifications;
|
||||
|
||||
std::shared_ptr<DrawRect> buildDrawRect(const JsonNode & config) const;
|
||||
|
||||
void updateTimer(PlayerColor player, uint32_t msPassed);
|
||||
|
||||
public:
|
||||
|
||||
|
@ -184,11 +184,11 @@ Point CGuiHandler::screenDimensions() const
|
||||
|
||||
void CGuiHandler::drawFPSCounter()
|
||||
{
|
||||
static SDL_Rect overlay = { 0, 0, 64, 32};
|
||||
static SDL_Rect overlay = { 0, 0, 24, 24};
|
||||
uint32_t black = SDL_MapRGB(screen->format, 10, 10, 10);
|
||||
SDL_FillRect(screen, &overlay, black);
|
||||
std::string fps = std::to_string(framerate().getFramerate());
|
||||
graphics->fonts[FONT_BIG]->renderTextLeft(screen, fps, Colors::YELLOW, Point(10, 10));
|
||||
graphics->fonts[FONT_BIG]->renderTextLeft(screen, fps, Colors::YELLOW, Point(4, 2));
|
||||
}
|
||||
|
||||
bool CGuiHandler::amIGuiThread()
|
||||
|
@ -158,6 +158,8 @@ std::string InterfaceObjectConfigurable::readText(const JsonNode & config) const
|
||||
return "";
|
||||
|
||||
std::string s = config.String();
|
||||
if(s.empty())
|
||||
return s;
|
||||
logGlobal->debug("Reading text from translations by key: %s", s);
|
||||
return CGI->generaltexth->translate(s);
|
||||
}
|
||||
@ -579,8 +581,8 @@ std::shared_ptr<CTextInput> InterfaceObjectConfigurable::buildTextInput(const Js
|
||||
result->font = readFont(config["font"]);
|
||||
if(!config["color"].isNull())
|
||||
result->setColor(readColor(config["color"]));
|
||||
if(!config["text"].isNull())
|
||||
result->setText(readText(config["text"]));
|
||||
if(!config["text"].isNull() && config["text"].isString())
|
||||
result->setText(config["text"].String()); //for input field raw string is taken
|
||||
if(!config["callback"].isNull())
|
||||
result->cb += callbacks_string.at(config["callback"].String());
|
||||
if(!config["help"].isNull())
|
||||
|
@ -3,7 +3,7 @@
|
||||
[
|
||||
{ //backgound color
|
||||
"type": "drawRect",
|
||||
"rect": {"x": 4, "y": 4, "w": 68, "h": 24},
|
||||
"rect": {"x": 4, "y": 4, "w": 72, "h": 24},
|
||||
"color": [10, 10, 10, 255]
|
||||
},
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
"alignment": "left",
|
||||
"color": "yellow",
|
||||
"text": "",
|
||||
"position": {"x": 24, "y": 2}
|
||||
"position": {"x": 26, "y": 2}
|
||||
},
|
||||
],
|
||||
|
||||
|
@ -14,12 +14,12 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
bool TurnTimerInfo::isEnabled() const
|
||||
{
|
||||
return turnTimer > 0;
|
||||
return turnTimer > 0 || baseTimer > 0;
|
||||
}
|
||||
|
||||
bool TurnTimerInfo::isBattleEnabled() const
|
||||
{
|
||||
return creatureTimer > 0;
|
||||
return creatureTimer > 0 || battleTimer > 0;
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -19,6 +19,9 @@ struct DLL_LINKAGE TurnTimerInfo
|
||||
int battleTimer = 0; //in ms, counts down during battles when creature timer runs out
|
||||
int creatureTimer = 0; //in ms, counts down when player is choosing action in battle
|
||||
|
||||
bool isActive = false; //is being counting down
|
||||
bool isBattle = false; //indicator for current timer mode
|
||||
|
||||
bool isEnabled() const;
|
||||
bool isBattleEnabled() const;
|
||||
|
||||
@ -29,6 +32,8 @@ struct DLL_LINKAGE TurnTimerInfo
|
||||
h & baseTimer;
|
||||
h & battleTimer;
|
||||
h & creatureTimer;
|
||||
h & isActive;
|
||||
h & isBattle;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -999,18 +999,14 @@ void CGameHandler::run(bool resume)
|
||||
turnOrder->onGameStarted();
|
||||
|
||||
//wait till game is done
|
||||
auto clockLast = std::chrono::steady_clock::now();
|
||||
while(lobby->getState() == EServerState::GAMEPLAY)
|
||||
{
|
||||
const int waitTime = 100; //ms
|
||||
|
||||
for(PlayerColor player(0); player < PlayerColor::PLAYER_LIMIT; ++player)
|
||||
if(gs->isPlayerMakingTurn(player))
|
||||
turnTimerHandler.onPlayerMakingTurn(player, waitTime);
|
||||
|
||||
if(gs->curB)
|
||||
turnTimerHandler.onBattleLoop(waitTime);
|
||||
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(waitTime));
|
||||
const auto clockDuration = std::chrono::steady_clock::now() - clockLast;
|
||||
const int timePassed = std::chrono::duration_cast<std::chrono::milliseconds>(clockDuration).count();
|
||||
clockLast += clockDuration;
|
||||
turnTimerHandler.update(timePassed);
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1182,11 +1178,10 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, boo
|
||||
for(auto topQuery = queries->topQuery(h->tempOwner); true; topQuery = queries->topQuery(h->tempOwner))
|
||||
{
|
||||
moveQuery = std::dynamic_pointer_cast<CHeroMovementQuery>(topQuery);
|
||||
if(moveQuery
|
||||
&& (!transit || result != TryMoveHero::SUCCESS))
|
||||
queries->popIfTop(moveQuery);
|
||||
else
|
||||
if(!moveQuery || (transit && result == TryMoveHero::SUCCESS))
|
||||
break;
|
||||
|
||||
queries->popIfTop(moveQuery);
|
||||
}
|
||||
logGlobal->trace("Hero %s ends movement", h->getNameTranslated());
|
||||
return result != TryMoveHero::FAILED;
|
||||
|
@ -28,168 +28,280 @@ TurnTimerHandler::TurnTimerHandler(CGameHandler & gh):
|
||||
|
||||
void TurnTimerHandler::onGameplayStart(PlayerColor player)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mx);
|
||||
if(const auto * si = gameHandler.getStartInfo())
|
||||
{
|
||||
if(si->turnTimerInfo.isEnabled())
|
||||
{
|
||||
timers[player] = si->turnTimerInfo;
|
||||
timers[player].turnTimer = 0;
|
||||
}
|
||||
timers[player] = si->turnTimerInfo;
|
||||
timers[player].turnTimer = 0;
|
||||
timers[player].isActive = true;
|
||||
timers[player].isBattle = false;
|
||||
lastUpdate[player] = std::numeric_limits<int>::max();
|
||||
}
|
||||
}
|
||||
|
||||
void TurnTimerHandler::setTimerEnabled(PlayerColor player, bool enabled)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mx);
|
||||
assert(player.isValidPlayer());
|
||||
timers[player].isActive = enabled;
|
||||
sendTimerUpdate(player);
|
||||
}
|
||||
|
||||
void TurnTimerHandler::sendTimerUpdate(PlayerColor player)
|
||||
{
|
||||
TurnTimeUpdate ttu;
|
||||
ttu.player = player;
|
||||
ttu.turnTimer = timers[player];
|
||||
gameHandler.sendAndApply(&ttu);
|
||||
lastUpdate[player] = 0;
|
||||
}
|
||||
|
||||
void TurnTimerHandler::onPlayerGetTurn(PlayerColor player)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mx);
|
||||
if(const auto * si = gameHandler.getStartInfo())
|
||||
{
|
||||
if(si->turnTimerInfo.isEnabled())
|
||||
{
|
||||
timers[player].baseTimer += timers[player].turnTimer;
|
||||
timers[player].turnTimer = si->turnTimerInfo.turnTimer;
|
||||
auto & timer = timers[player];
|
||||
if(si->turnTimerInfo.baseTimer > 0)
|
||||
timer.baseTimer += timer.turnTimer;
|
||||
timer.turnTimer = si->turnTimerInfo.turnTimer;
|
||||
|
||||
TurnTimeUpdate ttu;
|
||||
ttu.player = player;
|
||||
ttu.turnTimer = timers[player];
|
||||
gameHandler.sendAndApply(&ttu);
|
||||
sendTimerUpdate(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TurnTimerHandler::update(int waitTime)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mx);
|
||||
if(const auto * gs = gameHandler.gameState())
|
||||
{
|
||||
for(PlayerColor player(0); player < PlayerColor::PLAYER_LIMIT; ++player)
|
||||
if(gs->isPlayerMakingTurn(player))
|
||||
onPlayerMakingTurn(player, waitTime);
|
||||
|
||||
if(gs->curB)
|
||||
onBattleLoop(waitTime);
|
||||
}
|
||||
}
|
||||
|
||||
bool TurnTimerHandler::timerCountDown(int & timer, int initialTimer, PlayerColor player, int waitTime)
|
||||
{
|
||||
if(timer > 0)
|
||||
{
|
||||
timer -= waitTime;
|
||||
lastUpdate[player] += waitTime;
|
||||
int frequency = (timer > turnTimePropagateThreshold
|
||||
&& initialTimer - timer > turnTimePropagateThreshold)
|
||||
? turnTimePropagateFrequency : turnTimePropagateFrequencyCrit;
|
||||
|
||||
if(lastUpdate[player] >= frequency)
|
||||
sendTimerUpdate(player);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TurnTimerHandler::onPlayerMakingTurn(PlayerColor player, int waitTime)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mx);
|
||||
const auto * gs = gameHandler.gameState();
|
||||
const auto * si = gameHandler.getStartInfo();
|
||||
if(!si || !gs)
|
||||
if(!si || !gs || !si->turnTimerInfo.isEnabled())
|
||||
return;
|
||||
|
||||
auto & state = gs->players.at(player);
|
||||
|
||||
if(state.human && si->turnTimerInfo.isEnabled() && !gs->curB)
|
||||
auto & timer = timers[player];
|
||||
const auto * state = gameHandler.getPlayerState(player);
|
||||
if(state && state->human && timer.isActive && !timer.isBattle && state->status == EPlayerStatus::INGAME)
|
||||
{
|
||||
if(timers[player].turnTimer > 0)
|
||||
if(!timerCountDown(timer.turnTimer, si->turnTimerInfo.turnTimer, player, waitTime))
|
||||
{
|
||||
timers[player].turnTimer -= waitTime;
|
||||
int frequency = (timers[player].turnTimer > turnTimePropagateThreshold ? turnTimePropagateFrequency : turnTimePropagateFrequencyCrit);
|
||||
|
||||
if(state.status == EPlayerStatus::INGAME //do not send message if player is not active already
|
||||
&& timers[player].turnTimer % frequency == 0)
|
||||
if(timer.baseTimer > 0)
|
||||
{
|
||||
TurnTimeUpdate ttu;
|
||||
ttu.player = state.color;
|
||||
ttu.turnTimer = timers[player];
|
||||
gameHandler.sendAndApply(&ttu);
|
||||
timer.turnTimer = timer.baseTimer;
|
||||
timer.baseTimer = 0;
|
||||
onPlayerMakingTurn(player, 0);
|
||||
}
|
||||
else if(!gameHandler.queries->topQuery(state->color)) //wait for replies to avoid pending queries
|
||||
gameHandler.turnOrder->onPlayerEndsTurn(state->color);
|
||||
}
|
||||
else if(timers[player].baseTimer > 0)
|
||||
{
|
||||
timers[player].turnTimer = timers[player].baseTimer;
|
||||
timers[player].baseTimer = 0;
|
||||
onPlayerMakingTurn(player, 0);
|
||||
}
|
||||
else if(!gameHandler.queries->topQuery(state.color)) //wait for replies to avoid pending queries
|
||||
gameHandler.turnOrder->onPlayerEndsTurn(state.color);
|
||||
}
|
||||
}
|
||||
|
||||
bool TurnTimerHandler::isPvpBattle() const
|
||||
{
|
||||
const auto * gs = gameHandler.gameState();
|
||||
auto attacker = gs->curB->getSidePlayer(BattleSide::ATTACKER);
|
||||
auto defender = gs->curB->getSidePlayer(BattleSide::DEFENDER);
|
||||
if(attacker.isValidPlayer() && defender.isValidPlayer())
|
||||
{
|
||||
const auto * attackerState = gameHandler.getPlayerState(attacker);
|
||||
const auto * defenderState = gameHandler.getPlayerState(defender);
|
||||
if(attackerState && defenderState && attackerState->human && defenderState->human)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TurnTimerHandler::onBattleStart()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mx);
|
||||
const auto * gs = gameHandler.gameState();
|
||||
const auto * si = gameHandler.getStartInfo();
|
||||
if(!si || !gs || !gs->curB || !si->turnTimerInfo.isBattleEnabled())
|
||||
return;
|
||||
|
||||
|
||||
auto attacker = gs->curB->getSidePlayer(BattleSide::ATTACKER);
|
||||
auto defender = gs->curB->getSidePlayer(BattleSide::DEFENDER);
|
||||
|
||||
bool pvpBattle = isPvpBattle();
|
||||
|
||||
for(auto i : {attacker, defender})
|
||||
{
|
||||
if(i.isValidPlayer())
|
||||
{
|
||||
timers[i].battleTimer = si->turnTimerInfo.battleTimer;
|
||||
timers[i].creatureTimer = si->turnTimerInfo.creatureTimer;
|
||||
auto & timer = timers[i];
|
||||
timer.isBattle = true;
|
||||
timer.battleTimer = (pvpBattle ? si->turnTimerInfo.battleTimer : 0);
|
||||
timer.creatureTimer = (pvpBattle ? si->turnTimerInfo.creatureTimer : si->turnTimerInfo.battleTimer);
|
||||
|
||||
TurnTimeUpdate ttu;
|
||||
ttu.player = i;
|
||||
ttu.turnTimer = timers[i];
|
||||
gameHandler.sendAndApply(&ttu);
|
||||
sendTimerUpdate(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TurnTimerHandler::onBattleEnd()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mx);
|
||||
const auto * gs = gameHandler.gameState();
|
||||
const auto * si = gameHandler.getStartInfo();
|
||||
if(!si || !gs || !gs->curB || !si->turnTimerInfo.isBattleEnabled())
|
||||
return;
|
||||
|
||||
auto attacker = gs->curB->getSidePlayer(BattleSide::ATTACKER);
|
||||
auto defender = gs->curB->getSidePlayer(BattleSide::DEFENDER);
|
||||
|
||||
bool pvpBattle = isPvpBattle();
|
||||
|
||||
for(auto i : {attacker, defender})
|
||||
{
|
||||
if(i.isValidPlayer())
|
||||
{
|
||||
auto & timer = timers[i];
|
||||
timer.isBattle = false;
|
||||
|
||||
if(!pvpBattle)
|
||||
{
|
||||
if(si->turnTimerInfo.baseTimer && timer.baseTimer == 0)
|
||||
timer.baseTimer = timer.creatureTimer;
|
||||
else if(si->turnTimerInfo.turnTimer && timer.turnTimer == 0)
|
||||
timer.turnTimer = timer.creatureTimer;
|
||||
}
|
||||
|
||||
sendTimerUpdate(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TurnTimerHandler::onBattleNextStack(const CStack & stack)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mx);
|
||||
const auto * gs = gameHandler.gameState();
|
||||
const auto * si = gameHandler.getStartInfo();
|
||||
if(!si || !gs || !gs->curB || !si->turnTimerInfo.isBattleEnabled())
|
||||
return;
|
||||
|
||||
auto player = stack.getOwner();
|
||||
|
||||
if(!player.isValidPlayer())
|
||||
return;
|
||||
if(isPvpBattle())
|
||||
{
|
||||
auto player = stack.getOwner();
|
||||
|
||||
if(timers[player].battleTimer < si->turnTimerInfo.battleTimer)
|
||||
timers[player].battleTimer = timers[player].creatureTimer;
|
||||
timers[player].creatureTimer = si->turnTimerInfo.creatureTimer;
|
||||
auto & timer = timers[player];
|
||||
if(timer.battleTimer == 0)
|
||||
timer.battleTimer = timer.creatureTimer;
|
||||
timer.creatureTimer = si->turnTimerInfo.creatureTimer;
|
||||
|
||||
TurnTimeUpdate ttu;
|
||||
ttu.player = player;
|
||||
ttu.turnTimer = timers[player];
|
||||
gameHandler.sendAndApply(&ttu);
|
||||
sendTimerUpdate(player);
|
||||
}
|
||||
}
|
||||
|
||||
void TurnTimerHandler::onBattleLoop(int waitTime)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mx);
|
||||
const auto * gs = gameHandler.gameState();
|
||||
const auto * si = gameHandler.getStartInfo();
|
||||
if(!si || !gs || !gs->curB)
|
||||
if(!si || !gs || !gs->curB || !si->turnTimerInfo.isBattleEnabled())
|
||||
return;
|
||||
|
||||
const auto * stack = gs->curB.get()->battleGetStackByID(gs->curB->getActiveStackID());
|
||||
if(!stack || !stack->getOwner().isValidPlayer())
|
||||
ui8 side = 0;
|
||||
const CStack * stack = nullptr;
|
||||
bool isTactisPhase = gs->curB->battleTacticDist() > 0;
|
||||
|
||||
if(isTactisPhase)
|
||||
side = gs->curB->battleGetTacticsSide();
|
||||
else
|
||||
{
|
||||
stack = gs->curB->battleGetStackByID(gs->curB->getActiveStackID());
|
||||
if(!stack || !stack->getOwner().isValidPlayer())
|
||||
return;
|
||||
side = stack->unitSide();
|
||||
}
|
||||
|
||||
auto player = gs->curB->getSidePlayer(side);
|
||||
if(!player.isValidPlayer())
|
||||
return;
|
||||
|
||||
auto & state = gs->players.at(gs->curB->getSidePlayer(stack->unitSide()));
|
||||
const auto * state = gameHandler.getPlayerState(player);
|
||||
if(!state || state->status != EPlayerStatus::INGAME || !state->human)
|
||||
return;
|
||||
|
||||
auto turnTimerUpdateApplier = [&](TurnTimerInfo & tTimer, int waitTime)
|
||||
auto & timer = timers[player];
|
||||
if(timer.isActive && timer.isBattle && !timerCountDown(timer.creatureTimer, si->turnTimerInfo.creatureTimer, player, waitTime))
|
||||
{
|
||||
if(tTimer.creatureTimer > 0)
|
||||
if(isPvpBattle())
|
||||
{
|
||||
tTimer.creatureTimer -= waitTime;
|
||||
int frequency = (tTimer.creatureTimer > turnTimePropagateThreshold
|
||||
&& si->turnTimerInfo.creatureTimer - tTimer.creatureTimer > turnTimePropagateThreshold)
|
||||
? turnTimePropagateFrequency : turnTimePropagateFrequencyCrit;
|
||||
|
||||
if(state.status == EPlayerStatus::INGAME //do not send message if player is not active already
|
||||
&& tTimer.creatureTimer % frequency == 0)
|
||||
if(timer.battleTimer > 0)
|
||||
{
|
||||
TurnTimeUpdate ttu;
|
||||
ttu.player = state.color;
|
||||
ttu.turnTimer = tTimer;
|
||||
gameHandler.sendAndApply(&ttu);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if(state.human && si->turnTimerInfo.isBattleEnabled())
|
||||
{
|
||||
if(!turnTimerUpdateApplier(timers[state.color], waitTime))
|
||||
{
|
||||
if(timers[state.color].battleTimer > 0)
|
||||
{
|
||||
timers[state.color].creatureTimer = timers[state.color].battleTimer;
|
||||
timers[state.color].battleTimer = 0;
|
||||
turnTimerUpdateApplier(timers[state.color], 0);
|
||||
timer.creatureTimer = timer.battleTimer;
|
||||
timerCountDown(timer.creatureTimer, timer.battleTimer, player, 0);
|
||||
timer.battleTimer = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
BattleAction doNothing;
|
||||
doNothing.actionType = EActionType::DEFEND;
|
||||
doNothing.side = stack->unitSide();
|
||||
doNothing.stackNumber = stack->unitId();
|
||||
gameHandler.battles->makePlayerBattleAction(state.color, doNothing);
|
||||
doNothing.side = side;
|
||||
if(isTactisPhase)
|
||||
doNothing.actionType = EActionType::END_TACTIC_PHASE;
|
||||
else
|
||||
{
|
||||
doNothing.actionType = EActionType::DEFEND;
|
||||
doNothing.stackNumber = stack->unitId();
|
||||
}
|
||||
gameHandler.battles->makePlayerBattleAction(player, doNothing);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(timer.turnTimer > 0)
|
||||
{
|
||||
timer.creatureTimer = timer.turnTimer;
|
||||
timerCountDown(timer.creatureTimer, timer.turnTimer, player, 0);
|
||||
timer.turnTimer = 0;
|
||||
}
|
||||
else if(timer.baseTimer > 0)
|
||||
{
|
||||
timer.creatureTimer = timer.baseTimer;
|
||||
timerCountDown(timer.creatureTimer, timer.baseTimer, player, 0);
|
||||
timer.baseTimer = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
BattleAction retreat;
|
||||
retreat.side = side;
|
||||
retreat.actionType = EActionType::RETREAT; //harsh punishment
|
||||
gameHandler.battles->makePlayerBattleAction(player, retreat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,31 +10,42 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../lib/TurnTimerInfo.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class CStack;
|
||||
class PlayerColor;
|
||||
struct TurnTimerInfo;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class CGameHandler;
|
||||
|
||||
class TurnTimerHandler
|
||||
{
|
||||
{
|
||||
CGameHandler & gameHandler;
|
||||
const int turnTimePropagateFrequency = 5000;
|
||||
const int turnTimePropagateFrequencyCrit = 1000;
|
||||
const int turnTimePropagateThreshold = 3000;
|
||||
std::map<PlayerColor, TurnTimerInfo> timers;
|
||||
std::map<PlayerColor, int> lastUpdate;
|
||||
std::recursive_mutex mx;
|
||||
|
||||
void onPlayerMakingTurn(PlayerColor player, int waitTime);
|
||||
void onBattleLoop(int waitTime);
|
||||
|
||||
bool timerCountDown(int & timerToApply, int initialTimer, PlayerColor player, int waitTime);
|
||||
bool isPvpBattle() const;
|
||||
void sendTimerUpdate(PlayerColor player);
|
||||
|
||||
public:
|
||||
TurnTimerHandler(CGameHandler &);
|
||||
|
||||
void onGameplayStart(PlayerColor player);
|
||||
void onPlayerGetTurn(PlayerColor player);
|
||||
void onPlayerMakingTurn(PlayerColor player, int waitTime);
|
||||
void onBattleStart();
|
||||
void onBattleNextStack(const CStack & stack);
|
||||
void onBattleLoop(int waitTime);
|
||||
void onBattleEnd();
|
||||
void update(int waitTime);
|
||||
void setTimerEnabled(PlayerColor player, bool enabled);
|
||||
};
|
||||
|
@ -263,6 +263,7 @@ void BattleResultProcessor::endBattle(int3 tile, const CGHeroInstance * heroAtta
|
||||
otherBattleQuery->result = battleQuery->result;
|
||||
}
|
||||
|
||||
gameHandler->turnTimerHandler.onBattleEnd();
|
||||
gameHandler->sendAndApply(battleResult.get()); //after this point casualties objects are destroyed
|
||||
|
||||
if (battleResult->queryID == QueryID::NONE)
|
||||
@ -463,7 +464,7 @@ void BattleResultProcessor::endBattleConfirm(const BattleInfo * battleInfo)
|
||||
raccepted.heroResult[1].hero = const_cast<CGHeroInstance*>(battleInfo->sides.at(1).hero);
|
||||
raccepted.heroResult[0].exp = battleResult->exp[0];
|
||||
raccepted.heroResult[1].exp = battleResult->exp[1];
|
||||
raccepted.winnerSide = finishingBattle->winnerSide;
|
||||
raccepted.winnerSide = finishingBattle->winnerSide;
|
||||
gameHandler->sendAndApply(&raccepted);
|
||||
|
||||
gameHandler->queries->popIfTop(battleQuery);
|
||||
|
@ -28,12 +28,12 @@ bool PlayerStartsTurnQuery::blocksPack(const CPack *pack) const
|
||||
|
||||
void PlayerStartsTurnQuery::onAdding(PlayerColor color)
|
||||
{
|
||||
//gh->turnTimerHandler.setTimerEnabled(color, false);
|
||||
gh->turnTimerHandler.setTimerEnabled(color, false);
|
||||
}
|
||||
|
||||
void PlayerStartsTurnQuery::onRemoval(PlayerColor color)
|
||||
{
|
||||
//gh->turnTimerHandler.setTimerEnabled(color, true);
|
||||
gh->turnTimerHandler.setTimerEnabled(color, true);
|
||||
}
|
||||
|
||||
bool PlayerStartsTurnQuery::endsByPlayerAnswer() const
|
||||
|
Loading…
Reference in New Issue
Block a user