diff --git a/CCallback.cpp b/CCallback.cpp index 85cf0471b..4d932b117 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -260,12 +260,12 @@ void CCallback::setFormation(const CGHeroInstance * hero, EArmyFormation mode) sendRequest(&pack); } -void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero) +void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero, const HeroTypeID & nextHero) { assert(townOrTavern); assert(hero); - HireHero pack(hero->getHeroType(), townOrTavern->id); + HireHero pack(hero->getHeroType(), townOrTavern->id, nextHero); pack.player = *player; sendRequest(&pack); } diff --git a/CCallback.h b/CCallback.h index 068ac547b..5e9f1cf1a 100644 --- a/CCallback.h +++ b/CCallback.h @@ -73,7 +73,7 @@ public: virtual void castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos = int3(-1, -1, -1))=0; //cast adventure map spell //town - virtual void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero)=0; + virtual void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero, const HeroTypeID & nextHero=HeroTypeID::NONE)=0; virtual bool buildBuilding(const CGTownInstance *town, BuildingID buildingID)=0; virtual void recruitCreatures(const CGDwelling *obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level=-1)=0; virtual bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE)=0; //if newID==-1 then best possible upgrade will be made @@ -185,7 +185,7 @@ public: void trade(const IMarket * market, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero = nullptr) override; void trade(const IMarket * market, EMarketMode mode, const std::vector & id1, const std::vector & id2, const std::vector & val1, const CGHeroInstance * hero = nullptr) override; void setFormation(const CGHeroInstance * hero, EArmyFormation mode) override; - void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero) override; + void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero, const HeroTypeID & nextHero=HeroTypeID::NONE) override; void save(const std::string &fname) override; void sendMessage(const std::string &mess, const CGObjectInstance * currentObject = nullptr) override; void gamePause(bool pause) override; diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 8fed1bd40..c433451fb 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -17,6 +17,8 @@ #include "InfoWindows.h" #include "../CGameInfo.h" +#include "../CServerHandler.h" +#include "../Client.h" #include "../CMusicHandler.h" #include "../CPlayerInterface.h" #include "../CVideoHandler.h" @@ -48,6 +50,7 @@ #include "../lib/mapObjects/ObjectTemplate.h" #include "../lib/gameState/CGameState.h" #include "../lib/gameState/SThievesGuildInfo.h" +#include "../lib/gameState/TavernHeroesPool.h" #include "../lib/CGeneralTextHandler.h" #include "../lib/CHeroHandler.h" #include "../lib/GameSettings.h" @@ -514,7 +517,17 @@ void CTavernWindow::recruitb() const CGHeroInstance *toBuy = (selected ? h2 : h1)->h; const CGObjectInstance *obj = tavernObj; - LOCPLINT->cb->recruitHero(obj, toBuy); + const auto & heroesPool = CSH->client->gameState()->heroesPool; + HeroTypeID nextHero = HeroTypeID::NONE; + + for(auto & elem : heroesPool->unusedHeroesFromPool()) + { + bool heroAvailable = heroesPool->isHeroAvailableFor(elem.first, tavernObj->getOwner()); + if(heroAvailable && elem.second->getNameTranslated() == "Sir Mullich") + nextHero = elem.first; + } + + LOCPLINT->cb->recruitHero(obj, toBuy, nextHero); close(); } diff --git a/lib/networkPacks/PacksForServer.h b/lib/networkPacks/PacksForServer.h index de1d7d613..3b3df13c6 100644 --- a/lib/networkPacks/PacksForServer.h +++ b/lib/networkPacks/PacksForServer.h @@ -516,12 +516,14 @@ struct DLL_LINKAGE SetFormation : public CPackForServer struct DLL_LINKAGE HireHero : public CPackForServer { HireHero() = default; - HireHero(HeroTypeID HID, const ObjectInstanceID & TID) + HireHero(HeroTypeID HID, const ObjectInstanceID & TID, const HeroTypeID & NHID) : hid(HID) , tid(TID) + , nhid(NHID) { } HeroTypeID hid; //available hero serial + HeroTypeID nhid; //next hero ObjectInstanceID tid; //town (tavern) id PlayerColor player; @@ -531,6 +533,7 @@ struct DLL_LINKAGE HireHero : public CPackForServer { h & static_cast(*this); h & hid; + h & nhid; h & tid; h & player; } diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index a3d77db31..2d064316b 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -282,7 +282,7 @@ void ApplyGhNetPackVisitor::visitHireHero(HireHero & pack) { gh.throwIfWrongPlayer(&pack); - result = gh.heroPool->hireHero(pack.tid, pack.hid, pack.player); + result = gh.heroPool->hireHero(pack.tid, pack.hid, pack.player, pack.nhid); } void ApplyGhNetPackVisitor::visitBuildBoat(BuildBoat & pack) diff --git a/server/processors/HeroPoolProcessor.cpp b/server/processors/HeroPoolProcessor.cpp index 623340e6c..40ce321f3 100644 --- a/server/processors/HeroPoolProcessor.cpp +++ b/server/processors/HeroPoolProcessor.cpp @@ -104,7 +104,7 @@ void HeroPoolProcessor::clearHeroFromSlot(const PlayerColor & color, TavernHeroS gameHandler->sendAndApply(&sah); } -void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot, bool needNativeHero, bool giveArmy) +void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot, bool needNativeHero, bool giveArmy, const HeroTypeID & nextHero) { SetAvailableHero sah; sah.player = color; @@ -113,6 +113,10 @@ void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHe CGHeroInstance *newHero = pickHeroFor(needNativeHero, color); + const auto & heroesPool = gameHandler->gameState()->heroesPool; + if(gameHandler->getStartInfo()->extraOptionsInfo.unlimitedReplay && heroesPool->unusedHeroesFromPool().count(nextHero) && heroesPool->isHeroAvailableFor(nextHero, color)) + newHero = heroesPool->unusedHeroesFromPool()[nextHero]; + if (newHero) { sah.hid = newHero->getHeroType(); @@ -145,7 +149,7 @@ void HeroPoolProcessor::onNewWeek(const PlayerColor & color) 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, const HeroTypeID & nextHero) { const PlayerState * playerState = gameHandler->getPlayerState(player); const CGObjectInstance * mapObject = gameHandler->getObj(objectID); @@ -226,9 +230,9 @@ bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTy gameHandler->sendAndApply(&hr); if(recruitableHeroes[0] == recruitedHero) - selectNewHeroForSlot(player, TavernHeroSlot::NATIVE, false, false); + selectNewHeroForSlot(player, TavernHeroSlot::NATIVE, false, false, nextHero); else - selectNewHeroForSlot(player, TavernHeroSlot::RANDOM, false, false); + selectNewHeroForSlot(player, TavernHeroSlot::RANDOM, false, false, nextHero); gameHandler->giveResource(player, EGameResID::GOLD, -GameConstants::HERO_GOLD_COST); diff --git a/server/processors/HeroPoolProcessor.h b/server/processors/HeroPoolProcessor.h index eed300a79..c69255496 100644 --- a/server/processors/HeroPoolProcessor.h +++ b/server/processors/HeroPoolProcessor.h @@ -9,6 +9,8 @@ */ #pragma once +#include "../../lib/constants/EntityIdentifiers.h" + VCMI_LIB_NAMESPACE_BEGIN enum class TavernHeroSlot : int8_t; @@ -33,7 +35,7 @@ class HeroPoolProcessor : boost::noncopyable std::map> heroSeed; void clearHeroFromSlot(const PlayerColor & color, TavernHeroSlot slot); - void selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot, bool needNativeHero, bool giveStartingArmy); + void selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot, bool needNativeHero, bool giveStartingArmy, const HeroTypeID & nextHero = HeroTypeID::NONE); std::vector findAvailableClassesFor(const PlayerColor & player) const; std::vector findAvailableHeroesFor(const PlayerColor & player, const CHeroClass * heroClass) const; @@ -60,7 +62,7 @@ public: CRandomGenerator & getHeroSkillsRandomGenerator(const HeroTypeID & hero); /// Incoming net pack handling - bool hireHero(const ObjectInstanceID & objectID, const HeroTypeID & hid, const PlayerColor & player); + bool hireHero(const ObjectInstanceID & objectID, const HeroTypeID & hid, const PlayerColor & player, const HeroTypeID & nextHero); template void serialize(Handler &h, const int version) {