Переглянути джерело

Merge branch 'develop' into template

Laserlicht 4 місяців тому
батько
коміт
414712bb73
100 змінених файлів з 2614 додано та 1488 видалено
  1. 1 1
      AI/Nullkiller/AIGateway.cpp
  2. 1 14
      AI/VCAI/VCAI.cpp
  3. BIN
      Mods/vcmi/Content/Sprites/stackWindow/commander-bg.png
  4. 35 7
      Mods/vcmi/Content/config/chinese.json
  5. 5 3
      Mods/vcmi/Content/config/czech.json
  6. 3 3
      Mods/vcmi/Content/config/english.json
  7. 3 3
      Mods/vcmi/Content/config/french.json
  8. 3 3
      Mods/vcmi/Content/config/german.json
  9. 3 3
      Mods/vcmi/Content/config/hungarian.json
  10. 3 3
      Mods/vcmi/Content/config/italian.json
  11. 3 3
      Mods/vcmi/Content/config/polish.json
  12. 2 3
      Mods/vcmi/Content/config/portuguese.json
  13. 4 3
      Mods/vcmi/Content/config/russian.json
  14. 3 3
      Mods/vcmi/Content/config/spanish.json
  15. 4 3
      Mods/vcmi/Content/config/swedish.json
  16. 3 3
      Mods/vcmi/Content/config/ukrainian.json
  17. 3 3
      Mods/vcmi/Content/config/vietnamese.json
  18. 1 1
      client/CPlayerInterface.cpp
  19. 9 0
      client/NetPacksClient.cpp
  20. 5 2
      client/adventureMap/CMinimap.cpp
  21. 2 2
      client/battle/BattleEffectsController.cpp
  22. 3 1
      client/battle/BattleStacksController.cpp
  23. 168 119
      client/lobby/CBonusSelection.cpp
  24. 0 19
      client/render/Graphics.cpp
  25. 0 3
      client/render/Graphics.h
  26. 3 2
      client/widgets/CArtifactsOfHeroBase.cpp
  27. 1 3
      client/widgets/MiscWidgets.cpp
  28. 1 0
      client/widgets/TextControls.cpp
  29. 1 1
      client/windows/CCreatureWindow.cpp
  30. 3 0
      client/windows/CKingdomInterface.cpp
  31. 4 0
      client/windows/CKingdomInterface.h
  32. 3 2
      client/windows/CMapOverview.cpp
  33. 18 17
      client/windows/CSpellWindow.cpp
  34. 0 48
      config/ERMU_to_picture.json
  35. 42 12
      config/bonuses.json
  36. 0 239
      config/campaignMedia.json
  37. 189 4
      config/campaignOverrides.json
  38. 238 0
      config/campaignRegions.json
  39. 0 234
      config/campaign_regions.json
  40. 10 0
      config/creatures/dungeon.json
  41. 10 0
      config/creatures/fortress.json
  42. 39 4
      config/creatures/neutral.json
  43. 10 0
      config/creatures/rampart.json
  44. 1 1
      config/creatures/special.json
  45. 36 36
      config/factions/castle.json
  46. 46 37
      config/factions/conflux.json
  47. 37 37
      config/factions/dungeon.json
  48. 35 35
      config/factions/fortress.json
  49. 38 38
      config/factions/inferno.json
  50. 37 37
      config/factions/necropolis.json
  51. 38 38
      config/factions/rampart.json
  52. 35 35
      config/factions/stronghold.json
  53. 50 39
      config/factions/tower.json
  54. 211 13
      config/gameConfig.json
  55. 2 1
      config/heroes/castle.json
  56. 2 1
      config/heroes/dungeon.json
  57. 2 1
      config/heroes/fortress.json
  58. 2 1
      config/heroes/inferno.json
  59. 2 1
      config/heroes/stronghold.json
  60. 2 1
      config/heroes/tower.json
  61. 3 3
      config/schemas/artifact.json
  62. 26 16
      config/schemas/bonus.json
  63. 74 48
      config/schemas/bonusInstance.json
  64. 40 0
      config/schemas/campaignRegion.json
  65. 6 2
      config/schemas/gameSettings.json
  66. 8 0
      config/schemas/hero.json
  67. 9 0
      config/schemas/mod.json
  68. 6 0
      config/schemas/skill.json
  69. 63 13
      config/schemas/spell.json
  70. 19 0
      config/schemas/spellSchool.json
  71. 7 5
      config/schemas/townBuilding.json
  72. 5 0
      config/schemas/townStructure.json
  73. 21 0
      config/spellSchools.json
  74. 0 15
      config/spells/moats.json
  75. 0 2
      config/spells/other.json
  76. 0 4
      config/spells/vcmiAbility.json
  77. 1 1
      docs/Readme.md
  78. 6 6
      docs/modders/Bonus/Bonus_Limiters.md
  79. 77 35
      docs/modders/Bonus/Bonus_Types.md
  80. 37 15
      docs/modders/Bonus/Bonus_Updaters.md
  81. 2 2
      docs/modders/Entities_Format/Artifact_Format.md
  82. 1 1
      docs/modders/Entities_Format/Battle_Obstacle_Format.md
  83. 53 0
      docs/modders/Entities_Format/Bonus_Types_Format.md
  84. 2 0
      docs/modders/Entities_Format/Secondary_Skill_Format.md
  85. 363 98
      docs/modders/Entities_Format/Spell_Format.md
  86. 16 0
      docs/modders/Entities_Format/Spell_School_Format.md
  87. 5 10
      docs/modders/Entities_Format/Town_Building_Format.md
  88. 208 0
      docs/modders/Guides/Bonus_System.md
  89. 10 1
      docs/modders/Mod_File_Format.md
  90. 2 0
      include/vcmi/FactionMember.h
  91. 11 14
      lib/BasicTypes.cpp
  92. 42 22
      lib/CBonusTypeHandler.cpp
  93. 11 4
      lib/CBonusTypeHandler.h
  94. 23 33
      lib/CCreatureHandler.cpp
  95. 15 6
      lib/CMakeLists.txt
  96. 9 6
      lib/CSkillHandler.cpp
  97. 1 0
      lib/CSkillHandler.h
  98. 7 0
      lib/GameLibrary.cpp
  99. 6 0
      lib/GameLibrary.h
  100. 4 0
      lib/GameSettings.cpp

+ 1 - 1
AI/Nullkiller/AIGateway.cpp

@@ -781,7 +781,7 @@ void AIGateway::showGarrisonDialog(const CArmedInstance * up, const CGHeroInstan
 	//you can't request action from action-response thread
 	executeActionAsync("showGarrisonDialog", [this, up, down, removableUnits, queryID]()
 	{
-		if(removableUnits && up->tempOwner == down->tempOwner && nullkiller->settings->isGarrisonTroopsUsageAllowed() && !cb->getStartInfo()->isRestorationOfErathiaCampaign())
+		if(removableUnits && up->tempOwner == down->tempOwner && nullkiller->settings->isGarrisonTroopsUsageAllowed() && !cb->getStartInfo()->restrictedGarrisonsForAI())
 		{
 			pickBestCreatures(down, up);
 		}

+ 1 - 14
AI/VCAI/VCAI.cpp

@@ -453,19 +453,6 @@ void VCAI::objectRemoved(const CGObjectInstance * obj, const PlayerColor & initi
 	//clear resource manager goal cache
 	ah->removeOutdatedObjectives(checkRemovalValidity);
 
-	//TODO: Find better way to handle hero boat removal
-	if(auto hero = dynamic_cast<const CGHeroInstance *>(obj))
-	{
-		if(hero->inBoat())
-		{
-			vstd::erase_if_present(visitableObjs, hero->getBoat());
-			vstd::erase_if_present(alreadyVisited, hero->getBoat());
-
-			for(auto h : cb->getHeroesInfo())
-				unreserveObject(h, hero->getBoat());
-		}
-	}
-
 	//TODO
 	//there are other places where CGObjectinstance ptrs are stored...
 	//
@@ -762,7 +749,7 @@ void VCAI::showGarrisonDialog(const CArmedInstance * up, const CGHeroInstance *
 	//you can't request action from action-response thread
 	executeActionAsync("showGarrisonDialog", [this, down, up, removableUnits, queryID]()
 	{
-		if(removableUnits && !cb->getStartInfo()->isRestorationOfErathiaCampaign())
+		if(removableUnits && !cb->getStartInfo()->restrictedGarrisonsForAI())
 			pickBestCreatures(down, up);
 
 		answerQuery(queryID, 0);

BIN
Mods/vcmi/Content/Sprites/stackWindow/commander-bg.png


+ 35 - 7
Mods/vcmi/Content/config/chinese.json

@@ -28,6 +28,11 @@
 	"vcmi.adventureMap.movementPointsHeroInfo" 			 : "(移动点数: %REMAINING / %POINTS)",
 	"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "抱歉,重放对手行动功能目前暂未实现!",
 
+	"vcmi.adventureMap.dwelling2" : "{%s}\n\n你想招募%s还是%s?",
+	"vcmi.adventureMap.dwelling3" : "{%s}\n\n你想招募%s、%s还是%s?",
+
+	"vcmi.artifact.charges" : "充能",
+
 	"vcmi.bonusSource.artifact" : "宝物",
 	"vcmi.bonusSource.creature" : "技能",
 	"vcmi.bonusSource.spell" : "法术",
@@ -340,6 +345,8 @@
 	"vcmi.adventureOptions.mapScrollSpeed6.help": "将地图卷动速度设置为即刻。",
 	"vcmi.adventureOptions.hideBackground.hover" : "隐藏背景",
 	"vcmi.adventureOptions.hideBackground.help" : "{隐藏背景}\n\n隐藏冒险地图背景,以显示贴图代替。",
+	"vcmi.adventureOptions.minimapShowHeroes.hover" : "在小地图上显示英雄",
+	"vcmi.adventureOptions.minimapShowHeroes.help" : "{在小地图上显示英雄}\n\n在小地图上显示额外的英雄可视图标,使之在大地图上更容易被找到。",
 
 	"vcmi.battleOptions.queueSizeLabel.hover": "回合顺序指示器",
 	"vcmi.battleOptions.queueSizeNoneButton.hover": "关闭",
@@ -399,6 +406,9 @@
 	"vcmi.battleWindow.endWithAutocombat" : "您确定想以自动战斗立即结束吗?",
 
 	"vcmi.battleResultsWindow.applyResultsLabel" : "接受战斗结果",
+	"vcmi.battleResultsWindow.spellDurationRemaining.0" : "剩余持续时间: %d回合",
+	"vcmi.battleResultsWindow.spellDurationRemaining.1" : "剩余持续时间: %d回合",
+	"vcmi.battleResultsWindow.spellDurationRemaining.2" : "剩余持续时间: %d回合",
 
 	"vcmi.tutorialWindow.title" : "触摸屏介绍",
 	"vcmi.tutorialWindow.decription.RightClick" : "长按要右键单击的元素。 触摸其他区域以关闭。",
@@ -509,7 +519,7 @@
 	"vcmi.optionsTab.simturns.blocked1"       : "同时进行: 1 周, 屏蔽联系",
 	"vcmi.optionsTab.simturns.blocked2"       : "同时进行: 2 周, 屏蔽联系",
 	"vcmi.optionsTab.simturns.blocked4"       : "同时进行: 1 月, 屏蔽联系",
-	
+
 	// Translation note: translate strings below using form that is correct for "0 days", "1 day" and "2 days" in your language
 	// Using this information, VCMI will automatically select correct plural form for every possible amount
 	"vcmi.optionsTab.simturns.days.0" : " %d 天",
@@ -619,6 +629,9 @@
 
 	"mapObject.core.hillFort.object.description" : "升级生物,1-4级生物升级比城镇中更便宜。",
 
+	"creatures.core.azureDragon.bonus.fearful": "{恐惧}\n使得敌方一只部队恐惧",
+	"creatures.core.azureDragon.bonus.fearless": "{无惧}\n免疫恐惧特质",
+
 	"core.bonus.ADDITIONAL_ATTACK.description": "{双击}\n生物可以攻击两次",
 	"core.bonus.ADDITIONAL_RETALIATION.description": "{额外反击}\n每回合额外获得${val}次反击机会",
 	"core.bonus.ATTACKS_ALL_ADJACENT.description": "{环击}\n攻击所有相邻敌人",
@@ -638,21 +651,26 @@
 	"core.bonus.ENCHANTER.description": "{施法者}\n每回合群体施放${subtype.spell}",
 	"core.bonus.ENEMY_ATTACK_REDUCTION.description": "{忽略攻击 (${val}%)}\n被攻击时,进攻方${val}%的攻击力将被无视。",
 	"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "{忽略防御 (${val}%)}\n发动攻击时,防御方${val}%的防御力将被无视。",
-	"core.bonus.FEAR.description": "{恐惧}\n使得敌方一只部队恐惧",
-	"core.bonus.FEARLESS.description": "{无惧}\n免疫恐惧特质",
 	"core.bonus.FEROCITY.description": "{凶猛追击}\n杀死任意生物后额外攻击${val}次",
 	"core.bonus.FIRE_SHIELD.description": "{烈火神盾 (${val}%)}\n反弹部分受到的近战伤害",
+	"core.bonus.FIRST_STRIKE.description.bonusSubtype.damageTypeMelee" : "{抢先反击}\n该生物的反击将会在被近战攻击前进行",
+	"core.bonus.FIRST_STRIKE.description.bonusSubtype.damageTypeRanged" : "{抢先反击}\n该生物的反击将会在被远程攻击前进行",
 	"core.bonus.FIRST_STRIKE.description": "{抢先反击}\n该生物的反击将会在被攻击前进行",
+	"core.bonus.FLYING.description.bonusSubtype.movementTeleporting" : "{航送}\n该生物可以传送到任一一格,无视战场障碍。",
 	"core.bonus.FLYING.description": "{飞行能力}\n以飞行的方式移动(无视障碍)",
 	"core.bonus.FREE_SHOOTING.description": "{近身射击}\n能在近战范围内进行射击",
 	"core.bonus.GARGOYLE.description": "{石像鬼属性}\n不能被复活或治疗",
-	"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "{减少伤害 (${val}%)}\n减少从远程和近战中遭受的物理伤害",
+	"core.bonus.GENERAL_DAMAGE_REDUCTION.description.bonusSubtype.damageTypeMelee" : "{减少伤害(${val}%)}\n减少遭受的近战物理伤害${val}%",
+	"core.bonus.GENERAL_DAMAGE_REDUCTION.description.bonusSubtype.damageTypeRanged" : "{减少伤害(${val}%)}\n减少遭受的远程物理伤害${val}%",
+	"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "{减少伤害(${val}%)}\n减少遭受的远程和近战物理伤害${val}%",
 	"core.bonus.HATE.description": "{憎恨${subtype.creature}}\n对${subtype.creature}造成额外${val}%伤害",
 	"core.bonus.HEALER.description": "{治疗者}\n可以治疗友军单位",
 	"core.bonus.HP_REGENERATION.description": "{再生}\n每回合恢复${val}点生命值",
 	"core.bonus.INVINCIBLE.description": "{无敌}\n不受任何效果影响",
 	"core.bonus.JOUSTING.description": "{勇士冲锋}\n每移动一格 +${val}%伤害",
-	"core.bonus.KING.description": "{王牌}\n受${val}级或更高级屠戮成性影响",
+	"core.bonus.KING.description.2" : "{中级王牌}\n受中级屠戮成性法术影响的单位会对其造成额外伤害",
+	"core.bonus.KING.description.3" : "{高级王牌}\n受高级屠戮成性法术影响的单位会对其造成额外伤害",
+	"core.bonus.KING.description": "{王牌}\n受屠戮成性法术影响的单位会对其造成额外伤害",
 	"core.bonus.LEVEL_SPELL_IMMUNITY.description": "{免疫1-${val}级魔法}\n免疫1-${val}级的魔法",
 	"core.bonus.LIFE_DRAIN.description": "{吸取生命 (${val}%)}\n吸取${val}%伤害回复自身",
 	"core.bonus.LIMITED_SHOOTING_RANGE.description": "{射程限制}\n无法瞄准${val}格以外的单位",
@@ -694,7 +712,6 @@
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.water": "{水系免疫}\n此单位免疫所有水系魔法",
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description": "{魔法免疫}\n此单位免疫所有魔法",
 	"core.bonus.SUMMON_GUARDIANS.description": "{召唤守卫}\n战斗开始时召唤${subtype.creature}(${val}%)",
-	"core.bonus.SYNERGY_TARGET.description": "{协同攻击}\n生物受到协助攻击的影响",
 	"core.bonus.THREE_HEADED_ATTACK.description": "{半环攻击}\n攻击三格邻接单位",
 	"core.bonus.TRANSMUTATION.description": "{变形术}\n${val}%机会将被攻击单位变成其他生物",
 	"core.bonus.TWO_HEX_ATTACK_BREATH.description": "{吐息}\n吐息攻击(2格范围)",
@@ -722,5 +739,16 @@
 	"spell.core.strongholdMoat.name" : "栅栏",
 	"spell.core.strongholdMoatTrigger.name" : "栅栏",
 	"spell.core.summonDemons.name" : "召唤恶鬼",
-	"spell.core.towerMoat.name" : "埋设地雷"
+	"spell.core.towerMoat.name" : "埋设地雷",
+
+	"spell.core.stoneGaze.description.none" : "{石化凝视}\n\n目标单位被石化,三回合内无法行动。当其被攻击时受到50%的伤害并解除石化效果。",
+	"spell.core.poison.description.none" : "{中毒}\n\n目标生物中毒时,它最大生命值每回合减少10%,持续三回合。三回合后中毒解除,但减少的最大生命值不会恢复。",
+	"spell.core.bind.description.none" : "{缠绕}\n\n目标单位被束缚在原地,无法移动,效果持续到束缚它的部队离开或死亡。",
+	"spell.core.disease.description.none" : "{染病}\n\n目标有生命的部队染上疾病,其攻击力和防御力减少两点,持续三回合。",
+	"spell.core.paralyze.description.none" : "{麻痹}\n\n目标单位被麻痹,在接下来的战斗中跳过它的当前回合和接下来的两个回合,受到攻击后解除。被麻痹的生物将会受到全额攻击伤害,但其反击只会造成四分之一的伤害。",
+	"spell.core.age.description.none" : "{老化}\n\n老化减少目标部队50%的最大生命值,持续三回合。",
+	"spell.core.deathCloud.description.none" : "{死亡之云}\n\n死亡之云除了对远程攻击的格子中的目标造成正常伤害之外,还会对相邻6个格子范围内的所有有生命的生物造成伤害。",
+	"spell.core.thunderbolt.description.none" : "{闪电攻击}\n\n此部队攻击时,有20%的几率在敌人反击前进行一次闪电打击。如触发,闪电打击将造成等同于10倍雷鸟攻击者数量的伤害。",
+	"spell.core.dispelHelpful.description.none" : "{驱散有益法术}\n\n移除目标单位上所有的有益法术效果",
+	"spell.core.acidBreath.description.none" : "{酸液攻击}\n\n龙息减少目标部队3点防御力,且有20%的几率造成额外伤害,每个攻击生物会造成25点额外伤害。"
 }

+ 5 - 3
Mods/vcmi/Content/config/czech.json

@@ -345,6 +345,8 @@
 	"vcmi.adventureOptions.mapScrollSpeed6.help" : "Nastavit posouvání mapy na okamžité",
 	"vcmi.adventureOptions.hideBackground.hover" : "Skrýt pozadí",
 	"vcmi.adventureOptions.hideBackground.help" : "{Skrýt pozadí}\n\nSkryje mapu dobrodružství na pozadí a místo ní zobrazí texturu.",
+	"vcmi.adventureOptions.minimapShowHeroes.hover" : "Zobrazit hrdiny na minimapě",
+	"vcmi.adventureOptions.minimapShowHeroes.help" : "{Zobrazit hrdiny na minimapě}\n\nZobrazí dodatečnou ikonu pro všechny hrdiny viditelné na minimapě, aby byli lépe rozpoznatelní na velkých mapách",
 
 	"vcmi.battleOptions.queueSizeLabel.hover" : "Zobrazit frontu pořadí tahů",
 	"vcmi.battleOptions.queueSizeNoneButton.hover" : "VYPNUTO",
@@ -632,6 +634,9 @@
 	"creatures.core.devil.bonus.decreaseLuck" : "{Snižuje štěstí nepřátel}\nĎáblové a arciďáblové snižují štěstí nepřátel o 1 bod",
 	"creatures.core.boneDragon.bonus.decreaseMorale" : "{Snižuje morálku nepřátel}\nKostění a přizrační draci snižují morálku nepřátel o 1 bod",
 	"creatures.core.marksman.bonus.extraAttack" : "{Střílí dvakrát}\nTato jednotka může vystřelit dvakrát za kolo",
+	"creatures.core.azureDragon.bonus.fearful" : "{Strach}\nVyvolává strach u nepřátelské jednotky",
+	"creatures.core.azureDragon.bonus.fearless" : "{Nebojácnost}\nImunní vůči schopnosti Strach",
+
 
 	"core.bonus.ADDITIONAL_ATTACK.description" : "{Dvojitý útok}\nÚtočí dvakrát", // TODO: alternative descriptions for melee/ranged effect range
 	"core.bonus.ADDITIONAL_RETALIATION.description" : "{Další odvetné útoky}\nMůže odvetně zaútočit ${val} krát navíc",
@@ -652,8 +657,6 @@
 	"core.bonus.ENCHANTER.description" : "{Zaklínač}\nMůže každé kolo sesílat masové kouzlo ${subtype.spell}",
 	"core.bonus.ENEMY_ATTACK_REDUCTION.description" : "{Ignorování útoku (${val}%)}\nPři útoku je ignorováno ${val}% útočníkovy síly",
 	"core.bonus.ENEMY_DEFENCE_REDUCTION.description" : "{Ignorování obrany (${val}%)}\nPří útoku nebude bráno v potaz ${val}% bodů obrany obránce",
-	"core.bonus.FEAR.description" : "{Strach}\nVyvolává strach u nepřátelské jednotky",
-	"core.bonus.FEARLESS.description" : "{Nebojácnost}\nImunní vůči schopnosti Strach",
 	"core.bonus.FEROCITY.description" : "{Zuřivost}\nÚtočí ${val} krát navíc, pokud někoho zabije",
 	"core.bonus.FIRE_SHIELD.description" : "{Ohnivý štít (${val}%)}\nOdrazí část zranění při útoku z blízka",
 	"core.bonus.FIRST_STRIKE.description.bonusSubtype.damageTypeMelee" : "{První úder}\nJednotka zasazuje protiúder dříve, než je zasažena v boji na blízko",
@@ -715,7 +718,6 @@
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description" : "{Imunita vůči kouzlům}\nJednotka je imunní vůči všem kouzlům.",
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.water" : "{Vodní imunita}\nJednotka je imunní vůči všem kouzlům magie vody.",
 	"core.bonus.SUMMON_GUARDIANS.description" : "{Přivolání ochránců}\nNa začátku bitvy přivolá ${subtype.creature} (${val}%)",
-	"core.bonus.SYNERGY_TARGET.description" : "{Synergizovatelný}\nTato jednotka je náchylná k synergickým efektům",
 	"core.bonus.THREE_HEADED_ATTACK.description" : "{Tříhlavý útok}\nÚtočí na tři sousední jednotky",
 	"core.bonus.TRANSMUTATION.description" : "{Transmutace}\n${val}% šance na přeměnu napadené jednotky na jiný typ",
 	"core.bonus.TWO_HEX_ATTACK_BREATH.description" : "{Dech}\nÚtok dechem (dosah 2 polí)",

+ 3 - 3
Mods/vcmi/Content/config/english.json

@@ -634,6 +634,8 @@
 	"creatures.core.devil.bonus.decreaseLuck" : "{Reduces enemy luck}\nDevils and Archdevils reduce enemy luck by one",
 	"creatures.core.boneDragon.bonus.decreaseMorale" : "{Reduces enemy morale}\nBone Dragons and Ghost Dragons lower the morale of enemy units by one",
 	"creatures.core.marksman.bonus.extraAttack" : "{Shoots twice}\nThis unit can shoot twice",
+	"creatures.core.azureDragon.bonus.fearful" : "{Fear}\nEnemy units have a 10% chance of freezing in fear",
+	"creatures.core.azureDragon.bonus.fearless" : "{Fearless}\nImmune to Fear ability",
 
 	"core.bonus.ADDITIONAL_ATTACK.description" : "{Additional attacks}\nUnit can attack an additional {$val} times", // TODO: alternative descriptions for melee/ranged effect range
 	"core.bonus.ADDITIONAL_RETALIATION.description" : "{Additional retaliations}\nUnit can retaliate ${val} extra times",
@@ -654,8 +656,6 @@
 	"core.bonus.ENCHANTER.description" : "{Enchanter}\nCan cast ${subtype.spell} every turn",
 	"core.bonus.ENEMY_ATTACK_REDUCTION.description" : "{Ignore Attack (${val}%) }\nWhen being attacked, ${val}% of the attacker's attack is ignored",
 	"core.bonus.ENEMY_DEFENCE_REDUCTION.description" : "{Ignore Defense (${val}%) }\nWhen attacking, ${val}% of the defender's defense is ignored",
-	"core.bonus.FEAR.description" : "{Fear}\nEnemy units have a 10% chance of freezing in fear",
-	"core.bonus.FEARLESS.description" : "{Fearless}\nImmune to Fear ability",
 	"core.bonus.FEROCITY.description" : "{Ferocity}\nAttacks ${val} additional times if killed anybody",
 	"core.bonus.FIRE_SHIELD.description" : "{Fire Shield (${val}%) }\nThe unit reflects ${val} of the melee damage received",
 	"core.bonus.FIRST_STRIKE.description.bonusSubtype.damageTypeMelee" : "{First Strike}\nThe unit retaliates before being attacked in melee",
@@ -699,6 +699,7 @@
 	"core.bonus.REVENGE.description" : "{Revenge}\nDeals extra damage based on attacker's lost health in battle",
 	"core.bonus.SHOOTER.description" : "{Shoots}\nThis unit can use its ammo to perform ranged attacks",
 	"core.bonus.SHOOTS_ALL_ADJACENT.description" : "{Shoot all around}\nThis unit’s ranged attacks strike all targets in a small area",
+	"core.bonus.SKELETON_TRANSFORMER_TARGET.description" : "{Skeleton Transformation}\nThe Skeleton Transformer will turn this unit into a ${subtype.creature}",
 	"core.bonus.SOUL_STEAL.description" : "{Soul Steal}\nGains ${val} new creatures for each enemy killed",
 	"core.bonus.SPELL_AFTER_ATTACK.description" : "{Cast After Attack}\nHas a ${val}% chance to cast ${subtype.spell} after it attacks",
 	"core.bonus.SPELL_BEFORE_ATTACK.description" : "{Cast Before Attack}\nHas a ${val}% chance to cast ${subtype.spell} before it attacks",
@@ -717,7 +718,6 @@
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.fire"  : "{Immune to Fire Magic}\nImmune to all spells from the school of Fire Magic",
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.water" : "{Immune to Water Magic}\nImmune to all spells from the school of Water Magic",
 	"core.bonus.SUMMON_GUARDIANS.description" : "{Summon guardians}\nAt the start of battle summons ${subtype.creature} (${val}%)",
-	"core.bonus.SYNERGY_TARGET.description" : "{Synergizable}\nThis creature is vulnerable to synergy effect",
 	"core.bonus.THREE_HEADED_ATTACK.description" : "{Three-headed attack}\nAttacks three adjacent units",
 	"core.bonus.TRANSMUTATION.description" : "{Transmutation}\n${val}% chance to transform attacked unit to a different type",
 	"core.bonus.TWO_HEX_ATTACK_BREATH.description" : "{Breath Attack}\nAttacks by this unit will also hit any unit positioned immediately behind the target",

+ 3 - 3
Mods/vcmi/Content/config/french.json

@@ -180,6 +180,9 @@
 	"vcmi.stackExperience.rank.8" : "Élite",
 	"vcmi.stackExperience.rank.9" : "Maître",
 	"vcmi.stackExperience.rank.10" : "As",
+	
+	"creatures.core.azureDragon.bonus.fearful" : "{Peur}\nProvoque la peur sur une pile ennemie",
+	"creatures.core.azureDragon.bonus.fearless" : "{Intrépide}\nImmunité à la peur",
 
 	"core.bonus.ADDITIONAL_ATTACK.description": "{Double frappe}\nAttaque deux fois",
 	"core.bonus.ADDITIONAL_RETALIATION.description": "{Représailles supplémentaires}\nPeut riposter ${val} fois de plus",
@@ -198,8 +201,6 @@
 	"core.bonus.ENCHANTED.description": "{Enchanté}\nAffecté par ${subtype.spell} permanent",
 	"core.bonus.ENCHANTER.description": "{Enchanteur}\nPeut lancer en masse ${subtype.spell} à chaque tour",
 	"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "{Ignorer la défense (${val}%)}\nLors de l'attaque, ${val}% de la défense du défenseur est ignorée",
-	"core.bonus.FEAR.description": "{Peur}\nProvoque la peur sur une pile ennemie",
-	"core.bonus.FEARLESS.description": "{Intrépide}\nImmunité à la peur",
 	"core.bonus.FIRE_SHIELD.description": "{Bouclier de feu (${val}%)}\nReflète une partie des dégâts de mêlée",
 	"core.bonus.FIRST_STRIKE.description": "{Premier coup}\nCette créature riposte avant d'être attaquée",
 	"core.bonus.FLYING.description": "{Vol}\nVole en se déplaçant (ignore les obstacles)",
@@ -240,7 +241,6 @@
 	"core.bonus.SPELL_LIKE_ATTACK.description": "{Attaque semblable à un sort}\nAttaque avec ${subtype.spell}",
 	"core.bonus.SPELL_RESISTANCE_AURA.description": "{Aura de résistance}\nLes piles à proximité obtiennent ${val}% de résistance magique",
 	"core.bonus.SUMMON_GUARDIANS.description": "{Invoquer des gardiens}\nAu début de la bataille, invoque ${subtype.creature} (${val}%)",
-	"core.bonus.SYNERGY_TARGET.description": "{Synergique}\nCette créature est vulnérable à l'effet de synergie",
 	"core.bonus.THREE_HEADED_ATTACK.description": "{Attaque à trois têtes}\nAttaque trois unités adjacentes",
 	"core.bonus.TRANSMUTATION.description": "{Transmutation}\n${val}% de chances de transformer l'unité attaquée en un type différent",
 	"core.bonus.TWO_HEX_ATTACK_BREATH.description": "{Souffle}\nAttaque de souffle (portée de 2 hexagones)",

+ 3 - 3
Mods/vcmi/Content/config/german.json

@@ -619,6 +619,9 @@
 	
 	"mapObject.core.hillFort.object.description" : "Aufwertungen von Kreaturen. Die Stufen 1 - 4 sind billiger als in der zugehörigen Stadt.",
 
+	"creatures.core.azureDragon.bonus.fearful" : "{Furcht}\nVerursacht Furcht bei einem gegnerischen Stapel",
+	"creatures.core.azureDragon.bonus.fearless" : "{Furchtlos}\nimmun gegen die Fähigkeit Furcht",
+
 	"core.bonus.ADDITIONAL_ATTACK.description" : "{Doppelschlag}\nGreift zweimal an",
 	"core.bonus.ADDITIONAL_RETALIATION.description" : "{Zusätzliche Vergeltungsmaßnahmen}\nKann ${val} zusätzliche Male vergelten",
 	"core.bonus.ATTACKS_ALL_ADJACENT.description" : "{Rundum angreifen}\nGreift alle benachbarten Gegner an",
@@ -638,8 +641,6 @@
 	"core.bonus.ENCHANTER.description" : "{Verzauberer}\nKann jede Runde eine Masse von ${subtype.spell} zaubern",
 	"core.bonus.ENEMY_ATTACK_REDUCTION.description" : "{Angriff ignorieren (${val}%)}\nBei Angriff, wird ${val}% des Angreifers ignoriert.",
 	"core.bonus.ENEMY_DEFENCE_REDUCTION.description" : "{Ignoriere Verteidigung (${val}%)}\nIgnoriert einen Teil der Verteidigung für den Angriff",
-	"core.bonus.FEARLESS.description" : "{Furchtlos}\nimmun gegen die Fähigkeit Furcht",
-	"core.bonus.FEAR.description" : "{Furcht}\nVerursacht Furcht bei einem gegnerischen Stapel",
 	"core.bonus.FEROCITY.description" : "{Wildheit}\nGreift ${val} zusätzliche Male an, wenn jemand getötet wird",
 	"core.bonus.FIRE_SHIELD.description" : "{Feuerschild (${val}%)}\nReflektiert einen Teil des Nahkampfschadens",
 	"core.bonus.FIRST_STRIKE.description" : "{Erstschlag}\nDiese Kreatur greift zuerst an, anstatt zu vergelten",
@@ -694,7 +695,6 @@
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.water" : "{Wasser-Immunität}\nImmunität gegen Zauber der Wasser-Schule",
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description" : "{Zauber-Immunität}\nImmunität gegen alle Zauber-Schulen",
 	"core.bonus.SUMMON_GUARDIANS.description" : "{Wächter beschwören}\nBeschwört bei Kampfbeginn ${subtype.creature} (${val}%)",
-	"core.bonus.SYNERGY_TARGET.description" : "{Synergierbar}\nDiese Kreatur ist anfällig für Synergieeffekte",
 	"core.bonus.THREE_HEADED_ATTACK.description" : "{Dreiköpfiger Angriff}\nGreift drei benachbarte Einheiten an",
 	"core.bonus.TRANSMUTATION.description" : "{Transmutation}\n${val}% Chance, angegriffene Einheit in einen anderen Typ zu verwandeln",
 	"core.bonus.TWO_HEX_ATTACK_BREATH.description" : "{Atem}\nAtem-Angriff (2-Hex-Bereich)",

+ 3 - 3
Mods/vcmi/Content/config/hungarian.json

@@ -606,6 +606,9 @@
 	
 	"mapObject.core.hillFort.object.description" : "Lények fejlesztése. Az 1-4. szint olcsóbb, mint az adott városban.",
 
+	"creatures.core.azureDragon.bonus.fearful" : "{Félelem}\nFélelmet kelt az ellenséges egységekben",
+	"creatures.core.azureDragon.bonus.fearless" : "{Félelem nélküli}\nImmunis a félelem képességre",
+
 	"core.bonus.ADDITIONAL_ATTACK.description" : "{Dupla csapás}\nKétszer támad",
 	"core.bonus.ADDITIONAL_RETALIATION.description" : "{További visszatámadások}\nMég ${val} alkalommal visszatámadhat",
 	"core.bonus.ATTACKS_ALL_ADJACENT.description" : "{Mindenirányú támadás}\nMinden szomszédos ellenséget támad",
@@ -625,8 +628,6 @@
 	"core.bonus.ENCHANTER.description" : "{Varázsló}\nTömeges ${subtype.spell} varázslatot használ minden körben",
 	"core.bonus.ENEMY_ATTACK_REDUCTION.description" : "{Támadás figyelmen kívül hagyása (${val}%)}\nTámadáskor az ellenség támadásának ${val}%-át figyelmen kívül hagyja",
 	"core.bonus.ENEMY_DEFENCE_REDUCTION.description" : "{Védelem figyelmen kívül hagyása (${val}%)}\nTámadáskor az ellenség védekezésének ${val}%-át figyelmen kívül hagyja",
-	"core.bonus.FEARLESS.description" : "{Félelem nélküli}\nImmunis a félelem képességre",
-	"core.bonus.FEAR.description" : "{Félelem}\nFélelmet kelt az ellenséges egységekben",
 	"core.bonus.FEROCITY.description" : "{Vadság}\n${val} további alkalommal támad, ha bárkit megölt",
 	"core.bonus.FIRE_SHIELD.description" : "{Tűzpajzs (${val}%)}\nVisszaver egy részét a közelharci sebzésnek",
 	"core.bonus.FIRST_STRIKE.description" : "{Első csapás}\nEz a lény még a támadás előtt visszatámad",
@@ -681,7 +682,6 @@
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description" : "{Varázslatimmunitás}\nEz az egység immunis minden varázslattal szemben",
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.water" : "{Víz immunitás}\nEz az egység immunis minden víz mágia iskolájához tartozó varázslattal szemben",
 	"core.bonus.SUMMON_GUARDIANS.description" : "{Őrök idézése}\nA csata kezdetén idézi a(z) ${subtype.creature} lényeket (${val}%)",
-	"core.bonus.SYNERGY_TARGET.description" : "{Szinkronizálható}\nEz a lény érzékeny a szinergiahatásokra",
 	"core.bonus.THREE_HEADED_ATTACK.description" : "{Háromfejű támadás}\nHárom szomszédos egységet támad",
 	"core.bonus.TRANSMUTATION.description" : "{Átalakítás}\n${val}% eséllyel az ellenfelet más egységtípussá alakítja",
 	"core.bonus.TWO_HEX_ATTACK_BREATH.description" : "{Lélegzet}\nLélegzési támadás (2 hatszögnyi távolság)",

+ 3 - 3
Mods/vcmi/Content/config/italian.json

@@ -607,6 +607,9 @@
 	
 	"mapObject.core.hillFort.object.description" : "Aggiorna le creature. I livelli 1 - 4 sono meno costosi rispetto alla città associata.",
 
+	"creatures.core.azureDragon.bonus.fearful" : "{Paura}\nProvoca paura su una pila nemica",
+	"creatures.core.azureDragon.bonus.fearless" : "{Impavido}\nImmune all'abilità Paura",
+
 	"core.bonus.ADDITIONAL_ATTACK.description" : "{Doppio colpo}\nAttacca due volte",
 	"core.bonus.ADDITIONAL_RETALIATION.description" : "{Ritorsioni aggiuntive}\nPuò contrattaccare ${val} volte in più",
 	"core.bonus.ATTACKS_ALL_ADJACENT.description" : "{Attacco a 360°}\nAttacca tutti i nemici adiacenti",
@@ -626,8 +629,6 @@
 	"core.bonus.ENCHANTER.description" : "{Incantatore}\nPuò lanciare l'incantesimo ${subtype.spell} ogni turno",
 	"core.bonus.ENEMY_ATTACK_REDUCTION.description" : "{Ignora attacco (${val}%)}\nQuando viene attaccata, ignora il ${val}% dell'attacco dell'avversario",
 	"core.bonus.ENEMY_DEFENCE_REDUCTION.description" : "{Ignora difesa (${val}%)}\nQuando attacca, ignora il ${val}% della difesa dell'avversario",
-	"core.bonus.FEARLESS.description" : "{Impavido}\nImmune all'abilità Paura",
-	"core.bonus.FEAR.description" : "{Paura}\nProvoca paura su una pila nemica",
 	"core.bonus.FEROCITY.description" : "{Ferocia}\nAttacca ${val} volte aggiuntive se uccide qualcuno",
 	"core.bonus.FIRE_SHIELD.description" : "{Scudo di fuoco (${val}%)}\nRiflette una parte dei danni da mischia",
 	"core.bonus.FIRST_STRIKE.description" : "{Primo colpo}\nQuesta creatura contrattacca prima di essere attaccata",
@@ -682,7 +683,6 @@
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description" : "{Immunità agli incantesimi}\nQuesta unità è immune a tutti gli incantesimi",
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.water" : "{Immunità all'acqua}\nQuesta unità è immune a tutti gli incantesimi della scuola dell'Acqua",
 	"core.bonus.SUMMON_GUARDIANS.description" : "{Evoca guardiani}\nAll'inizio della battaglia evoca ${subtype.creature} (${val}%)",
-	"core.bonus.SYNERGY_TARGET.description" : "{Sinergizzabile}\nQuesta creatura è vulnerabile all'effetto sinergico",
 	"core.bonus.THREE_HEADED_ATTACK.description" : "{Attacco a tre teste}\nAttacca tre unità adiacenti",
 	"core.bonus.TRANSMUTATION.description" : "{Trasmutazione}\n${val}% di possibilità di trasformare l'unità attaccata in un altro tipo",
 	"core.bonus.TWO_HEX_ATTACK_BREATH.description" : "{Soffio}\nAttacco a soffio (raggio di 2 esagoni)",

+ 3 - 3
Mods/vcmi/Content/config/polish.json

@@ -629,6 +629,9 @@
 	
 	"mapObject.core.hillFort.object.description" : "Ulepsza jednostki. Koszt ulepszenia dla poziomów 1 - 4 jest bardziej korzystny niż w mieście.",
 
+	"creatures.core.azureDragon.bonus.fearful" : "{Strach}\nWzbudza strach na wrogim stworzeniu",
+	"creatures.core.azureDragon.bonus.fearless" : "{Nieustraszony}\nOdporny na strach",
+
 	"core.bonus.ADDITIONAL_ATTACK.description" : "{Podwójne Uderzenie}\nAtakuje dwa razy",
 	"core.bonus.ADDITIONAL_RETALIATION.description" : "{Dodatkowy odwet}\n${val} dodatkowy kontratak",
 	"core.bonus.ATTACKS_ALL_ADJACENT.description" : "{Obrotowy atak}\nAtakuje wszystkich sąsiadujących wrogów",
@@ -648,8 +651,6 @@
 	"core.bonus.ENCHANTER.description" : "{Czarodziej}\nRzuca czar ${subtype.spell}",
 	"core.bonus.ENEMY_ATTACK_REDUCTION.description" : "{Ignoruje Atak (${val}%)}\nPrzy zostaniu zaatakowanym ignoruje ${val}% ataku wroga",
 	"core.bonus.ENEMY_DEFENCE_REDUCTION.description" : "{Osłabienie Obrony (${val}%)}\nOsłabia obronę wroga podczas ataku",
-	"core.bonus.FEARLESS.description" : "{Nieustraszony}\nOdporny na strach",
-	"core.bonus.FEAR.description" : "{Strach}\nWzbudza strach na wrogim stworzeniu",
 	"core.bonus.FEROCITY.description" : "{Dzikość}\nDodatkowe ${val} ataków jeżeli zabito kogokolwiek",
 	"core.bonus.FIRE_SHIELD.description" : "{Ognista tarcza (${val}%)}\nOdbija część obrażeń z walki wręcz",
 	"core.bonus.FIRST_STRIKE.description" : "{Pierwsze Uderzenie}\nTo stworzenie atakuje pierwsze w ramach kontrataku",
@@ -695,7 +696,6 @@
 	"core.bonus.SPELL_LIKE_ATTACK.description" : "{Atak czaropodobny}\nAtakuje z użyciem ${subtype.spell}",
 	"core.bonus.SPELL_RESISTANCE_AURA.description" : "{O ${val}% słabszy}\nefekt czarów dla pobl. stwor.",
 	"core.bonus.SUMMON_GUARDIANS.description" : "{Wzywa na początku walki}\n${subtype.creature} (${val}%)",
-	"core.bonus.SYNERGY_TARGET.description" : "{Synergiczny}\nTo stworzenie jest podatne na efekt synergii",
 	"core.bonus.THREE_HEADED_ATTACK.description" : "{Atak trzema głowami}\nAtakuje trzy sąsiadujące jednostki",
 	"core.bonus.TRANSMUTATION.description" : "{Transmutacja}\n${val}% szans aby przetransformować atakowaną jednostkę na inny typ",
 	"core.bonus.TWO_HEX_ATTACK_BREATH.description" : "{Zionięcie}\nAtak zionący (zasięg 2 heksów)",

+ 2 - 3
Mods/vcmi/Content/config/portuguese.json

@@ -632,6 +632,8 @@
 	"creatures.core.devil.bonus.decreaseLuck" : "{Reduz a sorte do inimigo}\nDiabos e Arquidiabos reduzem a sorte do inimigo em um",
 	"creatures.core.boneDragon.bonus.decreaseMorale" : "{Reduz a moral do inimigo}\nDragões Esqueletos e Dragões Fantasmas diminuem a moral das unidades inimigas em um",
 	"creatures.core.marksman.bonus.extraAttack" : "{Atira duas vezes}\nEsta unidade pode atirar duas vezes",
+	"creatures.core.azureDragon.bonus.fearful" : "{Medo}\nCausa Medo em uma pilha inimiga",
+	"creatures.core.azureDragon.bonus.fearless" : "{Destemido}\nImune à habilidade de Medo",
 
 	"core.bonus.ADDITIONAL_ATTACK.description" : "{Ataque Duplo}\nAtaca duas vezes",
 	"core.bonus.ADDITIONAL_RETALIATION.description" : "{Contra-ataques Adicionais}\nPode contra-atacar ${val} vezes extras",
@@ -652,8 +654,6 @@
 	"core.bonus.ENCHANTER.description" : "{Encantador}\nPode lançar ${subtype.spell} em massa a cada turno",
 	"core.bonus.ENEMY_ATTACK_REDUCTION.description" : "{Ignorar Ataque (${val}%)}\nAo ser atacado, ${val}% do ataque do agressor é ignorado",
 	"core.bonus.ENEMY_DEFENCE_REDUCTION.description" : "{Ignorar Defesa (${val}%)}\nAo atacar, ${val}% da defesa do defensor é ignorada",
-	"core.bonus.FEAR.description" : "{Medo}\nCausa Medo em uma pilha inimiga",
-	"core.bonus.FEARLESS.description" : "{Destemido}\nImune à habilidade de Medo",
 	"core.bonus.FEROCITY.description" : "{Ferocidade}\nAtaca ${val} vezes adicionais se matar alguém",
 	"core.bonus.FIRE_SHIELD.description" : "{Escudo de Fogo (${val}%)}\nReflete parte do dano corpo a corpo",
 	"core.bonus.FIRST_STRIKE.description.bonusSubtype.damageTypeMelee" : "{Primeiro Ataque}\nA unidade contra-atacará antes de ser atacada em combate corpo a corpo",
@@ -715,7 +715,6 @@
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description" : "{Imunidade a Feitiços}\nEsta unidade é imune a todos os feitiços",
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.water" : "{Imunidade a Água}\nEsta unidade é imune a todos os feitiços da escola de Água",
 	"core.bonus.SUMMON_GUARDIANS.description" : "{Invocar Guardas}\nNo início da batalha, invoca ${subtype.creature} (${val}%)",
-	"core.bonus.SYNERGY_TARGET.description" : "{Alvo Sinergético}\nEsta criatura é vulnerável ao efeito de sinergia",
 	"core.bonus.THREE_HEADED_ATTACK.description" : "{Ataque das Três Cabeças}\nAtaca três unidades adjacentes",
 	"core.bonus.TRANSMUTATION.description" : "{Transmutação}\n${val}% de chance de transformar a unidade atacada em um tipo diferente",
 	"core.bonus.TWO_HEX_ATTACK_BREATH.description" : "{Sopro}\nAtaque de Sopro (alcança 2 hexágonos)",

+ 4 - 3
Mods/vcmi/Content/config/russian.json

@@ -623,8 +623,12 @@
 	"core.seerhut.quest.reachDate.visit.3" : "Закрыто до %s.",
 	"core.seerhut.quest.reachDate.visit.4" : "Закрыто до %s.",
 	"core.seerhut.quest.reachDate.visit.5" : "Закрыто до %s.",
+
 	"mapObject.core.hillFort.object.description" : "Улучшить существ. Уровни 1-4 стоят дешевле, чем в соответствующем городе.",
 
+	"creatures.core.azureDragon.bonus.fearful" : "{Страх}\nЗаставляет вражеских существ цепенеть от страха",
+	"creatures.core.azureDragon.bonus.fearless" : "{Бесстрашный}\nИммунитет к страху",
+
 	"core.bonus.ADDITIONAL_ATTACK.description" : "{Двойной удар}\nБьет дважды",
 	"core.bonus.ADDITIONAL_RETALIATION.description" : "{Дополнительные ответные атаки}\nОтвечает на атаку дополнительно ${val} раз",
 	"core.bonus.ATTACKS_ALL_ADJACENT.description" : "{Атака вокруг}\nАтакует все окружающие отряды",
@@ -644,8 +648,6 @@
 	"core.bonus.ENCHANTER.description" : "{Заклинатель (массовое)}\nМожет применять массовое ${subtype.spell} каждый ход",
 	"core.bonus.ENEMY_ATTACK_REDUCTION.description" : "{Игнорирует атаку (${val}%)}\nПри атаке ${val}%, атаки атакующего игнорируются",
 	"core.bonus.ENEMY_DEFENCE_REDUCTION.description" : "{Игнорирует броню (${val}%)}\nИгнорирует часть Защиты при атаке",
-	"core.bonus.FEARLESS.description" : "{Бесстрашный}\nИммунитет к страху",
-	"core.bonus.FEAR.description" : "{Страх}\nЗаставляет вражеских существ цепенеть от страха",
 	"core.bonus.FEROCITY.description" : "{Свирепость}\nАтакует дополнительно ${val} раз, если кого-то убивает",
 	"core.bonus.FIRE_SHIELD.description" : "{Огненный щит (${val}%)}\nНаносит огнем часть полученного урона",
 	"core.bonus.FIRST_STRIKE.description" : "{Первый удар}\nСущество бьет первым даже при ответной атаке",
@@ -700,7 +702,6 @@
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.fire" : "{Иммунитет к Огню}\nОтряд невосприимчив ко всем заклинаниям школы Огня",
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.water" : "{Иммунитет к Воде}\nОтряд невосприимчив ко всем заклинаниям школы Воды",
 	"core.bonus.SUMMON_GUARDIANS.description" : "{Призыв стражей}\nВ начале битвы призывает ${subtype.creature} (${val}%)",
-	"core.bonus.SYNERGY_TARGET.description" : "{Синергия}\nСущество уязвимо к эффектам синергии",
 	"core.bonus.THREE_HEADED_ATTACK.description" : "{Трехсторонняя атака}\nАтака трех отрядов с передней стороны",
 	"core.bonus.TRANSMUTATION.description" : "{Трансмутатор}\nШанс ${val}% превратить атакующий отряд в отряд другого типа",
 	"core.bonus.TWO_HEX_ATTACK_BREATH.description" : "{Дыхание}\nАтака дыханием (радиус в 2 гекса)",

+ 3 - 3
Mods/vcmi/Content/config/spanish.json

@@ -337,6 +337,9 @@
 	"vcmi.stackExperience.rank.9" : "Maestro",
 	"vcmi.stackExperience.rank.10" : "As",
 
+	"creatures.core.azureDragon.bonus.fearful" : "{Miedo}\nCausa miedo a un grupo enemigo",
+	"creatures.core.azureDragon.bonus.fearless" : "{Inmune al miedo}\nInmune a la habilidad de miedo",
+
 	"core.bonus.ADDITIONAL_ATTACK.description": "{Doble Ataque}\nAtaca dos veces",
 	"core.bonus.ADDITIONAL_RETALIATION.description": "{Contrataques adicionales}\nPuede contratacar ${val} veces adicionales",
 	"core.bonus.ATTACKS_ALL_ADJACENT.description": "{Ataque en todas las direcciones}\nAtaca a todos los enemigos adyacentes",
@@ -355,8 +358,6 @@
 	"core.bonus.ENCHANTER.description": "{Encantador}\nPuede lanzar ${subtype.spell} masivo cada turno",
 	"core.bonus.ENEMY_ATTACK_REDUCTION.description": "{Ignorar ataque (${val}%)}\nAl ser atacado, ${val}% del daño del atacante es ignorado",
 	"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "{Ignorar Defensa (${val}%)}\nIgnora una parte de la defensa al atacar",
-	"core.bonus.FEAR.description": "{Miedo}\nCausa miedo a un grupo enemigo",
-	"core.bonus.FEARLESS.description": "{Inmune al miedo}\nInmune a la habilidad de miedo",
 	"core.bonus.FEROCITY.description": "{Ferocidad}\nAtaca ${val} veces adicionales en caso de eliminar a alguien",
 	"core.bonus.FIRE_SHIELD.description": "{Escudo de Fuego (${val}%)}\nRefleja una parte del daño cuerpo a cuerpo",
 	"core.bonus.FIRST_STRIKE.description": "{Primer Ataque}\nEsta criatura ataca primero en lugar de contratacar",
@@ -399,7 +400,6 @@
 	"core.bonus.SPELL_LIKE_ATTACK.description": "{Ataque similar a hechizo}\nAtaca con ${subtype.spell}",
 	"core.bonus.SPELL_RESISTANCE_AURA.description": "{Aura de resistencia}\nLas unidades cercanas obtienen una resistencia mágica del ${val}%",
 	"core.bonus.SUMMON_GUARDIANS.description": "{Invocar guardianes}\nAl comienzo de la batalla invoca ${subtype.creature} (${val}%)",
-	"core.bonus.SYNERGY_TARGET.description": "{Sinergia}\nEsta criatura es vulnerable al efecto de sinergia",
 	"core.bonus.THREE_HEADED_ATTACK.description": "{Ataque de tres cabezas}\nAtaca a tres unidades adyacentes",
 	"core.bonus.TRANSMUTATION.description": "{Transmutación}\n${val}% de probabilidad de transformar la unidad atacada en otro tipo",
 	"core.bonus.TWO_HEX_ATTACK_BREATH.description": "{Aliento}\nAtaque de aliento (rango de 2 hexágonos)",

+ 4 - 3
Mods/vcmi/Content/config/swedish.json

@@ -345,6 +345,8 @@
 	"vcmi.adventureOptions.mapScrollSpeed6.help"            : "Ställ in kartans rullningshastighet till 'Omedelbar'.",
 	"vcmi.adventureOptions.hideBackground.hover"            : "Dölj bakgrund",
 	"vcmi.adventureOptions.hideBackground.help"             : "{Dölj bakgrund}\n\nDöljer äventyrskartan i bakgrunden och visar en textur istället.",
+	"vcmi.adventureOptions.minimapShowHeroes.hover"         : "Visa hjältar på minikartan",
+	"vcmi.adventureOptions.minimapShowHeroes.help"          : "{Visa hjältar på minikartan}\n\nVisa en extra ikon för alla hjältar på minikartan för att göra dem lättare att urskilja på stora kartor.",
 
 	"vcmi.battleOptions.queueSizeLabel.hover"            : "Visa turordningskö",
 	"vcmi.battleOptions.queueSizeNoneButton.hover"       : "AV",
@@ -632,6 +634,8 @@
 	"creatures.core.devil.bonus.decreaseLuck"            : "{Minskar fiendens lycka}\nDjävular och ärkedjävular minskar fiendens lycka med en poäng.",
 	"creatures.core.boneDragon.bonus.decreaseMorale"     : "{Minskar fiendens moral}\nBen- och spökdrakar minskar fiendens moral med en poäng.",
 	"creatures.core.marksman.bonus.extraAttack"          : "{Skjuter två gånger}\nDenna enhet kan skjuta två gånger per runda.",
+	"creatures.core.azureDragon.bonus.fearful" : "{Rädsla}\nOrsakar rädsla på ett fiendeförband",
+	"creatures.core.azureDragon.bonus.fearless" : "{Orädd}\nImmun mot rädsla.",
 
 	"core.bonus.ADDITIONAL_ATTACK.description" : "{Dubbelslag}\nAttackerar två gånger.", // Att göra: separata beskrivningar för närstrid/fjärrstrid
 	"core.bonus.ADDITIONAL_RETALIATION.description" : "{Ytterligare motattacker}\nKan slå tillbaka ${val} extra gång(er).",
@@ -652,8 +656,6 @@
 	"core.bonus.ENCHANTER.description" : "{Förtrollare}\nKastar mass-${subtype.spell} varje turomgång.",
 	"core.bonus.ENEMY_ATTACK_REDUCTION.description" : "{Avfärda attack (${val}%)}\nIgnorerar ${val}% av angriparens attack.",
 	"core.bonus.ENEMY_DEFENCE_REDUCTION.description" : "{Förbigå försvar (${val}%)}\nAttacker ignorerar ${val}% av fiendens försvar.",
-	"core.bonus.FEAR.description" : "{Rädsla}\nOrsakar rädsla på ett fiendeförband.",
-	"core.bonus.FEARLESS.description" : "{Orädd}\nImmun mot rädsla.",
 	"core.bonus.FEROCITY.description" : "{Vildsint}\n+${val} extra attack(er) om någon dödas.",
 	"core.bonus.FIRE_SHIELD.description" : "{Eldsköld (${val}%)}\nReflekterar en del av närstridsskadorna.",
 	"core.bonus.FIRST_STRIKE.description.bonusSubtype.damageTypeMelee" : "{Första slaget}\nRetalierar innan den träffas i närstrid.",
@@ -715,7 +717,6 @@
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description" : "{Immun mot all magi}\nEnheten är helt immun mot all magi.",
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.water" : "{Immun mot vattenmagi}\nEnheten är helt immun mot all vattenmagi.",
 	"core.bonus.SUMMON_GUARDIANS.description" : "{Åkalla väktare}\nVid strid åkallas: ${subtype.creature} ${val}%.",
-	"core.bonus.SYNERGY_TARGET.description" : "{Synergibar}\nDenna varelse är sårbar för synergieffekt.",
 	"core.bonus.THREE_HEADED_ATTACK.description" : "{Trehövdad attack}\nAttackerar upp till tre enheter framför sig.",
 	"core.bonus.TRANSMUTATION.description" : "{Transmutation}\n${val}% chans att förvandla angripen enhet.",
 	"core.bonus.TWO_HEX_ATTACK_BREATH.description" : "{Dödlig andedräkt}\nAndningsattack (2 rutors räckvidd).",

+ 3 - 3
Mods/vcmi/Content/config/ukrainian.json

@@ -624,6 +624,8 @@
 	"creatures.core.devil.bonus.decreaseLuck" : "{Зменшує ворожу удачу}\nДияволи та Архідияволи зменшують удачу ворога на одиницю",
 	"creatures.core.boneDragon.bonus.decreaseMorale" : "{Знижує ворожий бойовий дух}\nКістяні і Примарні дракони знижують бойовий дух ворожих загонів на одиницю",
 	"creatures.core.marksman.bonus.extraAttack" : "{Стріляє двічі}\nЦей загін може стріляти двічі",
+	"creatures.core.azureDragon.bonus.fearful" : "{Страх}\nВорожі загони мають 10% шанс завмерти від страху",
+	"creatures.core.azureDragon.bonus.fearless" : "{Безстрашний}\nІмунітет до страху",
 
 	"core.bonus.ADDITIONAL_ATTACK.description" : "{Додаткові удари}\nАтакує двічі",
 	"core.bonus.ADDITIONAL_RETALIATION.description" : "{Додаткові відплати}\nЗагін може нанести ${val} додаткових ударів у відповідь",
@@ -644,8 +646,6 @@
 	"core.bonus.ENCHANTER.description" : "{Чарівник}\nМоже застосовувати закляття ${subtype.spell} кожного ходу",
 	"core.bonus.ENEMY_ATTACK_REDUCTION.description" : "{Ігнорує ${val}% атаки}\nПри нападі ігнорується ${val}% атаки нападника",
 	"core.bonus.ENEMY_DEFENCE_REDUCTION.description" : "{Ігнорує ${val}% захисту}\nПри атаці ігнорується ${val}% захисту суперника",
-	"core.bonus.FEAR.description" : "{Страх}\nВорожі загони мають 10% шанс завмерти від страху",
-	"core.bonus.FEARLESS.description" : "{Безстрашний}\nІмунітет до страху",
 	"core.bonus.FEROCITY.description" : "{Лютість}\nЯкщо цей загін вбиває когось, то атакує ${val} разів додатково",
 	"core.bonus.FIRE_SHIELD.description" : "{Вогняний щит (${val}%) }\nЗагін повертає ${val} отриманої шкоди від ближнього бою назад атакуючому",
 	"core.bonus.FIRST_STRIKE.description" : "{Перший удар}\nЗагін завдає удару у відповідь до того, як його атакують",
@@ -689,6 +689,7 @@
 	"core.bonus.REVENGE.description" : "{Помста}\nЗавдає додаткової шкоди залежно від втраченого здоров'я в бою",
 	"core.bonus.SHOOTER.description" : "{Стрілок}\nЦей загін використовує боєприпаси для ведення дальніх атак",
 	"core.bonus.SHOOTS_ALL_ADJACENT.description" : "{Стріляйте по площі}\nДальні атаки цієї істоти вражають всі цілі на невеликій площі",
+	"core.bonus.SKELETON_TRANSFORMER_TARGET.description" : "{Перетворення у скелет}\nМашина скелетів перетворить цей загін на загін ${subtype.creature}",
 	"core.bonus.SOUL_STEAL.description" : "{Викрадення душ}\nОтримує ${val} нових істот за кожного вбитого ворога",
 	"core.bonus.SPELL_AFTER_ATTACK.description" : "{Закляття після атаки}\nЗастосовує ${subtype.spell} з вірогідністю ${val}% після атаки",
 	"core.bonus.SPELL_BEFORE_ATTACK.description" : "{Закляття перед атакою}\nЗастосовує ${subtype.spell} з вірогідністю ${val}% перед атакою",
@@ -707,7 +708,6 @@
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.fire" : "{Імунітет до Вогню}\nНа цей загін не діють жодні закляття школи Вогню",
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.water" : "{Імунітет до Води}\nНа цей загін не діють жодні закляття школи Води",
 	"core.bonus.SUMMON_GUARDIANS.description" : "{Закликає охоронців}\nНа початку бою викликає ${subtype.creature} (${val}%)",
-	"core.bonus.SYNERGY_TARGET.description" : "{Синергізм}\nЦя істота вразлива до ефекту синергії",
 	"core.bonus.THREE_HEADED_ATTACK.description" : "{Триголова атака}\nАтакує до трьох сусідніх загонів",
 	"core.bonus.TRANSMUTATION.description" : "{Трансмутація}\n${val}% шанс перетворити атакованого юніта в інший тип",
 	"core.bonus.TWO_HEX_ATTACK_BREATH.description" : "{Атака подихом}\nАтаки цього загону також вражатимуть загін, розташований позаду цілі",

+ 3 - 3
Mods/vcmi/Content/config/vietnamese.json

@@ -605,6 +605,9 @@
 	
 	"mapObject.core.hillFort.object.description" : "Nâng cấp quân cấp 1 - 4 với chi phí thấp hơn ở trong thành.",
 
+	"creatures.core.azureDragon.bonus.fearful" : "{Sợ hãi}\nGây sợ hãi cho một đạo quân địch",
+	"creatures.core.azureDragon.bonus.fearless" : "{Không sợ}\nKháng lại kỹ năng gây sợ hãi",
+
 	"core.bonus.ADDITIONAL_ATTACK.description" : "{Đánh 2 lần}\nTấn công hai lần",
 	"core.bonus.ADDITIONAL_RETALIATION.description" : "{Phản đòn thêm}\nCó thể phàn đòn thêm ${val} lần",
 	"core.bonus.ATTACKS_ALL_ADJACENT.description" : "{Đánh xung quanh}\nTấn công tất cả kẻ địch xung quanh",
@@ -624,8 +627,6 @@
 	"core.bonus.ENCHANTER.description" : "{Niệm phép}\nCó thể dùng phép mass ${subtype.spell} mỗi lượt",
 	"core.bonus.ENEMY_ATTACK_REDUCTION.description" : "{Giảm tấn công (${val}%)}\nKhi bị tấn công, giảm ${val}% tấn công của kẻ địch",
 	"core.bonus.ENEMY_DEFENCE_REDUCTION.description" : "{Giảm phòng thủ (${val}%)}\nKhi tấn công, giảm ${val}% phòng thủ của kẻ địch",
-	"core.bonus.FEARLESS.description" : "{Không sợ}\nKháng lại kỹ năng gây sợ hãi",
-	"core.bonus.FEAR.description" : "{Sợ hãi}\nGây sợ hãi cho một đạo quân địch",
 	"core.bonus.FEROCITY.description" : "{Hung ác}\nTấn công thêm ${val} lần nữa nếu giết chết kẻ địch",
 	"core.bonus.FIRE_SHIELD.description" : "{Khiên lửa (${val}%)}\nPhản lại một phần sát thương khi cận chiến",
 	"core.bonus.FIRST_STRIKE.description" : "{Đòn đánh phủ đầu}\nĐạo quân này phản đòn trước khi bị tấn công",
@@ -680,7 +681,6 @@
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description" : "{Kháng phép}\nĐơn vị này kháng tất cả phép thuật",
 	"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.water" : "{Kháng phép nước}\nĐơn vị này kháng tất cả phép nước",
 	"core.bonus.SUMMON_GUARDIANS.description" : "{Gọi bảo vệ}\nKhi bắt đầu trận sẽ triệu hồi ${subtype.creature} (${val}%)",
-	"core.bonus.SYNERGY_TARGET.description" : "{Hợp lực}\nQuân này dễ bị ảnh hưởng bởi nhiều hiệu ứng",
 	"core.bonus.THREE_HEADED_ATTACK.description" : "{Ba đầu}\nTấn công cả quân liền kề mục tiêu",
 	"core.bonus.TRANSMUTATION.description" : "{Biến đổi}\nCó ${val}% cơ hội biến đổi quân mục tiêu thành dạng khác",
 	"core.bonus.TWO_HEX_ATTACK_BREATH.description" : "{Đánh hai ô}\nTấn công bằng hơi thở (xuyên 2 ô)",

+ 1 - 1
client/CPlayerInterface.cpp

@@ -892,7 +892,7 @@ void CPlayerInterface::battleTriggerEffect(const BattleID & battleID, const Batt
 
 	battleInt->effectsController->battleTriggerEffect(bte);
 
-	if(bte.effect == vstd::to_underlying(BonusType::MANA_DRAIN))
+	if(bte.effect == BonusType::MANA_DRAIN)
 	{
 		const CGHeroInstance * manaDrainedHero = GAME->interface()->cb->getHero(ObjectInstanceID(bte.additionalInfo));
 		battleInt->windowObject->heroManaPointsChanged(manaDrainedHero);

+ 9 - 0
client/NetPacksClient.cpp

@@ -40,6 +40,7 @@
 #include "../lib/CSoundBase.h"
 #include "../lib/StartInfo.h"
 #include "../lib/CConfigHandler.h"
+#include "../lib/mapObjects/MiscObjects.h"
 #include "../lib/mapObjects/CGMarket.h"
 #include "../lib/mapObjects/CGTownInstance.h"
 #include "../lib/gameState/CGameState.h"
@@ -478,8 +479,11 @@ void ApplyClientNetPackVisitor::visitRemoveBonus(RemoveBonus & pack)
 void ApplyFirstClientNetPackVisitor::visitRemoveObject(RemoveObject & pack)
 {
 	const CGObjectInstance *o = cl.gameInfo().getObj(pack.objectID);
+	const auto * h = dynamic_cast<const CGHeroInstance*>(o);
 
 	GAME->map().onObjectFadeOut(o, pack.initiator);
+	if (h && h->inBoat())
+		GAME->map().onObjectFadeOut(h->getBoat(), pack.initiator);
 
 	//notify interfaces about removal
 	for(auto i=cl.playerint.begin(); i!=cl.playerint.end(); i++)
@@ -487,7 +491,11 @@ void ApplyFirstClientNetPackVisitor::visitRemoveObject(RemoveObject & pack)
 		//below line contains little cheat for AI so it will be aware of deletion of enemy heroes that moved or got re-covered by FoW
 		//TODO: loose requirements as next AI related crashes appear, for example another pack.player collects object that got re-covered by FoW, unsure if AI code workarounds this
 		if(gs.isVisibleFor(o, i->first) || (!cl.gameInfo().getPlayerState(i->first)->human && o->ID == Obj::HERO && o->tempOwner != i->first))
+		{
 			i->second->objectRemoved(o, pack.initiator);
+			if (h && h->inBoat())
+				i->second->objectRemoved(h->getBoat(), pack.initiator);
+		}
 	}
 
 	GAME->map().waitForOngoingAnimations();
@@ -509,6 +517,7 @@ void ApplyFirstClientNetPackVisitor::visitTryMoveHero(TryMoveHero & pack)
 	{
 		case TryMoveHero::EMBARK:
 			GAME->map().onBeforeHeroEmbark(h, pack.start, pack.end);
+			GAME->map().waitForOngoingAnimations(); // required - hero must play fade-out animation on his pre-embark position
 			break;
 		case TryMoveHero::TELEPORTATION:
 			GAME->map().onBeforeHeroTeleported(h, pack.start, pack.end);

+ 5 - 2
client/adventureMap/CMinimap.cpp

@@ -49,6 +49,9 @@ ColorRGBA CMinimapInstance::getTileColor(const int3 & pos) const
 		if(player == PlayerColor::NEUTRAL)
 			return graphics->neutralColor;
 
+		if (settings["adventure"]["minimapShowHeroes"].Bool() && obj->ID == MapObjectID::HERO)
+			continue;
+
 		if (player.isValidPlayer())
 			return graphics->playerColors[player.getNum()];
 	}
@@ -134,8 +137,8 @@ Point CMinimap::tileToPixels(const int3 &tile) const
 	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);
+	int x = static_cast<int>(stepX * (tile.x + 0.5));
+	int y = static_cast<int>(stepY * (tile.y + 0.5));
 
 	return Point(x,y);
 }

+ 2 - 2
client/battle/BattleEffectsController.cpp

@@ -66,7 +66,7 @@ void BattleEffectsController::battleTriggerEffect(const BattleTriggerEffect & bt
 		return;
 	}
 	//don't show animation when no HP is regenerated
-	switch(static_cast<BonusType>(bte.effect))
+	switch(bte.effect)
 	{
 		case BonusType::HP_REGENERATION:
 			displayEffect(EBattleEffect::REGENERATION, AudioPath::builtin("REGENER"), stack->getPosition(), 0.5);
@@ -77,7 +77,7 @@ void BattleEffectsController::battleTriggerEffect(const BattleTriggerEffect & bt
 		case BonusType::POISON:
 			displayEffect(EBattleEffect::POISON, AudioPath::builtin("POISON"), stack->getPosition());
 			break;
-		case BonusType::FEAR:
+		case BonusType::FEARFUL:
 			displayEffect(EBattleEffect::FEAR, AudioPath::builtin("FEAR"), stack->getPosition(), 0.5);
 			break;
 		case BonusType::MORALE:

+ 3 - 1
client/battle/BattleStacksController.cpp

@@ -442,7 +442,9 @@ void BattleStacksController::stacksAreAttacked(std::vector<StackAttackedInfo> at
 
 		// FIXME: this check is better, however not usable since stacksAreAttacked is called after net pack is applied - petrification is already removed
 		// if (needsReverse && !attackedInfo.defender->isFrozen())
-		if (needsReverse && stackAnimation[attackedInfo.defender->unitId()]->getType() != ECreatureAnimType::FROZEN)
+		if (needsReverse &&
+		   stackAnimation[attackedInfo.defender->unitId()]->getType() != ECreatureAnimType::FROZEN &&
+		   !attackedInfo.defender->hasBonusOfType(BonusType::VULNERABLE_FROM_BACK))
 		{
 			owner.addToAnimationStage(EAnimationEvents::MOVEMENT, [this, attackedInfo]()
 			{

+ 168 - 119
client/lobby/CBonusSelection.cpp

@@ -46,18 +46,20 @@
 #include "../../lib/GameLibrary.h"
 #include "../../lib/StartInfo.h"
 #include "../../lib/campaign/CampaignState.h"
+#include "../../lib/entities/artifact/CArtifact.h"
 #include "../../lib/entities/building/CBuilding.h"
-#include "../../lib/entities/building/CBuildingHandler.h"
 #include "../../lib/entities/faction/CFaction.h"
 #include "../../lib/entities/faction/CTown.h"
 #include "../../lib/entities/faction/CTownHandler.h"
 #include "../../lib/entities/hero/CHeroHandler.h"
+#include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/filesystem/Filesystem.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapping/CMapHeader.h"
 #include "../../lib/mapping/CMapInfo.h"
 #include "../../lib/mapping/CMapService.h"
 #include "../../lib/texts/CGeneralTextHandler.h"
+#include "mapping/MapFormatSettings.h"
 
 std::shared_ptr<CampaignState> CBonusSelection::getCampaign()
 {
@@ -173,149 +175,196 @@ void CBonusSelection::createBonusesIcons()
 
 	for(int i = 0; i < bonDescs.size(); i++)
 	{
-		int bonusType = static_cast<size_t>(bonDescs[i].type);
-		std::string picName = bonusPics[bonusType];
-		size_t picNumber = bonDescs[i].info2;
+		const CampaignBonus & bonus	= bonDescs[i];
+		CampaignBonusType bonusType = bonus.getType();
+		std::string picName = bonusPics[static_cast<int>(bonusType)];
+		size_t picNumber = -1;
 
 		MetaString desc;
-		switch(bonDescs[i].type)
+		switch(bonusType)
 		{
-		case CampaignBonusType::SPELL:
-			desc.appendLocalString(EMetaText::GENERAL_TXT, 715);
-			desc.replaceName(SpellID(bonDescs[i].info2));
-			break;
-		case CampaignBonusType::MONSTER:
-			picNumber = bonDescs[i].info2 + 2;
-			desc.appendLocalString(EMetaText::GENERAL_TXT, 717);
-			desc.replaceNumber(bonDescs[i].info3);
-			desc.replaceNamePlural(bonDescs[i].info2);
-			break;
-		case CampaignBonusType::BUILDING:
-		{
-			FactionID faction;
-			for(auto & elem : GAME->server().si->playerInfos)
+			case CampaignBonusType::SPELL:
 			{
-				if(elem.second.isControlledByHuman())
-				{
-					faction = elem.second.castle;
-					break;
-				}
-
+				const auto & bonusValue = bonus.getValue<CampaignBonusSpell>();
+				const auto * spell = bonusValue.spell.toSpell();
+				if (!spell->getIconScenarioBonus().empty())
+					picName = spell->getIconScenarioBonus();
+				else
+					picNumber = bonusValue.spell.getNum();
+
+				desc.appendLocalString(EMetaText::GENERAL_TXT, 715);
+				desc.replaceName(bonusValue.spell);
+				break;
 			}
-			assert(faction.hasValue());
-
-			BuildingID buildID;
-			if(getCampaign()->formatVCMI())
-				buildID = BuildingID(bonDescs[i].info1);
-			else
-				buildID = CBuildingHandler::campToERMU(bonDescs[i].info1, faction, std::set<BuildingID>());
-			picName = graphics->ERMUtoPicture[faction.getNum()][buildID.getNum()];
-			picNumber = -1;
-
-			if(vstd::contains((*LIBRARY->townh)[faction]->town->buildings, buildID))
-				desc.appendTextID((*LIBRARY->townh)[faction]->town->buildings.find(buildID)->second->getNameTextID());
-			break;
-		}
-		case CampaignBonusType::ARTIFACT:
-			desc.appendLocalString(EMetaText::GENERAL_TXT, 715);
-			desc.replaceName(ArtifactID(bonDescs[i].info2));
-			break;
-		case CampaignBonusType::SPELL_SCROLL:
-			desc.appendLocalString(EMetaText::GENERAL_TXT, 716);
-			desc.replaceName(SpellID(bonDescs[i].info2));
-			break;
-		case CampaignBonusType::PRIMARY_SKILL:
-		{
-			int leadingSkill = -1;
-			std::vector<std::pair<int, int>> toPrint; //primary skills to be listed <num, val>
-			const ui8 * ptr = reinterpret_cast<const ui8 *>(&bonDescs[i].info2);
-			for(int g = 0; g < GameConstants::PRIMARY_SKILLS; ++g)
+			case CampaignBonusType::MONSTER:
 			{
-				if(leadingSkill == -1 || ptr[g] > ptr[leadingSkill])
-				{
-					leadingSkill = g;
-				}
-				if(ptr[g] != 0)
+				const auto & bonusValue = bonus.getValue<CampaignBonusCreatures>();
+				picNumber = bonusValue.creature.getNum() + 2;
+				desc.appendLocalString(EMetaText::GENERAL_TXT, 717);
+				desc.replaceNumber(bonusValue.amount);
+				desc.replaceNamePlural(bonusValue.creature);
+				break;
+			}
+			case CampaignBonusType::BUILDING:
+			{
+				const auto & bonusValue = bonus.getValue<CampaignBonusBuilding>();
+				FactionID faction;
+				for(auto & elem : GAME->server().si->playerInfos)
 				{
-					toPrint.push_back(std::make_pair(g, ptr[g]));
+					if(elem.second.isControlledByHuman())
+					{
+						faction = elem.second.castle;
+						break;
+					}
 				}
-			}
-			picNumber = leadingSkill;
-			desc.appendLocalString(EMetaText::GENERAL_TXT, 715);
+				assert(faction.hasValue());
 
-			std::string substitute; //text to be printed instead of %s
-			for(int v = 0; v < toPrint.size(); ++v)
-			{
-				substitute += std::to_string(toPrint[v].second);
-				substitute += " " + LIBRARY->generaltexth->primarySkillNames[toPrint[v].first];
-				if(v != toPrint.size() - 1)
+				BuildingID buildID = bonusValue.buildingDecoded;
+				if (bonusValue.buildingH3M.hasValue())
 				{
-					substitute += ", ";
+					auto mapping = LIBRARY->mapFormat->getMapping(getCampaign()->getFormat());
+					buildID = mapping.remapBuilding(faction, bonusValue.buildingH3M);
 				}
-			}
 
-			desc.replaceRawString(substitute);
-			break;
-		}
-		case CampaignBonusType::SECONDARY_SKILL:
-			desc.appendLocalString(EMetaText::GENERAL_TXT, 718);
-			desc.replaceTextID(TextIdentifier("core", "skilllev", bonDescs[i].info3 - 1).get());
-			desc.replaceName(SecondarySkill(bonDescs[i].info2));
-			picNumber = bonDescs[i].info2 * 3 + bonDescs[i].info3 - 1;
-
-			break;
-		case CampaignBonusType::RESOURCE:
-		{
-			desc.appendLocalString(EMetaText::GENERAL_TXT, 717);
+				for (const auto & townStructure : faction.toFaction()->town->clientInfo.structures)
+					if (townStructure->building && townStructure->building->bid == buildID)
+						picName = townStructure->campaignBonus.getOriginalName();
+
+				picNumber = -1;
 
-			switch(bonDescs[i].info1)
+				if(vstd::contains(faction.toFaction()->town->buildings, buildID))
+					desc.appendTextID(faction.toFaction()->town->buildings.find(buildID)->second->getNameTextID());
+				break;
+			}
+			case CampaignBonusType::ARTIFACT:
 			{
-				case EGameResID::COMMON: //wood + ore
-				{
-					desc.replaceLocalString(EMetaText::GENERAL_TXT, 721);
-					picNumber = 7;
-					break;
-				}
-				case EGameResID::RARE : //mercury + sulfur + crystal + gems
+				const auto & bonusValue = bonus.getValue<CampaignBonusArtifact>();
+				const auto * artifact = bonusValue.artifact.toArtifact();
+				if (!artifact->scenarioBonus.empty())
+					picName = artifact->scenarioBonus;
+				else
+					picNumber = bonusValue.artifact.getNum();
+				desc.appendLocalString(EMetaText::GENERAL_TXT, 715);
+				desc.replaceName(bonusValue.artifact);
+				break;
+			}
+			case CampaignBonusType::SPELL_SCROLL:
+			{
+				const auto & bonusValue = bonus.getValue<CampaignBonusSpellScroll>();
+				const auto * spell = bonusValue.spell.toSpell();
+				if (!spell->getIconScenarioBonus().empty())
+					picName = spell->getIconScenarioBonus();
+				else
+					picNumber = bonusValue.spell.getNum();
+
+				desc.appendLocalString(EMetaText::GENERAL_TXT, 716);
+				desc.replaceName(bonusValue.spell);
+				break;
+			}
+			case CampaignBonusType::PRIMARY_SKILL:
+			{
+				const auto & bonusValue = bonus.getValue<CampaignBonusPrimarySkill>();
+				int leadingSkill = -1;
+				std::vector<std::pair<int, int>> toPrint; //primary skills to be listed <num, val>
+				for(int g = 0; g < bonusValue.amounts.size(); ++g)
 				{
-					desc.replaceLocalString(EMetaText::GENERAL_TXT, 722);
-					picNumber = 8;
-					break;
+					if(leadingSkill == -1 || bonusValue.amounts[g] > bonusValue.amounts[leadingSkill])
+					{
+						leadingSkill = g;
+					}
+					if(bonusValue.amounts[g] != 0)
+					{
+						toPrint.push_back(std::make_pair(g, bonusValue.amounts[g]));
+					}
 				}
-				default:
+				picNumber = leadingSkill;
+				desc.appendLocalString(EMetaText::GENERAL_TXT, 715);
+
+				std::string substitute; //text to be printed instead of %s
+				for(int v = 0; v < toPrint.size(); ++v)
 				{
-					desc.replaceName(GameResID(bonDescs[i].info1));
-					picNumber = bonDescs[i].info1;
+					substitute += std::to_string(toPrint[v].second);
+					substitute += " " + LIBRARY->generaltexth->primarySkillNames[toPrint[v].first];
+					if(v != toPrint.size() - 1)
+					{
+						substitute += ", ";
+					}
 				}
+
+				desc.replaceRawString(substitute);
+				break;
 			}
+			case CampaignBonusType::SECONDARY_SKILL:
+			{
+				const auto & bonusValue = bonus.getValue<CampaignBonusSecondarySkill>();
+				const auto * skill = bonusValue.skill.toSkill();
+				desc.appendLocalString(EMetaText::GENERAL_TXT, 718);
+				desc.replaceTextID(TextIdentifier("core", "skilllev", bonusValue.mastery - 1).get());
+				desc.replaceName(bonusValue.skill);
+				if (!skill->at(bonusValue.mastery).scenarioBonus.empty())
+					picName = skill->at(bonusValue.mastery).scenarioBonus.empty();
+				else
+					picNumber = bonusValue.skill.getNum() * 3 + bonusValue.mastery - 1;
+				break;
+			}
+			case CampaignBonusType::RESOURCE:
+			{
+				const auto & bonusValue = bonus.getValue<CampaignBonusStartingResources>();
+				desc.appendLocalString(EMetaText::GENERAL_TXT, 717);
 
-			desc.replaceNumber(bonDescs[i].info2);
-			break;
-		}
-		case CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO:
-		{
-			auto superhero = getCampaign()->strongestHero(static_cast<CampaignScenarioID>(bonDescs[i].info2), PlayerColor(bonDescs[i].info1));
-			if(!superhero)
-				logGlobal->warn("No superhero! How could it be transferred?");
-			picNumber = superhero ? superhero->getIconIndex() : 0;
-			desc.appendLocalString(EMetaText::GENERAL_TXT, 719);
-			desc.replaceRawString(getCampaign()->scenario(static_cast<CampaignScenarioID>(bonDescs[i].info2)).scenarioName.toString());
-			break;
-		}
+				switch(bonusValue.resource)
+				{
+					case EGameResID::COMMON: //wood + ore
+					{
+						desc.replaceLocalString(EMetaText::GENERAL_TXT, 721);
+						picNumber = 7;
+						break;
+					}
+					case EGameResID::RARE: //mercury + sulfur + crystal + gems
+					{
+						desc.replaceLocalString(EMetaText::GENERAL_TXT, 722);
+						picNumber = 8;
+						break;
+					}
+					default:
+					{
+						desc.replaceName(bonusValue.resource);
+						picNumber = bonusValue.resource.getNum();
+					}
+				}
 
-		case CampaignBonusType::HERO:
-			if(bonDescs[i].info2 == HeroTypeID::CAMP_RANDOM.getNum())
+				desc.replaceNumber(bonusValue.amount);
+				break;
+			}
+			case CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO:
 			{
-				desc.appendLocalString(EMetaText::GENERAL_TXT, 720); // Start with random hero
-				picNumber = -1;
-				picName = "CBONN1A3.BMP";
+				const auto & bonusValue = bonus.getValue<CampaignBonusHeroesFromScenario>();
+				auto superhero = getCampaign()->strongestHero(bonusValue.scenario, bonusValue.startingPlayer);
+				if(!superhero)
+					logGlobal->warn("No superhero! How could it be transferred?");
+				picNumber = superhero ? superhero->getIconIndex() : 0;
+				desc.appendLocalString(EMetaText::GENERAL_TXT, 719);
+				desc.replaceRawString(getCampaign()->scenario(bonusValue.scenario).scenarioName.toString());
+				break;
 			}
-			else
+
+			case CampaignBonusType::HERO:
 			{
-				desc.appendLocalString(EMetaText::GENERAL_TXT, 715); // Start with %s
-				desc.replaceTextID(LIBRARY->heroh->objects[bonDescs[i].info2]->getNameTextID());
+				const auto & bonusValue = bonus.getValue<CampaignBonusStartingHero>();
+				if(bonusValue.hero == HeroTypeID::CAMP_RANDOM.getNum())
+				{
+					desc.appendLocalString(EMetaText::GENERAL_TXT, 720); // Start with random hero
+					picNumber = -1;
+					picName = "CBONN1A3.BMP";
+				}
+				else
+				{
+					desc.appendLocalString(EMetaText::GENERAL_TXT, 715); // Start with %s
+					desc.replaceTextID(bonusValue.hero.toHeroType()->getNameTextID());
+					picNumber = bonusValue.hero.getNum();
+				}
+				break;
 			}
-			break;
 		}
 
 		std::shared_ptr<CToggleButton> bonusButton = std::make_shared<CToggleButton>(Point(475 + i * 68, 455), AnimationPath::builtin("campaignBonusSelection"), CButton::tooltip(desc.toString(), desc.toString()), nullptr, EShortcut::NONE, false, [this](){

+ 0 - 19
client/render/Graphics.cpp

@@ -118,7 +118,6 @@ Graphics::Graphics()
 {
 	loadPaletteAndColors();
 	initializeBattleGraphics();
-	loadErmuToPicture();
 
 	//(!) do not load any CAnimation here
 }
@@ -153,21 +152,3 @@ void Graphics::setPlayerFlagColor(SDL_Palette * targetPalette, PlayerColor playe
 		SDL_SetPaletteColors(targetPalette, &color, 5, 1);
 	}
 }
-
-void Graphics::loadErmuToPicture()
-{
-	//loading ERMU to picture
-	const JsonNode config(JsonPath::builtin("config/ERMU_to_picture.json"));
-	int etp_idx = 0;
-	for(const JsonNode &etp : config["ERMU_to_picture"].Vector()) {
-		int idx = 0;
-		for(const JsonNode &n : etp.Vector()) {
-			ERMUtoPicture[idx][etp_idx] = n.String();
-			idx ++;
-		}
-		assert (idx == std::size(ERMUtoPicture));
-
-		etp_idx ++;
-	}
-	assert (etp_idx == 44);
-}

+ 0 - 3
client/render/Graphics.h

@@ -20,7 +20,6 @@ class Graphics
 {
 	void initializeBattleGraphics();
 	void loadPaletteAndColors();
-	void loadErmuToPicture();
 
 public:
 	using PlayerPalette = std::array<ColorRGBA, 32>;
@@ -32,8 +31,6 @@ public:
 	PlayerPalette neutralColorPalette;
 	ColorRGBA neutralColor;
 
-	//towns
-	std::map<int, std::string> ERMUtoPicture[GameConstants::F_NUMBER]; //maps building ID to it's picture's name for each town type
 	//for battles
 	std::map< int, std::vector < std::string > > battleACToDef; //maps AC format to vector of appropriate def names
 

+ 3 - 2
client/widgets/CArtifactsOfHeroBase.cpp

@@ -269,12 +269,13 @@ void CArtifactsOfHeroBase::setSlotData(ArtPlacePtr artPlace, const ArtifactPosit
 	artPlace->slot = slot;
 	if(auto slotInfo = curHero->getSlot(slot))
 	{
+		const auto curArt = slotInfo->getArt();
+
 		artPlace->lockSlot(slotInfo->locked);
-		artPlace->setArtifact(slotInfo->getArt()->getTypeId(), slotInfo->getArt()->getScrollSpellID());
+		artPlace->setArtifact(curArt->getTypeId(), curArt->getScrollSpellID());
 		if(slotInfo->locked)
 			return;
 
-		const auto curArt = slotInfo->getArt();
 		// If the artifact has charges, add charges information
 		if(curArt->getType()->isCharged())
 			artPlace->addChargedArtInfo(curArt->getCharges());

+ 1 - 3
client/widgets/MiscWidgets.cpp

@@ -585,9 +585,7 @@ void MoraleLuckBox::set(const AFactionMember * node)
 	text = LIBRARY->generaltexth->arraytxt[textId[morale]];
 	boost::algorithm::replace_first(text,"%s",LIBRARY->generaltexth->arraytxt[neutralDescr[morale]-mrlt]);
 
-	if (morale && node && (node->getBonusBearer()->hasBonusOfType(BonusType::UNDEAD)
-			|| node->getBonusBearer()->hasBonusOfType(BonusType::NON_LIVING)
-			|| node->getBonusBearer()->hasBonusOfType(BonusType::MECHANICAL)))
+	if (morale && node && node->unaffectedByMorale())
 	{
 		text += LIBRARY->generaltexth->arraytxt[113]; //unaffected by morale
 		component.value = 0;

+ 1 - 0
client/widgets/TextControls.cpp

@@ -344,6 +344,7 @@ Rect CMultiLineLabel::getTextLocation()
 	case ETextAlignment::TOPLEFT:     return Rect(pos.topLeft(), textSizeComputed);
 	case ETextAlignment::TOPCENTER:   return Rect(pos.topLeft(), textSizeComputed);
 	case ETextAlignment::CENTER:      return Rect(pos.topLeft() + textOffset / 2, textSizeComputed);
+	case ETextAlignment::CENTERLEFT:  return Rect(pos.topLeft() + Point(0, textOffset.y / 2), textSizeComputed);
 	case ETextAlignment::CENTERRIGHT: return Rect(pos.topLeft() + Point(textOffset.x, textOffset.y / 2), textSizeComputed);
 	case ETextAlignment::BOTTOMRIGHT: return Rect(pos.topLeft() + textOffset, textSizeComputed);
 	}

+ 1 - 1
client/windows/CCreatureWindow.cpp

@@ -485,7 +485,7 @@ CStackWindow::CommanderMainSection::CommanderMainSection(CStackWindow * owner, i
 
 	auto getArtifactPos = [](int index)
 	{
-		return Point(269 + 47 * (index % 3), 22 + 47 * (index / 3));
+		return Point(269 + 52 * (index % 3), 22 + 52 * (index / 3));
 	};
 
 	for(auto equippedArtifact : parent->info->commander->artifactsWorn)

+ 3 - 0
client/windows/CKingdomInterface.cpp

@@ -831,6 +831,9 @@ CTownItem::CTownItem(const CGTownInstance * Town)
 	{
 		ENGINE->windows().createAndPushWindow<CCastleInterface>(town);
 	});
+
+	labelCreatureGrowth = std::make_shared<CMultiLineLabel>(Rect(4, 76, 50, 35), EFonts::FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, LIBRARY->generaltexth->translate("core.genrltxt.265"));
+	labelCreatureAvailable = std::make_shared<CMultiLineLabel>(Rect(349, 76, 57, 35), EFonts::FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, LIBRARY->generaltexth->translate("core.genrltxt.266"));
 }
 
 void CTownItem::updateGarrisons()

+ 4 - 0
client/windows/CKingdomInterface.h

@@ -31,6 +31,7 @@ class CListBox;
 class CTabbedInt;
 class CGStatusBar;
 class CGarrisonInt;
+class CMultiLineLabel;
 
 class CKingdHeroList;
 class CKingdTownList;
@@ -272,6 +273,9 @@ class CTownItem : public CIntObject, public IGarrisonHolder
 	std::vector<std::shared_ptr<CCreaInfo>> available;
 	std::vector<std::shared_ptr<CCreaInfo>> growth;
 
+	std::shared_ptr<CMultiLineLabel> labelCreatureAvailable;
+	std::shared_ptr<CMultiLineLabel> labelCreatureGrowth;
+
 	std::shared_ptr<LRClickableAreaOpenTown> openTown;
 
 	std::shared_ptr<CButton> fastTownHall;

+ 3 - 2
client/windows/CMapOverview.cpp

@@ -34,7 +34,7 @@
 #include "../../lib/mapping/MapFormat.h"
 #include "../../lib/TerrainHandler.h"
 #include "../../lib/filesystem/Filesystem.h"
-
+#include "../../lib/callback/EditorCallback.h"
 #include "../../lib/StartInfo.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/rmg/CMapGenOptions.h"
@@ -105,7 +105,8 @@ std::vector<std::shared_ptr<CanvasImage>> CMapOverviewWidget::createMinimaps(con
 	std::unique_ptr<CMap> map;
 	try
 	{
-		map = mapService.loadMap(resource, nullptr);
+		auto cb = std::make_unique<EditorCallback>(map.get());
+		map = mapService.loadMap(resource, cb.get());
 	}
 	catch (const std::exception & e)
 	{

+ 18 - 17
client/windows/CSpellWindow.cpp

@@ -33,7 +33,6 @@
 #include "../widgets/VideoWidget.h"
 #include "../adventureMap/AdventureMapInterface.h"
 
-
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/GameConstants.h"
 #include "../../lib/GameLibrary.h"
@@ -42,11 +41,21 @@
 #include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/spells/ISpellMechanics.h"
 #include "../../lib/spells/Problem.h"
+#include "../../lib/spells/SpellSchoolHandler.h"
 #include "../../lib/texts/CGeneralTextHandler.h"
 #include "../../lib/texts/TextOperations.h"
-
 #include "../../lib/mapObjects/CGHeroInstance.h"
 
+// Ordering of spell school tabs in SpelTab.def
+static const std::array schoolTabOrder =
+{
+	SpellSchool::AIR,
+	SpellSchool::FIRE,
+	SpellSchool::WATER,
+	SpellSchool::EARTH,
+	SpellSchool::ANY
+};
+
 CSpellWindow::InteractiveArea::InteractiveArea(const Rect & myRect, std::function<void()> funcL, int helpTextId, CSpellWindow * _owner)
 {
 	addUsedEvents(LCLICK | SHOW_POPUP | HOVER);
@@ -85,12 +94,11 @@ public:
 		if(A->getLevel() > B->getLevel())
 			return false;
 
-
-		for(auto schoolId = 0; schoolId < GameConstants::DEFAULT_SCHOOLS; schoolId++)
+		for (const auto schoolId : LIBRARY->spellSchoolHandler->getAllObjects())
 		{
-			if(A->school.at(SpellSchool(schoolId)) && !B->school.at(SpellSchool(schoolId)))
+			if(A->schools.count(schoolId) && !B->schools.count(schoolId))
 				return true;
-			if(!A->school.at(SpellSchool(schoolId)) && B->school.at(SpellSchool(schoolId)))
+			if(!A->schools.count(schoolId) && B->schools.count(schoolId))
 				return false;
 		}
 
@@ -423,7 +431,7 @@ void CSpellWindow::computeSpellsPerArea()
 	for(const CSpell * spell : mySpells)
 	{
 		if(spell->isCombat() ^ !battleSpellsOnly
-			&& ((selectedTab == 4) || spell->school.at(SpellSchool(selectedTab)))
+		   && ((selectedTab == 4) || spell->schools.count(schoolTabOrder.at(selectedTab)))
 			)
 		{
 			spellsCurSite.push_back(spell);
@@ -726,7 +734,7 @@ void CSpellWindow::SpellArea::setSpell(const CSpell * spell)
 	mySpell = spell;
 	if(mySpell)
 	{
-		SpellSchool whichSchool; //0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic,
+		SpellSchool whichSchool;
 		schoolLevel = owner->myHero->getSpellSchoolLevel(mySpell, &whichSchool);
 		auto spellCost = owner->myInt->cb->getSpellCost(mySpell, owner->myHero);
 
@@ -736,21 +744,14 @@ void CSpellWindow::SpellArea::setSpell(const CSpell * spell)
 		{
 			OBJECT_CONSTRUCTION;
 
-			static const std::array schoolBorders = {
-				AnimationPath::builtin("SplevA.def"),
-				AnimationPath::builtin("SplevF.def"),
-				AnimationPath::builtin("SplevW.def"),
-				AnimationPath::builtin("SplevE.def")
-			};
-
 			schoolBorder.reset();
 			if (owner->selectedTab >= 4)
 			{
 				if (whichSchool.hasValue())
-					schoolBorder = std::make_shared<CAnimImage>(schoolBorders.at(whichSchool.getNum()), schoolLevel);
+					schoolBorder = std::make_shared<CAnimImage>(LIBRARY->spellSchoolHandler->getById(whichSchool)->getSpellBordersPath(), schoolLevel);
 			}
 			else
-				schoolBorder = std::make_shared<CAnimImage>(schoolBorders.at(owner->selectedTab), schoolLevel);
+				schoolBorder = std::make_shared<CAnimImage>(LIBRARY->spellSchoolHandler->getById(schoolTabOrder.at(owner->selectedTab))->getSpellBordersPath(), schoolLevel);
 		}
 
 		ColorRGBA firstLineColor, secondLineColor;

+ 0 - 48
config/ERMU_to_picture.json

@@ -1,48 +0,0 @@
-{
-	"ERMU_to_picture": [
-		[ "BoCsMag1.pcx", "BoRMag1.pcx", "BoTGld1.pcx", "BoIMag1.pcx", "BoNmage1.pcx", "BoDmage1.pcx", "BoSmage1.pcx", "BoFmage1.pcx", "BoEgld1.pcx" ],
-		[ "BoCsMag2.pcx", "BoRMag2.pcx", "BoTGld2.pcx", "BoIMag2.pcx", "BoNmage2.pcx", "BoDmage2.pcx", "BoSmage2.pcx", "BoFmage2.pcx", "BoEgld2.pcx" ],
-		[ "BoCsMag3.pcx", "BoRMag3.pcx", "BoTGld3.pcx", "BoIMag3.pcx", "BoNmage3.pcx", "BoDmage3.pcx", "BoSmage3.pcx", "BoFmage3.pcx", "BoEgld3.pcx" ],
-		[ "BoCsMag4.pcx", "BoRMag4.pcx", "BoTGld4.pcx", "BoIMag4.pcx", "BoNmage4.pcx", "BoDmage4.pcx", "BoSmage4.pcx", "BoFmage4.pcx", "BoEgld4.pcx" ],
-		[ "BoCsMag5.pcx", "BoRMag5.pcx", "BoTGld5.pcx", "BoIMag5.pcx", "BoNmage5.pcx", "BoDmage5.pcx", "BoSmage5.pcx", "BoFmage5.pcx", "BoEgld5.pcx" ],
-		[ "BoCsTav1.pcx", "BoRTav.pcx", "BoTTav.pcx", "BoITav.pcx", "BoNtav.pcx", "BoDtav.pcx", "BoStav1.pcx", "BoFtav.pcx", "BoEtav.pcx" ],
-		[ "BoCsDock.pcx", "", "", "", "", "", "", "BoFship.pcx", "BoEship.pcx" ],
-		[ "BoCsCas1.pcx", "BoRCas1.pcx", "BoTCas1.pcx", "BoICas1.pcx", "BoNcast1.pcx", "BoDcas1.pcx", "BoScas1.pcx", "BoFcast1.pcx", "BoEcast1.pcx" ],
-		[ "BoCsCas2.pcx", "BoRCas2.pcx", "BoTCas2.pcx", "BoICas2.pcx", "BoNcast2.pcx", "BoDcas2.pcx", "BoScas2.pcx", "BoFcast2.pcx", "BoEcast2.pcx" ],
-		[ "BoCsCas3.pcx", "BoRCas3.pcx", "BoTCas3.pcx", "BoICas3.pcx", "BoNcast3.pcx", "BoDcas3.pcx", "BoScas3.pcx", "BoFcast3.pcx", "BoEcast3.pcx" ],
-		[ "BoCsHal1.pcx", "BoRHal1.pcx", "BoTHal1.pcx", "BoIHal1.pcx", "BoNhall1.pcx", "BoDhall1.pcx", "BoShall1.pcx", "BoFhall1.pcx", "BoEhall1.pcx" ],
-		[ "BoCsHal2.pcx", "BoRHal2.pcx", "BoTHal2.pcx", "BoIHal2.pcx", "BoNhall2.pcx", "BoDhall2.pcx", "BoShall2.pcx", "BoFhall2.pcx", "BoEhall2.pcx" ],
-		[ "BoCsHal3.pcx", "BoRHal3.pcx", "BoTHal3.pcx", "BoIHal3.pcx", "BoNhall3.pcx", "BoDhall3.pcx", "BoShall3.pcx", "BoFhall3.pcx", "BoEhall3.pcx" ],
-		[ "BoCsHal4.pcx", "BoRHal4.pcx", "BoTHal4.pcx", "BoIHal4.pcx", "BoNhall4.pcx", "BoDhall4.pcx", "BoShall4.pcx", "BoFhall4.pcx", "BoEhall4.pcx" ],
-		[ "BoCsMrk1.pcx", "BoRMrk1.pcx", "BoTMark.pcx", "BoIMrk1.pcx", "BoNmark1.pcx", "BoDmark1.pcx", "BoSmrk1.pcx", "BoFmark1.pcx", "BoEmark1.pcx" ],
-		[ "BoCsMrk2.pcx", "BoRMrk2.pcx", "BoTMarkS.pcx", "BoIMrk2.pcx", "BoNmark2.pcx", "BoDmark2.pcx", "BoSmrk2.pcx", "BoFmark2.pcx", "BoEmarkS.pcx" ],
-		[ "BoCsBlak.pcx", "BoRAid.pcx", "BoTBlack.pcx", "BoIBlak.pcx", "BoNsmith.pcx", "BoDsmith.pcx", "BoSblak1.pcx", "BoFapoth.pcx", "BoEblack.pcx" ],
-		[ "BoCsLite.pcx", "BoRGar1.pcx", "BoTMarkA.pcx", "", "BoNshrod.pcx", "BoDmarkA.pcx", "BoSescap.pcx", "BoFcage.pcx", "BoEmarkA.pcx" ],
-		[ "BoCsGr1H.pcx", "BoRDwf1h.pcx", "BoTGa1H.pcx", "BoIImpH.pcx", "BoNskelH.pcx", "BoDtrogH.pcx", "BoSgob1h.pcx", "BoFgno1h.pcx", "BoDhrd1.pcx" ],
-		[ "BoCsGr2H.pcx", "BoRDwf2h.pcx", "BoTGa2h.pcx", "BoIImp2H.pcx", "", "", "BoSgob2h.pcx", "BoFgno2h.pcx", "BoDhrd2.pcx" ],
-		[ "", "", "", "", "", "", "", "", "" ],
-		[ "BoCsCv2S.pcx", "BoRGar2.pcx", "BoTCasW.pcx", "BoICasB.pcx", "BoNnecro.pcx", "BoDvort.pcx", "BoSmrk1c.pcx", "BoFcastD.pcx", "BoEuniv.pcx" ],
-		[ "BoCsTav2.pcx", "BoRDwf1t.pcx", "BoTGldL.pcx", "BoICasG.pcx", "BoNskelT.pcx", "BoDport.pcx", "BoSblak2.pcx", "BoFcastA.pcx", "" ],
-		[ "", "", "BoTGldW.pcx", "BoIMagO.pcx", "", "BoDacad.pcx", "BoSvahal.pcx", "", "" ],
-		[ "", "BoRTre1h.pcx", "", "BoIHndH.pcx", "", "", "", "", "" ],
-		[ "", "BoRTre2h.pcx", "", "BoIHnd2H.pcx", "", "", "", "", "" ],
-		[ "BoCsHoly.pcx", "BoRHoly.pcx", "BoTHoly.pcx", "BoIHoly.pcx", "BoNholyG.pcx", "BoDholy.pcx", "BoSholy.pcx", "BoFgrail.pcx", "BoEgrail.pcx" ],
-		[ "", "", "", "", "", "", "", "", "" ],
-		[ "", "", "", "", "", "", "", "", "" ],
-		[ "", "", "", "", "", "", "", "", "" ],
-		[ "BoCsPik1.pcx", "BoRCen1.pcx", "BoTGrem1.pcx", "BoIImp1.pcx", "BoNskel1.pcx", "BoDtrog1.pcx", "BoSgob1.pcx", "BoFgnol1.pcx", "BoEdn_0.pcx" ],
-		[ "BoCsCrs1.pcx", "BoRDwf1.pcx", "BoTGar1.pcx", "BoIGog1.pcx", "BoNzomb1.pcx", "BoDharp1.pcx", "BoSwolf1.pcx", "BoFlizr1.pcx", "BoEdn_1.pcx" ],
-		[ "BoCsGr1.pcx", "BoRElf1.pcx", "BoTGolm1.pcx", "BoIHnd1.pcx", "BoNwigh1.pcx", "BoDbeh1.pcx", "BoSorc1.pcx", "BoFfly1.pcx", "BoEdn_2.pcx" ],
-		[ "BoCsSwd1.pcx", "BoRPeg1.pcx", "BoTMag1.pcx", "BoIDmn1.pcx", "BoNvamp1.pcx", "BoDmedu1.pcx", "BoSogre1.pcx", "BoFbas1.pcx", "BoEdn_3.pcx" ],
-		[ "BoCsMon1.pcx", "BoRTre1.pcx", "BoTGen1.pcx", "BoIPit1.pcx", "BoNlich1.pcx", "BoDmino1.pcx", "BoSroc1.pcx", "BoFgorg1.pcx", "BoEdn_4.pcx" ],
-		[ "BoCsCv1.pcx", "BoRUni1.pcx", "BoTNaga1.pcx", "BoIEfr1.pcx", "BoNbkni1.pcx", "BoDmant1.pcx", "BoScyc1.pcx", "BoFwyvr1.pcx", "BoEdn_5.pcx" ],
-		[ "BoCsAng1.pcx", "BoRDra1.pcx", "BoTTit1.pcx", "BoIDvl1.pcx", "BoNbone1.pcx", "BoDdrag1.pcx", "BoSbeh1.pcx", "BoFhydr1.pcx", "BoEdn_6.pcx" ],
-		[ "BoCsPik2.pcx", "BoRCen2.pcx", "BoTGrem2.pcx", "BoIImp2.pcx", "BoNskel2.pcx", "BoDtrog2.pcx", "BoSgob2.pcx", "BoFgnol2.pcx", "BoEup_0.pcx" ],
-		[ "BoCsCrs2.pcx", "BoRDwf2.pcx", "BoTGar2.pcx", "BoIGog2.pcx", "BoNzomb2.pcx", "BoDharp2.pcx", "BoSwolf2.pcx", "BoFlizr2.pcx", "BoEup_1.pcx" ],
-		[ "BoCsGr2.pcx", "BoRElf2.pcx", "BoTGolm2.pcx", "BoIHnd2.pcx", "BoNwigh2.pcx", "BoDbeh2.pcx", "BoSorc2.pcx", "BoFfly2.pcx", "BoEup_2.pcx" ],
-		[ "BoCsSwd2.pcx", "BoRPeg2.pcx", "BoTMag2.pcx", "BoIDmn2.pcx", "BoNvamp2.pcx", "BoDmedu2.pcx", "BoSogre2.pcx", "BoFbas2.pcx", "BoEup_3.pcx" ],
-		[ "BoCsMon2.pcx", "BoRTre2.pcx", "BoTGen2.pcx", "BoIPit2.pcx", "BoNlich2.pcx", "BoDmino2.pcx", "BoSroc2.pcx", "BoFgorg2.pcx", "BoEup_4.pcx" ],
-		[ "BoCsCv2.pcx", "BoRUni2.pcx", "BoTNaga2.pcx", "BoIEfr2.pcx", "BoNbkni2.pcx", "BoDmant2.pcx", "BoScyc2.pcx", "BoFwyvr2.pcx", "BoEup_5.pcx" ],
-		[ "BoCsAng2.pcx", "BoRDra2.pcx", "BoTTit2.pcx", "BoIDvl2.pcx", "BoNbone2.pcx", "BoDdrag2.pcx", "BoSbeh2.pcx", "BoFhydr2.pcx", "BoEup_6.pcx" ]
-	]
-}

+ 42 - 12
config/bonuses.json

@@ -85,6 +85,10 @@
 	"ENEMY_DEFENCE_REDUCTION":
 	{
 	},
+	
+	"FEARFUL":
+	{
+	},
 
 	"FIRE_SHIELD":
 	{
@@ -98,14 +102,6 @@
 		}
 	},
 
-	"FEAR":
-	{
-	},
-
-	"FEARLESS":
-	{
-	},
-
 	"FEROCITY":
 	{
 	},
@@ -120,9 +116,18 @@
 	"FREE_SHOOTING":
 	{
 	},
+	
+	"FULL_MAP_DARKNESS":
+	{
+	},
+	
+	"FULL_MAP_SCOUTING":
+	{
+	},
 
 	"GARGOYLE":
 	{
+		"creatureNature" : true,
 	},
 
 	"GENERAL_DAMAGE_REDUCTION":
@@ -178,6 +183,11 @@
 	"LIMITED_SHOOTING_RANGE":
 	{
 	},
+	
+	"LIVING":
+	{
+		"creatureNature" : true
+	},
 
 	"MANA_CHANNELING":
 	{
@@ -194,6 +204,11 @@
 	"MAGIC_RESISTANCE":
 	{
 	},
+	
+	"MECHANICAL":
+	{
+		"creatureNature" : true
+	},
 
 	"MIND_IMMUNITY":
 	{
@@ -231,10 +246,7 @@
 
 	"NON_LIVING":
 	{
-	},
-
-	"MECHANICAL":
-	{
+		"creatureNature" : true
 	},
 
 	"OPENING_BATTLE_SPELL":
@@ -269,6 +281,15 @@
 	"REVENGE":
 	{
 	},
+	
+	"SIEGE_WEAPON":
+	{
+		"creatureNature" : true
+	},
+	
+	"SKELETON_TRANSFORMER_TARGET":
+	{
+	},
 
 	"SHOOTER":
 	{
@@ -346,8 +367,13 @@
 	{
 	},
 
+	"TRANSMUTATION_IMMUNITY":
+	{
+	},
+
 	"UNDEAD":
 	{
+		"creatureNature" : true,
 	},
 	
 	"UNLIMITED_RETALIATIONS":
@@ -359,6 +385,10 @@
 		"hidden": true
 	},
 	
+	"VULNERABLE_FROM_BACK":
+	{
+	},
+	
 	"WIDE_BREATH":
 	{
 	},

+ 0 - 239
config/campaignMedia.json

@@ -1,239 +0,0 @@
-{
-	"videos": [
-	//Restoration of Erathia
-		//Long live the Queen
-		"GOOD1A.SMK", //Good1_a
-		"GOOD1B.SMK", //Good1_b
-		"GOOD1C.SMK", //Good1_c
-		//Dungeons and devils
-		"EVIL1A.SMK", //Evil1_a
-		"EVIL1B.SMK", //Evil1_b
-		"EVIL1C.SMK", //Evil1_c
-		//Spoils of War
-		"NEUTRALA.SMK", //Neutral1_a
-		"NEUTRALB.SMK", //Neutral1_b
-		"NEUTRALC.SMK", //Neutral1_c
-		//Liberation
-		"GOOD2A.SMK", //Good2_a
-		"GOOD2B.SMK", //Good2_b
-		"GOOD2C.SMK", //Good2_c
-		"GOOD2D.SMK", //Good2_d
-		//Long Live the King
-		"EVIL2A.SMK", //Evil2_a
-		"EVIL2AP1.SMK", //Evil2ap1
-		"EVIL2B.SMK", //Evil2_b
-		"EVIL2C.SMK", //Evil2_c
-		"EVIL2D.SMK", //Evil2_d
-		//Song for the Father
-		"GOOD3A.SMK", //Good3_a
-		"GOOD3B.SMK", //Good3_b
-		"GOOD3C.SMK", //Good3_c
-		//Seeds Of Discontent 
-		"SECRETA.SMK", //Secret_a
-		"SECRETB.SMK", //Secret_b
-		"SECRETC.SMK", //Secret_c
-	//Armageddon's Blade
-		//Armageddon's Blade 
- 		"H3ABab1.smk", //ArmageddonsBlade_a
- 		"H3ABab2.smk", //ArmageddonsBlade_b
- 		"H3ABab3.smk", //ArmageddonsBlade_c
- 		"H3ABab4.smk", //ArmageddonsBlade_d
- 		"H3ABab5.smk", //ArmageddonsBlade_e
- 		"H3ABab6.smk", //ArmageddonsBlade_f
- 		"H3ABab7.smk", //ArmageddonsBlade_g
- 		"H3ABab8.smk", //ArmageddonsBlade_h
- 		"H3ABab9.smk", //ArmageddonsBlade_end
- 		//Dragon's Blood 
- 		"H3ABdb1.smk", //DragonsBlood_a
- 		"H3ABdb2.smk", //DragonsBlood_b
- 		"H3ABdb3.smk", //DragonsBlood_c
- 		"H3ABdb4.smk", //DragonsBlood_d
- 		"H3ABdb5.smk", //DragonsBlood_end
- 		//Dragon Slayer 
- 		"H3ABds1.smk", //DragonSlayer_a
- 		"H3ABds2.smk", //DragonSlayer_b
- 		"H3ABds3.smk", //DragonSlayer_c
- 		"H3ABds4.smk", //DragonSlayer_d
- 		"H3ABds5.smk", //DragonSlayer_end
- 		//Festival of Life 
- 		"H3ABfl1.smk", //FestivalOfLife_a
- 		"H3ABfl2.smk", //FestivalOfLife_b
-		"H3ABfl3.smk", //FestivalOfLife_c
- 		"H3ABfl4.smk", //FestivalOfLife_d
- 		"H3ABfl5.smk", //FestivalOfLife_end
- 		//Foolhardy Waywardness 
-		"H3ABfw1.smk", //FoolhardyWaywardness_a
- 		"H3ABfw2.smk", //FoolhardyWaywardness_b
-		"H3ABfw3.smk", //FoolhardyWaywardness_c
- 		"H3ABfw4.smk", //FoolhardyWaywardness_d
- 		"H3ABfw5.smk", //FoolhardyWaywardness_end
- 		//Playing with Fire 
- 		"H3ABpf1.smk", //PlayingWithFire_a
- 		"H3ABpf2.smk", //PlayingWithFire_b
- 		"H3ABpf3.smk", //PlayingWithFire_c
- 		"H3ABpf4.smk", //PlayingWithFire_end
-	//Shadow of Death Campaigns 
- 		//Birth of a Barbarian 
- 		"H3x2_BBa.smk", //BirthOfABarbarian_a
- 		"H3x2_BBb.smk", //BirthOfABarbarian_b
- 		"H3x2_BBc.smk", //BirthOfABarbarian_c
- 		"H3x2_BBd.smk", //BirthOfABarbarian_d
- 		"H3x2_BBe.smk", //BirthOfABarbarian_e
- 		"H3x2_BBf.smk", //BirthOfABarbarian_end
- 		//Elixir of Life 
- 		"H3x2_Ela.smk", //ElixirOfLife_a
- 		"H3x2_Elb.smk", //ElixirOfLife_b
- 		"H3x2_Elc.smk", //ElixirOfLife_c
- 		"H3x2_Eld.smk", //ElixirOfLife_d
- 		"H3x2_Ele.smk", //ElixirOfLife_end
- 		//Hack and Slash 
- 		"H3x2_HSa.smk", //HackAndSlash_a
- 		"EVIL2C.SMK", //HackAndSlash_b
- 		"H3x2_HSc.smk", //HackAndSlash_c
- 		"H3x2_HSd.smk", //HackAndSlash_d
- 		"H3x2_HSe.smk", //HackAndSlash_end
-		//New Beginning 
- 		"H3x2_NBa.smk", //NewBeginning_a
- 		"H3x2_NBb.smk", //NewBeginning_b
- 		"H3x2_Nbc.smk", //NewBeginning_c
- 		"H3x2_Nbd.smk", //NewBeginning_d
- 		"H3x2_Nbe.smk", //NewBeginning_end
- 		//Rise of the Necromancer 
- 		"H3x2_RNa.smk", //RiseOfTheNecromancer_a
- 		"H3x2_RNb.smk", //RiseOfTheNecromancer_b
- 		"H3x2_RNc.smk", //RiseOfTheNecromancer_c
- 		"H3x2_RNd.smk", //RiseOfTheNecromancer_d
- 		"H3x2_RNe1.smk", //RiseOfTheNecromancer_end
- 		//Spectre of Power 
-		"H3x2_SPa.smk", //SpectreOfPower_a
- 		"H3x2_SPb.smk", //SpectreOfPower_b
- 		"H3x2_SPc.smk", //SpectreOfPower_c
- 		"H3x2_SPd.smk", //SpectreOfPower_d
- 		"H3x2_SPe.smk", //SpectreOfPower_end
- 		//Unholy Alliance 
- 		"H3x2_UAa.smk", //UnholyAlliance_a
-		"H3x2_UAb.smk", //UnholyAlliance_b
-		"H3x2_UAc.smk", //UnholyAlliance_c
-		"H3x2_UAd.smk", //UnholyAlliance_d
-		"H3x2_UAe.smk", //UnholyAlliance_e
-		"H3x2_UAf.smk", //UnholyAlliance_f
-		"H3x2_UAg.smk", //UnholyAlliance_g
-		"H3x2_UAh.smk", //UnholyAlliance_h
-		"H3x2_UAi.smk", //UnholyAlliance_i
-		"H3x2_UAj.smk", //UnholyAlliance_j
-		"H3x2_UAk.smk", //UnholyAlliance_k
-		"H3x2_UAl.smk", //UnholyAlliance_l
- 		"H3x2_UAm.smk", //UnholyAlliance_end //H3x2_UAm.bik?
-	],
-
-	"music" : [
-		// Use CmpMusic.txt from H3 instead
-	],
-
-	"voice" : [
-	//Restoration of Erathia
-		"G1A", //Long live the Queen 1 
-		"G1B", //Long live the Queen 2 
-		"G1C", //Long live the Queen 3
-		"E1A.wav", //Dungeons and Devils 1 
-		"E1B.wav", //Dungeons and Devils 2 
-		"E1C.wav", //Dungeons and Devils 3
-		"N1A", //Spoils of War 1 
-		"N1B", //Spoils of War 2 
-		"N1C_D", //Spoils of War 3
-		"G2A", //Liberation 1 
-		"G2B", //Liberation 2 
-		"G2C", //Liberation 3 
-		"G2D", //Liberation 4
-		"E2A.wav", //Long live the King 1 
-		"E2AE.wav", //Long live the King 1end 
-		"E2B.wav", //Long live the King 2 
-		"E2C.wav", //Long live the King 3 
-		"E2D.wav", //Long live the King 4
-		"G3A", //Song for the Father 1 
-		"G3B", //Song for the Father 2 
-		"G3C", //Song for the Father 3
-		"S1A", //Seeds of discontent 1 
-		"S1B", //Seeds of discontent 2 
-		"S1C", //Seeds of discontent 3
-	//Armageddon's Blade
-		"ABvoAB1.wav", //Armageddon's Blade 1 
-		"ABvoAB2.wav", //Armageddon's Blade 2 
-		"ABvoAB3.wav", //Armageddon's blade 3 
-		"ABvoAB4.wav", //Armageddon's blade 4 
-		"ABvoAB5.wav", //Armageddon's blade 5 
-		"ABvoAB6.wav", //Armageddon's blade 6 
-		"ABvoAB7.wav", //Armageddon's blade 7 
-		"ABvoAB8.wav", //Armageddon's blade 8 
-		"ABvoAB9.wav", //Armageddon's blade 8end
-		"ABvoDB1.wav", //Dragon's Blood 1 
-		"ABvoDB2.wav", //Dragon's Blood 2 
-		"ABvoDB3.wav", //Dragon's Blood 3 
-		"ABvoDB4.wav", //Dragon's Blood 4 
-		"ABvoDB5.wav", //Dragon's Blood 4end
-		"ABvoDS1.wav", //Dragon Slayer 1 
-		"ABvoDS2.wav", //Dragon Slayer 2 
-		"ABvoDS3.wav", //Dragon Slayer 3 
-		"ABvoDS4.wav", //Dragon Slayer 4 
-		"ABvoDS5.wav", //Dragon Slayer 4end 
-		"ABvoFL1.wav", //Festival of Life 1 
-		"ABvoFL2.wav", //Festival of Life 2 
-		"ABvoFL3.wav", //Festival of Life 3 
-		"ABvoFL4.wav", //Festival of Life 4 
-		"ABvoFL5.wav", //Festival of Life 4end
-		"ABvoFW1.wav", //Foolhardy Waywardness 1 
-		"ABvoFW2.wav", //Foolhardy Waywardness 2 
-		"ABvoFW3.wav", //Foolhardy Waywardness 3 
-		"ABvoFW4.wav", //Foolhardy Waywardness 4 
-		"ABvoFW5.wav", //Foolhardy Waywardness 4end 
-		"ABvoPF1.wav", //Playing with Fire 1 
-		"ABvoPF2.wav", //Playing with Fire 2 
-		"ABvoPF3.wav", //Playing with Fire 3 
-		"ABvoPF4.wav", //Playing with Fire 3end
-	//Shadow of Death Campaigns 
-		"H3x2BBa", //Birth of a Barbarian 1 
-		"H3x2BBb", //Birth of a Barbarian 2 
-		"H3x2BBc", //Birth of a Barbarian 3 
-		"H3x2BBd", //Birth of a Barbarian 4 
-		"H3x2BBe", //Birth of a Barbarian 5 
-		"H3x2BBf", //Birth of a Barbarian 5end
-		"H3x2ELa", //Elixir of life 1 
-		"H3x2ELb", //Elixir of life 2 
-		"H3x2ELc", //Elixir of life 3 
-		"H3x2ELd", //Elixir of life 4 
-		"H3x2ELe", //Elixir of life 4end 
-		"H3x2HSa", //Hack and Slash 1 
-		"H3x2HSb", //Hack and Slash 2 
-		"H3x2HSc", //Hack and Slash 3 
-		"H3x2HSd", //Hack and Slash 4 
-		"H3x2HSe", //Hack and Slash 4end 
-		"H3x2NBa", //New Beginning 1 
-		"H3x2NBb", //New Beginning 2 
-		"H3x2NBc", //New Beginning 3 
-		"H3x2NBd", //New Beginning 4 
-		"H3x2NBe", //New Beginning 4end 
-		"H3x2RNa", //Rise of the Necromancer 1 
-		"H3x2RNb", //Rise of the Necromancer 2 
-		"H3x2RNc", //Rise of the Necromancer 3 
-		"H3x2RNd", //Rise of the Necromancer 4 
-		"H3x2RNe", //Rise of the Necromancer 4end 
-		"H3x2SPa", //Spectre of Power 1 
-		"H3x2Spb", //Spectre of Power 2 
-		"H3x2Spc", //Spectre of Power 3 
-		"H3x2Spd", //Spectre of Power 4 
-		"H3x2Spe", //Spectre of Power 4end 
-		"H3x2UAa", //Unholy alliance 1 
-		"H3x2UAb", //Unholy alliance 2 
-		"H3x2UAc", //Unholy alliance 3 
-		"H3x2UAd", //Unholy alliance 4 
-		"H3x2UAe", //Unholy alliance 5 
-		"H3x2UAf", //Unholy alliance 6 
-		"H3x2UAg", //Unholy alliance 7 
-		"H3x2UAh", //Unholy alliance 8 
-		"H3x2UAi", //Unholy alliance 9 
-		"H3x2UAj", //Unholy alliance 10 
-		"H3x2UAk", //Unholy alliance 11 
-		"H3x2UAl", //Unholy alliance 12 
-		"H3x2UAm" //Unholy alliance 12end
-	]
-}

+ 189 - 4
config/campaignOverrides.json

@@ -1,11 +1,196 @@
 {
-	"DATA/GOOD3" : { // RoE - "Song for the Father"
-		"outroVideo": "Endgame"
+	/// RoE CAMPAIGNS
+	
+	"DATA/GOOD1" : { //Long live the Queen
+		"restrictedGarrisonsForAI" : true,
+		"scenarios": [
+			{ "voiceProlog": "G1A" },
+			{ "voiceProlog": "G1B" },
+			{ "voiceProlog": "G1C" }
+		]
+	},
+	"DATA/EVIL1" : { // Dungeons and Devils
+		"restrictedGarrisonsForAI" : true,
+		"scenarios": [
+			{ "voiceProlog": "E1A" },
+			{ "voiceProlog": "E1B" },
+			{ "voiceProlog": "E1C" }
+		]
+	},
+	"DATA/GOOD2" : { // Liberation
+		"restrictedGarrisonsForAI" : true,
+		"scenarios": [
+			{ "voiceProlog": "G2A" },
+			{ "voiceProlog": "G2B" },
+			{ "voiceProlog": "G2C" },
+			{ "voiceProlog": "G2D" }
+		]
 	},
-	"DATA/AB" : { // AB Intro
+	"DATA/NEUTRAL1" : { // Spoils of War
+		"restrictedGarrisonsForAI" : true,
+		"scenarios": [
+			{ "voiceProlog": "N1A" },
+			{ "voiceProlog": "N1B" },
+			{ "voiceProlog": "N1C_D" }
+		]
+	},
+	"DATA/EVIL2" : { // Long live the King
+		"restrictedGarrisonsForAI" : true,
+		"scenarios": [
+			{ "voiceProlog": "E2A", "voiceEpilog": "E2AE" },
+			{ "voiceProlog": "E2B" },
+			{ "voiceProlog": "E2C" },
+			{ "voiceProlog": "E2D" }
+		]
+	},
+	
+	"DATA/GOOD3" : { // Song for the Father
+		"outroVideo": "Endgame",
+		"restrictedGarrisonsForAI" : true,
+		"scenarios": [
+			{ "voiceProlog": "G3A" },
+			{ "voiceProlog": "G3B" },
+			{ "voiceProlog": "G3C" }
+		]
+	},
+	"DATA/SECRET1" : { // Seeds of discontent
+		"restrictedGarrisonsForAI" : true,
+		"scenarios": [
+			{ "voiceProlog": "S1A" },
+			{ "voiceProlog": "S1B" },
+			{ "voiceProlog": "S1C" }
+		]
+	},
+	
+	/// AB CAMPAIGNS
+
+	"DATA/AB" : { // Armageddon's Blade
 		"introVideo": "H3X1intr",
-		"videoRim": "IntroRm2"
+		"videoRim": "IntroRm2",
+		"scenarios": [
+			{ "voiceProlog": "ABvoAB1" },
+			{ "voiceProlog": "ABvoAB2" },
+			{ "voiceProlog": "ABvoAB3" },
+			{ "voiceProlog": "ABvoAB4" },
+			{ "voiceProlog": "ABvoAB5" },
+			{ "voiceProlog": "ABvoAB6" },
+			{ "voiceProlog": "ABvoAB7" },
+			{ "voiceProlog": "ABvoAB8", "voiceEpilog": "ABvoAB9" }
+		]
+	},
+	"DATA/BLOOD" : { // Dragon's Blood
+		"scenarios": [
+			{ "voiceProlog": "ABvoDB1" },
+			{ "voiceProlog": "ABvoDB2" },
+			{ "voiceProlog": "ABvoDB3" },
+			{ "voiceProlog": "ABvoDB4", "voiceEpilog": "ABvoDB5" }
+		]
+	},
+	"DATA/SLAYER" : { // Dragon Slayer
+		"scenarios": [
+			{ "voiceProlog": "ABvoDS1" },
+			{ "voiceProlog": "ABvoDS2" },
+			{ "voiceProlog": "ABvoDS3" },
+			{ "voiceProlog": "ABvoDS4", "voiceEpilog": "ABvoDS5" }
+		]
+	},
+	"DATA/FESTIVAL" : { // Festival of Life
+		"scenarios": [
+			{ "voiceProlog": "ABvoFL1" },
+			{ "voiceProlog": "ABvoFL2" },
+			{ "voiceProlog": "ABvoFL3" },
+			{ "voiceProlog": "ABvoFL4", "voiceEpilog": "ABvoFL5" }
+		]
+	},
+	"DATA/FIRE" : { // Playing with Fire
+		"scenarios": [
+			{ "voiceProlog": "ABvoPF1" },
+			{ "voiceProlog": "ABvoPF2" },
+			{ "voiceProlog": "ABvoPF3", "voiceEpilog": "ABvoPF4" }
+		]
+	},
+	"DATA/FOOL" : { // Foolhardy Waywardness
+		"scenarios": [
+			{ "voiceProlog": "ABvoFW1" },
+			{ "voiceProlog": "ABvoFW2" },
+			{ "voiceProlog": "ABvoFW3" },
+			{ "voiceProlog": "ABvoFW4", "voiceEpilog": "ABvoFW5" }
+		]
+	},
+
+	/// SoD CAMPAIGNS
+
+	"DATA/GELU" : { // Elixir of life
+		"scenarios": [
+			{ "voiceProlog": "H3x2ELa" },
+			{ "voiceProlog": "H3x2ELb" },
+			{ "voiceProlog": "H3x2ELc" },
+			{ "voiceProlog": "H3x2ELd", "voiceEpilog": "H3x2ELe" }
+		]
+	},
+	"DATA/CRAG" : { // Hack and Slash
+		"scenarios": [
+			{ "voiceProlog": "H3x2HSa" },
+			{ "voiceProlog": "H3x2HSb" },
+			{ "voiceProlog": "H3x2HSc" },
+			{ "voiceProlog": "H3x2HSd", "voiceEpilog": "H3x2HSe" }
+		]
+	},
+	"DATA/SANDRO" : { // Rise of the Necromancer
+		"scenarios": [
+			{ "voiceProlog": "H3x2RNa" },
+			{ "voiceProlog": "H3x2RNb" },
+			{ "voiceProlog": "H3x2RNc" },
+			{ "voiceProlog": "H3x2RNd", "voiceEpilog": "H3x2RNe" }
+		]
+	},
+	"DATA/GEM" : { // New Beginning
+		"heroGemSorceress" : "gem", // Gem (Sorceress class)
+		"scenarios": [
+			{ "voiceProlog": "H3x2NBa" },
+			{ "voiceProlog": "H3x2NBb" },
+			{ "voiceProlog": "H3x2NBc" },
+			{ "voiceProlog": "H3x2NBd", "voiceEpilog": "H3x2NBe" }
+		]
+	},
+	"DATA/YOG" : { // Birth of a Barbarian
+		"heroYogWizard" : "solmyr", // Yog (based on Solmyr)
+		"scenarios": [
+			{ "voiceProlog": "H3x2BBa" },
+			{ "voiceProlog": "H3x2BBb" },
+			{ "voiceProlog": "H3x2BBc" },
+			{ "voiceProlog": "H3x2BBd" },
+			{ "voiceProlog": "H3x2BBe", "voiceEpilog": "H3x2BBf" }
+		]
+	},
+	"DATA/FINAL" : { // Unholy Alliance
+		"heroGemSorceress" : "gem", // Gem (Sorceress class)
+		"scenarios": [
+			{ "voiceProlog": "H3x2UAa" },
+			{ "voiceProlog": "H3x2UAb" },
+			{ "voiceProlog": "H3x2UAc" },
+			{ "voiceProlog": "H3x2UAd" },
+			{ "voiceProlog": "H3x2UAe" },
+			{ "voiceProlog": "H3x2UAf" },
+			{ "voiceProlog": "H3x2UAg" },
+			{ "voiceProlog": "H3x2UAh" },
+			{ "voiceProlog": "H3x2UAi" },
+			{ "voiceProlog": "H3x2UAj" },
+			{ "voiceProlog": "H3x2UAk" },
+			{ "voiceProlog": "H3x2UAl", "voiceEpilog": "H3x2UAm" }
+		]
+	},
+	"DATA/SECRET" : { // Spectre of Power
+		"scenarios": [
+			{ "voiceProlog": "H3x2SPa" },
+			{ "voiceProlog": "H3x2Spb" },
+			{ "voiceProlog": "H3x2Spc" },
+			{ "voiceProlog": "H3x2Spd", "voiceEpilog": "H3x2Spe" }
+		]
 	},
+	
+	/// CHRONICLES CAMPAIGNS
+	
 	"MAPS/CHRONICLES/HC1_MAIN" : { // Heroes Chronicles 1
 		"regions":
 		{

+ 238 - 0
config/campaignRegions.json

@@ -0,0 +1,238 @@
+{
+	// RoE
+	
+	"good1" : {
+		"prefix": "G1",
+		"colorSuffixLength": 1,
+		"desc": [
+			{ "infix": "A", "x": 57, "y": 314 },
+			{ "infix": "B", "x": 137, "y": 309 },
+			{ "infix": "C", "x": 44, "y": 163 }
+		]
+	},
+
+	"good2" : {
+		"prefix": "G2",
+		"colorSuffixLength": 1,
+		"desc": [
+			{ "infix": "A", "x": 56, "y": 90 },
+			{ "infix": "B", "x": 316, "y": 49 },
+			{ "infix": "C", "x": 54, "y": 378 },
+			{ "infix": "D", "x": 151, "y": 126 }
+		]
+	},
+
+	"good3" : {
+		"prefix": "G3",
+		"colorSuffixLength": 1,
+		"desc": [
+			{ "infix": "A", "x": 289, "y": 376 },
+			{ "infix": "B", "x": 60, "y": 147 },
+			{ "infix": "C", "x": 131, "y": 202 }
+		]
+	},
+
+	"evil1" : {
+		"prefix": "E1",
+		"colorSuffixLength": 1,
+		"desc": [
+			{ "infix": "A", "x": 270, "y": 332 },
+			{ "infix": "B", "x": 138, "y": 113 },
+			{ "infix": "C", "x": 26, "y": 70 },
+			{ "infix": "P1", "x": 256, "y": 127 },
+			{ "infix": "P2", "x": 57, "y": 314 },
+			{ "infix": "P3", "x": 137, "y": 310 },
+			{ "infix": "P4", "x": 44, "y": 163 }
+		]
+	},
+
+	"evil2" : {
+		"prefix": "E2",
+		"colorSuffixLength": 1,
+		"desc": [
+			{ "infix": "A", "x": 131, "y": 202 },
+			{ "infix": "B", "x": 60, "y": 145 },
+			{ "infix": "C", "x": 92, "y": 261 },
+			{ "infix": "D", "x": 218, "y": 307 }
+		]
+	},
+
+	"neutral1" : {
+		"prefix": "N1",
+		"colorSuffixLength": 1,
+		"desc": [
+			{ "infix": "A", "x": 42, "y": 94 },
+			{ "infix": "B", "x": 309, "y": 290 },
+			{ "infix": "CD", "x": 188, "y": 202 }
+		]
+	},
+
+	"secret1" : {
+		"prefix": "S1",
+		"colorSuffixLength": 1,
+		"desc": [
+			{ "infix": "A", "x": 263, "y": 199 },
+			{ "infix": "B", "x": 182, "y": 210 },
+			{ "infix": "C", "x": 82, "y": 152 }
+		]
+	},
+
+	// AB
+
+	"dragonSlayer" : {
+		"prefix": "BR",
+		"colorSuffixLength": 2,
+		"desc": [
+			{ "infix": "A", "x": 18, "y": 233 },
+			{ "infix": "B", "x": 125, "y": 381 },
+			{ "infix": "C", "x": 224, "y": 357 },
+			{ "infix": "D", "x": 192, "y": 320 }
+		]
+	},
+
+	"foolhardyWaywardness" : {
+		"prefix": "IS",
+		"colorSuffixLength": 2,
+		"desc": [
+			{ "infix": "A", "x": 294, "y": 399 },
+			{ "infix": "B", "x": 183, "y": 293 },
+			{ "infix": "C", "x": 40, "y": 92 },
+			{ "infix": "D", "x": 294, "y": 398 }
+		]
+	},
+
+	"festivalOfLife" : {
+		"prefix": "KR",
+		"colorSuffixLength": 2,
+		"desc": [
+			{ "infix": "A", "x": 148, "y": 323 },
+			{ "infix": "B", "x": 192, "y": 235 },
+			{ "infix": "C", "x": 136, "y": 158 },
+			{ "infix": "D", "x": 87, "y": 107 }
+		]
+	},
+
+	"dragonsBlood" : {
+		"prefix": "NI",
+		"colorSuffixLength": 2,
+		"desc": [
+			{ "infix": "A", "x": 118, "y": 111 },
+			{ "infix": "B", "x": 223, "y": 145 },
+			{ "infix": "C", "x": 320, "y": 213 },
+			{ "infix": "D", "x": 233, "y": 250 }
+		]
+	},
+
+	"playingWithFire" : {
+		"prefix": "TA",
+		"colorSuffixLength": 2,
+		"desc": [
+			{ "infix": "A", "x": 228, "y": 233 },
+			{ "infix": "B", "x": 147, "y": 194 },
+			{ "infix": "C", "x": 112, "y": 97 }
+		]
+	},
+
+	"armageddonsBlade" : {
+		"prefix": "AR",
+		"colorSuffixLength": 2,
+		"desc": [
+			{ "infix": "A", "x": 135, "y": 238 },
+			{ "infix": "B", "x": 135, "y": 121 },
+			{ "infix": "C", "x": 206, "y": 155 },
+			{ "infix": "D", "x": 105, "y": 397 },
+			{ "infix": "E", "x": 109, "y": 275 },
+			{ "infix": "F", "x": 158, "y": 188 },
+			{ "infix": "G", "x": 200, "y": 261 },
+			{ "infix": "H", "x": 232, "y": 197 }
+		]
+	},
+
+	// SoD
+
+	"hackAndSlash" : {
+		"prefix": "HS",
+		"colorSuffixLength": 2,
+		"desc": [
+			{ "infix": "A", "x": 141, "y": 326 },
+			{ "infix": "B", "x": 238, "y": 275 },
+			{ "infix": "C", "x": 22, "y": 161 },
+			{ "infix": "D", "x": 5, "y": 9 }
+		]
+	},
+
+	"birthOfBarbarian" : {
+		"prefix": "BB",
+		"colorSuffixLength": 2,
+		"desc": [
+			{ "infix": "A", "x": 167, "y": 342 },
+			{ "infix": "B", "x": 217, "y": 263 },
+			{ "infix": "C", "x": 0, "y": 71 },
+			{ "infix": "D", "x": 291, "y": 79 },
+			{ "infix": "E", "x": 316, "y": 199 }
+		]
+	},
+
+	"newBeginning" : {
+		"prefix": "NB",
+		"colorSuffixLength": 2,
+		"desc": [
+			{ "infix": "A", "x": 6, "y": 292 },
+			{ "infix": "B", "x": 161, "y": 334 },
+			{ "infix": "C", "x": 63, "y": 195 },
+			{ "infix": "D", "x": 56, "y": 46 }
+		]
+	},
+
+	"elixirOfLife" : {
+		"prefix": "EL",
+		"colorSuffixLength": 2,
+		"desc": [
+			{ "infix": "A", "x": 11, "y": 73 },
+			{ "infix": "B", "x": 0, "y": 241 },
+			{ "infix": "C", "x": 254, "y": 34 },
+			{ "infix": "D", "x": 91, "y": 144 }
+		]
+	},
+
+	"riseOfTheNecromancer" : {
+		"prefix": "RN",
+		"colorSuffixLength": 2,
+		"desc": [
+			{ "infix": "A", "x": 84, "y": 319 },
+			{ "infix": "B", "x": 194, "y": 275 },
+			{ "infix": "C", "x": 67, "y": 185 },
+			{ "infix": "D", "x": 77, "y": 30 }
+		]
+	},
+
+	"unholyAlliance" : {
+		"prefix": "UA",
+		"colorSuffixLength": 2,
+		"desc": [
+			{ "infix": "A", "x": 157, "y": 409 },
+			{ "infix": "B", "x": 62, "y": 346 },
+			{ "infix": "C", "x": 8, "y": 8 },
+			{ "infix": "D", "x": 206, "y": 1 },
+			{ "infix": "E", "x": 132, "y": 357 },
+			{ "infix": "F", "x": 184, "y": 83 },
+			{ "infix": "G", "x": 159, "y": 263 },
+			{ "infix": "H", "x": 108, "y": 173 },
+			{ "infix": "I", "x": 55, "y": 127 },
+			{ "infix": "J", "x": 9, "y": 252 },
+			{ "infix": "K", "x": 210, "y": 176 },
+			{ "infix": "L", "x": 260, "y": 210 }
+		]
+	},
+
+	"spectreOfPower" : {
+		"prefix": "SP",
+		"colorSuffixLength": 2,
+		"desc": [
+			{ "infix": "A", "x": 7, "y": 295 },
+			{ "infix": "B", "x": 44, "y": 141 },
+			{ "infix": "C", "x": 141, "y": 21 },
+			{ "infix": "D", "x": 243, "y": 156 }
+		]
+	}
+}

+ 0 - 234
config/campaign_regions.json

@@ -1,234 +0,0 @@
-{
-	"campaign_regions": [
-		{
-			"prefix": "G1",
-			"colorSuffixLength": 1,
-			"desc": [
-				{ "infix": "A", "x": 57, "y": 314 },
-				{ "infix": "B", "x": 137, "y": 309 },
-				{ "infix": "C", "x": 44, "y": 163 }
-			]
-		},
-
-		{
-			"prefix": "G2",
-			"colorSuffixLength": 1,
-			"desc": [
-				{ "infix": "A", "x": 56, "y": 90 },
-				{ "infix": "B", "x": 316, "y": 49 },
-				{ "infix": "C", "x": 54, "y": 378 },
-				{ "infix": "D", "x": 151, "y": 126 }
-			]
-		},
-
-		{
-			"prefix": "G3",
-			"colorSuffixLength": 1,
-			"desc": [
-				{ "infix": "A", "x": 289, "y": 376 },
-				{ "infix": "B", "x": 60, "y": 147 },
-				{ "infix": "C", "x": 131, "y": 202 }
-			]
-		},
-
-		{
-			"prefix": "E1",
-			"colorSuffixLength": 1,
-			"desc": [
-				{ "infix": "A", "x": 270, "y": 332 },
-				{ "infix": "B", "x": 138, "y": 113 },
-				{ "infix": "C", "x": 26, "y": 70 },
-				{ "infix": "P1", "x": 256, "y": 127 },
-				{ "infix": "P2", "x": 57, "y": 314 },
-				{ "infix": "P3", "x": 137, "y": 310 },
-				{ "infix": "P4", "x": 44, "y": 163 }
-			]
-		},
-
-		{
-			"prefix": "E2",
-			"colorSuffixLength": 1,
-			"desc": [
-				{ "infix": "A", "x": 131, "y": 202 },
-				{ "infix": "B", "x": 60, "y": 145 },
-				{ "infix": "C", "x": 92, "y": 261 },
-				{ "infix": "D", "x": 218, "y": 307 }
-			]
-		},
-
-		{
-			"prefix": "N1",
-			"colorSuffixLength": 1,
-			"desc": [
-				{ "infix": "A", "x": 42, "y": 94 },
-				{ "infix": "B", "x": 309, "y": 290 },
-				{ "infix": "CD", "x": 188, "y": 202 }
-			]
-		},
-
-		{
-			"prefix": "S1",
-			"colorSuffixLength": 1,
-			"desc": [
-				{ "infix": "A", "x": 263, "y": 199 },
-				{ "infix": "B", "x": 182, "y": 210 },
-				{ "infix": "C", "x": 82, "y": 152 }
-			]
-		},
-
-		{
-			"prefix": "BR",
-			"colorSuffixLength": 2,
-			"desc": [
-				{ "infix": "A", "x": 18, "y": 233 },
-				{ "infix": "B", "x": 125, "y": 381 },
-				{ "infix": "C", "x": 224, "y": 357 },
-				{ "infix": "D", "x": 192, "y": 320 }
-			]
-		},
-
-		{
-			"prefix": "IS",
-			"colorSuffixLength": 2,
-			"desc": [
-				{ "infix": "A", "x": 294, "y": 399 },
-				{ "infix": "B", "x": 183, "y": 293 },
-				{ "infix": "C", "x": 40, "y": 92 },
-				{ "infix": "D", "x": 294, "y": 398 }
-			]
-		},
-
-		{
-			"prefix": "KR",
-			"colorSuffixLength": 2,
-			"desc": [
-				{ "infix": "A", "x": 148, "y": 323 },
-				{ "infix": "B", "x": 192, "y": 235 },
-				{ "infix": "C", "x": 136, "y": 158 },
-				{ "infix": "D", "x": 87, "y": 107 }
-			]
-		},
-
-		{
-			"prefix": "NI",
-			"colorSuffixLength": 2,
-			"desc": [
-				{ "infix": "A", "x": 118, "y": 111 },
-				{ "infix": "B", "x": 223, "y": 145 },
-				{ "infix": "C", "x": 320, "y": 213 },
-				{ "infix": "D", "x": 233, "y": 250 }
-			]
-		},
-
-		{
-			"prefix": "TA",
-			"colorSuffixLength": 2,
-			"desc": [
-				{ "infix": "A", "x": 228, "y": 233 },
-				{ "infix": "B", "x": 147, "y": 194 },
-				{ "infix": "C", "x": 112, "y": 97 }
-			]
-		},
-
-		{
-			"prefix": "AR",
-			"colorSuffixLength": 2,
-			"desc": [
-				{ "infix": "A", "x": 135, "y": 238 },
-				{ "infix": "B", "x": 135, "y": 121 },
-				{ "infix": "C", "x": 206, "y": 155 },
-				{ "infix": "D", "x": 105, "y": 397 },
-				{ "infix": "E", "x": 109, "y": 275 },
-				{ "infix": "F", "x": 158, "y": 188 },
-				{ "infix": "G", "x": 200, "y": 261 },
-				{ "infix": "H", "x": 232, "y": 197 }
-			]
-		},
-
-		{
-			"prefix": "HS",
-			"colorSuffixLength": 2,
-			"desc": [
-				{ "infix": "A", "x": 141, "y": 326 },
-				{ "infix": "B", "x": 238, "y": 275 },
-				{ "infix": "C", "x": 22, "y": 161 },
-				{ "infix": "D", "x": 5, "y": 9 }
-			]
-		},
-
-		{
-			"prefix": "BB",
-			"colorSuffixLength": 2,
-			"desc": [
-				{ "infix": "A", "x": 167, "y": 342 },
-				{ "infix": "B", "x": 217, "y": 263 },
-				{ "infix": "C", "x": 0, "y": 71 },
-				{ "infix": "D", "x": 291, "y": 79 },
-				{ "infix": "E", "x": 316, "y": 199 }
-			]
-		},
-
-		{
-			"prefix": "NB",
-			"colorSuffixLength": 2,
-			"desc": [
-				{ "infix": "A", "x": 6, "y": 292 },
-				{ "infix": "B", "x": 161, "y": 334 },
-				{ "infix": "C", "x": 63, "y": 195 },
-				{ "infix": "D", "x": 56, "y": 46 }
-			]
-		},
-
-		{
-			"prefix": "EL",
-			"colorSuffixLength": 2,
-			"desc": [
-				{ "infix": "A", "x": 11, "y": 73 },
-				{ "infix": "B", "x": 0, "y": 241 },
-				{ "infix": "C", "x": 254, "y": 34 },
-				{ "infix": "D", "x": 91, "y": 144 }
-			]
-		},
-
-		{
-			"prefix": "RN",
-			"colorSuffixLength": 2,
-			"desc": [
-				{ "infix": "A", "x": 84, "y": 319 },
-				{ "infix": "B", "x": 194, "y": 275 },
-				{ "infix": "C", "x": 67, "y": 185 },
-				{ "infix": "D", "x": 77, "y": 30 }
-			]
-		},
-
-		{
-			"prefix": "UA",
-			"colorSuffixLength": 2,
-			"desc": [
-				{ "infix": "A", "x": 157, "y": 409 },
-				{ "infix": "B", "x": 62, "y": 346 },
-				{ "infix": "C", "x": 8, "y": 8 },
-				{ "infix": "D", "x": 206, "y": 1 },
-				{ "infix": "E", "x": 132, "y": 357 },
-				{ "infix": "F", "x": 184, "y": 83 },
-				{ "infix": "G", "x": 159, "y": 263 },
-				{ "infix": "H", "x": 108, "y": 173 },
-				{ "infix": "I", "x": 55, "y": 127 },
-				{ "infix": "J", "x": 9, "y": 252 },
-				{ "infix": "K", "x": 210, "y": 176 },
-				{ "infix": "L", "x": 260, "y": 210 }
-			]
-		},
-
-		{
-			"prefix": "SP",
-			"colorSuffixLength": 2,
-			"desc": [
-				{ "infix": "A", "x": 7, "y": 295 },
-				{ "infix": "B", "x": 44, "y": 141 },
-				{ "infix": "C", "x": 141, "y": 21 },
-				{ "infix": "D", "x": 243, "y": 156 }
-			]
-		}
-	]
-}

+ 10 - 0
config/creatures/dungeon.json

@@ -429,6 +429,11 @@
 			{
 				"type" : "DRAGON_NATURE"
 			},
+			"dragonSkeleton" : 
+			{
+				"type" : "SKELETON_TRANSFORMER_TARGET",
+				"subtype" : "boneDragon"
+			},
 			"canFly" :
 			{
 				"type" : "FLYING"
@@ -474,6 +479,11 @@
 			{
 				"type" : "DRAGON_NATURE"
 			},
+			"dragonSkeleton" : 
+			{
+				"type" : "SKELETON_TRANSFORMER_TARGET",
+				"subtype" : "boneDragon"
+			},
 			"canFly" :
 			{
 				"type" : "FLYING"

+ 10 - 0
config/creatures/fortress.json

@@ -349,6 +349,11 @@
 			{
 				"type" : "ATTACKS_ALL_ADJACENT"
 			},
+			"dragonSkeleton" : 
+			{
+				"type" : "SKELETON_TRANSFORMER_TARGET",
+				"subtype" : "boneDragon"
+			},
 			"noRetaliation" :
 			{
 				"type" : "BLOCKS_RETALIATION"
@@ -385,6 +390,11 @@
 			{
 				"type" : "ATTACKS_ALL_ADJACENT"
 			},
+			"dragonSkeleton" : 
+			{
+				"type" : "SKELETON_TRANSFORMER_TARGET",
+				"subtype" : "boneDragon"
+			},
 			"noRetaliation" :
 			{
 				"type" : "BLOCKS_RETALIATION"

+ 39 - 4
config/creatures/neutral.json

@@ -75,6 +75,11 @@
 			{
 				"type" : "DRAGON_NATURE"
 			},
+			"dragonSkeleton" : 
+			{
+				"type" : "SKELETON_TRANSFORMER_TARGET",
+				"subtype" : "boneDragon"
+			},
 			"canFly" :
 			{
 				"type" : "FLYING"
@@ -83,13 +88,28 @@
 			{
 				"type" : "TWO_HEX_ATTACK_BREATH"
 			},
-			"fear" :
-			{
-				"type" : "FEAR"
+			"fearful" :
+			{
+				"type" : "FEARFUL",
+				"val" : 10,
+				"propagator": "BATTLE_WIDE",
+				"propagationUpdater" : "BONUS_OWNER_UPDATER",
+				"description" : "PLACEHOLDER",
+				"limiters" : [ 
+					"OPPOSITE_SIDE", 
+					{
+						"type" : "HAS_ANOTHER_BONUS_LIMITER",
+						"parameters" : [ "LIVING" ]
+					}
+				]
 			},
 			"fearless" :
 			{
-				"type" : "FEARLESS"
+				"type" : "FEARFUL",
+				"valueType" : "INDEPENDENT_MAX",
+				"description" : "PLACEHOLDER",
+				"val" : 0
+				
 			},
 			"spellImmunity" :
 			{
@@ -128,6 +148,11 @@
 			{
 				"type" : "DRAGON_NATURE"
 			},
+			"dragonSkeleton" : 
+			{
+				"type" : "SKELETON_TRANSFORMER_TARGET",
+				"subtype" : "boneDragon"
+			},
 			"crystals" :
 			{
 				"type" : "SPECIAL_CRYSTAL_GENERATION"
@@ -169,6 +194,11 @@
 			{
 				"type" : "DRAGON_NATURE"
 			},
+			"dragonSkeleton" : 
+			{
+				"type" : "SKELETON_TRANSFORMER_TARGET",
+				"subtype" : "boneDragon"
+			},
 			"canFly" :
 			{
 				"type" : "FLYING"
@@ -277,6 +307,11 @@
 			{
 				"type" : "DRAGON_NATURE"
 			},
+			"dragonSkeleton" : 
+			{
+				"type" : "SKELETON_TRANSFORMER_TARGET",
+				"subtype" : "boneDragon"
+			},
 			"canFly" :
 			{
 				"type" : "FLYING"

+ 10 - 0
config/creatures/rampart.json

@@ -366,6 +366,11 @@
 			{
 				"type" : "DRAGON_NATURE"
 			},
+			"dragonSkeleton" : 
+			{
+				"type" : "SKELETON_TRANSFORMER_TARGET",
+				"subtype" : "boneDragon"
+			},
 			"canFly" :
 			{
 				"type" : "FLYING"
@@ -411,6 +416,11 @@
 			{
 				"type" : "DRAGON_NATURE"
 			},
+			"dragonSkeleton" : 
+			{
+				"type" : "SKELETON_TRANSFORMER_TARGET",
+				"subtype" : "boneDragon"
+			},
 			"canFly" :
 			{
 				"type" : "FLYING"

+ 1 - 1
config/creatures/special.json

@@ -77,7 +77,7 @@
 	{
 		"special" : true,
 		"index": 146,
-		"level": 0,
+		"level": 5,
 		"faction": "neutral",
 		"doubleWide" : true,
 		"shots" : 24,

+ 36 - 36
config/factions/castle.json

@@ -80,43 +80,43 @@
 			"structures" :
 			{
 				"extraAnimation": { "animation" : "TBCSEXT2.def", "x" : 46,  "y" : 119 },
-				"mageGuild1":     { "animation" : "TBCSMAGE.def", "x" : 707, "y" : 166, "z" : 1, "border" : "TOCSMAG1.bmp", "area" : "TZCSMAG1.bmp" },
-				"mageGuild2":     { "animation" : "TBCSMAG2.def", "x" : 706, "y" : 135, "z" : 1, "border" : "TOCSMAG2.bmp", "area" : "TZCSMAG2.bmp" },
-				"mageGuild3":     { "animation" : "TBCSMAG3.def", "x" : 704, "y" : 107, "z" : 1, "border" : "TOCSM301.bmp", "area" : "TZCSM301.bmp" },
-				"mageGuild4":     { "animation" : "TBCSMAG4.def", "x" : 704, "y" : 76,  "z" : 1, "border" : "TOCSM401.bmp", "area" : "TZCSM401.bmp" },
-				"tavern":         { "animation" : "TBCSTVRN.def", "x" : 0,   "y" : 230, "z" : 2, "border" : "TOCSTAV1.bmp", "area" : "TZCSTAV1.bmp" },
-				"shipyard":       { "animation" : "TBCSDOCK.def", "x" : 478, "y" : 134, "z" : -3, "border" : "TOCSDKMS.bmp", "area" : "TZCSDKMS.bmp" },
-				"fort":           { "animation" : "TBCSCSTL.def", "x" : 595, "y" : 66,  "z" : -5, "border" : "TOCSCAS1.bmp", "area" : "TZCSCAS1.bmp" },
-				"citadel":        { "animation" : "TBCSCAS2.def", "x" : 478, "y" : 66,  "z" : -5, "border" : "TOCSCAS2.bmp", "area" : "TZCSCAS2.bmp" },
-				"castle":         { "animation" : "TBCSCAS3.def", "x" : 478, "y" : 37,  "z" : -5, "border" : "TOCSCAS3.bmp", "area" : "TZCSCAS3.bmp" },
-				"villageHall":    { "animation" : "TBCSHALL.def", "x" : 0,   "y" : 209, "z" : 1, "border" : "TOCSH101.bmp", "area" : "TZCSH101.bmp" },
-				"townHall":       { "animation" : "TBCSHAL2.def", "x" : 0,   "y" : 176, "z" : 1, "border" : "TOCSH201.bmp", "area" : "TZCSH201.bmp" },
-				"cityHall":       { "animation" : "TBCSHAL3.def", "x" : 0,   "y" : 164, "z" : 1, "border" : "TOCSH301.bmp", "area" : "TZCSH301.bmp" },
-				"capitol":        { "animation" : "TBCSHAL4.def", "x" : 0,   "y" : 154, "z" : 1, "border" : "TOCSH401.bmp", "area" : "TZCSH401.bmp" },
-				"marketplace":    { "animation" : "TBCSMARK.def", "x" : 413, "y" : 264, "z" : 0, "border" : "TOCSMRK1.bmp", "area" : "TZCSMRK1.bmp" },
-				"resourceSilo":   { "animation" : "TBCSSILO.def", "x" : 488, "y" : 228, "z" : 1, "border" : "TOCSMRK2.bmp", "area" : "TZCSMRK2.bmp" },
-				"blacksmith":     { "animation" : "TBCSBLAK.def", "x" : 213, "y" : 251, "z" : 0, "border" : "TOCSBLAK.bmp", "area" : "TZCSBLAK.bmp" },
-				"special1":       { "animation" : "TBCSSPEC.def", "x" : 533, "y" : 71,  "z" : -4, "border" : "TOCSLT01.bmp", "area" : "TZCSLT01.bmp" },
-				"horde1":         { "animation" : "TBCSHRD1.def", "x" : 76,  "y" : 53,  "z" : -1, "border" : "TOCSGR1H.bmp", "area" : "TZCSGR1H.bmp", "hidden" : true },
-				"horde1Upgr":     { "animation" : "TBCSHRD2.def", "x" : 76,  "y" : 35,  "z" : -1, "border" : "TOCSGR2H.bmp", "area" : "TZCSGR2H.bmp", "hidden" : true, "builds" : "horde1" },
+				"mageGuild1":     { "animation" : "TBCSMAGE.def", "x" : 707, "y" : 166, "z" : 1,  "campaignBonus" : "BoCsMag1.pcx", "border" : "TOCSMAG1.bmp", "area" : "TZCSMAG1.bmp" },
+				"mageGuild2":     { "animation" : "TBCSMAG2.def", "x" : 706, "y" : 135, "z" : 1,  "campaignBonus" : "BoCsMag2.pcx", "border" : "TOCSMAG2.bmp", "area" : "TZCSMAG2.bmp" },
+				"mageGuild3":     { "animation" : "TBCSMAG3.def", "x" : 704, "y" : 107, "z" : 1,  "campaignBonus" : "BoCsMag3.pcx", "border" : "TOCSM301.bmp", "area" : "TZCSM301.bmp" },
+				"mageGuild4":     { "animation" : "TBCSMAG4.def", "x" : 704, "y" : 76,  "z" : 1,  "campaignBonus" : "BoCsMag4.pcx", "border" : "TOCSM401.bmp", "area" : "TZCSM401.bmp" },
+				"tavern":         { "animation" : "TBCSTVRN.def", "x" : 0,   "y" : 230, "z" : 2,  "campaignBonus" : "BoCsTav1.pcx", "border" : "TOCSTAV1.bmp", "area" : "TZCSTAV1.bmp" },
+				"shipyard":       { "animation" : "TBCSDOCK.def", "x" : 478, "y" : 134, "z" : -3, "campaignBonus" : "BoCsDock.pcx", "border" : "TOCSDKMS.bmp", "area" : "TZCSDKMS.bmp" },
+				"fort":           { "animation" : "TBCSCSTL.def", "x" : 595, "y" : 66,  "z" : -5, "campaignBonus" : "BoCsCas1.pcx", "border" : "TOCSCAS1.bmp", "area" : "TZCSCAS1.bmp" },
+				"citadel":        { "animation" : "TBCSCAS2.def", "x" : 478, "y" : 66,  "z" : -5, "campaignBonus" : "BoCsCas2.pcx", "border" : "TOCSCAS2.bmp", "area" : "TZCSCAS2.bmp" },
+				"castle":         { "animation" : "TBCSCAS3.def", "x" : 478, "y" : 37,  "z" : -5, "campaignBonus" : "BoCsCas3.pcx", "border" : "TOCSCAS3.bmp", "area" : "TZCSCAS3.bmp" },
+				"villageHall":    { "animation" : "TBCSHALL.def", "x" : 0,   "y" : 209, "z" : 1,  "campaignBonus" : "BoCsHal1.pcx", "border" : "TOCSH101.bmp", "area" : "TZCSH101.bmp" },
+				"townHall":       { "animation" : "TBCSHAL2.def", "x" : 0,   "y" : 176, "z" : 1,  "campaignBonus" : "BoCsHal2.pcx", "border" : "TOCSH201.bmp", "area" : "TZCSH201.bmp" },
+				"cityHall":       { "animation" : "TBCSHAL3.def", "x" : 0,   "y" : 164, "z" : 1,  "campaignBonus" : "BoCsHal3.pcx", "border" : "TOCSH301.bmp", "area" : "TZCSH301.bmp" },
+				"capitol":        { "animation" : "TBCSHAL4.def", "x" : 0,   "y" : 154, "z" : 1,  "campaignBonus" : "BoCsHal4.pcx", "border" : "TOCSH401.bmp", "area" : "TZCSH401.bmp" },
+				"marketplace":    { "animation" : "TBCSMARK.def", "x" : 413, "y" : 264, "z" : 0,  "campaignBonus" : "BoCsMrk1.pcx", "border" : "TOCSMRK1.bmp", "area" : "TZCSMRK1.bmp" },
+				"resourceSilo":   { "animation" : "TBCSSILO.def", "x" : 488, "y" : 228, "z" : 1,  "campaignBonus" : "BoCsMrk2.pcx", "border" : "TOCSMRK2.bmp", "area" : "TZCSMRK2.bmp" },
+				"blacksmith":     { "animation" : "TBCSBLAK.def", "x" : 213, "y" : 251, "z" : 0,  "campaignBonus" : "BoCsBlak.pcx", "border" : "TOCSBLAK.bmp", "area" : "TZCSBLAK.bmp" },
+				"special1":       { "animation" : "TBCSSPEC.def", "x" : 533, "y" : 71,  "z" : -4, "campaignBonus" : "BoCsLite.pcx", "border" : "TOCSLT01.bmp", "area" : "TZCSLT01.bmp" },
+				"horde1":         { "animation" : "TBCSHRD1.def", "x" : 76,  "y" : 53,  "z" : -1, "campaignBonus" : "BoCsGr1H.pcx", "border" : "TOCSGR1H.bmp", "area" : "TZCSGR1H.bmp", "hidden" : true },
+				"horde1Upgr":     { "animation" : "TBCSHRD2.def", "x" : 76,  "y" : 35,  "z" : -1, "campaignBonus" : "BoCsGr2H.pcx", "border" : "TOCSGR2H.bmp", "area" : "TZCSGR2H.bmp", "hidden" : true, "builds" : "horde1" },
 				"ship":           { "animation" : "TBCSBOAT.def", "x" : 478, "y" : 134, "z" : -3, "border" : "TOCSDKMN.bmp", "area" : "TZCSDKMN.bmp", "hidden" : true },
-				"special2":       { "animation" : "TBCSEXT0.def", "x" : 384, "y" : 193, "z" : -2, "border" : "TOCSCAVM.bmp", "area" : "TZCSCAVM.bmp" },
-				"special3":       { "animation" : "TBCSEXT1.def", "x" : 0,   "y" : 198, "z" :  2, "border" : "TOCSTAV2.bmp", "area" : "TZCSTAV2.bmp" },
-				"grail":          { "animation" : "TBCSHOLY.def", "x" : 456, "y" : 109, "z" : -6, "border" : "TOCSHOLY.bmp", "area" : "TZCSHOLY.bmp" },
-				"dwellingLvl1":   { "animation" : "TBCSDW_0.def", "x" : 304, "y" : 92,  "z" : -1, "border" : "TOCSPIK1.bmp", "area" : "TZCSPIK1.bmp" },
-				"dwellingLvl2":   { "animation" : "TBCSDW_1.def", "x" : 360, "y" : 130, "z" : 0, "border" : "TOCSCRS1.bmp", "area" : "TZCSCRS1.bmp" },
-				"dwellingLvl3":   { "animation" : "TBCSDW_2.def", "x" : 76,  "y" : 57,  "z" : -1, "border" : "TOCSGR1N.bmp", "area" : "TZCSGR1N.bmp" },
-				"dwellingLvl4":   { "animation" : "TBCSDW_3.def", "x" : 176, "y" : 101, "z" : 0, "border" : "TOCSSWD1.bmp", "area" : "TZCSSWD1.bmp" },
-				"dwellingLvl5":   { "animation" : "TBCSDW_4.def", "x" : 563, "y" : 211, "z" : 1,  "border" : "TOCSMON1.bmp", "area" : "TZCSMON1.bmp" },
-				"dwellingLvl6":   { "animation" : "TBCSDW_5.def", "x" : 174, "y" : 190, "z" : -1, "border" : "TOCSC101.bmp", "area" : "TZCSCAV1.bmp" },
-				"dwellingLvl7":   { "animation" : "TBCSDW_6.def", "x" : 303, "y" : 0,   "z" : -2, "border" : "TOCSANG1.bmp", "area" : "TZCSANG1.bmp" },
-				"dwellingUpLvl1": { "animation" : "TBCSUP_0.def", "x" : 304, "y" : 65,  "z" : -1, "border" : "TOCSPIK2.bmp", "area" : "TZCSPIK2.bmp" },
-				"dwellingUpLvl2": { "animation" : "TBCSUP_1.def", "x" : 360, "y" : 115, "z" : 0, "border" : "TOCSCRS2.bmp", "area" : "TZCSCRS2.bmp" },
-				"dwellingUpLvl3": { "animation" : "TBCSUP_2.def", "x" : 76,  "y" : 35,  "z" : -1, "border" : "TOCSGR2N.bmp", "area" : "TZCSGR2N.bmp" },
-				"dwellingUpLvl4": { "animation" : "TBCSUP_3.def", "x" : 176, "y" : 85,  "z" : 0, "border" : "TOCSSWD2.bmp", "area" : "TZCSSWD2.bmp" },
-				"dwellingUpLvl5": { "animation" : "TBCSUP_4.def", "x" : 563, "y" : 173, "z" : 1,  "border" : "TOCSMON2.bmp", "area" : "TZCSMON2.bmp" },
-				"dwellingUpLvl6": { "animation" : "TBCSUP_5.def", "x" : 160, "y" : 190, "z" : -1, "border" : "TOCSCAV2.bmp", "area" : "TZCSCAV2.bmp" },
-				"dwellingUpLvl7": { "animation" : "TBCSUP_6.def", "x" : 303, "y" : 0,   "z" : -2, "border" : "TOCSANG2.bmp", "area" : "TZCSANG2.bmp" }
+				"special2":       { "animation" : "TBCSEXT0.def", "x" : 384, "y" : 193, "z" : -2, "campaignBonus" : "BoCsCv2S.pcx", "border" : "TOCSCAVM.bmp", "area" : "TZCSCAVM.bmp" },
+				"special3":       { "animation" : "TBCSEXT1.def", "x" : 0,   "y" : 198, "z" :  2, "campaignBonus" : "BoCsTav2.pcx", "border" : "TOCSTAV2.bmp", "area" : "TZCSTAV2.bmp" },
+				"grail":          { "animation" : "TBCSHOLY.def", "x" : 456, "y" : 109, "z" : -6, "campaignBonus" : "BoCsHoly.pcx", "border" : "TOCSHOLY.bmp", "area" : "TZCSHOLY.bmp" },
+				"dwellingLvl1":   { "animation" : "TBCSDW_0.def", "x" : 304, "y" : 92,  "z" : -1, "campaignBonus" : "BoCsPik1.pcx", "border" : "TOCSPIK1.bmp", "area" : "TZCSPIK1.bmp" },
+				"dwellingLvl2":   { "animation" : "TBCSDW_1.def", "x" : 360, "y" : 130, "z" : 0,  "campaignBonus" : "BoCsCrs1.pcx", "border" : "TOCSCRS1.bmp", "area" : "TZCSCRS1.bmp" },
+				"dwellingLvl3":   { "animation" : "TBCSDW_2.def", "x" : 76,  "y" : 57,  "z" : -1, "campaignBonus" : "BoCsGr1.pcx",  "border" : "TOCSGR1N.bmp", "area" : "TZCSGR1N.bmp" },
+				"dwellingLvl4":   { "animation" : "TBCSDW_3.def", "x" : 176, "y" : 101, "z" : 0,  "campaignBonus" : "BoCsSwd1.pcx", "border" : "TOCSSWD1.bmp", "area" : "TZCSSWD1.bmp" },
+				"dwellingLvl5":   { "animation" : "TBCSDW_4.def", "x" : 563, "y" : 211, "z" : 1,  "campaignBonus" : "BoCsMon1.pcx", "border" : "TOCSMON1.bmp", "area" : "TZCSMON1.bmp" },
+				"dwellingLvl6":   { "animation" : "TBCSDW_5.def", "x" : 174, "y" : 190, "z" : -1, "campaignBonus" : "BoCsCv1.pcx",  "border" : "TOCSC101.bmp", "area" : "TZCSCAV1.bmp" },
+				"dwellingLvl7":   { "animation" : "TBCSDW_6.def", "x" : 303, "y" : 0,   "z" : -2, "campaignBonus" : "BoCsAng1.pcx", "border" : "TOCSANG1.bmp", "area" : "TZCSANG1.bmp" },
+				"dwellingUpLvl1": { "animation" : "TBCSUP_0.def", "x" : 304, "y" : 65,  "z" : -1, "campaignBonus" : "BoCsPik2.pcx", "border" : "TOCSPIK2.bmp", "area" : "TZCSPIK2.bmp" },
+				"dwellingUpLvl2": { "animation" : "TBCSUP_1.def", "x" : 360, "y" : 115, "z" : 0,  "campaignBonus" : "BoCsCrs2.pcx", "border" : "TOCSCRS2.bmp", "area" : "TZCSCRS2.bmp" },
+				"dwellingUpLvl3": { "animation" : "TBCSUP_2.def", "x" : 76,  "y" : 35,  "z" : -1, "campaignBonus" : "BoCsGr2.pcx",  "border" : "TOCSGR2N.bmp", "area" : "TZCSGR2N.bmp" },
+				"dwellingUpLvl4": { "animation" : "TBCSUP_3.def", "x" : 176, "y" : 85,  "z" : 0,  "campaignBonus" : "BoCsSwd2.pcx", "border" : "TOCSSWD2.bmp", "area" : "TZCSSWD2.bmp" },
+				"dwellingUpLvl5": { "animation" : "TBCSUP_4.def", "x" : 563, "y" : 173, "z" : 1,  "campaignBonus" : "BoCsMon2.pcx", "border" : "TOCSMON2.bmp", "area" : "TZCSMON2.bmp" },
+				"dwellingUpLvl6": { "animation" : "TBCSUP_5.def", "x" : 160, "y" : 190, "z" : -1, "campaignBonus" : "BoCsCv2.pcx",  "border" : "TOCSCAV2.bmp", "area" : "TZCSCAV2.bmp" },
+				"dwellingUpLvl7": { "animation" : "TBCSUP_6.def", "x" : 303, "y" : 0,   "z" : -2, "campaignBonus" : "BoCsAng2.pcx", "border" : "TOCSANG2.bmp", "area" : "TZCSANG2.bmp" }
 			},
 
 			"musicTheme" : [ "music/CstleTown" ],

+ 46 - 37
config/factions/conflux.json

@@ -81,46 +81,46 @@
 			{
 				"extraAnimation2":{ "animation" : "TBELEXT5.def", "x" : 682, "y" : 183, "z" : 2 },
 				"extraAnimation": { "animation" : "TBELEXT1.def", "x" : 23,  "y" : 218, "z" : 3 },
-				"mageGuild1":     { "animation" : "TBELMAGE.def", "x" : 206, "y" : 58,  "z" : 7, "border" : "TOELMAGE.bmp", "area" : "TZELMAGE.bmp" },
-				"mageGuild2":     { "animation" : "TBELMAG2.def", "x" : 206, "y" : 58,  "z" : 7, "border" : "TOELMAG2.bmp", "area" : "TZELMAG2.bmp" },
-				"mageGuild3":     { "animation" : "TBELMAG3.def", "x" : 206, "y" : 58,  "z" : 7, "border" : "TOELMAG3.bmp", "area" : "TZELMAG3.bmp" },
-				"mageGuild4":     { "animation" : "TBELMAG4.def", "x" : 206, "y" : 58,  "z" : 7, "border" : "TOELMAG4.bmp", "area" : "TZELMAG4.bmp" },
-				"mageGuild5":     { "animation" : "TBELMAG5.def", "x" : 206, "y" : 58,  "z" : 7, "border" : "TOELMAG5.bmp", "area" : "TZELMAG5.bmp" },
-				"tavern":         { "animation" : "TBELTVRN.def", "x" : 553, "y" : 203, "z" : 7, "border" : "TOELTVRN.bmp", "area" : "TZELTVRN.bmp" },
-				"shipyard":       { "animation" : "TBELDOCK.def", "x" : 239, "y" : 215, "z" : 5, "border" : "TOELDOCK.bmp", "area" : "TZELDOCK.bmp" },
-				"fort":           { "animation" : "TBELCSTL.def", "x" : 349, "y" : 101, "z" : 1, "border" : "TOELCSTL.bmp", "area" : "TZELCSTL.bmp" },
-				"citadel":        { "animation" : "TBELCAS2.def", "x" : 349, "y" : 101, "z" : 1, "border" : "TOELCAS2.bmp", "area" : "TZELCAS2.bmp" },
-				"castle":         { "animation" : "TBELCAS3.def", "x" : 349, "y" : 101, "z" : 1, "border" : "TOELCAS3.bmp", "area" : "TZELCAS3.bmp" },
-				"villageHall":    { "animation" : "TBELHALL.def", "x" : -1,  "y" : 164, "z" : 8, "border" : "TOELHALL.bmp", "area" : "TZELHALL.bmp" },
-				"townHall":       { "animation" : "TBELHAL2.def", "x" : 0,   "y" : 165, "z" : 8, "border" : "TOELHAL2.bmp", "area" : "TZELHAL2.bmp" },
-				"cityHall":       { "animation" : "TBELHAL3.def", "x" : 0,   "y" : 165, "z" : 8, "border" : "TOELHAL3.bmp", "area" : "TZELHAL3.bmp" },
-				"capitol":        { "animation" : "TBELHAL4.def", "x" : 0,   "y" : 164, "z" : 8, "border" : "TOELHAL4.bmp", "area" : "TZELHAL4.bmp" },
-				"marketplace":    { "animation" : "TBELMARK.def", "x" : 347, "y" : 216, "z" : 8, "border" : "TOELMARK.bmp", "area" : "TZELMARK.bmp" },
-				"resourceSilo":   { "animation" : "TBELSILO.def", "x" : 372, "y" : 171, "z" : 7, "border" : "TOELSILO.bmp", "area" : "TZELSILO.bmp" },
-				"blacksmith":     { "animation" : "TBELBLAK.def", "x" : 449, "y" : 151, "z" : 5, "border" : "TOELBLAK.bmp", "area" : "TZELBLAK.bmp" },
-				"special1":       { "animation" : "TBELSPEC.def", "x" : 284, "y" : 246, "z" : 9, "border" : "TOELSPEC.bmp", "area" : "TZELSPEC.bmp" },
-				"horde1":         { "animation" : "TBELHRD1.def", "x" : 689, "y" : 250, "z" : 9, "border" : "TOELHRD1.bmp", "area" : "TZELHRD1.bmp", "hidden" : true },
-				"horde1Upgr":     { "animation" : "TBELHRD2.def", "x" : 689, "y" : 250, "z" : 9, "border" : "TOELHRD2.bmp", "area" : "TZELHRD2.bmp", "hidden" : true, "builds" : "horde1" },
+				"mageGuild1":     { "animation" : "TBELMAGE.def", "x" : 206, "y" : 58,  "z" : 7, "campaignBonus" : "BoEgld1.pcx", "border" : "TOELMAGE.bmp", "area" : "TZELMAGE.bmp" },
+				"mageGuild2":     { "animation" : "TBELMAG2.def", "x" : 206, "y" : 58,  "z" : 7, "campaignBonus" : "BoEgld2.pcx", "border" : "TOELMAG2.bmp", "area" : "TZELMAG2.bmp" },
+				"mageGuild3":     { "animation" : "TBELMAG3.def", "x" : 206, "y" : 58,  "z" : 7, "campaignBonus" : "BoEgld3.pcx", "border" : "TOELMAG3.bmp", "area" : "TZELMAG3.bmp" },
+				"mageGuild4":     { "animation" : "TBELMAG4.def", "x" : 206, "y" : 58,  "z" : 7, "campaignBonus" : "BoEgld4.pcx", "border" : "TOELMAG4.bmp", "area" : "TZELMAG4.bmp" },
+				"mageGuild5":     { "animation" : "TBELMAG5.def", "x" : 206, "y" : 58,  "z" : 7, "campaignBonus" : "BoEgld5.pcx", "border" : "TOELMAG5.bmp", "area" : "TZELMAG5.bmp" },
+				"tavern":         { "animation" : "TBELTVRN.def", "x" : 553, "y" : 203, "z" : 7, "campaignBonus" : "BoEtav.pcx",  "border" : "TOELTVRN.bmp", "area" : "TZELTVRN.bmp" },
+				"shipyard":       { "animation" : "TBELDOCK.def", "x" : 239, "y" : 215, "z" : 5, "campaignBonus" : "BoEship.pcx", "border" : "TOELDOCK.bmp", "area" : "TZELDOCK.bmp" },
+				"fort":           { "animation" : "TBELCSTL.def", "x" : 349, "y" : 101, "z" : 1, "campaignBonus" : "BoEcast1.pcx", "border" : "TOELCSTL.bmp", "area" : "TZELCSTL.bmp" },
+				"citadel":        { "animation" : "TBELCAS2.def", "x" : 349, "y" : 101, "z" : 1, "campaignBonus" : "BoEcast2.pcx", "border" : "TOELCAS2.bmp", "area" : "TZELCAS2.bmp" },
+				"castle":         { "animation" : "TBELCAS3.def", "x" : 349, "y" : 101, "z" : 1, "campaignBonus" : "BoEcast3.pcx", "border" : "TOELCAS3.bmp", "area" : "TZELCAS3.bmp" },
+				"villageHall":    { "animation" : "TBELHALL.def", "x" : -1,  "y" : 164, "z" : 8, "campaignBonus" : "BoEhall1.pcx", "border" : "TOELHALL.bmp", "area" : "TZELHALL.bmp" },
+				"townHall":       { "animation" : "TBELHAL2.def", "x" : 0,   "y" : 165, "z" : 8, "campaignBonus" : "BoEhall2.pcx", "border" : "TOELHAL2.bmp", "area" : "TZELHAL2.bmp" },
+				"cityHall":       { "animation" : "TBELHAL3.def", "x" : 0,   "y" : 165, "z" : 8, "campaignBonus" : "BoEhall3.pcx", "border" : "TOELHAL3.bmp", "area" : "TZELHAL3.bmp" },
+				"capitol":        { "animation" : "TBELHAL4.def", "x" : 0,   "y" : 164, "z" : 8, "campaignBonus" : "BoEhall4.pcx", "border" : "TOELHAL4.bmp", "area" : "TZELHAL4.bmp" },
+				"marketplace":    { "animation" : "TBELMARK.def", "x" : 347, "y" : 216, "z" : 8, "campaignBonus" : "BoEMark.pcx",  "border" : "TOELMARK.bmp", "area" : "TZELMARK.bmp" },
+				"resourceSilo":   { "animation" : "TBELSILO.def", "x" : 372, "y" : 171, "z" : 7, "campaignBonus" : "BoEMarkS.pcx", "border" : "TOELSILO.bmp", "area" : "TZELSILO.bmp" },
+				"blacksmith":     { "animation" : "TBELBLAK.def", "x" : 449, "y" : 151, "z" : 5, "campaignBonus" : "BoEblack.pcx", "border" : "TOELBLAK.bmp", "area" : "TZELBLAK.bmp" },
+				"special1":       { "animation" : "TBELSPEC.def", "x" : 284, "y" : 246, "z" : 9, "campaignBonus" : "BoEMarkA.pcx", "border" : "TOELSPEC.bmp", "area" : "TZELSPEC.bmp" },
+				"horde1":         { "animation" : "TBELHRD1.def", "x" : 689, "y" : 250, "z" : 9, "campaignBonus" : "BoEHrd1.pcx",  "border" : "TOELHRD1.bmp", "area" : "TZELHRD1.bmp", "hidden" : true },
+				"horde1Upgr":     { "animation" : "TBELHRD2.def", "x" : 689, "y" : 250, "z" : 9, "campaignBonus" : "BoEHrd2.pcx",  "border" : "TOELHRD2.bmp", "area" : "TZELHRD2.bmp", "hidden" : true, "builds" : "horde1" },
 				"ship":           { "animation" : "TBELBOAT.def", "x" : 239, "y" : 215, "z" : 5, "border" : "TOELBOAT.bmp", "area" : "TZELBOAT.bmp", "hidden" : true },
-				"special2":       { "animation" : "TBELEXT6.def", "x" : 104, "y" : 170, "z" : 6, "border" : "TOELEXT6.bmp", "area" : "TZELEXT6.bmp" },
-				"grail":          { "animation" : "TBELHOLY.def", "x" : 307, "y" : 2,   "z" : 0, "border" : "TOELHOLY.bmp", "area" : "TZELHOLY.bmp" },
+				"special2":       { "animation" : "TBELEXT6.def", "x" : 104, "y" : 170, "z" : 6, "campaignBonus" : "BoEuniv.pcx",  "border" : "TOELEXT6.bmp", "area" : "TZELEXT6.bmp" },
+				"grail":          { "animation" : "TBELHOLY.def", "x" : 307, "y" : 2,   "z" : 0, "campaignBonus" : "BoEgrail.pcx", "border" : "TOELHOLY.bmp", "area" : "TZELHOLY.bmp" },
 				"extraTownHall":  { "animation" : "TBELEXT2.def", "x" : 232, "y" : 205, "z" : 4 },
 				"extraCityHall":  { "animation" : "TBELEXT3.def", "x" : 516, "y" : 223, "z" : 6 },
 				"extraCapitol":   { "animation" : "TBELEXT4.def", "x" : 0,   "y" : 252, "z" : 9 },
-				"dwellingLvl1":   { "animation" : "TBELDW_0.def", "x" : 689, "y" : 250, "z" : 9, "border" : "TOELDW_0.bmp", "area" : "TZELDW_0.bmp" },
-				"dwellingLvl2":   { "animation" : "TBELDW_1.def", "x" : 630, "y" : 50,  "z" : 0, "border" : "TOELDW_1.bmp", "area" : "TZELDW_1.bmp" },
-				"dwellingLvl3":   { "animation" : "TBELDW_2.def", "x" : 709, "y" : 210, "z" : 5, "border" : "TOELDW_2.bmp", "area" : "TZELDW_2.bmp" },
-				"dwellingLvl4":   { "animation" : "TBELDW_3.def", "x" : 108, "y" : 131, "z" : 1, "border" : "TOELDW_3.bmp", "area" : "TZELDW_3.bmp" },
-				"dwellingLvl5":   { "animation" : "TBELDW_4.def", "x" : 264, "y" : 168, "z" : 0, "border" : "TOELDW_4.bmp", "area" : "TZELDW_4.bmp" },
-				"dwellingLvl6":   { "animation" : "TBELDW_5.def", "x" : 394, "y" : 283, "z" : 9, "border" : "TOELDW_5.bmp", "area" : "TZELDW_5.bmp" },
-				"dwellingLvl7":   { "animation" : "TBELDW_6.def", "x" : 34,  "y" : 16,  "z" : 0, "border" : "TOELDW_6.bmp", "area" : "TZELDW_6.bmp" },
-				"dwellingUpLvl1": { "animation" : "TBELUP_0.def", "x" : 689, "y" : 250, "z" : 9, "border" : "TOELUP_0.bmp", "area" : "TZELUP_0.bmp" },
-				"dwellingUpLvl2": { "animation" : "TBELUP_1.def", "x" : 630, "y" : 50,  "z" : 0, "border" : "TOELUP_1.bmp", "area" : "TZELUP_1.bmp" },
-				"dwellingUpLvl3": { "animation" : "TBELUP_2.def", "x" : 709, "y" : 210, "z" : 5, "border" : "TOELUP_2.bmp", "area" : "TZELUP_2.bmp" },
-				"dwellingUpLvl4": { "animation" : "TBELUP_3.def", "x" : 108, "y" : 131, "z" : 1, "border" : "TOELUP_3.bmp", "area" : "TZELUP_3.bmp" },
-				"dwellingUpLvl5": { "animation" : "TBELUP_4.def", "x" : 264, "y" : 168, "z" : 0, "border" : "TOELUP_4.bmp", "area" : "TZELUP_4.bmp" },
-				"dwellingUpLvl6": { "animation" : "TBELUP_5.def", "x" : 394, "y" : 283, "z" : 9, "border" : "TOELUP_5.bmp", "area" : "TZELUP_5.bmp" },
-				"dwellingUpLvl7": { "animation" : "TBELUP_6.def", "x" : 34,  "y" : 0,   "z" : 0, "border" : "TOELUP_6.bmp", "area" : "TZELUP_6.bmp" }
+				"dwellingLvl1":   { "animation" : "TBELDW_0.def", "x" : 689, "y" : 250, "z" : 9, "campaignBonus" : "BoEdn_0.pcx", "border" : "TOELDW_0.bmp", "area" : "TZELDW_0.bmp" },
+				"dwellingLvl2":   { "animation" : "TBELDW_1.def", "x" : 630, "y" : 50,  "z" : 0, "campaignBonus" : "BoEdn_1.pcx", "border" : "TOELDW_1.bmp", "area" : "TZELDW_1.bmp" },
+				"dwellingLvl3":   { "animation" : "TBELDW_2.def", "x" : 709, "y" : 210, "z" : 5, "campaignBonus" : "BoEdn_2.pcx", "border" : "TOELDW_2.bmp", "area" : "TZELDW_2.bmp" },
+				"dwellingLvl4":   { "animation" : "TBELDW_3.def", "x" : 108, "y" : 131, "z" : 1, "campaignBonus" : "BoEdn_3.pcx", "border" : "TOELDW_3.bmp", "area" : "TZELDW_3.bmp" },
+				"dwellingLvl5":   { "animation" : "TBELDW_4.def", "x" : 264, "y" : 168, "z" : 0, "campaignBonus" : "BoEdn_4.pcx", "border" : "TOELDW_4.bmp", "area" : "TZELDW_4.bmp" },
+				"dwellingLvl6":   { "animation" : "TBELDW_5.def", "x" : 394, "y" : 283, "z" : 9, "campaignBonus" : "BoEdn_5.pcx", "border" : "TOELDW_5.bmp", "area" : "TZELDW_5.bmp" },
+				"dwellingLvl7":   { "animation" : "TBELDW_6.def", "x" : 34,  "y" : 16,  "z" : 0, "campaignBonus" : "BoEdn_6.pcx", "border" : "TOELDW_6.bmp", "area" : "TZELDW_6.bmp" },
+				"dwellingUpLvl1": { "animation" : "TBELUP_0.def", "x" : 689, "y" : 250, "z" : 9, "campaignBonus" : "BoEup_0.pcx", "border" : "TOELUP_0.bmp", "area" : "TZELUP_0.bmp" },
+				"dwellingUpLvl2": { "animation" : "TBELUP_1.def", "x" : 630, "y" : 50,  "z" : 0, "campaignBonus" : "BoEup_1.pcx", "border" : "TOELUP_1.bmp", "area" : "TZELUP_1.bmp" },
+				"dwellingUpLvl3": { "animation" : "TBELUP_2.def", "x" : 709, "y" : 210, "z" : 5, "campaignBonus" : "BoEup_2.pcx", "border" : "TOELUP_2.bmp", "area" : "TZELUP_2.bmp" },
+				"dwellingUpLvl4": { "animation" : "TBELUP_3.def", "x" : 108, "y" : 131, "z" : 1, "campaignBonus" : "BoEup_3.pcx", "border" : "TOELUP_3.bmp", "area" : "TZELUP_3.bmp" },
+				"dwellingUpLvl5": { "animation" : "TBELUP_4.def", "x" : 264, "y" : 168, "z" : 0, "campaignBonus" : "BoEup_4.pcx", "border" : "TOELUP_4.bmp", "area" : "TZELUP_4.bmp" },
+				"dwellingUpLvl6": { "animation" : "TBELUP_5.def", "x" : 394, "y" : 283, "z" : 9, "campaignBonus" : "BoEup_5.pcx", "border" : "TOELUP_5.bmp", "area" : "TZELUP_5.bmp" },
+				"dwellingUpLvl7": { "animation" : "TBELUP_6.def", "x" : 34,  "y" : 0,   "z" : 0, "campaignBonus" : "BoEup_6.pcx", "border" : "TOELUP_6.bmp", "area" : "TZELUP_6.bmp" }
 			},
 
 			"musicTheme" : [ "music/ElemTown" ],
@@ -178,7 +178,16 @@
 				"horde1":         { "id" : 18, "upgrades" : "dwellingLvl1" },
 				"horde1Upgr":     { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
 				"ship":           { "id" : 20, "upgrades" : "shipyard" },
-				"special2":       { "requires" : [ "mageGuild1" ], "marketModes" : ["resource-skill"] },
+				"special2":       {
+					"requires" : [ "mageGuild1" ],
+					"marketModes" : ["resource-skill"],
+					"marketOffer" : [
+						"fireMagic",
+						"airMagic",
+						"waterMagic",
+						"earthMagic"
+					]
+				},
 				"grail":          { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
 				"extraTownHall":  { "id" : 27, "requires" : [ "townHall" ], "mode" : "auto" },
 				"extraCityHall":  { "id" : 28, "requires" : [ "cityHall" ], "mode" : "auto" },

+ 37 - 37
config/factions/dungeon.json

@@ -80,43 +80,43 @@
 			},
 			"structures" :
 			{
-				"mageGuild1":     { "animation" : "TBDNMAGE.def", "x" : 164, "y" : 119, "z" : -1, "border" : "TODMAG1.bmp",  "area" : "TZDMAG1.bmp" },
-				"mageGuild2":     { "animation" : "TBDNMAG2.def", "x" : 164, "y" : 97,  "z" : -1, "border" : "TODMAG2.bmp",  "area" : "TZDMAG2.bmp" },
-				"mageGuild3":     { "animation" : "TBDNMAG3.def", "x" : 164, "y" : 77,  "z" : -1, "border" : "TODMAG3.bmp",  "area" : "TZDMAG3.bmp" },
-				"mageGuild4":     { "animation" : "TBDNMAG4.def", "x" : 164, "y" : 61,  "z" : -1, "border" : "TODMAG4.bmp",  "area" : "TZDMAG4.bmp" },
-				"mageGuild5":     { "animation" : "TBDNMAG5.def", "x" : 164, "y" : 15,  "z" : -1, "border" : "TODMAG5.bmp",  "area" : "TZDMAG5.bmp" },
-				"tavern":         { "animation" : "TBDNTVRN.def", "x" : 211, "y" : 297, "border" : "TODTAV.bmp",   "area" : "TZDTAV.bmp" },
-				"fort":           { "animation" : "TBDNCSTL.def", "x" : 363, "y" : 87,  "z" : 2,  "border" : "TODCAS1.bmp",  "area" : "TZDCAS1.bmp" },
-				"citadel":        { "animation" : "TBDNCAS2.def", "x" : 363, "y" : 87,  "z" : 2,  "border" : "TODCAS2.bmp",  "area" : "TZDCAS2.bmp" },
-				"castle":         { "animation" : "TBDNCAS3.def", "x" : 363, "y" : 87,  "z" : 2,  "border" : "TODCAS3.bmp",  "area" : "TZDCAS3.bmp" },
-				"villageHall":    { "animation" : "TBDNHALL.def", "x" : 0,   "y" : 234, "border" : "TODHALL1.bmp", "area" : "TZDHALL1.bmp" },
-				"townHall":       { "animation" : "TBDNHAL2.def", "x" : 0,   "y" : 223, "border" : "TODHALL2.bmp", "area" : "TZDHALL2.bmp" },
-				"cityHall":       { "animation" : "TBDNHAL3.def", "x" : 0,   "y" : 223, "border" : "TODHALL3.bmp", "area" : "TZDHALL3.bmp" },
-				"capitol":        { "animation" : "TBDNHAL4.def", "x" : 0,   "y" : 203, "border" : "TODHALL4.bmp", "area" : "TZDHALL4.bmp" },
-				"marketplace":    { "animation" : "TBDNMARK.def", "x" : 590, "y" : 318, "z" : -2, "border" : "TODMARK.bmp",  "area" : "TZDMARK.bmp" },
-				"resourceSilo":   { "animation" : "TBDNSILO.def", "x" : 624, "y" : 335, "z" : 1,  "border" : "TODSILO.bmp",  "area" : "TZDSILO.bmp" },
-				"blacksmith":     { "animation" : "TBDNBLAK.def", "x" : 544, "y" : 248, "z" : -3, "border" : "TODSMITH.bmp", "area" : "TZDSMITH.bmp" },
-				"special1":       { "animation" : "TBDNSPEC.def", "x" : 746, "y" : 294, "z" : 1,  "border" : "TODART.bmp",   "area" : "TZDART.bmp" },
-				"horde1":         { "animation" : "TBDNHRD1.def", "x" : 0,   "y" : 326, "z" : 2,  "border" : "TODTR1HA.bmp", "area" : "TZDTR1HA.bmp", "hidden" : true },
-				"horde1Upgr":     { "animation" : "TBDNHRD2.def", "x" : 0,   "y" : 300, "z" : 2,  "border" : "TODTR2HA.bmp", "area" : "TZDTR2HA.bmp", "hidden" : true, "builds" : "horde1" },
-				"special2":       { "animation" : "TBDNEXT0.def", "x" : 131, "y" : 26 , "border" : "TODVOR1A.bmp", "area" : "TZDVOR1A.bmp" },
-				"special3":       { "animation" : "TBDNEXT1.def", "x" : 687, "y" : 177, "border" : "TODPORTA.bmp", "area" : "TZDPORTA.bmp" },
-				"special4":       { "animation" : "TBDNEXT2.def", "x" : 313, "y" : 298, "border" : "TODACAD.bmp",  "area" : "TZDACAD.bmp" },
-				"grail":          { "animation" : "TBDNHOLY.def", "x" : 562, "y" : 24,  "z" : 1,  "border" : "TODHOLY.bmp",  "area" : "TZDHOLY.bmp" },
-				"dwellingLvl1":   { "animation" : "TBDNDW_0.def", "x" : 0,   "y" : 326, "z" : 2,  "border" : "TODTRG1A.bmp", "area" : "TZDTRG1A.bmp" },
-				"dwellingLvl2":   { "animation" : "TBDNDW_1.def", "x" : 0,   "y" : 26,  "border" : "TODHAR1.bmp",  "area" : "TZDHAR1.bmp" },
-				"dwellingLvl3":   { "animation" : "TBDNDW_2.def", "x" : 118, "y" : 308, "z" : 1,  "border" : "TODBEH1A.bmp", "area" : "TZDBEH1A.bmp" },
-				"dwellingLvl4":   { "animation" : "TBDNDW_3.def", "x" : 300, "y" : 29,  "z" : -1, "border" : "TODMED1.bmp",  "area" : "TZDMED1.bmp" },
-				"dwellingLvl5":   { "animation" : "TBDNDW_4.def", "x" : 551, "y" : 186, "z" : 1,  "border" : "TODMIN1.bmp",  "area" : "TZDMIN1.bmp" },
-				"dwellingLvl6":   { "animation" : "TBDNDW_5.def", "x" : 270, "y" : 253, "z" : -1, "border" : "TODMAN1.bmp",  "area" : "TZDMAN1.bmp" },
-				"dwellingLvl7":   { "animation" : "TBDNDW_6.def", "x" : 550, "y" : 0,   "z" : -1, "border" : "TODDRA1A.bmp", "area" : "TZDDRA1A.bmp" },
-				"dwellingUpLvl1": { "animation" : "TBDNUP_0.def", "x" : 0,   "y" : 300, "z" : 2,  "border" : "TODTRG2A.bmp", "area" : "TZDTRG2A.bmp" },
-				"dwellingUpLvl2": { "animation" : "TBDNUP_1.def", "x" : 0,   "y" : 26,  "border" : "TODHAR2.bmp",  "area" : "TZDHAR2.bmp" },
-				"dwellingUpLvl3": { "animation" : "TBDNUP_2.def", "x" : 118, "y" : 256, "z" : 1,  "border" : "TODBEH2A.bmp", "area" : "TZDBEH2A.bmp" },
-				"dwellingUpLvl4": { "animation" : "TBDNUP_3.def", "x" : 300, "y" : 29,  "z" : -1, "border" : "TODMED2.bmp",  "area" : "TZDMED2.bmp" },
-				"dwellingUpLvl5": { "animation" : "TBDNUP_4.def", "x" : 519, "y" : 172, "z" : 1,  "border" : "TODMIN2.bmp",  "area" : "TZDMIN2.bmp" },
-				"dwellingUpLvl6": { "animation" : "TBDNUP_5.def", "x" : 270, "y" : 253, "z" : -1, "border" : "TODMAN2.bmp",  "area" : "TZDMAN2.bmp" },
-				"dwellingUpLvl7": { "animation" : "TBDNUP_6.def", "x" : 550, "y" : 0,   "z" : -1, "border" : "TODDRA2A.bmp", "area" : "TZDDRA2A.bmp" }
+				"mageGuild1":     { "animation" : "TBDNMAGE.def", "x" : 164, "y" : 119, "z" : -1, "campaignBonus" : "BoDmage1.pcx", "border" : "TODMAG1.bmp",  "area" : "TZDMAG1.bmp" },
+				"mageGuild2":     { "animation" : "TBDNMAG2.def", "x" : 164, "y" : 97,  "z" : -1, "campaignBonus" : "BoDmage2.pcx", "border" : "TODMAG2.bmp",  "area" : "TZDMAG2.bmp" },
+				"mageGuild3":     { "animation" : "TBDNMAG3.def", "x" : 164, "y" : 77,  "z" : -1, "campaignBonus" : "BoDmage3.pcx", "border" : "TODMAG3.bmp",  "area" : "TZDMAG3.bmp" },
+				"mageGuild4":     { "animation" : "TBDNMAG4.def", "x" : 164, "y" : 61,  "z" : -1, "campaignBonus" : "BoDmage4.pcx", "border" : "TODMAG4.bmp",  "area" : "TZDMAG4.bmp" },
+				"mageGuild5":     { "animation" : "TBDNMAG5.def", "x" : 164, "y" : 15,  "z" : -1, "campaignBonus" : "BoDmage5.pcx", "border" : "TODMAG5.bmp",  "area" : "TZDMAG5.bmp" },
+				"tavern":         { "animation" : "TBDNTVRN.def", "x" : 211, "y" : 297,           "campaignBonus" : "BoDtav.pcx",   "border" : "TODTAV.bmp",   "area" : "TZDTAV.bmp" },
+				"fort":           { "animation" : "TBDNCSTL.def", "x" : 363, "y" : 87,  "z" : 2,  "campaignBonus" : "BoDcas1.pcx",  "border" : "TODCAS1.bmp",  "area" : "TZDCAS1.bmp" },
+				"citadel":        { "animation" : "TBDNCAS2.def", "x" : 363, "y" : 87,  "z" : 2,  "campaignBonus" : "BoDcas2.pcx",  "border" : "TODCAS2.bmp",  "area" : "TZDCAS2.bmp" },
+				"castle":         { "animation" : "TBDNCAS3.def", "x" : 363, "y" : 87,  "z" : 2,  "campaignBonus" : "BoDcas3.pcx",  "border" : "TODCAS3.bmp",  "area" : "TZDCAS3.bmp" },
+				"villageHall":    { "animation" : "TBDNHALL.def", "x" : 0,   "y" : 234,           "campaignBonus" : "BoDhall1.pcx", "border" : "TODHALL1.bmp", "area" : "TZDHALL1.bmp" },
+				"townHall":       { "animation" : "TBDNHAL2.def", "x" : 0,   "y" : 223,           "campaignBonus" : "BoDhall2.pcx", "border" : "TODHALL2.bmp", "area" : "TZDHALL2.bmp" },
+				"cityHall":       { "animation" : "TBDNHAL3.def", "x" : 0,   "y" : 223,           "campaignBonus" : "BoDhall3.pcx", "border" : "TODHALL3.bmp", "area" : "TZDHALL3.bmp" },
+				"capitol":        { "animation" : "TBDNHAL4.def", "x" : 0,   "y" : 203,           "campaignBonus" : "BoDhall4.pcx", "border" : "TODHALL4.bmp", "area" : "TZDHALL4.bmp" },
+				"marketplace":    { "animation" : "TBDNMARK.def", "x" : 590, "y" : 318, "z" : -2, "campaignBonus" : "BoDmark1.pcx", "border" : "TODMARK.bmp",  "area" : "TZDMARK.bmp" },
+				"resourceSilo":   { "animation" : "TBDNSILO.def", "x" : 624, "y" : 335, "z" : 1,  "campaignBonus" : "BoDmark2.pcx", "border" : "TODSILO.bmp",  "area" : "TZDSILO.bmp" },
+				"blacksmith":     { "animation" : "TBDNBLAK.def", "x" : 544, "y" : 248, "z" : -3, "campaignBonus" : "BoDsmith.pcx", "border" : "TODSMITH.bmp", "area" : "TZDSMITH.bmp" },
+				"special1":       { "animation" : "TBDNSPEC.def", "x" : 746, "y" : 294, "z" : 1,  "campaignBonus" : "BoDmarkA.pcx", "border" : "TODART.bmp",   "area" : "TZDART.bmp" },
+				"horde1":         { "animation" : "TBDNHRD1.def", "x" : 0,   "y" : 326, "z" : 2,  "campaignBonus" : "BoDtrogH.pcx", "border" : "TODTR1HA.bmp", "area" : "TZDTR1HA.bmp", "hidden" : true },
+				"horde1Upgr":     { "animation" : "TBDNHRD2.def", "x" : 0,   "y" : 300, "z" : 2,  "campaignBonus" : "BoDtrogH.pcx", "border" : "TODTR2HA.bmp", "area" : "TZDTR2HA.bmp", "hidden" : true, "builds" : "horde1" },
+				"special2":       { "animation" : "TBDNEXT0.def", "x" : 131, "y" : 26 ,           "campaignBonus" : "BoDvort.pcx",  "border" : "TODVOR1A.bmp", "area" : "TZDVOR1A.bmp" },
+				"special3":       { "animation" : "TBDNEXT1.def", "x" : 687, "y" : 177,           "campaignBonus" : "BoDport.pcx",  "border" : "TODPORTA.bmp", "area" : "TZDPORTA.bmp" },
+				"special4":       { "animation" : "TBDNEXT2.def", "x" : 313, "y" : 298,           "campaignBonus" : "BoDacad.pcx",  "border" : "TODACAD.bmp",  "area" : "TZDACAD.bmp" },
+				"grail":          { "animation" : "TBDNHOLY.def", "x" : 562, "y" : 24,  "z" : 1,  "campaignBonus" : "BoDholy.pcx",  "border" : "TODHOLY.bmp",  "area" : "TZDHOLY.bmp" },
+				"dwellingLvl1":   { "animation" : "TBDNDW_0.def", "x" : 0,   "y" : 326, "z" : 2,  "campaignBonus" : "BoDtrog1.pcx", "border" : "TODTRG1A.bmp", "area" : "TZDTRG1A.bmp" },
+				"dwellingLvl2":   { "animation" : "TBDNDW_1.def", "x" : 0,   "y" : 26,            "campaignBonus" : "BoDharp1.pcx", "border" : "TODHAR1.bmp",  "area" : "TZDHAR1.bmp" },
+				"dwellingLvl3":   { "animation" : "TBDNDW_2.def", "x" : 118, "y" : 308, "z" : 1,  "campaignBonus" : "BoDbeh1.pcx",  "border" : "TODBEH1A.bmp", "area" : "TZDBEH1A.bmp" },
+				"dwellingLvl4":   { "animation" : "TBDNDW_3.def", "x" : 300, "y" : 29,  "z" : -1, "campaignBonus" : "BoDmedu1.pcx", "border" : "TODMED1.bmp",  "area" : "TZDMED1.bmp" },
+				"dwellingLvl5":   { "animation" : "TBDNDW_4.def", "x" : 551, "y" : 186, "z" : 1,  "campaignBonus" : "BoDmino1.pcx", "border" : "TODMIN1.bmp",  "area" : "TZDMIN1.bmp" },
+				"dwellingLvl6":   { "animation" : "TBDNDW_5.def", "x" : 270, "y" : 253, "z" : -1, "campaignBonus" : "BoDmant1.pcx", "border" : "TODMAN1.bmp",  "area" : "TZDMAN1.bmp" },
+				"dwellingLvl7":   { "animation" : "TBDNDW_6.def", "x" : 550, "y" : 0,   "z" : -1, "campaignBonus" : "BoDdrag1.pcx", "border" : "TODDRA1A.bmp", "area" : "TZDDRA1A.bmp" },
+				"dwellingUpLvl1": { "animation" : "TBDNUP_0.def", "x" : 0,   "y" : 300, "z" : 2,  "campaignBonus" : "BoDtrog2.pcx", "border" : "TODTRG2A.bmp", "area" : "TZDTRG2A.bmp" },
+				"dwellingUpLvl2": { "animation" : "TBDNUP_1.def", "x" : 0,   "y" : 26,            "campaignBonus" : "BoDharp2.pcx", "border" : "TODHAR2.bmp",  "area" : "TZDHAR2.bmp" },
+				"dwellingUpLvl3": { "animation" : "TBDNUP_2.def", "x" : 118, "y" : 256, "z" : 1,  "campaignBonus" : "BoDbeh2.pcx",  "border" : "TODBEH2A.bmp", "area" : "TZDBEH2A.bmp" },
+				"dwellingUpLvl4": { "animation" : "TBDNUP_3.def", "x" : 300, "y" : 29,  "z" : -1, "campaignBonus" : "BoDmedu2.pcx", "border" : "TODMED2.bmp",  "area" : "TZDMED2.bmp" },
+				"dwellingUpLvl5": { "animation" : "TBDNUP_4.def", "x" : 519, "y" : 172, "z" : 1,  "campaignBonus" : "BoDmino2.pcx", "border" : "TODMIN2.bmp",  "area" : "TZDMIN2.bmp" },
+				"dwellingUpLvl6": { "animation" : "TBDNUP_5.def", "x" : 270, "y" : 253, "z" : -1, "campaignBonus" : "BoDmant2.pcx", "border" : "TODMAN2.bmp",  "area" : "TZDMAN2.bmp" },
+				"dwellingUpLvl7": { "animation" : "TBDNUP_6.def", "x" : 550, "y" : 0,   "z" : -1, "campaignBonus" : "BoDdrag2.pcx", "border" : "TODDRA2A.bmp", "area" : "TZDDRA2A.bmp" }
 			},
 
 			"musicTheme" : [ "music/Dungeon" ],

+ 35 - 35
config/factions/fortress.json

@@ -80,43 +80,43 @@
 			"structures" :
 			{
 				"extraAnimation": { "animation" : "TBFREXT2.def", "x" : 372, "y" : 227, "z" : -1 },
-				"mageGuild1":     { "animation" : "TBFRMAGE.def", "x" : 0,   "y" : 200, "z" : -1, "border" : "TOFMAG1A.bmp", "area" : "TZFMAG1A.bmp" },
-				"mageGuild2":     { "animation" : "TBFRMAG2.def", "x" : 0,   "y" : 177, "z" : -1, "border" : "TOFMAG2A.bmp", "area" : "TZFMAG2A.bmp" },
-				"mageGuild3":     { "animation" : "TBFRMAG3.def", "x" : 0,   "y" : 135, "z" : -1, "border" : "TOFMAG3A.bmp", "area" : "TZFMAG3A.bmp" },
-				"tavern":         { "animation" : "TBFRTVRN.def", "x" : 634, "y" : 219, "z" : 3,  "border" : "TOFTAVA.bmp",  "area" : "TZFTAVA.bmp" },
-				"shipyard":       { "animation" : "TBFRDOCK.def", "x" : 197, "y" : 294, "z" : 1,  "border" : "TOFDCK2.bmp",  "area" : "TZFDCK2.bmp" },
-				"fort":           { "animation" : "TBFRCSTL.def", "x" : 368, "y" : 118, "z" : -2, "border" : "TOFCAS1.bmp",  "area" : "TZFCAS1.bmp" },
-				"citadel":        { "animation" : "TBFRCAS2.def", "x" : 368, "y" : 98,  "z" : -2, "border" : "TOFCAS2.bmp",  "area" : "TZFCAS2.bmp" },
-				"castle":         { "animation" : "TBFRCAS3.def", "x" : 368, "y" : 55,  "z" : -2, "border" : "TOFCAS3.bmp",  "area" : "TZFCAS3.bmp" },
-				"villageHall":    { "animation" : "TBFRHALL.def", "x" : 166, "y" : 128, "z" : 3,  "border" : "TOFHAL1.bmp",  "area" : "TZFHAL1.bmp" },
-				"townHall":       { "animation" : "TBFRHAL2.def", "x" : 166, "y" : 97,  "z" : 3,  "border" : "TOFHAL2.bmp",  "area" : "TZFHAL2.bmp" },
-				"cityHall":       { "animation" : "TBFRHAL3.def", "x" : 166, "y" : 51,  "z" : 3,  "border" : "TOFHAL3.bmp",  "area" : "TZFHAL3.bmp" },
-				"capitol":        { "animation" : "TBFRHAL4.def", "x" : 166, "y" : 2,   "z" : 3,  "border" : "TOFHAL4.bmp",  "area" : "TZFHAL4.bmp" },
-				"marketplace":    { "animation" : "TBFRMARK.def", "x" : 382, "y" : 219, "z" : 4,  "border" : "TOFMRKAA.bmp", "area" : "TZFMRKAA.bmp" },
-				"resourceSilo":   { "animation" : "TBFRSILO.def", "x" : 448, "y" : 210, "z" : 2,  "border" : "TOFMRK2A.bmp", "area" : "TZFMRK2A.bmp" },
-				"blacksmith":     { "animation" : "TBFRBLAK.def", "x" : 360, "y" : 160, "z" : 1, "border" : "TOFAIDA.bmp",  "area" : "TZFAIDA.bmp" },
-				"special1":       { "animation" : "TBFRSPEC.def", "x" : 703, "y" : 36,  "border" : "TOFCAGE.bmp",  "area" : "TZFCAGE.bmp" },
-				"horde1":         { "animation" : "TBFRHRD1.def", "x" : 641, "y" : 121, "z" : 1,  "border" : "TOFGNL1H.bmp", "area" : "TZFGNL1H.bmp", "hidden" : true },
-				"horde1Upgr":     { "animation" : "TBFRHRD2.def", "x" : 641, "y" : 68,  "z" : 1,  "border" : "TOFGNL2H.bmp", "area" : "TZFGNL2H.bmp", "hidden" : true, "builds" : "horde1" },
+				"mageGuild1":     { "animation" : "TBFRMAGE.def", "x" : 0,   "y" : 200, "z" : -1, "campaignBonus" : "BoFmage1.pcx", "border" : "TOFMAG1A.bmp", "area" : "TZFMAG1A.bmp" },
+				"mageGuild2":     { "animation" : "TBFRMAG2.def", "x" : 0,   "y" : 177, "z" : -1, "campaignBonus" : "BoFmage2.pcx", "border" : "TOFMAG2A.bmp", "area" : "TZFMAG2A.bmp" },
+				"mageGuild3":     { "animation" : "TBFRMAG3.def", "x" : 0,   "y" : 135, "z" : -1, "campaignBonus" : "BoFmage3.pcx", "border" : "TOFMAG3A.bmp", "area" : "TZFMAG3A.bmp" },
+				"tavern":         { "animation" : "TBFRTVRN.def", "x" : 634, "y" : 219, "z" : 3,  "campaignBonus" : "BoFtav.pcx",   "border" : "TOFTAVA.bmp",  "area" : "TZFTAVA.bmp" },
+				"shipyard":       { "animation" : "TBFRDOCK.def", "x" : 197, "y" : 294, "z" : 1,  "campaignBonus" : "BoFship.pcx",  "border" : "TOFDCK2.bmp",  "area" : "TZFDCK2.bmp" },
+				"fort":           { "animation" : "TBFRCSTL.def", "x" : 368, "y" : 118, "z" : -2, "campaignBonus" : "BoFcast1.pcx", "border" : "TOFCAS1.bmp",  "area" : "TZFCAS1.bmp" },
+				"citadel":        { "animation" : "TBFRCAS2.def", "x" : 368, "y" : 98,  "z" : -2, "campaignBonus" : "BoFcast2.pcx", "border" : "TOFCAS2.bmp",  "area" : "TZFCAS2.bmp" },
+				"castle":         { "animation" : "TBFRCAS3.def", "x" : 368, "y" : 55,  "z" : -2, "campaignBonus" : "BoFcast3.pcx", "border" : "TOFCAS3.bmp",  "area" : "TZFCAS3.bmp" },
+				"villageHall":    { "animation" : "TBFRHALL.def", "x" : 166, "y" : 128, "z" : 3,  "campaignBonus" : "BoFhall1.pcx", "border" : "TOFHAL1.bmp",  "area" : "TZFHAL1.bmp" },
+				"townHall":       { "animation" : "TBFRHAL2.def", "x" : 166, "y" : 97,  "z" : 3,  "campaignBonus" : "BoFhall2.pcx", "border" : "TOFHAL2.bmp",  "area" : "TZFHAL2.bmp" },
+				"cityHall":       { "animation" : "TBFRHAL3.def", "x" : 166, "y" : 51,  "z" : 3,  "campaignBonus" : "BoFhall3.pcx", "border" : "TOFHAL3.bmp",  "area" : "TZFHAL3.bmp" },
+				"capitol":        { "animation" : "TBFRHAL4.def", "x" : 166, "y" : 2,   "z" : 3,  "campaignBonus" : "BoFhall4.pcx", "border" : "TOFHAL4.bmp",  "area" : "TZFHAL4.bmp" },
+				"marketplace":    { "animation" : "TBFRMARK.def", "x" : 382, "y" : 219, "z" : 4,  "campaignBonus" : "BoFmark1.pcx", "border" : "TOFMRKAA.bmp", "area" : "TZFMRKAA.bmp" },
+				"resourceSilo":   { "animation" : "TBFRSILO.def", "x" : 448, "y" : 210, "z" : 2,  "campaignBonus" : "BoFmark2.pcx", "border" : "TOFMRK2A.bmp", "area" : "TZFMRK2A.bmp" },
+				"blacksmith":     { "animation" : "TBFRBLAK.def", "x" : 360, "y" : 160, "z" : 1,  "campaignBonus" : "BoFapoth.pcx", "border" : "TOFAIDA.bmp",  "area" : "TZFAIDA.bmp" },
+				"special1":       { "animation" : "TBFRSPEC.def", "x" : 703, "y" : 36,            "campaignBonus" : "BoFcage.pcx",  "border" : "TOFCAGE.bmp",  "area" : "TZFCAGE.bmp" },
+				"horde1":         { "animation" : "TBFRHRD1.def", "x" : 641, "y" : 121, "z" : 1,  "campaignBonus" : "BoFgno1h.pcx", "border" : "TOFGNL1H.bmp", "area" : "TZFGNL1H.bmp", "hidden" : true },
+				"horde1Upgr":     { "animation" : "TBFRHRD2.def", "x" : 641, "y" : 68,  "z" : 1,  "campaignBonus" : "BoFgno2h.pcx", "border" : "TOFGNL2H.bmp", "area" : "TZFGNL2H.bmp", "hidden" : true, "builds" : "horde1" },
 				"ship":           { "animation" : "TBFRBOAT.def", "x" : 197, "y" : 294, "z" : 1,  "border" : "TOFDCK1.bmp",  "area" : "TZFDCK1.bmp", "hidden" : true },
-				"special2":       { "animation" : "TBFREXT0.def", "x" : 341, "y" : 174, "z" : 0, "border" : "TOFCASD.bmp",  "area" : "TZFCASD.bmp" },
-				"special3":       { "animation" : "TBFREXT1.def", "x" : 349, "y" : 79,  "z" : -3, "border" : "TOFCASA.bmp",  "area" : "TZFCASA.bmp" },
-				"grail":          { "animation" : "TBFRHOLY.def", "x" : 468, "y" : 260, "z" : 5,  "border" : "TOFHLYAA.bmp", "area" : "TZFHLYAA.bmp" },
+				"special2":       { "animation" : "TBFREXT0.def", "x" : 341, "y" : 174, "z" : 0,  "campaignBonus" : "BoFcastD.pcx", "border" : "TOFCASD.bmp",  "area" : "TZFCASD.bmp" },
+				"special3":       { "animation" : "TBFREXT1.def", "x" : 349, "y" : 79,  "z" : -3, "campaignBonus" : "BoFcastA.pcx", "border" : "TOFCASA.bmp",  "area" : "TZFCASA.bmp" },
+				"grail":          { "animation" : "TBFRHOLY.def", "x" : 468, "y" : 260, "z" : 5,  "campaignBonus" : "BoFgrail.pcx", "border" : "TOFHLYAA.bmp", "area" : "TZFHLYAA.bmp" },
 				"extraCapitol":   { "animation" : "TBFRWTRW.def", "x" : 320, "y" : 141, "z" : 2 },
-				"dwellingLvl1":   { "animation" : "TBFRDW_0.def", "x" : 641, "y" : 168, "z" : 1,  "border" : "TOFGNL1.bmp",  "area" : "TZFGNL1.bmp" },
-				"dwellingLvl2":   { "animation" : "TBFRDW_1.def", "x" : 141, "y" : 178, "border" : "TOFLIZ1.bmp",  "area" : "TZFLIZ1.bmp" },
-				"dwellingLvl3":   { "animation" : "TBFRDW_3.def", "x" : 192, "y" : 85,  "border" : "TOFFLY1A.bmp", "area" : "TZFFLY1A.bmp" },
-				"dwellingLvl4":   { "animation" : "TBFRDW_4.def", "x" : 0,   "y" : 292, "z" : 1, "border" : "TOFBAS1.bmp",  "area" : "TZFBAS1.bmp" },
-				"dwellingLvl5":   { "animation" : "TBFRDW_2.def", "x" : 15,  "y" : 127, "z" : -2, "border" : "TOFGOR1.bmp",  "area" : "TZFGOR1.bmp" },
-				"dwellingLvl6":   { "animation" : "TBFRDW_5.def", "x" : 0,   "y" : 4,   "border" : "TOFWYV1.bmp",  "area" : "TZFWYV1.bmp" },
-				"dwellingLvl7":   { "animation" : "TBFRDW_6.def", "x" : 612, "y" : 291, "z" : 5,  "border" : "TOFHYD1A.bmp", "area" : "TZFHYD1A.bmp" },
-				"dwellingUpLvl1": { "animation" : "TBFRUP_0.def", "x" : 641, "y" : 107, "z" : 1,  "border" : "TOFGNL2.bmp",  "area" : "TZFGNL2.bmp" },
-				"dwellingUpLvl2": { "animation" : "TBFRUP_1.def", "x" : 125, "y" : 163, "border" : "TOFLIZ2.bmp",  "area" : "TZFLIZ2.bmp" },
-				"dwellingUpLvl3": { "animation" : "TBFRUP_3.def", "x" : 159, "y" : 19,  "border" : "TOFFLY2A.bmp", "area" : "TZFFLY2A.bmp" },
-				"dwellingUpLvl4": { "animation" : "TBFRUP_4.def", "x" : 0,   "y" : 257, "z" : 1, "border" : "TOFBAS2.bmp",  "area" : "TZFBAS2.bmp" },
-				"dwellingUpLvl5": { "animation" : "TBFRUP_2.def", "x" : 15,  "y" : 69,  "z" : -2, "border" : "TOFGOR2.bmp",  "area" : "TZFGOR2.bmp" },
-				"dwellingUpLvl6": { "animation" : "TBFRUP_5.def", "x" : 0,   "y" : 4,   "border" : "TOFWYV2.bmp",  "area" : "TZFWYV2.bmp" },
-				"dwellingUpLvl7": { "animation" : "TBFRUP_6.def", "x" : 587, "y" : 263, "z" : 5,  "border" : "TOFHYD2A.bmp", "area" : "TZFHYD2A.bmp" }
+				"dwellingLvl1":   { "animation" : "TBFRDW_0.def", "x" : 641, "y" : 168, "z" : 1,  "campaignBonus" : "BoFgnol1.pcx", "border" : "TOFGNL1.bmp",  "area" : "TZFGNL1.bmp" },
+				"dwellingLvl2":   { "animation" : "TBFRDW_1.def", "x" : 141, "y" : 178,           "campaignBonus" : "BoFlizr1.pcx", "border" : "TOFLIZ1.bmp",  "area" : "TZFLIZ1.bmp" },
+				"dwellingLvl3":   { "animation" : "TBFRDW_3.def", "x" : 192, "y" : 85,            "campaignBonus" : "BoFfly1.pcx",  "border" : "TOFFLY1A.bmp", "area" : "TZFFLY1A.bmp" },
+				"dwellingLvl4":   { "animation" : "TBFRDW_4.def", "x" : 0,   "y" : 292, "z" : 1,  "campaignBonus" : "BoFbas1.pcx",  "border" : "TOFBAS1.bmp",  "area" : "TZFBAS1.bmp" },
+				"dwellingLvl5":   { "animation" : "TBFRDW_2.def", "x" : 15,  "y" : 127, "z" : -2, "campaignBonus" : "BoFgorg1.pcx", "border" : "TOFGOR1.bmp",  "area" : "TZFGOR1.bmp" },
+				"dwellingLvl6":   { "animation" : "TBFRDW_5.def", "x" : 0,   "y" : 4,             "campaignBonus" : "BoFwyvr1.pcx", "border" : "TOFWYV1.bmp",  "area" : "TZFWYV1.bmp" },
+				"dwellingLvl7":   { "animation" : "TBFRDW_6.def", "x" : 612, "y" : 291, "z" : 5,  "campaignBonus" : "BoFhydr1.pcx", "border" : "TOFHYD1A.bmp", "area" : "TZFHYD1A.bmp" },
+				"dwellingUpLvl1": { "animation" : "TBFRUP_0.def", "x" : 641, "y" : 107, "z" : 1,  "campaignBonus" : "BoFgnol2.pcx", "border" : "TOFGNL2.bmp",  "area" : "TZFGNL2.bmp" },
+				"dwellingUpLvl2": { "animation" : "TBFRUP_1.def", "x" : 125, "y" : 163,           "campaignBonus" : "BoFlizr2.pcx", "border" : "TOFLIZ2.bmp",  "area" : "TZFLIZ2.bmp" },
+				"dwellingUpLvl3": { "animation" : "TBFRUP_3.def", "x" : 159, "y" : 19,            "campaignBonus" : "BoFfly2.pcx",  "border" : "TOFFLY2A.bmp", "area" : "TZFFLY2A.bmp" },
+				"dwellingUpLvl4": { "animation" : "TBFRUP_4.def", "x" : 0,   "y" : 257, "z" : 1,  "campaignBonus" : "BoFbas2.pcx",  "border" : "TOFBAS2.bmp",  "area" : "TZFBAS2.bmp" },
+				"dwellingUpLvl5": { "animation" : "TBFRUP_2.def", "x" : 15,  "y" : 69,  "z" : -2, "campaignBonus" : "BoFgorg2.pcx", "border" : "TOFGOR2.bmp",  "area" : "TZFGOR2.bmp" },
+				"dwellingUpLvl6": { "animation" : "TBFRUP_5.def", "x" : 0,   "y" : 4,             "campaignBonus" : "BoFwyvr2.pcx", "border" : "TOFWYV2.bmp",  "area" : "TZFWYV2.bmp" },
+				"dwellingUpLvl7": { "animation" : "TBFRUP_6.def", "x" : 587, "y" : 263, "z" : 5,  "campaignBonus" : "BoFhydr2.pcx", "border" : "TOFHYD2A.bmp", "area" : "TZFHYD2A.bmp" }
 			},
 
 			"musicTheme" : [ "music/FortressTown" ],

+ 38 - 38
config/factions/inferno.json

@@ -80,44 +80,44 @@
 			},
 			"structures" :
 			{
-				"mageGuild1":     { "animation" : "TBINMAGE.def", "x" : 667, "y" : 127, "border" : "TOIMAG1A.bmp", "area" : "TZIMAG1A.bmp" },
-				"mageGuild2":     { "animation" : "TBINMAG2.def", "x" : 667, "y" : 101, "border" : "TOIMAG2A.bmp", "area" : "TZIMAG2A.bmp" },
-				"mageGuild3":     { "animation" : "TBINMAG3.def", "x" : 667, "y" : 83,  "border" : "TOIMAG3A.bmp", "area" : "TZIMAG3A.bmp" },
-				"mageGuild4":     { "animation" : "TBINMAG4.def", "x" : 667, "y" : 56,  "border" : "TOIMAG4A.bmp", "area" : "TZIMAG4A.bmp" },
-				"mageGuild5":     { "animation" : "TBINMAG5.def", "x" : 667, "y" : 35,  "border" : "TOIMAG5A.bmp", "area" : "TZIMAG5A.bmp" },
-				"tavern":         { "animation" : "TBINTVRN.def", "x" : 105, "y" : 219, "z" : 1,  "border" : "TOITAV.bmp",   "area" : "TZITAV.bmp" },
-				"fort":           { "animation" : "TBINCSTL.def", "x" : 222, "y" : 44,  "z" : -2, "border" : "TOICAS2A.bmp", "area" : "TZICAS2A.bmp" },
-				"citadel":        { "animation" : "TBINCAS2.def", "x" : 222, "y" : 44,  "z" : -2, "border" : "TOICAS1A.bmp", "area" : "TZICAS1A.bmp" },
-				"castle":         { "animation" : "TBINCAS3.def", "x" : 222, "y" : 18,  "z" : -2, "border" : "TOICAS3A.bmp", "area" : "TZICAS3A.bmp" },
-				"villageHall":    { "animation" : "TBINHALL.def", "x" : 0,   "y" : 174, "border" : "TOIHAL1.bmp",  "area" : "TZIHAL1.bmp"  },
-				"townHall":       { "animation" : "TBINHAL2.def", "x" : 0,   "y" : 174, "border" : "TOIHAL2.bmp",  "area" : "TZIHAL2.bmp"  },
-				"cityHall":       { "animation" : "TBINHAL3.def", "x" : 0,   "y" : 174, "border" : "TOIHAL3.bmp",  "area" : "TZIHAL3.bmp"  },
-				"capitol":        { "animation" : "TBINHAL4.def", "x" : 0,   "y" : 131, "border" : "TOIHAL4.bmp",  "area" : "TZIHAL4.bmp"  },
-				"marketplace":    { "animation" : "TBINMARK.def", "x" : 511, "y" : 301, "z" : 4,  "border" : "TOIMAR1.bmp",  "area" : "TZIMAR1.bmp" },
-				"resourceSilo":   { "animation" : "TBINSILO.def", "x" : 497, "y" : 337, "z" : 5,  "border" : "TOIMAR2.bmp",  "area" : "TZIMAR2.bmp" },
-				"blacksmith":     { "animation" : "TBINBLAK.def", "x" : 684, "y" : 253, "z" : 1,  "border" : "TOIBLKA.bmp",  "area" : "TZIBLKA.bmp" },
-				"horde1":         { "animation" : "TBINHRD1.def", "x" : 614, "y" : 256, "border" : "TOIMP1HA.bmp", "area" : "TZIMP1HA.bmp", "hidden" : true },
-				"horde1Upgr":     { "animation" : "TBINHRD2.def", "x" : 614, "y" : 221, "border" : "TOIMP2HA.bmp", "area" : "TZIMP2HA.bmp", "hidden" : true, "builds" : "horde1" },
-				"special2":       { "animation" : "TBINEXT0.def", "x" : 297, "y" : 0,   "z" : -4, "border" : "TOICAB1A.bmp", "area" : "TZICAB1A.bmp" },
-				"special3":       { "animation" : "TBINEXT1.def", "x" : 227, "y" : 174, "z" : -1,  "border" : "TOICASGA.bmp", "area" : "TZICASGA.bmp" },
-				"special4":       { "animation" : "TBINEXT2.def", "x" : 593, "y" : 104, "z" : -1, "border" : "TOIPAIN.bmp",  "area" : "TZIPAIN.bmp"  },
-				"horde2":         { "animation" : "TBINHRD3.def", "x" : 10,  "y" : 301, "border" : "TOIHND1H.bmp", "area" : "TZIHND1H.bmp", "hidden" : true },
-				"horde2Upgr":     { "animation" : "TBINHRD4.def", "x" : 9,   "y" : 273, "border" : "TOIHND2H.bmp", "area" : "TZIHND2H.bmp", "hidden" : true, "builds" : "horde2" },
-				"grail":          { "animation" : "TBINHOLY.def", "x" : 24,  "y" : 10,  "z" : -1, "border" : "TOIHOLY.bmp",  "area" : "TZIHOLY.bmp" },
-				"dwellingLvl1":   { "animation" : "TBINDW_0.def", "x" : 614, "y" : 256, "border" : "TOIMP1A.bmp",  "area" : "TZIMP1A.bmp"  },
-				"dwellingLvl2":   { "animation" : "TBINDW_1.def", "x" : 187, "y" : 248, "z" : 4,  "border" : "TOIGOG1A.bmp", "area" : "TZIGOG1A.bmp" },
-				"dwellingLvl3":   { "animation" : "TBINDW_2.def", "x" : 9,   "y" : 325, "border" : "TOIHND1.bmp",  "area" : "TZIHND1.bmp"  },
-				"dwellingLvl4":   { "animation" : "TBINDW_3.def", "x" : 414, "y" : 204, "z" : 2,  "border" : "TOIDMN1.bmp",  "area" : "TZIDMN1.bmp" },
-				"dwellingLvl5":   { "animation" : "TBINDW_4.def", "x" : 359, "y" : 296, "z" : 3,  "border" : "TOIPIT1.bmp",  "area" : "TZIPIT1.bmp" },
-				"dwellingLvl6":   { "animation" : "TBINDW_5.def", "x" : 220, "y" : 350, "z" : 5,  "border" : "TOIEFR1.bmp",  "area" : "TZIEFR1.bmp" },
-				"dwellingLvl7":   { "animation" : "TBINDW_6.def", "x" : 420, "y" : 153, "z" : -3, "border" : "TOIDVL1.bmp",  "area" : "TZIDVL1.bmp" },
-				"dwellingUpLvl1": { "animation" : "TBINUP_0.def", "x" : 614, "y" : 221, "border" : "TOIMP2A.bmp",  "area" : "TZIMP2A.bmp"  },
-				"dwellingUpLvl2": { "animation" : "TBINUP_1.def", "x" : 187, "y" : 212, "z" : 4,  "border" : "TOIGOG2A.bmp", "area" : "TZIGOG2A.bmp" },
-				"dwellingUpLvl3": { "animation" : "TBINUP_2.def", "x" : 9,   "y" : 273, "border" : "TOIHND2.bmp",  "area" : "TZIHND2.bmp"  },
-				"dwellingUpLvl4": { "animation" : "TBINUP_3.def", "x" : 412, "y" : 197, "z" : 2,  "border" : "TOIDMN2.bmp",  "area" : "TZIDMN2.bmp" },
-				"dwellingUpLvl5": { "animation" : "TBINUP_4.def", "x" : 359, "y" : 244, "z" : 3,  "border" : "TOIPIT2.bmp",  "area" : "TZIPIT2.bmp" },
-				"dwellingUpLvl6": { "animation" : "TBINUP_5.def", "x" : 220, "y" : 282, "z" : 5,  "border" : "TOIEFR2.bmp",  "area" : "TZIEFR2.bmp" },
-				"dwellingUpLvl7": { "animation" : "TBINUP_6.def", "x" : 420, "y" : 105, "z" : -3, "border" : "TOIDVL2.bmp",  "area" : "TZIDVL2.bmp" }
+				"mageGuild1":     { "animation" : "TBINMAGE.def", "x" : 667, "y" : 127,           "campaignBonus" : "BoIMag1.pcx",  "border" : "TOIMAG1A.bmp", "area" : "TZIMAG1A.bmp" },
+				"mageGuild2":     { "animation" : "TBINMAG2.def", "x" : 667, "y" : 101,           "campaignBonus" : "BoIMag2.pcx",  "border" : "TOIMAG2A.bmp", "area" : "TZIMAG2A.bmp" },
+				"mageGuild3":     { "animation" : "TBINMAG3.def", "x" : 667, "y" : 83,            "campaignBonus" : "BoIMag3.pcx",  "border" : "TOIMAG3A.bmp", "area" : "TZIMAG3A.bmp" },
+				"mageGuild4":     { "animation" : "TBINMAG4.def", "x" : 667, "y" : 56,            "campaignBonus" : "BoIMag4.pcx",  "border" : "TOIMAG4A.bmp", "area" : "TZIMAG4A.bmp" },
+				"mageGuild5":     { "animation" : "TBINMAG5.def", "x" : 667, "y" : 35,            "campaignBonus" : "BoIMag5.pcx",  "border" : "TOIMAG5A.bmp", "area" : "TZIMAG5A.bmp" },
+				"tavern":         { "animation" : "TBINTVRN.def", "x" : 105, "y" : 219, "z" : 1,  "campaignBonus" : "BoITav.pcx",   "border" : "TOITAV.bmp",   "area" : "TZITAV.bmp" },
+				"fort":           { "animation" : "TBINCSTL.def", "x" : 222, "y" : 44,  "z" : -2, "campaignBonus" : "BoICas1.pcx",  "border" : "TOICAS2A.bmp", "area" : "TZICAS2A.bmp" },
+				"citadel":        { "animation" : "TBINCAS2.def", "x" : 222, "y" : 44,  "z" : -2, "campaignBonus" : "BoICas2.pcx",  "border" : "TOICAS1A.bmp", "area" : "TZICAS1A.bmp" },
+				"castle":         { "animation" : "TBINCAS3.def", "x" : 222, "y" : 18,  "z" : -2, "campaignBonus" : "BoICas3.pcx",  "border" : "TOICAS3A.bmp", "area" : "TZICAS3A.bmp" },
+				"villageHall":    { "animation" : "TBINHALL.def", "x" : 0,   "y" : 174,           "campaignBonus" : "BoIHal1.pcx",  "border" : "TOIHAL1.bmp",  "area" : "TZIHAL1.bmp"  },
+				"townHall":       { "animation" : "TBINHAL2.def", "x" : 0,   "y" : 174,           "campaignBonus" : "BoIHal2.pcx",  "border" : "TOIHAL2.bmp",  "area" : "TZIHAL2.bmp"  },
+				"cityHall":       { "animation" : "TBINHAL3.def", "x" : 0,   "y" : 174,           "campaignBonus" : "BoIHal3.pcx",  "border" : "TOIHAL3.bmp",  "area" : "TZIHAL3.bmp"  },
+				"capitol":        { "animation" : "TBINHAL4.def", "x" : 0,   "y" : 131,           "campaignBonus" : "BoIHal4.pcx",  "border" : "TOIHAL4.bmp",  "area" : "TZIHAL4.bmp"  },
+				"marketplace":    { "animation" : "TBINMARK.def", "x" : 511, "y" : 301, "z" : 4,  "campaignBonus" : "BoIMrk1.pcx",  "border" : "TOIMAR1.bmp",  "area" : "TZIMAR1.bmp" },
+				"resourceSilo":   { "animation" : "TBINSILO.def", "x" : 497, "y" : 337, "z" : 5,  "campaignBonus" : "BoIMrk2.pcx",  "border" : "TOIMAR2.bmp",  "area" : "TZIMAR2.bmp" },
+				"blacksmith":     { "animation" : "TBINBLAK.def", "x" : 684, "y" : 253, "z" : 1,  "campaignBonus" : "BoIBlak.pcx",  "border" : "TOIBLKA.bmp",  "area" : "TZIBLKA.bmp" },
+				"horde1":         { "animation" : "TBINHRD1.def", "x" : 614, "y" : 256,           "campaignBonus" : "BoIImpH.pcx",  "border" : "TOIMP1HA.bmp", "area" : "TZIMP1HA.bmp", "hidden" : true },
+				"horde1Upgr":     { "animation" : "TBINHRD2.def", "x" : 614, "y" : 221,           "campaignBonus" : "BoIImp2H.pcx", "border" : "TOIMP2HA.bmp", "area" : "TZIMP2HA.bmp", "hidden" : true, "builds" : "horde1" },
+				"special2":       { "animation" : "TBINEXT0.def", "x" : 297, "y" : 0,   "z" : -4, "campaignBonus" : "BoICasB.pcx",  "border" : "TOICAB1A.bmp", "area" : "TZICAB1A.bmp" },
+				"special3":       { "animation" : "TBINEXT1.def", "x" : 227, "y" : 174, "z" : -1, "campaignBonus" : "BoICasG.pcx",  "border" : "TOICASGA.bmp", "area" : "TZICASGA.bmp" },
+				"special4":       { "animation" : "TBINEXT2.def", "x" : 593, "y" : 104, "z" : -1, "campaignBonus" : "BoIMagO.pcx",  "border" : "TOIPAIN.bmp",  "area" : "TZIPAIN.bmp"  },
+				"horde2":         { "animation" : "TBINHRD3.def", "x" : 10,  "y" : 301,           "campaignBonus" : "BoIHndH.pcx",  "border" : "TOIHND1H.bmp", "area" : "TZIHND1H.bmp", "hidden" : true },
+				"horde2Upgr":     { "animation" : "TBINHRD4.def", "x" : 9,   "y" : 273,           "campaignBonus" : "BoIHnd2H.pcx", "border" : "TOIHND2H.bmp", "area" : "TZIHND2H.bmp", "hidden" : true, "builds" : "horde2" },
+				"grail":          { "animation" : "TBINHOLY.def", "x" : 24,  "y" : 10,  "z" : -1, "campaignBonus" : "BoIHoly.pcx",  "border" : "TOIHOLY.bmp",  "area" : "TZIHOLY.bmp" },
+				"dwellingLvl1":   { "animation" : "TBINDW_0.def", "x" : 614, "y" : 256,           "campaignBonus" : "BoIImp1.pcx",  "border" : "TOIMP1A.bmp",  "area" : "TZIMP1A.bmp"  },
+				"dwellingLvl2":   { "animation" : "TBINDW_1.def", "x" : 187, "y" : 248, "z" : 4,  "campaignBonus" : "BoIGog1.pcx",  "border" : "TOIGOG1A.bmp", "area" : "TZIGOG1A.bmp" },
+				"dwellingLvl3":   { "animation" : "TBINDW_2.def", "x" : 9,   "y" : 325,           "campaignBonus" : "BoIHnd1.pcx",  "border" : "TOIHND1.bmp",  "area" : "TZIHND1.bmp"  },
+				"dwellingLvl4":   { "animation" : "TBINDW_3.def", "x" : 414, "y" : 204, "z" : 2,  "campaignBonus" : "BoIDmn1.pcx",  "border" : "TOIDMN1.bmp",  "area" : "TZIDMN1.bmp" },
+				"dwellingLvl5":   { "animation" : "TBINDW_4.def", "x" : 359, "y" : 296, "z" : 3,  "campaignBonus" : "BoIPit1.pcx",  "border" : "TOIPIT1.bmp",  "area" : "TZIPIT1.bmp" },
+				"dwellingLvl6":   { "animation" : "TBINDW_5.def", "x" : 220, "y" : 350, "z" : 5,  "campaignBonus" : "BoIEfr1.pcx",  "border" : "TOIEFR1.bmp",  "area" : "TZIEFR1.bmp" },
+				"dwellingLvl7":   { "animation" : "TBINDW_6.def", "x" : 420, "y" : 153, "z" : -3, "campaignBonus" : "BoIDvl1.pcx",  "border" : "TOIDVL1.bmp",  "area" : "TZIDVL1.bmp" },
+				"dwellingUpLvl1": { "animation" : "TBINUP_0.def", "x" : 614, "y" : 221,           "campaignBonus" : "BoIImp2.pcx",  "border" : "TOIMP2A.bmp",  "area" : "TZIMP2A.bmp"  },
+				"dwellingUpLvl2": { "animation" : "TBINUP_1.def", "x" : 187, "y" : 212, "z" : 4,  "campaignBonus" : "BoIGog2.pcx",  "border" : "TOIGOG2A.bmp", "area" : "TZIGOG2A.bmp" },
+				"dwellingUpLvl3": { "animation" : "TBINUP_2.def", "x" : 9,   "y" : 273,           "campaignBonus" : "BoIHnd2.pcx",  "border" : "TOIHND2.bmp",  "area" : "TZIHND2.bmp"  },
+				"dwellingUpLvl4": { "animation" : "TBINUP_3.def", "x" : 412, "y" : 197, "z" : 2,  "campaignBonus" : "BoIDmn2.pcx",  "border" : "TOIDMN2.bmp",  "area" : "TZIDMN2.bmp" },
+				"dwellingUpLvl5": { "animation" : "TBINUP_4.def", "x" : 359, "y" : 244, "z" : 3,  "campaignBonus" : "BoIPit2.pcx",  "border" : "TOIPIT2.bmp",  "area" : "TZIPIT2.bmp" },
+				"dwellingUpLvl6": { "animation" : "TBINUP_5.def", "x" : 220, "y" : 282, "z" : 5,  "campaignBonus" : "BoIEfr2.pcx",  "border" : "TOIEFR2.bmp",  "area" : "TZIEFR2.bmp" },
+				"dwellingUpLvl7": { "animation" : "TBINUP_6.def", "x" : 420, "y" : 105, "z" : -3, "campaignBonus" : "BoIDvl2.pcx",  "border" : "TOIDVL2.bmp",  "area" : "TZIDVL2.bmp" }
 			},
 
 			"musicTheme" : [ "music/InfernoTown" ],

+ 37 - 37
config/factions/necropolis.json

@@ -81,47 +81,47 @@
 			"structures" :
 			{
 				"extraAnimation": { "animation" : "TBNCEXT2.def", "x" : 25,  "y" : 279, "z" : 9 },
-				"mageGuild1":     { "animation" : "TBNCMAGE.def", "x" : 341, "y" : 116, "z" : -1, "border" : "TONMAG1.bmp",  "area" : "TZNMAG1.bmp" },
-				"mageGuild2":     { "animation" : "TBNCMAG2.def", "x" : 341, "y" : 97,  "z" : -1, "border" : "TONMAG2.bmp",  "area" : "TZNMAG2.bmp" },
-				"mageGuild3":     { "animation" : "TBNCMAG3.def", "x" : 341, "y" : 78,  "z" : -1, "border" : "TONMAG3.bmp",  "area" : "TZNMAG3.bmp" },
-				"mageGuild4":     { "animation" : "TBNCMAG4.def", "x" : 340, "y" : 62,  "z" : -1, "border" : "TONMAG4.bmp",  "area" : "TZNMAG4.bmp" },
-				"mageGuild5":     { "animation" : "TBNCMAG5.def", "x" : 343, "y" : 35,  "z" : -1, "border" : "TONMAG5.bmp",  "area" : "TZNMAG5.bmp" },
-				"tavern":         { "animation" : "TBNCTVRN.def", "x" : 508, "y" : 189, "border" : "TONTAV.bmp",   "area" : "TZNTAV.bmp"  },
-				"shipyard":       { "animation" : "TBNCDOCK.def", "x" : 617, "y" : 265, "z" : -2, "border" : "TONSHPBA.bmp", "area" : "TZNSHPBA.bmp" },
-				"fort":           { "animation" : "TBNCCSTL.def", "x" : 138, "y" : 66,  "border" : "TONCAS1.bmp",  "area" : "TZNCAS1.bmp" },
-				"citadel":        { "animation" : "TBNCCAS2.def", "x" : 139, "y" : 66,  "border" : "TONCAS2.bmp",  "area" : "TZNCAS2.bmp" },
-				"castle":         { "animation" : "TBNCCAS3.def", "x" : 34,  "y" : 18,  "border" : "TONCAS3.bmp",  "area" : "TZNCAS3.bmp" },
-				"villageHall":    { "animation" : "TBNCHALL.def", "x" : 468, "y" : 76,  "z" : -2, "border" : "TONHAL1.bmp",  "area" : "TZNHAL1.bmp" },
-				"townHall":       { "animation" : "TBNCHAL2.def", "x" : 482, "y" : 56,  "z" : -2, "border" : "TONHAL2.bmp",  "area" : "TZNHAL2.bmp" },
-				"cityHall":       { "animation" : "TBNCHAL3.def", "x" : 478, "y" : 26,  "z" : -2, "border" : "TONHAL3.bmp",  "area" : "TZNHAL3.bmp" },
-				"capitol":        { "animation" : "TBNCHAL4.def", "x" : 481, "y" : 26,  "z" : -2, "border" : "TONHAL4.bmp",  "area" : "TZNHAL4.bmp" },
-				"marketplace":    { "animation" : "TBNCMARK.def", "x" : 347, "y" : 215, "z" : 2,  "border" : "TONMRK1.bmp",  "area" : "TZNMRK1.bmp" },
-				"resourceSilo":   { "animation" : "TBNCSILO.def", "x" : 276, "y" : 185, "z" : 1,  "border" : "TONMRK2.bmp",  "area" : "TZNMRK2.bmp" },
-				"blacksmith":     { "animation" : "TBNCBLAK.def", "x" : 382, "y" : 252, "z" : 7,  "border" : "TONSMITA.bmp", "area" : "TZNSMITA.bmp" },
-				"special1":       { "animation" : "TBNCSPEC.def", "x" : 18,  "y" : 0,   "z" : -1, "border" : "TONSHRDA.bmp", "area" : "TZNSHRDA.bmp" },
-				"horde1":         { "animation" : "TBNCHRD1.def", "x" : 80,  "y" : 222, "z" : 5, "border" : "TONSKE1H.bmp", "area" : "TZNSKE1H.bmp", "hidden" : true },
-				"horde1Upgr":     { "animation" : "TBNCHRD2.def", "x" : 64,  "y" : 222, "z" : 5, "border" : "TONSKE2H.bmp", "area" : "TZNSKE2H.bmp", "hidden" : true, "builds" : "horde1" },
+				"mageGuild1":     { "animation" : "TBNCMAGE.def", "x" : 341, "y" : 116, "z" : -1, "campaignBonus" : "BoNmage1.pcx", "border" : "TONMAG1.bmp",  "area" : "TZNMAG1.bmp" },
+				"mageGuild2":     { "animation" : "TBNCMAG2.def", "x" : 341, "y" : 97,  "z" : -1, "campaignBonus" : "BoNmage2.pcx", "border" : "TONMAG2.bmp",  "area" : "TZNMAG2.bmp" },
+				"mageGuild3":     { "animation" : "TBNCMAG3.def", "x" : 341, "y" : 78,  "z" : -1, "campaignBonus" : "BoNmage3.pcx", "border" : "TONMAG3.bmp",  "area" : "TZNMAG3.bmp" },
+				"mageGuild4":     { "animation" : "TBNCMAG4.def", "x" : 340, "y" : 62,  "z" : -1, "campaignBonus" : "BoNmage4.pcx", "border" : "TONMAG4.bmp",  "area" : "TZNMAG4.bmp" },
+				"mageGuild5":     { "animation" : "TBNCMAG5.def", "x" : 343, "y" : 35,  "z" : -1, "campaignBonus" : "BoNmage5.pcx", "border" : "TONMAG5.bmp",  "area" : "TZNMAG5.bmp" },
+				"tavern":         { "animation" : "TBNCTVRN.def", "x" : 508, "y" : 189,           "campaignBonus" : "BoNtav.pcx", "border" : "TONTAV.bmp",   "area" : "TZNTAV.bmp"  },
+				"shipyard":       { "animation" : "TBNCDOCK.def", "x" : 617, "y" : 265, "z" : -2, "campaignBonus" : "BoNship.pcx", "border" : "TONSHPBA.bmp", "area" : "TZNSHPBA.bmp" },
+				"fort":           { "animation" : "TBNCCSTL.def", "x" : 138, "y" : 66,            "campaignBonus" : "BoNcast1.pcx", "border" : "TONCAS1.bmp",  "area" : "TZNCAS1.bmp" },
+				"citadel":        { "animation" : "TBNCCAS2.def", "x" : 139, "y" : 66,            "campaignBonus" : "BoNcast2.pcx", "border" : "TONCAS2.bmp",  "area" : "TZNCAS2.bmp" },
+				"castle":         { "animation" : "TBNCCAS3.def", "x" : 34,  "y" : 18,            "campaignBonus" : "BoNcast3.pcx", "border" : "TONCAS3.bmp",  "area" : "TZNCAS3.bmp" },
+				"villageHall":    { "animation" : "TBNCHALL.def", "x" : 468, "y" : 76,  "z" : -2, "campaignBonus" : "BoNhall1.pcx", "border" : "TONHAL1.bmp",  "area" : "TZNHAL1.bmp" },
+				"townHall":       { "animation" : "TBNCHAL2.def", "x" : 482, "y" : 56,  "z" : -2, "campaignBonus" : "BoNhall2.pcx", "border" : "TONHAL2.bmp",  "area" : "TZNHAL2.bmp" },
+				"cityHall":       { "animation" : "TBNCHAL3.def", "x" : 478, "y" : 26,  "z" : -2, "campaignBonus" : "BoNhall3.pcx", "border" : "TONHAL3.bmp",  "area" : "TZNHAL3.bmp" },
+				"capitol":        { "animation" : "TBNCHAL4.def", "x" : 481, "y" : 26,  "z" : -2, "campaignBonus" : "BoNhall4.pcx", "border" : "TONHAL4.bmp",  "area" : "TZNHAL4.bmp" },
+				"marketplace":    { "animation" : "TBNCMARK.def", "x" : 347, "y" : 215, "z" : 2,  "campaignBonus" : "BoNmark1.pcx", "border" : "TONMRK1.bmp",  "area" : "TZNMRK1.bmp" },
+				"resourceSilo":   { "animation" : "TBNCSILO.def", "x" : 276, "y" : 185, "z" : 1,  "campaignBonus" : "BoNmark2.pcx", "border" : "TONMRK2.bmp",  "area" : "TZNMRK2.bmp" },
+				"blacksmith":     { "animation" : "TBNCBLAK.def", "x" : 382, "y" : 252, "z" : 7,  "campaignBonus" : "BoNsmith.pcx", "border" : "TONSMITA.bmp", "area" : "TZNSMITA.bmp" },
+				"special1":       { "animation" : "TBNCSPEC.def", "x" : 18,  "y" : 0,   "z" : -1, "campaignBonus" : "BoNshrod.pcx", "border" : "TONSHRDA.bmp", "area" : "TZNSHRDA.bmp" },
+				"horde1":         { "animation" : "TBNCHRD1.def", "x" : 80,  "y" : 222, "z" : 5,  "campaignBonus" : "BoNskelH.pcx", "border" : "TONSKE1H.bmp", "area" : "TZNSKE1H.bmp", "hidden" : true },
+				"horde1Upgr":     { "animation" : "TBNCHRD2.def", "x" : 64,  "y" : 222, "z" : 5,  "campaignBonus" : "BoNnecro.pcx", "border" : "TONSKE2H.bmp", "area" : "TZNSKE2H.bmp", "hidden" : true, "builds" : "horde1" },
 				"ship":           { "animation" : "TBNCBOAT.def", "x" : 617, "y" : 265, "z" : -2, "border" : "TONSHPNA.bmp", "area" : "TZNSHPNA.bmp", "hidden" : true },
-				"special2":       { "animation" : "TBNCEXT0.def", "x" : 307, "y" : 61,  "z" : -2, "border" : "TONNECRA.bmp", "area" : "TZNNECRA.bmp" },
-				"special3":       { "animation" : "TBNCEXT1.def", "x" : 247, "y" : 275, "z" : 4,  "border" : "TONSKELT.bmp", "area" : "TZNSKELT.bmp" },
-				"grail":          { "animation" : "TBNCHOLY.def", "x" : 410, "y" : 88,  "border" : "TONHOLYA.bmp", "area" : "TZNHOLYA.bmp" },
+				"special2":       { "animation" : "TBNCEXT0.def", "x" : 307, "y" : 61,  "z" : -2, "campaignBonus" : "BoNnecro.pcx", "border" : "TONNECRA.bmp", "area" : "TZNNECRA.bmp" },
+				"special3":       { "animation" : "TBNCEXT1.def", "x" : 247, "y" : 275, "z" : 4,  "campaignBonus" : "BoNskelT.pcx", "border" : "TONSKELT.bmp", "area" : "TZNSKELT.bmp" },
+				"grail":          { "animation" : "TBNCHOLY.def", "x" : 410, "y" : 88,            "campaignBonus" : "BoNholyG.pcx", "border" : "TONHOLYA.bmp", "area" : "TZNHOLYA.bmp" },
 				"extraTownHall":  { "animation" : "TBNCEXT3.def", "x" : 0,   "y" : 241, "z" : 6 },
 				"extraCityHall":  { "animation" : "TBNCEXT4.def", "x" : 321, "y" : 255, "z" : 6 },
 				"extraCapitol":   { "animation" : "TBNCEXT5.def", "x" : 475, "y" : 257, "z" : 6 },
-				"dwellingLvl1":   { "animation" : "TBNCDW_0.def", "x" : 80,  "y" : 222, "z" : 5, "border" : "TONSKEL1.bmp", "area" : "TZNSKEL1.bmp" },
-				"dwellingLvl2":   { "animation" : "TBNCDW_1.def", "x" : 502, "y" : 223, "z" : 1, "border" : "TONZOMB1.bmp", "area" : "TZNZOMB1.bmp" },
-				"dwellingLvl3":   { "animation" : "TBNCDW_2.def", "x" : 0,   "y" : 187, "z" : 8, "border" : "TONWIGH1.bmp", "area" : "TZNWIGH1.bmp" },
-				"dwellingLvl4":   { "animation" : "TBNCDW_3.def", "x" : 607, "y" : 212, "z" : 2, "border" : "TONVAM1.bmp",  "area" : "TZNVAM1.bmp" },
-				"dwellingLvl5":   { "animation" : "TBNCDW_4.def", "x" : 206, "y" : 207, "z" : 3, "border" : "TONLICH1.bmp", "area" : "TZNLICH1.bmp" },
-				"dwellingLvl6":   { "animation" : "TBNCDW_5.def", "x" : 0,   "y" : 31,  "z" : -2, "border" : "TONBKN1.bmp",  "area" : "TZNBKN1.bmp" },
-				"dwellingLvl7":   { "animation" : "TBNCDW_6.def", "x" : 663, "y" : 25,  "border" : "TONBON1.bmp",  "area" : "TZNBON1.bmp" },
-				"dwellingUpLvl1": { "animation" : "TBNCUP_0.def", "x" : 64,  "y" : 222, "z" : 5, "border" : "TONSKEL2.bmp", "area" : "TZNSKEL2.bmp" },
-				"dwellingUpLvl2": { "animation" : "TBNCUP_1.def", "x" : 498, "y" : 224, "z" : 1, "border" : "TONZOMB2.bmp", "area" : "TZNZOMB2.bmp" },
-				"dwellingUpLvl3": { "animation" : "TBNCUP_2.def", "x" : 0,   "y" : 179, "z" : 8, "border" : "TONWIGH2.bmp", "area" : "TZNWIGH2.bmp" },
-				"dwellingUpLvl4": { "animation" : "TBNCUP_3.def", "x" : 615, "y" : 193, "z" : 2, "border" : "TONVAM2.bmp",  "area" : "TZNVAM2.bmp" },
-				"dwellingUpLvl5": { "animation" : "TBNCUP_4.def", "x" : 222, "y" : 171, "z" : 3, "border" : "TONLICH2.bmp", "area" : "TZNLICH2.bmp" },
-				"dwellingUpLvl6": { "animation" : "TBNCUP_5.def", "x" : 0,   "y" : 30,  "z" : -2, "border" : "TONBKN2.bmp",  "area" : "TZNBKN2.bmp" },
-				"dwellingUpLvl7": { "animation" : "TBNCUP_6.def", "x" : 662, "y" : 23,  "border" : "TONBON2.bmp",  "area" : "TZNBON2.bmp" }
+				"dwellingLvl1":   { "animation" : "TBNCDW_0.def", "x" : 80,  "y" : 222, "z" : 5,  "campaignBonus" : "BoNskel1.pcx", "border" : "TONSKEL1.bmp", "area" : "TZNSKEL1.bmp" },
+				"dwellingLvl2":   { "animation" : "TBNCDW_1.def", "x" : 502, "y" : 223, "z" : 1,  "campaignBonus" : "BoNzomb1.pcx", "border" : "TONZOMB1.bmp", "area" : "TZNZOMB1.bmp" },
+				"dwellingLvl3":   { "animation" : "TBNCDW_2.def", "x" : 0,   "y" : 187, "z" : 8,  "campaignBonus" : "BoNwigh1.pcx", "border" : "TONWIGH1.bmp", "area" : "TZNWIGH1.bmp" },
+				"dwellingLvl4":   { "animation" : "TBNCDW_3.def", "x" : 607, "y" : 212, "z" : 2,  "campaignBonus" : "BoNvamp1.pcx", "border" : "TONVAM1.bmp",  "area" : "TZNVAM1.bmp" },
+				"dwellingLvl5":   { "animation" : "TBNCDW_4.def", "x" : 206, "y" : 207, "z" : 3,  "campaignBonus" : "BoNlich1.pcx", "border" : "TONLICH1.bmp", "area" : "TZNLICH1.bmp" },
+				"dwellingLvl6":   { "animation" : "TBNCDW_5.def", "x" : 0,   "y" : 31,  "z" : -2, "campaignBonus" : "BoNbkni1.pcx", "border" : "TONBKN1.bmp",  "area" : "TZNBKN1.bmp" },
+				"dwellingLvl7":   { "animation" : "TBNCDW_6.def", "x" : 663, "y" : 25,            "campaignBonus" : "BoNbone1.pcx", "border" : "TONBON1.bmp",  "area" : "TZNBON1.bmp" },
+				"dwellingUpLvl1": { "animation" : "TBNCUP_0.def", "x" : 64,  "y" : 222, "z" : 5,  "campaignBonus" : "BoNskel2.pcx", "border" : "TONSKEL2.bmp", "area" : "TZNSKEL2.bmp" },
+				"dwellingUpLvl2": { "animation" : "TBNCUP_1.def", "x" : 498, "y" : 224, "z" : 1,  "campaignBonus" : "BoNzomb2.pcx", "border" : "TONZOMB2.bmp", "area" : "TZNZOMB2.bmp" },
+				"dwellingUpLvl3": { "animation" : "TBNCUP_2.def", "x" : 0,   "y" : 179, "z" : 8,  "campaignBonus" : "BoNwigh2.pcx", "border" : "TONWIGH2.bmp", "area" : "TZNWIGH2.bmp" },
+				"dwellingUpLvl4": { "animation" : "TBNCUP_3.def", "x" : 615, "y" : 193, "z" : 2,  "campaignBonus" : "BoNvamp2.pcx", "border" : "TONVAM2.bmp",  "area" : "TZNVAM2.bmp" },
+				"dwellingUpLvl5": { "animation" : "TBNCUP_4.def", "x" : 222, "y" : 171, "z" : 3,  "campaignBonus" : "BoNlich2.pcx", "border" : "TONLICH2.bmp", "area" : "TZNLICH2.bmp" },
+				"dwellingUpLvl6": { "animation" : "TBNCUP_5.def", "x" : 0,   "y" : 30,  "z" : -2, "campaignBonus" : "BoNbkni2.pcx", "border" : "TONBKN2.bmp",  "area" : "TZNBKN2.bmp" },
+				"dwellingUpLvl7": { "animation" : "TBNCUP_6.def", "x" : 662, "y" : 23,            "campaignBonus" : "BoNbone2.pcx", "border" : "TONBON2.bmp",  "area" : "TZNBON2.bmp" }
 			},
 
 			"musicTheme" : [ "music/NecroTown" ],

+ 38 - 38
config/factions/rampart.json

@@ -80,47 +80,47 @@
 			"structures" :
 			{
 				"extraAnimation": { "animation" : "TBRMEXT2.def", "x" : 327, "y" : 236, "z" : 1 },
-				"mageGuild1":     { "animation" : "TBRMMAGE.def", "x" : 454, "y" : 200, "z" : -1, "border" : "TORMAG1.bmp",  "area" : "TZRMAG1.bmp" },
-				"mageGuild2":     { "animation" : "TBRMMAG2.def", "x" : 438, "y" : 178, "z" : -1, "border" : "TORMAG2.bmp",  "area" : "TZRMAG2.bmp" },
-				"mageGuild3":     { "animation" : "TBRMMAG3.def", "x" : 418, "y" : 153, "z" : -1, "border" : "TORMAG3.bmp",  "area" : "TZRMAG3.bmp" },
-				"mageGuild4":     { "animation" : "TBRMMAG4.def", "x" : 406, "y" : 129, "z" : -1, "border" : "TORMAG4.bmp",  "area" : "TZRMAG4.bmp" },
-				"mageGuild5":     { "animation" : "TBRMMAG5.def", "x" : 384, "y" : 104, "z" : -1, "border" : "TORMAG5.bmp",  "area" : "TZRMAG5.bmp" },
-				"tavern":         { "animation" : "TBRMTVRN.def", "x" : 181, "y" : 229, "z" : 1,  "border" : "TORTAV.bmp",   "area" : "TZRTAV.bmp" },
-				"fort":           { "animation" : "TBRMCSTL.def", "x" : 63,  "y" : 25,  "z" : -4, "border" : "TORCAS1.bmp",  "area" : "TZRCAS1.bmp" },
-				"citadel":        { "animation" : "TBRMCAS2.def", "x" : 79,  "y" : 18,  "z" : -4, "border" : "TORCAS3.bmp",  "area" : "TZRCAS3.bmp" },
-				"castle":         { "animation" : "TBRMCAS3.def", "x" : 79,  "y" : 18,  "z" : -4, "border" : "TORCAS2.bmp",  "area" : "TZRCAS2.bmp" },
-				"villageHall":    { "animation" : "TBRMHALL.def", "x" : 565, "y" : 216, "border" : "TORHAL1.bmp",  "area" : "TZRHAL1.bmp" },
-				"townHall":       { "animation" : "TBRMHAL2.def", "x" : 538, "y" : 187, "border" : "TORHAL2.bmp",  "area" : "TZRHAL2.bmp" },
-				"cityHall":       { "animation" : "TBRMHAL3.def", "x" : 538, "y" : 187, "border" : "TORHAL3.bmp",  "area" : "TZRHAL3.bmp" },
-				"capitol":        { "animation" : "TBRMHAL4.def", "x" : 534, "y" : 187, "border" : "TORHAL4.bmp",  "area" : "TZRHAL4.bmp" },
-				"marketplace":    { "animation" : "TBRMMARK.def", "x" : 129, "y" : 301, "z" : 3,  "border" : "TORMRK1.bmp",  "area" : "TZRMRK1.bmp" },
-				"resourceSilo":   { "animation" : "TBRMSILO.def", "x" : 245, "y" : 324, "z" : 4,  "border" : "TORMRK2.bmp",  "area" : "TZRMRK2.bmp" },
-				"blacksmith":     { "animation" : "TBRMBLAK.def", "x" : 558, "y" : 105, "z" : -3, "border" : "TORAID.bmp",   "area" : "TZRAID.bmp" },
-				"special1":       { "animation" : "TBRMSPEC.def", "x" : 555, "y" : 297, "z" : 2, "border" : "TORGAR1A.bmp", "area" : "TZRGAR1A.bmp" },
-				"horde1":         { "animation" : "TBRMHRD1.def", "x" : 0,   "y" : 154, "z" : -2, "border" : "TORDWF1H.bmp", "area" : "TZRDWF1H.bmp", "hidden" : true },
-				"horde1Upgr":     { "animation" : "TBRMHRD2.def", "x" : 0,   "y" : 143, "z" : -2, "border" : "TORDWF2H.bmp", "area" : "TZRDWF2H.bmp", "hidden" : true, "builds" : "horde1" },
-				"special2":       { "animation" : "TBRMEXT0.def", "x" : 555, "y" : 297, "z" : 2, "border" : "TORGAR2A.bmp", "area" : "TZRGAR2A.bmp" },
-				"special3":       { "animation" : "TBRMEXT1.def", "x" : 0,   "y" : 181, "z" : 1, "border" : "TORDWFT.bmp",  "area" : "TZRDWFT.bmp" },
-				"horde2":         { "animation" : "TBRMHRD3.def", "x" : 47,  "y" : 142, "z" : -1, "border" : "TORTRE1H.bmp", "area" : "TZRTRE1H.bmp", "hidden" : true },
-				"horde2Upgr":     { "animation" : "TBRMHRD4.def", "x" : 47,  "y" : 142, "z" : -1, "border" : "TORTRE2H.bmp", "area" : "TZRTRE2H.bmp", "hidden" : true, "builds" : "horde2" },
-				"grail":          { "animation" : "TBRMHOLY.def", "x" : 0,   "y" : 54,  "z" : -3, "border" : "TORHOLY.bmp",  "area" : "TZRHOLY.bmp" },
+				"mageGuild1":     { "animation" : "TBRMMAGE.def", "x" : 454, "y" : 200, "z" : -1, "campaignBonus" : "BoRMag1.pcx", "border" : "TORMAG1.bmp",  "area" : "TZRMAG1.bmp" },
+				"mageGuild2":     { "animation" : "TBRMMAG2.def", "x" : 438, "y" : 178, "z" : -1, "campaignBonus" : "BoRMag2.pcx", "border" : "TORMAG2.bmp",  "area" : "TZRMAG2.bmp" },
+				"mageGuild3":     { "animation" : "TBRMMAG3.def", "x" : 418, "y" : 153, "z" : -1, "campaignBonus" : "BoRMag3.pcx", "border" : "TORMAG3.bmp",  "area" : "TZRMAG3.bmp" },
+				"mageGuild4":     { "animation" : "TBRMMAG4.def", "x" : 406, "y" : 129, "z" : -1, "campaignBonus" : "BoRMag4.pcx", "border" : "TORMAG4.bmp",  "area" : "TZRMAG4.bmp" },
+				"mageGuild5":     { "animation" : "TBRMMAG5.def", "x" : 384, "y" : 104, "z" : -1, "campaignBonus" : "BoRMag5.pcx", "border" : "TORMAG5.bmp",  "area" : "TZRMAG5.bmp" },
+				"tavern":         { "animation" : "TBRMTVRN.def", "x" : 181, "y" : 229, "z" : 1,  "campaignBonus" : "BoRTav.pcx", "border" : "TORTAV.bmp",   "area" : "TZRTAV.bmp" },
+				"fort":           { "animation" : "TBRMCSTL.def", "x" : 63,  "y" : 25,  "z" : -4, "campaignBonus" : "BoRCas1.pcx", "border" : "TORCAS1.bmp",  "area" : "TZRCAS1.bmp" },
+				"citadel":        { "animation" : "TBRMCAS2.def", "x" : 79,  "y" : 18,  "z" : -4, "campaignBonus" : "BoRCas2.pcx", "border" : "TORCAS3.bmp",  "area" : "TZRCAS3.bmp" },
+				"castle":         { "animation" : "TBRMCAS3.def", "x" : 79,  "y" : 18,  "z" : -4, "campaignBonus" : "BoRCas3.pcx", "border" : "TORCAS2.bmp",  "area" : "TZRCAS2.bmp" },
+				"villageHall":    { "animation" : "TBRMHALL.def", "x" : 565, "y" : 216,           "campaignBonus" : "BoRHal1.pcx", "border" : "TORHAL1.bmp",  "area" : "TZRHAL1.bmp" },
+				"townHall":       { "animation" : "TBRMHAL2.def", "x" : 538, "y" : 187,           "campaignBonus" : "BoRHal2.pcx", "border" : "TORHAL2.bmp",  "area" : "TZRHAL2.bmp" },
+				"cityHall":       { "animation" : "TBRMHAL3.def", "x" : 538, "y" : 187,           "campaignBonus" : "BoRHal3.pcx", "border" : "TORHAL3.bmp",  "area" : "TZRHAL3.bmp" },
+				"capitol":        { "animation" : "TBRMHAL4.def", "x" : 534, "y" : 187,           "campaignBonus" : "BoRHal4.pcx", "border" : "TORHAL4.bmp",  "area" : "TZRHAL4.bmp" },
+				"marketplace":    { "animation" : "TBRMMARK.def", "x" : 129, "y" : 301, "z" : 3,  "campaignBonus" : "BoRMrk1.pcx", "border" : "TORMRK1.bmp",  "area" : "TZRMRK1.bmp" },
+				"resourceSilo":   { "animation" : "TBRMSILO.def", "x" : 245, "y" : 324, "z" : 4,  "campaignBonus" : "BoRMrk2.pcx", "border" : "TORMRK2.bmp",  "area" : "TZRMRK2.bmp" },
+				"blacksmith":     { "animation" : "TBRMBLAK.def", "x" : 558, "y" : 105, "z" : -3, "campaignBonus" : "BoRAid.pcx", "border" : "TORAID.bmp",   "area" : "TZRAID.bmp" },
+				"special1":       { "animation" : "TBRMSPEC.def", "x" : 555, "y" : 297, "z" : 2,  "campaignBonus" : "BoRGar1.pcx", "border" : "TORGAR1A.bmp", "area" : "TZRGAR1A.bmp" },
+				"horde1":         { "animation" : "TBRMHRD1.def", "x" : 0,   "y" : 154, "z" : -2, "campaignBonus" : "BoRDwf1h.pcx", "border" : "TORDWF1H.bmp", "area" : "TZRDWF1H.bmp", "hidden" : true },
+				"horde1Upgr":     { "animation" : "TBRMHRD2.def", "x" : 0,   "y" : 143, "z" : -2, "campaignBonus" : "BoRDwf2h.pcx", "border" : "TORDWF2H.bmp", "area" : "TZRDWF2H.bmp", "hidden" : true, "builds" : "horde1" },
+				"special2":       { "animation" : "TBRMEXT0.def", "x" : 555, "y" : 297, "z" : 2,  "campaignBonus" : "BoRGar2.pcx", "border" : "TORGAR2A.bmp", "area" : "TZRGAR2A.bmp" },
+				"special3":       { "animation" : "TBRMEXT1.def", "x" : 0,   "y" : 181, "z" : 1,  "campaignBonus" : "BoRDwf1t.pcx", "border" : "TORDWFT.bmp",  "area" : "TZRDWFT.bmp" },
+				"horde2":         { "animation" : "TBRMHRD3.def", "x" : 47,  "y" : 142, "z" : -1, "campaignBonus" : "BoRTre1h.pcx", "border" : "TORTRE1H.bmp", "area" : "TZRTRE1H.bmp", "hidden" : true },
+				"horde2Upgr":     { "animation" : "TBRMHRD4.def", "x" : 47,  "y" : 142, "z" : -1, "campaignBonus" : "BoRTre2h.pcx", "border" : "TORTRE2H.bmp", "area" : "TZRTRE2H.bmp", "hidden" : true, "builds" : "horde2" },
+				"grail":          { "animation" : "TBRMHOLY.def", "x" : 0,   "y" : 54,  "z" : -3, "campaignBonus" : "BoRHoly.pcx", "border" : "TORHOLY.bmp",  "area" : "TZRHOLY.bmp" },
 				"extraTownHall":  { "animation" : "TBRMEXT3.def", "x" : 293, "y" : 235, "z" : 2 },
 				"extraCityHall":  { "animation" : "TBRMEXT4.def", "x" : 295, "y" : 191, "z" : 0 },
 				"extraCapitol":   { "animation" : "TBRMEXT5.def", "x" : 260, "y" : 171, "z" : 3 },
-				"dwellingLvl1":   { "animation" : "TBRMDW_0.def", "x" : 0,   "y" : 236, "z" : 2,  "border" : "TORCEN1A.bmp", "area" : "TZRCEN1A.bmp" },
-				"dwellingLvl2":   { "animation" : "TBRMDW_1.def", "x" : 0,   "y" : 154, "z" : -2, "border" : "TORDWF1.bmp",  "area" : "TZRDWF1.bmp" },
-				"dwellingLvl3":   { "animation" : "TBRMDW_2.def", "x" : 668, "y" : 101, "z" : -1, "border" : "TORELF1.bmp",  "area" : "TZRELF1.bmp" },
-				"dwellingLvl4":   { "animation" : "TBRMDW_3.def", "x" : 287, "y" : 73,  "z" : -1, "border" : "TORPEG1A.bmp", "area" : "TZRPEG1A.bmp" },
-				"dwellingLvl5":   { "animation" : "TBRMDW_4.def", "x" : 68,  "y" : 146, "z" : -1, "border" : "TORTRE1.bmp",  "area" : "TZRTRE1.bmp" },
-				"dwellingLvl6":   { "animation" : "TBRMDW_5.def", "x" : 362, "y" : 90,  "z" : -2, "border" : "TORUNI1.bmp",  "area" : "TZRUNI1.bmp" },
-				"dwellingLvl7":   { "animation" : "TBRMDW_6.def", "x" : 502, "y" : 27,  "z" : -5, "border" : "TORDR1AA.bmp", "area" : "TZRDR1AA.bmp" },
-				"dwellingUpLvl1": { "animation" : "TBRMUP_0.def", "x" : 0,   "y" : 236, "z" : 2,  "border" : "TORCEN2A.bmp", "area" : "TZRCEN2A.bmp" },
-				"dwellingUpLvl2": { "animation" : "TBRMUP_1.def", "x" : 0,   "y" : 143, "z" : -2, "border" : "TORDWF2.bmp",  "area" : "TZRDWF2.bmp" },
-				"dwellingUpLvl3": { "animation" : "TBRMUP_2.def", "x" : 665, "y" : 101, "z" : -1, "border" : "TORELF2.bmp",  "area" : "TZRELF2.bmp" },
-				"dwellingUpLvl4": { "animation" : "TBRMUP_3.def", "x" : 287, "y" : 28,  "z" : -1, "border" : "TORPEG2A.bmp", "area" : "TZRPEG2A.bmp" },
-				"dwellingUpLvl5": { "animation" : "TBRMUP_4.def", "x" : 63,  "y" : 146, "z" : -1, "border" : "TORTRE2.bmp",  "area" : "TZRTRE2.bmp" },
-				"dwellingUpLvl6": { "animation" : "TBRMUP_5.def", "x" : 362, "y" : 90,  "z" : -2, "border" : "TORUNI2.bmp",  "area" : "TZRUNI2.bmp" },
-				"dwellingUpLvl7": { "animation" : "TBRMUP_6.def", "x" : 502, "y" : 5,   "z" : -5, "border" : "TORDR2AA.bmp", "area" : "TZRDR2AA.bmp" }
+				"dwellingLvl1":   { "animation" : "TBRMDW_0.def", "x" : 0,   "y" : 236, "z" : 2,  "campaignBonus" : "BoRCen1.pcx", "border" : "TORCEN1A.bmp", "area" : "TZRCEN1A.bmp" },
+				"dwellingLvl2":   { "animation" : "TBRMDW_1.def", "x" : 0,   "y" : 154, "z" : -2, "campaignBonus" : "BoRDwf1.pcx", "border" : "TORDWF1.bmp",  "area" : "TZRDWF1.bmp" },
+				"dwellingLvl3":   { "animation" : "TBRMDW_2.def", "x" : 668, "y" : 101, "z" : -1, "campaignBonus" : "BoRElf1.pcx", "border" : "TORELF1.bmp",  "area" : "TZRELF1.bmp" },
+				"dwellingLvl4":   { "animation" : "TBRMDW_3.def", "x" : 287, "y" : 73,  "z" : -1, "campaignBonus" : "BoRPeg1.pcx", "border" : "TORPEG1A.bmp", "area" : "TZRPEG1A.bmp" },
+				"dwellingLvl5":   { "animation" : "TBRMDW_4.def", "x" : 68,  "y" : 146, "z" : -1, "campaignBonus" : "BoRTre1.pcx", "border" : "TORTRE1.bmp",  "area" : "TZRTRE1.bmp" },
+				"dwellingLvl6":   { "animation" : "TBRMDW_5.def", "x" : 362, "y" : 90,  "z" : -2, "campaignBonus" : "BoRUni1.pcx", "border" : "TORUNI1.bmp",  "area" : "TZRUNI1.bmp" },
+				"dwellingLvl7":   { "animation" : "TBRMDW_6.def", "x" : 502, "y" : 27,  "z" : -5, "campaignBonus" : "BoRDra1.pcx", "border" : "TORDR1AA.bmp", "area" : "TZRDR1AA.bmp" },
+				"dwellingUpLvl1": { "animation" : "TBRMUP_0.def", "x" : 0,   "y" : 236, "z" : 2,  "campaignBonus" : "BoRCen2.pcx", "border" : "TORCEN2A.bmp", "area" : "TZRCEN2A.bmp" },
+				"dwellingUpLvl2": { "animation" : "TBRMUP_1.def", "x" : 0,   "y" : 143, "z" : -2, "campaignBonus" : "BoRDwf2.pcx", "border" : "TORDWF2.bmp",  "area" : "TZRDWF2.bmp" },
+				"dwellingUpLvl3": { "animation" : "TBRMUP_2.def", "x" : 665, "y" : 101, "z" : -1, "campaignBonus" : "BoRElf2.pcx", "border" : "TORELF2.bmp",  "area" : "TZRELF2.bmp" },
+				"dwellingUpLvl4": { "animation" : "TBRMUP_3.def", "x" : 287, "y" : 28,  "z" : -1, "campaignBonus" : "BoRPeg2.pcx", "border" : "TORPEG2A.bmp", "area" : "TZRPEG2A.bmp" },
+				"dwellingUpLvl5": { "animation" : "TBRMUP_4.def", "x" : 63,  "y" : 146, "z" : -1, "campaignBonus" : "BoRTre2.pcx", "border" : "TORTRE2.bmp",  "area" : "TZRTRE2.bmp" },
+				"dwellingUpLvl6": { "animation" : "TBRMUP_5.def", "x" : 362, "y" : 90,  "z" : -2, "campaignBonus" : "BoRUni2.pcx", "border" : "TORUNI2.bmp",  "area" : "TZRUNI2.bmp" },
+				"dwellingUpLvl7": { "animation" : "TBRMUP_6.def", "x" : 502, "y" : 5,   "z" : -5, "campaignBonus" : "BoRDra2.pcx", "border" : "TORDR2AA.bmp", "area" : "TZRDR2AA.bmp" }
 			},
 
 			"musicTheme" : [ "music/Rampart" ],

+ 35 - 35
config/factions/stronghold.json

@@ -80,41 +80,41 @@
 			"structures" :
 			{
 				"extraAnimation": { "animation" : "TBSTEXT3.def", "x" : 23,  "y" : 20 },
-				"mageGuild1":     { "animation" : "TBSTMAGE.def", "x" : 473, "y" : 67,  "z" : -2, "border" : "TOSMAG1.bmp",  "area" : "TZSMAG1.bmp" },
-				"mageGuild2":     { "animation" : "TBSTMAG2.def", "x" : 473, "y" : 37,  "z" : -2, "border" : "TOSMAG2.bmp",  "area" : "TZSMAG2.bmp" },
-				"mageGuild3":     { "animation" : "TBSTMAG3.def", "x" : 473, "y" : 1,   "z" : -2, "border" : "TOSMAG3.bmp",  "area" : "TZSMAG3.bmp" },
-				"tavern":         { "animation" : "TBSTTVRN.def", "x" : 170, "y" : 280, "z" : 2,  "border" : "TOSTAV.bmp",   "area" : "TZSTAV.bmp" },
-				"fort":           { "animation" : "TBSTCSTL.def", "x" : 402, "y" : 148, "z" : -1, "border" : "TOSCA1.bmp",   "area" : "TZSCA1.bmp" },
-				"citadel":        { "animation" : "TBSTCAS2.def", "x" : 402, "y" : 114, "z" : -1, "border" : "TOSCA2.bmp",   "area" : "TZSCA2.bmp" },
-				"castle":         { "animation" : "TBSTCAS3.def", "x" : 402, "y" : 114, "z" : -1, "border" : "TOSCA3.bmp",   "area" : "TZSCA3.bmp" },
-				"villageHall":    { "animation" : "TBSTHALL.def", "x" : 0,   "y" : 259, "border" : "TOSHAL1A.bmp", "area" : "TZSHAL1A.bmp" },
-				"townHall":       { "animation" : "TBSTHAL2.def", "x" : 0,   "y" : 225, "border" : "TOSHAL2A.bmp", "area" : "TZSHAL2A.bmp" },
-				"cityHall":       { "animation" : "TBSTHAL3.def", "x" : 0,   "y" : 201, "border" : "TOSHAL3A.bmp", "area" : "TZSHAL3A.bmp" },
-				"capitol":        { "animation" : "TBSTHAL4.def", "x" : 0,   "y" : 148, "border" : "TOSHAL4A.bmp", "area" : "TZSHAL4A.bmp" },
-				"marketplace":    { "animation" : "TBSTMARK.def", "x" : 397, "y" : 308, "z" : 1,  "border" : "TOSMRK1.bmp",  "area" : "TZSMRK1.bmp" },
-				"resourceSilo":   { "animation" : "TBSTSILO.def", "x" : 458, "y" : 248, "z" : 2,  "border" : "TOSMRK2.bmp",  "area" : "TZSMRK2.bmp" },
-				"blacksmith":     { "animation" : "TBSTBLAK.def", "x" : 660, "y" : 286, "border" : "TOSBLK1.bmp",  "area" : "TZSBLK1.bmp" },
-				"special1":       { "animation" : "TBSTSPEC.def", "x" : 550, "y" : 229, "border" : "TOSCA1EA.bmp", "area" : "TZSCA1EA.bmp" },
-				"horde1":         { "animation" : "TBSTHRD1.def", "x" : 373, "y" : 239, "border" : "TOSGOB1H.bmp", "area" : "TZSGOB1H.bmp", "hidden" : true },
-				"horde1Upgr":     { "animation" : "TBSTHRD2.def", "x" : 373, "y" : 220, "border" : "TOSGOB2H.bmp", "area" : "TZSGOB2H.bmp", "hidden" : true, "builds" : "horde1" },
-				"special2":       { "animation" : "TBSTEXT0.def", "x" : 473, "y" : 282, "z" : 3,  "border" : "TOSMRK1C.bmp", "area" : "TZSMRK1C.bmp" },
-				"special3":       { "animation" : "TBSTEXT1.def", "x" : 617, "y" : 286, "z" : 1,  "border" : "TOSBLK2.bmp",  "area" : "TZSBLK2.bmp" },
-				"special4":       { "animation" : "TBSTEXT2.def", "x" : 313, "y" : 13,  "z" : -1, "border" : "TOSVAH.bmp",   "area" : "TZSVAH.bmp" },
-				"grail":          { "animation" : "TBSTHOLY.def", "x" : 321, "y" : 105, "z" : 3,  "border" : "TOSHOLYA.bmp", "area" : "TZSHOLYA.bmp" },
-				"dwellingLvl1":   { "animation" : "TBSTDW_0.def", "x" : 373, "y" : 239, "border" : "TOSGOB1.bmp",  "area" : "TZSGOB1.bmp" },
-				"dwellingLvl2":   { "animation" : "TBSTDW_1.def", "x" : 266, "y" : 246, "z" : 1,  "border" : "TOSWOL1.bmp",  "area" : "TZSWOL1.bmp" },
-				"dwellingLvl3":   { "animation" : "TBSTDW_2.def", "x" : 566, "y" : 232, "z" : 2,  "border" : "TOSORC1.bmp",  "area" : "TZSORC1.bmp" },
-				"dwellingLvl4":   { "animation" : "TBSTDW_3.def", "x" : 197, "y" : 204, "border" : "TOSOGR1.bmp",  "area" : "TZSOGR1.bmp" },
-				"dwellingLvl5":   { "animation" : "TBSTDW_4.def", "x" : 137, "y" : 30,  "z" : -1, "border" : "TOSROC1.bmp",  "area" : "TZSROC1.bmp" },
-				"dwellingLvl6":   { "animation" : "TBSTDW_5.def", "x" : 622, "y" : 160, "z" : -2, "border" : "TOSCYC1.bmp",  "area" : "TZSCYC1.bmp" },
-				"dwellingLvl7":   { "animation" : "TBSTDW_6.def", "x" : 604, "y" : 0,   "border" : "TOSBEH1A.bmp", "area" : "TZSBEH1A.bmp" },
-				"dwellingUpLvl1": { "animation" : "TBSTUP_0.def", "x" : 373, "y" : 220, "border" : "TOSGOB2.bmp",  "area" : "TZSGOB2.bmp" },
-				"dwellingUpLvl2": { "animation" : "TBSTUP_1.def", "x" : 266, "y" : 225, "z" : 1,  "border" : "TOSWOL2.bmp",  "area" : "TZSWOL2.bmp" },
-				"dwellingUpLvl3": { "animation" : "TBSTUP_2.def", "x" : 566, "y" : 158, "z" : 2,  "border" : "TOSORC2.bmp",  "area" : "TZSORC2.bmp" },
-				"dwellingUpLvl4": { "animation" : "TBSTUP_3.def", "x" : 197, "y" : 137, "border" : "TOSOGR2.bmp",  "area" : "TZSOGR2.bmp" },
-				"dwellingUpLvl5": { "animation" : "TBSTUP_4.def", "x" : 129, "y" : 15,  "z" : -1, "border" : "TOSROC2.bmp",  "area" : "TZSROC2.bmp" },
-				"dwellingUpLvl6": { "animation" : "TBSTUP_5.def", "x" : 616, "y" : 93,  "z" : -2, "border" : "TOSCYC2A.bmp", "area" : "TZSCYC2A.bmp" },
-				"dwellingUpLvl7": { "animation" : "TBSTUP_6.def", "x" : 604, "y" : 0,   "border" : "TOSBEH2A.bmp", "area" : "TZSBEH2A.bmp" }
+				"mageGuild1":     { "animation" : "TBSTMAGE.def", "x" : 473, "y" : 67,  "z" : -2, "campaignBonus" : "BoSmage1.pcx", "border" : "TOSMAG1.bmp",  "area" : "TZSMAG1.bmp" },
+				"mageGuild2":     { "animation" : "TBSTMAG2.def", "x" : 473, "y" : 37,  "z" : -2, "campaignBonus" : "BoSmage2.pcx", "border" : "TOSMAG2.bmp",  "area" : "TZSMAG2.bmp" },
+				"mageGuild3":     { "animation" : "TBSTMAG3.def", "x" : 473, "y" : 1,   "z" : -2, "campaignBonus" : "BoSmage3.pcx", "border" : "TOSMAG3.bmp",  "area" : "TZSMAG3.bmp" },
+				"tavern":         { "animation" : "TBSTTVRN.def", "x" : 170, "y" : 280, "z" : 2,  "campaignBonus" : "BoStav1.pcx",  "border" : "TOSTAV.bmp",   "area" : "TZSTAV.bmp" },
+				"fort":           { "animation" : "TBSTCSTL.def", "x" : 402, "y" : 148, "z" : -1, "campaignBonus" : "BoScas1.pcx",  "border" : "TOSCA1.bmp",   "area" : "TZSCA1.bmp" },
+				"citadel":        { "animation" : "TBSTCAS2.def", "x" : 402, "y" : 114, "z" : -1, "campaignBonus" : "BoScas2.pcx",  "border" : "TOSCA2.bmp",   "area" : "TZSCA2.bmp" },
+				"castle":         { "animation" : "TBSTCAS3.def", "x" : 402, "y" : 114, "z" : -1, "campaignBonus" : "BoScas3.pcx",  "border" : "TOSCA3.bmp",   "area" : "TZSCA3.bmp" },
+				"villageHall":    { "animation" : "TBSTHALL.def", "x" : 0,   "y" : 259,           "campaignBonus" : "BoShall1.pcx", "border" : "TOSHAL1A.bmp", "area" : "TZSHAL1A.bmp" },
+				"townHall":       { "animation" : "TBSTHAL2.def", "x" : 0,   "y" : 225,           "campaignBonus" : "BoShall2.pcx", "border" : "TOSHAL2A.bmp", "area" : "TZSHAL2A.bmp" },
+				"cityHall":       { "animation" : "TBSTHAL3.def", "x" : 0,   "y" : 201,           "campaignBonus" : "BoShall3.pcx", "border" : "TOSHAL3A.bmp", "area" : "TZSHAL3A.bmp" },
+				"capitol":        { "animation" : "TBSTHAL4.def", "x" : 0,   "y" : 148,           "campaignBonus" : "BoShall4.pcx", "border" : "TOSHAL4A.bmp", "area" : "TZSHAL4A.bmp" },
+				"marketplace":    { "animation" : "TBSTMARK.def", "x" : 397, "y" : 308, "z" : 1,  "campaignBonus" : "BoSmrk1.pcx",  "border" : "TOSMRK1.bmp",  "area" : "TZSMRK1.bmp" },
+				"resourceSilo":   { "animation" : "TBSTSILO.def", "x" : 458, "y" : 248, "z" : 2,  "campaignBonus" : "BoSmrk2.pcx",  "border" : "TOSMRK2.bmp",  "area" : "TZSMRK2.bmp" },
+				"blacksmith":     { "animation" : "TBSTBLAK.def", "x" : 660, "y" : 286,           "campaignBonus" : "BoSblak1.pcx", "border" : "TOSBLK1.bmp",  "area" : "TZSBLK1.bmp" },
+				"special1":       { "animation" : "TBSTSPEC.def", "x" : 550, "y" : 229,           "campaignBonus" : "BoSescap.pcx", "border" : "TOSCA1EA.bmp", "area" : "TZSCA1EA.bmp" },
+				"horde1":         { "animation" : "TBSTHRD1.def", "x" : 373, "y" : 239,           "campaignBonus" : "BoSgob1h.pcx", "border" : "TOSGOB1H.bmp", "area" : "TZSGOB1H.bmp", "hidden" : true },
+				"horde1Upgr":     { "animation" : "TBSTHRD2.def", "x" : 373, "y" : 220,           "campaignBonus" : "BoSgob2h.pcx", "border" : "TOSGOB2H.bmp", "area" : "TZSGOB2H.bmp", "hidden" : true, "builds" : "horde1" },
+				"special2":       { "animation" : "TBSTEXT0.def", "x" : 473, "y" : 282, "z" : 3,  "campaignBonus" : "BoSmrk1c.pcx", "border" : "TOSMRK1C.bmp", "area" : "TZSMRK1C.bmp" },
+				"special3":       { "animation" : "TBSTEXT1.def", "x" : 617, "y" : 286, "z" : 1,  "campaignBonus" : "BoSblak2.pcx", "border" : "TOSBLK2.bmp",  "area" : "TZSBLK2.bmp" },
+				"special4":       { "animation" : "TBSTEXT2.def", "x" : 313, "y" : 13,  "z" : -1, "campaignBonus" : "BoSvahal.pcx", "border" : "TOSVAH.bmp",   "area" : "TZSVAH.bmp" },
+				"grail":          { "animation" : "TBSTHOLY.def", "x" : 321, "y" : 105, "z" : 3,  "campaignBonus" : "BoSholy.pcx",  "border" : "TOSHOLYA.bmp", "area" : "TZSHOLYA.bmp" },
+				"dwellingLvl1":   { "animation" : "TBSTDW_0.def", "x" : 373, "y" : 239,           "campaignBonus" : "BoSgob1.pcx",  "border" : "TOSGOB1.bmp",  "area" : "TZSGOB1.bmp" },
+				"dwellingLvl2":   { "animation" : "TBSTDW_1.def", "x" : 266, "y" : 246, "z" : 1,  "campaignBonus" : "BoSwolf1.pcx", "border" : "TOSWOL1.bmp",  "area" : "TZSWOL1.bmp" },
+				"dwellingLvl3":   { "animation" : "TBSTDW_2.def", "x" : 566, "y" : 232, "z" : 2,  "campaignBonus" : "BoSorc1.pcx",  "border" : "TOSORC1.bmp",  "area" : "TZSORC1.bmp" },
+				"dwellingLvl4":   { "animation" : "TBSTDW_3.def", "x" : 197, "y" : 204,           "campaignBonus" : "BoSogre1.pcx", "border" : "TOSOGR1.bmp",  "area" : "TZSOGR1.bmp" },
+				"dwellingLvl5":   { "animation" : "TBSTDW_4.def", "x" : 137, "y" : 30,  "z" : -1, "campaignBonus" : "BoSroc1.pcx",  "border" : "TOSROC1.bmp",  "area" : "TZSROC1.bmp" },
+				"dwellingLvl6":   { "animation" : "TBSTDW_5.def", "x" : 622, "y" : 160, "z" : -2, "campaignBonus" : "BoScyc1.pcx",  "border" : "TOSCYC1.bmp",  "area" : "TZSCYC1.bmp" },
+				"dwellingLvl7":   { "animation" : "TBSTDW_6.def", "x" : 604, "y" : 0,             "campaignBonus" : "BoSbeh1.pcx",  "border" : "TOSBEH1A.bmp", "area" : "TZSBEH1A.bmp" },
+				"dwellingUpLvl1": { "animation" : "TBSTUP_0.def", "x" : 373, "y" : 220,           "campaignBonus" : "BoSgob2.pcx",  "border" : "TOSGOB2.bmp",  "area" : "TZSGOB2.bmp" },
+				"dwellingUpLvl2": { "animation" : "TBSTUP_1.def", "x" : 266, "y" : 225, "z" : 1,  "campaignBonus" : "BoSwolf2.pcx", "border" : "TOSWOL2.bmp",  "area" : "TZSWOL2.bmp" },
+				"dwellingUpLvl3": { "animation" : "TBSTUP_2.def", "x" : 566, "y" : 158, "z" : 2,  "campaignBonus" : "BoSorc2.pcx",  "border" : "TOSORC2.bmp",  "area" : "TZSORC2.bmp" },
+				"dwellingUpLvl4": { "animation" : "TBSTUP_3.def", "x" : 197, "y" : 137,           "campaignBonus" : "BoSogre2.pcx", "border" : "TOSOGR2.bmp",  "area" : "TZSOGR2.bmp" },
+				"dwellingUpLvl5": { "animation" : "TBSTUP_4.def", "x" : 129, "y" : 15,  "z" : -1, "campaignBonus" : "BoSroc2.pcx",  "border" : "TOSROC2.bmp",  "area" : "TZSROC2.bmp" },
+				"dwellingUpLvl6": { "animation" : "TBSTUP_5.def", "x" : 616, "y" : 93,  "z" : -2, "campaignBonus" : "BoScyc2.pcx",  "border" : "TOSCYC2A.bmp", "area" : "TZSCYC2A.bmp" },
+				"dwellingUpLvl7": { "animation" : "TBSTUP_6.def", "x" : 604, "y" : 0,             "campaignBonus" : "BoSbeh2.pcx",  "border" : "TOSBEH2A.bmp", "area" : "TZSBEH2A.bmp" }
 			},
 
 			"musicTheme" : [ "music/Stronghold" ],

+ 50 - 39
config/factions/tower.json

@@ -79,43 +79,43 @@
 			},
 			"structures" :
 			{
-				"mageGuild1":     { "animation" : "TBTWMAGE.def", "x" : 597, "y" : 82,  "z" : -1, "border" : "TOTGLD1.bmp",  "area" : "TZTGLD1.bmp" },
-				"mageGuild2":     { "animation" : "TBTWMAG2.def", "x" : 593, "y" : 65,  "z" : -1, "border" : "TOTGLD2.bmp",  "area" : "TZTGLD2.bmp" },
-				"mageGuild3":     { "animation" : "TBTWMAG3.def", "x" : 593, "y" : 48,  "z" : -1, "border" : "TOTGLD3.bmp",  "area" : "TZTGLD3.bmp" },
-				"mageGuild4":     { "animation" : "TBTWMAG4.def", "x" : 593, "y" : 31,  "z" : -1, "border" : "TOTGLD4.bmp",  "area" : "TZTGLD4.bmp" },
-				"mageGuild5":     { "animation" : "TBTWMAG5.def", "x" : 593, "y" : 14,  "z" : -1, "border" : "TOTGLD5.bmp",  "area" : "TZTGLD5.bmp" },
-				"tavern":         { "animation" : "TBTWTVRN.def", "x" : 375, "y" : 278, "z" : 1, "border" : "TOTTAV.bmp",   "area" : "TZTTAV.bmp" },
-				"fort":           { "animation" : "TBTWCSTL.def", "x" : 304, "y" : 0,   "z" : -1, "border" : "TOTCAS1.bmp",  "area" : "TZTCAS1.bmp" },
-				"citadel":        { "animation" : "TBTWCAS2.def", "x" : 301, "y" : 0,   "z" : -1, "border" : "TOTCAS2.bmp",  "area" : "TZTCAS2.bmp" },
-				"castle":         { "animation" : "TBTWCAS3.def", "x" : 301, "y" : 0,   "z" : -1, "border" : "TOTCAS3.bmp",  "area" : "TZTCAS3.bmp" },
-				"villageHall":    { "animation" : "TBTWHALL.def", "x" : 0,   "y" : 259, "z" : 2,  "border" : "TOTHAL1.bmp",  "area" : "TZTHAL1.bmp" },
-				"townHall":       { "animation" : "TBTWHAL2.def", "x" : 0,   "y" : 220, "z" : 2,  "border" : "TOTHAL2.bmp",  "area" : "TZTHAL2.bmp" },
-				"cityHall":       { "animation" : "TBTWHAL3.def", "x" : 0,   "y" : 82,  "z" : 2,  "border" : "TOTHAL3.bmp",  "area" : "TZTHAL3.bmp" },
-				"capitol":        { "animation" : "TBTWHAL4.def", "x" : 0,   "y" : 82,  "z" : 2,  "border" : "TOTHAL4.bmp",  "area" : "TZTHAL4.bmp" },
-				"marketplace":    { "animation" : "TBTWMARK.def", "x" : 614, "y" : 292, "border" : "TOTMRK.bmp",   "area" : "TZTMRK.bmp" },
-				"resourceSilo":   { "animation" : "TBTWSILO.def", "x" : 763, "y" : 214, "z" : 3, "border" : "TOTMRKS.bmp",  "area" : "TZTMRKS.bmp" },
-				"blacksmith":     { "animation" : "TBTWBLAK.def", "x" : 478, "y" : 211, "border" : "TOTBLKA.bmp",  "area" : "TZTBLKA.bmp" },
-				"special1":       { "animation" : "TBTWSPEC.def", "x" : 674, "y" : 276, "z" : 2, "border" : "TOTMRKA.bmp",  "area" : "TZTMRKA.bmp" },
-				"horde1":         { "animation" : "TBTWHRD1.def", "x" : 0,   "y" : 47,  "border" : "TOTGAR1H.bmp", "area" : "TZTGAR1H.bmp", "hidden" : true },
-				"horde1Upgr":     { "animation" : "TBTWHRD2.def", "x" : 0,   "y" : 28,  "border" : "TOTGAR2H.bmp", "area" : "TZTGAR2H.bmp", "hidden" : true, "builds" : "horde1" },
-				"special2":       { "animation" : "TBTWEXT0.def", "x" : 409, "y" : 82,  "border" : "TOTCASW.bmp",  "area" : "TZTCASW.bmp" },
-				"special3":       { "animation" : "TBTWEXT1.def", "x" : 702, "y" : 115, "border" : "TOTGLDL.bmp",  "area" : "TZTGLDL.bmp" },
-				"special4":       { "animation" : "TBTWEXT2.def", "x" : 592, "y" : 189, "z" : 1,  "border" : "TOTGLDW.bmp",  "area" : "TZTGLDW.bmp" },
-				"grail":          { "animation" : "TBTWHOLY.def", "x" : 237, "y" : 14,  "z" : -2, "border" : "TOTHOLYA.bmp", "area" : "TZTHOLYA.bmp" },
-				"dwellingLvl1":   { "animation" : "TBTWDW_0.def", "x" : 453, "y" : 221, "z" : 1,  "border" : "TOTGRM1A.bmp", "area" : "TZTGRM1A.bmp" },
-				"dwellingLvl2":   { "animation" : "TBTWDW_1.def", "x" : 4,   "y" : 46,  "border" : "TOTGAR1.bmp",  "area" : "TZTGAR1.bmp" },
-				"dwellingLvl3":   { "animation" : "TBTWDW_2.def", "x" : 209, "y" : 177, "z" : 1,  "border" : "TOTGOL1A.bmp", "area" : "TZTGOL1A.bmp" },
-				"dwellingLvl4":   { "animation" : "TBTWDW_3.def", "x" : 613, "y" : 95,  "border" : "TOTMAG1.bmp",  "area" : "TZTMAG1.bmp" },
-				"dwellingLvl5":   { "animation" : "TBTWDW_4.def", "x" : 511, "y" : 75,  "border" : "TOTGEN1.bmp",  "area" : "TZTGEN1.bmp" },
-				"dwellingLvl6":   { "animation" : "TBTWDW_5.def", "x" : 681, "y" : 208, "z" : 2,  "border" : "TOTNAG1.bmp",  "area" : "TZTNAG1.bmp" },
-				"dwellingLvl7":   { "animation" : "TBTWDW_6.def", "x" : 75,  "y" : 144, "z" : -1, "border" : "TOTTIT1.bmp",  "area" : "TZTTIT1.bmp" },
-				"dwellingUpLvl1": { "animation" : "TBTWUP_0.def", "x" : 446, "y" : 221, "z" : 1,  "border" : "TOTGRM2A.bmp", "area" : "TZTGRM2A.bmp" },
-				"dwellingUpLvl2": { "animation" : "TBTWUP_1.def", "x" : 4,   "y" : 28,  "border" : "TOTGAR2.bmp",  "area" : "TZTGAR2.bmp" },
-				"dwellingUpLvl3": { "animation" : "TBTWUP_2.def", "x" : 209, "y" : 177, "z" : 1,  "border" : "TOTGOL2A.bmp", "area" : "TZTGOL2A.bmp" },
-				"dwellingUpLvl4": { "animation" : "TBTWUP_3.def", "x" : 613, "y" : 74,  "border" : "TOTMAG2.bmp",  "area" : "TZTMAG2.bmp" },
-				"dwellingUpLvl5": { "animation" : "TBTWUP_4.def", "x" : 511, "y" : 8,   "border" : "TOTGEN2.bmp",  "area" : "TZTGEN2.bmp" },
-				"dwellingUpLvl6": { "animation" : "TBTWUP_5.def", "x" : 681, "y" : 157, "z" : 2,  "border" : "TOTNAG2.bmp",  "area" : "TZTNAG2.bmp" },
-				"dwellingUpLvl7": { "animation" : "TBTWUP_6.def", "x" : 75,  "y" : 91,  "z" : -1, "border" : "TOTTIT2.bmp",  "area" : "TZTTIT2.bmp" }
+				"mageGuild1":     { "animation" : "TBTWMAGE.def", "x" : 597, "y" : 82,  "z" : -1, "campaignBonus" : "BoTGld1.pcx",  "border" : "TOTGLD1.bmp",  "area" : "TZTGLD1.bmp" },
+				"mageGuild2":     { "animation" : "TBTWMAG2.def", "x" : 593, "y" : 65,  "z" : -1, "campaignBonus" : "BoTGld2.pcx",  "border" : "TOTGLD2.bmp",  "area" : "TZTGLD2.bmp" },
+				"mageGuild3":     { "animation" : "TBTWMAG3.def", "x" : 593, "y" : 48,  "z" : -1, "campaignBonus" : "BoTGld3.pcx",  "border" : "TOTGLD3.bmp",  "area" : "TZTGLD3.bmp" },
+				"mageGuild4":     { "animation" : "TBTWMAG4.def", "x" : 593, "y" : 31,  "z" : -1, "campaignBonus" : "BoTGld4.pcx",  "border" : "TOTGLD4.bmp",  "area" : "TZTGLD4.bmp" },
+				"mageGuild5":     { "animation" : "TBTWMAG5.def", "x" : 593, "y" : 14,  "z" : -1, "campaignBonus" : "BoTGld5.pcx",  "border" : "TOTGLD5.bmp",  "area" : "TZTGLD5.bmp" },
+				"tavern":         { "animation" : "TBTWTVRN.def", "x" : 375, "y" : 278, "z" : 1,  "campaignBonus" : "BoTTav.pcx",   "border" : "TOTTAV.bmp",   "area" : "TZTTAV.bmp" },
+				"fort":           { "animation" : "TBTWCSTL.def", "x" : 304, "y" : 0,   "z" : -1, "campaignBonus" : "BoTCast1.pcx", "border" : "TOTCAS1.bmp",  "area" : "TZTCAS1.bmp" },
+				"citadel":        { "animation" : "TBTWCAS2.def", "x" : 301, "y" : 0,   "z" : -1, "campaignBonus" : "BoTCast2.pcx", "border" : "TOTCAS2.bmp",  "area" : "TZTCAS2.bmp" },
+				"castle":         { "animation" : "TBTWCAS3.def", "x" : 301, "y" : 0,   "z" : -1, "campaignBonus" : "BoTCast3.pcx", "border" : "TOTCAS3.bmp",  "area" : "TZTCAS3.bmp" },
+				"villageHall":    { "animation" : "TBTWHALL.def", "x" : 0,   "y" : 259, "z" : 2,  "campaignBonus" : "BoTHall1.pcx", "border" : "TOTHAL1.bmp",  "area" : "TZTHAL1.bmp" },
+				"townHall":       { "animation" : "TBTWHAL2.def", "x" : 0,   "y" : 220, "z" : 2,  "campaignBonus" : "BoTHall2.pcx", "border" : "TOTHAL2.bmp",  "area" : "TZTHAL2.bmp" },
+				"cityHall":       { "animation" : "TBTWHAL3.def", "x" : 0,   "y" : 82,  "z" : 2,  "campaignBonus" : "BoTHall3.pcx", "border" : "TOTHAL3.bmp",  "area" : "TZTHAL3.bmp" },
+				"capitol":        { "animation" : "TBTWHAL4.def", "x" : 0,   "y" : 82,  "z" : 2,  "campaignBonus" : "BoTHall4.pcx", "border" : "TOTHAL4.bmp",  "area" : "TZTHAL4.bmp" },
+				"marketplace":    { "animation" : "TBTWMARK.def", "x" : 614, "y" : 292,           "campaignBonus" : "BoTMark.pcx",  "border" : "TOTMRK.bmp",   "area" : "TZTMRK.bmp" },
+				"resourceSilo":   { "animation" : "TBTWSILO.def", "x" : 763, "y" : 214, "z" : 3,  "campaignBonus" : "BoTMarkS.pcx", "border" : "TOTMRKS.bmp",  "area" : "TZTMRKS.bmp" },
+				"blacksmith":     { "animation" : "TBTWBLAK.def", "x" : 478, "y" : 211,           "campaignBonus" : "BoTBlack.pcx", "border" : "TOTBLKA.bmp",  "area" : "TZTBLKA.bmp" },
+				"special1":       { "animation" : "TBTWSPEC.def", "x" : 674, "y" : 276, "z" : 2,  "campaignBonus" : "BoTMarkA.pcx", "border" : "TOTMRKA.bmp",  "area" : "TZTMRKA.bmp" },
+				"horde1":         { "animation" : "TBTWHRD1.def", "x" : 0,   "y" : 47,            "campaignBonus" : "BoTGa1H.pcx",  "border" : "TOTGAR1H.bmp", "area" : "TZTGAR1H.bmp", "hidden" : true },
+				"horde1Upgr":     { "animation" : "TBTWHRD2.def", "x" : 0,   "y" : 28,            "campaignBonus" : "BoTGa2h.pcx",  "border" : "TOTGAR2H.bmp", "area" : "TZTGAR2H.bmp", "hidden" : true, "builds" : "horde1" },
+				"special2":       { "animation" : "TBTWEXT0.def", "x" : 409, "y" : 82,            "campaignBonus" : "BoTCastW.pcx", "border" : "TOTCASW.bmp",  "area" : "TZTCASW.bmp" },
+				"special3":       { "animation" : "TBTWEXT1.def", "x" : 702, "y" : 115,           "campaignBonus" : "BoTGldL.pcx",  "border" : "TOTGLDL.bmp",  "area" : "TZTGLDL.bmp" },
+				"special4":       { "animation" : "TBTWEXT2.def", "x" : 592, "y" : 189, "z" : 1,  "campaignBonus" : "BoTGldW.pcx",  "border" : "TOTGLDW.bmp",  "area" : "TZTGLDW.bmp" },
+				"grail":          { "animation" : "TBTWHOLY.def", "x" : 237, "y" : 14,  "z" : -2, "campaignBonus" : "BoTGrail.pcx", "border" : "TOTHOLYA.bmp", "area" : "TZTHOLYA.bmp" },
+				"dwellingLvl1":   { "animation" : "TBTWDW_0.def", "x" : 453, "y" : 221, "z" : 1,  "campaignBonus" : "BoTGrem1.pcx", "border" : "TOTGRM1A.bmp", "area" : "TZTGRM1A.bmp" },
+				"dwellingLvl2":   { "animation" : "TBTWDW_1.def", "x" : 4,   "y" : 46,            "campaignBonus" : "BoTGar1.pcx",  "border" : "TOTGAR1.bmp",  "area" : "TZTGAR1.bmp" },
+				"dwellingLvl3":   { "animation" : "TBTWDW_2.def", "x" : 209, "y" : 177, "z" : 1,  "campaignBonus" : "BoTGolm1.pcx", "border" : "TOTGOL1A.bmp", "area" : "TZTGOL1A.bmp" },
+				"dwellingLvl4":   { "animation" : "TBTWDW_3.def", "x" : 613, "y" : 95,            "campaignBonus" : "BoTMag1.pcx",  "border" : "TOTMAG1.bmp",  "area" : "TZTMAG1.bmp" },
+				"dwellingLvl5":   { "animation" : "TBTWDW_4.def", "x" : 511, "y" : 75,            "campaignBonus" : "BoTGen1.pcx",  "border" : "TOTGEN1.bmp",  "area" : "TZTGEN1.bmp" },
+				"dwellingLvl6":   { "animation" : "TBTWDW_5.def", "x" : 681, "y" : 208, "z" : 2,  "campaignBonus" : "BoTNaga1.pcx", "border" : "TOTNAG1.bmp",  "area" : "TZTNAG1.bmp" },
+				"dwellingLvl7":   { "animation" : "TBTWDW_6.def", "x" : 75,  "y" : 144, "z" : -1, "campaignBonus" : "BoTTit1.pcx",  "border" : "TOTTIT1.bmp",  "area" : "TZTTIT1.bmp" },
+				"dwellingUpLvl1": { "animation" : "TBTWUP_0.def", "x" : 446, "y" : 221, "z" : 1,  "campaignBonus" : "BoTGrem2.pcx", "border" : "TOTGRM2A.bmp", "area" : "TZTGRM2A.bmp" },
+				"dwellingUpLvl2": { "animation" : "TBTWUP_1.def", "x" : 4,   "y" : 28,            "campaignBonus" : "BoTGar2.pcx",  "border" : "TOTGAR2.bmp",  "area" : "TZTGAR2.bmp" },
+				"dwellingUpLvl3": { "animation" : "TBTWUP_2.def", "x" : 209, "y" : 177, "z" : 1,  "campaignBonus" : "BoTGolm2.pcx", "border" : "TOTGOL2A.bmp", "area" : "TZTGOL2A.bmp" },
+				"dwellingUpLvl4": { "animation" : "TBTWUP_3.def", "x" : 613, "y" : 74,            "campaignBonus" : "BoTMag2.pcx",  "border" : "TOTMAG2.bmp",  "area" : "TZTMAG2.bmp" },
+				"dwellingUpLvl5": { "animation" : "TBTWUP_4.def", "x" : 511, "y" : 8,             "campaignBonus" : "BoTGen2.pcx",  "border" : "TOTGEN2.bmp",  "area" : "TZTGEN2.bmp" },
+				"dwellingUpLvl6": { "animation" : "TBTWUP_5.def", "x" : 681, "y" : 157, "z" : 2,  "campaignBonus" : "BoTNaga2.pcx", "border" : "TOTNAG2.bmp",  "area" : "TZTNAG2.bmp" },
+				"dwellingUpLvl7": { "animation" : "TBTWUP_6.def", "x" : 75,  "y" : 91,  "z" : -1, "campaignBonus" : "BoTTit2.pcx",  "border" : "TOTTIT2.bmp",  "area" : "TZTTIT2.bmp" }
 			},
 
 			"musicTheme" : [ "music/TowerTown" ],
@@ -171,7 +171,12 @@
 				"special1":       { "requires" : [ "marketplace" ], "marketModes" : ["resource-artifact", "artifact-resource"] },
 				"horde1":         { "id" : 18, "upgrades" : "dwellingLvl2" },
 				"horde1Upgr":     { "id" : 19, "upgrades" : "dwellingUpLvl2", "requires" : [ "horde1" ], "mode" : "auto" },
-				"special2":       { "height" : "high", "requires" : [ "fort" ] },
+				"special2":       { 
+					"requires" : [ "fort" ],
+					"bonuses": [
+						{ "type": "SIGHT_RADIUS", "val": 15 }, // 5 base + 15 bonus = 20 tiles range
+					]
+				},
 				"special3":       { "type" : "library", "requires" : [ "mageGuild1" ] },
 				"special4":       {
 					"requires" : [ "mageGuild1" ],
@@ -185,7 +190,13 @@
 						]
 					}
 				},
-				"grail":          { "height" : "skyship",  "produce" : { "gold": 5000 }, "bonuses": [ { "type": "PRIMARY_SKILL", "subtype": "primarySkill.knowledge", "val": 15 } ] },
+				"grail":          { 
+					"produce" : { "gold": 5000 },
+					"bonuses": [
+						{ "type": "PRIMARY_SKILL", "subtype": "primarySkill.knowledge", "val": 15 },
+						{ "type": "FULL_MAP_SCOUTING" }
+					]
+				},
 
 				"dwellingLvl1":   { "id" : 30, "requires" : [ "fort" ] },
 				"dwellingLvl2":   { "id" : 31, "requires" : [ "dwellingLvl1" ] },

+ 211 - 13
config/gameConfig.json

@@ -99,6 +99,10 @@
 		"config/spells/vcmiAbility.json",
 		"config/spells/moats.json"
 	],
+	"spellSchools" :
+	[
+		"config/spellSchools.json"
+	],
 	"skills" :
 	[
 		"config/skills.json"
@@ -123,6 +127,11 @@
 	[
 		"config/obstacles.json"
 	],
+	"campaignRegions" :
+	[
+		"config/campaignRegions.json"
+	],
+	
 	
 	"settings":
 	{
@@ -145,7 +154,6 @@
 			"restorationOfErathia" : { 
 				"supported" : true,
 				"iconIndex" : 0,
-
 				"buildingsCommon": {
 					"townHall"       : 0,
 					"cityHall"       : 1,
@@ -190,7 +198,6 @@
 					"dwellingLvl7"   : 39,
 					"dwellingUpLvl7" : 40
 				},
-
 				"buildings" : {
 					"castle" : {
 						"special1" : 18, // lighthouse
@@ -236,10 +243,88 @@
 						"special3" : 18  // glyphsOfFear
 					}
 				},
-
 				"portraits" : {
 					"catherine" : 128, // In "RoE" Catherine only has portrait
 					"portraitGeneralKendal" : 129
+				},
+				"campaignRegions" : {
+					
+					"good1" : 1,    // Long Live the Queen
+					"good2" : 2,    // Liberation
+					"good3" : 3,    // Song for the Father
+					"evil1" : 4,    // Dungeons and devils
+					"evil2" : 5,    // Long Live the King
+					"neutral1" : 6, // Spoils of War
+					"secret1" : 7   // Seeds Of Discontent 
+				},
+				"campaignMusic" : {
+					"CampainMusic01" : 0,
+					"CampainMusic02" : 1,
+					"CampainMusic03" : 2,
+					"CampainMusic04" : 3,
+					"CampainMusic05" : 4,
+					"CampainMusic06" : 5,
+					"CampainMusic07" : 6,
+					"CampainMusic08" : 7,
+					"CampainMusic09" : 8,
+					"AiTheme0" : 9,
+					"AiTheme1" : 10,
+					"AiTheme2" : 11,
+					"Combat01" : 12,
+					"Combat02" : 13,
+					"Combat03" : 14,
+					"Combat04" : 15,
+					"CstleTown" : 16,
+					"TowerTown" : 17,
+					"Rampart" : 18,
+					"InfernoTown" : 19,
+					"NecroTown" : 20,
+					"Dungeon" : 21,
+					"Stronghold" : 22,
+					"FortressTown" : 23,
+					"ElemTown" : 24,
+					"Dirt" : 25,
+					"Sand" : 26,
+					"Grass" : 27,
+					"Snow" : 28,
+					"Swamp" : 29,
+					"Rough" : 30,
+					"Underground" : 31,
+					"Lava" : 32,
+					"Water" : 33,
+					"GoodTheme" : 34,
+					"NeutralTheme" : 35,
+					"EvilTheme" : 36,
+					"SecretTheme" : 37,
+					"LoopLepr" : 38,
+					"MainMenu" : 39,
+					"Win Scenario" : 40
+				},
+				"campaignVideo" : {
+					"GOOD1A.SMK" : 0,
+					"GOOD1B.SMK" : 1,
+					"GOOD1C.SMK" : 2,
+					"EVIL1A.SMK" : 3,
+					"EVIL1B.SMK" : 4,
+					"EVIL1C.SMK" : 5,
+					"NEUTRALA.SMK" : 6,
+					"NEUTRALB.SMK" : 7,
+					"NEUTRALC.SMK" : 8,
+					"GOOD2A.SMK" : 9,
+					"GOOD2B.SMK" : 10,
+					"GOOD2C.SMK" : 11,
+					"GOOD2D.SMK" : 12,
+					"EVIL2A.SMK" : 13,
+					"EVIL2AP1.SMK" : 14,
+					"EVIL2B.SMK" : 15,
+					"EVIL2C.SMK" : 16,
+					"EVIL2D.SMK" : 17,
+					"GOOD3A.SMK" : 18,
+					"GOOD3B.SMK" : 19,
+					"GOOD3C.SMK" : 20,
+					"SECRETA.SMK" : 21,
+					"SECRETB.SMK" : 22,
+					"SECRETC.SMK" : 23
 				}
 			},
 			"armageddonsBlade" : {
@@ -251,19 +336,69 @@
 						"special2" : 18  // magicUniversity
 					}
 				},
-				
 				"portraits" : {
 					"pasis" : 128,
 					"thunar" : 129,
 					"portraitGeneralKendal" : 156,
 					"portraitYoungCristian" : 157,
 					"portraitOrdwald" : 158
+				},
+				"campaignRegions" : {
+					"dragonSlayer" : 8,
+					"foolhardyWaywardness" : 9,
+					"festivalOfLife" : 10,
+					"dragonsBlood" : 11,
+					"playingWithFire" : 12,
+					"armageddonsBlade" : 13
+				},
+				"campaignMusic" : {
+					"CampainMusic10" : 41,
+					"BladeABCampaign" : 42,
+					"BladeDBCampaign" : 43,
+					"BladeDSCampaign" : 44,
+					"BladeFLCampaign" : 45,
+					"BladeFWCampaign" : 46,
+					"BladePFCampaign" : 47
+				},
+				"campaignVideo" : {
+					"H3ABab1.smk" : 24,
+					"H3ABab2.smk" : 25,
+					"H3ABab3.smk" : 26,
+					"H3ABab4.smk" : 27,
+					"H3ABab5.smk" : 28,
+					"H3ABab6.smk" : 29,
+					"H3ABab7.smk" : 30,
+					"H3ABab8.smk" : 31,
+					"H3ABab9.smk" : 32,
+					"H3ABdb1.smk" : 33,
+					"H3ABdb2.smk" : 34,
+					"H3ABdb3.smk" : 35,
+					"H3ABdb4.smk" : 36,
+					"H3ABdb5.smk" : 37,
+					"H3ABds1.smk" : 38,
+					"H3ABds2.smk" : 39,
+					"H3ABds3.smk" : 40,
+					"H3ABds4.smk" : 41,
+					"H3ABds5.smk" : 42,
+					"H3ABfl1.smk" : 43,
+					"H3ABfl2.smk" : 44,
+					"H3ABfl3.smk" : 45,
+					"H3ABfl4.smk" : 46,
+					"H3ABfl5.smk" : 47,
+					"H3ABfw1.smk" : 48,
+					"H3ABfw2.smk" : 49,
+					"H3ABfw3.smk" : 50,
+					"H3ABfw4.smk" : 51,
+					"H3ABfw5.smk" : 52,
+					"H3ABpf1.smk" : 53,
+					"H3ABpf2.smk" : 54,
+					"H3ABpf3.smk" : 55,
+					"H3ABpf4.smk" : 56
 				}
 			},
 			"shadowOfDeath" : { 
 				"supported" : true,
 				"iconIndex" : 2,
-				
 				"portraits" : {
 					"portraitGeneralKendal" : 156,
 					"portraitYoungCristian" : 157,
@@ -272,6 +407,64 @@
 					"portraitYoungGem" : 160,
 					"portraitYoungSandro" : 161,
 					"portraitYoungYog" : 162
+				},
+				"campaignRegions" : {
+					"hackAndSlash" : 14,
+					"birthOfBarbarian" : 15,
+					"newBeginning" : 16,
+					"elixirOfLife" : 17,
+					"riseOfTheNecromancer" : 18,
+					"unholyAlliance" : 19,
+					"spectreOfPower" : 20
+				},
+				"campaignMusic" : {
+					"CampainMusic11" : 48
+				},
+				"campaignVideo" : {
+			 		"H3x2_BBa.smk" : 57,
+					"H3x2_BBb.smk" : 58,
+					"H3x2_BBc.smk" : 59,
+					"H3x2_BBd.smk" : 60,
+					"H3x2_BBe.smk" : 61,
+					"H3x2_BBf.smk" : 62,
+					"H3x2_Ela.smk" : 63,
+					"H3x2_Elb.smk" : 64,
+					"H3x2_Elc.smk" : 65,
+					"H3x2_Eld.smk" : 66,
+					"H3x2_Ele.smk" : 67,
+					"H3x2_HSa.smk" : 68,
+					"EVIL2C.SMK"   : 69,
+					"H3x2_HSc.smk" : 70,
+					"H3x2_HSd.smk" : 71,
+					"H3x2_HSe.smk" : 72,
+					"H3x2_NBa.smk" : 73,
+					"H3x2_NBb.smk" : 74,
+					"H3x2_Nbc.smk" : 75,
+					"H3x2_Nbd.smk" : 76,
+					"H3x2_Nbe.smk" : 77,
+					"H3x2_RNa.smk" : 78,
+					"H3x2_RNb.smk" : 79,
+					"H3x2_RNc.smk" : 80,
+					"H3x2_RNd.smk" : 81,
+					"H3x2_RNe1.smk": 82,
+					"H3x2_SPa.smk" : 83,
+					"H3x2_SPb.smk" : 84,
+					"H3x2_SPc.smk" : 85,
+					"H3x2_SPd.smk" : 86,
+					"H3x2_SPe.smk" : 87,
+					"H3x2_UAa.smk" : 88,
+					"H3x2_UAb.smk" : 89,
+					"H3x2_UAc.smk" : 90,
+					"H3x2_UAd.smk" : 91,
+					"H3x2_UAe.smk" : 92,
+					"H3x2_UAf.smk" : 93,
+					"H3x2_UAg.smk" : 94,
+					"H3x2_UAh.smk" : 95,
+					"H3x2_UAi.smk" : 96,
+					"H3x2_UAj.smk" : 97,
+					"H3x2_UAk.smk" : 98,
+					"H3x2_UAl.smk" : 99,
+					"H3x2_UAm.smk" : 100 //H3x2_UAm.bik?
 				}
 			},
 			"chronicles" : { 
@@ -313,7 +506,15 @@
 			/// movement points hero can get on start of the turn when on land, depending on speed of slowest creature (0-based list)
 			"movementPointsLand" : [ 1500, 1500, 1500, 1500, 1560, 1630, 1700, 1760, 1830, 1900, 1960, 2000 ],
 			/// movement points hero can get on start of the turn when on sea, depending on speed of slowest creature (0-based list)
-			"movementPointsSea" : [ 1500 ]
+			"movementPointsSea" : [ 1500 ],
+			
+			/// Base scouting range for hero without any range modifiers
+			"baseScoutingRange" : 5,
+
+			/// Strength of generic secondary skill specialties ( "secondary" : "skillName" ) per level
+			"specialtySecondarySkillGrowth" : 5,
+			/// Strength of generic creature specialties ( "creature" : "creatureName" ) per level
+			"specialtyCreatureGrowth" : 5
 		},
 
 		"towns":
@@ -335,7 +536,10 @@
 			// How much researchs/skips per day are possible? (array index is spell tier)
 			"spellResearchPerDay": [ 2, 2, 2, 2, 1 ],
 			// Exponent for increasing cost for each research (factor 1 disables this; array index is spell tier)
-			"spellResearchCostExponentPerResearch": [ 1.25, 1.25, 1.25, 1.25, 1.25 ]
+			"spellResearchCostExponentPerResearch": [ 1.25, 1.25, 1.25, 1.25, 1.25 ],
+			
+			// Base scouting range for town without any range modifiers
+			"baseScoutingRange" : 5
 		},
 
 		"combat":
@@ -581,12 +785,6 @@
 					"val" : 1,
 					"valueType" : "BASE_NUMBER"
 				},
-				"sightRadius" :
-				{
-					"type" : "SIGHT_RADIUS", //default sight radius
-					"val" : 5,
-					"valueType" : "BASE_NUMBER"
-				},
 				"experienceGain" : 
 				{
 					"type" : "HERO_EXPERIENCE_GAIN_PERCENT", //default hero xp

+ 2 - 1
config/heroes/castle.json

@@ -94,7 +94,8 @@
 			{ "skill" : "artillery", "level": "basic" }
 		],
 		"specialty" : {
-			"creature" : "ballista"
+			"creature" : "ballista",
+			"creatureLevel" : 5
 		}
 	},
 	"tyris":

+ 2 - 1
config/heroes/dungeon.json

@@ -24,7 +24,8 @@
 			{ "skill" : "offence", "level": "basic" }
 		],
 		"specialty" : {
-			"creature" : "ballista"
+			"creature" : "ballista",
+			"creatureLevel" : 5
 		}
 	},
 	"dace":

+ 2 - 1
config/heroes/fortress.json

@@ -108,7 +108,8 @@
 			{ "skill" : "artillery", "level": "basic" }
 		],
 		"specialty" : {
-			"creature" : "ballista"
+			"creature" : "ballista",
+			"creatureLevel" : 5
 		}
 	},
 	"broghild":

+ 2 - 1
config/heroes/inferno.json

@@ -98,7 +98,8 @@
 			{ "skill" : "logistics", "level": "basic" }
 		],
 		"specialty" : {
-			"creature" : "ballista"
+			"creature" : "ballista",
+			"creatureLevel" : 5
 		}
 	},
 	"nymus":

+ 2 - 1
config/heroes/stronghold.json

@@ -24,7 +24,8 @@
 			{ "skill" : "artillery", "level": "basic" }
 		],
 		"specialty" : {
-			"creature" : "ballista"
+			"creature" : "ballista",
+			"creatureLevel" : 5
 		}
 	},
 	"jabarkas":

+ 2 - 1
config/heroes/tower.json

@@ -71,7 +71,8 @@
 			{ "skill" : "tactics", "level": "basic" }
 		],
 		"specialty" : {
-			"creature" : "ballista"
+			"creature" : "ballista",
+			"creatureLevel" : 5
 		}
 	},
 	"fafner":

+ 3 - 3
config/schemas/artifact.json

@@ -91,16 +91,16 @@
 			"type" : "object",
 			"additionalProperties" : false,
 			"description" : "Graphical files associated with the artifact",
-			"required" : [ "image", "map" ],
+			"required" : [ "image", "map", "scenarioBonus" ],
 			"properties" : {
 				"image" : {
 					"type" : "string",
 					"description" : "Base image for this artifact, used for example in hero screen",
 					"format" : "imageFile"
 				},
-				"large" : {
+				"scenarioBonus" : {
 					"type" : "string",
-					"description" : "Large image, used for drag-and-drop and popup messages",
+					"description" : "Image 58x64 in size, for use as campaign scenario starting bonus selection",
 					"format" : "imageFile"
 				},
 				"map" : {

+ 26 - 16
config/schemas/bonus.json

@@ -10,6 +10,32 @@
 			"type" : "boolean",
 			"description" : "If set to true, all instances of this bonus will be hidden in UI"
 		},
+
+		"creatureNature" : {
+			"type" : "boolean",
+			"description" : "If set to true, this bonus will be considered 'creature nature' bonus, and such creature won't be automatically granted LIVING bonus"
+		},
+
+		"description" : {
+			"type" : "string"
+		},
+
+		"subtypeDescriptions" : {
+			"type" : "object",
+			"description" : "Custom description string for bonus subtype",
+			"additionalProperties" : {
+				"type" : "string"
+			}
+		},
+
+		"valueDescriptions" : {
+			"type" : "object",
+			"description" : "Custom description string for bonus value",
+			"additionalProperties" : {
+				"type" : "string"
+			}
+		},
+
 		"graphics" : {
 			"type" : "object",
 			"additionalProperties" : false,
@@ -38,22 +64,6 @@
 					}
 				}
 			}
-		},
-
-		"subtypeDescriptions" : {
-			"type" : "object",
-			"description" : "Custom description string for bonus subtype",
-			"additionalProperties" : {
-				"type" : "string"
-			}
-		},
-		
-		"valueDescriptions" : {
-			"type" : "object",
-			"description" : "Custom description string for bonus value",
-			"additionalProperties" : {
-				"type" : "string"
-			}
 		}
 	}
 }

+ 74 - 48
config/schemas/bonusInstance.json

@@ -37,6 +37,78 @@
 					}
 				}
 			]
+		},
+		"updater" : 
+		{
+			"anyOf" : [
+				{
+					"type" : "string",
+					"enum" : [ "TIMES_HERO_LEVEL", "TIMES_STACK_LEVEL", "DIVIDE_STACK_LEVEL", "BONUS_OWNER_UPDATER", "TIMES_HERO_LEVEL_DIVIDE_STACK_LEVEL" ]
+				},
+				{
+					"description" : "GROWS_WITH_LEVEL updater",
+					"type" : "object",
+					"required" : ["type", "valPer20", "stepSize"],
+					"additionalProperties" : false,
+					"properties" : {
+						"type" : {
+							"type" : "string",
+							"const" : "GROWS_WITH_LEVEL",
+						},
+						"valPer20" : {
+							"type" : "integer",
+							"description" : "Bonus value for each 20 steps"
+						},
+						"stepSize" : {
+							"type" : "integer",
+							"minimum" : 1,
+							"description" : "Size of each step, in levels"
+						}
+					}
+				},
+				{
+					"description" : "TIMES_HERO_LEVEL updater",
+					"type" : "object",
+					"required" : ["type"],
+					"additionalProperties" : false,
+					"properties" : {
+						"type" : {
+							"type" : "string",
+							"const" : "TIMES_HERO_LEVEL",
+						},
+						"stepSize" : {
+							"type" : "integer",
+							"minimum" : 1,
+							"description" : "Size of each step, in levels"
+						}
+					}
+				},
+				{
+					"description" : "TIMES_STACK_SIZE updater",
+					"type" : "object",
+					"required" : ["type"],
+					"additionalProperties" : false,
+					"properties" : {
+						"type" : {
+							"type" : "string",
+							"const" : "TIMES_STACK_SIZE",
+						},
+						"stepSize" : {
+							"type" : "integer",
+							"minimum" : 1,
+							"description" : "Size of each step, in levels"
+						},
+						"minimum" : {
+							"type" : "integer",
+							"description" : "Minimal bonus value"
+						},
+						"maximum" : {
+							"type" : "integer",
+							"description" : "Maximal bonus value"
+						}
+					}
+				}
+			]
 		}
 	},
 	"additionalProperties" : false,
@@ -69,56 +141,10 @@
 			"enum" : [ "BATTLE_WIDE", "VISITED_TOWN_AND_VISITOR", "PLAYER_PROPAGATOR", "HERO", "TEAM_PROPAGATOR", "GLOBAL_EFFECT" ]
 		},
 		"updater" : {
-			"anyOf" : [
-				{
-					"type" : "string",
-					"enum" : [ "TIMES_HERO_LEVEL", "TIMES_STACK_LEVEL", "DIVIDE_STACK_LEVEL", "BONUS_OWNER_UPDATER", "TIMES_HERO_LEVEL_DIVIDE_STACK_LEVEL" ]
-				},
-				{
-					"description" : "updater",
-					"type" : "object",
-					"required" : ["type", "parameters"],
-					"additionalProperties" : false,
-					"properties" : {
-						"type" : {
-							"type" : "string",
-							"enum" : [ "GROWS_WITH_LEVEL" ],
-							"description" : "type"
-						},
-						"parameters" : {
-							"type" : "array",
-							"description" : "parameters",
-							"additionalItems" : true
-						}
-					}
-				}
-			]
+			"$ref" : "#/definitions/updater"
 		},
 		"propagationUpdater" : {
-			"anyOf" : [
-				{
-					"type" : "string",
-					"enum" : [ "TIMES_HERO_LEVEL", "TIMES_STACK_LEVEL", "ARMY_MOVEMENT", "BONUS_OWNER_UPDATER" ]
-				},
-				{
-					"description" : "propagationUpdater",
-					"type" : "object",
-					"required" : ["type", "parameters"],
-					"additionalProperties" : false,
-					"properties" : {
-						"type" : {
-							"type" : "string",
-							"enum" : [ "GROWS_WITH_LEVEL", "ARMY_MOVEMENT" ],
-							"description" : "type"
-						},
-						"parameters" : {
-							"type" : "array",
-							"description" : "parameters",
-							"additionalItems" : true
-						}
-					}
-				}
-			]
+			"$ref" : "#/definitions/updater"
 		},
 		"limiters" : {
 			"$ref" : "#/definitions/nestedLimiter",

+ 40 - 0
config/schemas/campaignRegion.json

@@ -0,0 +1,40 @@
+{
+	"type" : "object",
+	"$schema" : "http://json-schema.org/draft-04/schema",
+	"title" : "VCMI campaign region format",
+	"description" : "Format used to define campaign regions for h3c maps in VCMI",
+	"required" : [ "prefix", "colorSuffixLength", "desc" ],
+	"additionalProperties" : false,
+	"properties" : {
+		"prefix" :
+		{
+			"type" : "string",
+			"description" : "Prefix for all images from this region"
+		},
+		"colorSuffixLength" :
+		{
+			"type" : "number",
+			"description" : "Number of symbols used to encode color, 1 or 2"
+		},
+		"desc" :
+		{
+			"type" : "array",
+			"description" : "List of regions in this campaign",
+				"items" : {
+				"type" : "object",
+				"additionalProperties" : false,
+				"properties" : {
+					"infix" : {
+						"type" : "string"
+					},
+					"x" : {
+						"type" : "number"
+					},
+					"y" : {
+						"type" : "number"
+					}
+				}
+			}
+		}
+	}
+}

+ 6 - 2
config/schemas/gameSettings.json

@@ -47,7 +47,10 @@
 				"minimalPrimarySkills" :      { "type" : "array" },
 				"movementCostBase"  :         { "type" : "number" },
 				"movementPointsLand" :        { "type" : "array" },
-				"movementPointsSea" :         { "type" : "array" }
+				"movementPointsSea" :         { "type" : "array" },
+				"specialtyCreatureGrowth" :    { "type" : "number" },
+				"specialtySecondarySkillGrowth" : { "type" : "number" },
+				"baseScoutingRange" :         { "type" : "number" }
 			}
 		},
 		"towns" : {
@@ -59,7 +62,8 @@
 				"spellResearch" :                        { "type" : "boolean" },
 				"spellResearchCost" :                    { "type" : "array" },
 				"spellResearchPerDay" :                  { "type" : "array" },
-				"spellResearchCostExponentPerResearch" : { "type" : "array" }
+				"spellResearchCostExponentPerResearch" : { "type" : "array" },
+				"baseScoutingRange" :                    { "type" : "number" }
 			}
 		},
 		"combat": {

+ 8 - 0
config/schemas/hero.json

@@ -136,6 +136,14 @@
 				"secondary" : {
 					"type" : "string",
 					"description" : "Shortcut for defining secondary skill specialty, using standard H3 rules."
+				},
+				"creatureLevel" : {
+					"type" : "integer",
+					"description" : "Assumed creature level for creature specialty"
+				},
+				"stepSize" : {
+					"type" : "integer",
+					"description" : "How creature or secondary skill specialty should grow per each step. Default is 5"
 				}
 			}
 		},

+ 9 - 0
config/schemas/mod.json

@@ -90,6 +90,7 @@
 		},
 		"version" : {
 			"type" : "string",
+			"format" : "version",
 			"description" : "Current mod version, up to 3 numbers, dot-separated. Format: A.B.C"
 		},
 		"changelog" : {
@@ -270,10 +271,18 @@
 			"description" : "List of configuration files for creatures",
 			"$ref" : "#/definitions/fileListOrObject"
 		},
+		"campaignRegions" : {
+			"description" : "List of configuration files for campaign regions",
+			"$ref" : "#/definitions/fileListOrObject"
+		},
 		"artifacts" : {
 			"description" : "List of configuration files for artifacts",
 			"$ref" : "#/definitions/fileListOrObject"
 		},
+		"spellSchools" : {
+			"description" : "List of configuration files for spell schools",
+			"$ref" : "#/definitions/fileListOrObject"
+		},
 		"spells" : {
 			"description" : "List of configuration files for spells",
 			"$ref" : "#/definitions/fileListOrObject"

+ 6 - 0
config/schemas/skill.json

@@ -16,6 +16,7 @@
 				"images" : {
 					"type" : "object",
 					"description" : "Skill icons of varying size",
+					"required" : [ "small", "medium", "large", "scenarioBonus"],
 					"properties" : {
 						"small" : {
 							"type" : "string",
@@ -31,6 +32,11 @@
 							"type" : "string",
 							"description" : "82x93 skill icon",
 							"format" : "imageFile"
+						},
+						"scenarioBonus" : {
+							"type" : "string",
+							"description" : "58x64 skill icon for use as campaign scenario bonus",
+							"format" : "imageFile"
 						}
 					}
 				},

+ 63 - 13
config/schemas/spell.json

@@ -58,7 +58,7 @@
 		},
 		"levelInfo" : {
 			"type" : "object",
-			"required" :["range", "description", "cost", "power", "aiValue"],
+			"required" :["range", "description", "cost", "power"],
 
 			"additionalProperties" : false,
 			"properties" : {
@@ -77,9 +77,6 @@
 					"type" : "string",
 					"description" : "spell range description in SRSL"
 				},
-				"aiValue" : {
-					"type" : "number"
-				},
 				"effects" : {
 					"type" : "object",
 					"description" : "Timed effects (updated by prolongation)",
@@ -108,11 +105,6 @@
 							"type" : "boolean",
 							"description" : "true: friendly/hostile based on positiveness; false: all targets"
 						},
-						"clearTarget" :
-						{
-							"type" : "boolean",
-							"description" : "LOCATION target only. Target hex/tile must be clear"
-						},
 						"clearAffected" :
 						{
 							"type" : "boolean",
@@ -127,8 +119,67 @@
 			"additionalProperties" : false
 		}
 	},
-	"required" : ["type", "name", "school", "level", "power","gainChance","flags","levels"],
+	"required" : ["type", "name", "school", "level", "power", "flags", "levels"],
 	"additionalProperties" : false,
+	
+	"allOf": [
+		{
+			"if": {
+				"not" : {
+					"properties": {
+						"index" : {
+							"type" : "number"
+						}
+					}
+				},
+				"properties": {
+					"type": {
+						"enum" : ["adventure", "combat"],
+					},
+					
+				}
+			},
+			"then": {
+				"required" : ["school", "gainChance" ],
+				"properties": {
+					"levels": {
+						"required" : ["none", "basic", "advanced", "expert"]
+					},
+					"graphics" : {
+						"required" : ["iconBook", "iconScroll", "iconEffect", "iconImmune", "iconScenarioBonus"]
+					},
+				}
+			}
+		},
+		{
+			"if": {
+				"not" : {
+					"properties": {
+						"index" : {
+							"type" : "number"
+						}
+					}
+				},
+				"properties": {
+					"type": {
+						"const" : "ability"
+					}
+				}
+			},
+			"then": {
+				"required" : ["school", "gainChance" ],
+				"properties": {
+					"levels": {
+						"required" : ["none"]
+					},
+					"graphics" : {
+						"required" : ["iconEffect", "iconImmune"]
+					},
+				}
+			}
+		}
+	],
+	
 	"properties" : {
 		"index" : {
 			"type" : "number",
@@ -273,7 +324,7 @@
 				},
 				"iconEffect" : {
 					"type" : "string",
-					"description" : "Resourse path of icon for spell effects during battle" ,
+					"description" : "Resourse path of icon for spell effects during battle",
 					"format" : "imageFile"
 				},
 				"iconImmune" : {
@@ -283,7 +334,7 @@
 				},
 				"iconScenarioBonus" : {
 					"type" : "string",
-					"description" : "Resourse path of icon for scenario bonus" ,
+					"description" : "Resourse path of 58x64 icon for scenario bonus",
 					"format" : "imageFile"
 				}
 			}
@@ -301,7 +352,6 @@
 		"levels" : {
 			 "type" : "object",
 			 "additionalProperties" : false,
-			 "required" : ["none", "basic", "advanced", "expert"],
 			 "properties" : {
 				"base" : {
 					"type" : "object",

+ 19 - 0
config/schemas/spellSchool.json

@@ -0,0 +1,19 @@
+{
+	"type" : "object",
+	"$schema" : "http://json-schema.org/draft-04/schema",
+	"title" : "VCMI spell school format",
+	"description" : "Format used to define new spell schools in VCMI",
+	"required" : [ "schoolBorders" ],
+	"additionalProperties" : false,
+	"properties" : {
+		"index" : {
+			"type" : "number",
+			"description" : "numeric id of h3 spell school, prohibited for new schools"
+		},
+		"schoolBorders" : {
+			"type" : "string",
+			"description" : "File with frame borders of mastery levels for spells of this spell school in spellbook",
+			"format" : "animationFile"
+		}
+	}
+}

+ 7 - 5
config/schemas/townBuilding.json

@@ -44,11 +44,6 @@
 			"enum" : [ "normal", "auto", "special", "grail" ],
 			"description" : "Mode in which this building will be built"
 		},
-		"height" : {
-			"type" : "string",
-			"enum" : [ "skyship", "high", "average", "low"],
-			"description" : "Height for lookout towers and some grails"
-		},
 		"requires" : {
 			"$ref" : "#/definitions/buildingRequirement",
 			"description" : "List of town buildings that must be built before this one"
@@ -133,6 +128,13 @@
 				"enum" : [ "resource-resource", "resource-player", "creature-resource", "resource-artifact", "artifact-resource", "artifact-experience", "creature-experience", "creature-undead", "resource-skill"],
 			},
 			"description" : "List of modes available in this market"
+		},
+		"marketOffer" : {
+			"type" : "array",
+			"items" : {
+				"type" : "string"
+			},
+			"description" : "List of predefined items available on market from this building"
 		}
 	}
 }

+ 5 - 0
config/schemas/townStructure.json

@@ -29,6 +29,11 @@
 			"description" : "Golden border around building, displayed when building is selected",
 			"format" : "imageFile"
 		},
+		"campaignBonus" : {
+			"type" : "string",
+			"description" : "Town building icon for campaigns",
+			"format" : "imageFile"
+		},
 		"x" : {
 			"type" : "number",
 			"description" : "Position on screen"

+ 21 - 0
config/spellSchools.json

@@ -0,0 +1,21 @@
+{
+	"air" : {
+		"index" : 0,
+		"schoolBorders" : "SplevA"
+	},
+	
+	"fire" : {
+		"index" : 1,
+		"schoolBorders" : "SplevF"
+	},
+	
+	"earth" : {
+		"index" : 2,
+		"schoolBorders" : "SplevE"
+	},
+	
+	"water" : {
+		"index" : 3,
+		"schoolBorders" : "SplevW"
+	}
+}

+ 0 - 15
config/spells/moats.json

@@ -14,7 +14,6 @@
 				"range" : "0",
 				"description" : "", //For validation
 				"cost" : 0, //For validation
-				"aiValue" : 0, //For validation
 				"battleEffects" : {
 					"directDamage" : {
 						"type":"core:damage"
@@ -52,7 +51,6 @@
 		"levels" : {
 			"base":{
 				"description" : "",
-				"aiValue" : 0,
 				"power" : 0,
 				"cost" : 0,
 				"targetModifier":{"smart":false},
@@ -111,7 +109,6 @@
 				"range" : "0",
 				"description" : "", //For validation
 				"cost" : 0, //For validation
-				"aiValue" : 0, //For validation
 				"battleEffects" : {
 					"directDamage" : {
 						"type":"core:damage"
@@ -149,7 +146,6 @@
 		"levels" : {
 			"base":{
 				"description" : "",
-				"aiValue" : 0,
 				"power" : 0,
 				"cost" : 0,
 				"targetModifier":{"smart":false},
@@ -205,7 +201,6 @@
 		"levels" : {
 			"base":{
 				"description" : "",
-				"aiValue" : 0,
 				"power" : 0,
 				"cost" : 0,
 				"targetModifier":{"smart":false},
@@ -259,7 +254,6 @@
 				"range" : "0",
 				"description" : "", //For validation
 				"cost" : 0, //For validation
-				"aiValue" : 0, //For validation
 				"battleEffects" : {
 					"directDamage" : {
 						"type":"core:damage"
@@ -297,7 +291,6 @@
 		"levels" : {
 			"base":{
 				"description" : "",
-				"aiValue" : 0,
 				"power" : 0,
 				"cost" : 0,
 				"targetModifier":{"smart":false},
@@ -356,7 +349,6 @@
 				"range" : "0",
 				"description" : "", //For validation
 				"cost" : 0, //For validation
-				"aiValue" : 0, //For validation
 				"battleEffects" : {
 					"directDamage" : {
 						"type":"core:damage"
@@ -394,7 +386,6 @@
 		"levels" : {
 			"base":{
 				"description" : "",
-				"aiValue" : 0,
 				"power" : 0,
 				"cost" : 0,
 				"targetModifier":{"smart":false},
@@ -453,7 +444,6 @@
 				"range" : "0",
 				"description" : "", //For validation
 				"cost" : 0, //For validation
-				"aiValue" : 0, //For validation
 				"battleEffects" : {
 					"directDamage" : {
 						"type":"core:damage"
@@ -491,7 +481,6 @@
 		"levels" : {
 			"base":{
 				"description" : "",
-				"aiValue" : 0,
 				"power" : 0,
 				"cost" : 0,
 				"targetModifier":{"smart":false},
@@ -550,7 +539,6 @@
 				"range" : "0",
 				"description" : "", //For validation
 				"cost" : 0, //For validation
-				"aiValue" : 0, //For validation
 				"battleEffects" : {
 					"directDamage" : {
 						"type":"core:damage"
@@ -588,7 +576,6 @@
 		"levels" : {
 			"base":{
 				"description" : "",
-				"aiValue" : 0,
 				"power" : 0,
 				"cost" : 0,
 				"targetModifier":{"smart":false},
@@ -647,7 +634,6 @@
 				"range" : "0",
 				"description" : "", //For validation
 				"cost" : 0, //For validation
-				"aiValue" : 0, //For validation
 				"battleEffects" : {
 					"directDamage" : {
 						"type":"core:damage"
@@ -685,7 +671,6 @@
 		"levels" : {
 			"base":{
 				"description" : "",
-				"aiValue" : 0,
 				"power" : 0,
 				"cost" : 0,
 				"targetModifier":{"smart":false},

+ 0 - 2
config/spells/other.json

@@ -78,7 +78,6 @@
 				"range" : "0",
 				"description" : "", //For validation
 				"cost" : 0, //For validation
-				"aiValue" : 0, //For validation
 				"battleEffects" : {
 					"directDamage" : {
 						"type":"core:damage"
@@ -254,7 +253,6 @@
 				"range" : "0",
 				"description" : "", //For validation
 				"cost" : 0, //For validation
-				"aiValue" : 0, //For validation
 				"battleEffects" : {
 					"directDamage" : {
 						"type":"core:damage"

+ 0 - 4
config/spells/vcmiAbility.json

@@ -17,7 +17,6 @@
 		"levels" : {
 			"base": {
 				"description" : "",
-				"aiValue" : 0,
 				"power" : 40,
 				"cost" : 1,
 				"targetModifier":{"smart":true},
@@ -68,7 +67,6 @@
 		"levels" : {
 			"base":{
 				"description" : "",
-				"aiValue" : 0,
 				"power" : 10,
 				"cost" : 0,
 				"targetModifier":{"smart":true},
@@ -117,7 +115,6 @@
 		"levels" : {
 			"base":{
 				"description" : "",
-				"aiValue" : 0,
 				"power" : 1,
 				"cost" : 0,
 				"targetModifier":{"smart":true},
@@ -198,7 +195,6 @@
 		"levels" : {
 			"base":{
 				"description" : "",
-				"aiValue" : 0,
 				"power" : 1,
 				"cost" : 0,
 				"targetModifier":{"smart":true},

+ 1 - 1
docs/Readme.md

@@ -99,4 +99,4 @@ Engine documentation: (NOTE: may be outdated)
 VCMI Project source code is licensed under GPL version 2 or later.
 VCMI Project assets are licensed under CC-BY-SA 4.0. Assets sources and information about contributors are available under following link: <https://github.com/vcmi/vcmi-assets>
 
-Copyright (C) 2007-2024  VCMI Team (check AUTHORS file for the contributors list)
+Copyright (C) 2007-2025  VCMI Team (check AUTHORS file for the contributors list)

+ 6 - 6
docs/modders/Bonus/Bonus_Limiters.md

@@ -28,8 +28,8 @@ Bonus is only active if affected entity has another bonus that meets conditions
 Parameters:
 
 - Bonus type
-- bonus subtype
-- bonus sourceType and sourceId in struct
+- Bonus subtype (only used if bonus type is set)
+- Bonus source type and bonus source ID
 
 All parameters are optional. Values that don't need checking can be replaces with `null`
 
@@ -42,11 +42,11 @@ Examples:
 		{
 			"type" : "HAS_ANOTHER_BONUS_LIMITER",
 			"parameters" : [
-				null,
-				null,
+				null, // bonus type is ignored
+				null, // bonus subtype is also ignored
 				{
-					"type" : "SPELL_EFFECT",
-					"id" : "spell.bless"
+					"type" : "SPELL_EFFECT", // look for bonus of type SPELL_EFFECT
+					"id" : "spell.bless"     // ... from spell "Bless"
 				}
 				]
 		}

+ 77 - 35
docs/modders/Bonus/Bonus_Types.md

@@ -27,12 +27,6 @@ Changes mastery level of spells of affected heroes and units. Examples are magic
 - subtype: school of magic
 - val: level
 
-### DARKNESS
-
-On each turn, hides area in fog of war around affected town for all players other than town owner. Currently does not work for any entities other than towns.
-
-- val: radius in tiles
-
 ## Player bonuses
 
 Intended to be setup as global effect, AI cheat etc.
@@ -78,9 +72,9 @@ Allows flying movement for affected heroes
 
 Eliminates terrain penalty on certain terrain types for affected heroes (Nomads ability).
 
-Note: to eliminate all terrain penalties see ROUGH_TERRAIN_DISCOUNT bonus
+Note: to eliminate all terrain penalties see [ROUGH_TERRAIN_DISCOUNT](#rough_terrain_discount) bonus
 
-- subtype: type of terrain, eg `terrain.sand`
+- subtype: type of terrain, eg `sand`
 
 ### TERRAIN_NATIVE
 
@@ -99,6 +93,27 @@ Reveal area of fog of war around affected heroes when hero is recruited or moves
 
 - val: radius in tiles
 
+### DARKNESS
+
+On each turn, hides area in fog of war around affected objects for all players other than town owner. Areas within scouting range of owned objects are not affected
+
+NOTE: when used by heroes, effect would still activate only on new turn, and not on every hero movement
+
+- val: radius in tiles
+- addInfo: optional, activation period (e.g. 7 = weekly, 28 = monthly)
+
+### FULL_MAP_SCOUTING
+
+On each turn, reveals entire map for owner of the bonus
+
+- addInfo: optional, activation period (e.g. 7 = weekly, 28 = monthly)
+
+### FULL_MAP_DARKNESS
+
+On each turn, hides entire map in fog of war for all players other than town owner. Areas within scouting range of owned objects are not affected
+
+- addInfo: optional, activation period (e.g. 7 = weekly, 28 = monthly)
+
 ### MANA_REGENERATION
 
 Restores specific amount of mana points for affected heroes on new turn
@@ -111,6 +126,12 @@ Restores specific percentage of mana pool for affected heroes on new turn. If he
 
 - val: percentage of spell points to restore
 
+### SKELETON_TRANSFORMER_TARGET
+
+Unit affected by this bonus will be transformed into creature other than Skeleton when placed into Skeleton Transformer
+
+- subtype: type of creature to which this unit should be converted
+
 ### NONEVIL_ALIGNMENT_MIX
 
 Allows mixing of creaturs of neutral and good factions in affected armies without penalty to morale (Angelic Alliance effect)
@@ -123,18 +144,24 @@ Changes surrender cost for affected heroes
 
 ### IMPROVED_NECROMANCY
 
-Allows to raise different creatures than Skeletons after battle.
+Bonus allows the hero to raise specific creatures from corpses after battle.
+
+If the hero has multiple bonuses of the same type, the game will select the unit with the higher level. If the units have the same level, the game will select the unit with the higher market value (the total cost of the unit in gold, including converted resources).
+
+If the hero has no free space for the target creature but has space for its upgrade (including subsequent upgrades), the upgraded unit will be raised instead at a rate of two-thirds.
 
 - subtype: creature raised
 - val: Necromancer power
-- addInfo: Level of Necromancy secondary skill (1 - Basic, 3 - Expert)
-- Example (from Cloak Of The Undead King):
+- addInfo: Requried total level of Necromancer power for this bonus to be active (val of all bonuses of this type)
+
+Example (from Cloak Of The Undead King):
 
 ```json
 {
     "type" : "IMPROVED_NECROMANCY",
     "subtype" : "creature.walkingDead",
-    "addInfo" : 1
+    "addInfo" : 1, // requires 1 val of IMPROVED_NECROMANCY from other source, e.g. skill
+    "val" : 0 // does not provides levels of necromancer power on its own
 }
 ```
 
@@ -182,7 +209,7 @@ Allows affected heroes to position army before start of battle (Tactics)
 
 ### BEFORE_BATTLE_REPOSITION_BLOCK
 
-Reduces distance in which enemy hero can reposition. Counters BEFORE_BATTLE_REPOSITION bonus
+Reduces distance in which enemy hero can reposition. Counters [BEFORE_BATTLE_REPOSITION](#before_battle_reposition) bonus
 
 - val: distance within which hero can reposition his troops
 
@@ -200,7 +227,7 @@ Increases experience gain from combat by affected units. No effect if stack expe
 
 ### UNDEAD_RAISE_PERCENTAGE
 
-Defines percentage of enemy troops that will be raised after battle into own army (Necromancy). Raised unit is determined by IMPROVED_NECROMANCY bonus
+Defines percentage of enemy troops that will be raised after battle into own army (Necromancy). Raised unit is determined by [IMPROVED_NECROMANCY](#improved_necromancy) bonus
 
 - val: percentage of raised troops
 
@@ -255,7 +282,7 @@ Gives additional bonus to effect of all spells of selected school
 
 ### SPECIFIC_SPELL_DAMAGE
 
-Gives additional bonus to effect of specific spell
+For `damage`, `heal` and `demonSummon` spell effects, increases spell power by specific percentage
 
 - subtype: identifier of affected spell
 - val: bonus to spell effect, percentage
@@ -411,31 +438,39 @@ Increases starting amount of shots that unit has in battle
 
 ## Creature abilities
 
-## Static abilities and immunities
+## Creature Natures
+
+### LIVING
+
+Affected unit is considered to be alive. Automatically granted to any unit that does not have any other creature nature bonus
+
+Living units can be affected by TRANSMUTATION, LIFE_DRAIN, and SOUL_STEAL bonuses
 
 ### NON_LIVING
 
-Affected unit is considered to not be alive and not affected by morale and certain spells
+Creature nature bonus. Affected unit is considered to not be alive and not affected by morale and certain spells
 
 ### MECHANICAL
 
-Affected unit is considered to not be alive and not affected by morale and certain spells but should be repairable from engineers (factory).
+Creature nature bonus. Affected unit is considered to not be alive and not affected by morale and certain spells but should be repairable from engineers (HotA Factory).
 
 ### GARGOYLE
 
-Affected unit is considered to be a gargoyle and not affected by certain spells
+Creature nature bonus. Affected unit is considered to be a gargoyle and not affected by certain spells
 
 ### UNDEAD
 
-Affected unit is considered to be undead, which makes it immune to many effects, and also reduce morale of allied living units.
+Creature nature bonus. Affected unit is considered to be undead, which makes it immune to many effects, not affected by morale, and also reduce morale of allied living units.
 
 ### SIEGE_WEAPON
 
-Affected unit is considered to be a siege machine and can not be raised, healed, have morale or move. All War Machines should have this bonus.
+Creature nature bonus. Affected unit is considered to be a siege machine and can not be raised, healed, have morale or move. All War Machines should have this bonus.
+
+## Static abilities and immunities
 
 ### DRAGON_NATURE
 
-Affected unit is dragon. This bonus proved no effect, but is used as limiter several effects.
+Affected unit is dragon. This bonus provides no effects on its own, but is used as limiter for Mutare specialty
 
 ### KING
 
@@ -443,10 +478,6 @@ Affected unit will take more damage from units under Slayer spell effect
 
 - val: required skill mastery of Slayer spell to affect this unit
 
-### FEARLESS
-
-Affected unit is immune to Fear ability
-
 ### NO_LUCK
 
 Affected units can not receive good or bad luck
@@ -495,6 +526,12 @@ Affected unit will deal more damage based on movement distance (Champions)
 
 - val: additional damage per passed tile, percentage
 
+### VULNERABLE_FROM_BACK
+
+When affected unit is attacked from behind, it will receive more damage when attacked and will not turn around to face the attacker
+
+- val: additional damage for attacks from behind, percentage (0-100)
+
 ### HATE
 
 Affected unit will deal more damage when attacking specific creature
@@ -546,10 +583,12 @@ Examples:
 - Prism Breath (mods): `[ "FL", "FF", "FR" ]` — a more powerful version of Dragon Breath; all units behind the target are attacked.
 
 This is how all tiles can be referenced in the event of a frontal attack (green is the attacker and red is the defender). The hex on which defender is located is always included unconditionally.
-![MULTIHEX_UNIT_ATTACK frontal attack hexes indexing](../images/Bonus_Multihex_Attack_Horizontal.svg)
+
+![MULTIHEX_UNIT_ATTACK frontal attack hexes indexing](../../images/Bonus_Multihex_Attack_Horizontal.svg)
 
 In the case of a double-wide unit that can attack hexes to the left and right (e.g. Cerberi), the left or right hex may end up inside the attacker in certain attack configurations. To avoid this, the hex that ends up inside the unit will be 'pushed' one hex forward. This does not affect single-wide units. See below for reference:
-![MULTIHEX_UNIT_ATTACK vertical attack hexes indexing](../images/Bonus_Multihex_Attack_Vertical.svg)
+
+![MULTIHEX_UNIT_ATTACK vertical attack hexes indexing](../../images/Bonus_Multihex_Attack_Vertical.svg)
 
 ### MULTIHEX_ENEMY_ATTACK
 
@@ -637,7 +676,7 @@ Affected unit will gain new creatures for each enemy killed by this unit
 
 ### TRANSMUTATION
 
-Affected units have chance to transform attacked unit to other creature type
+Affected units have chance to transform attacked, living unit to other creature type, unless attacked unit is under TRANSMUTATION_IMMUNITY bonus
 
 - val: chance for ability to trigger, percentage
 - subtype:
@@ -645,6 +684,10 @@ Affected units have chance to transform attacked unit to other creature type
   - transmutationPerUnit: transformed unit will have same number of units as original stack
 - addInfo: creature to transform to. If not set, creature will transform to same unit as attacker
 
+### TRANSMUTATION_IMMUNITY
+
+Affected unit is immune to TRANSMUTATION bonus effects
+
 ### SUMMON_GUARDIANS
 
 When battle starts, affected units will be surrounded from all side with summoned units
@@ -779,9 +822,11 @@ Affected unit has chance to deal double damage on attack (Death Knight)
 
 - val: chance to trigger, percentage
 
-### FEAR
+### FEARFUL
+
+Affected unit has chance to freeze in fear and entirely skip its turn (Azure Dragons)
 
-If enemy army has creatures affected by this bonus, they will skip their turn with 10% chance (Azure Dragon). Blocked by FEARLESS bonus.
+- val: chance for the unit to freeze in fear
 
 ### HEALER
 
@@ -919,6 +964,7 @@ Affected unit is permanently enchanted with a spell, that is cast again every tu
 Affected unit is immune to all spell with level below or equal to value of this bonus
 
 - val: level of spell up to which this unit is immune to
+- addInfo: if set to 1, this will be "absolute immunity" that can not be negated by Orb of Vulnerability
 
 ### MAGIC_RESISTANCE
 
@@ -1068,10 +1114,6 @@ Dummy bonus that acts as marker for Dendroid's Bind ability
 
 - addInfo: ID of stack that have bound the unit
 
-### SYNERGY_TARGET
-
-Dummy skill for alternative upgrades mod
-
 ### THIEVES_GUILD_ACCESS
 
 Increases amount of information available in affected thieves guild (in town or in adventure map tavern). Does not affects adventure map object "Den of Thieves". You may want to use PLAYER_PROPAGATOR with this bonus to make its effect player wide.

+ 37 - 15
docs/modders/Bonus/Bonus_Updaters.md

@@ -8,16 +8,15 @@ Check the files in `config/heroes/` for additional usage examples.
 
 ## GROWS_WITH_LEVEL
 
-- Type: Complex
-- Parameters: valPer20, stepSize=1
-- Effect: Updates val to `ceil(valPer20 * floor(heroLevel / stepSize) / 20)`
+Effect: Updates val to `ceil(valPer20 * floor(heroLevel / stepSize) / 20)`
 
 Example: The following updater will cause a bonus to grow by 6 for every 40 levels. At first level, rounding will cause the bonus to be 0.
 
 ```json
 "updater" : {
-    "parameters" : [ 6, 2 ],
-    "type" : "GROWS_WITH_LEVEL"
+    "type" : "GROWS_WITH_LEVEL",
+    "valPer20" : 6,
+    "stepSize" : 2
 }
 ```
 
@@ -25,8 +24,9 @@ Example: The following updater will cause a bonus to grow by 3 for every 20 leve
 
 ```json
 "updater" : {
-    "parameters" : [ 3 ],
-    "type" : "GROWS_WITH_LEVEL"
+    "type" : "GROWS_WITH_LEVEL",
+    "valPer20" : 3,
+    "stepSize" : 1
 }
 ```
 
@@ -37,15 +37,22 @@ Remarks:
 
 ## TIMES_HERO_LEVEL
 
-- Type: Simple
-- Effect: Updates val to `val * heroLevel`
+Effect: Updates val to `val * heroLevel / stepSize`
 
 Usage: `"updater" : "TIMES_HERO_LEVEL"`
 
+Usage with stepSize greater than one:
+
+```json
+"updater" : {
+    "type" : "TIMES_HERO_LEVEL",
+    "stepSize" : 2
+}
+```
+
 ## TIMES_STACK_LEVEL
 
-- Type: Simple
-- Effect: Updates val to `val * stackLevel`, where `stackLevel` is level of stack (Pikeman is level 1, Angel is level 7)
+Updates val to `val * stackLevel`, where `stackLevel` is level of stack (Pikeman is level 1, Angel is level 7)
 
 Usage:
 
@@ -55,8 +62,7 @@ Remark: The stack level for war machines is 0.
 
 ## DIVIDE_STACK_LEVEL
 
-- Type: Simple
-- Effect: Updates val to `val / stackLevel`, where `stackLevel` is level of stack (Pikeman is level 1, Angel is level 7)
+Updates val to `val / stackLevel`, where `stackLevel` is level of stack (Pikeman is level 1, Angel is level 7)
 
 Usage:
 
@@ -66,8 +72,7 @@ Remark: The stack level for war machines is 0.
 
 ## TIMES_HERO_LEVEL_DIVIDE_STACK_LEVEL
 
-- Type: Simple
-- Effect: Same effect as `TIMES_HERO_LEVEL` combined with `DIVIDE_STACK_LEVEL`: `val * heroLevel / stackLevel`
+Effect: Same effect as `TIMES_HERO_LEVEL` combined with `DIVIDE_STACK_LEVEL`: `val * heroLevel / stackLevel`
 
 Intended to be used as hero bonus (such as specialty, skill, or artifact), for bonuses that affect units (Example: Adela Bless specialty)
 
@@ -75,6 +80,23 @@ Usage:
 
 `"updater" : "TIMES_HERO_LEVEL_DIVIDE_STACK_LEVEL"`
 
+## TIMES_STACK_SIZE
+
+Effect: Updates val to `val = clamp(val * floor(stackSize / stepSize), minimum, maximum)`, where stackSize is total number of creatures in current unit stack
+
+Parameters `minimum` and `maximum` are optional and can be dropped if not needed
+
+Example:
+
+```json
+"updater" : {
+    "type" : "TIMES_STACK_SIZE",
+    "minimum" : 0,
+    "maximum" : 100,
+    "stepSize" : 2
+}
+```
+
 ## BONUS_OWNER_UPDATER
 
 Helper updater for proper functionality of `OPPOSITE_SIDE` limiter

+ 2 - 2
docs/modders/Entities_Format/Artifact_Format.md

@@ -46,8 +46,8 @@ In order to make functional artifact you also need:
 		// Base image for this artifact, used for example in hero screen
 		"image": "BigSword.png",
 
-		// Large image, used for drag-and-drop and popup messages
-		"large": "BigSword_large.png",
+		// Large 58x64 image, used for campaign scenario bonus selection
+		"scenarioBonus": "BigSword_large.png",
 
 		//def file for adventure map
 		"map": "BigSword.def"

+ 1 - 1
docs/modders/Entities_Format/Battle_Obstacle_Format.md

@@ -36,7 +36,7 @@ How blocked tiles are defined depends on whether obstacle is `absolute` or not:
 
 Non-absolute obstacles specify their coordinates relative to bottom-left corner of obstacle. If you wish to have obstacle that takes multiple rows, substracting 17 from hex number would block tile directly above bottom-left corner of your obstacle.
 
-For example, obstacle that blocks tiles `[1, 2, 3, -15, -16, -31]` would result in following layout on the battlefield:
+For example, obstacle that blocks tiles `[1, 2, 3, -14, -15, -31]` would result in following layout on the battlefield:
 
 ![Battlefield Relative Obstacle Example](../../images/Battle_Field_Relative_Obstacle.svg)
 

+ 53 - 0
docs/modders/Entities_Format/Bonus_Types_Format.md

@@ -0,0 +1,53 @@
+# Bonus Types Format
+
+WARNING: currently custom bonus types can only be used for custom "traits", for example to use them in limiters. At the moment it is not possible to provide custom mechanics for such bonus, or have custom bonuses with subtypes or addInfo parameters
+
+```json
+{
+	// If set to true, this bonus will be hidden in creature view
+	"hidden" : false,
+
+	// If set to true, this bonus will be considered a "creature nature" bonus
+	// If creature has no creature nature bonuses, it is considered to be a LIVING creature
+	"creatureNature" : false,
+	
+	// Generic human-readable description of this bonus
+	// Visible in creature window
+	// Can be overriden in creature abilities or artifact bonuses
+	"description" : "{Bonus Name}\nBonus description",
+	
+	"graphics" : {
+		// Generic icon of this bonus
+		// Visible in creature window
+		// Can be overriden in creature abilities or artifact bonuses
+		"icon" : "path/to/icon.png",
+		
+		// Custom icons for specific subtypes of this bonus
+		"subtypeIcons" : {
+			"spellSchool.air" : "",
+			"spellSchool.water" : "",
+		},
+		
+		// Custom icons for specific values of this bonus
+		// Note that values must be strings and wrapped in quotes
+		"valueIcons" : {
+			"1" : "",
+			"2" : "",
+		}
+	},
+	
+	// Custom descriptions for specific subtypes of this bonus
+	"subtypeDescriptions" : {
+		"spellSchool.air" : ""
+		"spellSchool.water" : "",
+	},
+	
+	// Custom descriptions for specific values of this bonus
+	// Note that values must be strings and wrapped in quotes
+	"valueDescriptions" : {
+			"1" : ""
+			"2" : ""
+		}
+	}
+}
+```

+ 2 - 0
docs/modders/Entities_Format/Secondary_Skill_Format.md

@@ -83,6 +83,8 @@ level fields become optional if they equal "base" configuration.
 		"medium" : "",
 		// 82x93 skill icon
 		"large" : "",
+		// 58x64 skill icon for campaign scenario bonus
+		"scenarioBonus" : ""
 	}
 }
 ```

+ 363 - 98
docs/modders/Entities_Format/Spell_Format.md

@@ -6,8 +6,11 @@
 {
 	"spellName":
 	{	
-		// Mandatory. Spell type 
 		// Allowed values: "adventure", "combat", "ability"
+		// adventure spells can only be cast by hero on adventure map
+		// combat spells can be cast by hero or by creatures during combat
+		// ability-type spells can not be rolled in town mage guild 
+		// learned by hero and can only be used by creatures
 		"type": "adventure",
 		
 		// Mandatory. Spell target type
@@ -20,16 +23,17 @@
 		// Localizable name of this spell
 		"name": "Localizable name",
 	
-		// Mandatory. List of spell schools this spell belongs to
+		// List of spell schools this spell belongs to. Require for spells other than abilities
 		"school": {"air":true, "earth":true, "fire":true, "water":true},
 	
-		// Mandatory. Spell level, value in range 1-5, or 0 for abilities
+		// Spell level, value in range 1-5, or 0 for abilities
 		"level": 1,
 
-		// Mandatory. Base power of the spell
+		// Base power of the spell. To see how it affects spell, 
+		// see description of corresponding battle effect(s)
 		"power": 10,
 	
-		// Mandatory. Default chance for this spell to appear in Mage Guilds
+		// Default chance for this spell to appear in Mage Guilds
 		// Used only if chance for a faction is not set in gainChance field
 		"defaultGainChance": 0, 
 	
@@ -43,20 +47,23 @@
 		"animation":{<Animation format>},
 			
 		// List of spells that will be countered by this spell
+		// If unit is affected by any spells from this list, 
+		// then casting this spell will remove effect of countered spell
 		"counters": {
 			"spellID" : true, 
 			...
 		},
 
-		//Mandatory. List of flags that describe this spell
-		// positive - this spell is positive to target (buff)
-		// negative - this spell is negative to target (debuff)
+		// List of flags that describe this spell
+		// positive - this spell is positive to target (buff) and can target allies
+		// negative - this spell is negative to target (debuff) and can target enemies
 		// indifferent - spell is neither positive, nor negative
-		// damage - spell does damage (direct or indirect)
-		// offensive - direct damage (implicitly sets damage and negative)
-		// rising - rising spell (implicitly sets positive)
-		// special - this spell is normally unavailable and can only be received explicitly, e.g. from bonus SPELL
-		// nonMagical - this spell is not affected by Sorcery or magic resistance. School resistances apply.
+		// damage - spell does damage (direct or indirect). 
+		// If set, AI will avoid obstacles with such effect, and spellbook popup will also list damage of the spell
+		// offensive - (Deprecated?) direct damage (implicitly sets damage and negative)
+		// rising - (Deprecated?) rising spell (implicitly sets positive)
+		// special - this spell can not be present in mage guild, or leared by hero, and can only be received explicitly, e.g. from bonus SPELL
+		// nonMagical - this spell is not affected by Sorcery or magic resistance. School resistances (if any) apply.
 		"flags" : {
 			"positive": true,
 		},
@@ -195,36 +202,40 @@ TODO
 ```json
 
 {
-	//Mandatory, localizable description. Use {xxx} for formatting
+	//Localizable description. Use {xxx} for formatting
 	"description": "",
 
-	//Mandatory, cost in mana points
+	//Cost in mana points
 	"cost": 1,
 
-	//Mandatory, number
+	// Base power of the spell. To see how it affects spell, 
+	// see description of corresponding battle effect(s)
 	"power": 10,
 
-	//Mandatory, number
-	"aiValue": 20,
-
 	//Mandatory, flags structure //TODO
 	// modifiers make sense for creature target
 	"targetModifier":
 	{
-		"smart": false,	//true: friendly/hostile based on positiveness; false: all targets
-		"clearTarget": false,
+		// If true, then this spell will not affect units if:
+		// - target is friendly and spell is negative
+		// - target is enemy, and spell is positive
+		// Othervice, all units in affected area will be hit by a spell, provided they are not immune
+		"smart": false,
+		// LOCATION target only. All affected hexes must be empty with no obstacles or units on them
 		"clearAffected": false,
 	},
 	
-	//Mandatory
-	//spell range description in SRSL
-	// range "X" + smart modifier = enchanter casting, expert massive spells
-	// range "X" + no smart modifier = armageddon, death ripple, destroy undead
-
+	// spell range description. Only for combat spells
+	// Describes distances from spell cast point that will be affected.
+	// For example, "range" : "0" will only affect hex on which this spell was cast (e.g. Magic Arrow)
+	// "range" : "0,1" will affect hex on which spell was cast, as well as all hexes around it (e.g. Fireball)
+	// "range" : "1" will only affect hexes around target, without affecting target itself (Frost Ring)
+	// "range" : "0,1,2," or "range" : "0-2" will affect all tiles 0,1 and 2 hexes away from the target (Inferno)
+	// Special case: range "X" implies massive spells that affect all units (armageddon, death ripple, destroy undead)
 	"range": "X",
 
-	//DEPRECATED, Optional, arbitrary name - bonus format map
-	//timed effects, overriding by name
+	// DEPRECATED, please use "battleEffects" with timed effect instead
+	// List of bonuses that will affect targets for duration of the spell
 	"effects":
 	{
 		"firstEffect": {[bonus format]},
@@ -233,162 +244,413 @@ TODO
 
 	
 	},
-	//DEPRECATED, cumulative effects that stack while active
+
+	// DEPRECATED, please use "battleEffects" with timed effect and "cumulative" set to true instead
+	// List of bonuses that will affect targets for duration of the spell. Casting spell repeatetly will cumulate effect (Disrupting Ray)
 	"cumulativeEffects":
 	{
 		"firstCumulativeEffect": {[bonus format]}
 		//...
-
 	},
+
+	/// See Configurable battle effects section below for detailed description
 	"battleEffects":
 	{
 		"mod:firstEffect": {[effect format]},
 		"mod:secondEffect": {[effect format]}
 		//...
-
 	}
 }
 ```
 
-## Configurable battle effects
+## Spell power
 
-**If spell have at least one special effect it become configurable spell and spell configuration processed different way**
+Most of battle effects are scaled based on spell effect value. This value is same across all effects and calculated as:
 
-### Configurable spell
+```text
+caster spell power * base spell power + spell mastery power(caster spell school)
+```
 
-Configurable spells ignore *offensive* flag, *effects* and *cumulativeEffects*. For backward compatibility *offensive* flag define Damage effect, *effects* and *cumulativeEffects* define Timed effect.
+Where:
 
-### Special effect common format
+- `caster spell school` is assumed spell school level for the spell. For unit this is value of [SPELLCASTER](../bonus/Bonus_Types.md#spellcaster) bonus. For hero this is value of [MAGIC_SCHOOL_SKILL](../bonus/Bonus_Types.md#magic_school_skill) or [SPELL](../bonus/Bonus_Types.md#spell) bonus, whichever is greater
+- `spell mastery power` is `power` parameter defined in config of corresponding mastery level of the spell
+- `base spell power` is `power` parameter, as defined in config of spell itself
+- `caster spell power` is spellpower of the hero, or [CREATURE_SPELL_POWER](../bonus/Bonus_Types.md#creature_spell_power) bonus for units
 
-TODO
+If unit has [SPECIFIC_SPELL_POWER](../bonus/Bonus_Types.md#specific_spell_power) bonus for corresponding spell, game will use value of the bonus instead
+
+Power of `damage`, `heal`, `summon`, and `demonSummon` effects cast by hero can also be affected by following bonuses:
+
+- [SPECIAL_SPELL_LEV](../bonus/Bonus_Types.md#special_spell_lev) bonus for the spell, scaled down by target level (Solmyr / Deemer)
+
+Following bonuses will only affect `damage`, `heal` and `demonSummon` effects
+
+- [SPELL_DAMAGE](../bonus/Bonus_Types.md#spell_damage) for specific spell school (Sorcery)
+- [SPECIFIC_SPELL_DAMAGE](../bonus/Bonus_Types.md#specific_spell_damage) for the spell (Luna / Ciele)
+
+## Smart target modifier
+
+To restrict spell from casting it on "wrong" side in combat, you can use `smart` target modifier. If this flag is set, and spell has `positive` flag, it can only affect friendly units. Similarly, spells with `negative` flag and `smart` target modifier can only affect enemies. This affects both primary targets and any secondary targets in case of area of effect or massive spells.
+
+## Configurable battle effects
+
+### Common format
 
 ```json
 
-"mod:effectId":{
+"firstSpellEffect":{
+	// identifier of effect type. See type-specific documentation below for possible values
+	"type":"core:effectType", 
 
-"type":"mod:effectType", //identifier of effect type
-"indirect": false, // effect will be deferred (f.e. land mine damage) 
-"optional": false // you can cast spell even if this effect in not applicable
+	// effect will be deferred (f.e. land mine damage) TODO: clarify. Only dispell uses this flag!
+	"indirect": false,
+	
+	// spell can be cast even if this effect in not applicable, for example due to immunity
+	// Can be used for secondary effects, to allow casting spell if only main effect is applicable
+	"optional": false 
 
-//for unit target effects
-"ignoreImmunity" : false,
-"chainFactor" : 0.5,
-"chainLength" : 4
+	/// following fields are only applicable to effects that are cast on units (and not locations or summon)
+	
+	/// Ignore immunity unless unit has SPELL_IMMUNITY bonus for this spell with addInfo set to 1
+	"ignoreImmunity" : false,
 
-//other fields depending on type
+	/// Specifies number of additional targets to hit in chain, similar to Chain Lightning
+	"chainLength" : 4
+	
+	// Only applicable for damage spells and only if chain length is non-zero.
+	// Multiplier for damage for each chained target
+	"chainFactor" : 0.5,
 }
 ```
 
 ### Catapult
 
-TODO
+This spell can only be used when attacking a town with existing, non-destroyed walls. Can be also cast by defender, unless spell uses "smart" targeting
+
+Casting the spell on location with wall will deal 0-2 damage to the walls or towers, depending on spell configuration.
+
+Casting the spell with "massive" target will randomly pick selected number of target using logic similar to H3
 
 ```json
 
-"mod:effectId":{
+"firstSpellEffect":{
+	"type": "core:catapult"
+
+	// How many targets will be attacked by the spell
+	"targetsToAttack": 1, 
+
+	// If it is a targeted spell, probability to hit keep
+	"chanceToHitKeep" : 5, 
 
-"type": "core:catapult"
-	"targetsToAttack": 1, //How many targets will be attacked by this
-	"chanceToHitKeep" : 5, //If it is a targeted spell, chances to hit keep
-	"chanceToHitGate" : 25, //If it is a targeted spell, chances to hit gate
-	"chanceToHitTower" : 10, //If it is a targeted spell, chances to hit tower
-	"chanceToHitWall" : 50, //If it is a targeted spell, chances to hit wall
-	"chanceToNormalHit" : 60, //Chance to have 1 damage to wall, used for both targeted and massive
-	"chanceToCrit" : 30 //Chance to have 2 damage to wall, used for both targeted and massive
+	// If it is a targeted spell, probability to hit gate
+	"chanceToHitGate" : 25, 
+
+	// If it is a targeted spell, probability to hit tower
+	"chanceToHitTower" : 10,
+
+	// If it is a targeted spell, probability to hit wall 
+	"chanceToHitWall" : 50, 
+
+	// probability to deal 1 damage to wall, used for both targeted and massive
+	"chanceToNormalHit" : 60, 
+
+	// probability to deal 2 damage to wall, used for both targeted and massive
+	// chance to miss is defined implicitly, as remainer of 100% chance of normal and critical hits
+	"chanceToCrit" : 30 
 }
 ```
 
 ### Clone
 
-TODO
+Configurable version of Clone spell. Casting the spell will create clone of targeted unit that belongs to side of spell caster.
 
-Configurable version of Clone spell.
+```json
+"firstSpellEffect":{
+	"type": "core:clone"
+
+	// Maximal tier of unit on which this spell can be cast
+	"maxTier" : 3
+}
+```
+
+### Damage
+
+Deals specified damage to all affected targets based on spell effect value:
+
+- if `killByPercentage` is set, spell will deal damage equal to unit total health * [spell effect power](#spell-power) / 100
+- if `killByCount` is set, spell will deal damage equal to single creature health * [spell effect power](#spell-power)
+- if neither flag is set, spell will deal damage equal to [spell effect power](#spell-power)
+
+If spell has chain effect, damage dealt to chained target will be multiplied by specified `chainFactor`
+
+Target with [SPELL_DAMAGE_REDUCTION](../bonus/Bonus_Types.md#spell_damage_reduction) bonus with value greater than 100% for any of spell school of the spell are immune to this effect
 
 ```json
+"firstSpellEffect":{
+	"type": "core:damage",
+	"killByCount": false, 
+	"killByPercentage" : false,
+}
+```
 
-"mod:effectId":{
+### Dispel
 
-"type": "core:clone"
+Dispells all bonuses provided by any other spells from this unit. Following spells can not be dispelled
 
-"maxTier" : 3//unit tier
+- Disrupting ray
+- Acid Breath
+- any effects from adventure spells
+- any effects that comes from this spell, including effects from previous casts of the spell
+
+Only bonuses from spells with specified positiveness(es) will be dispelled. See configuration example.
+
+```json
+"firstSpellEffect":{
+	"type": "core:dispel",
+	
+	/// if set, spell will dispell other spells with "positive" flag
+	"dispelPositive": false, 
+
+	/// if set, spell will dispell other spells with "negative" flag
+	"dispelNegative" : false,
+
+	/// if set, spell will dispell other spells with "indifferent" flag
+	"dispelNeutral" : false,
 }
 ```
 
-### Damage effect
+### Heal
 
-TODO
+Effect restores [spell effect power](#spell-power) health points of affected unit. Can only be cast on unit that is not a clone and have lost some health points in the battle.
 
-If effect is automatic, spell behave like offensive spell (uses power, levelPower etc)
+If parameter `minFullUnits` is non-zero, spell can only be cast if it will at least heal enough health points to fully restore health of specified number of units. For example, a single Archangel can only use Resurrection on units with 100 health points or lower
+
+Spell can be used on dead units, but only if corpse is not blocked by a living unit.
 
 ```json
+"firstSpellEffect":{
+	"type": "core:heal",
+	
+	/// Minimal amount of health points that this spell can restore, based on target creature health
+	"minFullUnits" : 1,
+	
+	/// "heal" - only heals the unit, without resurrecting any creatures
+	/// "resurrect" - heals, resurrecting any dead units from stack until running out of power
+	/// "overHeal" - similar to resurrect, however it may also increase unit stack size over its initial size
+	"healLevel" : "heal",
+	
+	/// "oneBattle" - any resurrected unit will only stay alive till end of battle
+	/// "permanent" - any resurrected units will stay permanently after the combat
+	"healPower" : "permanent"
+}
+```
 
-"mod:effectId":{
+### Sacrifice
 
-"type": "core:damage",
-"killByCount": false, //if true works like Death Stare
-"killByPercentage" : false, //if true works like DESTRUCTION ability
+Sacrifice spell. Allows to destroy first target, while healing the second one. Destroyed unit is completely removed from the game.
 
-//TODO: options override
+Effect configuration is identical to [Heal effect](#heal).
 
+```json
+"firstSpellEffect":{
+	"type": "core:sacrifice"
+	"minFullUnits" : 1,
+	"healLevel" : "heal",
+	"healPower" : "permanent"
 }
 ```
 
-### Dispel
+### Obstacle
 
 TODO
 
-### Heal
+```json
+"firstSpellEffect":{
+	"type": "core:obstacle"
+	
+	"hidden" : false,
+	"passable" : false,
+	"trap" : false,
+	"removeOnTrigger" : false,
+	"hideNative" : false,
+
+	"patchCount" : 1,
+	"turnsRemaining" : 1,
+	"triggerAbility" : "obstacleTriggerAbility",
+	
+	"attacker" : {
+		"shape" : [],
+		"range" : [],
+		"appearSound" : {},
+		"appearAnimation" : {},
+		"animation" : {},
+		"offsetY" : 0
+	},
+	
+	"defender" : {
+		"shape" : [],
+		"range" : [],
+		"appearSound" : {},
+		"appearAnimation" : {},
+		"animation" : {},
+		"offsetY" : 0
+	}
+}
+```
+
+### Moat
 
 TODO
 
-### Obstacle
+```json
+"firstSpellEffect":{
+	"type": "core:moat"
+	
+	"hidden" : false,
+	"trap" : false,
+	"removeOnTrigger" : false,
+	"dispellable" : false,
 
-TODO
+	"moatDamage" : 90,
+	"moatHexes" : [],
+
+	"triggerAbility" : "obstacleTriggerAbility",
+	
+	"defender" : {
+		"shape" : [],
+		"range" : [],
+		"appearSound" : {},
+		"appearAnimation" : {},
+		"animation" : {},
+		"offsetY" : 0
+	}
+}
+```
 
 ### Remove obstacle
 
-TODO
+Effect removes an obstacle from targeted hex
 
-### Sacrifice
+```json
+"firstSpellEffect":{
+	"type": "core:removeObstacle",
+	
+	/// If set to true, spell can remove large ("absolute") obstacles
+	"removeAbsolute" : false,
 
-TODO
+	/// If set to true, spell can remove small obstacles (H3 behavior)
+	"removeUsual" : true,
+	
+	// If set to true, spell can remove any obstacle that was created by spell
+	"removeAllSpells" : true,
+	
+	// If set to true, spell can remove obstacles that were created with specific spell
+	"removeSpells" : [ "spellA", "spellB" ],
+}
+```
 
 ### Summon
 
-TODO
-
-### Teleport
+Effect summons additional units to the battlefield.
 
-TODO
+If `exclusive` flag is set, attempt to summon a different creature by the same side in combat will fail (even if previous summon was non-exclusive)
 
-### Timed
+Amount of summoned units is equal to [spell effect power](#spell-power).  Summoned units will disappear after combat, unless `permanent` flag in effect config is set
 
-TODO
+If `summonByHealth` option is set, then number of summoned units will be equal to [spell effect power](#spell-power) / unit health. Hero need to be able to summon at least one full unit for spell to work
 
-If effect is automatic, spell behave like \[de\]buff spell (effect and
-cumulativeEffects ignored)
+if `summonSameUnit` flag is set, and same creature was already summoned before, spell will instead heal unit in "overheal" mode, using same [spell effect power](#spell-power).
 
 ```json
+"firstSpellEffect":{
+	"type": "core:summon",
+	
+	/// Unit to summon
+	"id" : "airElemental",
+	
+	"permanent" : false,
+	"exclusive" : false,
+	"summonByHealth" : false,
+	"summonSameUnit" : false,
+}
+```
 
-"mod:effectId":{
+### Demon Summon
 
-"type": "core:timed",
-"cumulative": false
-"bonus":
-{
-"firstBonus":{[bonus format]}
-//...
+Implements Pit Lord's ability with the same name. Raises targeted dead unit as unit specified in spell parameters on casters side. New unit will be placed on the same position as corpse, and corpse will be removed from the battlefield
+
+Raised amount of units is limited by (rounded down):
+
+- total HP of summoned unit can not be larger than [spell effect power](#spell-power) of caster
+- total HP of summoned unit can not be larger than total HP of dead unit
+- total stack size of summoned unit can not be greater than stack size of dead unit
+
+```json
+"firstSpellEffect":{
+	"type": "core:demonSummon",
+	
+	/// Unit to summon
+	"id" : "demon",
+	
+	/// If true, unit will remain after combat
+	"permanent" : false
 }
+```
+
+### Teleport
+
+Effect instantly moves unit from its current location to targeted tile
+
+```json
+"firstSpellEffect":{
+	"type": "core:teleport",
+	
+	/// If true, unit will trigger obstacles on destination location
+	"triggerObstacles" : false,
+	
+	/// If true, unit can be teleported across moat during town siege
+	"isMoatPassable" : false,
+
+	/// If true, unit can be teleported across walls during town siege
+	"isWallPassable" : false,
 }
 ```
 
-## Additional documentation
+### Timed
 
-### Targets, ranges, modifiers
+Timed effect gives affected units specified bonuses for duration of the spell.
 
-TODO
+Duration of effect is:
+
+- Hero: Spellpower + value of [SPELL_DURATION](../bonus/Bonus_Types.md#spell_duration) + [SPELL_DURATION](../bonus/Bonus_Types.md#spell_duration) for specific spell
+- Units: value of [CREATURE_ENCHANT_POWER](../bonus/Bonus_Types.md#creature_enchant_power), or 3 if no such bonus
+
+Value of all bonuses can be affected by following bonuses:
+
+- [SPECIAL_PECULIAR_ENCHANT](../bonus/Bonus_Types.md#special_peculiar_enchant): value modified by 1-3 according to level of target unit
+- [SPECIAL_ADD_VALUE_ENCHANT](../bonus/Bonus_Types.md#special_add_value_enchant): value from addInfo is added to val of bonus
+- [SPECIAL_FIXED_VALUE_ENCHANT](../bonus/Bonus_Types.md#special_fixed_value_enchant): value from addInfo replaces val of bonus
+
+```json
+"firstSpellEffect" : {
+	"type": "core:timed",
+
+	// if set to true, recasting same spell will accumulate (and prolong) effects of previous spellcast
+	"cumulative" : false
+	
+	// List of bonuses granted by this spell
+	"bonus" : {
+		"firstBonus" : {[bonus format]}
+		//...
+	}
+}
+```
+
+## Target types
+
+### CREATURE
 
-- CREATURE target (only battle spells)
 - range 0: smart assumed single creature target
 - range "X" + smart modifier = enchanter casting, expert massive spells
 - range "X" + no smart modifier = armageddon, death ripple, destroy undead
@@ -396,14 +658,17 @@ TODO
 - smart modifier: smth like cloud of confusion in H4 (if I remember correctly :) )
 - no smart modifier: like inferno, fireball etc. but target only creature
 
-- NO_TARGET
+### NO_TARGET
+
 - no target selection,(abilities, most adventure spells)
 
-- LOCATION
+### LOCATION
+
 - any tile on map/battlefield (inferno, fireball etc.), DD also here but with special handling
 - clearTarget - destination hex must be clear (unused so far)
 - clearAfffected - all affected hexes must be clear (forceField, fireWall)
 
-- OBSTACLE target
+### OBSTACLE
+
 - range 0: any single obstacle
 - range X: all obstacles

+ 16 - 0
docs/modders/Entities_Format/Spell_School_Format.md

@@ -0,0 +1,16 @@
+# Spell School Format
+
+WARNING: currently custom spell schools are only partially supported:
+
+- it is possible to use custom spell schools in bonus system
+- it is possible to make skill for specializing in such spell
+- it is possible to specify border decorations for mastery level of such spell in spellbook
+- it is NOT possible to add "bookmark" filter for spellbook for spells of such school
+
+```json
+	// Internal field for H3 schools. Do not use for mods
+	"index" : "",
+	
+	// animation file with spell borders for spell mastery levels
+	"schoolBorders" : "SplevA"
+```

+ 5 - 10
docs/modders/Entities_Format/Town_Building_Format.md

@@ -150,16 +150,7 @@ These are just a couple of examples of what can be done in VCMI. See vcmi config
 	// Generally only needs to be specified for "special" buildings
 	// See 'List of unique town buildings' section below for detailed description of this field
 	"type" : "",
-	
-	// If set, building will have Lookout Tower logic - extend sight radius of a town.
-	// Possible values: 
-	// low - increases town sight radius by 5 tiles
-	// average - sight radius extended by 15 tiles
-	// high - sight radius extended by 20 tiles
-	// skyship - entire map will be revealed
-	// If not set, building will not affect sight radius of a town
-	"height" : "average"
-	
+
 	// Resources produced each day by this building
 	"produce" : { 
 		"sulfur" : 1,
@@ -213,6 +204,10 @@ These are just a couple of examples of what can be done in VCMI. See vcmi config
 	
 	// If the building is a market, it requires market mode.
 	"marketModes" : [ "resource-resource", "resource-player" ],
+	
+	// Required if building offers resource-skill trade.
+	// NOTE: multiple resource-skill buildings in the same town are not supported
+	"marketOffer" : [ "fireMagic", "airMagic", "waterMagic", "earthMagic" ],
 }
 ```
 

+ 208 - 0
docs/modders/Guides/Bonus_System.md

@@ -0,0 +1,208 @@
+# Bonus System Guide
+
+Bonuses are effects that can be given to various game entities. A lot of game mechanics in VCMI are implemented as bonuses. Most notably, but not limited to:
+
+- All artifact effects
+- All hero specialties
+- All secondary skill effects
+- All creature abilities
+- Large number of spells
+- Some of town building
+
+While they don't provide same level of flexibility as ERM scripting from WoG, they are way easier to use and generally can be undestood by AI. List of supported effects is rather long, and covers all H3 mechanics, as well as some additions to support WoG creature abilities, HotA, and extensions requested by modders for VCMI.
+
+## Basic Usage
+
+See also: [List of Bonus Types](../Bonus/Bonus_Types.md)
+
+### Bonuses without parameters
+
+Some of the simplest bonuses don't require any parameters, so all you need to do is specify the bonus type:
+
+```json
+"bonuses" : {
+	"noPenalty" : {
+		"type" : "NO_DISTANCE_PENALTY"
+	}
+}
+```
+
+With this bonus, all ranged units in the army of the hero will not have a distance penalty when firing at distances larger than 10 hexes.
+
+### Bonuses with value
+
+Although bonuses without parameters do exist, the majority of bonuses require some configuration to suit your needs. For example:
+
+```json
+"bonuses" : {
+	"scouting" : {
+		"type" : "SIGHT_RADIUS",
+		"val" : 3
+	}
+}
+```
+
+This bonus increases the hero's sight (scouting radius) by three adventure map tiles. If the hero has multiple sources of this bonus (such as a secondary skill, specialty or other artifacts) all the bonuses will stack and the hero's actual scouting range will be equal to the sum of the values of all the bonuses.
+
+### Bonuses with subtypes
+
+In addition to value, many bonuses support so-called 'subtypes', which allow you to specify exactly what should be affected by the bonus. For example:
+
+```json
+"bonuses" : {
+	"noSandPenalty" : {
+		"type" : "NO_TERRAIN_PENALTY",
+		"subtype" : "sand"
+	}
+}
+```
+
+This bonus would eliminate the terrain penalty for your army when moving across sand terrain. Such subtypes can also be used to target objects added by mods without the need for additional bonus types in the game engine.
+
+It is also possible, and in fact required for many bonuses, to use both subtypes with a value. In this scenario, only bonuses of the same type and subtype will stack:
+
+```json
+"bonuses" : {
+	"attack" : {
+		"type" : "PRIMARY_SKILL",
+		"subtype" : "attack",
+		"val" : 3
+	}
+}
+```
+
+### Bonuses with additional info
+
+In addition to the `type`, `subtype` and `val` parameters, some bonuses may require or support an additional parameter called 'addInfo'. This is used by some bonuses to provide additional parameters that are not suitable for subtypes or values. For example:
+
+```json
+"bonuses" : {
+	"upgradeMages" : {
+		"type" : "SPECIAL_UPGRADE",
+		"subtype" : "creature.mage",
+		"addInfo" : "creature.enchanter"
+	}
+}
+```
+
+This bonus allows a hero with such an artefact to upgrade any mage in their army to an enchanter. For information on how to configure addInfo for a particular bonus, please refer to the [bonus types documentation](../Bonus/Bonus_Types.md).
+
+## Advanced Usage
+
+### Bonus Limiters
+
+Generally, a bonus affects the entity that has the bonus, as well as all entities located 'below' (or 'inside') the affected entity.
+
+For example, a bonus given to a player would affect all their heroes, towns and other owned objects, as well as their armies. However, this is undesirable in some scenarios. Heroes specialising in a specific creature, for instance, should only affect that creature and not their entire army. To support such a scenario, it is possible to use 'limiters', which enable bonuses to be applied only to certain affected entities:
+
+```json
+"specialty" : {
+	"bonuses" : {
+		"attack" : {
+			"type" : "PRIMARY_SKILL",
+			"subtype" : "primarySkill.attack"
+			"val" : 3
+			"limiters" : {
+				// Type of limiter. See bonus system reference for details
+				"type" : "CREATURE_TYPE_LIMITER", 
+				// Type-specific parameters of the limiter
+				"parameters" : [
+					"pixie", // affected unit
+					true // whether upgrades of affected unit should also be affected
+				],
+			}
+		}
+	}
+}
+```
+
+This speciality increases the attack of all Pixies in the army by 3, but does not affect any other units or the hero himself. The game supports multiple other limiters for various other use cases. Please refer to the [bonus limiters documentation](../Bonus/Bonus_Limiters.md). for details.
+
+### Bonus Propagators
+
+In some cases, it is preferable to extend the effect of bonuses instead. A typical example is a creature ability that affects the entire battlefield. For example, Angels increase the morale of all units in their hero's army. However, simply giving the Angels a morale bonus would only affect the Angels themselves. In order to affect all units in the army, such an ability would require the bonus to be 'propagated' upwards (i.e. outside of the affected entity). For such scenarios, it is possible to use `propagator`:
+
+```json
+"abilities":
+{
+	"raisesMorale" : {
+		"type" : "MORALE",
+		"val" : 1,
+		"propagator" : "HERO",
+	}
+}
+```
+
+This propagator extends the ability to all units in the hero's army, including the unit from which it originates. It is possible to propagate the bonus to most entities that form part of the bonus system. Please refer to the [bonus propagators documentation](../Bonus/Bonus_Propagators.md) for details.
+
+### Bonus Updaters
+
+Unlike propagators and limiters, updaters do not modify the entities affected by the bonus; instead, they modify the bonus itself. This is primarily used for H3 hero specialties, which are often scaled according to the level of the hero or the level of the affected unit. However, it is possible to use updaters in other areas if desired. Example:
+
+```json
+"specialty" : {
+	"bonuses" : {
+		"attack" : {
+			"type" : "PRIMARY_SKILL",
+			"subtype" : "primarySkill.attack",
+			"val" : 1,
+			"updater" : "TIMES_HERO_LEVEL"
+		}
+	}
+}
+```
+
+This speciality increases the hero's attack by 1, multiplied by their level. For example, a level 20 hero would have an attack of +20.
+
+Full list of supported bonus updaters can be found in [bonus updaters documentation](../Bonus/Bonus_Updaters.md)
+
+### Only enemy side bonus
+
+When creating a battle-wide bonus, you can use the 'BATTLE_WIDE' propagator to achieve the desired effect. Similarly, when creating a bonus that only affects allied units, use the 'HERO' propagator instead. However, due to the implementation details of the game's bonus system, bonuses that only affect the enemy side require specific configuration.
+
+For example, to implement the morale-reducing ability of Ghost Dragons, you can use the following form:
+
+```json
+"abilities":
+{
+	"decreaseMorale" : {
+		"type" : "MORALE",
+		"val" : -1,
+		"propagator": "BATTLE_WIDE",
+		"propagationUpdater" : "BONUS_OWNER_UPDATER",
+		"limiters" : [ "OPPOSITE_SIDE" ]
+	}
+}
+```
+
+As can be seen from the example, such bonuses must perform the following operations to work:
+
+- The `BATTLE_WIDE` propagator extends the effect of the bonus to the entire battlefield.
+- a `BONUS_OWNER_UPDATER` propagation updater – to indicate which side of the battlefield the bonus originates from
+- an `OPPOSITE_SIDE` limiter to restrict the bonus to units (or heroes) belonging to the other side of the battle.
+
+## Expert Usage
+
+### Full Bonus Tree Layout
+
+As mentioned in previous parts, the propagator allows bonuses to be propagated 'upwards', and bonuses only affect entities 'downwards' by default. Generally, it is clear which entities lie 'upwards' or 'downwards' – for example, creatures belong to an army, which belongs to a hero, who belongs to a player. Some cases might not be so clear, but you can consult the diagram below for help.
+
+In this diagram, all entities connected to an entity above it are considered to be 'below', and vice versa:
+
+![Bonus System Nodes Diagram](../../images/Bonus_System_Nodes.svg)
+
+### Combining updaters, propagators and limiters
+
+When the game evaluates bonuses, the following order of operations is performed:
+
+- If the bonus has a propagator, the game will attempt to look upwards through the bonus tree to find the entity to which the bonus should be propagated.
+- If such an entity is found and the bonus has a propagation updater, the updater is executed using the context of the bonus source.
+- The bonus is then moved to the entity to which it was propagated.
+- The game then collects all bonuses located upwards from the entity for which the bonus is being evaluated. Each time a bonus with an updater passes through a node, the updater is applied to the bonus using the context of the entity it passes through, including the original entity that holds the bonus and the current entity.
+- Once all bonuses have been collected, the game executes the limiter on each bonus and drops the bonus on a negative result.
+
+As a result, there are some considerations you should bear in mind.
+
+- The bonus updater is executed on every entity between the bonus source (or the bonus propagation target if a propagator is used). For example, a bonus propagated to a hero from a creature can use updaters that require either the hero or the creature.
+- The bonus propagation updater, however, can only be used with updaters that require a creature as context in the case of a creature ability.
+- A bonus limiter can only be used on the final entity through which the game accesses the bonus system for this particular bonus. For example, the SIGHT_RADIUS bonus is checked from the hero's perspective and can only be used with limiters that are valid for heroes.

+ 10 - 1
docs/modders/Mod_File_Format.md

@@ -119,7 +119,16 @@ These are fields that are present only in local mod.json file
 	],
 	
 	// List of configuration files for skills
-	skills
+	"skills" :
+	[
+		"config/skills.json"
+	],
+
+	// List of configuration files for campaign regions for h3c campaigns
+	"campaignRegions" :
+	[
+		"config/campaignRegions.json"
+	],
 
 	// list of creature configuration files
 	"creatures" :

+ 2 - 0
include/vcmi/FactionMember.h

@@ -61,6 +61,8 @@ public:
 	*/
 	int moraleValAndBonusList(std::shared_ptr<const BonusList> & bonusList) const;
 	int luckValAndBonusList(std::shared_ptr<const BonusList> & bonusList) const;
+
+	bool unaffectedByMorale() const;
 };
 
 VCMI_LIB_NAMESPACE_END

+ 11 - 14
lib/BasicTypes.cpp

@@ -69,6 +69,15 @@ int AFactionMember::getMaxDamage(bool ranged) const
 	return getBonusBearer()->valOfBonuses(selector, cachingStr);
 }
 
+bool AFactionMember::unaffectedByMorale() const
+{
+	static const auto unaffectedByMoraleSelector = Selector::type()(BonusType::NON_LIVING).Or(Selector::type()(BonusType::MECHANICAL)).Or(Selector::type()(BonusType::UNDEAD))
+													   .Or(Selector::type()(BonusType::SIEGE_WEAPON)).Or(Selector::type()(BonusType::NO_MORALE));
+
+	static const std::string cachingStrUn = "AFactionMember::unaffectedByMoraleSelector";
+	return getBonusBearer()->hasBonus(unaffectedByMoraleSelector, cachingStrUn);
+}
+
 int AFactionMember::moraleValAndBonusList(TConstBonusListPtr & bonusList) const
 {
 	int32_t maxGoodMorale = LIBRARY->engineSettings()->getVector(EGameSettings::COMBAT_GOOD_MORALE_CHANCE).size();
@@ -81,12 +90,7 @@ int AFactionMember::moraleValAndBonusList(TConstBonusListPtr & bonusList) const
 		return maxGoodMorale;
 	}
 
-	static const auto unaffectedByMoraleSelector = Selector::type()(BonusType::NON_LIVING).Or(Selector::type()(BonusType::MECHANICAL)).Or(Selector::type()(BonusType::UNDEAD))
-													.Or(Selector::type()(BonusType::SIEGE_WEAPON)).Or(Selector::type()(BonusType::NO_MORALE));
-
-	static const std::string cachingStrUn = "AFactionMember::unaffectedByMoraleSelector";
-	auto unaffected = getBonusBearer()->hasBonus(unaffectedByMoraleSelector, cachingStrUn);
-	if(unaffected)
+	if(unaffectedByMorale())
 	{
 		if(bonusList && !bonusList->empty())
 			bonusList = std::make_shared<const BonusList>();
@@ -186,14 +190,7 @@ ui32 ACreature::getMovementRange(int turn) const
 
 bool ACreature::isLiving() const //TODO: theoreticaly there exists "LIVING" bonus in stack experience documentation
 {
-	static const std::string cachingStr = "ACreature::isLiving";
-	static const CSelector selector = Selector::type()(BonusType::UNDEAD)
-		.Or(Selector::type()(BonusType::NON_LIVING))
-		.Or(Selector::type()(BonusType::MECHANICAL))
-		.Or(Selector::type()(BonusType::GARGOYLE))
-		.Or(Selector::type()(BonusType::SIEGE_WEAPON));
-
-	return !getBonusBearer()->hasBonus(selector, cachingStr);
+	return getBonusBearer()->hasBonusOfType(BonusType::LIVING);
 }
 
 

+ 42 - 22
lib/CBonusTypeHandler.cpp

@@ -24,16 +24,10 @@
 #include "texts/CGeneralTextHandler.h"
 #include "json/JsonUtils.h"
 
-template class std::vector<VCMI_LIB_WRAP_NAMESPACE(CBonusType)>;
-
 VCMI_LIB_NAMESPACE_BEGIN
 
 ///CBonusType
 
-CBonusType::CBonusType():
-	hidden(true)
-{}
-
 std::string CBonusType::getDescriptionTextID() const
 {
 	return TextIdentifier( "core", "bonus", identifier, "description").get();
@@ -45,21 +39,24 @@ CBonusTypeHandler::CBonusTypeHandler()
 {
 	//register predefined bonus types
 
-	#define BONUS_NAME(x) \
-	do { \
-		bonusTypes.push_back(CBonusType()); \
-	} while(0);
+#define BONUS_NAME(x) { #x },
+	builtinBonusNames = {
+		BONUS_LIST
+	};
+#undef BONUS_NAME
 
+	for (int i = 0; i < builtinBonusNames.size(); ++i)
+		bonusTypes.push_back(std::make_shared<CBonusType>());
 
-	BONUS_LIST;
-	#undef BONUS_NAME
+	for (int i = 0; i < builtinBonusNames.size(); ++i)
+		registerObject(ModScope::scopeBuiltin(), "bonus", builtinBonusNames[i], i);
 }
 
 CBonusTypeHandler::~CBonusTypeHandler() = default;
 
 std::string CBonusTypeHandler::bonusToString(const std::shared_ptr<Bonus> & bonus, const IBonusBearer * bearer) const
 {
-	const CBonusType & bt = bonusTypes[vstd::to_underlying(bonus->type)];
+	const CBonusType & bt = *bonusTypes.at(vstd::to_underlying(bonus->type));
 	int bonusValue = bearer->valOfBonuses(bonus->type, bonus->subtype);
 	if(bt.hidden)
 		return "";
@@ -93,7 +90,7 @@ std::string CBonusTypeHandler::bonusToString(const std::shared_ptr<Bonus> & bonu
 
 ImagePath CBonusTypeHandler::bonusToGraphics(const std::shared_ptr<Bonus> & bonus) const
 {
-	const CBonusType & bt = bonusTypes[vstd::to_underlying(bonus->type)];
+	const CBonusType & bt = *bonusTypes.at(vstd::to_underlying(bonus->type));
 
 	if (bonus->type == BonusType::SPELL_IMMUNITY && bonus->subtype.as<SpellID>().hasValue())
 	{
@@ -117,18 +114,21 @@ std::vector<JsonNode> CBonusTypeHandler::loadLegacyData()
 
 void CBonusTypeHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
 {
-	auto it = bonusNameMap.find(name);
-
-	if(it == bonusNameMap.end())
+	if (vstd::contains(builtinBonusNames, name))
 	{
-		logBonus->warn("Unrecognized bonus name! (%s)", name);
+		//h3 bonus
+		BonusType bonus = static_cast<BonusType>(vstd::find_pos(builtinBonusNames, name));
+		CBonusType & bt =*bonusTypes.at(vstd::to_underlying(bonus));
+		loadItem(data, bt, name);
+		logBonus->trace("Loaded bonus type %s", name);
 	}
 	else
 	{
-		CBonusType & bt = bonusTypes[vstd::to_underlying(it->second)];
-
-		loadItem(data, bt, name);
-		logBonus->trace("Loaded bonus type %s", name);
+		// new bonus
+		registerObject(scope, "bonus", name, bonusTypes.size());
+		bonusTypes.push_back(std::make_shared<CBonusType>());
+		loadItem(data, *bonusTypes.back(), name);
+		logBonus->trace("New bonus type %s", name);
 	}
 }
 
@@ -141,6 +141,7 @@ void CBonusTypeHandler::loadItem(const JsonNode & source, CBonusType & dest, con
 {
 	dest.identifier = name;
 	dest.hidden = source["hidden"].Bool(); //Null -> false
+	dest.creatureNature = source["creatureNature"].Bool(); //Null -> false
 
 	if (!dest.hidden)
 		LIBRARY->generaltexth->registerString( "vcmi", dest.getDescriptionTextID(), source["description"]);
@@ -185,4 +186,23 @@ void CBonusTypeHandler::loadItem(const JsonNode & source, CBonusType & dest, con
 	}
 }
 
+const std::string & CBonusTypeHandler::bonusToString(BonusType bonus) const
+{
+	return bonusTypes.at(static_cast<int>(bonus))->identifier;
+}
+
+bool CBonusTypeHandler::isCreatureNatureBonus(BonusType bonus) const
+{
+	return bonusTypes.at(static_cast<int>(bonus))->creatureNature;
+}
+
+std::vector<BonusType> CBonusTypeHandler::getAllObjets() const
+{
+	std::vector<BonusType> ret;
+	for (int i = 0; i < bonusTypes.size(); ++i)
+		ret.push_back(static_cast<BonusType>(i));
+
+	return ret;
+}
+
 VCMI_LIB_NAMESPACE_END

+ 11 - 4
lib/CBonusTypeHandler.h

@@ -19,10 +19,10 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 class JsonNode;
 
-class DLL_LINKAGE CBonusType
+class DLL_LINKAGE CBonusType : boost::noncopyable
 {
 public:
-	CBonusType();
+	CBonusType() = default;
 
 	std::string getDescriptionTextID() const;
 
@@ -36,11 +36,13 @@ private:
 	std::map<int, std::string> valueDescriptions;
 	std::string identifier;
 
-	bool hidden;
+	bool creatureNature = false;
+	bool hidden = true;
 };
 
 class DLL_LINKAGE CBonusTypeHandler : public IBonusTypeHandler
 {
+	std::vector<std::string> builtinBonusNames;
 public:
 	CBonusTypeHandler();
 	virtual ~CBonusTypeHandler();
@@ -52,10 +54,15 @@ public:
 	void loadObject(std::string scope, std::string name, const JsonNode & data) override;
 	void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override;
 
+	const std::string & bonusToString(BonusType bonus) const;
+
+	bool isCreatureNatureBonus(BonusType bonus) const;
+
+	std::vector<BonusType> getAllObjets() const;
 private:
 	void loadItem(const JsonNode & source, CBonusType & dest, const std::string & name) const;
 
-	std::vector<CBonusType> bonusTypes; //index = BonusType
+	std::vector<std::shared_ptr<CBonusType> > bonusTypes; //index = BonusType
 };
 
 VCMI_LIB_NAMESPACE_END

+ 23 - 33
lib/CCreatureHandler.cpp

@@ -10,6 +10,7 @@
 #include "StdInc.h"
 #include "CCreatureHandler.h"
 
+#include "CBonusTypeHandler.h"
 #include "ResourceSet.h"
 #include "entities/faction/CFaction.h"
 #include "entities/faction/CTownHandler.h"
@@ -902,37 +903,15 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c
 {
 	creature->animDefName = AnimationPath::fromJson(config["graphics"]["animation"]);
 
-	//FIXME: MOD COMPATIBILITY
-	if (config["abilities"].getType() == JsonNode::JsonType::DATA_STRUCT)
+	for(const auto & ability : config["abilities"].Struct())
 	{
-		for(const auto & ability : config["abilities"].Struct())
+		if (!ability.second.isNull())
 		{
-			if (!ability.second.isNull())
-			{
-				auto b = JsonUtils::parseBonus(ability.second, creature->getBonusTextID(ability.first));
-				b->source = BonusSource::CREATURE_ABILITY;
-				b->sid = BonusSourceID(creature->getId());
-				b->duration = BonusDuration::PERMANENT;
-				creature->addNewBonus(b);
-			}
-		}
-	}
-	else
-	{
-		for(const JsonNode &ability : config["abilities"].Vector())
-		{
-			if(ability.getType() == JsonNode::JsonType::DATA_VECTOR)
-			{
-				logMod->error("Ignored outdated creature ability format in %s", creature->getJsonKey());
-			}
-			else
-			{
-				auto b = JsonUtils::parseBonus(ability);
-				b->source = BonusSource::CREATURE_ABILITY;
-				b->sid = BonusSourceID(creature->getId());
-				b->duration = BonusDuration::PERMANENT;
-				creature->addNewBonus(b);
-			}
+			auto b = JsonUtils::parseBonus(ability.second, creature->getBonusTextID(ability.first));
+			b->source = BonusSource::CREATURE_ABILITY;
+			b->sid = BonusSourceID(creature->getId());
+			b->duration = BonusDuration::PERMANENT;
+			creature->addNewBonus(b);
 		}
 	}
 
@@ -1000,7 +979,7 @@ void CCreatureHandler::loadStackExperience(CCreature * creature, const JsonNode
 					auto bonus = JsonUtils::parseBonus (exp["bonus"]);
 					bonus->source = BonusSource::STACK_EXPERIENCE;
 					bonus->duration = BonusDuration::PERMANENT;
-					bonus->limiter = std::make_shared<RankRangeLimiter>(RankRangeLimiter(lowerLimit));
+					bonus->addLimiter(std::make_shared<RankRangeLimiter>(lowerLimit));
 					creature->addNewBonus (bonus);
 					break; //TODO: allow bonuses to turn off?
 				}
@@ -1020,7 +999,7 @@ void CCreatureHandler::loadStackExperience(CCreature * creature, const JsonNode
 					auto bonus = JsonUtils::parseBonus (bonusInput);
 					bonus->source = BonusSource::STACK_EXPERIENCE;
 					bonus->duration = BonusDuration::PERMANENT;
-					bonus->limiter.reset (new RankRangeLimiter(lowerLimit));
+					bonus->addLimiter(std::make_shared<RankRangeLimiter>(lowerLimit));
 					creature->addNewBonus (bonus);
 				}
 				lastVal = static_cast<int>(val.Float());
@@ -1076,7 +1055,7 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigPars
 		b.subtype = BonusCustomSubtype::deathStareGorgon;
 		break;
 	case 'F':
-		b.type = BonusType::FEAR; break;
+		b.type = BonusType::FEARFUL; break;
 	case 'g':
 		b.type = BonusType::SPELL_DAMAGE_REDUCTION;
 		b.subtype = BonusSubtypeID(SpellSchool::ANY);
@@ -1105,7 +1084,7 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigPars
 			case 'D':
 				b.type = BonusType::ADDITIONAL_ATTACK; break;
 			case 'f':
-				b.type = BonusType::FEARLESS; break;
+				b.type = BonusType::FEARFUL; break;
 			case 'F':
 				b.type = BonusType::FLYING; break;
 			case 'm':
@@ -1364,7 +1343,18 @@ CCreatureHandler::~CCreatureHandler()
 
 void CCreatureHandler::afterLoadFinalization()
 {
+	for(auto & creature : objects)
+	{
+		if (!creature)
+			continue;
+
+		auto natureBonuses = creature->getBonuses([](const Bonus * b){
+				return LIBRARY->bth->isCreatureNatureBonus(b->type);
+		});
 
+		if (natureBonuses->empty())
+			creature->addNewBonus(std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::LIVING, BonusSource::CREATURE_ABILITY, 0, BonusSourceID(creature->getId())));
+	}
 }
 
 std::set<CreatureID> CCreatureHandler::getDefaultAllowed() const

+ 15 - 6
lib/CMakeLists.txt

@@ -69,7 +69,6 @@ set(lib_MAIN_SRCS
 	bonuses/BonusCache.cpp
 	bonuses/BonusEnum.cpp
 	bonuses/BonusList.cpp
-	bonuses/BonusParams.cpp
 	bonuses/BonusSelector.cpp
 	bonuses/BonusCustomTypes.cpp
 	bonuses/CBonusSystemNode.cpp
@@ -86,9 +85,14 @@ set(lib_MAIN_SRCS
 	callback/CNonConstInfoCallback.cpp
 	callback/CPlayerSpecificInfoCallback.cpp
 	callback/GameRandomizer.cpp
+	callback/MapInfoCallback.cpp
+	callback/EditorCallback.cpp
 
+	campaign/CampaignBonus.cpp
 	campaign/CampaignHandler.cpp
 	campaign/CampaignState.cpp
+	campaign/CampaignRegions.cpp
+	campaign/CampaignRegionsHandler.cpp
 
 	constants/EntityIdentifiers.cpp
 
@@ -100,7 +104,6 @@ set(lib_MAIN_SRCS
 	entities/artifact/CArtifactInstance.cpp
 	entities/artifact/CArtifactSet.cpp
 	entities/building/CBuilding.cpp
-	entities/building/CBuildingHandler.cpp
 	entities/faction/CFaction.cpp
 	entities/faction/CTown.cpp
 	entities/faction/CTownHandler.cpp
@@ -172,6 +175,7 @@ set(lib_MAIN_SRCS
 	mapping/MapFormatH3M.cpp
 	mapping/MapReaderH3M.cpp
 	mapping/MapFormatJson.cpp
+	mapping/MapFormatSettings.cpp
 	mapping/ObstacleProxy.cpp
 
 	modding/ActiveModsInSaveList.cpp
@@ -256,6 +260,7 @@ set(lib_MAIN_SRCS
 	spells/ObstacleCasterProxy.cpp
 	spells/Problem.cpp
 	spells/ProxyCaster.cpp
+	spells/SpellSchoolHandler.cpp
 	spells/TargetCondition.cpp
 	spells/ViewSpellInt.cpp
 
@@ -450,7 +455,6 @@ set(lib_MAIN_HEADERS
 	bonuses/BonusCache.h
 	bonuses/BonusEnum.h
 	bonuses/BonusList.h
-	bonuses/BonusParams.h
 	bonuses/BonusSelector.h
 	bonuses/BonusCustomTypes.h
 	bonuses/CBonusSystemNode.h
@@ -480,10 +484,14 @@ set(lib_MAIN_HEADERS
 	callback/IGameInfoCallback.h
 	callback/IGameRandomizer.h
 	callback/GameRandomizer.h
-
-
+	callback/MapInfoCallback.h
+	callback/EditorCallback.h
+	
+	campaign/CampaignBonus.h
 	campaign/CampaignConstants.h
 	campaign/CampaignHandler.h
+	campaign/CampaignRegions.h
+	campaign/CampaignRegionsHandler.h
 	campaign/CampaignScenarioPrologEpilog.h
 	campaign/CampaignState.h
 
@@ -504,7 +512,6 @@ set(lib_MAIN_HEADERS
 	entities/artifact/CArtifactSet.h
 	entities/artifact/EArtifactClass.h
 	entities/building/CBuilding.h
-	entities/building/CBuildingHandler.h
 	entities/building/TownFortifications.h
 	entities/faction/CFaction.h
 	entities/faction/CTown.h
@@ -587,6 +594,7 @@ set(lib_MAIN_HEADERS
 	mapping/MapFeaturesH3M.h
 	mapping/MapFormatH3M.h
 	mapping/MapFormat.h
+	mapping/MapFormatSettings.h
 	mapping/MapReaderH3M.h
 	mapping/MapFormatJson.h
 	mapping/ObstacleProxy.h
@@ -701,6 +709,7 @@ set(lib_MAIN_HEADERS
 	spells/ObstacleCasterProxy.h
 	spells/Problem.h
 	spells/ProxyCaster.h
+	spells/SpellSchoolHandler.h
 	spells/TargetCondition.h
 	spells/ViewSpellInt.h
 

+ 9 - 6
lib/CSkillHandler.cpp

@@ -9,20 +9,19 @@
  */
 
 #include "StdInc.h"
+#include "CSkillHandler.h"
 
 #include <cctype>
 
-#include "CSkillHandler.h"
-
+#include "GameLibrary.h"
 #include "bonuses/Updaters.h"
 #include "constants/StringConstants.h"
 #include "filesystem/Filesystem.h"
-#include "json/JsonBonus.h"
-#include "json/JsonUtils.h"
 #include "modding/IdentifierStorage.h"
 #include "texts/CGeneralTextHandler.h"
 #include "texts/CLegacyConfigParser.h"
-#include "GameLibrary.h"
+#include "json/JsonBonus.h"
+#include "json/JsonUtils.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -250,6 +249,10 @@ std::shared_ptr<CSkill> CSkillHandler::loadFromJson(const std::string & scope, c
 		skillAtLevel.iconSmall = levelNode["images"]["small"].String();
 		skillAtLevel.iconMedium = levelNode["images"]["medium"].String();
 		skillAtLevel.iconLarge = levelNode["images"]["large"].String();
+		if (!levelNode["images"]["scenarioBonus"].isNull())
+			skillAtLevel.scenarioBonus = levelNode["images"]["scenarioBonus"].String();
+		else
+			skillAtLevel.scenarioBonus = skillAtLevel.iconMedium; // MOD COMPATIBILITY fallback for pre-1.7 mods
 	}
 
 	for(const auto & b : json["specialty"].Vector())
@@ -259,7 +262,7 @@ std::shared_ptr<CSkill> CSkillHandler::loadFromJson(const std::string & scope, c
 		if (bonusNode.isStruct())
 		{
 			auto bonus = JsonUtils::parseBonus(bonusNode);
-			bonus->val = 5; // default H3 value, hardcoded for now
+			bonus->val = 0; // set by HeroHandler on specialty load
 			bonus->updater = std::make_shared<TimesHeroLevelUpdater>();
 			bonus->valType = BonusValueType::PERCENT_TO_TARGET_TYPE;
 			bonus->targetSourceType = BonusSource::SECONDARY_SKILL;

+ 1 - 0
lib/CSkillHandler.h

@@ -28,6 +28,7 @@ public:
 		std::string iconSmall;
 		std::string iconMedium;
 		std::string iconLarge;
+		std::string scenarioBonus;
 		std::vector<std::shared_ptr<Bonus>> effects;
 	};
 

+ 7 - 0
lib/GameLibrary.cpp

@@ -18,6 +18,7 @@
 #include "RiverHandler.h"
 #include "TerrainHandler.h"
 #include "spells/CSpellHandler.h"
+#include "spells/SpellSchoolHandler.h"
 #include "spells/effects/Registry.h"
 #include "CSkillHandler.h"
 #include "entities/artifact/CArtHandler.h"
@@ -25,6 +26,8 @@
 #include "entities/hero/CHeroClassHandler.h"
 #include "entities/hero/CHeroHandler.h"
 #include "texts/CGeneralTextHandler.h"
+#include "campaign/CampaignRegionsHandler.h"
+#include "mapping/MapFormatSettings.h"
 #include "modding/CModHandler.h"
 #include "modding/IdentifierStorage.h"
 #include "modding/CModVersion.h"
@@ -178,9 +181,11 @@ void GameLibrary::initializeLibrary()
 	createHandler(biomeHandler);
 	createHandler(objh);
 	createHandler(objtypeh);
+	createHandler(spellSchoolHandler);
 	createHandler(spellh);
 	createHandler(skillh);
 	createHandler(terviewh);
+	createHandler(campaignRegions);
 	createHandler(tplh); //templates need already resolved identifiers (refactor?)
 #if SCRIPTING_ENABLED
 	createHandler(scriptHandler);
@@ -190,6 +195,8 @@ void GameLibrary::initializeLibrary()
 
 	modh->load();
 	modh->afterLoad();
+
+	createHandler(mapFormat);
 }
 
 #if SCRIPTING_ENABLED

+ 6 - 0
lib/GameLibrary.h

@@ -40,6 +40,9 @@ class IHandlerBase;
 class IGameSettings;
 class GameSettings;
 class CIdentifierStorage;
+class SpellSchoolHandler;
+class MapFormatSettings;
+class CampaignRegionsHandler;
 
 #if SCRIPTING_ENABLED
 namespace scripting
@@ -78,6 +81,7 @@ public:
 	std::unique_ptr<CHeroClassHandler> heroclassesh;
 	std::unique_ptr<CCreatureHandler> creh;
 	std::unique_ptr<CSpellHandler> spellh;
+	std::unique_ptr<SpellSchoolHandler> spellSchoolHandler;
 	std::unique_ptr<CSkillHandler> skillh;
 	// TODO: Remove ObjectHandler altogether?
 	std::unique_ptr<CObjectHandler> objh;
@@ -95,6 +99,8 @@ public:
 	std::unique_ptr<ObstacleHandler> obstacleHandler;
 	std::unique_ptr<GameSettings> settingsHandler;
 	std::unique_ptr<ObstacleSetHandler> biomeHandler;
+	std::unique_ptr<MapFormatSettings> mapFormat;
+	std::unique_ptr<CampaignRegionsHandler> campaignRegions;
 
 #if SCRIPTING_ENABLED
 	std::unique_ptr<scripting::ScriptHandler> scriptHandler;

+ 4 - 0
lib/GameSettings.cpp

@@ -77,6 +77,7 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
 		{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_OWNED,                  "dwellings", "accumulateWhenOwned"                  },
 		{EGameSettings::DWELLINGS_MERGE_ON_RECRUIT,                       "dwellings", "mergeOnRecruit"                       },
 		{EGameSettings::HEROES_BACKPACK_CAP,                              "heroes",    "backpackSize"                         },
+		{EGameSettings::HEROES_BASE_SCOUNTING_RANGE,                      "heroes",    "baseScoutingRange"                    },
 		{EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS,                    "heroes",    "minimalPrimarySkills"                 },
 		{EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP,                     "heroes",    "perPlayerOnMapCap"                    },
 		{EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP,                      "heroes",    "perPlayerTotalCap"                    },
@@ -86,6 +87,8 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
 		{EGameSettings::HEROES_MOVEMENT_COST_BASE,                        "heroes",    "movementCostBase"                     },
 		{EGameSettings::HEROES_MOVEMENT_POINTS_LAND,                      "heroes",    "movementPointsLand"                   },
 		{EGameSettings::HEROES_MOVEMENT_POINTS_SEA,                       "heroes",    "movementPointsSea"                    },
+		{EGameSettings::HEROES_SPECIALTY_CREATURE_GROWTH,                 "heroes",    "specialtyCreatureGrowth"              },
+		{EGameSettings::HEROES_SPECIALTY_SECONDARY_SKILL_GROWTH,          "heroes",    "specialtySecondarySkillGrowth"        },
 		{EGameSettings::MAP_FORMAT_ARMAGEDDONS_BLADE,                     "mapFormat", "armageddonsBlade"                     },
 		{EGameSettings::MAP_FORMAT_CHRONICLES,                            "mapFormat", "chronicles"                           },
 		{EGameSettings::MAP_FORMAT_HORN_OF_THE_ABYSS,                     "mapFormat", "hornOfTheAbyss"                       },
@@ -116,6 +119,7 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
 		{EGameSettings::TEXTS_ROAD,                                       "textData",  "road"                                 },
 		{EGameSettings::TEXTS_SPELL,                                      "textData",  "spell"                                },
 		{EGameSettings::TEXTS_TERRAIN,                                    "textData",  "terrain"                              },
+		{EGameSettings::TOWNS_BASE_SCOUNTING_RANGE,                       "towns",     "baseScoutingRange"                    },
 		{EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP,                     "towns",     "buildingsPerTurnCap"                  },
 		{EGameSettings::TOWNS_STARTING_DWELLING_CHANCES,                  "towns",     "startingDwellingChances"              },
 		{EGameSettings::TOWNS_SPELL_RESEARCH,                             "towns",     "spellResearch"                        },

Деякі файли не було показано, через те що забагато файлів було змінено