浏览代码

Merge remote-tracking branch 'origin/develop' into settings-rework

Dydzio 2 年之前
父节点
当前提交
be9c71934c
共有 100 个文件被更改,包括 3423 次插入2285 次删除
  1. 6 0
      Global.h
  2. 244 0
      Mods/vcmi/config/vcmi/russian.json
  3. 12 1
      Mods/vcmi/mod.json
  4. 2 0
      client/CMakeLists.txt
  5. 63 100
      client/CPlayerInterface.cpp
  6. 1 0
      client/CPlayerInterface.h
  7. 55 17
      client/CServerHandler.cpp
  8. 5 2
      client/CServerHandler.h
  9. 5 2
      client/Client.cpp
  10. 8 24
      client/ClientCommandManager.cpp
  11. 132 0
      client/ClientNetPackVisitors.h
  12. 56 0
      client/LobbyClientNetPackVisitors.h
  13. 297 320
      client/NetPacksClient.cpp
  14. 54 56
      client/NetPacksLobbyClient.cpp
  15. 172 100
      client/adventureMap/CAdvMapInt.cpp
  16. 91 64
      client/adventureMap/CAdvMapInt.h
  17. 0 2
      client/adventureMap/CAdvMapPanel.cpp
  18. 1 1
      client/adventureMap/CAdvMapPanel.h
  19. 2 1
      client/adventureMap/CAdventureOptions.h
  20. 4 3
      client/adventureMap/CInGameConsole.h
  21. 13 12
      client/adventureMap/CInfoBar.cpp
  22. 9 15
      client/adventureMap/CList.cpp
  23. 58 147
      client/adventureMap/CMinimap.cpp
  24. 17 21
      client/adventureMap/CMinimap.h
  25. 4 12
      client/adventureMap/CResDataBar.cpp
  26. 4 5
      client/adventureMap/CResDataBar.h
  27. 9 8
      client/adventureMap/CTerrainRect.cpp
  28. 14 7
      client/adventureMap/CTerrainRect.h
  29. 59 58
      client/adventureMap/mapHandler.cpp
  30. 164 156
      client/adventureMap/mapHandler.h
  31. 2 2
      client/battle/BattleInterface.cpp
  32. 31 5
      client/render/Canvas.cpp
  33. 16 4
      client/render/Canvas.h
  34. 71 37
      client/renderSDL/SDL_Extensions.cpp
  35. 3 2
      client/renderSDL/SDL_Extensions.h
  36. 33 66
      client/widgets/CArtifactHolder.cpp
  37. 0 1
      client/widgets/CArtifactHolder.h
  38. 3 3
      client/widgets/CComponent.cpp
  39. 2 3
      client/widgets/MiscWidgets.cpp
  40. 2 0
      client/windows/CCastleInterface.cpp
  41. 3 2
      client/windows/CKingdomInterface.cpp
  42. 3 5
      client/windows/CQuestLog.cpp
  43. 2 1
      client/windows/CSpellWindow.cpp
  44. 2 2
      client/windows/CTradeWindow.cpp
  45. 3 1
      client/windows/GUIClasses.cpp
  46. 8 5
      client/windows/InfoWindows.cpp
  47. 3 0
      cmake_modules/VCMI_lib.cmake
  48. 33 28
      lib/CArtHandler.cpp
  49. 1 1
      lib/CGameState.cpp
  50. 3 3
      lib/Color.h
  51. 1 4
      lib/ConstTransitivePtr.h
  52. 1 0
      lib/IGameEventsReceiver.h
  53. 165 0
      lib/NetPackVisitor.h
  54. 280 284
      lib/NetPacks.h
  55. 63 88
      lib/NetPacksBase.h
  56. 767 41
      lib/NetPacksLib.cpp
  57. 62 122
      lib/NetPacksLobby.h
  58. 29 36
      lib/int3.h
  59. 0 2
      lib/rmg/ObjectManager.cpp
  60. 0 1
      lib/rmg/TreasurePlacer.cpp
  61. 6 7
      lib/serializer/BinaryDeserializer.cpp
  62. 1 1
      lib/serializer/BinaryDeserializer.h
  63. 3 4
      lib/serializer/BinarySerializer.cpp
  64. 2 2
      lib/serializer/CLoadIntegrityValidator.cpp
  65. 1 1
      lib/serializer/CLoadIntegrityValidator.h
  66. 1 3
      lib/serializer/CMemorySerializer.cpp
  67. 2 11
      lib/serializer/CSerializer.cpp
  68. 2 3
      lib/serializer/CSerializer.h
  69. 1 1
      lib/serializer/CTypeList.cpp
  70. 1 6
      lib/serializer/CTypeList.h
  71. 27 22
      lib/serializer/Connection.cpp
  72. 2 2
      lib/serializer/Connection.h
  73. 49 0
      lib/serializer/ILICReader.cpp
  74. 23 0
      lib/serializer/ILICReader.h
  75. 0 30
      lib/serializer/JsonDeserializer.cpp
  76. 3 6
      lib/serializer/JsonDeserializer.h
  77. 12 26
      lib/serializer/JsonSerializeFormat.cpp
  78. 9 9
      lib/serializer/JsonSerializeFormat.h
  79. 3 4
      lib/serializer/JsonTreeSerializer.h
  80. 2 32
      lib/serializer/JsonUpdater.cpp
  81. 3 6
      lib/serializer/JsonUpdater.h
  82. 10 10
      lib/spells/AdventureSpellMechanics.cpp
  83. 23 20
      lib/spells/BattleSpellMechanics.cpp
  84. 3 3
      lib/spells/BonusCaster.cpp
  85. 19 56
      lib/spells/CSpellHandler.cpp
  86. 8 16
      lib/spells/CSpellHandler.h
  87. 16 25
      lib/spells/ISpellMechanics.cpp
  88. 2 2
      lib/spells/ISpellMechanics.h
  89. 1 11
      lib/spells/Problem.cpp
  90. 0 3
      lib/spells/Problem.h
  91. 13 45
      lib/spells/TargetCondition.cpp
  92. 0 6
      lib/spells/TargetCondition.h
  93. 0 6
      lib/spells/ViewSpellInt.cpp
  94. 5 5
      lib/spells/ViewSpellInt.h
  95. 4 4
      lib/spells/effects/Damage.cpp
  96. 0 4
      lib/spells/effects/Effects.cpp
  97. 1 2
      lib/spells/effects/Effects.h
  98. 2 2
      lib/spells/effects/Obstacle.cpp
  99. 0 5
      lib/spells/effects/Registry.h
  100. 10 1
      mapeditor/mainwindow.ui

+ 6 - 0
Global.h

@@ -746,6 +746,12 @@ namespace vstd
 		}
 		return std::to_string(number) + *iter;
 	}
+	
+	///compile-time version of std::abs for ints for int3, in clang++15 std::abs is constexpr
+	static constexpr int abs(int i) {
+		if(i < 0) return -i;
+		return i;
+	}
 }
 using vstd::operator-=;
 

+ 244 - 0
Mods/vcmi/config/vcmi/russian.json

@@ -0,0 +1,244 @@
+{
+	"vcmi.adventureMap.monsterThreat.title"     : "\n\n Опасность: ",
+	"vcmi.adventureMap.monsterThreat.levels.0"  : "Безопасно",
+	"vcmi.adventureMap.monsterThreat.levels.1"  : "Очень слабо",
+	"vcmi.adventureMap.monsterThreat.levels.2"  : "Слабо",
+	"vcmi.adventureMap.monsterThreat.levels.3"  : "Немного слабее",
+	"vcmi.adventureMap.monsterThreat.levels.4"  : "Равно",
+	"vcmi.adventureMap.monsterThreat.levels.5"  : "Немного сильнее",
+	"vcmi.adventureMap.monsterThreat.levels.6"  : "Сильно",
+	"vcmi.adventureMap.monsterThreat.levels.7"  : "Очень сильно",
+	"vcmi.adventureMap.monsterThreat.levels.8"  : "Предельно сильно",
+	"vcmi.adventureMap.monsterThreat.levels.9"  : "Слишком сильно",
+	"vcmi.adventureMap.monsterThreat.levels.10" : "Смертельно",
+	"vcmi.adventureMap.monsterThreat.levels.11" : "Невозможно",
+
+	"vcmi.adventureMap.confirmRestartGame"     : "Вы уверены, что хотите перезапустить игру?",
+	"vcmi.adventureMap.noTownWithMarket"       : "Нет союзных городов с рынками!",
+	"vcmi.adventureMap.noTownWithTavern"       : "Нет союзных городов с тавернами!",
+	"vcmi.adventureMap.spellUnknownProblem"    : "Неизвестная проблема с заклинанием, дополнительная информация недоступна.",
+	"vcmi.adventureMap.playerAttacked"         : "Игрок атакован: %s",
+	"vcmi.adventureMap.moveCostDetails"        : "Очки движения - Стоимость: %TURNS ходов + %POINTS очков, Останется: %REMAINING очков",
+	"vcmi.adventureMap.moveCostDetailsNoTurns" : "Очки движения - Стоимость: %POINTS очков, Останется: %REMAINING очков",
+
+	"vcmi.server.errors.existingProcess"     : "Запущен другой процесс vcmiserver, сначала завершите его.",
+	"vcmi.server.errors.modsIncompatibility" : "Требуемые моды для загрузки игры:",
+	"vcmi.server.confirmReconnect"          : "Подключиться к предыдущей сессии?",
+
+	"vcmi.systemOptions.fullscreenButton.hover" : "Полный экран",
+	"vcmi.systemOptions.fullscreenButton.help"  : "{Полный экран}\n\n Если выбрано, то VCMI будет работать в полноэкранном режиме, если нет - в окне",
+	"vcmi.systemOptions.resolutionButton.hover" : "Разрешение экрана",
+	"vcmi.systemOptions.resolutionButton.help"  : "{Разрешение экрана}\n\n Изменение разрешения экрана. Для применения нового разрешения требуется перезапуск игры.",
+	"vcmi.systemOptions.resolutionMenu.hover"   : "Выбрать разрешения экрана",
+	"vcmi.systemOptions.resolutionMenu.help"    : "Изменение разрешения экрана в игре.",
+	"vcmi.systemOptions.fullscreenFailed"       : "{Полный экран}\n\n Невозможно переключиться в полноэкранный режим - выбранное разрешение не поддерживается дисплеем!",
+
+	"vcmi.townHall.missingBase"             : "Сначала необходимо построить: %s",
+	"vcmi.townHall.noCreaturesToRecruit"    : "Нет существ для найма!",
+	"vcmi.townHall.greetingManaVortex"      : "Близ %s ваше тело наполняется новой силой. Ваша обычная магическая энергия ныне удвоена.",
+	"vcmi.townHall.greetingKnowledge"       : "Вы изучили знаки %s, на вас снизошло прозрение в деле магии (+1 Знания).",
+	"vcmi.townHall.greetingSpellPower"      : "В %s вас научили новым способам концентрации магической силы (+1 Силы)",
+	"vcmi.townHall.greetingExperience"      : "Посетив %s, вы узнали много нового (+1000 опыта).",
+	"vcmi.townHall.greetingAttack"          : "Пребывание в %s позволило вам лучше использовать боевые навыки (+1 Атаки).",
+	"vcmi.townHall.greetingDefence"         : "В %s искушенные воины преподали вам свои защитные умения (+1 Защиты).",
+	"vcmi.townHall.hasNotProduced"          : "В %s еще ничего не произведено.",
+	"vcmi.townHall.hasProduced"             : "В %s на этой неделе произведено: %d %s",
+	"vcmi.townHall.greetingCustomBonus"     : "%s дает вам +%d %s%s",
+	"vcmi.townHall.greetingCustomUntil"     : " до следующей битвы.",
+	"vcmi.townHall.greetingInTownMagicWell" : "%s восстанавливает ваши очки заклинаний до максимума.",
+
+	"vcmi.logicalExpressions.anyOf"  : "Любое из:",
+	"vcmi.logicalExpressions.allOf"  : "Все перечисленное:",
+	"vcmi.logicalExpressions.noneOf" : "Не:",
+
+	"vcmi.heroWindow.openCommander.hover" : "Открыть экран командира",
+	"vcmi.heroWindow.openCommander.help"  : "Показать информацию о командире у данного героя",
+
+	"vcmi.commanderWindow.artifactMessage" : "Вы хотите отдать артефакт назад герою?",
+
+	"vcmi.creatureWindow.showBonuses.hover"    : "Просмотр бонусов",
+	"vcmi.creatureWindow.showBonuses.help"     : "Просмотр всех активных бонусов командира",
+	"vcmi.creatureWindow.showSkills.hover"     : "Просмотр навыков",
+	"vcmi.creatureWindow.showSkills.help"      : "Просмотр всех изученных навыков командира",
+	"vcmi.creatureWindow.returnArtifact.hover" : "Отдать артефакт",
+	"vcmi.creatureWindow.returnArtifact.help"  : "Нажатие на эту кнопку передает артефакт в рюкзак героя",
+
+	"vcmi.questLog.hideComplete.hover" : "Скрыть завершенное",
+	"vcmi.questLog.hideComplete.help"  : "Скрыть все завершенные квесты",
+
+	"vcmi.randomMapTab.widgets.defaultTemplate"      : "default",
+	"vcmi.randomMapTab.widgets.templateLabel"        : "Template",
+	"vcmi.randomMapTab.widgets.teamAlignmentsButton" : "Setup...",
+	"vcmi.randomMapTab.widgets.teamAlignmentsLabel"  : "Team alignments",
+
+	// few strings from WoG used by vcmi
+	"vcmi.stackExperience.description" : "» О п ы т   с у щ е с т в «\n\nТип существа ................... : %s\nРанг опыта ................. : %s (%i)\nОчки опыта ............... : %i\nДо следующего .. : %i\nМаксимум за битву ... : %i%% (%i)\nЧисло в отряде .... : %i\nМаксимум новичков\n без потери ранга .... : %i\nМножитель опыта ........... : %.2f\nМножитель улучшения .......... : %.2f\nОпыт после 10 ранга ........ : %i\nМаксимум новичков для сохранения\n ранга 10 при максимальном опыте : %i",
+	"vcmi.stackExperience.rank.1" : "Рекрут",
+	"vcmi.stackExperience.rank.2" : "Новичок",
+	"vcmi.stackExperience.rank.3" : "Тренирован",
+	"vcmi.stackExperience.rank.4" : "Знающий",
+	"vcmi.stackExperience.rank.5" : "Подтвержденный",
+	"vcmi.stackExperience.rank.6" : "Ветеран",
+	"vcmi.stackExperience.rank.7" : "Адепт",
+	"vcmi.stackExperience.rank.8" : "Эксперт",
+	"vcmi.stackExperience.rank.9" : "Элита",
+	"vcmi.stackExperience.rank.10" : "Мастер",
+	"vcmi.stackExperience.rank.11" : "Ас",
+
+	"core.bonus.ADDITIONAL_ATTACK.name": "Двойной удар",
+	"core.bonus.ADDITIONAL_ATTACK.description": "Бьет дважды",
+	"core.bonus.ADDITIONAL_RETALIATION.name": "Дополнительные ответные атаки",
+	"core.bonus.ADDITIONAL_RETALIATION.description": "Отвечает на атаку дополнительно ${val} раз",
+	"core.bonus.AIR_IMMUNITY.name": "Иммунитет к воздуху",
+	"core.bonus.AIR_IMMUNITY.description": "Иммунитет ко всем заклинаниям Магии Воздуха",
+	"core.bonus.ATTACKS_ALL_ADJACENT.name": "Атака вокруг",
+	"core.bonus.ATTACKS_ALL_ADJACENT.description": "Атакует всех окружающих юнитов",
+	"core.bonus.BLOCKS_RETALIATION.name": "Безответная атакая",
+	"core.bonus.BLOCKS_RETALIATION.description": "Враг не отвечает в ближнем бою",
+	"core.bonus.BLOCKS_RANGED_RETALIATION.name": "Безответная стрельба",
+	"core.bonus.BLOCKS_RANGED_RETALIATION.description": "Враг не отвечает в дальнем бою",
+	"core.bonus.CATAPULT.name": "Стенобитное орудие",
+	"core.bonus.CATAPULT.description": "Может атаковать стены",
+	"core.bonus.CATAPULT_EXTRA_SHOTS.name": "Дополнительные атаки стен",
+	"core.bonus.CATAPULT_EXTRA_SHOTS.description": "Может дополнительно бить в стены ${val} раз за атаку",
+	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Снижение стоимости заклинаний (${val})",
+	"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Снижаемость стоимость заклинаний для героя",
+	"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "Подавитель магии (${val})",
+	"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description": "Увеличивает стоимость заклинаний противника",
+	"core.bonus.CHARGE_IMMUNITY.name": "Иммунитет к разгону",
+	"core.bonus.CHARGE_IMMUNITY.description": "Бонус разгона не применяется",
+	"core.bonus.DARKNESS.name": "Вуаль Тьмы",
+	"core.bonus.DARKNESS.description": "Затемняет все в радиусе ${val} клеток",
+	"core.bonus.DEATH_STARE.name": "Смертельный взгляд (${val}%)",
+	"core.bonus.DEATH_STARE.description": "${val}% шанс уничтожить одно существо",
+	"core.bonus.DEFENSIVE_STANCE.name": "Защитная стойка",
+	"core.bonus.DEFENSIVE_STANCE.description": "+${val} Защиты при обороне",
+	"core.bonus.DESTRUCTION.name": "Уничтожитель",
+	"core.bonus.DESTRUCTION.description": "Шанс ${val}% уничтожить дополнительных существ при атаке",
+	"core.bonus.DOUBLE_DAMAGE_CHANCE.name": "Смертельный удар",
+	"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "Шанс ${val}% на двойной урон",
+	"core.bonus.DRAGON_NATURE.name": "Дракон",
+	"core.bonus.DRAGON_NATURE.description": "Это существо - дракон",
+	"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "Иммунитет к магии прямого урона",
+	"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "Заклинания прямого урона не могут быть применены",
+	"core.bonus.EARTH_IMMUNITY.name": "Иммунитет к земле",
+	"core.bonus.EARTH_IMMUNITY.description": "Иммунитет ко всем заклинаниям Магии Земли",
+	"core.bonus.ENCHANTER.name": "Заклинатель (массовое)",
+	"core.bonus.ENCHANTER.description": "Может применять массовое ${subtype.spell} каждый ход",
+	"core.bonus.ENCHANTED.name": "Заколдован",
+	"core.bonus.ENCHANTED.description": "Перманентное заклинание ${subtype.spell}",
+	"core.bonus.ENEMY_DEFENCE_REDUCTION.name": "Игнорирует броню (${val}%)",
+	"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "Игнорирует часть Защиты при атаке",
+	"core.bonus.FIRE_IMMUNITY.name": "Иммунитет к огню",
+	"core.bonus.FIRE_IMMUNITY.description": "Иммунитет ко всем заклинаниям Магии Огня",
+	"core.bonus.FIRE_SHIELD.name": "Огненный щит (${val}%)",
+	"core.bonus.FIRE_SHIELD.description": "Наносит огнем часть полученного урона",
+	"core.bonus.FIRST_STRIKE.name": "Первый удар",
+	"core.bonus.FIRST_STRIKE.description": "Существо бьет первым даже при ответной атаке",
+	"core.bonus.FEAR.name": "Страх",
+	"core.bonus.FEAR.description": "Заставляет вражеских существ цепенеть от страха",
+	"core.bonus.FEARLESS.name": "Бесстрашный",
+	"core.bonus.FEARLESS.description": "Иммунитет к страху",
+	"core.bonus.FLYING.name": "Полет",
+	"core.bonus.FLYING.description": "Игнорирует препятствия на поле боя",
+	"core.bonus.FREE_SHOOTING.name": "Стреляет вблизи",
+	"core.bonus.FREE_SHOOTING.description": "Может стрелять в ближнем бою",
+	"core.bonus.FULL_HP_REGENERATION.name": "Регенерация",
+	"core.bonus.FULL_HP_REGENERATION.description": "Восстанавливает полное здоровье в начале своего хода",
+	"core.bonus.GARGOYLE.name": "Бескровный",
+	"core.bonus.GARGOYLE.description": "Не может быть исцелен и воскрешен",
+	"core.bonus.GENERAL_DAMAGE_REDUCTION.name": "Уменьшение урона (${val}%)",
+	"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "Уменьшает физический урон в ближнем бою и от выстрелов",
+	"core.bonus.HATE.name": "Ненависть: ${subtype.creature}",
+	"core.bonus.HATE.description": "Наносит на ${val}% больше урона",
+	"core.bonus.HEALER.name": "Целитель",
+	"core.bonus.HEALER.description": "Исцеляет дружественные юниты",
+	"core.bonus.HP_REGENERATION.name": "Регенерация",
+	"core.bonus.HP_REGENERATION.description": "Исцеляет ${val} очков здоровья каждый ход",
+	"core.bonus.JOUSTING.name": "Разгон",
+	"core.bonus.JOUSTING.description": "+5% урона за каждую пройденную клетку",
+	"core.bonus.KING1.name": "Король 1",
+	"core.bonus.KING1.description": "Уязвимость к заклинанию Палач 1 ступени",
+	"core.bonus.KING2.name": "Король 2",
+	"core.bonus.KING2.description": "Уязвимость к заклинанию Палач 2 ступени",
+	"core.bonus.KING3.name": "Король 3",
+	"core.bonus.KING3.description":"Уязвимость к заклинанию Палач 3 ступени",
+	"core.bonus.LEVEL_SPELL_IMMUNITY.name": "Иммунитет к заклинаниям 1-${val}",
+	"core.bonus.LEVEL_SPELL_IMMUNITY.description": "Иммунитет к заклинаниям до ${val} уровня",
+	"core.bonus.LIMITED_SHOOTING_RANGE.name" : "Ограниченный радиуст стрельбы",
+	"core.bonus.LIMITED_SHOOTING_RANGE.description" : "Не может стрелять далее чем на ${val} гексов",
+	"core.bonus.LIFE_DRAIN.name": "Вампир (${val}%)",
+	"core.bonus.LIFE_DRAIN.description": "Превращает ${val}% нанесенного урона в свое здоровье",
+	"core.bonus.MANA_CHANNELING.name": "Канал ${val}%",
+	"core.bonus.MANA_CHANNELING.description": "Передает вашему герою потраченную противником ману",
+	"core.bonus.MANA_DRAIN.name": "Высасывание маны",
+	"core.bonus.MANA_DRAIN.description": "Высасывает ${val} маны каждый ход",
+	"core.bonus.MAGIC_MIRROR.name": "Волшебное зеркало (${val}%)",
+	"core.bonus.MAGIC_MIRROR.description": "Шанс ${val}% отразить атакующие заклинание в противника",
+	"core.bonus.MAGIC_RESISTANCE.name": "Защита от магии (${MR}%)",
+	"core.bonus.MAGIC_RESISTANCE.description": "Шанс ${MR}% полностью проигнорировать заклинание",
+	"core.bonus.MIND_IMMUNITY.name": "Железная воля",
+	"core.bonus.MIND_IMMUNITY.description": "Иммунитет к заклинаниям, влияющим на разум",
+	"core.bonus.NO_DISTANCE_PENALTY.name": "Игнорирует расстояние",
+	"core.bonus.NO_DISTANCE_PENALTY.description": "Полный урон от стрельбы на любой дистанции",
+	"core.bonus.NO_MELEE_PENALTY.name": "Ближний бой",
+	"core.bonus.NO_MELEE_PENALTY.description": "Нет штрафа в ближнем бою",
+	"core.bonus.NO_MORALE.name": "Непоколебимый",
+	"core.bonus.NO_MORALE.description": "Боевой дух не оказывает действия на юнита",
+	"core.bonus.NO_WALL_PENALTY.name": "Игнорирует препятствия",
+	"core.bonus.NO_WALL_PENALTY.description": "Полный урон при стрельбе через стены",
+	"core.bonus.NON_LIVING.name": "Анимированный",
+	"core.bonus.NON_LIVING.description": "Иммунитет к ряду эффектов",
+	"core.bonus.RANDOM_SPELLCASTER.name": "Заклинатель (случайное заклинание)",
+	"core.bonus.RANDOM_SPELLCASTER.description": "Может применять случайное заклинание",
+	"core.bonus.RANGED_RETALIATION.name": "Стрелковый ответ",
+	"core.bonus.RANGED_RETALIATION.description": "Совершает ответные атаки при стрельбе",
+	"core.bonus.RECEPTIVE.name": "Принимающий",
+	"core.bonus.RECEPTIVE.description": "Нет иммунитета к дружественным заклинаниям",
+	"core.bonus.REBIRTH.name": "Реинкарнация (${val}%)",
+	"core.bonus.REBIRTH.description": "${val}% отряда оживет после его гибели",
+	"core.bonus.RETURN_AFTER_STRIKE.name": "Атака с возвратом",
+	"core.bonus.RETURN_AFTER_STRIKE.description": "После атаки возвращается на начальный гекс",
+	"core.bonus.SELF_LUCK.name": "Удачливый",
+	"core.bonus.SELF_LUCK.description": "Удача всегда позитивна",
+	"core.bonus.SELF_MORALE.name": "Воодушевленный",
+	"core.bonus.SELF_MORALE.description": "Боевой дух всегда позитивен",
+	"core.bonus.SHOOTER.name": "Стрелок",
+	"core.bonus.SHOOTER.description": "Совершает атаки в дальнем бою",
+	"core.bonus.SHOOTS_ALL_ADJACENT.name": "Стреляет по области",
+	"core.bonus.SHOOTS_ALL_ADJACENT.description": "Дальнобойная атака наносит урон по небольшой области вокруг цели",
+	"core.bonus.SOUL_STEAL.name": "Воровство душ",
+	"core.bonus.SOUL_STEAL.description": "Создает ${val} новых существ за каждого убитого врага",
+	"core.bonus.SPELLCASTER.name": "Заклинатель",
+	"core.bonus.SPELLCASTER.description": "Может применять ${subtype.spell}",
+	"core.bonus.SPELL_AFTER_ATTACK.name": "Заклинание после атаки",
+	"core.bonus.SPELL_AFTER_ATTACK.description": "Шанс ${val}% на применение ${subtype.spell} после атаки",
+	"core.bonus.SPELL_BEFORE_ATTACK.name": "Заклинание перед атакой",
+	"core.bonus.SPELL_BEFORE_ATTACK.description": "Шанс ${val}% на применение ${subtype.spell} перед атакой",
+	"core.bonus.SPELL_DAMAGE_REDUCTION.name": "Защита от магического урона",
+	"core.bonus.SPELL_DAMAGE_REDUCTION.description": "Урон от заклинаний уменьшается на ${val}%.",
+	"core.bonus.SPELL_IMMUNITY.name": "Иммунитет к заклинанию",
+	"core.bonus.SPELL_IMMUNITY.description": "Иммунитет к ${subtype.spell}",
+	"core.bonus.SPELL_LIKE_ATTACK.name": "Атака заклинанием",
+	"core.bonus.SPELL_LIKE_ATTACK.description": "Атака при помощи заклинания ${subtype.spell}",
+	"core.bonus.SPELL_RESISTANCE_AURA.name": "Аура сопротивления",
+	"core.bonus.SPELL_RESISTANCE_AURA.description": "Окружающие отряды получают ${val}% шанс игнорировать заклинания",
+	"core.bonus.SUMMON_GUARDIANS.name": "Призыв стражей",
+	"core.bonus.SUMMON_GUARDIANS.description": "В начале битвы призывает ${subtype.creature} (${val}%)",
+	"core.bonus.SYNERGY_TARGET.name": "Синергия",
+	"core.bonus.SYNERGY_TARGET.description": "Существо уязвимо к эффектам синергии",
+	"core.bonus.TWO_HEX_ATTACK_BREATH.name": "Дыхание",
+	"core.bonus.TWO_HEX_ATTACK_BREATH.description": "Атака дыханием (радиус в 2 гекса)",
+	"core.bonus.THREE_HEADED_ATTACK.name": "Трехсторонняя атака",
+	"core.bonus.THREE_HEADED_ATTACK.description": "Атака трех юнитов с передней стороны",
+	"core.bonus.TRANSMUTATION.name": "Трансмутатор",
+	"core.bonus.TRANSMUTATION.description": "Шанс ${val}% превратить атакующего юнита в юнита другого типа",
+	"core.bonus.UNDEAD.name": "Нежить",
+	"core.bonus.UNDEAD.description": "Это существо - нежить",
+	"core.bonus.UNLIMITED_RETALIATIONS.name": "Всегда отвечает",
+	"core.bonus.UNLIMITED_RETALIATIONS.description": "Отвечает на все атаки вражеских юнитов",
+	"core.bonus.WATER_IMMUNITY.name": "Иммунитет к воде",
+	"core.bonus.WATER_IMMUNITY.description": "Иммунитет ко всем заклинаниям Магии Воды",
+	"core.bonus.WIDE_BREATH.name": "Мощное дыхание",
+	"core.bonus.WIDE_BREATH.description": "Атака дыханием (расширенная)"
+}

+ 12 - 1
Mods/vcmi/mod.json

@@ -23,7 +23,18 @@
 			"config/vcmi/polish.json"
 		]
 	},
-	
+
+	"russian" : {
+		"name" : "Ключевые файлы VCMI",
+		"description" : "Файлы, необходимые для полноценной работы VCMI",
+		"author" : "Команда VCMI",
+		"modType" : "Графический",
+
+		"translations" : [
+			"config/vcmi/russian.json"
+		]
+	},
+
 	"ukrainian" : {
 		"name" : "VCMI - ключові файли",
 		"description" : "Ключові файли необхідні для повноцінної роботи VCMI",

+ 2 - 0
client/CMakeLists.txt

@@ -222,6 +222,8 @@ set(client_HEADERS
 	CVideoHandler.h
 	Client.h
 	ClientCommandManager.h
+	ClientNetPackVisitors.h
+	LobbyClientNetPackVisitors.h
 	resource.h
 )
 

+ 63 - 100
client/CPlayerInterface.cpp

@@ -13,6 +13,10 @@
 
 #include "adventureMap/CAdvMapInt.h"
 #include "adventureMap/mapHandler.h"
+#include "adventureMap/CList.h"
+#include "adventureMap/CTerrainRect.h"
+#include "adventureMap/CInfoBar.h"
+#include "adventureMap/CMinimap.h"
 #include "battle/BattleInterface.h"
 #include "battle/BattleEffectsController.h"
 #include "battle/BattleFieldController.h"
@@ -199,8 +203,7 @@ void CPlayerInterface::yourTurn()
 			autosaveCount %= 5;
 		}
 
-		if (adventureInt->player != playerID)
-			adventureInt->setPlayer(playerID);
+		adventureInt->setPlayer(playerID);
 
 		if (CSH->howManyPlayerInterfaces() > 1) //hot seat message
 		{
@@ -251,10 +254,13 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
 			return;
 	}
 
+	adventureInt->minimap->updateTile(hero->convertToVisitablePos(details.start));
+	adventureInt->minimap->updateTile(hero->convertToVisitablePos(details.end));
+
 	bool directlyAttackingCreature =
 		details.attackedFrom
-		&& adventureInt->terrain.currentPath					//in case if movement has been canceled in the meantime and path was already erased
-		&& adventureInt->terrain.currentPath->nodes.size() == 3;//FIXME should be 2 but works nevertheless...
+		&& adventureInt->terrain->currentPath					//in case if movement has been canceled in the meantime and path was already erased
+		&& adventureInt->terrain->currentPath->nodes.size() == 3;//FIXME should be 2 but works nevertheless...
 
 	if(makingTurn && hero->tempOwner == playerID) //we are moving our hero - we may need to update assigned path
 	{
@@ -264,10 +270,10 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
 
 		if(details.result == TryMoveHero::TELEPORTATION)
 		{
-			if(adventureInt->terrain.currentPath)
+			if(adventureInt->terrain->currentPath)
 			{
-				assert(adventureInt->terrain.currentPath->nodes.size() >= 2);
-				std::vector<CGPathNode>::const_iterator nodesIt = adventureInt->terrain.currentPath->nodes.end() - 1;
+				assert(adventureInt->terrain->currentPath->nodes.size() >= 2);
+				std::vector<CGPathNode>::const_iterator nodesIt = adventureInt->terrain->currentPath->nodes.end() - 1;
 
 				if((nodesIt)->coord == hero->convertToVisitablePos(details.start)
 					&& (nodesIt - 1)->coord == hero->convertToVisitablePos(details.end))
@@ -283,8 +289,8 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
 				}
 			}
 			adventureInt->centerOn(hero, true); //actualizing screen pos
-			adventureInt->minimap.redraw();
-			adventureInt->heroList.update(hero);
+			adventureInt->minimap->redraw();
+			adventureInt->heroList->update(hero);
 			return;	//teleport - no fancy moving animation
 					//TODO: smooth disappear / appear effect
 		}
@@ -294,7 +300,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
 		{
 			eraseCurrentPathOf(hero, false);
 		}
-		else if(adventureInt->terrain.currentPath && hero->pos == details.end) //&& hero is moving
+		else if(adventureInt->terrain->currentPath && hero->pos == details.end) //&& hero is moving
 		{
 			if(details.start != details.end) //so we don't touch path when revisiting with spacebar
 				removeLastNodeFromPath(hero);
@@ -306,7 +312,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
 		hero->isStanding = true;
 		stillMoveHero.setn(STOP_MOVE);
 		GH.totalRedraw();
-		adventureInt->heroList.update(hero);
+		adventureInt->heroList->update(hero);
 		return;
 	}
 
@@ -330,8 +336,8 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
 	}
 
 	adventureInt->centerOn(hero); //actualizing screen pos
-	adventureInt->minimap.redraw();
-	adventureInt->heroList.redraw();
+	adventureInt->minimap->redraw();
+	adventureInt->heroList->redraw();
 
 	initMovement(details, hero, hp);
 
@@ -353,7 +359,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
 #ifndef VCMI_ANDROID
 		// currently android doesn't seem to be able to handle all these full redraws here, so let's disable it so at least it looks less choppy;
 		// most likely this is connected with the way that this manual animation+framerate handling is solved
-		adventureInt->updateScreen = true;
+		adventureInt->requestRedrawMapOnNextFrame();
 #endif
 
 		//evil returns here ...
@@ -367,8 +373,8 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
 	hero->isStanding = true;
 
 	//move finished
-	adventureInt->minimap.redraw();
-	adventureInt->heroList.update(hero);
+	adventureInt->minimap->redraw();
+	adventureInt->heroList->update(hero);
 
 	//check if user cancelled movement
 	{
@@ -431,7 +437,7 @@ void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
 
 	wanderingHeroes -= hero;
 
-	adventureInt->heroList.update(hero);
+	adventureInt->heroList->update(hero);
 	if (makingTurn && newSelection)
 		adventureInt->select(newSelection, true);
 	else if (adventureInt->selection == hero)
@@ -455,7 +461,7 @@ void CPlayerInterface::heroCreated(const CGHeroInstance * hero)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	wanderingHeroes.push_back(hero);
-	adventureInt->heroList.update(hero);
+	adventureInt->heroList->update(hero);
 }
 void CPlayerInterface::openTownWindow(const CGTownInstance * town)
 {
@@ -474,10 +480,10 @@ int3 CPlayerInterface::repairScreenPos(int3 pos)
 		pos.x = -CGI->mh->frameW;
 	if (pos.y<-CGI->mh->frameH)
 		pos.y = -CGI->mh->frameH;
-	if (pos.x>CGI->mh->sizes.x - adventureInt->terrain.tilesw + CGI->mh->frameW)
-		pos.x = CGI->mh->sizes.x - adventureInt->terrain.tilesw + CGI->mh->frameW;
-	if (pos.y>CGI->mh->sizes.y - adventureInt->terrain.tilesh + CGI->mh->frameH)
-		pos.y = CGI->mh->sizes.y - adventureInt->terrain.tilesh + CGI->mh->frameH;
+	if (pos.x>CGI->mh->sizes.x - adventureInt->terrain->tilesw + CGI->mh->frameW)
+		pos.x = CGI->mh->sizes.x - adventureInt->terrain->tilesw + CGI->mh->frameW;
+	if (pos.y>CGI->mh->sizes.y - adventureInt->terrain->tilesh + CGI->mh->frameH)
+		pos.y = CGI->mh->sizes.y - adventureInt->terrain->tilesh + CGI->mh->frameH;
 	return pos;
 }
 
@@ -485,7 +491,7 @@ void CPlayerInterface::activateForSpectator()
 {
 	adventureInt->state = CAdvMapInt::INGAME;
 	adventureInt->activate();
-	adventureInt->minimap.activate();
+	adventureInt->minimap->activate();
 }
 
 void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val)
@@ -515,13 +521,13 @@ void CPlayerInterface::heroManaPointsChanged(const CGHeroInstance * hero)
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	updateInfo(hero);
 	if (makingTurn && hero->tempOwner == playerID)
-		adventureInt->heroList.update(hero);
+		adventureInt->heroList->update(hero);
 }
 void CPlayerInterface::heroMovePointsChanged(const CGHeroInstance * hero)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	if (makingTurn && hero->tempOwner == playerID)
-		adventureInt->heroList.update(hero);
+		adventureInt->heroList->update(hero);
 }
 void CPlayerInterface::receivedResource()
 {
@@ -572,7 +578,7 @@ void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
 		if (town->visitingHero->tempOwner == playerID && !vstd::contains(wanderingHeroes,town->visitingHero)) // our hero
 			wanderingHeroes.push_back(town->visitingHero);
 	}
-	adventureInt->heroList.update();
+	adventureInt->heroList->update();
 	adventureInt->updateNextHero(nullptr);
 
 	if(castleInt)
@@ -678,7 +684,7 @@ void CPlayerInterface::buildChanged(const CGTownInstance *town, BuildingID build
 			}
 		}
 	}
-	adventureInt->townList.update(town);
+	adventureInt->townList->update(town);
 }
 
 void CPlayerInterface::battleStartBefore(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2)
@@ -1036,7 +1042,7 @@ void CPlayerInterface::showComp(const Component &comp, std::string message)
 	waitWhileDialog(); //Fix for mantis #98
 
 	CCS->soundh->playSoundFromSet(CCS->soundh->pickupSounds);
-	adventureInt->infoBar.showComponent(comp, message);
+	adventureInt->infoBar->showComponent(comp, message);
 }
 
 void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector<Component> & components, int soundID)
@@ -1194,7 +1200,7 @@ void CPlayerInterface::tileRevealed(const std::unordered_set<int3, ShashInt3> &p
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	//FIXME: wait for dialog? Magi hut/eye would benefit from this but may break other areas
 	for (auto & po : pos)
-		adventureInt->minimap.showTile(po);
+		adventureInt->minimap->updateTile(po);
 	if (!pos.empty())
 		GH.totalRedraw();
 }
@@ -1203,7 +1209,7 @@ void CPlayerInterface::tileHidden(const std::unordered_set<int3, ShashInt3> &pos
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	for (auto & po : pos)
-		adventureInt->minimap.hideTile(po);
+		adventureInt->minimap->updateTile(po);
 	if (!pos.empty())
 		GH.totalRedraw();
 }
@@ -1332,7 +1338,7 @@ void CPlayerInterface::showGarrisonDialog( const CArmedInstance *up, const CGHer
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	auto onEnd = [=](){ cb->selectionMade(0, queryID); };
 
-	if (stillMoveHero.get() == DURING_MOVE  && adventureInt->terrain.currentPath && adventureInt->terrain.currentPath->nodes.size() > 1) //to ignore calls on passing through garrisons
+	if (stillMoveHero.get() == DURING_MOVE  && adventureInt->terrain->currentPath && adventureInt->terrain->currentPath->nodes.size() > 1) //to ignore calls on passing through garrisons
 	{
 		onEnd();
 		return;
@@ -1409,7 +1415,7 @@ void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop)
 		for(auto & po : pos)
 		{
 			if(cb->isVisible(po))
-				adventureInt->minimap.showTile(po);
+				adventureInt->minimap->updateTile(po);
 		}
 		if(obj->ID == Obj::TOWN)
 		{
@@ -1418,8 +1424,8 @@ void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop)
 			else
 				towns -= obj;
 
-			adventureInt->townList.update();
-			adventureInt->minimap.update();
+			adventureInt->townList->update();
+			adventureInt->minimap->update();
 		}
 		assert(cb->getTownsInfo().size() == towns.size());
 	}
@@ -1524,6 +1530,12 @@ void CPlayerInterface::objectRemoved(const CGObjectInstance * obj)
 	}
 }
 
+void CPlayerInterface::objectRemovedAfter()
+{
+	EVENT_HANDLER_CALLED_BY_CLIENT;
+	adventureInt->minimap->update();
+}
+
 void CPlayerInterface::playerBlocked(int reason, bool start)
 {
 	if(reason == PlayerBlocked::EReason::UPCOMING_BATTLE)
@@ -1575,23 +1587,13 @@ void CPlayerInterface::update()
 		dialogs.pop_front();
 	}
 
-	//in some conditions we may receive calls before selection is initialized - we must ignore them
-	if(adventureInt && GH.topInt() == adventureInt
-		&& (!adventureInt->selection && !settings["session"]["spectate"].Bool()))
-	{
-		return;
-	}
+	assert(adventureInt);
+	assert(adventureInt->selection);
 
 	// Handles mouse and key input
 	GH.updateTime();
 	GH.handleEvents();
-
-	if (!adventureInt || adventureInt->isActive())
-		GH.simpleRedraw();
-	else if((adventureInt->swipeEnabled && adventureInt->swipeMovementRequested) || (adventureInt->scrollingDir && GH.isKeyboardCtrlDown()))
-		GH.totalRedraw(); //player forces map scrolling though interface is disabled
-	else
-		GH.simpleRedraw();
+	GH.simpleRedraw();
 }
 
 int CPlayerInterface::getLastIndex( std::string namePrefix)
@@ -1712,8 +1714,8 @@ void CPlayerInterface::movementPxStep( const TryMoveHero &details, int i, const
 		}
 	}
 
-	adventureInt->terrain.moveX = (32 - i) * (heroImageNewX - heroImageOldX) / 32;
-	adventureInt->terrain.moveY = (32 - i) * (heroImageNewY - heroImageOldY) / 32;
+	adventureInt->terrain->moveX = (32 - i) * (heroImageNewX - heroImageOldX) / 32;
+	adventureInt->terrain->moveY = (32 - i) * (heroImageNewY - heroImageOldY) / 32;
 }
 
 void CPlayerInterface::finishMovement( const TryMoveHero &details, const int3 &hp, const CGHeroInstance * ho )
@@ -1851,7 +1853,7 @@ void CPlayerInterface::showPuzzleMap()
 
 void CPlayerInterface::viewWorldMap()
 {
-	adventureInt->changeMode(EAdvMapMode::WORLD_VIEW);
+	adventureInt->changeMode(EAdvMapMode::WORLD_VIEW, 0.36F);
 }
 
 void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, int spellID)
@@ -1891,14 +1893,14 @@ void CPlayerInterface::eraseCurrentPathOf(const CGHeroInstance * ho, bool checkF
 	assert(ho == adventureInt->selection);
 
 	paths.erase(ho);
-	adventureInt->terrain.currentPath = nullptr;
+	adventureInt->terrain->currentPath = nullptr;
 	adventureInt->updateMoveHero(ho, false);
 }
 
 void CPlayerInterface::removeLastNodeFromPath(const CGHeroInstance *ho)
 {
-	adventureInt->terrain.currentPath->nodes.erase(adventureInt->terrain.currentPath->nodes.end()-1);
-	if (adventureInt->terrain.currentPath->nodes.size() < 2)  //if it was the last one, remove entire path and path with only one tile is not a real path
+	adventureInt->terrain->currentPath->nodes.erase(adventureInt->terrain->currentPath->nodes.end()-1);
+	if (adventureInt->terrain->currentPath->nodes.size() < 2)  //if it was the last one, remove entire path and path with only one tile is not a real path
 		eraseCurrentPathOf(ho);
 }
 
@@ -1928,10 +1930,8 @@ CGPath * CPlayerInterface::getAndVerifyPath(const CGHeroInstance * h)
 
 void CPlayerInterface::acceptTurn()
 {
-	bool centerView = true;
 	if (settings["session"]["autoSkip"].Bool())
 	{
-		centerView = false;
 		while(CInfoWindow *iw = dynamic_cast<CInfoWindow *>(GH.topInt().get()))
 			iw->close();
 	}
@@ -1943,44 +1943,7 @@ void CPlayerInterface::acceptTurn()
 		adventureInt->startTurn();
 	}
 
-	adventureInt->heroList.update();
-	adventureInt->townList.update();
-
-	const CGHeroInstance * heroToSelect = nullptr;
-
-	// find first non-sleeping hero
-	for (auto hero : wanderingHeroes)
-	{
-		if (boost::range::find(sleepingHeroes, hero) == sleepingHeroes.end())
-		{
-			heroToSelect = hero;
-			break;
-		}
-	}
-
-	//select first hero if available.
-	if (heroToSelect != nullptr)
-	{
-		adventureInt->select(heroToSelect, centerView);
-	}
-	else if (towns.size())
-		adventureInt->select(towns.front(), centerView);
-	else
-		adventureInt->select(wanderingHeroes.front());
-
-	//show new day animation and sound on infobar
-	adventureInt->infoBar.showDate();
-
-	adventureInt->updateNextHero(nullptr);
-	adventureInt->showAll(screen);
-
-	if(settings["session"]["autoSkip"].Bool() && !GH.isKeyboardShiftDown())
-	{
-		if(CInfoWindow *iw = dynamic_cast<CInfoWindow *>(GH.topInt().get()))
-			iw->close();
-
-		adventureInt->fendTurn();
-	}
+	adventureInt->initializeNewTurn();
 
 	// warn player if he has no town
 	if (cb->howManyTowns() == 0)
@@ -2049,7 +2012,7 @@ void CPlayerInterface::tryDiggging(const CGHeroInstance * h)
 
 void CPlayerInterface::updateInfo(const CGObjectInstance * specific)
 {
-	adventureInt->infoBar.showSelection();
+	adventureInt->infoBar->showSelection();
 }
 
 void CPlayerInterface::battleNewRoundFirst( int round )
@@ -2160,14 +2123,14 @@ void CPlayerInterface::askToAssembleArtifact(const ArtifactLocation &al)
 void CPlayerInterface::artifactPut(const ArtifactLocation &al)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	adventureInt->infoBar.showSelection();
+	adventureInt->infoBar->showSelection();
 	askToAssembleArtifact(al);
 }
 
 void CPlayerInterface::artifactRemoved(const ArtifactLocation &al)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	adventureInt->infoBar.showSelection();
+	adventureInt->infoBar->showSelection();
 	for(auto isa : GH.listInt)
 	{
 		auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
@@ -2181,7 +2144,7 @@ void CPlayerInterface::artifactRemoved(const ArtifactLocation &al)
 void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	adventureInt->infoBar.showSelection();
+	adventureInt->infoBar->showSelection();
 	for(auto isa : GH.listInt)
 	{
 		auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
@@ -2202,7 +2165,7 @@ void CPlayerInterface::artifactPossibleAssembling(const ArtifactLocation & dst)
 void CPlayerInterface::artifactAssembled(const ArtifactLocation &al)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	adventureInt->infoBar.showSelection();
+	adventureInt->infoBar->showSelection();
 	for(auto isa : GH.listInt)
 	{
 		auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
@@ -2214,7 +2177,7 @@ void CPlayerInterface::artifactAssembled(const ArtifactLocation &al)
 void CPlayerInterface::artifactDisassembled(const ArtifactLocation &al)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	adventureInt->infoBar.showSelection();
+	adventureInt->infoBar->showSelection();
 	for(auto isa : GH.listInt)
 	{
 		auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
@@ -2233,7 +2196,7 @@ void CPlayerInterface::playerStartsTurn(PlayerColor player)
 	}
 	else
 	{
-		adventureInt->infoBar.showSelection();
+		adventureInt->infoBar->showSelection();
 		while (GH.listInt.front() != adventureInt && !dynamic_cast<CInfoWindow*>(GH.listInt.front().get())) //don't remove dialogs that expect query answer
 			GH.popInts(1);
 	}

+ 1 - 0
client/CPlayerInterface.h

@@ -176,6 +176,7 @@ public:
 	void centerView (int3 pos, int focusTime) override;
 	void objectPropertyChanged(const SetObjectProperty * sop) override;
 	void objectRemoved(const CGObjectInstance *obj) override;
+	void objectRemovedAfter() override;
 	void playerBlocked(int reason, bool start) override;
 	void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;
 	void playerStartsTurn(PlayerColor player) override; //called before yourTurn on active itnerface

+ 55 - 17
client/CServerHandler.cpp

@@ -37,7 +37,7 @@
 #include "../lib/CConfigHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CThreadHelper.h"
-#include "../lib/NetPacks.h"
+#include "../lib/NetPackVisitor.h"
 #include "../lib/StartInfo.h"
 #include "../lib/VCMIDirs.h"
 #include "../lib/mapping/CCampaignHandler.h"
@@ -53,6 +53,7 @@
 #include <boost/uuid/uuid_io.hpp>
 #include <boost/uuid/uuid_generators.hpp>
 #include "../lib/serializer/Cast.h"
+#include "LobbyClientNetPackVisitors.h"
 
 #include <vcmi/events/EventBus.h>
 
@@ -86,15 +87,21 @@ public:
 	bool applyOnLobbyHandler(CServerHandler * handler, void * pack) const override
 	{
 		T * ptr = static_cast<T *>(pack);
+		ApplyOnLobbyHandlerNetPackVisitor visitor(*handler);
+
 		logNetwork->trace("\tImmediately apply on lobby: %s", typeList.getTypeInfo(ptr)->name());
-		return ptr->applyOnLobbyHandler(handler);
+		ptr->visit(visitor);
+
+		return visitor.getResult();
 	}
 
 	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler, void * pack) const override
 	{
 		T * ptr = static_cast<T *>(pack);
+		ApplyOnLobbyScreenNetPackVisitor visitor(*handler, lobby);
+
 		logNetwork->trace("\tApply on lobby from queue: %s", typeList.getTypeInfo(ptr)->name());
-		ptr->applyOnLobbyScreen(lobby, handler);
+		ptr->visit(visitor);
 	}
 };
 
@@ -575,7 +582,7 @@ void CServerHandler::sendStartGame(bool allowOnlyAI) const
 	c->disableStackSendingByID();
 }
 
-void CServerHandler::startGameplay(CGameState * gameState)
+void CServerHandler::startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState)
 {
 	if(CMM)
 		CMM->disable();
@@ -767,6 +774,30 @@ void CServerHandler::debugStartTest(std::string filename, bool save)
 	}
 }
 
+class ServerHandlerCPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
+{
+private:
+	CServerHandler & handler;
+
+public:
+	ServerHandlerCPackVisitor(CServerHandler & handler)
+			:handler(handler)
+	{
+	}
+
+	virtual bool callTyped() override { return false; }
+
+	virtual void visitForLobby(CPackForLobby & lobbyPack) override
+	{
+		handler.visitForLobby(lobbyPack);
+	}
+
+	virtual void visitForClient(CPackForClient & clientPack) override
+	{
+		handler.visitForClient(clientPack);
+	}
+};
+
 void CServerHandler::threadHandleConnection()
 {
 	setThreadName("CServerHandler::threadHandleConnection");
@@ -787,20 +818,10 @@ void CServerHandler::threadHandleConnection()
 				// Though currently they'll be delivered and might cause crash.
 				vstd::clear_pointer(pack);
 			}
-			else if(auto lobbyPack = dynamic_ptr_cast<CPackForLobby>(pack))
-			{
-				if(applier->getApplier(typeList.getTypeID(pack))->applyOnLobbyHandler(this, pack))
-				{
-					if(!settings["session"]["headless"].Bool())
-					{
-						boost::unique_lock<boost::recursive_mutex> lock(*mx);
-						packsForLobbyScreen.push_back(lobbyPack);
-					}
-				}
-			}
-			else if(auto clientPack = dynamic_ptr_cast<CPackForClient>(pack))
+			else
 			{
-				client->handlePack(clientPack);
+				ServerHandlerCPackVisitor visitor(*this);
+				pack->visit(visitor);
 			}
 		}
 	}
@@ -836,6 +857,23 @@ void CServerHandler::threadHandleConnection()
 	}
 }
 
+void CServerHandler::visitForLobby(CPackForLobby & lobbyPack)
+{
+	if(applier->getApplier(typeList.getTypeID(&lobbyPack))->applyOnLobbyHandler(this, &lobbyPack))
+	{
+		if(!settings["session"]["headless"].Bool())
+		{
+			boost::unique_lock<boost::recursive_mutex> lock(*mx);
+			packsForLobbyScreen.push_back(&lobbyPack);
+		}
+	}
+}
+
+void CServerHandler::visitForClient(CPackForClient & clientPack)
+{
+	client->handlePack(&clientPack);
+}
+
 void CServerHandler::threadRunServer()
 {
 #if !defined(VCMI_ANDROID) && !defined(VCMI_IOS)

+ 5 - 2
client/CServerHandler.h

@@ -25,13 +25,13 @@ class CGameState;
 struct ClientPlayer;
 struct CPack;
 struct CPackForLobby;
+struct CPackForClient;
 
 template<typename T> class CApplier;
 
 VCMI_LIB_NAMESPACE_END
 
 class CClient;
-
 class CBaseForLobbyApply;
 
 // TODO: Add mutex so we can't set CONNECTION_CANCELLED if client already connected, but thread not setup yet
@@ -148,7 +148,7 @@ public:
 	void sendRestartGame() const override;
 	void sendStartGame(bool allowOnlyAI = false) const override;
 
-	void startGameplay(CGameState * gameState = nullptr);
+	void startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState = nullptr);
 	void endGameplay(bool closeConnection = true, bool restart = false);
 	void startCampaignScenario(std::shared_ptr<CCampaignState> cs = {});
 	void showServerError(std::string txt);
@@ -159,6 +159,9 @@ public:
 
 	void debugStartTest(std::string filename, bool save = false);
 	void restoreLastSession();
+
+	void visitForLobby(CPackForLobby & lobbyPack);
+	void visitForClient(CPackForClient & clientPack);
 };
 
 extern CServerHandler * CSH;

+ 5 - 2
client/Client.cpp

@@ -32,6 +32,7 @@
 #include "../lib/serializer/Connection.h"
 #include "../lib/serializer/CLoadIntegrityValidator.h"
 #include "../lib/NetPacks.h"
+#include "ClientNetPackVisitors.h"
 #include "../lib/VCMI_Lib.h"
 #include "../lib/VCMIDirs.h"
 #include "../lib/mapping/CMap.h"
@@ -80,12 +81,14 @@ public:
 	void applyOnClAfter(CClient * cl, void * pack) const override
 	{
 		T * ptr = static_cast<T *>(pack);
-		ptr->applyCl(cl);
+		ApplyClientNetPackVisitor visitor(*cl, *cl->gameState());
+		ptr->visit(visitor);
 	}
 	void applyOnClBefore(CClient * cl, void * pack) const override
 	{
 		T * ptr = static_cast<T *>(pack);
-		ptr->applyFirstCl(cl);
+		ApplyFirstClientNetPackVisitor visitor(*cl, *cl->gameState());
+		ptr->visit(visitor);
 	}
 };
 

+ 8 - 24
client/ClientCommandManager.cpp

@@ -18,6 +18,7 @@
 #include "CServerHandler.h"
 #include "gui/CGuiHandler.h"
 #include "../lib/NetPacks.h"
+#include "ClientNetPackVisitors.h"
 #include "../lib/CConfigHandler.h"
 #include "../lib/CGameState.h"
 #include "../lib/CPlayerState.h"
@@ -114,25 +115,6 @@ void ClientCommandManager::processCommand(const std::string &message, bool calle
 	{
 		exit(EXIT_SUCCESS);
 	}
-	else if(commandName == std::string("activate"))
-	{
-		int what;
-		singleWordBuffer >> what;
-		switch (what)
-		{
-			case 0:
-				GH.topInt()->activate();
-				break;
-			case 1:
-				adventureInt->activate();
-				break;
-			case 2:
-				LOCPLINT->castleInt->activate();
-				break;
-			default:
-				printCommandMessage("Wrong argument specified!", ELogLevel::ERROR);
-		}
-	}
 	else if(commandName == "redraw")
 	{
 		GH.totalRedraw();
@@ -273,7 +255,7 @@ void ClientCommandManager::processCommand(const std::string &message, bool calle
 	}
 	else if(commandName == "mp" && adventureInt)
 	{
-		if(const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(adventureInt->selection))
+		if(const CGHeroInstance *h = adventureInt->curHero())
 			printCommandMessage(std::to_string(h->movement) + "; max: " + std::to_string(h->maxMovePoints(true)) + "/" + std::to_string(h->maxMovePoints(false)) + "\n");
 	}
 	else if(commandName == "bonuses")
@@ -287,12 +269,12 @@ void ClientCommandManager::processCommand(const std::string &message, bool calle
 			ss << b;
 			return ss.str();
 		};
-		printCommandMessage("Bonuses of " + adventureInt->selection->getObjectName() + "\n");
-		printCommandMessage(format(adventureInt->selection->getBonusList()) + "\n");
+		printCommandMessage("Bonuses of " + adventureInt->curArmy()->getObjectName() + "\n");
+		printCommandMessage(format(adventureInt->curArmy()->getBonusList()) + "\n");
 
 		printCommandMessage("\nInherited bonuses:\n");
 		TCNodes parents;
-		adventureInt->selection->getParents(parents);
+		adventureInt->curArmy()->getParents(parents);
 		for(const CBonusSystemNode *parent : parents)
 		{
 			printCommandMessage(std::string("\nBonuses from ") + typeid(*parent).name() + "\n" + format(*parent->getAllBonuses(Selector::all, Selector::all)) + "\n");
@@ -427,7 +409,9 @@ void ClientCommandManager::giveTurn(const PlayerColor &colorIdentifier)
 	YourTurn yt;
 	yt.player = colorIdentifier;
 	yt.daysWithoutCastle = CSH->client->getPlayerState(colorIdentifier)->daysWithoutCastle;
-	yt.applyCl(CSH->client);
+
+	ApplyClientNetPackVisitor visitor(*CSH->client, *CSH->client->gameState());
+	yt.visit(visitor);
 }
 
 void ClientCommandManager::printInfoAboutInterfaceObject(const CIntObject *obj, int level)

+ 132 - 0
client/ClientNetPackVisitors.h

@@ -0,0 +1,132 @@
+/*
+ * ClientNetPackVisitors.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "../lib/NetPackVisitor.h"
+
+class CClient;
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class CGameState;
+
+VCMI_LIB_NAMESPACE_END
+
+class ApplyClientNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
+{
+private:
+	CClient & cl;
+	CGameState & gs;
+
+public:
+	ApplyClientNetPackVisitor(CClient & cl, CGameState & gs)
+		:cl(cl), gs(gs)
+	{
+	}
+
+	void visitSetResources(SetResources & pack) override;
+	void visitSetPrimSkill(SetPrimSkill & pack) override;
+	void visitSetSecSkill(SetSecSkill & pack) override;
+	void visitHeroVisitCastle(HeroVisitCastle & pack) override;
+	void visitSetMana(SetMana & pack) override;
+	void visitSetMovePoints(SetMovePoints & pack) override;
+	void visitFoWChange(FoWChange & pack) override;
+	void visitChangeStackCount(ChangeStackCount & pack) override;
+	void visitSetStackType(SetStackType & pack) override;
+	void visitEraseStack(EraseStack & pack) override;
+	void visitSwapStacks(SwapStacks & pack) override;
+	void visitInsertNewStack(InsertNewStack & pack) override;
+	void visitRebalanceStacks(RebalanceStacks & pack) override;
+	void visitBulkRebalanceStacks(BulkRebalanceStacks & pack) override;
+	void visitBulkSmartRebalanceStacks(BulkSmartRebalanceStacks & pack) override;
+	void visitPutArtifact(PutArtifact & pack) override;
+	void visitEraseArtifact(EraseArtifact & pack) override;
+	void visitMoveArtifact(MoveArtifact & pack) override;
+	void visitBulkMoveArtifacts(BulkMoveArtifacts & pack) override;
+	void visitAssembledArtifact(AssembledArtifact & pack) override;
+	void visitDisassembledArtifact(DisassembledArtifact & pack) override;
+	void visitHeroVisit(HeroVisit & pack) override;
+	void visitNewTurn(NewTurn & pack) override;
+	void visitGiveBonus(GiveBonus & pack) override;
+	void visitChangeObjPos(ChangeObjPos & pack) override;
+	void visitPlayerEndsGame(PlayerEndsGame & pack) override;
+	void visitPlayerReinitInterface(PlayerReinitInterface & pack) override;
+	void visitRemoveBonus(RemoveBonus & pack) override;
+	void visitRemoveObject(RemoveObject & pack) override;
+	void visitTryMoveHero(TryMoveHero & pack) override;
+	void visitNewStructures(NewStructures & pack) override;
+	void visitRazeStructures(RazeStructures & pack) override;
+	void visitSetAvailableCreatures(SetAvailableCreatures & pack) override;
+	void visitSetHeroesInTown(SetHeroesInTown & pack) override;
+	void visitHeroRecruited(HeroRecruited & pack) override;
+	void visitGiveHero(GiveHero & pack) override;
+	void visitInfoWindow(InfoWindow & pack) override;
+	void visitSetObjectProperty(SetObjectProperty & pack) override;
+	void visitHeroLevelUp(HeroLevelUp & pack) override;
+	void visitCommanderLevelUp(CommanderLevelUp & pack) override;
+	void visitBlockingDialog(BlockingDialog & pack) override;
+	void visitGarrisonDialog(GarrisonDialog & pack) override;
+	void visitExchangeDialog(ExchangeDialog & pack) override;
+	void visitTeleportDialog(TeleportDialog & pack) override;
+	void visitMapObjectSelectDialog(MapObjectSelectDialog & pack) override;
+	void visitBattleStart(BattleStart & pack) override;
+	void visitBattleNextRound(BattleNextRound & pack) override;
+	void visitBattleSetActiveStack(BattleSetActiveStack & pack) override;
+	void visitBattleLogMessage(BattleLogMessage & pack) override;
+	void visitBattleTriggerEffect(BattleTriggerEffect & pack) override;
+	void visitBattleAttack(BattleAttack & pack) override;
+	void visitBattleSpellCast(BattleSpellCast & pack) override;
+	void visitSetStackEffect(SetStackEffect & pack) override;
+	void visitStacksInjured(StacksInjured & pack) override;
+	void visitBattleResultsApplied(BattleResultsApplied & pack) override;
+	void visitBattleUnitsChanged(BattleUnitsChanged & pack) override;
+	void visitBattleObstaclesChanged(BattleObstaclesChanged & pack) override;
+	void visitCatapultAttack(CatapultAttack & pack) override;
+	void visitEndAction(EndAction & pack) override;
+	void visitPackageApplied(PackageApplied & pack) override;
+	void visitSystemMessage(SystemMessage & pack) override;
+	void visitPlayerBlocked(PlayerBlocked & pack) override;
+	void visitYourTurn(YourTurn & pack) override;
+	void visitSaveGameClient(SaveGameClient & pack) override;
+	void visitPlayerMessageClient(PlayerMessageClient & pack) override;
+	void visitShowInInfobox(ShowInInfobox & pack) override;
+	void visitAdvmapSpellCast(AdvmapSpellCast & pack) override;
+	void visitShowWorldViewEx(ShowWorldViewEx & pack) override;	
+	void visitOpenWindow(OpenWindow & pack) override;
+	void visitCenterView(CenterView & pack) override;
+	void visitNewObject(NewObject & pack) override;
+	void visitSetAvailableArtifacts(SetAvailableArtifacts & pack) override;
+	void visitEntitiesChanged(EntitiesChanged & pack) override;
+};
+
+class ApplyFirstClientNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
+{
+private:
+	CClient & cl;
+	CGameState & gs;
+
+public:
+	ApplyFirstClientNetPackVisitor(CClient & cl, CGameState & gs)
+		:cl(cl), gs(gs)
+	{
+	}
+
+	virtual void visitChangeObjPos(ChangeObjPos & pack) override;
+	virtual void visitRemoveObject(RemoveObject & pack) override;
+	virtual void visitTryMoveHero(TryMoveHero & pack) override;
+	virtual void visitGiveHero(GiveHero & pack) override;
+	virtual void visitBattleStart(BattleStart & pack) override;
+	virtual void visitBattleNextRound(BattleNextRound & pack) override;
+	virtual void visitBattleUpdateGateState(BattleUpdateGateState & pack) override;
+	virtual void visitBattleResult(BattleResult & pack) override;
+	virtual void visitBattleStackMoved(BattleStackMoved & pack) override;
+	virtual void visitBattleAttack(BattleAttack & pack) override;
+	virtual void visitStartAction(StartAction & pack) override;
+};

+ 56 - 0
client/LobbyClientNetPackVisitors.h

@@ -0,0 +1,56 @@
+/*
+ * ClientNetPackVisitors.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "../lib/NetPackVisitor.h"
+
+class CClient;
+class CGameState;
+
+class ApplyOnLobbyHandlerNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
+{
+private:
+	CServerHandler & handler;
+	bool result;
+
+public:
+	ApplyOnLobbyHandlerNetPackVisitor(CServerHandler & handler)
+		:handler(handler), result(true)
+	{
+	}
+
+	bool getResult() const { return result; }
+
+	virtual void visitLobbyClientConnected(LobbyClientConnected & pack) override;
+	virtual void visitLobbyClientDisconnected(LobbyClientDisconnected & pack) override;
+	virtual void visitLobbyEndGame(LobbyEndGame & pack) override;
+	virtual void visitLobbyStartGame(LobbyStartGame & pack) override;
+	virtual void visitLobbyUpdateState(LobbyUpdateState & pack) override;
+};
+
+class ApplyOnLobbyScreenNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
+{
+private:
+	CServerHandler & handler;
+	CLobbyScreen * lobby;
+
+public:
+	ApplyOnLobbyScreenNetPackVisitor(CServerHandler & handler, CLobbyScreen * lobby)
+		:handler(handler), lobby(lobby)
+	{
+	}
+
+	virtual void visitLobbyClientDisconnected(LobbyClientDisconnected & pack) override;
+	virtual void visitLobbyChatMessage(LobbyChatMessage & pack) override;
+	virtual void visitLobbyGuiAction(LobbyGuiAction & pack) override;
+	virtual void visitLobbyStartGame(LobbyStartGame & pack) override;
+	virtual void visitLobbyUpdateState(LobbyUpdateState & pack) override;
+	virtual void visitLobbyShowMessage(LobbyShowMessage & pack) override;
+};

文件差异内容过多而无法显示
+ 297 - 320
client/NetPacksClient.cpp


+ 54 - 56
client/NetPacksLobbyClient.cpp

@@ -9,6 +9,7 @@
  */
 #include "StdInc.h"
 
+#include "LobbyClientNetPackVisitors.h"
 #include "lobby/CSelectionBase.h"
 #include "lobby/CLobbyScreen.h"
 
@@ -28,123 +29,120 @@
 #include "../lib/NetPacksLobby.h"
 #include "../lib/serializer/Connection.h"
 
-bool LobbyClientConnected::applyOnLobbyHandler(CServerHandler * handler)
+void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientConnected(LobbyClientConnected & pack)
 {
+	result = false;
+
 	// Check if it's LobbyClientConnected for our client
-	if(uuid == handler->c->uuid)
+	if(pack.uuid == handler.c->uuid)
 	{
-		handler->c->connectionID = clientId;
+		handler.c->connectionID = pack.clientId;
 		if(!settings["session"]["headless"].Bool())
-			GH.pushIntT<CLobbyScreen>(static_cast<ESelectionScreen>(handler->screenType));
-		handler->state = EClientState::LOBBY;
-		return true;
+			GH.pushIntT<CLobbyScreen>(static_cast<ESelectionScreen>(handler.screenType));
+		handler.state = EClientState::LOBBY;
 	}
-	return false;
-}
-
-void LobbyClientConnected::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
-{
 }
 
-bool LobbyClientDisconnected::applyOnLobbyHandler(CServerHandler * handler)
+void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack)
 {
-	if(clientId != c->connectionID)
-		return false;
+	if(pack.clientId != pack.c->connectionID)
+	{
+		result = false;
+		return;
+	}
 
-	handler->stopServerConnection();
-	return true;
+	handler.stopServerConnection();
 }
 
-void LobbyClientDisconnected::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
+void ApplyOnLobbyScreenNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack)
 {
 	if(GH.listInt.size())
 		GH.popInts(1);
 }
 
-void LobbyChatMessage::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
+void ApplyOnLobbyScreenNetPackVisitor::visitLobbyChatMessage(LobbyChatMessage & pack)
 {
 	if(lobby && lobby->card)
 	{
-		lobby->card->chat->addNewMessage(playerName + ": " + message);
+		lobby->card->chat->addNewMessage(pack.playerName + ": " + pack.message);
 		lobby->card->setChat(true);
 		if(lobby->buttonChat)
 			lobby->buttonChat->addTextOverlay(CGI->generaltexth->allTexts[531], FONT_SMALL);
 	}
 }
 
-void LobbyGuiAction::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
+void ApplyOnLobbyScreenNetPackVisitor::visitLobbyGuiAction(LobbyGuiAction & pack)
 {
-	if(!lobby || !handler->isGuest())
+	if(!lobby || !handler.isGuest())
 		return;
 
-	switch(action)
+	switch(pack.action)
 	{
-	case NO_TAB:
+	case LobbyGuiAction::NO_TAB:
 		lobby->toggleTab(lobby->curTab);
 		break;
-	case OPEN_OPTIONS:
+	case LobbyGuiAction::OPEN_OPTIONS:
 		lobby->toggleTab(lobby->tabOpt);
 		break;
-	case OPEN_SCENARIO_LIST:
+	case LobbyGuiAction::OPEN_SCENARIO_LIST:
 		lobby->toggleTab(lobby->tabSel);
 		break;
-	case OPEN_RANDOM_MAP_OPTIONS:
+	case LobbyGuiAction::OPEN_RANDOM_MAP_OPTIONS:
 		lobby->toggleTab(lobby->tabRand);
 		break;
 	}
 }
 
-bool LobbyEndGame::applyOnLobbyHandler(CServerHandler * handler)
+void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyEndGame(LobbyEndGame & pack)
 {
-	if(handler->state == EClientState::GAMEPLAY)
+	if(handler.state == EClientState::GAMEPLAY)
 	{
-		handler->endGameplay(closeConnection, restart);
+		handler.endGameplay(pack.closeConnection, pack.restart);
 	}
 	
-	if(restart)
-		handler->sendStartGame();
-	
-	return true;
+	if(pack.restart)
+		handler.sendStartGame();
 }
 
-bool LobbyStartGame::applyOnLobbyHandler(CServerHandler * handler)
+void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
 {
-	if(clientId != -1 && clientId != handler->c->connectionID)
-		return false;
+	if(pack.clientId != -1 && pack.clientId != handler.c->connectionID)
+	{
+		result = false;
+		return;
+	}
 	
-	handler->state = EClientState::STARTING;
-	if(handler->si->mode != StartInfo::LOAD_GAME || clientId == handler->c->connectionID)
+	handler.state = EClientState::STARTING;
+	if(handler.si->mode != StartInfo::LOAD_GAME || pack.clientId == handler.c->connectionID)
 	{
-		auto modeBackup = handler->si->mode;
-		handler->si = initializedStartInfo;
-		handler->si->mode = modeBackup;
+		auto modeBackup = handler.si->mode;
+		handler.si = pack.initializedStartInfo;
+		handler.si->mode = modeBackup;
 	}
 	if(settings["session"]["headless"].Bool())
-		handler->startGameplay(initializedGameState);
-	return true;
+		handler.startGameplay(pack.initializedGameState);
 }
 
-void LobbyStartGame::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
+void ApplyOnLobbyScreenNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
 {
-	if(clientId != -1 && clientId != handler->c->connectionID)
+	if(pack.clientId != -1 && pack.clientId != handler.c->connectionID)
 		return;
 	
-	GH.pushIntT<CLoadingScreen>(std::bind(&CServerHandler::startGameplay, handler, initializedGameState));
+	GH.pushIntT<CLoadingScreen>(std::bind(&CServerHandler::startGameplay, &handler, pack.initializedGameState));
 }
 
-bool LobbyUpdateState::applyOnLobbyHandler(CServerHandler * handler)
+void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyUpdateState(LobbyUpdateState & pack)
 {
-	hostChanged = state.hostClientId != handler->hostClientId;
-	static_cast<LobbyState &>(*handler) = state;
-	return true;
+	pack.hostChanged = pack.state.hostClientId != handler.hostClientId;
+	static_cast<LobbyState &>(handler) = pack.state;
 }
 
-void LobbyUpdateState::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
+void ApplyOnLobbyScreenNetPackVisitor::visitLobbyUpdateState(LobbyUpdateState & pack)
 {
 	if(!lobby) //stub: ignore message for game mode
 		return;
 		
-	if(!lobby->bonusSel && handler->si->campState && handler->state == EClientState::LOBBY_CAMPAIGN)
+	if(!lobby->bonusSel && handler.si->campState && handler.state == EClientState::LOBBY_CAMPAIGN)
 	{
 		lobby->bonusSel = std::make_shared<CBonusSelection>();
 		GH.pushInt(lobby->bonusSel);
@@ -155,15 +153,15 @@ void LobbyUpdateState::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler *
 	else
 		lobby->updateAfterStateChange();
 
-	if(hostChanged)
-		lobby->toggleMode(handler->isHost());
+	if(pack.hostChanged)
+		lobby->toggleMode(handler.isHost());
 }
 
-void LobbyShowMessage::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
+void ApplyOnLobbyScreenNetPackVisitor::visitLobbyShowMessage(LobbyShowMessage & pack)
 {
 	if(!lobby) //stub: ignore message for game mode
 		return;
 	
 	lobby->buttonStart->block(false);
-	handler->showServerError(message);
+	handler.showServerError(pack.message);
 }

+ 172 - 100
client/adventureMap/CAdvMapInt.cpp

@@ -13,6 +13,11 @@
 #include "CAdvMapPanel.h"
 #include "CAdventureOptions.h"
 #include "CInGameConsole.h"
+#include "CMinimap.h"
+#include "CResDataBar.h"
+#include "CTerrainRect.h"
+#include "CList.h"
+#include "CInfoBar.h"
 #include "mapHandler.h"
 
 #include "../windows/CKingdomInterface.h"
@@ -48,7 +53,7 @@
 
 std::shared_ptr<CAdvMapInt> adventureInt;
 
-static void setScrollingCursor(ui8 direction)
+void CAdvMapInt::setScrollingCursor(ui8 direction) const
 {
 	if(direction & CAdvMapInt::RIGHT)
 	{
@@ -77,13 +82,16 @@ static void setScrollingCursor(ui8 direction)
 CAdvMapInt::CAdvMapInt():
 	mode(EAdvMapMode::NORMAL),
 	worldViewScale(0.0f), //actual init later in changeMode
-	minimap(Rect(ADVOPT.minimapX, ADVOPT.minimapY, ADVOPT.minimapW, ADVOPT.minimapH)),
+	minimap(new CMinimap(Rect(ADVOPT.minimapX, ADVOPT.minimapY, ADVOPT.minimapW, ADVOPT.minimapH))),
 	statusbar(CGStatusBar::create(ADVOPT.statusbarX,ADVOPT.statusbarY,ADVOPT.statusbarG)),
-	heroList(ADVOPT.hlistSize, Point(ADVOPT.hlistX, ADVOPT.hlistY), ADVOPT.hlistAU, ADVOPT.hlistAD),
-	townList(ADVOPT.tlistSize, Point(ADVOPT.tlistX, ADVOPT.tlistY), ADVOPT.tlistAU, ADVOPT.tlistAD),
-	infoBar(Rect(ADVOPT.infoboxX, ADVOPT.infoboxY, 192, 192)), state(NA),
+	heroList(new CHeroList(ADVOPT.hlistSize, Point(ADVOPT.hlistX, ADVOPT.hlistY), ADVOPT.hlistAU, ADVOPT.hlistAD)),
+	townList(new CTownList(ADVOPT.tlistSize, Point(ADVOPT.tlistX, ADVOPT.tlistY), ADVOPT.tlistAU, ADVOPT.tlistAD)),
+	infoBar(new CInfoBar(Rect(ADVOPT.infoboxX, ADVOPT.infoboxY, 192, 192))),
+	resdatabar(new CResDataBar),
+	terrain(new CTerrainRect),
+	state(NA),
 	spellBeingCasted(nullptr), position(int3(0, 0, 0)), selection(nullptr),
-	updateScreen(false), anim(0), animValHitCount(0), heroAnim(0), heroAnimValHitCount(0),
+	redrawOnNextFrame(false), anim(0), animValHitCount(0), heroAnim(0), heroAnimValHitCount(0),
 	activeMapPanel(nullptr), duringAITurn(false), scrollingDir(0), scrollingState(false),
 	swipeEnabled(settings["general"]["swipe"].Bool()), swipeMovementRequested(false),
 	swipeTargetPosition(int3(-1, -1, -1))
@@ -92,7 +100,7 @@ CAdvMapInt::CAdvMapInt():
 	pos.w = GH.screenDimensions().x;
 	pos.h = GH.screenDimensions().y;
 	strongInterest = true; // handle all mouse move events to prevent dead mouse move space in fullscreen mode
-	townList.onSelect = std::bind(&CAdvMapInt::selectionChanged,this);
+	townList->onSelect = std::bind(&CAdvMapInt::selectionChanged,this);
 	bg = IImage::createFromFile(ADVOPT.mainGraphic);
 	if(!ADVOPT.worldViewGraphic.empty())
 	{
@@ -136,11 +144,11 @@ CAdvMapInt::CAdvMapInt():
 	nextHero     = makeButton(301, std::bind(&CAdvMapInt::fnextHero,this),         ADVOPT.nextHero,     SDLK_h);
 	endTurn      = makeButton(302, std::bind(&CAdvMapInt::fendTurn,this),          ADVOPT.endTurn,      SDLK_e);
 
-	int panelSpaceBottom = GH.screenDimensions().y - resdatabar.pos.h - 4;
+	int panelSpaceBottom = GH.screenDimensions().y - resdatabar->pos.h - 4;
 
 	panelMain = std::make_shared<CAdvMapPanel>(nullptr, Point(0, 0));
 	// TODO correct drawing position
-	panelWorldView = std::make_shared<CAdvMapWorldViewPanel>(worldViewIcons, bgWorldView, Point(heroList.pos.x - 2, 195), panelSpaceBottom, LOCPLINT->playerID);
+	panelWorldView = std::make_shared<CAdvMapWorldViewPanel>(worldViewIcons, bgWorldView, Point(heroList->pos.x - 2, 195), panelSpaceBottom, LOCPLINT->playerID);
 
 	panelMain->addChildColorableButton(kingOverview);
 	panelMain->addChildColorableButton(underground);
@@ -208,7 +216,7 @@ CAdvMapInt::CAdvMapInt():
 	setPlayer(LOCPLINT->playerID);
 
 	int iconColorMultiplier = player.getNum() * 19;
-	int wvLeft = heroList.pos.x - 2; // TODO correct drawing position
+	int wvLeft = heroList->pos.x - 2; // TODO correct drawing position
 	//int wvTop = 195;
 	for (int i = 0; i < 5; ++i)
 	{
@@ -230,7 +238,7 @@ CAdvMapInt::CAdvMapInt():
 
 	activeMapPanel = panelMain;
 
-	changeMode(EAdvMapMode::NORMAL);
+	changeMode(EAdvMapMode::NORMAL, 0.36F);
 
 	underground->block(!CGI->mh->map->twoLevel);
 	questlog->block(!CGI->mh->map->quests.size());
@@ -246,7 +254,7 @@ void CAdvMapInt::fshowOverview()
 
 void CAdvMapInt::fworldViewBack()
 {
-	changeMode(EAdvMapMode::NORMAL);
+	changeMode(EAdvMapMode::NORMAL, 0.36F);
 	CGI->mh->discardWorldViewCache();
 
 	auto hero = curHero();
@@ -257,17 +265,17 @@ void CAdvMapInt::fworldViewBack()
 void CAdvMapInt::fworldViewScale1x()
 {
 	// TODO set corresponding scale button to "selected" mode
-	changeMode(EAdvMapMode::WORLD_VIEW, 0.22f);
+	changeMode(EAdvMapMode::WORLD_VIEW, 0.22f); // 7 pixels per tile
 }
 
 void CAdvMapInt::fworldViewScale2x()
 {
-	changeMode(EAdvMapMode::WORLD_VIEW, 0.36f);
+	changeMode(EAdvMapMode::WORLD_VIEW, 0.36f); // 11 pixels per tile
 }
 
 void CAdvMapInt::fworldViewScale4x()
 {
-	changeMode(EAdvMapMode::WORLD_VIEW, 0.5f);
+	changeMode(EAdvMapMode::WORLD_VIEW, 0.5f); // 16 pixels per tile
 }
 
 void CAdvMapInt::fswitchLevel()
@@ -285,11 +293,11 @@ void CAdvMapInt::fswitchLevel()
 	worldViewUnderground->setIndex(position.z, true);
 	worldViewUnderground->redraw();
 
-	updateScreen = true;
-	minimap.setLevel(position.z);
+	redrawOnNextFrame = true;
+	minimap->setLevel(position.z);
 
 	if (mode == EAdvMapMode::WORLD_VIEW)
-		terrain.redraw();
+		terrain->redraw();
 }
 void CAdvMapInt::fshowQuestlog()
 {
@@ -316,10 +324,10 @@ void CAdvMapInt::fsleepWake()
 void CAdvMapInt::fmoveHero()
 {
 	const CGHeroInstance *h = curHero();
-	if (!h || !terrain.currentPath || !CGI->mh->canStartHeroMovement())
+	if (!h || !terrain->currentPath || !CGI->mh->canStartHeroMovement())
 		return;
 
-	LOCPLINT->moveHero(h, *terrain.currentPath);
+	LOCPLINT->moveHero(h, *terrain->currentPath);
 }
 
 void CAdvMapInt::fshowSpellbok()
@@ -462,12 +470,12 @@ void CAdvMapInt::activate()
 		activeMapPanel->activate();
 		if (mode == EAdvMapMode::NORMAL)
 		{
-			heroList.activate();
-			townList.activate();
-			infoBar.activate();
+			heroList->activate();
+			townList->activate();
+			infoBar->activate();
 		}
-		minimap.activate();
-		terrain.activate();
+		minimap->activate();
+		terrain->activate();
 		statusbar->activate();
 
 		GH.fakeMouseMove(); //to restore the cursor
@@ -486,12 +494,12 @@ void CAdvMapInt::deactivate()
 		activeMapPanel->deactivate();
 		if (mode == EAdvMapMode::NORMAL)
 		{
-			heroList.deactivate();
-			townList.deactivate();
-			infoBar.deactivate();
+			heroList->deactivate();
+			townList->deactivate();
+			infoBar->deactivate();
 		}
-		minimap.deactivate();
-		terrain.deactivate();
+		minimap->deactivate();
+		terrain->deactivate();
 		statusbar->deactivate();
 	}
 }
@@ -507,23 +515,23 @@ void CAdvMapInt::showAll(SDL_Surface * to)
 	{
 	case EAdvMapMode::NORMAL:
 
-		heroList.showAll(to);
-		townList.showAll(to);
-		infoBar.showAll(to);
+		heroList->showAll(to);
+		townList->showAll(to);
+		infoBar->showAll(to);
 		break;
 	case EAdvMapMode::WORLD_VIEW:
 
-		terrain.showAll(to);
+		terrain->showAll(to);
 		break;
 	}
 	activeMapPanel->showAll(to);
 
-	updateScreen = true;
-	minimap.showAll(to);
+	redrawOnNextFrame = true;
+	minimap->showAll(to);
 	show(to);
 
 
-	resdatabar.showAll(to);
+	resdatabar->showAll(to);
 
 	statusbar->show(to);
 
@@ -563,7 +571,7 @@ void CAdvMapInt::show(SDL_Surface * to)
 		CGI->mh->updateWater();
 		animValHitCount = 0;
 		++anim;
-		updateScreen = true;
+		redrawOnNextFrame = true;
 	}
 
 	if(swipeEnabled)
@@ -584,7 +592,7 @@ void CAdvMapInt::show(SDL_Surface * to)
 		else
 			gems[i]->setFrame(LOCPLINT->playerID.getNum());
 	}
-	if(updateScreen)
+	if(redrawOnNextFrame)
 	{
 		int3 betterPos = LOCPLINT->repairScreenPos(position);
 		if (betterPos != position)
@@ -593,20 +601,20 @@ void CAdvMapInt::show(SDL_Surface * to)
 			position = betterPos;
 		}
 
-		terrain.show(to);
+		terrain->show(to);
 		for(int i = 0; i < 4; i++)
 			gems[i]->showAll(to);
-		updateScreen=false;
+		redrawOnNextFrame=false;
 		LOCPLINT->cingconsole->show(to);
 	}
-	else if (terrain.needsAnimUpdate())
+	else
 	{
-		terrain.showAnim(to);
+		terrain->showAnim(to);
 		for(int i = 0; i < 4; i++)
 			gems[i]->showAll(to);
 	}
 
-	infoBar.show(to);
+	infoBar->show(to);
 	statusbar->showAll(to);
 }
 
@@ -614,8 +622,7 @@ void CAdvMapInt::handleMapScrollingUpdate()
 {
 	int scrollSpeed = static_cast<int>(settings["adventure"]["scrollSpeed"].Float());
 	//if advmap needs updating AND (no dialog is shown OR ctrl is pressed)
-	if((animValHitCount % (4 / scrollSpeed)) == 0
-	   && GH.isKeyboardCtrlDown())
+	if((animValHitCount % (4 / scrollSpeed)) == 0)
 	{
 		if((scrollingDir & LEFT) && (position.x > -CGI->mh->frameW))
 			position.x--;
@@ -633,10 +640,10 @@ void CAdvMapInt::handleMapScrollingUpdate()
 		{
 			setScrollingCursor(scrollingDir);
 			scrollingState = true;
-			updateScreen = true;
-			minimap.redraw();
+			redrawOnNextFrame = true;
+			minimap->redraw();
 			if(mode == EAdvMapMode::WORLD_VIEW)
-				terrain.redraw();
+				terrain->redraw();
 		}
 		else if(scrollingState)
 		{
@@ -654,15 +661,15 @@ void CAdvMapInt::handleSwipeUpdate()
 		position.x = fixedPos.x;
 		position.y = fixedPos.y;
 		CCS->curh->set(Cursor::Map::POINTER);
-		updateScreen = true;
-		minimap.redraw();
+		redrawOnNextFrame = true;
+		minimap->redraw();
 		swipeMovementRequested = false;
 	}
 }
 
 void CAdvMapInt::selectionChanged()
 {
-	const CGTownInstance *to = LOCPLINT->towns[townList.getSelectedIndex()];
+	const CGTownInstance *to = LOCPLINT->towns[townList->getSelectedIndex()];
 	if (selection != to)
 		select(to);
 }
@@ -673,7 +680,7 @@ void CAdvMapInt::centerOn(int3 on, bool fade)
 
 	if (fade)
 	{
-		terrain.fadeFromCurrentView();
+		terrain->fadeFromCurrentView();
 	}
 
 	switch (mode)
@@ -693,17 +700,17 @@ void CAdvMapInt::centerOn(int3 on, bool fade)
 	on = LOCPLINT->repairScreenPos(on);
 
 	position = on;
-	updateScreen=true;
+	redrawOnNextFrame=true;
 	underground->setIndex(on.z,true); //change underground switch button image
 	underground->redraw();
 	worldViewUnderground->setIndex(on.z, true);
 	worldViewUnderground->redraw();
 	if (switchedLevels)
-		minimap.setLevel(position.z);
-	minimap.redraw();
+		minimap->setLevel(position.z);
+	minimap->redraw();
 
 	if (mode == EAdvMapMode::WORLD_VIEW)
-		terrain.redraw();
+		terrain->redraw();
 }
 
 void CAdvMapInt::centerOn(const CGObjectInstance * obj, bool fade)
@@ -854,7 +861,7 @@ void CAdvMapInt::keyPressed(const SDL_Keycode & key)
 			}
 			else if(isActive()) //no ctrl, advmapint is on the top => switch to town
 			{
-				townList.selectNext();
+				townList->selectNext();
 			}
 			return;
 		}
@@ -870,11 +877,14 @@ void CAdvMapInt::keyPressed(const SDL_Keycode & key)
 				  (direction->y<0 ? UP    : 0) |
 				  (direction->y>0 ? DOWN  : 0) ;
 
-			scrollingDir |= Dir;
+
 
 			//ctrl makes arrow move screen, not hero
 			if(GH.isKeyboardCtrlDown())
+			{
+				scrollingDir |= Dir;
 				return;
+			}
 
 			if(!h || !isActive())
 				return;
@@ -889,11 +899,11 @@ void CAdvMapInt::keyPressed(const SDL_Keycode & key)
 			}
 
 			CGPath &path = LOCPLINT->paths[h];
-			terrain.currentPath = &path;
+			terrain->currentPath = &path;
 			int3 dst = h->visitablePos() + int3(direction->x, direction->y, 0);
 			if(dst != verifyPos(dst) || !LOCPLINT->cb->getPathsInfo(h)->getPath(path, dst))
 			{
-				terrain.currentPath = nullptr;
+				terrain->currentPath = nullptr;
 				return;
 			}
 
@@ -929,13 +939,6 @@ boost::optional<Point> CAdvMapInt::keyToMoveDirection(const SDL_Keycode & key)
 	return boost::none;
 }
 
-void CAdvMapInt::handleRightClick(std::string text, tribool down)
-{
-	if(down)
-	{
-		CRClickPopup::createAndPush(text);
-	}
-}
 int3 CAdvMapInt::verifyPos(int3 ver)
 {
 	if (ver.x<0)
@@ -968,14 +971,14 @@ void CAdvMapInt::select(const CArmedInstance *sel, bool centerView)
 	if(centerView)
 		centerOn(sel);
 
-	terrain.currentPath = nullptr;
+	terrain->currentPath = nullptr;
 	if(sel->ID==Obj::TOWN)
 	{
 		auto town = dynamic_cast<const CGTownInstance*>(sel);
 
-		infoBar.showTownSelection(town);
-		townList.select(town);
-		heroList.select(nullptr);
+		infoBar->showTownSelection(town);
+		townList->select(town);
+		heroList->select(nullptr);
 
 		updateSleepWake(nullptr);
 		updateMoveHero(nullptr);
@@ -985,18 +988,18 @@ void CAdvMapInt::select(const CArmedInstance *sel, bool centerView)
 	{
 		auto hero = dynamic_cast<const CGHeroInstance*>(sel);
 
-		infoBar.showHeroSelection(hero);
-		heroList.select(hero);
-		townList.select(nullptr);
+		infoBar->showHeroSelection(hero);
+		heroList->select(hero);
+		townList->select(nullptr);
 
-		terrain.currentPath = LOCPLINT->getAndVerifyPath(hero);
+		terrain->currentPath = LOCPLINT->getAndVerifyPath(hero);
 
 		updateSleepWake(hero);
 		updateMoveHero(hero);
 		updateSpellbook(hero);
 	}
-	townList.redraw();
-	heroList.redraw();
+	townList->redraw();
+	heroList->redraw();
 }
 
 void CAdvMapInt::mouseMoved( const Point & cursorPosition )
@@ -1057,13 +1060,16 @@ void CAdvMapInt::startHotSeatWait(PlayerColor Player)
 
 void CAdvMapInt::setPlayer(PlayerColor Player)
 {
+	if (Player == player)
+		return;
+
 	player = Player;
 	bg->playerColored(player);
 
 	panelMain->setPlayerColor(player);
 	panelWorldView->setPlayerColor(player);
 	panelWorldView->recolorIcons(player, player.getNum() * 19);
-	resdatabar.background->colorize(player);
+	resdatabar->colorize(player);
 }
 
 void CAdvMapInt::startTurn()
@@ -1073,7 +1079,51 @@ void CAdvMapInt::startTurn()
 		|| settings["session"]["spectate"].Bool())
 	{
 		adjustActiveness(false);
-		minimap.setAIRadar(false);
+		minimap->setAIRadar(false);
+	}
+}
+
+void CAdvMapInt::initializeNewTurn()
+{
+	heroList->update();
+	townList->update();
+
+	const CGHeroInstance * heroToSelect = nullptr;
+
+	// find first non-sleeping hero
+	for (auto hero : LOCPLINT->wanderingHeroes)
+	{
+		if (boost::range::find(LOCPLINT->sleepingHeroes, hero) == LOCPLINT->sleepingHeroes.end())
+		{
+			heroToSelect = hero;
+			break;
+		}
+	}
+
+	bool centerView = !settings["session"]["autoSkip"].Bool();
+
+	//select first hero if available.
+	if (heroToSelect != nullptr)
+	{
+		select(heroToSelect, centerView);
+	}
+	else if (LOCPLINT->towns.size())
+		select(LOCPLINT->towns.front(), centerView);
+	else
+		select(LOCPLINT->wanderingHeroes.front());
+
+	//show new day animation and sound on infobar
+	infoBar->showDate();
+
+	updateNextHero(nullptr);
+	showAll(screen);
+
+	if(settings["session"]["autoSkip"].Bool() && !GH.isKeyboardShiftDown())
+	{
+		if(CInfoWindow *iw = dynamic_cast<CInfoWindow *>(GH.topInt().get()))
+			iw->close();
+
+		endingTurn();
 	}
 }
 
@@ -1138,7 +1188,7 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos)
 	bool isHero = false;
 	if(selection->ID != Obj::HERO) //hero is not selected (presumably town)
 	{
-		assert(!terrain.currentPath); //path can be active only when hero is selected
+		assert(!terrain->currentPath); //path can be active only when hero is selected
 		if(selection == topBlocking) //selected town clicked
 			LOCPLINT->openTownWindow(static_cast<const CGTownInstance*>(topBlocking));
 		else if(canSelect)
@@ -1161,10 +1211,10 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos)
 		}
 		else //still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise
 		{
-			if(terrain.currentPath && terrain.currentPath->endPos() == mapPos)//we'll be moving
+			if(terrain->currentPath && terrain->currentPath->endPos() == mapPos)//we'll be moving
 			{
 				if(CGI->mh->canStartHeroMovement())
-					LOCPLINT->moveHero(currentHero, *terrain.currentPath);
+					LOCPLINT->moveHero(currentHero, *terrain->currentPath);
 				return;
 			}
 			else //remove old path and find a new one if we clicked on accessible tile
@@ -1176,7 +1226,7 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos)
 					path = newpath;
 
 				if(path.nodes.size())
-					terrain.currentPath = &path;
+					terrain->currentPath = &path;
 				else
 					LOCPLINT->eraseCurrentPathOf(currentHero);
 
@@ -1275,7 +1325,7 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
 
 		if(GH.isKeyboardAltDown() && pathNode->reachable()) //overwrite status bar text with movement info
 		{
-			ShowMoveDetailsInStatusbar(*hero, *pathNode);
+			showMoveDetailsInStatusbar(*hero, *pathNode);
 		}
 
 		int turns = pathNode->turns;
@@ -1341,7 +1391,7 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
 	}
 }
 
-void CAdvMapInt::ShowMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode)
+void CAdvMapInt::showMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode)
 {
 	const int maxMovementPointsAtStartOfLastTurn = pathNode.turns > 0 ? hero.maxMovePoints(pathNode.layer == EPathfindingLayer::LAND) : hero.movement;
 	const int movementPointsLastTurnCost = maxMovementPointsAtStartOfLastTurn - pathNode.moveRemains;
@@ -1394,7 +1444,7 @@ void CAdvMapInt::enterCastingMode(const CSpell * sp)
 	spellBeingCasted = sp;
 
 	deactivate();
-	terrain.activate();
+	terrain->activate();
 	GH.fakeMouseMove();
 }
 
@@ -1403,7 +1453,7 @@ void CAdvMapInt::leaveCastingMode(bool cast, int3 dest)
 	assert(spellBeingCasted);
 	SpellID id = spellBeingCasted->id;
 	spellBeingCasted = nullptr;
-	terrain.deactivate();
+	terrain->deactivate();
 	activate();
 
 	if(cast)
@@ -1415,7 +1465,7 @@ void CAdvMapInt::leaveCastingMode(bool cast, int3 dest)
 const CGHeroInstance * CAdvMapInt::curHero() const
 {
 	if(selection && selection->ID == Obj::HERO)
-		return static_cast<const CGHeroInstance *>(selection);
+		return dynamic_cast<const CGHeroInstance *>(selection);
 	else
 		return nullptr;
 }
@@ -1423,11 +1473,29 @@ const CGHeroInstance * CAdvMapInt::curHero() const
 const CGTownInstance * CAdvMapInt::curTown() const
 {
 	if(selection && selection->ID == Obj::TOWN)
-		return static_cast<const CGTownInstance *>(selection);
+		return dynamic_cast<const CGTownInstance *>(selection);
+	else
+		return nullptr;
+}
+
+const CArmedInstance * CAdvMapInt::curArmy() const
+{
+	if (selection)
+		return dynamic_cast<const CArmedInstance *>(selection);
 	else
 		return nullptr;
 }
 
+Rect CAdvMapInt::terrainAreaPixels() const
+{
+	return terrain->pos;
+}
+
+Rect CAdvMapInt::terrainAreaTiles() const
+{
+	return terrain->visibleTilesArea();
+}
+
 const IShipyard * CAdvMapInt::ourInaccessibleShipyard(const CGObjectInstance *obj) const
 {
 	const IShipyard *ret = IShipyard::castFrom(obj);
@@ -1447,9 +1515,9 @@ void CAdvMapInt::aiTurnStarted()
 
 	adjustActiveness(true);
 	CCS->musich->playMusicFromSet("enemy-turn", true, false);
-	adventureInt->minimap.setAIRadar(true);
-	adventureInt->infoBar.startEnemyTurn(LOCPLINT->cb->getCurrentPlayer());
-	adventureInt->infoBar.showAll(screen);//force refresh on inactive object
+	adventureInt->minimap->setAIRadar(true);
+	adventureInt->infoBar->startEnemyTurn(LOCPLINT->cb->getCurrentPlayer());
+	adventureInt->infoBar->showAll(screen);//force refresh on inactive object
 }
 
 void CAdvMapInt::adjustActiveness(bool aiTurnStart)
@@ -1488,9 +1556,9 @@ void CAdvMapInt::changeMode(EAdvMapMode newMode, float newScale)
 			panelWorldView->deactivate();
 			activeMapPanel = panelMain;
 
-			townList.activate();
-			heroList.activate();
-			infoBar.activate();
+			townList->activate();
+			heroList->activate();
+			infoBar->activate();
 
 			worldViewOptions.clear();
 
@@ -1501,10 +1569,10 @@ void CAdvMapInt::changeMode(EAdvMapMode newMode, float newScale)
 
 			activeMapPanel = panelWorldView;
 
-			townList.deactivate();
-			heroList.deactivate();
-			infoBar.showSelection(); // to prevent new day animation interfering world view mode
-			infoBar.deactivate();
+			townList->deactivate();
+			heroList->deactivate();
+			infoBar->showSelection(); // to prevent new day animation interfering world view mode
+			infoBar->deactivate();
 
 			break;
 		}
@@ -1537,3 +1605,7 @@ void CAdvMapInt::WorldViewOptions::adjustDrawingInfo(MapDrawingInfo& info)
 	info.additionalIcons = &iconPositions;
 }
 
+void CAdvMapInt::requestRedrawMapOnNextFrame()
+{
+	redrawOnNextFrame = true;
+}

+ 91 - 64
client/adventureMap/CAdvMapInt.h

@@ -14,12 +14,6 @@
 #include "../../lib/int3.h"
 #include "../../lib/GameConstants.h"
 
-#include "CTerrainRect.h"
-#include "CResDataBar.h"
-#include "CList.h"
-#include "CInfoBar.h"
-#include "CMinimap.h"
-
 VCMI_LIB_NAMESPACE_BEGIN
 
 class CGObjectInstance;
@@ -39,6 +33,12 @@ class CGStatusBar;
 class CAdvMapPanel;
 class CAdvMapWorldViewPanel;
 class CAnimation;
+class CTerrainRect;
+class CResDataBar;
+class CHeroList;
+class CTownList;
+class CInfoBar;
+class CMinimap;
 
 struct MapDrawingInfo;
 
@@ -53,74 +53,77 @@ enum class EAdvMapMode
 /// can get to the towns and heroes.
 class CAdvMapInt : public CIntObject
 {
-	//Return object that must be active at this tile (=clickable)
-	const CGObjectInstance *getActiveObject(const int3 &tile);
-
-	boost::optional<Point> keyToMoveDirection(const SDL_Keycode & key);
-
-public:
-	CAdvMapInt();
+	//TODO: remove
+	friend class CPlayerInterface;
+	friend class CTerrainRect;
 
-	int3 position; //top left corner of visible map part
-	PlayerColor player;
+private:
+	enum EDirections {LEFT=1, RIGHT=2, UP=4, DOWN=8};
+	enum EGameStates {NA, INGAME, WAITING};
 
-	bool duringAITurn;
+	struct WorldViewOptions
+	{
+		bool showAllTerrain; //for expert viewEarth
+		std::vector<ObjectPosInfo> iconPositions;
+		WorldViewOptions();
+		void clear();
+		void adjustDrawingInfo(MapDrawingInfo & info);
+	};
 
-	enum{LEFT=1, RIGHT=2, UP=4, DOWN=8};
-	ui8 scrollingDir; //uses enum: LEFT RIGHT, UP, DOWN
-	bool scrollingState;
 	bool swipeEnabled;
 	bool swipeMovementRequested;
 	int3 swipeTargetPosition;
 
-	enum{NA, INGAME, WAITING} state;
+	EGameStates state;
 
-	bool updateScreen;
 	ui8 anim, animValHitCount; //animation frame
 	ui8 heroAnim, heroAnimValHitCount; //animation frame
 
+	/// top left corner of visible map part
+	int3 position;
+
 	EAdvMapMode mode;
 	float worldViewScale;
 
-	struct WorldViewOptions
-	{
-		bool showAllTerrain; //for expert viewEarth
+	WorldViewOptions worldViewOptions;
 
-		std::vector<ObjectPosInfo> iconPositions;
+	/// Currently selected object, can be town, hero or null
+	const CArmedInstance *selection;
 
-		WorldViewOptions();
+	/// currently acting player
+	PlayerColor player;
 
-		void clear();
+	bool duringAITurn;
 
-		void adjustDrawingInfo(MapDrawingInfo & info);
-	};
+	/// uses EDirections enum
+	ui8 scrollingDir;
+	bool scrollingState;
 
-	WorldViewOptions worldViewOptions;
+	const CSpell *spellBeingCasted; //nullptr if none
 
-	std::shared_ptr<IImage> bg;
-	std::shared_ptr<IImage> bgWorldView;
 	std::vector<std::shared_ptr<CAnimImage>> gems;
-	CMinimap minimap;
-	std::shared_ptr<CGStatusBar> statusbar;
 
+	std::shared_ptr<IImage> bg;
+	std::shared_ptr<IImage> bgWorldView;
 	std::shared_ptr<CButton> kingOverview;
+	std::shared_ptr<CButton> sleepWake;
 	std::shared_ptr<CButton> underground;
 	std::shared_ptr<CButton> questlog;
-	std::shared_ptr<CButton> sleepWake;
 	std::shared_ptr<CButton> moveHero;
 	std::shared_ptr<CButton> spellbook;
 	std::shared_ptr<CButton> advOptions;
 	std::shared_ptr<CButton> sysOptions;
 	std::shared_ptr<CButton> nextHero;
 	std::shared_ptr<CButton> endTurn;
-
 	std::shared_ptr<CButton> worldViewUnderground;
 
-	CTerrainRect terrain; //visible terrain
-	CResDataBar resdatabar;
-	CHeroList heroList;
-	CTownList townList;
-	CInfoBar infoBar;
+	std::shared_ptr<CTerrainRect> terrain;
+	std::shared_ptr<CMinimap> minimap;
+	std::shared_ptr<CHeroList> heroList;
+	std::shared_ptr<CTownList> townList;
+	std::shared_ptr<CInfoBar> infoBar;
+	std::shared_ptr<CGStatusBar> statusbar;
+	std::shared_ptr<CResDataBar> resdatabar;
 
 	std::shared_ptr<CAdvMapPanel> panelMain; // panel that holds all right-side buttons in normal view
 	std::shared_ptr<CAdvMapWorldViewPanel> panelWorldView; // panel that holds all buttons and other ui in world view
@@ -128,10 +131,7 @@ public:
 
 	std::shared_ptr<CAnimation> worldViewIcons;// images for world view overlay
 
-	const CSpell *spellBeingCasted; //nullptr if none
-
-	const CArmedInstance *selection; //currently selected town/hero
-
+private:
 	//functions bound to buttons
 	void fshowOverview();
 	void fworldViewBack();
@@ -148,22 +148,49 @@ public:
 	void fnextHero();
 	void fendTurn();
 
+	void setScrollingCursor(ui8 direction) const;
+	void selectionChanged();
+	bool isActive();
+	void adjustActiveness(bool aiTurnStart); //should be called every time at AI/human turn transition; blocks GUI during AI turn
+
+	const IShipyard * ourInaccessibleShipyard(const CGObjectInstance *obj) const; //checks if obj is our ashipyard and cursor is 0,0 -> returns shipyard or nullptr else
+	//button updates
+	void updateSleepWake(const CGHeroInstance *h);
+	void updateSpellbook(const CGHeroInstance *h);
+
+	void handleMapScrollingUpdate();
+	void handleSwipeUpdate();
+
+	void showMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode);
+
+	const CGObjectInstance *getActiveObject(const int3 &tile);
+
+	boost::optional<Point> keyToMoveDirection(const SDL_Keycode & key);
+
+	bool redrawOnNextFrame;
+public:
+	CAdvMapInt();
+
+	// CIntObject interface implementation
+
 	void activate() override;
 	void deactivate() override;
 
-	void show(SDL_Surface * to) override; //redraws terrain
-	void showAll(SDL_Surface * to) override; //shows and activates adv. map interface
+	void show(SDL_Surface * to) override;
+	void showAll(SDL_Surface * to) override;
+
+	void keyPressed(const SDL_Keycode & key) override;
+	void keyReleased(const SDL_Keycode & key) override;
+	void mouseMoved (const Point & cursorPosition) override;
+
+	// public interface
+
+	void requestRedrawMapOnNextFrame();
 
 	void select(const CArmedInstance *sel, bool centerView = true);
-	void selectionChanged();
 	void centerOn(int3 on, bool fade = false);
 	void centerOn(const CGObjectInstance *obj, bool fade = false);
 	int3 verifyPos(int3 ver);
-	void handleRightClick(std::string text, tribool down);
-	void keyPressed(const SDL_Keycode & key) override;
-	void keyReleased(const SDL_Keycode & key) override;
-	void mouseMoved (const Point & cursorPosition) override;
-	bool isActive();
 
 	bool isHeroSleeping(const CGHeroInstance *hero);
 	void setHeroSleeping(const CGHeroInstance *hero, bool sleep);
@@ -172,34 +199,34 @@ public:
 	void setPlayer(PlayerColor Player);
 	void startHotSeatWait(PlayerColor Player);
 	void startTurn();
+	void initializeNewTurn();
 	void endingTurn();
 	void aiTurnStarted();
 
-	void adjustActiveness(bool aiTurnStart); //should be called every time at AI/human turn transition; blocks GUI during AI turn
 	void quickCombatLock(); //should be called when quick battle started
 	void quickCombatUnlock();
+
 	void tileLClicked(const int3 &mapPos);
 	void tileHovered(const int3 &mapPos);
 	void tileRClicked(const int3 &mapPos);
+
 	void enterCastingMode(const CSpell * sp);
 	void leaveCastingMode(bool cast = false, int3 dest = int3(-1, -1, -1));
 	const CGHeroInstance * curHero() const;
 	const CGTownInstance * curTown() const;
-	const IShipyard * ourInaccessibleShipyard(const CGObjectInstance *obj) const; //checks if obj is our ashipyard and cursor is 0,0 -> returns shipyard or nullptr else
-	//button updates
-	void updateSleepWake(const CGHeroInstance *h);
+	const CArmedInstance * curArmy() const;
+
 	void updateMoveHero(const CGHeroInstance *h, tribool hasPath = boost::logic::indeterminate);
-	void updateSpellbook(const CGHeroInstance *h);
 	void updateNextHero(const CGHeroInstance *h);
 
-	/// changes current adventure map mode; used to switch between default view and world view; scale is ignored if EAdvMapMode == NORMAL
-	void changeMode(EAdvMapMode newMode, float newScale = 0.36f);
+	/// returns area of screen covered by terrain (main game area)
+	Rect terrainAreaPixels() const;
 
-	void handleMapScrollingUpdate();
-	void handleSwipeUpdate();
+	/// returs visible section of game map, in tiles
+	Rect terrainAreaTiles() const;
 
-private:
-	void ShowMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode);
+	/// changes current adventure map mode; used to switch between default view and world view; scale is ignored if EAdvMapMode == NORMAL
+	void changeMode(EAdvMapMode newMode, float newScale);
 };
 
 extern std::shared_ptr<CAdvMapInt> adventureInt;

+ 0 - 2
client/adventureMap/CAdvMapPanel.cpp

@@ -74,8 +74,6 @@ CAdvMapWorldViewPanel::CAdvMapWorldViewPanel(std::shared_ptr<CAnimation> _icons,
 	}
 }
 
-CAdvMapWorldViewPanel::~CAdvMapWorldViewPanel() = default;
-
 void CAdvMapWorldViewPanel::recolorIcons(const PlayerColor & color, int indexOffset)
 {
 	assert(iconsData.size() == currentIcons.size());

+ 1 - 1
client/adventureMap/CAdvMapPanel.h

@@ -51,9 +51,9 @@ class CAdvMapWorldViewPanel : public CAdvMapPanel
 	std::shared_ptr<CAnimation> icons;
 public:
 	CAdvMapWorldViewPanel(std::shared_ptr<CAnimation> _icons, std::shared_ptr<IImage> bg, Point position, int spaceBottom, const PlayerColor &color);
-	virtual ~CAdvMapWorldViewPanel();
 
 	void addChildIcon(std::pair<int, Point> data, int indexOffset);
+
 	/// recreates all pictures from given def to recolor them according to current player color
 	void recolorIcons(const PlayerColor & color, int indexOffset);
 };

+ 2 - 1
client/adventureMap/CAdventureOptions.h

@@ -16,7 +16,6 @@ class CButton;
 /// Adventure options dialog where you can view the world, dig, play the replay of the last turn,...
 class CAdventureOptions : public CWindowObject
 {
-public:
 	std::shared_ptr<CButton> exit;
 	std::shared_ptr<CButton> viewWorld;
 	std::shared_ptr<CButton> puzzle;
@@ -24,7 +23,9 @@ public:
 	std::shared_ptr<CButton> scenInfo;
 	/*std::shared_ptr<CButton> replay*/
 
+public:
 	CAdventureOptions();
+
 	static void showScenarioInfo();
 };
 

+ 4 - 3
client/adventureMap/CInGameConsole.h

@@ -22,12 +22,13 @@ private:
 	int maxDisplayedTexts; //hiw many texts can be displayed simultaneously
 
 	std::weak_ptr<IStatusBar> currentStatusBar;
-public:
 	std::string enteredText;
-	void show(SDL_Surface * to) override;
+
+public:
 	void print(const std::string &txt);
-	void keyPressed(const SDL_Keycode & key) override;
 
+	void show(SDL_Surface * to) override;
+	void keyPressed(const SDL_Keycode & key) override;
 	void textInputed(const std::string & enteredText) override;
 	void textEdited(const std::string & enteredText) override;
 

+ 13 - 12
client/adventureMap/CInfoBar.cpp

@@ -17,6 +17,7 @@
 #include "../widgets/Images.h"
 #include "../widgets/TextControls.h"
 #include "../widgets/MiscWidgets.h"
+#include "../windows/InfoWindows.h"
 #include "../CGameInfo.h"
 #include "../CMusicHandler.h"
 #include "../CPlayerInterface.h"
@@ -196,19 +197,18 @@ void CInfoBar::reset()
 void CInfoBar::showSelection()
 {
 	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
-	if(adventureInt->selection)
+	if(adventureInt->curHero())
 	{
-		if(auto hero = dynamic_cast<const CGHeroInstance *>(adventureInt->selection))
-		{
-			showHeroSelection(hero);
-			return;
-		}
-		else if(auto town = dynamic_cast<const CGTownInstance *>(adventureInt->selection))
-		{
-			showTownSelection(town);
-			return;
-		}
+		showHeroSelection(adventureInt->curHero());
+		return;
 	}
+
+	if(adventureInt->curTown())
+	{
+		showTownSelection(adventureInt->curTown());
+		return;
+	}
+
 	showGameStatus();//FIXME: may be incorrect but shouldn't happen in general
 }
 
@@ -234,7 +234,8 @@ void CInfoBar::clickLeft(tribool down, bool previousState)
 
 void CInfoBar::clickRight(tribool down, bool previousState)
 {
-	adventureInt->handleRightClick(CGI->generaltexth->allTexts[109], down);
+	if (down)
+		CRClickPopup::createAndPush(CGI->generaltexth->allTexts[109]);
 }
 
 void CInfoBar::hover(bool on)

+ 9 - 15
client/adventureMap/CList.cpp

@@ -202,7 +202,7 @@ std::shared_ptr<CIntObject> CHeroList::CHeroItem::genSelection()
 
 void CHeroList::CHeroItem::select(bool on)
 {
-	if(on && adventureInt->selection != hero)
+	if(on && adventureInt->curHero() != hero)
 		adventureInt->select(hero);
 }
 
@@ -253,12 +253,9 @@ void CHeroList::update(const CGHeroInstance * hero)
 	//simplest solution for now: reset list and restore selection
 
 	listBox->resize(LOCPLINT->wanderingHeroes.size());
-	if (adventureInt->selection)
-	{
-		auto selectedHero = dynamic_cast<const CGHeroInstance *>(adventureInt->selection);
-		if (selectedHero)
-			select(selectedHero);
-	}
+	if (adventureInt->curHero())
+		select(adventureInt->curHero());
+
 	CList::update();
 }
 
@@ -294,8 +291,8 @@ void CTownList::CTownItem::update()
 
 void CTownList::CTownItem::select(bool on)
 {
-	if (on && adventureInt->selection != town)
-			adventureInt->select(town);
+	if (on && adventureInt->curTown() != town)
+		adventureInt->select(town);
 }
 
 void CTownList::CTownItem::open()
@@ -328,12 +325,9 @@ void CTownList::update(const CGTownInstance *)
 	//simplest solution for now: reset list and restore selection
 
 	listBox->resize(LOCPLINT->towns.size());
-	if (adventureInt->selection)
-	{
-		auto town = dynamic_cast<const CGTownInstance *>(adventureInt->selection);
-		if (town)
-			select(town);
-	}
+	if (adventureInt->curTown())
+		select(adventureInt->curTown());
+
 	CList::update();
 }
 

+ 58 - 147
client/adventureMap/CMinimap.cpp

@@ -12,6 +12,7 @@
 #include "CMinimap.h"
 
 #include "CAdvMapInt.h"
+#include "CTerrainRect.h"
 
 #include "../widgets/Images.h"
 #include "../CGameInfo.h"
@@ -19,6 +20,7 @@
 #include "../gui/CGuiHandler.h"
 #include "../render/Colors.h"
 #include "../renderSDL/SDL_PixelAccess.h"
+#include "../windows/InfoWindows.h"
 
 #include "../../CCallback.h"
 #include "../../lib/CGeneralTextHandler.h"
@@ -26,159 +28,65 @@
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapping/CMapDefines.h"
 
-#include <SDL_surface.h>
-
-const SDL_Color & CMinimapInstance::getTileColor(const int3 & pos)
+ColorRGBA CMinimapInstance::getTileColor(const int3 & pos) const
 {
 	const TerrainTile * tile = LOCPLINT->cb->getTile(pos, false);
 
 	// if tile is not visible it will be black on minimap
 	if(!tile)
-		return Colors::BLACK;
+		return CSDL_Ext::fromSDL(Colors::BLACK);
 
 	// if object at tile is owned - it will be colored as its owner
 	for (const CGObjectInstance *obj : tile->blockingObjects)
 	{
-		//heroes will be blitted later
-		switch (obj->ID)
-		{
-			case Obj::HERO:
-			case Obj::PRISON:
-				continue;
-		}
-
 		PlayerColor player = obj->getOwner();
 		if(player == PlayerColor::NEUTRAL)
-			return *graphics->neutralColor;
-		else
+			return CSDL_Ext::fromSDL(*graphics->neutralColor);
+
 		if (player < PlayerColor::PLAYER_LIMIT)
-			return graphics->playerColors[player.getNum()];
+			return CSDL_Ext::fromSDL(graphics->playerColors[player.getNum()]);
 	}
 
-	// else - use terrain color (blocked version or normal)
-	const auto & colorPair = parent->colors.find(tile->terType->getId())->second;
 	if (tile->blocked && (!tile->visitable))
-		return colorPair.second;
+		return tile->terType->minimapBlocked;
 	else
-		return colorPair.first;
-}
-void CMinimapInstance::tileToPixels (const int3 &tile, int &x, int &y, int toX, int toY)
-{
-	int3 mapSizes = LOCPLINT->cb->getMapSize();
-
-	double stepX = double(pos.w) / mapSizes.x;
-	double stepY = double(pos.h) / mapSizes.y;
-
-	x = static_cast<int>(toX + stepX * tile.x);
-	y = static_cast<int>(toY + stepY * tile.y);
-}
-
-void CMinimapInstance::blitTileWithColor(const SDL_Color &color, const int3 &tile, SDL_Surface *to, int toX, int toY)
-{
-	//coordinates of rectangle on minimap representing this tile
-	// begin - first to blit, end - first NOT to blit
-	int xBegin, yBegin, xEnd, yEnd;
-	tileToPixels (tile, xBegin, yBegin, toX, toY);
-	tileToPixels (int3 (tile.x + 1, tile.y + 1, tile.z), xEnd, yEnd, toX, toY);
-
-	for (int y=yBegin; y<yEnd; y++)
-	{
-		uint8_t *ptr = (uint8_t*)to->pixels + y * to->pitch + xBegin * minimap->format->BytesPerPixel;
-
-		for (int x=xBegin; x<xEnd; x++)
-			ColorPutter<4, 1>::PutColor(ptr, color);
-	}
+		return tile->terType->minimapUnblocked;
 }
 
 void CMinimapInstance::refreshTile(const int3 &tile)
 {
-	blitTileWithColor(getTileColor(int3(tile.x, tile.y, level)), tile, minimap, 0, 0);
+	if (level == tile.z)
+		minimap->drawPoint(Point(tile.x, tile.y), getTileColor(tile));
 }
 
-void CMinimapInstance::drawScaled(int level)
+void CMinimapInstance::redrawMinimap()
 {
 	int3 mapSizes = LOCPLINT->cb->getMapSize();
 
-	//size of one map tile on our minimap
-	double stepX = double(pos.w) / mapSizes.x;
-	double stepY = double(pos.h) / mapSizes.y;
-
-	double currY = 0;
-	for (int y=0; y<mapSizes.y; y++, currY += stepY)
-	{
-		double currX = 0;
-		for (int x=0; x<mapSizes.x; x++, currX += stepX)
-		{
-			const SDL_Color &color = getTileColor(int3(x,y,level));
-
-			//coordinates of rectangle on minimap representing this tile
-			// begin - first to blit, end - first NOT to blit
-			int xBegin = static_cast<int>(currX);
-			int yBegin = static_cast<int>(currY);
-			int xEnd = static_cast<int>(currX + stepX);
-			int yEnd = static_cast<int>(currY + stepY);
-
-			for (int y=yBegin; y<yEnd; y++)
-			{
-				uint8_t *ptr = (uint8_t*)minimap->pixels + y * minimap->pitch + xBegin * minimap->format->BytesPerPixel;
-
-				for (int x=xBegin; x<xEnd; x++)
-					ColorPutter<4, 1>::PutColor(ptr, color);
-			}
-		}
-	}
+	for (int y = 0; y < mapSizes.y; ++y)
+		for (int x = 0; x < mapSizes.x; ++x)
+			minimap->drawPoint(Point(x, y), getTileColor(int3(x, y, level)));
 }
 
 CMinimapInstance::CMinimapInstance(CMinimap *Parent, int Level):
 	parent(Parent),
-	minimap(CSDL_Ext::createSurfaceWithBpp<4>(parent->pos.w, parent->pos.h)),
+	minimap(new Canvas(Point(LOCPLINT->cb->getMapSize().x, LOCPLINT->cb->getMapSize().y))),
 	level(Level)
 {
 	pos.w = parent->pos.w;
 	pos.h = parent->pos.h;
-	drawScaled(level);
-}
-
-CMinimapInstance::~CMinimapInstance()
-{
-	SDL_FreeSurface(minimap);
+	redrawMinimap();
 }
 
 void CMinimapInstance::showAll(SDL_Surface * to)
 {
-	blitAtLoc(minimap, 0, 0, to);
-
-	//draw heroes
-	std::vector <const CGHeroInstance *> heroes = LOCPLINT->cb->getHeroesInfo(false); //TODO: do we really need separate function for drawing heroes?
-	for(auto & hero : heroes)
-	{
-		int3 position = hero->visitablePos();
-		if(position.z == level)
-		{
-			const SDL_Color & color = graphics->playerColors[hero->getOwner().getNum()];
-			blitTileWithColor(color, position, to, pos.x, pos.y);
-		}
-	}
-}
-
-std::map<TerrainId, std::pair<SDL_Color, SDL_Color> > CMinimap::loadColors()
-{
-	std::map<TerrainId, std::pair<SDL_Color, SDL_Color> > ret;
-
-	for(const auto & terrain : CGI->terrainTypeHandler->objects)
-	{
-		SDL_Color normal = CSDL_Ext::toSDL(terrain->minimapUnblocked);
-		SDL_Color blocked = CSDL_Ext::toSDL(terrain->minimapBlocked);
-
-		ret[terrain->getId()] = std::make_pair(normal, blocked);
-	}
-	return ret;
+	Canvas target(to);
+	target.draw(*minimap, pos.topLeft(), pos.dimensions());
 }
 
 CMinimap::CMinimap(const Rect & position)
 	: CIntObject(LCLICK | RCLICK | HOVER | MOVE, position.topLeft()),
-	level(0),
-	colors(loadColors())
+	level(0)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	pos.w = position.w;
@@ -188,21 +96,36 @@ CMinimap::CMinimap(const Rect & position)
 	aiShield->disable();
 }
 
-int3 CMinimap::translateMousePosition()
+int3 CMinimap::pixelToTile(const Point & cursorPos) const
 {
 	// 0 = top-left corner, 1 = bottom-right corner
-	double dx = double(GH.getCursorPosition().x - pos.x) / pos.w;
-	double dy = double(GH.getCursorPosition().y - pos.y) / pos.h;
+	double dx = static_cast<double>(cursorPos.x) / pos.w;
+	double dy = static_cast<double>(cursorPos.y) / pos.h;
+
+	int3 mapSizes = LOCPLINT->cb->getMapSize();
+
+	int tileX(std::round(mapSizes.x * dx));
+	int tileY(std::round(mapSizes.y * dy));
+
+	return int3(tileX, tileY, level);
+}
 
+Point CMinimap::tileToPixels(const int3 &tile) const
+{
 	int3 mapSizes = LOCPLINT->cb->getMapSize();
 
-	int3 tile ((si32)(mapSizes.x * dx), (si32)(mapSizes.y * dy), level);
-	return tile;
+	double stepX = static_cast<double>(pos.w) / mapSizes.x;
+	double stepY = static_cast<double>(pos.h) / mapSizes.y;
+
+	int x = static_cast<int>(stepX * tile.x);
+	int y = static_cast<int>(stepY * tile.y);
+
+	return Point(x,y);
 }
 
 void CMinimap::moveAdvMapSelection()
 {
-	int3 newLocation = translateMousePosition();
+	int3 newLocation = pixelToTile(GH.getCursorPosition() - pos.topLeft());
 	adventureInt->centerOn(newLocation);
 
 	if (!(adventureInt->active & GENERAL))
@@ -219,7 +142,8 @@ void CMinimap::clickLeft(tribool down, bool previousState)
 
 void CMinimap::clickRight(tribool down, bool previousState)
 {
-	adventureInt->handleRightClick(CGI->generaltexth->zelp[291].second, down);
+	if (down)
+		CRClickPopup::createAndPush(CGI->generaltexth->zelp[291].second);
 }
 
 void CMinimap::hover(bool on)
@@ -241,33 +165,22 @@ void CMinimap::showAll(SDL_Surface * to)
 	CIntObject::showAll(to);
 	if(minimap)
 	{
+		Canvas target(to);
+
 		int3 mapSizes = LOCPLINT->cb->getMapSize();
-		int3 tileCountOnScreen = adventureInt->terrain.tileCountOnScreen();
+		Rect screenArea = adventureInt->terrainAreaTiles();
 
 		//draw radar
-		Rect oldClip;
 		Rect radar =
 		{
-			si16(adventureInt->position.x * pos.w / mapSizes.x + pos.x),
-			si16(adventureInt->position.y * pos.h / mapSizes.y + pos.y),
-			ui16(tileCountOnScreen.x * pos.w / mapSizes.x),
-			ui16(tileCountOnScreen.y * pos.h / mapSizes.y)
+			screenArea.x * pos.w / mapSizes.x,
+			screenArea.y * pos.h / mapSizes.y,
+			screenArea.w * pos.w / mapSizes.x - 1,
+			screenArea.h * pos.h / mapSizes.y - 1
 		};
 
-		if(adventureInt->mode == EAdvMapMode::WORLD_VIEW)
-		{
-			// adjusts radar so that it doesn't go out of map in world view mode (since there's no frame)
-			radar.x = std::min<int>(std::max(pos.x, radar.x), pos.x + pos.w - radar.w);
-			radar.y = std::min<int>(std::max(pos.y, radar.y), pos.y + pos.h - radar.h);
-
-			if(radar.x < pos.x && radar.y < pos.y)
-				return; // whole map is visible at once, no point in redrawing border
-		}
-
-		CSDL_Ext::getClipRect(to, oldClip);
-		CSDL_Ext::setClipRect(to, pos);
-		CSDL_Ext::drawDashedBorder(to, radar, Colors::PURPLE);
-		CSDL_Ext::setClipRect(to, oldClip);
+		Canvas clippedTarget(target, pos);
+		clippedTarget.drawBorderDashed(radar, CSDL_Ext::fromSDL(Colors::PURPLE));
 	}
 }
 
@@ -283,6 +196,9 @@ void CMinimap::update()
 
 void CMinimap::setLevel(int newLevel)
 {
+	if (level == newLevel)
+		return;
+
 	level = newLevel;
 	update();
 }
@@ -299,18 +215,13 @@ void CMinimap::setAIRadar(bool on)
 		aiShield->disable();
 		update();
 	}
-	// this my happen during AI turn when this interface is inactive
+
+	// this may happen during AI turn when this interface is inactive
 	// force redraw in order to properly update interface
 	GH.totalRedraw();
 }
 
-void CMinimap::hideTile(const int3 &pos)
-{
-	if(minimap)
-		minimap->refreshTile(pos);
-}
-
-void CMinimap::showTile(const int3 &pos)
+void CMinimap::updateTile(const int3 &pos)
 {
 	if(minimap)
 		minimap->refreshTile(pos);

+ 17 - 21
client/adventureMap/CMinimap.h

@@ -11,67 +11,63 @@
 
 #include "../gui/CIntObject.h"
 #include "../../lib/GameConstants.h"
+#include "../render/Canvas.h"
 
+VCMI_LIB_NAMESPACE_BEGIN
+class ColorRGBA;
+VCMI_LIB_NAMESPACE_END
 
-struct SDL_Color;
 class CMinimap;
 
 class CMinimapInstance : public CIntObject
 {
 	CMinimap * parent;
-	SDL_Surface * minimap;
+	std::unique_ptr<Canvas> minimap;
 	int level;
 
 	//get color of selected tile on minimap
-	const SDL_Color & getTileColor(const int3 & pos);
+	ColorRGBA getTileColor(const int3 & pos) const;
 
-	void blitTileWithColor(const SDL_Color & color, const int3 & pos, SDL_Surface * to, int x, int y);
-
-	//draw minimap already scaled.
-	//result is not antialiased. Will result in "missing" pixels on huge maps (>144)
-	void drawScaled(int level);
+	void redrawMinimap();
 public:
 	CMinimapInstance(CMinimap * parent, int level);
-	~CMinimapInstance();
 
 	void showAll(SDL_Surface * to) override;
-	void tileToPixels (const int3 & tile, int & x, int & y, int toX = 0, int toY = 0);
 	void refreshTile(const int3 & pos);
 };
 
 /// Minimap which is displayed at the right upper corner of adventure map
 class CMinimap : public CIntObject
 {
-protected:
 	std::shared_ptr<CPicture> aiShield; //the graphic displayed during AI turn
 	std::shared_ptr<CMinimapInstance> minimap;
 	int level;
 
-	//to initialize colors
-	std::map<TerrainId, std::pair<SDL_Color, SDL_Color> > loadColors();
-
 	void clickLeft(tribool down, bool previousState) override;
 	void clickRight(tribool down, bool previousState) override;
 	void hover (bool on) override;
 	void mouseMoved (const Point & cursorPosition) override;
 
+	/// relocates center of adventure map screen to currently hovered tile
 	void moveAdvMapSelection();
 
+protected:
+	/// computes coordinates of tile below cursor pos
+	int3 pixelToTile(const Point & cursorPos) const;
+
+	/// computes position of tile within minimap instance
+	Point tileToPixels(const int3 & position) const;
+
 public:
-	// terrainID -> (normal color, blocked color)
-	const std::map<TerrainId, std::pair<SDL_Color, SDL_Color> > colors;
 
-	CMinimap(const Rect & position);
+	explicit CMinimap(const Rect & position);
 
-	//should be called to invalidate whole map - different player or level
-	int3 translateMousePosition();
 	void update();
 	void setLevel(int level);
 	void setAIRadar(bool on);
 
 	void showAll(SDL_Surface * to) override;
 
-	void hideTile(const int3 &pos); //puts FoW
-	void showTile(const int3 &pos); //removes FoW
+	void updateTile(const int3 &pos);
 };
 

+ 4 - 12
client/adventureMap/CResDataBar.cpp

@@ -13,7 +13,6 @@
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
 #include "../render/Colors.h"
-#include "../renderSDL/SDL_Extensions.h"
 #include "../gui/CGuiHandler.h"
 #include "../widgets/Images.h"
 
@@ -23,10 +22,6 @@
 
 #define ADVOPT (conf.go()->ac)
 
-void CResDataBar::clickRight(tribool down, bool previousState)
-{
-}
-
 CResDataBar::CResDataBar(const std::string & defname, int x, int y, int offx, int offy, int resdist, int datedist)
 {
 	pos.x += x;
@@ -70,8 +65,6 @@ CResDataBar::CResDataBar()
 
 }
 
-CResDataBar::~CResDataBar() = default;
-
 std::string CResDataBar::buildDateString()
 {
 	std::string pattern = "%s: %d, %s: %d, %s: %d";
@@ -96,14 +89,13 @@ void CResDataBar::draw(SDL_Surface * to)
 	graphics->fonts[FONT_SMALL]->renderTextLeft(to, buildDateString(), Colors::WHITE, Point(txtpos[7].first, txtpos[7].second));
 }
 
-void CResDataBar::show(SDL_Surface * to)
-{
-
-}
-
 void CResDataBar::showAll(SDL_Surface * to)
 {
 	CIntObject::showAll(to);
 	draw(to);
 }
 
+void CResDataBar::colorize(PlayerColor player)
+{
+	background->colorize(player);
+}

+ 4 - 5
client/adventureMap/CResDataBar.h

@@ -17,18 +17,17 @@ class CResDataBar : public CIntObject
 {
 	std::string buildDateString();
 
-public:
 	std::shared_ptr<CPicture> background;
 
 	std::vector<std::pair<int,int> > txtpos;
 
-	void clickRight(tribool down, bool previousState) override;
+
+	void draw(SDL_Surface * to);
+public:
 	CResDataBar();
 	CResDataBar(const std::string &defname, int x, int y, int offx, int offy, int resdist, int datedist);
-	~CResDataBar();
 
-	void draw(SDL_Surface * to);
-	void show(SDL_Surface * to) override;
+	void colorize(PlayerColor player);
 	void showAll(SDL_Surface * to) override;
 };
 

+ 9 - 8
client/adventureMap/CTerrainRect.cpp

@@ -189,7 +189,7 @@ void CTerrainRect::hover(bool on)
 {
 	if (!on)
 	{
-		adventureInt->statusbar->clear();
+		GH.statusbar->clear();
 		CCS->curh->set(Cursor::Map::POINTER);
 	}
 	//Hoverable::hover(on);
@@ -360,10 +360,11 @@ void CTerrainRect::showAll(SDL_Surface * to)
 
 void CTerrainRect::showAnim(SDL_Surface * to)
 {
-	if (fadeAnim->isFading())
+	if (!needsAnimUpdate())
+		return;
+
+	if (fadeAnim->isFading() || lastRedrawStatus == EMapAnimRedrawStatus::REDRAW_REQUESTED)
 		show(to);
-	else if (lastRedrawStatus == EMapAnimRedrawStatus::REDRAW_REQUESTED)
-		show(to); // currently the same; maybe we should pass some flag to map handler so it redraws ONLY tiles that need redraw instead of full
 }
 
 int3 CTerrainRect::whichTileIsIt(const int x, const int y)
@@ -380,17 +381,17 @@ int3 CTerrainRect::whichTileIsIt()
 	return whichTileIsIt(GH.getCursorPosition().x, GH.getCursorPosition().y);
 }
 
-int3 CTerrainRect::tileCountOnScreen()
+Rect CTerrainRect::visibleTilesArea()
 {
 	switch (adventureInt->mode)
 	{
 	default:
 		logGlobal->error("Unknown map mode %d", (int)adventureInt->mode);
-		return int3();
+		return Rect();
 	case EAdvMapMode::NORMAL:
-		return int3(tilesw, tilesh, 1);
+		return Rect(adventureInt->position.x, adventureInt->position.y, tilesw, tilesh);
 	case EAdvMapMode::WORLD_VIEW:
-		return int3((si32)(tilesw / adventureInt->worldViewScale), (si32)(tilesh / adventureInt->worldViewScale), 1);
+		return Rect(adventureInt->position.x, adventureInt->position.y, tilesw / adventureInt->worldViewScale, tilesh / adventureInt->worldViewScale);
 	}
 }
 

+ 14 - 7
client/adventureMap/CTerrainRect.h

@@ -35,14 +35,22 @@ class CTerrainRect : public CIntObject
 	void handleSwipeMove(const Point & cursorPosition);
 	/// handles start/finish of swipe (press/release of corresponding button); returns true if state change was handled
 	bool handleSwipeStateChange(bool btnPressed);
+	int3 curHoveredTile;
+
+	int3 whichTileIsIt(const int x, const int y); //x,y are cursor position
+	int3 whichTileIsIt(); //uses current cursor pos
+	void showPath(const Rect &extRect, SDL_Surface * to);
+
+	bool needsAnimUpdate();
 public:
 	int tilesw, tilesh; //width and height of terrain to blit in tiles
-	int3 curHoveredTile;
 	int moveX, moveY; //shift between actual position of screen and the one we wil blit; ranges from -31 to 31 (in pixels)
 	CGPath * currentPath;
 
 	CTerrainRect();
-	virtual ~CTerrainRect();
+	~CTerrainRect();
+
+	// CIntObject interface implementation
 	void deactivate() override;
 	void clickLeft(tribool down, bool previousState) override;
 	void clickRight(tribool down, bool previousState) override;
@@ -51,14 +59,13 @@ public:
 	void mouseMoved (const Point & cursorPosition) override;
 	void show(SDL_Surface * to) override;
 	void showAll(SDL_Surface * to) override;
+
 	void showAnim(SDL_Surface * to);
-	void showPath(const Rect &extRect, SDL_Surface * to);
-	int3 whichTileIsIt(const int x, const int y); //x,y are cursor position
-	int3 whichTileIsIt(); //uses current cursor pos
+
 	/// @returns number of visible tiles on screen respecting current map scaling
-	int3 tileCountOnScreen();
+	Rect visibleTilesArea();
+
 	/// animates view by caching current surface and crossfading it with normal screen
 	void fadeFromCurrentView();
-	bool needsAnimUpdate();
 };
 

+ 59 - 58
client/adventureMap/mapHandler.cpp

@@ -23,6 +23,7 @@
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapObjects/CObjectClassesHandler.h"
 #include "../../lib/mapping/CMap.h"
+#include "../../lib/Color.h"
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/CStopWatch.h"
@@ -359,7 +360,7 @@ void CMapHandler::init()
 	logGlobal->info("\tMaking object rects: %d ms", th.getDiff());
 }
 
-CMapHandler::CMapBlitter *CMapHandler::resolveBlitter(const MapDrawingInfo * info) const
+CMapBlitter *CMapHandler::resolveBlitter(const MapDrawingInfo * info) const
 {
 	if (info->scaled)
 		return worldViewBlitter;
@@ -369,12 +370,12 @@ CMapHandler::CMapBlitter *CMapHandler::resolveBlitter(const MapDrawingInfo * inf
 	return normalBlitter;
 }
 
-void CMapHandler::CMapNormalBlitter::drawElement(EMapCacheType cacheType, std::shared_ptr<IImage> source, Rect * sourceRect, SDL_Surface * targetSurf, Rect * destRect) const
+void CMapNormalBlitter::drawElement(EMapCacheType cacheType, std::shared_ptr<IImage> source, Rect * sourceRect, SDL_Surface * targetSurf, Rect * destRect) const
 {
 	source->draw(targetSurf, destRect, sourceRect);
 }
 
-void CMapHandler::CMapNormalBlitter::init(const MapDrawingInfo * drawingInfo)
+void CMapNormalBlitter::init(const MapDrawingInfo * drawingInfo)
 {
 	info = drawingInfo;
 	// Width and height of the portion of the map to process. Units in tiles.
@@ -425,7 +426,7 @@ void CMapHandler::CMapNormalBlitter::init(const MapDrawingInfo * drawingInfo)
 		tileCount.y = parent->sizes.y + parent->frameH - topTile.y;
 }
 
-Rect CMapHandler::CMapNormalBlitter::clip(SDL_Surface * targetSurf) const
+Rect CMapNormalBlitter::clip(SDL_Surface * targetSurf) const
 {
 	Rect prevClip;
 	CSDL_Ext::getClipRect(targetSurf, prevClip);
@@ -433,7 +434,7 @@ Rect CMapHandler::CMapNormalBlitter::clip(SDL_Surface * targetSurf) const
 	return prevClip;
 }
 
-CMapHandler::CMapNormalBlitter::CMapNormalBlitter(CMapHandler * parent)
+CMapNormalBlitter::CMapNormalBlitter(CMapHandler * parent)
 	: CMapBlitter(parent)
 {
 	tileSize = 32;
@@ -441,7 +442,7 @@ CMapHandler::CMapNormalBlitter::CMapNormalBlitter(CMapHandler * parent)
 	defaultTileRect = Rect(0, 0, tileSize, tileSize);
 }
 
-std::shared_ptr<IImage> CMapHandler::CMapWorldViewBlitter::objectToIcon(Obj id, si32 subId, PlayerColor owner) const
+std::shared_ptr<IImage> CMapWorldViewBlitter::objectToIcon(Obj id, si32 subId, PlayerColor owner) const
 {
 	int ownerIndex = 0;
 	if(owner < PlayerColor::PLAYER_LIMIT)
@@ -475,7 +476,7 @@ std::shared_ptr<IImage> CMapHandler::CMapWorldViewBlitter::objectToIcon(Obj id,
 	return std::shared_ptr<IImage>();
 }
 
-void CMapHandler::CMapWorldViewBlitter::calculateWorldViewCameraPos()
+void CMapWorldViewBlitter::calculateWorldViewCameraPos()
 {
 	bool outsideLeft = topTile.x < 0;
 	bool outsideTop = topTile.y < 0;
@@ -507,7 +508,7 @@ void CMapHandler::CMapWorldViewBlitter::calculateWorldViewCameraPos()
 		topTile.y = parent->sizes.y - tileCount.y;
 }
 
-void CMapHandler::CMapWorldViewBlitter::drawElement(EMapCacheType cacheType, std::shared_ptr<IImage> source, Rect * sourceRect, SDL_Surface * targetSurf, Rect * destRect) const
+void CMapWorldViewBlitter::drawElement(EMapCacheType cacheType, std::shared_ptr<IImage> source, Rect * sourceRect, SDL_Surface * targetSurf, Rect * destRect) const
 {
 	auto scaled = parent->cache.requestWorldViewCacheOrCreate(cacheType, source);
 
@@ -515,7 +516,7 @@ void CMapHandler::CMapWorldViewBlitter::drawElement(EMapCacheType cacheType, std
 		scaled->draw(targetSurf, destRect, sourceRect);
 }
 
-void CMapHandler::CMapWorldViewBlitter::drawTileOverlay(SDL_Surface * targetSurf, const TerrainTile2 & tile) const
+void CMapWorldViewBlitter::drawTileOverlay(SDL_Surface * targetSurf, const TerrainTile2 & tile) const
 {
 	auto drawIcon = [this,targetSurf](Obj id, si32 subId, PlayerColor owner)
 	{
@@ -547,7 +548,7 @@ void CMapHandler::CMapWorldViewBlitter::drawTileOverlay(SDL_Surface * targetSurf
 	}
 }
 
-void CMapHandler::CMapWorldViewBlitter::drawOverlayEx(SDL_Surface * targetSurf)
+void CMapWorldViewBlitter::drawOverlayEx(SDL_Surface * targetSurf)
 {
 	if(nullptr == info->additionalIcons)
 		return;
@@ -579,7 +580,7 @@ void CMapHandler::CMapWorldViewBlitter::drawOverlayEx(SDL_Surface * targetSurf)
 	}
 }
 
-void CMapHandler::CMapWorldViewBlitter::drawHeroFlag(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, Rect * sourceRect, Rect * destRect, bool moving) const
+void CMapWorldViewBlitter::drawHeroFlag(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, Rect * sourceRect, Rect * destRect, bool moving) const
 {
 	if (moving)
 		return;
@@ -587,7 +588,7 @@ void CMapHandler::CMapWorldViewBlitter::drawHeroFlag(SDL_Surface * targetSurf, s
 	CMapBlitter::drawHeroFlag(targetSurf, source, sourceRect, destRect, false);
 }
 
-void CMapHandler::CMapWorldViewBlitter::drawObject(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, Rect * sourceRect, bool moving) const
+void CMapWorldViewBlitter::drawObject(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, Rect * sourceRect, bool moving) const
 {
 	if (moving)
 		return;
@@ -596,7 +597,7 @@ void CMapHandler::CMapWorldViewBlitter::drawObject(SDL_Surface * targetSurf, std
 	CMapBlitter::drawObject(targetSurf, source, &scaledSourceRect, false);
 }
 
-void CMapHandler::CMapBlitter::drawTileTerrain(SDL_Surface * targetSurf, const TerrainTile & tinfo, const TerrainTile2 & tile) const
+void CMapBlitter::drawTileTerrain(SDL_Surface * targetSurf, const TerrainTile & tinfo, const TerrainTile2 & tile) const
 {
 	Rect destRect(realTileRect);
 
@@ -611,7 +612,7 @@ void CMapHandler::CMapBlitter::drawTileTerrain(SDL_Surface * targetSurf, const T
 	drawElement(EMapCacheType::TERRAIN, parent->terrainImages[terrainName][tinfo.terView][rotation], nullptr, targetSurf, &destRect);
 }
 
-void CMapHandler::CMapWorldViewBlitter::init(const MapDrawingInfo * drawingInfo)
+void CMapWorldViewBlitter::init(const MapDrawingInfo * drawingInfo)
 {
 	info = drawingInfo;
 	parent->cache.updateWorldViewScale(info->scale);
@@ -632,7 +633,7 @@ void CMapHandler::CMapWorldViewBlitter::init(const MapDrawingInfo * drawingInfo)
 	calculateWorldViewCameraPos();
 }
 
-Rect CMapHandler::CMapWorldViewBlitter::clip(SDL_Surface * targetSurf) const
+Rect CMapWorldViewBlitter::clip(SDL_Surface * targetSurf) const
 {
 	Rect prevClip;
 
@@ -648,12 +649,12 @@ Rect CMapHandler::CMapWorldViewBlitter::clip(SDL_Surface * targetSurf) const
 	return prevClip;
 }
 
-CMapHandler::CMapWorldViewBlitter::CMapWorldViewBlitter(CMapHandler * parent)
+CMapWorldViewBlitter::CMapWorldViewBlitter(CMapHandler * parent)
 	: CMapBlitter(parent)
 {
 }
 
-void CMapHandler::CMapPuzzleViewBlitter::drawObjects(SDL_Surface * targetSurf, const TerrainTile2 & tile) const
+void CMapPuzzleViewBlitter::drawObjects(SDL_Surface * targetSurf, const TerrainTile2 & tile) const
 {
 	CMapBlitter::drawObjects(targetSurf, tile);
 
@@ -665,12 +666,12 @@ void CMapHandler::CMapPuzzleViewBlitter::drawObjects(SDL_Surface * targetSurf, c
 	}
 }
 
-void CMapHandler::CMapPuzzleViewBlitter::postProcessing(SDL_Surface * targetSurf) const
+void CMapPuzzleViewBlitter::postProcessing(SDL_Surface * targetSurf) const
 {
 	CSDL_Ext::applyEffect(targetSurf, info->drawBounds, static_cast<int>(!ADVOPT.puzzleSepia));
 }
 
-bool CMapHandler::CMapPuzzleViewBlitter::canDrawObject(const CGObjectInstance * obj) const
+bool CMapPuzzleViewBlitter::canDrawObject(const CGObjectInstance * obj) const
 {
 	if (!CMapBlitter::canDrawObject(obj))
 		return false;
@@ -685,43 +686,43 @@ bool CMapHandler::CMapPuzzleViewBlitter::canDrawObject(const CGObjectInstance *
 	return true;
 }
 
-CMapHandler::CMapPuzzleViewBlitter::CMapPuzzleViewBlitter(CMapHandler * parent)
+CMapPuzzleViewBlitter::CMapPuzzleViewBlitter(CMapHandler * parent)
 	: CMapNormalBlitter(parent)
 {
 	unblittableObjects.push_back(Obj::HOLE);
 }
 
-CMapHandler::CMapBlitter::CMapBlitter(CMapHandler * p)
+CMapBlitter::CMapBlitter(CMapHandler * p)
 	:parent(p), tileSize(0), halfTileSizeCeil(0), info(nullptr)
 {
 
 }
 
-CMapHandler::CMapBlitter::~CMapBlitter() = default;
+CMapBlitter::~CMapBlitter() = default;
 
-void CMapHandler::CMapBlitter::drawFrame(SDL_Surface * targetSurf) const
+void CMapBlitter::drawFrame(SDL_Surface * targetSurf) const
 {
 	Rect destRect(realTileRect);
 	drawElement(EMapCacheType::FRAME, parent->egdeImages[parent->edgeFrames[pos.x][pos.y][topTile.z]], nullptr, targetSurf, &destRect);
 }
 
-void CMapHandler::CMapBlitter::drawOverlayEx(SDL_Surface * targetSurf)
+void CMapBlitter::drawOverlayEx(SDL_Surface * targetSurf)
 {
 //nothing to do here
 }
 
-void CMapHandler::CMapBlitter::drawHeroFlag(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, Rect * sourceRect, Rect * destRect, bool moving) const
+void CMapBlitter::drawHeroFlag(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, Rect * sourceRect, Rect * destRect, bool moving) const
 {
 	drawElement(EMapCacheType::HERO_FLAGS, source, sourceRect, targetSurf, destRect);
 }
 
-void CMapHandler::CMapBlitter::drawObject(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, Rect * sourceRect, bool moving) const
+void CMapBlitter::drawObject(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, Rect * sourceRect, bool moving) const
 {
 	Rect dstRect(realTileRect);
 	drawElement(EMapCacheType::OBJECTS, source, sourceRect, targetSurf, &dstRect);
 }
 
-void CMapHandler::CMapBlitter::drawObjects(SDL_Surface * targetSurf, const TerrainTile2 & tile) const
+void CMapBlitter::drawObjects(SDL_Surface * targetSurf, const TerrainTile2 & tile) const
 {
 	auto & objects = tile.objects;
 	for(auto & object : objects)
@@ -781,7 +782,7 @@ void CMapHandler::CMapBlitter::drawObjects(SDL_Surface * targetSurf, const Terra
 	}
 }
 
-void CMapHandler::CMapBlitter::drawRoad(SDL_Surface * targetSurf, const TerrainTile & tinfo, const TerrainTile * tinfoUpper) const
+void CMapBlitter::drawRoad(SDL_Surface * targetSurf, const TerrainTile & tinfo, const TerrainTile * tinfoUpper) const
 {
 	if (tinfoUpper && tinfoUpper->roadType->getId() != Road::NO_ROAD)
 	{
@@ -802,14 +803,14 @@ void CMapHandler::CMapBlitter::drawRoad(SDL_Surface * targetSurf, const TerrainT
 	}
 }
 
-void CMapHandler::CMapBlitter::drawRiver(SDL_Surface * targetSurf, const TerrainTile & tinfo) const
+void CMapBlitter::drawRiver(SDL_Surface * targetSurf, const TerrainTile & tinfo) const
 {
 	Rect destRect(realTileRect);
 	ui8 rotation = (tinfo.extTileFlags >> 2) % 4;
 	drawElement(EMapCacheType::RIVERS, parent->riverImages[tinfo.riverType->getJsonKey()][tinfo.riverDir][rotation], nullptr, targetSurf, &destRect);
 }
 
-void CMapHandler::CMapBlitter::drawFow(SDL_Surface * targetSurf) const
+void CMapBlitter::drawFow(SDL_Surface * targetSurf) const
 {
 	const NeighborTilesInfo neighborInfo(pos, parent->sizes, info->visibilityMap);
 
@@ -828,7 +829,7 @@ void CMapHandler::CMapBlitter::drawFow(SDL_Surface * targetSurf) const
 	drawElement(EMapCacheType::FOW, image, nullptr, targetSurf, &destRect);
 }
 
-void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingInfo * info)
+void CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingInfo * info)
 {
 	init(info);
 	auto prevClip = clip(targetSurf);
@@ -925,21 +926,21 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn
 		{
 			for (realPos.y = initPos.y, pos.y = topTile.y; pos.y < topTile.y + tileCount.y; pos.y++, realPos.y += tileSize)
 			{
-				const int3 color(0x555555, 0x555555, 0x555555);
+				constexpr ColorRGBA color(0x55, 0x55, 0x55);
 
 				if (realPos.y >= info->drawBounds.y &&
 					realPos.y < info->drawBounds.y + info->drawBounds.h)
 					for(int i = 0; i < tileSize; i++)
 						if (realPos.x + i >= info->drawBounds.x &&
 							realPos.x + i < info->drawBounds.x + info->drawBounds.w)
-							CSDL_Ext::putPixelWithoutRefresh(targetSurf, realPos.x + i, realPos.y, color.x, color.y, color.z);
+							CSDL_Ext::putPixelWithoutRefresh(targetSurf, realPos.x + i, realPos.y, color.r, color.g, color.b);
 
 				if (realPos.x >= info->drawBounds.x &&
 					realPos.x < info->drawBounds.x + info->drawBounds.w)
 					for(int i = 0; i < tileSize; i++)
 						if (realPos.y + i >= info->drawBounds.y &&
 							realPos.y + i < info->drawBounds.y + info->drawBounds.h)
-							CSDL_Ext::putPixelWithoutRefresh(targetSurf, realPos.x, realPos.y + i, color.x, color.y, color.z);
+							CSDL_Ext::putPixelWithoutRefresh(targetSurf, realPos.x, realPos.y + i, color.r, color.g, color.b);
 			}
 		}
 	}
@@ -949,14 +950,14 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn
 	CSDL_Ext::setClipRect(targetSurf, prevClip);
 }
 
-CMapHandler::AnimBitmapHolder CMapHandler::CMapBlitter::findHeroBitmap(const CGHeroInstance * hero, int anim) const
+AnimBitmapHolder CMapBlitter::findHeroBitmap(const CGHeroInstance * hero, int anim) const
 {
 	if(hero && hero->moveDir && hero->type) //it's hero or boat
 	{
 		if(hero->tempOwner >= PlayerColor::PLAYER_LIMIT) //Neutral hero?
 		{
 			logGlobal->error("A neutral hero (%s) at %s. Should not happen!", hero->getNameTranslated(), hero->pos.toString());
-			return CMapHandler::AnimBitmapHolder();
+			return AnimBitmapHolder();
 		}
 
 		//pick graphics of hero (or boat if hero is sailing)
@@ -977,23 +978,23 @@ CMapHandler::AnimBitmapHolder CMapHandler::CMapBlitter::findHeroBitmap(const CGH
 			//get flag overlay only if we have main image
 			auto flagImage = findFlagBitmap(hero, anim, &hero->tempOwner, group);
 
-			return CMapHandler::AnimBitmapHolder(heroImage, flagImage, moving);
+			return AnimBitmapHolder(heroImage, flagImage, moving);
 		}
 	}
-	return CMapHandler::AnimBitmapHolder();
+	return AnimBitmapHolder();
 }
 
-CMapHandler::AnimBitmapHolder CMapHandler::CMapBlitter::findBoatBitmap(const CGBoat * boat, int anim) const
+AnimBitmapHolder CMapBlitter::findBoatBitmap(const CGBoat * boat, int anim) const
 {
 	auto animation = graphics->boatAnimations.at(boat->subID);
 	int group = getHeroFrameGroup(boat->direction, false);
 	if(animation->size(group) > 0)
-		return CMapHandler::AnimBitmapHolder(animation->getImage(anim % animation->size(group), group));
+		return AnimBitmapHolder(animation->getImage(anim % animation->size(group), group));
 	else
-		return CMapHandler::AnimBitmapHolder();
+		return AnimBitmapHolder();
 }
 
-std::shared_ptr<IImage> CMapHandler::CMapBlitter::findFlagBitmap(const CGHeroInstance * hero, int anim, const PlayerColor * color, int group) const
+std::shared_ptr<IImage> CMapBlitter::findFlagBitmap(const CGHeroInstance * hero, int anim, const PlayerColor * color, int group) const
 {
 	if(!hero)
 		return std::shared_ptr<IImage>();
@@ -1003,12 +1004,12 @@ std::shared_ptr<IImage> CMapHandler::CMapBlitter::findFlagBitmap(const CGHeroIns
 	return findHeroFlagBitmap(hero, anim, color, group);
 }
 
-std::shared_ptr<IImage> CMapHandler::CMapBlitter::findHeroFlagBitmap(const CGHeroInstance * hero, int anim, const PlayerColor * color, int group) const
+std::shared_ptr<IImage> CMapBlitter::findHeroFlagBitmap(const CGHeroInstance * hero, int anim, const PlayerColor * color, int group) const
 {
 	return findFlagBitmapInternal(graphics->heroFlagAnimations.at(color->getNum()), anim, group, hero->moveDir, !hero->isStanding);
 }
 
-std::shared_ptr<IImage> CMapHandler::CMapBlitter::findBoatFlagBitmap(const CGBoat * boat, int anim, const PlayerColor * color, int group, ui8 dir) const
+std::shared_ptr<IImage> CMapBlitter::findBoatFlagBitmap(const CGBoat * boat, int anim, const PlayerColor * color, int group, ui8 dir) const
 {
 	int boatType = boat->subID;
 	if(boatType < 0 || boatType >= graphics->boatFlagAnimations.size())
@@ -1030,7 +1031,7 @@ std::shared_ptr<IImage> CMapHandler::CMapBlitter::findBoatFlagBitmap(const CGBoa
 	return findFlagBitmapInternal(subtypeFlags.at(colorIndex), anim, group, dir, false);
 }
 
-std::shared_ptr<IImage> CMapHandler::CMapBlitter::findFlagBitmapInternal(std::shared_ptr<CAnimation> animation, int anim, int group, ui8 dir, bool moving) const
+std::shared_ptr<IImage> CMapBlitter::findFlagBitmapInternal(std::shared_ptr<CAnimation> animation, int anim, int group, ui8 dir, bool moving) const
 {
 	size_t groupSize = animation->size(group);
 	if(groupSize == 0)
@@ -1042,10 +1043,10 @@ std::shared_ptr<IImage> CMapHandler::CMapBlitter::findFlagBitmapInternal(std::sh
 		return animation->getImage((anim / 4) % groupSize, group);
 }
 
-CMapHandler::AnimBitmapHolder CMapHandler::CMapBlitter::findObjectBitmap(const CGObjectInstance * obj, int anim) const
+AnimBitmapHolder CMapBlitter::findObjectBitmap(const CGObjectInstance * obj, int anim) const
 {
 	if (!obj)
-		return CMapHandler::AnimBitmapHolder();
+		return AnimBitmapHolder();
 	if (obj->ID == Obj::HERO)
 		return findHeroBitmap(static_cast<const CGHeroInstance*>(obj), anim);
 	if (obj->ID == Obj::BOAT)
@@ -1055,18 +1056,18 @@ CMapHandler::AnimBitmapHolder CMapHandler::CMapBlitter::findObjectBitmap(const C
 	std::shared_ptr<CAnimation> animation = graphics->getAnimation(obj);
 	size_t groupSize = animation->size();
 	if(groupSize == 0)
-		return CMapHandler::AnimBitmapHolder();
+		return AnimBitmapHolder();
 
 	auto bitmap = animation->getImage((anim + getPhaseShift(obj)) % groupSize);
 	if(!bitmap)
-		return CMapHandler::AnimBitmapHolder();
+		return AnimBitmapHolder();
 
 	bitmap->setFlagColor(obj->tempOwner);
 
-	return CMapHandler::AnimBitmapHolder(bitmap);
+	return AnimBitmapHolder(bitmap);
 }
 
-ui8 CMapHandler::CMapBlitter::getPhaseShift(const CGObjectInstance *object) const
+ui8 CMapBlitter::getPhaseShift(const CGObjectInstance *object) const
 {
 	auto i = parent->animationPhase.find(object);
 	if(i == parent->animationPhase.end())
@@ -1079,13 +1080,13 @@ ui8 CMapHandler::CMapBlitter::getPhaseShift(const CGObjectInstance *object) cons
 	return i->second;
 }
 
-bool CMapHandler::CMapBlitter::canDrawObject(const CGObjectInstance * obj) const
+bool CMapBlitter::canDrawObject(const CGObjectInstance * obj) const
 {
 	//checking if object has non-empty graphic on this tile
 	return obj->ID == Obj::HERO || obj->coveringAt(pos.x, pos.y);
 }
 
-bool CMapHandler::CMapBlitter::canDrawCurrentTile() const
+bool CMapBlitter::canDrawCurrentTile() const
 {
 	if(settings["session"]["spectate"].Bool())
 		return true;
@@ -1094,7 +1095,7 @@ bool CMapHandler::CMapBlitter::canDrawCurrentTile() const
 	return !neighbors.areAllHidden();
 }
 
-ui8 CMapHandler::CMapBlitter::getHeroFrameGroup(ui8 dir, bool isMoving) const
+ui8 CMapBlitter::getHeroFrameGroup(ui8 dir, bool isMoving) const
 {
 	if(isMoving)
 	{
@@ -1402,26 +1403,26 @@ void CMapHandler::discardWorldViewCache()
 	cache.discardWorldViewCache();
 }
 
-CMapHandler::CMapCache::CMapCache()
+CMapCache::CMapCache()
 {
 	worldViewCachedScale = 0;
 }
 
-void CMapHandler::CMapCache::discardWorldViewCache()
+void CMapCache::discardWorldViewCache()
 {
 	for(auto & cache : data)
 		cache.clear();
 	logAnim->debug("Discarded world view cache");
 }
 
-void CMapHandler::CMapCache::updateWorldViewScale(float scale)
+void CMapCache::updateWorldViewScale(float scale)
 {
 	if (fabs(scale - worldViewCachedScale) > 0.001f)
 		discardWorldViewCache();
 	worldViewCachedScale = scale;
 }
 
-std::shared_ptr<IImage> CMapHandler::CMapCache::requestWorldViewCacheOrCreate(CMapHandler::EMapCacheType type, std::shared_ptr<IImage> fullSurface)
+std::shared_ptr<IImage> CMapCache::requestWorldViewCacheOrCreate(EMapCacheType type, std::shared_ptr<IImage> fullSurface)
 {
 	intptr_t key = (intptr_t) (fullSurface.get());
 	auto & cache = data[(ui8)type];

+ 164 - 156
client/adventureMap/mapHandler.h

@@ -37,6 +37,7 @@ struct SDL_Surface;
 class CAnimation;
 class IImage;
 class CFadeAnimation;
+class CMapHandler;
 
 enum class EWorldViewIcon
 {
@@ -161,163 +162,168 @@ private:
 	int offset;
 	std::vector<T> inver;
 };
-class CMapHandler
+
+enum class EMapCacheType : ui8
 {
-	enum class EMapCacheType : ui8
-	{
-		TERRAIN, OBJECTS, ROADS, RIVERS, FOW, HEROES, HERO_FLAGS, FRAME, AFTER_LAST
-	};
+	TERRAIN, OBJECTS, ROADS, RIVERS, FOW, HEROES, HERO_FLAGS, FRAME, AFTER_LAST
+};
 
-	/// temporarily caches rescaled frames for map world view redrawing
-	class CMapCache
-	{
-		std::array< std::map<intptr_t, std::shared_ptr<IImage>>, (ui8)EMapCacheType::AFTER_LAST> data;
-		float worldViewCachedScale;
-	public:
-		CMapCache();
-		/// destroys all cached data (frees surfaces)
-		void discardWorldViewCache();
-		/// updates scale and determines if currently cached data is still valid
-		void updateWorldViewScale(float scale);
-		/// asks for cached data; @returns cached data if found, new scaled surface otherwise, may return nullptr in case of scaling error
-		std::shared_ptr<IImage> requestWorldViewCacheOrCreate(EMapCacheType type, std::shared_ptr<IImage> fullSurface);
-	};
-
-	/// helper struct to pass around resolved bitmaps of an object; images can be nullptr if object doesn't have bitmap of that type
-	struct AnimBitmapHolder
-	{
-		std::shared_ptr<IImage> objBitmap; // main object bitmap
-		std::shared_ptr<IImage> flagBitmap; // flag bitmap for the object (probably only for heroes and boats with heroes)
-		bool isMoving; // indicates if the object is moving (again, heroes/boats only)
-
-		AnimBitmapHolder(std::shared_ptr<IImage> objBitmap_ = nullptr, std::shared_ptr<IImage> flagBitmap_ = nullptr, bool moving = false)
-			: objBitmap(objBitmap_),
-			  flagBitmap(flagBitmap_),
-			  isMoving(moving)
-		{}
-	};
-
-	class CMapBlitter
-	{
-	protected:
-		const int FRAMES_PER_MOVE_ANIM_GROUP = 8;
-		CMapHandler * parent; // ptr to enclosing map handler; generally for legacy reasons, probably could/should be refactored out of here
-		int tileSize; // size of a tile drawn on map [in pixels]
-		int halfTileSizeCeil; // half of the tile size, rounded up
-		int3 tileCount; // number of tiles in current viewport
-		int3 topTile; // top-left tile of the viewport
-		int3 initPos; // starting drawing position [in pixels]
-		int3 pos; // current position [in tiles]
-		int3 realPos; // current position [in pixels]
-		Rect realTileRect; // default rect based on current pos: [realPos.x, realPos.y, tileSize, tileSize]
-		Rect defaultTileRect; // default rect based on 0: [0, 0, tileSize, tileSize]
-		const MapDrawingInfo * info; // data for drawing passed from outside
-
-		/// general drawing method, called internally by more specialized ones
-		virtual void drawElement(EMapCacheType cacheType, std::shared_ptr<IImage> source, Rect * sourceRect, SDL_Surface * targetSurf, Rect * destRect) const = 0;
-
-		// first drawing pass
-
-		/// draws terrain bitmap (or custom bitmap if applicable) on current tile
-		virtual void drawTileTerrain(SDL_Surface * targetSurf, const TerrainTile & tinfo, const TerrainTile2 & tile) const;
-		/// draws a river segment on current tile
-		virtual void drawRiver(SDL_Surface * targetSurf, const TerrainTile & tinfo) const;
-		/// draws a road segment on current tile
-		virtual void drawRoad(SDL_Surface * targetSurf, const TerrainTile & tinfo, const TerrainTile * tinfoUpper) const;
-		/// draws all objects on current tile (higher-level logic, unlike other draw*** methods)
-		virtual void drawObjects(SDL_Surface * targetSurf, const TerrainTile2 & tile) const;
-		virtual void drawObject(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, Rect * sourceRect, bool moving) const;
-		virtual void drawHeroFlag(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, Rect * sourceRect, Rect * destRect, bool moving) const;
-
-		// second drawing pass
-
-		/// current tile: draws overlay over the map, used to draw world view icons
-		virtual void drawTileOverlay(SDL_Surface * targetSurf, const TerrainTile2 & tile) const = 0;
-		/// draws fog of war on current tile
-		virtual void drawFow(SDL_Surface * targetSurf) const;
-		/// draws map border frame on current position
-		virtual void drawFrame(SDL_Surface * targetSurf) const;
-		/// draws additional icons (for VIEW_AIR, VIEW_EARTH spells atm)
-		virtual void drawOverlayEx(SDL_Surface * targetSurf);
-
-		// third drawing pass
-
-		/// custom post-processing, if needed (used by puzzle view)
-		virtual void postProcessing(SDL_Surface * targetSurf) const {}
-
-		// misc methods
-
-		/// initializes frame-drawing (called at the start of every redraw)
-		virtual void init(const MapDrawingInfo * drawingInfo) = 0;
-		/// calculates clip region for map viewport
-		virtual Rect clip(SDL_Surface * targetSurf) const = 0;
-
-		virtual ui8 getHeroFrameGroup(ui8 dir, bool isMoving) const;
-		virtual ui8 getPhaseShift(const CGObjectInstance *object) const;
-
-		virtual bool canDrawObject(const CGObjectInstance * obj) const;
-		virtual bool canDrawCurrentTile() const;
-
-		// internal helper methods to choose correct bitmap(s) for object; called internally by findObjectBitmap
-		AnimBitmapHolder findHeroBitmap(const CGHeroInstance * hero, int anim) const;
-		AnimBitmapHolder findBoatBitmap(const CGBoat * hero, int anim) const;
-		std::shared_ptr<IImage> findFlagBitmap(const CGHeroInstance * obj, int anim, const PlayerColor * color, int group) const;
-		std::shared_ptr<IImage> findHeroFlagBitmap(const CGHeroInstance * obj, int anim, const PlayerColor * color, int group) const;
-		std::shared_ptr<IImage> findBoatFlagBitmap(const CGBoat * obj, int anim, const PlayerColor * color, int group, ui8 dir) const;
-		std::shared_ptr<IImage> findFlagBitmapInternal(std::shared_ptr<CAnimation> animation, int anim, int group, ui8 dir, bool moving) const;
-
-	public:
-		CMapBlitter(CMapHandler * p);
-		virtual ~CMapBlitter();
-		void blit(SDL_Surface * targetSurf, const MapDrawingInfo * info);
-		/// helper method that chooses correct bitmap(s) for given object
-		AnimBitmapHolder findObjectBitmap(const CGObjectInstance * obj, int anim) const;
-	};
-
-	class CMapNormalBlitter : public CMapBlitter
-	{
-	protected:
-		void drawElement(EMapCacheType cacheType, std::shared_ptr<IImage> source, Rect * sourceRect, SDL_Surface * targetSurf, Rect * destRect) const override;
-		void drawTileOverlay(SDL_Surface * targetSurf,const TerrainTile2 & tile) const override {}
-		void init(const MapDrawingInfo * info) override;
-		Rect clip(SDL_Surface * targetSurf) const override;
-	public:
-		CMapNormalBlitter(CMapHandler * parent);
-		virtual ~CMapNormalBlitter(){}
-	};
-
-	class CMapWorldViewBlitter : public CMapBlitter
-	{
-	private:
-		std::shared_ptr<IImage> objectToIcon(Obj id, si32 subId, PlayerColor owner) const;
-	protected:
-		void drawElement(EMapCacheType cacheType, std::shared_ptr<IImage> source, Rect * sourceRect, SDL_Surface * targetSurf, Rect * destRect) const override;
-		void drawTileOverlay(SDL_Surface * targetSurf, const TerrainTile2 & tile) const override;
-		void drawHeroFlag(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, Rect * sourceRect, Rect * destRect, bool moving) const override;
-		void drawObject(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, Rect * sourceRect, bool moving) const override;
-		void drawFrame(SDL_Surface * targetSurf) const override {}
-		void drawOverlayEx(SDL_Surface * targetSurf) override;
-		void init(const MapDrawingInfo * info) override;
-		Rect clip(SDL_Surface * targetSurf) const override;
-		ui8 getPhaseShift(const CGObjectInstance *object) const override { return 0u; }
-		void calculateWorldViewCameraPos();
-	public:
-		CMapWorldViewBlitter(CMapHandler * parent);
-		virtual ~CMapWorldViewBlitter(){}
-	};
-
-	class CMapPuzzleViewBlitter : public CMapNormalBlitter
-	{
-		std::vector<int> unblittableObjects;
+/// temporarily caches rescaled frames for map world view redrawing
+class CMapCache
+{
+	std::array< std::map<intptr_t, std::shared_ptr<IImage>>, (ui8)EMapCacheType::AFTER_LAST> data;
+	float worldViewCachedScale;
+public:
+	CMapCache();
+	/// destroys all cached data (frees surfaces)
+	void discardWorldViewCache();
+	/// updates scale and determines if currently cached data is still valid
+	void updateWorldViewScale(float scale);
+	/// asks for cached data; @returns cached data if found, new scaled surface otherwise, may return nullptr in case of scaling error
+	std::shared_ptr<IImage> requestWorldViewCacheOrCreate(EMapCacheType type, std::shared_ptr<IImage> fullSurface);
+};
+
+/// helper struct to pass around resolved bitmaps of an object; images can be nullptr if object doesn't have bitmap of that type
+struct AnimBitmapHolder
+{
+	std::shared_ptr<IImage> objBitmap; // main object bitmap
+	std::shared_ptr<IImage> flagBitmap; // flag bitmap for the object (probably only for heroes and boats with heroes)
+	bool isMoving; // indicates if the object is moving (again, heroes/boats only)
+
+	AnimBitmapHolder(std::shared_ptr<IImage> objBitmap_ = nullptr, std::shared_ptr<IImage> flagBitmap_ = nullptr, bool moving = false)
+		: objBitmap(objBitmap_),
+		  flagBitmap(flagBitmap_),
+		  isMoving(moving)
+	{}
+};
+
+class CMapBlitter
+{
+protected:
+	const int FRAMES_PER_MOVE_ANIM_GROUP = 8;
+	CMapHandler * parent; // ptr to enclosing map handler; generally for legacy reasons, probably could/should be refactored out of here
+	int tileSize; // size of a tile drawn on map [in pixels]
+	int halfTileSizeCeil; // half of the tile size, rounded up
+	int3 tileCount; // number of tiles in current viewport
+	int3 topTile; // top-left tile of the viewport
+	int3 initPos; // starting drawing position [in pixels]
+	int3 pos; // current position [in tiles]
+	int3 realPos; // current position [in pixels]
+	Rect realTileRect; // default rect based on current pos: [realPos.x, realPos.y, tileSize, tileSize]
+	Rect defaultTileRect; // default rect based on 0: [0, 0, tileSize, tileSize]
+	const MapDrawingInfo * info; // data for drawing passed from outside
+
+	/// general drawing method, called internally by more specialized ones
+	virtual void drawElement(EMapCacheType cacheType, std::shared_ptr<IImage> source, Rect * sourceRect, SDL_Surface * targetSurf, Rect * destRect) const = 0;
+
+	// first drawing pass
+
+	/// draws terrain bitmap (or custom bitmap if applicable) on current tile
+	virtual void drawTileTerrain(SDL_Surface * targetSurf, const TerrainTile & tinfo, const TerrainTile2 & tile) const;
+	/// draws a river segment on current tile
+	virtual void drawRiver(SDL_Surface * targetSurf, const TerrainTile & tinfo) const;
+	/// draws a road segment on current tile
+	virtual void drawRoad(SDL_Surface * targetSurf, const TerrainTile & tinfo, const TerrainTile * tinfoUpper) const;
+	/// draws all objects on current tile (higher-level logic, unlike other draw*** methods)
+	virtual void drawObjects(SDL_Surface * targetSurf, const TerrainTile2 & tile) const;
+	virtual void drawObject(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, Rect * sourceRect, bool moving) const;
+	virtual void drawHeroFlag(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, Rect * sourceRect, Rect * destRect, bool moving) const;
+
+	// second drawing pass
+
+	/// current tile: draws overlay over the map, used to draw world view icons
+	virtual void drawTileOverlay(SDL_Surface * targetSurf, const TerrainTile2 & tile) const = 0;
+	/// draws fog of war on current tile
+	virtual void drawFow(SDL_Surface * targetSurf) const;
+	/// draws map border frame on current position
+	virtual void drawFrame(SDL_Surface * targetSurf) const;
+	/// draws additional icons (for VIEW_AIR, VIEW_EARTH spells atm)
+	virtual void drawOverlayEx(SDL_Surface * targetSurf);
+
+	// third drawing pass
+
+	/// custom post-processing, if needed (used by puzzle view)
+	virtual void postProcessing(SDL_Surface * targetSurf) const {}
+
+	// misc methods
+
+	/// initializes frame-drawing (called at the start of every redraw)
+	virtual void init(const MapDrawingInfo * drawingInfo) = 0;
+	/// calculates clip region for map viewport
+	virtual Rect clip(SDL_Surface * targetSurf) const = 0;
+
+	virtual ui8 getHeroFrameGroup(ui8 dir, bool isMoving) const;
+	virtual ui8 getPhaseShift(const CGObjectInstance *object) const;
+
+	virtual bool canDrawObject(const CGObjectInstance * obj) const;
+	virtual bool canDrawCurrentTile() const;
+
+	// internal helper methods to choose correct bitmap(s) for object; called internally by findObjectBitmap
+	AnimBitmapHolder findHeroBitmap(const CGHeroInstance * hero, int anim) const;
+	AnimBitmapHolder findBoatBitmap(const CGBoat * hero, int anim) const;
+	std::shared_ptr<IImage> findFlagBitmap(const CGHeroInstance * obj, int anim, const PlayerColor * color, int group) const;
+	std::shared_ptr<IImage> findHeroFlagBitmap(const CGHeroInstance * obj, int anim, const PlayerColor * color, int group) const;
+	std::shared_ptr<IImage> findBoatFlagBitmap(const CGBoat * obj, int anim, const PlayerColor * color, int group, ui8 dir) const;
+	std::shared_ptr<IImage> findFlagBitmapInternal(std::shared_ptr<CAnimation> animation, int anim, int group, ui8 dir, bool moving) const;
+
+public:
+	CMapBlitter(CMapHandler * p);
+	virtual ~CMapBlitter();
+	void blit(SDL_Surface * targetSurf, const MapDrawingInfo * info);
+	/// helper method that chooses correct bitmap(s) for given object
+	AnimBitmapHolder findObjectBitmap(const CGObjectInstance * obj, int anim) const;
+};
+
+class CMapNormalBlitter : public CMapBlitter
+{
+protected:
+	void drawElement(EMapCacheType cacheType, std::shared_ptr<IImage> source, Rect * sourceRect, SDL_Surface * targetSurf, Rect * destRect) const override;
+	void drawTileOverlay(SDL_Surface * targetSurf,const TerrainTile2 & tile) const override {}
+	void init(const MapDrawingInfo * info) override;
+	Rect clip(SDL_Surface * targetSurf) const override;
+public:
+	CMapNormalBlitter(CMapHandler * parent);
+	virtual ~CMapNormalBlitter(){}
+};
+
+class CMapWorldViewBlitter : public CMapBlitter
+{
+private:
+	std::shared_ptr<IImage> objectToIcon(Obj id, si32 subId, PlayerColor owner) const;
+protected:
+	void drawElement(EMapCacheType cacheType, std::shared_ptr<IImage> source, Rect * sourceRect, SDL_Surface * targetSurf, Rect * destRect) const override;
+	void drawTileOverlay(SDL_Surface * targetSurf, const TerrainTile2 & tile) const override;
+	void drawHeroFlag(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, Rect * sourceRect, Rect * destRect, bool moving) const override;
+	void drawObject(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, Rect * sourceRect, bool moving) const override;
+	void drawFrame(SDL_Surface * targetSurf) const override {}
+	void drawOverlayEx(SDL_Surface * targetSurf) override;
+	void init(const MapDrawingInfo * info) override;
+	Rect clip(SDL_Surface * targetSurf) const override;
+	ui8 getPhaseShift(const CGObjectInstance *object) const override { return 0u; }
+	void calculateWorldViewCameraPos();
+public:
+	CMapWorldViewBlitter(CMapHandler * parent);
+	virtual ~CMapWorldViewBlitter(){}
+};
+
+class CMapPuzzleViewBlitter : public CMapNormalBlitter
+{
+	std::vector<int> unblittableObjects;
+
+	void drawObjects(SDL_Surface * targetSurf, const TerrainTile2 & tile) const override;
+	void drawFow(SDL_Surface * targetSurf) const override {} // skipping FoW in puzzle view
+	void postProcessing(SDL_Surface * targetSurf) const override;
+	bool canDrawObject(const CGObjectInstance * obj) const override;
+	bool canDrawCurrentTile() const override { return true; }
+public:
+	CMapPuzzleViewBlitter(CMapHandler * parent);
+};
 
-		void drawObjects(SDL_Surface * targetSurf, const TerrainTile2 & tile) const override;
-		void drawFow(SDL_Surface * targetSurf) const override {} // skipping FoW in puzzle view
-		void postProcessing(SDL_Surface * targetSurf) const override;
-		bool canDrawObject(const CGObjectInstance * obj) const override;
-		bool canDrawCurrentTile() const override { return true; }
-	public:
-		CMapPuzzleViewBlitter(CMapHandler * parent);
-	};
+class CMapHandler
+{
+	friend class CMapBlitter;
+	friend class CMapNormalBlitter;
+	friend class CMapWorldViewBlitter;
 
 	CMapCache cache;
 	CMapBlitter * normalBlitter;
@@ -335,7 +341,8 @@ class CMapHandler
 	void initBorderGraphics();
 	void initTerrainGraphics();
 	void prepareFOWDefs();
-public:
+
+public: //TODO: make private
 	boost::multi_array<TerrainTile2, 3> ttiles; //informations about map tiles [z][x][y]
 	int3 sizes; //map size (x = width, y = height, z = number of levels)
 	const CMap * map;
@@ -356,7 +363,7 @@ public:
 	int offsetY;
 
 	//terrain graphics
-
+private:
 	//FIXME: unique_ptr should be enough, but fails to compile in MSVS 2013
 	typedef std::map<std::string, std::array<std::shared_ptr<CAnimation>, 4>> TFlippedAnimations; //[type, rotation]
 	typedef std::map<std::string, std::vector<std::array<std::shared_ptr<IImage>, 4>>> TFlippedCache;//[type, view type, rotation]
@@ -383,6 +390,7 @@ public:
 
 	mutable std::map<const CGObjectInstance*, ui8> animationPhase;
 
+public:
 	CMapHandler();
 	~CMapHandler();
 

+ 2 - 2
client/battle/BattleInterface.cpp

@@ -132,10 +132,10 @@ BattleInterface::~BattleInterface()
 	CPlayerInterface::battleInt = nullptr;
 	givenCommand.cond.notify_all(); //that two lines should make any stacksController->getActiveStack() waiting thread to finish
 
-	if (adventureInt && adventureInt->selection)
+	if (adventureInt && adventureInt->curArmy())
 	{
 		//FIXME: this should be moved to adventureInt which should restore correct track based on selection/active player
-		const auto * terrain = LOCPLINT->cb->getTile(adventureInt->selection->visitablePos())->terType;
+		const auto * terrain = LOCPLINT->cb->getTile(adventureInt->curArmy()->visitablePos())->terType;
 		CCS->musich->playMusicFromSet("terrain", terrain->getJsonKey(), true, false);
 	}
 

+ 31 - 5
client/render/Canvas.cpp

@@ -23,14 +23,14 @@ Canvas::Canvas(SDL_Surface * surface):
 	surface->refcount++;
 }
 
-Canvas::Canvas(Canvas & other):
+Canvas::Canvas(const Canvas & other):
 	surface(other.surface),
 	renderOffset(other.renderOffset)
 {
 	surface->refcount++;
 }
 
-Canvas::Canvas(Canvas & other, const Rect & newClipRect):
+Canvas::Canvas(const Canvas & other, const Rect & newClipRect):
 	Canvas(other)
 {
 	clipRect.emplace();
@@ -43,9 +43,9 @@ Canvas::Canvas(Canvas & other, const Rect & newClipRect):
 }
 
 Canvas::Canvas(const Point & size):
-	renderOffset(0,0)
+	renderOffset(0,0),
+	surface(CSDL_Ext::newSurface(size.x, size.y))
 {
-	surface = CSDL_Ext::newSurface(size.x, size.y);
 }
 
 Canvas::~Canvas()
@@ -75,9 +75,35 @@ void Canvas::draw(Canvas & image, const Point & pos)
 	CSDL_Ext::blitAt(image.surface, renderOffset.x + pos.x, renderOffset.y + pos.y, surface);
 }
 
+void Canvas::draw(Canvas & image, const Point & pos, const Point & targetSize)
+{
+	SDL_Rect targetRect = CSDL_Ext::toSDL(Rect(pos, targetSize));
+	SDL_BlitScaled(image.surface, nullptr, surface, &targetRect );
+}
+
+void Canvas::drawPoint(const Point & dest, const ColorRGBA & color)
+{
+	CSDL_Ext::putPixelWithoutRefreshIfInSurf(surface, dest.x, dest.y, color.r, color.g, color.b, color.a);
+}
+
 void Canvas::drawLine(const Point & from, const Point & dest, const ColorRGBA & colorFrom, const ColorRGBA & colorDest)
 {
-	CSDL_Ext::drawLine(surface, renderOffset.x + from.x, renderOffset.y + from.y, renderOffset.x + dest.x, renderOffset.y + dest.y, CSDL_Ext::toSDL(colorFrom), CSDL_Ext::toSDL(colorDest));
+	CSDL_Ext::drawLine(surface, renderOffset + from, renderOffset + dest, CSDL_Ext::toSDL(colorFrom), CSDL_Ext::toSDL(colorDest));
+}
+
+void Canvas::drawLineDashed(const Point & from, const Point & dest, const ColorRGBA & color)
+{
+	CSDL_Ext::drawLineDashed(surface, renderOffset + from, renderOffset + dest, CSDL_Ext::toSDL(color));
+}
+
+void Canvas::drawBorderDashed(const Rect & target, const ColorRGBA & color)
+{
+	Rect realTarget = target + renderOffset;
+
+	CSDL_Ext::drawLineDashed(surface, realTarget.topLeft(),    realTarget.topRight(),    CSDL_Ext::toSDL(color));
+	CSDL_Ext::drawLineDashed(surface, realTarget.bottomLeft(), realTarget.bottomRight(), CSDL_Ext::toSDL(color));
+	CSDL_Ext::drawLineDashed(surface, realTarget.topLeft(),    realTarget.bottomLeft(),  CSDL_Ext::toSDL(color));
+	CSDL_Ext::drawLineDashed(surface, realTarget.topRight(),   realTarget.bottomRight(), CSDL_Ext::toSDL(color));
 }
 
 void Canvas::drawText(const Point & position, const EFonts & font, const SDL_Color & colorDest, ETextAlignment alignment, const std::string & text )

+ 16 - 4
client/render/Canvas.h

@@ -34,16 +34,16 @@ class Canvas
 public:
 
 	/// constructs canvas using existing surface. Caller maintains ownership on the surface
-	Canvas(SDL_Surface * surface);
+	explicit Canvas(SDL_Surface * surface);
 
 	/// copy contructor
-	Canvas(Canvas & other);
+	Canvas(const Canvas & other);
 
 	/// creates canvas that only covers specified subsection of a surface
-	Canvas(Canvas & other, const Rect & clipRect);
+	Canvas(const Canvas & other, const Rect & clipRect);
 
 	/// constructs canvas of specified size
-	Canvas(const Point & size);
+	explicit Canvas(const Point & size);
 
 	~Canvas();
 
@@ -56,9 +56,21 @@ public:
 	/// renders another canvas onto this canvas
 	void draw(Canvas & image, const Point & pos);
 
+	/// renders another canvas onto this canvas with scaling
+	void draw(Canvas & image, const Point & pos, const Point & targetSize);
+
+	/// renders single pixels with specified color
+	void drawPoint(const Point & dest, const ColorRGBA & color);
+
 	/// renders continuous, 1-pixel wide line with color gradient
 	void drawLine(const Point & from, const Point & dest, const ColorRGBA & colorFrom, const ColorRGBA & colorDest);
 
+	/// renders dashed, 1-pixel wide line with specified color
+	void drawLineDashed(const Point & from, const Point & dest, const ColorRGBA & color);
+
+	/// renders rectangular, dashed border in specified location
+	void drawBorderDashed(const Rect & target, const ColorRGBA & color);
+
 	/// renders single line of text with specified parameters
 	void drawText(const Point & position, const EFonts & font, const SDL_Color & colorDest, ETextAlignment alignment, const std::string & text );
 

+ 71 - 37
client/renderSDL/SDL_Extensions.cpp

@@ -364,11 +364,40 @@ uint32_t CSDL_Ext::colorTouint32_t(const SDL_Color * color)
 	return ret;
 }
 
+static void drawLineXDashed(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color)
+{
+	double length(x2 - x1);
+
+	for(int x = x1; x <= x2; x++)
+	{
+		double f = (x - x1) / length;
+		int y = vstd::lerp(y1, y2, f);
+
+		if (std::abs(x - x1) % 5 != 4)
+			CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur, x, y, color.r, color.g, color.b);
+	}
+}
+
+static void drawLineYDashed(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color)
+{
+	double length(y2 - y1);
+
+	for(int y = y1; y <= y2; y++)
+	{
+		double f = (y - y1) / length;
+		int x = vstd::lerp(x1, x2, f);
+
+		if (std::abs(y - y1) % 5 != 4)
+			CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur, x, y, color.r, color.g, color.b);
+	}
+}
+
 static void drawLineX(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color1, const SDL_Color & color2)
 {
+	double length(x2 - x1);
 	for(int x = x1; x <= x2; x++)
 	{
-		float f = float(x - x1) / float(x2 - x1);
+		double f = (x - x1) / length;
 		int y = vstd::lerp(y1, y2, f);
 
 		uint8_t r = vstd::lerp(color1.r, color2.r, f);
@@ -383,9 +412,10 @@ static void drawLineX(SDL_Surface * sur, int x1, int y1, int x2, int y2, const S
 
 static void drawLineY(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color1, const SDL_Color & color2)
 {
+	double length(y2 - y1);
 	for(int y = y1; y <= y2; y++)
 	{
-		float f = float(y - y1) / float(y2 - y1);
+		double f = (y - y1) / length;
 		int x = vstd::lerp(x1, x2, f);
 
 		uint8_t r = vstd::lerp(color1.r, color2.r, f);
@@ -398,31 +428,60 @@ static void drawLineY(SDL_Surface * sur, int x1, int y1, int x2, int y2, const S
 	}
 }
 
-void CSDL_Ext::drawLine(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color1, const SDL_Color & color2)
+void CSDL_Ext::drawLine(SDL_Surface * sur, const Point & from, const Point & dest, const SDL_Color & color1, const SDL_Color & color2)
 {
-	int width  = std::abs(x1-x2);
-	int height = std::abs(y1-y2);
+	//FIXME: duplicated code with drawLineDashed
+	int width  = std::abs(from.x - dest.x);
+	int height = std::abs(from.y - dest.y);
 
 	if ( width == 0 && height == 0)
 	{
-		uint8_t *p = CSDL_Ext::getPxPtr(sur, x1, y1);
+		uint8_t *p = CSDL_Ext::getPxPtr(sur, from.x, from.y);
 		ColorPutter<4, 0>::PutColorAlpha(p, color1);
 		return;
 	}
 
 	if (width > height)
 	{
-		if ( x1 < x2)
-			drawLineX(sur, x1,y1,x2,y2, color1, color2);
+		if ( from.x < dest.x)
+			drawLineX(sur, from.x, from.y, dest.x, dest.y, color1, color2);
 		else
-			drawLineX(sur, x2,y2,x1,y1, color2, color1);
+			drawLineX(sur, dest.x, dest.y, from.x, from.y, color2, color1);
 	}
 	else
 	{
-		if ( y1 < y2)
-			drawLineY(sur, x1,y1,x2,y2, color1, color2);
+		if ( from.y < dest.y)
+			drawLineY(sur, from.x, from.y, dest.x, dest.y, color1, color2);
 		else
-			drawLineY(sur, x2,y2,x1,y1, color2, color1);
+			drawLineY(sur, dest.x, dest.y, from.x, from.y, color2, color1);
+	}
+}
+
+void CSDL_Ext::drawLineDashed(SDL_Surface * sur, const Point & from, const Point & dest, const SDL_Color & color)
+{
+	//FIXME: duplicated code with drawLine
+	int width  = std::abs(from.x - dest.x);
+	int height = std::abs(from.y - dest.y);
+
+	if ( width == 0 && height == 0)
+	{
+		CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur, from.x, from.y, color.r, color.g, color.b);
+		return;
+	}
+
+	if (width > height)
+	{
+		if ( from.x < dest.x)
+			drawLineXDashed(sur, from.x, from.y, dest.x, dest.y, color);
+		else
+			drawLineXDashed(sur, dest.x, dest.y, from.x, from.y, color);
+	}
+	else
+	{
+		if ( from.y < dest.y)
+			drawLineYDashed(sur, from.x, from.y, dest.x, dest.y, color);
+		else
+			drawLineYDashed(sur, dest.x, dest.y, from.x, from.y, color);
 	}
 }
 
@@ -449,31 +508,6 @@ void CSDL_Ext::drawBorder( SDL_Surface * sur, const Rect &r, const SDL_Color &co
 	drawBorder(sur, r.x, r.y, r.w, r.h, color, depth);
 }
 
-void CSDL_Ext::drawDashedBorder(SDL_Surface * sur, const Rect &r, const SDL_Color &color)
-{
-	const int y1 = r.y, y2 = r.y + r.h-1;
-	for (int i=0; i<r.w; i++)
-	{
-		const int x = r.x + i;
-		if (i%4 || (i==0))
-		{
-			CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur, x, y1, color.r, color.g, color.b);
-			CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur, x, y2, color.r, color.g, color.b);
-		}
-	}
-
-	const int x1 = r.x, x2 = r.x + r.w-1;
-	for (int i=0; i<r.h; i++)
-	{
-		const int y = r.y + i;
-		if ((i%4) || (i==0))
-		{
-			CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur, x1, y, color.r, color.g, color.b);
-			CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur, x2, y, color.r, color.g, color.b);
-		}
-	}
-}
-
 void CSDL_Ext::setPlayerColor(SDL_Surface * sur, PlayerColor player)
 {
 	if(player==PlayerColor::UNFLAGGABLE)

+ 3 - 2
client/renderSDL/SDL_Extensions.h

@@ -81,10 +81,11 @@ typedef void (*TColorPutterAlpha)(uint8_t *&ptr, const uint8_t & R, const uint8_
 	uint32_t colorTouint32_t(const SDL_Color * color); //little endian only
 	SDL_Color makeColor(ui8 r, ui8 g, ui8 b, ui8 a);
 
-	void drawLine(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color1, const SDL_Color & color2);
+	void drawLine(SDL_Surface * sur, const Point & from, const Point & dest, const SDL_Color & color1, const SDL_Color & color2);
+	void drawLineDashed(SDL_Surface * sur, const Point & from, const Point & dest, const SDL_Color & color);
+
 	void drawBorder(SDL_Surface * sur, int x, int y, int w, int h, const SDL_Color &color, int depth = 1);
 	void drawBorder(SDL_Surface * sur, const Rect &r, const SDL_Color &color, int depth = 1);
-	void drawDashedBorder(SDL_Surface * sur, const Rect &r, const SDL_Color &color);
 	void setPlayerColor(SDL_Surface * sur, PlayerColor player); //sets correct color of flags; -1 for neutral
 
 	SDL_Surface * newSurface(int w, int h, SDL_Surface * mod); //creates new surface, with flags/format same as in surface given

+ 33 - 66
client/widgets/CArtifactHolder.cpp

@@ -149,19 +149,12 @@ void CHeroArtPlace::clickLeft(tribool down, bool previousState)
 				select();
 			}
 		}
-		else if(ourArt == ourOwner->commonInfo->src.art) //restore previously picked artifact
-		{
-			deselect();
-		}
-		else //perform artifact transition
+		// Perform artifact transition
+		else if(ourArt != ourOwner->commonInfo->src.art)
 		{
 			if(inBackpack) // Backpack destination.
 			{
-				if(srcInBackpack && slotID == ourOwner->commonInfo->src.slotID + 1) //next slot (our is not visible, so visually same as "old" place) to the art -> make nothing, return artifact to slot
-				{
-					deselect();
-				}
-				else
+				if(!srcInBackpack || slotID != ourOwner->commonInfo->src.slotID + 1)
 				{
 					const CArtifact * const cur = ourOwner->commonInfo->src.art->artType;
 
@@ -186,9 +179,7 @@ void CHeroArtPlace::clickLeft(tribool down, bool previousState)
 								|| ourOwner->commonInfo->src.slotID < ourOwner->commonInfo->dst.slotID) //rearranging arts in backpack after taking src artifact, the dest id will be shifted
 								vstd::advance(ourOwner->commonInfo->dst.slotID, -1);
 						}
-						if(srcInSameHero && ourOwner->commonInfo->dst.slotID == ourOwner->commonInfo->src.slotID) //we came to src == dst
-							deselect();
-						else
+						if(!srcInSameHero || ourOwner->commonInfo->dst.slotID != ourOwner->commonInfo->src.slotID)
 							ourOwner->realizeCurrentTransaction();
 					}
 				}
@@ -274,13 +265,13 @@ void CArtifactsOfHero::deactivate()
 /**
  * Selects artifact slot so that the containing artifact looks like it's picked up.
  */
-void CHeroArtPlace::select ()
+void CHeroArtPlace::select()
 {
-	if (locked)
+	if(locked)
 		return;
 
 	pickSlot(true);
-	if(ourArt->canBeDisassembled() && slotID < GameConstants::BACKPACK_START) //worn combined artifact -> locks have to disappear
+	if(ourArt->canBeDisassembled() && ArtifactUtils::isSlotEquipment(slotID)) //worn combined artifact -> locks have to disappear
 	{
 		for(auto slot : ArtifactUtils::constituentWornSlots())
 		{
@@ -292,41 +283,10 @@ void CHeroArtPlace::select ()
 
 	CCS->curh->dragAndDropCursor("artifact", ourArt->artType->getIconIndex());
 	ourOwner->commonInfo->src.setTo(this, false);
-	ourOwner->markPossibleSlots(ourArt);
-
-	if(slotID >= GameConstants::BACKPACK_START)
-		ourOwner->scrollBackpack(0); //will update slots
-
-	ourOwner->updateParentWindow();
-	ourOwner->safeRedraw();
-}
-
-/**
- * Deselects the artifact slot.
- */
-void CHeroArtPlace::deselect ()
-{
-	pickSlot(false);
-	if(ourArt && ourArt->canBeDisassembled()) //combined art returned to its slot -> restore locks
-	{
-		for(auto slot : ArtifactUtils::constituentWornSlots())
-		{
-			auto place = ourOwner->getArtPlace(slot);
-
-			if(nullptr != place)//getArtPlace may return null
-				place->pickSlot(false);
-		}
-	}
-
-	CCS->curh->dragAndDropCursor(nullptr);
-	ourOwner->unmarkSlots();
-	ourOwner->commonInfo->src.clear();
-	if(slotID >= GameConstants::BACKPACK_START)
-		ourOwner->scrollBackpack(0); //will update slots
+	ourOwner->commonInfo->src.slotID = ArtifactPosition::TRANSITION_POS;
 
-
-	ourOwner->updateParentWindow();
-	ourOwner->safeRedraw();
+	LOCPLINT->cb->swapArtifacts(ArtifactLocation(ourOwner->curHero, slotID),
+		ArtifactLocation(ourOwner->curHero, ArtifactPosition::TRANSITION_POS));
 }
 
 void CHeroArtPlace::showAll(SDL_Surface * to)
@@ -760,35 +720,42 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation & src, const Artifac
 		commonInfo->src.slotID = src.slot;
 	}
 	// Artifact was taken from us
-	else if(commonInfo->src == src)
+	else if(commonInfo->src == src && dst.slot != ArtifactPosition::TRANSITION_POS)
 	{
 		// Expected movement from slot ot slot
 		assert(commonInfo->dst == dst
 			// Artifact moved back to backpack (eg. to make place for art we are moving)
-			||  dst.slot == dst.getHolderArtSet()->artifactsInBackpack.size() + GameConstants::BACKPACK_START
+			|| dst.slot == dst.getHolderArtSet()->artifactsInBackpack.size() + GameConstants::BACKPACK_START
 			|| dst.getHolderArtSet()->bearerType() != ArtBearer::HERO);
 		commonInfo->reset();
 		unmarkSlots();
 	}
-	// The dest artifact was moved after the swap -> we are picking it
-	else if(commonInfo->dst == src)
+	else
 	{
-		assert(dst.slot == ArtifactPosition::TRANSITION_POS);
-		commonInfo->reset();
-
-		for(CArtifactsOfHero * aoh : commonInfo->participants)
+		// The dest artifact was moved after the swap -> we are picking it
+		if(commonInfo->dst == src)
 		{
-			if(dst.isHolder(aoh->curHero))
+			assert(dst.slot == ArtifactPosition::TRANSITION_POS);
+			commonInfo->reset();
+
+			for(CArtifactsOfHero * aoh : commonInfo->participants)
 			{
-				commonInfo->src.AOH = aoh;
-				break;
+				if(dst.isHolder(aoh->curHero))
+				{
+					commonInfo->src.AOH = aoh;
+					break;
+				}
 			}
-		}
 
-		commonInfo->src.art = dst.getArt();
-		commonInfo->src.slotID = dst.slot;
-		assert(commonInfo->src.AOH);
-		CCS->curh->dragAndDropCursor("artifact", dst.getArt()->artType->getIconIndex());
+			commonInfo->src.art = dst.getArt();
+			commonInfo->src.slotID = dst.slot;
+			assert(commonInfo->src.AOH);
+			CCS->curh->dragAndDropCursor("artifact", dst.getArt()->artType->getIconIndex());
+		}
+		if(!curHero->artifactsTransitionPos.empty())
+		{
+			markPossibleSlots(curHero->getArt(ArtifactPosition::TRANSITION_POS));
+		}
 	}
 
 	updateParentWindow();

+ 0 - 1
client/widgets/CArtifactHolder.h

@@ -90,7 +90,6 @@ public:
 	void clickLeft(tribool down, bool previousState) override;
 	void clickRight(tribool down, bool previousState) override;
 	void select();
-	void deselect();
 	void showAll(SDL_Surface * to) override;
 	bool fitsHere (const CArtifactInstance * art) const; //returns true if given artifact can be placed here
 

+ 3 - 3
client/widgets/CComponent.cpp

@@ -20,8 +20,8 @@
 #include "../gui/CursorHandler.h"
 #include "../gui/TextAlignment.h"
 #include "../renderSDL/SDL_Extensions.h"
-#include "../adventureMap/CAdvMapInt.h"
 #include "../windows/CMessage.h"
+#include "../windows/InfoWindows.h"
 #include "../widgets/TextControls.h"
 #include "../CGameInfo.h"
 
@@ -250,8 +250,8 @@ void CComponent::setSurface(std::string defName, int imgPos)
 
 void CComponent::clickRight(tribool down, bool previousState)
 {
-	if(!getDescription().empty())
-		adventureInt->handleRightClick(getDescription(), down);
+	if(down && !getDescription().empty())
+		CRClickPopup::createAndPush(getDescription());
 }
 
 void CSelectableComponent::clickLeft(tribool down, bool previousState)

+ 2 - 3
client/widgets/MiscWidgets.cpp

@@ -21,7 +21,6 @@
 #include "../windows/CCastleInterface.h"
 #include "../windows/InfoWindows.h"
 #include "../renderSDL/SDL_Extensions.h"
-#include "../adventureMap/CAdvMapInt.h"
 
 #include "../../CCallback.h"
 
@@ -57,8 +56,8 @@ void LRClickableAreaWText::clickLeft(tribool down, bool previousState)
 }
 void LRClickableAreaWText::clickRight(tribool down, bool previousState)
 {
-	if (!text.empty())
-		adventureInt->handleRightClick(text, down);
+	if (down && !text.empty())
+		CRClickPopup::createAndPush(text);
 }
 
 LRClickableAreaWText::LRClickableAreaWText()

+ 2 - 0
client/windows/CCastleInterface.cpp

@@ -28,6 +28,8 @@
 #include "../render/IImage.h"
 #include "../render/ColorFilter.h"
 #include "../adventureMap/CAdvMapInt.h"
+#include "../adventureMap/CList.h"
+#include "../adventureMap/CResDataBar.h"
 
 #include "../../CCallback.h"
 #include "../../lib/CArtHandler.h"

+ 3 - 2
client/windows/CKingdomInterface.cpp

@@ -15,12 +15,13 @@
 
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
+#include "../adventureMap/CResDataBar.h"
 #include "../gui/CGuiHandler.h"
 #include "../widgets/CComponent.h"
 #include "../widgets/TextControls.h"
 #include "../widgets/MiscWidgets.h"
 #include "../widgets/Buttons.h"
-#include "../adventureMap/CAdvMapInt.h"
+#include "../widgets/ObjectLists.h"
 #include "../renderSDL/SDL_Extensions.h"
 
 #include "../../CCallback.h"
@@ -95,7 +96,7 @@ void InfoBox::clickRight(tribool down, bool previousState)
 		if (comp)
 			CRClickPopup::createAndPush(text, CInfoWindow::TCompsInfo(1, comp));
 		else if (!text.empty())
-			adventureInt->handleRightClick(text, down);
+			CRClickPopup::createAndPush(text);
 	}
 }
 

+ 3 - 5
client/windows/CQuestLog.cpp

@@ -82,13 +82,11 @@ void CQuestMinimap::addQuestMarks (const QuestInfo * q)
 	else
 		tile = q->tile;
 
-	int x,y;
-	minimap->tileToPixels (tile, x, y);
+	Point offset = tileToPixels(tile);
 
-	if (level != tile.z)
-		setLevel(tile.z);
+	setLevel(tile.z);
 
-	auto pic = std::make_shared<CQuestIcon>("VwSymbol.def", 3, x, y);
+	auto pic = std::make_shared<CQuestIcon>("VwSymbol.def", 3, offset.x, offset.y);
 
 	pic->moveBy (Point ( -pic->pos.w/2, -pic->pos.h/2));
 	pic->callback = std::bind (&CQuestMinimap::iconClicked, this);

+ 2 - 1
client/windows/CSpellWindow.cpp

@@ -57,7 +57,8 @@ void CSpellWindow::InteractiveArea::clickLeft(tribool down, bool previousState)
 
 void CSpellWindow::InteractiveArea::clickRight(tribool down, bool previousState)
 {
-	adventureInt->handleRightClick(helpText, down);
+	if (down)
+		CRClickPopup::createAndPush(helpText);
 }
 
 void CSpellWindow::InteractiveArea::hover(bool on)

+ 2 - 2
client/windows/CTradeWindow.cpp

@@ -13,11 +13,11 @@
 #include "../gui/CGuiHandler.h"
 #include "../gui/CursorHandler.h"
 #include "../widgets/Images.h"
-#include "../adventureMap/CAdvMapInt.h"
 #include "../renderSDL/SDL_Extensions.h"
 #include "../gui/TextAlignment.h"
 #include "../widgets/Buttons.h"
 #include "../widgets/TextControls.h"
+#include "../windows/InfoWindows.h"
 
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
@@ -272,7 +272,7 @@ void CTradeWindow::CTradeableItem::clickRight(tribool down, bool previousState)
 		case ARTIFACT_PLACEHOLDER:
 			//TODO: it's would be better for market to contain actual CArtifactInstance and not just ids of certain artifact type so we can use getEffectiveDescription.
 			if(id >= 0)
-				adventureInt->handleRightClick(CGI->artifacts()->getByIndex(id)->getDescriptionTranslated(), down);
+				CRClickPopup::createAndPush(CGI->artifacts()->getByIndex(id)->getDescriptionTranslated());
 			break;
 		}
 	}

+ 3 - 1
client/windows/GUIClasses.cpp

@@ -40,6 +40,7 @@
 #include "../lobby/CSavingScreen.h"
 #include "../renderSDL/SDL_Extensions.h"
 #include "../render/CAnimation.h"
+#include "../CMT.h"
 
 #include "../../CCallback.h"
 
@@ -1860,9 +1861,10 @@ CObjectListWindow::CObjectListWindow(const std::vector<int> & _items, std::share
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	items.reserve(_items.size());
+
 	for(int id : _items)
 	{
-		items.push_back(std::make_pair(id, CGI->mh->map->objects[id]->getObjectName()));
+		items.push_back(std::make_pair(id, LOCPLINT->cb->getObjInstance(ObjectInstanceID(id))->getObjectName()));
 	}
 
 	init(titleWidget_, _title, _descr);

+ 8 - 5
client/windows/InfoWindows.cpp

@@ -22,6 +22,7 @@
 #include "../battle/BattleInterface.h"
 #include "../battle/BattleInterfaceClasses.h"
 #include "../adventureMap/CAdvMapInt.h"
+#include "../adventureMap/CTerrainRect.h"
 #include "../windows/CMessage.h"
 #include "../renderSDL/SDL_Extensions.h"
 #include "../gui/CursorHandler.h"
@@ -364,8 +365,10 @@ CRClickPopupInt::~CRClickPopupInt()
 
 Point CInfoBoxPopup::toScreen(Point p)
 {
-	vstd::abetween(p.x, adventureInt->terrain.pos.x + 100, adventureInt->terrain.pos.x + adventureInt->terrain.pos.w - 100);
-	vstd::abetween(p.y, adventureInt->terrain.pos.y + 100, adventureInt->terrain.pos.y + adventureInt->terrain.pos.h - 100);
+	auto bounds = adventureInt->terrainAreaPixels();
+
+	vstd::abetween(p.x, bounds.top() + 100, bounds.bottom() - 100);
+	vstd::abetween(p.y, bounds.left() + 100, bounds.right() - 100);
 
 	return p;
 }
@@ -374,7 +377,7 @@ CInfoBoxPopup::CInfoBoxPopup(Point position, const CGTownInstance * town)
 	: CWindowObject(RCLICK_POPUP | PLAYER_COLORED, "TOWNQVBK", toScreen(position))
 {
 	InfoAboutTown iah;
-	LOCPLINT->cb->getTownInfo(town, iah, adventureInt->selection); //todo: should this be nearest hero?
+	LOCPLINT->cb->getTownInfo(town, iah, adventureInt->curTown()); //todo: should this be nearest hero?
 
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	tooltip = std::make_shared<CTownTooltip>(Point(9, 10), iah);
@@ -384,7 +387,7 @@ CInfoBoxPopup::CInfoBoxPopup(Point position, const CGHeroInstance * hero)
 	: CWindowObject(RCLICK_POPUP | PLAYER_COLORED, "HEROQVBK", toScreen(position))
 {
 	InfoAboutHero iah;
-	LOCPLINT->cb->getHeroInfo(hero, iah, adventureInt->selection);//todo: should this be nearest hero?
+	LOCPLINT->cb->getHeroInfo(hero, iah, adventureInt->curHero());//todo: should this be nearest hero?
 
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	tooltip = std::make_shared<CHeroTooltip>(Point(9, 10), iah);
@@ -403,7 +406,7 @@ CInfoBoxPopup::CInfoBoxPopup(Point position, const CGGarrison * garr)
 std::shared_ptr<WindowBase> CRClickPopup::createInfoWin(Point position, const CGObjectInstance * specific) //specific=0 => draws info about selected town/hero
 {
 	if(nullptr == specific)
-		specific = adventureInt->selection;
+		specific = adventureInt->curArmy();
 
 	if(nullptr == specific)
 	{

+ 3 - 0
cmake_modules/VCMI_lib.cmake

@@ -125,6 +125,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/serializer/JsonSerializeFormat.cpp
 		${MAIN_LIB_DIR}/serializer/JsonSerializer.cpp
 		${MAIN_LIB_DIR}/serializer/JsonUpdater.cpp
+		${MAIN_LIB_DIR}/serializer/ILICReader.cpp
 
 		${MAIN_LIB_DIR}/spells/AbilityCaster.cpp
 		${MAIN_LIB_DIR}/spells/AdventureSpellMechanics.cpp
@@ -361,6 +362,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/serializer/JsonSerializeFormat.h
 		${MAIN_LIB_DIR}/serializer/JsonSerializer.h
 		${MAIN_LIB_DIR}/serializer/JsonUpdater.h
+		${MAIN_LIB_DIR}/serializer/ILICReader.h
 		${MAIN_LIB_DIR}/serializer/Cast.h
 
 		${MAIN_LIB_DIR}/spells/AbilityCaster.h
@@ -439,6 +441,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/NetPacksBase.h
 		${MAIN_LIB_DIR}/NetPacks.h
 		${MAIN_LIB_DIR}/NetPacksLobby.h
+		${MAIN_LIB_DIR}/NetPackVisitor.h
 		${MAIN_LIB_DIR}/ObstacleHandler.h
 		${MAIN_LIB_DIR}/PathfinderUtil.h
 		${MAIN_LIB_DIR}/Point.h

+ 33 - 28
lib/CArtHandler.cpp

@@ -1023,43 +1023,46 @@ bool CArtifactInstance::isPart(const CArtifactInstance *supposedPart) const
 	return supposedPart == this;
 }
 
-bool CCombinedArtifactInstance::canBePutAt(const CArtifactSet *artSet, ArtifactPosition slot, bool assumeDestRemoved) const
+bool CCombinedArtifactInstance::canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot, bool assumeDestRemoved) const
 {
 	if(slot == ArtifactPosition::TRANSITION_POS)
 		return true;
-	bool canMainArtifactBePlaced = CArtifactInstance::canBePutAt(artSet, slot, assumeDestRemoved);
-	if(!canMainArtifactBePlaced)
-		return false; //no is no...
+	if(!CArtifactInstance::canBePutAt(artSet, slot, assumeDestRemoved))
+		return false;
 	if(ArtifactUtils::isSlotBackpack(slot))
 		return true; //we can always remove combined art to the backapck
 
-
-	assert(artType->constituents);
-	std::vector<ConstituentInfo> constituentsToBePlaced = constituentsInfo; //we'll remove constituents from that list, as we find a suitable slot for them
-
-	//it may be that we picked a combined artifact in hero screen (though technically it's still there) to move it
-	//so we remove from the list all constituents that are already present on dst hero in the form of locks
-	for(const ConstituentInfo &constituent : constituentsInfo)
-	{
-		if(constituent.art == artSet->getArt(constituent.slot, false)) //no need to worry about locked constituent
-			constituentsToBePlaced -= constituent;
-	}
-
-	//we iterate over all active slots and check if constituents fits them
-	for(const auto pos : ArtifactUtils::constituentWornSlots())
+	CArtifactFittingSet fittingSet(artSet->bearerType());
+	fittingSet.artifactsWorn = artSet->artifactsWorn;
+	auto artToRemove = fittingSet.getArt(slot);
+	if(assumeDestRemoved && artToRemove)
 	{
-		for(auto art = constituentsToBePlaced.begin(); art != constituentsToBePlaced.end(); art++)
+		if(artToRemove->canBeDisassembled())
 		{
-			// pos == slot because we can remove already worn artifact only from that slot. That is our main destination
-			if(art->art->canBePutAt(artSet, pos, pos == slot))
+			auto combinedArtToRemove = dynamic_cast<CCombinedArtifactInstance*>(artToRemove);
+			for(auto & part : combinedArtToRemove->constituentsInfo)
 			{
-				constituentsToBePlaced.erase(art);
-				break;
+				if(ArtifactUtils::isSlotEquipment(part.slot))
+				{
+					fittingSet.eraseArtSlot(part.slot);
+				}
 			}
 		}
+		fittingSet.eraseArtSlot(slot);
 	}
-
-	return constituentsToBePlaced.empty();
+	for(auto & art : constituentsInfo)
+	{
+		auto possibleSlot = art.art->firstAvailableSlot(&fittingSet);
+		if(ArtifactUtils::isSlotEquipment(possibleSlot))
+		{
+			fittingSet.setNewArtSlot(possibleSlot, nullptr, true);
+		}
+		else
+		{
+			return false;
+		}
+	}
+	return true;
 }
 
 bool CCombinedArtifactInstance::canBeDisassembled() const
@@ -1365,8 +1368,10 @@ const ArtSlotInfo * CArtifactSet::getSlot(ArtifactPosition pos) const
 	if(pos == ArtifactPosition::TRANSITION_POS)
 	{
 		// Always add to the end. Always take from the beginning.
-		assert(!artifactsTransitionPos.empty());
-		return &(*artifactsTransitionPos.begin());
+		if(artifactsTransitionPos.empty())
+			return nullptr;
+		else
+			return &(*artifactsTransitionPos.begin());
 	}
 	if(vstd::contains(artifactsWorn, pos))
 		return &artifactsWorn.at(pos);
@@ -1563,7 +1568,7 @@ void CArtifactFittingSet::setNewArtSlot(ArtifactPosition slot, CArtifactInstance
 
 void CArtifactFittingSet::putArtifact(ArtifactPosition pos, CArtifactInstance * art)
 {
-	if(art && art->canBeDisassembled() && (pos < ArtifactPosition::AFTER_LAST))
+	if(art && art->canBeDisassembled() && ArtifactUtils::isSlotEquipment(pos))
 	{
 		for(auto & part : dynamic_cast<CCombinedArtifactInstance*>(art)->constituentsInfo)
 		{

+ 1 - 1
lib/CGameState.cpp

@@ -274,7 +274,7 @@ DLL_LINKAGE std::string MetaString::buildList () const
 	return lista;
 }
 
-void MetaString::addCreReplacement(CreatureID id, TQuantity count) //adds sing or plural name;
+void MetaString::addCreReplacement(const CreatureID & id, TQuantity count) //adds sing or plural name;
 {
 	if (!count)
 		addReplacement (CRE_PL_NAMES, id); //no creatures - just empty name (eg. defeat Angels)

+ 3 - 3
lib/Color.h

@@ -27,7 +27,7 @@ public:
 	uint8_t a;
 
 	//constructors
-	ColorRGBA()
+	constexpr ColorRGBA()
 		:r(0)
 		,g(0)
 		,b(0)
@@ -35,14 +35,14 @@ public:
 	{
 	}
 
-	ColorRGBA(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+	constexpr ColorRGBA(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 		: r(r)
 		, g(g)
 		, b(b)
 		, a(a)
 	{}
 
-	ColorRGBA(uint8_t r, uint8_t g, uint8_t b)
+	constexpr ColorRGBA(uint8_t r, uint8_t g, uint8_t b)
 		: r(r)
 		, g(g)
 		, b(b)

+ 1 - 4
lib/ConstTransitivePtr.h

@@ -16,7 +16,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 template <typename T>
 class ConstTransitivePtr
 {
-	T *ptr;
+	T *ptr = nullptr;
 	ConstTransitivePtr(const T *Ptr)
 		: ptr(const_cast<T*>(Ptr)) 
 	{}
@@ -25,10 +25,7 @@ public:
 		: ptr(Ptr) 
 	{}
 	ConstTransitivePtr(std::nullptr_t)
-		: ptr(nullptr) 
 	{}
-
-
 	const T& operator*() const
 	{
 		return *ptr;

+ 1 - 0
lib/IGameEventsReceiver.h

@@ -127,6 +127,7 @@ public:
 	virtual void requestRealized(PackageApplied *pa){};
 	virtual void objectPropertyChanged(const SetObjectProperty * sop){}; //eg. mine has been flagged
 	virtual void objectRemoved(const CGObjectInstance *obj){}; //eg. collected resource, picked artifact, beaten hero
+	virtual void objectRemovedAfter(){}; //eg. collected resource, picked artifact, beaten hero
 	virtual void playerBlocked(int reason, bool start){}; //reason: 0 - upcoming battle
 	virtual void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) {}; //player lost or won the game
 	virtual void playerStartsTurn(PlayerColor player){};

+ 165 - 0
lib/NetPackVisitor.h

@@ -0,0 +1,165 @@
+/*
+ * CServerHandler.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "NetPacks.h"
+#include "NetPacksLobby.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class ICPackVisitor
+{
+public:
+	virtual bool callTyped() { return true; }
+
+	virtual void visitForLobby(CPackForLobby & pack) {}
+	virtual void visitForServer(CPackForServer & pack) {}
+	virtual void visitForClient(CPackForClient & pack) {}
+	virtual void visitPackageApplied(PackageApplied & pack) {}
+	virtual void visitSystemMessage(SystemMessage & pack) {}
+	virtual void visitPlayerBlocked(PlayerBlocked & pack) {}
+	virtual void visitPlayerCheated(PlayerCheated & pack) {}
+	virtual void visitYourTurn(YourTurn & pack) {}
+	virtual void visitEntitiesChanged(EntitiesChanged & pack) {}
+	virtual void visitSetResources(SetResources & pack) {}
+	virtual void visitSetPrimSkill(SetPrimSkill & pack) {}
+	virtual void visitSetSecSkill(SetSecSkill & pack) {}
+	virtual void visitHeroVisitCastle(HeroVisitCastle & pack) {}
+	virtual void visitChangeSpells(ChangeSpells & pack) {}
+	virtual void visitSetMana(SetMana & pack) {}
+	virtual void visitSetMovePoints(SetMovePoints & pack) {}
+	virtual void visitFoWChange(FoWChange & pack) {}
+	virtual void visitSetAvailableHeroes(SetAvailableHeroes & pack) {}
+	virtual void visitGiveBonus(GiveBonus & pack) {}
+	virtual void visitChangeObjPos(ChangeObjPos & pack) {}
+	virtual void visitPlayerEndsGame(PlayerEndsGame & pack) {}
+	virtual void visitPlayerReinitInterface(PlayerReinitInterface & pack) {}
+	virtual void visitRemoveBonus(RemoveBonus & pack) {}
+	virtual void visitSetCommanderProperty(SetCommanderProperty & pack) {}
+	virtual void visitAddQuest(AddQuest & pack) {}
+	virtual void visitUpdateArtHandlerLists(UpdateArtHandlerLists & pack) {}
+	virtual void visitUpdateMapEvents(UpdateMapEvents & pack) {}
+	virtual void visitUpdateCastleEvents(UpdateCastleEvents & pack) {}
+	virtual void visitChangeFormation(ChangeFormation & pack) {}
+	virtual void visitRemoveObject(RemoveObject & pack) {}
+	virtual void visitTryMoveHero(TryMoveHero & pack) {}
+	virtual void visitNewStructures(NewStructures & pack) {}
+	virtual void visitRazeStructures(RazeStructures & pack) {}
+	virtual void visitSetAvailableCreatures(SetAvailableCreatures & pack) {}
+	virtual void visitSetHeroesInTown(SetHeroesInTown & pack) {}
+	virtual void visitHeroRecruited(HeroRecruited & pack) {}
+	virtual void visitGiveHero(GiveHero & pack) {}
+	virtual void visitCatapultAttack(CatapultAttack & pack) {}
+	virtual void visitOpenWindow(OpenWindow & pack) {}
+	virtual void visitNewObject(NewObject & pack) {}
+	virtual void visitSetAvailableArtifacts(SetAvailableArtifacts & pack) {}
+	virtual void visitNewArtifact(NewArtifact & pack) {}
+	virtual void visitChangeStackCount(ChangeStackCount & pack) {}
+	virtual void visitSetStackType(SetStackType & pack) {}
+	virtual void visitEraseStack(EraseStack & pack) {}
+	virtual void visitSwapStacks(SwapStacks & pack) {}
+	virtual void visitInsertNewStack(InsertNewStack & pack) {}
+	virtual void visitRebalanceStacks(RebalanceStacks & pack) {}
+	virtual void visitBulkRebalanceStacks(BulkRebalanceStacks & pack) {}
+	virtual void visitBulkSmartRebalanceStacks(BulkSmartRebalanceStacks & pack) {}
+	virtual void visitPutArtifact(PutArtifact & pack) {}
+	virtual void visitEraseArtifact(EraseArtifact & pack) {}
+	virtual void visitMoveArtifact(MoveArtifact & pack) {}
+	virtual void visitBulkMoveArtifacts(BulkMoveArtifacts & pack) {}
+	virtual void visitAssembledArtifact(AssembledArtifact & pack) {}
+	virtual void visitDisassembledArtifact(DisassembledArtifact & pack) {}
+	virtual void visitHeroVisit(HeroVisit & pack) {}
+	virtual void visitNewTurn(NewTurn & pack) {}
+	virtual void visitInfoWindow(InfoWindow & pack) {}
+	virtual void visitSetObjectProperty(SetObjectProperty & pack) {}
+	virtual void visitChangeObjectVisitors(ChangeObjectVisitors & pack) {}
+	virtual void visitPrepareHeroLevelUp(PrepareHeroLevelUp & pack) {}
+	virtual void visitHeroLevelUp(HeroLevelUp & pack) {}
+	virtual void visitCommanderLevelUp(CommanderLevelUp & pack) {}
+	virtual void visitBlockingDialog(BlockingDialog & pack) {}
+	virtual void visitGarrisonDialog(GarrisonDialog & pack) {}
+	virtual void visitExchangeDialog(ExchangeDialog & pack) {}
+	virtual void visitTeleportDialog(TeleportDialog & pack) {}
+	virtual void visitMapObjectSelectDialog(MapObjectSelectDialog & pack) {}
+	virtual void visitBattleStart(BattleStart & pack) {}
+	virtual void visitBattleNextRound(BattleNextRound & pack) {}
+	virtual void visitBattleSetActiveStack(BattleSetActiveStack & pack) {}
+	virtual void visitBattleResult(BattleResult & pack) {}
+	virtual void visitBattleLogMessage(BattleLogMessage & pack) {}
+	virtual void visitBattleStackMoved(BattleStackMoved & pack) {}
+	virtual void visitBattleUnitsChanged(BattleUnitsChanged & pack) {}
+	virtual void visitBattleAttack(BattleAttack & pack) {}
+	virtual void visitStartAction(StartAction & pack) {}
+	virtual void visitEndAction(EndAction & pack) {}
+	virtual void visitBattleSpellCast(BattleSpellCast & pack) {}
+	virtual void visitSetStackEffect(SetStackEffect & pack) {}
+	virtual void visitStacksInjured(StacksInjured & pack) {}
+	virtual void visitBattleResultsApplied(BattleResultsApplied & pack) {}
+	virtual void visitBattleObstaclesChanged(BattleObstaclesChanged & pack) {}
+	virtual void visitBattleSetStackProperty(BattleSetStackProperty & pack) {}
+	virtual void visitBattleTriggerEffect(BattleTriggerEffect & pack) {}
+	virtual void visitBattleUpdateGateState(BattleUpdateGateState & pack) {}
+	virtual void visitShowInInfobox(ShowInInfobox & pack) {}
+	virtual void visitAdvmapSpellCast(AdvmapSpellCast & pack) {}
+	virtual void visitShowWorldViewEx(ShowWorldViewEx & pack) {}
+	virtual void visitEndTurn(EndTurn & pack) {}
+	virtual void visitDismissHero(DismissHero & pack) {}
+	virtual void visitMoveHero(MoveHero & pack) {}
+	virtual void visitCastleTeleportHero(CastleTeleportHero & pack) {}
+	virtual void visitArrangeStacks(ArrangeStacks & pack) {}
+	virtual void visitBulkMoveArmy(BulkMoveArmy & pack) {}
+	virtual void visitBulkSplitStack(BulkSplitStack & pack) {}
+	virtual void visitBulkMergeStacks(BulkMergeStacks & pack) {}
+	virtual void visitBulkSmartSplitStack(BulkSmartSplitStack & pack) {}
+	virtual void visitDisbandCreature(DisbandCreature & pack) {}
+	virtual void visitBuildStructure(BuildStructure & pack) {}
+	virtual void visitRazeStructure(RazeStructure & pack) {}
+	virtual void visitRecruitCreatures(RecruitCreatures & pack) {}
+	virtual void visitUpgradeCreature(UpgradeCreature & pack) {}
+	virtual void visitGarrisonHeroSwap(GarrisonHeroSwap & pack) {}
+	virtual void visitExchangeArtifacts(ExchangeArtifacts & pack) {}
+	virtual void visitBulkExchangeArtifacts(BulkExchangeArtifacts & pack) {}
+	virtual void visitAssembleArtifacts(AssembleArtifacts & pack) {}
+	virtual void visitBuyArtifact(BuyArtifact & pack) {}
+	virtual void visitTradeOnMarketplace(TradeOnMarketplace & pack) {}
+	virtual void visitSetFormation(SetFormation & pack) {}
+	virtual void visitHireHero(HireHero & pack) {}
+	virtual void visitBuildBoat(BuildBoat & pack) {}
+	virtual void visitQueryReply(QueryReply & pack) {}
+	virtual void visitMakeAction(MakeAction & pack) {}
+	virtual void visitMakeCustomAction(MakeCustomAction & pack) {}
+	virtual void visitDigWithHero(DigWithHero & pack) {}
+	virtual void visitCastAdvSpell(CastAdvSpell & pack) {}
+	virtual void visitSaveGame(SaveGame & pack) {}
+	virtual void visitSaveGameClient(SaveGameClient & pack) {}
+	virtual void visitPlayerMessage(PlayerMessage & pack) {}
+	virtual void visitPlayerMessageClient(PlayerMessageClient & pack) {}
+	virtual void visitCenterView(CenterView & pack) {}
+	virtual void visitLobbyClientConnected(LobbyClientConnected & pack) {}
+	virtual void visitLobbyClientDisconnected(LobbyClientDisconnected & pack) {}
+	virtual void visitLobbyChatMessage(LobbyChatMessage & pack) {}
+	virtual void visitLobbyGuiAction(LobbyGuiAction & pack) {}
+	virtual void visitLobbyEndGame(LobbyEndGame & pack) {}
+	virtual void visitLobbyStartGame(LobbyStartGame & pack) {}
+	virtual void visitLobbyChangeHost(LobbyChangeHost & pack) {}
+	virtual void visitLobbyUpdateState(LobbyUpdateState & pack) {}
+	virtual void visitLobbySetMap(LobbySetMap & pack) {}
+	virtual void visitLobbySetCampaign(LobbySetCampaign & pack) {}
+	virtual void visitLobbySetCampaignMap(LobbySetCampaignMap & pack) {}
+	virtual void visitLobbySetCampaignBonus(LobbySetCampaignBonus & pack) {}
+	virtual void visitLobbyChangePlayerOption(LobbyChangePlayerOption & pack) {}
+	virtual void visitLobbySetPlayer(LobbySetPlayer & pack) {}
+	virtual void visitLobbySetTurnTime(LobbySetTurnTime & pack) {}
+	virtual void visitLobbySetDifficulty(LobbySetDifficulty & pack) {}
+	virtual void visitLobbyForceSetPlayer(LobbyForceSetPlayer & pack) {}
+	virtual void visitLobbyShowMessage(LobbyShowMessage & pack) {}
+};
+
+VCMI_LIB_NAMESPACE_END

文件差异内容过多而无法显示
+ 280 - 284
lib/NetPacks.h


+ 63 - 88
lib/NetPacksBase.h

@@ -17,6 +17,9 @@
 
 class CClient;
 class CGameHandler;
+class CLobbyScreen;
+class CServerHandler;
+class CVCMIServer;
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -30,12 +33,14 @@ class CArtifactSet;
 class CBonusSystemNode;
 struct ArtSlotInfo;
 
+class ICPackVisitor;
+
 struct DLL_LINKAGE CPack
 {
 	std::shared_ptr<CConnection> c; // Pointer to connection that pack received from
 
-	CPack() : c(nullptr) {};
-	virtual ~CPack() {};
+	CPack() = default;
+	virtual ~CPack() = default;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -45,34 +50,31 @@ struct DLL_LINKAGE CPack
 
 	void applyGs(CGameState * gs)
 	{}
+
+	void visit(ICPackVisitor & cpackVisitor);
+
+protected:
+	/// <summary>
+	/// For basic types of netpacks hierarchy like CPackForClient. Called first.
+	/// </summary>
+	virtual void visitBasic(ICPackVisitor & cpackVisitor);
+
+	/// <summary>
+	/// For leaf types of netpacks hierarchy. Called after visitBasic.
+	/// </summary>
+	virtual void visitTyped(ICPackVisitor & cpackVisitor);
 };
 
-struct CPackForClient : public CPack
+struct DLL_LINKAGE CPackForClient : public CPack
 {
-	CPackForClient(){};
-
-	CGameState* GS(CClient *cl);
-	void applyFirstCl(CClient *cl)//called before applying to gs
-	{}
-	void applyCl(CClient *cl)//called after applying to gs
-	{}
+protected:
+	virtual void visitBasic(ICPackVisitor & cpackVisitor) override;
 };
 
-struct CPackForServer : public CPack
+struct DLL_LINKAGE CPackForServer : public CPack
 {
-	mutable PlayerColor player;
+	mutable PlayerColor player = PlayerColor::NEUTRAL;
 	mutable si32 requestID;
-	CGameState* GS(CGameHandler *gh);
-	CPackForServer():
-		player(PlayerColor::NEUTRAL)
-	{
-	}
-
-	bool applyGh(CGameHandler *gh) //called after applying to gs
-	{
-		logGlobal->error("Should not happen... applying plain CPackForServer");
-		return false;
-	}
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -81,14 +83,15 @@ struct CPackForServer : public CPack
 	}
 
 protected:
-	void throwNotAllowedAction();
-	void throwOnWrongOwner(CGameHandler * gh, ObjectInstanceID id);
-	void throwOnWrongPlayer(CGameHandler * gh, PlayerColor player);
-	void throwAndComplain(CGameHandler * gh, std::string txt);
-	bool isPlayerOwns(CGameHandler * gh, ObjectInstanceID id);
+	virtual void visitBasic(ICPackVisitor & cpackVisitor) override;
+};
 
-private:
-	void wrongPlayerMessage(CGameHandler * gh, PlayerColor expectedplayer);
+struct DLL_LINKAGE CPackForLobby : public CPack
+{
+	virtual bool isForServer() const;
+
+protected:
+	virtual void visitBasic(ICPackVisitor & cpackVisitor) override;
 };
 
 struct DLL_LINKAGE MetaString
@@ -115,7 +118,7 @@ public:
 	void addTxt(ui8 type, ui32 serial)
 	{
 		message.push_back(TLOCAL_STRING);
-		localStrings.push_back(std::pair<ui8,ui32>(type, serial));
+		localStrings.emplace_back(type, serial);
 	}
 	MetaString& operator<<(const std::pair<ui8,ui32> &txt)
 	{
@@ -138,7 +141,7 @@ public:
 	void addReplacement(ui8 type, ui32 serial)
 	{
 		message.push_back(TREPLACE_LSTRING);
-		localStrings.push_back(std::pair<ui8,ui32>(type, serial));
+		localStrings.emplace_back(type, serial);
 	}
 	void addReplacement(const std::string &txt)
 	{
@@ -155,7 +158,7 @@ public:
 		message.push_back(TREPLACE_PLUSNUMBER);
 		numbers.push_back(txt);
 	}
-	void addCreReplacement(CreatureID id, TQuantity count); //adds sing or plural name;
+	void addCreReplacement(const CreatureID & id, TQuantity count); //adds sing or plural name;
 	void addReplacement(const CStackBasicDescriptor &stack); //adds sing or plural name;
 	std::string buildList () const;
 	void clear()
@@ -167,17 +170,16 @@ public:
 	}
 	void toString(std::string &dst) const;
 	std::string toString() const;
-	void getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst) const;
+	void getLocalString(const std::pair<ui8, ui32> & txt, std::string & dst) const;
 
-	MetaString(){}
 };
 
 struct Component
 {
 	enum EComponentType {PRIM_SKILL, SEC_SKILL, RESOURCE, CREATURE, ARTIFACT, EXPERIENCE, SPELL, MORALE, LUCK, BUILDING, HERO_PORTRAIT, FLAG};
-	ui16 id, subtype; //id uses ^^^ enums, when id==EXPPERIENCE subtype==0 means exp points and subtype==1 levels)
-	si32 val; // + give; - take
-	si16 when; // 0 - now; +x - within x days; -x - per x days
+	ui16 id = 0, subtype = 0; //id uses ^^^ enums, when id==EXPPERIENCE subtype==0 means exp points and subtype==1 levels)
+	si32 val = 0; // + give; - take
+	si16 when = 0; // 0 - now; +x - within x days; -x - per x days
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -186,10 +188,7 @@ struct Component
 		h & val;
 		h & when;
 	}
-	Component()
-		:id(0), subtype(0), val(0), when(0)
-	{
-	}
+	Component() = default;
 	DLL_LINKAGE explicit Component(const CStackBasicDescriptor &stack);
 	Component(Component::EComponentType Type, ui16 Subtype, si32 Val, si16 When)
 		:id(Type),subtype(Subtype),val(Val),when(When)
@@ -197,28 +196,27 @@ struct Component
 	}
 };
 
-typedef boost::variant<ConstTransitivePtr<CGHeroInstance>, ConstTransitivePtr<CStackInstance> > TArtHolder;
+using TArtHolder = boost::variant<ConstTransitivePtr<CGHeroInstance>, ConstTransitivePtr<CStackInstance>>;
 
 struct ArtifactLocation
 {
 	TArtHolder artHolder;//TODO: identify holder by id
-	ArtifactPosition slot;
+	ArtifactPosition slot = ArtifactPosition::PRE_FIRST;
 
 	ArtifactLocation()
+		: artHolder(ConstTransitivePtr<CGHeroInstance>())
 	{
-		artHolder = ConstTransitivePtr<CGHeroInstance>();
-		slot = ArtifactPosition::PRE_FIRST;
 	}
-	template <typename T>
-	ArtifactLocation(const T *ArtHolder, ArtifactPosition Slot)
+	template<typename T>
+	ArtifactLocation(const T * ArtHolder, ArtifactPosition Slot)
+		: artHolder(const_cast<T *>(ArtHolder)) //we are allowed here to const cast -> change will go through one of our packages... do not abuse!
+		, slot(Slot)
 	{
-		artHolder = const_cast<T*>(ArtHolder); //we are allowed here to const cast -> change will go through one of our packages... do not abuse!
-		slot = Slot;
 	}
-	ArtifactLocation(TArtHolder ArtHolder, ArtifactPosition Slot)
+	ArtifactLocation(TArtHolder ArtHolder, const ArtifactPosition & Slot)
+		: artHolder(std::move(std::move(ArtHolder)))
+		, slot(Slot)
 	{
-		artHolder = ArtHolder;
-		slot = Slot;
 	}
 
 	template <typename T>
@@ -253,15 +251,9 @@ struct ArtifactLocation
 class EntityChanges
 {
 public:
-	Metatype metatype;
-	int32_t entityIndex;
+	Metatype metatype = Metatype::UNKNOWN;
+	int32_t entityIndex = 0;
 	JsonNode data;
-	EntityChanges()
-		: metatype(Metatype::UNKNOWN),
-		entityIndex(0),
-		data()
-	{
-	}
 	template <typename Handler> void serialize(Handler & h, const int version)
 	{
 		h & metatype;
@@ -284,17 +276,11 @@ public:
 	};
 
 	JsonNode data;
-	EOperation operation;
-
-	BattleChanges()
-		: operation(EOperation::RESET_STATE),
-		data()
-	{
-	}
+	EOperation operation = EOperation::RESET_STATE;
 
+	BattleChanges() = default;
 	BattleChanges(EOperation operation_)
-		: operation(operation_),
-		data()
+		: operation(operation_)
 	{
 	}
 };
@@ -302,20 +288,13 @@ public:
 class UnitChanges : public BattleChanges
 {
 public:
-	uint32_t id;
-	int64_t healthDelta;
-
-	UnitChanges()
-		: BattleChanges(EOperation::RESET_STATE),
-		id(0),
-		healthDelta(0)
-	{
-	}
+	uint32_t id = 0;
+	int64_t healthDelta = 0;
 
+	UnitChanges() = default;
 	UnitChanges(uint32_t id_, EOperation operation_)
-		: BattleChanges(operation_),
-		id(id_),
-		healthDelta(0)
+		: BattleChanges(operation_)
+		, id(id_)
 	{
 	}
 
@@ -331,13 +310,9 @@ public:
 class ObstacleChanges : public BattleChanges
 {
 public:
-	uint32_t id;
+	uint32_t id = 0;
 
-	ObstacleChanges()
-		: BattleChanges(EOperation::RESET_STATE),
-		id(0)
-	{
-	}
+	ObstacleChanges() = default;
 
 	ObstacleChanges(uint32_t id_, EOperation operation_)
 		: BattleChanges(operation_),

文件差异内容过多而无法显示
+ 767 - 41
lib/NetPacksLib.cpp


+ 62 - 122
lib/NetPacksLobby.h

@@ -13,7 +13,6 @@
 
 #include "StartInfo.h"
 
-class CLobbyScreen;
 class CServerHandler;
 class CVCMIServer;
 
@@ -25,58 +24,26 @@ struct StartInfo;
 class CMapGenOptions;
 struct ClientPlayer;
 
-struct CPackForLobby : public CPack
+struct DLL_LINKAGE CLobbyPackToPropagate : public CPackForLobby
 {
-	bool checkClientPermissions(CVCMIServer * srv) const
-	{
-		return false;
-	}
-
-	bool applyOnServer(CVCMIServer * srv)
-	{
-		return true;
-	}
-
-	void applyOnServerAfterAnnounce(CVCMIServer * srv) {}
-
-	bool applyOnLobbyHandler(CServerHandler * handler)
-	{
-		return true;
-	}
-
-	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler) {}
 };
 
-struct CLobbyPackToPropagate : public CPackForLobby
+struct DLL_LINKAGE CLobbyPackToServer : public CPackForLobby
 {
-
+	virtual bool isForServer() const override;
 };
 
-struct CLobbyPackToServer : public CPackForLobby
-{
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	void applyOnServerAfterAnnounce(CVCMIServer * srv);
-};
-
-struct LobbyClientConnected : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyClientConnected : public CLobbyPackToPropagate
 {
 	// Set by client before sending pack to server
 	std::string uuid;
 	std::vector<std::string> names;
-	StartInfo::EMode mode;
+	StartInfo::EMode mode = StartInfo::INVALID;
 	// Changed by server before announcing pack
-	int clientId;
-	int hostClientId;
+	int clientId = -1;
+	int hostClientId = -1;
 
-	LobbyClientConnected()
-		: mode(StartInfo::INVALID), clientId(-1), hostClientId(-1)
-	{}
-
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	bool applyOnLobbyHandler(CServerHandler * handler);
-	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
-	bool applyOnServer(CVCMIServer * srv);
-	void applyOnServerAfterAnnounce(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler & h, const int version)
 	{
@@ -89,17 +56,13 @@ struct LobbyClientConnected : public CLobbyPackToPropagate
 	}
 };
 
-struct LobbyClientDisconnected : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyClientDisconnected : public CLobbyPackToPropagate
 {
 	int clientId;
-	bool shutdownServer;
+	bool shutdownServer = false;
+
 
-	LobbyClientDisconnected() : shutdownServer(false) {}
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	bool applyOnServer(CVCMIServer * srv);
-	void applyOnServerAfterAnnounce(CVCMIServer * srv);
-	bool applyOnLobbyHandler(CServerHandler * handler);
-	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -108,12 +71,11 @@ struct LobbyClientDisconnected : public CLobbyPackToPropagate
 	}
 };
 
-struct LobbyChatMessage : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyChatMessage : public CLobbyPackToPropagate
 {
 	std::string playerName, message;
 
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -122,15 +84,14 @@ struct LobbyChatMessage : public CLobbyPackToPropagate
 	}
 };
 
-struct LobbyGuiAction : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyGuiAction : public CLobbyPackToPropagate
 {
 	enum EAction : ui8 {
 		NONE, NO_TAB, OPEN_OPTIONS, OPEN_SCENARIO_LIST, OPEN_RANDOM_MAP_OPTIONS
-	} action;
+	} action = NONE;
 
-	LobbyGuiAction() : action(NONE) {}
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
+
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -138,14 +99,11 @@ struct LobbyGuiAction : public CLobbyPackToPropagate
 	}
 };
 
-struct LobbyEndGame : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyEndGame : public CLobbyPackToPropagate
 {
 	bool closeConnection = false, restart = false;
 	
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	bool applyOnServer(CVCMIServer * srv);
-	void applyOnServerAfterAnnounce(CVCMIServer * srv);
-	bool applyOnLobbyHandler(CServerHandler * handler);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 	
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -154,19 +112,14 @@ struct LobbyEndGame : public CLobbyPackToPropagate
 	}
 };
 
-struct LobbyStartGame : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyStartGame : public CLobbyPackToPropagate
 {
 	// Set by server
-	std::shared_ptr<StartInfo> initializedStartInfo;
-	CGameState * initializedGameState;
-	int clientId; //-1 means to all clients
+	std::shared_ptr<StartInfo> initializedStartInfo = nullptr;
+	CGameState * initializedGameState = nullptr;
+	int clientId = -1; //-1 means to all clients
 
-	LobbyStartGame() : initializedStartInfo(nullptr), initializedGameState(nullptr), clientId(-1) {}
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	bool applyOnServer(CVCMIServer * srv);
-	void applyOnServerAfterAnnounce(CVCMIServer * srv);
-	bool applyOnLobbyHandler(CServerHandler * handler);
-	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -179,14 +132,11 @@ struct LobbyStartGame : public CLobbyPackToPropagate
 	}
 };
 
-struct LobbyChangeHost : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyChangeHost : public CLobbyPackToPropagate
 {
-	int newHostConnectionId;
+	int newHostConnectionId = -1;
 
-	LobbyChangeHost() : newHostConnectionId(-1) {}
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	bool applyOnServer(CVCMIServer * srv);
-	bool applyOnServerAfterAnnounce(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler & h, const int version)
 	{
@@ -194,14 +144,12 @@ struct LobbyChangeHost : public CLobbyPackToPropagate
 	}
 };
 
-struct LobbyUpdateState : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyUpdateState : public CLobbyPackToPropagate
 {
 	LobbyState state;
-	bool hostChanged; // Used on client-side only
+	bool hostChanged = false; // Used on client-side only
 
-	LobbyUpdateState() : hostChanged(false) {}
-	bool applyOnLobbyHandler(CServerHandler * handler);
-	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -209,13 +157,14 @@ struct LobbyUpdateState : public CLobbyPackToPropagate
 	}
 };
 
-struct LobbySetMap : public CLobbyPackToServer
+struct DLL_LINKAGE LobbySetMap : public CLobbyPackToServer
 {
 	std::shared_ptr<CMapInfo> mapInfo;
 	std::shared_ptr<CMapGenOptions> mapGenOpts;
 
 	LobbySetMap() : mapInfo(nullptr), mapGenOpts(nullptr) {}
-	bool applyOnServer(CVCMIServer * srv);
+
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -224,12 +173,11 @@ struct LobbySetMap : public CLobbyPackToServer
 	}
 };
 
-struct LobbySetCampaign : public CLobbyPackToServer
+struct DLL_LINKAGE LobbySetCampaign : public CLobbyPackToServer
 {
 	std::shared_ptr<CCampaignState> ourCampaign;
 
-	LobbySetCampaign() {}
-	bool applyOnServer(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -237,12 +185,11 @@ struct LobbySetCampaign : public CLobbyPackToServer
 	}
 };
 
-struct LobbySetCampaignMap : public CLobbyPackToServer
+struct DLL_LINKAGE LobbySetCampaignMap : public CLobbyPackToServer
 {
-	int mapId;
+	int mapId = -1;
 
-	LobbySetCampaignMap() : mapId(-1) {}
-	bool applyOnServer(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -250,12 +197,11 @@ struct LobbySetCampaignMap : public CLobbyPackToServer
 	}
 };
 
-struct LobbySetCampaignBonus : public CLobbyPackToServer
+struct DLL_LINKAGE LobbySetCampaignBonus : public CLobbyPackToServer
 {
-	int bonusId;
+	int bonusId = -1;
 
-	LobbySetCampaignBonus() : bonusId(-1) {}
-	bool applyOnServer(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -263,16 +209,14 @@ struct LobbySetCampaignBonus : public CLobbyPackToServer
 	}
 };
 
-struct LobbyChangePlayerOption : public CLobbyPackToServer
+struct DLL_LINKAGE LobbyChangePlayerOption : public CLobbyPackToServer
 {
 	enum EWhat : ui8 {UNKNOWN, TOWN, HERO, BONUS};
-	ui8 what;
-	si8 direction; //-1 or +1
-	PlayerColor color;
+	ui8 what = UNKNOWN;
+	si8 direction = 0; //-1 or +1
+	PlayerColor color = PlayerColor::CANNOT_DETERMINE;
 
-	LobbyChangePlayerOption() : what(UNKNOWN), direction(0), color(PlayerColor::CANNOT_DETERMINE) {}
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	bool applyOnServer(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -282,12 +226,11 @@ struct LobbyChangePlayerOption : public CLobbyPackToServer
 	}
 };
 
-struct LobbySetPlayer : public CLobbyPackToServer
+struct DLL_LINKAGE LobbySetPlayer : public CLobbyPackToServer
 {
-	PlayerColor clickedColor;
+	PlayerColor clickedColor = PlayerColor::CANNOT_DETERMINE;
 
-	LobbySetPlayer() : clickedColor(PlayerColor::CANNOT_DETERMINE){}
-	bool applyOnServer(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -295,12 +238,11 @@ struct LobbySetPlayer : public CLobbyPackToServer
 	}
 };
 
-struct LobbySetTurnTime : public CLobbyPackToServer
+struct DLL_LINKAGE LobbySetTurnTime : public CLobbyPackToServer
 {
-	ui8 turnTime;
+	ui8 turnTime = 0;
 
-	LobbySetTurnTime() : turnTime(0) {}
-	bool applyOnServer(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -308,12 +250,11 @@ struct LobbySetTurnTime : public CLobbyPackToServer
 	}
 };
 
-struct LobbySetDifficulty : public CLobbyPackToServer
+struct DLL_LINKAGE LobbySetDifficulty : public CLobbyPackToServer
 {
-	ui8 difficulty;
+	ui8 difficulty = 0;
 
-	LobbySetDifficulty() : difficulty(0) {}
-	bool applyOnServer(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -321,13 +262,12 @@ struct LobbySetDifficulty : public CLobbyPackToServer
 	}
 };
 
-struct LobbyForceSetPlayer : public CLobbyPackToServer
+struct DLL_LINKAGE LobbyForceSetPlayer : public CLobbyPackToServer
 {
-	ui8 targetConnectedPlayer;
-	PlayerColor targetPlayerColor;
+	ui8 targetConnectedPlayer = -1;
+	PlayerColor targetPlayerColor = PlayerColor::CANNOT_DETERMINE;
 
-	LobbyForceSetPlayer() : targetConnectedPlayer(-1), targetPlayerColor(PlayerColor::CANNOT_DETERMINE) {}
-	bool applyOnServer(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler & h, const int version)
 	{
@@ -336,11 +276,11 @@ struct LobbyForceSetPlayer : public CLobbyPackToServer
 	}
 };
 
-struct LobbyShowMessage : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyShowMessage : public CLobbyPackToPropagate
 {
 	std::string message;
 	
-	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 	
 	template <typename Handler> void serialize(Handler & h, const int version)
 	{

+ 29 - 36
lib/int3.h

@@ -18,48 +18,41 @@ public:
 	si32 x, y, z;
 
 	//c-tor: x, y, z initialized to 0
-	int3() : x(0), y(0), z(0) {} // I think that x, y, z should be left uninitialized.
+	constexpr int3() : x(0), y(0), z(0) {} // I think that x, y, z should be left uninitialized.
 	//c-tor: x, y, z initialized to i
-	explicit int3(const si32 i) : x(i), y(i), z(i) {}
+	explicit constexpr int3(const si32 i) : x(i), y(i), z(i) {}
 	//c-tor: x, y, z initialized to X, Y, Z
-	int3(const si32 X, const si32 Y, const si32 Z) : x(X), y(Y), z(Z) {}
-	int3(const int3 & c) : x(c.x), y(c.y), z(c.z) {} // Should be set to default (C++11)?
+	constexpr int3(const si32 X, const si32 Y, const si32 Z) : x(X), y(Y), z(Z) {}
+	constexpr int3(const int3 & c) = default;
 
-	int3 & operator=(const int3 & c) // Should be set to default (C++11)?
-	{
-		x = c.x;
-		y = c.y;
-		z = c.z;
-
-		return *this;
-	}
-	int3 operator-() const { return int3(-x, -y, -z); }
+	constexpr int3 & operator=(const int3 & c) = default;
+	constexpr int3 operator-() const { return int3(-x, -y, -z); }
 
-	int3 operator+(const int3 & i) const { return int3(x + i.x, y + i.y, z + i.z); }
-	int3 operator-(const int3 & i) const { return int3(x - i.x, y - i.y, z - i.z); }
+	constexpr int3 operator+(const int3 & i) const { return int3(x + i.x, y + i.y, z + i.z); }
+	constexpr int3 operator-(const int3 & i) const { return int3(x - i.x, y - i.y, z - i.z); }
 	//returns int3 with coordinates increased by given number
-	int3 operator+(const si32 i) const { return int3(x + i, y + i, z + i); }
+	constexpr int3 operator+(const si32 i) const { return int3(x + i, y + i, z + i); }
 	//returns int3 with coordinates decreased by given number
-	int3 operator-(const si32 i) const { return int3(x - i, y - i, z - i); }
+	constexpr int3 operator-(const si32 i) const { return int3(x - i, y - i, z - i); }
 
 	//returns int3 with coordinates multiplied by given number
-	int3 operator*(const double i) const { return int3((int)(x * i), (int)(y * i), (int)(z * i)); }
+	constexpr int3 operator*(const double i) const { return int3((int)(x * i), (int)(y * i), (int)(z * i)); }
 	//returns int3 with coordinates divided by given number
-	int3 operator/(const double i) const { return int3((int)(x / i), (int)(y / i), (int)(z / i)); }
+	constexpr int3 operator/(const double i) const { return int3((int)(x / i), (int)(y / i), (int)(z / i)); }
 
 	//returns int3 with coordinates multiplied by given number
-	int3 operator*(const si32 i) const { return int3(x * i, y * i, z * i); }
+	constexpr int3 operator*(const si32 i) const { return int3(x * i, y * i, z * i); }
 	//returns int3 with coordinates divided by given number
-	int3 operator/(const si32 i) const { return int3(x / i, y / i, z / i); }
+	constexpr int3 operator/(const si32 i) const { return int3(x / i, y / i, z / i); }
 
-	int3 & operator+=(const int3 & i)
+	constexpr int3 & operator+=(const int3 & i)
 	{
 		x += i.x;
 		y += i.y;
 		z += i.z;
 		return *this;
 	}
-	int3 & operator-=(const int3 & i)
+	constexpr int3 & operator-=(const int3 & i)
 	{
 		x -= i.x;
 		y -= i.y;
@@ -68,7 +61,7 @@ public:
 	}
 
 	//increases all coordinates by given number
-	int3 & operator+=(const si32 i)
+	constexpr int3 & operator+=(const si32 i)
 	{
 		x += i;
 		y += i;
@@ -76,7 +69,7 @@ public:
 		return *this;
 	}
 	//decreases all coordinates by given number
-	int3 & operator-=(const si32 i)
+	constexpr int3 & operator-=(const si32 i)
 	{
 		x -= i;
 		y -= i;
@@ -84,10 +77,10 @@ public:
 		return *this;
 	}
 
-	bool operator==(const int3 & i) const { return (x == i.x && y == i.y && z == i.z); }
-	bool operator!=(const int3 & i) const { return (x != i.x || y != i.y || z != i.z); }
+	constexpr bool operator==(const int3 & i) const { return (x == i.x && y == i.y && z == i.z); }
+	constexpr bool operator!=(const int3 & i) const { return (x != i.x || y != i.y || z != i.z); }
 
-	bool operator<(const int3 & i) const
+	constexpr bool operator<(const int3 & i) const
 	{
 		if (z < i.z)
 			return true;
@@ -130,7 +123,7 @@ public:
 	}
 
 	//returns squared distance on Oxy plane (z coord is not used)
-	ui32 dist2dSQ(const int3 & o) const
+	constexpr ui32 dist2dSQ(const int3 & o) const
 	{
 		const si32 dx = (x - o.x);
 		const si32 dy = (y - o.y);
@@ -142,17 +135,17 @@ public:
 		return std::sqrt((double)dist2dSQ(o));
 	}
 	//manhattan distance used for patrol radius (z coord is not used)
-	double mandist2d(const int3 & o) const
+	constexpr double mandist2d(const int3 & o) const
 	{
-		return abs(o.x - x) + abs(o.y - y);
+		return vstd::abs(o.x - x) + vstd::abs(o.y - y);
 	}
 	//chebyshev distance used for ambient sounds (z coord is not used)
-	double chebdist2d(const int3 & o) const
+	constexpr double chebdist2d(const int3 & o) const
 	{
-		return std::max(std::abs(o.x - x), std::abs(o.y - y));
+		return std::max(vstd::abs(o.x - x), vstd::abs(o.y - y));
 	}
 
-	bool areNeighbours(const int3 & o) const
+	constexpr bool areNeighbours(const int3 & o) const
 	{
 		return (dist2dSQ(o) < 4) && (z == o.z);
 	}
@@ -169,7 +162,7 @@ public:
 		return result;
 	}
 
-	bool valid() const //Should be named "isValid"?
+	constexpr bool valid() const //Should be named "isValid"?
 	{
 		return z >= 0; //minimal condition that needs to be fulfilled for tiles in the map
 	}
@@ -182,7 +175,7 @@ public:
 		h & z;
 	}
 
-	static std::array<int3, 8> getDirs()
+	constexpr static std::array<int3, 8> getDirs()
 	{
 		return { { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
 			int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) } };

+ 0 - 2
lib/rmg/ObjectManager.cpp

@@ -241,7 +241,6 @@ bool ObjectManager::createRequiredObjects()
 	for(const auto & object : requiredObjects)
 	{
 		auto * obj = object.first;
-		int3 pos;
 		rmg::Object rmgObject(*obj);
 		rmgObject.setTemplate(zone.getTerrainType());
 		bool guarded = addGuard(rmgObject, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY));
@@ -278,7 +277,6 @@ bool ObjectManager::createRequiredObjects()
 	for(const auto & object : closeObjects)
 	{
 		auto * obj = object.first;
-		int3 pos;
 		auto possibleArea = zone.areaPossible();
 		rmg::Object rmgObject(*obj);
 		rmgObject.setTemplate(zone.getTerrainType());

+ 0 - 1
lib/rmg/TreasurePlacer.cpp

@@ -736,7 +736,6 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
 			if(guarded)
 				guarded = manager.addGuard(rmgObject, value);
 			
-			int3 pos;
 			auto possibleArea = zone.areaPossible();
 			
 			auto path = rmg::Path::invalid();

+ 6 - 7
lib/serializer/BinaryDeserializer.cpp

@@ -24,13 +24,12 @@ CLoadFile::CLoadFile(const boost::filesystem::path & fname, int minimalVersion)
 	openNextFile(fname, minimalVersion);
 }
 
-CLoadFile::~CLoadFile()
-{
-}
+//must be instantiated in .cpp file for access to complete types of all member fields
+CLoadFile::~CLoadFile() = default;
 
 int CLoadFile::read(void * data, unsigned size)
 {
-	sfile->read((char*)data,size);
+	sfile->read(reinterpret_cast<char *>(data), size);
 	return size;
 }
 
@@ -51,7 +50,7 @@ void CLoadFile::openNextFile(const boost::filesystem::path & fname, int minimalV
 		//we can read
 		char buffer[4];
 		sfile->read(buffer, 4);
-		if(std::memcmp(buffer,"VCMI",4))
+		if(std::memcmp(buffer, "VCMI", 4) != 0)
 			THROW_FORMAT("Error: not a VCMI file(%s)!", fName);
 
 		serializer & serializer.fileVersion;
@@ -62,7 +61,7 @@ void CLoadFile::openNextFile(const boost::filesystem::path & fname, int minimalV
 		{
 			logGlobal->warn("Warning format version mismatch: found %d when current is %d! (file %s)\n", serializer.fileVersion, SERIALIZATION_VERSION , fName);
 
-			auto versionptr = (char*)&serializer.fileVersion;
+			auto * versionptr = reinterpret_cast<char *>(&serializer.fileVersion);
 			std::reverse(versionptr, versionptr + 4);
 			logGlobal->warn("Version number reversed is %x, checking...", serializer.fileVersion);
 
@@ -99,7 +98,7 @@ void CLoadFile::clear()
 void CLoadFile::checkMagicBytes(const std::string &text)
 {
 	std::string loaded = text;
-	read((void*)loaded.data(), (unsigned int)text.length());
+	read((void *)loaded.data(), static_cast<unsigned int>(text.length()));
 	if(loaded != text)
 		throw std::runtime_error("Magic bytes doesn't match!");
 }

+ 1 - 1
lib/serializer/BinaryDeserializer.h

@@ -192,7 +192,7 @@ public:
 	void load(T &data)
 	{
 		unsigned length = sizeof(data);
-		char* dataPtr = (char*)&data;
+		char * dataPtr = reinterpret_cast<char *>(&data);
 		this->read(dataPtr,length);
 		if(reverseEndianess)
 			std::reverse(dataPtr, dataPtr + length);

+ 3 - 4
lib/serializer/BinarySerializer.cpp

@@ -24,9 +24,8 @@ CSaveFile::CSaveFile(const boost::filesystem::path &fname)
 	openNextFile(fname);
 }
 
-CSaveFile::~CSaveFile()
-{
-}
+//must be instantiated in .cpp file for access to complete types of all member fields
+CSaveFile::~CSaveFile() = default;
 
 int CSaveFile::write(const void * data, unsigned size)
 {
@@ -73,7 +72,7 @@ void CSaveFile::clear()
 
 void CSaveFile::putMagicBytes(const std::string &text)
 {
-	write(text.c_str(), (unsigned int)text.length());
+	write(text.c_str(), static_cast<unsigned int>(text.length()));
 }
 
 VCMI_LIB_NAMESPACE_END

+ 2 - 2
lib/serializer/CLoadIntegrityValidator.cpp

@@ -40,7 +40,7 @@ int CLoadIntegrityValidator::read( void * data, unsigned size )
 	if(!foundDesync)
 	{
 		controlFile->read(controlData.data(), size);
-		if(std::memcmp(data, controlData.data(), size))
+		if(std::memcmp(data, controlData.data(), size) != 0)
 		{
 			logGlobal->error("Desync found! Position: %d", primaryFile->sfile->tellg());
 			foundDesync = true;
@@ -57,7 +57,7 @@ std::unique_ptr<CLoadFile> CLoadIntegrityValidator::decay()
 	return std::move(primaryFile);
 }
 
-void CLoadIntegrityValidator::checkMagicBytes( const std::string &text )
+void CLoadIntegrityValidator::checkMagicBytes(const std::string & text) const
 {
 	assert(primaryFile);
 	assert(controlFile);

+ 1 - 1
lib/serializer/CLoadIntegrityValidator.h

@@ -25,7 +25,7 @@ public:
 	CLoadIntegrityValidator(const boost::filesystem::path &primaryFileName, const boost::filesystem::path &controlFileName, int minimalVersion = SERIALIZATION_VERSION); //throws!
 
 	int read( void * data, unsigned size) override; //throws!
-	void checkMagicBytes(const std::string &text);
+	void checkMagicBytes(const std::string & text) const;
 
 	std::unique_ptr<CLoadFile> decay(); //returns primary file. CLoadIntegrityValidator stops being usable anymore
 };

+ 1 - 3
lib/serializer/CMemorySerializer.cpp

@@ -32,13 +32,11 @@ int CMemorySerializer::write(const void * data, unsigned size)
 	return size;
 }
 
-CMemorySerializer::CMemorySerializer(): iser(this), oser(this)
+CMemorySerializer::CMemorySerializer(): iser(this), oser(this), readPos(0)
 {
-	readPos = 0;
 	registerTypes(iser);
 	registerTypes(oser);
 	iser.fileVersion = SERIALIZATION_VERSION;
 }
 
-
 VCMI_LIB_NAMESPACE_END

+ 2 - 11
lib/serializer/CSerializer.cpp

@@ -17,17 +17,8 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-CSerializer::~CSerializer()
-{
-
-}
-
-CSerializer::CSerializer()
-{
-	smartVectorMembersSerialization = false;
-	sendStackInstanceByIds = false;
-}
-
+//must be instantiated in .cpp file for access to complete types of all member fields
+CSerializer::~CSerializer() = default;
 
 void CSerializer::addStdVecItems(CGameState *gs, LibClasses *lib)
 {

+ 2 - 3
lib/serializer/CSerializer.h

@@ -80,10 +80,9 @@ class DLL_LINKAGE CSerializer
 	TTypeVecMap vectors; //entry must be a pointer to vector containing pointers to the objects of key type
 
 public:
-	bool smartVectorMembersSerialization;
-	bool sendStackInstanceByIds;
+	bool smartVectorMembersSerialization = false;
+	bool sendStackInstanceByIds = false;
 
-	CSerializer();
 	~CSerializer();
 
 	virtual void reportState(vstd::CLoggerBase * out){};

+ 1 - 1
lib/serializer/CTypeList.cpp

@@ -74,7 +74,7 @@ std::vector<CTypeList::TypeInfoPtr> CTypeList::castSequence(TypeInfoPtr from, Ty
 		std::map<TypeInfoPtr, TypeInfoPtr> previous;
 		std::queue<TypeInfoPtr> q;
 		q.push(to);
-		while(q.size())
+		while(!q.empty())
 		{
 			auto typeNode = q.front();
 			q.pop();

+ 1 - 6
lib/serializer/CTypeList.h

@@ -109,12 +109,7 @@ private:
 
 		return ptr;
 	}
-	CTypeList &operator=(CTypeList &)
-	{
-		// As above.
-		assert(0);
-		return *this;
-	}
+	CTypeList & operator=(CTypeList &) = delete;
 
 	TypeInfoPtr getTypeDescriptor(const std::type_info *type, bool throws = true) const; //if not throws, failure returns nullptr
 	TypeInfoPtr registerType(const std::type_info *type);

+ 27 - 22
lib/serializer/Connection.cpp

@@ -21,16 +21,6 @@ VCMI_LIB_NAMESPACE_BEGIN
 using namespace boost;
 using namespace boost::asio::ip;
 
-#if defined(__hppa__) || \
-	defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \
-	(defined(__MIPS__) && defined(__MISPEB__)) || \
-	defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \
-	defined(__sparc__)
-#define BIG_ENDIAN
-#else
-#define LIL_ENDIAN
-#endif
-
 struct ConnectionBuffers
 {
 	boost::asio::streambuf readBuffer;
@@ -58,7 +48,7 @@ void CConnection::init()
 	disableStackSendingByID();
 	registerTypes(iser);
 	registerTypes(oser);
-#ifdef LIL_ENDIAN
+#ifndef VCMI_ENDIAN_BIG
 	myEndianess = true;
 #else
 	myEndianess = false;
@@ -75,15 +65,21 @@ void CConnection::init()
 	iser.fileVersion = SERIALIZATION_VERSION;
 }
 
-CConnection::CConnection(std::string host, ui16 port, std::string Name, std::string UUID)
-	: io_service(std::make_shared<asio::io_service>()), iser(this), oser(this), name(Name), uuid(UUID), connectionID(0)
+CConnection::CConnection(const std::string & host, ui16 port, std::string Name, std::string UUID):
+	io_service(std::make_shared<asio::io_service>()),
+	iser(this),
+	oser(this),
+	name(std::move(Name)),
+	uuid(std::move(UUID))
 {
-	int i;
+	int i = 0;
 	boost::system::error_code error = asio::error::host_not_found;
 	socket = std::make_shared<tcp::socket>(*io_service);
 
 	tcp::resolver resolver(*io_service);
-	tcp::resolver::iterator end, pom, endpoint_iterator = resolver.resolve(tcp::resolver::query(host, std::to_string(port)),error);
+	tcp::resolver::iterator end;
+	tcp::resolver::iterator pom;
+	tcp::resolver::iterator endpoint_iterator = resolver.resolve(tcp::resolver::query(host, std::to_string(port)), error);
 	if(error)
 	{
 		logNetwork->error("Problem with resolving: \n%s", error.message());
@@ -97,7 +93,6 @@ CConnection::CConnection(std::string host, ui16 port, std::string Name, std::str
 		logNetwork->error("Critical problem: No endpoints found!");
 		goto connerror1;
 	}
-	i=0;
 	while(pom != end)
 	{
 		logNetwork->info("\t%d:%s", i, (boost::asio::ip::tcp::endpoint&)*pom);
@@ -129,13 +124,24 @@ connerror1:
 		logNetwork->error("No error info. ");
 	throw std::runtime_error("Can't establish connection :(");
 }
-CConnection::CConnection(std::shared_ptr<TSocket> Socket, std::string Name, std::string UUID)
-	: iser(this), oser(this), socket(Socket), name(Name), uuid(UUID), connectionID(0)
+CConnection::CConnection(std::shared_ptr<TSocket> Socket, std::string Name, std::string UUID):
+	iser(this),
+	oser(this),
+	socket(std::move(Socket)),
+	name(std::move(Name)),
+	uuid(std::move(UUID))
 {
 	init();
 }
-CConnection::CConnection(std::shared_ptr<TAcceptor> acceptor, std::shared_ptr<boost::asio::io_service> io_service, std::string Name, std::string UUID)
-	: io_service(io_service), iser(this), oser(this), name(Name), uuid(UUID), connectionID(0)
+CConnection::CConnection(const std::shared_ptr<TAcceptor> & acceptor,
+						 const std::shared_ptr<boost::asio::io_service> & io_service,
+						 std::string Name,
+						 std::string UUID):
+	io_service(io_service),
+	iser(this),
+	oser(this),
+	name(std::move(Name)),
+	uuid(std::move(UUID))
 {
 	boost::system::error_code error = asio::error::host_not_found;
 	socket = std::make_shared<tcp::socket>(*io_service);
@@ -181,8 +187,7 @@ int CConnection::write(const void * data, unsigned size)
 			return size;
 		}
 
-		int ret;
-		ret = static_cast<int>(asio::write(*socket,asio::const_buffers_1(asio::const_buffer(data,size))));
+		int ret = static_cast<int>(asio::write(*socket, asio::const_buffers_1(asio::const_buffer(data, size))));
 		return ret;
 	}
 	catch(...)

+ 2 - 2
lib/serializer/Connection.h

@@ -88,8 +88,8 @@ public:
 	int connectionID;
 	std::shared_ptr<boost::thread> handler;
 
-	CConnection(std::string host, ui16 port, std::string Name, std::string UUID);
-	CConnection(std::shared_ptr<TAcceptor> acceptor, std::shared_ptr<boost::asio::io_service> Io_service, std::string Name, std::string UUID);
+	CConnection(const std::string & host, ui16 port, std::string Name, std::string UUID);
+	CConnection(const std::shared_ptr<TAcceptor> & acceptor, const std::shared_ptr<boost::asio::io_service> & Io_service, std::string Name, std::string UUID);
 	CConnection(std::shared_ptr<TSocket> Socket, std::string Name, std::string UUID); //use immediately after accepting connection into socket
 
 	void close();

+ 49 - 0
lib/serializer/ILICReader.cpp

@@ -0,0 +1,49 @@
+/*
+ * JsonTreeSerializer.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "ILICReader.h"
+
+#include "../JsonNode.h"
+
+#include <vstd/StringUtils.h>
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+void ILICReader::readLICPart(const JsonNode & part, const JsonSerializeFormat::TDecoder & decoder, bool val, std::vector<bool> & value) const
+{
+	for(const auto & index : part.Vector())
+	{
+		const std::string & identifier = index.String();
+		const std::string type = typeid(decltype(this)).name();
+
+		const si32 rawId = decoder(identifier);
+		if(rawId >= 0)
+		{
+			if(rawId < value.size())
+				value[rawId] = val;
+			else
+				logGlobal->error("%s::serializeLIC: id out of bounds %d", type, rawId);
+		}
+	}
+}
+
+void ILICReader::readLICPart(const JsonNode & part, const JsonSerializeFormat::TDecoder & decoder, std::set<si32> & value) const
+{
+	for(const auto & index : part.Vector())
+	{
+		const std::string & identifier = index.String();
+
+		const si32 rawId = decoder(identifier);
+		if(rawId != -1)
+			value.insert(rawId);
+	}
+}
+
+VCMI_LIB_NAMESPACE_END

+ 23 - 0
lib/serializer/ILICReader.h

@@ -0,0 +1,23 @@
+/*
+ * ILICReader.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "JsonTreeSerializer.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class DLL_LINKAGE ILICReader
+{
+protected:
+	void readLICPart(const JsonNode & part, const JsonSerializeFormat::TDecoder & decoder, bool val, std::vector<bool> & value) const;
+	void readLICPart(const JsonNode & part, const JsonSerializeFormat::TDecoder & decoder, std::set<si32> & value) const;
+};
+
+VCMI_LIB_NAMESPACE_END

+ 0 - 30
lib/serializer/JsonDeserializer.cpp

@@ -254,34 +254,4 @@ void JsonDeserializer::serializeRaw(const std::string & fieldName, JsonNode & va
 	}
 }
 
-void JsonDeserializer::readLICPart(const JsonNode & part, const TDecoder & decoder, const bool val, std::vector<bool> & value)
-{
-	for(size_t index = 0; index < part.Vector().size(); index++)
-	{
-		const std::string & identifier = part.Vector()[index].String();
-
-		const si32 rawId = decoder(identifier);
-		if(rawId >= 0)
-		{
-			if(rawId < value.size())
-				value[rawId] = val;
-			else
-				logGlobal->error("JsonDeserializer::serializeLIC: id out of bounds %d", rawId);
-		}
-	}
-}
-
-void JsonDeserializer::readLICPart(const JsonNode & part, const TDecoder & decoder, std::set<si32> & value)
-{
-	for(size_t index = 0; index < part.Vector().size(); index++)
-	{
-		const std::string & identifier = part.Vector()[index].String();
-
-		const si32 rawId = decoder(identifier);
-		if(rawId != -1)
-			value.insert(rawId);
-	}
-}
-
-
 VCMI_LIB_NAMESPACE_END

+ 3 - 6
lib/serializer/JsonDeserializer.h

@@ -9,11 +9,12 @@
  */
 #pragma once
 
-#include  "JsonTreeSerializer.h"
+#include "ILICReader.h"
+#include "JsonTreeSerializer.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-class DLL_LINKAGE JsonDeserializer: public JsonTreeSerializer<const JsonNode *>
+class DLL_LINKAGE JsonDeserializer: public JsonTreeSerializer<const JsonNode *>, public ILICReader
 {
 public:
 	JsonDeserializer(const IInstanceResolver * instanceResolver_, const JsonNode & root_);
@@ -35,10 +36,6 @@ protected:
 
 	void serializeInternal(std::string & value) override;
 	void serializeInternal(int64_t & value) override;
-
-private:
-	void readLICPart(const JsonNode & part, const TDecoder & decoder, const bool val, std::vector<bool> & value);
-	void readLICPart(const JsonNode & part, const TDecoder & decoder, std::set<si32> & value);
 };
 
 VCMI_LIB_NAMESPACE_END

+ 12 - 26
lib/serializer/JsonSerializeFormat.cpp

@@ -15,9 +15,7 @@
 VCMI_LIB_NAMESPACE_BEGIN
 
 //JsonSerializeHelper
-JsonSerializeHelper::JsonSerializeHelper(JsonSerializeHelper && other):
-	owner(other.owner),
-	restoreState(false)
+JsonSerializeHelper::JsonSerializeHelper(JsonSerializeHelper && other) noexcept: owner(other.owner), restoreState(false)
 {
 	std::swap(restoreState, other.restoreState);
 }
@@ -40,33 +38,17 @@ JsonSerializeHelper::JsonSerializeHelper(JsonSerializeFormat * owner_)
 }
 
 //JsonStructSerializer
-JsonStructSerializer::JsonStructSerializer(JsonStructSerializer && other)
-	: JsonSerializeHelper(std::move(static_cast<JsonSerializeHelper &>(other)))
-{
-
-}
+JsonStructSerializer::JsonStructSerializer(JsonStructSerializer && other) noexcept: JsonSerializeHelper(std::move(static_cast<JsonSerializeHelper &>(other))) {}
 
 JsonStructSerializer::JsonStructSerializer(JsonSerializeFormat * owner_)
 	: JsonSerializeHelper(owner_)
 {
 }
 
-JsonStructSerializer::~JsonStructSerializer()
-{
-}
-
 //JsonArraySerializer
-JsonArraySerializer::JsonArraySerializer(JsonArraySerializer && other)
-	: JsonSerializeHelper(std::move(static_cast<JsonSerializeHelper &>(other)))
-{
-
-}
+JsonArraySerializer::JsonArraySerializer(JsonArraySerializer && other) noexcept: JsonSerializeHelper(std::move(static_cast<JsonSerializeHelper &>(other))) {}
 
-JsonArraySerializer::JsonArraySerializer(JsonSerializeFormat * owner_):
-	JsonSerializeHelper(owner_)
-{
-	thisNode = &owner->getCurrent();
-}
+JsonArraySerializer::JsonArraySerializer(JsonSerializeFormat * owner_): JsonSerializeHelper(owner_), thisNode(&owner->getCurrent()) {}
 
 JsonStructSerializer JsonArraySerializer::enterStruct(const size_t index)
 {
@@ -110,16 +92,20 @@ size_t JsonArraySerializer::size() const
 }
 
 //JsonSerializeFormat::LIC
-JsonSerializeFormat::LIC::LIC(const std::vector<bool> & Standard, const TDecoder Decoder, const TEncoder Encoder):
-	standard(Standard), decoder(Decoder), encoder(Encoder)
+JsonSerializeFormat::LIC::LIC(const std::vector<bool> & Standard, TDecoder Decoder, TEncoder Encoder):
+	standard(Standard),
+	decoder(std::move(Decoder)),
+	encoder(std::move(Encoder))
 {
 	any.resize(standard.size(), false);
 	all.resize(standard.size(), false);
 	none.resize(standard.size(), false);
 }
 
-JsonSerializeFormat::LICSet::LICSet(const std::set<si32>& Standard, const TDecoder Decoder, const TEncoder Encoder):
-	standard(Standard), decoder(Decoder), encoder(Encoder)
+JsonSerializeFormat::LICSet::LICSet(const std::set<si32> & Standard, TDecoder Decoder, TEncoder Encoder):
+	standard(Standard),
+	decoder(std::move(Decoder)),
+	encoder(std::move(Encoder))
 {
 
 }

+ 9 - 9
lib/serializer/JsonSerializeFormat.h

@@ -28,7 +28,7 @@ public:
 class DLL_LINKAGE JsonSerializeHelper: public boost::noncopyable
 {
 public:
-	JsonSerializeHelper(JsonSerializeHelper && other);
+	JsonSerializeHelper(JsonSerializeHelper && other) noexcept;
 	virtual ~JsonSerializeHelper();
 
 	JsonSerializeFormat * operator->();
@@ -44,8 +44,8 @@ private:
 class DLL_LINKAGE JsonStructSerializer: public JsonSerializeHelper
 {
 public:
-	JsonStructSerializer(JsonStructSerializer && other);
-	~JsonStructSerializer();
+	JsonStructSerializer(JsonStructSerializer && other) noexcept;
+
 protected:
 	JsonStructSerializer(JsonSerializeFormat * owner_);
 
@@ -56,7 +56,7 @@ protected:
 class DLL_LINKAGE JsonArraySerializer: public JsonSerializeHelper
 {
 public:
-	JsonArraySerializer(JsonArraySerializer && other);
+	JsonArraySerializer(JsonArraySerializer && other) noexcept;
 
 	JsonStructSerializer enterStruct(const size_t index);
 	JsonArraySerializer enterArray(const size_t index);
@@ -102,17 +102,17 @@ class DLL_LINKAGE JsonSerializeFormat: public boost::noncopyable
 public:
 	///user-provided callback to resolve string identifier
 	///returns resolved identifier or -1 on error
-	typedef std::function<si32(const std::string &)> TDecoder;
+	using TDecoder = std::function<si32(const std::string &)>;
 
 	///user-provided callback to get string identifier
 	///may assume that object index is valid
-	typedef std::function<std::string(si32)> TEncoder;
+	using TEncoder = std::function<std::string(si32)>;
 
-	typedef std::function<void(JsonSerializeFormat &)> TSerialize;
+	using TSerialize = std::function<void(JsonSerializeFormat &)>;
 
 	struct LIC
 	{
-		LIC(const std::vector<bool> & Standard, const TDecoder Decoder, const TEncoder Encoder);
+		LIC(const std::vector<bool> & Standard, TDecoder Decoder, TEncoder Encoder);
 
 		const std::vector<bool> & standard;
 		const TDecoder decoder;
@@ -122,7 +122,7 @@ public:
 
 	struct LICSet
 	{
-		LICSet(const std::set<si32> & Standard, const TDecoder Decoder, const TEncoder Encoder);
+		LICSet(const std::set<si32> & Standard, TDecoder Decoder, TEncoder Encoder);
 
 		const std::set<si32> & standard;
 		const TDecoder decoder;

+ 3 - 4
lib/serializer/JsonTreeSerializer.h

@@ -27,10 +27,9 @@ protected:
 	T currentObject;
 	std::vector<T> treeRoute;
 
-	JsonTreeSerializer(const IInstanceResolver * instanceResolver_, T root, const bool saving_, const bool updating_)
-		: JsonSerializeFormat(instanceResolver_, saving_, updating_),
-		currentObject(root),
-		treeRoute()
+	JsonTreeSerializer(const IInstanceResolver * instanceResolver_, T root, const bool saving_, const bool updating_):
+		JsonSerializeFormat(instanceResolver_, saving_, updating_),
+		currentObject(root)
 	{
 	}
 

+ 2 - 32
lib/serializer/JsonUpdater.cpp

@@ -245,7 +245,7 @@ void JsonUpdater::serializeBonuses(const std::string & fieldName, CBonusSystemNo
 
 	if(toAdd.getType() == JsonNode::JsonType::DATA_VECTOR)
 	{
-		for(auto & item : toAdd.Vector())
+		for(const auto & item : toAdd.Vector())
 		{
 			auto b = JsonUtils::parseBonus(item);
 			value->addNewBonus(b);
@@ -256,7 +256,7 @@ void JsonUpdater::serializeBonuses(const std::string & fieldName, CBonusSystemNo
 
 	if(toRemove.getType() == JsonNode::JsonType::DATA_VECTOR)
 	{
-		for(auto & item : toRemove.Vector())
+		for(const auto & item : toRemove.Vector())
 		{
 			auto mask = JsonUtils::parseBonus(item);
 
@@ -280,34 +280,4 @@ void JsonUpdater::serializeBonuses(const std::string & fieldName, CBonusSystemNo
 	}
 }
 
-void JsonUpdater::readLICPart(const JsonNode & part, const TDecoder & decoder, const bool val, std::vector<bool> & value)
-{
-	for(size_t index = 0; index < part.Vector().size(); index++)
-	{
-		const std::string & identifier = part.Vector()[index].String();
-
-		const si32 rawId = decoder(identifier);
-		if(rawId >= 0)
-		{
-			if(rawId < value.size())
-				value[rawId] = val;
-			else
-				logGlobal->error("JsonUpdater::serializeLIC: id out of bounds %d", rawId);
-		}
-	}
-}
-
-void JsonUpdater::readLICPart(const JsonNode & part, const TDecoder & decoder, std::set<si32> & value)
-{
-	for(size_t index = 0; index < part.Vector().size(); index++)
-	{
-		const std::string & identifier = part.Vector()[index].String();
-
-		const si32 rawId = decoder(identifier);
-		if(rawId != -1)
-			value.insert(rawId);
-	}
-}
-
-
 VCMI_LIB_NAMESPACE_END

+ 3 - 6
lib/serializer/JsonUpdater.h

@@ -9,13 +9,14 @@
  */
 #pragma once
 
-#include  "JsonTreeSerializer.h"
+#include "ILICReader.h"
+#include "JsonTreeSerializer.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
 class CBonusSystemNode;
 
-class DLL_LINKAGE JsonUpdater: public JsonTreeSerializer<const JsonNode *>
+class DLL_LINKAGE JsonUpdater: public JsonTreeSerializer<const JsonNode *>, public ILICReader
 {
 public:
 	JsonUpdater(const IInstanceResolver * instanceResolver_, const JsonNode & root_);
@@ -39,10 +40,6 @@ protected:
 
 	void serializeInternal(std::string & value) override;
 	void serializeInternal(int64_t & value) override;
-
-private:
-	void readLICPart(const JsonNode & part, const TDecoder & decoder, const bool val, std::vector<bool> & value);
-	void readLICPart(const JsonNode & part, const TDecoder & decoder, std::set<si32> & value);
 };
 
 VCMI_LIB_NAMESPACE_END

+ 10 - 10
lib/spells/AdventureSpellMechanics.cpp

@@ -79,7 +79,7 @@ ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(SpellCastEnviron
 
 		owner->getEffects(bonuses, schoolLevel, false, parameters.caster->getEnchantPower(owner));
 
-		for(Bonus b : bonuses)
+		for(const Bonus & b : bonuses)
 		{
 			GiveBonus gb;
 			gb.id = parameters.caster->id.getNum();
@@ -182,7 +182,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
 	{
 		if(obj && obj->ID == Obj::BOAT)
 		{
-			const CGBoat *b = static_cast<const CGBoat*>(obj);
+			const auto * b = dynamic_cast<const CGBoat *>(obj);
 			if(b->hero)
 				continue; //we're looking for unoccupied boat
 
@@ -249,7 +249,7 @@ ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(SpellCastEnvironmen
 
 	//TODO: test range, visibility
 	const TerrainTile *t = &env->getMap()->getTile(parameters.pos);
-	if(!t->visitableObjects.size() || t->visitableObjects.back()->ID != Obj::BOAT)
+	if(t->visitableObjects.empty() || t->visitableObjects.back()->ID != Obj::BOAT)
 	{
 		env->complain("There is no boat to scuttle!");
 		return ESpellCastResult::ERROR;
@@ -328,7 +328,7 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
 	{
 		SetMovePoints smp;
 		smp.hid = parameters.caster->id;
-		if(movementCost < (int)parameters.caster->movement)
+		if(movementCost < static_cast<int>(parameters.caster->movement))
 			smp.val = parameters.caster->movement - movementCost;
 		else
 			smp.val = 0;
@@ -356,7 +356,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
 		if(nullptr == destination)
 			return ESpellCastResult::ERROR;
 
-		if((int)parameters.caster->movement < moveCost)
+		if(static_cast<int>(parameters.caster->movement) < moveCost)
 			return ESpellCastResult::ERROR;
 
 		if(destination->visitingHero)
@@ -372,7 +372,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
 	{
 		const TerrainTile & tile = env->getMap()->getTile(parameters.pos);
 
-		const auto topObj = tile.topVisitableObj(false);
+		auto * const topObj = tile.topVisitableObj(false);
 
 		if(!topObj)
 		{
@@ -406,7 +406,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
 			return ESpellCastResult::ERROR;
 		}
 
-		if((int)parameters.caster->movement < moveCost)
+		if(static_cast<int>(parameters.caster->movement) < moveCost)
 		{
 			env->complain("This hero has not enough movement points!");
 			return ESpellCastResult::ERROR;
@@ -449,7 +449,7 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons
 
 	const int moveCost = movementCost(parameters);
 
-	if((int)parameters.caster->movement < moveCost)
+	if(static_cast<int>(parameters.caster->movement) < moveCost)
 	{
 		InfoWindow iw;
 		iw.player = parameters.caster->tempOwner;
@@ -464,7 +464,7 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons
 		{
 			if(reply.getType() == JsonNode::JsonType::DATA_INTEGER)
 			{
-				ObjectInstanceID townId((si32)reply.Integer());
+				ObjectInstanceID townId(static_cast<si32>(reply.Integer()));
 
 				const CGObjectInstance * o = env->getCb()->getObj(townId, true);
 				if(o == nullptr)
@@ -488,7 +488,7 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons
 
 		MapObjectSelectDialog request;
 
-		for(auto t : towns)
+		for(const auto * t : towns)
 		{
 			if(t->visitingHero == nullptr) //empty town
 				request.objects.push_back(t->id);

+ 23 - 20
lib/spells/BattleSpellMechanics.cpp

@@ -127,11 +127,12 @@ namespace SRSLPraserHelpers
 	}
 }
 
-
-BattleSpellMechanics::BattleSpellMechanics(const IBattleCast * event, std::shared_ptr<effects::Effects> effects_, std::shared_ptr<IReceptiveCheck> targetCondition_)
-	: BaseMechanics(event),
-	effects(effects_),
-	targetCondition(targetCondition_)
+BattleSpellMechanics::BattleSpellMechanics(const IBattleCast * event,
+										   std::shared_ptr<effects::Effects> effects_,
+										   std::shared_ptr<IReceptiveCheck> targetCondition_):
+	BaseMechanics(event),
+	effects(std::move(effects_)),
+	targetCondition(std::move(targetCondition_))
 {}
 
 BattleSpellMechanics::~BattleSpellMechanics() = default;
@@ -167,7 +168,7 @@ bool BattleSpellMechanics::canBeCast(Problem & problem) const
 	{
 	case Mode::HERO:
 		{
-			const CGHeroInstance * castingHero = dynamic_cast<const CGHeroInstance *>(caster);//todo: unify hero|creature spell cost
+			const auto * castingHero = dynamic_cast<const CGHeroInstance *>(caster); //todo: unify hero|creature spell cost
 			if(!castingHero)
 			{
 				logGlobal->debug("CSpell::canBeCast: invalid caster");
@@ -272,7 +273,7 @@ void BattleSpellMechanics::cast(ServerCallback * server, const Target & target)
 	//calculate spell cost
 	if(mode == Mode::HERO)
 	{
-		auto casterHero = dynamic_cast<const CGHeroInstance *>(caster);
+		const auto * casterHero = dynamic_cast<const CGHeroInstance *>(caster);
 		spellCost = battle()->battleGetSpellCost(owner, casterHero);
 
 		if(nullptr != otherHero) //handle mana channel
@@ -383,7 +384,7 @@ void BattleSpellMechanics::beforeCast(BattleSpellCast & sc, vstd::RNG & rng, con
 	std::set<const battle::Unit *> unitTargets = collectTargets();
 
 	//process them
-	for(auto unit : unitTargets)
+	for(const auto * unit : unitTargets)
 		filterUnit(unit);
 
 	//and update targets
@@ -405,7 +406,7 @@ void BattleSpellMechanics::beforeCast(BattleSpellCast & sc, vstd::RNG & rng, con
 		}
 	}
 
-	for(auto unit : resisted)
+	for(const auto * unit : resisted)
 		sc.resistedCres.insert(unit->unitId());
 }
 
@@ -448,16 +449,16 @@ void BattleSpellMechanics::doRemoveEffects(ServerCallback * server, const std::v
 {
 	SetStackEffect sse;
 
-	for(auto unit : targets)
+	for(const auto * unit : targets)
 	{
 		std::vector<Bonus> buffer;
 		auto bl = unit->getBonuses(selector);
 
-		for(auto item : *bl)
+		for(const auto & item : *bl)
 			buffer.emplace_back(*item);
 
 		if(!buffer.empty())
-			sse.toRemove.push_back(std::make_pair(unit->unitId(), buffer));
+			sse.toRemove.emplace_back(unit->unitId(), buffer);
 	}
 
 	if(!sse.toRemove.empty())
@@ -487,8 +488,10 @@ std::set<BattleHex> BattleSpellMechanics::spellRangeInHexes(BattleHex centralHex
 
 	if(rng.size() >= 2 && rng[0] != 'X') //there is at least one hex in range (+artificial comma)
 	{
-		std::string number1, number2;
-		int beg, end;
+		std::string number1;
+		std::string number2;
+		int beg = 0;
+		int end = 0;
 		bool readingFirst = true;
 		for(auto & elem : rng)
 		{
@@ -504,12 +507,12 @@ std::set<BattleHex> BattleSpellMechanics::spellRangeInHexes(BattleHex centralHex
 				//calculating variables
 				if(readingFirst)
 				{
-					beg = atoi(number1.c_str());
+					beg = std::stoi(number1);
 					number1 = "";
 				}
 				else
 				{
-					end = atoi(number2.c_str());
+					end = std::stoi(number2);
 					number2 = "";
 				}
 				//obtaining new hexes
@@ -524,7 +527,7 @@ std::set<BattleHex> BattleSpellMechanics::spellRangeInHexes(BattleHex centralHex
 					readingFirst = true;
 				}
 				//adding obtained hexes
-				for(auto & curLayer_it : curLayer)
+				for(const auto & curLayer_it : curLayer)
 				{
 					ret.insert(curLayer_it);
 				}
@@ -532,7 +535,7 @@ std::set<BattleHex> BattleSpellMechanics::spellRangeInHexes(BattleHex centralHex
 			}
 			else if(elem == '-') //dash
 			{
-				beg = atoi(number1.c_str());
+				beg = std::stoi(number1);
 				number1 = "";
 				readingFirst = false;
 			}
@@ -546,7 +549,7 @@ Target BattleSpellMechanics::transformSpellTarget(const Target & aimPoint) const
 {
 	Target spellTarget;
 
-	if(aimPoint.size() < 1)
+	if(aimPoint.empty())
 	{
 		logGlobal->error("Aimed spell cast with no destination.");
 	}
@@ -560,7 +563,7 @@ Target BattleSpellMechanics::transformSpellTarget(const Target & aimPoint) const
 		if(aimPointHex.isValid())
 		{
 			auto spellRange = spellRangeInHexes(aimPointHex);
-			for(auto & hex : spellRange)
+			for(const auto & hex : spellRange)
 				spellTarget.push_back(Destination(hex));
 		}
 	}

+ 3 - 3
lib/spells/BonusCaster.cpp

@@ -22,10 +22,10 @@ VCMI_LIB_NAMESPACE_BEGIN
 namespace spells
 {
 
-BonusCaster::BonusCaster(const Caster * actualCaster_, std::shared_ptr<Bonus> bonus_)
-	: ProxyCaster(actualCaster_),
+BonusCaster::BonusCaster(const Caster * actualCaster_, std::shared_ptr<Bonus> bonus_):
+	ProxyCaster(actualCaster_),
 	actualCaster(actualCaster_),
-	bonus(bonus_)
+	bonus(std::move(bonus_))
 {
 
 }

+ 19 - 56
lib/spells/CSpellHandler.cpp

@@ -84,24 +84,6 @@ static const ESpellSchool SCHOOL_ORDER[4] =
 };
 } //namespace SpellConfig
 
-///CSpell::LevelInfo
-CSpell::LevelInfo::LevelInfo():
-	cost(0),
-	power(0),
-	AIValue(0),
-	smartTarget(true),
-	clearTarget(false),
-	clearAffected(false),
-	range("0")
-{
-
-}
-
-CSpell::LevelInfo::~LevelInfo()
-{
-
-}
-
 ///CSpell
 CSpell::CSpell():
 	id(SpellID::NONE),
@@ -115,23 +97,19 @@ CSpell::CSpell():
 	damage(false),
 	offensive(false),
 	special(true),
-	targetType(spells::AimType::NO_TARGET),
-	mechanics(),
-	adventureMechanics()
+	targetType(spells::AimType::NO_TARGET)
 {
 	levels.resize(GameConstants::SPELL_SCHOOL_LEVELS);
 }
 
-CSpell::~CSpell()
-{
-
-}
+//must be instantiated in .cpp file for access to complete types of all member fields
+CSpell::~CSpell() = default;
 
 bool CSpell::adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
 {
 	assert(env);
 
-	if(!adventureMechanics.get())
+	if(!adventureMechanics)
 	{
 		env->complain("Invalid adventure spell cast attempt!");
 		return false;
@@ -185,7 +163,7 @@ void CSpell::forEachSchool(const std::function<void(const spells::SchoolInfo &,
 	bool stop = false;
 	for(ESpellSchool iter : SpellConfig::SCHOOL_ORDER)
 	{
-		const spells::SchoolInfo & cnf = SpellConfig::SCHOOL[(ui8)iter];
+		const spells::SchoolInfo & cnf = SpellConfig::SCHOOL[static_cast<ui8>(iter)];
 		if(school.at(cnf.id))
 		{
 			cb(cnf, stop);
@@ -405,15 +383,15 @@ int64_t CSpell::adjustRawDamage(const spells::Caster * caster, const battle::Uni
 	//affected creature-specific part
 	if(nullptr != affectedCreature)
 	{
-		auto bearer = affectedCreature;
+		const auto * bearer = affectedCreature;
 		//applying protections - when spell has more then one elements, only one protection should be applied (I think)
 		forEachSchool([&](const spells::SchoolInfo & cnf, bool & stop)
 		{
-			if(bearer->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, (ui8)cnf.id))
+			if(bearer->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, static_cast<ui8>(cnf.id)))
 			{
-				ret *= 100 - bearer->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, (ui8)cnf.id);
+				ret *= 100 - bearer->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, static_cast<ui8>(cnf.id));
 				ret /= 100;
-				stop = true;//only bonus from one school is used
+				stop = true; //only bonus from one school is used
 			}
 		});
 
@@ -439,7 +417,7 @@ int64_t CSpell::adjustRawDamage(const spells::Caster * caster, const battle::Uni
 
 int64_t CSpell::calculateRawEffectValue(int32_t effectLevel, int32_t basePowerMultiplier, int32_t levelPowerMultiplier) const
 {
-	return (int64_t)basePowerMultiplier * getBasePower() + levelPowerMultiplier * getLevelPower(effectLevel);
+	return static_cast<int64_t>(basePowerMultiplier) * getBasePower() + levelPowerMultiplier * getLevelPower(effectLevel);
 }
 
 void CSpell::setIsOffensive(const bool val)
@@ -540,18 +518,7 @@ CSpell::AnimationItem::AnimationItem() :
 
 }
 
-
 ///CSpell::AnimationInfo
-CSpell::AnimationInfo::AnimationInfo()
-{
-
-}
-
-CSpell::AnimationInfo::~AnimationInfo()
-{
-
-}
-
 std::string CSpell::AnimationInfo::selectProjectile(const double angle) const
 {
 	std::string res;
@@ -577,7 +544,7 @@ CSpell::TargetInfo::TargetInfo(const CSpell * spell, const int level, spells::Mo
 	clearAffected(false),
 	clearTarget(false)
 {
-	auto & levelInfo = spell->getLevelInfo(level);
+	const auto & levelInfo = spell->getLevelInfo(level);
 
 	smart = levelInfo.smartTarget;
 	massive = levelInfo.range == "X";
@@ -592,10 +559,6 @@ bool DLL_LINKAGE isInScreenRange(const int3 & center, const int3 & pos)
 }
 
 ///CSpellHandler
-CSpellHandler::CSpellHandler() = default;
-
-CSpellHandler::~CSpellHandler() = default;
-
 std::vector<JsonNode> CSpellHandler::loadLegacyData(size_t dataSize)
 {
 	using namespace SpellConfig;
@@ -648,8 +611,8 @@ std::vector<JsonNode> CSpellHandler::loadLegacyData(size_t dataSize)
 
 			auto & chances = lineNode["gainChance"].Struct();
 
-			for(size_t i = 0; i < GameConstants::F_NUMBER; i++)
-				chances[ETownType::names[i]].Integer() = static_cast<si64>(parser.readNumber());
+			for(const auto & name : ETownType::names)
+				chances[name].Integer() = static_cast<si64>(parser.readNumber());
 
 			auto AIVals = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS);
 
@@ -713,7 +676,7 @@ CSpell * CSpellHandler::loadFromJson(const std::string & scope, const JsonNode &
 
 	SpellID id(static_cast<si32>(index));
 
-	CSpell * spell = new CSpell();
+	auto * spell = new CSpell();
 	spell->id = id;
 	spell->identifier = identifier;
 	spell->modScope = scope;
@@ -774,9 +737,9 @@ CSpell * CSpellHandler::loadFromJson(const std::string & scope, const JsonNode &
 	{
 		if(counteredSpell.second.Bool())
 		{
-			VLC->modh->identifiers.requestIdentifier(counteredSpell.second.meta, counteredSpell.first, [=](si32 id)
+			VLC->modh->identifiers.requestIdentifier(counteredSpell.second.meta, counteredSpell.first, [=](si32 id) 
 			{
-				spell->counteredSpells.push_back(SpellID(id));
+				spell->counteredSpells.emplace_back(id);
 			});
 		}
 	}
@@ -820,7 +783,7 @@ CSpell * CSpellHandler::loadFromJson(const std::string & scope, const JsonNode &
 
 	spell->special = flags["special"].Bool();
 
-	auto findBonus = [&](std::string name, std::vector<Bonus::BonusType> & vec)
+	auto findBonus = [&](const std::string & name, std::vector<Bonus::BonusType> & vec)
 	{
 		auto it = bonusNameMap.find(name);
 		if(it == bonusNameMap.end())
@@ -829,11 +792,11 @@ CSpell * CSpellHandler::loadFromJson(const std::string & scope, const JsonNode &
 		}
 		else
 		{
-			vec.push_back((Bonus::BonusType)it->second);
+			vec.push_back(static_cast<Bonus::BonusType>(it->second));
 		}
 	};
 
-	auto readBonusStruct = [&](std::string name, std::vector<Bonus::BonusType> & vec)
+	auto readBonusStruct = [&](const std::string & name, std::vector<Bonus::BonusType> & vec)
 	{
 		for(auto bonusData: json[name].Struct())
 		{

+ 8 - 16
lib/spells/CSpellHandler.h

@@ -97,9 +97,6 @@ public:
 
 	struct DLL_LINKAGE AnimationInfo
 	{
-		AnimationInfo();
-		~AnimationInfo();
-
 		///displayed on all affected targets.
 		TAnimationQueue affect;
 
@@ -123,17 +120,18 @@ public:
 
 		std::string selectProjectile(const double angle) const;
 	} animationInfo;
+
 public:
 	struct LevelInfo
 	{
-		si32 cost;
-		si32 power;
-		si32 AIValue;
+		si32 cost = 0;
+		si32 power = 0;
+		si32 AIValue = 0;
 
-		bool smartTarget;
-		bool clearTarget;
-		bool clearAffected;
-		std::string range;
+		bool smartTarget = true;
+		bool clearTarget = false;
+		bool clearAffected = false;
+		std::string range = "0";
 
 		//TODO: remove these two when AI will understand special effects
 		std::vector<std::shared_ptr<Bonus>> effects; //deprecated
@@ -141,9 +139,6 @@ public:
 
 		JsonNode battleEffects;
 
-		LevelInfo();
-		~LevelInfo();
-
 		template <typename Handler> void serialize(Handler & h, const int version)
 		{
 			h & cost;
@@ -369,9 +364,6 @@ bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos); //for spe
 class DLL_LINKAGE CSpellHandler: public CHandlerBase<SpellID, spells::Spell, CSpell, spells::Service>
 {
 public:
-	CSpellHandler();
-	virtual ~CSpellHandler();
-
 	///IHandler base
 	std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
 	void afterLoadFinalization() override;

+ 16 - 25
lib/spells/ISpellMechanics.cpp

@@ -62,7 +62,7 @@ class CustomMechanicsFactory : public ISpellMechanicsFactory
 public:
 	std::unique_ptr<Mechanics> create(const IBattleCast * event) const override
 	{
-		BattleSpellMechanics * ret = new BattleSpellMechanics(event, effects, targetCondition);
+		auto * ret = new BattleSpellMechanics(event, effects, targetCondition);
 		return std::unique_ptr<Mechanics>(ret);
 	}
 protected:
@@ -117,7 +117,7 @@ public:
 
 			if(!levelInfo.effects.empty())
 			{
-				auto timed = new effects::Timed();
+				auto * timed = new effects::Timed();
 				timed->cumulative = false;
 				timed->bonus = levelInfo.effects;
 				effect.reset(timed);
@@ -125,7 +125,7 @@ public:
 
 			if(!levelInfo.cumulativeEffects.empty())
 			{
-				auto timed = new effects::Timed();
+				auto * timed = new effects::Timed();
 				timed->cumulative = true;
 				timed->bonus = levelInfo.cumulativeEffects;
 				effect.reset(timed);
@@ -137,20 +137,15 @@ public:
 	}
 };
 
-
-BattleCast::BattleCast(const CBattleInfoCallback * cb_, const Caster * caster_, const Mode mode_, const CSpell * spell_)
-	: spell(spell_),
+BattleCast::BattleCast(const CBattleInfoCallback * cb_, const Caster * caster_, const Mode mode_, const CSpell * spell_):
+	spell(spell_),
 	cb(cb_),
+	gameCb(IObjectInterface::cb), //FIXME: pass player callback (problem is that BattleAI do not have one)
 	caster(caster_),
 	mode(mode_),
-	magicSkillLevel(),
-	effectPower(),
-	effectDuration(),
-	effectValue(),
 	smart(boost::logic::indeterminate),
 	massive(boost::logic::indeterminate)
 {
-	gameCb = IObjectInterface::cb; //FIXME: pass player callback (problem is that BattleAI do not have one)
 }
 
 BattleCast::BattleCast(const BattleCast & orig, const Caster * caster_)
@@ -245,7 +240,7 @@ void BattleCast::setEffectValue(BattleCast::Value64 value)
 	effectValue = boost::make_optional(value);
 }
 
-void BattleCast::applyEffects(ServerCallback * server, Target target,  bool indirect, bool ignoreImmunity) const
+void BattleCast::applyEffects(ServerCallback * server, const Target & target, bool indirect, bool ignoreImmunity) const
 {
 	auto m = spell->battleMechanics(this);
 
@@ -296,7 +291,7 @@ void BattleCast::cast(ServerCallback * server, Target target)
 
 			if(!mirrorTargets.empty())
 			{
-				auto mirrorDestination = (*RandomGeneratorUtil::nextItem(mirrorTargets, *server->getRNG()));
+				const auto * mirrorDestination = (*RandomGeneratorUtil::nextItem(mirrorTargets, *server->getRNG()));
 
 				Target mirrorTarget;
 				mirrorTarget.emplace_back(mirrorDestination);
@@ -325,7 +320,7 @@ bool BattleCast::castIfPossible(ServerCallback * server, Target target)
 {
 	if(spell->canBeCast(cb, mode, caster))
 	{
-		cast(server, target);
+		cast(server, std::move(target));
 		return true;
 	}
 	return false;
@@ -397,10 +392,8 @@ ISpellMechanicsFactory::ISpellMechanicsFactory(const CSpell * s)
 
 }
 
-ISpellMechanicsFactory::~ISpellMechanicsFactory()
-{
-
-}
+//must be instantiated in .cpp file for access to complete types of all member fields
+ISpellMechanicsFactory::~ISpellMechanicsFactory() = default;
 
 std::unique_ptr<ISpellMechanicsFactory> ISpellMechanicsFactory::get(const CSpell * s)
 {
@@ -420,16 +413,14 @@ Mechanics::Mechanics()
 
 Mechanics::~Mechanics() = default;
 
-BaseMechanics::BaseMechanics(const IBattleCast * event)
-	: Mechanics(),
+BaseMechanics::BaseMechanics(const IBattleCast * event):
 	owner(event->getSpell()),
 	mode(event->getMode()),
 	smart(event->isSmart()),
-	massive(event->isMassive())
+	massive(event->isMassive()),
+	cb(event->getBattle()),
+	gameCb(event->getGame())
 {
-	cb = event->getBattle();
-	gameCb = event->getGame();
-
 	caster = event->getCaster();
 
 	//FIXME: do not crash on invalid side
@@ -510,7 +501,7 @@ bool BaseMechanics::adaptProblem(ESpellCastProblem::ESpellCastProblem source, Pr
 		{
 			MetaString text;
 			//TODO: refactor
-			auto hero = dynamic_cast<const CGHeroInstance *>(caster);
+			const auto * hero = dynamic_cast<const CGHeroInstance *>(caster);
 			if(!hero)
 				return adaptGenericProblem(target);
 

+ 2 - 2
lib/spells/ISpellMechanics.h

@@ -49,7 +49,7 @@ namespace scripting
 class DLL_LINKAGE SpellCastEnvironment : public ServerCallback
 {
 public:
-	virtual ~SpellCastEnvironment(){};
+	virtual ~SpellCastEnvironment() = default;
 
 	virtual const CMap * getMap() const = 0;
 	virtual const CGameInfoCallback * getCb() const = 0;
@@ -128,7 +128,7 @@ public:
 	void setEffectValue(Value64 value);
 
 	///only apply effects to specified targets
-	void applyEffects(ServerCallback * server, Target target, bool indirect = false, bool ignoreImmunity = false) const;
+	void applyEffects(ServerCallback * server, const Target & target, bool indirect = false, bool ignoreImmunity = false) const;
 
 	///normal cast
 	void cast(ServerCallback * server, Target target);

+ 1 - 11
lib/spells/Problem.cpp

@@ -18,19 +18,9 @@ namespace spells
 namespace detail
 {
 
-ProblemImpl::ProblemImpl()
-{
-
-}
-
-ProblemImpl::~ProblemImpl()
-{
-
-}
-
 void ProblemImpl::add(MetaString && description, Severity severity)
 {
-	data.push_back(std::make_pair(description, severity));
+	data.emplace_back(description, severity);
 }
 
 void ProblemImpl::getAll(std::vector<std::string> & target) const

+ 0 - 3
lib/spells/Problem.h

@@ -24,9 +24,6 @@ namespace detail
 class DLL_LINKAGE ProblemImpl : public Problem
 {
 public:
-	ProblemImpl();
-	virtual ~ProblemImpl();
-
 	void add(MetaString && description, Severity severity = CRITICAL) override;
 
 	void getAll(std::vector<std::string> & target) const override;

+ 13 - 45
lib/spells/TargetCondition.cpp

@@ -26,20 +26,11 @@ VCMI_LIB_NAMESPACE_BEGIN
 namespace spells
 {
 
-TargetConditionItem::TargetConditionItem() = default;
-TargetConditionItem::~TargetConditionItem() = default;
-
 class TargetConditionItemBase : public TargetConditionItem
 {
 public:
-	bool inverted;
-	bool exclusive;
-
-	TargetConditionItemBase()
-		: inverted(false),
-		exclusive(false)
-	{
-	}
+	bool inverted = false;
+	bool exclusive = false;
 
 	void setInverted(bool value) override
 	{
@@ -87,10 +78,7 @@ private:
 class CreatureCondition : public TargetConditionItemBase
 {
 public:
-	CreatureCondition(CreatureID type_)
-		: type(type_)
-	{
-	}
+	CreatureCondition(const CreatureID & type_): type(type_) {}
 
 protected:
 	bool check(const Mechanics * m, const battle::Unit * target) const override
@@ -218,11 +206,6 @@ protected:
 //for Hypnotize
 class HealthValueCondition : public TargetConditionItemBase
 {
-public:
-	HealthValueCondition()
-	{
-	}
-
 protected:
 	bool check(const Mechanics * m, const battle::Unit * target) const override
 	{
@@ -238,8 +221,7 @@ protected:
 class SpellEffectCondition : public TargetConditionItemBase
 {
 public:
-	SpellEffectCondition(SpellID spellID_)
-		: spellID(spellID_)
+	SpellEffectCondition(const SpellID & spellID_): spellID(spellID_)
 	{
 		std::stringstream builder;
 		builder << "source_" << Bonus::SPELL_EFFECT << "id_" << spellID.num;
@@ -262,13 +244,6 @@ private:
 
 class ReceptiveFeatureCondition : public TargetConditionItemBase
 {
-public:
-	ReceptiveFeatureCondition()
-	{
-		selector = Selector::type()(Bonus::RECEPTIVE);
-		cachingString = "type_RECEPTIVE";
-	}
-
 protected:
 	bool check(const Mechanics * m, const battle::Unit * target) const override
 	{
@@ -276,17 +251,12 @@ protected:
 	}
 
 private:
-	CSelector selector;
-	std::string cachingString;
+	CSelector selector = Selector::type()(Bonus::RECEPTIVE);
+	std::string cachingString = "type_RECEPTIVE";
 };
 
 class ImmunityNegationCondition : public TargetConditionItemBase
 {
-public:
-	ImmunityNegationCondition()
-	{
-	}
-
 protected:
 	bool check(const Mechanics * m, const battle::Unit * target) const override
 	{
@@ -404,16 +374,12 @@ const TargetConditionItemFactory * TargetConditionItemFactory::getDefault()
 	return singleton.get();
 }
 
-TargetCondition::TargetCondition() = default;
-
-TargetCondition::~TargetCondition() = default;
-
 bool TargetCondition::isReceptive(const Mechanics * m, const battle::Unit * target) const
 {
 	if(!check(absolute, m, target))
 		return false;
 
-	for(auto item : negation)
+	for(const auto & item : negation)
 	{
 		if(item->isReceptive(m, target))
 			return true;
@@ -462,7 +428,7 @@ bool TargetCondition::check(const ItemVector & condition, const Mechanics * m, c
 	bool nonExclusiveCheck = false;
 	bool nonExclusiveExits = false;
 
-	for(auto & item : condition)
+	for(const auto & item : condition)
 	{
 		if(item->isExclusive())
 		{
@@ -481,7 +447,7 @@ bool TargetCondition::check(const ItemVector & condition, const Mechanics * m, c
 
 void TargetCondition::loadConditions(const JsonNode & source, bool exclusive, bool inverted, const ItemFactory * itemFactory)
 {
-	for(auto & keyValue : source.Struct())
+	for(const auto & keyValue : source.Struct())
 	{
 		bool isAbsolute;
 
@@ -494,9 +460,11 @@ void TargetCondition::loadConditions(const JsonNode & source, bool exclusive, bo
 		else
 			continue;
 
-		std::string scope, type, identifier;
+		std::string scope;
+		std::string type;
+		std::string identifier;
 
-		VLC->modh->parseIdentifier(keyValue.first, scope, type, identifier);
+		CModHandler::parseIdentifier(keyValue.first, scope, type, identifier);
 
 		std::shared_ptr<TargetConditionItem> item = itemFactory->createConfigurable(scope, type, identifier);
 

+ 0 - 6
lib/spells/TargetCondition.h

@@ -30,9 +30,6 @@ class Mechanics;
 class DLL_LINKAGE TargetConditionItem : public IReceptiveCheck
 {
 public:
-	TargetConditionItem();
-	virtual ~TargetConditionItem();
-
 	virtual void setInverted(bool value) = 0;
 	virtual void setExclusive(bool value) = 0;
 
@@ -70,9 +67,6 @@ public:
 	ItemVector absolute;
 	ItemVector negation;
 
-	TargetCondition();
-	virtual ~TargetCondition();
-
 	bool isReceptive(const Mechanics * m, const battle::Unit * target) const override;
 
 	void serializeJson(JsonSerializeFormat & handler, const ItemFactory * itemFactory);

+ 0 - 6
lib/spells/ViewSpellInt.cpp

@@ -16,12 +16,6 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-ObjectPosInfo::ObjectPosInfo():
-	pos(), id(Obj::NO_OBJ), subId(-1), owner(PlayerColor::CANNOT_DETERMINE)
-{
-
-}
-
 ObjectPosInfo::ObjectPosInfo(const CGObjectInstance * obj):
 	pos(obj->visitablePos()), id(obj->ID), subId(obj->subID), owner(obj->tempOwner)
 {

+ 5 - 5
lib/spells/ViewSpellInt.h

@@ -20,11 +20,11 @@ VCMI_LIB_NAMESPACE_BEGIN
  struct DLL_LINKAGE ObjectPosInfo
  {
  	int3 pos;
- 	Obj id;
- 	si32 subId;
- 	PlayerColor owner;
- 	ObjectPosInfo();
- 	ObjectPosInfo(const CGObjectInstance * obj);
+	Obj id = Obj::NO_OBJ;
+	si32 subId = -1;
+	PlayerColor owner = PlayerColor::CANNOT_DETERMINE;
+	ObjectPosInfo() = default;
+	ObjectPosInfo(const CGObjectInstance * obj);
 
 	template <typename Handler> void serialize(Handler & h, const int version)
 	{

+ 4 - 4
lib/spells/effects/Damage.cpp

@@ -124,8 +124,8 @@ int64_t Damage::damageForTarget(size_t targetIndex, const Mechanics * m, const b
 
 	if(chainLength > 1 && targetIndex > 0)
 	{
-		double indexedFactor = std::pow(chainFactor, (double) targetIndex);
-		return (int64_t) (indexedFactor * baseDamage);
+		double indexedFactor = std::pow(chainFactor, static_cast<double>(targetIndex));
+		return static_cast<int64_t>(indexedFactor * baseDamage);
 	}
 
 	return baseDamage;
@@ -165,7 +165,7 @@ void Damage::describeEffect(std::vector<MetaString> & log, const Mechanics * m,
 			std::string text = VLC->generaltexth->allTexts[343]; //Does %d points of damage.
 			boost::algorithm::trim(text);
 			line << text;
-			line.addReplacement((int)damage); //no more text afterwards
+			line.addReplacement(static_cast<int>(damage)); //no more text afterwards
 			log.push_back(line);
 		}
 	}
@@ -175,7 +175,7 @@ void Damage::describeEffect(std::vector<MetaString> & log, const Mechanics * m,
 			MetaString line;
 			line.addTxt(MetaString::GENERAL_TXT, 376); // Spell %s does %d damage
 			line.addReplacement(MetaString::SPELL_NAME, m->getSpellIndex());
-			line.addReplacement((int)damage);
+			line.addReplacement(static_cast<int>(damage));
 
 			log.push_back(line);
 		}

+ 0 - 4
lib/spells/effects/Effects.cpp

@@ -25,10 +25,6 @@ namespace spells
 namespace effects
 {
 
-Effects::Effects() = default;
-
-Effects::~Effects() = default;
-
 void Effects::add(const std::string & name, const std::shared_ptr<Effect>& effect, const int level)
 {
 	effect->name = name;

+ 1 - 2
lib/spells/effects/Effects.h

@@ -30,8 +30,7 @@ public:
 
 	EffectData data;
 
-	Effects();
-	virtual ~Effects();
+	virtual ~Effects() = default;
 
 	void add(const std::string & name, const std::shared_ptr<Effect>& effect, const int level);
 

+ 2 - 2
lib/spells/effects/Obstacle.cpp

@@ -68,7 +68,7 @@ static void serializeRelativeShape(JsonSerializeFormat & handler, const std::str
 
 				if(!handler.saving)
 				{
-					value.at(outerIndex).at(innerIndex) = (BattleHex::EDir) vstd::find_pos(EDirMap, temp);
+					value.at(outerIndex).at(innerIndex) = static_cast<BattleHex::EDir>(vstd::find_pos(EDirMap, temp));
 				}
 			}
 		}
@@ -189,7 +189,7 @@ void Obstacle::apply(ServerCallback * server, const Mechanics * m, const EffectT
 		}
 		RandomGeneratorUtil::randomShuffle(availableTiles, *server->getRNG());
 
-		const int patchesToPut = std::min(patchCount, (int)availableTiles.size());
+		const int patchesToPut = std::min(patchCount, static_cast<int>(availableTiles.size()));
 
 		EffectTarget randomTarget;
 		randomTarget.reserve(patchesToPut);

+ 0 - 5
lib/spells/effects/Registry.h

@@ -29,7 +29,6 @@ namespace effects
 class DLL_LINKAGE IEffectFactory
 {
 public:
-	IEffectFactory() = default;
 	virtual ~IEffectFactory() = default;
 
 	virtual Effect * create() const = 0;
@@ -47,7 +46,6 @@ public:
 
 class DLL_LINKAGE GlobalRegistry
 {
-	GlobalRegistry() = default;
 public:
     static Registry * get();
 };
@@ -56,9 +54,6 @@ template<typename E>
 class EffectFactory : public IEffectFactory
 {
 public:
-	EffectFactory() = default;
-	virtual ~EffectFactory() = default;
-
 	Effect * create() const override
 	{
 		return new E();

+ 10 - 1
mapeditor/mainwindow.ui

@@ -114,7 +114,7 @@
   </widget>
   <widget class="QToolBar" name="toolBar">
    <property name="windowTitle">
-    <string notr="true"/>
+    <string>Toolbar</string>
    </property>
    <attribute name="toolBarArea">
     <enum>TopToolBarArea</enum>
@@ -159,6 +159,9 @@
      <height>214</height>
     </size>
    </property>
+   <property name="windowTitle">
+    <string>Minimap</string>
+   </property>
    <attribute name="dockWidgetArea">
     <number>2</number>
    </attribute>
@@ -232,6 +235,9 @@
      <height>524287</height>
     </size>
    </property>
+   <property name="windowTitle">
+    <string>Map Objects View</string>
+   </property>
    <attribute name="dockWidgetArea">
     <number>2</number>
    </attribute>
@@ -422,6 +428,9 @@
      <height>496</height>
     </size>
    </property>
+   <property name="windowTitle">
+    <string>Terrains View</string>
+   </property>
    <attribute name="dockWidgetArea">
     <number>1</number>
    </attribute>

部分文件因为文件数量过多而无法显示