mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	| @@ -261,12 +261,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); | ||||
| } | ||||
|   | ||||
| @@ -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<TradeItemSell> & id1, const std::vector<TradeItemBuy> & id2, const std::vector<ui32> & 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; | ||||
|   | ||||
| @@ -233,6 +233,8 @@ | ||||
| 	"vcmi.heroWindow.openBackpack.hover" : "Open artifact backpack window", | ||||
| 	"vcmi.heroWindow.openBackpack.help"  : "Opens window that allows easier artifact backpack management.", | ||||
|  | ||||
| 	"vcmi.tavernWindow.inviteHero"  : "Invite hero", | ||||
|  | ||||
| 	"vcmi.commanderWindow.artifactMessage" : "Do you want to return this artifact to the hero?", | ||||
|  | ||||
| 	"vcmi.creatureWindow.showBonuses.hover"    : "Switch to bonuses view", | ||||
|   | ||||
| @@ -232,6 +232,8 @@ | ||||
| 	"vcmi.heroWindow.openBackpack.hover" : "Artefakt-Rucksack-Fenster öffnen", | ||||
| 	"vcmi.heroWindow.openBackpack.help"  : "Öffnet ein Fenster, das die Verwaltung des Artefakt-Rucksacks erleichtert", | ||||
|  | ||||
| 	"vcmi.tavernWindow.inviteHero"  : "Helden einladen", | ||||
|  | ||||
| 	"vcmi.commanderWindow.artifactMessage" : "Möchtet Ihr diesen Artefakt dem Helden zurückgeben?", | ||||
|  | ||||
| 	"vcmi.creatureWindow.showBonuses.hover"    : "Wechsle zur Bonus-Ansicht", | ||||
|   | ||||
| @@ -407,8 +407,14 @@ void OptionsTabBase::recreate() | ||||
| 	} | ||||
|  | ||||
| 	if(auto buttonCheatAllowed = widget<CToggleButton>("buttonCheatAllowed")) | ||||
| 	{ | ||||
| 		buttonCheatAllowed->setSelectedSilent(SEL->getStartInfo()->extraOptionsInfo.cheatsAllowed); | ||||
| 		buttonCheatAllowed->block(SEL->screenType == ESelectionScreen::loadGame); | ||||
| 	} | ||||
|  | ||||
| 	if(auto buttonUnlimitedReplay = widget<CToggleButton>("buttonUnlimitedReplay")) | ||||
| 	{ | ||||
| 		buttonUnlimitedReplay->setSelectedSilent(SEL->getStartInfo()->extraOptionsInfo.unlimitedReplay); | ||||
| 		buttonUnlimitedReplay->block(SEL->screenType == ESelectionScreen::loadGame); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -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" | ||||
| @@ -443,7 +446,8 @@ CLevelWindow::~CLevelWindow() | ||||
| CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj, const std::function<void()> & onWindowClosed) | ||||
| 	: CStatusbarWindow(PLAYER_COLORED, ImagePath::builtin("TPTAVERN")), | ||||
| 	onWindowClosed(onWindowClosed), | ||||
| 	tavernObj(TavernObj) | ||||
| 	tavernObj(TavernObj), | ||||
| 	heroToInvite(nullptr) | ||||
| { | ||||
| 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); | ||||
|  | ||||
| @@ -459,8 +463,8 @@ CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj, const std::func | ||||
|  | ||||
| 	oldSelected = -1; | ||||
|  | ||||
| 	h1 = std::make_shared<HeroPortrait>(selected, 0, 72, 299, h[0]); | ||||
| 	h2 = std::make_shared<HeroPortrait>(selected, 1, 162, 299, h[1]); | ||||
| 	h1 = std::make_shared<HeroPortrait>(selected, 0, 72, 299, h[0], [this]() { if(!recruit->isBlocked()) recruitb(); }); | ||||
| 	h2 = std::make_shared<HeroPortrait>(selected, 1, 162, 299, h[1], [this]() { if(!recruit->isBlocked()) recruitb(); }); | ||||
|  | ||||
| 	title = std::make_shared<CLabel>(197, 32, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->jktexts[37]); | ||||
| 	cost = std::make_shared<CLabel>(320, 328, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, std::to_string(GameConstants::HERO_GOLD_COST)); | ||||
| @@ -507,6 +511,34 @@ CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj, const std::func | ||||
| 		CCS->videoh->open(townObj->town->clientInfo.tavernVideo); | ||||
| 	else | ||||
| 		CCS->videoh->open(VideoPath::builtin("TAVERN.BIK")); | ||||
|  | ||||
| 	addInvite(); | ||||
| } | ||||
|  | ||||
| void CTavernWindow::addInvite() | ||||
| { | ||||
| 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); | ||||
|  | ||||
| 	if(!VLC->settings()->getBoolean(EGameSettings::HEROES_TAVERN_INVITE)) | ||||
| 		return; | ||||
|  | ||||
| 	const auto & heroesPool = CSH->client->gameState()->heroesPool; | ||||
| 	for(auto & elem : heroesPool->unusedHeroesFromPool()) | ||||
| 	{ | ||||
| 		bool heroAvailable = heroesPool->isHeroAvailableFor(elem.first, tavernObj->getOwner()); | ||||
| 		if(heroAvailable) | ||||
| 			inviteableHeroes[elem.first] = elem.second; | ||||
| 	} | ||||
|  | ||||
| 	if(!inviteableHeroes.empty()) | ||||
| 	{ | ||||
| 		if(!heroToInvite) | ||||
| 			heroToInvite = (*RandomGeneratorUtil::nextItem(inviteableHeroes, CRandomGenerator::getDefault())).second; | ||||
|  | ||||
| 		inviteHero = std::make_shared<CLabel>(170, 444, EFonts::FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->translate("vcmi.tavernWindow.inviteHero")); | ||||
| 		inviteHeroImage = std::make_shared<CAnimImage>(AnimationPath::builtin("PortraitsSmall"), (*CGI->heroh)[heroToInvite->getHeroType()]->imageIndex, 0, 245, 428); | ||||
| 		inviteHeroImageArea = std::make_shared<LRClickableArea>(Rect(245, 428, 48, 32), [this](){ GH.windows().createAndPushWindow<HeroSelector>(inviteableHeroes, [this](CGHeroInstance* h){ heroToInvite = h; addInvite(); }); }, [this](){ GH.windows().createAndPushWindow<CRClickPopupInt>(std::make_shared<CHeroWindow>(heroToInvite)); }); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CTavernWindow::recruitb() | ||||
| @@ -514,7 +546,7 @@ void CTavernWindow::recruitb() | ||||
| 	const CGHeroInstance *toBuy = (selected ? h2 : h1)->h; | ||||
| 	const CGObjectInstance *obj = tavernObj; | ||||
|  | ||||
| 	LOCPLINT->cb->recruitHero(obj, toBuy); | ||||
| 	LOCPLINT->cb->recruitHero(obj, toBuy, heroToInvite ? heroToInvite->getHeroType() : HeroTypeID::NONE); | ||||
| 	close(); | ||||
| } | ||||
|  | ||||
| @@ -569,15 +601,23 @@ void CTavernWindow::HeroPortrait::clickPressed(const Point & cursorPosition) | ||||
| 		*_sel = _id; | ||||
| } | ||||
|  | ||||
| void CTavernWindow::HeroPortrait::clickDouble(const Point & cursorPosition) | ||||
| { | ||||
| 	clickPressed(cursorPosition); | ||||
| 	 | ||||
| 	if(onChoose) | ||||
| 		onChoose(); | ||||
| } | ||||
|  | ||||
| void CTavernWindow::HeroPortrait::showPopupWindow(const Point & cursorPosition) | ||||
| { | ||||
| 	if(h) | ||||
| 		GH.windows().createAndPushWindow<CRClickPopupInt>(std::make_shared<CHeroWindow>(h)); | ||||
| } | ||||
|  | ||||
| CTavernWindow::HeroPortrait::HeroPortrait(int & sel, int id, int x, int y, const CGHeroInstance * H) | ||||
| 	: CIntObject(LCLICK | SHOW_POPUP | HOVER), | ||||
| 	h(H), _sel(&sel), _id(id) | ||||
| CTavernWindow::HeroPortrait::HeroPortrait(int & sel, int id, int x, int y, const CGHeroInstance * H, std::function<void()> OnChoose) | ||||
| 	: CIntObject(LCLICK | DOUBLECLICK | SHOW_POPUP | HOVER), | ||||
| 	h(H), _sel(&sel), _id(id), onChoose(OnChoose) | ||||
| { | ||||
| 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); | ||||
| 	h = H; | ||||
| @@ -615,6 +655,33 @@ void CTavernWindow::HeroPortrait::hover(bool on) | ||||
| 		GH.statusbar()->clear(); | ||||
| } | ||||
|  | ||||
| CTavernWindow::HeroSelector::HeroSelector(std::map<HeroTypeID, CGHeroInstance*> InviteableHeroes, std::function<void(CGHeroInstance*)> OnChoose) | ||||
| 	: CWindowObject(BORDERED), inviteableHeroes(InviteableHeroes), onChoose(OnChoose) | ||||
| { | ||||
| 	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; | ||||
|  | ||||
| 	pos = Rect(0, 0, 16 * 48, (inviteableHeroes.size() / 16 + (inviteableHeroes.size() % 16 != 0)) * 32); | ||||
| 	background = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), Rect(0, 0, pos.w, pos.h)); | ||||
|  | ||||
| 	int x = 0; | ||||
| 	int y = 0; | ||||
| 	for(auto & h : inviteableHeroes) | ||||
| 	{ | ||||
| 		portraits.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("PortraitsSmall"), (*CGI->heroh)[h.first]->imageIndex, 0, x * 48, y * 32)); | ||||
| 		portraitAreas.push_back(std::make_shared<LRClickableArea>(Rect(x * 48, y * 32, 48, 32), [this, h](){ close(); onChoose(inviteableHeroes[h.first]); }, [this, h](){ GH.windows().createAndPushWindow<CRClickPopupInt>(std::make_shared<CHeroWindow>(inviteableHeroes[h.first])); })); | ||||
|  | ||||
| 		if(x > 0 && x % 15 == 0) | ||||
| 		{ | ||||
| 			x = 0; | ||||
| 			y++; | ||||
| 		} | ||||
| 		else | ||||
| 			x++; | ||||
| 	} | ||||
|  | ||||
| 	center(); | ||||
| } | ||||
|  | ||||
| static const std::string QUICK_EXCHANGE_MOD_PREFIX = "quick-exchange"; | ||||
| static const std::string QUICK_EXCHANGE_BG = QUICK_EXCHANGE_MOD_PREFIX + "/TRADEQE"; | ||||
|  | ||||
|   | ||||
| @@ -36,6 +36,8 @@ class CTextBox; | ||||
| class CGarrisonInt; | ||||
| class CGarrisonSlot; | ||||
| class CHeroArea; | ||||
| class CAnimImage; | ||||
| class CFilledTexture; | ||||
|  | ||||
| enum class EUserEvent; | ||||
|  | ||||
| @@ -206,10 +208,13 @@ public: | ||||
| 		std::string description; // "XXX is a level Y ZZZ with N artifacts" | ||||
| 		const CGHeroInstance * h; | ||||
|  | ||||
| 		std::function<void()> onChoose; | ||||
|  | ||||
| 		void clickPressed(const Point & cursorPosition) override; | ||||
| 		void clickDouble(const Point & cursorPosition) override; | ||||
| 		void showPopupWindow(const Point & cursorPosition) override; | ||||
| 		void hover (bool on) override; | ||||
| 		HeroPortrait(int & sel, int id, int x, int y, const CGHeroInstance * H); | ||||
| 		HeroPortrait(int & sel, int id, int x, int y, const CGHeroInstance * H, std::function<void()> OnChoose = nullptr); | ||||
|  | ||||
| 	private: | ||||
| 		int *_sel; | ||||
| @@ -218,6 +223,21 @@ public: | ||||
| 		std::shared_ptr<CAnimImage> portrait; | ||||
| 	}; | ||||
|  | ||||
| 	class HeroSelector : public CWindowObject | ||||
| 	{ | ||||
| 	public: | ||||
| 		std::shared_ptr<CFilledTexture> background; | ||||
|  | ||||
| 		HeroSelector(std::map<HeroTypeID, CGHeroInstance*> InviteableHeroes, std::function<void(CGHeroInstance*)> OnChoose); | ||||
|  | ||||
| 	private: | ||||
| 		std::map<HeroTypeID, CGHeroInstance*> inviteableHeroes; | ||||
| 		std::function<void(CGHeroInstance*)> onChoose; | ||||
|  | ||||
| 		std::vector<std::shared_ptr<CAnimImage>> portraits; | ||||
| 		std::vector<std::shared_ptr<LRClickableArea>> portraitAreas; | ||||
| 	}; | ||||
|  | ||||
| 	//recruitable heroes | ||||
| 	std::shared_ptr<HeroPortrait> h1; | ||||
| 	std::shared_ptr<HeroPortrait> h2; //recruitable heroes | ||||
| @@ -237,6 +257,13 @@ public: | ||||
| 	std::shared_ptr<CTextBox> heroDescription; | ||||
|  | ||||
| 	std::shared_ptr<CTextBox> rumor; | ||||
| 	 | ||||
| 	std::shared_ptr<CLabel> inviteHero; | ||||
| 	std::shared_ptr<CAnimImage> inviteHeroImage; | ||||
| 	std::shared_ptr<LRClickableArea> inviteHeroImageArea; | ||||
| 	std::map<HeroTypeID, CGHeroInstance*> inviteableHeroes; | ||||
| 	CGHeroInstance* heroToInvite; | ||||
| 	void addInvite(); | ||||
|  | ||||
| 	CTavernWindow(const CGObjectInstance * TavernObj, const std::function<void()> & onWindowClosed); | ||||
| 	~CTavernWindow(); | ||||
|   | ||||
| @@ -289,7 +289,9 @@ | ||||
| 			// Chances for a hero with default army to receive corresponding stack out of his predefined starting troops | ||||
| 			"startingStackChances": [ 100, 88, 25], | ||||
| 			// number of artifacts that can fit in a backpack. -1 is unlimited. | ||||
| 			"backpackSize"		: -1 | ||||
| 			"backpackSize"		: -1, | ||||
| 			// if heroes are invitable in tavern | ||||
| 			"tavernInvite"            : false | ||||
| 		}, | ||||
|  | ||||
| 		"towns": | ||||
|   | ||||
| @@ -64,12 +64,14 @@ | ||||
| 					"name": "buttonCheatAllowed", | ||||
| 					"image": "lobby/checkbox", | ||||
| 					"callback" : "setCheatAllowed", | ||||
| 					"help" : "vcmi.optionsTab.cheatAllowed", | ||||
| 					"selected" : true | ||||
| 				}, | ||||
| 				{ | ||||
| 					"name": "buttonUnlimitedReplay", | ||||
| 					"image": "lobby/checkbox", | ||||
| 					"callback" : "setUnlimitedReplay", | ||||
| 					"help" : "vcmi.optionsTab.unlimitedReplay", | ||||
| 					"selected" : true | ||||
| 				} | ||||
| 			] | ||||
|   | ||||
| @@ -74,6 +74,7 @@ void GameSettings::load(const JsonNode & input) | ||||
| 		{EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS,   "heroes",    "retreatOnWinWithoutTroops"  }, | ||||
| 		{EGameSettings::HEROES_STARTING_STACKS_CHANCES,         "heroes",    "startingStackChances"       }, | ||||
| 		{EGameSettings::HEROES_BACKPACK_CAP,                    "heroes",    "backpackSize"               }, | ||||
| 		{EGameSettings::HEROES_TAVERN_INVITE,                   "heroes",    "tavernInvite"               }, | ||||
| 		{EGameSettings::MAP_FORMAT_RESTORATION_OF_ERATHIA,      "mapFormat", "restorationOfErathia"       }, | ||||
| 		{EGameSettings::MAP_FORMAT_ARMAGEDDONS_BLADE,           "mapFormat", "armageddonsBlade"           }, | ||||
| 		{EGameSettings::MAP_FORMAT_SHADOW_OF_DEATH,             "mapFormat", "shadowOfDeath"              }, | ||||
|   | ||||
| @@ -38,6 +38,7 @@ enum class EGameSettings | ||||
| 	HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS, | ||||
| 	HEROES_STARTING_STACKS_CHANCES, | ||||
| 	HEROES_BACKPACK_CAP, | ||||
| 	HEROES_TAVERN_INVITE, | ||||
| 	MARKETS_BLACK_MARKET_RESTOCK_PERIOD, | ||||
| 	BANKS_SHOW_GUARDS_COMPOSITION, | ||||
| 	MODULE_COMMANDERS, | ||||
|   | ||||
| @@ -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<CPackForServer &>(*this); | ||||
| 		h & hid; | ||||
| 		h & nhid; | ||||
| 		h & tid; | ||||
| 		h & player; | ||||
| 	} | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -23,6 +23,7 @@ | ||||
| #include "../../lib/gameState/CGameState.h" | ||||
| #include "../../lib/gameState/TavernHeroesPool.h" | ||||
| #include "../../lib/gameState/TavernSlot.h" | ||||
| #include "../../lib/GameSettings.h" | ||||
|  | ||||
| HeroPoolProcessor::HeroPoolProcessor() | ||||
| 	: gameHandler(nullptr) | ||||
| @@ -104,14 +105,14 @@ 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; | ||||
| 	sah.slotID = slot; | ||||
| 	sah.replenishPoints = true; | ||||
|  | ||||
| 	CGHeroInstance *newHero = pickHeroFor(needNativeHero, color); | ||||
| 	CGHeroInstance *newHero = (nextHero == HeroTypeID::NONE) ? pickHeroFor(needNativeHero, color) : gameHandler->gameState()->heroesPool->unusedHeroesFromPool()[nextHero]; | ||||
|  | ||||
| 	if (newHero) | ||||
| 	{ | ||||
| @@ -145,11 +146,12 @@ 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); | ||||
| 	const CGTownInstance * town = gameHandler->getTown(objectID); | ||||
| 	const auto & heroesPool = gameHandler->gameState()->heroesPool; | ||||
|  | ||||
| 	if (!mapObject && gameHandler->complain("Invalid map object!")) | ||||
| 		return false; | ||||
| @@ -166,6 +168,18 @@ bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTy | ||||
| 	if (gameHandler->getHeroCount(player, true) >= VLC->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP) && gameHandler->complain("Cannot hire hero, too many heroes garrizoned and wandering already!")) | ||||
| 		return false; | ||||
|  | ||||
| 	if (nextHero != HeroTypeID::NONE) // player attempts to invite next hero | ||||
| 	{ | ||||
| 		if(!VLC->settings()->getBoolean(EGameSettings::HEROES_TAVERN_INVITE) && gameHandler->complain("Inviting heroes not allowed!")) | ||||
| 			return false; | ||||
|  | ||||
| 		if(!heroesPool->unusedHeroesFromPool().count(nextHero) && gameHandler->complain("Cannot invite specified hero!")) | ||||
| 			return false; | ||||
| 		 | ||||
| 		if(!heroesPool->isHeroAvailableFor(nextHero, player) && gameHandler->complain("Cannot invite specified hero!")) | ||||
| 			return false; | ||||
| 	} | ||||
|  | ||||
| 	if(town) //tavern in town | ||||
| 	{ | ||||
| 		if(gameHandler->getPlayerRelations(mapObject->tempOwner, player) == PlayerRelations::ENEMIES && gameHandler->complain("Can't buy hero in enemy town!")) | ||||
| @@ -192,7 +206,7 @@ bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTy | ||||
| 			return false; | ||||
| 	} | ||||
|  | ||||
| 	auto recruitableHeroes = gameHandler->gameState()->heroesPool->getHeroesFor(player); | ||||
| 	auto recruitableHeroes = heroesPool->getHeroesFor(player); | ||||
|  | ||||
| 	const CGHeroInstance * recruitedHero = nullptr; | ||||
|  | ||||
| @@ -226,9 +240,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); | ||||
|  | ||||
|   | ||||
| @@ -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<HeroTypeID, std::unique_ptr<CRandomGenerator>> 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<const CHeroClass *> findAvailableClassesFor(const PlayerColor & player) const; | ||||
| 	std::vector<CGHeroInstance *> 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 <typename Handler> void serialize(Handler &h) | ||||
| 	{ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user