mirror of
https://github.com/vcmi/vcmi.git
synced 2025-07-13 01:20:34 +02:00
Merge pull request #2636 from IvanSavenko/server_turn_order_processor
Reorganize player turn order handling on server
This commit is contained in:
@ -225,7 +225,8 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
|
|||||||
|
|
||||||
if(weakest->count == 1)
|
if(weakest->count == 1)
|
||||||
{
|
{
|
||||||
assert(resultingArmy.size() > 1);
|
if (resultingArmy.size() == 1)
|
||||||
|
logAi->warn("Unexpected resulting army size!");
|
||||||
|
|
||||||
resultingArmy.erase(weakest);
|
resultingArmy.erase(weakest);
|
||||||
}
|
}
|
||||||
|
@ -1625,15 +1625,6 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul
|
|||||||
if (GH.curInt == this)
|
if (GH.curInt == this)
|
||||||
GH.curInt = nullptr;
|
GH.curInt = nullptr;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (victoryLossCheckResult.loss() && cb->getPlayerStatus(playerID) == EPlayerStatus::INGAME) //enemy has lost
|
|
||||||
{
|
|
||||||
MetaString message = victoryLossCheckResult.messageToSelf;
|
|
||||||
message.appendLocalString(EMetaText::COLOR, player.getNum());
|
|
||||||
showInfoDialog(message.toString(), std::vector<std::shared_ptr<CComponent>>(1, std::make_shared<CComponent>(CComponent::flag, player.getNum(), 0)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPlayerInterface::playerBonusChanged( const Bonus &bonus, bool gain )
|
void CPlayerInterface::playerBonusChanged( const Bonus &bonus, bool gain )
|
||||||
|
@ -471,7 +471,6 @@ void ClientCommandManager::giveTurn(const PlayerColor &colorIdentifier)
|
|||||||
{
|
{
|
||||||
YourTurn yt;
|
YourTurn yt;
|
||||||
yt.player = colorIdentifier;
|
yt.player = colorIdentifier;
|
||||||
yt.daysWithoutCastle = CSH->client->getPlayerState(colorIdentifier)->daysWithoutCastle;
|
|
||||||
|
|
||||||
ApplyClientNetPackVisitor visitor(*CSH->client, *CSH->client->gameState());
|
ApplyClientNetPackVisitor visitor(*CSH->client, *CSH->client->gameState());
|
||||||
yt.visit(visitor);
|
yt.visit(visitor);
|
||||||
|
@ -397,7 +397,7 @@ void AdventureMapShortcuts::moveHeroDirectional(const Point & direction)
|
|||||||
|
|
||||||
bool AdventureMapShortcuts::optionCanViewQuests()
|
bool AdventureMapShortcuts::optionCanViewQuests()
|
||||||
{
|
{
|
||||||
return optionInMapView() && CGI->mh->getMap()->quests.empty();
|
return optionInMapView() && !CGI->mh->getMap()->quests.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AdventureMapShortcuts::optionCanToggleLevel()
|
bool AdventureMapShortcuts::optionCanToggleLevel()
|
||||||
|
@ -372,11 +372,15 @@ void AdventureMapWidget::setPlayerChildren(CIntObject * widget, const PlayerColo
|
|||||||
auto container = dynamic_cast<CAdventureMapContainerWidget *>(entry);
|
auto container = dynamic_cast<CAdventureMapContainerWidget *>(entry);
|
||||||
auto icon = dynamic_cast<CAdventureMapIcon *>(entry);
|
auto icon = dynamic_cast<CAdventureMapIcon *>(entry);
|
||||||
auto button = dynamic_cast<CButton *>(entry);
|
auto button = dynamic_cast<CButton *>(entry);
|
||||||
|
auto resDataBar = dynamic_cast<CResDataBar *>(entry);
|
||||||
auto texture = dynamic_cast<FilledTexturePlayerColored *>(entry);
|
auto texture = dynamic_cast<FilledTexturePlayerColored *>(entry);
|
||||||
|
|
||||||
if(button)
|
if(button)
|
||||||
button->setPlayerColor(player);
|
button->setPlayerColor(player);
|
||||||
|
|
||||||
|
if(resDataBar)
|
||||||
|
resDataBar->colorize(player);
|
||||||
|
|
||||||
if(icon)
|
if(icon)
|
||||||
icon->setPlayer(player);
|
icon->setPlayer(player);
|
||||||
|
|
||||||
|
@ -265,7 +265,7 @@ void BattleInterface::sendCommand(BattleAction command, const CStack * actor)
|
|||||||
{
|
{
|
||||||
logGlobal->trace("Setting command for %s", (actor ? actor->nodeName() : "hero"));
|
logGlobal->trace("Setting command for %s", (actor ? actor->nodeName() : "hero"));
|
||||||
stacksController->setActiveStack(nullptr);
|
stacksController->setActiveStack(nullptr);
|
||||||
LOCPLINT->cb->battleMakeUnitAction(command);
|
curInt->cb->battleMakeUnitAction(command);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -29,7 +29,7 @@ public:
|
|||||||
using ExecHandler = Sub::ExecHandler;
|
using ExecHandler = Sub::ExecHandler;
|
||||||
|
|
||||||
static Sub * getRegistry();
|
static Sub * getRegistry();
|
||||||
static void defaultExecute(const EventBus * bus, const ExecHandler & execHandler, PlayerColor & player);
|
static void defaultExecute(const EventBus * bus, const PlayerColor & player);
|
||||||
|
|
||||||
virtual PlayerColor getPlayer() const = 0;
|
virtual PlayerColor getPlayer() const = 0;
|
||||||
virtual void setPlayer(const PlayerColor & value) = 0;
|
virtual void setPlayer(const PlayerColor & value) = 0;
|
||||||
|
@ -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 visitDaysWithoutTown(DaysWithoutTown & pack) {}
|
||||||
virtual void visitTurnTimeUpdate(TurnTimeUpdate & 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) {}
|
||||||
|
@ -167,7 +167,21 @@ struct DLL_LINKAGE YourTurn : public CPackForClient
|
|||||||
void applyGs(CGameState * gs) const;
|
void applyGs(CGameState * gs) const;
|
||||||
|
|
||||||
PlayerColor player;
|
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;
|
virtual void visitTyped(ICPackVisitor & visitor) override;
|
||||||
|
|
||||||
|
@ -108,6 +108,11 @@ void YourTurn::visitTyped(ICPackVisitor & visitor)
|
|||||||
visitor.visitYourTurn(*this);
|
visitor.visitYourTurn(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DaysWithoutTown::visitTyped(ICPackVisitor & visitor)
|
||||||
|
{
|
||||||
|
visitor.visitDaysWithoutTown(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void EntitiesChanged::visitTyped(ICPackVisitor & visitor)
|
void EntitiesChanged::visitTyped(ICPackVisitor & visitor)
|
||||||
{
|
{
|
||||||
visitor.visitEntitiesChanged(*this);
|
visitor.visitEntitiesChanged(*this);
|
||||||
@ -2025,26 +2030,6 @@ void NewTurn::applyGs(CGameState *gs)
|
|||||||
|
|
||||||
if(gs->getDate(Date::DAY_OF_WEEK) == 1)
|
if(gs->getDate(Date::DAY_OF_WEEK) == 1)
|
||||||
gs->updateRumor();
|
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
|
void SetObjectProperty::applyGs(CGameState * gs) const
|
||||||
@ -2063,8 +2048,16 @@ void SetObjectProperty::applyGs(CGameState * gs) const
|
|||||||
{
|
{
|
||||||
auto * t = dynamic_cast<CGTownInstance *>(obj);
|
auto * t = dynamic_cast<CGTownInstance *>(obj);
|
||||||
assert(t);
|
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)
|
if(val < PlayerColor::PLAYER_LIMIT_I)
|
||||||
{
|
{
|
||||||
PlayerState * p = gs->getPlayerState(PlayerColor(val));
|
PlayerState * p = gs->getPlayerState(PlayerColor(val));
|
||||||
@ -2509,7 +2502,10 @@ void PlayerCheated::applyGs(CGameState * gs) const
|
|||||||
void YourTurn::applyGs(CGameState * gs) const
|
void YourTurn::applyGs(CGameState * gs) const
|
||||||
{
|
{
|
||||||
gs->currentPlayer = player;
|
gs->currentPlayer = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DaysWithoutTown::applyGs(CGameState * gs) const
|
||||||
|
{
|
||||||
auto & playerState = gs->players[player];
|
auto & playerState = gs->players[player];
|
||||||
playerState.daysWithoutCastle = daysWithoutCastle;
|
playerState.daysWithoutCastle = daysWithoutCastle;
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,11 @@ SubscriptionRegistry<PlayerGotTurn> * PlayerGotTurn::getRegistry()
|
|||||||
return Instance.get();
|
return Instance.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerGotTurn::defaultExecute(const EventBus * bus, const ExecHandler & execHandler, PlayerColor & player)
|
void PlayerGotTurn::defaultExecute(const EventBus * bus, const PlayerColor & player)
|
||||||
{
|
{
|
||||||
CPlayerGotTurn event;
|
CPlayerGotTurn event;
|
||||||
event.setPlayer(player);
|
event.setPlayer(player);
|
||||||
bus->executeEvent(event, execHandler);
|
bus->executeEvent(event);
|
||||||
player = event.getPlayer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CPlayerGotTurn::CPlayerGotTurn() = default;
|
CPlayerGotTurn::CPlayerGotTurn() = default;
|
||||||
|
@ -124,15 +124,6 @@ void TavernHeroesPool::onNewDay()
|
|||||||
hero.second->setMovementPoints(hero.second->movementPointsLimit(true));
|
hero.second->setMovementPoints(hero.second->movementPointsLimit(true));
|
||||||
hero.second->mana = hero.second->manaLimit();
|
hero.second->mana = hero.second->manaLimit();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto & slot : currentTavern)
|
|
||||||
{
|
|
||||||
if (slot.role == TavernSlotRole::RETREATED_TODAY)
|
|
||||||
slot.role = TavernSlotRole::RETREATED;
|
|
||||||
|
|
||||||
if (slot.role == TavernSlotRole::SURRENDERED_TODAY)
|
|
||||||
slot.role = TavernSlotRole::SURRENDERED;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TavernHeroesPool::addHeroToPool(CGHeroInstance * hero)
|
void TavernHeroesPool::addHeroToPool(CGHeroInstance * hero)
|
||||||
|
@ -24,12 +24,8 @@ enum class TavernSlotRole : int8_t
|
|||||||
|
|
||||||
SINGLE_UNIT, // hero was added after buying hero from this slot, and only has 1 creature in army
|
SINGLE_UNIT, // hero was added after buying hero from this slot, and only has 1 creature in army
|
||||||
FULL_ARMY, // hero was added to tavern on new week and still has full army
|
FULL_ARMY, // hero was added to tavern on new week and still has full army
|
||||||
|
|
||||||
RETREATED, // hero was owned by player before, but have retreated from battle and only has 1 creature in army
|
RETREATED, // hero was owned by player before, but have retreated from battle and only has 1 creature in army
|
||||||
RETREATED_TODAY,
|
SURRENDERED // hero was owned by player before, but have surrendered in battle and kept some troops
|
||||||
|
|
||||||
SURRENDERED, // hero was owned by player before, but have surrendered in battle and kept some troops
|
|
||||||
SURRENDERED_TODAY,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@ -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, DaysWithoutTown>();
|
||||||
s.template registerType<CPackForClient, TurnTimeUpdate>();
|
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>();
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "battles/BattleProcessor.h"
|
#include "battles/BattleProcessor.h"
|
||||||
#include "processors/HeroPoolProcessor.h"
|
#include "processors/HeroPoolProcessor.h"
|
||||||
#include "processors/PlayerMessageProcessor.h"
|
#include "processors/PlayerMessageProcessor.h"
|
||||||
|
#include "processors/TurnOrderProcessor.h"
|
||||||
#include "queries/QueriesProcessor.h"
|
#include "queries/QueriesProcessor.h"
|
||||||
#include "queries/MapQueries.h"
|
#include "queries/MapQueries.h"
|
||||||
|
|
||||||
@ -92,7 +93,7 @@ public:
|
|||||||
T *ptr = static_cast<T*>(pack);
|
T *ptr = static_cast<T*>(pack);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ApplyGhNetPackVisitor applier(*gh, *gs);
|
ApplyGhNetPackVisitor applier(*gh);
|
||||||
|
|
||||||
ptr->visit(applier);
|
ptr->visit(applier);
|
||||||
|
|
||||||
@ -123,51 +124,6 @@ static inline double distance(int3 a, int3 b)
|
|||||||
return std::sqrt((double)(a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
|
return std::sqrt((double)(a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerStatus PlayerStatuses::operator[](PlayerColor player)
|
|
||||||
{
|
|
||||||
boost::unique_lock<boost::mutex> l(mx);
|
|
||||||
if (players.find(player) != players.end())
|
|
||||||
{
|
|
||||||
return players.at(player);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw std::runtime_error("No such player!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void PlayerStatuses::addPlayer(PlayerColor player)
|
|
||||||
{
|
|
||||||
boost::unique_lock<boost::mutex> l(mx);
|
|
||||||
players[player];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PlayerStatuses::checkFlag(PlayerColor player, bool PlayerStatus::*flag)
|
|
||||||
{
|
|
||||||
boost::unique_lock<boost::mutex> l(mx);
|
|
||||||
if (players.find(player) != players.end())
|
|
||||||
{
|
|
||||||
return players[player].*flag;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw std::runtime_error("No such player!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlayerStatuses::setFlag(PlayerColor player, bool PlayerStatus::*flag, bool val)
|
|
||||||
{
|
|
||||||
boost::unique_lock<boost::mutex> l(mx);
|
|
||||||
if (players.find(player) != players.end())
|
|
||||||
{
|
|
||||||
players[player].*flag = val;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw std::runtime_error("No such player!");
|
|
||||||
}
|
|
||||||
cv.notify_all();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void callWith(std::vector<T> args, std::function<void(T)> fun, ui32 which)
|
void callWith(std::vector<T> args, std::function<void(T)> fun, ui32 which)
|
||||||
{
|
{
|
||||||
@ -480,7 +436,7 @@ void CGameHandler::changeSecSkill(const CGHeroInstance * hero, SecondarySkill wh
|
|||||||
|
|
||||||
void CGameHandler::handleClientDisconnection(std::shared_ptr<CConnection> c)
|
void CGameHandler::handleClientDisconnection(std::shared_ptr<CConnection> c)
|
||||||
{
|
{
|
||||||
if(lobby->state == EServerState::SHUTDOWN || !gs || !gs->scenarioOps)
|
if(lobby->getState() == EServerState::SHUTDOWN || !gs || !gs->scenarioOps)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for(auto & playerConnections : connections)
|
for(auto & playerConnections : connections)
|
||||||
@ -546,6 +502,7 @@ CGameHandler::CGameHandler(CVCMIServer * lobby)
|
|||||||
: lobby(lobby)
|
: lobby(lobby)
|
||||||
, heroPool(std::make_unique<HeroPoolProcessor>(this))
|
, heroPool(std::make_unique<HeroPoolProcessor>(this))
|
||||||
, battles(std::make_unique<BattleProcessor>(this))
|
, battles(std::make_unique<BattleProcessor>(this))
|
||||||
|
, turnOrder(std::make_unique<TurnOrderProcessor>(this))
|
||||||
, queries(std::make_unique<QueriesProcessor>())
|
, queries(std::make_unique<QueriesProcessor>())
|
||||||
, playerMessages(std::make_unique<PlayerMessageProcessor>(this))
|
, playerMessages(std::make_unique<PlayerMessageProcessor>(this))
|
||||||
, complainNoCreatures("No creatures to split")
|
, complainNoCreatures("No creatures to split")
|
||||||
@ -592,9 +549,7 @@ void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTrack
|
|||||||
getRandomGenerator().resetSeed();
|
getRandomGenerator().resetSeed();
|
||||||
|
|
||||||
for (auto & elem : gs->players)
|
for (auto & elem : gs->players)
|
||||||
{
|
turnOrder->addPlayer(elem.first);
|
||||||
states.addPlayer(elem.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
reinitScripting();
|
reinitScripting();
|
||||||
}
|
}
|
||||||
@ -646,7 +601,45 @@ void CGameHandler::setPortalDwelling(const CGTownInstance * town, bool forced=fa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameHandler::newTurn()
|
void CGameHandler::onPlayerTurnStarted(PlayerColor which)
|
||||||
|
{
|
||||||
|
events::PlayerGotTurn::defaultExecute(serverEventBus.get(), which);
|
||||||
|
turnTimerHandler.onPlayerGetTurn(gs->players[which]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGameHandler::onPlayerTurnEnded(PlayerColor which)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
if (newWeek) //new heroes in tavern
|
||||||
|
heroPool->onNewWeek(which);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGameHandler::onNewTurn()
|
||||||
{
|
{
|
||||||
logGlobal->trace("Turn %d", gs->day+1);
|
logGlobal->trace("Turn %d", gs->day+1);
|
||||||
NewTurn n;
|
NewTurn n;
|
||||||
@ -735,13 +728,13 @@ void CGameHandler::newTurn()
|
|||||||
{
|
{
|
||||||
if (elem.first == PlayerColor::NEUTRAL)
|
if (elem.first == PlayerColor::NEUTRAL)
|
||||||
continue;
|
continue;
|
||||||
else if (elem.first >= PlayerColor::PLAYER_LIMIT)
|
|
||||||
assert(0); //illegal player number!
|
assert(elem.first.isValidPlayer());//illegal player number!
|
||||||
|
|
||||||
std::pair<PlayerColor, si32> playerGold(elem.first, elem.second.resources[EGameResID::GOLD]);
|
std::pair<PlayerColor, si32> playerGold(elem.first, elem.second.resources[EGameResID::GOLD]);
|
||||||
hadGold.insert(playerGold);
|
hadGold.insert(playerGold);
|
||||||
|
|
||||||
if (newWeek) //new heroes in tavern
|
if (firstTurn)
|
||||||
heroPool->onNewWeek(elem.first);
|
heroPool->onNewWeek(elem.first);
|
||||||
|
|
||||||
n.res[elem.first] = elem.second.resources;
|
n.res[elem.first] = elem.second.resources;
|
||||||
@ -954,6 +947,9 @@ void CGameHandler::newTurn()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!firstTurn)
|
||||||
|
checkVictoryLossConditionsForAll(); // check for map turn limit
|
||||||
|
|
||||||
logGlobal->trace("Info about turn %d has been sent!", n.day);
|
logGlobal->trace("Info about turn %d has been sent!", n.day);
|
||||||
handleTimeEvents();
|
handleTimeEvents();
|
||||||
//call objects
|
//call objects
|
||||||
@ -965,6 +961,7 @@ void CGameHandler::newTurn()
|
|||||||
|
|
||||||
synchronizeArtifactHandlerLists(); //new day events may have changed them. TODO better of managing that
|
synchronizeArtifactHandlerLists(); //new day events may have changed them. TODO better of managing that
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameHandler::run(bool resume)
|
void CGameHandler::run(bool resume)
|
||||||
{
|
{
|
||||||
LOG_TRACE_PARAMS(logGlobal, "resume=%d", resume);
|
LOG_TRACE_PARAMS(logGlobal, "resume=%d", resume);
|
||||||
@ -989,114 +986,31 @@ void CGameHandler::run(bool resume)
|
|||||||
services()->scripts()->run(serverScripts);
|
services()->scripts()->run(serverScripts);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(resume)
|
if (!resume)
|
||||||
|
{
|
||||||
|
onNewTurn();
|
||||||
|
events::TurnStarted::defaultExecute(serverEventBus.get());
|
||||||
|
for(auto & player : gs->players)
|
||||||
|
turnTimerHandler.onGameplayStart(player.second);
|
||||||
|
}
|
||||||
|
else
|
||||||
events::GameResumed::defaultExecute(serverEventBus.get());
|
events::GameResumed::defaultExecute(serverEventBus.get());
|
||||||
|
|
||||||
auto playerTurnOrder = generatePlayerTurnOrder();
|
turnOrder->onGameStarted();
|
||||||
|
|
||||||
if(!resume)
|
//wait till game is done
|
||||||
for(auto & playerColor : playerTurnOrder)
|
while(lobby->getState() == EServerState::GAMEPLAY)
|
||||||
turnTimerHandler.onGameplayStart(gs->players[playerColor]);
|
|
||||||
|
|
||||||
while(lobby->state == EServerState::GAMEPLAY)
|
|
||||||
{
|
{
|
||||||
if(!resume)
|
const int waitTime = 100; //ms
|
||||||
{
|
|
||||||
newTurn();
|
|
||||||
events::TurnStarted::defaultExecute(serverEventBus.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::list<PlayerColor>::iterator it;
|
turnTimerHandler.onPlayerMakingTurn(gs->players[gs->getCurrentPlayer()], waitTime);
|
||||||
if (resume)
|
if(gs->curB)
|
||||||
{
|
turnTimerHandler.onBattleLoop(waitTime);
|
||||||
it = std::find(playerTurnOrder.begin(), playerTurnOrder.end(), gs->currentPlayer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
it = playerTurnOrder.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
resume = false;
|
boost::this_thread::sleep_for(boost::chrono::milliseconds(waitTime));
|
||||||
for (; (it != playerTurnOrder.end()) && (lobby->state == EServerState::GAMEPLAY) ; it++)
|
|
||||||
{
|
|
||||||
auto playerColor = *it;
|
|
||||||
|
|
||||||
auto onGetTurn = [&](events::PlayerGotTurn & event)
|
|
||||||
{
|
|
||||||
//if player runs out of time, he shouldn't get the turn (especially AI)
|
|
||||||
//pre-trigger may change anything, should check before each player
|
|
||||||
//TODO: is it enough to check only one player?
|
|
||||||
checkVictoryLossConditionsForAll();
|
|
||||||
|
|
||||||
auto player = event.getPlayer();
|
|
||||||
|
|
||||||
const PlayerState * playerState = &gs->players[player];
|
|
||||||
|
|
||||||
if(playerState->status != EPlayerStatus::INGAME)
|
|
||||||
{
|
|
||||||
event.setPlayer(PlayerColor::CANNOT_DETERMINE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
states.setFlag(player, &PlayerStatus::makingTurn, true);
|
|
||||||
|
|
||||||
YourTurn yt;
|
|
||||||
yt.player = player;
|
|
||||||
//Change local daysWithoutCastle counter for local interface message //TODO: needed?
|
|
||||||
yt.daysWithoutCastle = playerState->daysWithoutCastle;
|
|
||||||
applyAndSend(&yt);
|
|
||||||
|
|
||||||
turnTimerHandler.onPlayerGetTurn(gs->players[player]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
events::PlayerGotTurn::defaultExecute(serverEventBus.get(), onGetTurn, playerColor);
|
|
||||||
|
|
||||||
if(playerColor != PlayerColor::CANNOT_DETERMINE)
|
|
||||||
{
|
|
||||||
//wait till turn is done
|
|
||||||
const int waitTime = 100; //ms
|
|
||||||
boost::unique_lock<boost::mutex> lock(states.mx);
|
|
||||||
while(states.players.at(playerColor).makingTurn && lobby->state == EServerState::GAMEPLAY)
|
|
||||||
{
|
|
||||||
turnTimerHandler.onPlayerMakingTurn(gs->players[playerColor], waitTime);
|
|
||||||
if(gs->curB)
|
|
||||||
turnTimerHandler.onBattleLoop(waitTime);
|
|
||||||
|
|
||||||
states.cv.wait_for(lock, boost::chrono::milliseconds(waitTime));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//additional check that game is not finished
|
|
||||||
bool activePlayer = false;
|
|
||||||
for (auto player : playerTurnOrder)
|
|
||||||
{
|
|
||||||
if (gs->players[player].status == EPlayerStatus::INGAME)
|
|
||||||
activePlayer = true;
|
|
||||||
}
|
|
||||||
if(!activePlayer)
|
|
||||||
lobby->state = EServerState::GAMEPLAY_ENDED;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<PlayerColor> CGameHandler::generatePlayerTurnOrder() const
|
|
||||||
{
|
|
||||||
// Generate player turn order
|
|
||||||
std::list<PlayerColor> playerTurnOrder;
|
|
||||||
|
|
||||||
for (const auto & player : gs->players) // add human players first
|
|
||||||
{
|
|
||||||
if (player.second.human)
|
|
||||||
playerTurnOrder.push_back(player.first);
|
|
||||||
}
|
|
||||||
for (const auto & player : gs->players) // then add non-human players
|
|
||||||
{
|
|
||||||
if (!player.second.human)
|
|
||||||
playerTurnOrder.push_back(player.first);
|
|
||||||
}
|
|
||||||
return playerTurnOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CGameHandler::giveSpells(const CGTownInstance *t, const CGHeroInstance *h)
|
void CGameHandler::giveSpells(const CGTownInstance *t, const CGHeroInstance *h)
|
||||||
{
|
{
|
||||||
if (!h->hasSpellbook())
|
if (!h->hasSpellbook())
|
||||||
@ -3595,6 +3509,8 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
|
|||||||
peg.victoryLossCheckResult = victoryLossCheckResult;
|
peg.victoryLossCheckResult = victoryLossCheckResult;
|
||||||
sendAndApply(&peg);
|
sendAndApply(&peg);
|
||||||
|
|
||||||
|
turnOrder->onPlayerEndsGame(player);
|
||||||
|
|
||||||
if (victoryLossCheckResult.victory())
|
if (victoryLossCheckResult.victory())
|
||||||
{
|
{
|
||||||
//one player won -> all enemies lost
|
//one player won -> all enemies lost
|
||||||
@ -3617,7 +3533,7 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
|
|||||||
|
|
||||||
if(p->human)
|
if(p->human)
|
||||||
{
|
{
|
||||||
lobby->state = EServerState::GAMEPLAY_ENDED;
|
lobby->setState(EServerState::GAMEPLAY_ENDED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -3660,14 +3576,6 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
|
|||||||
}
|
}
|
||||||
checkVictoryLossConditions(playerColors);
|
checkVictoryLossConditions(playerColors);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto playerInfo = getPlayerState(gs->currentPlayer, false);
|
|
||||||
// If we are called before the actual game start, there might be no current player
|
|
||||||
if (playerInfo && playerInfo->status != EPlayerStatus::INGAME)
|
|
||||||
{
|
|
||||||
// If player making turn has lost his turn must be over as well
|
|
||||||
states.setFlag(gs->currentPlayer, &PlayerStatus::makingTurn, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4229,15 +4137,6 @@ void CGameHandler::createObject(const int3 & visitablePosition, Obj type, int32_
|
|||||||
sendAndApply(&no);
|
sendAndApply(&no);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameHandler::deserializationFix()
|
|
||||||
{
|
|
||||||
//FIXME: pointer to GameHandler itself can't be deserialized at the moment since GameHandler is top-level entity in serialization
|
|
||||||
// restore any places that requires such pointer manually
|
|
||||||
heroPool->gameHandler = this;
|
|
||||||
battles->setGameHandler(this);
|
|
||||||
playerMessages->gameHandler = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CGameHandler::startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, const CGTownInstance *town)
|
void CGameHandler::startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, const CGTownInstance *town)
|
||||||
{
|
{
|
||||||
battles->startBattlePrimary(army1, army2, tile, hero1, hero2, creatureBank, town);
|
battles->startBattlePrimary(army1, army2, tile, hero1, hero2, creatureBank, town);
|
||||||
|
@ -49,36 +49,10 @@ class CVCMIServer;
|
|||||||
class CBaseForGHApply;
|
class CBaseForGHApply;
|
||||||
class PlayerMessageProcessor;
|
class PlayerMessageProcessor;
|
||||||
class BattleProcessor;
|
class BattleProcessor;
|
||||||
|
class TurnOrderProcessor;
|
||||||
class QueriesProcessor;
|
class QueriesProcessor;
|
||||||
class CObjectVisitQuery;
|
class CObjectVisitQuery;
|
||||||
|
|
||||||
struct PlayerStatus
|
|
||||||
{
|
|
||||||
bool makingTurn;
|
|
||||||
|
|
||||||
PlayerStatus():makingTurn(false){};
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
|
||||||
{
|
|
||||||
h & makingTurn;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
class PlayerStatuses
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::map<PlayerColor,PlayerStatus> players;
|
|
||||||
boost::mutex mx;
|
|
||||||
boost::condition_variable cv; //notifies when any changes are made
|
|
||||||
|
|
||||||
void addPlayer(PlayerColor player);
|
|
||||||
PlayerStatus operator[](PlayerColor player);
|
|
||||||
bool checkFlag(PlayerColor player, bool PlayerStatus::*flag);
|
|
||||||
void setFlag(PlayerColor player, bool PlayerStatus::*flag, bool val);
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
|
||||||
{
|
|
||||||
h & players;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CGameHandler : public IGameCallback, public CBattleInfoCallback, public Environment
|
class CGameHandler : public IGameCallback, public CBattleInfoCallback, public Environment
|
||||||
{
|
{
|
||||||
CVCMIServer * lobby;
|
CVCMIServer * lobby;
|
||||||
@ -90,6 +64,7 @@ public:
|
|||||||
std::unique_ptr<HeroPoolProcessor> heroPool;
|
std::unique_ptr<HeroPoolProcessor> heroPool;
|
||||||
std::unique_ptr<BattleProcessor> battles;
|
std::unique_ptr<BattleProcessor> battles;
|
||||||
std::unique_ptr<QueriesProcessor> queries;
|
std::unique_ptr<QueriesProcessor> queries;
|
||||||
|
std::unique_ptr<TurnOrderProcessor> turnOrder;
|
||||||
|
|
||||||
//use enums as parameters, because doMove(sth, true, false, true) is not readable
|
//use enums as parameters, because doMove(sth, true, false, true) is not readable
|
||||||
enum EGuardLook {CHECK_FOR_GUARDS, IGNORE_GUARDS};
|
enum EGuardLook {CHECK_FOR_GUARDS, IGNORE_GUARDS};
|
||||||
@ -99,13 +74,11 @@ public:
|
|||||||
std::unique_ptr<PlayerMessageProcessor> playerMessages;
|
std::unique_ptr<PlayerMessageProcessor> playerMessages;
|
||||||
|
|
||||||
std::map<PlayerColor, std::set<std::shared_ptr<CConnection>>> connections; //player color -> connection to client with interface of that player
|
std::map<PlayerColor, std::set<std::shared_ptr<CConnection>>> connections; //player color -> connection to client with interface of that player
|
||||||
PlayerStatuses states; //player color -> player state
|
|
||||||
|
|
||||||
//queries stuff
|
//queries stuff
|
||||||
boost::recursive_mutex gsm;
|
boost::recursive_mutex gsm;
|
||||||
ui32 QID;
|
ui32 QID;
|
||||||
|
|
||||||
|
|
||||||
SpellCastEnvironment * spellEnv;
|
SpellCastEnvironment * spellEnv;
|
||||||
|
|
||||||
TurnTimerHandler turnTimerHandler;
|
TurnTimerHandler turnTimerHandler;
|
||||||
@ -237,6 +210,10 @@ public:
|
|||||||
void save(const std::string &fname);
|
void save(const std::string &fname);
|
||||||
bool load(const std::string &fname);
|
bool load(const std::string &fname);
|
||||||
|
|
||||||
|
void onPlayerTurnStarted(PlayerColor which);
|
||||||
|
void onPlayerTurnEnded(PlayerColor which);
|
||||||
|
void onNewTurn();
|
||||||
|
|
||||||
void handleTimeEvents();
|
void handleTimeEvents();
|
||||||
void handleTownEvents(CGTownInstance *town, NewTurn &n);
|
void handleTownEvents(CGTownInstance *town, NewTurn &n);
|
||||||
bool complain(const std::string &problem); //sends message to all clients, prints on the logs and return true
|
bool complain(const std::string &problem); //sends message to all clients, prints on the logs and return true
|
||||||
@ -248,14 +225,11 @@ public:
|
|||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
h & QID;
|
h & QID;
|
||||||
h & states;
|
|
||||||
h & battles;
|
|
||||||
h & heroPool;
|
|
||||||
h & getRandomGenerator();
|
h & getRandomGenerator();
|
||||||
h & playerMessages;
|
h & *battles;
|
||||||
|
h & *heroPool;
|
||||||
if (!h.saving)
|
h & *playerMessages;
|
||||||
deserializationFix();
|
h & *turnOrder;
|
||||||
|
|
||||||
#if SCRIPTING_ENABLED
|
#if SCRIPTING_ENABLED
|
||||||
JsonNode scriptsState;
|
JsonNode scriptsState;
|
||||||
@ -282,7 +256,6 @@ public:
|
|||||||
bool isPlayerOwns(CPackForServer * pack, ObjectInstanceID id);
|
bool isPlayerOwns(CPackForServer * pack, ObjectInstanceID id);
|
||||||
|
|
||||||
void run(bool resume);
|
void run(bool resume);
|
||||||
void newTurn();
|
|
||||||
bool sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, const std::vector<ArtifactPosition> & slot);
|
bool sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, const std::vector<ArtifactPosition> & slot);
|
||||||
void spawnWanderingMonsters(CreatureID creatureID);
|
void spawnWanderingMonsters(CreatureID creatureID);
|
||||||
|
|
||||||
@ -298,8 +271,6 @@ public:
|
|||||||
scripting::Pool * getContextPool() const override;
|
scripting::Pool * getContextPool() const override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::list<PlayerColor> generatePlayerTurnOrder() const;
|
|
||||||
|
|
||||||
friend class CVCMIServer;
|
friend class CVCMIServer;
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<events::EventBus> serverEventBus;
|
std::unique_ptr<events::EventBus> serverEventBus;
|
||||||
@ -308,7 +279,6 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void reinitScripting();
|
void reinitScripting();
|
||||||
void deserializationFix();
|
|
||||||
|
|
||||||
void getVictoryLossMessage(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult, InfoWindow & out) const;
|
void getVictoryLossMessage(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult, InfoWindow & out) const;
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ set(server_SRCS
|
|||||||
|
|
||||||
processors/HeroPoolProcessor.cpp
|
processors/HeroPoolProcessor.cpp
|
||||||
processors/PlayerMessageProcessor.cpp
|
processors/PlayerMessageProcessor.cpp
|
||||||
|
processors/TurnOrderProcessor.cpp
|
||||||
|
|
||||||
CGameHandler.cpp
|
CGameHandler.cpp
|
||||||
ServerSpellCastEnvironment.cpp
|
ServerSpellCastEnvironment.cpp
|
||||||
@ -37,6 +38,7 @@ set(server_HEADERS
|
|||||||
|
|
||||||
processors/HeroPoolProcessor.h
|
processors/HeroPoolProcessor.h
|
||||||
processors/PlayerMessageProcessor.h
|
processors/PlayerMessageProcessor.h
|
||||||
|
processors/TurnOrderProcessor.h
|
||||||
|
|
||||||
CGameHandler.h
|
CGameHandler.h
|
||||||
ServerSpellCastEnvironment.h
|
ServerSpellCastEnvironment.h
|
||||||
|
@ -158,6 +158,16 @@ CVCMIServer::~CVCMIServer()
|
|||||||
announceLobbyThread->join();
|
announceLobbyThread->join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CVCMIServer::setState(EServerState value)
|
||||||
|
{
|
||||||
|
state.store(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
EServerState CVCMIServer::getState() const
|
||||||
|
{
|
||||||
|
return state.load();
|
||||||
|
}
|
||||||
|
|
||||||
void CVCMIServer::run()
|
void CVCMIServer::run()
|
||||||
{
|
{
|
||||||
if(!restartGameplay)
|
if(!restartGameplay)
|
||||||
@ -1159,7 +1169,7 @@ int main(int argc, const char * argv[])
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while(server.state != EServerState::SHUTDOWN)
|
while(server.getState() != EServerState::SHUTDOWN)
|
||||||
{
|
{
|
||||||
server.run();
|
server.run();
|
||||||
}
|
}
|
||||||
@ -1168,7 +1178,7 @@ int main(int argc, const char * argv[])
|
|||||||
catch(boost::system::system_error & e) //for boost errors just log, not crash - probably client shut down connection
|
catch(boost::system::system_error & e) //for boost errors just log, not crash - probably client shut down connection
|
||||||
{
|
{
|
||||||
logNetwork->error(e.what());
|
logNetwork->error(e.what());
|
||||||
server.state = EServerState::SHUTDOWN;
|
server.setState(EServerState::SHUTDOWN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(boost::system::system_error & e)
|
catch(boost::system::system_error & e)
|
||||||
|
@ -56,10 +56,10 @@ class CVCMIServer : public LobbyInfo
|
|||||||
boost::recursive_mutex mx;
|
boost::recursive_mutex mx;
|
||||||
std::shared_ptr<CApplier<CBaseForServerApply>> applier;
|
std::shared_ptr<CApplier<CBaseForServerApply>> applier;
|
||||||
std::unique_ptr<boost::thread> announceLobbyThread, remoteConnectionsThread;
|
std::unique_ptr<boost::thread> announceLobbyThread, remoteConnectionsThread;
|
||||||
|
std::atomic<EServerState> state;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::shared_ptr<CGameHandler> gh;
|
std::shared_ptr<CGameHandler> gh;
|
||||||
std::atomic<EServerState> state;
|
|
||||||
ui16 port;
|
ui16 port;
|
||||||
|
|
||||||
boost::program_options::variables_map cmdLineOptions;
|
boost::program_options::variables_map cmdLineOptions;
|
||||||
@ -101,6 +101,9 @@ public:
|
|||||||
|
|
||||||
void updateAndPropagateLobbyState();
|
void updateAndPropagateLobbyState();
|
||||||
|
|
||||||
|
void setState(EServerState value);
|
||||||
|
EServerState getState() const;
|
||||||
|
|
||||||
// Work with LobbyInfo
|
// Work with LobbyInfo
|
||||||
void setPlayer(PlayerColor clickedColor);
|
void setPlayer(PlayerColor clickedColor);
|
||||||
void optionNextHero(PlayerColor player, int dir); //dir == -1 or +1
|
void optionNextHero(PlayerColor player, int dir); //dir == -1 or +1
|
||||||
|
@ -53,11 +53,11 @@ void ClientPermissionsCheckerNetPackVisitor::visitLobbyClientConnected(LobbyClie
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(srv.state == EServerState::LOBBY)
|
if(srv.getState() == EServerState::LOBBY)
|
||||||
{
|
{
|
||||||
result = true;
|
result = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//disconnect immediately and ignore this client
|
//disconnect immediately and ignore this client
|
||||||
srv.connections.erase(pack.c);
|
srv.connections.erase(pack.c);
|
||||||
@ -115,7 +115,7 @@ void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyClientConnected(LobbyCl
|
|||||||
// Until UUID set we only pass LobbyClientConnected to this client
|
// Until UUID set we only pass LobbyClientConnected to this client
|
||||||
pack.c->uuid = pack.uuid;
|
pack.c->uuid = pack.uuid;
|
||||||
srv.updateAndPropagateLobbyState();
|
srv.updateAndPropagateLobbyState();
|
||||||
if(srv.state == EServerState::GAMEPLAY)
|
if(srv.getState() == EServerState::GAMEPLAY)
|
||||||
{
|
{
|
||||||
//immediately start game
|
//immediately start game
|
||||||
std::unique_ptr<LobbyStartGame> startGameForReconnectedPlayer(new LobbyStartGame);
|
std::unique_ptr<LobbyStartGame> startGameForReconnectedPlayer(new LobbyStartGame);
|
||||||
@ -173,13 +173,13 @@ void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyClientDisconnected(Lobb
|
|||||||
if(pack.shutdownServer)
|
if(pack.shutdownServer)
|
||||||
{
|
{
|
||||||
logNetwork->info("Client requested shutdown, server will close itself...");
|
logNetwork->info("Client requested shutdown, server will close itself...");
|
||||||
srv.state = EServerState::SHUTDOWN;
|
srv.setState(EServerState::SHUTDOWN);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if(srv.connections.empty())
|
else if(srv.connections.empty())
|
||||||
{
|
{
|
||||||
logNetwork->error("Last connection lost, server will close itself...");
|
logNetwork->error("Last connection lost, server will close itself...");
|
||||||
srv.state = EServerState::SHUTDOWN;
|
srv.setState(EServerState::SHUTDOWN);
|
||||||
}
|
}
|
||||||
else if(pack.c == srv.hostClient)
|
else if(pack.c == srv.hostClient)
|
||||||
{
|
{
|
||||||
@ -198,7 +198,7 @@ void ClientPermissionsCheckerNetPackVisitor::visitLobbyChatMessage(LobbyChatMess
|
|||||||
|
|
||||||
void ApplyOnServerNetPackVisitor::visitLobbySetMap(LobbySetMap & pack)
|
void ApplyOnServerNetPackVisitor::visitLobbySetMap(LobbySetMap & pack)
|
||||||
{
|
{
|
||||||
if(srv.state != EServerState::LOBBY)
|
if(srv.getState() != EServerState::LOBBY)
|
||||||
{
|
{
|
||||||
result = false;
|
result = false;
|
||||||
return;
|
return;
|
||||||
@ -300,7 +300,7 @@ void ApplyOnServerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
|
|||||||
pack.initializedStartInfo = std::make_shared<StartInfo>(*srv.gh->getStartInfo(true));
|
pack.initializedStartInfo = std::make_shared<StartInfo>(*srv.gh->getStartInfo(true));
|
||||||
pack.initializedGameState = srv.gh->gameState();
|
pack.initializedGameState = srv.gh->gameState();
|
||||||
|
|
||||||
srv.state = EServerState::GAMEPLAY_STARTING;
|
srv.setState(EServerState::GAMEPLAY_STARTING);
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "battles/BattleProcessor.h"
|
#include "battles/BattleProcessor.h"
|
||||||
#include "processors/HeroPoolProcessor.h"
|
#include "processors/HeroPoolProcessor.h"
|
||||||
#include "processors/PlayerMessageProcessor.h"
|
#include "processors/PlayerMessageProcessor.h"
|
||||||
|
#include "processors/TurnOrderProcessor.h"
|
||||||
#include "queries/QueriesProcessor.h"
|
#include "queries/QueriesProcessor.h"
|
||||||
|
|
||||||
#include "../lib/IGameCallback.h"
|
#include "../lib/IGameCallback.h"
|
||||||
@ -36,24 +37,10 @@ void ApplyGhNetPackVisitor::visitSaveGame(SaveGame & pack)
|
|||||||
|
|
||||||
void ApplyGhNetPackVisitor::visitEndTurn(EndTurn & pack)
|
void ApplyGhNetPackVisitor::visitEndTurn(EndTurn & pack)
|
||||||
{
|
{
|
||||||
PlayerColor currentPlayer = gs.currentPlayer;
|
if (!gh.hasPlayerAt(pack.player, pack.c))
|
||||||
if(pack.player != currentPlayer)
|
gh.throwAndComplain(&pack, "No such pack.player!");
|
||||||
{
|
|
||||||
if(gh.getPlayerStatus(pack.player) == EPlayerStatus::INGAME)
|
|
||||||
gh.throwAndComplain(&pack, "pack.player attempted to end turn for another pack.player!");
|
|
||||||
|
|
||||||
logGlobal->debug("pack.player attempted to end turn after game over. Ignoring this request.");
|
result = gh.turnOrder->onPlayerEndsTurn(pack.player);
|
||||||
|
|
||||||
result = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gh.throwOnWrongPlayer(&pack, pack.player);
|
|
||||||
if(gh.queries->topQuery(pack.player))
|
|
||||||
gh.throwAndComplain(&pack, "Cannot end turn before resolving queries!");
|
|
||||||
|
|
||||||
gh.states.setFlag(gs.currentPlayer, &PlayerStatus::makingTurn, false);
|
|
||||||
result = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplyGhNetPackVisitor::visitDismissHero(DismissHero & pack)
|
void ApplyGhNetPackVisitor::visitDismissHero(DismissHero & pack)
|
||||||
@ -274,8 +261,6 @@ void ApplyGhNetPackVisitor::visitQueryReply(QueryReply & pack)
|
|||||||
if(pack.qid == QueryID(-1))
|
if(pack.qid == QueryID(-1))
|
||||||
gh.throwAndComplain(&pack, "Cannot answer the query with pack.id -1!");
|
gh.throwAndComplain(&pack, "Cannot answer the query with pack.id -1!");
|
||||||
|
|
||||||
assert(vstd::contains(gh.states.players, pack.player));
|
|
||||||
|
|
||||||
result = gh.queryReply(pack.qid, pack.reply, pack.player);
|
result = gh.queryReply(pack.qid, pack.reply, pack.player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,11 +16,10 @@ class ApplyGhNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
|
|||||||
private:
|
private:
|
||||||
bool result;
|
bool result;
|
||||||
CGameHandler & gh;
|
CGameHandler & gh;
|
||||||
CGameState & gs;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ApplyGhNetPackVisitor(CGameHandler & gh, CGameState & gs)
|
ApplyGhNetPackVisitor(CGameHandler & gh)
|
||||||
:gh(gh), gs(gs), result(false)
|
:gh(gh), result(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "CGameHandler.h"
|
#include "CGameHandler.h"
|
||||||
#include "battles/BattleProcessor.h"
|
#include "battles/BattleProcessor.h"
|
||||||
#include "queries/QueriesProcessor.h"
|
#include "queries/QueriesProcessor.h"
|
||||||
|
#include "processors/TurnOrderProcessor.h"
|
||||||
#include "../lib/battle/BattleInfo.h"
|
#include "../lib/battle/BattleInfo.h"
|
||||||
#include "../lib/gameState/CGameState.h"
|
#include "../lib/gameState/CGameState.h"
|
||||||
#include "../lib/CPlayerState.h"
|
#include "../lib/CPlayerState.h"
|
||||||
@ -84,7 +85,7 @@ void TurnTimerHandler::onPlayerMakingTurn(PlayerState & state, int waitTime)
|
|||||||
onPlayerMakingTurn(state, waitTime);
|
onPlayerMakingTurn(state, waitTime);
|
||||||
}
|
}
|
||||||
else if(!gameHandler.queries->topQuery(state.color)) //wait for replies to avoid pending queries
|
else if(!gameHandler.queries->topQuery(state.color)) //wait for replies to avoid pending queries
|
||||||
gameHandler.states.players.at(state.color).makingTurn = false; //force end turn
|
gameHandler.turnOrder->onPlayerEndsTurn(state.color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,6 +499,7 @@ bool BattleActionProcessor::dispatchBattleAction(const BattleAction & ba)
|
|||||||
{
|
{
|
||||||
switch(ba.actionType)
|
switch(ba.actionType)
|
||||||
{
|
{
|
||||||
|
case EActionType::BAD_MORALE:
|
||||||
case EActionType::NO_ACTION:
|
case EActionType::NO_ACTION:
|
||||||
return doEmptyAction(ba);
|
return doEmptyAction(ba);
|
||||||
case EActionType::END_TACTIC_PHASE:
|
case EActionType::END_TACTIC_PHASE:
|
||||||
|
@ -286,7 +286,6 @@ const CStack * BattleFlowProcessor::getNextStack()
|
|||||||
|
|
||||||
void BattleFlowProcessor::activateNextStack()
|
void BattleFlowProcessor::activateNextStack()
|
||||||
{
|
{
|
||||||
//TODO: activate next round if next == nullptr
|
|
||||||
const auto & curB = *gameHandler->gameState()->curB;
|
const auto & curB = *gameHandler->gameState()->curB;
|
||||||
|
|
||||||
// Find next stack that requires manual control
|
// Find next stack that requires manual control
|
||||||
|
@ -219,7 +219,8 @@ void BattleProcessor::updateGateState()
|
|||||||
bool BattleProcessor::makePlayerBattleAction(PlayerColor player, const BattleAction &ba)
|
bool BattleProcessor::makePlayerBattleAction(PlayerColor player, const BattleAction &ba)
|
||||||
{
|
{
|
||||||
bool result = actionsProcessor->makePlayerBattleAction(player, ba);
|
bool result = actionsProcessor->makePlayerBattleAction(player, ba);
|
||||||
flowProcessor->onActionMade(ba);
|
if (!resultProcessor->battleIsEnding() && gameHandler->gameState()->curB != nullptr)
|
||||||
|
flowProcessor->onActionMade(ba);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "HeroPoolProcessor.h"
|
#include "HeroPoolProcessor.h"
|
||||||
|
|
||||||
|
#include "TurnOrderProcessor.h"
|
||||||
#include "../CGameHandler.h"
|
#include "../CGameHandler.h"
|
||||||
|
|
||||||
#include "../../lib/CHeroHandler.h"
|
#include "../../lib/CHeroHandler.h"
|
||||||
@ -32,29 +33,6 @@ HeroPoolProcessor::HeroPoolProcessor(CGameHandler * gameHandler)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HeroPoolProcessor::playerEndedTurn(const PlayerColor & player)
|
|
||||||
{
|
|
||||||
// our player is acting right now and have not ended turn
|
|
||||||
if (player == gameHandler->gameState()->currentPlayer)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
auto turnOrder = gameHandler->generatePlayerTurnOrder();
|
|
||||||
|
|
||||||
for (auto const & entry : turnOrder)
|
|
||||||
{
|
|
||||||
// our player is yet to start turn
|
|
||||||
if (entry == gameHandler->gameState()->currentPlayer)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// our player have finished turn
|
|
||||||
if (entry == player)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
TavernHeroSlot HeroPoolProcessor::selectSlotForRole(const PlayerColor & player, TavernSlotRole roleID)
|
TavernHeroSlot HeroPoolProcessor::selectSlotForRole(const PlayerColor & player, TavernSlotRole roleID)
|
||||||
{
|
{
|
||||||
const auto & heroesPool = gameHandler->gameState()->heroesPool;
|
const auto & heroesPool = gameHandler->gameState()->heroesPool;
|
||||||
@ -90,10 +68,7 @@ TavernHeroSlot HeroPoolProcessor::selectSlotForRole(const PlayerColor & player,
|
|||||||
void HeroPoolProcessor::onHeroSurrendered(const PlayerColor & color, const CGHeroInstance * hero)
|
void HeroPoolProcessor::onHeroSurrendered(const PlayerColor & color, const CGHeroInstance * hero)
|
||||||
{
|
{
|
||||||
SetAvailableHero sah;
|
SetAvailableHero sah;
|
||||||
if (playerEndedTurn(color))
|
sah.roleID = TavernSlotRole::SURRENDERED;
|
||||||
sah.roleID = TavernSlotRole::SURRENDERED_TODAY;
|
|
||||||
else
|
|
||||||
sah.roleID = TavernSlotRole::SURRENDERED;
|
|
||||||
|
|
||||||
sah.slotID = selectSlotForRole(color, sah.roleID);
|
sah.slotID = selectSlotForRole(color, sah.roleID);
|
||||||
sah.player = color;
|
sah.player = color;
|
||||||
@ -104,10 +79,7 @@ void HeroPoolProcessor::onHeroSurrendered(const PlayerColor & color, const CGHer
|
|||||||
void HeroPoolProcessor::onHeroEscaped(const PlayerColor & color, const CGHeroInstance * hero)
|
void HeroPoolProcessor::onHeroEscaped(const PlayerColor & color, const CGHeroInstance * hero)
|
||||||
{
|
{
|
||||||
SetAvailableHero sah;
|
SetAvailableHero sah;
|
||||||
if (playerEndedTurn(color))
|
sah.roleID = TavernSlotRole::RETREATED;
|
||||||
sah.roleID = TavernSlotRole::RETREATED_TODAY;
|
|
||||||
else
|
|
||||||
sah.roleID = TavernSlotRole::RETREATED;
|
|
||||||
|
|
||||||
sah.slotID = selectSlotForRole(color, sah.roleID);
|
sah.slotID = selectSlotForRole(color, sah.roleID);
|
||||||
sah.player = color;
|
sah.player = color;
|
||||||
@ -161,26 +133,10 @@ void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHe
|
|||||||
|
|
||||||
void HeroPoolProcessor::onNewWeek(const PlayerColor & color)
|
void HeroPoolProcessor::onNewWeek(const PlayerColor & color)
|
||||||
{
|
{
|
||||||
const auto & heroesPool = gameHandler->gameState()->heroesPool;
|
clearHeroFromSlot(color, TavernHeroSlot::NATIVE);
|
||||||
const auto & heroes = heroesPool->getHeroesFor(color);
|
clearHeroFromSlot(color, TavernHeroSlot::RANDOM);
|
||||||
|
selectNewHeroForSlot(color, TavernHeroSlot::NATIVE, true, true);
|
||||||
const auto nativeSlotRole = heroes.size() < 1 ? TavernSlotRole::NONE : heroesPool->getSlotRole(heroes[0]->type->getId());
|
selectNewHeroForSlot(color, TavernHeroSlot::RANDOM, false, true);
|
||||||
const auto randomSlotRole = heroes.size() < 2 ? TavernSlotRole::NONE : heroesPool->getSlotRole(heroes[1]->type->getId());
|
|
||||||
|
|
||||||
bool resetNativeSlot = nativeSlotRole != TavernSlotRole::RETREATED_TODAY && nativeSlotRole != TavernSlotRole::SURRENDERED_TODAY;
|
|
||||||
bool resetRandomSlot = randomSlotRole != TavernSlotRole::RETREATED_TODAY && randomSlotRole != TavernSlotRole::SURRENDERED_TODAY;
|
|
||||||
|
|
||||||
if (resetNativeSlot)
|
|
||||||
clearHeroFromSlot(color, TavernHeroSlot::NATIVE);
|
|
||||||
|
|
||||||
if (resetRandomSlot)
|
|
||||||
clearHeroFromSlot(color, TavernHeroSlot::RANDOM);
|
|
||||||
|
|
||||||
if (resetNativeSlot)
|
|
||||||
selectNewHeroForSlot(color, TavernHeroSlot::NATIVE, true, true);
|
|
||||||
|
|
||||||
if (resetRandomSlot)
|
|
||||||
selectNewHeroForSlot(color, TavernHeroSlot::RANDOM, false, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTypeID & heroToRecruit, const PlayerColor & player)
|
bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTypeID & heroToRecruit, const PlayerColor & player)
|
||||||
|
@ -43,7 +43,6 @@ class HeroPoolProcessor : boost::noncopyable
|
|||||||
|
|
||||||
TavernHeroSlot selectSlotForRole(const PlayerColor & player, TavernSlotRole roleID);
|
TavernHeroSlot selectSlotForRole(const PlayerColor & player, TavernSlotRole roleID);
|
||||||
|
|
||||||
bool playerEndedTurn(const PlayerColor & player);
|
|
||||||
public:
|
public:
|
||||||
CGameHandler * gameHandler;
|
CGameHandler * gameHandler;
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ bool PlayerMessageProcessor::handleHostCommand(PlayerColor player, const std::st
|
|||||||
if(words[1] == "exit" || words[1] == "quit" || words[1] == "end")
|
if(words[1] == "exit" || words[1] == "quit" || words[1] == "end")
|
||||||
{
|
{
|
||||||
broadcastSystemMessage("game was terminated");
|
broadcastSystemMessage("game was terminated");
|
||||||
gameHandler->gameLobby()->state = EServerState::SHUTDOWN;
|
gameHandler->gameLobby()->setState(EServerState::SHUTDOWN);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
213
server/processors/TurnOrderProcessor.cpp
Normal file
213
server/processors/TurnOrderProcessor.cpp
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
/*
|
||||||
|
* TurnOrderProcessor.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 "StdInc.h"
|
||||||
|
#include "TurnOrderProcessor.h"
|
||||||
|
|
||||||
|
#include "../queries/QueriesProcessor.h"
|
||||||
|
#include "../CGameHandler.h"
|
||||||
|
#include "../CVCMIServer.h"
|
||||||
|
|
||||||
|
#include "../../lib/CPlayerState.h"
|
||||||
|
#include "../../lib/NetPacks.h"
|
||||||
|
|
||||||
|
TurnOrderProcessor::TurnOrderProcessor(CGameHandler * owner):
|
||||||
|
gameHandler(owner)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TurnOrderProcessor::canActSimultaneously(PlayerColor active, PlayerColor waiting) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TurnOrderProcessor::mustActBefore(PlayerColor left, PlayerColor right) const
|
||||||
|
{
|
||||||
|
const auto * leftInfo = gameHandler->getPlayerState(left, false);
|
||||||
|
const auto * rightInfo = gameHandler->getPlayerState(right, false);
|
||||||
|
|
||||||
|
assert(left != right);
|
||||||
|
assert(leftInfo && rightInfo);
|
||||||
|
|
||||||
|
if (!leftInfo)
|
||||||
|
return false;
|
||||||
|
if (!rightInfo)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (leftInfo->isHuman() && !rightInfo->isHuman())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!leftInfo->isHuman() && rightInfo->isHuman())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return left < right;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TurnOrderProcessor::canStartTurn(PlayerColor which) const
|
||||||
|
{
|
||||||
|
for (auto player : awaitingPlayers)
|
||||||
|
{
|
||||||
|
if (player != which && mustActBefore(player, which))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto player : actingPlayers)
|
||||||
|
{
|
||||||
|
if (!canActSimultaneously(player, which))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TurnOrderProcessor::doStartNewDay()
|
||||||
|
{
|
||||||
|
assert(awaitingPlayers.empty());
|
||||||
|
assert(actingPlayers.empty());
|
||||||
|
|
||||||
|
bool activePlayer = false;
|
||||||
|
for (auto player : actedPlayers)
|
||||||
|
{
|
||||||
|
if (gameHandler->getPlayerState(player)->status == EPlayerStatus::INGAME)
|
||||||
|
activePlayer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!activePlayer)
|
||||||
|
gameHandler->gameLobby()->setState(EServerState::GAMEPLAY_ENDED);
|
||||||
|
|
||||||
|
std::swap(actedPlayers, awaitingPlayers);
|
||||||
|
|
||||||
|
gameHandler->onNewTurn();
|
||||||
|
tryStartTurnsForPlayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TurnOrderProcessor::doStartPlayerTurn(PlayerColor which)
|
||||||
|
{
|
||||||
|
assert(gameHandler->getPlayerState(which));
|
||||||
|
assert(gameHandler->getPlayerState(which)->status == EPlayerStatus::INGAME);
|
||||||
|
|
||||||
|
//Note: on game load, "actingPlayer" might already contain list of players
|
||||||
|
actingPlayers.insert(which);
|
||||||
|
awaitingPlayers.erase(which);
|
||||||
|
gameHandler->onPlayerTurnStarted(which);
|
||||||
|
|
||||||
|
YourTurn yt;
|
||||||
|
yt.player = which;
|
||||||
|
gameHandler->sendAndApply(&yt);
|
||||||
|
|
||||||
|
assert(actingPlayers.size() == 1); // No simturns yet :(
|
||||||
|
assert(gameHandler->getCurrentPlayer() == *actingPlayers.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TurnOrderProcessor::doEndPlayerTurn(PlayerColor which)
|
||||||
|
{
|
||||||
|
assert(isPlayerMakingTurn(which));
|
||||||
|
assert(gameHandler->getPlayerStatus(which) == EPlayerStatus::INGAME);
|
||||||
|
|
||||||
|
actingPlayers.erase(which);
|
||||||
|
actedPlayers.insert(which);
|
||||||
|
|
||||||
|
if (!awaitingPlayers.empty())
|
||||||
|
tryStartTurnsForPlayers();
|
||||||
|
|
||||||
|
if (actingPlayers.empty())
|
||||||
|
doStartNewDay();
|
||||||
|
|
||||||
|
assert(!actingPlayers.empty());
|
||||||
|
assert(actingPlayers.size() == 1); // No simturns yet :(
|
||||||
|
assert(gameHandler->getCurrentPlayer() == *actingPlayers.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TurnOrderProcessor::addPlayer(PlayerColor which)
|
||||||
|
{
|
||||||
|
awaitingPlayers.insert(which);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TurnOrderProcessor::onPlayerEndsGame(PlayerColor which)
|
||||||
|
{
|
||||||
|
awaitingPlayers.erase(which);
|
||||||
|
actingPlayers.erase(which);
|
||||||
|
actedPlayers.erase(which);
|
||||||
|
|
||||||
|
if (!awaitingPlayers.empty())
|
||||||
|
tryStartTurnsForPlayers();
|
||||||
|
|
||||||
|
if (actingPlayers.empty())
|
||||||
|
doStartNewDay();
|
||||||
|
|
||||||
|
assert(!actingPlayers.empty());
|
||||||
|
assert(actingPlayers.size() == 1); // No simturns yet :(
|
||||||
|
assert(gameHandler->getCurrentPlayer() == *actingPlayers.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TurnOrderProcessor::onPlayerEndsTurn(PlayerColor which)
|
||||||
|
{
|
||||||
|
if (!isPlayerMakingTurn(which))
|
||||||
|
{
|
||||||
|
gameHandler->complain("Can not end turn for player that is not acting!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(gameHandler->getPlayerStatus(which) != EPlayerStatus::INGAME)
|
||||||
|
{
|
||||||
|
gameHandler->complain("Can not end turn for player that is not in game!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(gameHandler->queries->topQuery(which) != nullptr)
|
||||||
|
{
|
||||||
|
gameHandler->complain("Cannot end turn before resolving queries!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
gameHandler->onPlayerTurnEnded(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TurnOrderProcessor::onGameStarted()
|
||||||
|
{
|
||||||
|
// this may be game load - send notification to players that they can act
|
||||||
|
auto actingPlayersCopy = actingPlayers;
|
||||||
|
for (auto player : actingPlayersCopy)
|
||||||
|
doStartPlayerTurn(player);
|
||||||
|
|
||||||
|
tryStartTurnsForPlayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TurnOrderProcessor::tryStartTurnsForPlayers()
|
||||||
|
{
|
||||||
|
auto awaitingPlayersCopy = awaitingPlayers;
|
||||||
|
for (auto player : awaitingPlayersCopy)
|
||||||
|
{
|
||||||
|
if (canStartTurn(player))
|
||||||
|
doStartPlayerTurn(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TurnOrderProcessor::isPlayerAwaitsTurn(PlayerColor which) const
|
||||||
|
{
|
||||||
|
return vstd::contains(awaitingPlayers, which);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TurnOrderProcessor::isPlayerMakingTurn(PlayerColor which) const
|
||||||
|
{
|
||||||
|
return vstd::contains(actingPlayers, which);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TurnOrderProcessor::isPlayerAwaitsNewDay(PlayerColor which) const
|
||||||
|
{
|
||||||
|
return vstd::contains(actedPlayers, which);
|
||||||
|
}
|
66
server/processors/TurnOrderProcessor.h
Normal file
66
server/processors/TurnOrderProcessor.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* TurnOrderProcessor.h, 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 "../../lib/GameConstants.h"
|
||||||
|
|
||||||
|
class CGameHandler;
|
||||||
|
|
||||||
|
class TurnOrderProcessor : boost::noncopyable
|
||||||
|
{
|
||||||
|
CGameHandler * gameHandler;
|
||||||
|
|
||||||
|
std::set<PlayerColor> awaitingPlayers;
|
||||||
|
std::set<PlayerColor> actingPlayers;
|
||||||
|
std::set<PlayerColor> actedPlayers;
|
||||||
|
|
||||||
|
/// Returns true if waiting player can act alongside with currently acting player
|
||||||
|
bool canActSimultaneously(PlayerColor active, PlayerColor waiting) const;
|
||||||
|
|
||||||
|
/// Returns true if left player must act before right player
|
||||||
|
bool mustActBefore(PlayerColor left, PlayerColor right) const;
|
||||||
|
|
||||||
|
/// Returns true if player is ready to start turn
|
||||||
|
bool canStartTurn(PlayerColor which) const;
|
||||||
|
|
||||||
|
/// Starts turn for all players that can start turn
|
||||||
|
void tryStartTurnsForPlayers();
|
||||||
|
|
||||||
|
void doStartNewDay();
|
||||||
|
void doStartPlayerTurn(PlayerColor which);
|
||||||
|
void doEndPlayerTurn(PlayerColor which);
|
||||||
|
|
||||||
|
bool isPlayerAwaitsTurn(PlayerColor which) const;
|
||||||
|
bool isPlayerMakingTurn(PlayerColor which) const;
|
||||||
|
bool isPlayerAwaitsNewDay(PlayerColor which) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TurnOrderProcessor(CGameHandler * owner);
|
||||||
|
|
||||||
|
/// Add new player to handle (e.g. on game start)
|
||||||
|
void addPlayer(PlayerColor which);
|
||||||
|
|
||||||
|
/// 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();
|
||||||
|
|
||||||
|
template<typename Handler>
|
||||||
|
void serialize(Handler & h, const int version)
|
||||||
|
{
|
||||||
|
h & awaitingPlayers;
|
||||||
|
h & actingPlayers;
|
||||||
|
h & actedPlayers;
|
||||||
|
}
|
||||||
|
};
|
Reference in New Issue
Block a user