瀏覽代碼

Merge beta into develop

Ivan Savenko 2 年之前
父節點
當前提交
307065a633
共有 100 個文件被更改,包括 3491 次插入1542 次删除
  1. 12 4
      AI/BattleAI/BattleAI.cpp
  2. 1 1
      AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp
  3. 21 3
      AI/Nullkiller/Behaviors/DefenceBehavior.cpp
  4. 28 15
      AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp
  5. 1 2
      AI/Nullkiller/Engine/FuzzyHelper.cpp
  6. 3 1
      AI/Nullkiller/Engine/Nullkiller.cpp
  7. 9 4
      AI/Nullkiller/Engine/PriorityEvaluator.cpp
  8. 4 2
      AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp
  9. 4 0
      ChangeLog.md
  10. 11 17
      Mods/vcmi/config/vcmi/chinese.json
  11. 87 94
      Mods/vcmi/config/vcmi/english.json
  12. 28 23
      Mods/vcmi/config/vcmi/german.json
  13. 7 11
      Mods/vcmi/config/vcmi/polish.json
  14. 11 17
      Mods/vcmi/config/vcmi/russian.json
  15. 11 17
      Mods/vcmi/config/vcmi/spanish.json
  16. 11 17
      Mods/vcmi/config/vcmi/ukrainian.json
  17. 0 5
      Mods/vcmi/mod.json
  18. 40 13
      client/CMusicHandler.cpp
  19. 3 0
      client/CMusicHandler.h
  20. 1 1
      client/CPlayerInterface.cpp
  21. 8 0
      client/adventureMap/CAdvMapInt.cpp
  22. 17 9
      client/battle/BattleActionsController.cpp
  23. 3 0
      client/battle/BattleActionsController.h
  24. 9 1
      client/battle/BattleAnimationClasses.cpp
  25. 10 11
      client/battle/BattleFieldController.cpp
  26. 1 0
      client/battle/BattleFieldController.h
  27. 44 33
      client/battle/BattleStacksController.cpp
  28. 3 5
      client/battle/BattleStacksController.h
  29. 2 5
      client/battle/BattleWindow.cpp
  30. 17 2
      client/gui/InterfaceObjectConfigurable.cpp
  31. 1 1
      client/gui/NotificationHandler.cpp
  32. 7 2
      client/mapView/MapRenderer.cpp
  33. 4 2
      client/mapView/MapView.cpp
  34. 55 30
      client/mapView/MapViewController.cpp
  35. 2 1
      client/mapView/MapViewController.h
  36. 24 2
      client/widgets/Buttons.cpp
  37. 6 0
      client/widgets/Buttons.h
  38. 3 2
      client/widgets/CArtifactHolder.cpp
  39. 32 28
      client/windows/CCastleInterface.cpp
  40. 7 0
      client/windows/settings/GeneralOptionsTab.cpp
  41. 4 0
      cmake_modules/VCMI_lib.cmake
  42. 3 0
      config/ai/object-priorities.txt
  43. 1 20
      config/bonuses.json
  44. 2 2
      config/heroClasses.json
  45. 1 1
      config/heroes/special.json
  46. 1 1
      config/objects/generic.json
  47. 1 4
      config/schemas/mod.json
  48. 5 1
      config/schemas/obstacle.json
  49. 1 1
      config/schemas/settings.json
  50. 0 3
      config/skills.json
  51. 2 0
      config/widgets/settings/generalOptionsTab.json
  52. 63 12
      launcher/firstLaunch/firstlaunch_moc.cpp
  53. 6 0
      launcher/firstLaunch/firstlaunch_moc.h
  54. 214 65
      launcher/firstLaunch/firstlaunch_moc.ui
  55. 1 1
      launcher/lobby/lobby_moc.ui
  56. 0 1
      launcher/modManager/cmodlist.cpp
  57. 47 19
      launcher/modManager/cmodlistmodel_moc.cpp
  58. 1 0
      launcher/modManager/cmodlistmodel_moc.h
  59. 63 20
      launcher/modManager/cmodlistview_moc.cpp
  60. 5 2
      launcher/modManager/cmodlistview_moc.h
  61. 8 8
      launcher/settingsView/csettingsview_moc.cpp
  62. 232 88
      launcher/translation/english.ts
  63. 307 157
      launcher/translation/german.ts
  64. 257 107
      launcher/translation/polish.ts
  65. 372 132
      launcher/translation/russian.ts
  66. 564 206
      launcher/translation/spanish.ts
  67. 250 100
      launcher/translation/ukrainian.ts
  68. 2 2
      launcher/updatedialog_moc.ui
  69. 8 5
      lib/CCreatureHandler.cpp
  70. 1 1
      lib/CCreatureSet.h
  71. 18 14
      lib/CGameState.cpp
  72. 27 13
      lib/CGeneralTextHandler.cpp
  73. 9 0
      lib/GameConstants.h
  74. 1 2
      lib/JsonDetail.cpp
  75. 1 0
      lib/NetPacksLib.cpp
  76. 5 1
      lib/mapObjects/CObjectClassesHandler.cpp
  77. 3 2
      lib/mapObjects/CObjectClassesHandler.h
  78. 14 0
      lib/mapObjects/CRewardableConstructor.cpp
  79. 4 0
      lib/mapObjects/CRewardableConstructor.h
  80. 6 1
      lib/mapObjects/ObjectTemplate.h
  81. 10 9
      lib/mapping/MapFormatH3M.cpp
  82. 9 9
      lib/rmg/CMapGenerator.cpp
  83. 21 0
      lib/rmg/CRmgTemplate.cpp
  84. 3 0
      lib/rmg/CRmgTemplate.h
  85. 97 0
      lib/rmg/MinePlacer.cpp
  86. 30 0
      lib/rmg/MinePlacer.h
  87. 121 0
      lib/rmg/ObjectDistributor.cpp
  88. 32 0
      lib/rmg/ObjectDistributor.h
  89. 1 2
      lib/rmg/ObjectManager.cpp
  90. 4 0
      lib/rmg/RmgMap.cpp
  91. 3 47
      lib/rmg/TownPlacer.cpp
  92. 49 32
      lib/rmg/TreasurePlacer.cpp
  93. 3 0
      lib/rmg/TreasurePlacer.h
  94. 4 12
      lib/serializer/Connection.cpp
  95. 36 19
      server/CGameHandler.cpp
  96. 2 1
      server/CGameHandler.h
  97. 3 0
      server/CQuery.cpp
  98. 0 36
      server/CVCMIServer.cpp
  99. 1 1
      server/NetPacksServer.cpp
  100. 4 1
      vcmibuilder

+ 12 - 4
AI/BattleAI/BattleAI.cpp

@@ -609,9 +609,18 @@ void CBattleAI::attemptCastingSpell()
 
 		size_t ourUnits = 0;
 
-		for(auto unit : all)
+		std::set<uint32_t> unitIds;
+
+		state.battleGetUnitsIf([&](const battle::Unit * u)->bool
+		{
+			if(!u->isGhost() && !u->isTurret())
+				unitIds.insert(u->unitId());
+
+			return false;
+		});
+
+		for(auto unitId : unitIds)
 		{
-			auto unitId = unit->unitId();
 			auto localUnit = state.battleGetUnitByID(unitId);
 
 			newHealthOfStack[unitId] = localUnit->getAvailableHealth();
@@ -633,9 +642,8 @@ void CBattleAI::attemptCastingSpell()
 		{
 			int64_t totalGain = 0;
 
-			for(auto unit : all)
+			for(auto unitId : unitIds)
 			{
-				auto unitId = unit->unitId();
 				auto localUnit = state.battleGetUnitByID(unitId);
 
 				auto newValue = getValOr(newValueOfStack, unitId, 0);

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

@@ -70,7 +70,7 @@ void DangerHitMapAnalyzer::updateHitMap()
 				auto turn = path.turn();
 				auto & node = hitMap[pos.x][pos.y][pos.z];
 
-				if(tileDanger > node.maximumDanger.danger
+				if(tileDanger / (turn / 3 + 1) > node.maximumDanger.danger / (node.maximumDanger.turn / 3 + 1)
 					|| (tileDanger == node.maximumDanger.danger && node.maximumDanger.turn > turn))
 				{
 					node.maximumDanger.danger = tileDanger;

+ 21 - 3
AI/Nullkiller/Behaviors/DefenceBehavior.cpp

@@ -30,7 +30,7 @@ namespace NKAI
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
-const double TREAT_IGNORE_RATIO = 0.5;
+const float TREAT_IGNORE_RATIO = 2;
 
 using namespace Goals;
 
@@ -133,7 +133,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 				tasks.push_back(Goals::sptr(composition));
 			}
 
-			bool treatIsWeak = path.getHeroStrength() / treat.danger > TREAT_IGNORE_RATIO;
+			bool treatIsWeak = path.getHeroStrength() / (float)treat.danger > TREAT_IGNORE_RATIO;
 			bool needToSaveGrowth = treat.turn == 0 && dayOfWeek == 7;
 
 			if(treatIsWeak && !needToSaveGrowth)
@@ -244,7 +244,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 				continue;
 			}
 
-			if(path.targetHero == town->visitingHero && path.exchangeCount == 1)
+			if(path.targetHero == town->visitingHero.get() && path.exchangeCount == 1)
 			{
 #if NKAI_TRACE_LEVEL >= 1
 				logAi->trace("Put %s to garrison of town %s",
@@ -265,6 +265,24 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 
 				continue;
 			}
+
+			// main without army and visiting scout with army, very specific case
+			if(town->visitingHero && town->getUpperArmy()->stacksCount() == 0
+				&& path.targetHero != town->visitingHero.get() && path.exchangeCount == 1 && path.turn() == 0
+				&& ai->nullkiller->heroManager->evaluateHero(path.targetHero) > ai->nullkiller->heroManager->evaluateHero(town->visitingHero.get())
+				&& 10 * path.targetHero->getTotalStrength() < town->visitingHero->getTotalStrength())
+			{
+				path.heroArmy = town->visitingHero.get();
+
+				tasks.push_back(
+					Goals::sptr(Composition()
+						.addNext(DefendTown(town, treat, path))
+						.addNext(ExchangeSwapTownHeroes(town, town->visitingHero.get()))
+						.addNext(ExecuteHeroChain(path, town))
+						.addNext(ExchangeSwapTownHeroes(town, path.targetHero, HeroLockedReason::DEFENCE))));
+
+				continue;
+			}
 				
 			if(treat.turn == 0 || (path.turn() <= treat.turn && path.getHeroStrength() * SAFE_ATTACK_CONSTANT >= treat.danger))
 			{

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

@@ -65,6 +65,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 {
 	Goals::TGoalVec tasks;
 	const int3 pos = hero->visitablePos();
+	auto targetHeroScore = ai->nullkiller->heroManager->evaluateHero(hero);
 
 #if NKAI_TRACE_LEVEL >= 1
 	logAi->trace("Checking ways to gaher army for hero %s, %s", hero->getObjectName(), pos.toString());
@@ -113,7 +114,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 		float armyValue = (float)heroExchange.getReinforcementArmyStrength() / hero->getArmyStrength();
 
 		// avoid transferring very small amount of army
-		if(armyValue < 0.1f)
+		if(armyValue < 0.1f && armyValue < 20000)
 		{
 #if NKAI_TRACE_LEVEL >= 2
 			logAi->trace("Army value is too small.");
@@ -122,31 +123,33 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 		}
 
 		// avoid trying to move bigger army to the weaker one.
-		if(armyValue > 1)
+		bool hasOtherMainInPath = false;
+
+		for(auto node : path.nodes)
 		{
-			bool hasOtherMainInPath = false;
+			if(!node.targetHero) continue;
 
-			for(auto node : path.nodes)
-			{
-				if(!node.targetHero) continue;
+			auto heroRole = ai->nullkiller->heroManager->getHeroRole(node.targetHero);
 
-				auto heroRole = ai->nullkiller->heroManager->getHeroRole(node.targetHero);
+			if(heroRole == HeroRole::MAIN)
+			{
+				auto score = ai->nullkiller->heroManager->evaluateHero(node.targetHero);
 
-				if(heroRole == HeroRole::MAIN)
+				if(score >= targetHeroScore)
 				{
 					hasOtherMainInPath = true;
 
 					break;
 				}
 			}
+		}
 
-			if(hasOtherMainInPath)
-			{
+		if(hasOtherMainInPath)
+		{
 #if NKAI_TRACE_LEVEL >= 2
-				logAi->trace("Army value is too large.");
+			logAi->trace("Army value is too large.");
 #endif
-				continue;
-			}
+			continue;
 		}
 
 		auto danger = path.getTotalDanger();
@@ -180,7 +183,17 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 	#if NKAI_TRACE_LEVEL >= 2
 				logAi->trace("Action is blocked. Considering decomposition.");
 	#endif
-				composition.addNext(blockedAction->decompose(path.targetHero));
+				auto subGoal = blockedAction->decompose(path.targetHero);
+
+				if(subGoal->invalid())
+				{
+#if NKAI_TRACE_LEVEL >= 1
+					logAi->trace("Path is invalid. Skipping");
+#endif
+					continue;
+				}
+
+				composition.addNext(subGoal);
 			}
 			
 			tasks.push_back(sptr(composition));
@@ -261,7 +274,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 
 		auto armyValue = (float)upgrade.upgradeValue / path.getHeroStrength();
 
-		if(armyValue < 0.25f || upgrade.upgradeValue < 300) // avoid small upgrades
+		if((armyValue < 0.1f && armyValue < 20000) || upgrade.upgradeValue < 300) // avoid small upgrades
 		{
 #if NKAI_TRACE_LEVEL >= 2
 			logAi->trace("Ignore path. Army value is too small (%f)", armyValue);

+ 1 - 2
AI/Nullkiller/Engine/FuzzyHelper.cpp

@@ -130,8 +130,6 @@ ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj)
 
 		return danger;
 	}
-	case Obj::PANDORAS_BOX:
-		return 10000; //Who knows what awaits us there
 
 	case Obj::ARTIFACT:
 	case Obj::RESOURCE:
@@ -148,6 +146,7 @@ ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj)
 	case Obj::CREATURE_GENERATOR4:
 	case Obj::MINE:
 	case Obj::ABANDONED_MINE:
+	case Obj::PANDORAS_BOX:
 	{
 		const CArmedInstance * a = dynamic_cast<const CArmedInstance *>(obj);
 		return a->getArmyStrength();

+ 3 - 1
AI/Nullkiller/Engine/Nullkiller.cpp

@@ -299,6 +299,7 @@ void Nullkiller::makeTurn()
 
 void Nullkiller::executeTask(Goals::TTask task)
 {
+	auto start = std::chrono::high_resolution_clock::now();
 	std::string taskDescr = task->toString();
 
 	boost::this_thread::interruption_point();
@@ -307,10 +308,11 @@ void Nullkiller::executeTask(Goals::TTask task)
 	try
 	{
 		task->accept(ai.get());
+		logAi->trace("Task %s completed in %lld", taskDescr, timeElapsed(start));
 	}
 	catch(goalFulfilledException &)
 	{
-		logAi->trace("Task %s completed", task->toString());
+		logAi->trace("Task %s completed in %lld", taskDescr, timeElapsed(start));
 	}
 	catch(cannotFulfillGoalException & e)
 	{

+ 9 - 4
AI/Nullkiller/Engine/PriorityEvaluator.cpp

@@ -591,7 +591,7 @@ public:
 		uint64_t upgradeValue = armyUpgrade.getUpgradeValue();
 
 		evaluationContext.armyReward += upgradeValue;
-		evaluationContext.strategicalValue += upgradeValue / armyUpgrade.hero->getTotalStrength();
+		evaluationContext.strategicalValue += upgradeValue / (float)armyUpgrade.hero->getArmyStrength();
 	}
 };
 
@@ -627,7 +627,7 @@ private:
 				continue;
 
 			auto creature = creatureInfo.second.back().toCreature();
-			result += creature->AIValue * town->getGrowthInfo(creature->level).totalGrowth();
+			result += creature->AIValue * town->getGrowthInfo(creature->getLevel() - 1).totalGrowth();
 		}
 
 		return result;
@@ -648,6 +648,9 @@ public:
 
 		auto strategicalValue = std::sqrt(armyIncome / 20000.0f) + dailyIncome / 3000.0f;
 
+		if(evaluationContext.evaluator.ai->buildAnalyzer->getDevelopmentInfo().size() == 1)
+			strategicalValue = 1;
+
 		float multiplier = 1;
 
 		if(treat.turn < defendTown.getTurn())
@@ -781,9 +784,11 @@ public:
 		if(garrisonHero && swapCommand.getLockingReason() == HeroLockedReason::DEFENCE)
 		{
 			auto defenderRole = evaluationContext.evaluator.ai->heroManager->getHeroRole(garrisonHero);
+			auto mpLeft = garrisonHero->movement / (float)garrisonHero->maxMovePoints(true);
 
-			evaluationContext.movementCost += garrisonHero->movement;
-			evaluationContext.movementCostByRole[defenderRole] += garrisonHero->movement;
+			evaluationContext.movementCost += mpLeft;
+			evaluationContext.movementCostByRole[defenderRole] += mpLeft;
+			evaluationContext.heroRole = defenderRole;
 		}
 	}
 };

+ 4 - 2
AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp

@@ -53,13 +53,15 @@ namespace AIPathfinding
 
 		for(const CGTownInstance * t : cb->getTownsInfo())
 		{
-			if(t->hasBuilt(BuildingID::SHIPYARD))
+			// do not allow ally shipyards because of bug
+			if(t->hasBuilt(BuildingID::SHIPYARD) && t->getOwner() == ai->playerID)
 				shipyards.push_back(t);
 		}
 
 		for(const CGObjectInstance * obj : ai->memory->visitableObjs)
 		{
-			if(obj->ID != Obj::TOWN) //towns were handled in the previous loop
+			// do not allow ally shipyards because of bug
+			if(obj->ID != Obj::TOWN && obj->getOwner() == ai->playerID) //towns were handled in the previous loop
 			{
 				if(const IShipyard * shipyard = IShipyard::castFrom(obj))
 					shipyards.push_back(shipyard);

+ 4 - 0
ChangeLog.md

@@ -25,6 +25,7 @@
 * Implemented cut/copy/paste operations
 * Implemented lasso brush for terrain editing
 * Toolbar actions now have names
+* Added basic victory and lose conditions
 
 ### LAUNCHER:
 * Added initial Welcome/Setup screen for new players
@@ -34,6 +35,7 @@
 * Mods tab layout has been adjusted based on feedback from players
 * Settings tab layout has been redesigned to support longer texts
 * Added button to start map editor directly from Launcher
+* Simplified game starting flow from online lobby
 
 ### AI PLAYER:
 * AI should now be more active in destroying heroes causing treat on AI towns
@@ -70,6 +72,7 @@
 * It is now possible to use in-game console for vcmi commands
 * Stacks sized 1000-9999 units will not be displayed as "1k"
 * It is now possible to select destination town for Town Portal via double-click
+* Implemented extended options for random map tab: generate G+U size, select RMG template, manage teams and roads
 
 ### HERO SCREEN
 * Fixed cases of incorrect artifact slot highlighting
@@ -112,6 +115,7 @@
 * Hovering over hero now correctly shows hero cursor
 * Creature currently making turn is now highlighted in the Battle Queue 
 * Hovering over creature icon in Battle Queue will highlight this creature in the battlefield
+* New battle UI extension allows control over creatures' special abilities
 
 ### SPELLS:
 * Hero casting animation will play before spell effect

+ 11 - 17
Mods/vcmi/config/vcmi/chinese.json

@@ -118,17 +118,17 @@
 	
 	// few strings from WoG used by vcmi
 	"vcmi.stackExperience.description" : "» 经 验 获 得 明 细 «\n\n生物类型 ................... : %s\n经验等级 ................. : %s (%i)\n经验点数 ............... : %i\n下一个等级所需经验 .. : %i\n每次战斗最大获得经验 ... : %i%% (%i)\n获得经验的生物数量 .... : %i\n最大招募数量\n不会丢失经验升级 .... : %i\n经验倍数 ........... : %.2f\n升级倍数 .............. : %.2f\n10级后经验值 ........ : %i\n最大招募数量下\n 升级到10级所需经验数量: %i",
-	"vcmi.stackExperience.rank.1" : "新兵 1级",
-	"vcmi.stackExperience.rank.2" : "列兵 2级",
-	"vcmi.stackExperience.rank.3" : "下士 3级",
-	"vcmi.stackExperience.rank.4" : "中士 4级",
-	"vcmi.stackExperience.rank.5" : "上士 5级",
-	"vcmi.stackExperience.rank.6" : "少尉 6级",
-	"vcmi.stackExperience.rank.7" : "中尉 7级",
-	"vcmi.stackExperience.rank.8" : "上尉 8级",
-	"vcmi.stackExperience.rank.9" : "少校 9级",
-	"vcmi.stackExperience.rank.10" : "中校 10级",
-	"vcmi.stackExperience.rank.11" : "上校 11级",
+	"vcmi.stackExperience.rank.0" : "新兵 1级",
+	"vcmi.stackExperience.rank.1" : "列兵 2级",
+	"vcmi.stackExperience.rank.2" : "下士 3级",
+	"vcmi.stackExperience.rank.3" : "中士 4级",
+	"vcmi.stackExperience.rank.4" : "上士 5级",
+	"vcmi.stackExperience.rank.5" : "少尉 6级",
+	"vcmi.stackExperience.rank.6" : "中尉 7级",
+	"vcmi.stackExperience.rank.7" : "上尉 8级",
+	"vcmi.stackExperience.rank.8" : "少校 9级",
+	"vcmi.stackExperience.rank.9" : "中校 10级",
+	"vcmi.stackExperience.rank.10" : "上校 11级",
 	
 	"core.bonus.ADDITIONAL_ATTACK.name": "双击",
 	"core.bonus.ADDITIONAL_ATTACK.description": "可以攻击两次",
@@ -144,8 +144,6 @@
 	"core.bonus.BLOCKS_RANGED_RETALIATION.description": "敌人无法对射击进行反击",
 	"core.bonus.CATAPULT.name": "攻城",
 	"core.bonus.CATAPULT.description": "可以攻击城墙",
-	"core.bonus.CATAPULT_EXTRA_SHOTS.name": "额外攻击城墙",
-	"core.bonus.CATAPULT_EXTRA_SHOTS.description": "可以额外攻击城墙 ${val} 次",
 	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "施法消耗 - (${val})",
 	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "减少英雄的施法消耗",
 	"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "对方施法消耗 + (${val})",
@@ -244,10 +242,6 @@
 	"core.bonus.REBIRTH.description": "{val}% 数量死亡后会复活",
 	"core.bonus.RETURN_AFTER_STRIKE.name": "攻击并返回",
 	"core.bonus.RETURN_AFTER_STRIKE.description": "攻击后回到初始位置",
-	"core.bonus.SELF_LUCK.name": "永久幸运",
-	"core.bonus.SELF_LUCK.description": "永久拥有幸运值",
-	"core.bonus.SELF_MORALE.name": "士气高涨",
-	"core.bonus.SELF_MORALE.description": "永久拥有高昂的士气",
 	"core.bonus.SHOOTER.name": "射手",
 	"core.bonus.SHOOTER.description": "生物可以设计",
 	"core.bonus.SHOOTS_ALL_ADJACENT.name": "范围远程攻击",

+ 87 - 94
Mods/vcmi/config/vcmi/english.json

@@ -1,5 +1,5 @@
 {
-	"vcmi.adventureMap.monsterThreat.title"     : "\n\n Threat: ",
+	"vcmi.adventureMap.monsterThreat.title"     : "\n\nThreat: ",
 	"vcmi.adventureMap.monsterThreat.levels.0"  : "Effortless",
 	"vcmi.adventureMap.monsterThreat.levels.1"  : "Very Weak",
 	"vcmi.adventureMap.monsterThreat.levels.2"  : "Weak",
@@ -13,24 +13,24 @@
 	"vcmi.adventureMap.monsterThreat.levels.10" : "Deadly",
 	"vcmi.adventureMap.monsterThreat.levels.11" : "Impossible",
 
-	"vcmi.adventureMap.confirmRestartGame"     : "Are you sure you want to restart game?",
-	"vcmi.adventureMap.noTownWithMarket"       : "No available marketplace!",
-	"vcmi.adventureMap.noTownWithTavern"       : "No available town with tavern!",
-	"vcmi.adventureMap.spellUnknownProblem"    : "Unknown problem with this spell, no more information available.",
+	"vcmi.adventureMap.confirmRestartGame"     : "Are you sure you want to restart the game?",
+	"vcmi.adventureMap.noTownWithMarket"       : "There are no available marketplaces!",
+	"vcmi.adventureMap.noTownWithTavern"       : "There are no available towns with taverns!",
+	"vcmi.adventureMap.spellUnknownProblem"    : "There is an unknown problem with this spell! No more information is available.",
 	"vcmi.adventureMap.playerAttacked"         : "Player has been attacked: %s",
 	"vcmi.adventureMap.moveCostDetails"        : "Movement points - Cost: %TURNS turns + %POINTS points, Remaining points: %REMAINING",
 	"vcmi.adventureMap.moveCostDetailsNoTurns" : "Movement points - Cost: %POINTS points, Remaining points: %REMAINING",
 
-	"vcmi.server.errors.existingProcess"     : "Another vcmiserver process is running, please terminate it first",
-	"vcmi.server.errors.modsIncompatibility" : "Required mods to load game:",
-	"vcmi.server.confirmReconnect"           : "Connect to the last session?",
+	"vcmi.server.errors.existingProcess"     : "Another VCMI server process is running. Please terminate it before starting a new game.",
+	"vcmi.server.errors.modsIncompatibility" : "The following mods are required to load the game:",
+	"vcmi.server.confirmReconnect"           : "Do you want to reconnect to the last session?",
 
 	"vcmi.settingsMainWindow.generalTab.hover" : "General",
 	"vcmi.settingsMainWindow.generalTab.help"     : "Switches to General Options tab, which contains settings related to general game client behavior",
 	"vcmi.settingsMainWindow.battleTab.hover" : "Battle",
 	"vcmi.settingsMainWindow.battleTab.help"     : "Switches to Battle Options tab, which allows configuring game behavior during battles",
 	"vcmi.settingsMainWindow.adventureTab.hover" : "Adventure Map",
-	"vcmi.settingsMainWindow.adventureTab.help"  : "Switches to Adventure Map Options tab - adventure map is part of the game where you can move your heroes",
+	"vcmi.settingsMainWindow.adventureTab.help"  : "Switches to Adventure Map Options tab (adventure map is the section of the game where players can control the movements of their heroes)",
 
 	"vcmi.systemOptions.videoGroup" : "Video Settings",
 	"vcmi.systemOptions.audioGroup" : "Audio Settings",
@@ -38,51 +38,50 @@
 	"vcmi.systemOptions.townsGroup" : "Town Screen",
 
 	"vcmi.systemOptions.fullscreenButton.hover" : "Fullscreen",
-	"vcmi.systemOptions.fullscreenButton.help"  : "{Fullscreen}\n\n If selected, VCMI will run in fullscreen mode, otherwise VCMI will run in window",
+	"vcmi.systemOptions.fullscreenButton.help"  : "{Fullscreen}\n\nIf selected, VCMI will run in fullscreen mode, otherwise it will run in windowed mode",
 	"vcmi.systemOptions.resolutionButton.hover" : "Resolution: %wx%h",
-	"vcmi.systemOptions.resolutionButton.help"  : "{Select Resolution}\n\n Change in-game screen resolution. Game restart required to apply new resolution.",
+	"vcmi.systemOptions.resolutionButton.help"  : "{Select Resolution}\n\nChange in-game screen resolution. A game restart is required to apply the new resolution.",
 	"vcmi.systemOptions.resolutionMenu.hover"   : "Select Resolution",
 	"vcmi.systemOptions.resolutionMenu.help"    : "Change in-game screen resolution.",
-	"vcmi.systemOptions.fullscreenFailed"       : "{Fullscreen}\n\n Failed to switch to fullscreen mode! Current resolution is not supported by display!",
+	"vcmi.systemOptions.fullscreenFailed"       : "{Fullscreen}\n\nFailed to switch to fullscreen mode! The current resolution is not supported by the display!",
 	"vcmi.systemOptions.framerateButton.hover"  : "Show FPS",
-	"vcmi.systemOptions.framerateButton.help"   : "{Show FPS}\n\n Toggles visibility of Frames Per Second counter in corner of game window.",
+	"vcmi.systemOptions.framerateButton.help"   : "{Show FPS}\n\nToggle the visibility of the Frames Per Second counter in the corner of the game window",
 
 	"vcmi.adventureOptions.infoBarPick.hover" : "Show Messages in Info Panel",
-	"vcmi.adventureOptions.infoBarPick.help" : "{Show Messages in Info Panel}\n\nWhenever possible, game messages from visiting map objects will be shown in info bar instead of showing up as popup windows",
+	"vcmi.adventureOptions.infoBarPick.help" : "{Show Messages in Info Panel}\n\nWhenever possible, game messages from visiting map objects will be shown in the info pannel, instead of popping up in a separate window.",
 	"vcmi.adventureOptions.numericQuantities.hover" : "Numeric Creature Quantities",
-	"vcmi.adventureOptions.numericQuantities.help" : "{Numeric Creature Quantities}\n\n Shows approximate enemy creatures quantities in numeric A-B format.",
+	"vcmi.adventureOptions.numericQuantities.help" : "{Numeric Creature Quantities}\n\nShow the approximate quantities of enemy creatures in the numeric A-B format.",
 	"vcmi.adventureOptions.forceMovementInfo.hover" : "Always Show Movement Cost",
-	"vcmi.adventureOptions.forceMovementInfo.help" : "{Always Show Movement Cost}\n\n Replaces default status bar info with movement points data without need to hold ALT button.",
+	"vcmi.adventureOptions.forceMovementInfo.help" : "{Always Show Movement Cost}\n\nAlways show movement points data in status bar information. (Instead of viewing it only while you hold down ALT key)",
 	"vcmi.adventureOptions.showGrid.hover" : "Show Grid",
-	"vcmi.adventureOptions.showGrid.help" : "{Show Grid}\n\n Shows grid overlay, showing borders between adventure map tiles.",
-	"vcmi.adventureOptions.mapSwipe.hover" : "Map Swipe",
-	"vcmi.adventureOptions.mapSwipe.help" : "{Map Swipe}\n\n Allows map movement via finger swipe gesture on systems with touchscreen. As of right now, can also be accessed via left mouse button.",
+	"vcmi.adventureOptions.showGrid.help" : "{Show Grid}\n\nShow the grid overlay, highlighting the borders between adventure map tiles.",
+	"vcmi.adventureOptions.mapSwipe.hover" : "Map Swipe/Panning",
+	"vcmi.adventureOptions.mapSwipe.help" : "{Map Swipe/Panning}\n\nOn touchscreen devices, you can move the map by swiping with your finger. To pan the map using the mouse, hold down the left or middle mouse button and move the mouse.",
 	"vcmi.adventureOptions.mapScrollSpeed1.hover": "",
 	"vcmi.adventureOptions.mapScrollSpeed5.hover": "",
 	"vcmi.adventureOptions.mapScrollSpeed6.hover": "",
-	"vcmi.adventureOptions.mapScrollSpeed1.help": "Set map scrolling speed to very slow",
-	"vcmi.adventureOptions.mapScrollSpeed5.help": "Set map scrolling speed to very fast",
-	"vcmi.adventureOptions.mapScrollSpeed6.help": "Set map scrolling speed to instantaneous.",
+	"vcmi.adventureOptions.mapScrollSpeed1.help": "Set the map scrolling speed to very slow",
+	"vcmi.adventureOptions.mapScrollSpeed5.help": "Set the map scrolling speed to very fast",
+	"vcmi.adventureOptions.mapScrollSpeed6.help": "Set the map scrolling speed to instantaneous.",
 
-	"vcmi.battleOptions.queueSizeLabel.hover": "Show Creature Turn Order",
+	"vcmi.battleOptions.queueSizeLabel.hover": "Show Turn Order Queue",
 	"vcmi.battleOptions.queueSizeNoneButton.hover": "OFF",
 	"vcmi.battleOptions.queueSizeAutoButton.hover": "AUTO",
 	"vcmi.battleOptions.queueSizeSmallButton.hover": "SMALL",
 	"vcmi.battleOptions.queueSizeBigButton.hover": "BIG",
-	"vcmi.battleOptions.queueSizeNoneButton.help": "Completely disables visibility of creature turn order in battle",
-	"vcmi.battleOptions.queueSizeAutoButton.help": "Sets turn order size depending on game resolution (small when playing with screen resolution below 700 pixels high, big otherwise)",
-	"vcmi.battleOptions.queueSizeSmallButton.help": "Sets turn order size to small",
-	"vcmi.battleOptions.queueSizeBigButton.help": "Sets turn order size to big (not supported if game resolution is less than 700 pixels high)",
+	"vcmi.battleOptions.queueSizeNoneButton.help": "Do not display Turn Order Queue",
+	"vcmi.battleOptions.queueSizeAutoButton.help": "Automatically adjust the size of the turn order queue based on the game's resolution(SMALL size is used when playing the game on a resolution with a height lower than 700 pixels, BIG size is used otherwise)",
+	"vcmi.battleOptions.queueSizeSmallButton.help": "Sets turn order queue size to SMALL",
+	"vcmi.battleOptions.queueSizeBigButton.help": "Sets turn order queue size to BIG (not supported if game resolution height is less than 700 pixels)",
 	"vcmi.battleOptions.animationsSpeed1.hover": "",
 	"vcmi.battleOptions.animationsSpeed5.hover": "",
 	"vcmi.battleOptions.animationsSpeed6.hover": "",
-	"vcmi.battleOptions.animationsSpeed1.help": "Sets animation speed to very slow",
-	"vcmi.battleOptions.animationsSpeed5.help": "Sets animation speed to very fast",
-	"vcmi.battleOptions.animationsSpeed6.help": "Sets animation speed to instantaneous",
+	"vcmi.battleOptions.animationsSpeed1.help": "Set animation speed to very slow",
+	"vcmi.battleOptions.animationsSpeed5.help": "Set animation speed to very fast",
+	"vcmi.battleOptions.animationsSpeed6.help": "Set animation speed to instantaneous",
 	"vcmi.battleOptions.skipBattleIntroMusic.hover": "Skip Intro Music",
-	"vcmi.battleOptions.skipBattleIntroMusic.help": "{Skip Intro Music}\n\n Skip short music that plays at beginning of each battle before action starts. Can also be skipped by pressing ESC key.",
-	
-	"vcmi.battleWindow.pressKeyToSkipIntro" : "Press any key to skip battle intro",
+	"vcmi.battleOptions.skipBattleIntroMusic.help": "{Skip Intro Music}\n\nAllow actions during the intro music that plays at the beginning of each battle",
+	"vcmi.battleWindow.pressKeyToSkipIntro" : "Press any key to start battle immediately",
 
 	"vcmi.battleWindow.damageEstimation.melee" : "Attack %CREATURE (%DAMAGE).",
 	"vcmi.battleWindow.damageEstimation.meleeKills" : "Attack %CREATURE (%DAMAGE, %KILLS).",
@@ -96,11 +95,11 @@
 	"vcmi.battleWindow.damageEstimation.kills.1" : "%d will perish",
 
 	"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover" : "Show Available Creatures",
-	"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{Show Available Creatures}\n\n Shows creatures available to purchase instead of their growth in town summary (bottom-left corner).",
+	"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{Show Available Creatures}\n\nShow the number of creatures available to purchase instead of their growth in town summary (bottom-left corner of town screen).",
 	"vcmi.otherOptions.creatureGrowthAsDwellingLabel.hover" : "Show Weekly Growth of Creatures",
-	"vcmi.otherOptions.creatureGrowthAsDwellingLabel.help" : "{Show Weekly Growth of Creatures}\n\n Shows creatures' weekly growth instead of available amount in town summary (bottom-left corner).",
+	"vcmi.otherOptions.creatureGrowthAsDwellingLabel.help" : "{Show Weekly Growth of Creatures}\n\nShow creatures' weekly growth instead of available amount in town summary (bottom-left corner of town screen).",
 	"vcmi.otherOptions.compactTownCreatureInfo.hover": "Compact Creature Info",
-	"vcmi.otherOptions.compactTownCreatureInfo.help": "{Compact Creature Info}\n\n Smaller town creatures information in town summary.",
+	"vcmi.otherOptions.compactTownCreatureInfo.help": "{Compact Creature Info}\n\nShow smaller information for town creatures in town summary (bottom-left corner of town screen).",
 
 	"vcmi.townHall.missingBase"             : "Base building %s must be built first",
 	"vcmi.townHall.noCreaturesToRecruit"    : "There are no creatures to recruit!",
@@ -120,20 +119,20 @@
 	"vcmi.logicalExpressions.allOf"  : "All of the following:",
 	"vcmi.logicalExpressions.noneOf" : "None of the following:",
 
-	"vcmi.heroWindow.openCommander.hover" : "Open commander window",
-	"vcmi.heroWindow.openCommander.help"  : "Displays information about commander of this hero",
+	"vcmi.heroWindow.openCommander.hover" : "Open commander info window",
+	"vcmi.heroWindow.openCommander.help"  : "Shows details about the commander of this hero",
 
-	"vcmi.commanderWindow.artifactMessage" : "Do you want to give this artifact back to hero?",
+	"vcmi.commanderWindow.artifactMessage" : "Do you want to return this artifact to the hero?",
 
 	"vcmi.creatureWindow.showBonuses.hover"    : "Switch to bonuses view",
-	"vcmi.creatureWindow.showBonuses.help"     : "Displays all active bonuses of the commander",
+	"vcmi.creatureWindow.showBonuses.help"     : "Display all active bonuses of the commander",
 	"vcmi.creatureWindow.showSkills.hover"     : "Switch to skills view",
-	"vcmi.creatureWindow.showSkills.help"      : "Displays all learned skills of the commander",
-	"vcmi.creatureWindow.returnArtifact.hover" : "Give back artifact",
-	"vcmi.creatureWindow.returnArtifact.help"  : "Use this button to return stack artifact back into hero backpack",
+	"vcmi.creatureWindow.showSkills.help"      : "Display all learned skills of the commander",
+	"vcmi.creatureWindow.returnArtifact.hover" : "Return artifact",
+	"vcmi.creatureWindow.returnArtifact.help"  : "Click this button to return the artifact to the hero's backpack",
 
 	"vcmi.questLog.hideComplete.hover" : "Hide complete quests",
-	"vcmi.questLog.hideComplete.help"  : "Hide all quests that already completed",
+	"vcmi.questLog.hideComplete.help"  : "Hide all completed quests",
 
 	"vcmi.randomMapTab.widgets.defaultTemplate"      : "(default)",
 	"vcmi.randomMapTab.widgets.templateLabel"        : "Template",
@@ -143,108 +142,106 @@
 	
 	// few strings from WoG used by vcmi
 	"vcmi.stackExperience.description" : "» S t a c k   E x p e r i e n c e   D e t a i l s «\n\nCreature Type ................... : %s\nExperience Rank ................. : %s (%i)\nExperience Points ............... : %i\nExperience Points to Next Rank .. : %i\nMaximum Experience per Battle ... : %i%% (%i)\nNumber of Creatures in stack .... : %i\nMaximum New Recruits\n without losing current Rank .... : %i\nExperience Multiplier ........... : %.2f\nUpgrade Multiplier .............. : %.2f\nExperience after Rank 10 ........ : %i\nMaximum New Recruits to remain at\n Rank 10 if at Maximum Experience : %i",
-	"vcmi.stackExperience.rank.1" : "Basic",
-	"vcmi.stackExperience.rank.2" : "Novice",
-	"vcmi.stackExperience.rank.3" : "Trained",
-	"vcmi.stackExperience.rank.4" : "Skilled",
-	"vcmi.stackExperience.rank.5" : "Proven",
-	"vcmi.stackExperience.rank.6" : "Veteran",
-	"vcmi.stackExperience.rank.7" : "Adept",
-	"vcmi.stackExperience.rank.8" : "Expert",
-	"vcmi.stackExperience.rank.9" : "Elite",
-	"vcmi.stackExperience.rank.10" : "Master",
-	"vcmi.stackExperience.rank.11" : "Ace",
+	"vcmi.stackExperience.rank.0" : "Basic",
+	"vcmi.stackExperience.rank.1" : "Novice",
+	"vcmi.stackExperience.rank.2" : "Trained",
+	"vcmi.stackExperience.rank.3" : "Skilled",
+	"vcmi.stackExperience.rank.4" : "Proven",
+	"vcmi.stackExperience.rank.5" : "Veteran",
+	"vcmi.stackExperience.rank.6" : "Adept",
+	"vcmi.stackExperience.rank.7" : "Expert",
+	"vcmi.stackExperience.rank.8" : "Elite",
+	"vcmi.stackExperience.rank.9" : "Master",
+	"vcmi.stackExperience.rank.10" : "Ace",
 	
 	"core.bonus.ADDITIONAL_ATTACK.name": "Double Strike",
 	"core.bonus.ADDITIONAL_ATTACK.description": "Attacks twice",
 	"core.bonus.ADDITIONAL_RETALIATION.name": "Additional retaliations",
-	"core.bonus.ADDITIONAL_RETALIATION.description": "May Retaliate ${val} extra times",
+	"core.bonus.ADDITIONAL_RETALIATION.description": "May retaliate ${val} extra times",
 	"core.bonus.AIR_IMMUNITY.name": "Air immunity",
-	"core.bonus.AIR_IMMUNITY.description": "Immune to all Air school spells",
+	"core.bonus.AIR_IMMUNITY.description": "Immune to all spells from the school of Air magic",
 	"core.bonus.ATTACKS_ALL_ADJACENT.name": "Attack all around",
 	"core.bonus.ATTACKS_ALL_ADJACENT.description": "Attacks all adjacent enemies",
 	"core.bonus.BLOCKS_RETALIATION.name": "No retaliation",
 	"core.bonus.BLOCKS_RETALIATION.description": "Enemy cannot retaliate",
 	"core.bonus.BLOCKS_RANGED_RETALIATION.name": "No ranged retaliation",
-	"core.bonus.BLOCKS_RANGED_RETALIATION.description": "Enemy cannot Retaliate by shooting",
+	"core.bonus.BLOCKS_RANGED_RETALIATION.description": "Enemy cannot retaliate by using a ranged attack",
 	"core.bonus.CATAPULT.name": "Catapult",
 	"core.bonus.CATAPULT.description": "Attacks siege walls",
-	"core.bonus.CATAPULT_EXTRA_SHOTS.name": "Additional siege attacks",
-	"core.bonus.CATAPULT_EXTRA_SHOTS.description": "Can hit siege walls ${val} extra times per attack",
 	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Reduce Casting Cost (${val})",
-	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Reduces spell cost for hero",
+	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Reduces the spellcasting cost for the hero by ${val}",
 	"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "Magic Damper (${val})",
-	"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description": "Increases Cost of enemy spells",
+	"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description": "Increases spellcasting cost of enemy spells by ${val}",
 	"core.bonus.CHARGE_IMMUNITY.name": "Immune to Charge",
-	"core.bonus.CHARGE_IMMUNITY.description": "Immune to Champion charge",
+	"core.bonus.CHARGE_IMMUNITY.description": "Immune to Cavalier's and Champion's Charge",
 	"core.bonus.DARKNESS.name": "Darkness cover",
-	"core.bonus.DARKNESS.description": "Adds ${val} darkness radius",
+	"core.bonus.DARKNESS.description": "Creates a shroud of darkness with a ${val} radius",
 	"core.bonus.DEATH_STARE.name": "Death Stare (${val}%)",
-	"core.bonus.DEATH_STARE.description": "${val}% chance to kill single creature",
+	"core.bonus.DEATH_STARE.description": "Has a ${val}% chance to kill a single creature",
 	"core.bonus.DEFENSIVE_STANCE.name": "Defense Bonus",
 	"core.bonus.DEFENSIVE_STANCE.description": "+${val} Defense when defending",
 	"core.bonus.DESTRUCTION.name": "Destruction",
 	"core.bonus.DESTRUCTION.description": "Has ${val}% chance to kill extra units after attack",
 	"core.bonus.DOUBLE_DAMAGE_CHANCE.name": "Death Blow",
-	"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}% chance for double damage",
+	"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "Has a ${val}% chance of dealing double base damage when attacking",
 	"core.bonus.DRAGON_NATURE.name": "Dragon",
 	"core.bonus.DRAGON_NATURE.description": "Creature has a Dragon Nature",
 	"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "Direct Damage Immunity",
 	"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "Immune to direct damage spells",
 	"core.bonus.EARTH_IMMUNITY.name": "Earth immunity",
-	"core.bonus.EARTH_IMMUNITY.description": "Immune to all Earth school spells",
+	"core.bonus.EARTH_IMMUNITY.description": "Immune to all spells from the school of Earth magic",
 	"core.bonus.ENCHANTER.name": "Enchanter",
 	"core.bonus.ENCHANTER.description": "Can cast mass ${subtype.spell} every turn",
 	"core.bonus.ENCHANTED.name": "Enchanted",
 	"core.bonus.ENCHANTED.description": "Affected by permanent ${subtype.spell}",
 	"core.bonus.ENEMY_DEFENCE_REDUCTION.name": "Ignore Defense (${val}%)",
-	"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "Ignores part of Defence for the attack",
+	"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "When attacking, ${val}% of the defender's defense is ignored",
 	"core.bonus.FIRE_IMMUNITY.name": "Fire immunity",
-	"core.bonus.FIRE_IMMUNITY.description": "Immune to all Fire school spells",
+	"core.bonus.FIRE_IMMUNITY.description": "Immune to all spells from the school of Fire magic",
 	"core.bonus.FIRE_SHIELD.name": "Fire Shield (${val}%)",
 	"core.bonus.FIRE_SHIELD.description": "Reflects part of melee damage",
 	"core.bonus.FIRST_STRIKE.name": "First Strike",
-	"core.bonus.FIRST_STRIKE.description": "This creature attacks first instead of retaliating",
+	"core.bonus.FIRST_STRIKE.description": "This creature retaliates before being attacked",
 	"core.bonus.FEAR.name": "Fear",
 	"core.bonus.FEAR.description": "Causes Fear on an enemy stack",
 	"core.bonus.FEARLESS.name": "Fearless",
 	"core.bonus.FEARLESS.description": "Immune to Fear ability",
 	"core.bonus.FLYING.name": "Fly",
-	"core.bonus.FLYING.description": "Can Fly (ignores obstacles)",
+	"core.bonus.FLYING.description": "Flies when moving (ignores obstacles)",
 	"core.bonus.FREE_SHOOTING.name": "Shoot Close",
-	"core.bonus.FREE_SHOOTING.description": "Can shoot in Close Combat",
+	"core.bonus.FREE_SHOOTING.description": "Can use ranged attacks at melee range",
 	"core.bonus.GARGOYLE.name": "Gargoyle",
-	"core.bonus.GARGOYLE.description": "Cannot be rised or healed",
+	"core.bonus.GARGOYLE.description": "Cannot be raised or healed",
 	"core.bonus.GENERAL_DAMAGE_REDUCTION.name": "Reduce Damage (${val}%)",
-	"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "Reduces physical damage from ranged or melee",
+	"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "Reduces physical damage from ranged or melee attacks",
 	"core.bonus.HATE.name": "Hates ${subtype.creature}",
-	"core.bonus.HATE.description": "Does ${val}% more damage",
+	"core.bonus.HATE.description": "Does ${val}% more damage to ${subtype.creature}",
 	"core.bonus.HEALER.name": "Healer",
 	"core.bonus.HEALER.description": "Heals allied units",
 	"core.bonus.HP_REGENERATION.name": "Regeneration",
 	"core.bonus.HP_REGENERATION.description": "Heals ${SHval} hit points every round",
-	"core.bonus.JOUSTING.name": "Champion Charge",
-	"core.bonus.JOUSTING.description": "+${val}% damage per hex travelled",
+	"core.bonus.JOUSTING.name": "Champion charge",
+	"core.bonus.JOUSTING.description": "+${val}% damage for each hex travelled",
 	"core.bonus.KING.name": "King",
 	"core.bonus.KING.description": "Vulnerable to SLAYER level ${val} or higher",
 	"core.bonus.LEVEL_SPELL_IMMUNITY.name": "Spell immunity 1-${val}",
 	"core.bonus.LEVEL_SPELL_IMMUNITY.description": "Immune to spells of levels 1-${val}",
 	"core.bonus.LIMITED_SHOOTING_RANGE.name" : "Limited shooting range",
-	"core.bonus.LIMITED_SHOOTING_RANGE.description" : "Cannot shoot targets beyond ${val} hexes away",
+	"core.bonus.LIMITED_SHOOTING_RANGE.description" : "Unable to target units farther than ${val} hexes",
 	"core.bonus.LIFE_DRAIN.name": "Drain life (${val}%)",
 	"core.bonus.LIFE_DRAIN.description": "Drains ${val}% of damage dealt",
 	"core.bonus.MANA_CHANNELING.name": "Magic Channel ${val}%",
-	"core.bonus.MANA_CHANNELING.description": "Gives your hero mana spent by enemy",
+	"core.bonus.MANA_CHANNELING.description": "Gives your hero ${val}% of the mana spent by the enemy",
 	"core.bonus.MANA_DRAIN.name": "Mana Drain",
 	"core.bonus.MANA_DRAIN.description": "Drains ${val} mana every turn",
 	"core.bonus.MAGIC_MIRROR.name": "Magic Mirror (${val}%)",
-	"core.bonus.MAGIC_MIRROR.description": "${val}% chance to redirects an offensive spell to enemy",
-	"core.bonus.MAGIC_RESISTANCE.name": "Magic Resistance(${val}%)",
-	"core.bonus.MAGIC_RESISTANCE.description": "${val}% chance to resist enemy spell",
+	"core.bonus.MAGIC_MIRROR.description": "Has a ${val}% chance to redirect an offensive spell to an enemy unit",
+	"core.bonus.MAGIC_RESISTANCE.name": "Magic Resistance (${val}%)",
+	"core.bonus.MAGIC_RESISTANCE.description": "Has a ${val}% chance to resist an enemy spell",
 	"core.bonus.MIND_IMMUNITY.name": "Mind Spell Immunity",
 	"core.bonus.MIND_IMMUNITY.description": "Immune to Mind-type spells",
 	"core.bonus.NO_DISTANCE_PENALTY.name": "No distance penalty",
-	"core.bonus.NO_DISTANCE_PENALTY.description": "Full damage from any distance",
+	"core.bonus.NO_DISTANCE_PENALTY.description": "Does full damage at any distance",
 	"core.bonus.NO_MELEE_PENALTY.name": "No melee penalty",
 	"core.bonus.NO_MELEE_PENALTY.description": "Creature has no Melee Penalty",
 	"core.bonus.NO_MORALE.name": "Neutral Morale",
@@ -263,10 +260,6 @@
 	"core.bonus.REBIRTH.description": "${val}% of stack will rise after death",
 	"core.bonus.RETURN_AFTER_STRIKE.name": "Attack and Return",
 	"core.bonus.RETURN_AFTER_STRIKE.description": "Returns after melee attack",
-	"core.bonus.SELF_LUCK.name": "Positive luck",
-	"core.bonus.SELF_LUCK.description": "Always has Positive Luck",
-	"core.bonus.SELF_MORALE.name": "Positive morale",
-	"core.bonus.SELF_MORALE.description": "Always has Positive Morale",
 	"core.bonus.SHOOTER.name": "Ranged",
 	"core.bonus.SHOOTER.description": "Creature can shoot",
 	"core.bonus.SHOOTS_ALL_ADJACENT.name": "Shoot all around",
@@ -276,11 +269,11 @@
 	"core.bonus.SPELLCASTER.name": "Spellcaster",
 	"core.bonus.SPELLCASTER.description": "Can cast ${subtype.spell}",
 	"core.bonus.SPELL_AFTER_ATTACK.name": "Cast After Attack",
-	"core.bonus.SPELL_AFTER_ATTACK.description": "${val}% to cast ${subtype.spell} after attack",
+	"core.bonus.SPELL_AFTER_ATTACK.description": "Has a ${val}% chance to cast ${subtype.spell} after it attacks",
 	"core.bonus.SPELL_BEFORE_ATTACK.name": "Cast Before Attack",
-	"core.bonus.SPELL_BEFORE_ATTACK.description": "${val}% to cast ${subtype.spell} before attack",
+	"core.bonus.SPELL_BEFORE_ATTACK.description": "Has a ${val}% chance to cast ${subtype.spell} before it attacks",
 	"core.bonus.SPELL_DAMAGE_REDUCTION.name": "Spell Resistance",
-	"core.bonus.SPELL_DAMAGE_REDUCTION.description": "Damage from spells reduced ${val}%.",
+	"core.bonus.SPELL_DAMAGE_REDUCTION.description": "Damage from spells reduced by ${val}%.",
 	"core.bonus.SPELL_IMMUNITY.name": "Spell immunity",
 	"core.bonus.SPELL_IMMUNITY.description": "Immune to ${subtype.spell}",
 	"core.bonus.SPELL_LIKE_ATTACK.name": "Spell-like attack",
@@ -288,7 +281,7 @@
 	"core.bonus.SPELL_RESISTANCE_AURA.name": "Aura of Resistance",
 	"core.bonus.SPELL_RESISTANCE_AURA.description": "Nearby stacks get ${val}% magic resistance",
 	"core.bonus.SUMMON_GUARDIANS.name": "Summon guardians",
-	"core.bonus.SUMMON_GUARDIANS.description": "At battle start summons ${subtype.creature} (${val}%)",
+	"core.bonus.SUMMON_GUARDIANS.description": "At the start of battle summons ${subtype.creature} (${val}%)",
 	"core.bonus.SYNERGY_TARGET.name": "Synergizable",
 	"core.bonus.SYNERGY_TARGET.description": "This creature is vulnerable to synergy effect",
 	"core.bonus.TWO_HEX_ATTACK_BREATH.name": "Breath",
@@ -296,13 +289,13 @@
 	"core.bonus.THREE_HEADED_ATTACK.name": "Three-headed attack",
 	"core.bonus.THREE_HEADED_ATTACK.description": "Attacks three adjacent units",
 	"core.bonus.TRANSMUTATION.name": "Transmutation",
-	"core.bonus.TRANSMUTATION.description": "${val}% chance to transform attacked unit to other type",
+	"core.bonus.TRANSMUTATION.description": "${val}% chance to transform attacked unit to a different type",
 	"core.bonus.UNDEAD.name": "Undead",
 	"core.bonus.UNDEAD.description": "Creature is Undead",
 	"core.bonus.UNLIMITED_RETALIATIONS.name": "Unlimited retaliations",
-	"core.bonus.UNLIMITED_RETALIATIONS.description": "Retaliates any number of attacks",
+	"core.bonus.UNLIMITED_RETALIATIONS.description": "Can retaliate against an unlimited number of attacks",
 	"core.bonus.WATER_IMMUNITY.name": "Water immunity",
-	"core.bonus.WATER_IMMUNITY.description": "Immune to all Water school spells",
+	"core.bonus.WATER_IMMUNITY.description": "Immune to all spells from the school of Water magic",
 	"core.bonus.WIDE_BREATH.name": "Wide breath",
 	"core.bonus.WIDE_BREATH.description": "Wide breath attack (multiple hexes)"
 }

+ 28 - 23
Mods/vcmi/config/vcmi/german.json

@@ -47,6 +47,8 @@
 	"vcmi.systemOptions.framerateButton.hover"  : "FPS anzeigen",
 	"vcmi.systemOptions.framerateButton.help"   : "{FPS anzeigen}\n\n Schaltet die Sichtbarkeit des Zählers für die Bilder pro Sekunde in der Ecke des Spielfensters um.",
 
+	"vcmi.adventureOptions.infoBarPick.hover" : "Meldungen im Infobereich anzeigen",
+	"vcmi.adventureOptions.infoBarPick.help" : "{Meldungen im Infobereich anzeigen}\n\nWann immer möglich, werden Spielnachrichten von besuchten Kartenobjekten in der Infoleiste angezeigt, anstatt als Popup-Fenster zu erscheinen",
 	"vcmi.adventureOptions.numericQuantities.hover" : "Numerische Kreaturenmengen",
 	"vcmi.adventureOptions.numericQuantities.help" : "{Numerische Kreaturenmengen}\n\n Zeigt die ungefähre Menge der feindlichen Kreaturen im numerischen Format A-B an.",
 	"vcmi.adventureOptions.forceMovementInfo.hover" : "Bewegungskosten immer anzeigen",
@@ -79,6 +81,19 @@
 	"vcmi.battleOptions.animationsSpeed6.help": "Setzt die Animationsgeschwindigkeit auf sofort",
 	"vcmi.battleOptions.skipBattleIntroMusic.hover": "Intro-Musik überspringen",
 	"vcmi.battleOptions.skipBattleIntroMusic.help": "{Intro-Musik überspringen}\n\n Überspringe die kurze Musik, die zu Beginn eines jeden Kampfes gespielt wird, bevor die Action beginnt. Kann auch durch Drücken der ESC-Taste übersprungen werden.",
+	
+	"vcmi.battleWindow.pressKeyToSkipIntro" : "Beliebige Taste drücken, um das Kampf-Intro zu überspringen",
+
+	"vcmi.battleWindow.damageEstimation.melee" : "Angriff auf %CREATURE (%DAMAGE).",
+	"vcmi.battleWindow.damageEstimation.meleeKills" : "Angriff auf %CREATURE (%DAMAGE, %KILLS).",
+	"vcmi.battleWindow.damageEstimation.ranged" : "Schuss auf %CREATURE (%SHOTS, %DAMAGE).",
+	"vcmi.battleWindow.damageEstimation.rangedKills" : "Schuss auf %CREATURE (%SHOTS, %DAMAGE, %KILLS).",
+	"vcmi.battleWindow.damageEstimation.shots" : "%d Schüsse verbleibend",
+	"vcmi.battleWindow.damageEstimation.shots.1" : "%d Schüsse verbleibend",
+	"vcmi.battleWindow.damageEstimation.damage" : "%d Schaden",
+	"vcmi.battleWindow.damageEstimation.damage.1" : "%d Schaden",
+	"vcmi.battleWindow.damageEstimation.kills" : "%d werden verenden",
+	"vcmi.battleWindow.damageEstimation.kills.1" : "%d werden verenden",
 
 	"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover" : "Verfügbare Kreaturen anzeigen",
 	"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{Verfügbare Kreaturen anzeigen}\n\n Zeigt in der Stadtübersicht (linke untere Ecke) die zum Kauf verfügbaren Kreaturen anstelle ihres Wachstums an.",
@@ -128,17 +143,17 @@
 	
 	// few strings from WoG used by vcmi
 	"vcmi.stackExperience.description" : "» D e t a i l s   z u r   S t a p e l e r f a h r u n g «\n\nKreatur-Typ ................... : %s\nErfahrungsrang ................. : %s (%i)\nErfahrungspunkte ............... : %i\nErfahrungspunkte für den nächsten Rang .. : %i\nMaximale Erfahrung pro Kampf ... : %i%% (%i)\nAnzahl der Kreaturen im Stapel .... : %i\nMaximale Anzahl neuer Rekruten\n ohne Verlust von aktuellem Rang .... : %i\nErfahrungs-Multiplikator ........... : %.2f\nUpgrade-Multiplikator .............. : %.2f\nErfahrung nach Rang 10 ........ : %i\nMaximale Anzahl der neuen Rekruten, die bei\n Rang 10 bei maximaler Erfahrung übrig sind : %i",
-	"vcmi.stackExperience.rank.1" : "Grundlagen",
-	"vcmi.stackExperience.rank.2" : "Neuling",
-	"vcmi.stackExperience.rank.3" : "Ausgebildet",
-	"vcmi.stackExperience.rank.4" : "Kompetent",
-	"vcmi.stackExperience.rank.5" : "Bewährt",
-	"vcmi.stackExperience.rank.6" : "Veteran",
-	"vcmi.stackExperience.rank.7" : "Gekonnt",
-	"vcmi.stackExperience.rank.8" : "Experte",
-	"vcmi.stackExperience.rank.9" : "Elite",
-	"vcmi.stackExperience.rank.10" : "Meister",
-	"vcmi.stackExperience.rank.11" : "Ass",
+	"vcmi.stackExperience.rank.0" : "Grundlagen",
+	"vcmi.stackExperience.rank.1" : "Neuling",
+	"vcmi.stackExperience.rank.2" : "Ausgebildet",
+	"vcmi.stackExperience.rank.3" : "Kompetent",
+	"vcmi.stackExperience.rank.4" : "Bewährt",
+	"vcmi.stackExperience.rank.5" : "Veteran",
+	"vcmi.stackExperience.rank.6" : "Gekonnt",
+	"vcmi.stackExperience.rank.7" : "Experte",
+	"vcmi.stackExperience.rank.8" : "Elite",
+	"vcmi.stackExperience.rank.9" : "Meister",
+	"vcmi.stackExperience.rank.10" : "Ass",
 	
 	"core.bonus.ADDITIONAL_ATTACK.name": "Doppelschlag",
 	"core.bonus.ADDITIONAL_ATTACK.description": "Greift zweimal an",
@@ -154,8 +169,6 @@
 	"core.bonus.BLOCKS_RANGED_RETALIATION.description": "Feind kann nicht durch Schießen vergelten",
 	"core.bonus.CATAPULT.name": "Katapult",
 	"core.bonus.CATAPULT.description": "Greift Belagerungsmauern an",
-	"core.bonus.CATAPULT_EXTRA_SHOTS.name": "Zusätzliche Belagerungsangriffe",
-	"core.bonus.CATAPULT_EXTRA_SHOTS.description": "Kann Belagerungsmauern ${val} zusätzliche Male pro Angriff treffen",
 	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Reduziere Zauberkosten (${val})",
 	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Reduziert die Zauberkosten für den Helden",
 	"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "Zauberdämpfer (${val})",
@@ -210,12 +223,8 @@
 	"core.bonus.HP_REGENERATION.description": "Heilt ${SHval} Trefferpunkte jede Runde",
 	"core.bonus.JOUSTING.name": "Champion Charge",
 	"core.bonus.JOUSTING.description": "+${val}% Schaden pro zurückgelegtem Feld",
-	"core.bonus.KING1.name": "König 1",
-	"core.bonus.KING1.description": "Anfällig für grundlegende SLAYER",
-	"core.bonus.KING2.name": "König 2",
-	"core.bonus.KING2.description": "Anfällig für erweiterte SLAYER",
-	"core.bonus.KING3.name": "König3",
-	"core.bonus.KING3.description":"Anfällig für Experten-SLAYER",
+	"core.bonus.KING.name": "König",
+	"core.bonus.KING.description": "Anfällig für SLAYER Level ${val} oder höher",
 	"core.bonus.LEVEL_SPELL_IMMUNITY.name": "Zauberimmunität 1-${val}",
 	"core.bonus.LEVEL_SPELL_IMMUNITY.description": "Immun gegen Zaubersprüche der Stufen 1-${val}",
 	"core.bonus.LIMITED_SHOOTING_RANGE.name" : "Begrenzte Schussweite",
@@ -252,10 +261,6 @@
 	"core.bonus.REBIRTH.description": "${val}% des Stacks wird nach dem Tod auferstehen",
 	"core.bonus.RETURN_AFTER_STRIKE.name": "Angriff und Rückkehr",
 	"core.bonus.RETURN_AFTER_STRIKE.description": "Kehrt nach Nahkampfangriff zurück",
-	"core.bonus.SELF_LUCK.name": "Positives Glück",
-	"core.bonus.SELF_LUCK.description": "Hat immer positives Glück",
-	"core.bonus.SELF_MORALE.name": "Positive Moral",
-	"core.bonus.SELF_MORALE.description": "Hat immer positive Moral",
 	"core.bonus.SHOOTER.name": "Fernkämpfer",
 	"core.bonus.SHOOTER.description": "Kreatur kann schießen",
 	"core.bonus.SHOOTS_ALL_ADJACENT.name": "Schießt rundherum",

+ 7 - 11
Mods/vcmi/config/vcmi/polish.json

@@ -79,10 +79,10 @@
 	"vcmi.battleOptions.animationsSpeed1.help": "Ustawia szybkość animacji na bardzo wolną",
 	"vcmi.battleOptions.animationsSpeed5.help": "Ustawia szybkość animacji na bardzo szybką",
 	"vcmi.battleOptions.animationsSpeed6.help": "Ustawia szybkość animacji na błyskawiczną",
-	"vcmi.battleOptions.skipBattleIntroMusic.hover": "Pomiń muzykę startową",
-	"vcmi.battleOptions.skipBattleIntroMusic.help": "{Pomiń muzykę startową}\n\n Pomija krótką muzykę, która jest odtwarzana na początku każdej bitwy przed rozpoczęciem akcji. Może również być pominięta poprzez naciśnięcie ESC.",
+	"vcmi.battleOptions.skipBattleIntroMusic.hover": "Pomiń czekanie startowe",
+	"vcmi.battleOptions.skipBattleIntroMusic.help": "{Pomiń czekanie startowe}\n\n Pomija konieczność czekania podczas muzyki startowej, która jest odtwarzana na początku każdej bitwy przed rozpoczęciem akcji.",
 
-	"vcmi.battleWindow.pressKeyToSkipIntro" : "Naciśnij dowolny klawisz by pominąć muzykę startową",
+	"vcmi.battleWindow.pressKeyToSkipIntro" : "Naciśnij dowolny klawisz by rozpocząć bitwę natychmiastowo",
 
 	"vcmi.battleWindow.damageEstimation.melee" : "Atakuj %CREATURE (%DAMAGE).",
 	"vcmi.battleWindow.damageEstimation.meleeKills" : "Atakuj %CREATURE (%DAMAGE, %KILLS).",
@@ -92,8 +92,8 @@
 	"vcmi.battleWindow.damageEstimation.shots.1" : "pozostał %d strzał",
 	"vcmi.battleWindow.damageEstimation.damage" : "obrażenia: %d",
 	"vcmi.battleWindow.damageEstimation.damage.1" : "obrażenia: %d",
-	"vcmi.battleWindow.damageEstimation.kills" : "%d zginie",
-	"vcmi.battleWindow.damageEstimation.kills.1" : "%d zginie",
+	"vcmi.battleWindow.damageEstimation.kills" : "zginie: %d",
+	"vcmi.battleWindow.damageEstimation.kills.1" : "zginie: %d",
 
 	"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover" : "Pokaż dostępne stworzenia",
 	"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{Pokaż dostępne stworzenia}\n\n Pokazuje dostępne stworzenia zamiast tygodniowego przyrostu w podsumowaniu miasta (lewy dolny róg).",
@@ -141,6 +141,8 @@
 	"vcmi.randomMapTab.widgets.teamAlignmentsLabel"  : "Sojusze",
 	"vcmi.randomMapTab.widgets.roadTypesLabel"       : "Typy dróg",
 
+	//WoG strings translations missing here - should be taken from polish WoG translation
+
 	"core.bonus.ADDITIONAL_ATTACK.name": "Podwójne Uderzenie",
 	"core.bonus.ADDITIONAL_ATTACK.description": "Atakuje podwójnie",
 	"core.bonus.ADDITIONAL_RETALIATION.name": "Dodatkowe kontrataki",
@@ -155,8 +157,6 @@
 	"core.bonus.BLOCKS_RANGED_RETALIATION.description": "Wróg nie może kontratakować poprzez strzelanie",
 	"core.bonus.CATAPULT.name": "Katapulta",
 	"core.bonus.CATAPULT.description": "Atakuje mury obronne",
-	"core.bonus.CATAPULT_EXTRA_SHOTS.name": "Dodatkowe ataki oblężnicze",
-	"core.bonus.CATAPULT_EXTRA_SHOTS.description": "Może uderzyć mury obronne ${val} dodatkowych razy na atak",
 	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Zmniejsz koszt czarów (${val})",
 	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Zmniejsza koszt czaru bohatera",
 	"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "Tłumienie magii (${val})",
@@ -249,10 +249,6 @@
 	"core.bonus.REBIRTH.description": "${val}% stworzeń powstanie po śmierci",
 	"core.bonus.RETURN_AFTER_STRIKE.name": "Atak i Powrót",
 	"core.bonus.RETURN_AFTER_STRIKE.description": "Wraca po ataku wręcz",
-	"core.bonus.SELF_LUCK.name": "Pozytywne szczęście",
-	"core.bonus.SELF_LUCK.description": "Zawsze posiada pozytywne szczęście",
-	"core.bonus.SELF_MORALE.name": "Pozytywne Morale",
-	"core.bonus.SELF_MORALE.description": "Zawsze posiada pozytywne morale",
 	"core.bonus.SHOOTER.name": "Dystansowy",
 	"core.bonus.SHOOTER.description": "Stworzenie może strzelać",
 	"core.bonus.SHOOTS_ALL_ADJACENT.name": "Ostrzeliwuje wszystko dookoła",

+ 11 - 17
Mods/vcmi/config/vcmi/russian.json

@@ -155,17 +155,17 @@
 
 	// few strings from WoG used by vcmi
 	"vcmi.stackExperience.description" : "» О п ы т   с у щ е с т в «\n\nТип существа ................... : %s\nРанг опыта ................. : %s (%i)\nОчки опыта ............... : %i\nДо следующего .. : %i\nМаксимум за битву ... : %i%% (%i)\nЧисло в отряде .... : %i\nМаксимум новичков\n без потери ранга .... : %i\nМножитель опыта ........... : %.2f\nМножитель улучшения .......... : %.2f\nОпыт после 10 ранга ........ : %i\nМаксимум новичков для сохранения\n ранга 10 при максимальном опыте : %i",
-	"vcmi.stackExperience.rank.1" : "Рекрут",
-	"vcmi.stackExperience.rank.2" : "Новичок",
-	"vcmi.stackExperience.rank.3" : "Тренирован",
-	"vcmi.stackExperience.rank.4" : "Знающий",
-	"vcmi.stackExperience.rank.5" : "Подтвержденный",
-	"vcmi.stackExperience.rank.6" : "Ветеран",
-	"vcmi.stackExperience.rank.7" : "Адепт",
-	"vcmi.stackExperience.rank.8" : "Эксперт",
-	"vcmi.stackExperience.rank.9" : "Элита",
-	"vcmi.stackExperience.rank.10" : "Мастер",
-	"vcmi.stackExperience.rank.11" : "Ас",
+	"vcmi.stackExperience.rank.0" : "Рекрут",
+	"vcmi.stackExperience.rank.1" : "Новичок",
+	"vcmi.stackExperience.rank.2" : "Тренирован",
+	"vcmi.stackExperience.rank.3" : "Знающий",
+	"vcmi.stackExperience.rank.4" : "Подтвержденный",
+	"vcmi.stackExperience.rank.5" : "Ветеран",
+	"vcmi.stackExperience.rank.6" : "Адепт",
+	"vcmi.stackExperience.rank.7" : "Эксперт",
+	"vcmi.stackExperience.rank.8" : "Элита",
+	"vcmi.stackExperience.rank.9" : "Мастер",
+	"vcmi.stackExperience.rank.10" : "Ас",
 
 	"core.bonus.ADDITIONAL_ATTACK.name": "Двойной удар",
 	"core.bonus.ADDITIONAL_ATTACK.description": "Бьет дважды",
@@ -181,8 +181,6 @@
 	"core.bonus.BLOCKS_RANGED_RETALIATION.description": "Враг не отвечает в дальнем бою",
 	"core.bonus.CATAPULT.name": "Стенобитное орудие",
 	"core.bonus.CATAPULT.description": "Может атаковать стены",
-	"core.bonus.CATAPULT_EXTRA_SHOTS.name": "Дополнительные атаки стен",
-	"core.bonus.CATAPULT_EXTRA_SHOTS.description": "Может дополнительно бить в стены ${val} раз за атаку",
 	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Снижение стоимости заклинаний (${val})",
 	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Снижаемость стоимость заклинаний для героя",
 	"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "Подавитель магии (${val})",
@@ -275,10 +273,6 @@
 	"core.bonus.REBIRTH.description": "${val}% отряда оживет после его гибели",
 	"core.bonus.RETURN_AFTER_STRIKE.name": "Атака с возвратом",
 	"core.bonus.RETURN_AFTER_STRIKE.description": "После атаки возвращается на начальный гекс",
-	"core.bonus.SELF_LUCK.name": "Удачливый",
-	"core.bonus.SELF_LUCK.description": "Удача всегда позитивна",
-	"core.bonus.SELF_MORALE.name": "Воодушевленный",
-	"core.bonus.SELF_MORALE.description": "Боевой дух всегда позитивен",
 	"core.bonus.SHOOTER.name": "Стрелок",
 	"core.bonus.SHOOTER.description": "Совершает атаки в дальнем бою",
 	"core.bonus.SHOOTS_ALL_ADJACENT.name": "Стреляет по области",

+ 11 - 17
Mods/vcmi/config/vcmi/spanish.json

@@ -131,17 +131,17 @@
 	
 	// few strings from WoG used by vcmi
     "vcmi.stackExperience.description" : "» D e t a l l e s  d e  E x p e r i e n c i a  d e l  G r u p o «\n\nTipo de Criatura ................ : %s\nRango de Experiencia ............ : %s (%i)\nPuntos de Experiencia ............ : %i\nPuntos de Experiencia para el\nSiguiente Rango ............... : %i\nExperiencia Máxima por Batalla .. : %i%% (%i)\nNúmero de Criaturas en el grupo .. : %i\nMáximo de Nuevos Reclutas sin\nPerder el Rango Actual ......... : %i\nMultiplicador de Experiencia .... : %.2f\nMultiplicador de Actualización .. : %.2f\nExperiencia después del Rango 10 : %i\nMáximo de Nuevos Reclutas para\nMantener el Rango 10 si\nEstá en la Experiencia Máxima : %i",
-    "vcmi.stackExperience.rank.1" : "Básico",
-    "vcmi.stackExperience.rank.2" : "Novato",
-    "vcmi.stackExperience.rank.3" : "Entrenado",
-    "vcmi.stackExperience.rank.4" : "Hábil",
-    "vcmi.stackExperience.rank.5" : "Probado",
-    "vcmi.stackExperience.rank.6" : "Veterano",
-    "vcmi.stackExperience.rank.7" : "Experto",
-    "vcmi.stackExperience.rank.8" : "Experto Superior",
-    "vcmi.stackExperience.rank.9" : "Élite",
-    "vcmi.stackExperience.rank.10" : "Maestro",
-    "vcmi.stackExperience.rank.11" : "As",
+    "vcmi.stackExperience.rank.0" : "Básico",
+    "vcmi.stackExperience.rank.1" : "Novato",
+    "vcmi.stackExperience.rank.2" : "Entrenado",
+    "vcmi.stackExperience.rank.3" : "Hábil",
+    "vcmi.stackExperience.rank.4" : "Probado",
+    "vcmi.stackExperience.rank.5" : "Veterano",
+    "vcmi.stackExperience.rank.6" : "Experto",
+    "vcmi.stackExperience.rank.7" : "Experto Superior",
+    "vcmi.stackExperience.rank.8" : "Élite",
+    "vcmi.stackExperience.rank.9" : "Maestro",
+    "vcmi.stackExperience.rank.10" : "As",
 
     "core.bonus.ADDITIONAL_ATTACK.name": "Doble Ataque",
     "core.bonus.ADDITIONAL_ATTACK.description": "Ataca dos veces",
@@ -157,8 +157,6 @@
     "core.bonus.BLOCKS_RANGED_RETALIATION.description": "El enemigo no puede contraatacar disparando",
     "core.bonus.CATAPULT.name": "Catapulta",
     "core.bonus.CATAPULT.description": "Ataca a las paredes de asedio",
-    "core.bonus.CATAPULT_EXTRA_SHOTS.name": "Ataques adicionales de asedio",
-    "core.bonus.CATAPULT_EXTRA_SHOTS.description": "Puede golpear las paredes de asedio ${val} veces adicionales por ataque",
     "core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Reducir coste del conjuro (${val})",
     "core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Reduce el coste del conjuro para el héroe",
     "core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "Disminuir efecto mágico (${val})",
@@ -257,10 +255,6 @@
     "core.bonus.REBIRTH.description": "El ${val}% del grupo resucitará después de la muerte",
     "core.bonus.RETURN_AFTER_STRIKE.name": "Atacar y volver",
     "core.bonus.RETURN_AFTER_STRIKE.description": "Regresa después de un ataque cuerpo a cuerpo",
-    "core.bonus.SELF_LUCK.name": "Suerte positiva",
-    "core.bonus.SELF_LUCK.description": "Siempre tiene suerte positiva",
-    "core.bonus.SELF_MORALE.name": "Moral positiva",
-    "core.bonus.SELF_MORALE.description": "Siempre tiene moral positiva",
     "core.bonus.SHOOTER.name": "A distancia",
     "core.bonus.SHOOTER.description": "La criatura puede disparar",
     "core.bonus.SHOOTS_ALL_ADJACENT.name": "Dispara en todas direcciones",

+ 11 - 17
Mods/vcmi/config/vcmi/ukrainian.json

@@ -157,8 +157,6 @@
 	"core.bonus.BLOCKS_RANGED_RETALIATION.description" : "Ворог не може відповісти пострілом",
 	"core.bonus.CATAPULT.name" : "Катапульта",
 	"core.bonus.CATAPULT.description" : "Атакує стіни фортеці",
-	"core.bonus.CATAPULT_EXTRA_SHOTS.name" : "Додаткові атаки стін",
-	"core.bonus.CATAPULT_EXTRA_SHOTS.description" : "Може вражати стіни фортеці ${val} додаткових разів за атаку",
 	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name" : "Зменшує вартість закляття (${val})",
 	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description" : "Зменшує вартість закляття для героя",
 	"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name" : "Демпфер магії (${val})",
@@ -249,10 +247,6 @@
 	"core.bonus.REBIRTH.description" : "${val}% загону відродиться після смерті",
 	"core.bonus.RETURN_AFTER_STRIKE.name" : "Атакує і повертається",
 	"core.bonus.RETURN_AFTER_STRIKE.description" : "Повертається після атаки ближнього бою",
-	"core.bonus.SELF_LUCK.name" : "Позитивна удача",
-	"core.bonus.SELF_LUCK.description" : "Завжди має позитивну удачу",
-	"core.bonus.SELF_MORALE.name" : "Позитивний бойовий дух",
-	"core.bonus.SELF_MORALE.description" : "Завжди має позитивний бойовий дух",
 	"core.bonus.SHOOTER.name" : "Стрілок",
 	"core.bonus.SHOOTER.description" : "Істота може стріляти",
 	"core.bonus.SHOOTS_ALL_ADJACENT.name" : "Стріляйте по площі",
@@ -295,15 +289,15 @@
 	"core.bonus.LIMITED_SHOOTING_RANGE.description" : "Не може стріляти по цілях на відстані більше ${val} гексів",
 	
 	"vcmi.stackExperience.description" : "» S t a c k   E x p e r i e n c e   D e t a i l s «\n\nCreature Type ................... : %s\nExperience Rank ................. : %s (%i)\nExperience Points ............... : %i\nExperience Points to Next Rank .. : %i\nMaximum Experience per Battle ... : %i%% (%i)\nNumber of Creatures in stack .... : %i\nMaximum New Recruits\n without losing current Rank .... : %i\nExperience Multiplier ........... : %.2f\nUpgrade Multiplier .............. : %.2f\nExperience after Rank 10 ........ : %i\nMaximum New Recruits to remain at\n Rank 10 if at Maximum Experience : %i",
-	"vcmi.stackExperience.rank.1" :  "Початковий",
-	"vcmi.stackExperience.rank.2" :  "Новачок",
-	"vcmi.stackExperience.rank.3" :  "Підготовлений",
-	"vcmi.stackExperience.rank.4" :  "Досвідчений",
-	"vcmi.stackExperience.rank.5" :  "Випробуваний",
-	"vcmi.stackExperience.rank.6" :  "Ветеран",
-	"vcmi.stackExperience.rank.7" :  "Адепт",
-	"vcmi.stackExperience.rank.8" :  "Експерт",
-	"vcmi.stackExperience.rank.9" :  "Еліта",
-	"vcmi.stackExperience.rank.10" : "Майстер",
-	"vcmi.stackExperience.rank.11" : "Профі",
+	"vcmi.stackExperience.rank.0" :  "Початковий",
+	"vcmi.stackExperience.rank.1" :  "Новачок",
+	"vcmi.stackExperience.rank.2" :  "Підготовлений",
+	"vcmi.stackExperience.rank.3" :  "Досвідчений",
+	"vcmi.stackExperience.rank.4" :  "Випробуваний",
+	"vcmi.stackExperience.rank.5" :  "Ветеран",
+	"vcmi.stackExperience.rank.6" :  "Адепт",
+	"vcmi.stackExperience.rank.7" :  "Експерт",
+	"vcmi.stackExperience.rank.8" :  "Еліта",
+	"vcmi.stackExperience.rank.9" : "Майстер",
+	"vcmi.stackExperience.rank.10" : "Профі",
 }

+ 0 - 5
Mods/vcmi/mod.json

@@ -16,7 +16,6 @@
 		"name" : "VCMI - grundlegende Dateien",
 		"description" : "Grundlegende Dateien, die für die korrekte Ausführung von VCMI erforderlich sind",
 		"author" : "VCMI-Team",
-		"modType" : "Grafik",
 		
 		"skipValidation" : true,
 		"translations" : [
@@ -28,7 +27,6 @@
 		"name" : "Podstawowe pliki VCMI",
 		"description" : "Dodatkowe pliki wymagane do prawidłowego działania VCMI",
 		"author" : "Zespół VCMI",
-		"modType" : "Graficzny",
 		
 		"skipValidation" : true,
 		"translations" : [
@@ -40,7 +38,6 @@
 		"name" : "Ключевые файлы VCMI",
 		"description" : "Файлы, необходимые для полноценной работы VCMI",
 		"author" : "Команда VCMI",
-		"modType" : "Графический",
 
 		"skipValidation" : true,
 		"translations" : [
@@ -52,7 +49,6 @@
 		"name" : "VCMI - ключові файли",
 		"description" : "Ключові файли необхідні для повноцінної роботи VCMI",
 		"author" : "Команда VCMI",
-		"modType" : "Графіка",
 		
 		"translations" : [
 			"config/vcmi/ukrainian.json"
@@ -63,7 +59,6 @@
 		"name" : "VCMI - ficheros necesarios",
 		"description" : "Ficheros necesarios para ejecutar VCMI correctamente",
 		"author" : "Abel Rivas",
-		"modType" : "Gráfico",
 		
 		"skipValidation" : true,
 		"translations" : [

+ 40 - 13
client/CMusicHandler.cpp

@@ -142,11 +142,13 @@ Mix_Chunk *CSoundHandler::GetSoundChunk(std::string &sound, bool cache)
 
 int CSoundHandler::ambientDistToVolume(int distance) const
 {
-	if(distance >= ambientConfig["distances"].Vector().size())
+	const auto & distancesVector = ambientConfig["distances"].Vector();
+
+	if(distance >= distancesVector.size())
 		return 0;
 
-	int volume = static_cast<int>(ambientConfig["distances"].Vector()[distance].Integer());
-	return volume * (int)ambientConfig["volume"].Integer() * getVolume() / 10000;
+	int volume = static_cast<int>(distancesVector[distance].Integer());
+	return volume * (int)ambientConfig["volume"].Integer() / 100;
 }
 
 void CSoundHandler::ambientStopSound(std::string soundId)
@@ -211,7 +213,20 @@ void CSoundHandler::setVolume(ui32 percent)
 	CAudioBase::setVolume(percent);
 
 	if (initialized)
+	{
 		setChannelVolume(-1, volume);
+
+		for (auto const & channel : channelVolumes)
+			updateChannelVolume(channel.first);
+	}
+}
+
+void CSoundHandler::updateChannelVolume(int channel)
+{
+	if (channelVolumes.count(channel))
+		setChannelVolume(channel, getVolume() * channelVolumes[channel] / 100);
+	else
+		setChannelVolume(channel, getVolume());
 }
 
 // Sets the sound volume, from 0 (mute) to 100
@@ -258,29 +273,40 @@ void CSoundHandler::ambientUpdateChannels(std::map<std::string, int> soundsArg)
 	std::vector<std::string> stoppedSounds;
 	for(auto & pair : ambientChannels)
 	{
-		if(!vstd::contains(soundsArg, pair.first))
+		const std::string & soundId = pair.first;
+		const int channel = pair.second;
+
+		if(!vstd::contains(soundsArg, soundId))
 		{
-			ambientStopSound(pair.first);
-			stoppedSounds.push_back(pair.first);
+			ambientStopSound(soundId);
+			stoppedSounds.push_back(soundId);
 		}
 		else
 		{
-			int volume = ambientDistToVolume(soundsArg[pair.first]);
-			CCS->soundh->setChannelVolume(pair.second, volume);
+			int volume = ambientDistToVolume(soundsArg[soundId]);
+			channelVolumes[channel] = volume;
+			updateChannelVolume(channel);
 		}
 	}
 	for(auto soundId : stoppedSounds)
+	{
+		channelVolumes.erase(ambientChannels[soundId]);
 		ambientChannels.erase(soundId);
+	}
 
 	for(auto & pair : soundsArg)
 	{
-		if(!vstd::contains(ambientChannels, pair.first))
+		const std::string & soundId = pair.first;
+		const int distance = pair.second;
+
+		if(!vstd::contains(ambientChannels, soundId))
 		{
-			int channel = CCS->soundh->playSound(pair.first, -1);
-			int volume = ambientDistToVolume(pair.second);
+			int channel = playSound(soundId, -1);
+			int volume = ambientDistToVolume(distance);
+			channelVolumes[channel] = volume;
 
-			CCS->soundh->setChannelVolume(channel, volume);
-			CCS->soundh->ambientChannels.insert(std::make_pair(pair.first, channel));
+			updateChannelVolume(channel);
+			ambientChannels[soundId] = channel;
 		}
 	}
 }
@@ -293,6 +319,7 @@ void CSoundHandler::ambientStopAllChannels()
 	{
 		ambientStopSound(ch.first);
 	}
+	channelVolumes.clear();
 	ambientChannels.clear();
 }
 

+ 3 - 0
client/CMusicHandler.h

@@ -51,9 +51,12 @@ private:
 
 	int ambientDistToVolume(int distance) const;
 	void ambientStopSound(std::string soundId);
+	void updateChannelVolume(int channel);
 
 	const JsonNode ambientConfig;
+
 	std::map<std::string, int> ambientChannels;
+	std::map<int, int> channelVolumes;
 
 public:
 	CSoundHandler();

+ 1 - 1
client/CPlayerInterface.cpp

@@ -2042,7 +2042,7 @@ bool CPlayerInterface::capturedAllEvents()
 		return true;
 	}
 
-	bool needToLockAdventureMap = adventureInt->active && CGI->mh->hasOngoingAnimations();
+	bool needToLockAdventureMap = adventureInt && adventureInt->active && CGI->mh->hasOngoingAnimations();
 
 	if (ignoreEvents || needToLockAdventureMap)
 	{

+ 8 - 0
client/adventureMap/CAdvMapInt.cpp

@@ -49,6 +49,7 @@
 #include "../../lib/mapping/CMap.h"
 #include "../../lib/UnlockGuard.h"
 #include "../../lib/TerrainHandler.h"
+#include <SDL_keycode.h>
 
 #define ADVOPT (conf.go()->ac)
 
@@ -791,6 +792,13 @@ void CAdvMapInt::keyPressed(const SDL_Keycode & key)
 			}
 			return;
 		}
+	case SDLK_LALT:
+	case SDLK_RALT:
+		{
+			//fake mouse use to trigger onTileHovered()
+			GH.fakeMouseMove();
+			return;
+		}
 	default:
 		{
 			auto direction = keyToMoveDirection(key);

+ 17 - 9
client/battle/BattleActionsController.cpp

@@ -113,13 +113,17 @@ static std::string formatRangedAttack(const DamageEstimation & estimation, const
 
 BattleActionsController::BattleActionsController(BattleInterface & owner):
 	owner(owner),
+	selectedStack(nullptr),
 	heroSpellToCast(nullptr)
 {}
 
 void BattleActionsController::endCastingSpell()
 {
 	if(heroSpellToCast)
+	{
 		heroSpellToCast.reset();
+		owner.windowObject->blockUI(false);
+	}
 
 	if(owner.stacksController->getActiveStack())
 		possibleActions = getPossibleActionsForStack(owner.stacksController->getActiveStack()); //restore actions after they were cleared
@@ -174,7 +178,7 @@ void BattleActionsController::enterCreatureCastingMode()
 		if (isCastingPossible)
 		{
 			owner.giveCommand(EActionType::MONSTER_SPELL, BattleHex::INVALID, spell->getId());
-			owner.stacksController->setSelectedStack(nullptr);
+			selectedStack = nullptr;
 
 			CCS->curh->set(Cursor::Combat::POINTER);
 		}
@@ -287,6 +291,8 @@ void BattleActionsController::castThisSpell(SpellID spellID)
 		possibleActions.push_back (spellSelMode); //only this one action can be performed at the moment
 		GH.fakeMouseMove();//update cursor
 	}
+
+	owner.windowObject->blockUI(true);
 }
 
 const CSpell * BattleActionsController::getHeroSpellToCast( ) const
@@ -520,6 +526,8 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, B
 	switch (action.get())
 	{
 		case PossiblePlayerBattleAction::CHOOSE_TACTICS_STACK:
+			return (targetStack && targetStackOwned && targetStack->Speed() > 0);
+
 		case PossiblePlayerBattleAction::CREATURE_INFO:
 			return (targetStack && targetStackOwned);
 
@@ -561,7 +569,7 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, B
 			return isCastingPossibleHere(action.spell().toSpell(), owner.stacksController->getActiveStack(), targetStack, targetHex);
 
 		case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
-			return targetStack && isCastingPossibleHere(action.spell().toSpell(), owner.stacksController->getActiveStack(), targetStack, targetHex);
+			return !selectedStack && targetStack && isCastingPossibleHere(action.spell().toSpell(), owner.stacksController->getActiveStack(), targetStack, targetHex);
 
 		case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL:
 			if(targetStack && targetStackOwned && targetStack != owner.stacksController->getActiveStack() && targetStack->alive()) //only positive spells for other allied creatures
@@ -574,11 +582,11 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, B
 		case PossiblePlayerBattleAction::TELEPORT:
 		{
 			ui8 skill = getCurrentSpellcaster()->getEffectLevel(SpellID(SpellID::TELEPORT).toSpell());
-			return owner.curInt->cb->battleCanTeleportTo(owner.stacksController->getSelectedStack(), targetHex, skill);
+			return owner.curInt->cb->battleCanTeleportTo(selectedStack, targetHex, skill);
 		}
 
 		case PossiblePlayerBattleAction::SACRIFICE: //choose our living stack to sacrifice
-			return targetStack && targetStack != owner.stacksController->getSelectedStack() && targetStackOwned && targetStack->alive();
+			return targetStack && targetStack != selectedStack && targetStackOwned && targetStack->alive();
 
 		case PossiblePlayerBattleAction::OBSTACLE:
 		case PossiblePlayerBattleAction::FREE_LOCATION:
@@ -689,15 +697,15 @@ void BattleActionsController::actionRealize(PossiblePlayerBattleAction action, B
 				if (action.spell() == SpellID::SACRIFICE)
 				{
 					heroSpellToCast->aimToHex(targetHex);
-					possibleActions.push_back(PossiblePlayerBattleAction::SACRIFICE);
-					owner.stacksController->setSelectedStack(targetStack);
+					possibleActions.push_back({PossiblePlayerBattleAction::SACRIFICE, action.spell()});
+					selectedStack = targetStack;
 					return;
 				}
 				if (action.spell() == SpellID::TELEPORT)
 				{
 					heroSpellToCast->aimToUnit(targetStack);
-					possibleActions.push_back(PossiblePlayerBattleAction::TELEPORT);
-					owner.stacksController->setSelectedStack(targetStack);
+					possibleActions.push_back({PossiblePlayerBattleAction::TELEPORT, action.spell()});
+					selectedStack = targetStack;
 					return;
 				}
 			}
@@ -728,7 +736,7 @@ void BattleActionsController::actionRealize(PossiblePlayerBattleAction action, B
 				owner.curInt->cb->battleMakeAction(heroSpellToCast.get());
 				endCastingSpell();
 			}
-			owner.stacksController->setSelectedStack(nullptr);
+			selectedStack = nullptr;
 			return;
 		}
 	}

+ 3 - 0
client/battle/BattleActionsController.h

@@ -47,6 +47,9 @@ class BattleActionsController
 	/// if true, active stack could possibly cast some target spell
 	std::vector<const CSpell *> creatureSpells;
 
+	/// stack that has been selected as first target for multi-target spells (Teleport & Sacrifice)
+	const CStack * selectedStack;
+
 	bool isCastingPossibleHere (const CSpell * spell, const CStack *sactive, const CStack *shere, BattleHex myNumber);
 	bool canStackMoveHere (const CStack *sactive, BattleHex MyNumber) const; //TODO: move to BattleState / callback
 	std::vector<PossiblePlayerBattleAction> getPossibleActionsForStack (const CStack *stack) const; //called when stack gets its turn

+ 9 - 1
client/battle/BattleAnimationClasses.cpp

@@ -756,7 +756,15 @@ void ShootingAnimation::createProjectile(const Point & from, const Point & dest)
 uint32_t ShootingAnimation::getAttackClimaxFrame() const
 {
 	const CCreature *shooterInfo = getCreature();
-	return shooterInfo->animation.attackClimaxFrame;
+
+	uint32_t maxFrames = stackAnimation(attackingStack)->framesInGroup(getGroup());
+	uint32_t climaxFrame = shooterInfo->animation.attackClimaxFrame;
+	uint32_t selectedFrame = vstd::clamp(shooterInfo->animation.attackClimaxFrame, 1, maxFrames);
+
+	if (climaxFrame != selectedFrame)
+		logGlobal->warn("Shooter %s has ranged attack climax frame set to %d, but only %d available!", shooterInfo->getNamePluralTranslated(), climaxFrame, maxFrames);
+
+	return selectedFrame - 1; // H3 counts frames from 1
 }
 
 ECreatureAnimType ShootingAnimation::getUpwardsGroup() const

+ 10 - 11
client/battle/BattleFieldController.cpp

@@ -35,8 +35,6 @@
 #include "../../lib/CStack.h"
 #include "../../lib/spells/ISpellMechanics.h"
 
-#include <SDL_events.h>
-
 BattleFieldController::BattleFieldController(BattleInterface & owner):
 	owner(owner)
 {
@@ -67,10 +65,7 @@ BattleFieldController::BattleFieldController(BattleInterface & owner):
 
 	backgroundWithHexes = std::make_unique<Canvas>(Point(background->width(), background->height()));
 
-	auto accessibility = owner.curInt->cb->getAccesibility();
-	for(int i = 0; i < accessibility.size(); i++)
-		stackCountOutsideHexes[i] = (accessibility[i] == EAccessibility::ACCESSIBLE);
-
+	updateAccessibleHexes();
 	addUsedEvents(LCLICK | RCLICK | MOVE);
 	LOCPLINT->cingconsole->pos = this->pos;
 }
@@ -180,11 +175,6 @@ void BattleFieldController::redrawBackgroundWithHexes()
 	if (activeStack)
 		occupyableHexes = owner.curInt->cb->battleGetAvailableHexes(activeStack, true, &attackableHexes);
 
-	auto accessibility = owner.curInt->cb->getAccesibility();
-
-	for(int i = 0; i < accessibility.size(); i++)
-		stackCountOutsideHexes[i] = (accessibility[i] == EAccessibility::ACCESSIBLE);
-
 	//prepare background graphic with hexes and shaded hexes
 	backgroundWithHexes->draw(background, Point(0,0));
 	owner.obstacleController->showAbsoluteObstacles(*backgroundWithHexes);
@@ -579,6 +569,14 @@ bool BattleFieldController::isTileAttackable(const BattleHex & number) const
 	return false;
 }
 
+void BattleFieldController::updateAccessibleHexes()
+{
+	auto accessibility = owner.curInt->cb->getAccesibility();
+
+	for(int i = 0; i < accessibility.size(); i++)
+		stackCountOutsideHexes[i] = (accessibility[i] == EAccessibility::ACCESSIBLE || (accessibility[i] == EAccessibility::SIDE_COLUMN));
+}
+
 bool BattleFieldController::stackCountOutsideHex(const BattleHex & number) const
 {
 	return stackCountOutsideHexes[number];
@@ -591,6 +589,7 @@ void BattleFieldController::showAll(SDL_Surface * to)
 
 void BattleFieldController::show(SDL_Surface * to)
 {
+	updateAccessibleHexes();
 	owner.stacksController->update();
 	owner.obstacleController->update();
 

+ 1 - 0
client/battle/BattleFieldController.h

@@ -56,6 +56,7 @@ class BattleFieldController : public CIntObject
 	void showBackgroundImage(Canvas & canvas);
 	void showBackgroundImageWithHexes(Canvas & canvas);
 	void showHighlightedHexes(Canvas & canvas);
+	void updateAccessibleHexes();
 
 	BattleHex::EDir selectAttackDirection(BattleHex myNumber, const Point & point);
 

+ 44 - 33
client/battle/BattleStacksController.cpp

@@ -74,7 +74,6 @@ BattleStacksController::BattleStacksController(BattleInterface & owner):
 	owner(owner),
 	activeStack(nullptr),
 	stackToActivate(nullptr),
-	selectedStack(nullptr),
 	animIDhelper(0)
 {
 	//preparing graphics for displaying amounts of creatures
@@ -267,12 +266,8 @@ bool BattleStacksController::stackNeedsAmountBox(const CStack * stack) const
 		return false;
 
 	// if stack has any ongoing animation - hide the box
-	for(auto anim : currentAnimations)
-	{
-		auto stackAnimation = dynamic_cast<BattleStackAnimation*>(anim);
-		if(stackAnimation && (stackAnimation->stack->ID == stack->ID))
-			return false;
-	}
+	if (stackAmountBoxHidden.count(stack->ID))
+		return false;
 
 	return true;
 }
@@ -300,26 +295,42 @@ std::shared_ptr<IImage> BattleStacksController::getStackAmountBox(const CStack *
 
 void BattleStacksController::showStackAmountBox(Canvas & canvas, const CStack * stack)
 {
-	//blitting amount background box
 	auto amountBG = getStackAmountBox(stack);
 
-	const int sideShift = stack->side == BattleSide::ATTACKER ? 1 : -1;
-	const int reverseSideShift = stack->side == BattleSide::ATTACKER ? -1 : 1;
-	const BattleHex nextPos = stack->getPosition() + sideShift;
-	const bool edge = stack->getPosition() % GameConstants::BFIELD_WIDTH == (stack->side == BattleSide::ATTACKER ? GameConstants::BFIELD_WIDTH - 2 : 1);
-	const bool moveInside = !edge && !owner.fieldController->stackCountOutsideHex(nextPos);
+	bool doubleWide = stack->doubleWide();
+	bool turnedRight = facingRight(stack);
+	bool attacker = stack->side == BattleSide::ATTACKER;
+
+	BattleHex stackPos = stack->getPosition();
+
+	// double-wide unit turned around - use opposite hex for stack label
+	if (doubleWide && turnedRight != attacker)
+		stackPos = stack->occupiedHex();
+
+	BattleHex frontPos = turnedRight ?
+		stackPos.cloneInDirection(BattleHex::RIGHT) :
+		stackPos.cloneInDirection(BattleHex::LEFT);
 
-	int xAdd = (stack->side == BattleSide::ATTACKER ? 220 : 202) +
-			(stack->doubleWide() ? 44 : 0) * sideShift +
-			(moveInside ? amountBG->width() + 10 : 0) * reverseSideShift;
-	int yAdd = 260 + ((stack->side == BattleSide::ATTACKER || moveInside) ? 0 : -15);
+	bool moveInside = !owner.fieldController->stackCountOutsideHex(frontPos);
 
-	canvas.draw(amountBG, stackAnimation[stack->ID]->pos.topLeft() + Point(xAdd, yAdd));
+	Point boxPosition;
+
+	if (moveInside)
+	{
+		boxPosition = owner.fieldController->hexPositionLocal(stackPos).center() + Point(-15, 1);
+	}
+	else
+	{
+		if (turnedRight)
+			boxPosition = owner.fieldController->hexPositionLocal(frontPos).center() + Point (-22, 1);
+		else
+			boxPosition = owner.fieldController->hexPositionLocal(frontPos).center() + Point(-8, -14);
+	}
 
-	//blitting amount
-	Point textPos = stackAnimation[stack->ID]->pos.topLeft() + amountBG->dimensions()/2 + Point(xAdd, yAdd);
+	Point textPosition = amountBG->dimensions()/2 + boxPosition;
 
-	canvas.drawText(textPos, EFonts::FONT_TINY, Colors::WHITE, ETextAlignment::CENTER, TextOperations::formatMetric(stack->getCount(), 4));
+	canvas.draw(amountBG, boxPosition);
+	canvas.drawText(textPosition, EFonts::FONT_TINY, Colors::WHITE, ETextAlignment::CENTER, TextOperations::formatMetric(stack->getCount(), 4));
 }
 
 void BattleStacksController::showStack(Canvas & canvas, const CStack * stack)
@@ -368,6 +379,7 @@ void BattleStacksController::updateBattleAnimations()
 
 	if (hadAnimations && currentAnimations.empty())
 	{
+		//stackAmountBoxHidden.clear();
 		owner.executeStagedAnimations();
 		if (currentAnimations.empty())
 			owner.onAnimationsFinished();
@@ -378,8 +390,15 @@ void BattleStacksController::updateBattleAnimations()
 
 void BattleStacksController::addNewAnim(BattleAnimation *anim)
 {
+	if (currentAnimations.empty())
+		stackAmountBoxHidden.clear();
+
 	owner.onAnimationsStarted();
 	currentAnimations.push_back(anim);
+
+	auto stackAnimation = dynamic_cast<BattleStackAnimation*>(anim);
+	if(stackAnimation)
+		stackAmountBoxHidden.insert(stackAnimation->stack->ID);
 }
 
 void BattleStacksController::stackRemoved(uint32_t stackID)
@@ -668,6 +687,8 @@ void BattleStacksController::endAction(const BattleAction* action)
 	owner.executeStagedAnimations();
 	owner.waitForAnimations();
 
+	stackAmountBoxHidden.clear();
+
 	owner.windowObject->blockUI(activeStack == nullptr);
 	removeExpiredColorFilters();
 }
@@ -712,16 +733,6 @@ void BattleStacksController::activateStack()
 		return;
 }
 
-void BattleStacksController::setSelectedStack(const CStack *stack)
-{
-	selectedStack = stack;
-}
-
-const CStack* BattleStacksController::getSelectedStack() const
-{
-	return selectedStack;
-}
-
 const CStack* BattleStacksController::getActiveStack() const
 {
 	return activeStack;
@@ -738,8 +749,8 @@ Point BattleStacksController::getStackPositionAtHex(BattleHex hexNum, const CSta
 	if(stack && stack->initialPosition < 0) //creatures in turrets
 		return owner.siegeController->getTurretCreaturePosition(stack->initialPosition);
 
-	static const Point basePos(-190, -139); // position of creature in topleft corner
-	static const int imageShiftX = 30; // X offset to base pos for facing right stacks, negative for facing left
+	static const Point basePos(-189, -139); // position of creature in topleft corner
+	static const int imageShiftX = 29; // X offset to base pos for facing right stacks, negative for facing left
 
 	ret.x = basePos.x + 22 * ( (hexNum.getY() + 1)%2 ) + 44 * hexNum.getX();
 	ret.y = basePos.y + 42 * hexNum.getY();

+ 3 - 5
client/battle/BattleStacksController.h

@@ -67,6 +67,9 @@ class BattleStacksController
 	/// <creatureID, if false reverse creature's animation> //TODO: move it to battle callback
 	std::map<int, bool> stackFacingRight;
 
+	/// Stacks have amount box hidden due to ongoing animations
+	std::set<int> stackAmountBoxHidden;
+
 	/// currently active stack; nullptr - no one
 	const CStack *activeStack;
 
@@ -76,9 +79,6 @@ class BattleStacksController
 	///when animation is playing, we should wait till the end to make the next stack active; nullptr of none
 	const CStack *stackToActivate;
 
-	/// stack that was selected for multi-target spells - Teleport / Sacrifice
-	const CStack *selectedStack;
-
 	/// for giving IDs for animations
 	ui32 animIDhelper;
 
@@ -123,7 +123,6 @@ public:
 	void activateStack(); //copy stackToActivate to activeStack to enable controls of the stack
 
 	void setActiveStack(const CStack *stack);
-	void setSelectedStack(const CStack *stack);
 
 	void showAliveStack(Canvas & canvas, const CStack * stack);
 	void showStack(Canvas & canvas, const CStack * stack);
@@ -137,7 +136,6 @@ public:
 	void addNewAnim(BattleAnimation *anim); //adds new anim to pendingAnims
 
 	const CStack* getActiveStack() const;
-	const CStack* getSelectedStack() const;
 	const std::vector<uint32_t> getHoveredStacksUnitIds() const;
 
 	void update();

+ 2 - 5
client/battle/BattleWindow.cpp

@@ -541,11 +541,8 @@ void BattleWindow::blockUI(bool on)
 		w->block(on || owner.tacticsMode);
 	if(auto w = widget<CButton>("alternativeAction"))
 		w->block(on || owner.tacticsMode);
-
-	// block only if during enemy turn and auto-fight is off
-	// otherwise - crash on accessing non-exisiting active stack
-	if(auto w = widget<CButton>("options"))
-		w->block(!owner.curInt->isAutoFightOn && !owner.stacksController->getActiveStack());
+	if(auto w = widget<CButton>("autofight"))
+		w->block(owner.actionsController->spellcastingModeActive());
 
 	auto btactEnd = widget<CButton>("tacticEnd");
 	auto btactNext = widget<CButton>("tacticNext");

+ 17 - 2
client/gui/InterfaceObjectConfigurable.cpp

@@ -297,7 +297,14 @@ std::shared_ptr<CToggleButton> InterfaceObjectConfigurable::buildToggleButton(co
 		button->setImageOrder(imgOrder[0].Integer(), imgOrder[1].Integer(), imgOrder[2].Integer(), imgOrder[3].Integer());
 	}
 	if(!config["callback"].isNull())
-		button->addCallback(callbacks.at(config["callback"].String()));
+	{
+		std::string callbackName = config["callback"].String();
+
+		if (callbacks.count(callbackName))
+			button->addCallback(callbacks.at(callbackName));
+		else
+			logGlobal->error("Invalid callback '%s' in widget", callbackName );
+	}
 	return button;
 }
 
@@ -365,7 +372,15 @@ std::shared_ptr<CSlider> InterfaceObjectConfigurable::buildSlider(const JsonNode
 	auto itemsTotal = config["itemsTotal"].Integer();
 	auto value = config["selected"].Integer();
 	bool horizontal = config["orientation"].String() == "horizontal";
-	return std::make_shared<CSlider>(position, length, callbacks.at(config["callback"].String()), itemsVisible, itemsTotal, value, horizontal, style);
+	auto const & result = std::make_shared<CSlider>(position, length, callbacks.at(config["callback"].String()), itemsVisible, itemsTotal, value, horizontal, style);
+
+	if (!config["scrollBounds"].isNull())
+	{
+		Rect bounds = readRect(config["scrollBounds"]);
+		result->setScrollBounds(bounds);
+	}
+
+	return result;
 }
 
 std::shared_ptr<CAnimImage> InterfaceObjectConfigurable::buildImage(const JsonNode & config) const

+ 1 - 1
client/gui/NotificationHandler.cpp

@@ -12,9 +12,9 @@
 #include "NotificationHandler.h"
 #include <SDL_video.h>
 #include <SDL_events.h>
-#include <SDL_syswm.h>
 
 #if defined(VCMI_WINDOWS)
+#include <SDL_syswm.h>
 
 #define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
 // Windows Header Files:

+ 7 - 2
client/mapView/MapRenderer.cpp

@@ -98,8 +98,13 @@ void MapTileStorage::load(size_t index, const std::string & filename, EImageBlit
 
 	for(auto & entry : terrainAnimations)
 	{
-		entry = std::make_unique<CAnimation>(filename);
-		entry->preload();
+		if (!filename.empty())
+		{
+			entry = std::make_unique<CAnimation>(filename);
+			entry->preload();
+		}
+		else
+			entry = std::make_unique<CAnimation>();
 
 		for(size_t i = 0; i < entry->size(); ++i)
 			entry->getImage(i)->setBlitMode(blitMode);

+ 4 - 2
client/mapView/MapView.cpp

@@ -66,16 +66,18 @@ void BasicMapView::render(Canvas & target, bool fullUpdate)
 
 void BasicMapView::show(SDL_Surface * to)
 {
-	controller->update(GH.mainFPSmng->getElapsedMilliseconds());
+	controller->updateBefore(GH.mainFPSmng->getElapsedMilliseconds());
 
 	Canvas target(to);
 	CSDL_Ext::CClipRectGuard guard(to, pos);
 	render(target, false);
+
+	controller->updateAfter(GH.mainFPSmng->getElapsedMilliseconds());
 }
 
 void BasicMapView::showAll(SDL_Surface * to)
 {
-	controller->update(0);
+	controller->updateBefore(0);
 
 	Canvas target(to);
 	CSDL_Ext::CClipRectGuard guard(to, pos);

+ 55 - 30
client/mapView/MapViewController.cpp

@@ -18,6 +18,7 @@
 
 #include "../CPlayerInterface.h"
 #include "../adventureMap/CAdvMapInt.h"
+#include "../gui/CGuiHandler.h"
 
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CPathfinder.h"
@@ -88,7 +89,7 @@ std::shared_ptr<IMapRendererContext> MapViewController::getContext() const
 	return context;
 }
 
-void MapViewController::update(uint32_t timeDelta)
+void MapViewController::updateBefore(uint32_t timeDelta)
 {
 	// confirmed to match H3 for
 	// - hero embarking on boat (500 ms)
@@ -116,56 +117,32 @@ void MapViewController::update(uint32_t timeDelta)
 			settings["adventure"]["enemyMoveTime"].Float();
 
 		movementContext->progress += timeDelta / heroMoveTime;
+		movementContext->progress = std::min( 1.0, movementContext->progress);
 
 		Point positionFrom = Point(hero->convertToVisitablePos(movementContext->tileFrom)) * model->getSingleTileSize() + model->getSingleTileSize() / 2;
 		Point positionDest = Point(hero->convertToVisitablePos(movementContext->tileDest)) * model->getSingleTileSize() + model->getSingleTileSize() / 2;
 
 		Point positionCurr = vstd::lerp(positionFrom, positionDest, movementContext->progress);
 
-		if(movementContext->progress >= 1.0)
-		{
-			setViewCenter(hero->getSightCenter());
-
-			removeObject(context->getObject(movementContext->target));
-			addObject(context->getObject(movementContext->target));
-
-			activateAdventureContext(movementContext->animationTime);
-		}
-		else
-		{
-			setViewCenter(positionCurr, movementContext->tileDest.z);
-		}
+		setViewCenter(positionCurr, movementContext->tileDest.z);
 	}
 
 	if(teleportContext)
 	{
 		teleportContext->progress += timeDelta / heroTeleportDuration;
-		if(teleportContext->progress >= 1.0)
-		{
-			activateAdventureContext(teleportContext->animationTime);
-		}
+		teleportContext->progress = std::min( 1.0, teleportContext->progress);
 	}
 
 	if(fadingOutContext)
 	{
 		fadingOutContext->progress -= timeDelta / fadeOutDuration;
-
-		if(fadingOutContext->progress <= 0.0)
-		{
-			removeObject(context->getObject(fadingOutContext->target));
-
-			activateAdventureContext(fadingOutContext->animationTime);
-		}
+		fadingOutContext->progress = std::max( 0.0, fadingOutContext->progress);
 	}
 
 	if(fadingInContext)
 	{
 		fadingInContext->progress += timeDelta / fadeInDuration;
-
-		if(fadingInContext->progress >= 1.0)
-		{
-			activateAdventureContext(fadingInContext->animationTime);
-		}
+		fadingInContext->progress = std::min( 1.0, fadingInContext->progress);
 	}
 
 	if(adventureContext)
@@ -180,6 +157,48 @@ void MapViewController::update(uint32_t timeDelta)
 	}
 }
 
+void MapViewController::updateAfter(uint32_t timeDelta)
+{
+	if(movementContext)
+	{
+		const auto * object = context->getObject(movementContext->target);
+		const auto * hero = dynamic_cast<const CGHeroInstance *>(object);
+		const auto * boat = dynamic_cast<const CGBoat *>(object);
+
+		assert(boat || hero);
+
+		if(!hero)
+			hero = boat->hero;
+
+		if(movementContext->progress >= 1.0)
+		{
+			setViewCenter(hero->getSightCenter());
+
+			removeObject(context->getObject(movementContext->target));
+			addObject(context->getObject(movementContext->target));
+
+			activateAdventureContext(movementContext->animationTime);
+		}
+	}
+
+	if(teleportContext && teleportContext->progress >= 1.0)
+	{
+		activateAdventureContext(teleportContext->animationTime);
+	}
+
+	if(fadingOutContext && fadingOutContext->progress <= 0.0)
+	{
+		removeObject(context->getObject(fadingOutContext->target));
+
+		activateAdventureContext(fadingOutContext->animationTime);
+	}
+
+	if(fadingInContext && fadingInContext->progress >= 1.0)
+	{
+		activateAdventureContext(fadingInContext->animationTime);
+	}
+}
+
 bool MapViewController::isEventVisible(const CGObjectInstance * obj)
 {
 	if(adventureContext == nullptr)
@@ -188,6 +207,9 @@ bool MapViewController::isEventVisible(const CGObjectInstance * obj)
 	if(!LOCPLINT->makingTurn && settings["adventure"]["enemyMoveTime"].Float() < 0)
 		return false; // enemy move speed set to "hidden/none"
 
+	if(GH.topInt() != adventureInt)
+		return false;
+
 	if(obj->isVisitable())
 		return context->isVisible(obj->visitablePos());
 	else
@@ -202,6 +224,9 @@ bool MapViewController::isEventVisible(const CGHeroInstance * obj, const int3 &
 	if(!LOCPLINT->makingTurn && settings["adventure"]["enemyMoveTime"].Float() < 0)
 		return false; // enemy move speed set to "hidden/none"
 
+	if(GH.topInt() != adventureInt)
+		return false;
+
 	if(context->isVisible(obj->convertToVisitablePos(from)))
 		return true;
 

+ 2 - 1
client/mapView/MapViewController.h

@@ -83,7 +83,8 @@ public:
 	void setViewCenter(const int3 & position);
 	void setViewCenter(const Point & position, int level);
 	void setTileSize(const Point & tileSize);
-	void update(uint32_t timeDelta);
+	void updateBefore(uint32_t timeDelta);
+	void updateAfter(uint32_t timeDelta);
 
 	void activateAdventureContext(uint32_t animationTime);
 	void activateAdventureContext();

+ 24 - 2
client/widgets/Buttons.cpp

@@ -575,7 +575,7 @@ void CSlider::mouseMoved (const Point & cursorPosition)
 	v += 0.5;
 	if(v!=value)
 	{
-		moveTo((int)v);
+		moveTo(static_cast<int>(v));
 	}
 }
 
@@ -584,6 +584,16 @@ void CSlider::setScrollStep(int to)
 	scrollStep = to;
 }
 
+void CSlider::setScrollBounds(const Rect & bounds )
+{
+	scrollBounds = bounds;
+}
+
+void CSlider::clearScrollBounds()
+{
+	scrollBounds = boost::none;
+}
+
 int CSlider::getAmount() const
 {
 	return amount;
@@ -775,7 +785,19 @@ void CSlider::showAll(SDL_Surface * to)
 
 void CSlider::wheelScrolled(bool down, bool in)
 {
-	moveTo(value + 3 * (down ? +scrollStep : -scrollStep));
+	if (scrollBounds)
+	{
+		Rect testTarget = *scrollBounds + pos.topLeft();
+
+		if (!testTarget.isInside(GH.getCursorPosition()))
+			return;
+	}
+
+	// vertical slider -> scrolling up move slider upwards
+	// horizontal slider -> scrolling up moves slider towards right
+	bool positive = (down != horizontal);
+
+	moveTo(value + 3 * (positive ? +scrollStep : -scrollStep));
 }
 
 void CSlider::keyPressed(const SDL_Keycode & key)

+ 6 - 0
client/widgets/Buttons.h

@@ -229,6 +229,8 @@ class CSlider : public CIntObject
 	std::shared_ptr<CButton> right;
 	std::shared_ptr<CButton> slider;
 
+	boost::optional<Rect> scrollBounds;
+
 	int capacity;//how many elements can be active at same time (e.g. hero list = 5)
 	int positions; //number of highest position (0 if there is only one)
 	bool horizontal;
@@ -252,6 +254,10 @@ public:
 	/// Controls how many items wil be scrolled via one click
 	void setScrollStep(int to);
 
+	/// If set, mouse scroll will only scroll slider when inside of this area
+	void setScrollBounds(const Rect & bounds );
+	void clearScrollBounds();
+
 	/// Value modifiers
 	void moveLeft();
 	void moveRight();

+ 3 - 2
client/widgets/CArtifactHolder.cpp

@@ -294,7 +294,6 @@ void CHeroArtPlace::select()
 		}
 	}
 
-	CCS->curh->dragAndDropCursor("artifact", ourArt->artType->getIconIndex());
 	ourOwner->commonInfo->src.setTo(this, false);
 	ourOwner->commonInfo->src.slotID = ArtifactPosition::TRANSITION_POS;
 
@@ -760,7 +759,9 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation & src, const Artifac
 		}
 		if(!curHero->artifactsTransitionPos.empty() && withUIUpdate)
 		{
-			markPossibleSlots(curHero->getArt(ArtifactPosition::TRANSITION_POS));
+			auto artInst = curHero->getArt(ArtifactPosition::TRANSITION_POS);
+			markPossibleSlots(artInst);
+			CCS->curh->dragAndDropCursor("artifact", artInst->artType->getIconIndex());
 		}
 	}
 

+ 32 - 28
client/windows/CCastleInterface.cpp

@@ -405,31 +405,7 @@ void CHeroGSlot::clickLeft(tribool down, bool previousState)
 		}
 		else if(other->hero && other->isSelected())
 		{
-			bool allow = true;
-			if(upg) //moving hero out of town - check if it is allowed
-			{
-				if(!hero && LOCPLINT->cb->howManyHeroes(false) >= CGI->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP))
-				{
-					std::string tmp = CGI->generaltexth->allTexts[18]; //You already have %d adventuring heroes under your command.
-					boost::algorithm::replace_first(tmp,"%d",std::to_string(LOCPLINT->cb->howManyHeroes(false)));
-					LOCPLINT->showInfoDialog(tmp, std::vector<std::shared_ptr<CComponent>>(), soundBase::sound_todo);
-					allow = false;
-				}
-				else if(!other->hero->stacksCount()) //hero has no creatures - strange, but if we have appropriate error message...
-				{
-					LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[19], std::vector<std::shared_ptr<CComponent>>(), soundBase::sound_todo); //This hero has no creatures.  A hero must have creatures before he can brave the dangers of the countryside.
-					allow = false;
-				}
-			}
-
-			setHighlight(false);
-			other->setHighlight(false);
-
-			if(allow)
-			{
-				owner->swapArmies();
-				hero = other->hero;
-			}
+			owner->swapArmies();
 		}
 		else if(hero)
 		{
@@ -524,17 +500,42 @@ void HeroSlots::splitClicked()
 
 void HeroSlots::swapArmies()
 {
+	bool allow = true;
+
+	//moving hero out of town - check if it is allowed
+	if (town->garrisonHero)
+	{
+		if (!town->visitingHero && LOCPLINT->cb->howManyHeroes(false) >= CGI->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP))
+		{
+			std::string text = CGI->generaltexth->translate("core.genrltxt.18"); //You already have %d adventuring heroes under your command.
+			boost::algorithm::replace_first(text,"%d",std::to_string(LOCPLINT->cb->howManyHeroes(false)));
+
+			LOCPLINT->showInfoDialog(text, std::vector<std::shared_ptr<CComponent>>(), soundBase::sound_todo);
+			allow = false;
+		}
+		else if (town->garrisonHero->stacksCount() == 0)
+		{
+			//This hero has no creatures.  A hero must have creatures before he can brave the dangers of the countryside.
+			LOCPLINT->showInfoDialog(CGI->generaltexth->translate("core.genrltxt.19"), {}, soundBase::sound_todo);
+			allow = false;
+		}
+	}
+
 	if(!town->garrisonHero && town->visitingHero) //visiting => garrison, merge armies: town army => hero army
 	{
 		if(!town->visitingHero->canBeMergedWith(*town))
 		{
 			LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[275], std::vector<std::shared_ptr<CComponent>>(), soundBase::sound_todo);
-			return;
+			allow = false;
 		}
 	}
-	LOCPLINT->cb->swapGarrisonHero(town);
-}
 
+	garrisonedHero->setHighlight(false);
+	visitingHero->setHighlight(false);
+
+	if (allow)
+		LOCPLINT->cb->swapGarrisonHero(town);
+}
 
 class SORTHELP
 {
@@ -727,6 +728,7 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
 				switch(subID)
 				{
 				case BuildingSubID::NONE:
+						enterBuilding(building);
 						break;
 
 				case BuildingSubID::MYSTIC_POND:
@@ -1258,6 +1260,7 @@ void CCastleInterface::addBuilding(BuildingID bid)
 	builds->addBuilding(bid);
 	recreateIcons();
 	activate();
+	redraw();
 }
 
 void CCastleInterface::removeBuilding(BuildingID bid)
@@ -1266,6 +1269,7 @@ void CCastleInterface::removeBuilding(BuildingID bid)
 	builds->removeBuilding(bid);
 	recreateIcons();
 	activate();
+	redraw();
 }
 
 void CCastleInterface::recreateIcons()

+ 7 - 0
client/windows/settings/GeneralOptionsTab.cpp

@@ -142,6 +142,13 @@ GeneralOptionsTab::GeneralOptionsTab()
 
 	std::shared_ptr<CToggleButton> compactTownCreatureInfo = widget<CToggleButton>("compactTownCreatureInfoCheckbox");
 	compactTownCreatureInfo->setSelected(settings["gameTweaks"]["compactTownCreatureInfo"].Bool());
+
+	std::shared_ptr<CLabel> musicVolumeLabel = widget<CLabel>("musicValueLabel");
+	musicVolumeLabel->setText(std::to_string(CCS->musich->getVolume()) + "%");
+
+	std::shared_ptr<CLabel> soundVolumeLabel = widget<CLabel>("soundValueLabel");
+	soundVolumeLabel->setText(std::to_string(CCS->soundh->getVolume()) + "%");
+
 }
 
 

+ 4 - 0
cmake_modules/VCMI_lib.cmake

@@ -101,11 +101,13 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/rmg/Zone.cpp
 		${MAIN_LIB_DIR}/rmg/Functions.cpp
 		${MAIN_LIB_DIR}/rmg/ObjectManager.cpp
+		${MAIN_LIB_DIR}/rmg/ObjectDistributor.cpp
 		${MAIN_LIB_DIR}/rmg/RoadPlacer.cpp
 		${MAIN_LIB_DIR}/rmg/TreasurePlacer.cpp
 		${MAIN_LIB_DIR}/rmg/RmgMap.cpp
 		${MAIN_LIB_DIR}/rmg/ConnectionsPlacer.cpp
 		${MAIN_LIB_DIR}/rmg/WaterAdopter.cpp
+		${MAIN_LIB_DIR}/rmg/MinePlacer.cpp
 		${MAIN_LIB_DIR}/rmg/TownPlacer.cpp
 		${MAIN_LIB_DIR}/rmg/WaterProxy.cpp
 		${MAIN_LIB_DIR}/rmg/WaterRoutes.cpp
@@ -360,11 +362,13 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/rmg/Zone.h
 		${MAIN_LIB_DIR}/rmg/Functions.h
 		${MAIN_LIB_DIR}/rmg/ObjectManager.h
+		${MAIN_LIB_DIR}/rmg/ObjectDistributor.h
 		${MAIN_LIB_DIR}/rmg/RoadPlacer.h
 		${MAIN_LIB_DIR}/rmg/TreasurePlacer.h
 		${MAIN_LIB_DIR}/rmg/RmgMap.h
 		${MAIN_LIB_DIR}/rmg/ConnectionsPlacer.h
 		${MAIN_LIB_DIR}/rmg/WaterAdopter.h
+		${MAIN_LIB_DIR}/rmg/MinePlacer.h
 		${MAIN_LIB_DIR}/rmg/TownPlacer.h
 		${MAIN_LIB_DIR}/rmg/WaterProxy.h
 		${MAIN_LIB_DIR}/rmg/WaterRoutes.h

+ 3 - 0
config/ai/object-priorities.txt

@@ -191,7 +191,10 @@ RuleBlock: gold reward
   rule: if armyReward is LOW and heroRole is MAIN and danger is NONE and mainTurnDistance is LOWEST then Value is HIGH
   rule: if skillReward is LOW and heroRole is MAIN and armyLoss is LOW then Value is BITHIGH
   rule: if skillReward is MEDIUM and heroRole is MAIN and armyLoss is LOW and fear is not HIGH then Value is BITHIGH
+  rule: if skillReward is MEDIUM and heroRole is MAIN and rewardType is MIXED and armyLoss is LOW and fear is not HIGH then Value is HIGH with 0.5
   rule: if skillReward is HIGH and heroRole is MAIN and armyLoss is LOW and fear is not HIGH then Value is HIGH
+  rule: if skillReward is MEDIUM and heroRole is SCOUT then Value is LOWEST
+  rule: if skillReward is HIGH and heroRole is SCOUT then Value is LOWEST
   rule: if strategicalValue is LOW and heroRole is MAIN and armyLoss is LOW then Value is BITHIGH
   rule: if strategicalValue is LOWEST and heroRole is MAIN and armyLoss is LOW then Value is LOW
   rule: if strategicalValue is LOW and heroRole is SCOUT and armyLoss is LOW and fear is not HIGH then Value is HIGH with 0.5

+ 1 - 20
config/bonuses.json

@@ -53,10 +53,7 @@
 	
 	"CATAPULT_EXTRA_SHOTS":
 	{
-		"graphics":
-		{
-			"icon":  "zvs/Lib1.res/Catapult"
-		}
+		"hidden": true
 	},
 
 	"CHANGES_SPELL_COST_FOR_ALLY":
@@ -431,22 +428,6 @@
 		}
 	},
 
-	"SELF_LUCK":
-	{
-		"graphics":
-		{
-			"icon":  "zvs/Lib1.res/SelfLuck"
-		}
-	},
-
-	"SELF_MORALE":
-	{
-		"graphics":
-		{
-			"icon":  "zvs/Lib1.res/E_MINOT"
-		}
-	},
-
 	"SHOOTER":
 	{
 		"graphics":

+ 2 - 2
config/heroClasses.json

@@ -104,7 +104,7 @@
 		"index": 10,
 		"faction" : "dungeon",
 		"defaultTavern" : 5,
-		"affinity" : "might",
+		"affinity" : "magic",
 		"commander" : "medusaQueen",
 		"mapObject" : { "templates" : { "default" : { "animation" : "AH11_.def", "editorAnimation": "AH11_E.def" } } },
 		"animation":  { "battle" : { "male" : "CH010.DEF",  "female" : "CH11.DEF" } }
@@ -114,7 +114,7 @@
 		"index": 11,
 		"faction" : "dungeon",
 		"defaultTavern" : 5,
-		"affinity" : "magic",
+		"affinity" : "might",
 		"commander" : "medusaQueen",
 		"mapObject" : { "templates" : { "default" : { "animation" : "AH10_.def", "editorAnimation": "AH10_E.def" } } },
 		"animation":  { "battle" : { "male" : "CH010.DEF",  "female" : "CH11.DEF" } }

+ 1 - 1
config/heroes/special.json

@@ -214,7 +214,7 @@
 			{ "skill" : "armorer", "level": "basic" }
 		],
 		"specialty" : {
-			"creature" : "griffin"
+			"creature" : "swordsman"
 		}
 	},
 	"mutareDrake":

+ 1 - 1
config/objects/generic.json

@@ -215,7 +215,7 @@
 		"handler" : "whirlpool",
 		"base" : {
 			"sounds" : {
-				"ambient" : ["LOOPWHIRL"],
+				"ambient" : ["LOOPWHIR"],
 				"visit" : ["DANGER"]
 			}
 		},

+ 1 - 4
config/schemas/mod.json

@@ -18,10 +18,6 @@
 					"type":"string",
 					"description": "More lengthy description of mod. No hard limit"
 				},
-				"modType" : {
-					"type":"string",
-					"description": "Type of mod, e.g. Town, Artifacts, Graphical."
-				},
 				"author" : {
 					"type":"string",
 					"description": "Author of the mod. Can be nickname, real name or name of team"
@@ -60,6 +56,7 @@
 
 		"modType" : {
 			"type":"string",
+			"enum" : [ "Translation", "Town", "Test", "Templates", "Spells", "Music", "Sounds", "Skills", "Other", "Objects", "Mechanics", "Interface", "Heroes", "Graphical", "Expansion", "Creatures", "Artifacts", "AI" ],
 			"description": "Type of mod, e.g. Town, Artifacts, Graphical."
 		},
 

+ 5 - 1
config/schemas/obstacle.json

@@ -32,7 +32,11 @@
 		},
 		"animation": {
 			"type": "string",
-			"description": "Image resource"
+			"description": "Image resource",
+			"anyOf" : [
+				{ "format" : "defFile" },
+				{ "format" : "imageFile" }
+			]
 		},
 		"unknown": {
 			"type": "number",

+ 1 - 1
config/schemas/settings.json

@@ -451,7 +451,7 @@
 				"repositoryURL" : {
 					"type" : "array",
 					"default" : [
-						"https://raw.githubusercontent.com/vcmi/vcmi-mods-repository/develop/github.json"
+						"https://raw.githubusercontent.com/vcmi/vcmi-mods-repository/develop/vcmi-1.2.json"
 					],
 					"items" : {
 						"type" : "string"

+ 0 - 3
config/skills.json

@@ -385,19 +385,16 @@
 			}
 		},
 		"basic" : {
-			"description" : "{Basic Estates}\n\nYour hero contributes 125 gold per day to your cause.",
 			"effects" : {
 				"main" : { "val" : 125 }
 			}
 		},
 		"advanced" : {
-			"description" : "{Advanced Estates}\n\nYour hero contributes 250 gold per day to your cause.",
 			"effects" : {
 				"main" : { "val" : 250 }
 			}
 		},
 		"expert" : {
-			"description" : "{Expert Estates}\n\nYour hero contributes 500 gold per day to your cause.",
 			"effects" : {
 				"main" : { "val" : 500 }
 			}

+ 2 - 0
config/widgets/settings/generalOptionsTab.json

@@ -138,6 +138,7 @@
 			"name": "musicSlider",
 			"type": "slider",
 			"position": {"x": 385, "y": 115},
+			"scrollBounds" : { "x" : -4, "y" : -34, "w" : 208, "h" : 52 },
 			"size": 200,
 			"style": "brown",
 			"orientation": "horizontal",
@@ -165,6 +166,7 @@
 			"name": "soundVolumeSlider",
 			"type": "slider",
 			"position": {"x": 385, "y": 175},
+			"scrollBounds" : { "x" : -4, "y" : -34, "w" : 208, "h" : 52 },
 			"size": 200,
 			"style": "brown",
 			"orientation": "horizontal",

+ 63 - 12
launcher/firstLaunch/firstlaunch_moc.cpp

@@ -91,7 +91,7 @@ void FirstLaunchView::on_pushButtonDataCopy_clicked()
 
 void FirstLaunchView::on_pushButtonDataHelp_clicked()
 {
-	static const QUrl vcmibuilderWiki("https://wiki.vcmi.eu/Installation_on_Linux#Installing_Heroes_III_data_files");
+	static const QUrl vcmibuilderWiki("https://wiki.vcmi.eu/Using_vcmibuilder");
 	QDesktopServices::openUrl(vcmibuilderWiki);
 }
 
@@ -232,9 +232,10 @@ bool FirstLaunchView::heroesDataDetect()
 	CResourceHandler::load("config/filesystem.json");
 
 	// use file from lod archive to check presence of H3 data. Very rough estimate, but will work in majority of cases
-	bool heroesDataFound = CResourceHandler::get()->existsResource(ResourceID("DATA/GENRLTXT.TXT"));
+	bool heroesDataFoundROE = CResourceHandler::get()->existsResource(ResourceID("DATA/GENRLTXT.TXT"));
+	bool heroesDataFoundSOD = CResourceHandler::get()->existsResource(ResourceID("DATA/TENTCOLR.TXT"));
 
-	return heroesDataFound;
+	return heroesDataFoundROE && heroesDataFoundSOD;
 }
 
 void FirstLaunchView::heroesLanguageUpdate()
@@ -267,16 +268,46 @@ void FirstLaunchView::copyHeroesData()
 	QStringList dirMaps = sourceRoot.entryList({"maps"}, QDir::Filter::Dirs);
 	QStringList dirMp3 = sourceRoot.entryList({"mp3"}, QDir::Filter::Dirs);
 
-	if(dirData.empty() || dirMaps.empty() || dirMp3.empty())
+	if(dirData.empty())
+	{
+		QMessageBox::critical(this, "Heroes III data not found!", "Failed to detect valid Heroes III data in chosen directory.\nPlease select directory with installed Heroes III data.");
 		return;
+	}
 
 	QDir sourceData = sourceRoot.filePath(dirData.front());
-	QStringList lodArchives = sourceData.entryList({"*.lod"}, QDir::Filter::Files);
+	QStringList roeFiles = sourceData.entryList({"*.lod"}, QDir::Filter::Files);
+	QStringList sodFiles = sourceData.entryList({"H3ab*.lod"}, QDir::Filter::Files);
+	QStringList hdFiles = sourceData.entryList({"*.pak"}, QDir::Filter::Files);
+
+	if(sodFiles.empty())
+	{
+		if (roeFiles.empty())
+		{
+			// Directory structure is correct (Data/Maps/Mp3) but no .lod archives that should be present in any install
+			QMessageBox::critical(this, "Heroes III data not found!", "Failed to detect valid Heroes III data in chosen directory.\nPlease select directory with installed Heroes III data.");
+			return;
+		}
 
-	if(lodArchives.empty())
+		if (!hdFiles.empty())
+		{
+			// HD Edition contains only RoE data so we can't use even unmodified files from it
+			QMessageBox::critical(this, "Heroes III data not found!", "Heroes III: HD Edition files are not supported by VCMI.\nPlease select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death.");
+			return;
+		}
+
+		// RoE or some other unsupported edition. Demo version?
+		QMessageBox::critical(this, "Heroes III data not found!", "Unknown or unsupported Heroes III version found.\nPlease select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death.");
 		return;
+	}
+
+	QStringList copyDirectories;
 
-	QStringList copyDirectories = {dirData.front(), dirMaps.front(), dirMp3.front()};
+	copyDirectories += dirData.front();
+	if (!dirMaps.empty())
+		copyDirectories += dirMaps.front();
+
+	if (!dirMp3.empty())
+		copyDirectories += dirMp3.front();
 
 	QDir targetRoot = pathToQString(VCMIDirs::get().userDataPath());
 
@@ -304,12 +335,17 @@ void FirstLaunchView::modPresetUpdate()
 	bool translationExists = !findTranslationModName().isEmpty();
 
 	ui->labelPresetLanguage->setVisible(translationExists);
+	ui->labelPresetLanguageDescr->setVisible(translationExists);
 	ui->checkBoxPresetLanguage->setVisible(translationExists);
 
 	ui->checkBoxPresetLanguage->setEnabled(checkCanInstallTranslation());
 	ui->checkBoxPresetExtras->setEnabled(checkCanInstallExtras());
 	ui->checkBoxPresetHota->setEnabled(checkCanInstallHota());
 	ui->checkBoxPresetWog->setEnabled(checkCanInstallWog());
+
+	// we can't install anything - either repository checkout is off or all recommended mods are already installed
+	if (!checkCanInstallTranslation() && !checkCanInstallExtras() && !checkCanInstallHota() && !checkCanInstallWog())
+		exitSetup();
 }
 
 QString FirstLaunchView::findTranslationModName()
@@ -364,7 +400,7 @@ CModListView * FirstLaunchView::getModView()
 
 bool FirstLaunchView::checkCanInstallMod(const QString & modID)
 {
-	return getModView() && !getModView()->isModInstalled(modID);
+	return getModView() && getModView()->isModAvailable(modID);
 }
 
 void FirstLaunchView::on_pushButtonPresetBack_clicked()
@@ -376,16 +412,16 @@ void FirstLaunchView::on_pushButtonPresetNext_clicked()
 {
 	QStringList modsToInstall;
 
-	if (ui->checkBoxPresetLanguage && checkCanInstallTranslation())
+	if (ui->checkBoxPresetLanguage->isChecked() && checkCanInstallTranslation())
 		modsToInstall.push_back(findTranslationModName());
 
-	if (ui->checkBoxPresetExtras && checkCanInstallExtras())
+	if (ui->checkBoxPresetExtras->isChecked() && checkCanInstallExtras())
 		modsToInstall.push_back("vcmi-extras");
 
-	if (ui->checkBoxPresetWog && checkCanInstallWog())
+	if (ui->checkBoxPresetWog->isChecked() && checkCanInstallWog())
 		modsToInstall.push_back("wake-of-gods");
 
-	if (ui->checkBoxPresetHota && checkCanInstallHota())
+	if (ui->checkBoxPresetHota->isChecked() && checkCanInstallHota())
 		modsToInstall.push_back("hota");
 
 	exitSetup();
@@ -394,3 +430,18 @@ void FirstLaunchView::on_pushButtonPresetNext_clicked()
 		getModView()->doInstallMod(modName);
 }
 
+void FirstLaunchView::on_pushButtonDiscord_clicked()
+{
+	QDesktopServices::openUrl(QUrl("https://discord.gg/chBT42V"));
+}
+
+void FirstLaunchView::on_pushButtonSlack_clicked()
+{
+	QDesktopServices::openUrl(QUrl("https://slack.vcmi.eu/"));
+}
+
+void FirstLaunchView::on_pushButtonGithub_clicked()
+{
+	QDesktopServices::openUrl(QUrl("https://github.com/vcmi/vcmi"));
+}
+

+ 6 - 0
launcher/firstLaunch/firstlaunch_moc.h

@@ -93,6 +93,12 @@ private slots:
 
 	void on_pushButtonPresetNext_clicked();
 
+	void on_pushButtonDiscord_clicked();
+
+	void on_pushButtonSlack_clicked();
+
+	void on_pushButtonGithub_clicked();
+
 private:
 	Ui::FirstLaunchView * ui;
 

+ 214 - 65
launcher/firstLaunch/firstlaunch_moc.ui

@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>650</width>
-    <height>409</height>
+    <width>745</width>
+    <height>389</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -115,7 +115,7 @@
    <item>
     <widget class="QStackedWidget" name="installerTabs">
      <property name="currentIndex">
-      <number>1</number>
+      <number>0</number>
      </property>
      <widget class="QWidget" name="pageLanguageSelect">
       <layout class="QGridLayout" name="gridLayout_3">
@@ -131,6 +131,9 @@
        <property name="bottomMargin">
         <number>0</number>
        </property>
+       <property name="horizontalSpacing">
+        <number>6</number>
+       </property>
        <item row="0" column="0">
         <widget class="QLabel" name="labelLanguageTitle">
          <property name="font">
@@ -140,31 +143,18 @@
           </font>
          </property>
          <property name="text">
-          <string>Choose your language</string>
+          <string>Select your language</string>
          </property>
         </widget>
        </item>
-       <item row="2" column="2">
-        <widget class="QPushButton" name="pushButtonLanguageNext">
-         <property name="text">
-          <string>Next</string>
+       <item row="1" column="0" rowspan="4">
+        <widget class="QListWidget" name="listWidgetLanguage">
+         <property name="spacing">
+          <number>5</number>
          </property>
         </widget>
        </item>
        <item row="2" column="1">
-        <spacer name="horizontalSpacer">
-         <property name="orientation">
-          <enum>Qt::Horizontal</enum>
-         </property>
-         <property name="sizeHint" stdset="0">
-          <size>
-           <width>40</width>
-           <height>20</height>
-          </size>
-         </property>
-        </spacer>
-       </item>
-       <item row="1" column="2">
         <spacer name="verticalSpacer">
          <property name="orientation">
           <enum>Qt::Vertical</enum>
@@ -172,19 +162,90 @@
          <property name="sizeHint" stdset="0">
           <size>
            <width>20</width>
-           <height>40</height>
+           <height>8</height>
           </size>
          </property>
         </spacer>
        </item>
-       <item row="1" column="0" rowspan="2">
-        <widget class="QListWidget" name="listWidgetLanguage">
-         <property name="spacing">
-          <number>5</number>
+       <item row="3" column="1" colspan="2">
+        <layout class="QGridLayout" name="gridLayout_5">
+         <item row="3" column="0">
+          <widget class="QPushButton" name="pushButtonGithub">
+           <property name="text">
+            <string>VCMI on Github</string>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="0">
+          <widget class="QPushButton" name="pushButtonSlack">
+           <property name="text">
+            <string>VCMI on Slack</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0">
+          <widget class="QPushButton" name="pushButtonDiscord">
+           <property name="text">
+            <string>VCMI on Discord</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="0" colspan="2">
+          <widget class="QLabel" name="labelLanguageSocial">
+           <property name="text">
+            <string>Have a question? Found a bug? Want to help? Join us!</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item row="1" column="1" colspan="2">
+        <widget class="QLabel" name="labelLanguageWelcome">
+         <property name="text">
+          <string>Thank you for installing VCMI!
+
+Before you can start playing, there are a few more steps that need to be completed.
+
+Please keep in mind that in order to use VCMI you must own the original data files for Heroes® of Might and Magic® III: Complete or The Shadow of Death.
+
+Heroes® of Might and Magic® III HD is currently not supported!</string>
+         </property>
+         <property name="wordWrap">
+          <bool>true</bool>
          </property>
         </widget>
        </item>
+       <item row="4" column="1" colspan="2">
+        <layout class="QHBoxLayout" name="horizontalLayout_2">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <item>
+          <spacer name="horizontalSpacer">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>40</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+         <item>
+          <widget class="QPushButton" name="pushButtonLanguageNext">
+           <property name="text">
+            <string>Next</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
       </layout>
+      <zorder>labelLanguageWelcome</zorder>
+      <zorder>labelLanguageTitle</zorder>
+      <zorder>listWidgetLanguage</zorder>
      </widget>
      <widget class="QWidget" name="pageDataFiles">
       <layout class="QVBoxLayout" name="verticalLayout_4">
@@ -212,7 +273,7 @@
           </font>
          </property>
          <property name="text">
-          <string>Find Heroes III data files</string>
+          <string>Locate Heroes III data files</string>
          </property>
         </widget>
        </item>
@@ -301,7 +362,7 @@
             </sizepolicy>
            </property>
            <property name="text">
-            <string>If you don't have installed Heroes III copy, it is possible to use our automatic installation tool 'vcmibuilder' to extract data from GoG.com installer. Visit our wiki for detailed instructions.</string>
+            <string>If you don't have a copy of Heroes III installed, you can use our automatic installation tool 'vcmibuilder', which only requires the GoG.com Heroes III installer. Please visit our wiki for detailed instructions.</string>
            </property>
            <property name="wordWrap">
             <bool>true</bool>
@@ -317,7 +378,7 @@
             </sizepolicy>
            </property>
            <property name="text">
-            <string>VCMI requires Heroes III data files in one of the locations listed above. Please copy Heroes III data in one of these directories.</string>
+            <string>To run VCMI, Heroes III data files need to be present in one of the specified locations. Please copy the Heroes III data to one of these directories.</string>
            </property>
            <property name="wordWrap">
             <bool>true</bool>
@@ -352,7 +413,7 @@
             </sizepolicy>
            </property>
            <property name="text">
-            <string>Alternatively, you can select directory with installed Heroes III data and VCMI will copy exisiting data automatically.</string>
+            <string>Alternatively, you can provide the directory where Heroes III data is installed and VCMI will copy the existing data automatically.</string>
            </property>
            <property name="wordWrap">
             <bool>true</bool>
@@ -421,7 +482,7 @@
          <item row="2" column="0" colspan="2">
           <widget class="QLabel" name="labelDataFailure">
            <property name="text">
-            <string>Automatic detection of language failed. Please select language of your Heroes III copy</string>
+            <string>The automatic detection of the Heroes III language has failed. Please select the language of your Heroes III manually</string>
            </property>
            <property name="wordWrap">
             <bool>true</bool>
@@ -539,40 +600,46 @@
         </spacer>
        </item>
        <item>
-        <layout class="QFormLayout" name="formLayout">
-         <item row="0" column="0" colspan="2">
-          <widget class="QLabel" name="labelPresetDescription">
+        <layout class="QGridLayout" name="gridLayout_4" columnstretch="1,10,25">
+         <item row="2" column="0">
+          <widget class="QCheckBox" name="checkBoxPresetLanguage">
            <property name="sizePolicy">
-            <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-             <horstretch>100</horstretch>
+            <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+             <horstretch>0</horstretch>
              <verstretch>0</verstretch>
             </sizepolicy>
            </property>
            <property name="text">
-            <string>Optionally, you can install additional mods either now or at any point later:</string>
+            <string/>
            </property>
-           <property name="wordWrap">
+           <property name="checked">
             <bool>true</bool>
            </property>
           </widget>
          </item>
-         <item row="1" column="0">
-          <widget class="QCheckBox" name="checkBoxPresetLanguage">
+         <item row="4" column="1">
+          <widget class="QLabel" name="labelPresetHota">
            <property name="sizePolicy">
-            <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
-             <horstretch>0</horstretch>
+            <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+             <horstretch>100</horstretch>
              <verstretch>0</verstretch>
             </sizepolicy>
            </property>
+           <property name="font">
+            <font>
+             <weight>75</weight>
+             <bold>true</bold>
+            </font>
+           </property>
            <property name="text">
-            <string/>
+            <string>Horn of the Abyss</string>
            </property>
-           <property name="checked">
+           <property name="wordWrap">
             <bool>true</bool>
            </property>
           </widget>
          </item>
-         <item row="1" column="1">
+         <item row="2" column="1">
           <widget class="QLabel" name="labelPresetLanguage">
            <property name="sizePolicy">
             <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -580,15 +647,21 @@
              <verstretch>0</verstretch>
             </sizepolicy>
            </property>
+           <property name="font">
+            <font>
+             <weight>75</weight>
+             <bold>true</bold>
+            </font>
+           </property>
            <property name="text">
-            <string>Install translation of Heroes III to your language</string>
+            <string>Heroes III Translation</string>
            </property>
            <property name="wordWrap">
             <bool>true</bool>
            </property>
           </widget>
          </item>
-         <item row="2" column="0">
+         <item row="3" column="0">
           <widget class="QCheckBox" name="checkBoxPresetExtras">
            <property name="sizePolicy">
             <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
@@ -604,7 +677,33 @@
            </property>
           </widget>
          </item>
-         <item row="2" column="1">
+         <item row="4" column="0">
+          <widget class="QCheckBox" name="checkBoxPresetHota">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string/>
+           </property>
+          </widget>
+         </item>
+         <item row="5" column="0">
+          <widget class="QCheckBox" name="checkBoxPresetWog">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string/>
+           </property>
+          </widget>
+         </item>
+         <item row="3" column="1">
           <widget class="QLabel" name="labelPresetExtras">
            <property name="sizePolicy">
             <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -612,29 +711,60 @@
              <verstretch>0</verstretch>
             </sizepolicy>
            </property>
+           <property name="font">
+            <font>
+             <weight>75</weight>
+             <bold>true</bold>
+            </font>
+           </property>
            <property name="text">
-            <string>Install support for playing Heroes III in resolutions other than 800x600.</string>
+            <string>High Definition Support</string>
            </property>
            <property name="wordWrap">
             <bool>true</bool>
            </property>
           </widget>
          </item>
-         <item row="3" column="0">
-          <widget class="QCheckBox" name="checkBoxPresetHota">
+         <item row="5" column="1">
+          <widget class="QLabel" name="labelPresetWog">
            <property name="sizePolicy">
-            <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
-             <horstretch>0</horstretch>
+            <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+             <horstretch>100</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="font">
+            <font>
+             <weight>75</weight>
+             <bold>true</bold>
+            </font>
+           </property>
+           <property name="text">
+            <string>In The Wake of Gods</string>
+           </property>
+           <property name="wordWrap">
+            <bool>true</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="2">
+          <widget class="QLabel" name="labelPresetLanguageDescr">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+             <horstretch>100</horstretch>
              <verstretch>0</verstretch>
             </sizepolicy>
            </property>
            <property name="text">
-            <string/>
+            <string>Install a translation of Heroes III in your preferred language</string>
+           </property>
+           <property name="wordWrap">
+            <bool>true</bool>
            </property>
           </widget>
          </item>
-         <item row="3" column="1">
-          <widget class="QLabel" name="labelPresetHota">
+         <item row="0" column="0" colspan="3">
+          <widget class="QLabel" name="labelPresetDescription">
            <property name="sizePolicy">
             <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
              <horstretch>100</horstretch>
@@ -642,28 +772,47 @@
             </sizepolicy>
            </property>
            <property name="text">
-            <string>Install compatible version of addon Horn of the Abyss: fan-made Heroes III expansion, ported by VCMI team</string>
+            <string>Optionally, you can install additional mods either now, or at any point later, using the VCMI Launcher</string>
            </property>
            <property name="wordWrap">
             <bool>true</bool>
            </property>
           </widget>
          </item>
-         <item row="4" column="0">
-          <widget class="QCheckBox" name="checkBoxPresetWog">
+         <item row="3" column="2">
+          <widget class="QLabel" name="labelPresetExtrasDescr">
            <property name="sizePolicy">
-            <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
-             <horstretch>0</horstretch>
+            <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+             <horstretch>100</horstretch>
              <verstretch>0</verstretch>
             </sizepolicy>
            </property>
            <property name="text">
-            <string/>
+            <string>Install support for playing Heroes III in resolutions higher than 800x600</string>
+           </property>
+           <property name="wordWrap">
+            <bool>true</bool>
            </property>
           </widget>
          </item>
-         <item row="4" column="1">
-          <widget class="QLabel" name="labelPresetWog">
+         <item row="4" column="2">
+          <widget class="QLabel" name="labelPresetHotaDescr">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+             <horstretch>100</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string>Install compatible version of &quot;Horn of the Abyss&quot;, a fan-made Heroes III expansion ported by the VCMI team</string>
+           </property>
+           <property name="wordWrap">
+            <bool>true</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="5" column="2">
+          <widget class="QLabel" name="labelPresetWogDecsr">
            <property name="sizePolicy">
             <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
              <horstretch>100</horstretch>
@@ -671,7 +820,7 @@
             </sizepolicy>
            </property>
            <property name="text">
-            <string>Install compatible version of addon &quot;In The Wake of Gods&quot;: fan-made Heroes III expansion</string>
+            <string>Install compatible version of &quot;In The Wake of Gods&quot;, a fan-made Heroes III expansion</string>
            </property>
            <property name="wordWrap">
             <bool>true</bool>

+ 1 - 1
launcher/lobby/lobby_moc.ui

@@ -73,7 +73,7 @@
        <item>
         <widget class="QLabel" name="label_2">
          <property name="text">
-          <string>People in lobby</string>
+          <string>Players in lobby</string>
          </property>
         </widget>
        </item>

+ 0 - 1
launcher/modManager/cmodlist.cpp

@@ -316,7 +316,6 @@ CModEntry CModList::getMod(QString modname) const
 				settings["active"] = false;
 	}
 
-
 	for(auto entry : repositories)
 	{
 		QVariant repoVal = getValue(entry, path);

+ 47 - 19
launcher/modManager/cmodlistmodel_moc.cpp

@@ -12,19 +12,6 @@
 
 #include <QIcon>
 
-namespace ModFields
-{
-static const QString names[ModFields::COUNT] =
-{
-	"name",
-	"",
-	"",
-	"modType",
-	"version",
-};
-
-}
-
 namespace ModStatus
 {
 static const QString iconDelete = "icons:mod-delete.png";
@@ -48,18 +35,59 @@ QString CModListModel::modIndexToName(const QModelIndex & index) const
 	return "";
 }
 
+
+QString CModListModel::modTypeName(QString modTypeID) const
+{
+	static QMap<QString, QString> modTypes = {
+		{"Translation", tr("Translation")},
+		{"Town",        tr("Town")       },
+		{"Test",        tr("Test")       },
+		{"Templates",   tr("Templates")  },
+		{"Spells",      tr("Spells")     },
+		{"Music",       tr("Music")      },
+		{"Sounds",      tr("Sounds")     },
+		{"Skills",      tr("Skills")     },
+		{"Other",       tr("Other")      },
+		{"Objects",     tr("Objects")    },
+		{"Mechanical",  tr("Mechanics")  },
+		{"Mechanics",   tr("Mechanics")  },
+		{"Themes",      tr("Interface")  },
+		{"Interface",   tr("Interface")  },
+		{"Heroes",      tr("Heroes")     },
+		{"Graphic",     tr("Graphical")  },
+		{"Graphical",   tr("Graphical")  },
+		{"Expansion",   tr("Expansion")  },
+		{"Creatures",   tr("Creatures")  },
+		{"Artifacts",   tr("Artifacts")  },
+		{"AI",          tr("AI")         },
+	};
+
+	if (modTypes.contains(modTypeID))
+		return modTypes[modTypeID];
+	return tr("Other");
+}
+
 QVariant CModListModel::getValue(const CModEntry & mod, int field) const
 {
 	switch(field)
 	{
-	case ModFields::STATUS_ENABLED:
-		return mod.getModStatus() & (ModStatus::ENABLED | ModStatus::INSTALLED);
+		case ModFields::STATUS_ENABLED:
+			return mod.getModStatus() & (ModStatus::ENABLED | ModStatus::INSTALLED);
 
-	case ModFields::STATUS_UPDATE:
-		return mod.getModStatus() & (ModStatus::UPDATEABLE | ModStatus::INSTALLED);
+		case ModFields::STATUS_UPDATE:
+			return mod.getModStatus() & (ModStatus::UPDATEABLE | ModStatus::INSTALLED);
 
-	default:
-		return mod.getValue(ModFields::names[field]);
+		case ModFields::NAME:
+			return mod.getValue("name");
+
+		case ModFields::VERSION:
+			return mod.getValue("version");
+
+		case ModFields::TYPE:
+			return modTypeName(mod.getValue("modType").toString());
+
+		default:
+			return QVariant();
 	}
 }
 

+ 1 - 0
launcher/modManager/cmodlistmodel_moc.h

@@ -48,6 +48,7 @@ class CModListModel : public QAbstractItemModel, public CModList
 	void endResetModel();
 
 	QString modIndexToName(const QModelIndex & index) const;
+	QString modTypeName(QString modTypeID) const;
 
 	QVariant getTextAlign(int field) const;
 	QVariant getValue(const CModEntry & mod, int field) const;

+ 63 - 20
launcher/modManager/cmodlistview_moc.cpp

@@ -24,6 +24,7 @@
 #include "../jsonutils.h"
 
 #include "../../lib/CConfigHandler.h"
+#include "../../lib/Languages.h"
 
 void CModListView::setupModModel()
 {
@@ -74,8 +75,9 @@ void CModListView::setupModsView()
 		ui->allModsView->setColumnWidth(ModFields::TYPE, 75);
 		ui->allModsView->setColumnWidth(ModFields::VERSION, 60);
 	}
-	ui->allModsView->setColumnWidth(ModFields::STATUS_ENABLED, 24);
-	ui->allModsView->setColumnWidth(ModFields::STATUS_UPDATE, 24);
+
+	ui->allModsView->resizeColumnToContents(ModFields::STATUS_ENABLED);
+	ui->allModsView->resizeColumnToContents(ModFields::STATUS_UPDATE);
 
 	ui->allModsView->setUniformRowHeights(true);
 
@@ -212,6 +214,25 @@ QString CModListView::genChangelogText(CModEntry & mod)
 	return result;
 }
 
+QStringList CModListView::getModNames(QStringList input)
+{
+	QStringList result;
+
+	for(const auto & modID : input)
+	{
+		auto mod = modModel->getMod(modID);
+
+		QString modName = mod.getValue("name").toString();
+
+		if (modName.isEmpty())
+			result += modID;
+		else
+			result += modName;
+	}
+
+	return result;
+}
+
 QString CModListView::genModInfoText(CModEntry & mod)
 {
 	QString prefix = "<p><span style=\" font-weight:600;\">%1: </span>"; // shared prefix
@@ -221,7 +242,6 @@ QString CModListView::genModInfoText(CModEntry & mod)
 	QString textTemplate = prefix + "</p><p align=\"justify\">%2</p>";
 	QString listTemplate = "<p align=\"justify\">%1: %2</p>";
 	QString noteTemplate = "<p align=\"justify\">%1</p>";
-	QString compatibleString = prefix + tr("Mod is compatible") + "</p>";
 	QString incompatibleString = redPrefix + tr("Mod is incompatible") + "</p>";
 	QString supportedVersions = redPrefix + "%2 %3 %4</p>";
 
@@ -242,9 +262,7 @@ QString CModListView::genModInfoText(CModEntry & mod)
 		result += urlTemplate.arg(tr("Contact")).arg(mod.getValue("contact").toString()).arg(mod.getValue("contact").toString());
 
 	//compatibility info
-	if(mod.isCompatible())
-		result += compatibleString.arg(tr("Compatibility"));
-	else
+	if(!mod.isCompatible())
 	{
 		auto compatibilityInfo = mod.getValue("compatibility").toMap();
 		auto minStr = compatibilityInfo.value("min").toString();
@@ -267,26 +285,51 @@ QString CModListView::genModInfoText(CModEntry & mod)
 		}
 	}
 
-	result += replaceIfNotEmpty(mod.getValue("depends"), lineTemplate.arg(tr("Required mods")));
-	result += replaceIfNotEmpty(mod.getValue("conflicts"), lineTemplate.arg(tr("Conflicting mods")));
-	result += replaceIfNotEmpty(mod.getValue("description"), textTemplate.arg(tr("Description")));
+	QStringList supportedLanguages;
+	QVariant baseLanguageVariant = mod.getBaseValue("language");
+	QString baseLanguageID = baseLanguageVariant.isValid() ? baseLanguageVariant.toString() : "english";
+
+	bool needToShowSupportedLanguages = false;
+
+	for(const auto & language : Languages::getLanguageList())
+	{
+		if (!language.hasTranslation)
+			continue;
+
+		QString languageID = QString::fromStdString(language.identifier);
+
+		if (languageID != baseLanguageID && !mod.getValue(languageID).isValid())
+			continue;
+
+		if (languageID != baseLanguageID)
+			needToShowSupportedLanguages = true;
+
+		supportedLanguages += QApplication::translate("Language", language.nameEnglish.c_str());
+	}
+
+	if(needToShowSupportedLanguages)
+		result += replaceIfNotEmpty(supportedLanguages, lineTemplate.arg(tr("Languages")));
+
+	result += replaceIfNotEmpty(getModNames(mod.getValue("depends").toStringList()), lineTemplate.arg(tr("Required mods")));
+	result += replaceIfNotEmpty(getModNames(mod.getValue("conflicts").toStringList()), lineTemplate.arg(tr("Conflicting mods")));
+	result += replaceIfNotEmpty(getModNames(mod.getValue("description").toStringList()), textTemplate.arg(tr("Description")));
 
 	result += "<p></p>"; // to get some empty space
 
-	QString unknownDeps = tr("This mod can not be installed or enabled because following dependencies are not present");
-	QString blockingMods = tr("This mod can not be enabled because following mods are incompatible with this mod");
-	QString hasActiveDependentMods = tr("This mod can not be disabled because it is required to run following mods");
-	QString hasDependentMods = tr("This mod can not be uninstalled or updated because it is required to run following mods");
-	QString thisIsSubmod = tr("This is submod and it can not be installed or uninstalled separately from parent mod");
+	QString unknownDeps = tr("This mod can not be installed or enabled because the following dependencies are not present");
+	QString blockingMods = tr("This mod can not be enabled because the following mods are incompatible with it");
+	QString hasActiveDependentMods = tr("This mod cannot be disabled because it is required by the following mods");
+	QString hasDependentMods = tr("This mod cannot be uninstalled or updated because it is required by the following mods");
+	QString thisIsSubmod = tr("This is a submod and it cannot be installed or uninstalled separately from its parent mod");
 
 	QString notes;
 
-	notes += replaceIfNotEmpty(findInvalidDependencies(mod.getName()), listTemplate.arg(unknownDeps));
-	notes += replaceIfNotEmpty(findBlockingMods(mod.getName()), listTemplate.arg(blockingMods));
+	notes += replaceIfNotEmpty(getModNames(findInvalidDependencies(mod.getName())), listTemplate.arg(unknownDeps));
+	notes += replaceIfNotEmpty(getModNames(findBlockingMods(mod.getName())), listTemplate.arg(blockingMods));
 	if(mod.isEnabled())
-		notes += replaceIfNotEmpty(findDependentMods(mod.getName(), true), listTemplate.arg(hasActiveDependentMods));
+		notes += replaceIfNotEmpty(getModNames(findDependentMods(mod.getName(), true)), listTemplate.arg(hasActiveDependentMods));
 	if(mod.isInstalled())
-		notes += replaceIfNotEmpty(findDependentMods(mod.getName(), false), listTemplate.arg(hasDependentMods));
+		notes += replaceIfNotEmpty(getModNames(findDependentMods(mod.getName(), false)), listTemplate.arg(hasDependentMods));
 
 	if(mod.getName().contains('.'))
 		notes += noteTemplate.arg(thisIsSubmod);
@@ -829,10 +872,10 @@ void CModListView::doInstallMod(const QString & modName)
 	}
 }
 
-bool CModListView::isModInstalled(const QString & modName)
+bool CModListView::isModAvailable(const QString & modName)
 {
 	auto mod = modModel->getMod(modName);
-	return mod.isInstalled();
+	return mod.isAvailable();
 }
 
 bool CModListView::isModEnabled(const QString & modName)

+ 5 - 2
launcher/modManager/cmodlistview_moc.h

@@ -47,6 +47,9 @@ class CModListView : public QWidget
 
 	void checkManagerErrors();
 
+	/// replace mod ID's with proper human-readable mod names
+	QStringList getModNames(QStringList input);
+
 	// find mods unknown to mod list (not present in repo and not installed)
 	QStringList findInvalidDependencies(QString mod);
 	// find mods that block enabling of this mod: conflicting with this mod or one of required mods
@@ -86,8 +89,8 @@ public:
 	/// install mod by name
 	void doInstallMod(const QString & modName);
 
-	/// returns true if mod is currently installed
-	bool isModInstalled(const QString & modName);
+	/// returns true if mod is available in repository and can be installed
+	bool isModAvailable(const QString & modName);
 
 	/// finds translation mod for specified languages. Returns empty string on error
 	QString getTranslationModName(const QString & language);

+ 8 - 8
launcher/settingsView/csettingsview_moc.cpp

@@ -372,10 +372,10 @@ void CSettingsView::loadTranslation()
 	ui->labelTranslationStatus->setVisible(showTranslation);
 	ui->pushButtonTranslation->setVisible(showTranslation);
 
-	if (!translationExists)
+	if (!translationExists || !translationNeeded)
 		return;
 
-	bool translationInstalled = mainWindow->getModView()->isModInstalled(modName);
+	bool translationAvailable = mainWindow->getModView()->isModAvailable(modName);
 	bool translationEnabled = mainWindow->getModView()->isModEnabled(modName);
 
 	ui->pushButtonTranslation->setVisible(!translationEnabled);
@@ -385,13 +385,13 @@ void CSettingsView::loadTranslation()
 		ui->labelTranslationStatus->setText(tr("Active"));
 	}
 
-	if (translationInstalled && !translationEnabled)
+	if (!translationEnabled && !translationAvailable)
 	{
 		ui->labelTranslationStatus->setText(tr("Disabled"));
 		ui->pushButtonTranslation->setText(tr("Enable"));
 	}
 
-	if (!translationInstalled)
+	if (translationAvailable)
 	{
 		ui->labelTranslationStatus->setText(tr("Not Installed"));
 		ui->pushButtonTranslation->setText(tr("Install"));
@@ -413,14 +413,14 @@ void CSettingsView::on_pushButtonTranslation_clicked()
 	if (modName.isEmpty())
 		return;
 
-	if (mainWindow->getModView()->isModInstalled(modName))
+	if (mainWindow->getModView()->isModAvailable(modName))
 	{
-		mainWindow->getModView()->enableModByName(modName);
+		mainWindow->switchToModsTab();
+		mainWindow->getModView()->doInstallMod(modName);
 	}
 	else
 	{
-		mainWindow->switchToModsTab();
-		mainWindow->getModView()->doInstallMod(modName);
+		mainWindow->getModView()->enableModByName(modName);
 	}
 }
 

+ 232 - 88
launcher/translation/english.ts

@@ -4,17 +4,111 @@
 <context>
     <name>CModListModel</name>
     <message>
-        <location filename="../modManager/cmodlistmodel_moc.cpp" line="142"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="42"/>
+        <source>Translation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="43"/>
+        <source>Town</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="44"/>
+        <source>Test</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="45"/>
+        <source>Templates</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="46"/>
+        <source>Spells</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="47"/>
+        <source>Music</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="48"/>
+        <source>Sounds</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="49"/>
+        <source>Skills</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="50"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="67"/>
+        <source>Other</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="51"/>
+        <source>Objects</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="52"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="53"/>
+        <source>Mechanics</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="54"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="55"/>
+        <source>Interface</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="56"/>
+        <source>Heroes</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="57"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="58"/>
+        <source>Graphical</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="59"/>
+        <source>Expansion</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="60"/>
+        <source>Creatures</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="61"/>
+        <source>Artifacts</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="62"/>
+        <source>AI</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="170"/>
         <source>Name</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistmodel_moc.cpp" line="145"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="173"/>
         <source>Type</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistmodel_moc.cpp" line="146"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="174"/>
         <source>Version</source>
         <translation type="unfinished"></translation>
     </message>
@@ -63,7 +157,7 @@
     </message>
     <message>
         <location filename="../modManager/cmodlistview_moc.ui" line="163"/>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="272"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="315"/>
         <source>Description</source>
         <translation type="unfinished"></translation>
     </message>
@@ -113,114 +207,113 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="230"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="250"/>
         <source>Mod name</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="231"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="251"/>
         <source>Installed version</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="232"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="252"/>
         <source>Latest version</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="235"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="255"/>
         <source>Download size</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="236"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="256"/>
         <source>Authors</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="239"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="259"/>
         <source>License</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="242"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="262"/>
         <source>Contact</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="246"/>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="253"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="271"/>
         <source>Compatibility</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="255"/>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="263"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="273"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="281"/>
         <source>Required VCMI version</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="261"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="279"/>
         <source>Supported VCMI version</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="266"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="284"/>
         <source>Supported VCMI versions</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="270"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="311"/>
+        <source>Languages</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="313"/>
         <source>Required mods</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="271"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="314"/>
         <source>Conflicting mods</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="276"/>
-        <source>This mod can not be installed or enabled because following dependencies are not present</source>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="319"/>
+        <source>This mod can not be installed or enabled because the following dependencies are not present</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="277"/>
-        <source>This mod can not be enabled because following mods are incompatible with this mod</source>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="320"/>
+        <source>This mod can not be enabled because the following mods are incompatible with it</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="278"/>
-        <source>This mod can not be disabled because it is required to run following mods</source>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="321"/>
+        <source>This mod cannot be disabled because it is required by the following mods</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="279"/>
-        <source>This mod can not be uninstalled or updated because it is required to run following mods</source>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="322"/>
+        <source>This mod cannot be uninstalled or updated because it is required by the following mods</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="280"/>
-        <source>This is submod and it can not be installed or uninstalled separately from parent mod</source>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="323"/>
+        <source>This is a submod and it cannot be installed or uninstalled separately from its parent mod</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="295"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="338"/>
         <source>Notes</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="797"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="840"/>
         <source>Screenshot %1</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="224"/>
-        <source>Mod is compatible</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="225"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="245"/>
         <source>Mod is incompatible</source>
         <translation type="unfinished"></translation>
     </message>
@@ -449,32 +542,83 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="384"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="146"/>
+        <source>Select your language</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="196"/>
+        <source>Have a question? Found a bug? Want to help? Join us!</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="205"/>
+        <source>Thank you for installing VCMI!
+
+Before you can start playing, there are a few more steps that need to be completed.
+
+Please keep in mind that in order to use VCMI you must own the original data files for Heroes® of Might and Magic® III: Complete or The Shadow of Death.
+
+Heroes® of Might and Magic® III HD is currently not supported!</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="276"/>
+        <source>Locate Heroes III data files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="365"/>
+        <source>If you don&apos;t have a copy of Heroes III installed, you can use our automatic installation tool &apos;vcmibuilder&apos;, which only requires the GoG.com Heroes III installer. Please visit our wiki for detailed instructions.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="381"/>
+        <source>To run VCMI, Heroes III data files need to be present in one of the specified locations. Please copy the Heroes III data to one of these directories.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="416"/>
+        <source>Alternatively, you can provide the directory where Heroes III data is installed and VCMI will copy the existing data automatically.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="445"/>
         <source>Your Heroes III data files have been successfully found.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="552"/>
-        <source>Optionally, you can install additional mods either now or at any point later:</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="485"/>
+        <source>The automatic detection of the Heroes III language has failed. Please select the language of your Heroes III manually</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="616"/>
-        <source>Install support for playing Heroes III in resolutions other than 800x600.</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="759"/>
+        <source>Install a translation of Heroes III in your preferred language</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="645"/>
-        <source>Install compatible version of addon Horn of the Abyss: fan-made Heroes III expansion, ported by VCMI team</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="775"/>
+        <source>Optionally, you can install additional mods either now, or at any point later, using the VCMI Launcher</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="674"/>
-        <source>Install compatible version of addon &quot;In The Wake of Gods&quot;: fan-made Heroes III expansion</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="791"/>
+        <source>Install support for playing Heroes III in resolutions higher than 800x600</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="721"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="807"/>
+        <source>Install compatible version of &quot;Horn of the Abyss&quot;, a fan-made Heroes III expansion ported by the VCMI team</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="823"/>
+        <source>Install compatible version of &quot;In The Wake of Gods&quot;, a fan-made Heroes III expansion</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="870"/>
         <source>Finish</source>
         <translation type="unfinished"></translation>
     </message>
@@ -484,85 +628,85 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="143"/>
-        <source>Choose your language</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="175"/>
+        <source>VCMI on Github</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="150"/>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="493"/>
-        <source>Next</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="182"/>
+        <source>VCMI on Slack</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="215"/>
-        <source>Find Heroes III data files</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="189"/>
+        <source>VCMI on Discord</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="246"/>
-        <source>Open help in browser</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="239"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="554"/>
+        <source>Next</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="259"/>
-        <source>Search again</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="307"/>
+        <source>Open help in browser</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="304"/>
-        <source>If you don&apos;t have installed Heroes III copy, it is possible to use our automatic installation tool &apos;vcmibuilder&apos; to extract data from GoG.com installer. Visit our wiki for detailed instructions.</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="320"/>
+        <source>Search again</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="320"/>
-        <source>VCMI requires Heroes III data files in one of the locations listed above. Please copy Heroes III data in one of these directories.</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="403"/>
+        <source>Heroes III data files</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="342"/>
-        <source>Heroes III data files</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="432"/>
+        <source>Copy existing data</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="355"/>
-        <source>Alternatively, you can select directory with installed Heroes III data and VCMI will copy exisiting data automatically.</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="475"/>
+        <source>Your Heroes III language has been successfully detected.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="371"/>
-        <source>Copy existing data</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="504"/>
+        <source>Heroes III language</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="414"/>
-        <source>Your Heroes III language has been successfully detected.</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="547"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="863"/>
+        <source>Back</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="424"/>
-        <source>Automatic detection of language failed. Please select language of your Heroes III copy</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="585"/>
+        <source>Install VCMI Mod Preset</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="443"/>
-        <source>Heroes III language</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="635"/>
+        <source>Horn of the Abyss</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="486"/>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="714"/>
-        <source>Back</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="657"/>
+        <source>Heroes III Translation</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="524"/>
-        <source>Install VCMI Mod Preset</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="721"/>
+        <source>High Definition Support</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="584"/>
-        <source>Install translation of Heroes III to your language</source>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="743"/>
+        <source>In The Wake of Gods</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
@@ -660,11 +804,6 @@
         <source>Server</source>
         <translation type="unfinished"></translation>
     </message>
-    <message>
-        <location filename="../lobby/lobby_moc.ui" line="76"/>
-        <source>People in lobby</source>
-        <translation type="unfinished"></translation>
-    </message>
     <message>
         <location filename="../lobby/lobby_moc.ui" line="114"/>
         <source>Lobby chat</source>
@@ -700,6 +839,11 @@
         <source>New room</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="76"/>
+        <source>Players in lobby</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
         <location filename="../lobby/lobby_moc.ui" line="159"/>
         <source>Join room</source>
@@ -801,7 +945,7 @@
     <name>UpdateDialog</name>
     <message>
         <location filename="../updatedialog_moc.ui" line="71"/>
-        <source>You have latest version</source>
+        <source>You have the latest version</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
@@ -811,7 +955,7 @@
     </message>
     <message>
         <location filename="../updatedialog_moc.ui" line="101"/>
-        <source>Check updates on startup</source>
+        <source>Check for updates on startup</source>
         <translation type="unfinished"></translation>
     </message>
 </context>

+ 307 - 157
launcher/translation/german.ts

@@ -4,17 +4,111 @@
 <context>
     <name>CModListModel</name>
     <message>
-        <location filename="../modManager/cmodlistmodel_moc.cpp" line="142"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="42"/>
+        <source>Translation</source>
+        <translation>Übersetzung</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="43"/>
+        <source>Town</source>
+        <translation>Stadt</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="44"/>
+        <source>Test</source>
+        <translation>Test</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="45"/>
+        <source>Templates</source>
+        <translation>Templates</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="46"/>
+        <source>Spells</source>
+        <translation>Zaubersprüche</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="47"/>
+        <source>Music</source>
+        <translation>Musik</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="48"/>
+        <source>Sounds</source>
+        <translation>Sounds</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="49"/>
+        <source>Skills</source>
+        <translation>Fertigkeiten</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="50"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="67"/>
+        <source>Other</source>
+        <translation>Andere</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="51"/>
+        <source>Objects</source>
+        <translation>Objekte</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="52"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="53"/>
+        <source>Mechanics</source>
+        <translation>Mechaniken</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="54"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="55"/>
+        <source>Interface</source>
+        <translation>Schnittstelle</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="56"/>
+        <source>Heroes</source>
+        <translation>Helden</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="57"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="58"/>
+        <source>Graphical</source>
+        <translation>Grafisches</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="59"/>
+        <source>Expansion</source>
+        <translation>Erweiterung</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="60"/>
+        <source>Creatures</source>
+        <translation>Kreaturen</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="61"/>
+        <source>Artifacts</source>
+        <translation>Artefakte</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="62"/>
+        <source>AI</source>
+        <translation>KI</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="170"/>
         <source>Name</source>
         <translation>Name</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistmodel_moc.cpp" line="145"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="173"/>
         <source>Type</source>
         <translation>Typ</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistmodel_moc.cpp" line="146"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="174"/>
         <source>Version</source>
         <translation>Version</translation>
     </message>
@@ -63,7 +157,7 @@
     </message>
     <message>
         <location filename="../modManager/cmodlistview_moc.ui" line="163"/>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="272"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="315"/>
         <source>Description</source>
         <translation>Beschreibung</translation>
     </message>
@@ -113,114 +207,113 @@
         <translation>Abbrechen</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="230"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="250"/>
         <source>Mod name</source>
         <translation>Mod-Name</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="231"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="251"/>
         <source>Installed version</source>
         <translation>Installierte Version</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="232"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="252"/>
         <source>Latest version</source>
         <translation>Letzte Version</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="235"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="255"/>
         <source>Download size</source>
         <translation>Downloadgröße</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="236"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="256"/>
         <source>Authors</source>
         <translation>Autoren</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="239"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="259"/>
         <source>License</source>
         <translation>Lizenz</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="242"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="262"/>
         <source>Contact</source>
-        <translation type="unfinished"></translation>
+        <translation>Kontakt</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="246"/>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="253"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="271"/>
         <source>Compatibility</source>
         <translation>Kompatibilität</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="255"/>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="263"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="273"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="281"/>
         <source>Required VCMI version</source>
         <translation>Benötigte VCMI Version</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="261"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="279"/>
         <source>Supported VCMI version</source>
         <translation>Unterstützte VCMI Version</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="266"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="284"/>
         <source>Supported VCMI versions</source>
         <translation>Unterstützte VCMI Versionen</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="270"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="311"/>
+        <source>Languages</source>
+        <translation>Sprachen</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="313"/>
         <source>Required mods</source>
         <translation>Benötigte Mods</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="271"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="314"/>
         <source>Conflicting mods</source>
         <translation>Mods mit Konflikt</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="276"/>
-        <source>This mod can not be installed or enabled because following dependencies are not present</source>
-        <translation>Diese Mod kann nicht installiert oder aktiviert werden, da die folgenden Abhängigkeiten nicht vorhanden sind</translation>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="319"/>
+        <source>This mod can not be installed or enabled because the following dependencies are not present</source>
+        <translation type="unfinished">Diese Mod kann nicht installiert oder aktiviert werden, da die folgenden Abhängigkeiten nicht vorhanden sind</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="277"/>
-        <source>This mod can not be enabled because following mods are incompatible with this mod</source>
-        <translation>Diese Mod kann nicht aktiviert werden, da folgende Mods nicht mit dieser Mod kompatibel sind</translation>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="320"/>
+        <source>This mod can not be enabled because the following mods are incompatible with it</source>
+        <translation type="unfinished">Diese Mod kann nicht aktiviert werden, da folgende Mods nicht mit dieser Mod kompatibel sind</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="278"/>
-        <source>This mod can not be disabled because it is required to run following mods</source>
-        <translation>Diese Mod kann nicht deaktiviert werden, da sie zum Ausführen der folgenden Mods erforderlich ist</translation>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="321"/>
+        <source>This mod cannot be disabled because it is required by the following mods</source>
+        <translation type="unfinished">Diese Mod kann nicht deaktiviert werden, da sie zum Ausführen der folgenden Mods erforderlich ist</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="279"/>
-        <source>This mod can not be uninstalled or updated because it is required to run following mods</source>
-        <translation>Diese Mod kann nicht deinstalliert oder aktualisiert werden, da sie für die folgenden Mods erforderlich ist</translation>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="322"/>
+        <source>This mod cannot be uninstalled or updated because it is required by the following mods</source>
+        <translation type="unfinished">Diese Mod kann nicht deinstalliert oder aktualisiert werden, da sie für die folgenden Mods erforderlich ist</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="280"/>
-        <source>This is submod and it can not be installed or uninstalled separately from parent mod</source>
-        <translation>Dies ist eine Submod und kann nicht separat von der Hauptmod installiert oder deinstalliert werden</translation>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="323"/>
+        <source>This is a submod and it cannot be installed or uninstalled separately from its parent mod</source>
+        <translation type="unfinished">Dies ist eine Submod und kann nicht separat von der Hauptmod installiert oder deinstalliert werden</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="295"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="338"/>
         <source>Notes</source>
         <translation>Anmerkungen</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="797"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="840"/>
         <source>Screenshot %1</source>
         <translation>Screenshot %1</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="224"/>
-        <source>Mod is compatible</source>
-        <translation>Mod ist kompatibel</translation>
-    </message>
-    <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="225"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="245"/>
         <source>Mod is incompatible</source>
         <translation>Mod ist inkompatibel</translation>
     </message>
@@ -242,7 +335,7 @@
     <message>
         <location filename="../settingsView/csettingsview_moc.ui" line="590"/>
         <source>Adventure Map AI</source>
-        <translation type="unfinished"></translation>
+        <translation>Abenteuerkarte KI</translation>
     </message>
     <message>
         <location filename="../settingsView/csettingsview_moc.ui" line="334"/>
@@ -261,18 +354,18 @@
         <location filename="../settingsView/csettingsview_moc.ui" line="79"/>
         <location filename="../settingsView/csettingsview_moc.ui" line="576"/>
         <source>Artificial Intelligence</source>
-        <translation type="unfinished"></translation>
+        <translation>Künstliche Intelligenz</translation>
     </message>
     <message>
         <location filename="../settingsView/csettingsview_moc.ui" line="89"/>
         <location filename="../settingsView/csettingsview_moc.ui" line="415"/>
         <source>Mod Repositories</source>
-        <translation type="unfinished"></translation>
+        <translation>Mod-Repositorien</translation>
     </message>
     <message>
         <location filename="../settingsView/csettingsview_moc.ui" line="368"/>
         <source>Update now</source>
-        <translation type="unfinished"></translation>
+        <translation>Jetzt aktualisieren</translation>
     </message>
     <message>
         <location filename="../settingsView/csettingsview_moc.ui" line="261"/>
@@ -285,37 +378,37 @@
     <message>
         <location filename="../settingsView/csettingsview_moc.ui" line="215"/>
         <source>Cursor</source>
-        <translation type="unfinished"></translation>
+        <translation>Zeiger</translation>
     </message>
     <message>
         <location filename="../settingsView/csettingsview_moc.ui" line="500"/>
         <source>Heroes III Data Language</source>
-        <translation type="unfinished"></translation>
+        <translation>Sprache der Heroes III Daten</translation>
     </message>
     <message>
         <location filename="../settingsView/csettingsview_moc.ui" line="552"/>
         <source>Default</source>
-        <translation type="unfinished"></translation>
+        <translation>Standard</translation>
     </message>
     <message>
         <location filename="../settingsView/csettingsview_moc.ui" line="557"/>
         <source>Hardware</source>
-        <translation type="unfinished"></translation>
+        <translation>Hardware</translation>
     </message>
     <message>
         <location filename="../settingsView/csettingsview_moc.ui" line="562"/>
         <source>Software</source>
-        <translation type="unfinished"></translation>
+        <translation>Software</translation>
     </message>
     <message>
         <location filename="../settingsView/csettingsview_moc.ui" line="597"/>
         <source>Heroes III Translation</source>
-        <translation type="unfinished"></translation>
+        <translation>Heroes III Übersetzung</translation>
     </message>
     <message>
         <location filename="../settingsView/csettingsview_moc.ui" line="429"/>
         <source>Check on startup</source>
-        <translation type="unfinished"></translation>
+        <translation>Beim Start prüfen</translation>
     </message>
     <message>
         <location filename="../settingsView/csettingsview_moc.ui" line="158"/>
@@ -408,27 +501,27 @@
     <message>
         <location filename="../settingsView/csettingsview_moc.cpp" line="385"/>
         <source>Active</source>
-        <translation type="unfinished">Aktiv</translation>
+        <translation>Aktiv</translation>
     </message>
     <message>
         <location filename="../settingsView/csettingsview_moc.cpp" line="390"/>
         <source>Disabled</source>
-        <translation type="unfinished"></translation>
+        <translation>Deaktiviert</translation>
     </message>
     <message>
         <location filename="../settingsView/csettingsview_moc.cpp" line="391"/>
         <source>Enable</source>
-        <translation type="unfinished">Aktivieren</translation>
+        <translation>Aktivieren</translation>
     </message>
     <message>
         <location filename="../settingsView/csettingsview_moc.cpp" line="396"/>
         <source>Not Installed</source>
-        <translation type="unfinished"></translation>
+        <translation>Nicht installiert</translation>
     </message>
     <message>
         <location filename="../settingsView/csettingsview_moc.cpp" line="397"/>
         <source>Install</source>
-        <translation type="unfinished">Installieren</translation>
+        <translation>Installieren</translation>
     </message>
 </context>
 <context>
@@ -436,134 +529,191 @@
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="28"/>
         <source>Language</source>
-        <translation type="unfinished"></translation>
+        <translation>Sprache</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="53"/>
         <source>Heroes III Data</source>
-        <translation type="unfinished"></translation>
+        <translation>Heroes III Daten</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="78"/>
         <source>Mods Preset</source>
-        <translation type="unfinished"></translation>
+        <translation>Mods Voreinstellung</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="146"/>
+        <source>Select your language</source>
+        <translation type="unfinished">Wählen Sie Ihre Sprache</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="196"/>
+        <source>Have a question? Found a bug? Want to help? Join us!</source>
+        <translation type="unfinished">Haben Sie eine Frage? Einen Fehler gefunden? Möchten Sie helfen? Machen Sie mit:</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="205"/>
+        <source>Thank you for installing VCMI!
+
+Before you can start playing, there are a few more steps that need to be completed.
+
+Please keep in mind that in order to use VCMI you must own the original data files for Heroes® of Might and Magic® III: Complete or The Shadow of Death.
+
+Heroes® of Might and Magic® III HD is currently not supported!</source>
+        <translation type="unfinished">Vielen Dank für die Installation von VCMI.
+
+Es sind noch ein paar Schritte notwendig, bevor Sie mit dem Spielen beginnen können.
+
+Denken Sie daran, dass Sie die Originaldateien, Heroes III: Complete Edition oder Shadow of Death besitzen müssen, um VCMI verwenden zu können.
+
+Heroes III: HD Edition wird derzeit nicht unterstützt</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="276"/>
+        <source>Locate Heroes III data files</source>
+        <translation type="unfinished">Heroes III Daten suchen</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="365"/>
+        <source>If you don&apos;t have a copy of Heroes III installed, you can use our automatic installation tool &apos;vcmibuilder&apos;, which only requires the GoG.com Heroes III installer. Please visit our wiki for detailed instructions.</source>
+        <translation type="unfinished">Wenn Sie keine Kopie von Heroes III installiert haben, können Sie unser automatisches Installationstool &apos;vcmibuilder&apos; verwenden, um Daten aus dem GoG.com-Installationsprogramm zu extrahieren. Besuchen Sie unser Wiki für detaillierte Anweisungen.</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="384"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="381"/>
+        <source>To run VCMI, Heroes III data files need to be present in one of the specified locations. Please copy the Heroes III data to one of these directories.</source>
+        <translation type="unfinished">VCMI benötigt Heroes III Daten in einem der oben aufgeführten Verzeichnisse. Bitte kopieren Sie die Heroes III-Daten in eines dieser Verzeichnisse.</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="416"/>
+        <source>Alternatively, you can provide the directory where Heroes III data is installed and VCMI will copy the existing data automatically.</source>
+        <translation type="unfinished">Alternativ können Sie ein Verzeichnis mit installierten Heroes III-Daten auswählen, und VCMI wird die vorhandenen Daten automatisch kopieren.</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="445"/>
         <source>Your Heroes III data files have been successfully found.</source>
-        <translation type="unfinished"></translation>
+        <translation>Ihre Heroes III-Datendateien wurden erfolgreich gefunden.</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="552"/>
-        <source>Optionally, you can install additional mods either now or at any point later:</source>
-        <translation type="unfinished"></translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="485"/>
+        <source>The automatic detection of the Heroes III language has failed. Please select the language of your Heroes III manually</source>
+        <translation type="unfinished">Automatische Erkennung der Sprache fehlgeschlagen. Bitte wählen Sie die Sprache Ihrer Heroes III Kopie</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="616"/>
-        <source>Install support for playing Heroes III in resolutions other than 800x600.</source>
-        <translation type="unfinished"></translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="759"/>
+        <source>Install a translation of Heroes III in your preferred language</source>
+        <translation type="unfinished">Übersetzung von Heroes III für Ihre Sprache installieren</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="645"/>
-        <source>Install compatible version of addon Horn of the Abyss: fan-made Heroes III expansion, ported by VCMI team</source>
-        <translation type="unfinished"></translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="775"/>
+        <source>Optionally, you can install additional mods either now, or at any point later, using the VCMI Launcher</source>
+        <translation type="unfinished">Optional können Sie jetzt oder zu einem beliebigen späteren Zeitpunkt zusätzliche Mods installieren:</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="674"/>
-        <source>Install compatible version of addon &quot;In The Wake of Gods&quot;: fan-made Heroes III expansion</source>
-        <translation type="unfinished"></translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="791"/>
+        <source>Install support for playing Heroes III in resolutions higher than 800x600</source>
+        <translation type="unfinished">Installieren Sie Unterstützung für das Spielen von Heroes III in anderen Auflösungen als 800x600.</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="721"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="807"/>
+        <source>Install compatible version of &quot;Horn of the Abyss&quot;, a fan-made Heroes III expansion ported by the VCMI team</source>
+        <translation type="unfinished">Installieren Sie die kompatible Version des Addons Horn of the Abyss: eine von Fans entwickelte Heroes III-Erweiterung, portiert vom VCMI-Team</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="823"/>
+        <source>Install compatible version of &quot;In The Wake of Gods&quot;, a fan-made Heroes III expansion</source>
+        <translation type="unfinished">Installieren Sie die kompatible Version des Addons &quot;In The Wake of Gods&quot;: von Fans entwickelte Heroes III-Erweiterung</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="870"/>
         <source>Finish</source>
-        <translation type="unfinished"></translation>
+        <translation>Fertigstellen</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="111"/>
         <source>Step %v out of %m</source>
-        <translation type="unfinished"></translation>
+        <translation>Schritt %v von %m</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="143"/>
-        <source>Choose your language</source>
-        <translation type="unfinished"></translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="175"/>
+        <source>VCMI on Github</source>
+        <translation>VCMI auf Github</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="150"/>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="493"/>
-        <source>Next</source>
-        <translation type="unfinished"></translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="182"/>
+        <source>VCMI on Slack</source>
+        <translation>VCMI auf Slack</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="215"/>
-        <source>Find Heroes III data files</source>
-        <translation type="unfinished"></translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="189"/>
+        <source>VCMI on Discord</source>
+        <translation>VCMI auf Discord</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="246"/>
-        <source>Open help in browser</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="259"/>
-        <source>Search again</source>
-        <translation type="unfinished"></translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="239"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="554"/>
+        <source>Next</source>
+        <translation>Weiter</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="304"/>
-        <source>If you don&apos;t have installed Heroes III copy, it is possible to use our automatic installation tool &apos;vcmibuilder&apos; to extract data from GoG.com installer. Visit our wiki for detailed instructions.</source>
-        <translation type="unfinished"></translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="307"/>
+        <source>Open help in browser</source>
+        <translation>Hilfe im Browser öffnen</translation>
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.ui" line="320"/>
-        <source>VCMI requires Heroes III data files in one of the locations listed above. Please copy Heroes III data in one of these directories.</source>
-        <translation type="unfinished"></translation>
+        <source>Search again</source>
+        <translation>Erneut suchen</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="342"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="403"/>
         <source>Heroes III data files</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="355"/>
-        <source>Alternatively, you can select directory with installed Heroes III data and VCMI will copy exisiting data automatically.</source>
-        <translation type="unfinished"></translation>
+        <translation>Heroes III Dateien</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="371"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="432"/>
         <source>Copy existing data</source>
-        <translation type="unfinished"></translation>
+        <translation>Vorhandene Daten kopieren</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="414"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="475"/>
         <source>Your Heroes III language has been successfully detected.</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="424"/>
-        <source>Automatic detection of language failed. Please select language of your Heroes III copy</source>
-        <translation type="unfinished"></translation>
+        <translation>Ihre Heroes III-Sprache wurde erfolgreich erkannt.</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="443"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="504"/>
         <source>Heroes III language</source>
-        <translation type="unfinished"></translation>
+        <translation>Heroes III Sprache</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="486"/>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="714"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="547"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="863"/>
         <source>Back</source>
-        <translation type="unfinished"></translation>
+        <translation>Zurück</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="524"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="585"/>
         <source>Install VCMI Mod Preset</source>
-        <translation type="unfinished"></translation>
+        <translation>VCMI Mod Voreinstellung installieren</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="584"/>
-        <source>Install translation of Heroes III to your language</source>
-        <translation type="unfinished"></translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="635"/>
+        <source>Horn of the Abyss</source>
+        <translation>Horn of the Abyss</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="657"/>
+        <source>Heroes III Translation</source>
+        <translation>Heroes III Übersetzung</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="721"/>
+        <source>High Definition Support</source>
+        <translation>Unterstützung für hohe Auflösungen</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="743"/>
+        <source>In The Wake of Gods</source>
+        <translation>In The Wake of Gods</translation>
     </message>
 </context>
 <context>
@@ -579,67 +729,67 @@
     <message>
         <location filename="../languages.cpp" line="23"/>
         <source>Chinese</source>
-        <translation type="unfinished"></translation>
+        <translation>Chinesisch</translation>
     </message>
     <message>
         <location filename="../languages.cpp" line="24"/>
         <source>English</source>
-        <translation type="unfinished">English (Englisch)</translation>
+        <translation>Englisch</translation>
     </message>
     <message>
         <location filename="../languages.cpp" line="25"/>
         <source>French</source>
-        <translation type="unfinished"></translation>
+        <translation>Französisch</translation>
     </message>
     <message>
         <location filename="../languages.cpp" line="26"/>
         <source>German</source>
-        <translation type="unfinished"></translation>
+        <translation>Deutsch</translation>
     </message>
     <message>
         <location filename="../languages.cpp" line="27"/>
         <source>Korean</source>
-        <translation type="unfinished"></translation>
+        <translation>Koreanisch</translation>
     </message>
     <message>
         <location filename="../languages.cpp" line="28"/>
         <source>Polish</source>
-        <translation type="unfinished"></translation>
+        <translation>Polnisch</translation>
     </message>
     <message>
         <location filename="../languages.cpp" line="29"/>
         <source>Russian</source>
-        <translation type="unfinished"></translation>
+        <translation>Russisch</translation>
     </message>
     <message>
         <location filename="../languages.cpp" line="30"/>
         <source>Spanish</source>
-        <translation type="unfinished"></translation>
+        <translation>Spanisch</translation>
     </message>
     <message>
         <location filename="../languages.cpp" line="31"/>
         <source>Ukrainian</source>
-        <translation type="unfinished"></translation>
+        <translation>Ukrainisch</translation>
     </message>
     <message>
         <location filename="../languages.cpp" line="32"/>
         <source>Other (East European)</source>
-        <translation type="unfinished"></translation>
+        <translation>Sonstige (osteuropäisch)</translation>
     </message>
     <message>
         <location filename="../languages.cpp" line="33"/>
         <source>Other (Cyrillic Script)</source>
-        <translation type="unfinished"></translation>
+        <translation>Sonstige (kyrillische Schrift)</translation>
     </message>
     <message>
         <location filename="../languages.cpp" line="34"/>
         <source>Other (West European)</source>
-        <translation type="unfinished"></translation>
+        <translation>Sonstige (westeuropäisch)</translation>
     </message>
     <message>
         <location filename="../languages.cpp" line="56"/>
         <source>Auto (%1)</source>
-        <translation type="unfinished"></translation>
+        <translation>Auto (%1)</translation>
     </message>
 </context>
 <context>
@@ -660,15 +810,10 @@
         <source>Server</source>
         <translation>Server</translation>
     </message>
-    <message>
-        <location filename="../lobby/lobby_moc.ui" line="76"/>
-        <source>People in lobby</source>
-        <translation type="unfinished"></translation>
-    </message>
     <message>
         <location filename="../lobby/lobby_moc.ui" line="114"/>
         <source>Lobby chat</source>
-        <translation type="unfinished"></translation>
+        <translation>Lobby-Chat</translation>
     </message>
     <message>
         <location filename="../lobby/lobby_moc.ui" line="194"/>
@@ -683,23 +828,28 @@
     <message>
         <location filename="../lobby/lobby_moc.ui" line="274"/>
         <source>Resolve</source>
-        <translation type="unfinished"></translation>
+        <translation>Auflösen</translation>
     </message>
     <message>
         <location filename="../lobby/lobby_moc.ui" line="286"/>
         <source>New game</source>
-        <translation type="unfinished"></translation>
+        <translation>Neues Spiel</translation>
     </message>
     <message>
         <location filename="../lobby/lobby_moc.ui" line="293"/>
         <source>Load game</source>
-        <translation type="unfinished"></translation>
+        <translation>Spiel laden</translation>
     </message>
     <message>
         <location filename="../lobby/lobby_moc.ui" line="149"/>
         <source>New room</source>
         <translation>Neuer Raum</translation>
     </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="76"/>
+        <source>Players in lobby</source>
+        <translation type="unfinished">Personen in der Lobby</translation>
+    </message>
     <message>
         <location filename="../lobby/lobby_moc.ui" line="159"/>
         <source>Join room</source>
@@ -733,12 +883,12 @@
     <message>
         <location filename="../lobby/lobby_moc.cpp" line="369"/>
         <source>Disconnect</source>
-        <translation type="unfinished"></translation>
+        <translation>Verbindung trennen</translation>
     </message>
     <message>
         <location filename="../lobby/lobby_moc.cpp" line="461"/>
         <source>No issues detected</source>
-        <translation type="unfinished"></translation>
+        <translation>Keine Probleme festgestellt</translation>
     </message>
 </context>
 <context>
@@ -789,7 +939,7 @@
     <message>
         <location filename="../mainwindow_moc.ui" line="226"/>
         <source>Map Editor</source>
-        <translation type="unfinished"></translation>
+        <translation>Karteneditor</translation>
     </message>
     <message>
         <location filename="../mainwindow_moc.ui" line="279"/>
@@ -801,8 +951,8 @@
     <name>UpdateDialog</name>
     <message>
         <location filename="../updatedialog_moc.ui" line="71"/>
-        <source>You have latest version</source>
-        <translation>Sie haben die neueste Version</translation>
+        <source>You have the latest version</source>
+        <translation type="unfinished">Sie haben die neueste Version</translation>
     </message>
     <message>
         <location filename="../updatedialog_moc.ui" line="94"/>
@@ -811,8 +961,8 @@
     </message>
     <message>
         <location filename="../updatedialog_moc.ui" line="101"/>
-        <source>Check updates on startup</source>
-        <translation>Nach Aktualisierungen beim Starten prüfen</translation>
+        <source>Check for updates on startup</source>
+        <translation type="unfinished">Nach Aktualisierungen beim Starten prüfen</translation>
     </message>
 </context>
 </TS>

+ 257 - 107
launcher/translation/polish.ts

@@ -4,17 +4,111 @@
 <context>
     <name>CModListModel</name>
     <message>
-        <location filename="../modManager/cmodlistmodel_moc.cpp" line="142"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="42"/>
+        <source>Translation</source>
+        <translation>Tłumaczenie</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="43"/>
+        <source>Town</source>
+        <translation>Miasto</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="44"/>
+        <source>Test</source>
+        <translation>Test</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="45"/>
+        <source>Templates</source>
+        <translation>Szablony</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="46"/>
+        <source>Spells</source>
+        <translation>Zaklęcia</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="47"/>
+        <source>Music</source>
+        <translation>Muzyczny</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="48"/>
+        <source>Sounds</source>
+        <translation>Dźwięki</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="49"/>
+        <source>Skills</source>
+        <translation>Umiejętności</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="50"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="67"/>
+        <source>Other</source>
+        <translation>Inne</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="51"/>
+        <source>Objects</source>
+        <translation>Obiekty</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="52"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="53"/>
+        <source>Mechanics</source>
+        <translation>Mechaniki</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="54"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="55"/>
+        <source>Interface</source>
+        <translation>Interfejs</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="56"/>
+        <source>Heroes</source>
+        <translation>Bohaterowie</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="57"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="58"/>
+        <source>Graphical</source>
+        <translation>Graficzny</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="59"/>
+        <source>Expansion</source>
+        <translation>Dodatek</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="60"/>
+        <source>Creatures</source>
+        <translation>Stworzenia</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="61"/>
+        <source>Artifacts</source>
+        <translation>Artefakty</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="62"/>
+        <source>AI</source>
+        <translation>AI</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="170"/>
         <source>Name</source>
         <translation>Nazwa</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistmodel_moc.cpp" line="145"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="173"/>
         <source>Type</source>
         <translation>Typ</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistmodel_moc.cpp" line="146"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="174"/>
         <source>Version</source>
         <translation>Wersja</translation>
     </message>
@@ -63,7 +157,7 @@
     </message>
     <message>
         <location filename="../modManager/cmodlistview_moc.ui" line="163"/>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="272"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="315"/>
         <source>Description</source>
         <translation>Opis</translation>
     </message>
@@ -113,114 +207,113 @@
         <translation>Przerwij</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="230"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="250"/>
         <source>Mod name</source>
         <translation>Nazwa moda</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="231"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="251"/>
         <source>Installed version</source>
         <translation>Zainstalowana wersja</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="232"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="252"/>
         <source>Latest version</source>
         <translation>Najnowsza wersja</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="235"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="255"/>
         <source>Download size</source>
         <translation>Rozmiar pobierania</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="236"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="256"/>
         <source>Authors</source>
         <translation>Autorzy</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="239"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="259"/>
         <source>License</source>
         <translation>Licencja</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="242"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="262"/>
         <source>Contact</source>
         <translation>Kontakt</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="246"/>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="253"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="271"/>
         <source>Compatibility</source>
         <translation>Kompatybilność</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="255"/>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="263"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="273"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="281"/>
         <source>Required VCMI version</source>
         <translation>Wymagana wersja VCMI</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="261"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="279"/>
         <source>Supported VCMI version</source>
         <translation>Wspierana wersja VCMI</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="266"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="284"/>
         <source>Supported VCMI versions</source>
         <translation>Wspierane wersje VCMI</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="270"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="311"/>
+        <source>Languages</source>
+        <translation>Języki</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="313"/>
         <source>Required mods</source>
         <translation>Wymagane mody</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="271"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="314"/>
         <source>Conflicting mods</source>
         <translation>Konfliktujące mody</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="276"/>
-        <source>This mod can not be installed or enabled because following dependencies are not present</source>
-        <translation>Ten mod nie może zostać zainstalowany lub włączony ponieważ następujące zależności nie zostały spełnione</translation>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="319"/>
+        <source>This mod can not be installed or enabled because the following dependencies are not present</source>
+        <translation type="unfinished">Ten mod nie może zostać zainstalowany lub włączony ponieważ następujące zależności nie zostały spełnione</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="277"/>
-        <source>This mod can not be enabled because following mods are incompatible with this mod</source>
-        <translation>Ten mod nie może zostać włączony ponieważ następujące mody są z nim niekompatybilne</translation>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="320"/>
+        <source>This mod can not be enabled because the following mods are incompatible with it</source>
+        <translation type="unfinished">Ten mod nie może zostać włączony ponieważ następujące mody są z nim niekompatybilne</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="278"/>
-        <source>This mod can not be disabled because it is required to run following mods</source>
-        <translation>Ten mod nie może zostać wyłączony ponieważ jest wymagany by do uruchomienia następujących modów</translation>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="321"/>
+        <source>This mod cannot be disabled because it is required by the following mods</source>
+        <translation type="unfinished">Ten mod nie może zostać wyłączony ponieważ jest wymagany by do uruchomienia następujących modów</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="279"/>
-        <source>This mod can not be uninstalled or updated because it is required to run following mods</source>
-        <translation>Ten mod nie może zostać odinstalowany lub zaktualizowany ponieważ jest wymagany do uruchomienia następujących modów</translation>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="322"/>
+        <source>This mod cannot be uninstalled or updated because it is required by the following mods</source>
+        <translation type="unfinished">Ten mod nie może zostać odinstalowany lub zaktualizowany ponieważ jest wymagany do uruchomienia następujących modów</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="280"/>
-        <source>This is submod and it can not be installed or uninstalled separately from parent mod</source>
-        <translation>To jest moduł składowy innego moda i nie może być zainstalowany lub odinstalowany oddzielnie od moda nadrzędnego</translation>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="323"/>
+        <source>This is a submod and it cannot be installed or uninstalled separately from its parent mod</source>
+        <translation type="unfinished">To jest moduł składowy innego moda i nie może być zainstalowany lub odinstalowany oddzielnie od moda nadrzędnego</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="295"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="338"/>
         <source>Notes</source>
         <translation>Uwagi</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="797"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="840"/>
         <source>Screenshot %1</source>
         <translation>Zrzut ekranu %1</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="224"/>
-        <source>Mod is compatible</source>
-        <translation>Mod jest kompatybilny</translation>
-    </message>
-    <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="225"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="245"/>
         <source>Mod is incompatible</source>
         <translation>Mod jest niekompatybilny</translation>
     </message>
@@ -449,32 +542,89 @@
         <translation>Zestaw modów</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="384"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="146"/>
+        <source>Select your language</source>
+        <translation type="unfinished">Wybierz język</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="196"/>
+        <source>Have a question? Found a bug? Want to help? Join us!</source>
+        <translation type="unfinished">Masz pytanie? Znalazłeś błąd? Chcesz pomóc? Dołącz do nas!</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="205"/>
+        <source>Thank you for installing VCMI!
+
+Before you can start playing, there are a few more steps that need to be completed.
+
+Please keep in mind that in order to use VCMI you must own the original data files for Heroes® of Might and Magic® III: Complete or The Shadow of Death.
+
+Heroes® of Might and Magic® III HD is currently not supported!</source>
+        <translation type="unfinished">Dziękujemy za zainstalowanie VCMI.
+
+Jest jeszcze kilka kroków, które trzeba wykonać żeby móc zagrać.
+
+Miej na uwadze, że aby używać VCMI potrzebne są pliki oryginalnej gry zawierające przynajmniej dodatek The Shadow of Death (Złota Edycja / Complete też się kwalifikują)
+
+Heroes III: HD Edition nie jest obecnie wspierane!</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="276"/>
+        <source>Locate Heroes III data files</source>
+        <translation type="unfinished">Znajdź pliki Heroes III</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="365"/>
+        <source>If you don&apos;t have a copy of Heroes III installed, you can use our automatic installation tool &apos;vcmibuilder&apos;, which only requires the GoG.com Heroes III installer. Please visit our wiki for detailed instructions.</source>
+        <translation type="unfinished">Jeśli nie masz zainstalowanej kopii Heroes III istnieje możliwość użycia naszego automatycznego narzędzia instalacyjnego &apos;vcmibuilder&apos; by wyodrębnić dane z instalatora GoG.com. Odwiedź nasze wiki po szczegółowe instrukcje.</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="381"/>
+        <source>To run VCMI, Heroes III data files need to be present in one of the specified locations. Please copy the Heroes III data to one of these directories.</source>
+        <translation type="unfinished">VCMI wymaga plików Heroes III w jednej z wymienionych wyżej lokalizacji. Proszę, skopiuj pliki Heroes III do jednego z tych katalogów.</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="416"/>
+        <source>Alternatively, you can provide the directory where Heroes III data is installed and VCMI will copy the existing data automatically.</source>
+        <translation type="unfinished">Możesz też wybrać folder z zainstalowanym Heroes III i VCMI automatycznie skopiuje istniejące dane.</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="445"/>
         <source>Your Heroes III data files have been successfully found.</source>
         <translation>Twoje pliki Heroes III zostały pomyślnie znalezione.</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="552"/>
-        <source>Optionally, you can install additional mods either now or at any point later:</source>
-        <translation>Opcjonalnie możesz zainstalować dodatkowe modyfikacje teraz lub później:</translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="485"/>
+        <source>The automatic detection of the Heroes III language has failed. Please select the language of your Heroes III manually</source>
+        <translation type="unfinished">Automatyczna detekcja języka nie powiodła się. Proszę wybrać język twojego Heroes III</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="616"/>
-        <source>Install support for playing Heroes III in resolutions other than 800x600.</source>
-        <translation>Zapinstaluj wsparcie dla grania w Heroes III w rozdzielczości innej niż 800x600.</translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="759"/>
+        <source>Install a translation of Heroes III in your preferred language</source>
+        <translation type="unfinished">Zainstaluj tłumaczenie Heroes III dla twojego języka</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="645"/>
-        <source>Install compatible version of addon Horn of the Abyss: fan-made Heroes III expansion, ported by VCMI team</source>
-        <translation>Zainstaluj kompatybilną wersję fanowskiego dodatku Horn of the Abyss przeportowaną przez zespół VCMI</translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="775"/>
+        <source>Optionally, you can install additional mods either now, or at any point later, using the VCMI Launcher</source>
+        <translation type="unfinished">Opcjonalnie możesz zainstalować dodatkowe modyfikacje teraz lub później</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="674"/>
-        <source>Install compatible version of addon &quot;In The Wake of Gods&quot;: fan-made Heroes III expansion</source>
-        <translation>Zainstaluj kompatybilną wersję fanowskiego dodatku &quot;In The Wake Of Gods&quot;</translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="791"/>
+        <source>Install support for playing Heroes III in resolutions higher than 800x600</source>
+        <translation type="unfinished">Zapinstaluj wsparcie dla grania w Heroes III w rozdzielczości innej niż 800x600</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="721"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="807"/>
+        <source>Install compatible version of &quot;Horn of the Abyss&quot;, a fan-made Heroes III expansion ported by the VCMI team</source>
+        <translation type="unfinished">Zainstaluj kompatybilną wersję fanowskiego dodatku Horn of the Abyss odtworzoną przez zespół VCMI</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="823"/>
+        <source>Install compatible version of &quot;In The Wake of Gods&quot;, a fan-made Heroes III expansion</source>
+        <translation type="unfinished">Zainstaluj kompatybilną wersję fanowskiego dodatku &quot;In The Wake Of Gods&quot;</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="870"/>
         <source>Finish</source>
         <translation>Zakończ</translation>
     </message>
@@ -484,86 +634,86 @@
         <translation>Krok %v z %m</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="143"/>
-        <source>Choose your language</source>
-        <translation>Wybierz język</translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="175"/>
+        <source>VCMI on Github</source>
+        <translation>VCMI na Github</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="150"/>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="493"/>
-        <source>Next</source>
-        <translation>Dalej</translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="182"/>
+        <source>VCMI on Slack</source>
+        <translation>VCMI na Slacku</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="189"/>
+        <source>VCMI on Discord</source>
+        <translation>VCMI na Discordzie</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="215"/>
-        <source>Find Heroes III data files</source>
-        <translation>Znajdź pliki Heroes III</translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="239"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="554"/>
+        <source>Next</source>
+        <translation>Dalej</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="246"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="307"/>
         <source>Open help in browser</source>
         <translation>Otwórz pomoc w przeglądarce</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="259"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="320"/>
         <source>Search again</source>
         <translation>Szukaj ponownie</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="304"/>
-        <source>If you don&apos;t have installed Heroes III copy, it is possible to use our automatic installation tool &apos;vcmibuilder&apos; to extract data from GoG.com installer. Visit our wiki for detailed instructions.</source>
-        <translation>Jeśli nie masz zainstalowanej kopii Heroes III istnieje możliwość użycia naszego automatycznego narzędzia instalacyjnego &apos;vcmibuilder&apos; by wyodrębnić dane z instalatora GoG.com. Odwiedź nasze wiki po szczegółowe instrukcje.</translation>
-    </message>
-    <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="320"/>
-        <source>VCMI requires Heroes III data files in one of the locations listed above. Please copy Heroes III data in one of these directories.</source>
-        <translation>VCMI wymaga plików Heroes III w jednej z wymienionych wyżej lokalizacji. Proszę, skopiuj pliki Heroes III do jednego z tych katalogów.</translation>
-    </message>
-    <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="342"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="403"/>
         <source>Heroes III data files</source>
         <translation>Pliki Heroes III</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="355"/>
-        <source>Alternatively, you can select directory with installed Heroes III data and VCMI will copy exisiting data automatically.</source>
-        <translation>Możesz też wybrać folder z zainstalowanym Heroes III i VCMI automatycznie skopiuje istniejące dane.</translation>
-    </message>
-    <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="371"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="432"/>
         <source>Copy existing data</source>
         <translation>Skopiuj istniejące dane</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="414"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="475"/>
         <source>Your Heroes III language has been successfully detected.</source>
         <translation>Twój język Heroes III został pomyślnie wykryty.</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="424"/>
-        <source>Automatic detection of language failed. Please select language of your Heroes III copy</source>
-        <translation>Automatyczna detekcja języka nie powiodła się. Proszę wybrać język twojego Heroes III</translation>
-    </message>
-    <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="443"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="504"/>
         <source>Heroes III language</source>
         <translation>Język Heroes III</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="486"/>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="714"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="547"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="863"/>
         <source>Back</source>
         <translation>Wstecz</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="524"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="585"/>
         <source>Install VCMI Mod Preset</source>
         <translation>Zainstaluj zestaw modyfikacji</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="584"/>
-        <source>Install translation of Heroes III to your language</source>
-        <translation>Zainstaluj tłumaczenie Heroes III dla twojego języka</translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="635"/>
+        <source>Horn of the Abyss</source>
+        <translation>Horn of the Abyss</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="657"/>
+        <source>Heroes III Translation</source>
+        <translation>Tłumaczenie Heroes III</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="721"/>
+        <source>High Definition Support</source>
+        <translation>Wsparcie High Definition</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="743"/>
+        <source>In The Wake of Gods</source>
+        <translation>In The Wake of Gods</translation>
     </message>
 </context>
 <context>
@@ -660,11 +810,6 @@
         <source>Server</source>
         <translation>Serwer</translation>
     </message>
-    <message>
-        <location filename="../lobby/lobby_moc.ui" line="76"/>
-        <source>People in lobby</source>
-        <translation>Ludzie w lobby</translation>
-    </message>
     <message>
         <location filename="../lobby/lobby_moc.ui" line="114"/>
         <source>Lobby chat</source>
@@ -700,6 +845,11 @@
         <source>New room</source>
         <translation>Nowy pokój</translation>
     </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="76"/>
+        <source>Players in lobby</source>
+        <translation type="unfinished">Ludzie w lobby</translation>
+    </message>
     <message>
         <location filename="../lobby/lobby_moc.ui" line="159"/>
         <source>Join room</source>
@@ -801,8 +951,8 @@
     <name>UpdateDialog</name>
     <message>
         <location filename="../updatedialog_moc.ui" line="71"/>
-        <source>You have latest version</source>
-        <translation>Posiadasz obecnie aktualną wersję</translation>
+        <source>You have the latest version</source>
+        <translation type="unfinished">Posiadasz obecnie aktualną wersję</translation>
     </message>
     <message>
         <location filename="../updatedialog_moc.ui" line="94"/>
@@ -811,8 +961,8 @@
     </message>
     <message>
         <location filename="../updatedialog_moc.ui" line="101"/>
-        <source>Check updates on startup</source>
-        <translation>Sprawdź aktualizacje przy uruchomieniu</translation>
+        <source>Check for updates on startup</source>
+        <translation type="unfinished">Sprawdź aktualizacje przy uruchomieniu</translation>
     </message>
 </context>
 </TS>

文件差異過大導致無法顯示
+ 372 - 132
launcher/translation/russian.ts


文件差異過大導致無法顯示
+ 564 - 206
launcher/translation/spanish.ts


+ 250 - 100
launcher/translation/ukrainian.ts

@@ -4,17 +4,111 @@
 <context>
     <name>CModListModel</name>
     <message>
-        <location filename="../modManager/cmodlistmodel_moc.cpp" line="142"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="42"/>
+        <source>Translation</source>
+        <translation>Переклад</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="43"/>
+        <source>Town</source>
+        <translation>Місто</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="44"/>
+        <source>Test</source>
+        <translation>Тестування</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="45"/>
+        <source>Templates</source>
+        <translation>Шаблони</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="46"/>
+        <source>Spells</source>
+        <translation>Закляття</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="47"/>
+        <source>Music</source>
+        <translation>Музика</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="48"/>
+        <source>Sounds</source>
+        <translation>Звуки</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="49"/>
+        <source>Skills</source>
+        <translation>Вміння</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="50"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="67"/>
+        <source>Other</source>
+        <translation>Інше</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="51"/>
+        <source>Objects</source>
+        <translation>Об&apos;єкти</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="52"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="53"/>
+        <source>Mechanics</source>
+        <translation>Механіки</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="54"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="55"/>
+        <source>Interface</source>
+        <translation>Інтерфейс</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="56"/>
+        <source>Heroes</source>
+        <translation>Герої</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="57"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="58"/>
+        <source>Graphical</source>
+        <translation>Графічний</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="59"/>
+        <source>Expansion</source>
+        <translation>Розширення</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="60"/>
+        <source>Creatures</source>
+        <translation>Істоти</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="61"/>
+        <source>Artifacts</source>
+        <translation>Артефакти</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="62"/>
+        <source>AI</source>
+        <translation>ШІ</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="170"/>
         <source>Name</source>
         <translation>Назва</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistmodel_moc.cpp" line="145"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="173"/>
         <source>Type</source>
         <translation>Тип</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistmodel_moc.cpp" line="146"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="174"/>
         <source>Version</source>
         <translation>Версія</translation>
     </message>
@@ -63,7 +157,7 @@
     </message>
     <message>
         <location filename="../modManager/cmodlistview_moc.ui" line="163"/>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="272"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="315"/>
         <source>Description</source>
         <translation>Опис</translation>
     </message>
@@ -113,114 +207,113 @@
         <translation>Відмінити</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="230"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="250"/>
         <source>Mod name</source>
         <translation>Назва модифікації</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="231"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="251"/>
         <source>Installed version</source>
         <translation>Встановлена версія</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="232"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="252"/>
         <source>Latest version</source>
         <translation>Найновіша версія</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="235"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="255"/>
         <source>Download size</source>
         <translation>Розмір для завантаження</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="236"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="256"/>
         <source>Authors</source>
         <translation>Автори</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="239"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="259"/>
         <source>License</source>
         <translation>Ліцензія</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="242"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="262"/>
         <source>Contact</source>
         <translation>Контакти</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="246"/>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="253"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="271"/>
         <source>Compatibility</source>
         <translation>Сумісність</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="255"/>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="263"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="273"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="281"/>
         <source>Required VCMI version</source>
         <translation>Необхідна версія VCMI</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="261"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="279"/>
         <source>Supported VCMI version</source>
         <translation>Підтримувана версія VCMI</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="266"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="284"/>
         <source>Supported VCMI versions</source>
         <translation>Підтримувані версії VCMI</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="270"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="311"/>
+        <source>Languages</source>
+        <translation>Мови</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="313"/>
         <source>Required mods</source>
         <translation>Необхідні модифікації</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="271"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="314"/>
         <source>Conflicting mods</source>
         <translation>Конфліктуючі модифікації</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="276"/>
-        <source>This mod can not be installed or enabled because following dependencies are not present</source>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="319"/>
+        <source>This mod can not be installed or enabled because the following dependencies are not present</source>
         <translation>Цю модифікацію не можна встановити чи активувати, оскільки відсутні наступні залежності</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="277"/>
-        <source>This mod can not be enabled because following mods are incompatible with this mod</source>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="320"/>
+        <source>This mod can not be enabled because the following mods are incompatible with it</source>
         <translation>Цю модифікацію не можна ввімкнути, оскільки наступні модифікації несумісні з цією модифікацією</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="278"/>
-        <source>This mod can not be disabled because it is required to run following mods</source>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="321"/>
+        <source>This mod cannot be disabled because it is required by the following mods</source>
         <translation>Цю модифікацію не можна відключити, оскільки вона необхідна для запуску наступних модифікацій</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="279"/>
-        <source>This mod can not be uninstalled or updated because it is required to run following mods</source>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="322"/>
+        <source>This mod cannot be uninstalled or updated because it is required by the following mods</source>
         <translation>Цю модифікацію не можна видалити або оновити, оскільки вона необхідна для запуску наступних модифікацій</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="280"/>
-        <source>This is submod and it can not be installed or uninstalled separately from parent mod</source>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="323"/>
+        <source>This is a submod and it cannot be installed or uninstalled separately from its parent mod</source>
         <translation>Це вкладена модифікація, і її не можна встановити або видалити окремо від батьківської модифікації</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="295"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="338"/>
         <source>Notes</source>
         <translation>Примітки</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="797"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="840"/>
         <source>Screenshot %1</source>
         <translation>Знімок екрану %1</translation>
     </message>
     <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="224"/>
-        <source>Mod is compatible</source>
-        <translation>Модифікація сумісна</translation>
-    </message>
-    <message>
-        <location filename="../modManager/cmodlistview_moc.cpp" line="225"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="245"/>
         <source>Mod is incompatible</source>
         <translation>Модифікація несумісна</translation>
     </message>
@@ -449,32 +542,89 @@
         <translation>Початкові модифікації</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="384"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="146"/>
+        <source>Select your language</source>
+        <translation>Оберіть свою мову</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="196"/>
+        <source>Have a question? Found a bug? Want to help? Join us!</source>
+        <translation>Маєте питання? Виявили помилку? Хочете допомогти? Приєднуйтесь до нас!</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="205"/>
+        <source>Thank you for installing VCMI!
+
+Before you can start playing, there are a few more steps that need to be completed.
+
+Please keep in mind that in order to use VCMI you must own the original data files for Heroes® of Might and Magic® III: Complete or The Shadow of Death.
+
+Heroes® of Might and Magic® III HD is currently not supported!</source>
+        <translation>Дякуємо, що встановили VCMI.
+
+Залишилося зробити ще кілька кроків, перш ніж ви зможете почати грати.
+
+Майте на увазі, що для використання VCMI вам потрібно мати оригінальні файли гри Heroes® of Might and Magic® III: Complete або The Shadow of Death.
+
+Heroes® of Might and Magic® III HD наразі не підтримується!</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="276"/>
+        <source>Locate Heroes III data files</source>
+        <translation>Пошук файлів даних Heroes III</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="365"/>
+        <source>If you don&apos;t have a copy of Heroes III installed, you can use our automatic installation tool &apos;vcmibuilder&apos;, which only requires the GoG.com Heroes III installer. Please visit our wiki for detailed instructions.</source>
+        <translation>Якщо у вас не встановлена копія Heroes III, ви можете скористатися нашим засобом встановлення &quot;vcmibuilder&quot;, яка вимагає лише інсталятора GoG.com. Докладні інструкції можна знайти у нашій вікі.</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="381"/>
+        <source>To run VCMI, Heroes III data files need to be present in one of the specified locations. Please copy the Heroes III data to one of these directories.</source>
+        <translation>VCMI потребує файлів даних Heroes III в одному з перелічених вище розташувань. Будь ласка, скопіюйте дані Heroes III в одну з цих директорій.</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="416"/>
+        <source>Alternatively, you can provide the directory where Heroes III data is installed and VCMI will copy the existing data automatically.</source>
+        <translation>Або ж ви можете вибрати директорію зі встановленими даними Heroes III, і VCMI автоматично скопіює ці дані.</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="445"/>
         <source>Your Heroes III data files have been successfully found.</source>
         <translation>Файли даних вашої гри Heroes III успішно знайдено.</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="552"/>
-        <source>Optionally, you can install additional mods either now or at any point later:</source>
-        <translation>За бажанням ви можете встановити додаткові модифікації зараз або пізніше:</translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="485"/>
+        <source>The automatic detection of the Heroes III language has failed. Please select the language of your Heroes III manually</source>
+        <translation>Не вдалося визначити мову гри. Будь ласка, виберіть мову вашої копії Heroes III</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="616"/>
-        <source>Install support for playing Heroes III in resolutions other than 800x600.</source>
-        <translation type="unfinished"></translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="759"/>
+        <source>Install a translation of Heroes III in your preferred language</source>
+        <translation>Встановити переклад Heroes III на вашу мову</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="645"/>
-        <source>Install compatible version of addon Horn of the Abyss: fan-made Heroes III expansion, ported by VCMI team</source>
-        <translation type="unfinished"></translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="775"/>
+        <source>Optionally, you can install additional mods either now, or at any point later, using the VCMI Launcher</source>
+        <translation>За бажанням ви можете встановити додаткові модифікації зараз або пізніше, використовуючи VCMI Launcher</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="674"/>
-        <source>Install compatible version of addon &quot;In The Wake of Gods&quot;: fan-made Heroes III expansion</source>
-        <translation type="unfinished"></translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="791"/>
+        <source>Install support for playing Heroes III in resolutions higher than 800x600</source>
+        <translation>Встановити підтримку для гри в Heroes III у роздільних здатностях, більших за 800x600</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="721"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="807"/>
+        <source>Install compatible version of &quot;Horn of the Abyss&quot;, a fan-made Heroes III expansion ported by the VCMI team</source>
+        <translation>Встановити сумісну версію доповнення &quot;Horn of the Abyss&quot;, фанатське доповнення Heroes III, портоване командою VCMI</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="823"/>
+        <source>Install compatible version of &quot;In The Wake of Gods&quot;, a fan-made Heroes III expansion</source>
+        <translation>Встановити сумісну версію доповнення &quot;In The Wake of Gods&quot;, фанатське доповнення до Heroes III</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="870"/>
         <source>Finish</source>
         <translation>Завершити</translation>
     </message>
@@ -484,86 +634,86 @@
         <translation>Крок %v з %m</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="143"/>
-        <source>Choose your language</source>
-        <translation>Оберіть свою мову</translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="175"/>
+        <source>VCMI on Github</source>
+        <translation>VCMI на Github</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="150"/>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="493"/>
-        <source>Next</source>
-        <translation>Далі</translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="182"/>
+        <source>VCMI on Slack</source>
+        <translation>VCMI на Slack</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="215"/>
-        <source>Find Heroes III data files</source>
-        <translation>Пошук файлів даних Heroes III</translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="189"/>
+        <source>VCMI on Discord</source>
+        <translation>VCMI на Discord</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="246"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="239"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="554"/>
+        <source>Next</source>
+        <translation>Далі</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="307"/>
         <source>Open help in browser</source>
         <translation>Відкрити довідку у браузері</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="259"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="320"/>
         <source>Search again</source>
         <translation>Повторити пошук</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="304"/>
-        <source>If you don&apos;t have installed Heroes III copy, it is possible to use our automatic installation tool &apos;vcmibuilder&apos; to extract data from GoG.com installer. Visit our wiki for detailed instructions.</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="320"/>
-        <source>VCMI requires Heroes III data files in one of the locations listed above. Please copy Heroes III data in one of these directories.</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="342"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="403"/>
         <source>Heroes III data files</source>
         <translation>Файли даних Heroes III</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="355"/>
-        <source>Alternatively, you can select directory with installed Heroes III data and VCMI will copy exisiting data automatically.</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="371"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="432"/>
         <source>Copy existing data</source>
         <translation>Копіювати наявні дані</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="414"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="475"/>
         <source>Your Heroes III language has been successfully detected.</source>
         <translation>Мову вашої гри Heroes III успішно визначено.</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="424"/>
-        <source>Automatic detection of language failed. Please select language of your Heroes III copy</source>
-        <translation>Не вдалося визначити мову гри. Будь ласка, виберіть мову вашої копії Heroes III</translation>
-    </message>
-    <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="443"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="504"/>
         <source>Heroes III language</source>
         <translation>Мова Heroes III</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="486"/>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="714"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="547"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="863"/>
         <source>Back</source>
         <translation>Назад</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="524"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="585"/>
         <source>Install VCMI Mod Preset</source>
         <translation>Встановлення початкових модифікацій VCMI</translation>
     </message>
     <message>
-        <location filename="../firstLaunch/firstlaunch_moc.ui" line="584"/>
-        <source>Install translation of Heroes III to your language</source>
-        <translation>Встановити переклад Heroes III на вашу мову</translation>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="635"/>
+        <source>Horn of the Abyss</source>
+        <translation>Horn of the Abyss</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="657"/>
+        <source>Heroes III Translation</source>
+        <translation>Переклад Heroes III</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="721"/>
+        <source>High Definition Support</source>
+        <translation>Підтримка високих роздільних здатностей</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="743"/>
+        <source>In The Wake of Gods</source>
+        <translation>In The Wake of Gods</translation>
     </message>
 </context>
 <context>
@@ -660,11 +810,6 @@
         <source>Server</source>
         <translation>Сервер</translation>
     </message>
-    <message>
-        <location filename="../lobby/lobby_moc.ui" line="76"/>
-        <source>People in lobby</source>
-        <translation>Гравці у лобі</translation>
-    </message>
     <message>
         <location filename="../lobby/lobby_moc.ui" line="114"/>
         <source>Lobby chat</source>
@@ -700,6 +845,11 @@
         <source>New room</source>
         <translation>Створити кімнату</translation>
     </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="76"/>
+        <source>Players in lobby</source>
+        <translation>Гравці у лобі</translation>
+    </message>
     <message>
         <location filename="../lobby/lobby_moc.ui" line="159"/>
         <source>Join room</source>
@@ -801,7 +951,7 @@
     <name>UpdateDialog</name>
     <message>
         <location filename="../updatedialog_moc.ui" line="71"/>
-        <source>You have latest version</source>
+        <source>You have the latest version</source>
         <translation>У вас встановлена остання версія</translation>
     </message>
     <message>
@@ -811,7 +961,7 @@
     </message>
     <message>
         <location filename="../updatedialog_moc.ui" line="101"/>
-        <source>Check updates on startup</source>
+        <source>Check for updates on startup</source>
         <translation>Перевіряти наявність оновлень при запуску</translation>
     </message>
 </context>

+ 2 - 2
launcher/updatedialog_moc.ui

@@ -68,7 +68,7 @@
       <enum>QFrame::Raised</enum>
      </property>
      <property name="text">
-      <string>You have latest version</string>
+      <string>You have the latest version</string>
      </property>
     </widget>
    </item>
@@ -98,7 +98,7 @@
    <item row="3" column="0" colspan="2">
     <widget class="QCheckBox" name="checkOnStartup">
      <property name="text">
-      <string>Check updates on startup</string>
+      <string>Check for updates on startup</string>
      </property>
     </widget>
    </item>

+ 8 - 5
lib/CCreatureHandler.cpp

@@ -328,12 +328,12 @@ bool CCreature::isItNativeTerrain(TerrainId terrain) const
 
 TerrainId CCreature::getNativeTerrain() const
 {
-	const std::string cachingStringNoTerrainPenalty = "type_NO_TERRAIN_PENALTY";
-	static const auto selectorNoTerrainPenalty = Selector::type()(Bonus::NO_TERRAIN_PENALTY);
+	const std::string cachingStringNoTerrainPenalty = "type_NO_TERRAIN_PENALTY_sANY";
+	static const auto selectorNoTerrainPenalty = Selector::typeSubtype(Bonus::NO_TERRAIN_PENALTY, static_cast<int>(ETerrainId::ANY_TERRAIN));
 
 	//this code is used in the CreatureTerrainLimiter::limit to setup battle bonuses
 	//and in the CGHeroInstance::getNativeTerrain() to setup movement bonuses or/and penalties.
-	return hasBonus(selectorNoTerrainPenalty, selectorNoTerrainPenalty)
+	return hasBonus(selectorNoTerrainPenalty, cachingStringNoTerrainPenalty)
 		? TerrainId(ETerrainId::ANY_TERRAIN)
 		: (*VLC->townh)[faction]->nativeTerrain;
 }
@@ -433,8 +433,11 @@ const CCreature * CCreatureHandler::getCreature(const std::string & scope, const
 
 void CCreatureHandler::loadCommanders()
 {
-	JsonNode data(ResourceID("config/commanders.json"));
-	data.setMeta(CModHandler::scopeBuiltin()); // assume that commanders are in core mod (for proper bonuses resolution)
+	ResourceID configResource("config/commanders.json");
+
+	std::string modSource = VLC->modh->findResourceOrigin(configResource);
+	JsonNode data(configResource);
+	data.setMeta(modSource);
 
 	const JsonNode & config = data; // switch to const data accessors
 

+ 1 - 1
lib/CCreatureSet.h

@@ -199,7 +199,7 @@ class DLL_LINKAGE CCreatureSet : public IArmyDescriptor //seven combined creatur
 	CCreatureSet &operator=(const CCreatureSet&);
 public:
 	TSlots stacks; //slots[slot_id]->> pair(creature_id,creature_quantity)
-	ui8 formation; //0 - wide, 1 - tight
+	ui8 formation = 0; //0 - wide, 1 - tight
 
 	CCreatureSet() = default; //Should be here to avoid compile errors
 	virtual ~CCreatureSet();

+ 18 - 14
lib/CGameState.cpp

@@ -1767,33 +1767,37 @@ void CGameState::initTowns()
 			}
 		}
 
-		//#1444 - remove entries that don't have buildings defined (like some unused extra town hall buildings)
-		vstd::erase_if(vti->builtBuildings, [vti](const BuildingID & bid)
-		{
-			return !vti->town->buildings.count(bid) || !vti->town->buildings.at(bid);
-		});
-
-		if (vstd::contains(vti->builtBuildings, BuildingID::SHIPYARD) && vti->shipyardStatus()==IBoatGenerator::TILE_BLOCKED)
-			vti->builtBuildings.erase(BuildingID::SHIPYARD);//if we have harbor without water - erase it (this is H3 behaviour)
-
 		//init hordes
-		for (int i = 0; i<GameConstants::CREATURES_PER_TOWN; i++)
-			if (vstd::contains(vti->builtBuildings,(-31-i))) //if we have horde for this level
+		for (int i = 0; i < GameConstants::CREATURES_PER_TOWN; i++)
+		{
+			if (vstd::contains(vti->builtBuildings, (BuildingID::HORDE_PLACEHOLDER1 - i))) //if we have horde for this level
 			{
-				vti->builtBuildings.erase(BuildingID(-31-i));//remove old ID
+				vti->builtBuildings.erase(BuildingID(BuildingID::HORDE_PLACEHOLDER1 - i));//remove old ID
 				if (vti->town->hordeLvl.at(0) == i)//if town first horde is this one
 				{
 					vti->builtBuildings.insert(BuildingID::HORDE_1);//add it
-					if (vstd::contains(vti->builtBuildings,(BuildingID::DWELL_UP_FIRST+i)))//if we have upgraded dwelling as well
+					//if we have upgraded dwelling as well
+					if (vstd::contains(vti->builtBuildings, (BuildingID::DWELL_UP_FIRST + i)))
 						vti->builtBuildings.insert(BuildingID::HORDE_1_UPGR);//add it as well
 				}
 				if (vti->town->hordeLvl.at(1) == i)//if town second horde is this one
 				{
 					vti->builtBuildings.insert(BuildingID::HORDE_2);
-					if (vstd::contains(vti->builtBuildings,(BuildingID::DWELL_UP_FIRST+i)))
+					if (vstd::contains(vti->builtBuildings, (BuildingID::DWELL_UP_FIRST + i)))
 						vti->builtBuildings.insert(BuildingID::HORDE_2_UPGR);
 				}
 			}
+		}
+
+		//#1444 - remove entries that don't have buildings defined (like some unused extra town hall buildings)
+		//But DO NOT remove horde placeholders before they are replaced
+		vstd::erase_if(vti->builtBuildings, [vti](const BuildingID & bid)
+			{
+				return !vti->town->buildings.count(bid) || !vti->town->buildings.at(bid);
+			});
+
+		if (vstd::contains(vti->builtBuildings, BuildingID::SHIPYARD) && vti->shipyardStatus()==IBoatGenerator::TILE_BLOCKED)
+			vti->builtBuildings.erase(BuildingID::SHIPYARD);//if we have harbor without water - erase it (this is H3 behaviour)
 
 		//Early check for #1444-like problems
 		for(const auto & building : vti->builtBuildings)

+ 27 - 13
lib/CGeneralTextHandler.cpp

@@ -48,6 +48,20 @@ void CGeneralTextHandler::detectInstallParameters()
 		"ukrainian"
 	} };
 
+	if(!CResourceHandler::get("core")->existsResource(ResourceID("DATA/GENRLTXT.TXT", EResType::TEXT)))
+	{
+		Settings language = settings.write["session"]["language"];
+		language->String() = "english";
+
+		Settings confidence = settings.write["session"]["languageDeviation"];
+		confidence->Float() = 1.0;
+
+		Settings encoding = settings.write["session"]["encoding"];
+		encoding->String() = Languages::getLanguageOptions("english").encoding;
+
+		return;
+	}
+
 	// load file that will be used for footprint generation
 	// this is one of the most text-heavy files in game and consists solely from translated texts
 	auto resource = CResourceHandler::get("core")->load(ResourceID("DATA/GENRLTXT.TXT", EResType::TEXT));
@@ -254,7 +268,7 @@ const std::string & CGeneralTextHandler::deserialize(const TextIdentifier & iden
 		return identifier.get();
 	}
 
-	auto const & entry = stringsLocalizations.at(identifier.get());
+	const auto & entry = stringsLocalizations.at(identifier.get());
 
 	if (!entry.overrideValue.empty())
 		return entry.overrideValue;
@@ -312,7 +326,7 @@ bool CGeneralTextHandler::validateTranslation(const std::string & language, cons
 {
 	bool allPresent = true;
 
-	for (auto const & string : stringsLocalizations)
+	for(const auto & string : stringsLocalizations)
 	{
 		if (string.second.modContext != modContext)
 			continue; // Not our mod
@@ -341,17 +355,17 @@ bool CGeneralTextHandler::validateTranslation(const std::string & language, cons
 
 	bool allFound = true;
 
-	for (auto const & string : config.Struct())
-	{
-		if (stringsLocalizations.count(string.first) > 0)
-			continue;
-
-		if (allFound)
-			logMod->warn("Translation into language '%s' in mod '%s' has unused lines:", language, modContext);
-
-		logMod->warn(R"(    "%s" : "%s",)", string.first, TextOperations::escapeString(string.second.String()));
-		allFound = false;
-	}
+//	for(const auto & string : config.Struct())
+//	{
+//		if (stringsLocalizations.count(string.first) > 0)
+//			continue;
+//
+//		if (allFound)
+//			logMod->warn("Translation into language '%s' in mod '%s' has unused lines:", language, modContext);
+//
+//		logMod->warn(R"(    "%s" : "%s",)", string.first, TextOperations::escapeString(string.second.String()));
+//		allFound = false;
+//	}
 
 	return allPresent && allFound;
 }

+ 9 - 0
lib/GameConstants.h

@@ -472,7 +472,16 @@ public:
 	enum EBuildingID
 	{
 		DEFAULT = -50,
+		HORDE_PLACEHOLDER7 = -36,
+		HORDE_PLACEHOLDER6 = -35,
+		HORDE_PLACEHOLDER5 = -34,
+		HORDE_PLACEHOLDER4 = -33,
+		HORDE_PLACEHOLDER3 = -32,
+		HORDE_PLACEHOLDER2 = -31,
+		HORDE_PLACEHOLDER1 = -30,
+		HORDE_BUILDING_CONVERTER = -29, //-1 => -30
 		NONE = -1,
+		FIRST_REGULAR_ID = 0,
 		MAGES_GUILD_1 = 0,  MAGES_GUILD_2, MAGES_GUILD_3,     MAGES_GUILD_4,   MAGES_GUILD_5,
 		TAVERN,         SHIPYARD,      FORT,              CITADEL,         CASTLE,
 		VILLAGE_HALL,   TOWN_HALL,     CITY_HALL,         CAPITOL,         MARKETPLACE,

+ 1 - 2
lib/JsonDetail.cpp

@@ -1059,8 +1059,7 @@ namespace
 
 		std::string defFile(const JsonNode & node)
 		{
-			TEST_FILE(node.meta, "Sprites/", node.String(), EResType::ANIMATION);
-			return "Def file \"" + node.String() + "\" was not found";
+			return testAnimation(node.String(), node.meta);
 		}
 
 		std::string animationFile(const JsonNode & node)

+ 1 - 0
lib/NetPacksLib.cpp

@@ -2174,6 +2174,7 @@ void BattleTriggerEffect::applyGs(CGameState * gs) const
 		break;
 	}
 	case Bonus::ENCHANTER:
+	case Bonus::MORALE:
 		break;
 	case Bonus::FEAR:
 		st->fear = true;

+ 5 - 1
lib/mapObjects/CObjectClassesHandler.cpp

@@ -508,7 +508,11 @@ void AObjectTypeHandler::init(const JsonNode & input)
 	if (!input["rmg"].isNull())
 	{
 		rmgInfo.value =     static_cast<ui32>(input["rmg"]["value"].Float());
-		rmgInfo.mapLimit =  loadJsonOrMax(input["rmg"]["mapLimit"]);
+
+		const JsonNode & mapLimit = input["rmg"]["mapLimit"];
+		if (!mapLimit.isNull())
+			rmgInfo.mapLimit.reset(static_cast<ui32>(mapLimit.Float()));
+
 		rmgInfo.zoneLimit = loadJsonOrMax(input["rmg"]["zoneLimit"]);
 		rmgInfo.rarity =    static_cast<ui32>(input["rmg"]["rarity"].Float());
 	} // else block is not needed - set in constructor

+ 3 - 2
lib/mapObjects/CObjectClassesHandler.h

@@ -43,7 +43,7 @@ struct DLL_LINKAGE RandomMapInfo
 	ui32 value;
 
 	/// How many of such objects can be placed on map, 0 = object can not be placed by RMG
-	ui32 mapLimit;
+	boost::optional<ui32> mapLimit;
 
 	/// How many of such objects can be placed in one zone, 0 = unplaceable
 	ui32 zoneLimit;
@@ -53,11 +53,12 @@ struct DLL_LINKAGE RandomMapInfo
 
 	RandomMapInfo():
 		value(0),
-		mapLimit(0),
 		zoneLimit(0),
 		rarity(0)
 	{}
 
+	void setMapLimit(ui32 val) { mapLimit.reset(val); }
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & value;

+ 14 - 0
lib/mapObjects/CRewardableConstructor.cpp

@@ -16,6 +16,7 @@
 #include "../CModHandler.h"
 #include "JsonRandom.h"
 #include "../IGameCallback.h"
+#include "../CGeneralTextHandler.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -307,9 +308,22 @@ bool CRandomRewardObjectInfo::givesBonuses() const
 	return testForKey(parameters, "bonuses");
 }
 
+const JsonNode & CRandomRewardObjectInfo::getParameters() const
+{
+	return parameters;
+}
+
 void CRewardableConstructor::initTypeData(const JsonNode & config)
 {
 	objectInfo.init(config);
+
+	if (!config["name"].isNull())
+		VLC->generaltexth->registerString( config.meta, getNameTextID(), config["name"].String());
+}
+
+bool CRewardableConstructor::hasNameTextID() const
+{
+	return !objectInfo.getParameters()["name"].isNull();
 }
 
 CGObjectInstance * CRewardableConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const

+ 4 - 0
lib/mapObjects/CRewardableConstructor.h

@@ -28,6 +28,8 @@ class DLL_LINKAGE CRandomRewardObjectInfo : public IObjectInfo
 	void configureReward(CRewardableObject * object, CRandomGenerator & rng, CRewardInfo & info, const JsonNode & source) const;
 	void configureResetInfo(CRewardableObject * object, CRandomGenerator & rng, CRewardResetInfo & info, const JsonNode & source) const;
 public:
+	const JsonNode & getParameters() const;
+
 	bool givesResources() const override;
 
 	bool givesExperience() const override;
@@ -60,6 +62,8 @@ class DLL_LINKAGE CRewardableConstructor : public AObjectTypeHandler
 	void initTypeData(const JsonNode & config) override;
 
 public:
+	bool hasNameTextID() const override;
+
 	CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
 
 	void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;

+ 6 - 1
lib/mapObjects/ObjectTemplate.h

@@ -105,7 +105,12 @@ public:
 	inline bool canBePlacedAtAnyTerrain() const
 	{
 		return anyTerrain;
-	}; 
+	};
+
+	const std::set<TerrainId>& getAllowedTerrains() const
+	{
+		return allowedTerrains;
+	}
 
 	// Checks if object can be placed on specific terrain
 	bool canBePlacedAt(TerrainId terrain) const;

+ 10 - 9
lib/mapping/MapFormatH3M.cpp

@@ -2086,7 +2086,7 @@ CGTownInstance * CMapLoaderH3M::readTown(int castleID, const int3 & position)
 
 std::set<BuildingID> CMapLoaderH3M::convertBuildings(const std::set<BuildingID> & h3m, int castleID, bool addAuxiliary) const
 {
-	std::map<int, BuildingID> mapa;
+	std::map<int, BuildingID> helperMap;
 	std::set<BuildingID> ret;
 
 	// Note: this file is parsed many times.
@@ -2098,23 +2098,24 @@ std::set<BuildingID> CMapLoaderH3M::convertBuildings(const std::set<BuildingID>
 
 		if (town == castleID || town == -1)
 		{
-			mapa[static_cast<int>(entry["h3"].Float())] = BuildingID(static_cast<si32>(entry["vcmi"].Float()));
+			helperMap[static_cast<int>(entry["h3"].Float())] = BuildingID(static_cast<si32>(entry["vcmi"].Float()));
 		}
 	}
 
 	for(const auto & elem : h3m)
 	{
-		if(mapa[elem] >= 0)
+		if(helperMap[elem] >= BuildingID::FIRST_REGULAR_ID)
 		{
-			ret.insert(mapa[elem]);
+			ret.insert(helperMap[elem]);
 		}
-		// horde buildings
-		else if(mapa[elem] >= (-GameConstants::CREATURES_PER_TOWN))
+		// horde buildings use indexes from -1 to -5, where creature level is 1 to 5
+		else if(helperMap[elem] >= (-GameConstants::CREATURES_PER_TOWN))
 		{
-			int level = (mapa[elem]);
+			int level = (helperMap[elem]);
 
-			//(-30)..(-36) - horde buildings (for game loading only), don't see other way to handle hordes in random towns
-			ret.insert(BuildingID(level - 30));
+			//(-30)..(-36) - horde buildings (for game loading only)
+			//They will be replaced in CGameState::initTowns()
+			ret.insert(BuildingID(level + BuildingID::HORDE_BUILDING_CONVERTER)); //-1 => -30
 		}
 		else
 		{

+ 9 - 9
lib/rmg/CMapGenerator.cpp

@@ -276,11 +276,11 @@ void CMapGenerator::genZones()
 
 void CMapGenerator::createWaterTreasures()
 {
-	if(!getZoneWater())
+	if (!getZoneWater())
 		return;
-	
+
 	//add treasures on water
-	for(const auto & treasureInfo : getConfig().waterTreasure)
+	for (const auto& treasureInfo : getConfig().waterTreasure)
 	{
 		getZoneWater()->addTreasureInfo(treasureInfo);
 	}
@@ -296,7 +296,7 @@ void CMapGenerator::fillZones()
 	//we need info about all town types to evaluate dwellings and pandoras with creatures properly
 	//place main town in the middle
 	Load::Progress::setupStepsTill(map->getZones().size(), 50);
-	for(const auto & it : map->getZones())
+	for (const auto& it : map->getZones())
 	{
 		it.second->initFreeTiles();
 		it.second->initModificators();
@@ -305,10 +305,10 @@ void CMapGenerator::fillZones()
 
 	Load::Progress::setupStepsTill(map->getZones().size(), 240);
 	std::vector<std::shared_ptr<Zone>> treasureZones;
-	for(const auto & it : map->getZones())
+	for (const auto& it : map->getZones())
 	{
 		it.second->processModificators();
-		
+
 		if (it.second->getType() == ETemplateZoneType::TREASURE)
 			treasureZones.push_back(it.second);
 
@@ -316,10 +316,10 @@ void CMapGenerator::fillZones()
 	}
 
 	//find place for Grail
-	if(treasureZones.empty())
+	if (treasureZones.empty())
 	{
-		for(const auto & it : map->getZones())
-			if(it.second->getType() != ETemplateZoneType::WATER)
+		for (const auto& it : map->getZones())
+			if (it.second->getType() != ETemplateZoneType::WATER)
 				treasureZones.push_back(it.second);
 	}
 	auto grailZone = *RandomGeneratorUtil::nextItem(treasureZones, rand);

+ 21 - 0
lib/rmg/CRmgTemplate.cpp

@@ -136,6 +136,7 @@ ZoneOptions::ZoneOptions():
 	id(0),
 	type(ETemplateZoneType::PLAYER_START),
 	size(1),
+	maxTreasureValue(0),
 	owner(boost::none),
 	matchTerrainToTown(true),
 	townsAreSameType(false),
@@ -242,11 +243,22 @@ std::map<TResource, ui16> ZoneOptions::getMinesInfo() const
 void ZoneOptions::setTreasureInfo(const std::vector<CTreasureInfo> & value)
 {
 	treasureInfo = value;
+	recalculateMaxTreasureValue();
+}
+
+void ZoneOptions::recalculateMaxTreasureValue()
+{
+	maxTreasureValue = 0;
+	for (const auto& ti : treasureInfo)
+	{
+		vstd::amax(maxTreasureValue, ti.max);
+	}
 }
 	
 void ZoneOptions::addTreasureInfo(const CTreasureInfo & value)
 {
 	treasureInfo.push_back(value);
+	vstd::amax(maxTreasureValue, value.max);
 }
 
 const std::vector<CTreasureInfo> & ZoneOptions::getTreasureInfo() const
@@ -254,6 +266,11 @@ const std::vector<CTreasureInfo> & ZoneOptions::getTreasureInfo() const
 	return treasureInfo;
 }
 
+ui32 ZoneOptions::getMaxTreasureValue() const
+{
+	return maxTreasureValue;
+}
+
 TRmgTemplateZoneId ZoneOptions::getMinesLikeZone() const
 {
 	return minesLikeZone;
@@ -386,6 +403,10 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler)
 	{
 		auto treasureData = handler.enterArray("treasure");
 		treasureData.serializeStruct(treasureInfo);
+		if (!handler.saving)
+		{
+			recalculateMaxTreasureValue();
+		}
 	}
 
 	if((minesLikeZone == NO_ZONE) && (!handler.saving || !mines.empty()))

+ 3 - 0
lib/rmg/CRmgTemplate.h

@@ -144,6 +144,8 @@ public:
 	void setTreasureInfo(const std::vector<CTreasureInfo> & value);
 	void addTreasureInfo(const CTreasureInfo & value);
 	const std::vector<CTreasureInfo> & getTreasureInfo() const;
+	ui32 getMaxTreasureValue() const;
+	void recalculateMaxTreasureValue();
 
 	TRmgTemplateZoneId getMinesLikeZone() const;
 	TRmgTemplateZoneId getTerrainTypeLikeZone() const;
@@ -163,6 +165,7 @@ protected:
 	TRmgTemplateZoneId id;
 	ETemplateZoneType::ETemplateZoneType type;
 	int size;
+	ui32 maxTreasureValue;
 	boost::optional<int> owner;
 	CTownInfo playerTowns;
 	CTownInfo neutralTowns;

+ 97 - 0
lib/rmg/MinePlacer.cpp

@@ -0,0 +1,97 @@
+/*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+
+#include "StdInc.h"
+#include "MinePlacer.h"
+#include "TownPlacer.h"
+#include "CMapGenerator.h"
+#include "RmgMap.h"
+#include "../mapping/CMap.h"
+#include "../mapping/CMapEditManager.h"
+#include "../mapObjects/CObjectClassesHandler.h"
+#include "../spells/CSpellHandler.h" //for choosing random spells
+#include "RmgPath.h"
+#include "RmgObject.h"
+#include "ObjectManager.h"
+#include "Functions.h"
+#include "RoadPlacer.h"
+#include "WaterAdopter.h"
+#include "TileInfo.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+void MinePlacer::process()
+{
+	auto * manager = zone.getModificator<ObjectManager>();
+	if(!manager)
+	{
+		logGlobal->error("ObjectManager doesn't exist for zone %d, skip modificator %s", zone.getId(), getName());
+		return;
+	}
+
+	placeMines(*manager);
+}
+
+void MinePlacer::init()
+{
+	DEPENDENCY(TownPlacer);
+	POSTFUNCTION(ObjectManager);
+	POSTFUNCTION(RoadPlacer);
+}
+
+bool MinePlacer::placeMines(ObjectManager & manager)
+{
+	using namespace Res;
+	std::vector<CGMine*> createdMines;
+
+	std::vector<std::pair<CGObjectInstance*, ui32>> requiredObjects;
+
+	for(const auto & mineInfo : zone.getMinesInfo())
+	{
+		ERes res = static_cast<ERes>(mineInfo.first);
+		for(int i = 0; i < mineInfo.second; ++i)
+		{
+			auto mineHandler = VLC->objtypeh->getHandlerFor(Obj::MINE, res);
+			const auto & rmginfo = mineHandler->getRMGInfo();
+			auto * mine = dynamic_cast<CGMine *>(mineHandler->create());
+			mine->producedResource = res;
+			mine->tempOwner = PlayerColor::NEUTRAL;
+			mine->producedQuantity = mine->defaultResProduction();
+			createdMines.push_back(mine);
+
+
+			if(!i && (res == ERes::WOOD || res == ERes::ORE))
+				manager.addCloseObject(mine, rmginfo.value); //only first wood&ore mines are close
+			else
+				requiredObjects.push_back(std::pair<CGObjectInstance*, ui32>(mine, rmginfo.value));
+		}
+	}
+
+	//Shuffle mines to avoid patterns, but don't shuffle key objects like towns
+	RandomGeneratorUtil::randomShuffle(requiredObjects, generator.rand);
+	for (const auto& obj : requiredObjects)
+	{
+		manager.addRequiredObject(obj.first, obj.second);
+	}
+
+	//create extra resources
+	if(int extraRes = generator.getConfig().mineExtraResources)
+	{
+		for(auto * mine : createdMines)
+		{
+			for(int rc = generator.rand.nextInt(1, extraRes); rc > 0; --rc)
+			{
+				auto * resourse = dynamic_cast<CGResource *>(VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create());
+				resourse->amount = CGResource::RANDOM_AMOUNT;
+				manager.addNearbyObject(resourse, mine);
+			}
+		}
+	}
+
+	return true;
+}
+
+VCMI_LIB_NAMESPACE_END

+ 30 - 0
lib/rmg/MinePlacer.h

@@ -0,0 +1,30 @@
+/*
+* MinePlacer.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+#include "Zone.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class ObjectManager;
+
+class MinePlacer: public Modificator
+{
+public:
+	MODIFICATOR(MinePlacer);
+
+	void process() override;
+	void init() override;
+
+protected:
+	bool placeMines(ObjectManager & manager);
+
+};
+
+VCMI_LIB_NAMESPACE_END

+ 121 - 0
lib/rmg/ObjectDistributor.cpp

@@ -0,0 +1,121 @@
+/*
+* ObjectDistributor.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+
+#include "StdInc.h"
+#include "ObjectDistributor.h"
+
+#include "../VCMI_Lib.h"
+#include "RmgMap.h"
+#include "CMapGenerator.h"
+#include "TreasurePlacer.h"
+#include "TownPlacer.h"
+#include "TerrainPainter.h"
+#include "../mapObjects/CObjectClassesHandler.h"
+#include "../mapObjects/MapObjects.h"
+#include "Functions.h"
+#include "RmgObject.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+void ObjectDistributor::process()
+{
+	//Firts call will add objects to ALL zones, once they were added skip it
+	if (zone.getModificator<TreasurePlacer>()->getPossibleObjectsSize() == 0)
+	{
+		ObjectDistributor::distributeLimitedObjects();
+	}
+}
+
+void ObjectDistributor::init()
+{
+	DEPENDENCY(TownPlacer);
+	DEPENDENCY(TerrainPainter);
+	POSTFUNCTION(TreasurePlacer);
+}
+
+void ObjectDistributor::distributeLimitedObjects()
+{
+	//FIXME: Must be called after TerrainPainter::process()
+
+	ObjectInfo oi;
+	auto zones = map.getZones();
+
+	for (auto primaryID : VLC->objtypeh->knownObjects())
+	{
+		for (auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID))
+		{
+			auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID);
+			if (!handler->isStaticObject() && handler->getRMGInfo().value)
+			{
+				auto rmgInfo = handler->getRMGInfo();
+
+				//Skip objects which don't have global per-map limit here 
+				if (rmgInfo.mapLimit)
+				{
+					//Count all zones where this object can be placed
+					std::vector<std::shared_ptr<Zone>> matchingZones;
+
+					for (const auto& it : zones)
+					{
+						if (!handler->getTemplates(it.second->getTerrainType()).empty() &&
+							rmgInfo.value <= it.second->getMaxTreasureValue())
+						{
+							matchingZones.push_back(it.second);
+						}
+					}
+
+					size_t numZones = matchingZones.size();
+					if (!numZones)
+						continue;
+
+					auto rmgInfo = handler->getRMGInfo();
+
+					for (auto& zone : matchingZones)
+					{
+						//We already know there are some templates
+						auto templates = handler->getTemplates(zone->getTerrainType());
+
+						//Assume the template with fewest terrains is the most suitable
+						auto temp = *boost::min_element(templates, [](std::shared_ptr<const ObjectTemplate> lhs, std::shared_ptr<const ObjectTemplate> rhs) -> bool
+						{
+							return lhs->getAllowedTerrains().size() < rhs->getAllowedTerrains().size();
+						});
+
+						oi.generateObject = [temp]() -> CGObjectInstance *
+						{
+							return VLC->objtypeh->getHandlerFor(temp->id, temp->subid)->create(temp);
+						};
+						
+						oi.value = rmgInfo.value;
+						oi.probability = rmgInfo.rarity;
+						oi.templ = temp;
+
+						//Rounding up will make sure all possible objects are exhausted
+						uint32_t mapLimit = rmgInfo.mapLimit.get();
+						uint32_t maxPerZone = std::ceil(float(mapLimit) / numZones);
+
+						//But not more than zone limit
+						oi.maxPerZone = std::min(maxPerZone, rmgInfo.zoneLimit);
+						numZones--;
+
+						rmgInfo.setMapLimit(mapLimit - oi.maxPerZone);
+						//Don't add objects with 0 count remaining
+						if (oi.maxPerZone)
+						{
+							zone->getModificator<TreasurePlacer>()->addObjectToRandomPool(oi);
+						}
+					}
+				}
+			}
+		}
+	}
+}
+
+VCMI_LIB_NAMESPACE_END

+ 32 - 0
lib/rmg/ObjectDistributor.h

@@ -0,0 +1,32 @@
+/*
+* ObjectDistributor.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+
+#pragma once
+
+#include "Zone.h"
+#include "RmgObject.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class CGObjectInstance;
+class ObjectTemplate;
+
+class ObjectDistributor : public Modificator
+{
+	void distributeLimitedObjects();
+
+public:
+	MODIFICATOR(ObjectDistributor);
+
+	void process() override;
+	void init() override;
+};
+
+VCMI_LIB_NAMESPACE_END

+ 1 - 2
lib/rmg/ObjectManager.cpp

@@ -236,8 +236,7 @@ rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg
 bool ObjectManager::createRequiredObjects()
 {
 	logGlobal->trace("Creating required objects");
-	
-	RandomGeneratorUtil::randomShuffle(requiredObjects, generator.rand);	
+		
 	for(const auto & object : requiredObjects)
 	{
 		auto * obj = object.first;

+ 4 - 0
lib/rmg/RmgMap.cpp

@@ -21,6 +21,8 @@
 #include "TreasurePlacer.h"
 #include "ConnectionsPlacer.h"
 #include "TownPlacer.h"
+#include "MinePlacer.h"
+#include "ObjectDistributor.h"
 #include "WaterAdopter.h"
 #include "WaterProxy.h"
 #include "WaterRoutes.h"
@@ -118,6 +120,7 @@ void RmgMap::addModificators()
 		auto zone = z.second;
 		
 		zone->addModificator<ObjectManager>();
+		zone->addModificator<ObjectDistributor>();
 		zone->addModificator<TreasurePlacer>();
 		zone->addModificator<ObstaclePlacer>();
 		zone->addModificator<TerrainPainter>();
@@ -135,6 +138,7 @@ void RmgMap::addModificators()
 		else
 		{
 			zone->addModificator<TownPlacer>();
+			zone->addModificator<MinePlacer>();
 			zone->addModificator<ConnectionsPlacer>();
 			zone->addModificator<RoadPlacer>();
 			zone->addModificator<RiverPlacer>();

+ 3 - 47
lib/rmg/TownPlacer.cpp

@@ -21,6 +21,7 @@
 #include "ObjectManager.h"
 #include "Functions.h"
 #include "RoadPlacer.h"
+#include "MinePlacer.h"
 #include "WaterAdopter.h"
 #include "TileInfo.h"
 
@@ -35,14 +36,12 @@ void TownPlacer::process()
 		return;
 	}
 	
-	
 	placeTowns(*manager);
-	placeMines(*manager);
 }
 
 void TownPlacer::init()
 {
-	POSTFUNCTION(ObjectManager);
+	POSTFUNCTION(MinePlacer);
 	POSTFUNCTION(RoadPlacer);
 } 
 
@@ -146,56 +145,13 @@ int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town)
 		float distance = zone.getPos().dist2dSQ(t);
 		return 100000.f - distance; //some big number
 	}, ObjectManager::OptimizeType::WEIGHT);
-	rmgObject.setPosition(position);
+	rmgObject.setPosition(position + int3(2, 2, 0)); //place visitable tile in the exact center of a zone
 	manager.placeObject(rmgObject, false, true);
 	cleanupBoundaries(rmgObject);
 	zone.setPos(rmgObject.getVisitablePosition()); //roads lead to main town
 	return position;
 }
 
-bool TownPlacer::placeMines(ObjectManager & manager)
-{
-	using namespace Res;
-	std::vector<CGMine*> createdMines;
-	
-	for(const auto & mineInfo : zone.getMinesInfo())
-	{
-		ERes res = static_cast<ERes>(mineInfo.first);
-		for(int i = 0; i < mineInfo.second; ++i)
-		{
-			auto mineHandler = VLC->objtypeh->getHandlerFor(Obj::MINE, res);
-			const auto & rmginfo = mineHandler->getRMGInfo();
-			auto * mine = dynamic_cast<CGMine *>(mineHandler->create());
-			mine->producedResource = res;
-			mine->tempOwner = PlayerColor::NEUTRAL;
-			mine->producedQuantity = mine->defaultResProduction();
-			createdMines.push_back(mine);
-			
-			
-			if(!i && (res == ERes::WOOD || res == ERes::ORE))
-				manager.addCloseObject(mine, rmginfo.value); //only first wood&ore mines are close
-			else
-				manager.addRequiredObject(mine, rmginfo.value);
-		}
-	}
-	
-	//create extra resources
-	if(int extraRes = generator.getConfig().mineExtraResources)
-	{
-		for(auto * mine : createdMines)
-		{
-			for(int rc = generator.rand.nextInt(1, extraRes); rc > 0; --rc)
-			{
-				auto * resourse = dynamic_cast<CGResource *>(VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create());
-				resourse->amount = CGResource::RANDOM_AMOUNT;
-				manager.addNearbyObject(resourse, mine);
-			}
-		}
-	}
-	
-	return true;
-}
-
 void TownPlacer::cleanupBoundaries(const rmg::Object & rmgObject)
 {
 	for(const auto & t : rmgObject.getArea().getBorderOutside())

+ 49 - 32
lib/rmg/TreasurePlacer.cpp

@@ -46,12 +46,15 @@ void TreasurePlacer::setQuestArtZone(Zone * otherZone)
 	questArtZone = otherZone;
 }
 
+void TreasurePlacer::addObjectToRandomPool(const ObjectInfo& oi)
+{
+	possibleObjects.push_back(oi);
+}
+
 void TreasurePlacer::addAllPossibleObjects()
 {
 	ObjectInfo oi;
 	
-	int numZones = static_cast<int>(map.getZones().size());
-	
 	for(auto primaryID : VLC->objtypeh->knownObjects())
 	{
 		for(auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID))
@@ -59,23 +62,32 @@ void TreasurePlacer::addAllPossibleObjects()
 			auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID);
 			if(!handler->isStaticObject() && handler->getRMGInfo().value)
 			{
-				for(const auto & temp : handler->getTemplates())
+				auto rmgInfo = handler->getRMGInfo();
+				if (rmgInfo.mapLimit || rmgInfo.value > zone.getMaxTreasureValue())
 				{
-					if(temp->canBePlacedAt(zone.getTerrainType()))
-					{
-						oi.generateObject = [temp]() -> CGObjectInstance *
-						{
-							return VLC->objtypeh->getHandlerFor(temp->id, temp->subid)->create(temp);
-						};
-						auto rmgInfo = handler->getRMGInfo();
-						oi.value = rmgInfo.value;
-						oi.probability = rmgInfo.rarity;
-						oi.templ = temp;
-						oi.maxPerZone = rmgInfo.zoneLimit;
-						vstd::amin(oi.maxPerZone, rmgInfo.mapLimit / numZones); //simple, but should distribute objects evenly on large maps
-						possibleObjects.push_back(oi);
-					}
+					//Skip objects with per-map limit here
+					continue;
 				}
+
+				auto templates = handler->getTemplates(zone.getTerrainType());
+				if (templates.empty())
+					continue;
+
+				//Assume the template with fewest terrains is the most suitable
+				auto temp = *boost::min_element(templates, [](std::shared_ptr<const ObjectTemplate> lhs, std::shared_ptr<const ObjectTemplate> rhs) -> bool
+				{
+					return lhs->getAllowedTerrains().size() < rhs->getAllowedTerrains().size();
+				});
+
+				oi.generateObject = [temp]() -> CGObjectInstance *
+				{
+					return VLC->objtypeh->getHandlerFor(temp->id, temp->subid)->create(temp);
+				};
+				oi.value = rmgInfo.value;
+				oi.probability = rmgInfo.rarity;
+				oi.templ = temp;
+				oi.maxPerZone = rmgInfo.zoneLimit;
+				addObjectToRandomPool(oi);
 			}
 		}
 	}
@@ -114,7 +126,7 @@ void TreasurePlacer::addAllPossibleObjects()
 		oi.value = generator.getConfig().prisonValues[i];
 		oi.probability = 30;
 		oi.maxPerZone = generator.getPrisonsRemaning() / 5; //probably not perfect, but we can't generate more prisons than hereos.
-		possibleObjects.push_back(oi);
+		addObjectToRandomPool(oi);
 	}
 	
 	//all following objects are unlimited
@@ -170,7 +182,7 @@ void TreasurePlacer::addAllPossibleObjects()
 						};
 
 						oi.templ = tmplate;
-						possibleObjects.push_back(oi);
+						addObjectToRandomPool(oi);
 					}
 				}
 			}
@@ -199,7 +211,7 @@ void TreasurePlacer::addAllPossibleObjects()
 		oi.setTemplate(Obj::SPELL_SCROLL, 0, zone.getTerrainType());
 		oi.value = generator.getConfig().scrollValues[i];
 		oi.probability = 30;
-		possibleObjects.push_back(oi);
+		addObjectToRandomPool(oi);
 	}
 	
 	//pandora box with gold
@@ -215,7 +227,7 @@ void TreasurePlacer::addAllPossibleObjects()
 		oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
 		oi.value = i * generator.getConfig().pandoraMultiplierGold;
 		oi.probability = 5;
-		possibleObjects.push_back(oi);
+		addObjectToRandomPool(oi);
 	}
 	
 	//pandora box with experience
@@ -231,7 +243,7 @@ void TreasurePlacer::addAllPossibleObjects()
 		oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
 		oi.value = i * generator.getConfig().pandoraMultiplierExperience;
 		oi.probability = 20;
-		possibleObjects.push_back(oi);
+		addObjectToRandomPool(oi);
 	}
 	
 	//pandora box with creatures
@@ -284,7 +296,7 @@ void TreasurePlacer::addAllPossibleObjects()
 		oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
 		oi.value = static_cast<ui32>((2 * (creature->AIValue) * creaturesAmount * (1 + static_cast<float>(map.getZoneCount(creature->faction)) / map.getTotalZoneCount())) / 3);
 		oi.probability = 3;
-		possibleObjects.push_back(oi);
+		addObjectToRandomPool(oi);
 	}
 	
 	//Pandora with 12 spells of certain level
@@ -313,7 +325,7 @@ void TreasurePlacer::addAllPossibleObjects()
 		oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
 		oi.value = (i + 1) * generator.getConfig().pandoraMultiplierSpells; //5000 - 15000
 		oi.probability = 2;
-		possibleObjects.push_back(oi);
+		addObjectToRandomPool(oi);
 	}
 	
 	//Pandora with 15 spells of certain school
@@ -342,7 +354,7 @@ void TreasurePlacer::addAllPossibleObjects()
 		oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
 		oi.value = generator.getConfig().pandoraSpellSchool;
 		oi.probability = 2;
-		possibleObjects.push_back(oi);
+		addObjectToRandomPool(oi);
 	}
 	
 	// Pandora box with 60 random spells
@@ -370,7 +382,7 @@ void TreasurePlacer::addAllPossibleObjects()
 	oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
 	oi.value = generator.getConfig().pandoraSpell60;
 	oi.probability = 2;
-	possibleObjects.push_back(oi);
+	addObjectToRandomPool(oi);
 	
 	//seer huts with creatures or generic rewards
 	
@@ -436,14 +448,14 @@ void TreasurePlacer::addAllPossibleObjects()
 				generator.banQuestArt(artid);
 				
 				
-				this->questArtZone->getModificator<TreasurePlacer>()->possibleObjects.push_back(generateArtInfo(artid));
+				this->questArtZone->getModificator<TreasurePlacer>()->addObjectToRandomPool(generateArtInfo(artid));
 				
 				return obj;
 			};
 			oi.setTemplate(Obj::SEER_HUT, randomAppearance, zone.getTerrainType());
 			oi.value = static_cast<ui32>(((2 * (creature->AIValue) * creaturesAmount * (1 + static_cast<float>(map.getZoneCount(creature->faction)) / map.getTotalZoneCount())) - 4000) / 3);
 			oi.probability = 3;
-			possibleObjects.push_back(oi);
+			addObjectToRandomPool(oi);
 		}
 		
 		static int seerLevels = std::min(generator.getConfig().questValues.size(), generator.getConfig().questRewardValues.size());
@@ -472,12 +484,12 @@ void TreasurePlacer::addAllPossibleObjects()
 				
 				generator.banQuestArt(artid);
 				
-				this->questArtZone->getModificator<TreasurePlacer>()->possibleObjects.push_back(generateArtInfo(artid));
+				this->questArtZone->getModificator<TreasurePlacer>()->addObjectToRandomPool(generateArtInfo(artid));
 				
 				return obj;
 			};
 			
-			possibleObjects.push_back(oi);
+			addObjectToRandomPool(oi);
 			
 			oi.generateObject = [i, randomAppearance, this, generateArtInfo]() -> CGObjectInstance *
 			{
@@ -495,16 +507,21 @@ void TreasurePlacer::addAllPossibleObjects()
 				
 				generator.banQuestArt(artid);
 				
-				this->questArtZone->getModificator<TreasurePlacer>()->possibleObjects.push_back(generateArtInfo(artid));
+				this->questArtZone->getModificator<TreasurePlacer>()->addObjectToRandomPool(generateArtInfo(artid));
 				
 				return obj;
 			};
 			
-			possibleObjects.push_back(oi);
+			addObjectToRandomPool(oi);
 		}
 	}
 }
 
+size_t TreasurePlacer::getPossibleObjectsSize() const
+{
+	return possibleObjects.size();
+}
+
 bool TreasurePlacer::isGuardNeededForTreasure(int value)
 {
 	return zone.getType() != ETemplateZoneType::WATER && value > minGuardedValue;

+ 3 - 0
lib/rmg/TreasurePlacer.h

@@ -45,7 +45,10 @@ public:
 	void createTreasures(ObjectManager & manager);
 	
 	void setQuestArtZone(Zone * otherZone);
+	void addObjectToRandomPool(const ObjectInfo& oi);
 	void addAllPossibleObjects(); //add objects, including zone-specific, to possibleObjects
+
+	size_t getPossibleObjectsSize() const;
 	
 protected:
 	bool isGuardNeededForTreasure(int value);

+ 4 - 12
lib/serializer/Connection.cpp

@@ -83,7 +83,7 @@ CConnection::CConnection(const std::string & host, ui16 port, std::string Name,
 	if(error)
 	{
 		logNetwork->error("Problem with resolving: \n%s", error.message());
-		goto connerror1;
+		throw std::runtime_error("Can't establish connection: Problem with resolving");
 	}
 	pom = endpoint_iterator;
 	if(pom != end)
@@ -91,7 +91,7 @@ CConnection::CConnection(const std::string & host, ui16 port, std::string Name,
 	else
 	{
 		logNetwork->error("Critical problem: No endpoints found!");
-		goto connerror1;
+		throw std::runtime_error("Can't establish connection: No endpoints found!");
 	}
 	while(pom != end)
 	{
@@ -110,20 +110,12 @@ CConnection::CConnection(const std::string & host, ui16 port, std::string Name,
 		}
 		else
 		{
-			logNetwork->error("Problem with connecting: %s", error.message());
+			throw std::runtime_error("Can't establish connection: Failed to connect!");
 		}
 		endpoint_iterator++;
 	}
-
-	//we shouldn't be here - error handling
-connerror1:
-	logNetwork->error("Something went wrong... checking for error info");
-	if(error)
-		logNetwork->error(error.message());
-	else
-		logNetwork->error("No error info. ");
-	throw std::runtime_error("Can't establish connection :(");
 }
+
 CConnection::CConnection(std::shared_ptr<TSocket> Socket, std::string Name, std::string UUID):
 	iser(this),
 	oser(this),

+ 36 - 19
server/CGameHandler.cpp

@@ -52,6 +52,8 @@
 #include "../lib/serializer/Cast.h"
 #include "../lib/serializer/JsonSerializer.h"
 #include "../lib/ScriptHandler.h"
+#include "vstd/CLoggerBase.h"
+#include <memory>
 #include <vcmi/events/EventBus.h>
 #include <vcmi/events/GenericEvents.h>
 #include <vcmi/events/AdventureEvents.h>
@@ -991,7 +993,7 @@ void CGameHandler::makeAttack(const CStack * attacker, const CStack * defender,
 		auto spell = bat.spellID.toSpell();
 
 		battle::Target target;
-		target.emplace_back(defender);
+		target.emplace_back(defender, targetHex);
 
 		spells::BattleCast event(gs->curB, attacker, spells::Mode::SPELL_LIKE_ATTACK, spell);
 		event.setSpellLevel(bonus->val);
@@ -1340,7 +1342,11 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
 
 	ret = path.second;
 
-	int creSpeed = gs->curB->tacticDistance ? GameConstants::BFIELD_SIZE : curStack->Speed(0, true);
+	int creSpeed = curStack->Speed(0, true);
+
+	if (gs->curB->tacticDistance > 0 && creSpeed > 0)
+		creSpeed = GameConstants::BFIELD_SIZE;
+
 	bool hasWideMoat = vstd::contains_if(battleGetAllObstaclesOnPos(BattleHex(ESiegeHex::GATE_BRIDGE), false), [](const std::shared_ptr<const CObstacleInstance> & obst)
 	{
 		return obst->obstacleType == CObstacleInstance::MOAT;
@@ -1589,6 +1595,12 @@ CGameHandler::CGameHandler(CVCMIServer * lobby)
 
 CGameHandler::~CGameHandler()
 {
+	if (battleThread)
+	{
+		//Setting battleMadeAction is needed because battleThread waits for the action to continue the main loop
+		battleMadeAction.setn(true);
+		battleThread->join();
+	}
 	delete spellEnv;
 	delete gs;
 }
@@ -2325,6 +2337,9 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, boo
 		if (leavingTile == LEAVING_TILE)
 			leaveTile();
 
+		if (isInTheMap(guardPos))
+			tmh.attackedFrom = boost::make_optional(guardPos);
+
 		tmh.result = result;
 		sendAndApply(&tmh);
 
@@ -2332,10 +2347,8 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, boo
 		{ // Hero should be always able to visit any object he staying on even if there guards around
 			visitObjectOnTile(t, h);
 		}
-		else if (lookForGuards == CHECK_FOR_GUARDS && this->isInTheMap(guardPos))
+		else if (lookForGuards == CHECK_FOR_GUARDS && isInTheMap(guardPos))
 		{
-			tmh.attackedFrom = boost::make_optional(guardPos);
-
 			const TerrainTile &guardTile = *gs->getTile(guardPos);
 			objectVisited(guardTile.visitableObjects.back(), h);
 
@@ -2630,7 +2643,7 @@ void CGameHandler::startBattlePrimary(const CArmedInstance *army1, const CArmedI
 	auto battleQuery = std::make_shared<CBattleQuery>(this, gs->curB);
 	queries.addQuery(battleQuery);
 
-	boost::thread(&CGameHandler::runBattle, this);
+	this->battleThread = std::make_unique<boost::thread>(boost::thread(&CGameHandler::runBattle, this));
 }
 
 void CGameHandler::startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank)
@@ -4888,8 +4901,12 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
 void CGameHandler::playerMessage(PlayerColor player, const std::string &message, ObjectInstanceID currObj)
 {
 	bool cheated = false;
-	PlayerMessageClient temp_message(player, message);
-	sendAndApply(&temp_message);
+	
+	if(!getPlayerSettings(player)->isControlledByAI())
+	{
+		PlayerMessageClient temp_message(player, message);
+		sendAndApply(&temp_message);
+	}
 
 	std::vector<std::string> words;
 	boost::split(words, message, boost::is_any_of(" "));
@@ -4943,7 +4960,7 @@ void CGameHandler::playerMessage(PlayerColor player, const std::string &message,
 	}
 	
 	int obj = 0;
-	if (words.size() == 2 && words[0] != "vcmiexp")
+	if (words.size() == 2 && words[0] != "vcmiexp" && words[0] != "vcmiolorin")
 	{
 		obj = std::atoi(words[1].c_str());
 		if (obj)
@@ -4955,7 +4972,7 @@ void CGameHandler::playerMessage(PlayerColor player, const std::string &message,
 	if (!town && hero)
 		town = hero->visitedTown;
 
-	if((words[0] == "vcmiarmy" || words[0] == "vcmiexp") && words.size() > 1)
+	if(words.size() > 1 && (words[0] == "vcmiarmy" || words[0] == "vcminissi" || words[0] == "vcmiexp" || words[0] == "vcmiolorin"))
 	{
 		std::string cheatCodeWithOneParameter = std::string(words[0]) + " " + words[1];
 		handleCheatCode(cheatCodeWithOneParameter, player, hero, town, cheated);
@@ -5591,7 +5608,7 @@ void CGameHandler::objectVisitEnded(const CObjectVisitQuery & query)
 	ObjectVisitEnded::defaultExecute(serverEventBus.get(), endVisit, query.players.front(), query.visitingHero->id);
 }
 
-bool CGameHandler::buildBoat(ObjectInstanceID objid)
+bool CGameHandler::buildBoat(ObjectInstanceID objid, PlayerColor playerID)
 {
 	const IShipyard *obj = IShipyard::castFrom(getObj(objid));
 
@@ -5607,7 +5624,6 @@ bool CGameHandler::buildBoat(ObjectInstanceID objid)
 		return false;
 	}
 
-	const PlayerColor playerID = obj->o->tempOwner;
 	TResources boatCost;
 	obj->getBoatCost(boatCost);
 	TResources aviable = getPlayerState(playerID)->resources;
@@ -6339,7 +6355,7 @@ void CGameHandler::runBattle()
 
 	//tactic round
 	{
-		while (gs->curB->tacticDistance && !battleResult.get())
+		while ((lobby->state != EServerState::SHUTDOWN) && gs->curB->tacticDistance && !battleResult.get())
 			boost::this_thread::sleep(boost::posix_time::milliseconds(50));
 	}
 
@@ -6417,7 +6433,7 @@ void CGameHandler::runBattle()
 	bool firstRound = true;//FIXME: why first round is -1?
 
 	//main loop
-	while (!battleResult.get()) //till the end of the battle ;]
+	while ((lobby->state != EServerState::SHUTDOWN) && !battleResult.get()) //till the end of the battle ;]
 	{
 		BattleNextRound bnr;
 		bnr.round = gs->curB->round + 1;
@@ -6482,7 +6498,7 @@ void CGameHandler::runBattle()
 		};
 
 		const CStack * next = nullptr;
-		while((next = getNextStack()))
+		while((lobby->state != EServerState::SHUTDOWN) && (next = getNextStack()))
 		{
 			BattleUnitsChanged removeGhosts;
 			for(auto stack : curB.stacks)
@@ -6661,7 +6677,7 @@ void CGameHandler::runBattle()
 
 						boost::unique_lock<boost::mutex> lock(battleMadeAction.mx);
 						battleMadeAction.data = false;
-						while (!actionWasMade())
+						while ((lobby->state != EServerState::SHUTDOWN) && !actionWasMade())
 						{
 							battleMadeAction.cond.wait(lock);
 							if (battleGetStackByID(nextId, false) != next)
@@ -6717,7 +6733,8 @@ void CGameHandler::runBattle()
 		firstRound = false;
 	}
 
-	endBattle(gs->curB->tile, gs->curB->battleGetFightingHero(0), gs->curB->battleGetFightingHero(1));
+	if (lobby->state != EServerState::SHUTDOWN)
+		endBattle(gs->curB->tile, gs->curB->battleGetFightingHero(0), gs->curB->battleGetFightingHero(1));
 }
 
 bool CGameHandler::makeAutomaticAction(const CStack *stack, BattleAction &ba)
@@ -6899,7 +6916,7 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
 			if (!hero->hasStackAtSlot(SlotID(i)))
 				insertNewStack(StackLocation(hero, SlotID(i)), creature, creatures[cheat].second);
 	}
-	else if (boost::starts_with(cheat, "vcmiarmy"))
+	else if (boost::starts_with(cheat, "vcmiarmy") || boost::starts_with(cheat, "vcminissi"))
 	{
 		cheated = true;
 		if (!hero) return;
@@ -6953,7 +6970,7 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
 		///selected hero gains a new level
 		changePrimSkill(hero, PrimarySkill::EXPERIENCE, VLC->heroh->reqExp(hero->level + 1) - VLC->heroh->reqExp(hero->level));
 	}
-	else if (boost::starts_with(cheat, "vcmiexp"))
+	else if (boost::starts_with(cheat, "vcmiexp") || boost::starts_with(cheat, "vcmiolorin"))
 	{
 		cheated = true;
 		if (!hero) return;

+ 2 - 1
server/CGameHandler.h

@@ -96,6 +96,7 @@ class CGameHandler : public IGameCallback, public CBattleInfoCallback, public En
 {
 	CVCMIServer * lobby;
 	std::shared_ptr<CApplier<CBaseForGHApply>> applier;
+	std::unique_ptr<boost::thread> battleThread;
 public:
 	using FireShieldInfo = std::vector<std::pair<const CStack *, int64_t>>;
 	//use enums as parameters, because doMove(sth, true, false, true) is not readable
@@ -236,7 +237,7 @@ public:
 	void removeObstacle(const CObstacleInstance &obstacle);
 	bool queryReply( QueryID qid, const JsonNode & answer, PlayerColor player );
 	bool hireHero( const CGObjectInstance *obj, ui8 hid, PlayerColor player );
-	bool buildBoat( ObjectInstanceID objid );
+	bool buildBoat( ObjectInstanceID objid, PlayerColor player );
 	bool setFormation( ObjectInstanceID hid, ui8 formation );
 	bool tradeResources(const IMarket *market, ui32 val, PlayerColor player, ui32 id1, ui32 id2);
 	bool sacrificeCreatures(const IMarket * market, const CGHeroInstance * hero, const std::vector<SlotID> & slot, const std::vector<ui32> & count);

+ 3 - 0
server/CQuery.cpp

@@ -380,6 +380,9 @@ bool CGarrisonDialogQuery::blocksPack(const CPack * pack) const
 	if(auto upgrade = dynamic_ptr_cast<UpgradeCreature>(pack))
 		return !vstd::contains(ourIds, upgrade->id);
 
+	if(auto formation = dynamic_ptr_cast<SetFormation>(pack))
+		return !vstd::contains(ourIds, formation->hid);
+
 	return CDialogQuery::blocksPack(pack);
 }
 

+ 0 - 36
server/CVCMIServer.cpp

@@ -59,10 +59,6 @@
 
 #include "../lib/CGameState.h"
 
-#if defined(__GNUC__) && !defined(__UCLIBC__) && !defined(__MINGW32__) && !defined(VCMI_MOBILE)
-#include <execinfo.h>
-#endif
-
 template<typename T> class CApplyOnServer;
 
 class CBaseForServerApply
@@ -999,33 +995,6 @@ ui8 CVCMIServer::getIdOfFirstUnallocatedPlayer() const
 	return 0;
 }
 
-#if defined(__GNUC__) && !defined(__UCLIBC__) && !defined(__MINGW32__) && !defined(VCMI_MOBILE)
-void handleLinuxSignal(int sig)
-{
-	const int STACKTRACE_SIZE = 100;
-	void * buffer[STACKTRACE_SIZE];
-	int ptrCount = backtrace(buffer, STACKTRACE_SIZE);
-	char * * strings;
-
-	logGlobal->error("Error: signal %d :", sig);
-	strings = backtrace_symbols(buffer, ptrCount);
-	if(strings == nullptr)
-	{
-		logGlobal->error("There are no symbols.");
-	}
-	else
-	{
-		for(int i = 0; i < ptrCount; ++i)
-		{
-			logGlobal->error(strings[i]);
-		}
-		free(strings);
-	}
-
-	_exit(EXIT_FAILURE);
-}
-#endif
-
 static void handleCommandOptions(int argc, const char * argv[], boost::program_options::variables_map & options)
 {
 	namespace po = boost::program_options;
@@ -1101,11 +1070,6 @@ int main(int argc, const char * argv[])
 	// Correct working dir executable folder (not bundle folder) so we can use executable relative paths
 	boost::filesystem::current_path(boost::filesystem::system_complete(argv[0]).parent_path());
 #endif
-	// Installs a sig sev segmentation violation handler
-	// to log stacktrace
-#if defined(__GNUC__) && !defined(__UCLIBC__) && !defined(__MINGW32__) && !defined(VCMI_MOBILE)
-	signal(SIGSEGV, handleLinuxSignal);
-#endif
 
 #ifndef VCMI_IOS
 	console = new CConsoleHandler();

+ 1 - 1
server/NetPacksServer.cpp

@@ -253,7 +253,7 @@ void ApplyGhNetPackVisitor::visitBuildBoat(BuildBoat & pack)
 	if(gh.getPlayerRelations(gh.getOwner(pack.objid), gh.getPlayerAt(pack.c)) == PlayerRelations::ENEMIES)
 		gh.throwAndComplain(&pack, "Can't build boat at enemy shipyard");
 
-	result = gh.buildBoat(pack.objid);
+	result = gh.buildBoat(pack.objid, gh.getPlayerAt(pack.c));
 }
 
 void ApplyGhNetPackVisitor::visitQueryReply(QueryReply & pack)

+ 4 - 1
vcmibuilder

@@ -156,7 +156,10 @@ fi
 
 if [[ -z "$dest_dir" ]]
 then
-	if [[ -z "$XDG_DATA_HOME" ]]
+	if [[ "$(uname)" == Darwin ]]
+	then
+		dest_dir="$HOME/Library/Application Support/vcmi"
+	elif [[ -z "$XDG_DATA_HOME" ]]
 	then
 		dest_dir="$HOME/.local/share/vcmi"
 	else

部分文件因文件數量過多而無法顯示