浏览代码

Merge remote-tracking branch 'vcmi/beta' into develop

Ivan Savenko 2 年之前
父节点
当前提交
f13a53c1d9
共有 95 个文件被更改,包括 1438 次插入954 次删除
  1. 19 1
      AI/BattleAI/BattleAI.cpp
  2. 4 2
      AI/BattleAI/BattleExchangeVariant.cpp
  3. 0 11
      AI/BattleAI/PotentialTargets.cpp
  4. 1 1
      AI/Nullkiller/Analyzers/ArmyManager.cpp
  5. 35 27
      AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp
  6. 13 2
      AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.h
  7. 1 1
      AI/Nullkiller/Behaviors/DefenceBehavior.cpp
  8. 13 15
      AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp
  9. 5 0
      AI/Nullkiller/Engine/Nullkiller.cpp
  10. 1 1
      AI/VCAI/VCAI.cpp
  11. 1 1
      CMakePresets.json
  12. 8 0
      ChangeLog.md
  13. 1 1
      Mods/vcmi/mod.json
  14. 10 3
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityMods.java
  15. 1 1
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/NativeMethods.java
  16. 6 0
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/ExportDataController.java
  17. 7 1
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/FileUtil.java
  18. 24 15
      client/CMT.cpp
  19. 4 0
      client/CMT.h
  20. 49 38
      client/CMusicHandler.cpp
  21. 10 3
      client/CMusicHandler.h
  22. 11 2
      client/CServerHandler.cpp
  23. 1 1
      client/CServerHandler.h
  24. 1 1
      client/Client.cpp
  25. 8 0
      client/NetPacksClient.cpp
  26. 12 0
      client/adventureMap/AdventureMapInterface.cpp
  27. 2 0
      client/battle/BattleInterface.cpp
  28. 3 1
      client/battle/BattleInterfaceClasses.cpp
  29. 20 8
      client/battle/BattleObstacleController.cpp
  30. 1 2
      client/battle/BattleRenderer.h
  31. 1 3
      client/battle/BattleSiegeController.cpp
  32. 1 1
      client/battle/BattleStacksController.cpp
  33. 2 0
      client/gui/CGuiHandler.cpp
  34. 9 7
      client/gui/CursorHandler.h
  35. 7 3
      client/lobby/RandomMapTab.cpp
  36. 1 1
      client/lobby/SelectionTab.cpp
  37. 8 0
      client/mapView/MapRenderer.cpp
  38. 20 4
      client/mapView/MapViewController.cpp
  39. 1 0
      client/mapView/MapViewController.h
  40. 14 13
      client/render/Graphics.cpp
  41. 4 0
      client/render/Graphics.h
  42. 6 0
      client/renderSDL/CBitmapFont.cpp
  43. 11 0
      client/renderSDL/SDL_Extensions.cpp
  44. 9 1
      client/renderSDL/ScreenHandler.cpp
  45. 3 2
      client/widgets/CGarrisonInt.cpp
  46. 23 18
      client/widgets/MiscWidgets.cpp
  47. 5 3
      client/widgets/MiscWidgets.h
  48. 39 15
      client/windows/CCastleInterface.cpp
  49. 6 3
      client/windows/CTradeWindow.cpp
  50. 0 2
      config/schemas/settings.json
  51. 6 0
      debian/changelog
  52. 1 0
      launcher/eu.vcmi.VCMI.metainfo.xml
  53. 9 7
      launcher/settingsView/csettingsview_moc.cpp
  54. 2 0
      launcher/settingsView/csettingsview_moc.h
  55. 284 253
      launcher/settingsView/csettingsview_moc.ui
  56. 54 49
      launcher/translation/chinese.ts
  57. 54 49
      launcher/translation/english.ts
  58. 54 49
      launcher/translation/french.ts
  59. 54 49
      launcher/translation/german.ts
  60. 54 49
      launcher/translation/polish.ts
  61. 54 49
      launcher/translation/russian.ts
  62. 54 49
      launcher/translation/spanish.ts
  63. 54 49
      launcher/translation/ukrainian.ts
  64. 4 4
      lib/CCreatureSet.cpp
  65. 3 3
      lib/CCreatureSet.h
  66. 4 13
      lib/CGeneralTextHandler.cpp
  67. 1 1
      lib/CHeroHandler.cpp
  68. 12 4
      lib/CTownHandler.cpp
  69. 7 1
      lib/JsonNode.cpp
  70. 1 1
      lib/NetPacks.h
  71. 19 5
      lib/NetPacksLib.cpp
  72. 3 3
      lib/StartInfo.cpp
  73. 14 4
      lib/mapObjectConstructors/CObjectClassesHandler.cpp
  74. 1 1
      lib/mapObjects/CBank.cpp
  75. 3 0
      lib/mapObjects/CGTownBuilding.cpp
  76. 6 0
      lib/mapObjects/CGTownInstance.cpp
  77. 1 1
      lib/mapping/MapFormatH3M.cpp
  78. 6 0
      lib/rmg/RmgArea.cpp
  79. 1 0
      lib/rmg/RmgArea.h
  80. 0 10
      lib/rmg/RmgObject.cpp
  81. 51 5
      lib/rmg/modificators/ObjectManager.cpp
  82. 6 4
      lib/rmg/modificators/QuestArtifactPlacer.cpp
  83. 10 0
      lib/rmg/modificators/RoadPlacer.cpp
  84. 6 0
      lib/rmg/threadpool/MapProxy.cpp
  85. 1 0
      lib/rmg/threadpool/MapProxy.h
  86. 56 9
      lib/spells/BattleSpellMechanics.cpp
  87. 1 1
      lib/spells/BattleSpellMechanics.h
  88. 3 3
      lib/spells/ISpellMechanics.cpp
  89. 2 2
      lib/spells/ISpellMechanics.h
  90. 1 1
      mapeditor/inspector/rewardswidget.cpp
  91. 19 3
      server/CGameHandler.cpp
  92. 6 0
      server/CVCMIServer.cpp
  93. 2 2
      server/HeroPoolProcessor.cpp
  94. 11 5
      server/NetPacksServer.cpp
  95. 1 1
      test/mock/mock_spells_Mechanics.h

+ 19 - 1
AI/BattleAI/BattleAI.cpp

@@ -254,6 +254,13 @@ void CBattleAI::yourTacticPhase(int distance)
 	cb->battleMakeTacticAction(BattleAction::makeEndOFTacticPhase(cb->battleGetTacticsSide()));
 }
 
+uint64_t timeElapsed(std::chrono::time_point<std::chrono::high_resolution_clock> start)
+{
+	auto end = std::chrono::high_resolution_clock::now();
+
+	return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
+}
+
 void CBattleAI::activeStack( const CStack * stack )
 {
 	LOG_TRACE_PARAMS(logAi, "stack: %s", stack->nodeName());
@@ -261,6 +268,8 @@ void CBattleAI::activeStack( const CStack * stack )
 	BattleAction result = BattleAction::makeDefend(stack);
 	setCbc(cb); //TODO: make solid sure that AIs always use their callbacks (need to take care of event handlers too)
 
+	auto start = std::chrono::high_resolution_clock::now();
+
 	try
 	{
 		if(stack->creatureId() == CreatureID::CATAPULT)
@@ -276,6 +285,8 @@ void CBattleAI::activeStack( const CStack * stack )
 
 		attemptCastingSpell();
 
+		logAi->trace("Spellcast attempt completed in %lld", timeElapsed(start));
+
 		if(cb->battleIsFinished() || !stack->alive())
 		{
 			//spellcast may finish battle or kill active stack
@@ -312,6 +323,8 @@ void CBattleAI::activeStack( const CStack * stack )
 		movesSkippedByDefense = 0;
 	}
 
+	logAi->trace("BattleAI decission made in %lld", timeElapsed(start));
+
 	cb->battleMakeUnitAction(result);
 }
 
@@ -494,7 +507,12 @@ void CBattleAI::attemptCastingSpell()
 	{
 		spells::BattleCast temp(cb.get(), hero, spells::Mode::HERO, spell);
 
-		for(auto & target : temp.findPotentialTargets())
+		if(!spell->isDamage() && spell->getTargetType() == spells::AimType::LOCATION)
+			continue;
+		
+		const bool FAST = true;
+
+		for(auto & target : temp.findPotentialTargets(FAST))
 		{
 			PossibleSpellcast ps;
 			ps.dest = target;

+ 4 - 2
AI/BattleAI/BattleExchangeVariant.cpp

@@ -600,6 +600,8 @@ void BattleExchangeEvaluator::updateReachabilityMap(HypotheticBattle & hb)
 			if(unit->isTurret())
 				continue;
 
+			auto unitSpeed = unit->speed(turn);
+
 			if(turnBattle.battleCanShoot(unit))
 			{
 				for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); hex = hex + 1)
@@ -614,7 +616,7 @@ void BattleExchangeEvaluator::updateReachabilityMap(HypotheticBattle & hb)
 
 			for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); hex = hex + 1)
 			{
-				bool reachable = unitReachability.distances[hex] <= unit->speed(turn);
+				bool reachable = unitReachability.distances[hex] <= unitSpeed;
 
 				if(!reachable && unitReachability.accessibility[hex] == EAccessibility::ALIVE_STACK)
 				{
@@ -624,7 +626,7 @@ void BattleExchangeEvaluator::updateReachabilityMap(HypotheticBattle & hb)
 					{
 						for(BattleHex neighbor : hex.neighbouringTiles())
 						{
-							reachable = unitReachability.distances[neighbor] <= unit->speed(turn);
+							reachable = unitReachability.distances[neighbor] <= unitSpeed;
 
 							if(reachable) break;
 						}

+ 0 - 11
AI/BattleAI/PotentialTargets.cpp

@@ -84,17 +84,6 @@ PotentialTargets::PotentialTargets(const battle::Unit * attacker, const Hypothet
 	{
 		return lhs.damageDiff() > rhs.damageDiff();
 	});
-
-	if (!possibleAttacks.empty())
-	{
-		auto & bestAp = possibleAttacks[0];
-
-		logGlobal->debug("Battle AI best: %s -> %s at %d from %d, affects %d units: d:%lld a:%lld c:%lld s:%lld",
-			bestAp.attack.attacker->unitType()->getJsonKey(),
-			state.battleGetUnitByPos(bestAp.dest)->unitType()->getJsonKey(),
-			(int)bestAp.dest, (int)bestAp.from, (int)bestAp.affectedUnits.size(),
-			bestAp.defenderDamageReduce, bestAp.attackerDamageReduce, bestAp.collateralDamageReduce, bestAp.shootersBlockedDmg);
-	}
 }
 
 int64_t PotentialTargets::bestActionValue() const

+ 1 - 1
AI/Nullkiller/Analyzers/ArmyManager.cpp

@@ -170,7 +170,7 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
 
 		std::vector<SlotInfo> newArmy;
 		uint64_t newValue = 0;
-		newArmyInstance.clear();
+		newArmyInstance.clearSlots();
 
 		for(auto & slot : sortedSlots)
 		{

+ 35 - 27
AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp

@@ -53,6 +53,13 @@ void DangerHitMapAnalyzer::updateHitMap()
 		}
 	}
 
+	auto ourTowns = cb->getTownsInfo();
+
+	for(auto town : ourTowns)
+	{
+		townTreats[town->id]; // insert empty list
+	}
+
 	foreach_tile_pos([&](const int3 & pos){
 		hitMap[pos.x][pos.y][pos.z].reset();
 	});
@@ -95,33 +102,33 @@ void DangerHitMapAnalyzer::updateHitMap()
 					node.fastestDanger = newTreat;
 				}
 
-				if(newTreat.turn == 0)
+				auto objects = cb->getVisitableObjs(pos, false);
+
+				for(auto obj : objects)
 				{
-					auto objects = cb->getVisitableObjs(pos, false);
-					
-					for(auto obj : objects)
+					if(obj->ID == Obj::TOWN && obj->getOwner() == ai->playerID)
 					{
-						if(cb->getPlayerRelations(obj->tempOwner, ai->playerID) != PlayerRelations::ENEMIES)
-							enemyHeroAccessibleObjects[path.targetHero].insert(obj);
+						auto & treats = townTreats[obj->id];
+						auto treat = std::find_if(treats.begin(), treats.end(), [&](const HitMapInfo & i) -> bool
+							{
+								return i.hero.hid == path.targetHero->id;
+							});
 
-						if(obj->ID == Obj::TOWN && obj->getOwner() == ai->playerID)
+						if(treat == treats.end())
 						{
-							auto & treats = townTreats[obj->id];
-							auto treat = std::find_if(treats.begin(), treats.end(), [&](const HitMapInfo & i) -> bool
-								{
-									return i.hero.hid == path.targetHero->id;
-								});
+							treats.emplace_back();
+							treat = std::prev(treats.end(), 1);
+						}
 
-							if(treat == treats.end())
-							{
-								treats.emplace_back();
-								treat = std::prev(treats.end(), 1);
-							}
+						if(newTreat.value() > treat->value())
+						{
+							*treat = newTreat;
+						}
 
-							if(newTreat.value() > treat->value())
-							{
-								*treat = newTreat;
-							}
+						if(newTreat.turn == 0)
+						{
+							if(cb->getPlayerRelations(obj->tempOwner, ai->playerID) != PlayerRelations::ENEMIES)
+								enemyHeroAccessibleObjects.emplace_back(path.targetHero, obj);
 						}
 					}
 				}
@@ -274,16 +281,17 @@ const HitMapNode & DangerHitMapAnalyzer::getTileTreat(const int3 & tile) const
 
 const std::set<const CGObjectInstance *> empty = {};
 
-const std::set<const CGObjectInstance *> & DangerHitMapAnalyzer::getOneTurnAccessibleObjects(const CGHeroInstance * enemy) const
+std::set<const CGObjectInstance *> DangerHitMapAnalyzer::getOneTurnAccessibleObjects(const CGHeroInstance * enemy) const
 {
-	auto result = enemyHeroAccessibleObjects.find(enemy);
-	
-	if(result == enemyHeroAccessibleObjects.end())
+	std::set<const CGObjectInstance *> result;
+
+	for(auto & obj : enemyHeroAccessibleObjects)
 	{
-		return empty;
+		if(obj.hero == enemy)
+			result.insert(obj.obj);
 	}
 
-	return result->second;
+	return result;
 }
 
 void DangerHitMapAnalyzer::reset()

+ 13 - 2
AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.h

@@ -55,11 +55,22 @@ struct HitMapNode
 	}
 };
 
+struct EnemyHeroAccessibleObject
+{
+	const CGHeroInstance * hero;
+	const CGObjectInstance * obj;
+
+	EnemyHeroAccessibleObject(const CGHeroInstance * hero, const CGObjectInstance * obj)
+		:hero(hero), obj(obj)
+	{
+	}
+};
+
 class DangerHitMapAnalyzer
 {
 private:
 	boost::multi_array<HitMapNode, 3> hitMap;
-	std::map<const CGHeroInstance *, std::set<const CGObjectInstance *>> enemyHeroAccessibleObjects;
+	tbb::concurrent_vector<EnemyHeroAccessibleObject> enemyHeroAccessibleObjects;
 	bool hitMapUpToDate = false;
 	bool tileOwnersUpToDate = false;
 	const Nullkiller * ai;
@@ -73,7 +84,7 @@ public:
 	uint64_t enemyCanKillOurHeroesAlongThePath(const AIPath & path) const;
 	const HitMapNode & getObjectTreat(const CGObjectInstance * obj) const;
 	const HitMapNode & getTileTreat(const int3 & tile) const;
-	const std::set<const CGObjectInstance *> & getOneTurnAccessibleObjects(const CGHeroInstance * enemy) const;
+	std::set<const CGObjectInstance *> getOneTurnAccessibleObjects(const CGHeroInstance * enemy) const;
 	void reset();
 	void resetTileOwners() { tileOwnersUpToDate = false; }
 	PlayerColor getTileOwner(const int3 & tile) const;

+ 1 - 1
AI/Nullkiller/Behaviors/DefenceBehavior.cpp

@@ -189,7 +189,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 			town->getNameTranslated(),
 			treat.danger,
 			std::to_string(treat.turn),
-			treat.hero->getNameTranslated());
+			treat.hero ? treat.hero->getNameTranslated() : std::string("<no hero>"));
 
 		handleCounterAttack(town, treat, treatNode.maximumDanger, tasks);
 

+ 13 - 15
AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp

@@ -92,15 +92,6 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 			continue;
 		}
 
-		bool garrisoned = false;
-
-		if(path.turn() == 0 && hero->inTownGarrison)
-		{
-#if NKAI_TRACE_LEVEL >= 1
-			garrisoned = true;
-#endif
-		}
-
 		if(path.turn() > 0 && ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
 		{
 #if NKAI_TRACE_LEVEL >= 2
@@ -184,15 +175,22 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 
 			composition.addNext(heroExchange);
 
-			if(garrisoned && path.turn() == 0)
+			if(hero->inTownGarrison && path.turn() == 0)
 			{
 				auto lockReason = ai->nullkiller->getHeroLockedReason(hero);
 
-				composition.addNextSequence({
-					sptr(ExchangeSwapTownHeroes(hero->visitedTown)),
-					sptr(exchangePath),
-					sptr(ExchangeSwapTownHeroes(hero->visitedTown, hero, lockReason))
-				});
+				if(path.targetHero->visitedTown == hero->visitedTown)
+				{
+					composition.addNextSequence({
+						sptr(ExchangeSwapTownHeroes(hero->visitedTown, hero, lockReason))});
+				}
+				else
+				{
+					composition.addNextSequence({
+						sptr(ExchangeSwapTownHeroes(hero->visitedTown)),
+						sptr(exchangePath),
+						sptr(ExchangeSwapTownHeroes(hero->visitedTown, hero, lockReason))});
+				}
 			}
 			else
 			{

+ 5 - 0
AI/Nullkiller/Engine/Nullkiller.cpp

@@ -323,6 +323,11 @@ void Nullkiller::makeTurn()
 		}
 
 		executeTask(bestTask);
+
+		if(i == MAXPASS)
+		{
+			logAi->error("Goal %s exceeded maxpass. Terminating AI turn.", bestTask->toString());
+		}
 	}
 }
 

+ 1 - 1
AI/VCAI/VCAI.cpp

@@ -1359,7 +1359,7 @@ void VCAI::wander(HeroPtr h)
 
 	TimeCheck tc("looking for wander destination");
 
-	while(h->movementPointsRemaining())
+	for(int k = 0; k < 10 && h->movementPointsRemaining(); k++)
 	{
 		validateVisitableObjs();
 		ah->updatePaths(getMyHeroes());

+ 1 - 1
CMakePresets.json

@@ -240,7 +240,7 @@
                 "default-release"
             ],
             "cacheVariables": {
-                "CMAKE_BUILD_TYPE": "Release"
+                "CMAKE_BUILD_TYPE": "RelWithDebInfo"
             }
         }
     ],

+ 8 - 0
ChangeLog.md

@@ -1,3 +1,11 @@
+# 1.3.0 -> 1.3.1
+(unreleased)
+
+* Fixed crash on starting game with outdated mods
+* Fixed Android mod manager crash 
+* Fixed framerate drops on hero movement with active hota mod
+* Reverted FPS limit on mobile systems back to 60 fps
+
 # 1.2.1 -> 1.3.0
 
 ### GENERAL:

+ 1 - 1
Mods/vcmi/mod.json

@@ -78,7 +78,7 @@
 		]
 	},
 
-	"version" : "1.2",
+	"version" : "1.3",
 	"author" : "VCMI Team",
 	"contact" : "http://forum.vcmi.eu/index.php",
 	"modType" : "Graphical",

+ 10 - 3
android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityMods.java

@@ -182,9 +182,16 @@ public class ActivityMods extends ActivityWithToolbar
         public void onSuccess(ServerResponse<List<VCMIMod>> response)
         {
             Log.i(this, "Initialized mods repo");
-            mModContainer.updateFromRepo(response.mContent);
-            mModsAdapter.updateModsList(mModContainer.submods());
-            mProgress.setVisibility(View.GONE);
+			if (mModContainer == null)
+			{
+				handleNoData();
+			}
+			else
+			{
+				mModContainer.updateFromRepo(response.mContent);
+				mModsAdapter.updateModsList(mModContainer.submods());
+				mProgress.setVisibility(View.GONE);
+			}
         }
 
         @Override

+ 1 - 1
android/vcmi-app/src/main/java/eu/vcmi/vcmi/NativeMethods.java

@@ -146,7 +146,7 @@ public class NativeMethods
     public static void hapticFeedback()
     {
         final Context ctx = SDL.getContext();
-        if (Build.VERSION.SDK_INT >= 26) {
+        if (Build.VERSION.SDK_INT >= 29) {
             ((Vibrator) ctx.getSystemService(ctx.VIBRATOR_SERVICE)).vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK));
         } else {
             ((Vibrator) ctx.getSystemService(ctx.VIBRATOR_SERVICE)).vibrate(30);

+ 6 - 0
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/ExportDataController.java

@@ -133,6 +133,12 @@ public class ExportDataController extends LauncherSettingController<Void, Void>
                         }
                     }
 
+                    if (exported == null)
+                    {
+                        publishProgress("Failed to copy file " + child.getName());
+                        return false;
+                    }
+
                     try(
                             final OutputStream targetStream = owner.getContentResolver()
                                     .openOutputStream(exported.getUri());

+ 7 - 1
android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/FileUtil.java

@@ -81,7 +81,7 @@ public class FileUtil
     {
         if (file == null)
         {
-            Log.e("Broken path given to fileutil");
+            Log.e("Broken path given to fileutil::ensureWriteable");
             return false;
         }
 
@@ -99,6 +99,12 @@ public class FileUtil
 
     public static boolean clearDirectory(final File dir)
     {
+        if (dir == null || dir.listFiles() == null)
+        {
+            Log.e("Broken path given to fileutil::clearDirectory");
+            return false;
+        }
+
         for (final File f : dir.listFiles())
         {
             if (f.isDirectory() && !clearDirectory(f))

+ 24 - 15
client/CMT.cpp

@@ -39,6 +39,7 @@
 #include <vstd/StringUtils.h>
 
 #include <SDL_main.h>
+#include <SDL.h>
 
 #ifdef VCMI_ANDROID
 #include "../lib/CAndroidVMHelper.h"
@@ -249,24 +250,17 @@ int main(int argc, char * argv[])
 	logGlobal->debug("settings = %s", settings.toJsonNode().toJson());
 
 	// Some basic data validation to produce better error messages in cases of incorrect install
-	auto testFile = [](std::string filename, std::string message) -> bool
+	auto testFile = [](std::string filename, std::string message)
 	{
-		if (CResourceHandler::get()->existsResource(ResourceID(filename)))
-			return true;
-
-		logGlobal->error("Error: %s was not found!", message);
-		return false;
+		if (!CResourceHandler::get()->existsResource(ResourceID(filename)))
+			handleFatalError(message, false);
 	};
 
-	if (!testFile("DATA/HELP.TXT", "Heroes III data") ||
-		!testFile("MODS/VCMI/MOD.JSON", "VCMI data"))
-	{
-		exit(1); // These are unrecoverable errors
-	}
-
-	// these two are optional + some installs have them on CD and not in data directory
-	testFile("VIDEO/GOOD1A.SMK", "campaign movies");
-	testFile("SOUNDS/G1A.WAV", "campaign music"); //technically not a music but voiced intro sounds
+	testFile("DATA/HELP.TXT", "VCMI requires Heroes III: Shadow of Death or Heroes III: Complete data files to run!");
+	testFile("MODS/VCMI/MOD.JSON", "VCMI installation is corrupted! Built-in mod was not found!");
+	testFile("DATA/PLAYERS.PAL", "Heroes III data files are missing or corruped! Please reinstall them.");
+	testFile("SPRITES/DEFAULT.DEF", "Heroes III data files are missing or corruped! Please reinstall them.");
+	testFile("DATA/TENTCOLR.TXT", "Heroes III: Restoration of Erathia (including HD Edition) data files are not supported!");
 
 	srand ( (unsigned int)time(nullptr) );
 
@@ -504,3 +498,18 @@ void handleQuit(bool ask)
 		quitApplication();
 	}
 }
+
+void handleFatalError(const std::string & message, bool terminate)
+{
+	logGlobal->error("FATAL ERROR ENCOUTERED, VCMI WILL NOW TERMINATE");
+	logGlobal->error("Reason: %s", message);
+
+	std::string messageToShow = "Fatal error! " + message;
+
+	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error!", messageToShow.c_str(), nullptr);
+
+	if (terminate)
+		throw std::runtime_error(message);
+	else
+		exit(1);
+}

+ 4 - 0
client/CMT.h

@@ -21,3 +21,7 @@ extern SDL_Surface *screen2;     // and hlp surface (used to store not-active in
 extern SDL_Surface *screenBuf; // points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed
 
 void handleQuit(bool ask = true);
+
+/// Notify user about encoutered fatal error and terminate the game
+/// TODO: decide on better location for this method
+[[noreturn]] void handleFatalError(const std::string & message, bool terminate);

+ 49 - 38
client/CMusicHandler.cpp

@@ -14,6 +14,7 @@
 #include "CMusicHandler.h"
 #include "CGameInfo.h"
 #include "renderSDL/SDLRWwrapper.h"
+#include "gui/CGuiHandler.h"
 
 #include "../lib/JsonNode.h"
 #include "../lib/GameConstants.h"
@@ -185,9 +186,9 @@ int CSoundHandler::playSound(std::string sound, int repeats, bool cache)
 				Mix_FreeChunk(chunk);
 		}
 		else if (cache)
-			callbacks[channel];
+			initCallback(channel);
 		else
-			callbacks[channel] = [chunk](){ Mix_FreeChunk(chunk);};
+			initCallback(channel, [chunk](){ Mix_FreeChunk(chunk);});
 	}
 	else
 		channel = -1;
@@ -237,28 +238,50 @@ void CSoundHandler::setChannelVolume(int channel, ui32 percent)
 
 void CSoundHandler::setCallback(int channel, std::function<void()> function)
 {
-	std::map<int, std::function<void()> >::iterator iter;
-	iter = callbacks.find(channel);
+	boost::unique_lock lockGuard(mutexCallbacks);
+
+	auto iter = callbacks.find(channel);
 
 	//channel not found. It may have finished so fire callback now
 	if(iter == callbacks.end())
 		function();
 	else
-		iter->second = function;
+		iter->second.push_back(function);
 }
 
 void CSoundHandler::soundFinishedCallback(int channel)
 {
-	std::map<int, std::function<void()> >::iterator iter;
-	iter = callbacks.find(channel);
-	if (iter == callbacks.end())
+	boost::unique_lock lockGuard(mutexCallbacks);
+
+	if (callbacks.count(channel) == 0)
 		return;
 
-	auto callback = std::move(iter->second);
-	callbacks.erase(iter);
+	// store callbacks from container locally - SDL might reuse this channel for another sound
+	// but do actualy execution in separate thread, to avoid potential deadlocks in case if callback requires locks of its own
+	auto callback = callbacks.at(channel);
+	callbacks.erase(channel);
 
-	if (callback)
-		callback();
+	if (!callback.empty())
+	{
+		GH.dispatchMainThread([callback](){
+			for (auto entry : callback)
+				entry();
+		});
+	}
+}
+
+void CSoundHandler::initCallback(int channel)
+{
+	boost::unique_lock lockGuard(mutexCallbacks);
+	assert(callbacks.count(channel) == 0);
+	callbacks[channel] = {};
+}
+
+void CSoundHandler::initCallback(int channel, const std::function<void()> & function)
+{
+	boost::unique_lock lockGuard(mutexCallbacks);
+	assert(callbacks.count(channel) == 0);
+	callbacks[channel].push_back(function);
 }
 
 int CSoundHandler::ambientGetRange() const
@@ -471,44 +494,32 @@ void CMusicHandler::setVolume(ui32 percent)
 
 void CMusicHandler::musicFinishedCallback()
 {
-	// boost::mutex::scoped_lock guard(mutex);
-	// FIXME: WORKAROUND FOR A POTENTIAL DEADLOCK
+	// call music restart in separate thread to avoid deadlock in some cases
 	// It is possible for:
 	// 1) SDL thread to call this method on end of playback
 	// 2) VCMI code to call queueNext() method to queue new file
 	// this leads to:
 	// 1) SDL thread waiting to acquire music lock in this method (while keeping internal SDL mutex locked)
 	// 2) VCMI thread waiting to acquire internal SDL mutex (while keeping music mutex locked)
-	// Because of that (and lack of clear way to fix that)
-	// We will try to acquire lock here and if failed - do nothing
-	// This may break music playback till next song is enqued but won't deadlock the game
 
-	if (!mutex.try_lock())
+	GH.dispatchMainThread([this]()
 	{
-		logGlobal->error("Failed to acquire mutex! Unable to restart music!");
-		return;
-	}
-
-	if (current.get() != nullptr)
-	{
-		// if music is looped, play it again
-		if (current->play())
+		boost::unique_lock lockGuard(mutex);
+		if (current.get() != nullptr)
 		{
-			mutex.unlock();
-			return;
+			// if music is looped, play it again
+			if (current->play())
+				return;
+			else
+				current.reset();
 		}
-		else
+
+		if (current.get() == nullptr && next.get() != nullptr)
 		{
-			current.reset();
+			current.reset(next.release());
+			current->play();
 		}
-	}
-
-	if (current.get() == nullptr && next.get() != nullptr)
-	{
-		current.reset(next.release());
-		current->play();
-	}
-	mutex.unlock();
+	});
 }
 
 MusicEntry::MusicEntry(CMusicHandler *owner, std::string setName, std::string musicURI, bool looped, bool fromStart):

+ 10 - 3
client/CMusicHandler.h

@@ -45,9 +45,13 @@ private:
 
 	Mix_Chunk *GetSoundChunk(std::string &sound, bool cache);
 
-	//have entry for every currently active channel
-	//std::function will be nullptr if callback was not set
-	std::map<int, std::function<void()> > callbacks;
+	/// have entry for every currently active channel
+	/// vector will be empty if callback was not set
+	std::map<int, std::vector<std::function<void()>> > callbacks;
+
+	/// Protects access to callbacks member to avoid data races:
+	/// SDL calls sound finished callbacks from audio thread
+	boost::mutex mutexCallbacks;
 
 	int ambientDistToVolume(int distance) const;
 	void ambientStopSound(std::string soundId);
@@ -58,6 +62,9 @@ private:
 	std::map<std::string, int> ambientChannels;
 	std::map<int, int> channelVolumes;
 
+	void initCallback(int channel, const std::function<void()> & function);
+	void initCallback(int channel);
+
 public:
 	CSoundHandler();
 

+ 11 - 2
client/CServerHandler.cpp

@@ -539,7 +539,16 @@ void CServerHandler::sendRestartGame() const
 
 void CServerHandler::sendStartGame(bool allowOnlyAI) const
 {
-	verifyStateBeforeStart(allowOnlyAI ? true : settings["session"]["onlyai"].Bool());
+	try
+	{
+		verifyStateBeforeStart(allowOnlyAI ? true : settings["session"]["onlyai"].Bool());
+	}
+	catch (const std::exception & e)
+	{
+		showServerError( std::string("Unable to start map! Reason: ") + e.what());
+		return;
+	}
+
 	LobbyStartGame lsg;
 	if(client)
 	{
@@ -663,7 +672,7 @@ void CServerHandler::startCampaignScenario(std::shared_ptr<CampaignState> cs)
 	});
 }
 
-void CServerHandler::showServerError(std::string txt)
+void CServerHandler::showServerError(std::string txt) const
 {
 	CInfoWindow::showInfoDialog(txt, {});
 }

+ 1 - 1
client/CServerHandler.h

@@ -151,7 +151,7 @@ public:
 	void startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState = nullptr);
 	void endGameplay(bool closeConnection = true, bool restart = false);
 	void startCampaignScenario(std::shared_ptr<CampaignState> cs = {});
-	void showServerError(std::string txt);
+	void showServerError(std::string txt) const;
 
 	// TODO: LobbyState must be updated within game so we should always know how many player interfaces our client handle
 	int howManyPlayerInterfaces();

+ 1 - 1
client/Client.cpp

@@ -219,7 +219,7 @@ void CClient::loadGame(CGameState * initializedGameState)
 	// try to deserialize client data including sleepingHeroes
 	try
 	{
-		boost::filesystem::path clientSaveName = *CResourceHandler::get("local")->getResourceName(ResourceID(CSH->si->mapname, EResType::CLIENT_SAVEGAME));
+		boost::filesystem::path clientSaveName = *CResourceHandler::get()->getResourceName(ResourceID(CSH->si->mapname, EResType::CLIENT_SAVEGAME));
 
 		if(clientSaveName.empty())
 			throw std::runtime_error("Cannot open client part of " + CSH->si->mapname);

+ 8 - 0
client/NetPacksClient.cpp

@@ -95,6 +95,14 @@ void callAllInterfaces(CClient & cl, void (T::*ptr)(Args...), Args2 && ...args)
 template<typename T, typename ... Args, typename ... Args2>
 void callBattleInterfaceIfPresentForBothSides(CClient & cl, void (T::*ptr)(Args...), Args2 && ...args)
 {
+	assert(cl.gameState()->curB);
+
+	if (!cl.gameState()->curB)
+	{
+		logGlobal->error("Attempt to call battle interface without ongoing battle!");
+		return;
+	}
+
 	callOnlyThatBattleInterface(cl, cl.gameState()->curB->sides[0].color, ptr, std::forward<Args2>(args)...);
 	callOnlyThatBattleInterface(cl, cl.gameState()->curB->sides[1].color, ptr, std::forward<Args2>(args)...);
 	if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool() && LOCPLINT->battleInt)

+ 12 - 0
client/adventureMap/AdventureMapInterface.cpp

@@ -822,6 +822,15 @@ void AdventureMapInterface::hotkeyZoom(int delta)
 void AdventureMapInterface::onScreenResize()
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
+
+	// remember our activation state and reactive after reconstruction
+	// since othervice activate() calls for created elements will bypass virtual dispatch
+	// and will call directly CIntObject::activate() instead of dispatching virtual function call
+	bool widgetActive = isActive();
+
+	if (widgetActive)
+		deactivate();
+
 	widget.reset();
 	pos.x = pos.y = 0;
 	pos.w = GH.screenDimensions().x;
@@ -838,4 +847,7 @@ void AdventureMapInterface::onScreenResize()
 		widget->getMapView()->onCenteredObject(LOCPLINT->localState->getCurrentArmy());
 
 	adjustActiveness();
+
+	if (widgetActive)
+		activate();
 }

+ 2 - 0
client/battle/BattleInterface.cpp

@@ -445,6 +445,8 @@ void BattleInterface::spellCast(const BattleSpellCast * sc)
 			stacksController->addNewAnim(new EffectAnimation(*this, side ? "SP07_B.DEF" : "SP07_A.DEF", rightHero));
 		});
 	}
+
+	// animations will be executed by spell effects
 }
 
 void BattleInterface::battleStacksEffectsSet(const SetStackEffect & sse)

+ 3 - 1
client/battle/BattleInterfaceClasses.cpp

@@ -638,7 +638,9 @@ void BattleResultWindow::show(Canvas & to)
 
 void BattleResultWindow::buttonPressed(int button)
 {
-	resultCallback(button);
+	if (resultCallback)
+		resultCallback(button);
+
 	CPlayerInterface &intTmp = owner; //copy reference because "this" will be destructed soon
 
 	close();

+ 20 - 8
client/battle/BattleObstacleController.cpp

@@ -127,13 +127,26 @@ void BattleObstacleController::obstaclePlaced(const std::vector<std::shared_ptr<
 void BattleObstacleController::showAbsoluteObstacles(Canvas & canvas)
 {
 	//Blit absolute obstacles
-	for(auto & oi : owner.curInt->cb->battleGetAllObstacles())
+	for(auto & obstacle : owner.curInt->cb->battleGetAllObstacles())
 	{
-		if(oi->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
+		if(obstacle->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
 		{
-			auto img = getObstacleImage(*oi);
+			auto img = getObstacleImage(*obstacle);
 			if(img)
-				canvas.draw(img, Point(oi->getInfo().width, oi->getInfo().height));
+				canvas.draw(img, Point(obstacle->getInfo().width, obstacle->getInfo().height));
+		}
+
+		if (obstacle->obstacleType == CObstacleInstance::USUAL)
+		{
+			if (obstacle->getInfo().isForegroundObstacle)
+				continue;
+
+			auto img = getObstacleImage(*obstacle);
+			if(img)
+			{
+				Point p = getObstaclePosition(img, *obstacle);
+				canvas.draw(img, p);
+			}
 		}
 	}
 }
@@ -148,11 +161,10 @@ void BattleObstacleController::collectRenderableObjects(BattleRenderer & rendere
 		if (obstacle->obstacleType == CObstacleInstance::MOAT)
 			continue;
 
-		bool isForeground = obstacle->obstacleType == CObstacleInstance::USUAL && obstacle->getInfo().isForegroundObstacle;
-
-		auto layer = isForeground ? EBattleFieldLayer::OBSTACLES_FG : EBattleFieldLayer::OBSTACLES_BG;
+		if (obstacle->obstacleType == CObstacleInstance::USUAL && !obstacle->getInfo().isForegroundObstacle)
+			continue;
 
-		renderer.insert(layer, obstacle->pos, [this, obstacle]( BattleRenderer::RendererRef canvas ){
+		renderer.insert(EBattleFieldLayer::OBSTACLES, obstacle->pos, [this, obstacle]( BattleRenderer::RendererRef canvas ){
 			auto img = getObstacleImage(*obstacle);
 			if(img)
 			{

+ 1 - 2
client/battle/BattleRenderer.h

@@ -16,12 +16,11 @@ class BattleInterface;
 
 enum class EBattleFieldLayer {
 					   // confirmed ordering requirements:
-	OBSTACLES_BG  = 0,
 	CORPSES       = 0,
 	WALLS         = 1,
 	HEROES        = 2,
 	STACKS        = 2, // after corpses, obstacles, walls
-	OBSTACLES_FG  = 3, // after stacks
+	OBSTACLES     = 3, // after stacks
 	STACK_AMOUNTS = 3, // after stacks, obstacles, corpses
 	EFFECTS       = 4, // after obstacles, battlements
 };

+ 1 - 3
client/battle/BattleSiegeController.cpp

@@ -307,15 +307,13 @@ void BattleSiegeController::collectRenderableObjects(BattleRenderer & renderer)
 			renderer.insert( EBattleFieldLayer::STACKS, getWallPiecePosition(wallPiece), [this, wallPiece](BattleRenderer::RendererRef canvas){
 				owner.stacksController->showStack(canvas, getTurretStack(wallPiece));
 			});
-			renderer.insert( EBattleFieldLayer::OBSTACLES_FG, getWallPiecePosition(wallPiece), [this, wallPiece](BattleRenderer::RendererRef canvas){
+			renderer.insert( EBattleFieldLayer::OBSTACLES, getWallPiecePosition(wallPiece), [this, wallPiece](BattleRenderer::RendererRef canvas){
 				showWallPiece(canvas, wallPiece);
 			});
 		}
 		renderer.insert( EBattleFieldLayer::WALLS, getWallPiecePosition(wallPiece), [this, wallPiece](BattleRenderer::RendererRef canvas){
 			showWallPiece(canvas, wallPiece);
 		});
-
-
 	}
 }
 

+ 1 - 1
client/battle/BattleStacksController.cpp

@@ -499,7 +499,7 @@ void BattleStacksController::stacksAreAttacked(std::vector<StackAttackedInfo> at
 void BattleStacksController::stackTeleported(const CStack *stack, std::vector<BattleHex> destHex, int distance)
 {
 	assert(destHex.size() > 0);
-	owner.checkForAnimations();
+	//owner.checkForAnimations(); // NOTE: at this point spellcast animations were added, but not executed
 
 	owner.addToAnimationStage(EAnimationEvents::HIT, [=](){
 		addNewAnim( new ColorTransformAnimation(owner, stack, "teleportFadeOut", nullptr) );

+ 2 - 0
client/gui/CGuiHandler.cpp

@@ -91,6 +91,8 @@ void CGuiHandler::handleEvents()
 void CGuiHandler::fakeMouseMove()
 {
 	dispatchMainThread([](){
+		assert(CPlayerInterface::pim);
+		boost::unique_lock lock(*CPlayerInterface::pim);
 		GH.events().dispatchMouseMoved(Point(0, 0), GH.getCursorPosition());
 	});
 }

+ 9 - 7
client/gui/CursorHandler.h

@@ -31,8 +31,6 @@ namespace Cursor
 	};
 
 	enum class Combat {
-		INVALID        = -1,
-
 		BLOCKED        = 0,
 		MOVE           = 1,
 		FLY            = 2,
@@ -157,12 +155,16 @@ public:
 	template<typename Index>
 	Index get()
 	{
-		assert((std::is_same<Index, Cursor::Default>::value   )|| type != Cursor::Type::DEFAULT );
-		assert((std::is_same<Index, Cursor::Map>::value       )|| type != Cursor::Type::ADVENTURE );
-		assert((std::is_same<Index, Cursor::Combat>::value    )|| type != Cursor::Type::COMBAT );
-		assert((std::is_same<Index, Cursor::Spellcast>::value )|| type != Cursor::Type::SPELLBOOK );
+		bool typeValid = true;
+
+		typeValid &= (std::is_same<Index, Cursor::Default>::value   )|| type != Cursor::Type::DEFAULT;
+		typeValid &= (std::is_same<Index, Cursor::Map>::value       )|| type != Cursor::Type::ADVENTURE;
+		typeValid &= (std::is_same<Index, Cursor::Combat>::value    )|| type != Cursor::Type::COMBAT;
+		typeValid &= (std::is_same<Index, Cursor::Spellcast>::value )|| type != Cursor::Type::SPELLBOOK;
 
-		return static_cast<Index>(frame);
+		if (typeValid)
+			return static_cast<Index>(frame);
+		return Index::POINTER;
 	}
 
 	Point getPivotOffsetDefault(size_t index);

+ 7 - 3
client/lobby/RandomMapTab.cpp

@@ -146,7 +146,6 @@ void RandomMapTab::updateMapInfoByHost()
 	mapInfo->mapHeader->twoLevel = mapGenOptions->getHasTwoLevels();
 
 	// Generate player information
-	mapInfo->mapHeader->players.clear();
 	int playersToGen = PlayerColor::PLAYER_LIMIT_I;
 	if(mapGenOptions->getPlayerCount() != CMapGenOptions::RANDOM_SIZE)
 	{
@@ -156,10 +155,15 @@ void RandomMapTab::updateMapInfoByHost()
 			playersToGen = mapGenOptions->getPlayerCount();
 	}
 
-
 	mapInfo->mapHeader->howManyTeams = playersToGen;
 
 	std::set<TeamID> occupiedTeams;
+	for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; ++i)
+	{
+		mapInfo->mapHeader->players[i].canComputerPlay = false;
+		mapInfo->mapHeader->players[i].canHumanPlay = false;
+	}
+
 	for(int i = 0; i < playersToGen; ++i)
 	{
 		PlayerInfo player;
@@ -178,7 +182,7 @@ void RandomMapTab::updateMapInfoByHost()
 		occupiedTeams.insert(team);
 		player.hasMainTown = true;
 		player.generateHeroAtMainTown = true;
-		mapInfo->mapHeader->players.push_back(player);
+		mapInfo->mapHeader->players[i] = player;
 	}
 	for(auto & player : mapInfo->mapHeader->players)
 	{

+ 1 - 1
client/lobby/SelectionTab.cpp

@@ -425,7 +425,7 @@ void SelectionTab::select(int position)
 
 	if(inputName && inputName->isActive())
 	{
-		auto filename = *CResourceHandler::get("local")->getResourceName(ResourceID(curItems[py]->fileURI, EResType::SAVEGAME));
+		auto filename = *CResourceHandler::get()->getResourceName(ResourceID(curItems[py]->fileURI, EResType::SAVEGAME));
 		inputName->setText(filename.stem().string());
 	}
 

+ 8 - 0
client/mapView/MapRenderer.cpp

@@ -523,6 +523,14 @@ uint8_t MapRendererObjects::checksum(IMapRendererContext & context, const int3 &
 	for(const auto & objectID : context.getObjects(coordinates))
 	{
 		const auto * objectInstance = context.getObject(objectID);
+
+		assert(objectInstance);
+		if(!objectInstance)
+		{
+			logGlobal->error("Stray map object that isn't fading");
+			continue;
+		}
+
 		size_t groupIndex = context.objectGroupIndex(objectInstance->id);
 		Point offsetPixels = context.objectImageOffset(objectInstance->id, coordinates);
 

+ 20 - 4
client/mapView/MapViewController.cpp

@@ -247,6 +247,20 @@ void MapViewController::afterRender()
 	}
 }
 
+bool MapViewController::isEventInstant(const CGObjectInstance * obj)
+{
+	if (!isEventVisible(obj))
+		return true;
+
+	if(!LOCPLINT->makingTurn && settings["adventure"]["enemyMoveTime"].Float() <= 0)
+		return true; // instant movement speed
+
+	if(LOCPLINT->makingTurn && settings["adventure"]["heroMoveTime"].Float() <= 0)
+		return true; // instant movement speed
+
+	return false;
+}
+
 bool MapViewController::isEventVisible(const CGObjectInstance * obj)
 {
 	if(adventureContext == nullptr)
@@ -358,7 +372,8 @@ void MapViewController::onBeforeHeroEmbark(const CGHeroInstance * obj, const int
 {
 	if(isEventVisible(obj, from, dest))
 	{
-		fadeOutObject(obj);
+		if (!isEventInstant(obj))
+			fadeOutObject(obj);
 		setViewCenter(obj->getSightCenter());
 	}
 	else
@@ -381,7 +396,8 @@ void MapViewController::onAfterHeroDisembark(const CGHeroInstance * obj, const i
 {
 	if(isEventVisible(obj, from, dest))
 	{
-		fadeInObject(obj);
+		if (!isEventInstant(obj))
+			fadeInObject(obj);
 		setViewCenter(obj->getSightCenter());
 	}
 	addObject(obj);
@@ -391,7 +407,7 @@ void MapViewController::onObjectFadeIn(const CGObjectInstance * obj)
 {
 	assert(!hasOngoingAnimations());
 
-	if(isEventVisible(obj))
+	if(isEventVisible(obj) && !isEventInstant(obj) )
 		fadeInObject(obj);
 
 	addObject(obj);
@@ -401,7 +417,7 @@ void MapViewController::onObjectFadeOut(const CGObjectInstance * obj)
 {
 	assert(!hasOngoingAnimations());
 
-	if(isEventVisible(obj))
+	if(isEventVisible(obj) && !isEventInstant(obj) )
 		fadeOutObject(obj);
 	else
 		removeObject(obj);

+ 1 - 0
client/mapView/MapViewController.h

@@ -50,6 +50,7 @@ class MapViewController : public IMapObjectObserver
 	std::shared_ptr<MapRendererPuzzleMapContext> puzzleMapContext;
 
 private:
+	bool isEventInstant(const CGObjectInstance * obj);
 	bool isEventVisible(const CGObjectInstance * obj);
 	bool isEventVisible(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
 

+ 14 - 13
client/render/Graphics.cpp

@@ -125,24 +125,11 @@ void Graphics::initializeBattleGraphics()
 }
 Graphics::Graphics()
 {
-	#if 0
-
-	std::vector<Task> tasks; //preparing list of graphics to load
-	tasks += std::bind(&Graphics::loadFonts,this);
-	tasks += std::bind(&Graphics::loadPaletteAndColors,this);
-	tasks += std::bind(&Graphics::initializeBattleGraphics,this);
-	tasks += std::bind(&Graphics::loadErmuToPicture,this);
-	tasks += std::bind(&Graphics::initializeImageLists,this);
-
-	CThreadHelper th(&tasks,std::max((ui32)1,boost::thread::hardware_concurrency()));
-	th.run();
-	#else
 	loadFonts();
 	loadPaletteAndColors();
 	initializeBattleGraphics();
 	loadErmuToPicture();
 	initializeImageLists();
-	#endif
 
 	//(!) do not load any CAnimation here
 }
@@ -291,3 +278,17 @@ void Graphics::initializeImageLists()
 	addImageListEntries(CGI->spells());
 	addImageListEntries(CGI->skills());
 }
+
+std::shared_ptr<CAnimation> Graphics::getAnimation(const std::string & path)
+{
+	ResourceID animationPath(path, EResType::ANIMATION);
+
+	if (cachedAnimations.count(animationPath.getName()) != 0)
+		return cachedAnimations.at(animationPath.getName());
+
+	auto newAnimation = std::make_shared<CAnimation>(animationPath.getName());
+
+	newAnimation->preload();
+	cachedAnimations[animationPath.getName()] = newAnimation;
+	return newAnimation;
+}

+ 4 - 0
client/render/Graphics.h

@@ -42,7 +42,11 @@ class Graphics
 	void loadFonts();
 	void initializeImageLists();
 
+	std::map<std::string, std::shared_ptr<CAnimation>> cachedAnimations;
+
 public:
+	std::shared_ptr<CAnimation> getAnimation(const std::string & path);
+
 	//Fonts
 	static const int FONTS_NUMBER = 9;
 	std::array< std::shared_ptr<IFont>, FONTS_NUMBER> fonts;

+ 6 - 0
client/renderSDL/CBitmapFont.cpp

@@ -26,6 +26,12 @@
 
 void CBitmapFont::loadModFont(const std::string & modName, const ResourceID & resource)
 {
+	if (!CResourceHandler::get(modName)->existsResource(resource))
+	{
+		logGlobal->error("Failed to load font %s from mod %s", resource.getName(), modName);
+		return;
+	}
+
 	auto data = CResourceHandler::get(modName)->load(resource)->readAll();
 	std::string modLanguage = CGI->modh->getModLanguage(modName);
 	std::string modEncoding = Languages::getLanguageOptions(modLanguage).encoding;

+ 11 - 0
client/renderSDL/SDL_Extensions.cpp

@@ -82,6 +82,17 @@ SDL_Surface * CSDL_Ext::newSurface(int w, int h)
 SDL_Surface * CSDL_Ext::newSurface(int w, int h, SDL_Surface * mod) //creates new surface, with flags/format same as in surface given
 {
 	SDL_Surface * ret = SDL_CreateRGBSurface(0,w,h,mod->format->BitsPerPixel,mod->format->Rmask,mod->format->Gmask,mod->format->Bmask,mod->format->Amask);
+
+	if(ret == nullptr)
+	{
+		const char * error = SDL_GetError();
+
+		std::string messagePattern = "Failed to create SDL Surface of size %d x %d, %d bpp. Reason: %s";
+		std::string message = boost::str(boost::format(messagePattern) % w % h % mod->format->BitsPerPixel % error);
+
+		handleFatalError(message, true);
+	}
+
 	if (mod->format->palette)
 	{
 		assert(ret->format->palette);

+ 9 - 1
client/renderSDL/ScreenHandler.cpp

@@ -264,7 +264,15 @@ void ScreenHandler::initializeWindow()
 	mainWindow = createWindow();
 
 	if(mainWindow == nullptr)
-		throw std::runtime_error("Unable to create window\n");
+	{
+		const char * error = SDL_GetError();
+		Point dimensions = getPreferredWindowResolution();
+
+		std::string messagePattern = "Failed to create SDL Window of size %d x %d. Reason: %s";
+		std::string message = boost::str(boost::format(messagePattern) % dimensions.x % dimensions.y % error);
+
+		handleFatalError(message, true);
+	}
 
 	//create first available renderer if preferred not set. Use no flags, so HW accelerated will be preferred but SW renderer also will possible
 	mainRenderer = SDL_CreateRenderer(mainWindow, getPreferredRenderingDriver(), 0);

+ 3 - 2
client/widgets/CGarrisonInt.cpp

@@ -17,6 +17,7 @@
 #include "../gui/CGuiHandler.h"
 #include "../gui/WindowHandler.h"
 #include "../render/IImage.h"
+#include "../render/Graphics.h"
 #include "../windows/CCreatureWindow.h"
 #include "../windows/GUIClasses.h"
 #include "../CGameInfo.h"
@@ -422,10 +423,10 @@ CGarrisonSlot::CGarrisonSlot(CGarrisonInt * Owner, int x, int y, SlotID IID, EGa
 
 	std::string imgName = owner->smallIcons ? "cprsmall" : "TWCRPORT";
 
-	creatureImage = std::make_shared<CAnimImage>(imgName, 0);
+	creatureImage = std::make_shared<CAnimImage>(graphics->getAnimation(imgName), 0);
 	creatureImage->disable();
 
-	selectionImage = std::make_shared<CAnimImage>(imgName, 1);
+	selectionImage = std::make_shared<CAnimImage>(graphics->getAnimation(imgName), 1);
 	selectionImage->disable();
 
 	if(Owner->smallIcons)

+ 23 - 18
client/widgets/MiscWidgets.cpp

@@ -18,6 +18,7 @@
 #include "../CPlayerInterface.h"
 #include "../CGameInfo.h"
 #include "../widgets/TextControls.h"
+#include "../widgets/CGarrisonInt.h"
 #include "../windows/CCastleInterface.h"
 #include "../windows/InfoWindows.h"
 #include "../render/Canvas.h"
@@ -302,28 +303,30 @@ CHeroTooltip::CHeroTooltip(Point pos, const CGHeroInstance * hero):
 	init(InfoAboutHero(hero, InfoAboutHero::EInfoLevel::DETAILED));
 }
 
-CInteractableHeroTooltip::CInteractableHeroTooltip(Point pos, const CGHeroInstance * hero):
-		CGarrisonInt(pos + Point(0, 73), 4, Point(0, 0), hero, nullptr, true, true, CGarrisonInt::ESlotsLayout::REVERSED_TWO_ROWS)
+CInteractableHeroTooltip::CInteractableHeroTooltip(Point pos, const CGHeroInstance * hero)
 {
 	init(InfoAboutHero(hero, InfoAboutHero::EInfoLevel::DETAILED));
+
+	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
+	garrison = std::make_shared<CGarrisonInt>(pos + Point(0, 73), 4, Point(0, 0), hero, nullptr, true, true, CGarrisonInt::ESlotsLayout::REVERSED_TWO_ROWS);
 }
 
 void CInteractableHeroTooltip::init(const InfoAboutHero & hero)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
-	portrait = std::make_shared<CAnimImage>("PortraitsLarge", hero.portrait, 0, 3, 2-73);
-	title = std::make_shared<CLabel>(66, 2-73, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, hero.name);
+	portrait = std::make_shared<CAnimImage>("PortraitsLarge", hero.portrait, 0, 3, 2);
+	title = std::make_shared<CLabel>(66, 2, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, hero.name);
 
 	if(hero.details)
 	{
 		for(size_t i = 0; i < hero.details->primskills.size(); i++)
-			labels.push_back(std::make_shared<CLabel>(75 + 28 * (int)i, 58-73, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE,
+			labels.push_back(std::make_shared<CLabel>(75 + 28 * (int)i, 58, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE,
 													  std::to_string(hero.details->primskills[i])));
 
-		labels.push_back(std::make_shared<CLabel>(158, 98-73, FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, std::to_string(hero.details->mana)));
+		labels.push_back(std::make_shared<CLabel>(158, 98, FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, std::to_string(hero.details->mana)));
 
-		morale = std::make_shared<CAnimImage>("IMRL22", hero.details->morale + 3, 0, 5, 74-73);
-		luck = std::make_shared<CAnimImage>("ILCK22", hero.details->luck + 3, 0, 5, 91-73);
+		morale = std::make_shared<CAnimImage>("IMRL22", hero.details->morale + 3, 0, 5, 74);
+		luck = std::make_shared<CAnimImage>("ILCK22", hero.details->luck + 3, 0, 5, 91);
 	}
 }
 
@@ -382,9 +385,11 @@ CTownTooltip::CTownTooltip(Point pos, const CGTownInstance * town)
 }
 
 CInteractableTownTooltip::CInteractableTownTooltip(Point pos, const CGTownInstance * town)
-		: CGarrisonInt(pos + Point(0, 73), 4, Point(0, 0), town->getUpperArmy(), nullptr, true, true, CGarrisonInt::ESlotsLayout::REVERSED_TWO_ROWS)
 {
 	init(InfoAboutTown(town, true));
+
+	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
+	garrison = std::make_shared<CGarrisonInt>(pos + Point(0, 73), 4, Point(0, 0), town->getUpperArmy(), nullptr, true, true, CGarrisonInt::ESlotsLayout::REVERSED_TWO_ROWS);
 }
 
 void CInteractableTownTooltip::init(const InfoAboutTown & town)
@@ -394,37 +399,37 @@ void CInteractableTownTooltip::init(const InfoAboutTown & town)
 	//order of icons in def: fort, citadel, castle, no fort
 	size_t fortIndex = town.fortLevel ? town.fortLevel - 1 : 3;
 
-	fort = std::make_shared<CAnimImage>("ITMCLS", fortIndex, 0, 105, 31-73);
+	fort = std::make_shared<CAnimImage>("ITMCLS", fortIndex, 0, 105, 31);
 
 	assert(town.tType);
 
 	size_t iconIndex = town.tType->clientInfo.icons[town.fortLevel > 0][town.built >= CGI->settings()->getInteger(EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP)];
 
-	build = std::make_shared<CAnimImage>("itpt", iconIndex, 0, 3, 2-73);
-	title = std::make_shared<CLabel>(66, 2-73, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, town.name);
+	build = std::make_shared<CAnimImage>("itpt", iconIndex, 0, 3, 2);
+	title = std::make_shared<CLabel>(66, 2, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, town.name);
 
 	if(town.details)
 	{
-		hall = std::make_shared<CAnimImage>("ITMTLS", town.details->hallLevel, 0, 67, 31-73);
+		hall = std::make_shared<CAnimImage>("ITMTLS", town.details->hallLevel, 0, 67, 31);
 
 		if(town.details->goldIncome)
 		{
-			income = std::make_shared<CLabel>(157, 58-73, FONT_TINY, ETextAlignment::CENTER, Colors::WHITE,
+			income = std::make_shared<CLabel>(157, 58, FONT_TINY, ETextAlignment::CENTER, Colors::WHITE,
 											  std::to_string(town.details->goldIncome));
 		}
 		if(town.details->garrisonedHero) //garrisoned hero icon
-			garrisonedHero = std::make_shared<CPicture>("TOWNQKGH", 149, 76-73);
+			garrisonedHero = std::make_shared<CPicture>("TOWNQKGH", 149, 76);
 
 		if(town.details->customRes)//silo is built
 		{
 			if(town.tType->primaryRes == EGameResID::WOOD_AND_ORE )// wood & ore
 			{
-				res1 = std::make_shared<CAnimImage>("SMALRES", GameResID(EGameResID::WOOD), 0, 7, 75-73);
-				res2 = std::make_shared<CAnimImage>("SMALRES", GameResID(EGameResID::ORE), 0, 7, 88-73);
+				res1 = std::make_shared<CAnimImage>("SMALRES", GameResID(EGameResID::WOOD), 0, 7, 75);
+				res2 = std::make_shared<CAnimImage>("SMALRES", GameResID(EGameResID::ORE), 0, 7, 88);
 			}
 			else
 			{
-				res1 = std::make_shared<CAnimImage>("SMALRES", town.tType->primaryRes, 0, 7, 81-73);
+				res1 = std::make_shared<CAnimImage>("SMALRES", town.tType->primaryRes, 0, 7, 81);
 			}
 		}
 	}

+ 5 - 3
client/widgets/MiscWidgets.h

@@ -10,7 +10,6 @@
 #pragma once
 
 #include "../gui/CIntObject.h"
-#include "CGarrisonInt.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -26,6 +25,7 @@ class AFactionMember;
 VCMI_LIB_NAMESPACE_END
 
 class CLabel;
+class CGarrisonInt;
 class CCreatureAnim;
 class CComponent;
 class CAnimImage;
@@ -86,13 +86,14 @@ public:
 };
 
 /// Class for HD mod-like interactable infobox tooltip. Does not have any background!
-class CInteractableHeroTooltip : public CGarrisonInt
+class CInteractableHeroTooltip : public CIntObject
 {
 	std::shared_ptr<CLabel> title;
 	std::shared_ptr<CAnimImage> portrait;
 	std::vector<std::shared_ptr<CLabel>> labels;
 	std::shared_ptr<CAnimImage> morale;
 	std::shared_ptr<CAnimImage> luck;
+	std::shared_ptr<CGarrisonInt> garrison;
 
 	void init(const InfoAboutHero & hero);
 public:
@@ -119,7 +120,7 @@ public:
 };
 
 /// Class for HD mod-like interactable infobox tooltip. Does not have any background!
-class CInteractableTownTooltip : public CGarrisonInt
+class CInteractableTownTooltip : public CIntObject
 {
 	std::shared_ptr<CLabel> title;
 	std::shared_ptr<CAnimImage> fort;
@@ -129,6 +130,7 @@ class CInteractableTownTooltip : public CGarrisonInt
 	std::shared_ptr<CPicture> garrisonedHero;
 	std::shared_ptr<CAnimImage> res1;
 	std::shared_ptr<CAnimImage> res2;
+	std::shared_ptr<CGarrisonInt> garrison;
 
 	void init(const InfoAboutTown & town);
 public:

+ 39 - 15
client/windows/CCastleInterface.cpp

@@ -848,7 +848,13 @@ void CCastleBuildings::enterCastleGate()
 
 void CCastleBuildings::enterDwelling(int level)
 {
-	assert(level >= 0 && level < town->creatures.size());
+	if (level < 0 || level >= town->creatures.size() || town->creatures[level].second.empty())
+	{
+		assert(0);
+		logGlobal->error("Attempt to enter into invalid dwelling of level %d in town %s (%s)", level, town->getNameTranslated(), town->town->faction->getNameTranslated());
+		return;
+	}
+
 	auto recruitCb = [=](CreatureID id, int count)
 	{
 		LOCPLINT->cb->recruitCreatures(town, town->getUpperArmy(), id, count, level);
@@ -1444,7 +1450,11 @@ CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Buildin
 	auto statusbarBackground = std::make_shared<CPicture>(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26);
 	statusbar = CGStatusBar::create(statusbarBackground);
 
-	name = std::make_shared<CLabel>(197, 30, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, boost::str(boost::format(CGI->generaltexth->hcommands[7]) % building->getNameTranslated()));
+	MetaString nameString;
+	nameString.appendTextID("core.hallinfo.7");
+	nameString.replaceTextID(building->getNameTextID());
+
+	name = std::make_shared<CLabel>(197, 30, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, nameString.toString());
 	description = std::make_shared<CTextBox>(building->getDescriptionTranslated(), Rect(33, 135, 329, 67), 0, FONT_MEDIUM, ETextAlignment::CENTER);
 	stateText = std::make_shared<CTextBox>(getTextForState(state), Rect(33, 216, 329, 67), 0, FONT_SMALL, ETextAlignment::CENTER);
 
@@ -1461,14 +1471,20 @@ CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Buildin
 
 	if(!rightClick)
 	{	//normal window
-		std::string tooltipYes = boost::str(boost::format(CGI->generaltexth->allTexts[595]) % building->getNameTranslated());
-		std::string tooltipNo  = boost::str(boost::format(CGI->generaltexth->allTexts[596]) % building->getNameTranslated());
 
-		buy = std::make_shared<CButton>(Point(45, 446), "IBUY30", CButton::tooltip(tooltipYes), [&](){ buyFunc(); }, EShortcut::GLOBAL_ACCEPT);
+		MetaString tooltipYes;
+		tooltipYes.appendTextID("core.genrltxt.595");
+		tooltipYes.replaceTextID(building->getNameTextID());
+
+		MetaString tooltipNo;
+		tooltipNo.appendTextID("core.genrltxt.596");
+		tooltipNo.replaceTextID(building->getNameTextID());
+
+		buy = std::make_shared<CButton>(Point(45, 446), "IBUY30", CButton::tooltip(tooltipYes.toString()), [&](){ buyFunc(); }, EShortcut::GLOBAL_ACCEPT);
 		buy->setBorderColor(Colors::METALLIC_GOLD);
 		buy->block(state!=7 || LOCPLINT->playerID != town->tempOwner);
 
-		cancel = std::make_shared<CButton>(Point(290, 445), "ICANCEL", CButton::tooltip(tooltipNo), [&](){ close();}, EShortcut::GLOBAL_CANCEL);
+		cancel = std::make_shared<CButton>(Point(290, 445), "ICANCEL", CButton::tooltip(tooltipNo.toString()), [&](){ close();}, EShortcut::GLOBAL_CANCEL);
 		cancel->setBorderColor(Colors::METALLIC_GOLD);
 	}
 }
@@ -1834,17 +1850,25 @@ CBlacksmithDialog::CBlacksmithDialog(bool possible, CreatureID creMachineID, Art
 	anim = std::make_shared<CCreatureAnim>(64, 50, creature->animDefName);
 	anim->clipRect(113,125,200,150);
 
-	title = std::make_shared<CLabel>(165, 28, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW,
-				boost::str(boost::format(CGI->generaltexth->allTexts[274]) % creature->getNameSingularTranslated()));
-	costText = std::make_shared<CLabel>(165, 218, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->jktexts[43]);
-	costValue = std::make_shared<CLabel>(165, 292, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE,
-	                std::to_string(aid.toArtifact(CGI->artifacts())->getPrice()));
+	MetaString titleString;
+	titleString.appendTextID("core.genrltxt.274");
+	titleString.replaceTextID(creature->getNameSingularTextID());
+
+	MetaString buyText;
+	buyText.appendTextID("core.genrltxt.595");
+	buyText.replaceTextID(creature->getNameSingularTextID());
 
-	std::string text = boost::str(boost::format(CGI->generaltexth->allTexts[595]) % creature->getNameSingularTranslated());
-	buy = std::make_shared<CButton>(Point(42, 312), "IBUY30.DEF", CButton::tooltip(text), [&](){ close(); }, EShortcut::GLOBAL_ACCEPT);
+	MetaString cancelText;
+	cancelText.appendTextID("core.genrltxt.596");
+	cancelText.replaceTextID(creature->getNameSingularTextID());
 
-	text = boost::str(boost::format(CGI->generaltexth->allTexts[596]) % creature->getNameSingularTranslated());
-	cancel = std::make_shared<CButton>(Point(224, 312), "ICANCEL.DEF", CButton::tooltip(text), [&](){ close(); }, EShortcut::GLOBAL_CANCEL);
+	std::string costString = std::to_string(aid.toArtifact(CGI->artifacts())->getPrice());
+
+	title = std::make_shared<CLabel>(165, 28, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, titleString.toString());
+	costText = std::make_shared<CLabel>(165, 218, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->jktexts[43]);
+	costValue = std::make_shared<CLabel>(165, 292, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, costString);
+	buy = std::make_shared<CButton>(Point(42, 312), "IBUY30.DEF", CButton::tooltip(buyText.toString()), [&](){ close(); }, EShortcut::GLOBAL_ACCEPT);
+	cancel = std::make_shared<CButton>(Point(224, 312), "ICANCEL.DEF", CButton::tooltip(cancelText.toString()), [&](){ close(); }, EShortcut::GLOBAL_CANCEL);
 
 	if(possible)
 		buy->addCallback([=](){ LOCPLINT->cb->buyArtifact(LOCPLINT->cb->getHero(hid),aid); });

+ 6 - 3
client/windows/CTradeWindow.cpp

@@ -1262,11 +1262,14 @@ void CAltarWindow::SacrificeAll()
 	}
 	else
 	{
-		for(const auto & aw : arts->visibleArtSet.artifactsWorn)
+		std::vector<ConstTransitivePtr<CArtifactInstance>> artsForMove;
+		for(const auto& slotInfo : arts->visibleArtSet.artifactsWorn)
 		{
-			if(!aw.second.locked)
-				moveArtToAltar(nullptr, aw.second.artifact);
+			if(!slotInfo.second.locked && slotInfo.second.artifact->artType->isTradable())
+				artsForMove.push_back(slotInfo.second.artifact);
 		}
+		for(auto artInst : artsForMove)
+			moveArtToAltar(nullptr, artInst);
 		arts->updateWornSlots();
 		SacrificeBackpack();
 	}

+ 0 - 2
config/schemas/settings.json

@@ -201,8 +201,6 @@
 				},
 				"targetfps" : {
 					"type" : "number",
-					"defaultIOS" : 30, // reduce battery usage
-					"defaultAndroid" : 30, // reduce battery usage
 					"default" : 60
 				}
 			}

+ 6 - 0
debian/changelog

@@ -4,6 +4,12 @@ vcmi (1.4.0) jammy; urgency=medium
 
  -- Ivan Savenko <[email protected]>  Fri, 22 Dec 2023 16:00:00 +0200
 
+vcmi (1.3.1) jammy; urgency=medium
+
+  * New upstream release
+
+ -- Ivan Savenko <[email protected]>  Fri, 18 Aug 2023 16:00:00 +0200
+
 vcmi (1.3.0) jammy; urgency=medium
 
   * New upstream release

+ 1 - 0
launcher/eu.vcmi.VCMI.metainfo.xml

@@ -52,6 +52,7 @@
 	</categories>
 	<releases>
 		<release version="1.4.0" date="2023-12-22" type="development" />
+		<release version="1.3.1" date="2023-08-18" type="development" />
 		<release version="1.3.0" date="2023-08-04" />
 		<release version="1.2.1" date="2023-04-28" />
 		<release version="1.2.0" date="2023-04-14" />

+ 9 - 7
launcher/settingsView/csettingsview_moc.cpp

@@ -72,6 +72,8 @@ void CSettingsView::loadSettings()
 	ui->comboBoxFullScreen->hide();
 	ui->labelFullScreen->hide();
 #else
+	ui->labelReservedArea->hide();
+	ui->spinBoxReservedArea->hide();
 	if (settings["video"]["realFullscreen"].Bool())
 		ui->comboBoxFullScreen->setCurrentIndex(2);
 	else
@@ -81,6 +83,7 @@ void CSettingsView::loadSettings()
 
 	ui->spinBoxInterfaceScaling->setValue(settings["video"]["resolution"]["scaling"].Float());
 	ui->spinBoxFramerateLimit->setValue(settings["video"]["targetfps"].Float());
+	ui->spinBoxReservedArea->setValue(std::round(settings["video"]["reservedWidth"].Float() * 100));
 
 	ui->comboBoxFriendlyAI->setCurrentText(QString::fromStdString(settings["server"]["friendlyAI"].String()));
 	ui->comboBoxNeutralAI->setCurrentText(QString::fromStdString(settings["server"]["neutralAI"].String()));
@@ -468,14 +471,12 @@ void CSettingsView::on_lineEditRepositoryExtra_textEdited(const QString &arg1)
 	node->String() = arg1.toStdString();
 }
 
-
 void CSettingsView::on_spinBoxInterfaceScaling_valueChanged(int arg1)
 {
 	Settings node = settings.write["video"]["resolution"]["scaling"];
 	node->Float() = arg1;
 }
 
-
 void CSettingsView::on_refreshRepositoriesButton_clicked()
 {
 	auto * mainWindow = dynamic_cast<MainWindow *>(qApp->activeWindow());
@@ -487,7 +488,6 @@ void CSettingsView::on_refreshRepositoriesButton_clicked()
 	mainWindow->getModView()->loadRepositories();
 }
 
-
 void CSettingsView::on_spinBoxFramerateLimit_valueChanged(int arg1)
 {
 	Settings node = settings.write["video"]["targetfps"];
@@ -500,14 +500,12 @@ void CSettingsView::on_comboBoxEnemyPlayerAI_currentTextChanged(const QString &a
 	node->String() = arg1.toUtf8().data();
 }
 
-
 void CSettingsView::on_comboBoxAlliedPlayerAI_currentTextChanged(const QString &arg1)
 {
 	Settings node = settings.write["server"]["alliedAI"];
 	node->String() = arg1.toUtf8().data();
 }
 
-
 void CSettingsView::on_checkBoxAutoSavePrefix_stateChanged(int arg1)
 {
     Settings node = settings.write["general"]["useSavePrefix"];
@@ -515,17 +513,21 @@ void CSettingsView::on_checkBoxAutoSavePrefix_stateChanged(int arg1)
     ui->lineEditAutoSavePrefix->setEnabled(arg1);
 }
 
-
 void CSettingsView::on_spinBoxAutoSaveLimit_valueChanged(int arg1)
 {
     Settings node = settings.write["general"]["autosaveCountLimit"];
     node->Float() = arg1;
 }
 
-
 void CSettingsView::on_lineEditAutoSavePrefix_textEdited(const QString &arg1)
 {
     Settings node = settings.write["general"]["savePrefix"];
     node->String() = arg1.toStdString();
 }
 
+void CSettingsView::on_spinBoxReservedArea_valueChanged(int arg1)
+{
+	Settings node = settings.write["video"]["reservedWidth"];
+	node->Float() = float(arg1) / 100; // percentage -> ratio
+}
+

+ 2 - 0
launcher/settingsView/csettingsview_moc.h

@@ -72,6 +72,8 @@ private slots:
 
     void on_lineEditAutoSavePrefix_textEdited(const QString &arg1);
 
+	void on_spinBoxReservedArea_valueChanged(int arg1);
+
 private:
 	Ui::CSettingsView * ui;
 

+ 284 - 253
launcher/settingsView/csettingsview_moc.ui

@@ -42,6 +42,7 @@
      </property>
      <property name="font">
       <font>
+       <weight>75</weight>
        <bold>true</bold>
       </font>
      </property>
@@ -106,306 +107,261 @@
       <property name="geometry">
        <rect>
         <x>0</x>
-        <y>0</y>
+        <y>-197</y>
         <width>610</width>
-        <height>790</height>
+        <height>768</height>
        </rect>
       </property>
       <layout class="QGridLayout" name="gridLayout" columnstretch="2,0,1,1,1">
-       <item row="18" column="0">
-        <widget class="QLabel" name="labelArtificialIntelligence">
-         <property name="font">
-          <font>
-           <bold>true</bold>
-          </font>
-         </property>
-         <property name="text">
-          <string>Artificial Intelligence</string>
-         </property>
-        </widget>
-       </item>
-       <item row="26" column="2" colspan="3">
-        <widget class="QLineEdit" name="lineEditRepositoryDefault">
+       <item row="5" column="1" colspan="3">
+        <widget class="QLabel" name="labelTranslationStatus">
          <property name="text">
-          <string notr="true"/>
-         </property>
-         <property name="readOnly">
-          <bool>true</bool>
+          <string/>
          </property>
         </widget>
        </item>
-       <item row="26" column="1">
-        <widget class="QCheckBox" name="checkBoxRepositoryDefault">
-         <property name="enabled">
-          <bool>true</bool>
-         </property>
-         <property name="text">
-          <string/>
+       <item row="23" column="1" colspan="4">
+        <widget class="QComboBox" name="comboBoxFriendlyAI">
+         <property name="editable">
+          <bool>false</bool>
          </property>
-         <property name="checked">
-          <bool>true</bool>
+         <property name="currentText">
+          <string notr="true">BattleAI</string>
          </property>
+         <item>
+          <property name="text">
+           <string notr="true">BattleAI</string>
+          </property>
+         </item>
+         <item>
+          <property name="text">
+           <string notr="true">StupidAI</string>
+          </property>
+         </item>
         </widget>
        </item>
-       <item row="27" column="1">
-        <widget class="QCheckBox" name="checkBoxRepositoryExtra">
+       <item row="17" column="0">
+        <widget class="QLabel" name="labelDisplayIndex">
          <property name="text">
-          <string/>
-         </property>
-         <property name="checked">
-          <bool>true</bool>
+          <string>Display index</string>
          </property>
         </widget>
        </item>
-       <item row="12" column="1" colspan="4">
-        <widget class="QComboBox" name="comboBoxFullScreen">
-         <property name="toolTip">
-          <string>Select display mode for game
-
-Windowed - game will run inside a window that covers part of your screen
-
-Borderless Windowed Mode - game will run in a window that covers entirely of your screen, using same resolution as your screen.
-
-Fullscreen Exclusive Mode - game will cover entirety of your screen and will use selected resolution.</string>
-         </property>
-         <property name="currentIndex">
-          <number>0</number>
+       <item row="21" column="1" colspan="4">
+        <widget class="QComboBox" name="comboBoxAlliedPlayerAI">
+         <property name="currentText">
+          <string notr="true">VCAI</string>
          </property>
          <item>
           <property name="text">
-           <string>Windowed</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string>Borderless fullscreen</string>
+           <string notr="true">VCAI</string>
           </property>
          </item>
          <item>
           <property name="text">
-           <string>Exclusive fullscreen</string>
+           <string notr="true">Nullkiller</string>
           </property>
          </item>
         </widget>
        </item>
-       <item row="11" column="0">
-        <widget class="QLabel" name="labelResolution">
+       <item row="8" column="0">
+        <widget class="QLabel" name="labelAutoSaveLimit">
          <property name="text">
-          <string>Resolution</string>
+          <string>Autosave limit (0 = off)</string>
          </property>
         </widget>
        </item>
-       <item row="20" column="0">
-        <widget class="QLabel" name="labelAlliedPlayerAI">
+       <item row="12" column="0">
+        <widget class="QLabel" name="labelFullScreen">
          <property name="text">
-          <string>Adventure Map Allies</string>
+          <string>Fullscreen</string>
          </property>
         </widget>
        </item>
-       <item row="23" column="1" colspan="4">
-        <widget class="QComboBox" name="comboBoxEnemyAI">
-         <property name="editable">
-          <bool>false</bool>
-         </property>
-         <property name="currentText">
-          <string notr="true">BattleAI</string>
+       <item row="6" column="0">
+        <widget class="QLabel" name="labelNetworkPort">
+         <property name="text">
+          <string>Network port</string>
          </property>
-         <item>
-          <property name="text">
-           <string notr="true">BattleAI</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string notr="true">StupidAI</string>
-          </property>
-         </item>
         </widget>
        </item>
-       <item row="24" column="0">
-        <widget class="QLabel" name="labelRepositories">
+       <item row="0" column="0">
+        <widget class="QLabel" name="labelGeneral">
          <property name="font">
           <font>
+           <weight>75</weight>
            <bold>true</bold>
           </font>
          </property>
          <property name="text">
-          <string>Mod Repositories</string>
+          <string>General</string>
          </property>
         </widget>
        </item>
-       <item row="25" column="1" colspan="2">
-        <widget class="QComboBox" name="comboBoxAutoCheck">
-         <property name="currentIndex">
-          <number>1</number>
+       <item row="7" column="0">
+        <widget class="QLabel" name="labelAutoSave">
+         <property name="text">
+          <string>Autosave</string>
          </property>
+        </widget>
+       </item>
+       <item row="18" column="1" colspan="4">
+        <widget class="QComboBox" name="comboBoxCursorType">
          <item>
           <property name="text">
-           <string>Off</string>
+           <string>Hardware</string>
           </property>
          </item>
          <item>
           <property name="text">
-           <string>On</string>
+           <string>Software</string>
           </property>
          </item>
         </widget>
        </item>
-       <item row="5" column="1" colspan="3">
-        <widget class="QLabel" name="labelTranslationStatus">
+       <item row="16" column="0">
+        <widget class="QLabel" name="labelShowIntro">
          <property name="text">
-          <string/>
+          <string>Show intro</string>
          </property>
         </widget>
        </item>
-       <item row="26" column="0">
-        <widget class="QLabel" name="labelRepositoryDefault">
+       <item row="28" column="1">
+        <widget class="QCheckBox" name="checkBoxRepositoryExtra">
          <property name="text">
-          <string>Default repository</string>
+          <string/>
          </property>
-        </widget>
-       </item>
-       <item row="27" column="0">
-        <widget class="QLabel" name="labelRepositoryExtra">
-         <property name="text">
-          <string>Additional repository</string>
+         <property name="checked">
+          <bool>true</bool>
          </property>
         </widget>
        </item>
-       <item row="21" column="0">
-        <widget class="QLabel" name="labelNeutralAI">
-         <property name="text">
-          <string>Neutral AI in battles</string>
+       <item row="27" column="1">
+        <widget class="QCheckBox" name="checkBoxRepositoryDefault">
+         <property name="enabled">
+          <bool>true</bool>
          </property>
-        </widget>
-       </item>
-       <item row="5" column="4">
-        <widget class="QPushButton" name="pushButtonTranslation">
          <property name="text">
           <string/>
          </property>
+         <property name="checked">
+          <bool>true</bool>
+         </property>
         </widget>
        </item>
-       <item row="13" column="1" colspan="4">
-        <widget class="QSpinBox" name="spinBoxInterfaceScaling">
+       <item row="15" column="1" colspan="4">
+        <widget class="QSpinBox" name="spinBoxFramerateLimit">
          <property name="minimum">
-          <number>50</number>
+          <number>20</number>
          </property>
          <property name="maximum">
-          <number>400</number>
+          <number>1000</number>
          </property>
          <property name="singleStep">
           <number>10</number>
          </property>
         </widget>
        </item>
-       <item row="13" column="0">
-        <widget class="QLabel" name="labelInterfaceScaling">
-         <property name="text">
-          <string>Interface Scaling</string>
-         </property>
-        </widget>
-       </item>
-       <item row="4" column="0">
-        <widget class="QLabel" name="labelLanguageBase">
-         <property name="text">
-          <string>Heroes III Data Language</string>
-         </property>
-        </widget>
+       <item row="17" column="1" colspan="4">
+        <widget class="QComboBox" name="comboBoxDisplayIndex"/>
        </item>
-       <item row="8" column="0">
-        <widget class="QLabel" name="labelAutoSaveLimit">
+       <item row="15" column="0">
+        <widget class="QLabel" name="labelFramerateLimit">
          <property name="text">
-          <string>Autosave limit (0 = off)</string>
+          <string>Framerate Limit</string>
          </property>
         </widget>
        </item>
        <item row="4" column="1" colspan="4">
         <widget class="QComboBox" name="comboBoxLanguageBase"/>
        </item>
-       <item row="22" column="0">
-        <widget class="QLabel" name="labelFriendlyAI">
+       <item row="20" column="0">
+        <widget class="QLabel" name="labelEnemyPlayerAI">
          <property name="text">
-          <string>Friendly AI in battles</string>
+          <string>Adventure Map Enemies</string>
          </property>
         </widget>
        </item>
-       <item row="22" column="1" colspan="4">
-        <widget class="QComboBox" name="comboBoxFriendlyAI">
-         <property name="editable">
-          <bool>false</bool>
-         </property>
-         <property name="currentText">
-          <string notr="true">BattleAI</string>
+       <item row="27" column="0">
+        <widget class="QLabel" name="labelRepositoryDefault">
+         <property name="text">
+          <string>Default repository</string>
          </property>
-         <item>
-          <property name="text">
-           <string notr="true">BattleAI</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string notr="true">StupidAI</string>
-          </property>
-         </item>
         </widget>
        </item>
        <item row="11" column="1" colspan="4">
         <widget class="QComboBox" name="comboBoxResolution"/>
        </item>
-       <item row="20" column="1" colspan="4">
-        <widget class="QComboBox" name="comboBoxAlliedPlayerAI">
-         <property name="currentText">
-          <string notr="true">VCAI</string>
+       <item row="7" column="1" colspan="4">
+        <widget class="QComboBox" name="comboBoxAutoSave">
+         <property name="currentIndex">
+          <number>1</number>
          </property>
          <item>
           <property name="text">
-           <string notr="true">VCAI</string>
+           <string>Off</string>
           </property>
          </item>
          <item>
           <property name="text">
-           <string notr="true">Nullkiller</string>
+           <string>On</string>
           </property>
          </item>
         </widget>
        </item>
-       <item row="16" column="0">
-        <widget class="QLabel" name="labelDisplayIndex">
+       <item row="10" column="0">
+        <widget class="QLabel" name="labelVideo">
+         <property name="font">
+          <font>
+           <weight>75</weight>
+           <bold>true</bold>
+          </font>
+         </property>
          <property name="text">
-          <string>Display index</string>
+          <string>Video</string>
          </property>
         </widget>
        </item>
-       <item row="19" column="0">
-        <widget class="QLabel" name="labelEnemyPlayerAI">
+       <item row="9" column="0">
+        <widget class="QLabel" name="labelAutoSavePrefix">
          <property name="text">
-          <string>Adventure Map Enemies</string>
+          <string>Autosave prefix</string>
          </property>
         </widget>
        </item>
-       <item row="14" column="1" colspan="4">
-        <widget class="QSpinBox" name="spinBoxFramerateLimit">
-         <property name="minimum">
-          <number>20</number>
+       <item row="11" column="0">
+        <widget class="QLabel" name="labelResolution">
+         <property name="text">
+          <string>Resolution</string>
          </property>
-         <property name="maximum">
-          <number>1000</number>
+        </widget>
+       </item>
+       <item row="28" column="2" colspan="3">
+        <widget class="QLineEdit" name="lineEditRepositoryExtra">
+         <property name="text">
+          <string notr="true"/>
          </property>
-         <property name="singleStep">
-          <number>10</number>
+        </widget>
+       </item>
+       <item row="27" column="2" colspan="3">
+        <widget class="QLineEdit" name="lineEditRepositoryDefault">
+         <property name="text">
+          <string notr="true"/>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
          </property>
         </widget>
        </item>
-       <item row="15" column="0">
-        <widget class="QLabel" name="labelShowIntro">
+       <item row="18" column="0">
+        <widget class="QLabel" name="labelCursorType">
          <property name="text">
-          <string>Show intro</string>
+          <string>Cursor</string>
          </property>
         </widget>
        </item>
-       <item row="7" column="1" colspan="4">
-        <widget class="QComboBox" name="comboBoxAutoSave">
+       <item row="26" column="1" colspan="2">
+        <widget class="QComboBox" name="comboBoxAutoCheck">
          <property name="currentIndex">
           <number>1</number>
          </property>
@@ -421,45 +377,45 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
          </item>
         </widget>
        </item>
-       <item row="1" column="1" colspan="4">
-        <widget class="QComboBox" name="comboBoxLanguage"/>
-       </item>
-       <item row="17" column="1" colspan="4">
-        <widget class="QComboBox" name="comboBoxCursorType">
-         <item>
-          <property name="text">
-           <string>Hardware</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string>Software</string>
-          </property>
-         </item>
+       <item row="26" column="3" colspan="2">
+        <widget class="QPushButton" name="refreshRepositoriesButton">
+         <property name="text">
+          <string>Refresh now</string>
+         </property>
         </widget>
        </item>
-       <item row="12" column="0">
-        <widget class="QLabel" name="labelFullScreen">
+       <item row="5" column="4">
+        <widget class="QPushButton" name="pushButtonTranslation">
          <property name="text">
-          <string>Fullscreen</string>
+          <string/>
          </property>
         </widget>
        </item>
-       <item row="17" column="0">
-        <widget class="QLabel" name="labelCursorType">
+       <item row="5" column="0">
+        <widget class="QLabel" name="labelTranslation">
          <property name="text">
-          <string>Cursor</string>
+          <string>Heroes III Translation</string>
          </property>
         </widget>
        </item>
-       <item row="27" column="2" colspan="3">
-        <widget class="QLineEdit" name="lineEditRepositoryExtra">
-         <property name="text">
-          <string notr="true"/>
+       <item row="16" column="1" colspan="4">
+        <widget class="QComboBox" name="comboBoxShowIntro">
+         <property name="currentIndex">
+          <number>1</number>
          </property>
+         <item>
+          <property name="text">
+           <string>Off</string>
+          </property>
+         </item>
+         <item>
+          <property name="text">
+           <string>On</string>
+          </property>
+         </item>
         </widget>
        </item>
-       <item row="23" column="0">
+       <item row="24" column="0">
         <widget class="QLabel" name="labelEnemyAI">
          <property name="text">
           <string>Enemy AI in battles</string>
@@ -467,26 +423,30 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
         </widget>
        </item>
        <item row="25" column="0">
-        <widget class="QLabel" name="labelAutoCheck">
-         <property name="text">
-          <string>Check on startup</string>
-         </property>
-        </widget>
-       </item>
-       <item row="0" column="0">
-        <widget class="QLabel" name="labelGeneral">
+        <widget class="QLabel" name="labelRepositories">
          <property name="font">
           <font>
+           <weight>75</weight>
            <bold>true</bold>
           </font>
          </property>
          <property name="text">
-          <string>General</string>
+          <string>Mod Repositories</string>
          </property>
         </widget>
        </item>
-       <item row="21" column="1" colspan="4">
-        <widget class="QComboBox" name="comboBoxNeutralAI">
+       <item row="28" column="0">
+        <widget class="QLabel" name="labelRepositoryExtra">
+         <property name="text">
+          <string>Additional repository</string>
+         </property>
+        </widget>
+       </item>
+       <item row="24" column="1" colspan="4">
+        <widget class="QComboBox" name="comboBoxEnemyAI">
+         <property name="editable">
+          <bool>false</bool>
+         </property>
          <property name="currentText">
           <string notr="true">BattleAI</string>
          </property>
@@ -502,10 +462,40 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
          </item>
         </widget>
        </item>
-       <item row="25" column="3" colspan="2">
-        <widget class="QPushButton" name="refreshRepositoriesButton">
+       <item row="14" column="1" colspan="4">
+        <widget class="QSpinBox" name="spinBoxInterfaceScaling">
+         <property name="minimum">
+          <number>50</number>
+         </property>
+         <property name="maximum">
+          <number>400</number>
+         </property>
+         <property name="singleStep">
+          <number>10</number>
+         </property>
+        </widget>
+       </item>
+       <item row="4" column="0">
+        <widget class="QLabel" name="labelLanguageBase">
          <property name="text">
-          <string>Refresh now</string>
+          <string>Heroes III Data Language</string>
+         </property>
+        </widget>
+       </item>
+       <item row="8" column="1" colspan="4">
+        <widget class="QSpinBox" name="spinBoxAutoSaveLimit"/>
+       </item>
+       <item row="22" column="0">
+        <widget class="QLabel" name="labelNeutralAI">
+         <property name="text">
+          <string>Neutral AI in battles</string>
+         </property>
+        </widget>
+       </item>
+       <item row="14" column="0">
+        <widget class="QLabel" name="labelInterfaceScaling">
+         <property name="text">
+          <string>Interface Scaling</string>
          </property>
         </widget>
        </item>
@@ -522,17 +512,17 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
          </property>
         </widget>
        </item>
-       <item row="6" column="0">
-        <widget class="QLabel" name="labelNetworkPort">
+       <item row="9" column="1">
+        <widget class="QCheckBox" name="checkBoxAutoSavePrefix">
          <property name="text">
-          <string>Network port</string>
+          <string/>
          </property>
         </widget>
        </item>
-       <item row="5" column="0">
-        <widget class="QLabel" name="labelTranslation">
+       <item row="23" column="0">
+        <widget class="QLabel" name="labelFriendlyAI">
          <property name="text">
-          <string>Heroes III Translation</string>
+          <string>Friendly AI in battles</string>
          </property>
         </widget>
        </item>
@@ -543,26 +533,54 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
          </property>
         </widget>
        </item>
-       <item row="7" column="0">
-        <widget class="QLabel" name="labelAutoSave">
+       <item row="1" column="1" colspan="4">
+        <widget class="QComboBox" name="comboBoxLanguage"/>
+       </item>
+       <item row="26" column="0">
+        <widget class="QLabel" name="labelAutoCheck">
          <property name="text">
-          <string>Autosave</string>
+          <string>Check on startup</string>
          </property>
         </widget>
        </item>
-       <item row="10" column="0">
-        <widget class="QLabel" name="labelVideo">
+       <item row="22" column="1" colspan="4">
+        <widget class="QComboBox" name="comboBoxNeutralAI">
+         <property name="currentText">
+          <string notr="true">BattleAI</string>
+         </property>
+         <item>
+          <property name="text">
+           <string notr="true">BattleAI</string>
+          </property>
+         </item>
+         <item>
+          <property name="text">
+           <string notr="true">StupidAI</string>
+          </property>
+         </item>
+        </widget>
+       </item>
+       <item row="21" column="0">
+        <widget class="QLabel" name="labelAlliedPlayerAI">
+         <property name="text">
+          <string>Adventure Map Allies</string>
+         </property>
+        </widget>
+       </item>
+       <item row="19" column="0">
+        <widget class="QLabel" name="labelArtificialIntelligence">
          <property name="font">
           <font>
+           <weight>75</weight>
            <bold>true</bold>
           </font>
          </property>
          <property name="text">
-          <string>Video</string>
+          <string>Artificial Intelligence</string>
          </property>
         </widget>
        </item>
-       <item row="19" column="1" colspan="4">
+       <item row="20" column="1" colspan="4">
         <widget class="QComboBox" name="comboBoxEnemyPlayerAI">
          <property name="currentText">
           <string notr="true">VCAI</string>
@@ -579,54 +597,67 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
          </item>
         </widget>
        </item>
-       <item row="16" column="1" colspan="4">
-        <widget class="QComboBox" name="comboBoxDisplayIndex"/>
-       </item>
-       <item row="14" column="0">
-        <widget class="QLabel" name="labelFramerateLimit">
-         <property name="text">
-          <string>Framerate Limit</string>
+       <item row="9" column="2" colspan="3">
+        <widget class="QLineEdit" name="lineEditAutoSavePrefix">
+         <property name="placeholderText">
+          <string>empty = map name prefix</string>
          </property>
         </widget>
        </item>
-       <item row="15" column="1" colspan="4">
-        <widget class="QComboBox" name="comboBoxShowIntro">
+       <item row="12" column="1" colspan="4">
+        <widget class="QComboBox" name="comboBoxFullScreen">
+         <property name="toolTip">
+          <string>Select display mode for game
+
+Windowed - game will run inside a window that covers part of your screen
+
+Borderless Windowed Mode - game will run in a window that covers entirely of your screen, using same resolution as your screen.
+
+Fullscreen Exclusive Mode - game will cover entirety of your screen and will use selected resolution.</string>
+         </property>
          <property name="currentIndex">
-          <number>1</number>
+          <number>0</number>
          </property>
          <item>
           <property name="text">
-           <string>Off</string>
+           <string>Windowed</string>
           </property>
          </item>
          <item>
           <property name="text">
-           <string>On</string>
+           <string>Borderless fullscreen</string>
+          </property>
+         </item>
+         <item>
+          <property name="text">
+           <string>Exclusive fullscreen</string>
           </property>
          </item>
         </widget>
        </item>
-       <item row="9" column="0">
-        <widget class="QLabel" name="labelAutoSavePrefix">
+       <item row="13" column="0">
+        <widget class="QLabel" name="labelReservedArea">
          <property name="text">
-          <string>Autosave prefix</string>
+          <string>Reserved screen area</string>
          </property>
         </widget>
        </item>
-       <item row="8" column="1" colspan="4">
-        <widget class="QSpinBox" name="spinBoxAutoSaveLimit"/>
-       </item>
-       <item row="9" column="1">
-        <widget class="QCheckBox" name="checkBoxAutoSavePrefix">
-         <property name="text">
-          <string/>
+       <item row="13" column="1" colspan="4">
+        <widget class="QSpinBox" name="spinBoxReservedArea">
+         <property name="suffix">
+          <string notr="true">%</string>
          </property>
-        </widget>
-       </item>
-       <item row="9" column="2" colspan="3">
-        <widget class="QLineEdit" name="lineEditAutoSavePrefix">
-         <property name="placeholderText">
-          <string>empty = map name prefix</string>
+         <property name="minimum">
+          <number>0</number>
+         </property>
+         <property name="maximum">
+          <number>25</number>
+         </property>
+         <property name="singleStep">
+          <number>1</number>
+         </property>
+         <property name="value">
+          <number>0</number>
          </property>
         </widget>
        </item>

+ 54 - 49
launcher/translation/chinese.ts

@@ -407,123 +407,123 @@
 <context>
     <name>CSettingsView</name>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="244"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="414"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="599"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="302"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="370"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="408"/>
         <source>Off</source>
         <translation>关闭</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="78"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="123"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="79"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="579"/>
         <source>Artificial Intelligence</source>
         <translation>人工智能</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="83"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="233"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="84"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="434"/>
         <source>Mod Repositories</source>
         <translation>模组仓库</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="305"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="498"/>
         <source>Interface Scaling</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="278"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="491"/>
         <source>Neutral AI in battles</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="465"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="421"/>
         <source>Enemy AI in battles</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="271"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="441"/>
         <source>Additional repository</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="201"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="566"/>
         <source>Adventure Map Allies</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="383"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="281"/>
         <source>Adventure Map Enemies</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="176"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="623"/>
         <source>Windowed</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="181"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="628"/>
         <source>Borderless fullscreen</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="186"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="633"/>
         <source>Exclusive fullscreen</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="319"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="170"/>
         <source>Autosave limit (0 = off)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="329"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="525"/>
         <source>Friendly AI in battles</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="588"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="271"/>
         <source>Framerate Limit</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="612"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="328"/>
         <source>Autosave prefix</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="629"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="603"/>
         <source>empty = map name prefix</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="508"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="383"/>
         <source>Refresh now</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="264"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="288"/>
         <source>Default repository</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="249"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="419"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="604"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="307"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="375"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="413"/>
         <source>On</source>
         <translation>开启</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="451"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="359"/>
         <source>Cursor</source>
         <translation>鼠标指针</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="312"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="481"/>
         <source>Heroes III Data Language</source>
         <translation>英雄无敌3数据语言</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="163"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="610"/>
         <source>Select display mode for game
 
 Windowed - game will run inside a window that covers part of your screen
@@ -534,95 +534,100 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="431"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="641"/>
+        <source>Reserved screen area</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="212"/>
         <source>Hardware</source>
         <translation>硬件</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="436"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="217"/>
         <source>Software</source>
         <translation>软件</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="535"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="397"/>
         <source>Heroes III Translation</source>
         <translatorcomment>发布版本里找不到这个项,不太清楚意义</translatorcomment>
         <translation>英雄无敌3翻译</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="472"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="542"/>
         <source>Check on startup</source>
         <translation>启动时检查更新</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="444"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="177"/>
         <source>Fullscreen</source>
         <translation>全屏</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="68"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="484"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="69"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="197"/>
         <source>General</source>
         <translation>通用设置</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="542"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="532"/>
         <source>VCMI Language</source>
         <translation>VCMI语言</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="194"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="335"/>
         <source>Resolution</source>
         <translation>分辨率</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="549"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="204"/>
         <source>Autosave</source>
         <translation>自动存档</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="376"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="146"/>
         <source>Display index</source>
         <translation>显示器序号</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="528"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="184"/>
         <source>Network port</source>
         <translation>网络端口</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="73"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="561"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="74"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="321"/>
         <source>Video</source>
         <translation>视频设置</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="403"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="225"/>
         <source>Show intro</source>
         <translation>显示开场动画</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="402"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="405"/>
         <source>Active</source>
         <translation>激活</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="407"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="410"/>
         <source>Disabled</source>
         <translation>禁用</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="408"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="411"/>
         <source>Enable</source>
         <translation>启用</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="413"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="416"/>
         <source>Not Installed</source>
         <translation>未安装</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="414"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="417"/>
         <source>Install</source>
         <translation>安装</translation>
     </message>

+ 54 - 49
launcher/translation/english.ts

@@ -406,123 +406,123 @@
 <context>
     <name>CSettingsView</name>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="244"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="414"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="599"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="302"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="370"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="408"/>
         <source>Off</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="78"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="123"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="79"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="579"/>
         <source>Artificial Intelligence</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="83"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="233"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="84"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="434"/>
         <source>Mod Repositories</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="305"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="498"/>
         <source>Interface Scaling</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="278"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="491"/>
         <source>Neutral AI in battles</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="465"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="421"/>
         <source>Enemy AI in battles</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="271"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="441"/>
         <source>Additional repository</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="201"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="566"/>
         <source>Adventure Map Allies</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="383"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="281"/>
         <source>Adventure Map Enemies</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="176"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="623"/>
         <source>Windowed</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="181"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="628"/>
         <source>Borderless fullscreen</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="186"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="633"/>
         <source>Exclusive fullscreen</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="319"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="170"/>
         <source>Autosave limit (0 = off)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="329"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="525"/>
         <source>Friendly AI in battles</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="588"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="271"/>
         <source>Framerate Limit</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="612"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="328"/>
         <source>Autosave prefix</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="629"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="603"/>
         <source>empty = map name prefix</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="508"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="383"/>
         <source>Refresh now</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="264"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="288"/>
         <source>Default repository</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="249"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="419"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="604"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="307"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="375"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="413"/>
         <source>On</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="451"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="359"/>
         <source>Cursor</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="312"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="481"/>
         <source>Heroes III Data Language</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="163"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="610"/>
         <source>Select display mode for game
 
 Windowed - game will run inside a window that covers part of your screen
@@ -533,94 +533,99 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="431"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="641"/>
+        <source>Reserved screen area</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="212"/>
         <source>Hardware</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="436"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="217"/>
         <source>Software</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="535"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="397"/>
         <source>Heroes III Translation</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="472"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="542"/>
         <source>Check on startup</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="444"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="177"/>
         <source>Fullscreen</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="68"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="484"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="69"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="197"/>
         <source>General</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="542"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="532"/>
         <source>VCMI Language</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="194"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="335"/>
         <source>Resolution</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="549"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="204"/>
         <source>Autosave</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="376"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="146"/>
         <source>Display index</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="528"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="184"/>
         <source>Network port</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="73"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="561"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="74"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="321"/>
         <source>Video</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="403"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="225"/>
         <source>Show intro</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="402"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="405"/>
         <source>Active</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="407"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="410"/>
         <source>Disabled</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="408"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="411"/>
         <source>Enable</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="413"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="416"/>
         <source>Not Installed</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="414"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="417"/>
         <source>Install</source>
         <translation type="unfinished"></translation>
     </message>

+ 54 - 49
launcher/translation/french.ts

@@ -411,43 +411,43 @@
 <context>
     <name>CSettingsView</name>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="244"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="414"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="599"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="302"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="370"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="408"/>
         <source>Off</source>
         <translation>Désactivé</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="78"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="123"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="79"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="579"/>
         <source>Artificial Intelligence</source>
         <translation>Intelligence Artificielle</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="83"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="233"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="84"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="434"/>
         <source>Mod Repositories</source>
         <translation>Dépôts de Mod</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="249"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="419"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="604"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="307"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="375"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="413"/>
         <source>On</source>
         <translation>Activé</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="465"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="421"/>
         <source>Enemy AI in battles</source>
         <translation>IA ennemie dans les batailles</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="264"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="288"/>
         <source>Default repository</source>
         <translation>Dépôt par défaut</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="163"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="610"/>
         <source>Select display mode for game
 
 Windowed - game will run inside a window that covers part of your screen
@@ -464,174 +464,179 @@ Mode fenêtré sans bord - le jeu s&quot;exécutera dans une fenêtre qui couvre
 Mode exclusif plein écran - le jeu couvrira l&quot;intégralité de votre écran et utilisera la résolution sélectionnée.</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="176"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="623"/>
         <source>Windowed</source>
         <translation>Fenêtré</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="181"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="628"/>
         <source>Borderless fullscreen</source>
         <translation>Fenêtré sans bord</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="186"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="633"/>
         <source>Exclusive fullscreen</source>
         <translation>Plein écran exclusif</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="278"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="641"/>
+        <source>Reserved screen area</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="491"/>
         <source>Neutral AI in battles</source>
         <translation>IA neutre dans les batailles</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="319"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="170"/>
         <source>Autosave limit (0 = off)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="383"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="281"/>
         <source>Adventure Map Enemies</source>
         <translation>Ennemis de la carte d&quot;aventure</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="612"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="328"/>
         <source>Autosave prefix</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="629"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="603"/>
         <source>empty = map name prefix</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="305"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="498"/>
         <source>Interface Scaling</source>
         <translation>Mise à l&quot;échelle de l&quot;interface</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="451"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="359"/>
         <source>Cursor</source>
         <translation>Curseur</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="312"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="481"/>
         <source>Heroes III Data Language</source>
         <translation>Langue des Données de Heroes III</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="588"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="271"/>
         <source>Framerate Limit</source>
         <translation>Limite de fréquence d&quot;images</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="431"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="212"/>
         <source>Hardware</source>
         <translation>Matériel</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="436"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="217"/>
         <source>Software</source>
         <translation>Logiciel</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="535"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="397"/>
         <source>Heroes III Translation</source>
         <translation>Traduction de Heroes III</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="201"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="566"/>
         <source>Adventure Map Allies</source>
         <translation>Alliés de la carte d&quot;aventure</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="271"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="441"/>
         <source>Additional repository</source>
         <translation>Dépôt supplémentaire</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="472"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="542"/>
         <source>Check on startup</source>
         <translation>Vérifier au démarrage</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="508"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="383"/>
         <source>Refresh now</source>
         <translation>Actualiser maintenant</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="329"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="525"/>
         <source>Friendly AI in battles</source>
         <translation>IA amicale dans les batailles</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="444"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="177"/>
         <source>Fullscreen</source>
         <translation>Plein écran</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="68"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="484"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="69"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="197"/>
         <source>General</source>
         <translation>Général</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="542"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="532"/>
         <source>VCMI Language</source>
         <translation>Langue de VCMI</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="194"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="335"/>
         <source>Resolution</source>
         <translation>Résolution</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="549"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="204"/>
         <source>Autosave</source>
         <translation>Sauvegarde automatique</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="376"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="146"/>
         <source>Display index</source>
         <translation>Index d&apos;affichage</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="528"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="184"/>
         <source>Network port</source>
         <translation>Port de réseau</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="73"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="561"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="74"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="321"/>
         <source>Video</source>
         <translation>Vidéo</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="403"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="225"/>
         <source>Show intro</source>
         <translation>Montrer l&apos;intro</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="402"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="405"/>
         <source>Active</source>
         <translation>Actif</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="407"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="410"/>
         <source>Disabled</source>
         <translation>Désactivé</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="408"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="411"/>
         <source>Enable</source>
         <translation>Activé</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="413"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="416"/>
         <source>Not Installed</source>
         <translation>Pas Installé</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="414"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="417"/>
         <source>Install</source>
         <translation>Installer</translation>
     </message>

+ 54 - 49
launcher/translation/german.ts

@@ -406,123 +406,123 @@
 <context>
     <name>CSettingsView</name>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="244"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="414"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="599"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="302"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="370"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="408"/>
         <source>Off</source>
         <translation>Aus</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="78"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="123"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="79"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="579"/>
         <source>Artificial Intelligence</source>
         <translation>Künstliche Intelligenz</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="83"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="233"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="84"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="434"/>
         <source>Mod Repositories</source>
         <translation>Mod-Repositorien</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="305"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="498"/>
         <source>Interface Scaling</source>
         <translation>Skalierung der Benutzeroberfläche</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="278"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="491"/>
         <source>Neutral AI in battles</source>
         <translation>Neutrale KI in Kämpfen</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="465"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="421"/>
         <source>Enemy AI in battles</source>
         <translation>Gegnerische KI in Kämpfen</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="271"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="441"/>
         <source>Additional repository</source>
         <translation>Zusätzliches Repository</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="201"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="566"/>
         <source>Adventure Map Allies</source>
         <translation>Abenteuerkarte Verbündete</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="383"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="281"/>
         <source>Adventure Map Enemies</source>
         <translation>Abenteuerkarte Feinde</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="176"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="623"/>
         <source>Windowed</source>
         <translation>Fenstermodus</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="181"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="628"/>
         <source>Borderless fullscreen</source>
         <translation>Randloser Vollbildmodus</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="186"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="633"/>
         <source>Exclusive fullscreen</source>
         <translation>Exklusiver Vollbildmodus</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="319"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="170"/>
         <source>Autosave limit (0 = off)</source>
         <translation>Limit für Autospeicherung (0 = aus)</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="329"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="525"/>
         <source>Friendly AI in battles</source>
         <translation>Freundliche KI in Kämpfen</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="588"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="271"/>
         <source>Framerate Limit</source>
         <translation>Limit der Bildrate</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="612"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="328"/>
         <source>Autosave prefix</source>
         <translation>Präfix für Autospeicherung</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="629"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="603"/>
         <source>empty = map name prefix</source>
         <translation>leer = Kartenname als Präfix</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="508"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="383"/>
         <source>Refresh now</source>
         <translation>Jetzt aktualisieren</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="264"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="288"/>
         <source>Default repository</source>
         <translation>Standard Repository</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="249"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="419"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="604"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="307"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="375"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="413"/>
         <source>On</source>
         <translation>An</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="451"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="359"/>
         <source>Cursor</source>
         <translation>Zeiger</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="312"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="481"/>
         <source>Heroes III Data Language</source>
         <translation>Sprache der Heroes III Daten</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="163"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="610"/>
         <source>Select display mode for game
 
 Windowed - game will run inside a window that covers part of your screen
@@ -539,94 +539,99 @@ Randloser Fenstermodus - das Spiel läuft in einem Fenster, das den gesamten Bil
 Exklusiver Vollbildmodus - das Spiel bedeckt den gesamten Bildschirm und verwendet die gewählte Auflösung.</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="431"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="641"/>
+        <source>Reserved screen area</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="212"/>
         <source>Hardware</source>
         <translation>Hardware</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="436"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="217"/>
         <source>Software</source>
         <translation>Software</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="535"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="397"/>
         <source>Heroes III Translation</source>
         <translation>Heroes III Übersetzung</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="472"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="542"/>
         <source>Check on startup</source>
         <translation>Beim Start prüfen</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="444"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="177"/>
         <source>Fullscreen</source>
         <translation>Vollbild</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="68"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="484"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="69"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="197"/>
         <source>General</source>
         <translation>Allgemein</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="542"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="532"/>
         <source>VCMI Language</source>
         <translation>VCMI-Sprache</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="194"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="335"/>
         <source>Resolution</source>
         <translation>Auflösung</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="549"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="204"/>
         <source>Autosave</source>
         <translation>Autospeichern</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="376"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="146"/>
         <source>Display index</source>
         <translation>Anzeige-Index</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="528"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="184"/>
         <source>Network port</source>
         <translation>Netzwerk-Port</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="73"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="561"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="74"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="321"/>
         <source>Video</source>
         <translation>Video</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="403"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="225"/>
         <source>Show intro</source>
         <translation>Intro anzeigen</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="402"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="405"/>
         <source>Active</source>
         <translation>Aktiv</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="407"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="410"/>
         <source>Disabled</source>
         <translation>Deaktiviert</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="408"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="411"/>
         <source>Enable</source>
         <translation>Aktivieren</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="413"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="416"/>
         <source>Not Installed</source>
         <translation>Nicht installiert</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="414"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="417"/>
         <source>Install</source>
         <translation>Installieren</translation>
     </message>

+ 54 - 49
launcher/translation/polish.ts

@@ -406,123 +406,123 @@
 <context>
     <name>CSettingsView</name>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="244"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="414"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="599"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="302"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="370"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="408"/>
         <source>Off</source>
         <translation>Wyłączony</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="78"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="123"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="79"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="579"/>
         <source>Artificial Intelligence</source>
         <translation>Sztuczna Inteligencja</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="83"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="233"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="84"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="434"/>
         <source>Mod Repositories</source>
         <translation>Repozytoria modów</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="305"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="498"/>
         <source>Interface Scaling</source>
         <translation>Skala interfejsu</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="278"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="491"/>
         <source>Neutral AI in battles</source>
         <translation>AI bitewne jednostek neutralnych</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="465"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="421"/>
         <source>Enemy AI in battles</source>
         <translation>AI bitewne wrogów</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="271"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="441"/>
         <source>Additional repository</source>
         <translation>Dodatkowe repozytorium</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="201"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="566"/>
         <source>Adventure Map Allies</source>
         <translation>AI sojuszników mapy przygody</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="383"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="281"/>
         <source>Adventure Map Enemies</source>
         <translation>AI wrogów mapy przygody</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="176"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="623"/>
         <source>Windowed</source>
         <translation>Okno</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="181"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="628"/>
         <source>Borderless fullscreen</source>
         <translation>Pełny ekran (tryb okna)</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="186"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="633"/>
         <source>Exclusive fullscreen</source>
         <translation>Pełny ekran klasyczny</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="319"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="170"/>
         <source>Autosave limit (0 = off)</source>
         <translation>Limit autozapisów (0 = brak)</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="329"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="525"/>
         <source>Friendly AI in battles</source>
         <translation>AI bitewne sojuszników</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="588"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="271"/>
         <source>Framerate Limit</source>
         <translation>Limit FPS</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="612"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="328"/>
         <source>Autosave prefix</source>
         <translation>Przedrostek autozapisu</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="629"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="603"/>
         <source>empty = map name prefix</source>
         <translation>puste = przedrostek z nazwy mapy</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="508"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="383"/>
         <source>Refresh now</source>
         <translation>Odśwież</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="264"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="288"/>
         <source>Default repository</source>
         <translation>Domyślne repozytorium</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="249"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="419"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="604"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="307"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="375"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="413"/>
         <source>On</source>
         <translation>Włączony</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="451"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="359"/>
         <source>Cursor</source>
         <translation>Kursor</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="312"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="481"/>
         <source>Heroes III Data Language</source>
         <translation>Język plików Heroes III</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="163"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="610"/>
         <source>Select display mode for game
 
 Windowed - game will run inside a window that covers part of your screen
@@ -539,94 +539,99 @@ Pełny ekran w trybie okna - gra uruchomi się w oknie przysłaniającym cały e
 Pełny ekran klasyczny - gra przysłoni cały ekran uruchamiając się w wybranej przez ciebie rozdzielczości ekranu.</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="431"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="641"/>
+        <source>Reserved screen area</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="212"/>
         <source>Hardware</source>
         <translation>Sprzętowy</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="436"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="217"/>
         <source>Software</source>
         <translation>Programowy</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="535"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="397"/>
         <source>Heroes III Translation</source>
         <translation>Tłumaczenie Heroes III</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="472"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="542"/>
         <source>Check on startup</source>
         <translation>Sprawdzaj przy uruchomieniu</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="444"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="177"/>
         <source>Fullscreen</source>
         <translation>Pełny ekran</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="68"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="484"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="69"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="197"/>
         <source>General</source>
         <translation>Ogólne</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="542"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="532"/>
         <source>VCMI Language</source>
         <translation>Język VCMI</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="194"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="335"/>
         <source>Resolution</source>
         <translation>Rozdzielczość</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="549"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="204"/>
         <source>Autosave</source>
         <translation>Autozapis</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="376"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="146"/>
         <source>Display index</source>
         <translation>Numer wyświetlacza</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="528"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="184"/>
         <source>Network port</source>
         <translation>Port sieciowy</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="73"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="561"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="74"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="321"/>
         <source>Video</source>
         <translation>Obraz</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="403"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="225"/>
         <source>Show intro</source>
         <translation>Pokaż intro</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="402"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="405"/>
         <source>Active</source>
         <translation>Aktywny</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="407"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="410"/>
         <source>Disabled</source>
         <translation>Wyłączone</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="408"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="411"/>
         <source>Enable</source>
         <translation>Włącz</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="413"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="416"/>
         <source>Not Installed</source>
         <translation>Nie zainstalowano</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="414"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="417"/>
         <source>Install</source>
         <translation>Zainstaluj</translation>
     </message>

+ 54 - 49
launcher/translation/russian.ts

@@ -406,144 +406,149 @@
 <context>
     <name>CSettingsView</name>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="305"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="498"/>
         <source>Interface Scaling</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="244"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="414"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="599"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="302"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="370"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="408"/>
         <source>Off</source>
         <translation>Отключено</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="249"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="419"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="604"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="307"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="375"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="413"/>
         <source>On</source>
         <translation>Включено</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="278"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="491"/>
         <source>Neutral AI in battles</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="465"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="421"/>
         <source>Enemy AI in battles</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="271"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="441"/>
         <source>Additional repository</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="472"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="542"/>
         <source>Check on startup</source>
         <translation>Проверять при запуске</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="444"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="177"/>
         <source>Fullscreen</source>
         <translation>Полноэкранный режим</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="68"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="484"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="69"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="197"/>
         <source>General</source>
         <translation>Общее</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="542"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="532"/>
         <source>VCMI Language</source>
         <translation>Язык VCMI</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="451"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="359"/>
         <source>Cursor</source>
         <translation>Курсор</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="78"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="123"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="79"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="579"/>
         <source>Artificial Intelligence</source>
         <translation>Искусственный интеллект</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="83"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="233"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="84"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="434"/>
         <source>Mod Repositories</source>
         <translation>Репозитории модов</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="201"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="566"/>
         <source>Adventure Map Allies</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="508"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="383"/>
         <source>Refresh now</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="383"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="281"/>
         <source>Adventure Map Enemies</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="176"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="623"/>
         <source>Windowed</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="181"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="628"/>
         <source>Borderless fullscreen</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="186"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="633"/>
         <source>Exclusive fullscreen</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="319"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="641"/>
+        <source>Reserved screen area</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="170"/>
         <source>Autosave limit (0 = off)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="329"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="525"/>
         <source>Friendly AI in battles</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="588"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="271"/>
         <source>Framerate Limit</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="612"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="328"/>
         <source>Autosave prefix</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="629"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="603"/>
         <source>empty = map name prefix</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="264"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="288"/>
         <source>Default repository</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="312"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="481"/>
         <source>Heroes III Data Language</source>
         <translation>Язык данных Героев III</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="163"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="610"/>
         <source>Select display mode for game
 
 Windowed - game will run inside a window that covers part of your screen
@@ -554,73 +559,73 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="431"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="212"/>
         <source>Hardware</source>
         <translation>Аппаратный</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="436"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="217"/>
         <source>Software</source>
         <translation>Программный</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="535"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="397"/>
         <source>Heroes III Translation</source>
         <translation>Перевод Героев III</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="194"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="335"/>
         <source>Resolution</source>
         <translation>Разрешение экрана</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="549"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="204"/>
         <source>Autosave</source>
         <translation>Автосохранение</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="376"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="146"/>
         <source>Display index</source>
         <translation>Дисплей</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="528"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="184"/>
         <source>Network port</source>
         <translation>Сетевой порт</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="73"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="561"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="74"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="321"/>
         <source>Video</source>
         <translation>Графика</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="403"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="225"/>
         <source>Show intro</source>
         <translation>Вступление</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="402"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="405"/>
         <source>Active</source>
         <translation>Активен</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="407"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="410"/>
         <source>Disabled</source>
         <translation>Отключен</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="408"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="411"/>
         <source>Enable</source>
         <translation>Включить</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="413"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="416"/>
         <source>Not Installed</source>
         <translation>Не установлен</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="414"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="417"/>
         <source>Install</source>
         <translation>Установить</translation>
     </message>

+ 54 - 49
launcher/translation/spanish.ts

@@ -406,165 +406,170 @@
 <context>
     <name>CSettingsView</name>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="244"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="414"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="599"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="302"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="370"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="408"/>
         <source>Off</source>
         <translation>Desactivado</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="78"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="123"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="79"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="579"/>
         <source>Artificial Intelligence</source>
         <translation>Inteligencia Artificial</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="83"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="233"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="84"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="434"/>
         <source>Mod Repositories</source>
         <translation>Repositorios de Mods</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="305"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="498"/>
         <source>Interface Scaling</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="278"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="491"/>
         <source>Neutral AI in battles</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="465"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="421"/>
         <source>Enemy AI in battles</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="271"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="441"/>
         <source>Additional repository</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="201"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="566"/>
         <source>Adventure Map Allies</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="383"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="281"/>
         <source>Adventure Map Enemies</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="176"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="623"/>
         <source>Windowed</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="181"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="628"/>
         <source>Borderless fullscreen</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="186"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="633"/>
         <source>Exclusive fullscreen</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="319"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="170"/>
         <source>Autosave limit (0 = off)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="329"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="525"/>
         <source>Friendly AI in battles</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="588"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="271"/>
         <source>Framerate Limit</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="612"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="328"/>
         <source>Autosave prefix</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="629"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="603"/>
         <source>empty = map name prefix</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="508"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="383"/>
         <source>Refresh now</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="264"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="288"/>
         <source>Default repository</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="249"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="419"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="604"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="307"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="375"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="413"/>
         <source>On</source>
         <translation>Encendido</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="451"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="359"/>
         <source>Cursor</source>
         <translation>Cursor</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="535"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="397"/>
         <source>Heroes III Translation</source>
         <translation>Traducción de Heroes III</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="444"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="641"/>
+        <source>Reserved screen area</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="177"/>
         <source>Fullscreen</source>
         <translation>Pantalla completa</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="68"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="484"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="69"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="197"/>
         <source>General</source>
         <translation>General</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="542"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="532"/>
         <source>VCMI Language</source>
         <translation>Idioma de VCMI</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="194"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="335"/>
         <source>Resolution</source>
         <translation>Resolución</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="549"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="204"/>
         <source>Autosave</source>
         <translation>Autoguardado</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="376"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="146"/>
         <source>Display index</source>
         <translation>Mostrar índice</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="528"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="184"/>
         <source>Network port</source>
         <translation>Puerto de red</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="73"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="561"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="74"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="321"/>
         <source>Video</source>
         <translation>Vídeo</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="163"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="610"/>
         <source>Select display mode for game
 
 Windowed - game will run inside a window that covers part of your screen
@@ -575,52 +580,52 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="431"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="212"/>
         <source>Hardware</source>
         <translation>Hardware</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="436"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="217"/>
         <source>Software</source>
         <translation>Software</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="403"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="225"/>
         <source>Show intro</source>
         <translation>Mostrar introducción</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="472"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="542"/>
         <source>Check on startup</source>
         <translation>Comprovar al inicio</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="312"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="481"/>
         <source>Heroes III Data Language</source>
         <translation>Idioma de los datos de Heroes III.</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="402"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="405"/>
         <source>Active</source>
         <translation>Activado</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="407"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="410"/>
         <source>Disabled</source>
         <translation>Desactivado</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="408"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="411"/>
         <source>Enable</source>
         <translation>Activar</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="413"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="416"/>
         <source>Not Installed</source>
         <translation>No Instalado</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="414"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="417"/>
         <source>Install</source>
         <translation>Instalar</translation>
     </message>

+ 54 - 49
launcher/translation/ukrainian.ts

@@ -406,123 +406,123 @@
 <context>
     <name>CSettingsView</name>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="244"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="414"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="599"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="302"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="370"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="408"/>
         <source>Off</source>
         <translation>Вимкнено</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="78"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="123"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="79"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="579"/>
         <source>Artificial Intelligence</source>
         <translation>Штучний інтелект</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="83"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="233"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="84"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="434"/>
         <source>Mod Repositories</source>
         <translation>Репозиторії модифікацій</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="305"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="498"/>
         <source>Interface Scaling</source>
         <translation>Масштабування інтерфейсу</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="278"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="491"/>
         <source>Neutral AI in battles</source>
         <translation>Нейтральний ШІ в боях</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="465"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="421"/>
         <source>Enemy AI in battles</source>
         <translation>Ворожий ШІ в боях</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="271"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="441"/>
         <source>Additional repository</source>
         <translation>Додатковий репозиторій</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="201"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="566"/>
         <source>Adventure Map Allies</source>
         <translation>Союзники на мапі пригод</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="383"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="281"/>
         <source>Adventure Map Enemies</source>
         <translation>Вороги на мапі пригод</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="176"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="623"/>
         <source>Windowed</source>
         <translation>У вікні</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="181"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="628"/>
         <source>Borderless fullscreen</source>
         <translation>Повноекранне вікно</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="186"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="633"/>
         <source>Exclusive fullscreen</source>
         <translation>Повноекранний (ексклюзивно)</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="319"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="170"/>
         <source>Autosave limit (0 = off)</source>
         <translation>Кількість автозбережень</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="329"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="525"/>
         <source>Friendly AI in battles</source>
         <translation>Дружній ШІ в боях</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="588"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="271"/>
         <source>Framerate Limit</source>
         <translation>Обмеження частоти кадрів</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="612"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="328"/>
         <source>Autosave prefix</source>
         <translation>Префікс назв автозбережень</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="629"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="603"/>
         <source>empty = map name prefix</source>
         <translation>(використовувати назву карти)</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="508"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="383"/>
         <source>Refresh now</source>
         <translation>Оновити зараз</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="264"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="288"/>
         <source>Default repository</source>
         <translation>Стандартний репозиторій</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="249"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="419"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="604"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="307"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="375"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="413"/>
         <source>On</source>
         <translation>Увімкнено</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="451"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="359"/>
         <source>Cursor</source>
         <translation>Курсор</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="312"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="481"/>
         <source>Heroes III Data Language</source>
         <translation>Мова Heroes III</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="163"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="610"/>
         <source>Select display mode for game
 
 Windowed - game will run inside a window that covers part of your screen
@@ -539,94 +539,99 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
 Повноекранний ексклюзивний режим - гра займатиме весь екран і використовуватиме вибрану роздільну здатність.</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="431"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="641"/>
+        <source>Reserved screen area</source>
+        <translation>Зарезервована зона екрану</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="212"/>
         <source>Hardware</source>
         <translation>Апаратний</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="436"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="217"/>
         <source>Software</source>
         <translation>Програмний</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="535"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="397"/>
         <source>Heroes III Translation</source>
         <translation>Переклад Heroes III</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="472"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="542"/>
         <source>Check on startup</source>
         <translation>Перевіряти на старті</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="444"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="177"/>
         <source>Fullscreen</source>
         <translation>Повноекранний режим</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="68"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="484"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="69"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="197"/>
         <source>General</source>
         <translation>Загальні налаштування</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="542"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="532"/>
         <source>VCMI Language</source>
         <translation>Мова VCMI</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="194"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="335"/>
         <source>Resolution</source>
         <translation>Роздільна здатність</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="549"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="204"/>
         <source>Autosave</source>
         <translation>Автозбереження</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="376"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="146"/>
         <source>Display index</source>
         <translation>Дісплей</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="528"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="184"/>
         <source>Network port</source>
         <translation>Мережевий порт</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="73"/>
-        <location filename="../settingsView/csettingsview_moc.ui" line="561"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="74"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="321"/>
         <source>Video</source>
         <translation>Графіка</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.ui" line="403"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="225"/>
         <source>Show intro</source>
         <translation>Вступні відео</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="402"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="405"/>
         <source>Active</source>
         <translation>Активні</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="407"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="410"/>
         <source>Disabled</source>
         <translation>Деактивований</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="408"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="411"/>
         <source>Enable</source>
         <translation>Активувати</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="413"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="416"/>
         <source>Not Installed</source>
         <translation>Не встановлено</translation>
     </message>
     <message>
-        <location filename="../settingsView/csettingsview_moc.cpp" line="414"/>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="417"/>
         <source>Install</source>
         <translation>Встановити</translation>
     </message>

+ 4 - 4
lib/CCreatureSet.cpp

@@ -445,7 +445,7 @@ void CCreatureSet::setStackExp(const SlotID & slot, TExpType exp)
 	stacks[slot]->experience = exp;
 }
 
-void CCreatureSet::clear()
+void CCreatureSet::clearSlots()
 {
 	while(!stacks.empty())
 	{
@@ -533,12 +533,12 @@ void CCreatureSet::changeStackCount(const SlotID & slot, TQuantity toAdd)
 
 CCreatureSet::~CCreatureSet()
 {
-	clear();
+	clearSlots();
 }
 
 void CCreatureSet::setToArmy(CSimpleArmy &src)
 {
-	clear();
+	clearSlots();
 	while(src)
 	{
 		auto i = src.army.begin();
@@ -1050,7 +1050,7 @@ void CStackBasicDescriptor::serializeJson(JsonSerializeFormat & handler)
 	}
 }
 
-void CSimpleArmy::clear()
+void CSimpleArmy::clearSlots()
 {
 	army.clear();
 }

+ 3 - 3
lib/CCreatureSet.h

@@ -181,7 +181,7 @@ using TCreatureQueue = std::priority_queue<TPairCreatureSlot, std::vector<TPairC
 class IArmyDescriptor
 {
 public:
-	virtual void clear() = 0;
+	virtual void clearSlots() = 0;
 	virtual bool setCreature(SlotID slot, CreatureID cre, TQuantity count) = 0;
 };
 
@@ -190,7 +190,7 @@ class DLL_LINKAGE CSimpleArmy : public IArmyDescriptor
 {
 public:
 	TSimpleSlots army;
-	void clear() override;
+	void clearSlots() override;
 	bool setCreature(SlotID slot, CreatureID cre, TQuantity count) override;
 	operator bool() const;
 
@@ -226,7 +226,7 @@ public:
 
 	void addToSlot(const SlotID & slot, const CreatureID & cre, TQuantity count, bool allowMerging = true); //Adds stack to slot. Slot must be empty or with same type creature
 	void addToSlot(const SlotID & slot, CStackInstance * stack, bool allowMerging = true); //Adds stack to slot. Slot must be empty or with same type creature
-	void clear() override;
+	void clearSlots() override;
 	void setFormation(bool tight);
 	CArmedInstance *castToArmyObj();
 

+ 4 - 13
lib/CGeneralTextHandler.cpp

@@ -313,19 +313,7 @@ void CGeneralTextHandler::registerStringOverride(const std::string & modContext,
 	assert(!modContext.empty());
 	assert(!language.empty());
 
-	std::string baseModLanguage = getModLanguage(modContext);
-
-	if (baseModLanguage != language)
-	{
-		// this is translation - only add text to existing strings, do not register new ones
-		if (stringsLocalizations.count(UID.get()) == 0)
-		{
-			logMod->warn("Unknown string '%s' in mod '%s' for language '%s'. Ignoring", UID.get(), modContext, language);
-			return;
-		}
-	}
-
-	// NOTE: implicitly creates entry, intended - strings added by vcmi (and potential UI mods) are not registered anywhere at the moment
+	// NOTE: implicitly creates entry, intended - strings added by maps, campaigns, vcmi and potentially - UI mods are not registered anywhere at the moment
 	auto & entry = stringsLocalizations[UID.get()];
 
 	entry.overrideLanguage = language;
@@ -349,6 +337,9 @@ bool CGeneralTextHandler::validateTranslation(const std::string & language, cons
 		if (string.second.baseLanguage == language && !string.second.baseValue.empty())
 			continue; // Base string already uses our language
 
+		if (string.second.baseLanguage.empty())
+			continue; // String added in localization, not present in base language (e.g. maps/campaigns)
+
 		if (config.Struct().count(string.first) > 0)
 			continue;
 

+ 1 - 1
lib/CHeroHandler.cpp

@@ -770,7 +770,7 @@ std::vector<bool> CHeroHandler::getDefaultAllowed() const
 
 	for(const CHero * hero : objects)
 	{
-		allowedHeroes.push_back(!hero->special);
+		allowedHeroes.push_back(hero && !hero->special);
 	}
 
 	return allowedHeroes;

+ 12 - 4
lib/CTownHandler.cpp

@@ -1184,11 +1184,19 @@ void CTownHandler::initializeRequirements()
 		{
 			if (node.Vector().size() > 1)
 			{
-				logMod->warn("Unexpected length of town buildings requirements: %d", node.Vector().size());
-				logMod->warn("Entry contains: ");
-				logMod->warn(node.toJson());
+				logMod->error("Unexpected length of town buildings requirements: %d", node.Vector().size());
+				logMod->error("Entry contains: ");
+				logMod->error(node.toJson());
 			}
-			return BuildingID(VLC->identifiers()->getIdentifier(requirement.town->getBuildingScope(), node.Vector()[0]).value());
+
+			auto index = VLC->identifiers()->getIdentifier(requirement.town->getBuildingScope(), node[0]);
+
+			if (!index.has_value())
+			{
+				logMod->error("Unknown building in town buildings: %s", node[0].String());
+				return BuildingID::NONE;
+			}
+			return BuildingID(index.value());
 		});
 	}
 	requirementsToLoad.clear();

+ 7 - 1
lib/JsonNode.cpp

@@ -829,7 +829,13 @@ std::shared_ptr<Bonus> JsonUtils::parseBonus(const JsonNode &ability)
 	auto b = std::make_shared<Bonus>();
 	if (!parseBonus(ability, b.get()))
 	{
-		return nullptr;
+		// caller code can not handle this case and presumes that returned bonus is always valid
+		logGlobal->error("Failed to parse bonus! Json config was %S ", ability.toJson());
+
+		b->type = BonusType::NONE;
+		assert(0); // or throw? Game *should* work with dummy bonus
+
+		return b;
 	}
 	return b;
 }

+ 1 - 1
lib/NetPacks.h

@@ -335,7 +335,7 @@ struct DLL_LINKAGE SetAvailableHero : public CPackForClient
 {
 	SetAvailableHero()
 	{
-		army.clear();
+		army.clearSlots();
 	}
 	void applyGs(CGameState * gs);
 

+ 19 - 5
lib/NetPacksLib.cpp

@@ -37,6 +37,8 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+#define THROW_IF_NO_BATTLE if (!gs->curB) throw std::runtime_error("Trying to apply pack when no battle!");
+
 void CPack::visit(ICPackVisitor & visitor)
 {
 	visitBasic(visitor);
@@ -2126,16 +2128,19 @@ void BattleStart::applyGs(CGameState * gs) const
 
 void BattleNextRound::applyGs(CGameState * gs) const
 {
+	THROW_IF_NO_BATTLE
 	gs->curB->nextRound(round);
 }
 
 void BattleSetActiveStack::applyGs(CGameState * gs) const
 {
+	THROW_IF_NO_BATTLE
 	gs->curB->nextTurn(stack);
 }
 
 void BattleTriggerEffect::applyGs(CGameState * gs) const
 {
+	THROW_IF_NO_BATTLE
 	CStack * st = gs->curB->getStack(stackID);
 	assert(st);
 	switch(static_cast<BonusType>(effect))
@@ -2231,6 +2236,7 @@ void BattleLogMessage::applyBattle(IBattleState * battleState)
 
 void BattleStackMoved::applyGs(CGameState *gs)
 {
+	THROW_IF_NO_BATTLE
 	applyBattle(gs->curB);
 }
 
@@ -2241,6 +2247,7 @@ void BattleStackMoved::applyBattle(IBattleState * battleState)
 
 void BattleStackAttacked::applyGs(CGameState * gs)
 {
+	THROW_IF_NO_BATTLE
 	applyBattle(gs->curB);
 }
 
@@ -2251,6 +2258,7 @@ void BattleStackAttacked::applyBattle(IBattleState * battleState)
 
 void BattleAttack::applyGs(CGameState * gs)
 {
+	THROW_IF_NO_BATTLE
 	CStack * attacker = gs->curB->getStack(stackAttacking);
 	assert(attacker);
 
@@ -2264,6 +2272,8 @@ void BattleAttack::applyGs(CGameState * gs)
 
 void StartAction::applyGs(CGameState *gs)
 {
+	THROW_IF_NO_BATTLE
+
 	CStack *st = gs->curB->getStack(ba.stackNumber);
 
 	if(ba.actionType == EActionType::END_TACTIC_PHASE)
@@ -2312,7 +2322,7 @@ void StartAction::applyGs(CGameState *gs)
 
 void BattleSpellCast::applyGs(CGameState * gs) const
 {
-	assert(gs->curB);
+	THROW_IF_NO_BATTLE
 
 	if(castByHero)
 	{
@@ -2325,6 +2335,7 @@ void BattleSpellCast::applyGs(CGameState * gs) const
 
 void SetStackEffect::applyGs(CGameState *gs)
 {
+	THROW_IF_NO_BATTLE
 	applyBattle(gs->curB);
 }
 
@@ -2343,6 +2354,7 @@ void SetStackEffect::applyBattle(IBattleState * battleState)
 
 void StacksInjured::applyGs(CGameState *gs)
 {
+	THROW_IF_NO_BATTLE
 	applyBattle(gs->curB);
 }
 
@@ -2354,6 +2366,7 @@ void StacksInjured::applyBattle(IBattleState * battleState)
 
 void BattleUnitsChanged::applyGs(CGameState *gs)
 {
+	THROW_IF_NO_BATTLE
 	applyBattle(gs->curB);
 }
 
@@ -2384,8 +2397,8 @@ void BattleUnitsChanged::applyBattle(IBattleState * battleState)
 
 void BattleObstaclesChanged::applyGs(CGameState * gs)
 {
-	if(gs->curB)
-		applyBattle(gs->curB);
+	THROW_IF_NO_BATTLE;
+	applyBattle(gs->curB);
 }
 
 void BattleObstaclesChanged::applyBattle(IBattleState * battleState)
@@ -2416,8 +2429,8 @@ CatapultAttack::~CatapultAttack() = default;
 
 void CatapultAttack::applyGs(CGameState * gs)
 {
-	if(gs->curB)
-		applyBattle(gs->curB);
+	THROW_IF_NO_BATTLE
+	applyBattle(gs->curB);
 }
 
 void CatapultAttack::visitTyped(ICPackVisitor & visitor)
@@ -2443,6 +2456,7 @@ void CatapultAttack::applyBattle(IBattleState * battleState)
 
 void BattleSetStackProperty::applyGs(CGameState * gs) const
 {
+	THROW_IF_NO_BATTLE
 	CStack * stack = gs->curB->getStack(stackID);
 	switch(which)
 	{

+ 3 - 3
lib/StartInfo.cpp

@@ -71,7 +71,7 @@ std::string StartInfo::getCampaignName() const
 void LobbyInfo::verifyStateBeforeStart(bool ignoreNoHuman) const
 {
 	if(!mi || !mi->mapHeader)
-		throw std::domain_error("ExceptionMapMissing");
+		throw std::domain_error("There is no map to start!");
 	
 	auto missingMods = CMapService::verifyMapHeaderMods(*mi->mapHeader);
 	ModIncompatibility::ModList modList;
@@ -88,12 +88,12 @@ void LobbyInfo::verifyStateBeforeStart(bool ignoreNoHuman) const
 			break;
 
 	if(i == si->playerInfos.cend() && !ignoreNoHuman)
-		throw std::domain_error("ExceptionNoHuman");
+		throw std::domain_error("There is no human player on map");
 
 	if(si->mapGenOptions && si->mode == StartInfo::NEW_GAME)
 	{
 		if(!si->mapGenOptions->checkOptions())
-			throw std::domain_error("ExceptionNoTemplate");
+			throw std::domain_error("No random map template found!");
 	}
 }
 

+ 14 - 4
lib/mapObjectConstructors/CObjectClassesHandler.cpp

@@ -316,11 +316,21 @@ std::vector<bool> CObjectClassesHandler::getDefaultAllowed() const
 
 TObjectTypeHandler CObjectClassesHandler::getHandlerFor(si32 type, si32 subtype) const
 {
-	assert(type < objects.size());
-	assert(objects[type]);
-	assert(subtype < objects[type]->objects.size());
+	try
+	{
+		auto result = objects.at(type)->objects.at(subtype);
+
+		if (result != nullptr)
+			return result;
+	}
+	catch (std::out_of_range & e)
+	{
+		// Leave catch block silently
+	}
 
-	return objects.at(type)->objects.at(subtype);
+	std::string errorString = "Failed to find object of type " + std::to_string(type) + "::" + std::to_string(subtype);
+	logGlobal->error(errorString);
+	throw std::runtime_error(errorString);
 }
 
 TObjectTypeHandler CObjectClassesHandler::getHandlerFor(const std::string & scope, const std::string & type, const std::string & subtype) const

+ 1 - 1
lib/mapObjects/CBank.cpp

@@ -58,7 +58,7 @@ std::string CBank::getHoverText(PlayerColor player) const
 void CBank::setConfig(const BankConfig & config)
 {
 	bc = std::make_unique<BankConfig>(config);
-	clear(); // remove all stacks, if any
+	clearSlots(); // remove all stacks, if any
 
 	for(const auto & stack : config.guards)
 		setCreature (SlotID(stacksCount()), stack.type->getId(), stack.count);

+ 3 - 0
lib/mapObjects/CGTownBuilding.cpp

@@ -356,6 +356,9 @@ void CTownRewardableBuilding::heroLevelUpDone(const CGHeroInstance *hero) const
 
 void CTownRewardableBuilding::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
 {
+	if(answer == 0)
+		return; // player refused
+	
 	if(visitors.find(hero->id) != visitors.end())
 		return; // query not for this building
 

+ 6 - 0
lib/mapObjects/CGTownInstance.cpp

@@ -996,6 +996,12 @@ CBuilding::TRequired CGTownInstance::genBuildingRequirements(const BuildingID &
 	std::function<CBuilding::TRequired::Variant(const BuildingID &)> dependTest =
 	[&](const BuildingID & id) -> CBuilding::TRequired::Variant
 	{
+		if (town->buildings.count(id) == 0)
+		{
+			logMod->error("Invalid building ID %d in building dependencies!", id.getNum());
+			return CBuilding::TRequired::OperatorAll();
+		}
+
 		const CBuilding * build = town->buildings.at(id);
 		CBuilding::TRequired::OperatorAll requirements;
 

+ 1 - 1
lib/mapping/MapFormatH3M.cpp

@@ -1735,7 +1735,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec
 		{
 			if(!object->spells.empty())
 			{
-				object->clear();
+				object->spells.clear();
 				logGlobal->debug("Hero %s subID=%d has spells set twice (in map properties and on adventure map instance). Using the latter set...", object->getNameTextID(), object->subID);
 			}
 

+ 6 - 0
lib/rmg/RmgArea.cpp

@@ -376,6 +376,12 @@ void Area::translate(const int3 & shift)
 	//toAbsolute(dTiles, shift);
 }
 
+void Area::erase_if(std::function<bool(const int3&)> predicate)
+{
+	invalidate();
+	vstd::erase_if(dTiles, predicate);
+}
+
 Area operator- (const Area & l, const int3 & r)
 {
 	Area result(l);

+ 1 - 0
lib/rmg/RmgArea.h

@@ -64,6 +64,7 @@ namespace rmg
 		void intersect(const Area & area);
 		void subtract(const Area & area);
 		void translate(const int3 & shift);
+		void erase_if(std::function<bool(const int3&)> predicate);
 		
 		friend Area operator+ (const Area & l, const int3 & r); //translation
 		friend Area operator- (const Area & l, const int3 & r); //translation

+ 0 - 10
lib/rmg/RmgObject.cpp

@@ -344,16 +344,6 @@ void Object::Instance::finalize(RmgMap & map)
 			setTemplate(terrainType->getId());
 		}
 	}
-	if (dObject.ID == Obj::MONSTER)
-	{
-		//Make up for extra offset in HotA creature templates
-		auto visitableOffset = dObject.getVisitableOffset();
-		auto fixedPos = getPosition(true) + visitableOffset;
-		vstd::abetween(fixedPos.x, visitableOffset.x, map.width() - 1);
-		vstd::abetween(fixedPos.y, visitableOffset.y, map.height() - 1);
-		int3 parentPos = getPosition(true) - getPosition(false);
-		setPosition(fixedPos - parentPos);
-	}
 
 	if (dObject.isVisitable() && !map.isOnMap(dObject.visitablePos()))
 		throw rmgException(boost::to_string(boost::format("Visitable tile %s of object %d at %s is outside the map") % dObject.visitablePos().toString() % dObject.id % dObject.pos.toString()));

+ 51 - 5
lib/rmg/modificators/ObjectManager.cpp

@@ -129,6 +129,19 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object
 {
 	float bestWeight = 0.f;
 	int3 result(-1, -1, -1);
+
+	//Blocked area might not cover object position if it has an offset from (0,0)
+	auto outsideTheMap = [this, &obj]() -> bool
+	{
+		for (const auto& oi : obj.instances())
+		{
+			if (!map.isOnMap(oi->getPosition(true)))
+			{
+				return true;
+			}
+		}
+		return false;
+	};
 	
 	if(optimizer & OptimizeType::DISTANCE)
 	{
@@ -149,6 +162,9 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object
 			
 			if(!searchArea.contains(obj.getArea()) || !searchArea.overlap(obj.getAccessibleArea()))
 				continue;
+
+			if (outsideTheMap())
+				continue;
 			
 			float weight = weightFunction(tile);
 			if(weight > bestWeight)
@@ -168,9 +184,12 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object
 
 			if (obj.getVisibleTop().y < 0)
 				continue;
-			
+					
 			if(!searchArea.contains(obj.getArea()) || !searchArea.overlap(obj.getAccessibleArea()))
 				continue;
+
+			if (outsideTheMap())
+				continue;
 			
 			float weight = weightFunction(tile);
 			if(weight > bestWeight)
@@ -416,7 +435,7 @@ bool ObjectManager::createRequiredObjects()
 	
 	//create object on specific positions
 	//TODO: implement guards
-	for (const auto &objInfo : instantObjects)
+	for (const auto &objInfo : instantObjects) //Unused ATM
 	{
 		rmg::Object rmgObject(*objInfo.obj);
 		rmgObject.setPosition(objInfo.pos);
@@ -433,6 +452,28 @@ bool ObjectManager::createRequiredObjects()
 
 void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateDistance, bool createRoad/* = false*/)
 {	
+	//object.finalize(map);
+
+	if (object.instances().size() == 1 && object.instances().front()->object().ID == Obj::MONSTER)
+	{
+		//Fix for HoTA offset - lonely guards
+		
+		auto monster = object.instances().front();
+		if (!monster->object().appearance)
+		{
+			//Needed to determine visitable offset
+			monster->setAnyTemplate();
+		}
+		object.getPosition();
+		auto visitableOffset = monster->object().getVisitableOffset();
+		auto fixedPos = monster->getPosition(true) + visitableOffset;
+
+		//Do not place guard outside the map
+		vstd::abetween(fixedPos.x, visitableOffset.x, map.width() - 1);
+		vstd::abetween(fixedPos.y, visitableOffset.y, map.height() - 1);
+		int3 parentOffset = monster->getPosition(true) - monster->getPosition(false);
+		monster->setPosition(fixedPos - parentOffset);
+	}
 	object.finalize(map);
 
 	Zone::Lock lock(zone.areaMutex);
@@ -443,8 +484,8 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
 		zone.freePaths().add(object.getVisitablePosition());
 	zone.areaUsed().unite(object.getArea());
 	zone.areaUsed().erase(object.getVisitablePosition());
-	
-	if(guarded)
+
+	if(guarded) //We assume the monster won't be guarded
 	{
 		auto guardedArea = object.instances().back()->getAccessibleArea();
 		guardedArea.add(object.instances().back()->getVisitablePosition());
@@ -501,6 +542,7 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
 		{
 			case Obj::RANDOM_TREASURE_ART:
 			case Obj::RANDOM_MINOR_ART: //In OH3 quest artifacts have higher value than normal arts
+			case Obj::RANDOM_RESOURCE:
 			{
 				if (auto * qap = zone.getModificator<QuestArtifactPlacer>())
 				{
@@ -629,8 +671,12 @@ bool ObjectManager::addGuard(rmg::Object & object, si32 strength, bool zoneGuard
 	});
 	
 	auto & instance = object.addInstance(*guard);
-	instance.setPosition(guardPos - object.getPosition());
 	instance.setAnyTemplate(); //terrain is irrelevant for monsters, but monsters need some template now
+
+	//Fix HoTA monsters with offset template
+	auto visitableOffset = instance.object().getVisitableOffset();
+	auto fixedPos = guardPos - object.getPosition() + visitableOffset;
+	instance.setPosition(fixedPos);
 		
 	return true;
 }

+ 6 - 4
lib/rmg/modificators/QuestArtifactPlacer.cpp

@@ -89,14 +89,15 @@ void QuestArtifactPlacer::placeQuestArtifacts(CRandomGenerator & rand)
 				artifactToReplace->getObjectName(),
 				artifactToReplace->getPosition().toString(),
 				VLC->artifacts()->getById(artifactToPlace)->getNameTranslated());
-			artifactToReplace->ID = Obj::ARTIFACT;
-			artifactToReplace->subID = artifactToPlace;
 
 			//Update appearance. Terrain is irrelevant.
 			auto handler = VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, artifactToPlace);
+			auto newObj = handler->create();
 			auto templates = handler->getTemplates();
-			artifactToReplace->appearance = templates.front();
-			//FIXME: Instance name is still "randomArtifact"
+			//artifactToReplace->appearance = templates.front();
+			newObj->appearance  = templates.front();
+			newObj->pos = artifactToReplace->pos;
+			mapProxy->insertObject(newObj);
 
 			for (auto z : map.getZones())
 			{
@@ -107,6 +108,7 @@ void QuestArtifactPlacer::placeQuestArtifacts(CRandomGenerator & rand)
 					localQap->dropReplacedArtifact(artifactToReplace);
 				}
 			}
+			mapProxy->removeObject(artifactToReplace);
 			break;
 		}
 	}

+ 10 - 0
lib/rmg/modificators/RoadPlacer.cpp

@@ -19,9 +19,12 @@
 #include "../../mapping/CMapEditManager.h"
 #include "../../modding/IdentifierStorage.h"
 #include "../../modding/ModScope.h"
+#include "../../TerrainHandler.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+class TerrainType;
+
 void RoadPlacer::process()
 {
 	if(generator.getConfig().defaultRoadType.empty() && generator.getConfig().secondaryRoadType.empty())
@@ -115,6 +118,13 @@ void RoadPlacer::drawRoads(bool secondary)
 		//Clean space under roads even if they won't be eventually generated
 		Zone::Lock lock(zone.areaMutex);
 
+		//Do not draw roads on underground rock or water
+		roads.erase_if([this](const int3& pos) -> bool
+		{
+			const auto* terrain = map.getTile(pos).terType;;
+			return !terrain->isPassable() || !terrain->isLand();
+		});
+
 		zone.areaPossible().subtract(roads);
 		zone.freePaths().unite(roads);
 	}

+ 6 - 0
lib/rmg/threadpool/MapProxy.cpp

@@ -30,6 +30,12 @@ void MapProxy::insertObjects(std::set<CGObjectInstance*>& objects)
     map.getEditManager()->insertObjects(objects);
 }
 
+void MapProxy::removeObject(CGObjectInstance * obj)
+{
+    Lock lock(mx);
+    map.getEditManager()->removeObject(obj);
+}
+
 void MapProxy::drawTerrain(CRandomGenerator & generator, std::vector<int3> & tiles, TerrainId terrain)
 {
     Lock lock(mx);

+ 1 - 0
lib/rmg/threadpool/MapProxy.h

@@ -26,6 +26,7 @@ public:
 
     void insertObject(CGObjectInstance * obj);
     void insertObjects(std::set<CGObjectInstance*>& objects);
+    void removeObject(CGObjectInstance* obj);
 
     void drawTerrain(CRandomGenerator & generator, std::vector<int3> & tiles, TerrainId terrain);
     void drawRivers(CRandomGenerator & generator, std::vector<int3> & tiles, TerrainId terrain);

+ 56 - 9
lib/spells/BattleSpellMechanics.cpp

@@ -590,7 +590,7 @@ std::vector<AimType> BattleSpellMechanics::getTargetTypes() const
 	return ret;
 }
 
-std::vector<Destination> BattleSpellMechanics::getPossibleDestinations(size_t index, AimType aimType, const Target & current) const
+std::vector<Destination> BattleSpellMechanics::getPossibleDestinations(size_t index, AimType aimType, const Target & current, bool fast) const
 {
 	//TODO: BattleSpellMechanics::getPossibleDestinations
 
@@ -602,19 +602,66 @@ std::vector<Destination> BattleSpellMechanics::getPossibleDestinations(size_t in
 	switch(aimType)
 	{
 	case AimType::CREATURE:
+	{
+		auto stacks = battle()->battleGetAllStacks();
+
+		for(auto stack : stacks)
+		{
+			Target tmp = current;
+			tmp.emplace_back(stack->getPosition());
+
+			detail::ProblemImpl ignored;
+
+			if(canBeCastAt(tmp, ignored))
+				ret.emplace_back(stack->getPosition());
+		}
+
+		break;
+	}
+
 	case AimType::LOCATION:
-		for(int i = 0; i < GameConstants::BFIELD_SIZE; i++)
+		if(fast)
+		{
+			auto stacks = battle()->battleGetAllStacks();
+			std::set<BattleHex> hexesToCheck;
+
+			for(auto stack : stacks)
+			{
+				hexesToCheck.insert(stack->getPosition());
+
+				for(auto adjacent : stack->getPosition().neighbouringTiles())
+					hexesToCheck.insert(adjacent);
+			}
+
+			for(auto hex : hexesToCheck)
+			{
+				if(hex.isAvailable())
+				{
+					Target tmp = current;
+					tmp.emplace_back(hex);
+
+					detail::ProblemImpl ignored;
+
+					if(canBeCastAt(tmp, ignored))
+						ret.emplace_back(hex);
+				}
+			}
+		}
+		else
 		{
-			BattleHex dest(i);
-			if(dest.isAvailable())
+			for(int i = 0; i < GameConstants::BFIELD_SIZE; i++)
 			{
-				Target tmp = current;
-				tmp.emplace_back(dest);
+				BattleHex dest(i);
+				if(dest.isAvailable())
+				{
+					Target tmp = current;
+					tmp.emplace_back(dest);
 
-				detail::ProblemImpl ignored;
+					detail::ProblemImpl ignored;
 
-				if(canBeCastAt(tmp, ignored))
-					ret.emplace_back(dest);
+					if(canBeCastAt(tmp, ignored))
+						ret.emplace_back(dest);
+				}
 			}
 		}
 		break;

+ 1 - 1
lib/spells/BattleSpellMechanics.h

@@ -50,7 +50,7 @@ public:
 	/// Returns vector of all possible destinations for specified aim type
 	/// index - ???
 	/// current - ???
-	std::vector<Destination> getPossibleDestinations(size_t index, AimType aimType, const Target & current) const override final;
+	std::vector<Destination> getPossibleDestinations(size_t index, AimType aimType, const Target & current, bool fast) const override final;
 
 	/// Returns true if spell can be cast on unit
 	bool isReceptive(const battle::Unit * target) const override;

+ 3 - 3
lib/spells/ISpellMechanics.cpp

@@ -326,7 +326,7 @@ bool BattleCast::castIfPossible(ServerCallback * server, Target target)
 	return false;
 }
 
-std::vector<Target> BattleCast::findPotentialTargets() const
+std::vector<Target> BattleCast::findPotentialTargets(bool fast) const
 {
 	//TODO: for more than 2 destinations per target much more efficient algorithm is required
 
@@ -354,7 +354,7 @@ std::vector<Target> BattleCast::findPotentialTargets() const
 			if(previous.empty())
 			{
 				Target empty;
-				destinations = m->getPossibleDestinations(index, targetTypes.at(index), empty);
+				destinations = m->getPossibleDestinations(index, targetTypes.at(index), empty, fast);
 
 				for(auto & destination : destinations)
 				{
@@ -367,7 +367,7 @@ std::vector<Target> BattleCast::findPotentialTargets() const
 			{
 				for(const Target & current : previous)
 				{
-					destinations = m->getPossibleDestinations(index, targetTypes.at(index), current);
+					destinations = m->getPossibleDestinations(index, targetTypes.at(index), current, fast);
 
 					for(auto & destination : destinations)
 					{

+ 2 - 2
lib/spells/ISpellMechanics.h

@@ -139,7 +139,7 @@ public:
 	///cast with silent check for permitted cast
 	bool castIfPossible(ServerCallback * server, Target target);
 
-	std::vector<Target> findPotentialTargets() const;
+	std::vector<Target> findPotentialTargets(bool fast = false) const;
 
 private:
 	///spell school level
@@ -199,7 +199,7 @@ public:
 
 	virtual std::vector<AimType> getTargetTypes() const = 0;
 
-	virtual std::vector<Destination> getPossibleDestinations(size_t index, AimType aimType, const Target & current) const = 0;
+	virtual std::vector<Destination> getPossibleDestinations(size_t index, AimType aimType, const Target & current, bool fast = false) const = 0;
 
 	virtual const Spell * getSpell() const = 0;
 

+ 1 - 1
mapeditor/inspector/rewardswidget.cpp

@@ -222,7 +222,7 @@ bool RewardsWidget::commitChanges()
 		pandora->resources = ResourceSet();
 		pandora->artifacts.clear();
 		pandora->spells.clear();
-		pandora->creatures.clear();
+		pandora->creatures.clearSlots();
 		
 		for(int row = 0; row < rewards; ++row)
 		{

+ 19 - 3
server/CGameHandler.cpp

@@ -39,6 +39,7 @@
 #include "../lib/CCreatureHandler.h"
 #include "../lib/gameState/CGameState.h"
 #include "../lib/CStack.h"
+#include "../lib/UnlockGuard.h"
 #include "../lib/GameSettings.h"
 #include "../lib/battle/BattleInfo.h"
 #include "../lib/CondSh.h"
@@ -76,6 +77,7 @@
 #define COMPLAIN_RETF(txt, FORMAT) {complain(boost::str(boost::format(txt) % FORMAT)); return false;}
 
 CondSh<bool> battleMadeAction(false);
+boost::recursive_mutex battleActionMutex;
 CondSh<BattleResult *> battleResult(nullptr);
 template <typename T> class CApplyOnGH;
 
@@ -2122,7 +2124,7 @@ void CGameHandler::setupBattle(int3 tile, const CArmedInstance *armies[2], const
 
 	BattleField terType = gs->battleGetBattlefieldType(tile, getRandomGenerator());
 	if (heroes[0] && heroes[0]->boat && heroes[1] && heroes[1]->boat)
-		terType = BattleField(*VLC->identifiers()->getIdentifier("core", "battlefield", "ship_to_ship"));
+		terType = BattleField(*VLC->identifiers()->getIdentifier("core", "battlefield.ship_to_ship"));
 
 	//send info about battles
 	BattleStart bs;
@@ -2942,7 +2944,7 @@ bool CGameHandler::load(const std::string & filename)
 	try
 	{
 		{
-			CLoadFile lf(*CResourceHandler::get("local")->getResourceName(ResourceID(stem.to_string(), EResType::SAVEGAME)), MINIMAL_SERIALIZATION_VERSION);
+			CLoadFile lf(*CResourceHandler::get()->getResourceName(ResourceID(stem.to_string(), EResType::SAVEGAME)), MINIMAL_SERIALIZATION_VERSION);
 			loadCommonState(lf);
 			logGlobal->info("Loading server state");
 			lf >> *this;
@@ -4394,6 +4396,8 @@ void CGameHandler::updateGateState()
 
 bool CGameHandler::makeBattleAction(BattleAction &ba)
 {
+	boost::unique_lock lock(battleActionMutex);
+
 	bool ok = true;
 
 	battle::Target target = ba.getTarget(gs->curB);
@@ -4817,6 +4821,8 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
 
 bool CGameHandler::makeCustomAction(BattleAction & ba)
 {
+	boost::unique_lock lock(battleActionMutex);
+
 	switch(ba.actionType)
 	{
 	case EActionType::HERO_SPELL:
@@ -6048,6 +6054,8 @@ bool CGameHandler::swapStacks(const StackLocation & sl1, const StackLocation & s
 
 void CGameHandler::runBattle()
 {
+	boost::unique_lock lock(battleActionMutex);
+
 	setBattle(gs->curB);
 	assert(gs->curB);
 	//TODO: pre-tactic stuff, call scripts etc.
@@ -6066,7 +6074,10 @@ void CGameHandler::runBattle()
 	//tactic round
 	{
 		while ((lobby->state != EServerState::SHUTDOWN) && gs->curB->tacticDistance && !battleResult.get())
+		{
+			auto unlockGuard = vstd::makeUnlockGuard(battleActionMutex);
 			boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+		}
 	}
 
 	//initial stacks appearance triggers, e.g. built-in bonus spells
@@ -6389,7 +6400,10 @@ void CGameHandler::runBattle()
 						battleMadeAction.data = false;
 						while ((lobby->state != EServerState::SHUTDOWN) && !actionWasMade())
 						{
-							battleMadeAction.cond.wait(lock);
+							{
+								auto unlockGuard = vstd::makeUnlockGuard(battleActionMutex);
+								battleMadeAction.cond.wait(lock);
+							}
 							if (battleGetStackByID(nextId, false) != next)
 								next = nullptr; //it may be removed, while we wait
 						}
@@ -6450,6 +6464,8 @@ void CGameHandler::runBattle()
 
 bool CGameHandler::makeAutomaticAction(const CStack *stack, BattleAction &ba)
 {
+	boost::unique_lock lock(battleActionMutex);
+
 	BattleSetActiveStack bsa;
 	bsa.stack = stack->unitId();
 	bsa.askPlayerInterface = false;

+ 6 - 0
server/CVCMIServer.cpp

@@ -258,6 +258,12 @@ void CVCMIServer::prepareToRestart()
 		* si = * gh->gs->initialOpts;
 		si->seedToBeUsed = si->seedPostInit = 0;
 		state = EServerState::LOBBY;
+		if (si->campState)
+		{
+			assert(si->campState->currentScenario().has_value());
+			campaignMap = si->campState->currentScenario().value_or(CampaignScenarioID(0));
+			campaignBonus = si->campState->getBonusID(campaignMap).value_or(-1);
+		}
 		// FIXME: dirry hack to make sure old CGameHandler::run is finished
 		boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
 	}

+ 2 - 2
server/HeroPoolProcessor.cpp

@@ -112,7 +112,7 @@ void HeroPoolProcessor::onHeroEscaped(const PlayerColor & color, const CGHeroIns
 	sah.slotID = selectSlotForRole(color, sah.roleID);
 	sah.player = color;
 	sah.hid = hero->subID;
-	sah.army.clear();
+	sah.army.clearSlots();
 	sah.army.setCreature(SlotID(0), hero->type->initialArmy.at(0).creature, 1);
 
 	gameHandler->sendAndApply(&sah);
@@ -148,7 +148,7 @@ void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHe
 		else
 		{
 			sah.roleID = TavernSlotRole::SINGLE_UNIT;
-			sah.army.clear();
+			sah.army.clearSlots();
 			sah.army.setCreature(SlotID(0), newHero->type->initialArmy[0].creature, 1);
 		}
 	}

+ 11 - 5
server/NetPacksServer.cpp

@@ -25,6 +25,8 @@
 #include "../lib/spells/ISpellMechanics.h"
 #include "../lib/serializer/Cast.h"
 
+extern boost::recursive_mutex battleActionMutex;
+
 void ApplyGhNetPackVisitor::visitSaveGame(SaveGame & pack)
 {
 	gh.save(pack.fname);
@@ -280,26 +282,28 @@ void ApplyGhNetPackVisitor::visitQueryReply(QueryReply & pack)
 
 void ApplyGhNetPackVisitor::visitMakeAction(MakeAction & pack)
 {
+	boost::unique_lock lock(battleActionMutex);
+
 	const BattleInfo * b = gs.curB;
 	if(!b)
-		gh.throwNotAllowedAction(&pack);
+		gh.throwAndComplain(&pack, "Can not make action - there is no battle ongoing!");
 
 	if(b->tacticDistance)
 	{
 		if(pack.ba.actionType != EActionType::WALK && pack.ba.actionType != EActionType::END_TACTIC_PHASE
 			&& pack.ba.actionType != EActionType::RETREAT && pack.ba.actionType != EActionType::SURRENDER)
-			gh.throwNotAllowedAction(&pack);
+			gh.throwAndComplain(&pack, "Can not make actions while in tactics mode!");
 		if(!vstd::contains(gh.connections[b->sides[b->tacticsSide].color], pack.c))
-			gh.throwNotAllowedAction(&pack);
+			gh.throwAndComplain(&pack, "Can not make actions in battles you are not part of!");
 	}
 	else
 	{
 		auto active = b->battleActiveUnit();
 		if(!active)
-			gh.throwNotAllowedAction(&pack);
+			gh.throwAndComplain(&pack, "No active unit in battle!");
 		auto unitOwner = b->battleGetOwner(active);
 		if(!vstd::contains(gh.connections[unitOwner], pack.c))
-			gh.throwNotAllowedAction(&pack);
+			gh.throwAndComplain(&pack, "Can not make actions in battles you are not part of!");
 	}
 
 	result = gh.makeBattleAction(pack.ba);
@@ -307,6 +311,8 @@ void ApplyGhNetPackVisitor::visitMakeAction(MakeAction & pack)
 
 void ApplyGhNetPackVisitor::visitMakeCustomAction(MakeCustomAction & pack)
 {
+	boost::unique_lock lock(battleActionMutex);
+
 	const BattleInfo * b = gs.curB;
 	if(!b)
 		gh.throwNotAllowedAction(&pack);

+ 1 - 1
test/mock/mock_spells_Mechanics.h

@@ -34,7 +34,7 @@ public:
 
 	MOCK_CONST_METHOD1(isReceptive, bool(const battle::Unit * ));
 	MOCK_CONST_METHOD0(getTargetTypes, std::vector<AimType>());
-	MOCK_CONST_METHOD3(getPossibleDestinations, std::vector<Destination>(size_t, AimType, const Target &));
+	MOCK_CONST_METHOD4(getPossibleDestinations, std::vector<Destination>(size_t, AimType, const Target &, bool));
 
 	MOCK_CONST_METHOD0(getSpell, const Spell *());