diff --git a/config/heroes/castle.json b/config/heroes/castle.json index 151156f37..ac013f35d 100644 --- a/config/heroes/castle.json +++ b/config/heroes/castle.json @@ -10,16 +10,7 @@ { "skill" : "archery", "level": "basic" } ], "specialty" : { - "bonuses" : { - "archery" : { - "type" : "PERCENTAGE_DAMAGE_BOOST", - "subtype" : "damageTypeRanged", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE", - "targetSourceType" : "SECONDARY_SKILL" - } - } + "secondary" : "archery" } }, "valeska": @@ -61,16 +52,7 @@ { "skill" : "navigation", "level": "basic" } ], "specialty" : { - "bonuses" : { - "navigation" : { - "targetSourceType" : "SECONDARY_SKILL", - "subtype" : "heroMovementSea", - "type" : "MOVEMENT", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "navigation" } }, "lordHaart": @@ -84,16 +66,7 @@ { "skill" : "estates", "level": "basic" } ], "specialty" : { - "bonuses" : { - "estates" : { - "subtype" : "resource.gold", - "type" : "GENERATE_RESOURCE", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE", - "targetSourceType" : "SECONDARY_SKILL" - } - } + "secondary" : "estates" } }, "sorsha": @@ -150,16 +123,7 @@ { "skill" : "firstAid", "level": "basic" } ], "specialty" : { - "bonuses" : { - "firstAid" : { - "subtype" : "spell.firstAid", - "type" : "SPECIFIC_SPELL_POWER", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE", - "targetSourceType" : "SECONDARY_SKILL" - } - } + "secondary" : "firstAid" } }, "adela": @@ -265,15 +229,7 @@ { "skill" : "eagleEye", "level": "basic" } ], "specialty" : { - "bonuses" : { - "eagleEye" : { - "type" : "LEARN_BATTLE_SPELL_CHANCE", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE", - "targetSourceType" : "SECONDARY_SKILL" - } - } + "secondary" : "eagleEye" } }, "loynis": diff --git a/config/heroes/dungeon.json b/config/heroes/dungeon.json index 44f3b245d..58b067466 100644 --- a/config/heroes/dungeon.json +++ b/config/heroes/dungeon.json @@ -85,16 +85,7 @@ { "skill" : "tactics", "level": "basic" } ], "specialty" : { - "bonuses" : { - "logistics" : { - "targetSourceType" : "SECONDARY_SKILL", - "subtype" : "heroMovementLand", - "type" : "MOVEMENT", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "logistics" } }, "synca": @@ -159,15 +150,7 @@ { "skill" : "mysticism", "level": "basic" } ], "specialty" : { - "bonuses" : { - "mysticism" : { - "targetSourceType" : "SECONDARY_SKILL", - "type" : "MANA_REGENERATION", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "mysticism" } }, "malekith": @@ -182,16 +165,7 @@ { "skill" : "sorcery", "level": "basic" } ], "specialty" : { - "bonuses" : { - "sorcery" : { - "targetSourceType" : "SECONDARY_SKILL", - "type" : "SPELL_DAMAGE", - "subtype" : "spellSchool.any", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "sorcery" } }, "jeddite": @@ -227,15 +201,7 @@ { "skill" : "eagleEye", "level": "basic" } ], "specialty" : { - "bonuses" : { - "eagleEye" : { - "type" : "LEARN_BATTLE_SPELL_CHANCE", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE", - "targetSourceType" : "SECONDARY_SKILL" - } - } + "secondary" : "eagleEye" } }, "deemer": diff --git a/config/heroes/fortress.json b/config/heroes/fortress.json index d8b5b4464..c17bbaae3 100644 --- a/config/heroes/fortress.json +++ b/config/heroes/fortress.json @@ -66,16 +66,7 @@ { "skill" : "armorer", "level": "advanced" } ], "specialty" : { - "bonuses" : { - "armorer" : { - "type" : "GENERAL_DAMAGE_REDUCTION", - "subtype" : "damageTypeAll", - "targetSourceType" : "SECONDARY_SKILL", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "armorer" } }, "alkin": @@ -166,15 +157,7 @@ { "skill" : "mysticism", "level": "basic" } ], "specialty" : { - "bonuses" : { - "mysticism" : { - "targetSourceType" : "SECONDARY_SKILL", - "type" : "MANA_REGENERATION", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "mysticism" } }, "voy": @@ -189,16 +172,7 @@ { "skill" : "navigation", "level": "basic" } ], "specialty" : { - "bonuses" : { - "navigation" : { - "targetSourceType" : "SECONDARY_SKILL", - "subtype" : "heroMovementSea", - "type" : "MOVEMENT", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "navigation" } }, "verdish": @@ -213,16 +187,7 @@ { "skill" : "firstAid", "level": "basic" } ], "specialty" : { - "bonuses" : { - "firstAid" : { - "subtype" : "spell.firstAid", - "type" : "SPECIFIC_SPELL_POWER", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE", - "targetSourceType" : "SECONDARY_SKILL" - } - } + "secondary" : "firstAid" } }, "merist": @@ -258,16 +223,7 @@ { "skill" : "sorcery", "level": "basic" } ], "specialty" : { - "bonuses" : { - "sorcery" : { - "targetSourceType" : "SECONDARY_SKILL", - "type" : "SPELL_DAMAGE", - "subtype" : "spellSchool.any", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "sorcery" } }, "andra": @@ -282,15 +238,7 @@ { "skill" : "intelligence", "level": "basic" } ], "specialty" : { - "bonuses" : { - "intelligence" : { - "targetSourceType" : "SECONDARY_SKILL", - "type" : "MANA_PER_KNOWLEDGE_PERCENTAGE", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "intelligence" } }, "tiva": @@ -305,15 +253,7 @@ { "skill" : "eagleEye", "level": "basic" } ], "specialty" : { - "bonuses" : { - "eagleEye" : { - "type" : "LEARN_BATTLE_SPELL_CHANCE", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE", - "targetSourceType" : "SECONDARY_SKILL" - } - } + "secondary" : "eagleEye" } } } diff --git a/config/heroes/inferno.json b/config/heroes/inferno.json index f9154f73b..1991298d3 100644 --- a/config/heroes/inferno.json +++ b/config/heroes/inferno.json @@ -126,15 +126,7 @@ { "skill" : "intelligence", "level": "basic" } ], "specialty" : { - "bonuses" : { - "intelligence" : { - "targetSourceType" : "SECONDARY_SKILL", - "type" : "MANA_PER_KNOWLEDGE_PERCENTAGE", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "intelligence" } }, "xyron": @@ -171,15 +163,7 @@ { "skill" : "mysticism", "level": "basic" } ], "specialty" : { - "bonuses" : { - "mysticism" : { - "targetSourceType" : "SECONDARY_SKILL", - "type" : "MANA_REGENERATION", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "mysticism" } }, "olema": @@ -257,16 +241,7 @@ { "skill" : "sorcery", "level": "basic" } ], "specialty" : { - "bonuses" : { - "sorcery" : { - "targetSourceType" : "SECONDARY_SKILL", - "type" : "SPELL_DAMAGE", - "subtype" : "spellSchool.any", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "sorcery" } }, "xarfax": diff --git a/config/heroes/necropolis.json b/config/heroes/necropolis.json index 491d377f8..37719588a 100644 --- a/config/heroes/necropolis.json +++ b/config/heroes/necropolis.json @@ -85,15 +85,7 @@ { "skill" : "necromancy", "level": "advanced" } ], "specialty" : { - "bonuses" : { - "necromancy" : { - "type" : "UNDEAD_RAISE_PERCENTAGE", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE", - "targetSourceType" : "SECONDARY_SKILL" - } - } + "secondary" : "necromancy" } }, "clavius": @@ -188,16 +180,7 @@ { "skill" : "sorcery", "level": "basic" } ], "specialty" : { - "bonuses" : { - "sorcery" : { - "targetSourceType" : "SECONDARY_SKILL", - "type" : "SPELL_DAMAGE", - "subtype" : "spellSchool.any", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "sorcery" } }, "nimbus": @@ -212,15 +195,7 @@ { "skill" : "eagleEye", "level": "basic" } ], "specialty" : { - "bonuses" : { - "eagleEye" : { - "type" : "LEARN_BATTLE_SPELL_CHANCE", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE", - "targetSourceType" : "SECONDARY_SKILL" - } - } + "secondary" : "eagleEye" } }, "thant": @@ -277,15 +252,7 @@ { "skill" : "necromancy", "level": "advanced" } ], "specialty" : { - "bonuses" : { - "necromancy" : { - "type" : "UNDEAD_RAISE_PERCENTAGE", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE", - "targetSourceType" : "SECONDARY_SKILL" - } - } + "secondary" : "necromancy" } }, "nagash": diff --git a/config/heroes/rampart.json b/config/heroes/rampart.json index 7d9a68187..3b3528fbb 100644 --- a/config/heroes/rampart.json +++ b/config/heroes/rampart.json @@ -10,16 +10,7 @@ { "skill" : "armorer", "level": "basic" } ], "specialty" : { - "bonuses" : { - "armorer" : { - "type" : "GENERAL_DAMAGE_REDUCTION", - "subtype" : "damageTypeAll", - "targetSourceType" : "SECONDARY_SKILL", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "armorer" } }, "ufretin": @@ -79,15 +70,7 @@ { "skill" : "resistance", "level": "advanced" } ], "specialty" : { - "bonuses" : { - "resistance" : { - "type" : "MAGIC_RESISTANCE", - "targetSourceType" : "SECONDARY_SKILL", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "resistance" } }, "ivor": @@ -129,16 +112,7 @@ { "skill" : "logistics", "level": "basic" } ], "specialty" : { - "bonuses" : { - "logistics" : { - "targetSourceType" : "SECONDARY_SKILL", - "subtype" : "heroMovementLand", - "type" : "MOVEMENT", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "logistics" } }, "coronius": @@ -196,15 +170,7 @@ { "skill" : "intelligence", "level": "basic" } ], "specialty" : { - "bonuses" : { - "intelligence" : { - "targetSourceType" : "SECONDARY_SKILL", - "type" : "MANA_PER_KNOWLEDGE_PERCENTAGE", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "intelligence" } }, "gem": @@ -219,16 +185,7 @@ { "skill" : "firstAid", "level": "basic" } ], "specialty" : { - "bonuses" : { - "firstAid" : { - "subtype" : "spell.firstAid", - "type" : "SPECIFIC_SPELL_POWER", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE", - "targetSourceType" : "SECONDARY_SKILL" - } - } + "secondary" : "firstAid" } }, "malcom": @@ -243,15 +200,7 @@ { "skill" : "eagleEye", "level": "basic" } ], "specialty" : { - "bonuses" : { - "eagleEye" : { - "type" : "LEARN_BATTLE_SPELL_CHANCE", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE", - "targetSourceType" : "SECONDARY_SKILL" - } - } + "secondary" : "eagleEye" } }, "melodia": diff --git a/config/heroes/stronghold.json b/config/heroes/stronghold.json index 13238de3d..b18748ff8 100644 --- a/config/heroes/stronghold.json +++ b/config/heroes/stronghold.json @@ -93,16 +93,7 @@ { "skill" : "offence", "level": "advanced" } ], "specialty" : { - "bonuses" : { - "offence" : { - "subtype" : "damageTypeMelee", - "type" : "PERCENTAGE_DAMAGE_BOOST", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE", - "targetSourceType" : "SECONDARY_SKILL" - } - } + "secondary" : "offence" } }, "tyraxor": @@ -131,16 +122,7 @@ { "skill" : "sorcery", "level": "basic" } ], "specialty" : { - "bonuses" : { - "sorcery" : { - "targetSourceType" : "SECONDARY_SKILL", - "type" : "SPELL_DAMAGE", - "subtype" : "spellSchool.any", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "sorcery" } }, "vey": @@ -170,16 +152,7 @@ { "skill" : "logistics", "level": "basic" } ], "specialty" : { - "bonuses" : { - "logistics" : { - "targetSourceType" : "SECONDARY_SKILL", - "subtype" : "heroMovementLand", - "type" : "MOVEMENT", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "logistics" } }, "terek": @@ -236,16 +209,7 @@ { "skill" : "offence", "level": "basic" } ], "specialty" : { - "bonuses" : { - "offence" : { - "subtype" : "damageTypeMelee", - "type" : "PERCENTAGE_DAMAGE_BOOST", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE", - "targetSourceType" : "SECONDARY_SKILL" - } - } + "secondary" : "offence" } }, "oris": @@ -260,15 +224,7 @@ { "skill" : "eagleEye", "level": "basic" } ], "specialty" : { - "bonuses" : { - "eagleEye" : { - "type" : "LEARN_BATTLE_SPELL_CHANCE", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE", - "targetSourceType" : "SECONDARY_SKILL" - } - } + "secondary" : "eagleEye" } }, "saurug": diff --git a/config/heroes/tower.json b/config/heroes/tower.json index 4ddc15e86..705c2477a 100644 --- a/config/heroes/tower.json +++ b/config/heroes/tower.json @@ -55,16 +55,7 @@ { "skill" : "armorer", "level": "basic" } ], "specialty" : { - "bonuses" : { - "armorer" : { - "type" : "GENERAL_DAMAGE_REDUCTION", - "subtype" : "damageTypeAll", - "targetSourceType" : "SECONDARY_SKILL", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "armorer" } }, "torosar": @@ -167,15 +158,7 @@ { "skill" : "mysticism", "level": "basic" } ], "specialty" : { - "bonuses" : { - "mysticism" : { - "targetSourceType" : "SECONDARY_SKILL", - "type" : "MANA_REGENERATION", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE" - } - } + "secondary" : "mysticism" } }, "serena": @@ -190,15 +173,7 @@ { "skill" : "eagleEye", "level": "basic" } ], "specialty" : { - "bonuses" : { - "eagleEye" : { - "type" : "LEARN_BATTLE_SPELL_CHANCE", - "updater" : "TIMES_HERO_LEVEL", - "val" : 5, - "valueType" : "PERCENT_TO_TARGET_TYPE", - "targetSourceType" : "SECONDARY_SKILL" - } - } + "secondary" : "eagleEye" } }, "daremyth": diff --git a/config/schemas/hero.json b/config/schemas/hero.json index 02aee6f61..689911972 100644 --- a/config/schemas/hero.json +++ b/config/schemas/hero.json @@ -132,6 +132,10 @@ "creature" : { "type" : "string", "description" : "Shortcut for defining creature specialty, using standard H3 rules." + }, + "secondary" : { + "type" : "string", + "description" : "Shortcut for defining secondary skill specialty, using standard H3 rules." } } }, diff --git a/config/schemas/skill.json b/config/schemas/skill.json index 846a534ae..6e98987f0 100644 --- a/config/schemas/skill.json +++ b/config/schemas/skill.json @@ -43,7 +43,7 @@ } } }, - "required" : ["name", "basic", "advanced", "expert"], + "required" : ["name", "basic", "advanced", "expert", "specialty", "gainChance" ], "properties" : { "name" : { "type" : "string", @@ -61,6 +61,11 @@ "type" : "boolean", "description" : "This skill is minor obligatory (like H3 Magic school)" }, + "specialty" : { + "type" : "array", + "description" : "List of bonuses that are affected by hero specialty", + "items" : { "type" : "string" } + }, "gainChance" : { "description" : "Chance for the skill to be offered on level-up (heroClass may override)", "type" : "object", diff --git a/config/skills.json b/config/skills.json index fe7a01db1..564451cac 100644 --- a/config/skills.json +++ b/config/skills.json @@ -1,6 +1,7 @@ { "pathfinding" : { "index" : 0, + "specialty" : [], // no generic specialty "base" : { "effects" : { "main" : { @@ -27,6 +28,9 @@ }, "archery" : { "index" : 1, + "specialty" : [ + "main" + ], "base" : { "effects" : { "main" : { @@ -54,6 +58,9 @@ }, "logistics" : { "index" : 2, + "specialty" : [ + "main" + ], "base" : { "effects" : { "main" : { @@ -81,6 +88,9 @@ }, "scouting" : { "index" : 3, + "specialty" : [ // NOTE: no such specialists in H3, hota uses different logic + "main" + ], "base" : { "effects" : { "main" : { @@ -107,6 +117,9 @@ }, "diplomacy" : { "index" : 4, + "specialty" : [ // NOTE: no such specialists in H3 + "surr" + ], "base" : { "effects" : { "main" : { @@ -141,6 +154,9 @@ }, "navigation" : { "index" : 5, + "specialty" : [ + "main" + ], "base" : { "effects" : { "main" : { @@ -169,6 +185,7 @@ }, "leadership" : { "index" : 6, + "specialty" : [], // generic specialty not applicable "base" : { "effects" : { "main" : { @@ -195,6 +212,7 @@ }, "wisdom" : { "index" : 7, + "specialty" : [], // generic specialty not applicable "obligatoryMajor" : true, "base" : { "effects" : { @@ -222,6 +240,9 @@ }, "mysticism" : { "index" : 8, + "specialty" : [ + "main" + ], "base" : { "effects" : { "main" : { @@ -248,6 +269,7 @@ }, "luck" : { "index" : 9, + "specialty" : [], // generic specialty not applicable "base" : { "effects" : { "main" : { @@ -274,6 +296,7 @@ }, "ballistics" : { "index" : 10, + "specialty" : [], // generic specialty not applicable "base" : { "effects" : { "main" : { @@ -307,6 +330,9 @@ }, "eagleEye" : { "index" : 11, + "specialty" : [ + "main" + ], "base" : { "effects" : { "main" : { @@ -340,6 +366,9 @@ }, "necromancy" : { "index" : 12, + "specialty" : [ + "main" + ], "base" : { "effects" : { "main" : { @@ -374,6 +403,9 @@ }, "estates" : { "index" : 13, + "specialty" : [ + "main" + ], "base" : { "effects" : { "main" : { @@ -401,6 +433,7 @@ }, "fireMagic" : { "index" : 14, + "specialty" : [], // generic specialty not applicable "obligatoryMinor" : true, "base" : { "effects" : { @@ -429,6 +462,7 @@ }, "airMagic" : { "index" : 15, + "specialty" : [], // generic specialty not applicable "obligatoryMinor" : true, "base" : { "effects" : { @@ -457,6 +491,7 @@ }, "waterMagic" : { "index" : 16, + "specialty" : [], // generic specialty not applicable "obligatoryMinor" : true, "base" : { "effects" : { @@ -485,6 +520,7 @@ }, "earthMagic" : { "index" : 17, + "specialty" : [], // generic specialty not applicable "obligatoryMinor" : true, "base" : { "effects" : { @@ -513,6 +549,7 @@ }, "scholar" : { "index" : 18, + "specialty" : [], // generic specialty not applicable "base" : { "effects" : { "main" : { @@ -539,6 +576,7 @@ }, "tactics" : { "index" : 19, + "specialty" : [], // generic specialty not applicable "base" : { "effects" : { "main" : { @@ -572,6 +610,7 @@ }, "artillery" : { "index" : 20, + "specialty" : [], // generic specialty not applicable, H3 heroes specialize in ballista creature instead "base" : { "effects" : { "main" : { @@ -625,6 +664,9 @@ }, "learning" : { "index" : 21, + "specialty" : [ + "main" + ], "base" : { "effects" : { "main" : { @@ -651,6 +693,9 @@ }, "offence" : { "index" : 22, + "specialty" : [ + "main" + ], "base" : { "effects" : { "main" : { @@ -678,6 +723,9 @@ }, "armorer" : { "index" : 23, + "specialty" : [ + "main" + ], "base" : { "effects" : { "main" : { @@ -705,6 +753,9 @@ }, "intelligence" : { "index" : 24, + "specialty" : [ + "main" + ], "base" : { "effects" : { "main" : { @@ -731,6 +782,9 @@ }, "sorcery" : { "index" : 25, + "specialty" : [ + "main" + ], "base" : { "effects" : { "main" : { @@ -758,6 +812,9 @@ }, "resistance" : { "index" : 26, + "specialty" : [ + "main" + ], "base" : { "effects" : { "main" : { @@ -784,6 +841,9 @@ }, "firstAid" : { "index" : 27, + "specialty" : [ + "main" + ], "base" : { "effects" : { "main" : { diff --git a/docs/modders/Entities_Format/Hero_Type_Format.md b/docs/modders/Entities_Format/Hero_Type_Format.md index 1028bd86f..c8dc305f9 100644 --- a/docs/modders/Entities_Format/Hero_Type_Format.md +++ b/docs/modders/Entities_Format/Hero_Type_Format.md @@ -129,8 +129,11 @@ In order to make functional hero you also need: "someBonus" : {Bonus Format}, "anotherOne" : {Bonus Format} }, - // Optional. Shortcut for defining creature specialty, using standard H3 rules - "creature" : "griffin" + // Shortcut for defining creature specialty, using standard H3 rules + "creature" : "griffin", + + // Shortcut for defining specialty in secondary skill, using standard H3 rules + "secondary" : "offence" } } ``` diff --git a/docs/modders/Entities_Format/Secondary_Skill_Format.md b/docs/modders/Entities_Format/Secondary_Skill_Format.md index d5be72bd0..54da74461 100644 --- a/docs/modders/Entities_Format/Secondary_Skill_Format.md +++ b/docs/modders/Entities_Format/Secondary_Skill_Format.md @@ -26,6 +26,11 @@ "advanced": {Skill level format}, "expert": {Skill level format}, + // Names of bonuses of the skill that are affected by default secondary skill specialty of a hero + "specialty" : [ + "main" + ], + // Chance for the skill to be offered on level-up (heroClass may override) "gainChance" : { // Chance for hero classes with might affinity diff --git a/lib/CSkillHandler.cpp b/lib/CSkillHandler.cpp index 44c9a8f92..4941af95b 100644 --- a/lib/CSkillHandler.cpp +++ b/lib/CSkillHandler.cpp @@ -14,16 +14,14 @@ #include "CSkillHandler.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 "modding/ModUtility.h" -#include "modding/ModScope.h" #include "texts/CGeneralTextHandler.h" #include "texts/CLegacyConfigParser.h" -#include "texts/TextOperations.h" #include "GameLibrary.h" VCMI_LIB_NAMESPACE_BEGIN @@ -253,6 +251,23 @@ std::shared_ptr CSkillHandler::loadFromJson(const std::string & scope, c skillAtLevel.iconMedium = levelNode["images"]["medium"].String(); skillAtLevel.iconLarge = levelNode["images"]["large"].String(); } + + for(const auto & b : json["specialty"].Vector()) + { + const auto & bonusNode = json["basic"]["effects"][b.String()]; + + if (bonusNode.isStruct()) + { + auto bonus = JsonUtils::parseBonus(bonusNode); + bonus->val = 5; // default H3 value, hardcoded for now + bonus->updater = std::make_shared(); + bonus->valType = BonusValueType::PERCENT_TO_TARGET_TYPE; + bonus->targetSourceType = BonusSource::SECONDARY_SKILL; + skill->specialtyTargetBonuses.push_back(bonus); + } + else + logMod->warn("Failed to load speciality bonus '%s' for skill '%s'", b.String(), identifier); + } logMod->debug("loaded secondary skill %s(%d)", identifier, skill->id.getNum()); return skill; diff --git a/lib/CSkillHandler.h b/lib/CSkillHandler.h index ae89435eb..5096e526d 100644 --- a/lib/CSkillHandler.h +++ b/lib/CSkillHandler.h @@ -71,6 +71,9 @@ public: std::array gainChance; // gainChance[0/1] = default gain chance on level-up for might/magic heroes + /// Bonuses that should be given to hero that specializes in this skill + std::vector> specialtyTargetBonuses; + void updateFrom(const JsonNode & data); void serializeJson(JsonSerializeFormat & handler); diff --git a/lib/entities/hero/CHeroHandler.cpp b/lib/entities/hero/CHeroHandler.cpp index c97725372..968549b1d 100644 --- a/lib/entities/hero/CHeroHandler.cpp +++ b/lib/entities/hero/CHeroHandler.cpp @@ -21,6 +21,7 @@ #include "../../json/JsonBonus.h" #include "../../json/JsonUtils.h" #include "../../modding/IdentifierStorage.h" +#include "../../CSkillHandler.h" #include "../../texts/CGeneralTextHandler.h" #include "../../texts/CLegacyConfigParser.h" @@ -140,7 +141,7 @@ void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node) const } /// creates standard H3 hero specialty for creatures -static std::vector> createCreatureSpecialty(CreatureID cid) +std::vector> CHeroHandler::createCreatureSpecialty(CreatureID cid) const { std::vector> result; @@ -174,6 +175,19 @@ static std::vector> createCreatureSpecialty(CreatureID ci bonus->updater.reset(new GrowsWithLevelUpdater(specCreature.getDefense(false), stepSize)); result.push_back(bonus); } + return result; +} + +std::vector> CHeroHandler::createSecondarySkillSpecialty(SecondarySkill skillID) const +{ + std::vector> result; + const auto & skillPtr = LIBRARY->skillh->objects[skillID.getNum()]; + + if (skillPtr->specialtyTargetBonuses.empty()) + logMod->warn("Skill '%s' does not supports generic specialties!", skillPtr->getJsonKey()); + + for (const auto & bonus : skillPtr->specialtyTargetBonuses) + result.push_back(std::make_shared(*bonus)); return result; } @@ -201,15 +215,7 @@ void CHeroHandler::beforeValidate(JsonNode & object) } } -void CHeroHandler::afterLoadFinalization() -{ - for(const auto & functor : callAfterLoadFinalization) - functor(); - - callAfterLoadFinalization.clear(); -} - -void CHeroHandler::loadHeroSpecialty(CHero * hero, const JsonNode & node) +void CHeroHandler::loadHeroSpecialty(CHero * hero, const JsonNode & node) const { auto prepSpec = [=](std::shared_ptr bonus) { @@ -230,18 +236,21 @@ void CHeroHandler::loadHeroSpecialty(CHero * hero, const JsonNode & node) //creature specialty - alias for simplicity if(!specialtyNode["creature"].isNull()) { - JsonNode creatureNode = specialtyNode["creature"]; - - std::function specialtyLoader = [creatureNode, hero, prepSpec] + LIBRARY->identifiers()->requestIdentifier("creature", specialtyNode["creature"], [this, hero, prepSpec](si32 creature) { - LIBRARY->identifiers()->requestIdentifier("creature", creatureNode, [hero, prepSpec](si32 creature) - { - for (const auto & bonus : createCreatureSpecialty(CreatureID(creature))) - hero->specialty.push_back(prepSpec(bonus)); - }); - }; + for (const auto & bonus : createCreatureSpecialty(CreatureID(creature))) + hero->specialty.push_back(prepSpec(bonus)); + }); + } - callAfterLoadFinalization.push_back(specialtyLoader); + //secondary skill specialty - alias for simplicity + if(!specialtyNode["secondary"].isNull()) + { + LIBRARY->identifiers()->requestIdentifier("secondarySkill", specialtyNode["secondary"], [this, hero, prepSpec](si32 creature) + { + for (const auto & bonus : createSecondarySkillSpecialty(SecondarySkill(creature))) + hero->specialty.push_back(prepSpec(bonus)); + }); } for(const auto & keyValue : specialtyNode["bonuses"].Struct()) diff --git a/lib/entities/hero/CHeroHandler.h b/lib/entities/hero/CHeroHandler.h index d62911599..fa1550bf5 100644 --- a/lib/entities/hero/CHeroHandler.h +++ b/lib/entities/hero/CHeroHandler.h @@ -28,11 +28,12 @@ class DLL_LINKAGE CHeroHandler : public CHandlerBase> callAfterLoadFinalization; + std::vector> createCreatureSpecialty(CreatureID cid) const; + std::vector> createSecondarySkillSpecialty(SecondarySkill skillID) const; public: ui32 level(TExpType experience) const; //calculates level corresponding to given experience amount @@ -44,7 +45,6 @@ public: void beforeValidate(JsonNode & object) override; 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; - void afterLoadFinalization() override; CHeroHandler(); ~CHeroHandler();