mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Merge pull request #6211 from Laserlicht/text_edit
Allowing editing town name
This commit is contained in:
		| @@ -104,6 +104,7 @@ public: | ||||
| 	void visitSetAvailableArtifacts(SetAvailableArtifacts & pack) override; | ||||
| 	void visitEntitiesChanged(EntitiesChanged & pack) override; | ||||
| 	void visitPlayerCheated(PlayerCheated & pack) override; | ||||
| 	void visitChangeTownName(ChangeTownName & pack) override; | ||||
| }; | ||||
|  | ||||
| class ApplyFirstClientNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor) | ||||
|   | ||||
| @@ -1071,3 +1071,16 @@ void ApplyClientNetPackVisitor::visitPlayerCheated(PlayerCheated & pack) | ||||
| 	if(pack.colorScheme != ColorScheme::KEEP && vstd::contains(cl.playerint, pack.player)) | ||||
| 		cl.playerint[pack.player]->setColorScheme(pack.colorScheme); | ||||
| } | ||||
|  | ||||
| void ApplyClientNetPackVisitor::visitChangeTownName(ChangeTownName & pack) | ||||
| { | ||||
| 	if(!adventureInt) | ||||
| 		return; | ||||
|  | ||||
| 	const CGTownInstance *town = gs.getTown(pack.tid); | ||||
| 	if(town) | ||||
| 	{ | ||||
| 		adventureInt->onTownChanged(town); | ||||
| 		ENGINE->windows().totalRedraw(); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1035,10 +1035,7 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry(const PlayerSettings & S, con | ||||
| 	if(s->isControlledByAI() || GAME->server().isGuest()) | ||||
| 		labelPlayerName = std::make_shared<CLabel>(55, 10, EFonts::FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, name, 95); | ||||
| 	else | ||||
| 	{ | ||||
| 		labelPlayerNameEdit = std::make_shared<CTextInput>(Rect(6, 3, 95, 15), EFonts::FONT_SMALL, ETextAlignment::CENTER, false); | ||||
| 		labelPlayerNameEdit->setText(name); | ||||
| 	} | ||||
| 		labelPlayerNameEdit = std::make_shared<CTextInputWithConfirm>(Rect(6, 3, 95, 15), EFonts::FONT_SMALL, ETextAlignment::CENTER, name, false, [this](){ updateName(); }); | ||||
|  | ||||
| 	labelWhoCanPlay = std::make_shared<CMultiLineLabel>(Rect(6, 21, 45, 26), EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->arraytxt[206 + whoCanPlay]); | ||||
|  | ||||
| @@ -1114,28 +1111,6 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry(const PlayerSettings & S, con | ||||
| 	bonus = std::make_shared<SelectedBox>(Point(271, 2), *s, BONUS); | ||||
| } | ||||
|  | ||||
| bool OptionsTab::PlayerOptionsEntry::captureThisKey(EShortcut key) | ||||
| { | ||||
| 	return labelPlayerNameEdit && labelPlayerNameEdit->hasFocus() && key == EShortcut::GLOBAL_ACCEPT; | ||||
| } | ||||
|  | ||||
| void OptionsTab::PlayerOptionsEntry::keyPressed(EShortcut key) | ||||
| { | ||||
| 	if(labelPlayerNameEdit && key == EShortcut::GLOBAL_ACCEPT) | ||||
| 		updateName(); | ||||
| } | ||||
|  | ||||
| bool OptionsTab::PlayerOptionsEntry::receiveEvent(const Point & position, int eventType) const | ||||
| { | ||||
| 	return eventType == AEventsReceiver::LCLICK; // capture all left clicks (not only within control) | ||||
| } | ||||
|  | ||||
| void OptionsTab::PlayerOptionsEntry::clickReleased(const Point & cursorPosition) | ||||
| { | ||||
| 	if(labelPlayerNameEdit && !labelPlayerNameEdit->pos.isInside(cursorPosition)) | ||||
| 		updateName(); | ||||
| } | ||||
|  | ||||
| void OptionsTab::PlayerOptionsEntry::updateName() { | ||||
| 	if(labelPlayerNameEdit->getText() != name) | ||||
| 	{ | ||||
| @@ -1146,8 +1121,6 @@ void OptionsTab::PlayerOptionsEntry::updateName() { | ||||
| 			set->String() = labelPlayerNameEdit->getText(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	labelPlayerNameEdit->removeFocus(); | ||||
| 	name = labelPlayerNameEdit->getText(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -28,6 +28,7 @@ class CTextBox; | ||||
| class CButton; | ||||
| class CSlider; | ||||
| class LRClickableArea; | ||||
| class CTextInputWithConfirm; | ||||
|  | ||||
| class FilledTexturePlayerColored; | ||||
| class TransparentFilledRectangle; | ||||
| @@ -196,7 +197,7 @@ private: | ||||
| 		std::unique_ptr<PlayerInfo> pi; | ||||
| 		std::unique_ptr<PlayerSettings> s; | ||||
| 		std::shared_ptr<CLabel> labelPlayerName; | ||||
| 		std::shared_ptr<CTextInput> labelPlayerNameEdit; | ||||
| 		std::shared_ptr<CTextInputWithConfirm> labelPlayerNameEdit; | ||||
| 		std::shared_ptr<CMultiLineLabel> labelWhoCanPlay; | ||||
| 		std::shared_ptr<CPicture> background; | ||||
| 		std::shared_ptr<CButton> buttonTownLeft; | ||||
| @@ -215,10 +216,6 @@ private: | ||||
|  | ||||
| 		PlayerOptionsEntry(const PlayerSettings & S, const OptionsTab & parentTab); | ||||
| 		void hideUnavailableButtons(); | ||||
| 		bool captureThisKey(EShortcut key) override; | ||||
| 		void keyPressed(EShortcut key) override; | ||||
| 		void clickReleased(const Point & cursorPosition) override; | ||||
| 		bool receiveEvent(const Point & position, int eventType) const override; | ||||
|  | ||||
| 	private: | ||||
| 		const OptionsTab & parentTab; | ||||
|   | ||||
| @@ -85,6 +85,7 @@ private: | ||||
| 	//void visitSetCommanderProperty(SetCommanderProperty & pack) override; | ||||
| 	//void visitAddQuest(AddQuest & pack) override; | ||||
| 	//void visitChangeFormation(ChangeFormation & pack) override; | ||||
| 	//void visitChangeTownName(ChangeTownName & pack) override; | ||||
| 	//void visitChangeSpells(ChangeSpells & pack) override; | ||||
| 	//void visitSetAvailableHero(SetAvailableHero & pack) override; | ||||
| 	//void visitChangeObjectVisitors(ChangeObjectVisitors & pack) override; | ||||
|   | ||||
| @@ -27,6 +27,85 @@ | ||||
| std::list<CFocusable *> CFocusable::focusables; | ||||
| CFocusable * CFocusable::inputWithFocus; | ||||
|  | ||||
| CTextInputWithConfirm::CTextInputWithConfirm(const Rect & Pos, EFonts font, ETextAlignment alignment, std::string text, bool limitToRect, std::function<void()> confirmCallback) | ||||
| 	: CTextInput(Pos, font, alignment, false), confirmCb(confirmCallback), limitToRect(limitToRect), initialText(text) | ||||
| { | ||||
| 	setText(text); | ||||
| } | ||||
|  | ||||
| bool CTextInputWithConfirm::captureThisKey(EShortcut key) | ||||
| { | ||||
| 	return hasFocus() && (key == EShortcut::GLOBAL_ACCEPT || key == EShortcut::GLOBAL_CANCEL || key == EShortcut::GLOBAL_BACKSPACE); | ||||
| } | ||||
|  | ||||
| void CTextInputWithConfirm::keyPressed(EShortcut key) | ||||
| { | ||||
| 	if(!hasFocus()) | ||||
| 		return; | ||||
|  | ||||
| 	if(key == EShortcut::GLOBAL_ACCEPT) | ||||
| 		confirm(); | ||||
| 	else if(key == EShortcut::GLOBAL_CANCEL) | ||||
| 	{ | ||||
| 		setText(initialText); | ||||
| 		removeFocus(); | ||||
| 	} | ||||
| 	 | ||||
| 	CTextInput::keyPressed(key); | ||||
| } | ||||
|  | ||||
| bool CTextInputWithConfirm::receiveEvent(const Point & position, int eventType) const | ||||
| { | ||||
| 	return eventType == AEventsReceiver::LCLICK; // capture all left clicks (not only within control) | ||||
| } | ||||
|  | ||||
| void CTextInputWithConfirm::clickReleased(const Point & cursorPosition) | ||||
| { | ||||
| 	if(!pos.isInside(cursorPosition)) // clicked outside | ||||
| 		confirm(); | ||||
| } | ||||
|  | ||||
| void CTextInputWithConfirm::clickPressed(const Point & cursorPosition) | ||||
| { | ||||
| 	if(pos.isInside(cursorPosition)) // clickPressed should respect control area (receiveEvent also affects this) | ||||
| 		CTextInput::clickPressed(cursorPosition); | ||||
| } | ||||
|  | ||||
| void CTextInputWithConfirm::onFocusGot() | ||||
| { | ||||
| 	initialText = getText(); | ||||
|  | ||||
| 	CTextInput::onFocusGot(); | ||||
| } | ||||
|  | ||||
| void CTextInputWithConfirm::textInputted(const std::string & enteredText) | ||||
| { | ||||
| 	if(!hasFocus()) | ||||
| 		return; | ||||
|  | ||||
| 	std::string visibleText = getVisibleText() + enteredText; | ||||
| 	const auto & font = ENGINE->renderHandler().loadFont(label->font); | ||||
| 	if(!limitToRect || (font->getStringWidth(visibleText) - CLabel::getDelimitersWidth(label->font, visibleText)) < pos.w) | ||||
| 		CTextInput::textInputted(enteredText); | ||||
| } | ||||
|  | ||||
| void CTextInputWithConfirm::deactivate() | ||||
| { | ||||
| 	removeUsedEvents(LCLICK); | ||||
|  | ||||
| 	CTextInput::deactivate(); | ||||
| } | ||||
|  | ||||
| void CTextInputWithConfirm::confirm() | ||||
| { | ||||
| 	if(getText().empty()) | ||||
| 		setText(initialText); | ||||
|  | ||||
| 	if(confirmCb && initialText != getText()) | ||||
| 		confirmCb(); | ||||
| 	removeFocus(); | ||||
| } | ||||
|  | ||||
| CTextInput::CTextInput(const Rect & Pos) | ||||
| 	:originalAlignment(ETextAlignment::CENTERLEFT) | ||||
| { | ||||
| @@ -196,7 +275,7 @@ void CTextInput::updateLabel() | ||||
| 	label->alignment = originalAlignment; | ||||
| 	const auto & font = ENGINE->renderHandler().loadFont(label->font); | ||||
|  | ||||
| 	while (font->getStringWidth(visibleText) > pos.w) | ||||
| 	while ((font->getStringWidth(visibleText) - CLabel::getDelimitersWidth(label->font, visibleText)) > pos.w) | ||||
| 	{ | ||||
| 		label->alignment = ETextAlignment::CENTERRIGHT; | ||||
| 		visibleText = visibleText.substr(TextOperations::getUnicodeCharacterSize(visibleText[0])); | ||||
|   | ||||
| @@ -45,8 +45,9 @@ public: | ||||
| }; | ||||
|  | ||||
| /// Text input box where players can enter text | ||||
| class CTextInput final : public CFocusable | ||||
| class CTextInput : public CFocusable | ||||
| { | ||||
| protected: | ||||
| 	using TextEditedCallback = std::function<void(const std::string &)>; | ||||
| 	using TextFilterCallback = std::function<void(std::string &, const std::string &)>; | ||||
|  | ||||
| @@ -71,12 +72,12 @@ class CTextInput final : public CFocusable | ||||
| 	void createLabel(bool giveFocusToInput); | ||||
| 	void updateLabel(); | ||||
|  | ||||
| 	void clickPressed(const Point & cursorPosition) final; | ||||
| 	void textInputted(const std::string & enteredText) final; | ||||
| 	void textEdited(const std::string & enteredText) final; | ||||
| 	void onFocusGot() final; | ||||
| 	void onFocusLost() final; | ||||
| 	void showPopupWindow(const Point & cursorPosition) final; | ||||
| 	void clickPressed(const Point & cursorPosition) override; | ||||
| 	void textInputted(const std::string & enteredText) override; | ||||
| 	void textEdited(const std::string & enteredText) override; | ||||
| 	void onFocusGot() override; | ||||
| 	void onFocusLost() override; | ||||
| 	void showPopupWindow(const Point & cursorPosition) override; | ||||
|  | ||||
| 	CTextInput(const Rect & Pos); | ||||
| public: | ||||
| @@ -105,7 +106,27 @@ public: | ||||
| 	void setAlignment(ETextAlignment alignment); | ||||
|  | ||||
| 	// CIntObject interface impl | ||||
| 	void keyPressed(EShortcut key) final; | ||||
| 	void activate() final; | ||||
| 	void deactivate() final; | ||||
| 	void keyPressed(EShortcut key) override; | ||||
| 	void activate() override; | ||||
| 	void deactivate() override; | ||||
| }; | ||||
|  | ||||
| class CTextInputWithConfirm final : public CTextInput | ||||
| { | ||||
| 	std::string initialText; | ||||
| 	std::function<void()> confirmCb; | ||||
| 	bool limitToRect; | ||||
|  | ||||
| 	void confirm(); | ||||
| public: | ||||
| 	CTextInputWithConfirm(const Rect & Pos, EFonts font, ETextAlignment alignment, std::string text, bool limitToRect, std::function<void()> confirmCallback); | ||||
|  | ||||
| 	bool captureThisKey(EShortcut key) override; | ||||
| 	void keyPressed(EShortcut key) override; | ||||
| 	void clickReleased(const Point & cursorPosition) override; | ||||
| 	void clickPressed(const Point & cursorPosition) override; | ||||
| 	bool receiveEvent(const Point & position, int eventType) const override; | ||||
| 	void onFocusGot() override; | ||||
| 	void textInputted(const std::string & enteredText) override; | ||||
| 	void deactivate() override; | ||||
| }; | ||||
|   | ||||
| @@ -182,29 +182,39 @@ std::vector<std::string> CMultiLineLabel::getLines() | ||||
| 	return lines; | ||||
| } | ||||
|  | ||||
| void CTextContainer::blitLine(Canvas & to, Rect destRect, std::string what) | ||||
| const std::string delimiters = "{}"; | ||||
|  | ||||
| int CTextContainer::getDelimitersWidth(EFonts font, std::string text) | ||||
| { | ||||
| 	const auto f = ENGINE->renderHandler().loadFont(font); | ||||
| 	Point where = destRect.topLeft(); | ||||
| 	const std::string delimiters = "{}"; | ||||
| 	auto delimitersCount = std::count_if(what.cbegin(), what.cend(), [&delimiters](char c) | ||||
| 	auto delimitersWidth = std::count_if(text.cbegin(), text.cend(), [](char c) | ||||
| 	{ | ||||
| 		return delimiters.find(c) != std::string::npos; | ||||
| 	}); | ||||
| 	//We should count delimiters length from string to correct centering later. | ||||
| 	delimitersCount *= f->getStringWidth(delimiters)/2; | ||||
| 	delimitersWidth *= f->getStringWidth(delimiters)/2; | ||||
|  | ||||
| 	std::smatch match; | ||||
| 	std::regex expr("\\{(.*?)\\|"); | ||||
| 	std::string::const_iterator searchStart( what.cbegin() ); | ||||
| 	while(std::regex_search(searchStart, what.cend(), match, expr)) | ||||
| 	std::string::const_iterator searchStart( text.cbegin() ); | ||||
| 	while(std::regex_search(searchStart, text.cend(), match, expr)) | ||||
| 	{ | ||||
| 		std::string colorText = match[1].str(); | ||||
| 		if(auto c = Colors::parseColor(colorText)) | ||||
| 			delimitersCount += f->getStringWidth(colorText + "|"); | ||||
| 			delimitersWidth += f->getStringWidth(colorText + "|"); | ||||
| 		searchStart = match.suffix().first; | ||||
| 	} | ||||
|  | ||||
| 	return delimitersWidth; | ||||
| } | ||||
|  | ||||
| void CTextContainer::blitLine(Canvas & to, Rect destRect, std::string what) | ||||
| { | ||||
| 	const auto f = ENGINE->renderHandler().loadFont(font); | ||||
| 	Point where = destRect.topLeft(); | ||||
|  | ||||
| 	int delimitersWidth = getDelimitersWidth(font, what); | ||||
|  | ||||
| 	// input is rect in which given text should be placed | ||||
| 	// calculate proper position for top-left corner of the text | ||||
|  | ||||
| @@ -212,10 +222,10 @@ void CTextContainer::blitLine(Canvas & to, Rect destRect, std::string what) | ||||
| 		where.x += getBorderSize().x; | ||||
|  | ||||
| 	if(alignment == ETextAlignment::CENTER || alignment == ETextAlignment::TOPCENTER || alignment == ETextAlignment::BOTTOMCENTER) | ||||
| 		where.x += (destRect.w - (static_cast<int>(f->getStringWidth(what)) - delimitersCount)) / 2; | ||||
| 		where.x += (destRect.w - (static_cast<int>(f->getStringWidth(what)) - delimitersWidth)) / 2; | ||||
|  | ||||
| 	if(alignment == ETextAlignment::TOPRIGHT || alignment == ETextAlignment::BOTTOMRIGHT || alignment == ETextAlignment::CENTERRIGHT) | ||||
| 		where.x += getBorderSize().x + destRect.w - (static_cast<int>(f->getStringWidth(what)) - delimitersCount); | ||||
| 		where.x += getBorderSize().x + destRect.w - (static_cast<int>(f->getStringWidth(what)) - delimitersWidth); | ||||
|  | ||||
| 	if(alignment == ETextAlignment::TOPLEFT || alignment == ETextAlignment::TOPCENTER || alignment == ETextAlignment::TOPRIGHT) | ||||
| 		where.y += getBorderSize().y; | ||||
|   | ||||
| @@ -31,6 +31,8 @@ protected: | ||||
| 	CTextContainer(ETextAlignment alignment, EFonts font, ColorRGBA color); | ||||
|  | ||||
| public: | ||||
| 	static int getDelimitersWidth(EFonts font, std::string text); | ||||
|  | ||||
| 	ETextAlignment alignment; | ||||
| 	EFonts font; | ||||
| 	ColorRGBA color; // default font color. Can be overridden by placing "{}" into the string | ||||
|   | ||||
| @@ -30,6 +30,7 @@ | ||||
| #include "../widgets/MiscWidgets.h" | ||||
| #include "../widgets/CComponent.h" | ||||
| #include "../widgets/CGarrisonInt.h" | ||||
| #include "../widgets/CTextInput.h" | ||||
| #include "../widgets/Buttons.h" | ||||
| #include "../widgets/TextControls.h" | ||||
| #include "../widgets/RadialMenu.h" | ||||
| @@ -1435,7 +1436,15 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst | ||||
| 	garr->setRedrawParent(true); | ||||
|  | ||||
| 	heroes = std::make_shared<HeroSlots>(town, Point(241, 387), Point(241, 483), garr, true); | ||||
| 	title = std::make_shared<CLabel>(85, 387, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, town->getNameTranslated()); | ||||
| 	title = std::make_shared<CTextInputWithConfirm>(Rect(83, 386, 140, 20), FONT_MEDIUM, ETextAlignment::TOPLEFT, town->getNameTranslated(), true, [this](){  | ||||
| 		std::string name = title->getText(); | ||||
| 		std::string originalName = LIBRARY->generaltexth->translate(town->getNameTextID()); | ||||
| 		if(name == originalName) | ||||
| 			name = ""; // use textID again | ||||
| 		GAME->interface()->cb->setTownName(town, name); | ||||
| 	}); | ||||
| 	if(town->tempOwner != GAME->interface()->playerID) // disable changing for allied towns | ||||
| 		title->deactivate(); | ||||
| 	income = std::make_shared<CLabel>(195, 443, FONT_SMALL, ETextAlignment::CENTER); | ||||
| 	icon = std::make_shared<CAnimImage>(AnimationPath::builtin("ITPT"), 0, 0, 15, 387); | ||||
|  | ||||
|   | ||||
| @@ -37,6 +37,7 @@ class CGarrisonInt; | ||||
| class CComponent; | ||||
| class CComponentBox; | ||||
| class LRClickableArea; | ||||
| class CTextInputWithConfirm; | ||||
|  | ||||
| /// Building "button" | ||||
| class CBuildingRect : public CShowableAnim | ||||
| @@ -225,7 +226,7 @@ public: | ||||
| /// Class which manages the castle window | ||||
| class CCastleInterface final : public CStatusbarWindow, public IGarrisonHolder, public IArtifactsHolder | ||||
| { | ||||
| 	std::shared_ptr<CLabel> title; | ||||
| 	std::shared_ptr<CTextInputWithConfirm> title; | ||||
| 	std::shared_ptr<CLabel> income; | ||||
| 	std::shared_ptr<CAnimImage> icon; | ||||
|  | ||||
|   | ||||
| @@ -286,6 +286,12 @@ void CCallback::setFormation(const CGHeroInstance * hero, EArmyFormation mode) | ||||
| 	sendRequest(pack); | ||||
| } | ||||
|  | ||||
| void CCallback::setTownName(const CGTownInstance * town, std::string & name) | ||||
| { | ||||
| 	SetTownName pack(town->id, name); | ||||
| 	sendRequest(pack); | ||||
| } | ||||
|  | ||||
| void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero, const HeroTypeID & nextHero) | ||||
| { | ||||
| 	assert(townOrTavern); | ||||
|   | ||||
| @@ -76,6 +76,7 @@ public: | ||||
| 	void trade(const ObjectInstanceID marketId, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero = nullptr) override; | ||||
| 	void trade(const ObjectInstanceID marketId, 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 setTownName(const CGTownInstance * town, std::string & name) 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; | ||||
|   | ||||
| @@ -70,6 +70,7 @@ public: | ||||
| 	virtual void endTurn()=0; | ||||
| 	virtual void buyArtifact(const CGHeroInstance *hero, ArtifactID aid)=0; //used to buy artifacts in towns (including spell book in the guild and war machines in blacksmith) | ||||
| 	virtual void setFormation(const CGHeroInstance * hero, EArmyFormation mode)=0; | ||||
| 	virtual void setTownName(const CGTownInstance * town, std::string & name)=0; | ||||
|  | ||||
| 	virtual void save(const std::string &fname) = 0; | ||||
| 	virtual void sendMessage(const std::string &mess, const CGObjectInstance * currentObject = nullptr) = 0; | ||||
|   | ||||
| @@ -123,6 +123,11 @@ void GameStatePackVisitor::visitChangeFormation(ChangeFormation & pack) | ||||
| 	gs.getHero(pack.hid)->setFormation(pack.formation); | ||||
| } | ||||
|  | ||||
| void GameStatePackVisitor::visitChangeTownName(ChangeTownName & pack) | ||||
| { | ||||
| 	gs.getTown(pack.tid)->setCustomName(pack.name); | ||||
| } | ||||
|  | ||||
| void GameStatePackVisitor::visitHeroVisitCastle(HeroVisitCastle & pack) | ||||
| { | ||||
| 	CGHeroInstance *h = gs.getHero(pack.hid); | ||||
|   | ||||
| @@ -87,6 +87,7 @@ public: | ||||
| 	void visitSetCommanderProperty(SetCommanderProperty & pack) override; | ||||
| 	void visitAddQuest(AddQuest & pack) override; | ||||
| 	void visitChangeFormation(ChangeFormation & pack) override; | ||||
| 	void visitChangeTownName(ChangeTownName & pack) override; | ||||
| 	void visitChangeSpells(ChangeSpells & pack) override; | ||||
| 	void visitSetAvailableHero(SetAvailableHero & pack) override; | ||||
| 	void visitChangeObjectVisitors(ChangeObjectVisitors & pack) override; | ||||
|   | ||||
| @@ -860,7 +860,7 @@ CBonusSystemNode & CGTownInstance::whatShouldBeAttached() | ||||
|  | ||||
| std::string CGTownInstance::getNameTranslated() const | ||||
| { | ||||
| 	return LIBRARY->generaltexth->translate(nameTextId); | ||||
| 	return customName.empty() ? LIBRARY->generaltexth->translate(nameTextId) : customName; | ||||
| } | ||||
|  | ||||
| std::string CGTownInstance::getNameTextID() const | ||||
| @@ -873,6 +873,11 @@ void CGTownInstance::setNameTextId( const std::string & newName ) | ||||
| 	nameTextId = newName; | ||||
| } | ||||
|  | ||||
| void CGTownInstance::setCustomName( const std::string & newName ) | ||||
| { | ||||
| 	customName = newName; | ||||
| } | ||||
|  | ||||
| const CArmedInstance * CGTownInstance::getUpperArmy() const | ||||
| { | ||||
| 	if(getGarrisonHero()) | ||||
|   | ||||
| @@ -46,6 +46,7 @@ class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public I | ||||
| { | ||||
| 	friend class CTownInstanceConstructor; | ||||
| 	std::string nameTextId; // name of town | ||||
| 	std::string customName; | ||||
|  | ||||
| 	std::map<BuildingID, TownRewardableBuildingInstance*> convertOldBuildings(std::vector<TownRewardableBuildingInstance*> oldVector); | ||||
| 	std::set<BuildingID> builtBuildings; | ||||
| @@ -75,6 +76,8 @@ public: | ||||
| 	{ | ||||
| 		h & static_cast<CGDwelling&>(*this); | ||||
| 		h & nameTextId; | ||||
| 		if (h.version >= Handler::Version::CUSTOM_NAMES) | ||||
| 			h & customName; | ||||
| 		h & built; | ||||
| 		h & destroyed; | ||||
| 		h & identifier; | ||||
| @@ -128,6 +131,7 @@ public: | ||||
| 	std::string getNameTranslated() const; | ||||
| 	std::string getNameTextID() const; | ||||
| 	void setNameTextId(const std::string & newName); | ||||
| 	void setCustomName(const std::string & newName); | ||||
|  | ||||
| 	////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|   | ||||
| @@ -58,6 +58,7 @@ public: | ||||
| 	virtual void visitSetCommanderProperty(SetCommanderProperty & pack) {} | ||||
| 	virtual void visitAddQuest(AddQuest & pack) {} | ||||
| 	virtual void visitChangeFormation(ChangeFormation & pack) {} | ||||
| 	virtual void visitChangeTownName(ChangeTownName & pack) {} | ||||
| 	virtual void visitRemoveObject(RemoveObject & pack) {} | ||||
| 	virtual void visitTryMoveHero(TryMoveHero & pack) {} | ||||
| 	virtual void visitNewStructures(NewStructures & pack) {} | ||||
| @@ -144,6 +145,7 @@ public: | ||||
| 	virtual void visitBuyArtifact(BuyArtifact & pack) {} | ||||
| 	virtual void visitTradeOnMarketplace(TradeOnMarketplace & pack) {} | ||||
| 	virtual void visitSetFormation(SetFormation & pack) {} | ||||
| 	virtual void visitSetTownName(SetTownName & pack) {} | ||||
| 	virtual void visitHireHero(HireHero & pack) {} | ||||
| 	virtual void visitBuildBoat(BuildBoat & pack) {} | ||||
| 	virtual void visitQueryReply(QueryReply & pack) {} | ||||
|   | ||||
| @@ -213,6 +213,11 @@ void ChangeFormation::visitTyped(ICPackVisitor & visitor) | ||||
| 	visitor.visitChangeFormation(*this); | ||||
| } | ||||
|  | ||||
| void ChangeTownName::visitTyped(ICPackVisitor & visitor) | ||||
| { | ||||
| 	visitor.visitChangeTownName(*this); | ||||
| } | ||||
|  | ||||
| void RemoveObject::visitTyped(ICPackVisitor & visitor) | ||||
| { | ||||
| 	visitor.visitRemoveObject(*this); | ||||
| @@ -643,6 +648,11 @@ void SetFormation::visitTyped(ICPackVisitor & visitor) | ||||
| 	visitor.visitSetFormation(*this); | ||||
| } | ||||
|  | ||||
| void SetTownName::visitTyped(ICPackVisitor & visitor) | ||||
| { | ||||
| 	visitor.visitSetTownName(*this); | ||||
| } | ||||
|  | ||||
| void HireHero::visitTyped(ICPackVisitor & visitor) | ||||
| { | ||||
| 	visitor.visitHireHero(*this); | ||||
|   | ||||
| @@ -614,6 +614,20 @@ struct DLL_LINKAGE ChangeFormation : public CPackForClient | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE ChangeTownName : public CPackForClient | ||||
| { | ||||
| 	ObjectInstanceID tid; | ||||
| 	std::string name; | ||||
|  | ||||
| 	void visitTyped(ICPackVisitor & visitor) override; | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler & h) | ||||
| 	{ | ||||
| 		h & tid; | ||||
| 		h & name; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE RemoveObject : public CPackForClient | ||||
| { | ||||
| 	RemoveObject() = default; | ||||
|   | ||||
| @@ -609,6 +609,28 @@ struct DLL_LINKAGE SetFormation : public CPackForServer | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE SetTownName : public CPackForServer | ||||
| { | ||||
| 	SetTownName() = default; | ||||
| 	; | ||||
| 	SetTownName(const ObjectInstanceID & TID, std::string Name) | ||||
| 		: tid(TID) | ||||
| 		, name(Name) | ||||
| 	{ | ||||
| 	} | ||||
| 	ObjectInstanceID tid; | ||||
| 	std::string name; | ||||
|  | ||||
| 	void visitTyped(ICPackVisitor & visitor) override; | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler & h) | ||||
| 	{ | ||||
| 		h & static_cast<CPackForServer &>(*this); | ||||
| 		h & tid; | ||||
| 		h & name; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE HireHero : public CPackForServer | ||||
| { | ||||
| 	HireHero() = default; | ||||
|   | ||||
| @@ -50,8 +50,9 @@ enum class ESerializationVersion : int32_t | ||||
| 	BONUS_HIDDEN, // hidden bonus | ||||
| 	MORE_MAP_LAYERS, // more map layers | ||||
| 	CONFIGURABLE_RESOURCES, // configurable resources | ||||
| 	CUSTOM_NAMES, // custom names | ||||
|  | ||||
| 	CURRENT = CONFIGURABLE_RESOURCES, | ||||
| 	CURRENT = CUSTOM_NAMES, | ||||
| }; | ||||
|  | ||||
| static_assert(ESerializationVersion::MINIMAL <= ESerializationVersion::CURRENT, "Invalid serialization version definition!"); | ||||
|   | ||||
| @@ -291,6 +291,8 @@ void registerTypes(Serializer &s) | ||||
| 	s.template registerType<TimesStackSizeUpdater>(249); | ||||
| 	s.template registerType<TimesArmySizeUpdater>(250); | ||||
| 	s.template registerType<PackageReceived>(251); | ||||
| 	s.template registerType<ChangeTownName>(252); | ||||
| 	s.template registerType<SetTownName>(253); | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -3219,6 +3219,23 @@ bool CGameHandler::setFormation(ObjectInstanceID hid, EArmyFormation formation) | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool CGameHandler::setTownName(ObjectInstanceID tid, std::string & name) | ||||
| { | ||||
| 	const CGTownInstance *t = gameInfo().getTown(tid); | ||||
| 	if (!t) | ||||
| 	{ | ||||
| 		logGlobal->error("Town doesn't exist!"); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	ChangeTownName ctn; | ||||
| 	ctn.tid = tid; | ||||
| 	ctn.name = name; | ||||
| 	sendAndApply(ctn); | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool CGameHandler::queryReply(QueryID qid, std::optional<int32_t> answer, PlayerColor player) | ||||
| { | ||||
| 	logGlobal->trace("Player %s attempts answering query %d with answer:", player, qid); | ||||
|   | ||||
| @@ -212,6 +212,7 @@ public: | ||||
| 	bool queryReply( QueryID qid, std::optional<int32_t> reply, PlayerColor player ); | ||||
| 	bool buildBoat( ObjectInstanceID objid, PlayerColor player ); | ||||
| 	bool setFormation( ObjectInstanceID hid, EArmyFormation formation ); | ||||
| 	bool setTownName( ObjectInstanceID tid, std::string & name ); | ||||
| 	bool tradeResources(const IMarket *market, ui32 amountToSell, PlayerColor player, GameResID toSell, GameResID toBuy); | ||||
| 	bool sacrificeCreatures(const IMarket * market, const CGHeroInstance * hero, const std::vector<SlotID> & slot, const std::vector<ui32> & count); | ||||
| 	bool sendResources(ui32 val, PlayerColor player, GameResID r1, PlayerColor r2); | ||||
|   | ||||
| @@ -362,6 +362,13 @@ void ApplyGhNetPackVisitor::visitSetFormation(SetFormation & pack) | ||||
| 	result = gh.setFormation(pack.hid, pack.formation); | ||||
| } | ||||
|  | ||||
| void ApplyGhNetPackVisitor::visitSetTownName(SetTownName & pack) | ||||
| { | ||||
| 	gh.throwIfWrongOwner(connection, &pack, pack.tid); | ||||
|  | ||||
| 	result = gh.setTownName(pack.tid, pack.name); | ||||
| } | ||||
|  | ||||
| void ApplyGhNetPackVisitor::visitHireHero(HireHero & pack) | ||||
| { | ||||
| 	gh.throwIfWrongPlayer(connection, &pack); | ||||
|   | ||||
| @@ -58,6 +58,7 @@ public: | ||||
| 	void visitBuyArtifact(BuyArtifact & pack) override; | ||||
| 	void visitTradeOnMarketplace(TradeOnMarketplace & pack) override; | ||||
| 	void visitSetFormation(SetFormation & pack) override; | ||||
| 	void visitSetTownName(SetTownName & pack) override; | ||||
| 	void visitHireHero(HireHero & pack) override; | ||||
| 	void visitBuildBoat(BuildBoat & pack) override; | ||||
| 	void visitQueryReply(QueryReply & pack) override; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user