mirror of
https://github.com/vcmi/vcmi.git
synced 2026-06-19 22:57:37 +02:00
Schedule turn-start hero visits on towns; fixes multiple hero level-ups on map start with more than 2 towns with built War Academies
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
#include "TurnTimerHandler.h"
|
||||
#include "ServerNetPackVisitors.h"
|
||||
#include "ServerSpellCastEnvironment.h"
|
||||
#include "TurnStartVisitScheduler.h"
|
||||
#include "battles/BattleProcessor.h"
|
||||
#include "processors/HeroPoolProcessor.h"
|
||||
#include "processors/NewTurnProcessor.h"
|
||||
@@ -542,6 +543,7 @@ CGameHandler::CGameHandler(IGameServer & server)
|
||||
, heroPool(std::make_unique<HeroPoolProcessor>(this))
|
||||
, battles(std::make_unique<BattleProcessor>(this))
|
||||
, queries(std::make_unique<QueriesProcessor>())
|
||||
, turnStartVisitScheduler(std::make_unique<TurnStartVisitScheduler>(*this, *queries))
|
||||
, turnOrder(std::make_unique<TurnOrderProcessor>(this))
|
||||
, turnTimerHandler(std::make_unique<TurnTimerHandler>(*this))
|
||||
, newTurnProcessor(std::make_unique<NewTurnProcessor>(this))
|
||||
@@ -553,6 +555,7 @@ CGameHandler::CGameHandler(IGameServer & server)
|
||||
, complainNotEnoughCreatures("Cannot split that stack, not enough creatures!")
|
||||
, complainInvalidSlot("Invalid slot accessed!")
|
||||
{
|
||||
queries->setListener(turnStartVisitScheduler.get());
|
||||
}
|
||||
|
||||
CGameHandler::~CGameHandler() = default;
|
||||
@@ -4216,6 +4219,9 @@ bool CGameHandler::isBlockedByQueries(const CPackForServer *pack, PlayerColor pl
|
||||
if (dynamic_cast<const SaveLocalState *>(pack) != nullptr)
|
||||
return false;
|
||||
|
||||
if(dynamic_cast<const AdvInterfaceReady *>(pack) != nullptr)
|
||||
return false;
|
||||
|
||||
auto query = queries->topQuery(player);
|
||||
if (query && query->blocksPack(pack))
|
||||
{
|
||||
|
||||
@@ -51,6 +51,7 @@ class QueriesProcessor;
|
||||
class CObjectVisitQuery;
|
||||
class NewTurnProcessor;
|
||||
class IGameServer;
|
||||
class TurnStartVisitScheduler;
|
||||
|
||||
class CGameHandler : public Environment, public IGameEventCallback
|
||||
{
|
||||
@@ -60,6 +61,7 @@ public:
|
||||
std::unique_ptr<HeroPoolProcessor> heroPool;
|
||||
std::unique_ptr<BattleProcessor> battles;
|
||||
std::unique_ptr<QueriesProcessor> queries;
|
||||
std::unique_ptr<TurnStartVisitScheduler> turnStartVisitScheduler;
|
||||
std::unique_ptr<TurnOrderProcessor> turnOrder;
|
||||
std::unique_ptr<TurnTimerHandler> turnTimerHandler;
|
||||
std::unique_ptr<NewTurnProcessor> newTurnProcessor;
|
||||
|
||||
@@ -24,6 +24,7 @@ set(vcmiservercommon_SRCS
|
||||
NetPacksServer.cpp
|
||||
NetPacksLobbyServer.cpp
|
||||
TurnTimerHandler.cpp
|
||||
TurnStartVisitScheduler.cpp
|
||||
)
|
||||
|
||||
set(vcmiservercommon_HEADERS
|
||||
@@ -39,6 +40,7 @@ set(vcmiservercommon_HEADERS
|
||||
queries/MapQueries.h
|
||||
queries/VisitQueries.h
|
||||
queries/QueriesProcessor.h
|
||||
queries/IQueryStackListener.h
|
||||
|
||||
processors/HeroPoolProcessor.h
|
||||
processors/NewTurnProcessor.h
|
||||
@@ -53,6 +55,7 @@ set(vcmiservercommon_HEADERS
|
||||
LobbyNetPackVisitors.h
|
||||
ServerNetPackVisitors.h
|
||||
TurnTimerHandler.h
|
||||
TurnStartVisitScheduler.h
|
||||
)
|
||||
|
||||
assign_source_group(${vcmiservercommon_SRCS} ${vcmiservercommon_HEADERS})
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* TurnStartVisitScheduler.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 "TurnStartVisitScheduler.h"
|
||||
|
||||
#include "CGameHandler.h"
|
||||
#include "lib/gameState/CGameState.h"
|
||||
#include "queries/QueriesProcessor.h"
|
||||
|
||||
TurnStartVisitScheduler::TurnStartVisitScheduler(CGameHandler & gameHandler, QueriesProcessor & queries)
|
||||
: gameHandler(gameHandler)
|
||||
, queries(queries)
|
||||
{
|
||||
}
|
||||
|
||||
void TurnStartVisitScheduler::enqueue(PlayerColor player, std::deque<PendingTurnStartVisit> visits)
|
||||
{
|
||||
sessions[player] = std::move(visits);
|
||||
}
|
||||
|
||||
void TurnStartVisitScheduler::processNext(PlayerColor player)
|
||||
{
|
||||
auto & session = sessions[player];
|
||||
|
||||
if(session.empty())
|
||||
return;
|
||||
|
||||
if(queries.topQuery(player))
|
||||
return;
|
||||
|
||||
if(!session.empty())
|
||||
{
|
||||
const auto next = session.front();
|
||||
session.pop_front();
|
||||
|
||||
const auto * object = gameHandler.gameState().getObjInstance(next.objectId);
|
||||
const auto * hero = gameHandler.gameState().getHero(next.heroId);
|
||||
|
||||
gameHandler.objectVisited(object, hero);
|
||||
}
|
||||
}
|
||||
|
||||
void TurnStartVisitScheduler::onQueryStackChanged(PlayerColor player)
|
||||
{
|
||||
processNext(player);
|
||||
}
|
||||
|
||||
void TurnStartVisitScheduler::clear(PlayerColor player)
|
||||
{
|
||||
sessions[player].clear();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* TurnStartVisitScheduler.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 "queries/IQueryStackListener.h"
|
||||
|
||||
class CGameHandler;
|
||||
class QueriesProcessor;
|
||||
|
||||
struct PendingTurnStartVisit
|
||||
{
|
||||
PlayerColor player;
|
||||
ObjectInstanceID objectId;
|
||||
ObjectInstanceID heroId;
|
||||
};
|
||||
|
||||
// Schedules deferred turn-start object visits and runs them one at a time
|
||||
// per player, resuming only after the previous visit's query chain finishes.
|
||||
class TurnStartVisitScheduler final : public IQueryStackListener
|
||||
{
|
||||
public:
|
||||
TurnStartVisitScheduler(CGameHandler & gameHandler, QueriesProcessor & queries);
|
||||
|
||||
void enqueue(PlayerColor player, std::deque<PendingTurnStartVisit> visits);
|
||||
void processNext(PlayerColor player);
|
||||
void onQueryStackChanged(PlayerColor player) override;
|
||||
void clear(PlayerColor player);
|
||||
|
||||
private:
|
||||
CGameHandler & gameHandler;
|
||||
QueriesProcessor & queries;
|
||||
std::array<std::deque<PendingTurnStartVisit>, PlayerColor::PLAYER_LIMIT_I> sessions;
|
||||
};
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "../../lib/networkPacks/StackLocation.h"
|
||||
#include "../../lib/pathfinder/TurnInfo.h"
|
||||
#include "../../lib/texts/CGeneralTextHandler.h"
|
||||
#include "../TurnStartVisitScheduler.h"
|
||||
|
||||
#include <vstd/RNG.h>
|
||||
|
||||
@@ -149,15 +150,20 @@ void NewTurnProcessor::onPlayerTurnStarted(PlayerColor which)
|
||||
for (const auto * t : playerState->getTowns())
|
||||
handleTownEvents(t);
|
||||
|
||||
std::deque<PendingTurnStartVisit> visits;
|
||||
|
||||
for (const auto * t : playerState->getTowns())
|
||||
{
|
||||
//garrison hero first - consistent with original H3 Mana Vortex and Battle Scholar Academy levelup windows order
|
||||
if (t->getGarrisonHero() != nullptr)
|
||||
gameHandler->objectVisited(t, t->getGarrisonHero());
|
||||
if(t->getGarrisonHero() != nullptr)
|
||||
visits.push_back({which, t->id, t->getGarrisonHero()->id});
|
||||
|
||||
if (t->getVisitingHero() != nullptr)
|
||||
gameHandler->objectVisited(t, t->getVisitingHero());
|
||||
if(t->getVisitingHero() != nullptr)
|
||||
visits.push_back({which, t->id, t->getVisitingHero()->id});
|
||||
}
|
||||
|
||||
gameHandler->turnStartVisitScheduler->enqueue(which, std::move(visits));
|
||||
gameHandler->turnStartVisitScheduler->processNext(which);
|
||||
}
|
||||
|
||||
void NewTurnProcessor::onPlayerTurnEnded(PlayerColor which)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* IQueryStackListener.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/constants/EntityIdentifiers.h"
|
||||
|
||||
class IQueryStackListener
|
||||
{
|
||||
public:
|
||||
virtual ~IQueryStackListener() = default;
|
||||
|
||||
// Called after a player's query stack changes and local query lifecycle
|
||||
// handling for that operation is complete.
|
||||
virtual void onQueryStackChanged(PlayerColor player) = 0;
|
||||
};
|
||||
@@ -33,6 +33,9 @@ void QueriesProcessor::popQuery(PlayerColor player, QueryPtr query)
|
||||
//Exposure on query below happens only if removal didn't trigger any new query
|
||||
if(nextQuery && nextQuery == topQuery(player))
|
||||
nextQuery->onExposure(query);
|
||||
|
||||
if(queriesStackListener)
|
||||
queriesStackListener->onQueryStackChanged(player);
|
||||
}
|
||||
|
||||
void QueriesProcessor::popQuery(const CQuery &query)
|
||||
@@ -82,6 +85,9 @@ void QueriesProcessor::addQuery(PlayerColor player, QueryPtr query)
|
||||
query->onAdding(player);
|
||||
queries[player].push_back(query);
|
||||
query->onAdded(player);
|
||||
|
||||
if(queriesStackListener)
|
||||
queriesStackListener->onQueryStackChanged(player);
|
||||
}
|
||||
|
||||
QueryPtr QueriesProcessor::topQuery(PlayerColor player)
|
||||
@@ -145,3 +151,8 @@ int QueriesProcessor::countQuery(const QueryPtr & query) const
|
||||
return result;
|
||||
}
|
||||
|
||||
void QueriesProcessor::setListener(IQueryStackListener * listener)
|
||||
{
|
||||
queriesStackListener = listener;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "../../lib/GameConstants.h"
|
||||
#include "constants/EntityIdentifiers.h"
|
||||
#include "queries/CQuery.h"
|
||||
#include "IQueryStackListener.h"
|
||||
|
||||
class CQuery;
|
||||
using QueryPtr = std::shared_ptr<CQuery>;
|
||||
@@ -22,11 +23,15 @@ public:
|
||||
using QueriesStack = std::vector<QueryPtr>;
|
||||
using QueriesPerPlayer = std::array<QueriesStack, PlayerColor::PLAYER_LIMIT_I>;
|
||||
|
||||
// Sets an optional listener notified when a player's query stack changes.
|
||||
void setListener(IQueryStackListener * listener);
|
||||
|
||||
private:
|
||||
void addQuery(PlayerColor player, QueryPtr query);
|
||||
void popQuery(PlayerColor player, QueryPtr query);
|
||||
|
||||
QueriesPerPlayer queries;
|
||||
IQueryStackListener * queriesStackListener = nullptr;
|
||||
|
||||
template<typename StorageT>
|
||||
class AllQueriesViewT
|
||||
@@ -55,7 +60,7 @@ private:
|
||||
|
||||
decltype(auto) operator*() const
|
||||
{
|
||||
return (*storage)[outer][inner]; // QueryPtr& albo const QueryPtr&
|
||||
return (*storage)[outer][inner]; // QueryPtr& or const QueryPtr&
|
||||
}
|
||||
|
||||
iterator & operator++()
|
||||
|
||||
Reference in New Issue
Block a user