Browse Source

Merge pull request #3360 from vcmi/beta

Merge beta -> master
Ivan Savenko 1 year ago
parent
commit
64d52964b5
86 changed files with 4508 additions and 643 deletions
  1. 11 1
      .github/workflows/github.yml
  2. 20 17
      AI/BattleAI/BattleEvaluator.cpp
  3. 1 0
      CI/conan/base/cross-macro.j2
  4. 16 0
      CI/mingw-32/before_install.sh
  5. 1 1
      CI/mingw/before_install.sh
  6. 4 1
      CMakeLists.txt
  7. 101 9
      Mods/vcmi/config/vcmi/chinese.json
  8. 237 138
      Mods/vcmi/config/vcmi/czech.json
  9. 2 0
      Mods/vcmi/config/vcmi/english.json
  10. 2 0
      Mods/vcmi/config/vcmi/german.json
  11. 2 2
      android/vcmi-app/build.gradle
  12. 0 8
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/NativeMethods.java
  13. 71 0
      android/vcmi-app/src/main/res/values-cs/strings.xml
  14. 16 3
      client/CMT.cpp
  15. 1 1
      client/CVideoHandler.cpp
  16. 1 1
      client/NetPacksLobbyClient.cpp
  17. 2 2
      client/PlayerLocalState.h
  18. 3 0
      client/adventureMap/CInGameConsole.cpp
  19. 2 3
      client/mainmenu/CHighScoreScreen.cpp
  20. 30 6
      client/widgets/CArtifactHolder.cpp
  21. 14 9
      client/widgets/CArtifactHolder.h
  22. 3 3
      client/widgets/CArtifactsOfHeroAltar.cpp
  23. 1 1
      client/widgets/CArtifactsOfHeroAltar.h
  24. 148 40
      client/widgets/CArtifactsOfHeroBackpack.cpp
  25. 24 6
      client/widgets/CArtifactsOfHeroBackpack.h
  26. 26 11
      client/widgets/CArtifactsOfHeroBase.cpp
  27. 7 4
      client/widgets/CArtifactsOfHeroBase.h
  28. 6 5
      client/widgets/CArtifactsOfHeroKingdom.cpp
  29. 1 1
      client/widgets/CArtifactsOfHeroKingdom.h
  30. 4 3
      client/widgets/CArtifactsOfHeroMain.cpp
  31. 1 1
      client/widgets/CArtifactsOfHeroMain.h
  32. 2 2
      client/widgets/CArtifactsOfHeroMarket.cpp
  33. 1 1
      client/widgets/CArtifactsOfHeroMarket.h
  34. 46 12
      client/widgets/CWindowWithArtifacts.cpp
  35. 5 3
      client/widgets/CWindowWithArtifacts.h
  36. 51 14
      client/windows/CHeroBackpackWindow.cpp
  37. 17 2
      client/windows/CHeroBackpackWindow.h
  38. 0 1
      client/windows/CHeroWindow.h
  39. 3 3
      client/windows/CMapOverview.cpp
  40. 114 62
      client/windows/CSpellWindow.cpp
  41. 8 0
      client/windows/CSpellWindow.h
  42. 1 1
      client/windows/CTradeWindow.cpp
  43. 1 1
      client/windows/CTradeWindow.h
  44. 1 1
      cmake_modules/VersionDefinition.cmake
  45. 1 1
      config/schemas/settings.json
  46. 1 1
      config/spells/timed.json
  47. 6 0
      debian/changelog
  48. 2 1
      docs/modders/Bonus/Bonus_Duration_Types.md
  49. 15 3
      docs/players/Cheat_Codes.md
  50. 1 1
      include/vstd/DateUtils.h
  51. 61 37
      launcher/eu.vcmi.VCMI.metainfo.xml
  52. 7 7
      launcher/mainwindow_moc.cpp
  53. 9 1
      launcher/modManager/cmodlistview_moc.cpp
  54. 1 0
      launcher/modManager/cmodlistview_moc.h
  55. 16 5
      launcher/modManager/cmodmanager.cpp
  56. 1207 0
      launcher/translation/czech.ts
  57. 20 6
      lib/BasicTypes.cpp
  58. 5 3
      lib/CArtifactInstance.cpp
  59. 1 0
      lib/CArtifactInstance.h
  60. 5 0
      lib/CGameInfoCallback.cpp
  61. 1 1
      lib/CGameInfoCallback.h
  62. 24 21
      lib/Languages.h
  63. 9 0
      lib/TextOperations.cpp
  64. 3 0
      lib/TextOperations.h
  65. 5 0
      lib/bonuses/Bonus.h
  66. 1 0
      lib/bonuses/BonusEnum.cpp
  67. 6 2
      lib/bonuses/BonusEnum.h
  68. 38 56
      lib/filesystem/CZipLoader.cpp
  69. 9 8
      lib/filesystem/CZipLoader.h
  70. 19 3
      lib/mapObjectConstructors/AObjectTypeHandler.cpp
  71. 3 2
      lib/mapObjectConstructors/AObjectTypeHandler.h
  72. 0 1
      lib/mapObjectConstructors/CObjectClassesHandler.cpp
  73. 4 1
      lib/mapObjects/CGHeroInstance.cpp
  74. 4 3
      lib/mapping/CMapInfo.cpp
  75. 6 1
      lib/modding/CModInfo.cpp
  76. 3 0
      lib/networkPacks/NetPacksLib.cpp
  77. 4 2
      lib/rewardable/Configuration.cpp
  78. 1 1
      lib/rewardable/Configuration.h
  79. 4 4
      lib/rewardable/Info.cpp
  80. 6 0
      lib/serializer/Connection.cpp
  81. 3 23
      lib/vstd/DateUtils.cpp
  82. 1831 0
      mapeditor/translation/czech.ts
  83. 1 5
      server/CGameHandler.cpp
  84. 1 2
      server/CGameHandler.h
  85. 152 61
      server/processors/PlayerMessageProcessor.cpp
  86. 4 0
      server/processors/PlayerMessageProcessor.h

+ 11 - 1
.github/workflows/github.yml

@@ -116,7 +116,7 @@ jobs:
             pack_type: RelWithDebInfo
             extension: exe
             preset: windows-msvc-release-ccache
-          - platform: mingw-ubuntu
+          - platform: mingw
             os: ubuntu-22.04
             test: 0
             pack: 1
@@ -126,6 +126,16 @@ jobs:
             cmake_args: -G Ninja
             preset: windows-mingw-conan-linux
             conan_profile: mingw64-linux.jinja
+          - platform: mingw-32
+            os: ubuntu-22.04
+            test: 0
+            pack: 1
+            pack_type: Release
+            extension: exe
+            cpack_args: -D CPACK_NSIS_EXECUTABLE=`which makensis`
+            cmake_args: -G Ninja
+            preset: windows-mingw-conan-linux
+            conan_profile: mingw32-linux.jinja
           - platform: android-32
             os: ubuntu-22.04
             extension: apk

+ 20 - 17
AI/BattleAI/BattleEvaluator.cpp

@@ -427,33 +427,36 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
 
 				state->nextTurn(unit->unitId());
 
-				PotentialTargets pt(unit, damageCache, state);
+				PotentialTargets potentialTargets(unit, damageCache, state);
 
-				if(!pt.possibleAttacks.empty())
+				if(!potentialTargets.possibleAttacks.empty())
 				{
-					AttackPossibility ap = pt.bestAction();
+					AttackPossibility attackPossibility = potentialTargets.bestAction();
 
-					auto swb = state->getForUpdate(unit->unitId());
-					*swb = *ap.attackerState;
+					auto stackWithBonuses = state->getForUpdate(unit->unitId());
+					*stackWithBonuses = *attackPossibility.attackerState;
 
-					if(ap.defenderDamageReduce > 0)
-						swb->removeUnitBonus(Bonus::UntilAttack);
-					if(ap.attackerDamageReduce > 0)
-						swb->removeUnitBonus(Bonus::UntilBeingAttacked);
+					if(attackPossibility.defenderDamageReduce > 0)
+					{
+						stackWithBonuses->removeUnitBonus(Bonus::UntilAttack);
+						stackWithBonuses->removeUnitBonus(Bonus::UntilOwnAttack);
+					}
+					if(attackPossibility.attackerDamageReduce > 0)
+						stackWithBonuses->removeUnitBonus(Bonus::UntilBeingAttacked);
 
-					for(auto affected : ap.affectedUnits)
+					for(auto affected : attackPossibility.affectedUnits)
 					{
-						swb = state->getForUpdate(affected->unitId());
-						*swb = *affected;
+						stackWithBonuses = state->getForUpdate(affected->unitId());
+						*stackWithBonuses = *affected;
 
-						if(ap.defenderDamageReduce > 0)
-							swb->removeUnitBonus(Bonus::UntilBeingAttacked);
-						if(ap.attackerDamageReduce > 0 && ap.attack.defender->unitId() == affected->unitId())
-							swb->removeUnitBonus(Bonus::UntilAttack);
+						if(attackPossibility.defenderDamageReduce > 0)
+							stackWithBonuses->removeUnitBonus(Bonus::UntilBeingAttacked);
+						if(attackPossibility.attackerDamageReduce > 0 && attackPossibility.attack.defender->unitId() == affected->unitId())
+							stackWithBonuses->removeUnitBonus(Bonus::UntilAttack);
 					}
 				}
 
-				auto bav = pt.bestActionValue();
+				auto bav = potentialTargets.bestActionValue();
 
 				//best action is from effective owner`s point if view, we need to convert to our point if view
 				if(state->battleGetOwner(unit) != playerID)

+ 1 - 0
CI/conan/base/cross-macro.j2

@@ -17,4 +17,5 @@ RC={{ target_host }}-windres
 {% macro generate_conf(target_host) -%}
 tools.build:compiler_executables = {"c": "{{ target_host }}-gcc", "cpp": "{{ target_host }}-g++"}
 tools.build:sysroot = /usr/{{ target_host }}
+tools.build:defines = ["WINVER=0x0601", "_WIN32_WINNT=0x0601"]
 {%- endmacro -%}

+ 16 - 0
CI/mingw-32/before_install.sh

@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+sudo apt-get update
+sudo apt-get install ninja-build mingw-w64 nsis
+sudo update-alternatives --set i686-w64-mingw32-g++ /usr/bin/i686-w64-mingw32-g++-posix
+
+# Workaround for getting new MinGW headers on Ubuntu 22.04.
+# Remove it once MinGW headers version in repository will be 10.0 at least
+curl -O -L http://mirrors.kernel.org/ubuntu/pool/universe/m/mingw-w64/mingw-w64-common_10.0.0-3_all.deb \
+  && sudo dpkg -i mingw-w64-common_10.0.0-3_all.deb;
+curl -O -L http://mirrors.kernel.org/ubuntu/pool/universe/m/mingw-w64/mingw-w64-i686-dev_10.0.0-3_all.deb \
+  && sudo dpkg -i mingw-w64-i686-dev_10.0.0-3_all.deb;
+
+mkdir ~/.conan ; cd ~/.conan
+curl -L "https://github.com/vcmi/vcmi-deps-windows-conan/releases/download/1.1/vcmi-deps-windows-conan-w32.tgz" \
+	| tar -xzf -

+ 1 - 1
CI/mingw-ubuntu/before_install.sh → CI/mingw/before_install.sh

@@ -12,5 +12,5 @@ curl -O -L http://mirrors.kernel.org/ubuntu/pool/universe/m/mingw-w64/mingw-w64-
   && sudo dpkg -i mingw-w64-x86-64-dev_10.0.0-3_all.deb;
 
 mkdir ~/.conan ; cd ~/.conan
-curl -L "https://github.com/vcmi/vcmi-deps-windows-conan/releases/download/1.0/vcmi-deps-windows-conan-w64.tgz" \
+curl -L "https://github.com/vcmi/vcmi-deps-windows-conan/releases/download/1.1/vcmi-deps-windows-conan-w64.tgz" \
 	| tar -xzf -

+ 4 - 1
CMakeLists.txt

@@ -255,7 +255,10 @@ endif()
 
 if(MINGW OR MSVC)
 	# Windows Vista or newer for FuzzyLite 6 to compile
-	add_definitions(-D_WIN32_WINNT=0x0600)
+	# Except for conan which already has this definition in its preset
+	if(NOT USING_CONAN)
+		add_definitions(-D_WIN32_WINNT=0x0600)
+	endif()
 
 	#delete lib prefix for dlls (libvcmi -> vcmi)
 	set(CMAKE_SHARED_LIBRARY_PREFIX "")

+ 101 - 9
Mods/vcmi/config/vcmi/chinese.json

@@ -36,14 +36,27 @@
 	"vcmi.heroOverview.spells" : "魔法",
 
 	"vcmi.radialWheel.mergeSameUnit" : "合并相同生物",
-	"vcmi.radialWheel.showUnitInformation" : "显示生物信息",
+	"vcmi.radialWheel.fillSingleUnit" : "单个生物填充空格",
 	"vcmi.radialWheel.splitSingleUnit" : "分割单个生物",
 	"vcmi.radialWheel.splitUnitEqually" : "平均分配生物",
 	"vcmi.radialWheel.moveUnit" : "将生物移动到部队",
 	"vcmi.radialWheel.splitUnit" : "分割生物到其他空位",
 
+	"vcmi.radialWheel.heroGetArmy" : "移动生物",
+	"vcmi.radialWheel.heroSwapArmy" : "交换生物",
+	"vcmi.radialWheel.heroExchange" : "开启英雄交换",
+	"vcmi.radialWheel.heroGetArtifacts" : "移动宝物",
+	"vcmi.radialWheel.heroSwapArtifacts" : "交换宝物",
+	"vcmi.radialWheel.heroDismiss" : "解雇英雄",
+
+	"vcmi.radialWheel.moveTop" : "移到顶端",
+	"vcmi.radialWheel.moveUp" : "上移",
+	"vcmi.radialWheel.moveDown" : "下移",
+	"vcmi.radialWheel.moveBottom" : "移到底端",
+	
 	"vcmi.mainMenu.serverConnecting" : "连接中...",
 	"vcmi.mainMenu.serverAddressEnter" : "使用地址:",
+	"vcmi.mainMenu.serverConnectionFailed" : "连接失败",
 	"vcmi.mainMenu.serverClosing" : "关闭中...",
 	"vcmi.mainMenu.hostTCP" : "创建TCP/IP游戏",
 	"vcmi.mainMenu.joinTCP" : "加入TCP/IP游戏",
@@ -51,11 +64,18 @@
 
 	"vcmi.lobby.filename" : "文件名",
 	"vcmi.lobby.creationDate" : "创建时间",
+	"vcmi.lobby.scenarioName" : "场景名称",
+	"vcmi.lobby.mapPreview" : "地图预览",
+	"vcmi.lobby.noPreview" : "无地上部分",
+	"vcmi.lobby.noUnderground" : "无地下部分",
 
 	"vcmi.server.errors.existingProcess"     : "一个VCMI进程已经在运行,启动新进程前请结束它。",
 	"vcmi.server.errors.modsToEnable"    : "{需要启用的mod列表}",
 	"vcmi.server.errors.modsToDisable"   : "{需要禁用的mod列表}",
 	"vcmi.server.confirmReconnect"           : "您想要重连上一个会话么?",
+	"vcmi.server.errors.modNoDependency" : "读取mod包 {'%s'}失败!\n 需要的mod {'%s'} 没有安装或无效!\n",
+	"vcmi.server.errors.modConflict" : "读取的mod包 {'%s'}无法运行!\n 与另一个mod {'%s'}冲突!\n",
+	"vcmi.server.errors.unknownEntity" : "加载保存失败! 在保存的游戏中发现未知实体'%s'! 保存可能与当前安装的mod版本不兼容!",
 
 	"vcmi.settingsMainWindow.generalTab.hover"   : "常规",
 	"vcmi.settingsMainWindow.generalTab.help"    : "切换到“常规”选项卡 - 设置游戏客户端呈现",
@@ -92,6 +112,10 @@
 	"vcmi.systemOptions.hapticFeedbackButton.help"   : "{触觉反馈}\n\n切换触摸输入的触觉反馈。",
 	"vcmi.systemOptions.enableUiEnhancementsButton.hover"  : "界面增强",
 	"vcmi.systemOptions.enableUiEnhancementsButton.help"   : "{界面增强}\n\n显示所有界面增强内容,如大背包和魔法书等。",
+	"vcmi.systemOptions.enableLargeSpellbookButton.hover"  : "增大魔法书界面",
+	"vcmi.systemOptions.enableLargeSpellbookButton.help"   : "{增大魔法书界面}\n\n可以在魔法书单页中显示更多的魔法,从而获得更好的视觉效果。",
+	"vcmi.systemOptions.audioMuteFocus.hover"  : "切换窗口时静音",
+	"vcmi.systemOptions.audioMuteFocus.help"   : "{切换窗口时静音}\n\n快速切换窗口时将静音,在工作时,切换游戏窗口不会有声音。",
 
 	"vcmi.adventureOptions.infoBarPick.hover" : "在信息面板显示消息",
 	"vcmi.adventureOptions.infoBarPick.help" : "{在信息面板显示消息}\n\n来自访问地图物件的信息将显示在信息面板,而不是弹出窗口。",
@@ -107,6 +131,8 @@
 	"vcmi.adventureOptions.infoBarCreatureManagement.help" : "{信息面板生物管理}\n\n允许在信息面板中重新排列生物,而不是在默认组件之间循环。",
 	"vcmi.adventureOptions.leftButtonDrag.hover" : "左键拖动地图",
 	"vcmi.adventureOptions.leftButtonDrag.help" : "{左键拖动地图}\n\n启用后,按住左键移动鼠标将拖动冒险地图视图。",
+	"vcmi.adventureOptions.smoothDragging.hover" : "平滑地图拖动",
+	"vcmi.adventureOptions.smoothDragging.help" : "{平滑地图拖动}\n\n启用后,地图拖动会产生柔和的羽化效果。",
 	"vcmi.adventureOptions.mapScrollSpeed1.hover": "",
 	"vcmi.adventureOptions.mapScrollSpeed5.hover": "",
 	"vcmi.adventureOptions.mapScrollSpeed6.hover": "",
@@ -139,6 +165,9 @@
 	"vcmi.battleOptions.enableAutocombatSpells.help": "{魔法}\n\n快速战斗时不会使用魔法。",
 	"vcmi.battleOptions.skipBattleIntroMusic.hover": "跳过战斗开始音乐",
 	"vcmi.battleOptions.skipBattleIntroMusic.help": "{跳过战斗开始音乐}\n\n战斗开始音乐播放期间,你也能够进行操作。",
+	
+	"vcmi.adventureMap.revisitObject.hover" : "重新访问",
+	"vcmi.adventureMap.revisitObject.help" : "{重新访问}\n\n让当前英雄重新访问地图建筑或城镇。",
 
 	"vcmi.battleWindow.pressKeyToSkipIntro" : "按下任意键立即开始战斗",
 	"vcmi.battleWindow.damageEstimation.melee" : "近战攻击 %CREATURE (%DAMAGE).",
@@ -153,6 +182,15 @@
 	"vcmi.battleWindow.damageEstimation.kills.1" : "%d 将被消灭",
 
 	"vcmi.battleResultsWindow.applyResultsLabel" : "接受战斗结果",
+	
+	"vcmi.tutorialWindow.title" : "触摸屏介绍",
+	"vcmi.tutorialWindow.decription.RightClick" : "触摸并按住要右键单击的元素。 触摸可用区域以关闭。",
+	"vcmi.tutorialWindow.decription.MapPanning" : "用一根手指触摸并拖动来移动地图。",
+	"vcmi.tutorialWindow.decription.MapZooming" : "用两根手指捏合可更改地图缩放比例。",
+	"vcmi.tutorialWindow.decription.RadialWheel" : "滑动可打开径向轮以执行各种操作,例如生物/英雄管理和城镇排序。",
+	"vcmi.tutorialWindow.decription.BattleDirection" : "要从特定方向攻击,请向要进行攻击的方向滑动。",
+	"vcmi.tutorialWindow.decription.BattleDirectionAbort" : "如果手指距离足够远,可以取消攻击方向手势。",
+	"vcmi.tutorialWindow.decription.AbortSpell" : "触摸并按住可取消魔法。",
 
 	"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover" : "显示可招募生物",
 	"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{显示可招募生物}\n\n在城镇摘要(城镇屏幕的左下角)中显示可招募的生物数量,而不是增长。",
@@ -202,14 +240,68 @@
 	"vcmi.randomMapTab.widgets.teamAlignmentsLabel"  : "同盟关系",
 	"vcmi.randomMapTab.widgets.roadTypesLabel"       : "道路类型",
 
-	"vcmi.optionsTab.chessFieldBase.hover" : "额外计时器",
-	"vcmi.optionsTab.chessFieldTurn.hover" : "转动计时器",
+	"vcmi.optionsTab.turnOptions.hover" : "回合选项",
+	"vcmi.optionsTab.turnOptions.help" : "选择回合计时器并同步回合选项",
+	"vcmi.optionsTab.selectPreset" : "预设",
+
+	"vcmi.optionsTab.chessFieldBase.hover" : "基本计时器",
+	"vcmi.optionsTab.chessFieldTurn.hover" : "回合计时器",
 	"vcmi.optionsTab.chessFieldBattle.hover" : "战斗计时器",
-	"vcmi.optionsTab.chessFieldUnit.hover" : "堆栈计时器",
-	"vcmi.optionsTab.chessFieldBase.help" : "当{转动计时器}达到零时开始倒计时。 它仅在游戏开始时设置一次。 当计时器达到零时,玩家的回合结束。",
-	"vcmi.optionsTab.chessFieldTurn.help" : "当玩家在冒险地图上开始回合时开始倒计时。 它在每回合开始时重置为其初始值。 任何未使用的回合时间将被添加到{额外计时器}(如果正在使用)中。",
-	"vcmi.optionsTab.chessFieldBattle.help" : "战斗期间当 {堆栈计时器} 达到0时进行倒计时。 每次战斗开始时都会重置为初始值。 如果计时器达到零,当前活动的堆栈将进行防御。",
-	"vcmi.optionsTab.chessFieldUnit.help" : "当玩家在战斗中为当前堆栈选择一个动作时开始倒计时。 堆栈操作完成后,它会重置为其初始值。",
+	"vcmi.optionsTab.chessFieldUnit.hover" : "单位计时器",
+	"vcmi.optionsTab.chessFieldBase.help" : "当 {回合计时器} 达到 0 时使用。在游戏开始时设置一次。达到 0 时,结束当前回合。任何正在进行的战斗都会以失败告终。",
+	"vcmi.optionsTab.chessFieldTurnAccumulate.help" : "在战斗外或{战斗计时器}耗尽时使用。 每回合重置。 剩余部分在回合结束时添加至 {基本计时器}。",
+	"vcmi.optionsTab.chessFieldTurnDiscard.help" : "在战斗外或{战斗计时器}耗尽时使用。 每回合重置。 任何未花费的时间都会丢失。",
+	"vcmi.optionsTab.chessFieldBattle.help" : "在与 AI 的战斗中使用,或者在 {单位计时器} 耗尽时用于 pvp 战斗。在每次战斗开始时重置。",
+	"vcmi.optionsTab.chessFieldUnitAccumulate.help" : "在 PVP 战斗中选择单位动作时使用。 在单位回合结束时将剩余物添加到{战斗计时器}。",
+	"vcmi.optionsTab.chessFieldUnitDiscard.help" : "在 PVP 战斗中选择单位动作时使用。 在每个单位回合开始时重置。 任何未花费的时间都会丢失。",
+	
+	"vcmi.optionsTab.accumulate" : "累积",
+
+	"vcmi.optionsTab.simturnsTitle" : "同时进行回合",
+	"vcmi.optionsTab.simturnsMin.hover" : "最少回合",
+	"vcmi.optionsTab.simturnsMax.hover" : "最多回合",
+	"vcmi.optionsTab.simturnsAI.hover" : "(测试中) AI回合同时行动",
+	"vcmi.optionsTab.simturnsMin.help" : "同时游戏进行的最少指定天数。在此期间玩家之间的联系将被阻止",
+	"vcmi.optionsTab.simturnsMax.help" : "同时游戏指定的最多天数或直到与其他玩家联系",
+	"vcmi.optionsTab.simturnsAI.help" : "{AI回合同时行动}\n实验选项。启用同时回合后,允许 AI 玩家与人类玩家同时行动。",
+	
+	"vcmi.optionsTab.turnTime.select"     : "回合计时器预设",
+	"vcmi.optionsTab.turnTime.unlimited"  : "无限时",
+	"vcmi.optionsTab.turnTime.classic.1"  : "经典计时器: 1 分钟",
+	"vcmi.optionsTab.turnTime.classic.2"  : "经典计时器: 2 分钟",
+	"vcmi.optionsTab.turnTime.classic.5"  : "经典计时器: 5 分钟",
+	"vcmi.optionsTab.turnTime.classic.10" : "经典计时器: 10 分钟",
+	"vcmi.optionsTab.turnTime.classic.20" : "经典计时器: 20 分钟",
+	"vcmi.optionsTab.turnTime.classic.30" : "经典计时器: 30 分钟",
+	"vcmi.optionsTab.turnTime.chess.20"   : "国际象棋计时器: 20:00 + 10:00 + 02:00 + 00:00",
+	"vcmi.optionsTab.turnTime.chess.16"   : "国际象棋计时器: 16:00 + 08:00 + 01:30 + 00:00",
+	"vcmi.optionsTab.turnTime.chess.8"    : "国际象棋计时器: 08:00 + 04:00 + 01:00 + 00:00",
+	"vcmi.optionsTab.turnTime.chess.4"    : "国际象棋计时器: 04:00 + 02:00 + 00:30 + 00:00",
+	"vcmi.optionsTab.turnTime.chess.2"    : "国际象棋计时器: 02:00 + 01:00 + 00:15 + 00:00",
+	"vcmi.optionsTab.turnTime.chess.1"    : "国际象棋计时器: 01:00 + 01:00 + 00:00 + 00:00",
+	
+	"vcmi.optionsTab.simturns.select"         : "同时进行回合预设",
+	"vcmi.optionsTab.simturns.none"           : "不同时进行回合",
+	"vcmi.optionsTab.simturns.tillContactMax" : "同时进行: 可进行联系",
+	"vcmi.optionsTab.simturns.tillContact1"   : "同时进行: 1 周, 联系时中断",
+	"vcmi.optionsTab.simturns.tillContact2"   : "同时进行: 2 周, 联系时中断",
+	"vcmi.optionsTab.simturns.tillContact4"   : "同时进行: 1 月, 联系时中断",
+	"vcmi.optionsTab.simturns.blocked1"       : "同时进行: 1 周, 屏蔽联系",
+	"vcmi.optionsTab.simturns.blocked2"       : "同时进行: 2 周, 屏蔽联系",
+	"vcmi.optionsTab.simturns.blocked4"       : "同时进行: 1 月, 屏蔽联系",
+	
+	// Translation note: translate strings below using form that is correct for "0 days", "1 day" and "2 days" in your language
+	// Using this information, VCMI will automatically select correct plural form for every possible amount
+	"vcmi.optionsTab.simturns.days.0" : " %d 天",
+	"vcmi.optionsTab.simturns.days.1" : " %d 天",
+	"vcmi.optionsTab.simturns.days.2" : " %d 天",
+	"vcmi.optionsTab.simturns.weeks.0" : " %d 周",
+	"vcmi.optionsTab.simturns.weeks.1" : " %d 周",
+	"vcmi.optionsTab.simturns.weeks.2" : " %d 周",
+	"vcmi.optionsTab.simturns.months.0" : " %d 月",
+	"vcmi.optionsTab.simturns.months.1" : " %d 月",
+	"vcmi.optionsTab.simturns.months.2" : " %d 月",
+
 
 	// Custom victory conditions for H3 campaigns and HotA maps
 	"vcmi.map.victoryCondition.daysPassed.toOthers" : "敌人依然存活至今,你失败了!",
@@ -275,7 +367,7 @@
 	"core.bonus.ENEMY_DEFENCE_REDUCTION.name": "忽略防御 (${val}%)",
 	"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "当攻击时,目标生物${val}%的防御力将被无视。",
 	"core.bonus.FIRE_IMMUNITY.name": "火系免疫",
-	"core.bonus.FIRE_IMMUNITY.description": "免疫所有火系魔法",
+	"core.bonus.FIRE_IMMUNITY.description": "免疫所有火系魔法",
 	"core.bonus.FIRE_SHIELD.name": "烈火神盾 (${val}%)",
 	"core.bonus.FIRE_SHIELD.description": "反弹部分受到的近战伤害",
 	"core.bonus.FIRST_STRIKE.name": "抢先反击",

+ 237 - 138
Mods/vcmi/config/vcmi/czech.json

@@ -29,16 +29,34 @@
 	"vcmi.capitalColors.5" : "Fialový",
 	"vcmi.capitalColors.6" : "Tyrkysový",
 	"vcmi.capitalColors.7" : "Růžový",
+
+	"vcmi.heroOverview.startingArmy" : "Počáteční jednotky",
+	"vcmi.heroOverview.warMachine" : "Bojové stroje",
+	"vcmi.heroOverview.secondarySkills" : "Druhotné schopnosti",
+	"vcmi.heroOverview.spells" : "Kouzla",
 	
 	"vcmi.radialWheel.mergeSameUnit" : "Sloučit stejné jednotky",
 	"vcmi.radialWheel.fillSingleUnit" : "Vyplnit jednou jednotkou",
 	"vcmi.radialWheel.splitSingleUnit" : "Rozdělit jedinou jednotku",
-	"vcmi.radialWheel.splitUnitEqually" : "Rovnoměrně rozdělit jednotky",
+	"vcmi.radialWheel.splitUnitEqually" : "Rozdělit jednotky rovnoměrně",
 	"vcmi.radialWheel.moveUnit" : "Přesunout jednotky do jiného oddílu",
 	"vcmi.radialWheel.splitUnit" : "Rozdělit jednotku do jiné pozice",
 
+	"vcmi.radialWheel.heroGetArmy" : "Získat armádu jiného hrdiny",
+	"vcmi.radialWheel.heroSwapArmy" : "Vyměnit armádu s jiným hrdinou",
+	"vcmi.radialWheel.heroExchange" : "Otevřít výměnu hrdinů",
+	"vcmi.radialWheel.heroGetArtifacts" : "Získat artefakty od jiního hrdiny",
+	"vcmi.radialWheel.heroSwapArtifacts" : "Vyměnit artefakty s jiným hrdinou",
+	"vcmi.radialWheel.heroDismiss" : "Propustit hrdinu",
+
+	"vcmi.radialWheel.moveTop" : "Move to top",
+	"vcmi.radialWheel.moveUp" : "Move up",
+	"vcmi.radialWheel.moveDown" : "Move down",
+	"vcmi.radialWheel.moveBottom" : "Move to bottom",
+
 	"vcmi.mainMenu.serverConnecting" : "Připojování...",
 	"vcmi.mainMenu.serverAddressEnter" : "Zadejte adresu:",
+	"vcmi.mainMenu.serverConnectionFailed" : "Připojování selhalo",
 	"vcmi.mainMenu.serverClosing" : "Zavírání...",
 	"vcmi.mainMenu.hostTCP" : "Pořádat hru TCP/IP",
 	"vcmi.mainMenu.joinTCP" : "Připojit se do hry TCP/IP",
@@ -46,10 +64,18 @@
 	
 	"vcmi.lobby.filepath" : "Název souboru",
 	"vcmi.lobby.creationDate" : "Datum vytvoření",
+	"vcmi.lobby.scenarioName" : "Název scénáře",
+	"vcmi.lobby.mapPreview" : "Náhled mapy",
+	"vcmi.lobby.noPreview" : "bez náhledu",
+	"vcmi.lobby.noUnderground" : "bez podzemí",
 
 	"vcmi.server.errors.existingProcess" : "Již běží jiný server VCMI. Prosím, ukončete ho před startem nové hry.",
 	"vcmi.server.errors.modsToEnable"    : "{Následující modifikace jsou nutné pro načtení hry}",
+	"vcmi.server.errors.modsToDisable"   : "{Následující modifikace musí být zakázány}",
 	"vcmi.server.confirmReconnect"       : "Chcete se připojit k poslední relaci?",
+	"vcmi.server.errors.modNoDependency" : "Nelze načíst modifikaci {'%s'}!\n Závisí na modifikaci {'%s'}, která není aktivní!\n",
+	"vcmi.server.errors.modConflict" : "Nelze načíst modifikaci {'%s'}!\n Je v kolizi s aktivní modifikací {'%s'}!\n",
+	"vcmi.server.errors.unknownEntity" : "Nelze načíst uloženou pozici! Neznámá entita '%s' nalezena v uložené pozici! Uložná pozice nemusí být kompatibilní s aktuálními verzemi modifikací!",
 
 	"vcmi.settingsMainWindow.generalTab.hover" : "Obecné",
 	"vcmi.settingsMainWindow.generalTab.help"     : "Přepne na kartu obecných nastavení, která obsahuje nastavení související s obecným chováním klienta hry",
@@ -84,6 +110,10 @@
 	"vcmi.systemOptions.framerateButton.help"   : "{Zobrazit FPS}\n\nPřepne viditelnost počitadla snímků za sekundu v rohu obrazovky hry",
 	"vcmi.systemOptions.hapticFeedbackButton.hover"  : "Vibrace",
 	"vcmi.systemOptions.hapticFeedbackButton.help"   : "{Vibrace}\n\nPřepnout stav vibrací při dotykovém ovládání",
+	"vcmi.systemOptions.enableUiEnhancementsButton.hover"  : "Vylepšení rozhraní",
+	"vcmi.systemOptions.enableUiEnhancementsButton.help"   : "{Vylepšení rozhraní}\n\nZapne různá vylepšení rozhraní, jako je tlačítko batohu atd. Zakažte pro zážitek klasické hry.",
+	"vcmi.systemOptions.enableLargeSpellbookButton.hover"  : "Velká kniha kouzel",
+	"vcmi.systemOptions.enableLargeSpellbookButton.help"   : "{Velká kniha kouzel}\n\nPovolí větší knihu kouzel, do které se jich více vleze na jednu stranu. Animace změny stránek s tímto nastavením nefunguje.",
 
 	"vcmi.adventureOptions.infoBarPick.hover" : "Zobrazit zprávy v panelu informací",
 	"vcmi.adventureOptions.infoBarPick.help" : "{Zobrazit zprávy v panelu informací}\n\nKdyž bude možné, herní zprávy z návštěv míst na mapě budou zobrazeny v panelu informací místo ve zvláštním okně.",
@@ -97,8 +127,8 @@
 	"vcmi.adventureOptions.borderScroll.help" : "{Posouvání okraji}\n\nPosouvat mapu světa, když je kurzor na okraji obrazovky. Může být zakázáno držením klávesy CTRL.",
 	"vcmi.adventureOptions.infoBarCreatureManagement.hover" : "Info Panel Creature Management", //TODO
 	"vcmi.adventureOptions.infoBarCreatureManagement.help" : "{Info Panel Creature Management}\n\nAllows rearranging creatures in info panel instead of cycling between default components",
-	"vcmi.adventureOptions.leftButtonDrag.hover" : "Left Click Drag Map",
-	"vcmi.adventureOptions.leftButtonDrag.help" : "{Left Click Drag Map}\n\nWhen enabled, moving mouse with left button pressed will drag adventure map view",
+	"vcmi.adventureOptions.leftButtonDrag.hover" : "Posouvání mapy levým kliknutím",
+	"vcmi.adventureOptions.leftButtonDrag.help" : "{Posouvání mapy levým kliknutím}\n\nPosouvání mapy tažením myši se stisknutým levým tlačítkem",
 	"vcmi.adventureOptions.mapScrollSpeed1.hover": "",
 	"vcmi.adventureOptions.mapScrollSpeed5.hover": "",
 	"vcmi.adventureOptions.mapScrollSpeed6.hover": "",
@@ -129,6 +159,8 @@
 	"vcmi.battleOptions.showStickyHeroInfoWindows.help": "{Zobrazit okno statistik hrdinů}\n\nTrvale zapne okno statistiky hrdinů, které ukazuje hlavní schopnosti a magickou energii.",
 	"vcmi.battleOptions.skipBattleIntroMusic.hover": "Přeskočit úvodní hudbu",
 	"vcmi.battleOptions.skipBattleIntroMusic.help": "{Přeskočit úvodní hudbu}\n\nPovolí akce při úvodní hudbě přehrávané při začátku každé bitvy.",
+	"vcmi.adventureMap.revisitObject.hover" : "Znovu navštívit místo",
+	"vcmi.adventureMap.revisitObject.help" : "{Znovu navštívit místo}\n\nPokud se hrdina nachází na nějakém místě mapy, může jej znovu navštívit.",
 
 	"vcmi.battleWindow.pressKeyToSkipIntro" : "Stiskněte jakoukoliv klávesu pro okamžité zahájení bitvy",
 	"vcmi.battleWindow.damageEstimation.melee" : "Zaútočit na %CREATURE (%DAMAGE).",
@@ -144,6 +176,15 @@
 
 	"vcmi.battleResultsWindow.applyResultsLabel" : "Použít výsledek bitvy",
 
+	"vcmi.tutorialWindow.title" : "Úvod ovládání dotykem",
+	"vcmi.tutorialWindow.decription.RightClick" : "Klepněte a držte prvek, na který byste chtěli použít pravé tlačítko myši. Klepněte na volnou oblast pro zavření.",
+	"vcmi.tutorialWindow.decription.MapPanning" : "Klepněte a držte jedním prstem pro posouvání mapy.",
+	"vcmi.tutorialWindow.decription.MapZooming" : "Přibližte dva prsty k sobě pro přiblížení mapy.",
+	"vcmi.tutorialWindow.decription.RadialWheel" : "Přejetí otevře kruhovou nabídku pro různé akce, třeba správa hrdiny/bojovnínků a příkazy měst.",
+	"vcmi.tutorialWindow.decription.BattleDirection" : "Pro útok ze speifického úhlu, přejeďte směrem, ze kterého má být útok vykonán.",
+	"vcmi.tutorialWindow.decription.BattleDirectionAbort" : "Gesto útoku pod úhlem může být zrušeno, pokud, pokud je prst dostatečně daleko.",
+	"vcmi.tutorialWindow.decription.AbortSpell" : "Klepněte a držte pro zrušení kouzla.",
+
 	"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover" : "Zobrazit dostupné jednotky",
 	"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{Zobrazit dostupné jednotky}\n\nZobrazit počet jednotek dostupných ke koupení místo jejich týdenního přírůstku v přehledu města. (levý spodní okraj obrazovky města).",
 	"vcmi.otherOptions.creatureGrowthAsDwellingLabel.hover" : "Zobrazit týdenní přírůstek jednotek",
@@ -152,18 +193,18 @@
 	"vcmi.otherOptions.compactTownCreatureInfo.help": "{Kompaktní informace o jednotkách}\n\nZobrazit menší informace o jednotkách města v jeho přehledu (levý spodní okraj obrazovky města).",
 
 	"vcmi.townHall.missingBase"             : "Základní budova %s musí být postavena jako první",
-	"vcmi.townHall.noCreaturesToRecruit"    : "Žádné jednotky k vycvičení!", //TODO
-	"vcmi.townHall.greetingManaVortex"      : "As you near the %s your body is filled with new energy. You have doubled your normal spell points.",
-	"vcmi.townHall.greetingKnowledge"       : "You study the glyphs on the %s and gain insight into the workings of various magics (+1 Knowledge).",
+	"vcmi.townHall.noCreaturesToRecruit"    : "Žádné jednotky k vycvičení!",
+	"vcmi.townHall.greetingManaVortex"      : "Při pobytu u místa %s se vaše tělo naplnilo novou energií. Máte dvojnásobné množství maximální magické energie.",
+	"vcmi.townHall.greetingKnowledge"       : "Studujete glyfy na the %s a porozumíte fungování různých magií (+1 Znalosti).",
 	"vcmi.townHall.greetingSpellPower"      : "%s vás učí nové cesty zaměření vaší magické síly (+1 Síla kouzel).",
 	"vcmi.townHall.greetingExperience"      : "Návštěva %s vás naučila spoustu nových dovedností (+1000 zkušeností).",
-	"vcmi.townHall.greetingAttack"          : "Čas strávený poblíž místa zvaného %s allows you to learn more effective combat skills (+1 Attack Skill).",
-	"vcmi.townHall.greetingDefence"         : "Spending time in the %s, the experienced warriors therein teach you additional defensive skills (+1 Defense).",
-	"vcmi.townHall.hasNotProduced"          : "%s zatím nic nevyrobil.",
-	"vcmi.townHall.hasProduced"             : "%s vyrobil %d %s tento týden.",
+	"vcmi.townHall.greetingAttack"          : "Čas strávený poblíž místa zvaného %s vám dovolil se naučit efektivnější bojové dovednosti (+1 Útočná síla).",
+	"vcmi.townHall.greetingDefence"         : "Trávíte čas na místě zvaném %s, zkušení bojovníci vás u toho naučili nové metody obrany (+1 Obranná síla).",
+	"vcmi.townHall.hasNotProduced"          : "%s - zatím nic nevyrobeno.",
+	"vcmi.townHall.hasProduced"             : "%s - vyrobeno %d %s tento týden.",
 	"vcmi.townHall.greetingCustomBonus"     : "%s vám dává +%d %s%s",
 	"vcmi.townHall.greetingCustomUntil"     : " do další bitvy.",
-	"vcmi.townHall.greetingInTownMagicWell" : "%s obnovil na maximum vaši magickou energii.",
+	"vcmi.townHall.greetingInTownMagicWell" : "%s - obnoveno na maximum vaši magickou energii.",
 
 	"vcmi.logicalExpressions.anyOf"  : "Něco z následujících:",
 	"vcmi.logicalExpressions.allOf"  : "Všechny následující:",
@@ -174,17 +215,17 @@
 	"vcmi.heroWindow.openBackpack.hover" : "Open artifact backpack window",
 	"vcmi.heroWindow.openBackpack.help"  : "Opens window that allows easier artifact backpack management",
 
-	"vcmi.commanderWindow.artifactMessage" : "Do you want to return this artifact to the hero?",
+	"vcmi.commanderWindow.artifactMessage" : "Chcete navrátit tento artefakt hrdinovi?",
 
-	"vcmi.creatureWindow.showBonuses.hover"    : "Switch to bonuses view",
+	"vcmi.creatureWindow.showBonuses.hover"    : "Přepnout na zobrazení bonusů",
 	"vcmi.creatureWindow.showBonuses.help"     : "Display all active bonuses of the commander",
-	"vcmi.creatureWindow.showSkills.hover"     : "Switch to skills view",
+	"vcmi.creatureWindow.showSkills.hover"     : "Přepnout na zobrazení schoostí",
 	"vcmi.creatureWindow.showSkills.help"      : "Display all learned skills of the commander",
 	"vcmi.creatureWindow.returnArtifact.hover" : "Vrátit artefakt",
 	"vcmi.creatureWindow.returnArtifact.help"  : "Klikněte na toto tlačítko pro navrácení artefaktů do hrdinova batohu",
 
-	"vcmi.questLog.hideComplete.hover" : "Hide complete quests",
-	"vcmi.questLog.hideComplete.help"  : "Hide all completed quests",
+	"vcmi.questLog.hideComplete.hover" : "Skrýt dokončené úkoly",
+	"vcmi.questLog.hideComplete.help"  : "Skrýt všechny dokončené úkoly",
 
 	"vcmi.randomMapTab.widgets.randomTemplate"      : "(Random)",
 	"vcmi.randomMapTab.widgets.templateLabel"        : "Šablona",
@@ -192,9 +233,67 @@
 	"vcmi.randomMapTab.widgets.teamAlignmentsLabel"  : "Team Alignments",
 	"vcmi.randomMapTab.widgets.roadTypesLabel"       : "Druhy cest",
 
-	"vcmi.optionsTab.widgets.labelTimer" : "Časovač",
-	"vcmi.optionsTab.widgets.timerModeSwitch.classic" : "Klasický časovač",
-	"vcmi.optionsTab.widgets.timerModeSwitch.chess" : "Šachový časovač",
+	"vcmi.optionsTab.turnOptions.hover" : "Možnosti tahu",
+	"vcmi.optionsTab.turnOptions.help" : "Vyberte odpočítávadlo tahů a nastavení souběžných tahů",
+	"vcmi.optionsTab.selectPreset" : "Preset",
+
+	"vcmi.optionsTab.chessFieldBase.hover" : "Base timer",
+	"vcmi.optionsTab.chessFieldTurn.hover" : "Turn timer",
+	"vcmi.optionsTab.chessFieldBattle.hover" : "Battle timer",
+	"vcmi.optionsTab.chessFieldUnit.hover" : "Unit timer",
+	"vcmi.optionsTab.chessFieldBase.help" : "Used when {Turn Timer} reaches 0. Set once at game start. On reaching zero, ends current turn. Any ongoing combat with end with a loss.",
+	"vcmi.optionsTab.chessFieldTurnAccumulate.help" : "Used out of combat or when {Battle Timer} runs out. Reset each turn. Leftover added to {Base Timer} at turn's end.",
+	"vcmi.optionsTab.chessFieldTurnDiscard.help" : "Used out of combat or when {Battle Timer} runs out. Reset each turn. Any unspent time is lost",
+	"vcmi.optionsTab.chessFieldBattle.help" : "Used in battles with AI or in pvp combat when {Unit Timer} runs out. Reset at start of each combat.",
+	"vcmi.optionsTab.chessFieldUnitAccumulate.help" : "Used when selecting unit action in pvp combat. Leftover added to {Battle Timer} at end of unit turn.",
+	"vcmi.optionsTab.chessFieldUnitDiscard.help" : "Used when selecting unit action in pvp combat. Reset at start of each unit's turn. Any unspent time is lost",
+
+	"vcmi.optionsTab.accumulate" : "Accumulate",
+
+	"vcmi.optionsTab.simturnsTitle" : "Souběžné tahy",
+	"vcmi.optionsTab.simturnsMin.hover" : "At least for",
+	"vcmi.optionsTab.simturnsMax.hover" : "At most for",
+	"vcmi.optionsTab.simturnsAI.hover" : "(Experimental) Simultaneous AI Turns",
+	"vcmi.optionsTab.simturnsMin.help" : "Play simultaneously for specified number of days. Contacts between players during this period are blocked",
+	"vcmi.optionsTab.simturnsMax.help" : "Play simultaneously for specified number of days or until contact with another player",
+	"vcmi.optionsTab.simturnsAI.help" : "{Simultaneous AI Turns}\nExperimental option. Allows AI players to act at the same time as human player when simultaneous turns are enabled.",
+
+	"vcmi.optionsTab.turnTime.select"     : "Select turn timer preset",
+	"vcmi.optionsTab.turnTime.unlimited"  : "Unlimited turn time",
+	"vcmi.optionsTab.turnTime.classic.1"  : "Classic timer: 1 minute",
+	"vcmi.optionsTab.turnTime.classic.2"  : "Classic timer: 2 minutes",
+	"vcmi.optionsTab.turnTime.classic.5"  : "Classic timer: 5 minutes",
+	"vcmi.optionsTab.turnTime.classic.10" : "Classic timer: 10 minutes",
+	"vcmi.optionsTab.turnTime.classic.20" : "Classic timer: 20 minutes",
+	"vcmi.optionsTab.turnTime.classic.30" : "Classic timer: 30 minutes",
+	"vcmi.optionsTab.turnTime.chess.20"   : "Chess: 20:00 + 10:00 + 02:00 + 00:00",
+	"vcmi.optionsTab.turnTime.chess.16"   : "Chess: 16:00 + 08:00 + 01:30 + 00:00",
+	"vcmi.optionsTab.turnTime.chess.8"    : "Chess: 08:00 + 04:00 + 01:00 + 00:00",
+	"vcmi.optionsTab.turnTime.chess.4"    : "Chess: 04:00 + 02:00 + 00:30 + 00:00",
+	"vcmi.optionsTab.turnTime.chess.2"    : "Chess: 02:00 + 01:00 + 00:15 + 00:00",
+	"vcmi.optionsTab.turnTime.chess.1"    : "Chess: 01:00 + 01:00 + 00:00 + 00:00",
+
+	"vcmi.optionsTab.simturns.select"         : "Select simultaneous turns preset",
+	"vcmi.optionsTab.simturns.none"           : "No simultaneous turns",
+	"vcmi.optionsTab.simturns.tillContactMax" : "Simturns: Until contact",
+	"vcmi.optionsTab.simturns.tillContact1"   : "Simturns: 1 week, break on contact",
+	"vcmi.optionsTab.simturns.tillContact2"   : "Simturns: 2 weeks, break on contact",
+	"vcmi.optionsTab.simturns.tillContact4"   : "Simturns: 1 month, break on contact",
+	"vcmi.optionsTab.simturns.blocked1"       : "Simturns: 1 week, contacts blocked",
+	"vcmi.optionsTab.simturns.blocked2"       : "Simturns: 2 weeks, contacts blocked",
+	"vcmi.optionsTab.simturns.blocked4"       : "Simturns: 1 month, contacts blocked",
+	
+	// Translation note: translate strings below using form that is correct for "0 days", "1 day" and "2 days" in your language
+	// Using this information, VCMI will automatically select correct plural form for every possible amount
+	"vcmi.optionsTab.simturns.days.0" : " %d dní",
+	"vcmi.optionsTab.simturns.days.1" : " %d den",
+	"vcmi.optionsTab.simturns.days.2" : " %d dny",
+	"vcmi.optionsTab.simturns.weeks.0" : " %d týdnů",
+	"vcmi.optionsTab.simturns.weeks.1" : " %d týden",
+	"vcmi.optionsTab.simturns.weeks.2" : " %d týdny",
+	"vcmi.optionsTab.simturns.months.0" : " %d měsíců",
+	"vcmi.optionsTab.simturns.months.1" : " %d měsíc",
+	"vcmi.optionsTab.simturns.months.2" : " %d měsíce",
 
 	// Custom victory conditions for H3 campaigns and HotA maps
 	"vcmi.map.victoryCondition.daysPassed.toOthers" : "Nepřítel zvládl přežít do této chvíle. Vítězství je jeho!",
@@ -203,162 +302,162 @@
 	"vcmi.map.victoryCondition.eliminateMonsters.toSelf" : "Gratulace! Porazili jste všechny nepřátele zamořující tuto zemi a můžete si nárokovat vítězství!",
 	"vcmi.map.victoryCondition.collectArtifacts.message" : "Získejte tři artefakty",
 	"vcmi.map.victoryCondition.angelicAlliance.toSelf" : "Gratulace! Všichni vaši nepřítelé byli poraženi a máte Andělskou alianci! Vítězství je vaše!",
-	"vcmi.map.victoryCondition.angelicAlliance.message" : "Porazte všechny nepřátele a vyrobte Andělskou alianci",
+	"vcmi.map.victoryCondition.angelicAlliance.message" : "Porazte všechny nepřátele a utužte Andělskou alianci",
 
 	// 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.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.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", //TODO
+	"vcmi.stackExperience.rank.0" : "Začátečník",
+	"vcmi.stackExperience.rank.1" : "Učeň",
+	"vcmi.stackExperience.rank.2" : "Trénovaný",
+	"vcmi.stackExperience.rank.3" : "Zručný",
+	"vcmi.stackExperience.rank.4" : "Prověřený",
+	"vcmi.stackExperience.rank.5" : "Veterán",
 	"vcmi.stackExperience.rank.6" : "Adept",
 	"vcmi.stackExperience.rank.7" : "Expert",
-	"vcmi.stackExperience.rank.8" : "Elite",
+	"vcmi.stackExperience.rank.8" : "Elit",
 	"vcmi.stackExperience.rank.9" : "Master",
-	"vcmi.stackExperience.rank.10" : "Ace",
+	"vcmi.stackExperience.rank.10" : "Eso",
 	
 	"core.bonus.ADDITIONAL_ATTACK.name": "Dvojitý úder",
 	"core.bonus.ADDITIONAL_ATTACK.description": "Útočí dvakrát",
-	"core.bonus.ADDITIONAL_RETALIATION.name": "Additional retaliations",
-	"core.bonus.ADDITIONAL_RETALIATION.description": "May retaliate ${val} extra times",
+	"core.bonus.ADDITIONAL_RETALIATION.name": "Další odveta",
+	"core.bonus.ADDITIONAL_RETALIATION.description": "Může zaútočit zpět navíc ${val}x",
 	"core.bonus.AIR_IMMUNITY.name": "Vzdušná odolnost",
-	"core.bonus.AIR_IMMUNITY.description": "Immune to all spells from the school of Air magic",
+	"core.bonus.AIR_IMMUNITY.description": "Imunní všem kouzlům školy vzdušné magie",
 	"core.bonus.ATTACKS_ALL_ADJACENT.name": "Útok okolo",
 	"core.bonus.ATTACKS_ALL_ADJACENT.description": "Útočí na všechny sousední jednotky",
-	"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 using a ranged attack",
+	"core.bonus.BLOCKS_RETALIATION.name": "Žádná odplata",
+	"core.bonus.BLOCKS_RETALIATION.description": "Nepřítel nemůže zaútočit zpět",
+	"core.bonus.BLOCKS_RANGED_RETALIATION.name": "Žádná odplata na dálku",
+	"core.bonus.BLOCKS_RANGED_RETALIATION.description": "Nepřítel nemůže zaútočit zpět útokem na dálku",
 	"core.bonus.CATAPULT.name": "Katapult",
-	"core.bonus.CATAPULT.description": "Attacks siege walls",
-	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Reduce Casting Cost (${val})",
-	"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 spellcasting cost of enemy spells by ${val}",
-	"core.bonus.CHARGE_IMMUNITY.name": "Immune to Charge",
+	"core.bonus.CATAPULT.description": "Útočí na ochranné hradby",
+	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Snížit cenu kouzel (${val})",
+	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Snižuje cenu energie hrdiny o ${val}",
+	"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "Tlumič magie (${val})",
+	"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description": "Zvyšuje cenu energie kouzlení nepřítele o ${val}",
+	"core.bonus.CHARGE_IMMUNITY.name": "Immune to Charge", // TODO
 	"core.bonus.CHARGE_IMMUNITY.description": "Immune to Cavalier's and Champion's Charge",
-	"core.bonus.DARKNESS.name": "Darkness cover",
-	"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": "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": "Has a ${val}% chance of dealing double base damage when attacking",
+	"core.bonus.DARKNESS.name": "Závoj temnoty",
+	"core.bonus.DARKNESS.description": "Vytvoří clonu temnoty v oblasti ${val} polí",
+	"core.bonus.DEATH_STARE.name": "Smrtící pohled (${val}%)",
+	"core.bonus.DEATH_STARE.description": "Má ${val}% šanci zabít jednu creature",
+	"core.bonus.DEFENSIVE_STANCE.name": "Obranný bonus",
+	"core.bonus.DEFENSIVE_STANCE.description": "+${val} obranné síly při obraně",
+	"core.bonus.DESTRUCTION.name": "Zničení",
+	"core.bonus.DESTRUCTION.description": "Má ${val}% šanci zabít další jednotky po útoku",
+	"core.bonus.DOUBLE_DAMAGE_CHANCE.name": "Smrtící rána",
+	"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "Má ${val}% šanci na udělení dvojnásobného základního poškození při útoku",
 	"core.bonus.DRAGON_NATURE.name": "Drak",
-	"core.bonus.DRAGON_NATURE.description": "Creature has a Dragon Nature",
+	"core.bonus.DRAGON_NATURE.description": "Jednotka má povahu draka",
 	"core.bonus.EARTH_IMMUNITY.name": "Zemní odolnost",
-	"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": "When attacking, ${val}% of the defender's defense is ignored",
+	"core.bonus.EARTH_IMMUNITY.description": "Imunní všem kouzlům školy zemské magie",
+	"core.bonus.ENCHANTER.name": "Zaklínač",
+	"core.bonus.ENCHANTER.description": "Může masově seslat ${subtype.spell} každý tah",
+	"core.bonus.ENCHANTED.name": "Očarovaný",
+	"core.bonus.ENCHANTED.description": "Trvale ovlivněm kouzlem ${subtype.spell}",
+	"core.bonus.ENEMY_DEFENCE_REDUCTION.name": "Nevšímá si ${val} % bodů obrany",
+	"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "Pří útoku nebude brát v potaz ${val}% bodů obrany obránce",
 	"core.bonus.FIRE_IMMUNITY.name": "Ohnivá odolnost",
-	"core.bonus.FIRE_IMMUNITY.description": "Immune to all spells from the school of Fire magic",
+	"core.bonus.FIRE_IMMUNITY.description": "Imunní všem kouzlům školy ohnivé magie",
 	"core.bonus.FIRE_SHIELD.name": "Ohnivý štít (${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 retaliates before being attacked",
-	"core.bonus.FEAR.name": "Fear",
-	"core.bonus.FEAR.description": "Causes Fear on an enemy stack",
+	"core.bonus.FIRE_SHIELD.description": "Odrazí část zranení útoku zblízka",
+	"core.bonus.FIRST_STRIKE.name": "První úder",
+	"core.bonus.FIRST_STRIKE.description": "Tato jednotka útočí zpět ještě než je na ni zaútočeno",
+	"core.bonus.FEAR.name": "Strach",
+	"core.bonus.FEAR.description": "Způsobí strach nepřátelskému oddílu",
 	"core.bonus.FEARLESS.name": "Nebojácnost",
 	"core.bonus.FEARLESS.description": "Odolnost proti strachu",
 	"core.bonus.FLYING.name": "Letec",
 	"core.bonus.FLYING.description": "Při pohybu létá (přes překážky)",
 	"core.bonus.FREE_SHOOTING.name": "Blízké výstřely",
 	"core.bonus.FREE_SHOOTING.description": "Může použít výstřely při útoku zblízka",
-	"core.bonus.GARGOYLE.name": "Gargoyle",
-	"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 attacks",
+	"core.bonus.GARGOYLE.name": "Chrlič",
+	"core.bonus.GARGOYLE.description": "Cannot be raised or healed", // TODO
+	"core.bonus.GENERAL_DAMAGE_REDUCTION.name": "Snižuje poškození (${val}%)",
+	"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "Snižuje poškození od útoků z dálky a blízka",
 	"core.bonus.HATE.name": "Nesnáší ${subtype.creature}",
-	"core.bonus.HATE.description": "Does ${val}% more damage to ${subtype.creature}",
+	"core.bonus.HATE.description": "Dává o ${val} % větší zranění jednotce ${subtype.creature}",
 	"core.bonus.HEALER.name": "Léčitel",
 	"core.bonus.HEALER.description": "Léčí spojenecké jednotky",
 	"core.bonus.HP_REGENERATION.name": "Regenerace",
 	"core.bonus.HP_REGENERATION.description": "Každé kolo léčí ${val} životů",
-	"core.bonus.JOUSTING.name": "Champion charge",
-	"core.bonus.JOUSTING.description": "+${val}% damage for each hex travelled",
+	"core.bonus.JOUSTING.name": "Nabití šampiona",
+	"core.bonus.JOUSTING.description": "+${val}% poškození za každé projité pole",
 	"core.bonus.KING.name": "Král",
-	"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" : "Unable to target units farther than ${val} hexes",
+	"core.bonus.KING.description": "Zranitelný zabijákovi úrovně ${val} a vyšší",
+	"core.bonus.LEVEL_SPELL_IMMUNITY.name": "Odolnost kouzel 1-${val}",
+	"core.bonus.LEVEL_SPELL_IMMUNITY.description": "Odolnost vůči kouzlům úrovní 1-${val}",
+	"core.bonus.LIMITED_SHOOTING_RANGE.name" : "Omezený dosah střelby",
+	"core.bonus.LIMITED_SHOOTING_RANGE.description" : "Nevystřelí na jednotky dále než ${val} polí",
 	"core.bonus.LIFE_DRAIN.name": "Vysátí životů (${val}%)",
 	"core.bonus.LIFE_DRAIN.description": "Vysaje ${val}% uděleného poškození",
-	"core.bonus.MANA_CHANNELING.name": "Magic Channel ${val}%",
-	"core.bonus.MANA_CHANNELING.description": "Gives your hero ${val}% of the mana spent by the enemy",
+	"core.bonus.MANA_CHANNELING.name": "Magic Channel ${val}%", // TODO
+	"core.bonus.MANA_CHANNELING.description": "Dá vašemu hrdinovi ${val} % many využité nepřítelem",
 	"core.bonus.MANA_DRAIN.name": "Vysátí many",
 	"core.bonus.MANA_DRAIN.description": "Každé kolo vysaje ${val} many",
 	"core.bonus.MAGIC_MIRROR.name": "Kouzelné zrcadlo (${val}%)",
-	"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": "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",
-	"core.bonus.NO_MORALE.description": "Creature is immune to morale effects",
-	"core.bonus.NO_WALL_PENALTY.name": "No wall penalty",
-	"core.bonus.NO_WALL_PENALTY.description": "Full damage during siege",
-	"core.bonus.NON_LIVING.name": "Non living",
-	"core.bonus.NON_LIVING.description": "Immunity to many effects",
-	"core.bonus.RANDOM_SPELLCASTER.name": "Random spellcaster",
-	"core.bonus.RANDOM_SPELLCASTER.description": "Can cast random spell",
-	"core.bonus.RANGED_RETALIATION.name": "Ranged retaliation",
-	"core.bonus.RANGED_RETALIATION.description": "Can perform ranged counterattack",
-	"core.bonus.RECEPTIVE.name": "Receptive",
-	"core.bonus.RECEPTIVE.description": "No Immunity to Friendly Spells",
-	"core.bonus.REBIRTH.name": "Rebirth (${val}%)",
-	"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.SHOOTER.name": "Ranged",
-	"core.bonus.SHOOTER.description": "Creature can shoot",
-	"core.bonus.SHOOTS_ALL_ADJACENT.name": "Shoot all around",
-	"core.bonus.SHOOTS_ALL_ADJACENT.description": "This creature's ranged attacks strike all targets in a small area",
-	"core.bonus.SOUL_STEAL.name": "Soul Steal",
-	"core.bonus.SOUL_STEAL.description": "Gains ${val} new creatures for each enemy killed",
-	"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": "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": "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 by ${val}%.",
+	"core.bonus.MAGIC_MIRROR.description": "Má ${val}% šanci odrazit útočné kouzlo na nepřátelskou jednotku",
+	"core.bonus.MAGIC_RESISTANCE.name": "Magická odolnost (${val}%)",
+	"core.bonus.MAGIC_RESISTANCE.description": "Má ${val}% šanci ustát nepřátelské kouzlo",
+	"core.bonus.MIND_IMMUNITY.name": "Imunita kouzel mysli",
+	"core.bonus.MIND_IMMUNITY.description": "Imunní vůči kouzlům cílícím na mysl",
+	"core.bonus.NO_DISTANCE_PENALTY.name": "Bez penalizace vzdálenosti",
+	"core.bonus.NO_DISTANCE_PENALTY.description": "Plné poškození na jakoukoliv vzdálenost",
+	"core.bonus.NO_MELEE_PENALTY.name": "Bez penalizace útoku zblízka",
+	"core.bonus.NO_MELEE_PENALTY.description": "Jednotka není penalizována za útok zblízka",
+	"core.bonus.NO_MORALE.name": "Neutrální morálka",
+	"core.bonus.NO_MORALE.description": "Jednotka je imunní vůči efektu morálky",
+	"core.bonus.NO_WALL_PENALTY.name": "Bez penalizace hradbami",
+	"core.bonus.NO_WALL_PENALTY.description": "Plné poškození při obléhání",
+	"core.bonus.NON_LIVING.name": "Neživoucí",
+	"core.bonus.NON_LIVING.description": "Imunní vůči mnohým efektům",
+	"core.bonus.RANDOM_SPELLCASTER.name": "Náhodný kouzelník",
+	"core.bonus.RANDOM_SPELLCASTER.description": "Může seslat náhodné kouzlo",
+	"core.bonus.RANGED_RETALIATION.name": "Vzdálená msta",
+	"core.bonus.RANGED_RETALIATION.description": "Může provést protiútok na dálku",
+	"core.bonus.RECEPTIVE.name": "Přijímavý",
+	"core.bonus.RECEPTIVE.description": "Není imunní vůči přátelským kouzlům",
+	"core.bonus.REBIRTH.name": "Znovuzrození (${val}%)",
+	"core.bonus.REBIRTH.description": "${val}% oddílu se po smrti znovu narodí",
+	"core.bonus.RETURN_AFTER_STRIKE.name": "Útok a návrat",
+	"core.bonus.RETURN_AFTER_STRIKE.description": "Navrátí se po útoku na blízko",
+	"core.bonus.SHOOTER.name": "Střelec",
+	"core.bonus.SHOOTER.description": "Jednotka může střílet",
+	"core.bonus.SHOOTS_ALL_ADJACENT.name": "Střílí okolo",
+	"core.bonus.SHOOTS_ALL_ADJACENT.description": "Vzdálené útoky této jednotky zasáhnou všechny cíle v malé oblasti",
+	"core.bonus.SOUL_STEAL.name": "Zloděj duší",
+	"core.bonus.SOUL_STEAL.description": "Získá ${val} nových jednotek za každého zabitého nepřítele",
+	"core.bonus.SPELLCASTER.name": "Kouzelník",
+	"core.bonus.SPELLCASTER.description": "Může seslat ${subtype.spell}",
+	"core.bonus.SPELL_AFTER_ATTACK.name": "Kouzlení po útoku",
+	"core.bonus.SPELL_AFTER_ATTACK.description": "Má ${val}% šanci seslat ${subtype.spell} po zaútočení",
+	"core.bonus.SPELL_BEFORE_ATTACK.name": "Kouzlení před útokem",
+	"core.bonus.SPELL_BEFORE_ATTACK.description": "Má ${val}% šanci seslat ${subtype.spell} před zaútočením",
+	"core.bonus.SPELL_DAMAGE_REDUCTION.name": "Magická odolnost",
+	"core.bonus.SPELL_DAMAGE_REDUCTION.description": "Zranění od kouzel sníženo o ${val}%.",
 	"core.bonus.SPELL_IMMUNITY.name": "Odolnost vůči kouzlům",
 	"core.bonus.SPELL_IMMUNITY.description": "Odolnost proti ${subtype.spell}",
-	"core.bonus.SPELL_LIKE_ATTACK.name": "Spell-like attack",
-	"core.bonus.SPELL_LIKE_ATTACK.description": "Attacks with ${subtype.spell}",
-	"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 the start of battle summons ${subtype.creature} (${val}%)",
-	"core.bonus.SYNERGY_TARGET.name": "Synergizable",
+	"core.bonus.SPELL_LIKE_ATTACK.name": "Útok kouzlem",
+	"core.bonus.SPELL_LIKE_ATTACK.description": "Útočí kouzlem ${subtype.spell}",
+	"core.bonus.SPELL_RESISTANCE_AURA.name": "Aura odolnosti",
+	"core.bonus.SPELL_RESISTANCE_AURA.description": "Oddíly poblíž získají ${val}% magickou odolnost",
+	"core.bonus.SUMMON_GUARDIANS.name": "Povolat strážce",
+	"core.bonus.SUMMON_GUARDIANS.description": "Na začátku bitvy povolá ${subtype.creature} (${val}%)",
+	"core.bonus.SYNERGY_TARGET.name": "Synergizable", // TODO
 	"core.bonus.SYNERGY_TARGET.description": "This creature is vulnerable to synergy effect",
-	"core.bonus.TWO_HEX_ATTACK_BREATH.name": "Breath",
-	"core.bonus.TWO_HEX_ATTACK_BREATH.description": "Breath Attack (2-hex range)",
-	"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 a different type",
+	"core.bonus.TWO_HEX_ATTACK_BREATH.name": "Dech",
+	"core.bonus.TWO_HEX_ATTACK_BREATH.description": "Dechový útok (dosah do dvou polí)",
+	"core.bonus.THREE_HEADED_ATTACK.name": "Tříhlavý útok",
+	"core.bonus.THREE_HEADED_ATTACK.description": "Útočí na tři sousední jednotky",
+	"core.bonus.TRANSMUTATION.name": "Transmutace",
+	"core.bonus.TRANSMUTATION.description": "${val}% šance na přeměnu útočené jednotky na jiný druh",
 	"core.bonus.UNDEAD.name": "Nemrtvý",
 	"core.bonus.UNDEAD.description": "Jednotka je nemrtvá",
-	"core.bonus.UNLIMITED_RETALIATIONS.name": "Unlimited retaliations",
-	"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 spells from the school of Water magic",
-	"core.bonus.WIDE_BREATH.name": "Wide breath",
-	"core.bonus.WIDE_BREATH.description": "Wide breath attack (multiple hexes)"
+	"core.bonus.UNLIMITED_RETALIATIONS.name": "Neomezené odvety",
+	"core.bonus.UNLIMITED_RETALIATIONS.description": "Může se mstít za neomezený počet útoků",
+	"core.bonus.WATER_IMMUNITY.name": "Vodní odolnost",
+	"core.bonus.WATER_IMMUNITY.description": "Imunní všem kouzlům školy vodní magie",
+	"core.bonus.WIDE_BREATH.name": "Široký dech",
+	"core.bonus.WIDE_BREATH.description": "Útočí širokým dechem (více polí)"
 }

+ 2 - 0
Mods/vcmi/config/vcmi/english.json

@@ -54,6 +54,8 @@
 	"vcmi.radialWheel.moveDown" : "Move down",
 	"vcmi.radialWheel.moveBottom" : "Move to bottom",
 
+	"vcmi.spellBook.search" : "search...",
+
 	"vcmi.mainMenu.serverConnecting" : "Connecting...",
 	"vcmi.mainMenu.serverAddressEnter" : "Enter address:",
 	"vcmi.mainMenu.serverConnectionFailed" : "Failed to connect",

+ 2 - 0
Mods/vcmi/config/vcmi/german.json

@@ -54,6 +54,8 @@
 	"vcmi.radialWheel.moveDown" : "Nach unten bewegen",
 	"vcmi.radialWheel.moveBottom" : "Ganz nach unten bewegen",
 
+	"vcmi.spellBook.search" : "suchen...",
+
 	"vcmi.mainMenu.serverConnecting" : "Verbinde...",
 	"vcmi.mainMenu.serverAddressEnter" : "Addresse eingeben:",
 	"vcmi.mainMenu.serverConnectionFailed" : "Verbindung fehlgeschlagen",

+ 2 - 2
android/vcmi-app/build.gradle

@@ -10,8 +10,8 @@ android {
 		applicationId "is.xyz.vcmi"
 		minSdk 19
 		targetSdk 33
-		versionCode 1412
-		versionName "1.4.1"
+		versionCode 1420
+		versionName "1.4.2"
 		setProperty("archivesBaseName", "vcmi")
 	}
 

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

@@ -156,14 +156,6 @@ public class NativeMethods
         }
     }
 
-    @SuppressWarnings(Const.JNI_METHOD_SUPPRESS)
-    public static String getFormattedDateTime()
-    {
-        String currentDate = new SimpleDateFormat((new SimpleDateFormat()).toLocalizedPattern(), Locale.getDefault()).format(new Date());
-
-        return currentDate;
-    }
-
     private static void internalProgressDisplay(final boolean show)
     {
         final Context ctx = SDL.getContext();

+ 71 - 0
android/vcmi-app/src/main/res/values-cs/strings.xml

@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="url_project_page" translatable="false">https://vcmi.eu</string>
+    <string name="url_project_repo" translatable="false">https://github.com/vcmi/vcmi</string>
+    <string name="url_launcher_repo" translatable="false">https://github.com/vcmi/vcmi-android</string>
+    <string name="url_launcher_privacy" translatable="false">https://github.com/vcmi/vcmi/blob/master/docs/players/Privacy_Policy.md</string>
+
+    <string name="app_name">VCMI</string>
+    <string name="server_name">Server VCMI</string>
+    <string name="launcher_title">Spouštěč VCMI</string>
+    <string name="launcher_btn_scale_title">Škálování herního rozlišení</string>
+    <string name="launcher_btn_scale_subtitle_unknown">Současné: neznámé</string>
+    <string name="launcher_btn_scale_subtitle">Současné: %1$d%%</string>
+    <string name="launcher_btn_start_title">Spustit VCMI</string>
+    <string name="launcher_btn_start_subtitle">Současná verze VCMI: %1$s</string>
+    <string name="launcher_btn_mods_title">Modifikace</string>
+    <string name="launcher_btn_mods_subtitle">Nainstalovat nové frakce, přeměty a bonusy</string>
+    <string name="launcher_btn_language_title">Jazyk</string>
+    <string name="launcher_btn_language_subtitle_unknown">Současný: neznámý</string>
+    <string name="launcher_btn_language_subtitle">Současný: %1$s</string>
+    <string name="launcher_btn_pointermode_title">Změnit režim ukazatele</string>
+    <string name="launcher_btn_pointermode_subtitle">Současný: %1$s</string>
+    <string name="launcher_btn_pointermulti_title">Násobitel rychlosti relativního ukazatele</string>
+    <string name="launcher_btn_pointermulti_subtitle">Současný: %1$s</string>
+    <string name="launcher_btn_sound_title">Hlasitost zvuků</string>
+    <string name="launcher_btn_music_title">Hlasitost hudby</string>
+    <string name="launcher_btn_adventure_ai">AI světa</string>
+    <string name="launcher_btn_adventure_ai_title">Změnit AI světa</string>
+    <string name="launcher_btn_import_title">Importovat data VCMI</string>
+    <string name="launcher_btn_import_description">Zkopírovat soubury VCMI do vestavěného úložiště. Můžete importovat starou složku dat vcmi z vydání 0.99 nebo soubory HOMM3</string>
+    <string name="launcher_btn_export_title">Exportovat data VCMI</string>
+    <string name="launcher_btn_export_description">Udělat kopii dat VCMI před odinstalací nebo pro synchronizaci s desktopovou verzí. Též můžete přímo přistoupit k interním datům.</string>
+    <string name="launcher_progress_copy">Kopírování %1$s</string>
+    <string name="launcher_version">Současná verze spouštěče: %1$s</string>
+    <string name="launcher_error_vcmi_data_root_failed">Nelze vytvořit datovou složku VCMI v %1$s.</string>
+    <string name="launcher_error_h3_data_missing">Nelze najít datovou složku v \'%1$s\'. Vložte do ní své datové soubory HoMM3 nebo použijte tlačítko níže. Možná budete muset také restartovat aplikaci.</string>
+    <string name="launcher_error_vcmi_data_internal_missing">Nelze najít nebo rozbalit data vcmi ze zdrojů aplikace. Zkuste přeinstalovat aplikaci.</string>
+    <string name="launcher_error_vcmi_data_internal_update">Nelze aktualizovat data vcmi ze zdrojů aplikace. Zkuste přeinstalovat aplikaci.</string>
+    <string name="launcher_error_permissions">Tato aplikace potřebuje oprávnění k zápisu pro použití obsahu na externím úložišti</string>
+    <string name="launcher_error_permission_broken">Nelze správně vyřešit oprávnění</string>
+    <string name="mods_item_author_template">od %1$s</string>
+    <string name="misc_try_again">Zkusit znovu</string>
+    <string name="launcher_section_init">Inicializae hry</string>
+    <string name="launcher_section_settings">Nastavení</string>
+    <string name="menu_mods_download_repo">Stáhnout data repozitáře</string>
+
+    <string name="misc_pointermode_normal">Normální</string>
+    <string name="misc_pointermode_relative">Relativní</string>
+    <string name="menu_launcher_about">O spouštěči</string>
+
+    <string name="mods_title">Nalezené modifikace</string>
+    <string name="mods_failed_mod_loading">Nelze načíst modifikaci ve složce \'%1$s\'</string>
+    <string name="mods_removal_title">Odebírání %1$s</string>
+    <string name="mods_removal_confirmation">Jste si jisti odebráním %1$s?</string>
+
+    <string name="about_title">O aplikaci</string>
+    <string name="about_version_app">Verze aplikace: %1$s</string>
+    <string name="about_version_launcher">Verze spouštěče: %1$s</string>
+    <string name="about_section_project">Projekt</string>
+    <string name="about_section_legal">Právní záležitosti</string>
+    <string name="about_links_main">Hlavní stránka: %1$s</string>
+    <string name="about_links_repo">Repozitář projektu: %1$s</string>
+    <string name="about_links_repo_launcher">Repozitář spouštěče: %1$s</string>
+    <string name="about_btn_authors">Autoři</string>
+    <string name="about_btn_privacy">Zásady ochrany osobních údajů: %1$s</string>
+    <string name="about_error_opening_url">Nebylo možné otevřít webovou stránku (nenalezena patřičná aplikace)</string>
+
+    <string name="dialog_authors_vcmi">Autoři VCMI</string>
+    <string name="dialog_authors_launcher">Autoři spouštěče</string>
+    <string name="launcher_error_config_saving_failed">Nelze uložit konfigurační soubor VCMI; důvod: %1$s</string>
+</resources>

+ 16 - 3
client/CMT.cpp

@@ -55,12 +55,14 @@
 namespace po = boost::program_options;
 namespace po_style = boost::program_options::command_line_style;
 
+static std::atomic<bool> quitRequestedDuringOpeningPlayback = false;
 static po::variables_map vm;
 
 #ifndef VCMI_IOS
 void processCommand(const std::string &message);
 #endif
 void playIntro();
+[[noreturn]] static void quitApplication();
 static void mainLoop();
 
 static CBasicLogConfigurator *logConfig;
@@ -313,7 +315,6 @@ int main(int argc, char * argv[])
 		GH.screenHandler().clearScreen();
 	}
 
-
 #ifndef VCMI_NO_THREADED_LOAD
 	#ifdef VCMI_ANDROID // android loads the data quite slowly so we display native progressbar to prevent having only black screen for few seconds
 	{
@@ -327,6 +328,9 @@ int main(int argc, char * argv[])
 	#endif // ANDROID
 #endif // THREADED
 
+	if (quitRequestedDuringOpeningPlayback)
+		quitApplication();
+
 	if(!settings["session"]["headless"].Bool())
 	{
 		pomtime.getDiff();
@@ -414,7 +418,7 @@ int main(int argc, char * argv[])
 	else
 	{
 		while(true)
-			boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
+			boost::this_thread::sleep_for(boost::chrono::milliseconds(200));
 	}
 
 	return 0;
@@ -451,7 +455,7 @@ static void mainLoop()
 	}
 }
 
-static void quitApplication()
+[[noreturn]] static void quitApplication()
 {
 	if(!settings["session"]["headless"].Bool())
 	{
@@ -516,6 +520,15 @@ static void quitApplication()
 
 void handleQuit(bool ask)
 {
+	// FIXME: avoids crash if player attempts to close game while opening is still playing
+	// use cursor handler as indicator that loading is not done yet
+	// proper solution would be to abort init thread (or wait for it to finish)
+	if (!CCS->curh)
+	{
+		quitRequestedDuringOpeningPlayback = true;
+		return;
+	}
+
 	if(ask)
 	{
 		CCS->curh->set(Cursor::Map::POINTER);

+ 1 - 1
client/CVideoHandler.cpp

@@ -637,7 +637,7 @@ bool CVideoPlayer::playVideo(int x, int y, bool stopOnKey)
 
 		SDL_Rect rect = CSDL_Ext::toSDL(pos);
 
-		SDL_RenderClear(mainRenderer);
+		SDL_RenderFillRect(mainRenderer, &rect);
 		SDL_RenderCopy(mainRenderer, texture, nullptr, &rect);
 		SDL_RenderPresent(mainRenderer);
 

+ 1 - 1
client/NetPacksLobbyClient.cpp

@@ -47,7 +47,7 @@ void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientConnected(LobbyClientCon
 
 void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack)
 {
-	if(pack.clientId != pack.c->connectionID)
+	if(pack.clientId != handler.c->connectionID)
 	{
 		result = false;
 		return;

+ 2 - 2
client/PlayerLocalState.h

@@ -42,7 +42,7 @@ public:
 	{
 		//on which page we left spellbook
 		int spellbookLastPageBattle = 0;
-		int spellbokLastPageAdvmap = 0;
+		int spellbookLastPageAdvmap = 0;
 		int spellbookLastTabBattle = 4;
 		int spellbookLastTabAdvmap = 4;
 
@@ -50,7 +50,7 @@ public:
 		void serialize(Handler & h, const int version)
 		{
 			h & spellbookLastPageBattle;
-			h & spellbokLastPageAdvmap;
+			h & spellbookLastPageAdvmap;
 			h & spellbookLastTabBattle;
 			h & spellbookLastTabAdvmap;
 		}

+ 3 - 0
client/adventureMap/CInGameConsole.cpp

@@ -151,6 +151,9 @@ void CInGameConsole::keyPressed (EShortcut key)
 		break;
 
 	case EShortcut::GAME_ACTIVATE_CONSOLE:
+		if(GH.isKeyboardAltDown())
+			return; //QoL for alt-tab operating system shortcut
+
 		if(!enteredText.empty())
 			endEnteringText(false);
 		else

+ 2 - 3
client/mainmenu/CHighScoreScreen.cpp

@@ -29,8 +29,7 @@
 #include "../../lib/CCreatureHandler.h"
 #include "../../lib/constants/EntityIdentifiers.h"
 #include "../../lib/TextOperations.h"
-
-#include "vstd/DateUtils.h"
+#include "../../lib/Languages.h"
 
 auto HighScoreCalculation::calculate()
 {
@@ -264,7 +263,7 @@ int CHighScoreInputScreen::addEntry(std::string text) {
 		newNode["scenarioName"].String() = calc.calculate().cheater ? CGI->generaltexth->translate("core.genrltxt.260") : calc.parameters[0].scenarioName;
 	newNode["days"].Integer() = calc.calculate().sumDays;
 	newNode["points"].Integer() = calc.calculate().cheater ? 0 : calc.calculate().total;
-	newNode["datetime"].String() = vstd::getFormattedDateTime(std::time(nullptr));
+	newNode["datetime"].String() = TextOperations::getFormattedDateTimeLocal(std::time(nullptr));
 	newNode["posFlag"].Bool() = true;
 
 	baseNode.push_back(newNode);

+ 30 - 6
client/widgets/CArtifactHolder.cpp

@@ -139,7 +139,7 @@ void CCommanderArtPlace::clickPressed(const Point & cursorPosition)
 		LOCPLINT->showYesNoDialog(CGI->generaltexth->translate("vcmi.commanderWindow.artifactMessage"), [this]() { returnArtToHeroCallback(); }, []() {});
 }
 
-void CCommanderArtPlace::showPopupWindow(const Point & cursorPosition)
+void CCommanderArtPlace::showPopupWindow(const Point& cursorPosition)
 {
 	if(ourArt && text.size())
 		CArtPlace::showPopupWindow(cursorPosition);
@@ -180,16 +180,25 @@ bool CArtPlace::isSelected() const
 	return selection->visible;
 }
 
-void CHeroArtPlace::clickPressed(const Point & cursorPosition)
+void CArtPlace::clickPressed(const Point & cursorPosition)
 {
-	if(leftClickCallback)
-		leftClickCallback(*this);
+	if(clickPressedCallback)
+		clickPressedCallback(*this, cursorPosition);
 }
 
-void CHeroArtPlace::showPopupWindow(const Point & cursorPosition)
+void CArtPlace::showPopupWindow(const Point & cursorPosition)
 {
 	if(showPopupCallback)
-		showPopupCallback(*this);
+		showPopupCallback(*this, cursorPosition);
+}
+
+void CArtPlace::gesture(bool on, const Point & initialPosition, const Point & finalPosition)
+{
+	if(!on)
+		return;
+
+	if(gestureCallback)
+		gestureCallback(*this, initialPosition);
 }
 
 void CArtPlace::showAll(Canvas & to)
@@ -216,6 +225,21 @@ void CArtPlace::setArtifact(const CArtifactInstance * art)
 	}
 }
 
+void CArtPlace::setClickPressedCallback(ClickFunctor callback)
+{
+	clickPressedCallback = callback;
+}
+
+void CArtPlace::setShowPopupCallback(ClickFunctor callback)
+{
+	showPopupCallback = callback;
+}
+
+void CArtPlace::setGestureCallback(ClickFunctor callback)
+{
+	gestureCallback = callback;
+}
+
 void CHeroArtPlace::addCombinedArtInfo(std::map<const CArtifact*, int> & arts)
 {
 	for(const auto & combinedArt : arts)

+ 14 - 9
client/widgets/CArtifactHolder.h

@@ -32,14 +32,24 @@ public:
 class CArtPlace : public LRClickableAreaWTextComp
 {
 public:
+	using ClickFunctor = std::function<void(CArtPlace&, const Point&)>;
+
+	ArtifactPosition slot;
+
 	CArtPlace(Point position, const CArtifactInstance * art = nullptr);
-	const CArtifactInstance* getArt();
+	const CArtifactInstance * getArt();
 	void lockSlot(bool on);
 	bool isLocked() const;
 	void selectSlot(bool on);
 	bool isSelected() const;
 	void showAll(Canvas & to) override;
 	void setArtifact(const CArtifactInstance * art);
+	void setClickPressedCallback(ClickFunctor callback);
+	void setShowPopupCallback(ClickFunctor callback);
+	void setGestureCallback(ClickFunctor callback);
+	void clickPressed(const Point & cursorPosition) override;
+	void showPopupWindow(const Point & cursorPosition) override;
+	void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override;
 
 protected:
 	std::shared_ptr<CAnimImage> image;
@@ -47,6 +57,9 @@ protected:
 	int imageIndex;
 	std::shared_ptr<CAnimImage> selection;
 	bool locked;
+	ClickFunctor clickPressedCallback;
+	ClickFunctor showPopupCallback;
+	ClickFunctor gestureCallback;
 
 	void setInternals(const CArtifactInstance * artInst);
 };
@@ -68,15 +81,7 @@ public:
 class CHeroArtPlace: public CArtPlace
 {
 public:
-	using ClickFunctor = std::function<void(CHeroArtPlace&)>;
-
-	ArtifactPosition slot;
-	ClickFunctor leftClickCallback;
-	ClickFunctor showPopupCallback;
-
 	CHeroArtPlace(Point position, const CArtifactInstance * art = nullptr);
-	void clickPressed(const Point & cursorPosition) override;
-	void showPopupWindow(const Point & cursorPosition) override;
 	void addCombinedArtInfo(std::map<const CArtifact*, int> & arts);
 };
 

+ 3 - 3
client/widgets/CArtifactsOfHeroAltar.cpp

@@ -23,8 +23,8 @@ CArtifactsOfHeroAltar::CArtifactsOfHeroAltar(const Point & position)
 	: visibleArtSet(ArtBearer::ArtBearer::HERO)
 {
 	init(
-		std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1), 
-		std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1), 
+		std::bind(&CArtifactsOfHeroBase::clickPrassedArtPlace, this, _1, _2),
+		std::bind(&CArtifactsOfHeroBase::showPopupArtPlace, this, _1, _2),
 		position,
 		std::bind(&CArtifactsOfHeroAltar::scrollBackpack, this, _1));
 	pickedArtFromSlot = ArtifactPosition::PRE_FIRST;
@@ -69,7 +69,7 @@ void CArtifactsOfHeroAltar::scrollBackpack(int offset)
 	redraw();
 }
 
-void CArtifactsOfHeroAltar::pickUpArtifact(CHeroArtPlace & artPlace)
+void CArtifactsOfHeroAltar::pickUpArtifact(CArtPlace & artPlace)
 {
 	if(const auto art = artPlace.getArt())
 	{

+ 1 - 1
client/widgets/CArtifactsOfHeroAltar.h

@@ -26,7 +26,7 @@ public:
 	void updateWornSlots() override;
 	void updateBackpackSlots() override;
 	void scrollBackpack(int offset) override;
-	void pickUpArtifact(CHeroArtPlace & artPlace);
+	void pickUpArtifact(CArtPlace & artPlace);
 	void swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc);
 	void pickedArtMoveToAltar(const ArtifactPosition & slot);
 	void deleteFromVisible(const CArtifactInstance * artInst);

+ 148 - 40
client/widgets/CArtifactsOfHeroBackpack.cpp

@@ -17,37 +17,91 @@
 #include "ObjectLists.h"
 
 #include "../CPlayerInterface.h"
+#include "../../lib/ArtifactUtils.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/networkPacks/ArtifactLocation.h"
 
 #include "../../CCallback.h"
 
-CArtifactsOfHeroBackpack::CArtifactsOfHeroBackpack(const Point & position)
+CArtifactsOfHeroBackpack::CArtifactsOfHeroBackpack(size_t slotsColumnsMax, size_t slotsRowsMax)
+	: slotsColumnsMax(slotsColumnsMax)
+	, slotsRowsMax(slotsRowsMax)
 {
-	OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
-	pos += position;
 	setRedrawParent(true);
+}
 
+CArtifactsOfHeroBackpack::CArtifactsOfHeroBackpack()
+	: CArtifactsOfHeroBackpack(8, 8)
+{
 	const auto backpackCap = VLC->settings()->getInteger(EGameSettings::HEROES_BACKPACK_CAP);
-	auto visibleCapacityMax = HERO_BACKPACK_WINDOW_SLOT_ROWS * HERO_BACKPACK_WINDOW_SLOT_COLUMNS;
+	auto visibleCapacityMax = slotsRowsMax * slotsColumnsMax;
 	if(backpackCap >= 0)
 		visibleCapacityMax = visibleCapacityMax > backpackCap ? backpackCap : visibleCapacityMax;
 
-	backpack.resize(visibleCapacityMax);
+	initAOHbackpack(visibleCapacityMax, backpackCap < 0 || visibleCapacityMax < backpackCap);
+}
+
+void CArtifactsOfHeroBackpack::swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc)
+{
+	LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc);
+}
+
+void CArtifactsOfHeroBackpack::pickUpArtifact(CArtPlace & artPlace)
+{
+	LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, artPlace.slot),
+		ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS));
+}
+
+void CArtifactsOfHeroBackpack::scrollBackpack(int offset)
+{
+	if(backpackListBox)
+		backpackListBox->resize(getActiveSlotRowsNum());
+	backpackPos += offset;
+	auto slot = ArtifactPosition::BACKPACK_START + backpackPos;
+	for(auto artPlace : backpack)
+	{
+		setSlotData(artPlace, slot, *curHero);
+		slot = slot + 1;
+	}
+	redraw();
+}
+
+void CArtifactsOfHeroBackpack::updateBackpackSlots()
+{
+	if(backpackListBox)
+		backpackListBox->resize(getActiveSlotRowsNum());
+	CArtifactsOfHeroBase::updateBackpackSlots();
+}
+
+size_t CArtifactsOfHeroBackpack::getActiveSlotRowsNum()
+{
+	return (curHero->artifactsInBackpack.size() + slotsColumnsMax - 1) / slotsColumnsMax;
+}
+
+size_t CArtifactsOfHeroBackpack::getSlotsNum()
+{
+	return backpack.size();
+}
+
+void CArtifactsOfHeroBackpack::initAOHbackpack(size_t slots, bool slider)
+{
+	OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
+
+	backpack.resize(slots);
 	size_t artPlaceIdx = 0;
 	for(auto & artPlace : backpack)
 	{
-		const auto pos = Point(slotSizeWithMargin * (artPlaceIdx % HERO_BACKPACK_WINDOW_SLOT_COLUMNS),
-			slotSizeWithMargin * (artPlaceIdx / HERO_BACKPACK_WINDOW_SLOT_COLUMNS));
+		const auto pos = Point(slotSizeWithMargin * (artPlaceIdx % slotsColumnsMax),
+			slotSizeWithMargin * (artPlaceIdx / slotsColumnsMax));
 		backpackSlotsBackgrounds.emplace_back(std::make_shared<CPicture>(ImagePath::builtin("heroWindow/artifactSlotEmpty"), pos));
 		artPlace = std::make_shared<CHeroArtPlace>(pos);
 		artPlace->setArtifact(nullptr);
-		artPlace->leftClickCallback = std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1);
-		artPlace->showPopupCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1);
+		artPlace->setClickPressedCallback(std::bind(&CArtifactsOfHeroBase::clickPrassedArtPlace, this, _1, _2));
+		artPlace->setShowPopupCallback(std::bind(&CArtifactsOfHeroBase::showPopupArtPlace, this, _1, _2));
 		artPlaceIdx++;
 	}
 
-	if(backpackCap < 0 || visibleCapacityMax < backpackCap)
+	if(slider)
 	{
 		auto onCreate = [](size_t index) -> std::shared_ptr<CIntObject>
 		{
@@ -55,57 +109,111 @@ CArtifactsOfHeroBackpack::CArtifactsOfHeroBackpack(const Point & position)
 		};
 		CListBoxWithCallback::MovedPosCallback posMoved = [this](size_t pos) -> void
 		{
-			scrollBackpack(static_cast<int>(pos) * HERO_BACKPACK_WINDOW_SLOT_COLUMNS - backpackPos);
+			scrollBackpack(static_cast<int>(pos) * slotsColumnsMax - backpackPos);
 		};
 		backpackListBox = std::make_shared<CListBoxWithCallback>(
-				posMoved, onCreate, Point(0, 0), Point(0, 0), HERO_BACKPACK_WINDOW_SLOT_ROWS, 0, 0, 1,
-				Rect(HERO_BACKPACK_WINDOW_SLOT_COLUMNS * slotSizeWithMargin + sliderPosOffsetX, 0, HERO_BACKPACK_WINDOW_SLOT_ROWS * slotSizeWithMargin - 2, 0));
+			posMoved, onCreate, Point(0, 0), Point(0, 0), slotsRowsMax, 0, 0, 1,
+			Rect(slotsColumnsMax * slotSizeWithMargin + sliderPosOffsetX, 0, slotsRowsMax * slotSizeWithMargin - 2, 0));
 	}
 
-	pos.w = visibleCapacityMax > HERO_BACKPACK_WINDOW_SLOT_COLUMNS ? HERO_BACKPACK_WINDOW_SLOT_COLUMNS : visibleCapacityMax;
+	pos.w = slots > slotsColumnsMax ? slotsColumnsMax : slots;
 	pos.w *= slotSizeWithMargin;
-	if(backpackListBox)
+	if(slider)
 		pos.w += sliderPosOffsetX + 16; // 16 is slider width. TODO: get it from CListBox directly;
-
-	pos.h = (visibleCapacityMax / HERO_BACKPACK_WINDOW_SLOT_COLUMNS);
-	if(visibleCapacityMax % HERO_BACKPACK_WINDOW_SLOT_COLUMNS != 0)
-		pos.h += 1;
-	pos.h *= slotSizeWithMargin;
+	pos.h = calcRows(slots) * slotSizeWithMargin;
 }
 
-void CArtifactsOfHeroBackpack::swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc)
+size_t CArtifactsOfHeroBackpack::calcRows(size_t slots)
 {
-	LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc);
+	size_t rows = 0;
+	if(slotsColumnsMax != 0)
+	{
+		rows = slots / slotsColumnsMax;
+		if(slots % slotsColumnsMax != 0)
+			rows += 1;
+	}
+	return rows;
 }
 
-void CArtifactsOfHeroBackpack::pickUpArtifact(CHeroArtPlace & artPlace)
+CArtifactsOfHeroQuickBackpack::CArtifactsOfHeroQuickBackpack(const ArtifactPosition filterBySlot)
+	: CArtifactsOfHeroBackpack(0, 0)
 {
-	LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, artPlace.slot),
-		ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS));
+	assert(filterBySlot != ArtifactPosition::FIRST_AVAILABLE);
+
+	if(!ArtifactUtils::isSlotEquipment(filterBySlot))
+		return;
+
+	this->filterBySlot = filterBySlot;
 }
 
-void CArtifactsOfHeroBackpack::scrollBackpack(int offset)
+void CArtifactsOfHeroQuickBackpack::setHero(const CGHeroInstance * hero)
 {
-	if(backpackListBox)
-		backpackListBox->resize(getActiveSlotRowsNum());
-	backpackPos += offset;
-	auto slot = ArtifactPosition::BACKPACK_START + backpackPos;
-	for(auto artPlace : backpack)
+	if(curHero == hero)
+		return;
+	
+	curHero = hero;
+	if(curHero)
 	{
-		setSlotData(artPlace, slot, *curHero);
-		slot = slot + 1;
+		ArtifactID artInSlotId = ArtifactID::NONE;
+		SpellID scrollInSlotSpellId = SpellID::NONE;
+		if(auto artInSlot = curHero->getArt(filterBySlot))
+		{
+			artInSlotId = artInSlot->getTypeId();
+			scrollInSlotSpellId = artInSlot->getScrollSpellID();
+		}
+
+		std::map<const ArtifactID, const CArtifactInstance*> filteredArts;
+		for(auto & slotInfo : curHero->artifactsInBackpack)
+			if(slotInfo.artifact->getTypeId() != artInSlotId &&	!slotInfo.artifact->isScroll() &&
+				slotInfo.artifact->artType->canBePutAt(curHero, filterBySlot, true))
+			{
+				filteredArts.insert(std::pair(slotInfo.artifact->getTypeId(), slotInfo.artifact));
+			}
+
+		std::map<const SpellID, const CArtifactInstance*> filteredScrolls;
+		if(filterBySlot == ArtifactPosition::MISC1 || filterBySlot == ArtifactPosition::MISC2 || filterBySlot == ArtifactPosition::MISC3 ||
+			filterBySlot == ArtifactPosition::MISC4 || filterBySlot == ArtifactPosition::MISC5)
+		{
+			for(auto & slotInfo : curHero->artifactsInBackpack)
+			{
+				if(slotInfo.artifact->isScroll() && slotInfo.artifact->getScrollSpellID() != scrollInSlotSpellId)
+					filteredScrolls.insert(std::pair(slotInfo.artifact->getScrollSpellID(), slotInfo.artifact));
+			}
+		}
+
+		backpack.clear();
+		auto requiredSlots = filteredArts.size() + filteredScrolls.size();
+		slotsColumnsMax = ceilf(sqrtf(requiredSlots));
+		slotsRowsMax = calcRows(requiredSlots);
+		initAOHbackpack(requiredSlots, false);
+		auto artPlace = backpack.begin();
+		for(auto & art : filteredArts)
+			setSlotData(*artPlace++, curHero->getSlotByInstance(art.second), *curHero);
+		for(auto & art : filteredScrolls)
+			setSlotData(*artPlace++, curHero->getSlotByInstance(art.second), *curHero);
 	}
-	redraw();
 }
 
-void CArtifactsOfHeroBackpack::updateBackpackSlots()
+ArtifactPosition CArtifactsOfHeroQuickBackpack::getFilterSlot()
 {
-	if(backpackListBox)
-		backpackListBox->resize(getActiveSlotRowsNum());
-	CArtifactsOfHeroBase::updateBackpackSlots();
+	return filterBySlot;
 }
 
-size_t CArtifactsOfHeroBackpack::getActiveSlotRowsNum()
+void CArtifactsOfHeroQuickBackpack::selectSlotAt(const Point & position)
 {
-	return (curHero->artifactsInBackpack.size() + HERO_BACKPACK_WINDOW_SLOT_COLUMNS - 1) / HERO_BACKPACK_WINDOW_SLOT_COLUMNS;
+	for(auto & artPlace : backpack)
+		artPlace->selectSlot(artPlace->pos.isInside(position));
 }
+
+void CArtifactsOfHeroQuickBackpack::swapSelected()
+{
+	ArtifactLocation backpackLoc(curHero->id, ArtifactPosition::PRE_FIRST);
+	for(auto & artPlace : backpack)
+		if(artPlace->isSelected())
+		{
+			backpackLoc.slot = artPlace->slot;
+			break;
+		}
+	if(backpackLoc.slot != ArtifactPosition::PRE_FIRST && filterBySlot != ArtifactPosition::PRE_FIRST && curHero)
+		swapArtifacts(backpackLoc, ArtifactLocation(curHero->id, filterBySlot));
+}

+ 24 - 6
client/widgets/CArtifactsOfHeroBackpack.h

@@ -22,18 +22,36 @@ class CListBoxWithCallback;
 class CArtifactsOfHeroBackpack : public CArtifactsOfHeroBase
 {
 public:
-	CArtifactsOfHeroBackpack(const Point & position);
+	CArtifactsOfHeroBackpack(size_t slotsColumnsMax, size_t slotsRowsMax);
+	CArtifactsOfHeroBackpack();
 	void swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc);
-	void pickUpArtifact(CHeroArtPlace & artPlace);
+	void pickUpArtifact(CArtPlace & artPlace);
 	void scrollBackpack(int offset) override;
 	void updateBackpackSlots() override;
 	size_t getActiveSlotRowsNum();
+	size_t getSlotsNum();
 
-private:
+protected:
 	std::shared_ptr<CListBoxWithCallback> backpackListBox;
 	std::vector<std::shared_ptr<CPicture>> backpackSlotsBackgrounds;
-	const size_t HERO_BACKPACK_WINDOW_SLOT_COLUMNS = 8;
-	const size_t HERO_BACKPACK_WINDOW_SLOT_ROWS = 8;
+	size_t slotsColumnsMax;
+	size_t slotsRowsMax;
 	const int slotSizeWithMargin = 46;
-	const int sliderPosOffsetX = 10;
+	const int sliderPosOffsetX = 5;
+
+	void initAOHbackpack(size_t slots, bool slider);
+	size_t calcRows(size_t slots);
+};
+
+class CArtifactsOfHeroQuickBackpack : public CArtifactsOfHeroBackpack
+{
+public:
+	CArtifactsOfHeroQuickBackpack(const ArtifactPosition filterBySlot);
+	void setHero(const CGHeroInstance * hero);
+	ArtifactPosition getFilterSlot();
+	void selectSlotAt(const Point & position);
+	void swapSelected();
+
+private:
+	ArtifactPosition filterBySlot;
 };

+ 26 - 11
client/widgets/CArtifactsOfHeroBase.cpp

@@ -56,8 +56,8 @@ void CArtifactsOfHeroBase::setPutBackPickedArtifactCallback(PutBackPickedArtCall
 }
 
 void CArtifactsOfHeroBase::init(
-	CHeroArtPlace::ClickFunctor lClickCallback,
-	CHeroArtPlace::ClickFunctor showPopupCallback,
+	CArtPlace::ClickFunctor lClickCallback,
+	CArtPlace::ClickFunctor showPopupCallback,
 	const Point & position,
 	BpackScrollFunctor scrollCallback)
 {
@@ -78,14 +78,14 @@ void CArtifactsOfHeroBase::init(
 	{
 		artPlace.second->slot = artPlace.first;
 		artPlace.second->setArtifact(nullptr);
-		artPlace.second->leftClickCallback = lClickCallback;
-		artPlace.second->showPopupCallback = showPopupCallback;
+		artPlace.second->setClickPressedCallback(lClickCallback);
+		artPlace.second->setShowPopupCallback(showPopupCallback);
 	}
 	for(auto artPlace : backpack)
 	{
 		artPlace->setArtifact(nullptr);
-		artPlace->leftClickCallback = lClickCallback;
-		artPlace->showPopupCallback = showPopupCallback;
+		artPlace->setClickPressedCallback(lClickCallback);
+		artPlace->setShowPopupCallback(showPopupCallback);
 	}
 	leftBackpackRoll = std::make_shared<CButton>(Point(379, 364), AnimationPath::builtin("hsbtns3.def"), CButton::tooltip(), [scrollCallback]() {scrollCallback(-1);}, EShortcut::MOVE_LEFT);
 	rightBackpackRoll = std::make_shared<CButton>(Point(632, 364), AnimationPath::builtin("hsbtns5.def"), CButton::tooltip(), [scrollCallback]() {scrollCallback(+1);}, EShortcut::MOVE_RIGHT);
@@ -95,16 +95,22 @@ void CArtifactsOfHeroBase::init(
 	setRedrawParent(true);
 }
 
-void CArtifactsOfHeroBase::leftClickArtPlace(CHeroArtPlace & artPlace)
+void CArtifactsOfHeroBase::clickPrassedArtPlace(CArtPlace & artPlace, const Point & cursorPosition)
 {
-	if(leftClickCallback)
-		leftClickCallback(*this, artPlace);
+	if(clickPressedCallback)
+		clickPressedCallback(*this, artPlace, cursorPosition);
 }
 
-void CArtifactsOfHeroBase::rightClickArtPlace(CHeroArtPlace & artPlace)
+void CArtifactsOfHeroBase::showPopupArtPlace(CArtPlace & artPlace, const Point & cursorPosition)
 {
 	if(showPopupCallback)
-		showPopupCallback(*this, artPlace);
+		showPopupCallback(*this, artPlace, cursorPosition);
+}
+
+void CArtifactsOfHeroBase::gestureArtPlace(CArtPlace & artPlace, const Point & cursorPosition)
+{
+	if(gestureCallback)
+		gestureCallback(*this, artPlace, cursorPosition);
 }
 
 void CArtifactsOfHeroBase::setHero(const CGHeroInstance * hero)
@@ -241,6 +247,15 @@ const CArtifactInstance * CArtifactsOfHeroBase::getPickedArtifact()
 		return curHero->getArt(ArtifactPosition::TRANSITION_POS);
 }
 
+void CArtifactsOfHeroBase::addGestureCallback(CArtPlace::ClickFunctor callback)
+{
+	for(auto & artPlace : artWorn)
+	{
+		artPlace.second->setGestureCallback(callback);
+		artPlace.second->addUsedEvents(GESTURE);
+	}
+}
+
 void CArtifactsOfHeroBase::setSlotData(ArtPlacePtr artPlace, const ArtifactPosition & slot, const CArtifactSet & artSet)
 {
 	// Spurious call from artifactMoved in attempt to update hidden backpack slot

+ 7 - 4
client/widgets/CArtifactsOfHeroBase.h

@@ -21,17 +21,19 @@ protected:
 
 public:
 	using ArtPlaceMap = std::map<ArtifactPosition, ArtPlacePtr>;
-	using ClickFunctor = std::function<void(CArtifactsOfHeroBase&, CHeroArtPlace&)>;
+	using ClickFunctor = std::function<void(CArtifactsOfHeroBase&, CArtPlace&, const Point&)>;
 	using PutBackPickedArtCallback = std::function<void()>;
 
-	ClickFunctor leftClickCallback;
+	ClickFunctor clickPressedCallback;
 	ClickFunctor showPopupCallback;
+	ClickFunctor gestureCallback;
 	
 	CArtifactsOfHeroBase();
 	virtual void putBackPickedArtifact();
 	virtual void setPutBackPickedArtifactCallback(PutBackPickedArtCallback callback);
-	virtual void leftClickArtPlace(CHeroArtPlace & artPlace);
-	virtual void rightClickArtPlace(CHeroArtPlace & artPlace);
+	virtual void clickPrassedArtPlace(CArtPlace & artPlace, const Point & cursorPosition);
+	virtual void showPopupArtPlace(CArtPlace & artPlace, const Point & cursorPosition);
+	virtual void gestureArtPlace(CArtPlace & artPlace, const Point & cursorPosition);
 	virtual void setHero(const CGHeroInstance * hero);
 	virtual const CGHeroInstance * getHero() const;
 	virtual void scrollBackpack(int offset);
@@ -42,6 +44,7 @@ public:
 	virtual void updateBackpackSlots();
 	virtual void updateSlot(const ArtifactPosition & slot);
 	virtual const CArtifactInstance * getPickedArtifact();
+	void addGestureCallback(CArtPlace::ClickFunctor callback);
 
 protected:
 	const CGHeroInstance * curHero;

+ 6 - 5
client/widgets/CArtifactsOfHeroKingdom.cpp

@@ -30,14 +30,15 @@ CArtifactsOfHeroKingdom::CArtifactsOfHeroKingdom(ArtPlaceMap ArtWorn, std::vecto
 	{
 		artPlace.second->slot = artPlace.first;
 		artPlace.second->setArtifact(nullptr);
-		artPlace.second->leftClickCallback = std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1);
-		artPlace.second->showPopupCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1);
+		artPlace.second->setClickPressedCallback(std::bind(&CArtifactsOfHeroBase::clickPrassedArtPlace, this, _1, _2));
+		artPlace.second->setShowPopupCallback(std::bind(&CArtifactsOfHeroBase::showPopupArtPlace, this, _1, _2));
 	}
+	addGestureCallback(std::bind(&CArtifactsOfHeroBase::gestureArtPlace, this, _1, _2));
 	for(auto artPlace : backpack)
 	{
 		artPlace->setArtifact(nullptr);
-		artPlace->leftClickCallback = std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1);
-		artPlace->showPopupCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1);
+		artPlace->setClickPressedCallback(std::bind(&CArtifactsOfHeroBase::clickPrassedArtPlace, this, _1, _2));
+		artPlace->setShowPopupCallback(std::bind(&CArtifactsOfHeroBase::showPopupArtPlace, this, _1, _2));
 	}
 	leftBackpackRoll->addCallback(std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, -1));
 	rightBackpackRoll->addCallback(std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, +1));
@@ -55,7 +56,7 @@ void CArtifactsOfHeroKingdom::swapArtifacts(const ArtifactLocation & srcLoc, con
 	LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc);
 }
 
-void CArtifactsOfHeroKingdom::pickUpArtifact(CHeroArtPlace & artPlace)
+void CArtifactsOfHeroKingdom::pickUpArtifact(CArtPlace & artPlace)
 {
 	LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, artPlace.slot),
 		ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS));

+ 1 - 1
client/widgets/CArtifactsOfHeroKingdom.h

@@ -24,5 +24,5 @@ public:
 		std::shared_ptr<CButton> leftScroll, std::shared_ptr<CButton> rightScroll);
 	~CArtifactsOfHeroKingdom();
 	void swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc);
-	void pickUpArtifact(CHeroArtPlace & artPlace);
+	void pickUpArtifact(CArtPlace & artPlace);
 };

+ 4 - 3
client/widgets/CArtifactsOfHeroMain.cpp

@@ -19,10 +19,11 @@
 CArtifactsOfHeroMain::CArtifactsOfHeroMain(const Point & position)
 {
 	init(
-		std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1),
-		std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1),
+		std::bind(&CArtifactsOfHeroBase::clickPrassedArtPlace, this, _1, _2),
+		std::bind(&CArtifactsOfHeroBase::showPopupArtPlace, this, _1, _2),
 		position,
 		std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, _1));
+	addGestureCallback(std::bind(&CArtifactsOfHeroBase::gestureArtPlace, this, _1, _2));
 }
 
 CArtifactsOfHeroMain::~CArtifactsOfHeroMain()
@@ -35,7 +36,7 @@ void CArtifactsOfHeroMain::swapArtifacts(const ArtifactLocation & srcLoc, const
 	LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc);
 }
 
-void CArtifactsOfHeroMain::pickUpArtifact(CHeroArtPlace & artPlace)
+void CArtifactsOfHeroMain::pickUpArtifact(CArtPlace & artPlace)
 {
 	LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, artPlace.slot),
 		ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS));

+ 1 - 1
client/widgets/CArtifactsOfHeroMain.h

@@ -23,5 +23,5 @@ public:
 	CArtifactsOfHeroMain(const Point & position);
 	~CArtifactsOfHeroMain();
 	void swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc);
-	void pickUpArtifact(CHeroArtPlace & artPlace);
+	void pickUpArtifact(CArtPlace & artPlace);
 };

+ 2 - 2
client/widgets/CArtifactsOfHeroMarket.cpp

@@ -15,8 +15,8 @@
 CArtifactsOfHeroMarket::CArtifactsOfHeroMarket(const Point & position)
 {
 	init(
-		std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1), 
-		std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1), 
+		std::bind(&CArtifactsOfHeroBase::clickPrassedArtPlace, this, _1, _2),
+		std::bind(&CArtifactsOfHeroBase::showPopupArtPlace, this, _1, _2),
 		position,
 		std::bind(&CArtifactsOfHeroMarket::scrollBackpack, this, _1));
 };

+ 1 - 1
client/widgets/CArtifactsOfHeroMarket.h

@@ -14,7 +14,7 @@
 class CArtifactsOfHeroMarket : public CArtifactsOfHeroBase
 {
 public:
-	std::function<void(CHeroArtPlace*)> selectArtCallback;
+	std::function<void(CArtPlace*)> selectArtCallback;
 
 	CArtifactsOfHeroMarket(const Point & position);
 	void scrollBackpack(int offset) override;

+ 46 - 12
client/widgets/CWindowWithArtifacts.cpp

@@ -23,6 +23,7 @@
 #include "../windows/CHeroWindow.h"
 #include "../windows/CSpellWindow.h"
 #include "../windows/GUIClasses.h"
+#include "../windows/CHeroBackpackWindow.h"
 #include "../CPlayerInterface.h"
 #include "../CGameInfo.h"
 
@@ -39,18 +40,19 @@ void CWindowWithArtifacts::addSet(CArtifactsOfHeroPtr artSet)
 
 void CWindowWithArtifacts::addSetAndCallbacks(CArtifactsOfHeroPtr artSet)
 {
-	CArtifactsOfHeroBase::PutBackPickedArtCallback artPutBackHandler = []() -> void
+	CArtifactsOfHeroBase::PutBackPickedArtCallback artPutBackFunctor = []() -> void
 	{
 		CCS->curh->dragAndDropCursor(nullptr);
 	};
 
 	addSet(artSet);
-	std::visit([this, artPutBackHandler](auto artSetWeak)
+	std::visit([this, artPutBackFunctor](auto artSetWeak)
 		{
 			auto artSet = artSetWeak.lock();
-			artSet->leftClickCallback = std::bind(&CWindowWithArtifacts::leftClickArtPlaceHero, this, _1, _2);
-			artSet->showPopupCallback = std::bind(&CWindowWithArtifacts::rightClickArtPlaceHero, this, _1, _2);
-			artSet->setPutBackPickedArtifactCallback(artPutBackHandler);
+			artSet->clickPressedCallback = std::bind(&CWindowWithArtifacts::clickPressedArtPlaceHero, this, _1, _2, _3);
+			artSet->showPopupCallback = std::bind(&CWindowWithArtifacts::showPopupArtPlaceHero, this, _1, _2, _3);
+			artSet->gestureCallback = std::bind(&CWindowWithArtifacts::gestureArtPlaceHero, this, _1, _2, _3);
+			artSet->setPutBackPickedArtifactCallback(artPutBackFunctor);
 		}, artSet);
 }
 
@@ -77,7 +79,7 @@ const CArtifactInstance * CWindowWithArtifacts::getPickedArtifact()
 		return nullptr;
 }
 
-void CWindowWithArtifacts::leftClickArtPlaceHero(CArtifactsOfHeroBase & artsInst, CHeroArtPlace & artPlace)
+void CWindowWithArtifacts::clickPressedArtPlaceHero(CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition)
 {
 	const auto artSetWeak = findAOHbyRef(artsInst);
 	assert(artSetWeak.has_value());
@@ -85,7 +87,7 @@ void CWindowWithArtifacts::leftClickArtPlaceHero(CArtifactsOfHeroBase & artsInst
 	if(artPlace.isLocked())
 		return;
 
-	const auto checkSpecialArts = [](const CGHeroInstance * hero, CHeroArtPlace & artPlace) -> bool
+	const auto checkSpecialArts = [](const CGHeroInstance * hero, CArtPlace & artPlace) -> bool
 	{
 		if(artPlace.getArt()->getTypeId() == ArtifactID::SPELLBOOK)
 		{
@@ -206,10 +208,18 @@ void CWindowWithArtifacts::leftClickArtPlaceHero(CArtifactsOfHeroBase & artsInst
 					}
 				}
 			}
+			else if constexpr(std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroQuickBackpack>>)
+			{
+				const auto hero = artSetPtr->getHero();
+				artSetPtr->swapArtifacts(ArtifactLocation(hero->id, artPlace.slot),
+					ArtifactLocation(hero->id, artSetPtr->getFilterSlot()));
+				if(closeCallback)
+					closeCallback();
+			}
 		}, artSetWeak.value());
 }
 
-void CWindowWithArtifacts::rightClickArtPlaceHero(CArtifactsOfHeroBase & artsInst, CHeroArtPlace & artPlace)
+void CWindowWithArtifacts::showPopupArtPlaceHero(CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition)
 {
 	const auto artSetWeak = findAOHbyRef(artsInst);
 	assert(artSetWeak.has_value());
@@ -218,7 +228,7 @@ void CWindowWithArtifacts::rightClickArtPlaceHero(CArtifactsOfHeroBase & artsIns
 		return;
 
 	std::visit(
-		[&artPlace](auto artSetWeak) -> void
+		[&artPlace, &cursorPosition](auto artSetWeak) -> void
 		{
 			const auto artSetPtr = artSetWeak.lock();
 
@@ -239,16 +249,40 @@ void CWindowWithArtifacts::rightClickArtPlaceHero(CArtifactsOfHeroBase & artsIns
 						return;
 					}
 					if(artPlace.text.size())
-						artPlace.LRClickableAreaWTextComp::showPopupWindow(GH.getCursorPosition());
+						artPlace.LRClickableAreaWTextComp::showPopupWindow(cursorPosition);
 				}
 			}
 			// Altar window, Market window right click handler
 			else if constexpr(
 				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroAltar>> ||
-				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMarket>>)
+				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMarket>> ||
+				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroQuickBackpack>>)
 			{
 				if(artPlace.getArt() && artPlace.text.size())
-					artPlace.LRClickableAreaWTextComp::showPopupWindow(GH.getCursorPosition());
+					artPlace.LRClickableAreaWTextComp::showPopupWindow(cursorPosition);
+			}
+		}, artSetWeak.value());
+}
+
+void CWindowWithArtifacts::gestureArtPlaceHero(CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition)
+{
+	const auto artSetWeak = findAOHbyRef(artsInst);
+	assert(artSetWeak.has_value());
+	if(artPlace.isLocked())
+		return;
+
+	std::visit(
+		[&artPlace, cursorPosition](auto artSetWeak) -> void
+		{
+			const auto artSetPtr = artSetWeak.lock();
+			if constexpr(
+				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMain>> ||
+				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroKingdom>>)
+			{
+				GH.windows().createAndPushWindow<CHeroQuickBackpackWindow>(artSetPtr->getHero(), artPlace.slot);
+				auto backpackWindow = GH.windows().topWindow<CHeroQuickBackpackWindow>();
+				backpackWindow->moveTo(cursorPosition - Point(1, 1));
+				backpackWindow->fitToScreen(15);
 			}
 		}, artSetWeak.value());
 }

+ 5 - 3
client/widgets/CWindowWithArtifacts.h

@@ -24,7 +24,8 @@ public:
 		std::weak_ptr<CArtifactsOfHeroAltar>,
 		std::weak_ptr<CArtifactsOfHeroKingdom>,
 		std::weak_ptr<CArtifactsOfHeroMain>,
-		std::weak_ptr<CArtifactsOfHeroBackpack>>;
+		std::weak_ptr<CArtifactsOfHeroBackpack>,
+		std::weak_ptr<CArtifactsOfHeroQuickBackpack>>;
 	using CloseCallback = std::function<void()>;
 
 	void addSet(CArtifactsOfHeroPtr artSet);
@@ -32,8 +33,9 @@ public:
 	void addCloseCallback(CloseCallback callback);
 	const CGHeroInstance * getHeroPickedArtifact();
 	const CArtifactInstance * getPickedArtifact();
-	void leftClickArtPlaceHero(CArtifactsOfHeroBase & artsInst, CHeroArtPlace & artPlace);
-	void rightClickArtPlaceHero(CArtifactsOfHeroBase & artsInst, CHeroArtPlace & artPlace);
+	void clickPressedArtPlaceHero(CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition);
+	void showPopupArtPlaceHero(CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition);
+	void gestureArtPlaceHero(CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition);
 
 	void artifactRemoved(const ArtifactLocation & artLoc) override;
 	void artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw) override;

+ 51 - 14
client/windows/CHeroBackpackWindow.cpp

@@ -22,25 +22,19 @@
 CHeroBackpackWindow::CHeroBackpackWindow(const CGHeroInstance * hero)
 	: CWindowObject((EOptions)0)
 {
-	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
+	OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
 
-	stretchedBackground = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), Rect(0, 0, 410, 425));
-
-	arts = std::make_shared<CArtifactsOfHeroBackpack>(Point(windowMargin, windowMargin));
-	arts->setHero(hero);
+	stretchedBackground = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), Rect(0, 0, 0, 0));
+	arts = std::make_shared<CArtifactsOfHeroBackpack>();
+	arts->moveBy(Point(windowMargin, windowMargin));
 	addSetAndCallbacks(arts);
-
+	arts->setHero(hero);
 	addCloseCallback(std::bind(&CHeroBackpackWindow::close, this));
-
 	quitButton = std::make_shared<CButton>(Point(), AnimationPath::builtin("IOKAY32.def"), CButton::tooltip(""), [this]() { close(); }, EShortcut::GLOBAL_RETURN);
-
-	stretchedBackground->pos.w = arts->pos.w + 2 * windowMargin;
-	stretchedBackground->pos.h = arts->pos.h + quitButton->pos.h + 3 * windowMargin;
-	pos.w = stretchedBackground->pos.w;
-	pos.h = stretchedBackground->pos.h;
+	pos.w = stretchedBackground->pos.w = arts->pos.w + 2 * windowMargin;
+	pos.h = stretchedBackground->pos.h = arts->pos.h + quitButton->pos.h + 3 * windowMargin;
+	quitButton->moveTo(Point(pos.x + pos.w / 2 - quitButton->pos.w / 2, pos.y + arts->pos.h + 2 * windowMargin));
 	center();
-
-	quitButton->moveBy(Point(GH.screenDimensions().x / 2 - quitButton->pos.w / 2 - quitButton->pos.x, arts->pos.h + 2 * windowMargin));
 }
 
 void CHeroBackpackWindow::showAll(Canvas & to)
@@ -48,3 +42,46 @@ void CHeroBackpackWindow::showAll(Canvas & to)
 	CIntObject::showAll(to);
 	CMessage::drawBorder(PlayerColor(LOCPLINT->playerID), to.getInternalSurface(), pos.w+28, pos.h+29, pos.x-14, pos.y-15);
 }
+
+CHeroQuickBackpackWindow::CHeroQuickBackpackWindow(const CGHeroInstance * hero, ArtifactPosition targetSlot)
+	: CWindowObject((EOptions)0)
+{
+	OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
+
+	stretchedBackground = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), Rect(0, 0, 0, 0));
+	arts = std::make_shared<CArtifactsOfHeroQuickBackpack>(targetSlot);
+	arts->moveBy(Point(windowMargin, windowMargin));
+	addSetAndCallbacks(static_cast<std::weak_ptr<CArtifactsOfHeroQuickBackpack>>(arts));
+	arts->setHero(hero);
+	addCloseCallback(std::bind(&CHeroQuickBackpackWindow::close, this));
+	addUsedEvents(GESTURE);
+	pos.w = stretchedBackground->pos.w = arts->pos.w + 2 * windowMargin;
+	pos.h = stretchedBackground->pos.h = arts->pos.h + windowMargin;
+}
+
+void CHeroQuickBackpackWindow::gesture(bool on, const Point & initialPosition, const Point & finalPosition)
+{
+	if(on)
+		return;
+
+	arts->swapSelected();
+	close();
+}
+
+void CHeroQuickBackpackWindow::gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance)
+{
+	arts->selectSlotAt(currentPosition);
+	redraw();
+}
+
+void CHeroQuickBackpackWindow::showAll(Canvas & to)
+{
+	if(arts->getSlotsNum() == 0)
+	{
+		// Dirty solution for closing that window
+		close();
+		return;
+	}
+	CMessage::drawBorder(PlayerColor(LOCPLINT->playerID), to.getInternalSurface(), pos.w + 28, pos.h + 29, pos.x - 14, pos.y - 15);
+	CIntObject::showAll(to);
+}

+ 17 - 2
client/windows/CHeroBackpackWindow.h

@@ -19,11 +19,26 @@ class CHeroBackpackWindow : public CWindowObject, public CWindowWithArtifacts
 public:
 	CHeroBackpackWindow(const CGHeroInstance * hero);
 	
-private:
+protected:
 	std::shared_ptr<CArtifactsOfHeroBackpack> arts;
 	std::shared_ptr<CButton> quitButton;
 	std::shared_ptr<CFilledTexture> stretchedBackground;
-	const int windowMargin = 10;
+	const int windowMargin = 5;
+
+	void showAll(Canvas & to) override;
+};
+
+class CHeroQuickBackpackWindow : public CWindowObject, public CWindowWithArtifacts
+{
+public:
+	CHeroQuickBackpackWindow(const CGHeroInstance * hero, ArtifactPosition targetSlot);
+	void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override;
+	void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override;
+
+private:
+	std::shared_ptr<CArtifactsOfHeroQuickBackpack> arts;
+	std::shared_ptr<CFilledTexture> stretchedBackground;
+	const int windowMargin = 5;
 
 	void showAll(Canvas & to) override;
 };

+ 0 - 1
client/windows/CHeroWindow.h

@@ -111,6 +111,5 @@ public:
 	void createBackpackWindow();
 
 	//friends
-	friend void CHeroArtPlace::clickPressed(const Point & cursorPosition);
 	friend class CPlayerInterface;
 };

+ 3 - 3
client/windows/CMapOverview.cpp

@@ -13,8 +13,6 @@
 
 #include "../lobby/SelectionTab.h"
 
-#include <vstd/DateUtils.h>
-
 #include "../gui/CGuiHandler.h"
 #include "../gui/WindowHandler.h"
 #include "../widgets/CComponent.h"
@@ -29,6 +27,7 @@
 #include "../render/Graphics.h"
 
 #include "../../lib/CGeneralTextHandler.h"
+#include "../../lib/TextOperations.h"
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/campaign/CampaignState.h"
 #include "../../lib/mapping/CMap.h"
@@ -42,6 +41,7 @@
 #include "../../lib/serializer/CLoadFile.h"
 #include "../../lib/StartInfo.h"
 #include "../../lib/rmg/CMapGenOptions.h"
+#include "../../lib/Languages.h"
 
 CMapOverview::CMapOverview(std::string mapName, std::string fileName, std::string date, ResourcePath resource, ESelectionScreen tabType)
 	: CWindowObject(BORDERED | RCLICK_POPUP), resource(resource), mapName(mapName), fileName(fileName), date(date), tabType(tabType)
@@ -199,7 +199,7 @@ CMapOverviewWidget::CMapOverviewWidget(CMapOverview& parent):
 		if(p.date.empty())
 		{
 			std::time_t time = boost::filesystem::last_write_time(*CResourceHandler::get()->getResourceName(ResourcePath(p.resource.getName(), p.tabType == ESelectionScreen::campaignList ? EResType::CAMPAIGN : EResType::MAP)));
-			w->setText(vstd::getFormattedDateTime(time));
+			w->setText(TextOperations::getFormattedDateTimeLocal(time));
 		}
 		else
 			w->setText(p.date);

+ 114 - 62
client/windows/CSpellWindow.cpp

@@ -124,70 +124,22 @@ CSpellWindow::CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _m
 		offL = offR = offT = offB = offRM = 0;
 		spellsPerPage = 12;
 	}
-	pos = background->center(Point(pos.w/2 + pos.x, pos.h/2 + pos.y));
-
-	//initializing castable spells
-	mySpells.reserve(CGI->spellh->objects.size());
-	for(const CSpell * spell : CGI->spellh->objects)
-	{
-		if(!spell->isCreatureAbility() && myHero->canCastThisSpell(spell))
-			mySpells.push_back(spell);
-	}
-	std::sort(mySpells.begin(), mySpells.end(), spellsorter);
-
-	//initializing sizes of spellbook's parts
-	for(auto & elem : sitesPerTabAdv)
-		elem = 0;
-	for(auto & elem : sitesPerTabBattle)
-		elem = 0;
-
-	for(const auto spell : mySpells)
-	{
-		int * sitesPerOurTab = spell->isCombat() ? sitesPerTabBattle : sitesPerTabAdv;
-
-		++sitesPerOurTab[4];
-
-		spell->forEachSchool([&sitesPerOurTab](const SpellSchool & school, bool & stop)
-		{
-			++sitesPerOurTab[school];
-		});
-	}
-	if(sitesPerTabAdv[4] % spellsPerPage == 0)
-		sitesPerTabAdv[4]/=spellsPerPage;
-	else
-		sitesPerTabAdv[4] = sitesPerTabAdv[4]/spellsPerPage + 1;
-
-	for(int v=0; v<4; ++v)
-	{
-		if(sitesPerTabAdv[v] <= spellsPerPage - 2)
-			sitesPerTabAdv[v] = 1;
-		else
-		{
-			if((sitesPerTabAdv[v] - spellsPerPage - 2) % spellsPerPage == 0)
-				sitesPerTabAdv[v] = (sitesPerTabAdv[v] - spellsPerPage - 2) / spellsPerPage + 1;
-			else
-				sitesPerTabAdv[v] = (sitesPerTabAdv[v] - spellsPerPage - 2) / spellsPerPage + 2;
-		}
-	}
 
-	if(sitesPerTabBattle[4] % spellsPerPage == 0)
-		sitesPerTabBattle[4]/=spellsPerPage;
-	else
-		sitesPerTabBattle[4] = sitesPerTabBattle[4]/spellsPerPage + 1;
+	pos = background->center(Point(pos.w/2 + pos.x, pos.h/2 + pos.y));
 
-	for(int v=0; v<4; ++v)
+	if(settings["general"]["enableUiEnhancements"].Bool())
 	{
-		if(sitesPerTabBattle[v] <= spellsPerPage - 2)
-			sitesPerTabBattle[v] = 1;
-		else
-		{
-			if((sitesPerTabBattle[v] - spellsPerPage - 2) % spellsPerPage == 0)
-				sitesPerTabBattle[v] = (sitesPerTabBattle[v] - spellsPerPage - 2) / spellsPerPage + 1;
-			else
-				sitesPerTabBattle[v] = (sitesPerTabBattle[v] - spellsPerPage - 2) / spellsPerPage + 2;
-		}
+		Rect r(90, isBigSpellbook ? 480 : 420, isBigSpellbook ? 160 : 110, 16);
+		const ColorRGBA rectangleColor = ColorRGBA(0, 0, 0, 75);
+		const ColorRGBA borderColor = ColorRGBA(128, 100, 75);
+		const ColorRGBA grayedColor = ColorRGBA(158, 130, 105);
+		searchBoxRectangle = std::make_shared<TransparentFilledRectangle>(r.resize(1), rectangleColor, borderColor);
+		searchBoxDescription = std::make_shared<CLabel>(r.center().x, r.center().y, FONT_SMALL, ETextAlignment::CENTER, grayedColor, CGI->generaltexth->translate("vcmi.spellBook.search"));
+
+		searchBox = std::make_shared<CTextInput>(r, FONT_SMALL, std::bind(&CSpellWindow::searchInput, this));
 	}
 
+	processSpells();
 
 	//numbers of spell pages computed
 
@@ -253,7 +205,7 @@ CSpellWindow::CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _m
 
 	selectedTab = battleSpellsOnly ? myInt->localState->spellbookSettings.spellbookLastTabBattle : myInt->localState->spellbookSettings.spellbookLastTabAdvmap;
 	schoolTab->setFrame(selectedTab, 0);
-	int cp = battleSpellsOnly ? myInt->localState->spellbookSettings.spellbookLastPageBattle : myInt->localState->spellbookSettings.spellbokLastPageAdvmap;
+	int cp = battleSpellsOnly ? myInt->localState->spellbookSettings.spellbookLastPageBattle : myInt->localState->spellbookSettings.spellbookLastPageAdvmap;
 	// spellbook last page battle index is not reset after battle, so this needs to stay here
 	vstd::abetween(cp, 0, std::max(0, pagesWithinCurrentTab() - 1));
 	setCurrentPage(cp);
@@ -292,14 +244,114 @@ std::shared_ptr<IImage> CSpellWindow::createBigSpellBook()
 	Canvas tmp5 = Canvas(Point(409, 141));
 	tmp5.draw(img, Point(0, 0), Rect(100, 38 + 45, 509 - 15, 400 - 38));
 	canvas.drawScaled(tmp5, Point(90, 45), Point(615, 415));
+	// carpet
+	Canvas tmp6 = Canvas(Point(590, 59));
+	tmp6.draw(img, Point(0, 0), Rect(15, 484, 590, 59));
+	canvas.drawScaled(tmp6, Point(0, 545), Point(800, 59));
+	// remove bookmarks
+	for (int i = 0; i < 56; i++)
+		canvas.draw(Canvas(canvas, Rect(i < 30 ? 268 : 327, 464, 1, 46)), Point(269 + i, 464));
+	for (int i = 0; i < 56; i++)
+		canvas.draw(Canvas(canvas, Rect(469, 464, 1, 42)), Point(470 + i, 464));
+	for (int i = 0; i < 57; i++)
+		canvas.draw(Canvas(canvas, Rect(i < 30 ? 564 : 630, 464, 1, 44)), Point(565 + i, 464));
+	for (int i = 0; i < 56; i++)
+		canvas.draw(Canvas(canvas, Rect(656, 464, 1, 47)), Point(657 + i, 464));
+	// draw bookmarks
+	canvas.draw(img, Point(278, 464), Rect(220, 405, 37, 47));
+	canvas.draw(img, Point(481, 465), Rect(354, 406, 37, 41));
+	canvas.draw(img, Point(575, 465), Rect(417, 406, 37, 45));
+	canvas.draw(img, Point(667, 465), Rect(478, 406, 37, 47));
 
 	return GH.renderHandler().createImage(canvas.getInternalSurface());
 }
 
+void CSpellWindow::searchInput()
+{
+	if(searchBox)
+		searchBoxDescription->setEnabled(searchBox->getText().empty());
+
+	processSpells();
+
+	int cp = 0;
+	// spellbook last page battle index is not reset after battle, so this needs to stay here
+	vstd::abetween(cp, 0, std::max(0, pagesWithinCurrentTab() - 1));
+	setCurrentPage(cp);
+	computeSpellsPerArea();
+}
+
+void CSpellWindow::processSpells()
+{
+	mySpells.clear();
+
+	//initializing castable spells
+	mySpells.reserve(CGI->spellh->objects.size());
+	for(const CSpell * spell : CGI->spellh->objects)
+	{
+		bool searchTextFound = !searchBox || boost::algorithm::contains(boost::algorithm::to_lower_copy(spell->getNameTranslated()), boost::algorithm::to_lower_copy(searchBox->getText()));
+		if(!spell->isCreatureAbility() && myHero->canCastThisSpell(spell) && searchTextFound)
+			mySpells.push_back(spell);
+	}
+	std::sort(mySpells.begin(), mySpells.end(), spellsorter);
+
+	//initializing sizes of spellbook's parts
+	for(auto & elem : sitesPerTabAdv)
+		elem = 0;
+	for(auto & elem : sitesPerTabBattle)
+		elem = 0;
+
+	for(const auto spell : mySpells)
+	{
+		int * sitesPerOurTab = spell->isCombat() ? sitesPerTabBattle : sitesPerTabAdv;
+
+		++sitesPerOurTab[4];
+
+		spell->forEachSchool([&sitesPerOurTab](const SpellSchool & school, bool & stop)
+		{
+			++sitesPerOurTab[school];
+		});
+	}
+	if(sitesPerTabAdv[4] % spellsPerPage == 0)
+		sitesPerTabAdv[4]/=spellsPerPage;
+	else
+		sitesPerTabAdv[4] = sitesPerTabAdv[4]/spellsPerPage + 1;
+
+	for(int v=0; v<4; ++v)
+	{
+		if(sitesPerTabAdv[v] <= spellsPerPage - 2)
+			sitesPerTabAdv[v] = 1;
+		else
+		{
+			if((sitesPerTabAdv[v] - spellsPerPage - 2) % spellsPerPage == 0)
+				sitesPerTabAdv[v] = (sitesPerTabAdv[v] - spellsPerPage - 2) / spellsPerPage + 1;
+			else
+				sitesPerTabAdv[v] = (sitesPerTabAdv[v] - spellsPerPage - 2) / spellsPerPage + 2;
+		}
+	}
+
+	if(sitesPerTabBattle[4] % spellsPerPage == 0)
+		sitesPerTabBattle[4]/=spellsPerPage;
+	else
+		sitesPerTabBattle[4] = sitesPerTabBattle[4]/spellsPerPage + 1;
+
+	for(int v=0; v<4; ++v)
+	{
+		if(sitesPerTabBattle[v] <= spellsPerPage - 2)
+			sitesPerTabBattle[v] = 1;
+		else
+		{
+			if((sitesPerTabBattle[v] - spellsPerPage - 2) % spellsPerPage == 0)
+				sitesPerTabBattle[v] = (sitesPerTabBattle[v] - spellsPerPage - 2) / spellsPerPage + 1;
+			else
+				sitesPerTabBattle[v] = (sitesPerTabBattle[v] - spellsPerPage - 2) / spellsPerPage + 2;
+		}
+	}
+}
+
 void CSpellWindow::fexitb()
 {
 	(myInt->battleInt ? myInt->localState->spellbookSettings.spellbookLastTabBattle : myInt->localState->spellbookSettings.spellbookLastTabAdvmap) = selectedTab;
-	(myInt->battleInt ? myInt->localState->spellbookSettings.spellbookLastPageBattle : myInt->localState->spellbookSettings.spellbokLastPageAdvmap) = currentPage;
+	(myInt->battleInt ? myInt->localState->spellbookSettings.spellbookLastPageBattle : myInt->localState->spellbookSettings.spellbookLastPageAdvmap) = currentPage;
 
 	close();
 }
@@ -595,7 +647,7 @@ void CSpellWindow::SpellArea::clickPressed(const Point & cursorPosition)
 			auto guard = vstd::makeScopeGuard([this]()
 			{
 				owner->myInt->localState->spellbookSettings.spellbookLastTabAdvmap = owner->selectedTab;
-				owner->myInt->localState->spellbookSettings.spellbokLastPageAdvmap = owner->currentPage;
+				owner->myInt->localState->spellbookSettings.spellbookLastPageAdvmap = owner->currentPage;
 			});
 
 			if(mySpell->getTargetType() == spells::AimType::LOCATION)

+ 8 - 0
client/windows/CSpellWindow.h

@@ -26,6 +26,8 @@ class CLabel;
 class CGStatusBar;
 class CPlayerInterface;
 class CSpellWindow;
+class CTextInput;
+class TransparentFilledRectangle;
 
 /// The spell window
 class CSpellWindow : public CWindowObject
@@ -80,6 +82,10 @@ class CSpellWindow : public CWindowObject
 
 	std::vector<std::shared_ptr<InteractiveArea>> interactiveAreas;
 
+	std::shared_ptr<CTextInput> searchBox;
+	std::shared_ptr<TransparentFilledRectangle> searchBoxRectangle;
+	std::shared_ptr<CLabel> searchBoxDescription;
+
 	bool isBigSpellbook;
 	int spellsPerPage;
 	int offL;
@@ -99,6 +105,8 @@ class CSpellWindow : public CWindowObject
 	const CGHeroInstance * myHero; //hero whose spells are presented
 	CPlayerInterface * myInt;
 
+	void processSpells();
+	void searchInput();
 	void computeSpellsPerArea(); //recalculates spellAreas::mySpell
 
 	void setCurrentPage(int value);

+ 1 - 1
client/windows/CTradeWindow.cpp

@@ -331,7 +331,7 @@ void CTradeWindow::setMode(EMarketMode Mode)
 	}
 }
 
-void CTradeWindow::artifactSelected(CHeroArtPlace *slot)
+void CTradeWindow::artifactSelected(CArtPlace * slot)
 {
 	assert(mode == EMarketMode::ARTIFACT_RESOURCE);
 	items[1][0]->setArtInstance(slot->getArt());

+ 1 - 1
client/windows/CTradeWindow.h

@@ -40,7 +40,7 @@ public:
 	void getPositionsFor(std::vector<Rect> &poss, bool Left, EType type) const;
 	void setMode(EMarketMode Mode); //mode setter
 
-	void artifactSelected(CHeroArtPlace *slot); //used when selling artifacts -> called when user clicked on artifact slot
+	void artifactSelected(CArtPlace * slot); //used when selling artifacts -> called when user clicked on artifact slot
 	virtual void selectionChanged(bool side) = 0; //true == left
 	virtual Point selectionOffset(bool Left) const = 0;
 	virtual std::string updateSlotSubtitle(bool Left) const = 0;

+ 1 - 1
cmake_modules/VersionDefinition.cmake

@@ -1,6 +1,6 @@
 set(VCMI_VERSION_MAJOR 1)
 set(VCMI_VERSION_MINOR 4)
-set(VCMI_VERSION_PATCH 1)
+set(VCMI_VERSION_PATCH 2)
 add_definitions(
 	-DVCMI_VERSION_MAJOR=${VCMI_VERSION_MAJOR}
 	-DVCMI_VERSION_MINOR=${VCMI_VERSION_MINOR}

+ 1 - 1
config/schemas/settings.json

@@ -615,7 +615,7 @@
 				},
 				"infoBarCreatureManagement": {
 					"type" : "boolean",
-					"default" : false
+					"default" : true
 				},
 				"enableLargeSpellbook" : {
 					"type": "boolean",

+ 1 - 1
config/spells/timed.json

@@ -1221,7 +1221,7 @@
 				"effects" : {
 					"attacksNearestCreature" : {
 						"type" : "ATTACKS_NEAREST_CREATURE",
-						"duration" : "UNTIL_ATTACK"
+						"duration" : "UNTIL_OWN_ATTACK"
 					}
 				}
 			},

+ 6 - 0
debian/changelog

@@ -1,3 +1,9 @@
+vcmi (1.4.2) jammy; urgency=medium
+
+  * New upstream release
+
+ -- Ivan Savenko <[email protected]>  Fri, 22 Dec 2023 16:00:00 +0200
+
 vcmi (1.4.1) jammy; urgency=medium
 
   * New upstream release

+ 2 - 1
docs/modders/Bonus/Bonus_Duration_Types.md

@@ -13,4 +13,5 @@ Bonus may have any of these durations. They acts in disjunction.
 -   UNTIL_BEING_ATTACKED: removed after any damage-inflicting attack
 -   UNTIL_ATTACK: removed after attack and counterattacks are performed
 -   STACK_GETS_TURN: removed when stack gets its turn - used for defensive stance
--   COMMANDER_KILLED
+-   COMMANDER_KILLED
+-   UNTIL_OWN_ATTACK: removed after attack (not counterattack) is performed

+ 15 - 3
docs/players/Cheat_Codes.md

@@ -36,7 +36,7 @@ Gives specific creature in every slot, with optional amount. Examples:
 
 ### Movement points
 
-`nwcnebuchadnezzar` or `vcminahar` or `vcmimove` - give 1000000 movement points and free ship boarding for 1 day  
+`nwcnebuchadnezzar` or `vcminahar` or `vcmimove` - give unlimited (or specified amount of) movement points and free ship boarding
 Alternative usage: `vcmimove <amount>` - gives specified amount of movement points
 
 ### Resources
@@ -57,10 +57,22 @@ Alternative usage: `vcmilevel <amount>` - advances hero by specified number of l
 - `vcmiolorin` or `vcmiexp` - gives selected hero 10000 experience
 Alternative usage: `vcmiexp <amount>` - gives selected hero specified amount of experience
 
+### Luck and morale
+
+`nwcfollowthewhiterabbit` or `vcmiluck` - the currently selected hero permanently gains maximum luck
+`nwcmorpheus` or `vcmimorale` - the currently selected hero permanently gains maximum morale
+
+### Puzzle map
+
+`nwcoracle` or `vcmiobelisk` - reveals the puzzle map
+
 ### Finishing the game
 
-`nwcredpill` or `vcmisilmaril` or `vcmiwin` - player wins  
-`nwcbluepill` or `vcmimelkor` or `vcmilose` - player loses  
+`nwcredpill` or `vcmisilmaril` or `vcmiwin` - player wins
+`nwcbluepill` or `vcmimelkor` or `vcmilose` - player loses
+
+### Misc
+`nwctheone` or `vcmigod` - reveals the whole map, gives 5 archangels in each empty slot, unlimited movement points and permanent flight
 
 ## Using cheat codes on other players
 By default, all cheat codes apply to current player. Alternatively, it is possible to specify player that you want to target:

+ 1 - 1
include/vstd/DateUtils.h

@@ -5,7 +5,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 namespace vstd
 {
 
-	DLL_LINKAGE std::string getFormattedDateTime(std::time_t dt);
+	DLL_LINKAGE std::string getFormattedDateTime(std::time_t dt, std::string format);
 	DLL_LINKAGE std::string getDateTimeISO8601Basic(std::time_t dt);
 
 }

+ 61 - 37
launcher/eu.vcmi.VCMI.metainfo.xml

@@ -1,56 +1,94 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<component type="desktop-application">
+<?xml version='1.0' encoding='utf-8'?>
+<component type="desktop">
 	<id>eu.vcmi.VCMI</id>
-	<metadata_license>CC-BY-SA-4.0</metadata_license>
-	<project_license>GPL-2.0-or-later</project_license>
 	<name>VCMI</name>
 	<summary>Open-source game engine for Heroes of Might and Magic III</summary>
+	<summary xml:lang="cs">Herní engine s otevřeným kódem pro Heroes of Might and Magic III</summary>
+	<summary xml:lang="de">Open-Source-Spielengine für Heroes of Might and Magic III</summary>
+	<developer_name>VCMI Team</developer_name>
+	<developer_name xml:lang="cs">Tým VCMI</developer_name>
+	<metadata_license>CC-BY-SA-4.0</metadata_license>
+	<project_license>GPL-2.0-or-later</project_license>
 	<description>
 		<p>VCMI is an open-source engine for Heroes of Might and Magic III with new possibilities. Years of intensive work resulted in an impressive amount of features. Among the current features are:</p>
+		<p xml:lang="cs">VCMI je engine s otevřeným kódem a novými možnostmi pro Heroes of Might and Magic III. Roky usilovné práce vyústily v úchvatném počtu nových funkcí. Mezi současnými funkcemi jsou:</p>
+		<p xml:lang="de">VCMI ist eine Open-Source-Engine für Heroes of Might and Magic III mit neuen Möglichkeiten. Jahrelange intensive Arbeit führte zu einer beeindruckenden Anzahl von Features. Zu den aktuellen Features gehören:</p>
 		<ul>
 			<li>Complete gameplay mechanics</li>
+			<li xml:lang="cs">Kompletní herní mechaniky</li>
+			<li xml:lang="de">Vollständige Spielmechanik</li>
 			<li>Almost all objects, abilities, spells and other content</li>
+			<li xml:lang="cs">Skoro všechny předměty, schopnosti, kouzla a ostatní obsah</li>
+			<li xml:lang="de">Fast alle Objekte, Fähigkeiten, Zaubersprüche und andere Inhalte</li>
 			<li>Basic battle AI and adventure AI</li>
+			<li xml:lang="cs">Základní AI boje a mapy světa</li>
+			<li xml:lang="de">Grundlegende Kampf- und Abenteuer-KI</li>
 			<li>Many GUI improvements: high resolutions, stack queue, creature window</li>
+			<li xml:lang="cs">Mnoho vylepšení rozhraní: vyšší rozlišení, fronta oddílů a okno bojovníků</li>
+			<li xml:lang="de">Viele GUI-Verbesserungen: Hohe Auflösungen, Truppenwarteschlange, Kreaturenfenster</li>
 			<li>Advanced and easy mod support - add new towns, creatures, heroes, artifacts and spells without limits or conflicts</li>
+			<li xml:lang="cs">Pokročilá a jednoduchá podpora modifikací - přidání nových měst, bojovníků, hrdinů, artefaktů a kouzel bez limitů a konfliktů</li>
+			<li xml:lang="de">Erweiterte und einfache Mod-Unterstützung - füge neue Städte, Kreaturen, Helden, Artefakte und Zaubersprüche ohne Einschränkungen oder Konflikte hinzu</li>
 			<li>Launcher for easy configuration - download mods from our server and install them immediately!</li>
+			<li xml:lang="cs">Spouštěč pro jednoduché nastavení - stahujte modifikace z našeho serveru a hned je instalujte!</li>
+			<li xml:lang="de">Launcher für einfache Konfiguration - Mods von unserem Server herunterladen und sofort installieren!</li>
 			<li>Random map generator that supports objects added by mods</li>
+			<li xml:lang="cs">Náhodný generátor map, který podporuje předměty přidané modifikacemi</li>
+			<li xml:lang="de">Zufallsgenerator für Karten, der von Mods hinzugefügte Objekte unterstützt</li>
 		</ul>
 		<p>Note: In order to play the game using VCMI you need to own data files for Heroes of Might and Magic III: The Shadow of Death.</p>
+		<p xml:lang="cs">Poznámka: pokud chcete hrát hru přes VCMI, musíte vlastnit datové soubory Heroes of Might and Magic III: The Shadow of Death.</p>
+		<p xml:lang="de">Hinweis: Um das Spiel mit VCMI spielen zu können, sind die Originaldateien für Heroes of Might and Magic III: The Shadow of Death erforderlich.</p>
 		<p>If you want help, please check our forum, bug tracker or GitHub page.</p>
+		<p xml:lang="cs">Pokud chcete pomoct, prosíme, podívejte se na naše fórum nebo GitHub.</p>
+		<p xml:lang="de">Wird Hilfe benötigt, besucht bitte unser Forum, den Bugtracker oder die GitHub-Seite.</p>
 	</description>
 	<screenshots>
 		<screenshot type="default">
-			<image>https://play-lh.googleusercontent.com/-ZErg8hUyc3TScVQZYh30KzaUDWmnIGRcN3WPCQimdviOJVoMkjDdIW19WqX0KZO7mk=w2560-h1440</image>
+			<image type="source">https://play-lh.googleusercontent.com/-ZErg8hUyc3TScVQZYh30KzaUDWmnIGRcN3WPCQimdviOJVoMkjDdIW19WqX0KZO7mk=w2560-h1440</image>
 		</screenshot>
 		<screenshot>
-			<image>https://play-lh.googleusercontent.com/8Uuuir7_0pDCPohacS7nd6CQuvaOO7nKmKWo-A-EysRY8uIK5qLsv0cpMxT3AFu2DA=w2560-h1440</image>
+			<image type="source">https://play-lh.googleusercontent.com/8Uuuir7_0pDCPohacS7nd6CQuvaOO7nKmKWo-A-EysRY8uIK5qLsv0cpMxT3AFu2DA=w2560-h1440</image>
 		</screenshot>
 		<screenshot>
-			<image>https://play-lh.googleusercontent.com/993tSmsQh1qgb1_XQUxD1wmS8Zuiydf0LsQK_nU2fAiJl62n0_Vl_5JMVK-J9lvVeQ=w2560-h1440</image>
+			<image type="source">https://play-lh.googleusercontent.com/993tSmsQh1qgb1_XQUxD1wmS8Zuiydf0LsQK_nU2fAiJl62n0_Vl_5JMVK-J9lvVeQ=w2560-h1440</image>
 		</screenshot>
 		<screenshot>
-			<image>https://play-lh.googleusercontent.com/N3fn4cqX2iN0t9jzEo0vgEMBpUYWyRgOz8AtDsMLmF-BTyxO4l2uoAderEIhXPvPsg=w2560-h1440</image>
+			<image type="source">https://play-lh.googleusercontent.com/N3fn4cqX2iN0t9jzEo0vgEMBpUYWyRgOz8AtDsMLmF-BTyxO4l2uoAderEIhXPvPsg=w2560-h1440</image>
 		</screenshot>
 		<screenshot>
-			<image>https://play-lh.googleusercontent.com/9wSQsPlMPSBAzekEcw1OVpzeOvYiHrYRYHgpa1aRk31pvR752gu_cUZws5x8JMCACw=w2560-h1440</image>
+			<image type="source">https://play-lh.googleusercontent.com/9wSQsPlMPSBAzekEcw1OVpzeOvYiHrYRYHgpa1aRk31pvR752gu_cUZws5x8JMCACw=w2560-h1440</image>
 		</screenshot>
 		<screenshot>
-			<image>https://play-lh.googleusercontent.com/nU_MSwmEhiQ14Sx9xI3QFF1FhL7BYTDMbEYQg-XglSYwIMpYZDpP92_ybTtu4cVJSxo=w2560-h1440</image>
+			<image type="source">https://play-lh.googleusercontent.com/nU_MSwmEhiQ14Sx9xI3QFF1FhL7BYTDMbEYQg-XglSYwIMpYZDpP92_ybTtu4cVJSxo=w2560-h1440</image>
 		</screenshot>
 		<screenshot>
-			<image>https://play-lh.googleusercontent.com/rhI5moPCKQpYdjnRaJJ_CdIxSAXUkrH7ddghuPWaW0vcyaltd-ZGZG4Kl6v3YKGVlzU=w2560-h1440</image>
+			<image type="source">https://play-lh.googleusercontent.com/rhI5moPCKQpYdjnRaJJ_CdIxSAXUkrH7ddghuPWaW0vcyaltd-ZGZG4Kl6v3YKGVlzU=w2560-h1440</image>
 		</screenshot>
 		<screenshot>
-			<image>https://play-lh.googleusercontent.com/1zecL5SvayVkNoIHijeS_rm0Yr_XyHbp_dmSx70NXhjckvnezwvWkpINYBHAy4o8EaM=w2560-h1440</image>
+			<image type="source">https://play-lh.googleusercontent.com/1zecL5SvayVkNoIHijeS_rm0Yr_XyHbp_dmSx70NXhjckvnezwvWkpINYBHAy4o8EaM=w2560-h1440</image>
 		</screenshot>
 		<screenshot>
-			<image>https://play-lh.googleusercontent.com/e5ibvuWm442dYN2z_pAwDC3Ktc7XOY6FEI4N7BkFB7DqGi3Q-Vl46KqoJ_EFSrXRNw=w2560-h1440</image>
+			<image type="source">https://play-lh.googleusercontent.com/e5ibvuWm442dYN2z_pAwDC3Ktc7XOY6FEI4N7BkFB7DqGi3Q-Vl46KqoJ_EFSrXRNw=w2560-h1440</image>
 		</screenshot>
 		<screenshot>
-			<image>https://play-lh.googleusercontent.com/wD6xJK2nuzVyKUFODfQs2SEbUuSyEglojpp_FE6GeAuzdA_R44Dp6gTyN70KCwpRtD9M=w2560-h1440</image>
+			<image type="source">https://play-lh.googleusercontent.com/wD6xJK2nuzVyKUFODfQs2SEbUuSyEglojpp_FE6GeAuzdA_R44Dp6gTyN70KCwpRtD9M=w2560-h1440</image>
 		</screenshot>
 	</screenshots>
+	<releases>
+		<release version="1.4.2" date="2023-12-22" type="stable"/>
+		<release version="1.4.1" date="2023-12-12" type="stable"/>
+		<release version="1.4.0" date="2023-12-08" type="stable"/>
+		<release version="1.3.2" date="2023-09-15" type="stable"/>
+		<release version="1.3.1" date="2023-08-18" type="stable"/>
+		<release version="1.3.0" date="2023-08-04" type="stable"/>
+		<release version="1.2.1" date="2023-04-28" type="stable"/>
+		<release version="1.2.0" date="2023-04-14" type="stable"/>
+		<release version="1.1.1" date="2023-02-03" type="stable"/>
+		<release version="1.1.0" date="2022-12-23" type="stable"/>
+		<release version="1.0.0" date="2022-09-11" type="stable"/>
+		<release version="0.99" date="2016-11-01" type="stable"/>
+	</releases>
 	<url type="homepage">https://vcmi.eu/</url>
 	<url type="bugtracker">https://github.com/vcmi/vcmi/issues</url>
 	<url type="faq">https://vcmi.eu/faq/</url>
@@ -58,33 +96,15 @@
 	<url type="translate">https://github.com/vcmi/vcmi/blob/master/docs/modders/Translations.md</url>
 	<url type="contact">https://discord.gg/chBT42V</url>
 	<url type="vcs-browser">https://github.com/vcmi/vcmi</url>
-	<recommends>
-		<control>keyboard</control>
-		<control>pointing</control>
-		<control>touch</control>
-	</recommends>
 	<categories>
 		<category>Game</category>
 		<category>StrategyGame</category>
 	</categories>
-	<releases>
-		<release version="1.4.1" date="2023-12-12" />
-		<release version="1.4.0" date="2023-12-08" />
-		<release version="1.3.2" date="2023-09-15" />
-		<release version="1.3.1" date="2023-08-18" />
-		<release version="1.3.0" date="2023-08-04" />
-		<release version="1.2.1" date="2023-04-28" />
-		<release version="1.2.0" date="2023-04-14" />
-		<release version="1.1.1" date="2023-02-03" />
-		<release version="1.1.0" date="2022-12-23" />
-		<release version="1.0.0" date="2022-09-11" />
-		<release version="0.99" date="2016-11-01" />
-	</releases>
-	<keywords>
-		<keyword translate="no">heroes3</keyword>
-		<keyword translate="no">homm3</keyword>
-	</keywords>
-	<developer_name>VCMI Team</developer_name>
+	<recommends>
+		<control>pointing</control>
+		<control>keyboard</control>
+		<control>touch</control>
+	</recommends>
 	<content_rating type="oars-1.1">
 		<content_attribute id="violence-cartoon">moderate</content_attribute>
 		<content_attribute id="violence-fantasy">moderate</content_attribute>
@@ -98,4 +118,8 @@
 		<binary>vcmilauncher</binary>
 		<binary>vcmiserver</binary>
 	</provides>
+	<keywords>
+		<keyword>heroes3</keyword>
+		<keyword>homm3</keyword>
+	</keywords>
 </component>

+ 7 - 7
launcher/mainwindow_moc.cpp

@@ -136,14 +136,14 @@ void MainWindow::detectPreferredLanguage()
 		for (auto const & vcmiLang : Languages::getLanguageList())
 			if (vcmiLang.tagIETF == userLang.toStdString())
 				selectedLanguage = vcmiLang.identifier;
-	}
-
-	logGlobal->info("Selected language: %s", selectedLanguage);
 
-	if (!selectedLanguage.empty())
-	{
-		Settings node = settings.write["general"]["language"];
-		node->String() = selectedLanguage;
+		if (!selectedLanguage.empty())
+		{
+			logGlobal->info("Selected language: %s", selectedLanguage);
+			Settings node = settings.write["general"]["language"];
+			node->String() = selectedLanguage;
+			return;
+		}
 	}
 }
 

+ 9 - 1
launcher/modManager/cmodlistview_moc.cpp

@@ -591,7 +591,7 @@ void CModListView::downloadFile(QString file, QString url, QString description,
 			this, SLOT(downloadFinished(QStringList,QStringList,QStringList)));
 		
 		connect(manager.get(), SIGNAL(extractionProgress(qint64,qint64)),
-			this, SLOT(downloadProgress(qint64,qint64)));
+			this, SLOT(extractionProgress(qint64,qint64)));
 		
 		connect(modModel, &CModListModel::dataChanged, filterModel, &QAbstractItemModel::dataChanged);
 
@@ -613,6 +613,14 @@ void CModListView::downloadProgress(qint64 current, qint64 max)
 	ui->progressBar->setValue(current / (1024 * 1024));
 }
 
+void CModListView::extractionProgress(qint64 current, qint64 max)
+{
+	// display progress, in extracted files
+	ui->progressBar->setVisible(true);
+	ui->progressBar->setMaximum(max);
+	ui->progressBar->setValue(current);
+}
+
 void CModListView::downloadFinished(QStringList savedFiles, QStringList failedFiles, QStringList errors)
 {
 	QString title = tr("Download failed");

+ 1 - 0
launcher/modManager/cmodlistview_moc.h

@@ -98,6 +98,7 @@ private slots:
 	void dataChanged(const QModelIndex & topleft, const QModelIndex & bottomRight);
 	void modSelected(const QModelIndex & current, const QModelIndex & previous);
 	void downloadProgress(qint64 current, qint64 max);
+	void extractionProgress(qint64 current, qint64 max);
 	void downloadFinished(QStringList savedFiles, QStringList failedFiles, QStringList errors);
 	void modelReset();
 	void hideProgressBar();

+ 16 - 5
launcher/modManager/cmodmanager.cpp

@@ -24,7 +24,9 @@ namespace
 {
 QString detectModArchive(QString path, QString modName, std::vector<std::string> & filesToExtract)
 {
-	filesToExtract = ZipArchive::listFiles(qstringToPath(path));
+	ZipArchive archive(qstringToPath(path));
+
+	filesToExtract = archive.listFiles();
 
 	QString modDirName;
 
@@ -285,14 +287,23 @@ bool CModManager::doInstallMod(QString modname, QString archivePath)
 	if(!modDirName.size())
 		return addError(modname, "Mod archive is invalid or corrupted");
 	
-	auto futureExtract = std::async(std::launch::async, [&archivePath, &destDir, &filesToExtract]()
+	std::atomic<int> filesCounter = 0;
+
+	auto futureExtract = std::async(std::launch::async, [&archivePath, &destDir, &filesCounter, &filesToExtract]()
 	{
-		return ZipArchive::extract(qstringToPath(archivePath), qstringToPath(destDir), filesToExtract);
+		ZipArchive archive(qstringToPath(archivePath));
+		for (auto const & file : filesToExtract)
+		{
+			if (!archive.extract(qstringToPath(destDir), file))
+				return false;
+			++filesCounter;
+		}
+		return true;
 	});
 	
-	while(futureExtract.wait_for(std::chrono::milliseconds(50)) != std::future_status::ready)
+	while(futureExtract.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready)
 	{
-		emit extractionProgress(0, 0);
+		emit extractionProgress(filesCounter, filesToExtract.size());
 		qApp->processEvents();
 	}
 	

+ 1207 - 0
launcher/translation/czech.ts

@@ -0,0 +1,1207 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="cs_CZ">
+<context>
+    <name>AboutProjectView</name>
+    <message>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="22"/>
+        <source>VCMI on Discord</source>
+        <translation>VCMI na Discordu</translation>
+    </message>
+    <message>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="29"/>
+        <source>Have a question? Found a bug? Want to help? Join us!</source>
+        <translation>Máte otázku? Našli jste chybu? Chcete pomoct? Připojte se k nám!</translation>
+    </message>
+    <message>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="36"/>
+        <source>VCMI on Github</source>
+        <translation>VCMI na GitHubu</translation>
+    </message>
+    <message>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="55"/>
+        <source>Our Community</source>
+        <translation>Naše komunita</translation>
+    </message>
+    <message>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="62"/>
+        <source>VCMI on Slack</source>
+        <translation>VCMI na Slacku</translation>
+    </message>
+    <message>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="98"/>
+        <source>Build Information</source>
+        <translation>Informace o sestavení</translation>
+    </message>
+    <message>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="115"/>
+        <source>User data directory</source>
+        <translation>Složka uživatelských dat</translation>
+    </message>
+    <message>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="122"/>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="129"/>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="193"/>
+        <source>Open</source>
+        <translation>Otevřít</translation>
+    </message>
+    <message>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="136"/>
+        <source>Check for updates</source>
+        <translation>Zkontrolovat aktualizace</translation>
+    </message>
+    <message>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="156"/>
+        <source>Game version</source>
+        <translation>Verze hry</translation>
+    </message>
+    <message>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="163"/>
+        <source>Log files directory</source>
+        <translation>Složka záznamů hry</translation>
+    </message>
+    <message>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="176"/>
+        <source>Data Directories</source>
+        <translation>Složky dat</translation>
+    </message>
+    <message>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="213"/>
+        <source>Game data directory</source>
+        <translation>Složka herních dat</translation>
+    </message>
+    <message>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="220"/>
+        <source>Operating System</source>
+        <translation>Operační systém</translation>
+    </message>
+    <message>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="273"/>
+        <source>Project homepage</source>
+        <translation>Domovská stránka projektu</translation>
+    </message>
+    <message>
+        <location filename="../aboutProject/aboutproject_moc.ui" line="286"/>
+        <source>Report a bug</source>
+        <translation>Nahlásit chybu</translation>
+    </message>
+</context>
+<context>
+    <name>CModListModel</name>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="42"/>
+        <source>Translation</source>
+        <translation>Překlad</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="43"/>
+        <source>Town</source>
+        <translation>Město</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="44"/>
+        <source>Test</source>
+        <translation>Zkouška</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="45"/>
+        <source>Templates</source>
+        <translation>Šablony</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="46"/>
+        <source>Spells</source>
+        <translation>Kouzla</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="47"/>
+        <source>Music</source>
+        <translation>Hudba</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="48"/>
+        <source>Maps</source>
+        <translation>Mapy</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="49"/>
+        <source>Sounds</source>
+        <translation>Zvuky</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="50"/>
+        <source>Skills</source>
+        <translation>Schopnosti</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="51"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="69"/>
+        <source>Other</source>
+        <translation>Ostatní</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="52"/>
+        <source>Objects</source>
+        <translation>Objekty</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="53"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="54"/>
+        <source>Mechanics</source>
+        <translation>Mechaniky</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="55"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="56"/>
+        <source>Interface</source>
+        <translation>Rozhraní</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="57"/>
+        <source>Heroes</source>
+        <translation>Hrdinové</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="58"/>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="59"/>
+        <source>Graphical</source>
+        <translation>Grafika</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="60"/>
+        <source>Expansion</source>
+        <translation>Rozšíření</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="61"/>
+        <source>Creatures</source>
+        <translation>Bojovníci</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="62"/>
+        <source>Compatibility</source>
+        <translation>Kompabilita</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="63"/>
+        <source>Artifacts</source>
+        <translation>Artefakty</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="64"/>
+        <source>AI</source>
+        <translation>AI</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="172"/>
+        <source>Name</source>
+        <translation>Název</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="175"/>
+        <source>Type</source>
+        <translation>Druh</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistmodel_moc.cpp" line="176"/>
+        <source>Version</source>
+        <translation>Verze</translation>
+    </message>
+</context>
+<context>
+    <name>CModListView</name>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="43"/>
+        <source>Filter</source>
+        <translation>Filtrovat</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="66"/>
+        <source>All mods</source>
+        <translation>Všechny modifikace</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="71"/>
+        <source>Downloadable</source>
+        <translation>Stahovatelné</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="76"/>
+        <source>Installed</source>
+        <translation>Nainstalované</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="81"/>
+        <source>Updatable</source>
+        <translation>Aktualizovatelné</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="86"/>
+        <source>Active</source>
+        <translation>Aktivní</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="91"/>
+        <source>Inactive</source>
+        <translation>Neaktivní</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="105"/>
+        <source>Download &amp;&amp; refresh repositories</source>
+        <translation>Stáhnout a aktualizovat repozitáře</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="163"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="316"/>
+        <source>Description</source>
+        <translation>Popis</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="209"/>
+        <source>Changelog</source>
+        <translation>Seznam změn</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="231"/>
+        <source>Screenshots</source>
+        <translation>Snímky obrazovky</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="389"/>
+        <source>Uninstall</source>
+        <translation>Odinstalovat</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="424"/>
+        <source>Enable</source>
+        <translation>Povolit</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="459"/>
+        <source>Disable</source>
+        <translation>Zakázat</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="494"/>
+        <source>Update</source>
+        <translation>Aktualizovat</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="529"/>
+        <source>Install</source>
+        <translation>Instalovat</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="327"/>
+        <source> %p% (%v KB out of %m KB)</source>
+        <translation> %p% (%v KB z %m KB)</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.ui" line="340"/>
+        <source>Abort</source>
+        <translation>Zrušit</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="248"/>
+        <source>Mod name</source>
+        <translation>Název modifikace</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="249"/>
+        <source>Installed version</source>
+        <translation>Nainstalovaná verze</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="250"/>
+        <source>Latest version</source>
+        <translation>Nejnovější verze</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="253"/>
+        <source>Size</source>
+        <translation>Velikost</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="255"/>
+        <source>Download size</source>
+        <translation>Velikost ke stažení</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="257"/>
+        <source>Authors</source>
+        <translation>Autoři</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="260"/>
+        <source>License</source>
+        <translation>Licence</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="263"/>
+        <source>Contact</source>
+        <translation>Kontakt</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="272"/>
+        <source>Compatibility</source>
+        <translation>Kompabilita</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="274"/>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="282"/>
+        <source>Required VCMI version</source>
+        <translation>Vyžadovaná verze VCMI</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="280"/>
+        <source>Supported VCMI version</source>
+        <translation>Podporovaná verze VCMI</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="285"/>
+        <source>Supported VCMI versions</source>
+        <translation>Podporované verze VCMI</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="312"/>
+        <source>Languages</source>
+        <translation>Jazyky</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="314"/>
+        <source>Required mods</source>
+        <translation>Vyžadované modifikace VCMI</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="315"/>
+        <source>Conflicting mods</source>
+        <translation>Modifikace v kolizi</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="320"/>
+        <source>This mod can not be installed or enabled because the following dependencies are not present</source>
+        <translation>Tato modifikace nemůže být nainstalována nebo povolena, protože následující závislosti nejsou přítomny</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="321"/>
+        <source>This mod can not be enabled because the following mods are incompatible with it</source>
+        <translation>Tato modifikace nemůže být povolena, protože následující modifikace s ní nejsou kompatibilní</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="322"/>
+        <source>This mod cannot be disabled because it is required by the following mods</source>
+        <translation>Tato modifikace nemůže být zakázána, protože je vyžadována následujícími modifikacemi</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="323"/>
+        <source>This mod cannot be uninstalled or updated because it is required by the following mods</source>
+        <translation>Tato modifikace nemůže být odinstalována nebo aktualizována, protože je vyžadována následujícími modifikacemi</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="324"/>
+        <source>This is a submod and it cannot be installed or uninstalled separately from its parent mod</source>
+        <translation>Toto je podmodifikace, která nemůže být nainstalována nebo odinstalována bez její rodičovské modifikace</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="339"/>
+        <source>Notes</source>
+        <translation>Poznámky</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="599"/>
+        <source>Downloading %s%. %p% (%v MB out of %m MB) finished</source>
+        <translation>Stahování %s%. %p% (%v MB z %m MB) dokončeno</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="618"/>
+        <source>Download failed</source>
+        <translation>Stahování selhalo</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="619"/>
+        <source>Unable to download all files.
+
+Encountered errors:
+
+</source>
+        <translation>Nelze stáhnout všechny soubory.
+
+Vyskytly se chyby:
+
+</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="620"/>
+        <source>
+
+Install successfully downloaded?</source>
+        <translation>
+
+Nainstalovat úspěšně stažené?</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="759"/>
+        <source>Installing mod %1</source>
+        <translation>Instalování modifikace %1</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="811"/>
+        <source>Operation failed</source>
+        <translation>Operace selhala</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="812"/>
+        <source>Encountered errors:
+</source>
+        <translation>Vyskytly se chyby:
+</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="847"/>
+        <source>Screenshot %1</source>
+        <translation>Snímek obrazovky %1</translation>
+    </message>
+    <message>
+        <location filename="../modManager/cmodlistview_moc.cpp" line="243"/>
+        <source>Mod is incompatible</source>
+        <translation>Modifikace není kompatibilní</translation>
+    </message>
+</context>
+<context>
+    <name>CSettingsView</name>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="276"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="580"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="649"/>
+        <source>Off</source>
+        <translation>Vypnuto</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="78"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="195"/>
+        <source>Artificial Intelligence</source>
+        <translation>Umělá inteligence</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="83"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="484"/>
+        <source>Mod Repositories</source>
+        <translation>Repozitáře modifikací</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="144"/>
+        <source>Interface Scaling</source>
+        <translation>Škálování rozhraní</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="624"/>
+        <source>Neutral AI in battles</source>
+        <translation>Neutrální AI v bitvách</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="289"/>
+        <source>Enemy AI in battles</source>
+        <translation>Nepřátelská AI v bitvách</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="239"/>
+        <source>Additional repository</source>
+        <translation>Další repozitáře</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="158"/>
+        <source>Adventure Map Allies</source>
+        <translation>Spojenci na mapě světa</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="638"/>
+        <source>Adventure Map Enemies</source>
+        <translation>Nepřátelé na mapě světa</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="353"/>
+        <source>Windowed</source>
+        <translation>V okně</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="358"/>
+        <source>Borderless fullscreen</source>
+        <translation>Celá obrazovka bez okrajů</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="363"/>
+        <source>Exclusive fullscreen</source>
+        <translation>Exkluzivní celá obrazovka</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="229"/>
+        <source>Autosave limit (0 = off)</source>
+        <translation>Limit aut. uložení (0=vypnuto)</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="436"/>
+        <source>Friendly AI in battles</source>
+        <translation>Přátelské AI v bitvách</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="429"/>
+        <source>Framerate Limit</source>
+        <translation>Omezení snímků za sekundu</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="151"/>
+        <source>Autosave prefix</source>
+        <translation>Předpona aut. uložení</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="202"/>
+        <source>empty = map name prefix</source>
+        <translation>prázná = předpona - název mapy</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="593"/>
+        <source>Refresh now</source>
+        <translation>Obnovit nyní</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="405"/>
+        <source>Default repository</source>
+        <translation>Výchozí repozitář</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="281"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="585"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="654"/>
+        <source>On</source>
+        <translation>Zapnuto</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="525"/>
+        <source>Cursor</source>
+        <translation>Kurzor</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="412"/>
+        <source>Heroes III Data Language</source>
+        <translation>Jazyk dat Heroes III</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="340"/>
+        <source>Select display mode for game
+
+Windowed - game will run inside a window that covers part of your screen
+
+Borderless Windowed Mode - game will run in a window that covers entirely of your screen, using same resolution as your screen.
+
+Fullscreen Exclusive Mode - game will cover entirety of your screen and will use selected resolution.</source>
+        <translation>Vyberte režim zobrazení pro hru
+
+V okně - hra bude běžet v okně zakrývajícím část vaší obrazovky
+
+Celá obrazovka bez okrajů-  hra poběží v okně, které zakryje vaši celou obrazovku se stejným rozlišením.
+
+Exkluzivní celá obrazovka - hra zakryje vaši celou obrazovku a použije vybrané rozlišení.</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="631"/>
+        <source>Reserved screen area</source>
+        <translation>Vyhrazená část obrazovky</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="533"/>
+        <source>Hardware</source>
+        <translation>Hardware</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="538"/>
+        <source>Software</source>
+        <translation>Software</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="118"/>
+        <source>Heroes III Translation</source>
+        <translation>Překlad Heroes III</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="617"/>
+        <source>Check on startup</source>
+        <translation>Zkontrolovat při zapnutí</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="137"/>
+        <source>Fullscreen</source>
+        <translation>Celá obrazovka</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="68"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="130"/>
+        <source>General</source>
+        <translation>Všeobecné</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="443"/>
+        <source>VCMI Language</source>
+        <translation>Jazyk VCMI</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="498"/>
+        <source>Resolution</source>
+        <translation>Rozlišení</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="518"/>
+        <source>Autosave</source>
+        <translation>Automatické uložení</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="662"/>
+        <source>VSync</source>
+        <translation>VSync</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="374"/>
+        <source>Display index</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="549"/>
+        <source>Network port</source>
+        <translation>Síťový port</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="73"/>
+        <location filename="../settingsView/csettingsview_moc.ui" line="170"/>
+        <source>Video</source>
+        <translation>Zobrazení</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.ui" line="265"/>
+        <source>Show intro</source>
+        <translation>Zobrazit intro</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="409"/>
+        <source>Active</source>
+        <translation>Aktivní</translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="414"/>
+        <source>Disabled</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="415"/>
+        <source>Enable</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="420"/>
+        <source>Not Installed</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settingsView/csettingsview_moc.cpp" line="421"/>
+        <source>Install</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Chat</name>
+    <message>
+        <location filename="../lobby/chat_moc.ui" line="14"/>
+        <source>Form</source>
+        <translation>Formulář</translation>
+    </message>
+    <message>
+        <location filename="../lobby/chat_moc.ui" line="40"/>
+        <source>Users in lobby</source>
+        <translation>Uživatelé v předsíni</translation>
+    </message>
+    <message>
+        <location filename="../lobby/chat_moc.ui" line="50"/>
+        <source>Global chat</source>
+        <translation>Obecná konverzace</translation>
+    </message>
+    <message>
+        <location filename="../lobby/chat_moc.ui" line="104"/>
+        <source>type you message</source>
+        <translation>zadejte vaši zprávu</translation>
+    </message>
+    <message>
+        <location filename="../lobby/chat_moc.ui" line="111"/>
+        <source>send</source>
+        <translation>poslat</translation>
+    </message>
+</context>
+<context>
+    <name>FirstLaunchView</name>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="28"/>
+        <source>Language</source>
+        <translation>Jazyk</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="53"/>
+        <source>Heroes III Data</source>
+        <translation>Data Heroes III</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="78"/>
+        <source>Mods Preset</source>
+        <translation>Předvybrané modifikace</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="127"/>
+        <source>Select your language</source>
+        <translation>Vyberte váš jazyk</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="177"/>
+        <source>Have a question? Found a bug? Want to help? Join us!</source>
+        <translation>Máte otázku? Našli jste chybu? Chcete pomoct? Připojte se k nám!</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="186"/>
+        <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>Děkujeme za instalaci VCMI!
+
+Před začátkem hraní musíte ještě dokončit pár kroků.
+
+Prosíme, mějte na paměti, že abyste mohli hrát VCMI, musíte vlastnit originální datové soubory Heroes® of Might and Magic® III: Complete nebo The Shadow of Death.
+
+Heroes® of Might and Magic® III HD není v současnosti podporovaný!</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="257"/>
+        <source>Locate Heroes III data files</source>
+        <translation>Najít soubory dat Heroes III</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="346"/>
+        <source>If you don&apos;t have a copy of Heroes III installed, you can use our automatic installation tool &apos;vcmibuilder&apos;, which only requires the GoG.com Heroes III installer. Please visit our wiki for detailed instructions.</source>
+        <translation>Pokud nemáte Heroes III nainstalované, můžete použít náš automatický instalační nástroj &apos;vcmibuilder&apos;, který vyžaduje pouze GOG instalátor Heroes III. Prosíme, navštivte naši wiki pro podrobné instrukce.</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="362"/>
+        <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>Pro běh VCMI, datové soubory Heroes III musí být přítomny v jednom z určených umístění. Prosíme, zkopírujte data Heroes do jedné z těchto složek.</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="397"/>
+        <source>Alternatively, you can provide the directory where Heroes III data is installed and VCMI will copy the existing data automatically.</source>
+        <translation>Nebo můžete poskytnout složku s instalací Heroes III a VCMI zkopíruje existující data automaticky.</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="426"/>
+        <source>Your Heroes III data files have been successfully found.</source>
+        <translation>Vaše soubory dat Heroes III byly úspěšně nalezeny.</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="466"/>
+        <source>The automatic detection of the Heroes III language has failed. Please select the language of your Heroes III manually</source>
+        <translation>Automatické rozpoznání jazyka Heroes III selhalo. Prosíme, vyberte jazyk vašich Heroes III ručně</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="702"/>
+        <source>Interface Improvements</source>
+        <translation>Vylepšení rozhraní</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="740"/>
+        <source>Install a translation of Heroes III in your preferred language</source>
+        <translation>Instalovat překlad Heroes III vašeho upřednostněného jazyka</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="756"/>
+        <source>Optionally, you can install additional mods either now, or at any point later, using the VCMI Launcher</source>
+        <translation>Nyní můžete volitelně nainstalovat další modifikace, nebo též kdykoliv potom pomocí spouštěče VCMI</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="772"/>
+        <source>Install mod that provides various interface improvements, such as better interface for random maps and selectable actions in battles</source>
+        <translation>Instalovat modifikaci, která poskytuje různá vylepšení rozhraní, například lepší rozhraní pro náhodné mapy a volitelné akce v bitvách</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="788"/>
+        <source>Install compatible version of &quot;Horn of the Abyss&quot;, a fan-made Heroes III expansion ported by the VCMI team</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="804"/>
+        <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="851"/>
+        <source>Finish</source>
+        <translation>Dokončit</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="156"/>
+        <source>VCMI on Github</source>
+        <translation>VCMI na GitHubu</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="163"/>
+        <source>VCMI on Slack</source>
+        <translation>VCMI na Slacku</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="170"/>
+        <source>VCMI on Discord</source>
+        <translation>VCMI na Discordu</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="220"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="535"/>
+        <source>Next</source>
+        <translation>Další</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="288"/>
+        <source>Open help in browser</source>
+        <translation>Otevřít nápovědu v prohlížeči</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="301"/>
+        <source>Search again</source>
+        <translation>Hledat znovu</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="384"/>
+        <source>Heroes III data files</source>
+        <translation>Soubory dat Heroes III</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="413"/>
+        <source>Copy existing data</source>
+        <translation>Kopírovat existující data</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="456"/>
+        <source>Your Heroes III language has been successfully detected.</source>
+        <translation>Váš jazyk Heroes III byl úspěšně zjištěn.</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="485"/>
+        <source>Heroes III language</source>
+        <translation>Jazyk Heroes III</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="528"/>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="844"/>
+        <source>Back</source>
+        <translation>Zpět</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="566"/>
+        <source>Install VCMI Mod Preset</source>
+        <translation>Instalovat předvybrané modifiakce VCMI</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="616"/>
+        <source>Horn of the Abyss</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="638"/>
+        <source>Heroes III Translation</source>
+        <translation>Překlady Heroes III</translation>
+    </message>
+    <message>
+        <location filename="../firstLaunch/firstlaunch_moc.ui" line="724"/>
+        <source>In The Wake of Gods</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>ImageViewer</name>
+    <message>
+        <location filename="../modManager/imageviewer_moc.ui" line="20"/>
+        <source>Image Viewer</source>
+        <translation>Prohlížeč obrázků</translation>
+    </message>
+</context>
+<context>
+    <name>Language</name>
+    <message>
+        <location filename="../languages.cpp" line="23"/>
+        <source>Czech</source>
+        <translation>Čeština</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="24"/>
+        <source>Chinese</source>
+        <translation>Čínština</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="25"/>
+        <source>English</source>
+        <translation>Angličtina</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="26"/>
+        <source>Finnish</source>
+        <translation>Finština</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="27"/>
+        <source>French</source>
+        <translation>Francouzština</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="28"/>
+        <source>German</source>
+        <translation>Němčina</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="29"/>
+        <source>Hungarian</source>
+        <translation>Maďarština</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="30"/>
+        <source>Italian</source>
+        <translation>Italština</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="31"/>
+        <source>Korean</source>
+        <translation>Korejština</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="32"/>
+        <source>Polish</source>
+        <translation>Polština</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="33"/>
+        <source>Portuguese</source>
+        <translation>Portugalština</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="34"/>
+        <source>Russian</source>
+        <translation>Ruština</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="35"/>
+        <source>Spanish</source>
+        <translation>Španělština</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="36"/>
+        <source>Swedish</source>
+        <translation>Švédština</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="37"/>
+        <source>Turkish</source>
+        <translation>Turečtina</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="38"/>
+        <source>Ukrainian</source>
+        <translation>Ukrajinština</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="39"/>
+        <source>Vietnamese</source>
+        <translation>Vietnamština</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="40"/>
+        <source>Other (East European)</source>
+        <translation>Ostatní (východní Evropa)</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="41"/>
+        <source>Other (Cyrillic Script)</source>
+        <translation>Ostatní (azbuka)</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="42"/>
+        <source>Other (West European)</source>
+        <translation>Ostatní (západní Evropa)</translation>
+    </message>
+    <message>
+        <location filename="../languages.cpp" line="64"/>
+        <source>Auto (%1)</source>
+        <translation>Automaticky (%1)</translation>
+    </message>
+</context>
+<context>
+    <name>Lobby</name>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="42"/>
+        <location filename="../lobby/lobby_moc.cpp" line="402"/>
+        <source>Connect</source>
+        <translation>Připojit</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="29"/>
+        <source>Username</source>
+        <translation>Uživatelské jméno</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="59"/>
+        <source>Server</source>
+        <translation>Server</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="173"/>
+        <source>Session</source>
+        <translation>Relace</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="178"/>
+        <source>Players</source>
+        <translation>Hráči</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="268"/>
+        <source>Resolve</source>
+        <translation>Vyřešit</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="280"/>
+        <source>New game</source>
+        <translation>Nová hra</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="287"/>
+        <source>Load game</source>
+        <translation>Načíst hru</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="128"/>
+        <source>New room</source>
+        <translation>Nová místnost</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="138"/>
+        <source>Join room</source>
+        <translation>Připojit se do místnosti</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="261"/>
+        <source>Ready</source>
+        <translation>Připraven</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="244"/>
+        <source>Mods mismatch</source>
+        <translation>Nesoulad modifikací</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="237"/>
+        <source>Leave</source>
+        <translation>Odejít</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="210"/>
+        <source>Kick player</source>
+        <translation>Vyhodit hráče</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobby_moc.ui" line="230"/>
+        <source>Players in the room</source>
+        <translation>Hráči v místnosti</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobby_moc.cpp" line="369"/>
+        <source>Disconnect</source>
+        <translation>Odpojit</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobby_moc.cpp" line="462"/>
+        <source>No issues detected</source>
+        <translation>Bez problémů</translation>
+    </message>
+</context>
+<context>
+    <name>LobbyRoomRequest</name>
+    <message>
+        <location filename="../lobby/lobbyroomrequest_moc.ui" line="17"/>
+        <source>Room settings</source>
+        <translation>Nastavení místnosti</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobbyroomrequest_moc.ui" line="32"/>
+        <source>Room name</source>
+        <translation>Název místnosti</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobbyroomrequest_moc.ui" line="42"/>
+        <source>Maximum players</source>
+        <translation>Maximum hráčů</translation>
+    </message>
+    <message>
+        <location filename="../lobby/lobbyroomrequest_moc.ui" line="97"/>
+        <source>Password (optional)</source>
+        <translation>Heslo (volitelné)</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <location filename="../mainwindow_moc.ui" line="20"/>
+        <source>VCMI Launcher</source>
+        <translation>Spouštěč VCMI</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow_moc.ui" line="107"/>
+        <source>Settings</source>
+        <translation>Nastavení</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow_moc.ui" line="207"/>
+        <source>Help</source>
+        <translation>Nápověda</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow_moc.ui" line="276"/>
+        <source>Map Editor</source>
+        <translation>Editor map</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow_moc.ui" line="329"/>
+        <source>Start game</source>
+        <translation>Spustit hru</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow_moc.ui" line="157"/>
+        <source>Lobby</source>
+        <translation>Předsíň</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow_moc.ui" line="57"/>
+        <source>Mods</source>
+        <translation>Modifikace</translation>
+    </message>
+</context>
+<context>
+    <name>UpdateDialog</name>
+    <message>
+        <location filename="../updatedialog_moc.ui" line="71"/>
+        <source>You have the latest version</source>
+        <translation>Máte nejnovější verzi</translation>
+    </message>
+    <message>
+        <location filename="../updatedialog_moc.ui" line="94"/>
+        <source>Close</source>
+        <translation>Zavřít</translation>
+    </message>
+    <message>
+        <location filename="../updatedialog_moc.ui" line="101"/>
+        <source>Check for updates on startup</source>
+        <translation>Zkontrolovat aktualizace při startu</translation>
+    </message>
+</context>
+</TS>

+ 20 - 6
lib/BasicTypes.cpp

@@ -93,6 +93,16 @@ int AFactionMember::getPrimSkillLevel(PrimarySkill id) const
 
 int AFactionMember::moraleValAndBonusList(TConstBonusListPtr & bonusList) const
 {
+	int32_t maxGoodMorale = VLC->settings()->getVector(EGameSettings::COMBAT_GOOD_MORALE_DICE).size();
+	int32_t maxBadMorale = - (int32_t) VLC->settings()->getVector(EGameSettings::COMBAT_BAD_MORALE_DICE).size();
+
+	if(getBonusBearer()->hasBonusOfType(BonusType::MAX_MORALE))
+	{
+		if(bonusList && !bonusList->empty())
+			bonusList = std::make_shared<const BonusList>();
+		return maxGoodMorale;
+	}
+
 	static const auto unaffectedByMoraleSelector = Selector::type()(BonusType::NON_LIVING).Or(Selector::type()(BonusType::UNDEAD))
 													.Or(Selector::type()(BonusType::SIEGE_WEAPON)).Or(Selector::type()(BonusType::NO_MORALE));
 
@@ -109,14 +119,21 @@ int AFactionMember::moraleValAndBonusList(TConstBonusListPtr & bonusList) const
 	static const std::string cachingStrMor = "type_MORALE";
 	bonusList = getBonusBearer()->getBonuses(moraleSelector, cachingStrMor);
 
-	int32_t maxGoodMorale = VLC->settings()->getVector(EGameSettings::COMBAT_GOOD_MORALE_DICE).size();
-	int32_t maxBadMorale = - (int32_t) VLC->settings()->getVector(EGameSettings::COMBAT_BAD_MORALE_DICE).size();
-
 	return std::clamp(bonusList->totalValue(), maxBadMorale, maxGoodMorale);
 }
 
 int AFactionMember::luckValAndBonusList(TConstBonusListPtr & bonusList) const
 {
+	int32_t maxGoodLuck = VLC->settings()->getVector(EGameSettings::COMBAT_GOOD_LUCK_DICE).size();
+	int32_t maxBadLuck = - (int32_t) VLC->settings()->getVector(EGameSettings::COMBAT_BAD_LUCK_DICE).size();
+
+	if(getBonusBearer()->hasBonusOfType(BonusType::MAX_LUCK))
+	{
+		if(bonusList && !bonusList->empty())
+			bonusList = std::make_shared<const BonusList>();
+		return maxGoodLuck;
+	}
+
 	if(getBonusBearer()->hasBonusOfType(BonusType::NO_LUCK))
 	{
 		if(bonusList && !bonusList->empty())
@@ -128,9 +145,6 @@ int AFactionMember::luckValAndBonusList(TConstBonusListPtr & bonusList) const
 	static const std::string cachingStrLuck = "type_LUCK";
 	bonusList = getBonusBearer()->getBonuses(luckSelector, cachingStrLuck);
 
-	int32_t maxGoodLuck = VLC->settings()->getVector(EGameSettings::COMBAT_GOOD_LUCK_DICE).size();
-	int32_t maxBadLuck = - (int32_t) VLC->settings()->getVector(EGameSettings::COMBAT_BAD_LUCK_DICE).size();
-
 	return std::clamp(bonusList->totalValue(), maxBadLuck, maxGoodLuck);
 }
 

+ 5 - 3
lib/CArtifactInstance.cpp

@@ -64,10 +64,7 @@ SpellID CScrollArtifactInstance::getScrollSpellID() const
 	auto artInst = static_cast<const CArtifactInstance*>(this);
 	const auto bonus = artInst->getBonusLocalFirst(Selector::type()(BonusType::SPELL));
 	if(!bonus)
-	{
-		logMod->warn("Warning: %s doesn't bear any spell!", artInst->nodeName());
 		return SpellID::NONE;
-	}
 	return bonus->subtype.as<SpellID>();
 }
 
@@ -165,6 +162,11 @@ bool CArtifactInstance::isCombined() const
 	return artType->isCombined();
 }
 
+bool CArtifactInstance::isScroll() const
+{
+	return artType->isScroll();
+}
+
 void CArtifactInstance::putAt(CArtifactSet & set, const ArtifactPosition slot)
 {
 	auto placementMap = set.putArtifact(slot, this);

+ 1 - 0
lib/CArtifactInstance.h

@@ -87,6 +87,7 @@ public:
 	bool canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot = ArtifactPosition::FIRST_AVAILABLE,
 		bool assumeDestRemoved = false) const;
 	bool isCombined() const;
+	bool isScroll() const;
 	void putAt(CArtifactSet & set, const ArtifactPosition slot);
 	void removeFrom(CArtifactSet & set, const ArtifactPosition slot);
 	void move(CArtifactSet & srcSet, const ArtifactPosition srcSlot, CArtifactSet & dstSet, const ArtifactPosition dstSlot);

+ 5 - 0
lib/CGameInfoCallback.cpp

@@ -721,6 +721,11 @@ bool CGameInfoCallback::isPlayerMakingTurn(PlayerColor player) const
 	return gs->actingPlayers.count(player);
 }
 
+CGameInfoCallback::CGameInfoCallback():
+	gs(nullptr)
+{
+}
+
 CGameInfoCallback::CGameInfoCallback(CGameState * GS):
 	gs(GS)
 {

+ 1 - 1
lib/CGameInfoCallback.h

@@ -134,7 +134,7 @@ class DLL_LINKAGE CGameInfoCallback : public IGameInfoCallback
 protected:
 	CGameState * gs;//todo: replace with protected const getter, only actual Server and Client objects should hold game state
 
-	CGameInfoCallback() = default;
+	CGameInfoCallback();
 	CGameInfoCallback(CGameState * GS);
 	bool hasAccess(std::optional<PlayerColor> playerId) const;
 

+ 24 - 21
lib/Languages.h

@@ -68,6 +68,9 @@ struct Options
 	/// primary IETF language tag
 	std::string tagIETF;
 
+	/// DateTime format
+	std::string dateTimeFormat;
+
 	/// Ruleset for plural forms in this language
 	EPluralForms pluralForms = EPluralForms::NONE;
 
@@ -79,27 +82,27 @@ inline const auto & getLanguageList()
 {
 	static const std::array<Options, 20> languages
 	{ {
-		{ "czech",       "Czech",       "Čeština",    "CP1250", "cs", EPluralForms::CZ_3, true },
-		{ "chinese",     "Chinese",     "简体中文",       "GBK",    "zh", EPluralForms::VI_1, true }, // Note: actually Simplified Chinese
-		{ "english",     "English",     "English",    "CP1252", "en", EPluralForms::EN_2, true },
-		{ "finnish",     "Finnish",     "Suomi",      "CP1252", "fi", EPluralForms::EN_2, true },
-		{ "french",      "French",      "Français",   "CP1252", "fr", EPluralForms::FR_2, true },
-		{ "german",      "German",      "Deutsch",    "CP1252", "de", EPluralForms::EN_2, true },
-		{ "hungarian",   "Hungarian",   "Magyar",     "CP1250", "hu", EPluralForms::EN_2, true },
-		{ "italian",     "Italian",     "Italiano",   "CP1250", "it", EPluralForms::EN_2, true },
-		{ "korean",      "Korean",      "한국어",        "CP949",  "ko", EPluralForms::VI_1, true },
-		{ "polish",      "Polish",      "Polski",     "CP1250", "pl", EPluralForms::PL_3, true },
-		{ "portuguese",  "Portuguese",  "Português",  "CP1252", "pt", EPluralForms::EN_2, true }, // Note: actually Brazilian Portuguese
-		{ "russian",     "Russian",     "Русский",    "CP1251", "ru", EPluralForms::UK_3, true },
-		{ "spanish",     "Spanish",     "Español",    "CP1252", "es", EPluralForms::EN_2, true },
-		{ "swedish",     "Swedish",     "Svenska",    "CP1252", "sv", EPluralForms::EN_2, true },
-		{ "turkish",     "Turkish",     "Türkçe",     "CP1254", "tr", EPluralForms::EN_2, true },
-		{ "ukrainian",   "Ukrainian",   "Українська", "CP1251", "uk", EPluralForms::UK_3, true },
-		{ "vietnamese",  "Vietnamese",  "Tiếng Việt", "UTF-8",  "vi", EPluralForms::VI_1, true }, // Fan translation uses special encoding
-
-		{ "other_cp1250", "Other (East European)",   "", "CP1250", "", EPluralForms::NONE, false },
-		{ "other_cp1251", "Other (Cyrillic Script)", "", "CP1251", "", EPluralForms::NONE, false },
-		{ "other_cp1252", "Other (West European)",   "", "CP1252", "", EPluralForms::NONE, false }
+		{ "czech",       "Czech",       "Čeština",    "CP1250", "cs", "%d.%m.%Y %T", EPluralForms::CZ_3, true },
+		{ "chinese",     "Chinese",     "简体中文",       "GBK",    "zh", "%F %T", EPluralForms::VI_1, true }, // Note: actually Simplified Chinese
+		{ "english",     "English",     "English",    "CP1252", "en", "%F %T", EPluralForms::EN_2, true }, // English uses international date/time format here
+		{ "finnish",     "Finnish",     "Suomi",      "CP1252", "fi", "%d.%m.%Y %T", EPluralForms::EN_2, true },
+		{ "french",      "French",      "Français",   "CP1252", "fr", "%d/%m/%Y %T", EPluralForms::FR_2, true },
+		{ "german",      "German",      "Deutsch",    "CP1252", "de", "%d.%m.%Y %T", EPluralForms::EN_2, true },
+		{ "hungarian",   "Hungarian",   "Magyar",     "CP1250", "hu", "%Y. %m. %d. %T", EPluralForms::EN_2, true },
+		{ "italian",     "Italian",     "Italiano",   "CP1250", "it", "%d/%m/%Y %T", EPluralForms::EN_2, true },
+		{ "korean",      "Korean",      "한국어",        "CP949",  "ko", "%F %T", EPluralForms::VI_1, true },
+		{ "polish",      "Polish",      "Polski",     "CP1250", "pl", "%d.%m.%Y %T", EPluralForms::PL_3, true },
+		{ "portuguese",  "Portuguese",  "Português",  "CP1252", "pt", "%d/%m/%Y %T", EPluralForms::EN_2, true }, // Note: actually Brazilian Portuguese
+		{ "russian",     "Russian",     "Русский",    "CP1251", "ru", "%d.%m.%Y %T", EPluralForms::UK_3, true },
+		{ "spanish",     "Spanish",     "Español",    "CP1252", "es", "%d/%m/%Y %T", EPluralForms::EN_2, true },
+		{ "swedish",     "Swedish",     "Svenska",    "CP1252", "sv", "%F %T", EPluralForms::EN_2, true },
+		{ "turkish",     "Turkish",     "Türkçe",     "CP1254", "tr", "%d.%m.%Y %T", EPluralForms::EN_2, true },
+		{ "ukrainian",   "Ukrainian",   "Українська", "CP1251", "uk", "%d.%m.%Y %T", EPluralForms::UK_3, true },
+		{ "vietnamese",  "Vietnamese",  "Tiếng Việt", "UTF-8",  "vi", "%d/%m/%Y %T", EPluralForms::VI_1, true }, // Fan translation uses special encoding
+
+		{ "other_cp1250", "Other (East European)",   "", "CP1250", "", "", EPluralForms::NONE, false },
+		{ "other_cp1251", "Other (Cyrillic Script)", "", "CP1251", "", "", EPluralForms::NONE, false },
+		{ "other_cp1252", "Other (West European)",   "", "CP1252", "", "", EPluralForms::NONE, false }
 	} };
 	static_assert(languages.size() == static_cast<size_t>(ELanguages::COUNT), "Languages array is missing a value!");
 

+ 9 - 0
lib/TextOperations.cpp

@@ -11,6 +11,10 @@
 #include "TextOperations.h"
 
 #include "CGeneralTextHandler.h"
+#include "Languages.h"
+#include "CConfigHandler.h"
+
+#include <vstd/DateUtils.h>
 
 #include <boost/locale.hpp>
 
@@ -210,4 +214,9 @@ std::string TextOperations::escapeString(std::string input)
 	return input;
 }
 
+std::string TextOperations::getFormattedDateTimeLocal(std::time_t dt)
+{
+	return vstd::getFormattedDateTime(dt, Languages::getLanguageOptions(settings["general"]["language"].String()).dateTimeFormat);
+}
+
 VCMI_LIB_NAMESPACE_END

+ 3 - 0
lib/TextOperations.h

@@ -56,6 +56,9 @@ namespace TextOperations
 
 	/// replaces all symbols that normally need escaping with appropriate escape sequences
 	std::string escapeString(std::string input);
+
+	/// get formatted DateTime depending on the language selected
+	DLL_LINKAGE std::string getFormattedDateTimeLocal(std::time_t dt);
 };
 
 

+ 5 - 0
lib/bonuses/Bonus.h

@@ -162,6 +162,11 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
 		auto set = hb->duration & BonusDuration::COMMANDER_KILLED;
 		return set.any();
 	}
+	static bool UntilOwnAttack(const Bonus *hb)
+	{
+		auto set = hb->duration & BonusDuration::UNTIL_OWN_ATTACK;
+		return set.any();
+	}
 	inline bool operator == (const BonusType & cf) const
 	{
 		return type == cf;

+ 1 - 0
lib/bonuses/BonusEnum.cpp

@@ -44,6 +44,7 @@ const std::map<std::string, BonusDuration::Type> bonusDurationMap =
 	BONUS_ITEM(UNTIL_ATTACK)
 	BONUS_ITEM(STACK_GETS_TURN)
 	BONUS_ITEM(COMMANDER_KILLED)
+	BONUS_ITEM(UNTIL_OWN_ATTACK)
 	{ "UNITL_BEING_ATTACKED", BonusDuration::UNTIL_BEING_ATTACKED }//typo, but used in some mods
 };
 #undef BONUS_ITEM

+ 6 - 2
lib/bonuses/BonusEnum.h

@@ -169,7 +169,10 @@ class JsonNode;
 	BONUS_NAME(MAX_LEARNABLE_SPELL_LEVEL) /*This can work as wisdom before. val = max learnable spell level*/\
 	BONUS_NAME(SPELL_SCHOOL_IMMUNITY) /*This bonus will work as spell school immunity for all spells, subtype - spell school: 0 - air, 1 - fire, 2 - water, 3 - earth. Any is not handled for reducing overlap from LEVEL_SPELL_IMMUNITY*/\
 	BONUS_NAME(NEGATIVE_EFFECTS_IMMUNITY) /*This bonus will work as spell school immunity for negative effects from spells of school, subtype - spell school: -1 - any, 0 - air, 1 - fire, 2 - water, 3 - earth*/\
-	BONUS_NAME(TERRAIN_NATIVE)
+	BONUS_NAME(TERRAIN_NATIVE) \
+	BONUS_NAME(UNLIMITED_MOVEMENT) /*cheat bonus*/ \
+	BONUS_NAME(MAX_MORALE) /*cheat bonus*/ \
+	BONUS_NAME(MAX_LUCK) /*cheat bonus*/ \
 	/* end of list */
 
 
@@ -212,7 +215,7 @@ enum class BonusType
 };
 namespace BonusDuration  //when bonus is automatically removed
 {
-	using Type = std::bitset<10>;
+	using Type = std::bitset<11>;
 	extern JsonNode toJson(const Type & duration);
 	constexpr Type PERMANENT = 1 << 0;
 	constexpr Type ONE_BATTLE = 1 << 1; //at the end of battle
@@ -224,6 +227,7 @@ namespace BonusDuration  //when bonus is automatically removed
 	constexpr Type UNTIL_ATTACK = 1 << 7; /*removed after attack and counterattacks are performed*/
 	constexpr Type STACK_GETS_TURN = 1 << 8; /*removed when stack gets its turn - used for defensive stance*/
 	constexpr Type COMMANDER_KILLED = 1 << 9;
+	constexpr Type UNTIL_OWN_ATTACK = 1 << 10; /*removed after attack is performed (not counterattack)*/;
 };
 enum class BonusSource
 {

+ 38 - 56
lib/filesystem/CZipLoader.cpp

@@ -150,22 +150,11 @@ static bool extractCurrent(unzFile file, std::ostream & where)
 	return false;
 }
 
-std::vector<std::string> ZipArchive::listFiles(const boost::filesystem::path & filename)
+std::vector<std::string> ZipArchive::listFiles()
 {
 	std::vector<std::string> ret;
 
-	CDefaultIOApi zipAPI;
-	auto zipStructure = zipAPI.getApiStructure();
-
-	unzFile file = unzOpen2_64(filename.c_str(), &zipStructure);
-
-	if (file == nullptr)
-	{
-		logGlobal->error("Failed to open file '%s'! Unable to list files!", filename.string());
-		return {};
-	}
-
-	int result = unzGoToFirstFile(file);
+	int result = unzGoToFirstFile(archive);
 
 	if (result == UNZ_OK)
 	{
@@ -174,73 +163,66 @@ std::vector<std::string> ZipArchive::listFiles(const boost::filesystem::path & f
 			unz_file_info64 info;
 			std::vector<char> zipFilename;
 
-			unzGetCurrentFileInfo64 (file, &info, nullptr, 0, nullptr, 0, nullptr, 0);
+			unzGetCurrentFileInfo64 (archive, &info, nullptr, 0, nullptr, 0, nullptr, 0);
 
 			zipFilename.resize(info.size_filename);
 			// Get name of current file. Contrary to docs "info" parameter can't be null
-			unzGetCurrentFileInfo64(file, &info, zipFilename.data(), static_cast<uLong>(zipFilename.size()), nullptr, 0, nullptr, 0);
+			unzGetCurrentFileInfo64(archive, &info, zipFilename.data(), static_cast<uLong>(zipFilename.size()), nullptr, 0, nullptr, 0);
 
 			ret.emplace_back(zipFilename.data(), zipFilename.size());
 
-			result = unzGoToNextFile(file);
+			result = unzGoToNextFile(archive);
 		}
 		while (result == UNZ_OK);
-
-		if (result != UNZ_OK && result != UNZ_END_OF_LIST_OF_FILE)
-		{
-			logGlobal->error("Failed to list file from '%s'! Error code %d", filename.string(), result);
-		}
-	}
-	else
-	{
-		logGlobal->error("Failed to list files from '%s'! Error code %d", filename.string(), result);
 	}
-
-	unzClose(file);
-
 	return ret;
 }
 
-bool ZipArchive::extract(const boost::filesystem::path & from, const boost::filesystem::path & where)
-{
-	// Note: may not be fast enough for large archives (should NOT happen with mods)
-	// because locating each file by name may be slow. Unlikely slower than decompression though
-	return extract(from, where, listFiles(from));
-}
-
-bool ZipArchive::extract(const boost::filesystem::path & from, const boost::filesystem::path & where, const std::vector<std::string> & what)
+ZipArchive::ZipArchive(const boost::filesystem::path & from)
 {
 	CDefaultIOApi zipAPI;
 	auto zipStructure = zipAPI.getApiStructure();
 
-	unzFile archive = unzOpen2_64(from.c_str(), &zipStructure);
+	archive = unzOpen2_64(from.c_str(), &zipStructure);
 
-	auto onExit = vstd::makeScopeGuard([&]()
-	{
-		unzClose(archive);
-	});
+	if (archive == nullptr)
+		throw std::runtime_error("Failed to open file" + from.string() + "'%s'! Unable to list files!");
+}
 
+ZipArchive::~ZipArchive()
+{
+	unzClose(archive);
+}
+
+bool ZipArchive::extract(const boost::filesystem::path & where, const std::vector<std::string> & what)
+{
 	for (const std::string & file : what)
-	{
-		if (unzLocateFile(archive, file.c_str(), 1) != UNZ_OK)
+		if (!extract(where, file))
 			return false;
 
-		const boost::filesystem::path fullName = where / file;
-		const boost::filesystem::path fullPath = fullName.parent_path();
+	return true;
+}
 
-		boost::filesystem::create_directories(fullPath);
-		// directory. No file to extract
-		// TODO: better way to detect directory? Probably check return value of unzOpenCurrentFile?
-		if (boost::algorithm::ends_with(file, "/"))
-			continue;
+bool ZipArchive::extract(const boost::filesystem::path & where, const std::string & file)
+{
+	if (unzLocateFile(archive, file.c_str(), 1) != UNZ_OK)
+		return false;
 
-		std::fstream destFile(fullName.c_str(), std::ios::out | std::ios::binary);
-		if (!destFile.good())
-			return false;
+	const boost::filesystem::path fullName = where / file;
+	const boost::filesystem::path fullPath = fullName.parent_path();
 
-		if (!extractCurrent(archive, destFile))
-			return false;
-	}
+	boost::filesystem::create_directories(fullPath);
+	// directory. No file to extract
+	// TODO: better way to detect directory? Probably check return value of unzOpenCurrentFile?
+	if (boost::algorithm::ends_with(file, "/"))
+		return true;
+
+	std::fstream destFile(fullName.c_str(), std::ios::out | std::ios::binary);
+	if (!destFile.good())
+		return false;
+
+	if (!extractCurrent(archive, destFile))
+		return false;
 	return true;
 }
 

+ 9 - 8
lib/filesystem/CZipLoader.h

@@ -61,16 +61,17 @@ public:
 	std::unordered_set<ResourcePath> getFilteredFiles(std::function<bool(const ResourcePath &)> filter) const override;
 };
 
-namespace ZipArchive
+class DLL_LINKAGE ZipArchive : boost::noncopyable
 {
-	/// List all files present in archive
-	std::vector<std::string> DLL_LINKAGE listFiles(const boost::filesystem::path & filename);
+	unzFile archive;
 
-	/// extracts all files from archive "from" into destination directory "where". Directory must exist
-	bool DLL_LINKAGE extract(const boost::filesystem::path & from, const boost::filesystem::path & where);
+public:
+	ZipArchive(const boost::filesystem::path & from);
+	~ZipArchive();
 
-	///same as above, but extracts only files mentioned in "what" list
-	bool DLL_LINKAGE extract(const boost::filesystem::path & from, const boost::filesystem::path & where, const std::vector<std::string> & what);
-}
+	std::vector<std::string> listFiles();
+	bool extract(const boost::filesystem::path & where, const std::vector<std::string> & what);
+	bool extract(const boost::filesystem::path & where, const std::string & what);
+};
 
 VCMI_LIB_NAMESPACE_END

+ 19 - 3
lib/mapObjectConstructors/AObjectTypeHandler.cpp

@@ -20,6 +20,19 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+AObjectTypeHandler::AObjectTypeHandler() = default;
+
+AObjectTypeHandler::~AObjectTypeHandler()
+{
+	// FIXME: currently on Android there is a weird crash in destructor of 'base' member
+	// this code attempts to localize and fix this crash
+	if (base)
+	{
+		base->clear();
+		base.reset();
+	}
+}
+
 std::string AObjectTypeHandler::getJsonKey() const
 {
 	return modScope + ':' + subTypeName;
@@ -55,7 +68,8 @@ static ui32 loadJsonOrMax(const JsonNode & input)
 
 void AObjectTypeHandler::init(const JsonNode & input)
 {
-	base = input["base"];
+	if (!input["base"].isNull())
+		base = std::make_unique<JsonNode>(input["base"]);
 
 	if (!input["rmg"].isNull())
 	{
@@ -72,7 +86,8 @@ void AObjectTypeHandler::init(const JsonNode & input)
 	for (auto entry : input["templates"].Struct())
 	{
 		entry.second.setType(JsonNode::JsonType::DATA_STRUCT);
-		JsonUtils::inherit(entry.second, base);
+		if (base)
+			JsonUtils::inherit(entry.second, *base);
 
 		auto * tmpl = new ObjectTemplate;
 		tmpl->id = Obj(type);
@@ -171,7 +186,8 @@ void AObjectTypeHandler::addTemplate(const std::shared_ptr<const ObjectTemplate>
 void AObjectTypeHandler::addTemplate(JsonNode config)
 {
 	config.setType(JsonNode::JsonType::DATA_STRUCT); // ensure that input is not null
-	JsonUtils::inherit(config, base);
+	if (base)
+		JsonUtils::inherit(config, *base);
 	auto * tmpl = new ObjectTemplate;
 	tmpl->id = Obj(type);
 	tmpl->subid = subtype;

+ 3 - 2
lib/mapObjectConstructors/AObjectTypeHandler.h

@@ -27,7 +27,7 @@ class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable
 
 	RandomMapInfo rmgInfo;
 
-	JsonNode base; /// describes base template
+	std::unique_ptr<JsonNode> base; /// describes base template
 
 	std::vector<std::shared_ptr<const ObjectTemplate>> templates;
 
@@ -54,7 +54,8 @@ protected:
 	virtual void initTypeData(const JsonNode & input);
 public:
 
-	virtual ~AObjectTypeHandler() = default;
+	AObjectTypeHandler();
+	virtual ~AObjectTypeHandler();
 
 	si32 getIndex() const;
 	si32 getSubIndex() const;

+ 0 - 1
lib/mapObjectConstructors/CObjectClassesHandler.cpp

@@ -443,7 +443,6 @@ void CObjectClassesHandler::generateExtraMonolithsForRMG(ObjectClass * container
 		//deep copy of noncopyable object :?
 		auto newPortal = std::make_shared<CDefaultObjectTypeHandler<CGMonolith>>();
 		newPortal->rmgInfo = portal->getRMGInfo();
-		newPortal->base = portal->base; //not needed?
 		newPortal->templates = portal->getTemplates();
 		newPortal->sounds = portal->getSounds();
 		newPortal->aiValue = portal->getAiValue();

+ 4 - 1
lib/mapObjects/CGHeroInstance.cpp

@@ -239,7 +239,10 @@ int CGHeroInstance::movementPointsRemaining() const
 
 void CGHeroInstance::setMovementPoints(int points)
 {
-	movement = std::max(0, points);
+	if(getBonusBearer()->hasBonusOfType(BonusType::UNLIMITED_MOVEMENT))
+		movement = 1000000;
+	else
+		movement = std::max(0, points);
 }
 
 int CGHeroInstance::movementPointsLimit(bool onLand) const

+ 4 - 3
lib/mapping/CMapInfo.cpp

@@ -10,8 +10,6 @@
 #include "StdInc.h"
 #include "CMapInfo.h"
 
-#include <vstd/DateUtils.h>
-
 #include "../filesystem/ResourcePath.h"
 #include "../StartInfo.h"
 #include "../GameConstants.h"
@@ -23,10 +21,13 @@
 #include "../filesystem/Filesystem.h"
 #include "../serializer/CLoadFile.h"
 #include "../CGeneralTextHandler.h"
+#include "../TextOperations.h"
 #include "../rmg/CMapGenOptions.h"
 #include "../CCreatureHandler.h"
 #include "../GameSettings.h"
 #include "../CHeroHandler.h"
+#include "../Languages.h"
+#include "../CConfigHandler.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -64,7 +65,7 @@ void CMapInfo::saveInit(const ResourcePath & file)
 	fullFileURI = boost::filesystem::canonical(*CResourceHandler::get()->getResourceName(file)).string();
 	countPlayers();
 	std::time_t time = boost::filesystem::last_write_time(*CResourceHandler::get()->getResourceName(file));
-	date = vstd::getFormattedDateTime(time);
+	date = TextOperations::getFormattedDateTimeLocal(time);
 
 	// We absolutely not need this data for lobby and server will read it from save
 	// FIXME: actually we don't want them in CMapHeader!

+ 6 - 1
lib/modding/CModInfo.cpp

@@ -49,7 +49,11 @@ CModInfo::CModInfo(const std::string & identifier, const JsonNode & local, const
 	validation(PENDING),
 	config(addMeta(config, identifier))
 {
-	verificationInfo.name = config["name"].String();
+	if (!config["name"].String().empty())
+		verificationInfo.name = config["name"].String();
+	else
+		verificationInfo.name = identifier;
+
 	verificationInfo.version = CModVersion::fromString(config["version"].String());
 	verificationInfo.parent = identifier.substr(0, identifier.find_last_of('.'));
 	if(verificationInfo.parent == identifier)
@@ -189,6 +193,7 @@ bool CModInfo::checkModGameplayAffecting() const
 
 const ModVerificationInfo & CModInfo::getVerificationInfo() const
 {
+	assert(!verificationInfo.name.empty());
 	return verificationInfo;
 }
 

+ 3 - 0
lib/networkPacks/NetPacksLib.cpp

@@ -2260,6 +2260,9 @@ void BattleAttack::applyGs(CGameState * gs)
 		stackAttacked.applyGs(gs);
 
 	attacker->removeBonusesRecursive(Bonus::UntilAttack);
+
+	if(!this->counter())
+		attacker->removeBonusesRecursive(Bonus::UntilOwnAttack);
 }
 
 void StartAction::applyGs(CGameState *gs)

+ 4 - 2
lib/rewardable/Configuration.cpp

@@ -34,14 +34,16 @@ std::optional<int> Rewardable::Configuration::getVariable(const std::string & ca
 	return std::nullopt;
 }
 
-JsonNode Rewardable::Configuration::getPresetVariable(const std::string & category, const std::string & name) const
+const JsonNode & Rewardable::Configuration::getPresetVariable(const std::string & category, const std::string & name) const
 {
+	static const JsonNode emptyNode;
+
 	std::string variableID = category + '@' + name;
 
 	if (variables.preset.count(variableID))
 		return variables.preset.at(variableID);
 	else
-		return JsonNode();
+		return emptyNode;
 }
 
 void Rewardable::Configuration::presetVariable(const std::string & category, const std::string & name, const JsonNode & value)

+ 1 - 1
lib/rewardable/Configuration.h

@@ -167,7 +167,7 @@ struct DLL_LINKAGE Configuration
 	ui16 getResetDuration() const;
 
 	std::optional<int> getVariable(const std::string & category, const std::string & name) const;
-	JsonNode getPresetVariable(const std::string & category, const std::string & name) const;
+	const JsonNode & getPresetVariable(const std::string & category, const std::string & name) const;
 	void presetVariable(const std::string & category, const std::string & name, const JsonNode & value);
 	void initVariable(const std::string & category, const std::string & name, int value);
 	

+ 4 - 4
lib/rewardable/Info.cpp

@@ -272,18 +272,18 @@ void Rewardable::Info::replaceTextPlaceholders(MetaString & target, const Variab
 
 void Rewardable::Info::configureRewards(
 		Rewardable::Configuration & object,
-		CRandomGenerator & rng, const
-		JsonNode & source,
+		CRandomGenerator & rng,
+		const JsonNode & source,
 		Rewardable::EEventType event,
 		const std::string & modeName) const
 {
 	for(size_t i = 0; i < source.Vector().size(); ++i)
 	{
-		const JsonNode reward = source.Vector()[i];
+		const JsonNode & reward = source.Vector().at(i);
 
 		if (!reward["appearChance"].isNull())
 		{
-			JsonNode chance = reward["appearChance"];
+			const JsonNode & chance = reward["appearChance"];
 			std::string diceID = std::to_string(chance["dice"].Integer());
 
 			auto diceValue = object.getVariable("dice", diceID);

+ 6 - 0
lib/serializer/Connection.cpp

@@ -148,6 +148,9 @@ void CConnection::flushBuffers()
 	if(!enableBufferedWrite)
 		return;
 
+	if (!socket)
+		throw std::runtime_error("Can't write to closed socket!");
+
 	try
 	{
 		asio::write(*socket, connectionBuffers->writeBuffer);
@@ -164,6 +167,9 @@ void CConnection::flushBuffers()
 
 int CConnection::write(const void * data, unsigned size)
 {
+	if (!socket)
+		throw std::runtime_error("Can't write to closed socket!");
+
 	try
 	{
 		if(enableBufferedWrite)

+ 3 - 23
lib/vstd/DateUtils.cpp

@@ -1,42 +1,22 @@
 #include "StdInc.h"
 #include <vstd/DateUtils.h>
 
-#if defined(VCMI_ANDROID)
-#include "../CAndroidVMHelper.h"
-#endif
-
 VCMI_LIB_NAMESPACE_BEGIN
 
 namespace vstd
 {
 
-	DLL_LINKAGE std::string getFormattedDateTime(std::time_t dt)
+	DLL_LINKAGE std::string getFormattedDateTime(std::time_t dt, std::string format)
 	{
-#if defined(VCMI_ANDROID)
-		CAndroidVMHelper vmHelper;
-		return vmHelper.callStaticStringMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "getFormattedDateTime");
-#endif
-
 		std::tm tm = *std::localtime(&dt);
 		std::stringstream s;
-		try
-		{
-			s.imbue(std::locale(""));
-		}
-		catch(const std::runtime_error & e)
-		{
-			// locale not be available - keep default / global
-		}
-		s << std::put_time(&tm, "%x %X");
+		s << std::put_time(&tm, format.c_str());
 		return s.str();
 	}
 
 	DLL_LINKAGE std::string getDateTimeISO8601Basic(std::time_t dt)
 	{
-		std::tm tm = *std::localtime(&dt);
-		std::stringstream s;
-		s << std::put_time(&tm, "%Y%m%dT%H%M%S");
-		return s.str();
+		return getFormattedDateTime(dt, "%Y%m%dT%H%M%S");
 	}
 
 }

+ 1831 - 0
mapeditor/translation/czech.ts

@@ -0,0 +1,1831 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="cs_CZ">
+<context>
+    <name>ArmyWidget</name>
+    <message>
+        <location filename="../inspector/armywidget.ui" line="23"/>
+        <source>Army settings</source>
+        <translation>Nastavení armády</translation>
+    </message>
+    <message>
+        <location filename="../inspector/armywidget.ui" line="121"/>
+        <source>Wide formation</source>
+        <translation>Široká formace</translation>
+    </message>
+    <message>
+        <location filename="../inspector/armywidget.ui" line="108"/>
+        <source>Tight formation</source>
+        <translation>Úzká formace</translation>
+    </message>
+</context>
+<context>
+    <name>EventSettings</name>
+    <message>
+        <location filename="../mapsettings/eventsettings.ui" line="14"/>
+        <source>Form</source>
+        <translation>Formulář</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/eventsettings.ui" line="34"/>
+        <source>Timed events</source>
+        <translation>Načasované události</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/eventsettings.ui" line="60"/>
+        <source>Add</source>
+        <translation>Přidat</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/eventsettings.ui" line="73"/>
+        <source>Remove</source>
+        <translation>Odebrat</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/eventsettings.cpp" line="101"/>
+        <source>New event</source>
+        <translation>Nová událost</translation>
+    </message>
+</context>
+<context>
+    <name>GeneralSettings</name>
+    <message>
+        <location filename="../mapsettings/generalsettings.ui" line="14"/>
+        <source>Form</source>
+        <translation>Formulář</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/generalsettings.ui" line="32"/>
+        <source>Map name</source>
+        <translation>Název mapy</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/generalsettings.ui" line="42"/>
+        <source>Map description</source>
+        <translation>Popis mapy</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/generalsettings.ui" line="76"/>
+        <source>Limit maximum heroes level</source>
+        <translation>Omezit max. úroveň hrdinů</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/generalsettings.ui" line="85"/>
+        <source>Difficulty</source>
+        <translation>Obtížnost</translation>
+    </message>
+</context>
+<context>
+    <name>GeneratorProgress</name>
+    <message>
+        <location filename="../generatorprogress.ui" line="29"/>
+        <source>Generating map</source>
+        <translation>Generování mapy</translation>
+    </message>
+</context>
+<context>
+    <name>HeroSkillsWidget</name>
+    <message>
+        <location filename="../inspector/heroskillswidget.ui" line="14"/>
+        <source>Hero skills</source>
+        <translation>Schopnosti hrdiny</translation>
+    </message>
+    <message>
+        <location filename="../inspector/heroskillswidget.ui" line="28"/>
+        <location filename="../inspector/heroskillswidget.ui" line="38"/>
+        <location filename="../inspector/heroskillswidget.ui" line="48"/>
+        <location filename="../inspector/heroskillswidget.ui" line="58"/>
+        <source>TextLabel</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../inspector/heroskillswidget.ui" line="97"/>
+        <source>Add</source>
+        <translation>Přidat</translation>
+    </message>
+    <message>
+        <location filename="../inspector/heroskillswidget.ui" line="113"/>
+        <source>Remove</source>
+        <translation>Odebrat</translation>
+    </message>
+    <message>
+        <location filename="../inspector/heroskillswidget.ui" line="153"/>
+        <source>Skill</source>
+        <translation>Dovednost</translation>
+    </message>
+    <message>
+        <location filename="../inspector/heroskillswidget.ui" line="158"/>
+        <source>Level</source>
+        <translation>Úroveň</translation>
+    </message>
+    <message>
+        <location filename="../inspector/heroskillswidget.ui" line="166"/>
+        <source>Customize skills</source>
+        <translation>Přizpůsobit schopnosti</translation>
+    </message>
+</context>
+<context>
+    <name>LoseConditions</name>
+    <message>
+        <location filename="../mapsettings/loseconditions.ui" line="14"/>
+        <source>Form</source>
+        <translation>Formulář</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/loseconditions.ui" line="40"/>
+        <source>Defeat message</source>
+        <translation>Zpráva prohry</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/loseconditions.ui" line="59"/>
+        <source>7 days without town</source>
+        <translation>7 dní bez města</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/loseconditions.ui" line="72"/>
+        <source>Parameters</source>
+        <translation>Parametry</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/loseconditions.cpp" line="37"/>
+        <source>No special loss</source>
+        <translation>Bez speciální prohry</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/loseconditions.cpp" line="38"/>
+        <source>Lose castle</source>
+        <translation>Ztráta města</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/loseconditions.cpp" line="39"/>
+        <source>Lose hero</source>
+        <translation>Ztráta hrdiny</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/loseconditions.cpp" line="40"/>
+        <source>Time expired</source>
+        <translation>Vypršení času</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/loseconditions.cpp" line="41"/>
+        <source>Days without town</source>
+        <translation>Dny bez města</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <location filename="../mainwindow.ui" line="14"/>
+        <source>VCMI Map Editor</source>
+        <translation>Editor map VCMI</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="59"/>
+        <source>File</source>
+        <translation>Soubor</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="70"/>
+        <source>Map</source>
+        <translation>Mapa</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="81"/>
+        <source>Edit</source>
+        <translation>Upravit</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="94"/>
+        <source>View</source>
+        <translation>Zobrazit</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="106"/>
+        <source>Player</source>
+        <translation>Hráč</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="126"/>
+        <source>Toolbar</source>
+        <translation>Panel nástrojů</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="183"/>
+        <source>Minimap</source>
+        <translation>Minimapa</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="256"/>
+        <source>Map Objects View</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="300"/>
+        <source>Browser</source>
+        <translation>Prohlížeč</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="378"/>
+        <source>Inspector</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="420"/>
+        <source>Property</source>
+        <translation>Vlastnost</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="425"/>
+        <source>Value</source>
+        <translation>Hodnota</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="449"/>
+        <source>Tools</source>
+        <translation>Nástroje</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="845"/>
+        <source>Painting</source>
+        <translation>Malování</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="897"/>
+        <source>Terrains</source>
+        <translation>Krajiny</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="940"/>
+        <source>Roads</source>
+        <translation>Cesty</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="980"/>
+        <source>Rivers</source>
+        <translation>Řeky</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1014"/>
+        <source>Preview</source>
+        <translation>Náhled</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1052"/>
+        <source>Open</source>
+        <translation>Otevřít</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1064"/>
+        <source>Save</source>
+        <translation>Uložit</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1076"/>
+        <source>New</source>
+        <translation>Nový</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1084"/>
+        <source>Save as...</source>
+        <translation>Uložit jako...</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1087"/>
+        <source>Ctrl+Shift+S</source>
+        <translation>Ctrl+Shift+S</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1096"/>
+        <source>U/G</source>
+        <translation>P/Z</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1099"/>
+        <location filename="../mainwindow.cpp" line="781"/>
+        <source>View underground</source>
+        <translation>Zobrazit podzemí</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1114"/>
+        <source>Pass</source>
+        <translation>Průchodnost</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1126"/>
+        <source>Cut</source>
+        <translation>Vyjmout</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1138"/>
+        <source>Copy</source>
+        <translation>Kopírovat</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1150"/>
+        <source>Paste</source>
+        <translation>Vložit</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1162"/>
+        <source>Fill</source>
+        <translation>Vyplnit</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1165"/>
+        <source>Fills the selection with obstacles</source>
+        <translation>Vyplní výběr překážkami</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1180"/>
+        <source>Grid</source>
+        <translation>Mřížka</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1191"/>
+        <source>General</source>
+        <translation>Všeobecné</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1194"/>
+        <source>Map title and description</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1205"/>
+        <source>Players settings</source>
+        <translation>Hráčské nastavení</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1220"/>
+        <location filename="../mainwindow.ui" line="1223"/>
+        <source>Undo</source>
+        <translation>Zpět</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1241"/>
+        <source>Redo</source>
+        <translation>Znovu</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1259"/>
+        <source>Erase</source>
+        <translation>Smazat</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1270"/>
+        <source>Neutral</source>
+        <translation>Neutrální</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1278"/>
+        <source>Validate</source>
+        <translation>Posoudit</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1289"/>
+        <location filename="../mainwindow.cpp" line="1052"/>
+        <location filename="../mainwindow.cpp" line="1056"/>
+        <location filename="../mainwindow.cpp" line="1113"/>
+        <source>Update appearance</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1300"/>
+        <source>Recreate obstacles</source>
+        <translation>Přetvořit překážky</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1311"/>
+        <source>Player 1</source>
+        <translation>Hráč 1</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1322"/>
+        <source>Player 2</source>
+        <translation>Hráč 2</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1333"/>
+        <source>Player 3</source>
+        <translation>Hráč 3</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1344"/>
+        <source>Player 4</source>
+        <translation>Hráč 4</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1355"/>
+        <source>Player 5</source>
+        <translation>Hráč 5</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1366"/>
+        <source>Player 6</source>
+        <translation>Hráč 6</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1377"/>
+        <source>Player 7</source>
+        <translation>Hráč 7</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1388"/>
+        <source>Player 8</source>
+        <translation>Hráč 8</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1396"/>
+        <source>Export as...</source>
+        <translation>Exportovat jako...</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1408"/>
+        <source>Translations</source>
+        <translation>Překlady</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1411"/>
+        <source>Ctrl+T</source>
+        <translation>Ctrl+T</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1416"/>
+        <location filename="../mainwindow.ui" line="1419"/>
+        <source>h3m converter</source>
+        <translation>Převodník h3m</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1428"/>
+        <source>Lock</source>
+        <translation>Zamknout</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1431"/>
+        <source>Lock objects on map to avoid unnecessary changes</source>
+        <translation>Zamknout objekty na mapě pro zabránění nadbytečných změn</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1434"/>
+        <source>Ctrl+L</source>
+        <translation>Ctrl+L</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1443"/>
+        <source>Unlock</source>
+        <translation>Odemknout</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1446"/>
+        <source>Unlock all objects on the map</source>
+        <translation>Odemknout objekty na mapě</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1449"/>
+        <source>Ctrl+Shift+L</source>
+        <translation>Ctrl+Shift+L</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1458"/>
+        <source>Zoom in</source>
+        <translation>Přiblížit</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1461"/>
+        <source>Ctrl+=</source>
+        <translation>Ctrl+=</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1470"/>
+        <source>Zoom out</source>
+        <translation>Oddálit</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1473"/>
+        <source>Ctrl+-</source>
+        <translation>Ctrl+-</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1482"/>
+        <source>Zoom reset</source>
+        <translation>Zrušit přiblížení</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.ui" line="1485"/>
+        <source>Ctrl+Shift+=</source>
+        <translation>Ctrl+Shift+=</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="258"/>
+        <source>Confirmation</source>
+        <translation>Potvrzení</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="258"/>
+        <source>Unsaved changes will be lost, are you sure?</source>
+        <translation>Neuložené změny budou ztraceny, jste si jisti?</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="384"/>
+        <source>Open map</source>
+        <translation>Otevřít mapu</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="386"/>
+        <source>All supported maps (*.vmap *.h3m);;VCMI maps(*.vmap);;HoMM3 maps(*.h3m)</source>
+        <translation>Všechny podporované mapy (*.vmap *.h3m);; Mapy VCMI(*.vmap);;Mapy HoMM3(*.h3m)</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="437"/>
+        <source>Save map</source>
+        <translation>Uložit mapu</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="437"/>
+        <source>VCMI maps (*.vmap)</source>
+        <translation>Mapy VCMI (*.vmap)</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="623"/>
+        <source>Type</source>
+        <translation>Druh</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="785"/>
+        <source>View surface</source>
+        <translation>Zobrazit povrch</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="1052"/>
+        <source>No objects selected</source>
+        <translation>Nejsou vybrány žádné objekty</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="1056"/>
+        <source>This operation is irreversible. Do you want to continue?</source>
+        <translation>Tento úkon je nezvratný. Chcete pokračovat?</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="1113"/>
+        <source>Errors occurred. %1 objects were not updated</source>
+        <translation>Nastaly chyby. Nebylo aktualizováno %1 objektů</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="1153"/>
+        <source>Save to image</source>
+        <translation>Uložit do obrázku</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="1172"/>
+        <source>Select maps to convert</source>
+        <translation>Vyberte mapy pro převod</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="1174"/>
+        <source>HoMM3 maps(*.h3m)</source>
+        <translation>Mapy HoMM3 (*.h3m)</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="1178"/>
+        <source>Choose directory to save converted maps</source>
+        <translation>Vyberte složku pro uložení převedených map</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="1191"/>
+        <source>Operation completed</source>
+        <translation>Operace dokončena</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="1191"/>
+        <source>Successfully converted %1 maps</source>
+        <translation>Úspěšně převedeno %1 map</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="1195"/>
+        <source>Failed to convert the map. Abort operation</source>
+        <translation>Převod map selhal. Úkon zrušen</translation>
+    </message>
+</context>
+<context>
+    <name>MapSettings</name>
+    <message>
+        <location filename="../mapsettings/mapsettings.ui" line="23"/>
+        <source>Map settings</source>
+        <translation>Nastavení mapy</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/mapsettings.ui" line="42"/>
+        <source>General</source>
+        <translation>Obecné</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/mapsettings.ui" line="61"/>
+        <source>Mods</source>
+        <translation>Modifikace</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/mapsettings.ui" line="80"/>
+        <source>Events</source>
+        <translation>Události</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/mapsettings.ui" line="99"/>
+        <source>Victory</source>
+        <translation>Vítězství</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/mapsettings.ui" line="118"/>
+        <source>Loss</source>
+        <translation>Prohra</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/mapsettings.ui" line="137"/>
+        <source>Timed</source>
+        <translation>Načasované</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/mapsettings.ui" line="156"/>
+        <source>Rumors</source>
+        <translation>Klepy</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/mapsettings.ui" line="179"/>
+        <source>Abilities</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/mapsettings.ui" line="214"/>
+        <source>Spells</source>
+        <translation>Kouzla</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/mapsettings.ui" line="249"/>
+        <source>Artifacts</source>
+        <translation>Artefakty</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/mapsettings.ui" line="284"/>
+        <source>Heroes</source>
+        <translation>Hrdinové</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/mapsettings.ui" line="322"/>
+        <source>Ok</source>
+        <translation>Dobře</translation>
+    </message>
+</context>
+<context>
+    <name>MapView</name>
+    <message>
+        <location filename="../mapview.cpp" line="623"/>
+        <source>Can&apos;t place object</source>
+        <translation>Nelze umístit objekt</translation>
+    </message>
+</context>
+<context>
+    <name>MessageWidget</name>
+    <message>
+        <location filename="../inspector/messagewidget.ui" line="23"/>
+        <source>Message</source>
+        <translation>Zpráva</translation>
+    </message>
+</context>
+<context>
+    <name>ModSettings</name>
+    <message>
+        <location filename="../mapsettings/modsettings.ui" line="14"/>
+        <source>Form</source>
+        <translation>Formulář</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/modsettings.ui" line="32"/>
+        <source>Mandatory mods to play this map</source>
+        <translation>Potřebné modifikace pro hraní této mapy</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/modsettings.ui" line="46"/>
+        <source>Mod name</source>
+        <translation>Název modifikace</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/modsettings.ui" line="51"/>
+        <source>Version</source>
+        <translation>Verze</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/modsettings.ui" line="61"/>
+        <source>Automatic assignment</source>
+        <translation>Automatické přiřazení</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/modsettings.ui" line="68"/>
+        <source>Set required mods based on objects placed on the map. This method may cause problems if you have customized rewards, garrisons, etc from mods</source>
+        <translation>Nastavit potřebné modifikace v závislosti na umístěných objektech na mapě. Tato metoda může způsobit problém, pokud máte přizpůsobené odměny, posátky atd.  z modifikací</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/modsettings.ui" line="71"/>
+        <source>Map objects mods</source>
+        <translation>Modifikace objektů mapy</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/modsettings.ui" line="81"/>
+        <source>Set all mods having a game content as mandatory</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/modsettings.ui" line="84"/>
+        <source>Full content mods</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>PlayerParams</name>
+    <message>
+        <location filename="../playerparams.ui" line="89"/>
+        <source>Human/CPU</source>
+        <translation>Člověk/CPU</translation>
+    </message>
+    <message>
+        <location filename="../playerparams.ui" line="76"/>
+        <source>CPU only</source>
+        <translation>Pouze CPU</translation>
+    </message>
+    <message>
+        <location filename="../playerparams.ui" line="96"/>
+        <source>Team</source>
+        <translation>Tým</translation>
+    </message>
+    <message>
+        <location filename="../playerparams.ui" line="134"/>
+        <source>Main town</source>
+        <translation>Hlavní město</translation>
+    </message>
+    <message>
+        <location filename="../playerparams.ui" line="117"/>
+        <source>Color</source>
+        <translation>Barva</translation>
+    </message>
+    <message>
+        <location filename="../playerparams.ui" line="164"/>
+        <source>...</source>
+        <translation>...</translation>
+    </message>
+    <message>
+        <location filename="../playerparams.ui" line="221"/>
+        <source>Random faction</source>
+        <translation type="unfinished">Náhodná frakce</translation>
+    </message>
+    <message>
+        <location filename="../playerparams.ui" line="179"/>
+        <source>Generate hero at main</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../playerparams.ui" line="156"/>
+        <source>(default)</source>
+        <translation>(výchozí)</translation>
+    </message>
+    <message>
+        <location filename="../playerparams.cpp" line="111"/>
+        <source>Player ID: %1</source>
+        <translation>ID hráče: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PlayerSettings</name>
+    <message>
+        <location filename="../playersettings.ui" line="20"/>
+        <source>Player settings</source>
+        <translation>Hráčské nastavení</translation>
+    </message>
+    <message>
+        <location filename="../playersettings.ui" line="63"/>
+        <source>Players</source>
+        <translation>Hráči</translation>
+    </message>
+    <message>
+        <location filename="../playersettings.ui" line="74"/>
+        <source>1</source>
+        <translation>1</translation>
+    </message>
+    <message>
+        <location filename="../playersettings.ui" line="117"/>
+        <source>Ok</source>
+        <translation>Dobře</translation>
+    </message>
+</context>
+<context>
+    <name>PortraitWidget</name>
+    <message>
+        <location filename="../inspector/portraitwidget.ui" line="14"/>
+        <source>Portrait</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../inspector/portraitwidget.ui" line="52"/>
+        <location filename="../inspector/portraitwidget.ui" line="71"/>
+        <source>...</source>
+        <translation>...</translation>
+    </message>
+    <message>
+        <location filename="../inspector/portraitwidget.ui" line="85"/>
+        <source>Default</source>
+        <translation>Výchozí</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../inspector/heroskillswidget.cpp" line="19"/>
+        <source>Beginner</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../inspector/heroskillswidget.cpp" line="20"/>
+        <source>Advanced</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../inspector/heroskillswidget.cpp" line="21"/>
+        <source>Expert</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../inspector/inspector.cpp" line="35"/>
+        <source>Compliant</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../inspector/inspector.cpp" line="36"/>
+        <source>Friendly</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../inspector/inspector.cpp" line="37"/>
+        <source>Aggressive</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../inspector/inspector.cpp" line="38"/>
+        <source>Hostile</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../inspector/inspector.cpp" line="39"/>
+        <source>Savage</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../inspector/inspector.cpp" line="478"/>
+        <location filename="../inspector/inspector.cpp" line="845"/>
+        <source>neutral</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../inspector/inspector.cpp" line="843"/>
+        <source>UNFLAGGABLE</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>QuestWidget</name>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="17"/>
+        <source>Mission goal</source>
+        <translation>Cíl mise</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="28"/>
+        <source>Day of week</source>
+        <translation>Den týdne</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="45"/>
+        <source>Days passed</source>
+        <translation>Uběhlých dní</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="79"/>
+        <source>Hero level</source>
+        <translation>Úroveň hrdiny</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="96"/>
+        <source>Hero experience</source>
+        <translation>Zkušenosti hrdiny</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="136"/>
+        <source>Spell points</source>
+        <translation>Magická energie</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="156"/>
+        <source>%</source>
+        <translation>%</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="183"/>
+        <source>Kill hero/monster</source>
+        <translation>Zabít hrdinu/příšeru</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="197"/>
+        <source>...</source>
+        <translation>...</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="206"/>
+        <source>Primary skills</source>
+        <translation>Základní schopnosti</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="221"/>
+        <source>Attack</source>
+        <translation>Útok</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="231"/>
+        <source>Defence</source>
+        <translation>Obrana</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="241"/>
+        <source>Spell power</source>
+        <translation>Síla kouzel</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="251"/>
+        <source>Knowledge</source>
+        <translation>Znalosti</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="289"/>
+        <source>Resources</source>
+        <translation>Zdroje</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="338"/>
+        <source>Artifacts</source>
+        <translation>Artefakty</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="370"/>
+        <source>Spells</source>
+        <translation>Kouzla</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="402"/>
+        <source>Skills</source>
+        <translation>Dovednosti</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="442"/>
+        <source>Creatures</source>
+        <translation>Jednotky</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="491"/>
+        <source>Add</source>
+        <translation>Přidat</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="498"/>
+        <source>Remove</source>
+        <translation>Odebrat</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="538"/>
+        <source>Heroes</source>
+        <translation>Hrdinové</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="570"/>
+        <source>Hero classes</source>
+        <translation>Třídy hrdinů</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.ui" line="602"/>
+        <source>Players</source>
+        <translation>Hráči</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.cpp" line="34"/>
+        <source>None</source>
+        <translation>Žádný</translation>
+    </message>
+    <message>
+        <location filename="../inspector/questwidget.cpp" line="36"/>
+        <source>Day %1</source>
+        <translation>Den %1</translation>
+    </message>
+</context>
+<context>
+    <name>RewardsWidget</name>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="17"/>
+        <source>Rewards</source>
+        <translation>Odměny</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="33"/>
+        <location filename="../inspector/rewardswidget.ui" line="703"/>
+        <location filename="../inspector/rewardswidget.ui" line="818"/>
+        <location filename="../inspector/rewardswidget.ui" line="1407"/>
+        <source>Add</source>
+        <translation>Přidat</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="40"/>
+        <location filename="../inspector/rewardswidget.ui" line="710"/>
+        <location filename="../inspector/rewardswidget.ui" line="825"/>
+        <location filename="../inspector/rewardswidget.ui" line="1414"/>
+        <source>Remove</source>
+        <translation>Odebrat</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="61"/>
+        <source>Visit mode</source>
+        <translation>Režim návštěvy</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="82"/>
+        <source>Select mode</source>
+        <translation>Režim výběru</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="104"/>
+        <source>On select text</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="111"/>
+        <source>Can refuse</source>
+        <translation>Lze odmítnout</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="118"/>
+        <source>Reset parameters</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="138"/>
+        <source>Period</source>
+        <translation>Cyklus</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="145"/>
+        <source> days</source>
+        <translation> dní</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="157"/>
+        <source>Reset visitors</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="164"/>
+        <source>Reset rewards</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="176"/>
+        <source>Window type</source>
+        <translation>Druh okna</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="196"/>
+        <source>Event info</source>
+        <translation>Informace události</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="214"/>
+        <source>Message to be displayed on granting of this reward</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="225"/>
+        <source>Reward</source>
+        <translation>Odměna</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="245"/>
+        <location filename="../inspector/rewardswidget.ui" line="1032"/>
+        <source>Hero level</source>
+        <translation>Úroveň hrdiny</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="262"/>
+        <location filename="../inspector/rewardswidget.ui" line="1049"/>
+        <source>Hero experience</source>
+        <translation>Zkušenosti hrdiny</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="302"/>
+        <location filename="../inspector/rewardswidget.ui" line="1089"/>
+        <source>Spell points</source>
+        <translation>Magická energie</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="325"/>
+        <location filename="../inspector/rewardswidget.ui" line="345"/>
+        <location filename="../inspector/rewardswidget.ui" line="379"/>
+        <location filename="../inspector/rewardswidget.ui" line="1109"/>
+        <source>%</source>
+        <translation>%</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="338"/>
+        <source>Overflow</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="362"/>
+        <source>Movement</source>
+        <translation>Pohyb</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="405"/>
+        <source>Remove object</source>
+        <translation>Odstranit objekt</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="414"/>
+        <location filename="../inspector/rewardswidget.ui" line="1134"/>
+        <source>Primary skills</source>
+        <translation>Hlavní dovednosti</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="429"/>
+        <location filename="../inspector/rewardswidget.ui" line="1149"/>
+        <source>Attack</source>
+        <translation>Útok</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="443"/>
+        <location filename="../inspector/rewardswidget.ui" line="1159"/>
+        <source>Defence</source>
+        <translation>Obrana</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="457"/>
+        <location filename="../inspector/rewardswidget.ui" line="1169"/>
+        <source>Spell power</source>
+        <translation>Síla kouzel</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="471"/>
+        <location filename="../inspector/rewardswidget.ui" line="1179"/>
+        <source>Knowledge</source>
+        <translation>Znalosti</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="501"/>
+        <location filename="../inspector/rewardswidget.ui" line="1205"/>
+        <source>Resources</source>
+        <translation>Zdroje</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="550"/>
+        <location filename="../inspector/rewardswidget.ui" line="1254"/>
+        <source>Artifacts</source>
+        <translation>Artefakty</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="582"/>
+        <location filename="../inspector/rewardswidget.ui" line="1286"/>
+        <source>Spells</source>
+        <translation>Kouzla</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="614"/>
+        <location filename="../inspector/rewardswidget.ui" line="1318"/>
+        <source>Skills</source>
+        <translation>Dovednosti</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="654"/>
+        <location filename="../inspector/rewardswidget.ui" line="1358"/>
+        <source>Creatures</source>
+        <translation>Jednotky</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="747"/>
+        <source>Bonuses</source>
+        <translation>Bonusy</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="764"/>
+        <location filename="../inspector/rewardswidget.ui" line="856"/>
+        <source>Duration</source>
+        <translation>Délka</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="774"/>
+        <location filename="../inspector/rewardswidget.ui" line="861"/>
+        <source>Type</source>
+        <translation>Druh</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="784"/>
+        <location filename="../inspector/rewardswidget.ui" line="866"/>
+        <source>Value</source>
+        <translation>Hodnota</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="875"/>
+        <source>Cast</source>
+        <translation>Seslat kouzlo</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="881"/>
+        <source>Cast an adventure map spell</source>
+        <translation>Seslat kouzlo mapy světa</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="890"/>
+        <source>Spell</source>
+        <translation>Kouzlo</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="911"/>
+        <source>Magic school level</source>
+        <translation>Úroveň školy magie</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="961"/>
+        <source>Limiter</source>
+        <translation>Omezovač</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="981"/>
+        <source>Day of week</source>
+        <translation>Den týdne</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="998"/>
+        <source>Days passed</source>
+        <translation>Uběhlých dní</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="1454"/>
+        <source>Heroes</source>
+        <translation>Hrdinové</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="1486"/>
+        <source>Hero classes</source>
+        <translation>Třídy hrdinů</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.ui" line="1518"/>
+        <source>Players</source>
+        <translation>Hráči</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.cpp" line="45"/>
+        <source>None</source>
+        <translation>Žádný</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.cpp" line="47"/>
+        <source>Day %1</source>
+        <translation>Den %1</translation>
+    </message>
+    <message>
+        <location filename="../inspector/rewardswidget.cpp" line="234"/>
+        <location filename="../inspector/rewardswidget.cpp" line="605"/>
+        <source>Reward %1</source>
+        <translation>Odměna %1</translation>
+    </message>
+</context>
+<context>
+    <name>RumorSettings</name>
+    <message>
+        <location filename="../mapsettings/rumorsettings.ui" line="14"/>
+        <source>Form</source>
+        <translation>Formulář</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/rumorsettings.ui" line="37"/>
+        <source>Tavern rumors</source>
+        <translation>Městské klepy</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/rumorsettings.ui" line="63"/>
+        <source>Add</source>
+        <translation>Přidat</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/rumorsettings.ui" line="82"/>
+        <source>Remove</source>
+        <translation>Odebrat</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/rumorsettings.cpp" line="59"/>
+        <source>New rumor</source>
+        <translation>Nový klep</translation>
+    </message>
+</context>
+<context>
+    <name>TimedEvent</name>
+    <message>
+        <location filename="../mapsettings/timedevent.ui" line="17"/>
+        <source>Timed event</source>
+        <translation>Načasovaná událost</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/timedevent.ui" line="28"/>
+        <source>Event name</source>
+        <translation>Název události</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/timedevent.ui" line="35"/>
+        <source>Type event message text</source>
+        <translation>Zadejte text zprávy události</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/timedevent.ui" line="47"/>
+        <source>affects human</source>
+        <translation>ovlivňuje lidi</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/timedevent.ui" line="54"/>
+        <source>affects AI</source>
+        <translation>ovlivňuje AI</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/timedevent.ui" line="70"/>
+        <source>Day of first occurance</source>
+        <translation>Den prvního výskytu</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/timedevent.ui" line="87"/>
+        <source>Repeat after (0 = no repeat)</source>
+        <translation>Opakovat po (0 = bez opak.)</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/timedevent.ui" line="108"/>
+        <source>Affected players</source>
+        <translation>Ovlivnění hráči</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/timedevent.ui" line="131"/>
+        <source>Resources</source>
+        <translation>Zdroje</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/timedevent.ui" line="190"/>
+        <source>type</source>
+        <translation>druh</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/timedevent.ui" line="195"/>
+        <source>qty</source>
+        <translation>množství</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/timedevent.ui" line="203"/>
+        <source>Ok</source>
+        <translation>Dobře</translation>
+    </message>
+</context>
+<context>
+    <name>TownBulidingsWidget</name>
+    <message>
+        <location filename="../inspector/townbulidingswidget.ui" line="29"/>
+        <source>Buildings</source>
+        <translation>Budovy</translation>
+    </message>
+</context>
+<context>
+    <name>Translations</name>
+    <message>
+        <location filename="../mapsettings/translations.ui" line="14"/>
+        <source>Map translations</source>
+        <translation>Překlady mapy</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/translations.ui" line="31"/>
+        <source>Language</source>
+        <translation>Jazyk</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/translations.ui" line="48"/>
+        <source>Suppported</source>
+        <translation>Podporovaný</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/translations.ui" line="70"/>
+        <source>String ID</source>
+        <translation>ID řetězce</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/translations.ui" line="75"/>
+        <source>Text</source>
+        <translation>Text</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/translations.cpp" line="156"/>
+        <location filename="../mapsettings/translations.cpp" line="160"/>
+        <source>Remove translation</source>
+        <translation>Odebrat překlad</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/translations.cpp" line="156"/>
+        <source>Default language cannot be removed</source>
+        <translation>Výchozí jazyk nemůže být odstraněn</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/translations.cpp" line="160"/>
+        <source>All existing text records for this language will be removed. Continue?</source>
+        <translation>Všechny textové záznamy pro tento jazyk budou odstraněny. Pokračovat?</translation>
+    </message>
+</context>
+<context>
+    <name>Validator</name>
+    <message>
+        <location filename="../validator.ui" line="17"/>
+        <source>Map validation results</source>
+        <translation>Výsledky posudku mapy</translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="52"/>
+        <source>Map is not loaded</source>
+        <translation>Mapa není načtena</translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="72"/>
+        <source>No factions allowed for player %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="75"/>
+        <source>No players allowed to play this map</source>
+        <translation>Žádní hráči nejsou dovoleni hrát tuto mapu</translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="77"/>
+        <source>Map is allowed for one player and cannot be started</source>
+        <translation>Mapa je pouze pro jednoho hráče na nemůže být spuštěna</translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="79"/>
+        <source>No human players allowed to play this map</source>
+        <translation>Žádní lidští hráči nejsou dovoleni hrát tuto mapu</translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="95"/>
+        <source>Armored instance %1 is UNFLAGGABLE but must have NEUTRAL or player owner</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="101"/>
+        <source>Object %1 is assigned to non-playable player %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="108"/>
+        <source>Town %1 has undefined owner %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="118"/>
+        <source>Prison %1 must be a NEUTRAL</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="124"/>
+        <source>Hero %1 must have an owner</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="129"/>
+        <source>Hero %1 is prohibited by map settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="132"/>
+        <source>Hero %1 has duplicate on map</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="135"/>
+        <source>Hero %1 has an empty type and must be removed</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="146"/>
+        <source>Spell scroll %1 is prohibited by map settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="149"/>
+        <source>Spell scroll %1 doesn&apos;t have instance assigned and must be removed</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="155"/>
+        <source>Artifact %1 is prohibited by map settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="164"/>
+        <source>Player %1 doesn&apos;t have any starting town</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="168"/>
+        <source>Map name is not specified</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="170"/>
+        <source>Map description is not specified</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="177"/>
+        <source>Map contains object from mod &quot;%1&quot;, but doesn&apos;t require it</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="183"/>
+        <source>Exception occurs during validation: %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../validator.cpp" line="187"/>
+        <source>Unknown exception occurs during validation</source>
+        <translation>Nasta neznámá výjimka při posudku</translation>
+    </message>
+</context>
+<context>
+    <name>VictoryConditions</name>
+    <message>
+        <location filename="../mapsettings/victoryconditions.ui" line="14"/>
+        <source>Form</source>
+        <translation>Formulář</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/victoryconditions.ui" line="40"/>
+        <source>Victory message</source>
+        <translation>Zpráva vítězství</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/victoryconditions.ui" line="59"/>
+        <source>Only for human players</source>
+        <translation>Jen pro lidské hráče</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/victoryconditions.ui" line="66"/>
+        <source>Allow standard victory</source>
+        <translation>Povolit standardní výhru</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/victoryconditions.ui" line="79"/>
+        <source>Parameters</source>
+        <translation>Parametry</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/victoryconditions.cpp" line="36"/>
+        <source>No special victory</source>
+        <translation>Bez speciálního vítězství</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/victoryconditions.cpp" line="37"/>
+        <source>Capture artifact</source>
+        <translation>Získat artefakt</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/victoryconditions.cpp" line="38"/>
+        <source>Hire creatures</source>
+        <translation>Najmout bojovníky</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/victoryconditions.cpp" line="39"/>
+        <source>Accumulate resources</source>
+        <translation>Nashromáždit zdroje</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/victoryconditions.cpp" line="40"/>
+        <source>Construct building</source>
+        <translation>Postavit budovu</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/victoryconditions.cpp" line="41"/>
+        <source>Capture town</source>
+        <translation>Získat město</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/victoryconditions.cpp" line="42"/>
+        <source>Defeat hero</source>
+        <translation>Porazit hrdinu</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/victoryconditions.cpp" line="43"/>
+        <source>Transport artifact</source>
+        <translation>Přesunout artefakt</translation>
+    </message>
+    <message>
+        <location filename="../mapsettings/victoryconditions.cpp" line="44"/>
+        <source>Kill monster</source>
+        <translation>Zabít příšeru</translation>
+    </message>
+</context>
+<context>
+    <name>WindowNewMap</name>
+    <message>
+        <location filename="../windownewmap.ui" line="32"/>
+        <source>Create new map</source>
+        <translation>Vytvořit novou mapu</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="47"/>
+        <source>Map size</source>
+        <translation>Velikost mapy</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="62"/>
+        <source>Two level map</source>
+        <translation>Dvě úrovně</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="82"/>
+        <source>Height</source>
+        <translation>Výška</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="114"/>
+        <source>Width</source>
+        <translation>Šířka</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="152"/>
+        <source>S (36x36)</source>
+        <translation>S (36x36)</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="157"/>
+        <source>M (72x72)</source>
+        <translation>M (72x72)</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="162"/>
+        <source>L (108x108)</source>
+        <translation>L (108x108)</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="167"/>
+        <source>XL (144x144)</source>
+        <translation>XL (144x144)</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="193"/>
+        <source>Random map</source>
+        <translation>Náhodná mapa</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="205"/>
+        <source>Players</source>
+        <translation>Hráči</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="724"/>
+        <source>0</source>
+        <translation>0</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="349"/>
+        <source>Human/Computer</source>
+        <translation>Hráč/počítač</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="221"/>
+        <location filename="../windownewmap.ui" line="289"/>
+        <location filename="../windownewmap.ui" line="443"/>
+        <location filename="../windownewmap.ui" line="584"/>
+        <source>Random</source>
+        <translation>Náhodně</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="269"/>
+        <source>Computer only</source>
+        <translation>Pouze počítač</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="380"/>
+        <source>Human teams</source>
+        <translation>Lidské týmy</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="399"/>
+        <source>Computer teams</source>
+        <translation>Počítačové týmy</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="416"/>
+        <source>Monster strength</source>
+        <translation>Síla příšer</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="465"/>
+        <source>Weak</source>
+        <translation>Slabá</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="484"/>
+        <location filename="../windownewmap.ui" line="625"/>
+        <source>Normal</source>
+        <translation>Normální</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="503"/>
+        <source>Strong</source>
+        <translation>Silná</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="557"/>
+        <source>Water content</source>
+        <translation>Obsah vody</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="606"/>
+        <source>None</source>
+        <translation>Žádná</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="644"/>
+        <source>Islands</source>
+        <translation>Ostrovy</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="689"/>
+        <source>Template</source>
+        <translation>Šablona</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="737"/>
+        <source>Custom seed</source>
+        <translation>Vlastní semínko</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="751"/>
+        <source>Generate random map</source>
+        <translation>Vygenerovat náhodnou mapu</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="785"/>
+        <source>Ok</source>
+        <translation>Dobře</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.ui" line="804"/>
+        <source>Cancel</source>
+        <translation>Zrušit</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.cpp" line="276"/>
+        <source>No template</source>
+        <translation>Bez šablony</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.cpp" line="276"/>
+        <source>No template for parameters scecified. Random map cannot be generated.</source>
+        <translation>Žádná šablona pro vybrané parametry. Náhodná mapa nemůže být vygenerována.</translation>
+    </message>
+    <message>
+        <location filename="../windownewmap.cpp" line="296"/>
+        <source>RMG failure</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>main</name>
+    <message>
+        <location filename="../mainwindow.cpp" line="106"/>
+        <source>Filepath of the map to open.</source>
+        <translation>Cesta k souboru mapy pro otevření.</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="109"/>
+        <source>Extract original H3 archives into a separate folder.</source>
+        <translation>Rozbalit originální archivy H3 do zvláštní složky.</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="110"/>
+        <source>From an extracted archive, it Splits TwCrPort, CPRSMALL, FlagPort, ITPA, ITPt, Un32 and Un44 into individual PNG&apos;s.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="111"/>
+        <source>From an extracted archive, Converts single Images (found in Images folder) from .pcx to png.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="112"/>
+        <source>Delete original files, for the ones split / converted.</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+</TS>

+ 1 - 5
server/CGameHandler.cpp

@@ -489,11 +489,6 @@ void CGameHandler::handleReceivedPack(CPackForServer * pack)
 	vstd::clear_pointer(pack);
 }
 
-
-CGameHandler::CGameHandler()
-	: turnTimerHandler(*this)
-{}
-
 CGameHandler::CGameHandler(CVCMIServer * lobby)
 	: lobby(lobby)
 	, heroPool(std::make_unique<HeroPoolProcessor>(this))
@@ -518,6 +513,7 @@ CGameHandler::~CGameHandler()
 {
 	delete spellEnv;
 	delete gs;
+	gs = nullptr;
 }
 
 void CGameHandler::reinitScripting()

+ 1 - 2
server/CGameHandler.h

@@ -92,8 +92,7 @@ public:
 	bool isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2);
 	void giveSpells(const CGTownInstance *t, const CGHeroInstance *h);
 
-	CGameHandler();
-	CGameHandler(CVCMIServer * lobby);
+	explicit CGameHandler(CVCMIServer * lobby);
 	~CGameHandler();
 
 	//////////////////////////////////////////////////////////////////////////

+ 152 - 61
server/processors/PlayerMessageProcessor.cpp

@@ -24,6 +24,7 @@
 #include "../../lib/mapObjects/CGTownInstance.h"
 #include "../../lib/modding/IdentifierStorage.h"
 #include "../../lib/modding/ModScope.h"
+#include "../../lib/mapping/CMap.h"
 #include "../../lib/networkPacks/PacksForClient.h"
 #include "../../lib/networkPacks/StackLocation.h"
 
@@ -289,6 +290,7 @@ void PlayerMessageProcessor::cheatMovement(PlayerColor player, const CGHeroInsta
 
 	SetMovePoints smp;
 	smp.hid = hero->id;
+	bool unlimited = false;
 	try
 	{
 		smp.val = std::stol(words.at(0));;
@@ -296,16 +298,27 @@ void PlayerMessageProcessor::cheatMovement(PlayerColor player, const CGHeroInsta
 	catch(std::logic_error&)
 	{
 		smp.val = 1000000;
+		unlimited = true;
 	}
 
 	gameHandler->sendAndApply(&smp);
 
 	GiveBonus gb(GiveBonus::ETarget::OBJECT);
 	gb.bonus.type = BonusType::FREE_SHIP_BOARDING;
-	gb.bonus.duration = BonusDuration::ONE_DAY;
+	gb.bonus.duration = unlimited ? BonusDuration::PERMANENT : BonusDuration::ONE_DAY;
 	gb.bonus.source = BonusSource::OTHER;
 	gb.id = hero->id;
 	gameHandler->giveHeroBonus(&gb);
+
+	if(unlimited)
+	{
+		GiveBonus gb(GiveBonus::ETarget::OBJECT);
+		gb.bonus.type = BonusType::UNLIMITED_MOVEMENT;
+		gb.bonus.duration = BonusDuration::PERMANENT;
+		gb.bonus.source = BonusSource::OTHER;
+		gb.id = hero->id;
+		gameHandler->giveHeroBonus(&gb);
+	}
 }
 
 void PlayerMessageProcessor::cheatResources(PlayerColor player, std::vector<std::string> words)
@@ -365,6 +378,63 @@ void PlayerMessageProcessor::cheatMapReveal(PlayerColor player, bool reveal)
 	gameHandler->sendAndApply(&fc);
 }
 
+void PlayerMessageProcessor::cheatPuzzleReveal(PlayerColor player)
+{
+	TeamState *t = gameHandler->gameState()->getPlayerTeam(player);
+
+	for(auto & obj : gameHandler->gameState()->map->objects)
+	{
+		if(obj->ID == Obj::OBELISK)
+		{
+			gameHandler->setObjPropertyID(obj->id, ObjProperty::OBELISK_VISITED, t->id);
+			for(const auto & color : t->players)
+			{
+				gameHandler->setObjPropertyID(obj->id, ObjProperty::VISITED, color);
+
+				PlayerCheated pc;
+				pc.player = color;
+				gameHandler->sendAndApply(&pc);
+			}
+		}
+	}
+}
+
+void PlayerMessageProcessor::cheatMaxLuck(PlayerColor player, const CGHeroInstance * hero)
+{
+	if (!hero)
+		return;
+
+	GiveBonus gb;
+	gb.bonus = Bonus(BonusDuration::PERMANENT, BonusType::MAX_LUCK, BonusSource::OTHER, 0, BonusSourceID(Obj(Obj::NO_OBJ)));
+	gb.id = hero->id;
+
+	gameHandler->giveHeroBonus(&gb);
+}
+
+void PlayerMessageProcessor::cheatFly(PlayerColor player, const CGHeroInstance * hero)
+{
+	if (!hero)
+		return;
+		
+	GiveBonus gb;
+	gb.bonus = Bonus(BonusDuration::PERMANENT, BonusType::FLYING_MOVEMENT, BonusSource::OTHER, 0, BonusSourceID(Obj(Obj::NO_OBJ)));
+	gb.id = hero->id;
+
+	gameHandler->giveHeroBonus(&gb);
+}
+
+void PlayerMessageProcessor::cheatMaxMorale(PlayerColor player, const CGHeroInstance * hero)
+{
+	if (!hero)
+		return;
+		
+	GiveBonus gb;
+	gb.bonus = Bonus(BonusDuration::PERMANENT, BonusType::MAX_MORALE, BonusSource::OTHER, 0, BonusSourceID(Obj(Obj::NO_OBJ)));
+	gb.id = hero->id;
+
+	gameHandler->giveHeroBonus(&gb);
+}
+
 bool PlayerMessageProcessor::handleCheatCode(const std::string & cheat, PlayerColor player, ObjectInstanceID currObj)
 {
 	std::vector<std::string> words;
@@ -383,19 +453,23 @@ bool PlayerMessageProcessor::handleCheatCode(const std::string & cheat, PlayerCo
 		"vcmimelkor",    "vcmilose",      "nwcbluepill",
 		"vcmisilmaril",  "vcmiwin",       "nwcredpill",
 		"vcmieagles",    "vcmimap",       "nwcwhatisthematrix",
-		"vcmiungoliant", "vcmihidemap",   "nwcignoranceisbliss"
+		"vcmiungoliant", "vcmihidemap",   "nwcignoranceisbliss",
+		"vcmiobelisk",                    "nwcoracle"
 	};
 	std::vector<std::string> heroTargetedCheats = {
-		"vcmiainur",             "vcmiarchangel",   "nwctrinity",
-		"vcmiangband",           "vcmiblackknight", "nwcagents",
-		"vcmiglaurung",          "vcmicrystal",     "vcmiazure",
-		"vcmifaerie",            "vcmiarmy",        "vcminissi",
-		"vcmiistari",            "vcmispells",      "nwcthereisnospoon",
-		"vcminoldor",            "vcmimachines",     "nwclotsofguns",
-		"vcmiglorfindel",        "vcmilevel",       "nwcneo",
-		"vcminahar",             "vcmimove",        "nwcnebuchadnezzar",
-		"vcmiforgeofnoldorking", "vcmiartifacts",
-		"vcmiolorin",            "vcmiexp",
+		"vcmiainur",               "vcmiarchangel",   "nwctrinity",
+		"vcmiangband",             "vcmiblackknight", "nwcagents",
+		"vcmiglaurung",            "vcmicrystal",     "vcmiazure",
+		"vcmifaerie",              "vcmiarmy",        "vcminissi",
+		"vcmiistari",              "vcmispells",      "nwcthereisnospoon",
+		"vcminoldor",              "vcmimachines",    "nwclotsofguns",
+		"vcmiglorfindel",          "vcmilevel",       "nwcneo",
+		"vcminahar",               "vcmimove",        "nwcnebuchadnezzar",
+		"vcmiforgeofnoldorking",   "vcmiartifacts",
+		"vcmiolorin",              "vcmiexp",
+		"vcmiluck",                                   "nwcfollowthewhiterabbit", 
+		"vcmimorale",                                 "nwcmorpheus",
+		"vcmigod",                                    "nwctheone"
 	};
 
 	if (!vstd::contains(townTargetedCheats, cheatName) && !vstd::contains(playerTargetedCheats, cheatName) && !vstd::contains(heroTargetedCheats, cheatName))
@@ -469,60 +543,77 @@ void PlayerMessageProcessor::executeCheatCode(const std::string & cheatName, Pla
 	const auto & doCheatDefeat = [&]() { cheatDefeat(player); };
 	const auto & doCheatMapReveal = [&]() { cheatMapReveal(player, true); };
 	const auto & doCheatMapHide = [&]() { cheatMapReveal(player, false); };
+	const auto & doCheatRevealPuzzle = [&]() { cheatPuzzleReveal(player); };
+	const auto & doCheatMaxLuck = [&]() { cheatMaxLuck(player, hero); };
+	const auto & doCheatMaxMorale = [&]() { cheatMaxMorale(player, hero); };
+	const auto & doCheatTheOne = [&]()
+	{
+		if(!hero)
+			return;
+		cheatMapReveal(player, true);
+		cheatGiveArmy(player, hero, { "archangel", "5" });
+		cheatMovement(player, hero, { });
+		cheatFly(player, hero);
+	};
 
 	// Unimplemented H3 cheats:
-	// nwcfollowthewhiterabbit - The currently selected hero permanently gains maximum luck.
-	// nwcmorpheus - The currently selected hero permanently gains maximum morale.
-	// nwcoracle - The puzzle map is permanently revealed.
 	// nwcphisherprice - Changes and brightens the game colors.
 
 	std::map<std::string, std::function<void()>> callbacks = {
-		{"vcmiainur",            [&] () {doCheatGiveArmyFixed({ "archangel", "5" });} },
-		{"nwctrinity",           [&] () {doCheatGiveArmyFixed({ "archangel", "5" });} },
-		{"vcmiangband",          [&] () {doCheatGiveArmyFixed({ "blackKnight", "10" });} },
-		{"vcmiglaurung",         [&] () {doCheatGiveArmyFixed({ "crystalDragon", "5000" });} },
-		{"vcmiarchangel",        [&] () {doCheatGiveArmyFixed({ "archangel", "5" });} },
-		{"nwcagents",            [&] () {doCheatGiveArmyFixed({ "blackKnight", "10" });} },
-		{"vcmiblackknight",      [&] () {doCheatGiveArmyFixed({ "blackKnight", "10" });} },
-		{"vcmicrystal",          [&] () {doCheatGiveArmyFixed({ "crystalDragon", "5000" });} },
-		{"vcmiazure",            [&] () {doCheatGiveArmyFixed({ "azureDragon", "5000" });} },
-		{"vcmifaerie",           [&] () {doCheatGiveArmyFixed({ "fairieDragon", "5000" });} },
-		{"vcmiarmy",              doCheatGiveArmyCustom },
-		{"vcminissi",             doCheatGiveArmyCustom },
-		{"vcmiistari",            doCheatGiveSpells     },
-		{"vcmispells",            doCheatGiveSpells     },
-		{"nwcthereisnospoon",     doCheatGiveSpells     },
-		{"vcmiarmenelos",         doCheatBuildTown      },
-		{"vcmibuild",             doCheatBuildTown      },
-		{"nwczion",               doCheatBuildTown      },
-		{"vcminoldor",            doCheatGiveMachines   },
-		{"vcmimachines",          doCheatGiveMachines   },
-		{"nwclotsofguns",         doCheatGiveMachines   },
-		{"vcmiforgeofnoldorking", doCheatGiveArtifacts  },
-		{"vcmiartifacts",         doCheatGiveArtifacts  },
-		{"vcmiglorfindel",        doCheatLevelup        },
-		{"vcmilevel",             doCheatLevelup        },
-		{"nwcneo",                doCheatLevelup        },
-		{"vcmiolorin",            doCheatExperience     },
-		{"vcmiexp",               doCheatExperience     },
-		{"vcminahar",             doCheatMovement       },
-		{"vcmimove",              doCheatMovement       },
-		{"nwcnebuchadnezzar",     doCheatMovement       },
-		{"vcmiformenos",          doCheatResources      },
-		{"vcmiresources",         doCheatResources      },
-		{"nwctheconstruct",       doCheatResources      },
-		{"nwcbluepill",           doCheatDefeat         },
-		{"vcmimelkor",            doCheatDefeat         },
-		{"vcmilose",              doCheatDefeat         },
-		{"nwcredpill",            doCheatVictory        },
-		{"vcmisilmaril",          doCheatVictory        },
-		{"vcmiwin",               doCheatVictory        },
-		{"nwcwhatisthematrix",    doCheatMapReveal      },
-		{"vcmieagles",            doCheatMapReveal      },
-		{"vcmimap",               doCheatMapReveal      },
-		{"vcmiungoliant",         doCheatMapHide        },
-		{"vcmihidemap",           doCheatMapHide        },
-		{"nwcignoranceisbliss",   doCheatMapHide        },
+		{"vcmiainur",              [&] () {doCheatGiveArmyFixed({ "archangel", "5" });} },
+		{"nwctrinity",             [&] () {doCheatGiveArmyFixed({ "archangel", "5" });} },
+		{"vcmiangband",            [&] () {doCheatGiveArmyFixed({ "blackKnight", "10" });} },
+		{"vcmiglaurung",           [&] () {doCheatGiveArmyFixed({ "crystalDragon", "5000" });} },
+		{"vcmiarchangel",          [&] () {doCheatGiveArmyFixed({ "archangel", "5" });} },
+		{"nwcagents",              [&] () {doCheatGiveArmyFixed({ "blackKnight", "10" });} },
+		{"vcmiblackknight",        [&] () {doCheatGiveArmyFixed({ "blackKnight", "10" });} },
+		{"vcmicrystal",            [&] () {doCheatGiveArmyFixed({ "crystalDragon", "5000" });} },
+		{"vcmiazure",              [&] () {doCheatGiveArmyFixed({ "azureDragon", "5000" });} },
+		{"vcmifaerie",             [&] () {doCheatGiveArmyFixed({ "fairieDragon", "5000" });} },
+		{"vcmiarmy",                doCheatGiveArmyCustom },
+		{"vcminissi",               doCheatGiveArmyCustom },
+		{"vcmiistari",              doCheatGiveSpells     },
+		{"vcmispells",              doCheatGiveSpells     },
+		{"nwcthereisnospoon",       doCheatGiveSpells     },
+		{"vcmiarmenelos",           doCheatBuildTown      },
+		{"vcmibuild",               doCheatBuildTown      },
+		{"nwczion",                 doCheatBuildTown      },
+		{"vcminoldor",              doCheatGiveMachines   },
+		{"vcmimachines",            doCheatGiveMachines   },
+		{"nwclotsofguns",           doCheatGiveMachines   },
+		{"vcmiforgeofnoldorking",   doCheatGiveArtifacts  },
+		{"vcmiartifacts",           doCheatGiveArtifacts  },
+		{"vcmiglorfindel",          doCheatLevelup        },
+		{"vcmilevel",               doCheatLevelup        },
+		{"nwcneo",                  doCheatLevelup        },
+		{"vcmiolorin",              doCheatExperience     },
+		{"vcmiexp",                 doCheatExperience     },
+		{"vcminahar",               doCheatMovement       },
+		{"vcmimove",                doCheatMovement       },
+		{"nwcnebuchadnezzar",       doCheatMovement       },
+		{"vcmiformenos",            doCheatResources      },
+		{"vcmiresources",           doCheatResources      },
+		{"nwctheconstruct",         doCheatResources      },
+		{"nwcbluepill",             doCheatDefeat         },
+		{"vcmimelkor",              doCheatDefeat         },
+		{"vcmilose",                doCheatDefeat         },
+		{"nwcredpill",              doCheatVictory        },
+		{"vcmisilmaril",            doCheatVictory        },
+		{"vcmiwin",                 doCheatVictory        },
+		{"nwcwhatisthematrix",      doCheatMapReveal      },
+		{"vcmieagles",              doCheatMapReveal      },
+		{"vcmimap",                 doCheatMapReveal      },
+		{"vcmiungoliant",           doCheatMapHide        },
+		{"vcmihidemap",             doCheatMapHide        },
+		{"nwcignoranceisbliss",     doCheatMapHide        },
+		{"vcmiobelisk",             doCheatRevealPuzzle   },
+		{"nwcoracle",               doCheatRevealPuzzle   },
+		{"vcmiluck",                doCheatMaxLuck        },
+		{"nwcfollowthewhiterabbit", doCheatMaxLuck        },
+		{"vcmimorale",              doCheatMaxMorale      },
+		{"nwcmorpheus",             doCheatMaxMorale      },
+		{"vcmigod",                 doCheatTheOne         },
+		{"nwctheone",               doCheatTheOne         },
 	};
 
 	assert(callbacks.count(cheatName));

+ 4 - 0
server/processors/PlayerMessageProcessor.h

@@ -37,6 +37,10 @@ class PlayerMessageProcessor
 	void cheatVictory(PlayerColor player);
 	void cheatDefeat(PlayerColor player);
 	void cheatMapReveal(PlayerColor player, bool reveal);
+	void cheatPuzzleReveal(PlayerColor player);
+	void cheatMaxLuck(PlayerColor player, const CGHeroInstance * hero);
+	void cheatMaxMorale(PlayerColor player, const CGHeroInstance * hero);
+	void cheatFly(PlayerColor player, const CGHeroInstance * hero);
 
 public:
 	CGameHandler * gameHandler;