Browse Source

Merge pull request #2327 from IvanSavenko/bugfixing_from_stream

Fix accumulated minor bugs
Ivan Savenko 2 years ago
parent
commit
a4ba5a9b65
41 changed files with 383 additions and 162 deletions
  1. 2 1
      client/CMT.cpp
  2. 41 10
      client/CPlayerInterface.cpp
  3. 7 4
      client/adventureMap/AdventureMapInterface.cpp
  4. 12 15
      client/adventureMap/CList.cpp
  5. 11 4
      client/adventureMap/CList.h
  6. 1 0
      client/gui/CIntObject.cpp
  7. 3 0
      client/render/IScreenHandler.h
  8. 3 3
      client/renderSDL/ScreenHandler.cpp
  9. 3 3
      client/renderSDL/ScreenHandler.h
  10. 1 4
      client/windows/CHeroWindow.cpp
  11. 14 6
      client/windows/CKingdomInterface.cpp
  12. 1 0
      client/windows/CKingdomInterface.h
  13. 19 9
      client/windows/GUIClasses.cpp
  14. 13 14
      client/windows/settings/GeneralOptionsTab.cpp
  15. 26 26
      config/battleStartpos.json
  16. 9 9
      config/filesystem.json
  17. 2 0
      config/gameConfig.json
  18. 2 2
      config/schemas/settings.json
  19. 2 2
      config/widgets/settings/battleOptionsTab.json
  20. 8 8
      launcher/firstLaunch/firstlaunch_moc.ui
  21. 2 0
      launcher/mainwindow_moc.cpp
  22. 13 6
      launcher/settingsView/csettingsview_moc.cpp
  23. 4 1
      launcher/settingsView/csettingsview_moc.h
  24. 11 3
      launcher/translation/chinese.ts
  25. 6 6
      launcher/translation/english.ts
  26. 13 5
      launcher/translation/french.ts
  27. 11 3
      launcher/translation/german.ts
  28. 11 3
      launcher/translation/polish.ts
  29. 11 3
      launcher/translation/russian.ts
  30. 12 4
      launcher/translation/spanish.ts
  31. 11 3
      launcher/translation/ukrainian.ts
  32. 1 0
      lib/GameSettings.cpp
  33. 1 0
      lib/GameSettings.h
  34. 10 1
      lib/NetPacksLib.cpp
  35. 2 2
      lib/mapObjectConstructors/CObjectClassesHandler.cpp
  36. 40 0
      lib/mapObjectConstructors/CommonConstructors.cpp
  37. 22 0
      lib/mapObjectConstructors/CommonConstructors.h
  38. 15 0
      lib/mapObjects/CGDwelling.cpp
  39. 2 0
      lib/registerTypes/RegisterTypes.h
  40. 1 1
      lib/spells/TargetCondition.cpp
  41. 4 1
      lib/spells/effects/Timed.cpp

+ 2 - 1
client/CMT.cpp

@@ -443,7 +443,8 @@ void playIntro()
 {
 	if(CCS->videoh->openAndPlayVideo("3DOLOGO.SMK", 0, 1, true, true))
 	{
-		CCS->videoh->openAndPlayVideo("AZVS.SMK", 0, 1, true, true);
+		if (CCS->videoh->openAndPlayVideo("NWCLOGO.SMK", 0, 1, true, true))
+			CCS->videoh->openAndPlayVideo("H3INTRO.SMK", 0, 1, true, true);
 	}
 }
 

+ 41 - 10
client/CPlayerInterface.cpp

@@ -398,7 +398,10 @@ void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	LOG_TRACE_PARAMS(logGlobal, "Hero %s killed handler for player %s", hero->getNameTranslated() % playerID);
 
-	localState->removeWanderingHero(hero);
+	// if hero is not in town garrison
+	if (vstd::contains(localState->getWanderingHeroes(), hero))
+		localState->removeWanderingHero(hero);
+
 	adventureInt->onHeroChanged(hero);
 	localState->erasePath(hero);
 }
@@ -500,7 +503,7 @@ void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
 	if(town->garrisonHero) //wandering hero moved to the garrison
 	{
 		// This method also gets called on hero recruitment -> garrisoned hero is already in garrison
-		if(town->garrisonHero->tempOwner == playerID && !vstd::contains(localState->getWanderingHeroes(), town->visitingHero))
+		if(town->garrisonHero->tempOwner == playerID && vstd::contains(localState->getWanderingHeroes(), town->garrisonHero))
 			localState->removeWanderingHero(town->garrisonHero);
 	}
 
@@ -520,7 +523,9 @@ void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
 		castleInt->garr->setArmy(town->visitingHero, 1);
 		castleInt->garr->recreateSlots();
 		castleInt->heroes->update();
-		castleInt->redraw();
+
+		// Perform totalRedraw to update hero list on adventure map
+		GH.windows().totalRedraw();
 	}
 
 	for (auto ki : GH.windows().findWindows<CKingdomInterface>())
@@ -587,9 +592,11 @@ void CPlayerInterface::garrisonsChanged(std::vector<const CGObjectInstance *> ob
 void CPlayerInterface::buildChanged(const CGTownInstance *town, BuildingID buildingID, int what) //what: 1 - built, 2 - demolished
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
+	adventureInt->onTownChanged(town);
+
 	if (castleInt)
 	{
-		castleInt->townlist->update(town);
+		castleInt->townlist->updateElement(town);
 
 		if (castleInt->town == town)
 		{
@@ -604,8 +611,10 @@ void CPlayerInterface::buildChanged(const CGTownInstance *town, BuildingID build
 				break;
 			}
 		}
+
+		// Perform totalRedraw in order to force redraw of updated town list icon from adventure map
+		GH.windows().totalRedraw();
 	}
-	adventureInt->onTownChanged(town);
 }
 
 void CPlayerInterface::battleStartBefore(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2)
@@ -1473,6 +1482,13 @@ void CPlayerInterface::objectRemovedAfter()
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	adventureInt->onMapTilesChanged(boost::none);
+
+	// visiting or garrisoned hero removed - recreate castle window
+	if (castleInt)
+		openTownWindow(castleInt->town);
+
+	for (auto ki : GH.windows().findWindows<CKingdomInterface>())
+		ki->heroRemoved();
 }
 
 void CPlayerInterface::playerBlocked(int reason, bool start)
@@ -1971,8 +1987,17 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
 		int soundChannel = -1;
 		std::string soundName;
 
-		auto getMovementSoundFor = [&](const CGHeroInstance * hero, int3 posPrev, int3 posNext) -> std::string
+		auto getMovementSoundFor = [&](const CGHeroInstance * hero, int3 posPrev, int3 posNext, EPathNodeAction moveType) -> std::string
 		{
+			if (moveType == EPathNodeAction::TELEPORT_BATTLE || moveType == EPathNodeAction::TELEPORT_BLOCKING_VISIT || moveType == EPathNodeAction::TELEPORT_NORMAL)
+				return "";
+
+			if (moveType == EPathNodeAction::EMBARK || moveType == EPathNodeAction::DISEMBARK)
+				return "";
+
+			if (moveType == EPathNodeAction::BLOCKING_VISIT)
+				return "";
+
 			// flying movement sound
 			if (hero->hasBonusOfType(BonusType::FLYING_MOVEMENT))
 				return "HORSE10.wav";
@@ -2024,8 +2049,11 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
 				}
 				if(i != path.nodes.size() - 1)
 				{
-					soundName = getMovementSoundFor(h, prevCoord, nextCoord);
-					soundChannel = CCS->soundh->playSound(soundName, -1);
+					soundName = getMovementSoundFor(h, prevCoord, nextCoord, path.nodes[i-1].action);
+					if (!soundName.empty())
+						soundChannel = CCS->soundh->playSound(soundName, -1);
+					else
+						soundChannel = -1;
 				}
 				continue;
 			}
@@ -2038,14 +2066,17 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
 
 			{
 				// Start a new sound for the hero movement or let the existing one carry on.
-				std::string newSoundName = getMovementSoundFor(h, prevCoord, nextCoord);
+				std::string newSoundName = getMovementSoundFor(h, prevCoord, nextCoord, path.nodes[i-1].action);
 
 				if(newSoundName != soundName)
 				{
 					soundName = newSoundName;
 
 					CCS->soundh->stopSound(soundChannel);
-					soundChannel = CCS->soundh->playSound(soundName, -1);
+					if (!soundName.empty())
+						soundChannel = CCS->soundh->playSound(soundName, -1);
+					else
+						soundChannel = -1;
 				}
 			}
 

+ 7 - 4
client/adventureMap/AdventureMapInterface.cpp

@@ -92,7 +92,7 @@ void AdventureMapInterface::onHeroMovementStarted(const CGHeroInstance * hero)
 
 void AdventureMapInterface::onHeroChanged(const CGHeroInstance *h)
 {
-	widget->getHeroList()->update(h);
+	widget->getHeroList()->updateElement(h);
 
 	if (h && h == LOCPLINT->localState->getCurrentHero() && !widget->getInfoBar()->showingComponents())
 		widget->getInfoBar()->showSelection();
@@ -102,7 +102,7 @@ void AdventureMapInterface::onHeroChanged(const CGHeroInstance *h)
 
 void AdventureMapInterface::onTownChanged(const CGTownInstance * town)
 {
-	widget->getTownList()->update(town);
+	widget->getTownList()->updateElement(town);
 
 	if (town && town == LOCPLINT->localState->getCurrentTown() && !widget->getInfoBar()->showingComponents())
 		widget->getInfoBar()->showSelection();
@@ -365,8 +365,8 @@ void AdventureMapInterface::onPlayerTurnStarted(PlayerColor playerID)
 		widget->getInfoBar()->showSelection();
 	}
 
-	widget->getHeroList()->update();
-	widget->getTownList()->update();
+	widget->getHeroList()->updateWidget();
+	widget->getTownList()->updateWidget();
 
 	const CGHeroInstance * heroToSelect = nullptr;
 
@@ -827,5 +827,8 @@ void AdventureMapInterface::onScreenResize()
 	widget->getMinimap()->update();
 	widget->getInfoBar()->showSelection();
 
+	if (LOCPLINT && LOCPLINT->localState->getCurrentArmy())
+		widget->getMapView()->onCenteredObject(LOCPLINT->localState->getCurrentArmy());
+
 	adjustActiveness();
 }

+ 12 - 15
client/adventureMap/CList.cpp

@@ -280,21 +280,15 @@ void CHeroList::select(const CGHeroInstance * hero)
 	selectIndex(vstd::find_pos(LOCPLINT->localState->getWanderingHeroes(), hero));
 }
 
-void CHeroList::update(const CGHeroInstance * hero)
+void CHeroList::updateElement(const CGHeroInstance * hero)
 {
-	//this hero is already present, update its status
-	for(auto & elem : listBox->getItems())
-	{
-		auto item = std::dynamic_pointer_cast<CHeroItem>(elem);
-		if(item && item->hero == hero && vstd::contains(LOCPLINT->localState->getWanderingHeroes(), hero))
-		{
-			item->update();
-			return;
-		}
-	}
-	//simplest solution for now: reset list and restore selection
+	updateWidget();
+}
 
+void CHeroList::updateWidget()
+{
 	listBox->resize(LOCPLINT->localState->getWanderingHeroes().size());
+	listBox->reset();
 	if (LOCPLINT->localState->getCurrentHero())
 		select(LOCPLINT->localState->getCurrentHero());
 
@@ -363,14 +357,17 @@ void CTownList::select(const CGTownInstance * town)
 	selectIndex(vstd::find_pos(LOCPLINT->localState->getOwnedTowns(), town));
 }
 
-void CTownList::update(const CGTownInstance *)
+void CTownList::updateElement(const CGTownInstance * town)
 {
-	//simplest solution for now: reset list and restore selection
+	updateWidget();
+}
 
+void CTownList::updateWidget()
+{
 	listBox->resize(LOCPLINT->localState->getOwnedTowns().size());
+	listBox->reset();
 	if (LOCPLINT->localState->getCurrentTown())
 		select(LOCPLINT->localState->getCurrentTown());
 
 	CList::update();
 }
-

+ 11 - 4
client/adventureMap/CList.h

@@ -77,6 +77,9 @@ protected:
 
 	virtual std::shared_ptr<CIntObject> createItem(size_t index) = 0;
 
+	/// should be called when list is invalidated
+	void update();
+
 public:
 	/// functions that will be called when selection changes
 	CFunctionList<void()> onSelect;
@@ -87,8 +90,6 @@ public:
 	void setScrollUpButton(std::shared_ptr<CButton> button);
 	void setScrollDownButton(std::shared_ptr<CButton> button);
 
-	/// should be called when list is invalidated
-	void update();
 
 	/// set of methods to switch selection
 	void selectIndex(int which);
@@ -137,7 +138,10 @@ public:
 	void select(const CGHeroInstance * hero = nullptr);
 
 	/// Update hero. Will add or remove it from the list if needed
-	void update(const CGHeroInstance * hero = nullptr);
+	void updateElement(const CGHeroInstance * hero);
+
+	/// Update all heroes
+	void updateWidget();
 };
 
 /// List of towns which is shown at the right of the adventure map screen or in the town screen
@@ -167,6 +171,9 @@ public:
 	void select(const CGTownInstance * town = nullptr);
 
 	/// Update town. Will add or remove it from the list if needed
-	void update(const CGTownInstance * town = nullptr);
+	void updateElement(const CGTownInstance * town);
+
+	/// Update all towns
+	void updateWidget();
 };
 

+ 1 - 0
client/gui/CIntObject.cpp

@@ -305,6 +305,7 @@ CKeyShortcut::CKeyShortcut()
 
 CKeyShortcut::CKeyShortcut(EShortcut key)
 	: assignedKey(key)
+	, shortcutPressed(false)
 {
 }
 

+ 3 - 0
client/render/IScreenHandler.h

@@ -37,4 +37,7 @@ public:
 
 	/// Converts provided rect from logical coordinates into coordinates within window, accounting for scaling and viewport
 	virtual Rect convertLogicalPointsToWindow(const Rect & input) const = 0;
+
+	/// Dimensions of render output
+	virtual Point getRenderResolution() const = 0;
 };

+ 3 - 3
client/renderSDL/ScreenHandler.cpp

@@ -46,7 +46,7 @@ std::tuple<int, int> ScreenHandler::getSupportedScalingRange() const
 	// arbitrary limit on *downscaling*. Allow some downscaling, if requested by user. Should be generally limited to 100+ for all but few devices
 	static const double minimalScaling = 50;
 
-	Point renderResolution = getActualRenderResolution();
+	Point renderResolution = getRenderResolution();
 	double reservedAreaWidth = settings["video"]["reservedWidth"].Float();
 	Point availableResolution = Point(renderResolution.x * (1 - reservedAreaWidth), renderResolution.y);
 
@@ -85,7 +85,7 @@ Rect ScreenHandler::convertLogicalPointsToWindow(const Rect & input) const
 
 Point ScreenHandler::getPreferredLogicalResolution() const
 {
-	Point renderResolution = getActualRenderResolution();
+	Point renderResolution = getRenderResolution();
 	double reservedAreaWidth = settings["video"]["reservedWidth"].Float();
 	Point availableResolution = Point(renderResolution.x * (1 - reservedAreaWidth), renderResolution.y);
 
@@ -99,7 +99,7 @@ Point ScreenHandler::getPreferredLogicalResolution() const
 	return logicalResolution;
 }
 
-Point ScreenHandler::getActualRenderResolution() const
+Point ScreenHandler::getRenderResolution() const
 {
 	assert(mainRenderer != nullptr);
 

+ 3 - 3
client/renderSDL/ScreenHandler.h

@@ -39,9 +39,6 @@ class ScreenHandler final : public IScreenHandler
 	/// This value is what player views as window size
 	Point getPreferredWindowResolution() const;
 
-	/// Dimensions of render output, usually same as window size except for high-DPI screens on macOS / iOS
-	Point getActualRenderResolution() const;
-
 	EWindowMode getPreferredWindowMode() const;
 
 	/// Returns index of display on which window should be created
@@ -86,6 +83,9 @@ public:
 	/// Fills screen with black color, erasing any existing content
 	void clearScreen() final;
 
+	/// Dimensions of render output, usually same as window size except for high-DPI screens on macOS / iOS
+	Point getRenderResolution() const final;
+
 	std::vector<Point> getSupportedResolutions() const final;
 	std::vector<Point> getSupportedResolutions(int displayIndex) const;
 	std::tuple<int, int> getSupportedScalingRange() const final;

+ 1 - 4
client/windows/CHeroWindow.cpp

@@ -319,9 +319,6 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded)
 				noDismiss = true;
 	}
 
-	for(auto ki : GH.windows().findWindows<CKingdomInterface>())
-		noDismiss = true;
-
 	//if player only have one hero and no towns
 	if(!LOCPLINT->cb->howManyTowns() && LOCPLINT->cb->howManyHeroes() == 1)
 		noDismiss = true;
@@ -329,7 +326,7 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded)
 	if(curHero->isMissionCritical())
 		noDismiss = true;
 
-	dismissButton->block(!!curHero->visitedTown || noDismiss);
+	dismissButton->block(noDismiss);
 
 	if(curHero->valOfBonuses(Selector::type()(BonusType::BEFORE_BATTLE_REPOSITION)) == 0)
 	{

+ 14 - 6
client/windows/CKingdomInterface.cpp

@@ -15,6 +15,7 @@
 
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
+#include "../PlayerLocalState.h"
 #include "../adventureMap/CResDataBar.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/Shortcut.h"
@@ -638,6 +639,11 @@ void CKingdomInterface::townChanged(const CGTownInstance *town)
 		townList->townChanged(town);
 }
 
+void CKingdomInterface::heroRemoved()
+{
+	tabArea->reset();
+}
+
 void CKingdomInterface::updateGarrisons()
 {
 	if(auto garrison = std::dynamic_pointer_cast<CGarrisonHolder>(tabArea->getItem()))
@@ -694,11 +700,12 @@ void CKingdHeroList::updateGarrisons()
 std::shared_ptr<CIntObject> CKingdHeroList::createHeroItem(size_t index)
 {
 	ui32 picCount = 4; // OVSLOT contains 4 images
-	size_t heroesCount = LOCPLINT->cb->howManyHeroes(false);
 
-	if(index < heroesCount)
+	auto heroesList = LOCPLINT->localState->getWanderingHeroes();
+
+	if(index < heroesList.size())
 	{
-		auto hero = std::make_shared<CHeroItem>(LOCPLINT->cb->getHeroBySerial((int)index, false));
+		auto hero = std::make_shared<CHeroItem>(heroesList[index]);
 		addSet(hero->heroArts);
 		return hero;
 	}
@@ -745,10 +752,11 @@ void CKingdTownList::updateGarrisons()
 std::shared_ptr<CIntObject> CKingdTownList::createTownItem(size_t index)
 {
 	ui32 picCount = 4; // OVSLOT contains 4 images
-	size_t townsCount = LOCPLINT->cb->howManyTowns();
 
-	if(index < townsCount)
-		return std::make_shared<CTownItem>(LOCPLINT->cb->getTownBySerial((int)index));
+	auto townsList = LOCPLINT->localState->getOwnedTowns();
+
+	if(index < townsList.size())
+		return std::make_shared<CTownItem>(townsList[index]);
 	else
 		return std::make_shared<CAnimImage>("OVSLOT", (index-2) % picCount );
 }

+ 1 - 0
client/windows/CKingdomInterface.h

@@ -246,6 +246,7 @@ public:
 	CKingdomInterface();
 
 	void townChanged(const CGTownInstance *town);
+	void heroRemoved();
 	void updateGarrisons() override;
 	void artifactRemoved(const ArtifactLocation &artLoc) override;
 	void artifactMoved(const ArtifactLocation &artLoc, const ArtifactLocation &destLoc, bool withRedraw) override;

+ 19 - 9
client/windows/GUIClasses.cpp

@@ -158,23 +158,33 @@ void CRecruitmentWindow::select(std::shared_ptr<CCreatureCard> card)
 void CRecruitmentWindow::buy()
 {
 	CreatureID crid =  selected->creature->getId();
-	SlotID dstslot = dst-> getSlotFor(crid);
+	SlotID dstslot = dst->getSlotFor(crid);
 
 	if(!dstslot.validSlot() && (selected->creature->warMachine == ArtifactID::NONE)) //no available slot
 	{
-		std::string txt;
-		if(dst->ID == Obj::HERO)
+		std::pair<SlotID, SlotID> toMerge;
+		bool allowMerge = CGI->settings()->getBoolean(EGameSettings::DWELLINGS_ACCUMULATE_WHEN_OWNED);
+
+		if (allowMerge && dst->mergableStacks(toMerge))
 		{
-			txt = CGI->generaltexth->allTexts[425]; //The %s would join your hero, but there aren't enough provisions to support them.
-			boost::algorithm::replace_first(txt, "%s", slider->getValue() > 1 ? CGI->creh->objects[crid]->getNamePluralTranslated() : CGI->creh->objects[crid]->getNameSingularTranslated());
+			LOCPLINT->cb->mergeStacks( dst, dst, toMerge.first, toMerge.second);
 		}
 		else
 		{
-			txt = CGI->generaltexth->allTexts[17]; //There is no room in the garrison for this army.
-		}
+			std::string txt;
+			if(dst->ID == Obj::HERO)
+			{
+				txt = CGI->generaltexth->allTexts[425]; //The %s would join your hero, but there aren't enough provisions to support them.
+				boost::algorithm::replace_first(txt, "%s", slider->getValue() > 1 ? CGI->creh->objects[crid]->getNamePluralTranslated() : CGI->creh->objects[crid]->getNameSingularTranslated());
+			}
+			else
+			{
+				txt = CGI->generaltexth->allTexts[17]; //There is no room in the garrison for this army.
+			}
 
-		LOCPLINT->showInfoDialog(txt);
-		return;
+			LOCPLINT->showInfoDialog(txt);
+			return;
+		}
 	}
 
 	onRecruit(crid, slider->getValue());

+ 13 - 14
client/windows/settings/GeneralOptionsTab.cpp

@@ -224,25 +224,19 @@ void GeneralOptionsTab::updateResolutionSelector()
 	std::shared_ptr<CButton> resolutionButton = widget<CButton>("resolutionButton");
 	std::shared_ptr<CLabel> resolutionLabel = widget<CLabel>("resolutionLabel");
 
-	if (settings["video"]["fullscreen"].Bool() && !settings["video"]["realFullscreen"].Bool())
+	if (resolutionButton)
 	{
-		if (resolutionButton)
+		if (settings["video"]["fullscreen"].Bool() && !settings["video"]["realFullscreen"].Bool())
 			resolutionButton->disable();
-
-		if (resolutionLabel)
-			resolutionLabel->setText(resolutionToLabelString(GH.screenDimensions().x, GH.screenDimensions().y));
-	}
-	else
-	{
-		const auto & currentResolution = settings["video"]["resolution"];
-
-		if (resolutionButton)
+		else
 			resolutionButton->enable();
-
-		if (resolutionLabel)
-			resolutionLabel->setText(resolutionToLabelString(currentResolution["width"].Integer(), currentResolution["height"].Integer()));
 	}
 
+	if (resolutionLabel)
+	{
+		Point resolution = GH.screenHandler().getRenderResolution();
+		resolutionLabel->setText(resolutionToLabelString(resolution.x, resolution.y));
+	}
 }
 
 void GeneralOptionsTab::selectGameResolution()
@@ -370,6 +364,11 @@ void GeneralOptionsTab::setGameScaling(int index)
 	gameRes["scaling"].Float() = scaling;
 
 	widget<CLabel>("scalingLabel")->setText(scalingToLabelString(scaling));
+
+	GH.dispatchMainThread([](){
+		boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
+		GH.onScreenResize();
+	});
 }
 
 void GeneralOptionsTab::selectLongTouchDuration()

+ 26 - 26
config/battleStartpos.json

@@ -4,12 +4,12 @@
 		{
 			"name" : "attackerLoose", // loose formation, attacker
 			"levels": [
-				[ 86 ],
-				[ 35, 137 ],
-				[ 35, 86, 137 ],
-				[ 1, 69, 103, 171 ],
-				[ 1, 35, 86, 137, 171 ],
-				[ 1, 35, 69, 103, 137, 171 ],
+				[            86                ],
+				[ 35,                 137      ],
+				[ 35,        86,      137      ],
+				[ 1,     69,     103,      171 ],
+				[ 1, 35,     86,      137, 171 ],
+				[ 1, 35, 69,     103, 137, 171 ],
 				[ 1, 35, 69, 86, 103, 137, 171 ]
 				]
 		},
@@ -17,12 +17,12 @@
 		{
 			"name" : "defenderLoose", // loose formation, defender
 			"levels": [
-				[ 100 ],
-				[ 49, 151 ],
-				[ 49, 100, 151 ],
-				[ 15, 83, 117, 185 ],
-				[ 15, 49, 100, 151, 185 ],
-				[ 15, 49, 83, 117, 151, 185 ],
+				[             100                ],
+				[     49,               151      ],
+				[     49,     100,      151      ],
+				[ 15,     83,      117,      185 ],
+				[ 15, 49,     100,      151, 185 ],
+				[ 15, 49, 83,      117, 151, 185 ],
 				[ 15, 49, 83, 100, 117, 151, 185 ]
 				]
 		},
@@ -30,26 +30,26 @@
 		{
 			"name" : "attackerTight", // tight formation, attacker
 			"levels": [
-				[ 86 ],
-				[ 69, 103 ],
-				[ 69, 86, 103 ],
-				[ 52, 69, 103, 120 ],
-				[ 52, 69, 86, 103, 120 ],
-				[ 35, 52, 69, 103, 120, 137 ],
-				[ 35, 52, 69, 86, 103, 120, 137 ]
+				[             86                ],
+				[         69,     103           ],
+				[         69, 86, 103           ],
+				[     35, 69,     103, 137      ],
+				[     35, 69, 86, 103, 137      ],
+				[  1, 35, 69,     103, 137, 171 ],
+				[  1, 35, 69, 86, 103, 137, 171 ]
 				]
 		},
 
 		{
 			"name" : "defenderTight", // tight formation, defender
 			"levels": [
-				[ 100 ],
-				[ 83, 117 ],
-				[ 83, 100, 117 ],
-				[ 66, 83, 117, 134 ],
-				[ 66, 83, 100, 117, 134 ],
-				[ 49, 66, 83, 117, 134, 151 ],
-				[ 49, 66, 83, 100, 117, 134, 151 ]
+				[             100                ],
+				[         83,      117           ],
+				[         83, 100, 117           ],
+				[     49, 83,      117, 151      ],
+				[     49, 83, 100, 117, 151      ],
+				[ 15, 49, 83,      117, 151, 185 ],
+				[ 15, 49, 83, 100, 117, 151, 185 ]
 				]
 		},
 

+ 9 - 9
config/filesystem.json

@@ -9,25 +9,25 @@
 	{
 		"DATA/" :
 		[
-			{"type" : "lod", "path" : "Data/H3ab_bmp.lod"},
-			{"type" : "lod", "path" : "Data/H3bitmap.lod"},
-			{"type" : "lod", "path" : "Data/h3abp_bm.lod"}, // Polish version of H3 only
-			{"type" : "lod", "path" : "Data/H3pbitma.lod"}, // Polish version of H3 only
+			{"type" : "lod", "path" : "Data/H3ab_bmp.lod"}, // Contains H3:AB data
+			{"type" : "lod", "path" : "Data/h3abp_bm.lod"}, // Localized versions only, contains H3:AB patch data
+			{"type" : "lod", "path" : "Data/H3bitmap.lod"}, // Contains H3:SoD data (overrides H3:AB data)
+			{"type" : "lod", "path" : "Data/H3pbitma.lod"}, // Localized versions only, contains H3:SoD patch data
 			{"type" : "dir",  "path" : "Data"}
 		],
 		"SPRITES/":
 		[
-			{"type" : "lod", "path" : "Data/H3ab_spr.lod"},
-			{"type" : "lod", "path" : "Data/H3sprite.lod"},
-			{"type" : "lod", "path" : "Data/h3abp_sp.lod"}, // Polish version of H3 only
-			{"type" : "lod", "path" : "Data/H3psprit.lod"}, // Polish version of H3 only
+			{"type" : "lod", "path" : "Data/H3ab_spr.lod"}, // Contains H3:AB data
+			{"type" : "lod", "path" : "Data/H3sprite.lod"}, // Localized versions only, contains H3:AB patch data
+			{"type" : "lod", "path" : "Data/h3abp_sp.lod"}, // Contains H3:SoD data (overrides H3:AB data)
+//			{"type" : "lod", "path" : "Data/H3psprit.lod"}, // Localized versions only, contains H3:SoD patch data. Unused? Has corrupted data, e.g. lock icon for artifacts
 			{"type" : "dir",  "path" : "Sprites"}
 		],
 		"SOUNDS/":
 		[
 			{"type" : "snd", "path" : "Data/H3ab_ahd.snd"},
-			{"type" : "snd", "path" : "Data/Heroes3-cd2.snd"},
 			{"type" : "snd", "path" : "Data/Heroes3.snd"},
+			{"type" : "snd", "path" : "Data/Heroes3-cd2.snd"},
 			//WoG have overriden sounds with .82m extension in Data
 			{"type" : "dir",  "path" : "Data", "depth": 0}
 		],

+ 2 - 0
config/gameConfig.json

@@ -311,6 +311,8 @@
 			"accumulateWhenNeutral" : false,
 			// if enabled, dwellings owned by players will accumulate creatures 
 			"accumulateWhenOwned" : false
+			// if enabled, game will attempt to merge slots in army on recruit if all slots in hero army are in use
+			"mergeOnRecruit" : true
 		},
 		
 		"markets" : 

+ 2 - 2
config/schemas/settings.json

@@ -59,12 +59,12 @@
 				},
 				"language" : {
 					"type" : "string",
-					"enum" : [ "english", "czech", "chinese", "german", "hungarian", "italian", "korean", "polish", "russian", "spanish", "ukrainian" ],
+					"enum" : [ "english", "czech", "chinese", "french", "german", "hungarian", "italian", "korean", "polish", "russian", "spanish", "ukrainian" ],
 					"default" : "english"
 				},
 				"gameDataLanguage" : {
 					"type" : "string",
-					"enum" : [ "auto", "english", "czech", "chinese", "german", "hungarian", "italian", "korean", "polish", "russian", "spanish", "ukrainian", "other_cp1250", "other_cp1251", "other_cp1252" ],
+					"enum" : [ "auto", "english", "czech", "chinese", "french", "german", "hungarian", "italian", "korean", "polish", "russian", "spanish", "ukrainian", "other_cp1250", "other_cp1251", "other_cp1252" ],
 					"default" : "auto"
 				},
 				"lastSave" : {

+ 2 - 2
config/widgets/settings/battleOptionsTab.json

@@ -245,7 +245,7 @@
 					]
 				},
 				{
-					"index": 12,
+					"index": 9,
 					"type": "toggleButton",
 					"image": "settingsWindow/button46",
 					"help": "vcmi.battleOptions.animationsSpeed5",
@@ -261,7 +261,7 @@
 					]
 				},
 				{
-					"index": 24,
+					"index": 18,
 					"type": "toggleButton",
 					"image": "settingsWindow/button46",
 					"help": "vcmi.battleOptions.animationsSpeed6",

+ 8 - 8
launcher/firstLaunch/firstlaunch_moc.ui

@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>745</width>
-    <height>389</height>
+    <height>397</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -96,7 +96,7 @@
    <item>
     <widget class="QStackedWidget" name="installerTabs">
      <property name="currentIndex">
-      <number>0</number>
+      <number>2</number>
      </property>
      <widget class="QWidget" name="pageLanguageSelect">
       <layout class="QGridLayout" name="gridLayout_3">
@@ -616,7 +616,7 @@ Heroes® of Might and Magic® III HD is currently not supported!</string>
             <string>Horn of the Abyss</string>
            </property>
            <property name="wordWrap">
-            <bool>true</bool>
+            <bool>false</bool>
            </property>
           </widget>
          </item>
@@ -638,7 +638,7 @@ Heroes® of Might and Magic® III HD is currently not supported!</string>
             <string>Heroes III Translation</string>
            </property>
            <property name="wordWrap">
-            <bool>true</bool>
+            <bool>false</bool>
            </property>
           </widget>
          </item>
@@ -699,10 +699,10 @@ Heroes® of Might and Magic® III HD is currently not supported!</string>
             </font>
            </property>
            <property name="text">
-            <string>High Definition Support</string>
+            <string>Interface Improvements</string>
            </property>
            <property name="wordWrap">
-            <bool>true</bool>
+            <bool>false</bool>
            </property>
           </widget>
          </item>
@@ -724,7 +724,7 @@ Heroes® of Might and Magic® III HD is currently not supported!</string>
             <string>In The Wake of Gods</string>
            </property>
            <property name="wordWrap">
-            <bool>true</bool>
+            <bool>false</bool>
            </property>
           </widget>
          </item>
@@ -769,7 +769,7 @@ Heroes® of Might and Magic® III HD is currently not supported!</string>
             </sizepolicy>
            </property>
            <property name="text">
-            <string>Install support for playing Heroes III in resolutions higher than 800x600</string>
+            <string>Install mod that provides various interface improvements, such as better interface for random maps and selectable actions in battles</string>
            </property>
            <property name="wordWrap">
             <bool>true</bool>

+ 2 - 0
launcher/mainwindow_moc.cpp

@@ -153,6 +153,7 @@ void MainWindow::enterSetup()
 	ui->startEditorButton->setEnabled(false);
 	ui->lobbyButton->setEnabled(false);
 	ui->settingsButton->setEnabled(false);
+	ui->aboutButton->setEnabled(false);
 	ui->modslistButton->setEnabled(false);
 	ui->tabListWidget->setCurrentIndex(TabRows::SETUP);
 }
@@ -166,6 +167,7 @@ void MainWindow::exitSetup()
 	ui->startEditorButton->setEnabled(true);
 	ui->lobbyButton->setEnabled(true);
 	ui->settingsButton->setEnabled(true);
+	ui->aboutButton->setEnabled(true);
 	ui->modslistButton->setEnabled(true);
 	ui->tabListWidget->setCurrentIndex(TabRows::MODS);
 }

+ 13 - 6
launcher/settingsView/csettingsview_moc.cpp

@@ -273,12 +273,6 @@ void CSettingsView::on_comboBoxDisplayIndex_currentIndexChanged(int index)
 	fillValidResolutionsForScreen(index);
 }
 
-void CSettingsView::on_comboBoxPlayerAI_currentTextChanged(const QString & arg1)
-{
-	Settings node = settings.write["server"]["playerAI"];
-	node->String() = arg1.toUtf8().data();
-}
-
 void CSettingsView::on_comboBoxFriendlyAI_currentTextChanged(const QString & arg1)
 {
 	Settings node = settings.write["server"]["friendlyAI"];
@@ -493,3 +487,16 @@ void CSettingsView::on_spinBoxFramerateLimit_valueChanged(int arg1)
 	node->Float() = arg1;
 }
 
+void CSettingsView::on_comboBoxEnemyPlayerAI_currentTextChanged(const QString &arg1)
+{
+	Settings node = settings.write["server"]["playerAI"];
+	node->String() = arg1.toUtf8().data();
+}
+
+
+void CSettingsView::on_comboBoxAlliedPlayerAI_currentTextChanged(const QString &arg1)
+{
+	Settings node = settings.write["server"]["alliedAI"];
+	node->String() = arg1.toUtf8().data();
+}
+

+ 4 - 1
launcher/settingsView/csettingsview_moc.h

@@ -35,7 +35,6 @@ public slots:
 private slots:
 	void on_comboBoxResolution_currentTextChanged(const QString & arg1);
 	void on_comboBoxFullScreen_currentIndexChanged(int index);
-	void on_comboBoxPlayerAI_currentTextChanged(const QString & arg1);
 	void on_comboBoxFriendlyAI_currentTextChanged(const QString & arg1);
 	void on_comboBoxNeutralAI_currentTextChanged(const QString & arg1);
 	void on_comboBoxEnemyAI_currentTextChanged(const QString & arg1);
@@ -63,6 +62,10 @@ private slots:
 
 	void on_spinBoxFramerateLimit_valueChanged(int arg1);
 
+	void on_comboBoxEnemyPlayerAI_currentTextChanged(const QString &arg1);
+
+	void on_comboBoxAlliedPlayerAI_currentTextChanged(const QString &arg1);
+
 private:
 	Ui::CSettingsView * ui;
 

+ 11 - 3
launcher/translation/chinese.ts

@@ -710,6 +710,11 @@ Heroes® of Might and Magic® III HD is currently not supported!</source>
         <source>The automatic detection of the Heroes III language has failed. Please select the language of your Heroes III manually</source>
         <translation>自动检测英雄无敌3语言失败。请手动选择英雄无敌3语言</translation>
     </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="702"/>
+        <source>Interface Improvements</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="740"/>
         <source>Install a translation of Heroes III in your preferred language</source>
@@ -722,8 +727,12 @@ Heroes® of Might and Magic® III HD is currently not supported!</source>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="772"/>
+        <source>Install mod that provides various interface improvements, such as better interface for random maps and selectable actions in battles</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <source>Install support for playing Heroes III in resolutions higher than 800x600</source>
-        <translation>安装英雄无敌3的800x600以上分辨率支持</translation>
+        <translation type="vanished">安装英雄无敌3的800x600以上分辨率支持</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="788"/>
@@ -813,9 +822,8 @@ Heroes® of Might and Magic® III HD is currently not supported!</source>
         <translation>英雄无敌3翻译</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="702"/>
         <source>High Definition Support</source>
-        <translation>高分辨率支持</translation>
+        <translation type="vanished">高分辨率支持</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="724"/>

+ 6 - 6
launcher/translation/english.ts

@@ -678,6 +678,11 @@ Heroes® of Might and Magic® III HD is currently not supported!</source>
         <source>The automatic detection of the Heroes III language has failed. Please select the language of your Heroes III manually</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="702"/>
+        <source>Interface Improvements</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="740"/>
         <source>Install a translation of Heroes III in your preferred language</source>
@@ -690,7 +695,7 @@ Heroes® of Might and Magic® III HD is currently not supported!</source>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="772"/>
-        <source>Install support for playing Heroes III in resolutions higher than 800x600</source>
+        <source>Install mod that provides various interface improvements, such as better interface for random maps and selectable actions in battles</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
@@ -780,11 +785,6 @@ Heroes® of Might and Magic® III HD is currently not supported!</source>
         <source>Heroes III Translation</source>
         <translation type="unfinished"></translation>
     </message>
-    <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="702"/>
-        <source>High Definition Support</source>
-        <translation type="unfinished"></translation>
-    </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="724"/>
         <source>In The Wake of Gods</source>

+ 13 - 5
launcher/translation/french.ts

@@ -257,7 +257,7 @@
         <translation>Impressions écran</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.ui" line="323"/>
+        <location filename="../modManager/cmodlistview_moc.ui" line="324"/>
         <source> %p% (%v KB out of %m KB)</source>
         <translation> %p% (%v Ko sur %m Ko)</translation>
     </message>
@@ -780,9 +780,8 @@ Mode exclusif plein écran - le jeu couvrira l&quot;intégralité de votre écra
             </translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="772"/>
         <source>Install support for playing Heroes III in resolutions higher than 800x600</source>
-        <translation>Installer un support pour jouer à Heroes III avec des résolutions supérieures à 800x600
+        <translation type="vanished">Installer un support pour jouer à Heroes III avec des résolutions supérieures à 800x600
             </translation>
     </message>
     <message>
@@ -860,7 +859,7 @@ Heroes® of Might and Magic® III HD n&quot;est actuellement pas pris en charge
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="346"/>
         <source>If you don&apos;t have a copy of Heroes III installed, you can use our automatic installation tool &apos;vcmibuilder&apos;, which only requires the GoG.com Heroes III installer. Please visit our wiki for detailed instructions.</source>
-        <translation>Si vous n&quot;avez pas installé de copie de Heroes III, vous pouvez utiliser notre outil d&quot;installation automatique "vcmibuilder", qui ne nécessite que le programme d&quot;installation de GoG.com Heroes III. Veuillez visiter notre wiki pour des instructions détaillées.</translation>
+        <translation>Si vous n&quot;avez pas installé de copie de Heroes III, vous pouvez utiliser notre outil d&quot;installation automatique &quot;vcmibuilder&quot;, qui ne nécessite que le programme d&quot;installation de GoG.com Heroes III. Veuillez visiter notre wiki pour des instructions détaillées.</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="362"/>
@@ -920,8 +919,12 @@ Heroes® of Might and Magic® III HD n&quot;est actuellement pas pris en charge
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="702"/>
+        <source>Interface Improvements</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <source>High Definition Support</source>
-        <translation>Support de Haute Définition</translation>
+        <translation type="vanished">Support de Haute Définition</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="724"/>
@@ -933,6 +936,11 @@ Heroes® of Might and Magic® III HD n&quot;est actuellement pas pris en charge
         <source>Optionally, you can install additional mods either now, or at any point later, using the VCMI Launcher</source>
         <translation>En option, vous pouvez installer des mods supplémentaires soit maintenant, soit à tout moment plus tard, à l&quot;aide du lanceur VCMI</translation>
     </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="772"/>
+        <source>Install mod that provides various interface improvements, such as better interface for random maps and selectable actions in battles</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="788"/>
         <source>Install compatible version of &quot;Horn of the Abyss&quot;, a fan-made Heroes III expansion ported by the VCMI team</source>

+ 11 - 3
launcher/translation/german.ts

@@ -714,6 +714,11 @@ Heroes III: HD Edition wird derzeit nicht unterstützt</translation>
         <source>The automatic detection of the Heroes III language has failed. Please select the language of your Heroes III manually</source>
         <translation>Automatische Erkennung der Sprache fehlgeschlagen. Bitte wählen Sie die Sprache Ihrer Heroes III Kopie</translation>
     </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="702"/>
+        <source>Interface Improvements</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="740"/>
         <source>Install a translation of Heroes III in your preferred language</source>
@@ -726,8 +731,12 @@ Heroes III: HD Edition wird derzeit nicht unterstützt</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="772"/>
+        <source>Install mod that provides various interface improvements, such as better interface for random maps and selectable actions in battles</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <source>Install support for playing Heroes III in resolutions higher than 800x600</source>
-        <translation>Installieren Sie Unterstützung für das Spielen von Heroes III in anderen Auflösungen als 800x600</translation>
+        <translation type="vanished">Installieren Sie Unterstützung für das Spielen von Heroes III in anderen Auflösungen als 800x600</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="788"/>
@@ -817,9 +826,8 @@ Heroes III: HD Edition wird derzeit nicht unterstützt</translation>
         <translation>Heroes III Übersetzung</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="702"/>
         <source>High Definition Support</source>
-        <translation>Unterstützung für hohe Auflösungen</translation>
+        <translation type="vanished">Unterstützung für hohe Auflösungen</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="724"/>

+ 11 - 3
launcher/translation/polish.ts

@@ -714,6 +714,11 @@ Heroes III: HD Edition nie jest obecnie wspierane!</translation>
         <source>The automatic detection of the Heroes III language has failed. Please select the language of your Heroes III manually</source>
         <translation>Automatyczna detekcja języka nie powiodła się. Proszę wybrać język twojego Heroes III</translation>
     </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="702"/>
+        <source>Interface Improvements</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="740"/>
         <source>Install a translation of Heroes III in your preferred language</source>
@@ -726,8 +731,12 @@ Heroes III: HD Edition nie jest obecnie wspierane!</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="772"/>
+        <source>Install mod that provides various interface improvements, such as better interface for random maps and selectable actions in battles</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <source>Install support for playing Heroes III in resolutions higher than 800x600</source>
-        <translation>Zainstaluj wsparcie dla grania w Heroes III w rozdzielczości innej niż 800x600</translation>
+        <translation type="vanished">Zainstaluj wsparcie dla grania w Heroes III w rozdzielczości innej niż 800x600</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="788"/>
@@ -817,9 +826,8 @@ Heroes III: HD Edition nie jest obecnie wspierane!</translation>
         <translation>Tłumaczenie Heroes III</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="702"/>
         <source>High Definition Support</source>
-        <translation>Wsparcie High Definition</translation>
+        <translation type="vanished">Wsparcie High Definition</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="724"/>

+ 11 - 3
launcher/translation/russian.ts

@@ -708,6 +708,11 @@ Heroes® of Might and Magic® III HD is currently not supported!</source>
         <source>The automatic detection of the Heroes III language has failed. Please select the language of your Heroes III manually</source>
         <translation>Язык Героев III не был определен. Пожалуйста, выберите язык вашей копии Героев III</translation>
     </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="702"/>
+        <source>Interface Improvements</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="740"/>
         <source>Install a translation of Heroes III in your preferred language</source>
@@ -720,8 +725,12 @@ Heroes® of Might and Magic® III HD is currently not supported!</source>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="772"/>
+        <source>Install mod that provides various interface improvements, such as better interface for random maps and selectable actions in battles</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <source>Install support for playing Heroes III in resolutions higher than 800x600</source>
-        <translation>Установить поддержку запуска Героев III в разрешениях, отличных от 800x600</translation>
+        <translation type="vanished">Установить поддержку запуска Героев III в разрешениях, отличных от 800x600</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="788"/>
@@ -811,9 +820,8 @@ Heroes® of Might and Magic® III HD is currently not supported!</source>
         <translation>Перевод Героев III</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="702"/>
         <source>High Definition Support</source>
-        <translation>Поддержка высоких разрешений</translation>
+        <translation type="vanished">Поддержка высоких разрешений</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="724"/>

+ 12 - 4
launcher/translation/spanish.ts

@@ -702,6 +702,16 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
         <source>Your Heroes III language has been successfully detected.</source>
         <translation>Se ha detectado con éxito el idioma de tu Heroes III.</translation>
     </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="702"/>
+        <source>Interface Improvements</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="772"/>
+        <source>Install mod that provides various interface improvements, such as better interface for random maps and selectable actions in battles</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="127"/>
         <source>Select your language</source>
@@ -781,9 +791,8 @@ Ten en cuenta que para usar VCMI debes ser dueño de los archivos de datos origi
         <translation>Traducción de Heroes III.</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="702"/>
         <source>High Definition Support</source>
-        <translation>Soporte para resoluciones en Alta Definición</translation>
+        <translation type="vanished">Soporte para resoluciones en Alta Definición</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="724"/>
@@ -801,9 +810,8 @@ Ten en cuenta que para usar VCMI debes ser dueño de los archivos de datos origi
         <translation>Opcionalmente, puedes instalar mods adicionales ya sea ahora o en cualquier momento posterior, utilizando el lanzador de VCMI.</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="772"/>
         <source>Install support for playing Heroes III in resolutions higher than 800x600</source>
-        <translation>Instalar soporte para jugar Heroes III en resoluciones superiores a 800x600</translation>
+        <translation type="vanished">Instalar soporte para jugar Heroes III en resoluciones superiores a 800x600</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="788"/>

+ 11 - 3
launcher/translation/ukrainian.ts

@@ -718,6 +718,11 @@ Heroes® of Might and Magic® III HD наразі не підтримуєтьс
         <source>The automatic detection of the Heroes III language has failed. Please select the language of your Heroes III manually</source>
         <translation>Не вдалося визначити мову гри. Будь ласка, виберіть мову вашої копії Heroes III</translation>
     </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="702"/>
+        <source>Interface Improvements</source>
+        <translation>Удосконалення нтерфейсу</translation>
+    </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="740"/>
         <source>Install a translation of Heroes III in your preferred language</source>
@@ -730,8 +735,12 @@ Heroes® of Might and Magic® III HD наразі не підтримуєтьс
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="772"/>
+        <source>Install mod that provides various interface improvements, such as better interface for random maps and selectable actions in battles</source>
+        <translation>Встановити різноманітні покращення інтерфейсу, такі як покращений інтерфейс випадкових карт та вибір варіантів дій у боях</translation>
+    </message>
+    <message>
         <source>Install support for playing Heroes III in resolutions higher than 800x600</source>
-        <translation>Встановити підтримку для гри в Heroes III у роздільних здатностях, більших за 800x600</translation>
+        <translation type="vanished">Встановити підтримку для гри в Heroes III у роздільних здатностях, більших за 800x600</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="788"/>
@@ -821,9 +830,8 @@ Heroes® of Might and Magic® III HD наразі не підтримуєтьс
         <translation>Переклад Heroes III</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="702"/>
         <source>High Definition Support</source>
-        <translation>Підтримка високих роздільних здатностей</translation>
+        <translation type="vanished">Підтримка високих роздільних здатностей</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="724"/>

+ 1 - 0
lib/GameSettings.cpp

@@ -68,6 +68,7 @@ void GameSettings::load(const JsonNode & input)
 		{EGameSettings::CREATURES_WEEKLY_GROWTH_PERCENT,        "creatures", "weeklyGrowthPercent"        },
 		{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_NEUTRAL,      "dwellings", "accumulateWhenNeutral"      },
 		{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_OWNED,        "dwellings", "accumulateWhenOwned"        },
+		{EGameSettings::DWELLINGS_MERGE_ON_RECRUIT,             "dwellings", "mergeOnRecruit"             },
 		{EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP,           "heroes",    "perPlayerOnMapCap"          },
 		{EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP,            "heroes",    "perPlayerTotalCap"          },
 		{EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS,   "heroes",    "retreatOnWinWithoutTroops"  },

+ 1 - 0
lib/GameSettings.h

@@ -32,6 +32,7 @@ enum class EGameSettings
 	CREATURES_WEEKLY_GROWTH_PERCENT,
 	DWELLINGS_ACCUMULATE_WHEN_NEUTRAL,
 	DWELLINGS_ACCUMULATE_WHEN_OWNED,
+	DWELLINGS_MERGE_ON_RECRUIT,
 	HEROES_PER_PLAYER_ON_MAP_CAP,
 	HEROES_PER_PLAYER_TOTAL_CAP,
 	HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS,

+ 10 - 1
lib/NetPacksLib.cpp

@@ -1124,7 +1124,16 @@ void RemoveObject::applyGs(CGameState *gs)
 		PlayerState * p = gs->getPlayerState(beatenHero->tempOwner);
 		gs->map->heroesOnMap -= beatenHero;
 		p->heroes -= beatenHero;
-		beatenHero->detachFrom(*beatenHero->whereShouldBeAttachedOnSiege(gs));
+
+
+		auto * siegeNode = beatenHero->whereShouldBeAttachedOnSiege(gs);
+
+		// FIXME: workaround:
+		// hero should be attached to siegeNode after battle
+		// however this code might also be called on dismissing hero while in town
+		if (siegeNode && vstd::contains(beatenHero->getParentNodes(), siegeNode))
+				beatenHero->detachFrom(*siegeNode);
+
 		beatenHero->tempOwner = PlayerColor::NEUTRAL; //no one owns beaten hero
 		vstd::erase_if(beatenHero->artifactsInBackpack, [](const ArtSlotInfo& asi)
 		{

+ 2 - 2
lib/mapObjectConstructors/CObjectClassesHandler.cpp

@@ -57,6 +57,8 @@ CObjectClassesHandler::CObjectClassesHandler()
 	SET_HANDLER_CLASS("shrine", ShrineInstanceConstructor);
 	SET_HANDLER_CLASS("hillFort", HillFortInstanceConstructor);
 	SET_HANDLER_CLASS("shipyard", ShipyardInstanceConstructor);
+	SET_HANDLER_CLASS("monster", CreatureInstanceConstructor);
+	SET_HANDLER_CLASS("resource", ResourceInstanceConstructor);
 
 	SET_HANDLER_CLASS("static", CObstacleConstructor);
 	SET_HANDLER_CLASS("", CObstacleConstructor);
@@ -73,7 +75,6 @@ CObjectClassesHandler::CObjectClassesHandler()
 	SET_HANDLER("artifact", CGArtifact);
 	SET_HANDLER("borderGate", CGBorderGate);
 	SET_HANDLER("borderGuard", CGBorderGuard);
-	SET_HANDLER("monster", CGCreature);
 	SET_HANDLER("denOfThieves", CGDenOfthieves);
 	SET_HANDLER("event", CGEvent);
 	SET_HANDLER("garrison", CGGarrison);
@@ -87,7 +88,6 @@ CObjectClassesHandler::CObjectClassesHandler()
 	SET_HANDLER("pandora", CGPandoraBox);
 	SET_HANDLER("prison", CGHeroInstance);
 	SET_HANDLER("questGuard", CGQuestGuard);
-	SET_HANDLER("resource", CGResource);
 	SET_HANDLER("scholar", CGScholar);
 	SET_HANDLER("seerHut", CGSeerHut);
 	SET_HANDLER("sign", CGSignBottle);

+ 40 - 0
lib/mapObjectConstructors/CommonConstructors.cpp

@@ -35,6 +35,26 @@ bool CObstacleConstructor::isStaticObject()
 	return true;
 }
 
+bool CreatureInstanceConstructor::hasNameTextID() const
+{
+	return true;
+}
+
+std::string CreatureInstanceConstructor::getNameTextID() const
+{
+	return VLC->creatures()->getByIndex(getSubIndex())->getNamePluralTextID();
+}
+
+bool ResourceInstanceConstructor::hasNameTextID() const
+{
+	return true;
+}
+
+std::string ResourceInstanceConstructor::getNameTextID() const
+{
+	return TextIdentifier("core", "restypes", getSubIndex()).get();
+}
+
 void CTownInstanceConstructor::initTypeData(const JsonNode & input)
 {
 	VLC->modh->identifiers.requestIdentifier("faction", input["faction"], [&](si32 index)
@@ -86,6 +106,16 @@ void CTownInstanceConstructor::randomizeObject(CGTownInstance * object, CRandomG
 		object->appearance = templ;
 }
 
+bool CTownInstanceConstructor::hasNameTextID() const
+{
+	return true;
+}
+
+std::string CTownInstanceConstructor::getNameTextID() const
+{
+	return faction->getNameTextID();
+}
+
 void CHeroInstanceConstructor::initTypeData(const JsonNode & input)
 {
 	VLC->modh->identifiers.requestIdentifier(
@@ -133,6 +163,16 @@ void CHeroInstanceConstructor::randomizeObject(CGHeroInstance * object, CRandomG
 
 }
 
+bool CHeroInstanceConstructor::hasNameTextID() const
+{
+	return true;
+}
+
+std::string CHeroInstanceConstructor::getNameTextID() const
+{
+	return heroClass->getNameTextID();
+}
+
 void BoatInstanceConstructor::initTypeData(const JsonNode & input)
 {
 	layer = EPathfindingLayer::SAIL;

+ 22 - 0
lib/mapObjectConstructors/CommonConstructors.h

@@ -13,6 +13,7 @@
 #include "../LogicalExpression.h"
 
 #include "../mapObjects/MiscObjects.h"
+#include "../mapObjects/CGCreature.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -22,6 +23,7 @@ class CGTownInstance;
 class CGHeroInstance;
 class CGMarket;
 class CHeroClass;
+class CGCreature;
 class CBank;
 class CGBoat;
 class CFaction;
@@ -33,6 +35,20 @@ public:
 	bool isStaticObject() override;
 };
 
+class CreatureInstanceConstructor : public CDefaultObjectTypeHandler<CGCreature>
+{
+public:
+	bool hasNameTextID() const override;
+	std::string getNameTextID() const override;
+};
+
+class ResourceInstanceConstructor : public CDefaultObjectTypeHandler<CGResource>
+{
+public:
+	bool hasNameTextID() const override;
+	std::string getNameTextID() const override;
+};
+
 class CTownInstanceConstructor : public CDefaultObjectTypeHandler<CGTownInstance>
 {
 	JsonNode filtersJson;
@@ -48,6 +64,9 @@ public:
 	void randomizeObject(CGTownInstance * object, CRandomGenerator & rng) const override;
 	void afterLoadFinalization() override;
 
+	bool hasNameTextID() const override;
+	std::string getNameTextID() const override;
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & filtersJson;
@@ -72,6 +91,9 @@ public:
 	void randomizeObject(CGHeroInstance * object, CRandomGenerator & rng) const override;
 	void afterLoadFinalization() override;
 
+	bool hasNameTextID() const override;
+	std::string getNameTextID() const override;
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & filtersJson;

+ 15 - 0
lib/mapObjects/CGDwelling.cpp

@@ -324,6 +324,21 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
 	{
 		if(count) //there are available creatures
 		{
+
+			if (VLC->settings()->getBoolean(EGameSettings::DWELLINGS_ACCUMULATE_WHEN_OWNED))
+			{
+				SlotID testSlot = h->getSlotFor(crid);
+				if(!testSlot.validSlot()) //no available slot - try merging army of visiting hero
+				{
+					std::pair<SlotID, SlotID> toMerge;
+					if (h->mergableStacks(toMerge))
+					{
+						cb->moveStack(StackLocation(h, toMerge.first), StackLocation(h, toMerge.second), -1); //merge toMerge.first into toMerge.second
+						assert(!h->hasStackAtSlot(toMerge.first)); //we have now a new free slot
+					}
+				}
+			}
+
 			SlotID slot = h->getSlotFor(crid);
 			if(!slot.validSlot()) //no available slot
 			{

+ 2 - 0
lib/registerTypes/RegisterTypes.h

@@ -107,6 +107,8 @@ void registerTypesMapObjectTypes(Serializer &s)
 	s.template registerType<AObjectTypeHandler, ShrineInstanceConstructor>();
 	s.template registerType<AObjectTypeHandler, ShipyardInstanceConstructor>();
 	s.template registerType<AObjectTypeHandler, HillFortInstanceConstructor>();
+	s.template registerType<AObjectTypeHandler, CreatureInstanceConstructor>();
+	s.template registerType<AObjectTypeHandler, ResourceInstanceConstructor>();
 
 #define REGISTER_GENERIC_HANDLER(TYPENAME) s.template registerType<AObjectTypeHandler, CDefaultObjectTypeHandler<TYPENAME> >()
 

+ 1 - 1
lib/spells/TargetCondition.cpp

@@ -541,7 +541,7 @@ void TargetCondition::loadConditions(const JsonNode & source, bool exclusive, bo
 
 			CModHandler::parseIdentifier(keyValue.first, scope, type, identifier);
 
-			item = itemFactory->createConfigurable(scope, type, identifier);
+			item = itemFactory->createConfigurable(keyValue.second.meta, type, identifier);
 		}
 
 		if(item)

+ 4 - 1
lib/spells/effects/Timed.cpp

@@ -250,7 +250,10 @@ void Timed::serializeJsonUnitEffect(JsonSerializeFormat & handler)
 			auto guard = handler.enterStruct(p.first);
 			const JsonNode & bonusNode = handler.getCurrent();
 			auto b = JsonUtils::parseBonus(bonusNode);
-			bonus.push_back(b);
+			if (b)
+				bonus.push_back(b);
+			else
+				logMod->error("Failed to parse bonus '%s'!", p.first);
 		}
 	}
 }