mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Merge pull request #379 from henningkoehlernz/hero_specialty_scaling
Hero specialty scaling
This commit is contained in:
commit
7fd090786c
2
AUTHORS
2
AUTHORS
@ -68,4 +68,4 @@ Piotr Wójcik aka Chocimier, <chocimier@tlen.pl>
|
||||
* Various bug fixes
|
||||
|
||||
Henning Koehler, <henning.koehler.nz@gmail.com>
|
||||
* skill modding
|
||||
* skill modding, bonus updaters
|
||||
|
@ -27,6 +27,7 @@ MODS:
|
||||
* Improve support for WoG commander artifacts and skill descriptions
|
||||
* Added basic support for secondary skill modding
|
||||
* Map object sounds can now be configured via json
|
||||
* Added bonus updaters for hero specialties
|
||||
|
||||
SOUND:
|
||||
* Fixed many mising or wrong pickup and visit sounds for map objects
|
||||
|
18
Global.h
18
Global.h
@ -369,6 +369,24 @@ namespace vstd
|
||||
return std::find(c.begin(),c.end(),i);
|
||||
}
|
||||
|
||||
//returns first key that maps to given value if present, returns success via found if provided
|
||||
template <typename Key, typename T>
|
||||
Key findKey(const std::map<Key, T> & map, const T & value, bool * found = nullptr)
|
||||
{
|
||||
for(auto iter = map.cbegin(); iter != map.cend(); iter++)
|
||||
{
|
||||
if(iter->second == value)
|
||||
{
|
||||
if(found)
|
||||
*found = true;
|
||||
return iter->first;
|
||||
}
|
||||
}
|
||||
if(found)
|
||||
*found = false;
|
||||
return Key();
|
||||
}
|
||||
|
||||
//removes element i from container c, returns false if c does not contain i
|
||||
template <typename Container, typename Item>
|
||||
typename Container::size_type operator-=(Container &c, const Item &i)
|
||||
|
@ -9,10 +9,17 @@
|
||||
{ "skill" : "leadership", "level": "basic" },
|
||||
{ "skill" : "archery", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 1, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"archery" : {
|
||||
"subtype" : "skill.archery",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"valeska":
|
||||
{
|
||||
@ -24,10 +31,9 @@
|
||||
{ "skill" : "leadership", "level": "basic" },
|
||||
{ "skill" : "archery", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 2 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "archer"
|
||||
}
|
||||
},
|
||||
"edric":
|
||||
{
|
||||
@ -39,10 +45,9 @@
|
||||
{ "skill" : "leadership", "level": "basic" },
|
||||
{ "skill" : "armorer", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 4 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "griffin"
|
||||
}
|
||||
},
|
||||
"sylvia":
|
||||
{
|
||||
@ -54,10 +59,17 @@
|
||||
{ "skill" : "leadership", "level": "basic" },
|
||||
{ "skill" : "navigation", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 2, "subtype": 5, "info": 1 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"navigation" : {
|
||||
"subtype" : "skill.navigation",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"lordHaart":
|
||||
{
|
||||
@ -70,10 +82,17 @@
|
||||
{ "skill" : "leadership", "level": "basic" },
|
||||
{ "skill" : "estates", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 13, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"estates" : {
|
||||
"subtype" : "skill.estates",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sorsha":
|
||||
{
|
||||
@ -85,10 +104,9 @@
|
||||
{ "skill" : "leadership", "level": "basic" },
|
||||
{ "skill" : "offence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 6 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "swordsman"
|
||||
}
|
||||
},
|
||||
"christian":
|
||||
{
|
||||
@ -100,10 +118,9 @@
|
||||
{ "skill" : "leadership", "level": "basic" },
|
||||
{ "skill" : "artillery", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 146 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "ballista"
|
||||
}
|
||||
},
|
||||
"tyris":
|
||||
{
|
||||
@ -115,10 +132,9 @@
|
||||
{ "skill" : "leadership", "level": "basic" },
|
||||
{ "skill" : "tactics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 10 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "cavalier"
|
||||
}
|
||||
},
|
||||
"rion":
|
||||
{
|
||||
@ -131,10 +147,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "firstAid", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 27, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"firstAid" : {
|
||||
"subtype" : "skill.firstAid",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"adela":
|
||||
{
|
||||
@ -147,10 +170,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "diplomacy", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":6, "val": 3, "subtype": 41, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"bless" : {
|
||||
"addInfo" : 0,
|
||||
"subtype" : "spell.bless",
|
||||
"type" : "SPECIAL_BLESS_DAMAGE",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cuthbert":
|
||||
{
|
||||
@ -163,10 +193,15 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "estates", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":8, "val": 0, "subtype": 45, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"weakness" : {
|
||||
"addInfo" : 0,
|
||||
"subtype" : "spell.weakness",
|
||||
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"adelaide":
|
||||
{
|
||||
@ -178,10 +213,16 @@
|
||||
[
|
||||
{ "skill" : "wisdom", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":3, "val": 3, "subtype": 20, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"frostRing" : {
|
||||
"subtype" : "spell.frostRing",
|
||||
"type" : "SPECIAL_SPELL_LEV",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ingham":
|
||||
{
|
||||
@ -194,10 +235,9 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "mysticism", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 8 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "monk"
|
||||
}
|
||||
},
|
||||
"sanya":
|
||||
{
|
||||
@ -210,10 +250,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "eagleEye", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 11, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"eagleEye" : {
|
||||
"subtype" : "skill.eagleEye",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"loynis":
|
||||
{
|
||||
@ -226,10 +273,15 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "learning", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":8, "val": 0, "subtype": 48, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"prayer" : {
|
||||
"addInfo" : 0,
|
||||
"subtype" : "spell.prayer",
|
||||
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"caitlin":
|
||||
{
|
||||
@ -242,9 +294,14 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "intelligence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"gold" : {
|
||||
"subtype" : "resource.gold",
|
||||
"type" : "GENERATE_RESOURCE",
|
||||
"val" : 350
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,11 +9,22 @@
|
||||
{ "skill" : "artillery", "level": "basic" },
|
||||
{ "skill" : "offence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":4, "val": 3, "subtype": 1, "info": 120 },
|
||||
{ "type":4, "val": 3, "subtype": 2, "info": 120 }
|
||||
]
|
||||
"specialty" : {
|
||||
"base" : {
|
||||
"limiters" : [
|
||||
{
|
||||
"parameters" : [ "psychicElemental", true ],
|
||||
"type" : "CREATURE_TYPE_LIMITER"
|
||||
}
|
||||
],
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 3
|
||||
},
|
||||
"bonuses" : {
|
||||
"attack" : { "subtype" : "primSkill.attack" },
|
||||
"defence" : { "subtype" : "primSkill.defence" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"thunar":
|
||||
{
|
||||
@ -25,12 +36,32 @@
|
||||
{ "skill" : "estates", "level": "basic" },
|
||||
{ "skill" : "tactics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":4, "val": 2, "subtype": 1, "info": 113 },
|
||||
{ "type":4, "val": 1, "subtype": 2, "info": 113 },
|
||||
{ "type":4, "val": 5, "subtype": 4, "info": 113 }
|
||||
]
|
||||
"specialty" : {
|
||||
"base" : {
|
||||
"limiters" : [
|
||||
{
|
||||
"parameters" : [ "earthElemental", true ],
|
||||
"type" : "CREATURE_TYPE_LIMITER"
|
||||
}
|
||||
]
|
||||
},
|
||||
"bonuses" : {
|
||||
"health" : {
|
||||
"type" : "STACK_HEALTH",
|
||||
"val" : 5
|
||||
},
|
||||
"attack" : {
|
||||
"subtype" : "primSkill.attack",
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 2
|
||||
},
|
||||
"defence" : {
|
||||
"subtype" : "primSkill.defence",
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ignissa":
|
||||
{
|
||||
@ -42,12 +73,33 @@
|
||||
{ "skill" : "artillery", "level": "basic" },
|
||||
{ "skill" : "offence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":4, "val": 1, "subtype": 1, "info": 114 },
|
||||
{ "type":4, "val": 2, "subtype": 1, "info": 114 },
|
||||
{ "type":4, "val": 2, "subtype": 3, "info": 114 }
|
||||
]
|
||||
"specialty" : {
|
||||
"base" : {
|
||||
"limiters" : [
|
||||
{
|
||||
"parameters" : [ "fireElemental", true ],
|
||||
"type" : "CREATURE_TYPE_LIMITER"
|
||||
}
|
||||
]
|
||||
},
|
||||
"bonuses" : {
|
||||
"damage" : {
|
||||
"subtype" : 0,
|
||||
"type" : "CREATURE_DAMAGE",
|
||||
"val" : 2
|
||||
},
|
||||
"attack" : {
|
||||
"subtype" : "primSkill.attack",
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 1
|
||||
},
|
||||
"defence" : {
|
||||
"subtype" : "primSkill.defence",
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 2
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"lacus":
|
||||
{
|
||||
@ -58,10 +110,21 @@
|
||||
[
|
||||
{ "skill" : "tactics", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":4, "val": 2, "subtype": 1, "info": 115 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"attack" : {
|
||||
"limiters" : [
|
||||
{
|
||||
"parameters" : [ "waterElemental", true ],
|
||||
"type" : "CREATURE_TYPE_LIMITER"
|
||||
}
|
||||
],
|
||||
"subtype" : "primSkill.attack",
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 2
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"monere":
|
||||
{
|
||||
@ -73,11 +136,22 @@
|
||||
{ "skill" : "logistics", "level": "basic" },
|
||||
{ "skill" : "offence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":4, "val": 3, "subtype": 1, "info": 120 },
|
||||
{ "type":4, "val": 3, "subtype": 2, "info": 120 }
|
||||
]
|
||||
"specialty" : {
|
||||
"base" : {
|
||||
"limiters" : [
|
||||
{
|
||||
"parameters" : [ "psychicElemental", true ],
|
||||
"type" : "CREATURE_TYPE_LIMITER"
|
||||
}
|
||||
],
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 3
|
||||
},
|
||||
"bonuses" : {
|
||||
"attack" : { "subtype" : "primSkill.attack" },
|
||||
"defence" : { "subtype" : "primSkill.defence" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"erdamon":
|
||||
{
|
||||
@ -89,12 +163,32 @@
|
||||
{ "skill" : "estates", "level": "basic" },
|
||||
{ "skill" : "tactics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":4, "val": 2, "subtype": 1, "info": 113 },
|
||||
{ "type":4, "val": 1, "subtype": 2, "info": 113 },
|
||||
{ "type":4, "val": 5, "subtype": 4, "info": 113 }
|
||||
]
|
||||
"specialty" : {
|
||||
"base" : {
|
||||
"limiters" : [
|
||||
{
|
||||
"parameters" : [ "earthElemental", true ],
|
||||
"type" : "CREATURE_TYPE_LIMITER"
|
||||
}
|
||||
]
|
||||
},
|
||||
"bonuses" : {
|
||||
"health" : {
|
||||
"type" : "STACK_HEALTH",
|
||||
"val" : 5
|
||||
},
|
||||
"attack" : {
|
||||
"subtype" : "primSkill.attack",
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 2
|
||||
},
|
||||
"defence" : {
|
||||
"subtype" : "primSkill.defence",
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"fiur":
|
||||
{
|
||||
@ -105,12 +199,33 @@
|
||||
[
|
||||
{ "skill" : "offence", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":4, "val": 1, "subtype": 1, "info": 114 },
|
||||
{ "type":4, "val": 2, "subtype": 1, "info": 114 },
|
||||
{ "type":4, "val": 2, "subtype": 3, "info": 114 }
|
||||
]
|
||||
"specialty" : {
|
||||
"base" : {
|
||||
"limiters" : [
|
||||
{
|
||||
"parameters" : [ "fireElemental", true ],
|
||||
"type" : "CREATURE_TYPE_LIMITER"
|
||||
}
|
||||
]
|
||||
},
|
||||
"bonuses" : {
|
||||
"damage" : {
|
||||
"subtype" : 0,
|
||||
"type" : "CREATURE_DAMAGE",
|
||||
"val" : 2
|
||||
},
|
||||
"attack" : {
|
||||
"subtype" : "primSkill.attack",
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 1
|
||||
},
|
||||
"defence" : {
|
||||
"subtype" : "primSkill.defence",
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 2
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kalt":
|
||||
{
|
||||
@ -122,10 +237,21 @@
|
||||
{ "skill" : "tactics", "level": "basic" },
|
||||
{ "skill" : "learning", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":4, "val": 2, "subtype": 1, "info": 115 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"attack" : {
|
||||
"limiters" : [
|
||||
{
|
||||
"parameters" : [ "waterElemental", true ],
|
||||
"type" : "CREATURE_TYPE_LIMITER"
|
||||
}
|
||||
],
|
||||
"subtype" : "primSkill.attack",
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 2
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"luna":
|
||||
{
|
||||
@ -138,10 +264,16 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "fireMagic", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":5, "val": 100, "subtype": 13, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"fireWall" : {
|
||||
"subtype" : "spell.fireWall",
|
||||
"type" : "SPECIFIC_SPELL_DAMAGE",
|
||||
"val" : 100,
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"brissa":
|
||||
{
|
||||
@ -154,10 +286,15 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "airMagic", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":8, "val": 0, "subtype": 53, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"haste" : {
|
||||
"addInfo" : 0,
|
||||
"subtype" : "spell.haste",
|
||||
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ciele":
|
||||
{
|
||||
@ -170,10 +307,16 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "waterMagic", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":5, "val": 50, "subtype": 15, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"magicArrow" : {
|
||||
"subtype" : "spell.magicArrow",
|
||||
"type" : "SPECIFIC_SPELL_DAMAGE",
|
||||
"val" : 50,
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"labetha":
|
||||
{
|
||||
@ -186,10 +329,15 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "earthMagic", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":8, "val": 0, "subtype": 46, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"stoneSkin" : {
|
||||
"addInfo" : 0,
|
||||
"subtype" : "spell.stoneSkin",
|
||||
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"inteus":
|
||||
{
|
||||
@ -202,10 +350,15 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "fireMagic", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":8, "val": 0, "subtype": 43, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"bloodlust" : {
|
||||
"addInfo" : 0,
|
||||
"subtype" : "spell.bloodlust",
|
||||
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"aenain":
|
||||
{
|
||||
@ -218,10 +371,15 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "airMagic", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":8, "val": 0, "subtype": 47, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"disruptingRay" : {
|
||||
"addInfo" : 0,
|
||||
"subtype" : "spell.disruptingRay",
|
||||
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"gelare":
|
||||
{
|
||||
@ -234,10 +392,15 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "waterMagic", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"gold" : {
|
||||
"subtype" : "resource.gold",
|
||||
"type" : "GENERATE_RESOURCE",
|
||||
"val" : 350
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"grindan":
|
||||
{
|
||||
@ -250,9 +413,14 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "earthMagic", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"gold" : {
|
||||
"subtype" : "resource.gold",
|
||||
"type" : "GENERATE_RESOURCE",
|
||||
"val" : 350
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,10 +9,9 @@
|
||||
{ "skill" : "scouting", "level": "basic" },
|
||||
{ "skill" : "leadership", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 72 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "harpy"
|
||||
}
|
||||
},
|
||||
"arlach":
|
||||
{
|
||||
@ -24,10 +23,9 @@
|
||||
{ "skill" : "artillery", "level": "basic" },
|
||||
{ "skill" : "offence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 146 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "ballista"
|
||||
}
|
||||
},
|
||||
"dace":
|
||||
{
|
||||
@ -39,10 +37,9 @@
|
||||
{ "skill" : "tactics", "level": "basic" },
|
||||
{ "skill" : "offence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 78 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "minotaur"
|
||||
}
|
||||
},
|
||||
"ajit":
|
||||
{
|
||||
@ -54,10 +51,9 @@
|
||||
{ "skill" : "leadership", "level": "basic" },
|
||||
{ "skill" : "resistance", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 74 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "beholder"
|
||||
}
|
||||
},
|
||||
"damacon":
|
||||
{
|
||||
@ -68,10 +64,15 @@
|
||||
[
|
||||
{ "skill" : "offence", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"gold" : {
|
||||
"subtype" : "resource.gold",
|
||||
"type" : "GENERATE_RESOURCE",
|
||||
"val" : 350
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"gunnar":
|
||||
{
|
||||
@ -83,10 +84,17 @@
|
||||
{ "skill" : "logistics", "level": "basic" },
|
||||
{ "skill" : "tactics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 2, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"logistics" : {
|
||||
"subtype" : "skill.logistics",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"synca":
|
||||
{
|
||||
@ -98,10 +106,9 @@
|
||||
{ "skill" : "leadership", "level": "basic" },
|
||||
{ "skill" : "scholar", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 80 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "manticore"
|
||||
}
|
||||
},
|
||||
"shakti":
|
||||
{
|
||||
@ -113,10 +120,9 @@
|
||||
{ "skill" : "tactics", "level": "basic" },
|
||||
{ "skill" : "offence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 70 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "troglodyte"
|
||||
}
|
||||
},
|
||||
"alamar":
|
||||
{
|
||||
@ -129,10 +135,16 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "scholar", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":3, "val": 3, "subtype": 38, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"resurrection" : {
|
||||
"subtype" : "spell.resurrection",
|
||||
"type" : "SPECIAL_SPELL_LEV",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"jaegar":
|
||||
{
|
||||
@ -145,10 +157,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "mysticism", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 8, "info": 1 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"mysticism" : {
|
||||
"subtype" : "skill.mysticism",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"malekith":
|
||||
{
|
||||
@ -161,10 +180,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "sorcery", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 25, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"sorcery" : {
|
||||
"subtype" : "skill.sorcery",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"jeddite":
|
||||
{
|
||||
@ -176,10 +202,16 @@
|
||||
[
|
||||
{ "skill" : "wisdom", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":3, "val": 3, "subtype": 38, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"resurrection" : {
|
||||
"subtype" : "spell.resurrection",
|
||||
"type" : "SPECIAL_SPELL_LEV",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"geon":
|
||||
{
|
||||
@ -192,10 +224,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "eagleEye", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 11, "info": 1 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"eagleEye" : {
|
||||
"subtype" : "skill.eagleEye",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deemer":
|
||||
{
|
||||
@ -208,10 +247,16 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "scouting", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":3, "val": 3, "subtype": 23, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"meteorShower" : {
|
||||
"subtype" : "spell.meteorShower",
|
||||
"type" : "SPECIAL_SPELL_LEV",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sephinroth":
|
||||
{
|
||||
@ -224,10 +269,15 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "intelligence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":10, "val": 1, "subtype": 4, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"crystal" : {
|
||||
"subtype" : "resource.crystal",
|
||||
"type" : "GENERATE_RESOURCE",
|
||||
"val" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"darkstorn":
|
||||
{
|
||||
@ -240,9 +290,14 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "learning", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":8, "val": 0, "subtype": 46, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"stoneSkin" : {
|
||||
"addInfo" : 0,
|
||||
"subtype" : "spell.stoneSkin",
|
||||
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,10 +9,9 @@
|
||||
{ "skill" : "armorer", "level": "basic" },
|
||||
{ "skill" : "resistance", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 106 }
|
||||
],
|
||||
"specialty" : {
|
||||
"creature" : "basilisk"
|
||||
},
|
||||
"army" :
|
||||
[
|
||||
{
|
||||
@ -39,10 +38,9 @@
|
||||
{ "skill" : "armorer", "level": "basic" },
|
||||
{ "skill" : "leadership", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 98 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "gnoll"
|
||||
}
|
||||
},
|
||||
"wystan":
|
||||
{
|
||||
@ -54,10 +52,9 @@
|
||||
{ "skill" : "armorer", "level": "basic" },
|
||||
{ "skill" : "archery", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 100 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "lizardman"
|
||||
}
|
||||
},
|
||||
"tazar":
|
||||
{
|
||||
@ -68,10 +65,17 @@
|
||||
[
|
||||
{ "skill" : "armorer", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 23, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"armorer" : {
|
||||
"subtype" : "skill.armorer",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"alkin":
|
||||
{
|
||||
@ -83,10 +87,9 @@
|
||||
{ "skill" : "armorer", "level": "basic" },
|
||||
{ "skill" : "offence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 102 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "gorgon"
|
||||
}
|
||||
},
|
||||
"korbac":
|
||||
{
|
||||
@ -98,10 +101,9 @@
|
||||
{ "skill" : "armorer", "level": "basic" },
|
||||
{ "skill" : "pathfinding", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 104 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "serpentFly"
|
||||
}
|
||||
},
|
||||
"gerwulf":
|
||||
{
|
||||
@ -113,10 +115,9 @@
|
||||
{ "skill" : "armorer", "level": "basic" },
|
||||
{ "skill" : "artillery", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 146 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "ballista"
|
||||
}
|
||||
},
|
||||
"broghild":
|
||||
{
|
||||
@ -128,10 +129,9 @@
|
||||
{ "skill" : "armorer", "level": "basic" },
|
||||
{ "skill" : "scouting", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 108 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "wyvern"
|
||||
}
|
||||
},
|
||||
"mirlanda":
|
||||
{
|
||||
@ -143,10 +143,15 @@
|
||||
[
|
||||
{ "skill" : "wisdom", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":8, "val": 0, "subtype": 45, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"weakness" : {
|
||||
"addInfo" : 0,
|
||||
"subtype" : "spell.weakness",
|
||||
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"rosic":
|
||||
{
|
||||
@ -159,10 +164,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "mysticism", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 8, "info": 1 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"mysticism" : {
|
||||
"subtype" : "skill.mysticism",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"voy":
|
||||
{
|
||||
@ -175,10 +187,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "navigation", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 2, "subtype": 5, "info": 1 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"navigation" : {
|
||||
"subtype" : "skill.navigation",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"verdish":
|
||||
{
|
||||
@ -191,10 +210,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "firstAid", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 27, "info": 1 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"firstAid" : {
|
||||
"subtype" : "skill.firstAid",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"merist":
|
||||
{
|
||||
@ -207,10 +233,15 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "learning", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":8, "val": 0, "subtype": 46, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"stoneSkin" : {
|
||||
"addInfo" : 0,
|
||||
"subtype" : "spell.stoneSkin",
|
||||
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"styg":
|
||||
{
|
||||
@ -223,10 +254,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "sorcery", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 25, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"sorcery" : {
|
||||
"subtype" : "skill.sorcery",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"andra":
|
||||
{
|
||||
@ -239,10 +277,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "intelligence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 24, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"intelligence" : {
|
||||
"subtype" : "skill.intelligence",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tiva":
|
||||
{
|
||||
@ -255,9 +300,16 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "eagleEye", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 11, "info": 1 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"eagleEye" : {
|
||||
"subtype" : "skill.eagleEye",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,10 +8,9 @@
|
||||
[
|
||||
{ "skill" : "scouting", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 46 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "hellHound"
|
||||
}
|
||||
},
|
||||
"rashka":
|
||||
{
|
||||
@ -23,10 +22,9 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "scholar", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 52 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "efreet"
|
||||
}
|
||||
},
|
||||
"marius":
|
||||
{
|
||||
@ -37,10 +35,9 @@
|
||||
[
|
||||
{ "skill" : "armorer", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 48 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "demon"
|
||||
}
|
||||
},
|
||||
"ignatius":
|
||||
{
|
||||
@ -52,10 +49,9 @@
|
||||
{ "skill" : "tactics", "level": "basic" },
|
||||
{ "skill" : "resistance", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 42 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "imp"
|
||||
}
|
||||
},
|
||||
"octavia":
|
||||
{
|
||||
@ -67,10 +63,15 @@
|
||||
{ "skill" : "scholar", "level": "basic" },
|
||||
{ "skill" : "offence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"gold" : {
|
||||
"subtype" : "resource.gold",
|
||||
"type" : "GENERATE_RESOURCE",
|
||||
"val" : 350
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"calh":
|
||||
{
|
||||
@ -82,10 +83,9 @@
|
||||
{ "skill" : "archery", "level": "basic" },
|
||||
{ "skill" : "scouting", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 42 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "gog"
|
||||
}
|
||||
},
|
||||
"pyre":
|
||||
{
|
||||
@ -97,10 +97,9 @@
|
||||
{ "skill" : "artillery", "level": "basic" },
|
||||
{ "skill" : "logistics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 146 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "ballista"
|
||||
}
|
||||
},
|
||||
"nymus":
|
||||
{
|
||||
@ -111,10 +110,9 @@
|
||||
[
|
||||
{ "skill" : "offence", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 50 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "pitFiend"
|
||||
}
|
||||
},
|
||||
"ayden":
|
||||
{
|
||||
@ -127,10 +125,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "intelligence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 24, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"intelligence" : {
|
||||
"subtype" : "skill.intelligence",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"xyron":
|
||||
{
|
||||
@ -143,10 +148,16 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "scholar", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":3, "val": 3, "subtype": 22, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"inferno" : {
|
||||
"subtype" : "spell.inferno",
|
||||
"type" : "SPECIAL_SPELL_LEV",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"axsis":
|
||||
{
|
||||
@ -159,10 +170,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "mysticism", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 8, "info": 1 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"mysticism" : {
|
||||
"subtype" : "skill.mysticism",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"olema":
|
||||
{
|
||||
@ -175,10 +193,15 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "ballistics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":8, "val": 0, "subtype": 45, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"weakness" : {
|
||||
"addInfo" : 0,
|
||||
"subtype" : "spell.weakness",
|
||||
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"calid":
|
||||
{
|
||||
@ -191,10 +214,15 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "learning", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":10, "val": 1, "subtype": 3, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"sulfur" : {
|
||||
"subtype" : "resource.sulfur",
|
||||
"type" : "GENERATE_RESOURCE",
|
||||
"val" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ash":
|
||||
{
|
||||
@ -207,10 +235,15 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "eagleEye", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":8, "val": 0, "subtype": 43, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"bloodlust" : {
|
||||
"addInfo" : 0,
|
||||
"subtype" : "spell.bloodlust",
|
||||
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"zydar":
|
||||
{
|
||||
@ -223,10 +256,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "sorcery", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 25, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"sorcery" : {
|
||||
"subtype" : "skill.sorcery",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"xarfax":
|
||||
{
|
||||
@ -239,9 +279,15 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "leadership", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":3, "val": 3, "subtype": 21, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"fireball" : {
|
||||
"subtype" : "spell.fireball",
|
||||
"type" : "SPECIAL_SPELL_LEV",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,10 +10,9 @@
|
||||
{ "skill" : "necromancy", "level": "basic" },
|
||||
{ "skill" : "resistance", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 58 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "walkingDead"
|
||||
}
|
||||
},
|
||||
"vokial":
|
||||
{
|
||||
@ -26,10 +25,9 @@
|
||||
{ "skill" : "necromancy", "level": "basic" },
|
||||
{ "skill" : "artillery", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 62 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "vampire"
|
||||
}
|
||||
},
|
||||
"moandor":
|
||||
{
|
||||
@ -42,10 +40,9 @@
|
||||
{ "skill" : "necromancy", "level": "basic" },
|
||||
{ "skill" : "learning", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 64 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "lich"
|
||||
}
|
||||
},
|
||||
"charna":
|
||||
{
|
||||
@ -58,10 +55,9 @@
|
||||
{ "skill" : "necromancy", "level": "basic" },
|
||||
{ "skill" : "tactics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 60 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "wight"
|
||||
}
|
||||
},
|
||||
"tamika":
|
||||
{
|
||||
@ -74,10 +70,9 @@
|
||||
{ "skill" : "necromancy", "level": "basic" },
|
||||
{ "skill" : "offence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 66 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "blackKnight"
|
||||
}
|
||||
},
|
||||
"isra":
|
||||
{
|
||||
@ -89,10 +84,17 @@
|
||||
[
|
||||
{ "skill" : "necromancy", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 12, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"necromancy" : {
|
||||
"subtype" : "skill.necromancy",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"clavius":
|
||||
{
|
||||
@ -105,10 +107,15 @@
|
||||
{ "skill" : "necromancy", "level": "basic" },
|
||||
{ "skill" : "offence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"gold" : {
|
||||
"subtype" : "resource.gold",
|
||||
"type" : "GENERATE_RESOURCE",
|
||||
"val" : 350
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"galthran":
|
||||
{
|
||||
@ -121,10 +128,9 @@
|
||||
{ "skill" : "necromancy", "level": "basic" },
|
||||
{ "skill" : "armorer", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 56 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "skeleton"
|
||||
}
|
||||
},
|
||||
"septienna":
|
||||
{
|
||||
@ -137,10 +143,16 @@
|
||||
{ "skill" : "necromancy", "level": "basic" },
|
||||
{ "skill" : "scholar", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":3, "val": 3, "subtype": 24, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"deathRipple" : {
|
||||
"subtype" : "spell.deathRipple",
|
||||
"type" : "SPECIAL_SPELL_LEV",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"aislinn":
|
||||
{
|
||||
@ -153,10 +165,16 @@
|
||||
{ "skill" : "necromancy", "level": "basic" },
|
||||
{ "skill" : "wisdom", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":3, "val": 3, "subtype": 23, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"meteorShower" : {
|
||||
"subtype" : "spell.meteorShower",
|
||||
"type" : "SPECIAL_SPELL_LEV",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sandro":
|
||||
{
|
||||
@ -169,10 +187,17 @@
|
||||
{ "skill" : "necromancy", "level": "basic" },
|
||||
{ "skill" : "sorcery", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 25, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"sorcery" : {
|
||||
"subtype" : "skill.sorcery",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"nimbus":
|
||||
{
|
||||
@ -185,10 +210,17 @@
|
||||
{ "skill" : "necromancy", "level": "basic" },
|
||||
{ "skill" : "eagleEye", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 11, "info": 1 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"eagleEye" : {
|
||||
"subtype" : "skill.eagleEye",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"thant":
|
||||
{
|
||||
@ -201,10 +233,16 @@
|
||||
{ "skill" : "necromancy", "level": "basic" },
|
||||
{ "skill" : "mysticism", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":3, "val": 39, "subtype": 0, "info": 3 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"animateDead" : {
|
||||
"subtype" : "spell.animateDead",
|
||||
"type" : "SPECIAL_SPELL_LEV",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"xsi":
|
||||
{
|
||||
@ -217,10 +255,15 @@
|
||||
{ "skill" : "necromancy", "level": "basic" },
|
||||
{ "skill" : "learning", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":8, "val": 0, "subtype": 46, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"stoneSkin" : {
|
||||
"addInfo" : 0,
|
||||
"subtype" : "spell.stoneSkin",
|
||||
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"vidomina":
|
||||
{
|
||||
@ -232,10 +275,17 @@
|
||||
[
|
||||
{ "skill" : "necromancy", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 12, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"necromancy" : {
|
||||
"subtype" : "skill.necromancy",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"nagash":
|
||||
{
|
||||
@ -248,9 +298,14 @@
|
||||
{ "skill" : "necromancy", "level": "basic" },
|
||||
{ "skill" : "intelligence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"gold" : {
|
||||
"subtype" : "resource.gold",
|
||||
"type" : "GENERATE_RESOURCE",
|
||||
"val" : 350
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,10 +9,17 @@
|
||||
{ "skill" : "leadership", "level": "basic" },
|
||||
{ "skill" : "armorer", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 23, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"armorer" : {
|
||||
"subtype" : "skill.armorer",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ufretin":
|
||||
{
|
||||
@ -24,10 +31,9 @@
|
||||
{ "skill" : "luck", "level": "basic" },
|
||||
{ "skill" : "resistance", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 16 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "dwarf"
|
||||
}
|
||||
},
|
||||
"jenova":
|
||||
{
|
||||
@ -38,10 +44,15 @@
|
||||
[
|
||||
{ "skill" : "archery", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"gold" : {
|
||||
"subtype" : "resource.gold",
|
||||
"type" : "GENERATE_RESOURCE",
|
||||
"val" : 350
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ryland":
|
||||
{
|
||||
@ -53,10 +64,9 @@
|
||||
{ "skill" : "diplomacy", "level": "basic" },
|
||||
{ "skill" : "leadership", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 22 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "dendroidGuard"
|
||||
}
|
||||
},
|
||||
"thorgrim":
|
||||
{
|
||||
@ -67,10 +77,17 @@
|
||||
[
|
||||
{ "skill" : "resistance", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 26, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"resistance" : {
|
||||
"subtype" : "skill.resistance",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ivor":
|
||||
{
|
||||
@ -82,10 +99,9 @@
|
||||
{ "skill" : "archery", "level": "basic" },
|
||||
{ "skill" : "offence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 18 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "woodElf"
|
||||
}
|
||||
},
|
||||
"clancy":
|
||||
{
|
||||
@ -97,10 +113,9 @@
|
||||
{ "skill" : "pathfinding", "level": "basic" },
|
||||
{ "skill" : "resistance", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 24 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "unicorn"
|
||||
}
|
||||
},
|
||||
"kyrre":
|
||||
{
|
||||
@ -112,10 +127,17 @@
|
||||
{ "skill" : "archery", "level": "basic" },
|
||||
{ "skill" : "logistics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 2, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"logistics" : {
|
||||
"subtype" : "skill.logistics",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"coronius":
|
||||
{
|
||||
@ -128,10 +150,15 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "scholar", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":8, "val": 0, "subtype": 55, "info": 1 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"slayer" : {
|
||||
"addInfo" : 1,
|
||||
"subtype" : "spell.slayer",
|
||||
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"uland":
|
||||
{
|
||||
@ -144,10 +171,16 @@
|
||||
{ "skill" : "wisdom", "level": "advanced" },
|
||||
{ "skill" : "ballistics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":3, "val": 3, "subtype": 37, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"cure" : {
|
||||
"subtype" : "spell.cure",
|
||||
"type" : "SPECIAL_SPELL_LEV",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"elleshar":
|
||||
{
|
||||
@ -160,10 +193,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "intelligence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 24, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"intelligence" : {
|
||||
"subtype" : "skill.intelligence",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"gem":
|
||||
{
|
||||
@ -176,10 +216,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "firstAid", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 27, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"firstAid" : {
|
||||
"subtype" : "skill.firstAid",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"malcom":
|
||||
{
|
||||
@ -192,10 +239,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "eagleEye", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 11, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"eagleEye" : {
|
||||
"subtype" : "skill.eagleEye",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"melodia":
|
||||
{
|
||||
@ -208,10 +262,14 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "luck", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":7, "val": 0, "subtype": 51, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"fortune" : {
|
||||
"subtype" : "spell.fortune",
|
||||
"type" : "MAXED_SPELL"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"alagar":
|
||||
{
|
||||
@ -224,10 +282,16 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "sorcery", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":3, "val": 3, "subtype": 16, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"iceBolt" : {
|
||||
"subtype" : "spell.iceBolt",
|
||||
"type" : "SPECIAL_SPELL_LEV",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"aeris":
|
||||
{
|
||||
@ -240,9 +304,8 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "scouting", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 20 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "pegasus"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,10 +10,14 @@
|
||||
[
|
||||
{ "skill" : "leadership", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":12, "val": 2, "subtype": 0, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"speed" : {
|
||||
"type" : "STACKS_SPEED",
|
||||
"val" : 2
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"adrienne":
|
||||
{
|
||||
@ -25,11 +29,9 @@
|
||||
"skills":
|
||||
[
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "fireMagic", "level": "expert" } ],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":11, "val": 14, "subtype": 0, "info": 3 }
|
||||
]
|
||||
{ "skill" : "fireMagic", "level": "expert" }
|
||||
],
|
||||
"specialty" : { "bonuses" : { } } // has expert fire magic as "specialty"
|
||||
},
|
||||
"catherine":
|
||||
{
|
||||
@ -42,10 +44,9 @@
|
||||
{ "skill" : "leadership", "level": "basic" },
|
||||
{ "skill" : "offence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 4 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "swordsman"
|
||||
}
|
||||
},
|
||||
"dracon":
|
||||
{
|
||||
@ -58,11 +59,18 @@
|
||||
[
|
||||
{ "skill" : "wisdom", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":9, "val": 0, "subtype": 8, "info": 136 },
|
||||
{ "type":9, "val": 0, "subtype": 34, "info": 136 }
|
||||
]
|
||||
"specialty" : {
|
||||
"base" : {
|
||||
"addInfo" : "creature.enchanter",
|
||||
"type" : "SPECIAL_UPGRADE"
|
||||
},
|
||||
"bonuses" : {
|
||||
"archMage2enchanter" : { "subtype" : "creature.archMage" },
|
||||
"mage2enchanter" : { "subtype" : "creature.mage" },
|
||||
"monk2enchanter" : { "subtype" : "creature.monk" },
|
||||
"zealot2enchanter" : { "subtype" : "creature.zealot" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"gelu":
|
||||
{
|
||||
@ -75,11 +83,18 @@
|
||||
{ "skill" : "archery", "level": "basic" },
|
||||
{ "skill" : "leadership", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":9, "val": 0, "subtype": 2, "info": 137 },
|
||||
{ "type":9, "val": 0, "subtype": 18, "info": 137 }
|
||||
]
|
||||
"specialty" : {
|
||||
"base" : {
|
||||
"addInfo" : "creature.sharpshooter",
|
||||
"type" : "SPECIAL_UPGRADE"
|
||||
},
|
||||
"bonuses" : {
|
||||
"archer2sharpshooter" : { "subtype" : "creature.archer" },
|
||||
"grandElf2sharpshooter" : { "subtype" : "creature.grandElf" },
|
||||
"marksman2sharpshooter" : { "subtype" : "creature.marksman" },
|
||||
"woodElf2sharpshooter" : { "subtype" : "creature.woodElf" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"kilgor":
|
||||
{
|
||||
@ -91,12 +106,33 @@
|
||||
[
|
||||
{ "skill" : "offence", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":4, "val": 5, "subtype": 1, "info": 96 },
|
||||
{ "type":4, "val": 5, "subtype": 2, "info": 96 },
|
||||
{ "type":4, "val": 10, "subtype": 3, "info": 96 }
|
||||
]
|
||||
"specialty" : {
|
||||
"base" : {
|
||||
"limiters" : [
|
||||
{
|
||||
"parameters" : [ "behemoth", true ],
|
||||
"type" : "CREATURE_TYPE_LIMITER"
|
||||
}
|
||||
]
|
||||
},
|
||||
"bonuses" : {
|
||||
"damage" : {
|
||||
"subtype" : 0,
|
||||
"type" : "CREATURE_DAMAGE",
|
||||
"val" : 10
|
||||
},
|
||||
"attack" : {
|
||||
"subtype" : "primSkill.attack",
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 5
|
||||
},
|
||||
"defence" : {
|
||||
"subtype" : "primSkill.defence",
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 5
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"undeadHaart": // undead version of Lord Haart
|
||||
{
|
||||
@ -109,12 +145,33 @@
|
||||
[
|
||||
{ "skill" : "necromancy", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":4, "val": 5, "subtype": 1, "info": 66 },
|
||||
{ "type":4, "val": 5, "subtype": 2, "info": 66 },
|
||||
{ "type":4, "val": 10, "subtype": 3, "info": 66 }
|
||||
]
|
||||
"specialty" : {
|
||||
"base" : {
|
||||
"limiters" : [
|
||||
{
|
||||
"parameters" : [ "blackKnight", true ],
|
||||
"type" : "CREATURE_TYPE_LIMITER"
|
||||
}
|
||||
]
|
||||
},
|
||||
"bonuses" : {
|
||||
"damage" : {
|
||||
"subtype" : 0,
|
||||
"type" : "CREATURE_DAMAGE",
|
||||
"val" : 10
|
||||
},
|
||||
"attack" : {
|
||||
"subtype" : "primSkill.attack",
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 5
|
||||
},
|
||||
"defence" : {
|
||||
"subtype" : "primSkill.defence",
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 5
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"mutare":
|
||||
{
|
||||
@ -128,11 +185,22 @@
|
||||
{ "skill" : "estates", "level": "basic" },
|
||||
{ "skill" : "tactics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":13, "val": 5, "subtype": 1, "info": 0 },
|
||||
{ "type":13, "val": 5, "subtype": 2, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"base" : {
|
||||
"limiters" : [
|
||||
{
|
||||
"parameters" : [ "DRAGON_NATURE" ],
|
||||
"type" : "HAS_ANOTHER_BONUS_LIMITER"
|
||||
}
|
||||
],
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 5
|
||||
},
|
||||
"bonuses" : {
|
||||
"attack" : { "subtype" : "primSkill.attack" },
|
||||
"defence" : { "subtype" : "primSkill.defence" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"roland":
|
||||
{
|
||||
@ -145,10 +213,9 @@
|
||||
{ "skill" : "leadership", "level": "basic" },
|
||||
{ "skill" : "armorer", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 4 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "griffin"
|
||||
}
|
||||
},
|
||||
"mutareDrake":
|
||||
{
|
||||
@ -162,11 +229,22 @@
|
||||
{ "skill" : "estates", "level": "basic" },
|
||||
{ "skill" : "tactics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":13, "val": 1, "subtype": 1, "info": 5 },
|
||||
{ "type":13, "val": 1, "subtype": 1, "info": 5 }
|
||||
],
|
||||
"specialty" : {
|
||||
"base" : {
|
||||
"limiters" : [
|
||||
{
|
||||
"parameters" : [ "DRAGON_NATURE" ],
|
||||
"type" : "HAS_ANOTHER_BONUS_LIMITER"
|
||||
}
|
||||
],
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 5
|
||||
},
|
||||
"bonuses" : {
|
||||
"attack" : { "subtype" : "primSkill.attack" },
|
||||
"defence" : { "subtype" : "primSkill.defence" }
|
||||
}
|
||||
},
|
||||
"army" :
|
||||
[
|
||||
{
|
||||
@ -194,10 +272,9 @@
|
||||
{ "skill" : "tactics", "level": "basic" },
|
||||
{ "skill" : "offence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 90 }
|
||||
],
|
||||
"specialty" : {
|
||||
"creature" : "ogre"
|
||||
},
|
||||
"army" :
|
||||
[
|
||||
{
|
||||
@ -225,12 +302,32 @@
|
||||
{ "skill" : "leadership", "level": "basic" },
|
||||
{ "skill" : "tactics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":4, "val": 4, "subtype": 1, "info": 54 },
|
||||
{ "type":4, "val": 2, "subtype": 2, "info": 54 },
|
||||
{ "type":4, "val": 1, "subtype": 5, "info": 54 }
|
||||
],
|
||||
"specialty" : {
|
||||
"base" : {
|
||||
"limiters" : [
|
||||
{
|
||||
"parameters" : [ "devil", true ],
|
||||
"type" : "CREATURE_TYPE_LIMITER"
|
||||
}
|
||||
]
|
||||
},
|
||||
"bonuses" : {
|
||||
"attack" : {
|
||||
"subtype" : "primSkill.attack",
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 4
|
||||
},
|
||||
"defence" : {
|
||||
"subtype" : "primSkill.defence",
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"val" : 2
|
||||
},
|
||||
"speed" : {
|
||||
"type" : "STACKS_SPEED",
|
||||
"val" : 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"army" :
|
||||
[
|
||||
{
|
||||
|
@ -9,10 +9,9 @@
|
||||
{ "skill" : "offence", "level": "basic" },
|
||||
{ "skill" : "ballistics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 94 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "cyclop"
|
||||
}
|
||||
},
|
||||
"gurnisson":
|
||||
{
|
||||
@ -24,10 +23,9 @@
|
||||
{ "skill" : "offence", "level": "basic" },
|
||||
{ "skill" : "artillery", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 146 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "ballista"
|
||||
}
|
||||
},
|
||||
"jabarkas":
|
||||
{
|
||||
@ -39,10 +37,9 @@
|
||||
{ "skill" : "offence", "level": "basic" },
|
||||
{ "skill" : "archery", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 88 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "orc"
|
||||
}
|
||||
},
|
||||
"shiva":
|
||||
{
|
||||
@ -54,10 +51,9 @@
|
||||
{ "skill" : "offence", "level": "basic" },
|
||||
{ "skill" : "scouting", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 92 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "roc"
|
||||
}
|
||||
},
|
||||
"gretchin":
|
||||
{
|
||||
@ -69,10 +65,9 @@
|
||||
{ "skill" : "offence", "level": "basic" },
|
||||
{ "skill" : "pathfinding", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 84 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "goblin"
|
||||
}
|
||||
},
|
||||
"krellion":
|
||||
{
|
||||
@ -84,10 +79,9 @@
|
||||
{ "skill" : "offence", "level": "basic" },
|
||||
{ "skill" : "resistance", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 90 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "ogre"
|
||||
}
|
||||
},
|
||||
"cragHack":
|
||||
{
|
||||
@ -98,10 +92,17 @@
|
||||
[
|
||||
{ "skill" : "offence", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 22, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"offence" : {
|
||||
"subtype" : "skill.offence",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tyraxor":
|
||||
{
|
||||
@ -113,10 +114,9 @@
|
||||
{ "skill" : "offence", "level": "basic" },
|
||||
{ "skill" : "tactics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 86 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "goblinWolfRider"
|
||||
}
|
||||
},
|
||||
"gird":
|
||||
{
|
||||
@ -129,10 +129,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "sorcery", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 25, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"sorcery" : {
|
||||
"subtype" : "skill.sorcery",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"vey":
|
||||
{
|
||||
@ -145,10 +152,9 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "leadership", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 90 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "ogre"
|
||||
}
|
||||
},
|
||||
"dessa":
|
||||
{
|
||||
@ -161,10 +167,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "logistics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 2, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"logistics" : {
|
||||
"subtype" : "skill.logistics",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"terek":
|
||||
{
|
||||
@ -177,10 +190,15 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "tactics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":8, "val": 0, "subtype": 53, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"haste" : {
|
||||
"addInfo" : 0,
|
||||
"subtype" : "spell.haste",
|
||||
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"zubin":
|
||||
{
|
||||
@ -193,10 +211,15 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "artillery", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":8, "val": 0, "subtype": 44, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"precision" : {
|
||||
"addInfo" : 0,
|
||||
"subtype" : "spell.precision",
|
||||
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"gundula":
|
||||
{
|
||||
@ -209,10 +232,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "offence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 25, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"sorcery" : {
|
||||
"subtype" : "skill.sorcery",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"oris":
|
||||
{
|
||||
@ -225,10 +255,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "eagleEye", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 11, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"eagleEye" : {
|
||||
"subtype" : "skill.eagleEye",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"saurug":
|
||||
{
|
||||
@ -241,9 +278,14 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "resistance", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":10, "val": 1, "subtype": 5, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"gems" : {
|
||||
"subtype" : "resource.gems",
|
||||
"type" : "GENERATE_RESOURCE",
|
||||
"val" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,10 +10,9 @@
|
||||
{ "skill" : "scouting", "level": "basic" },
|
||||
{ "skill" : "mysticism", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 30 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "stoneGargoyle"
|
||||
}
|
||||
},
|
||||
"thane":
|
||||
{
|
||||
@ -25,10 +24,9 @@
|
||||
[
|
||||
{ "skill" : "scholar", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 36 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "genie"
|
||||
}
|
||||
},
|
||||
"josephine":
|
||||
{
|
||||
@ -41,10 +39,9 @@
|
||||
{ "skill" : "mysticism", "level": "basic" },
|
||||
{ "skill" : "sorcery", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 32 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "ironGolem"
|
||||
}
|
||||
},
|
||||
"neela":
|
||||
{
|
||||
@ -57,10 +54,17 @@
|
||||
{ "skill" : "scholar", "level": "basic" },
|
||||
{ "skill" : "armorer", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 23, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"armorer" : {
|
||||
"subtype" : "skill.armorer",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"torosar ":
|
||||
{
|
||||
@ -73,10 +77,9 @@
|
||||
{ "skill" : "mysticism", "level": "basic" },
|
||||
{ "skill" : "tactics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 146 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "ballista"
|
||||
}
|
||||
},
|
||||
"fafner":
|
||||
{
|
||||
@ -89,10 +92,9 @@
|
||||
{ "skill" : "scholar", "level": "basic" },
|
||||
{ "skill" : "resistance", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 38 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "naga"
|
||||
}
|
||||
},
|
||||
"rissa":
|
||||
{
|
||||
@ -105,10 +107,15 @@
|
||||
{ "skill" : "mysticism", "level": "basic" },
|
||||
{ "skill" : "offence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":10, "val": 1, "subtype": 1, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"mercury" : {
|
||||
"subtype" : "resource.mercury",
|
||||
"type" : "GENERATE_RESOURCE",
|
||||
"val" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"iona":
|
||||
{
|
||||
@ -121,10 +128,9 @@
|
||||
{ "skill" : "scholar", "level": "basic" },
|
||||
{ "skill" : "intelligence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 36 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "genie"
|
||||
}
|
||||
},
|
||||
"astral":
|
||||
{
|
||||
@ -136,10 +142,16 @@
|
||||
[
|
||||
{ "skill" : "wisdom", "level": "advanced" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":3, "val": 3, "subtype": 60, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"hypnotize" : {
|
||||
"subtype" : "spell.hypnotize",
|
||||
"type" : "SPECIAL_SPELL_LEV",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"halon":
|
||||
{
|
||||
@ -152,10 +164,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "mysticism", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 8, "info": 1 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"mysticism" : {
|
||||
"subtype" : "skill.mysticism",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"serena":
|
||||
{
|
||||
@ -168,10 +187,17 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "eagleEye", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":2, "val": 5, "subtype": 11, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"eagleEye" : {
|
||||
"subtype" : "skill.eagleEye",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 5,
|
||||
"valueType" : "PERCENT_TO_BASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"daremyth":
|
||||
{
|
||||
@ -184,10 +210,14 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "intelligence", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":7, "val": 0, "subtype": 51, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"fortune" : {
|
||||
"subtype" : "spell.fortune",
|
||||
"type" : "MAXED_SPELL"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"theodorus":
|
||||
{
|
||||
@ -200,10 +230,9 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "ballistics", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":1, "val": 0, "subtype": 0, "info": 34 }
|
||||
]
|
||||
"specialty" : {
|
||||
"creature" : "mage"
|
||||
}
|
||||
},
|
||||
"solmyr":
|
||||
{
|
||||
@ -216,10 +245,16 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "sorcery", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":3, "val": 3, "subtype": 19, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"chainLightning" : {
|
||||
"subtype" : "spell.chainLightning",
|
||||
"type" : "SPECIAL_SPELL_LEV",
|
||||
"updater" : "TIMES_HERO_LEVEL",
|
||||
"val" : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cyra":
|
||||
{
|
||||
@ -232,10 +267,15 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "diplomacy", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":8, "val": 0, "subtype": 53, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"haste" : {
|
||||
"addInfo" : 0,
|
||||
"subtype" : "spell.haste",
|
||||
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"aine":
|
||||
{
|
||||
@ -248,9 +288,14 @@
|
||||
{ "skill" : "wisdom", "level": "basic" },
|
||||
{ "skill" : "scholar", "level": "basic" }
|
||||
],
|
||||
"specialties":
|
||||
[
|
||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
||||
]
|
||||
"specialty" : {
|
||||
"bonuses" : {
|
||||
"gold" : {
|
||||
"subtype" : "resource.gold",
|
||||
"type" : "GENERATE_RESOURCE",
|
||||
"val" : 350
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +70,30 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"updater" : {
|
||||
"anyOf" : [
|
||||
{
|
||||
"type" : "string"
|
||||
},
|
||||
{
|
||||
"description" : "updater",
|
||||
"type" : "object",
|
||||
"required" : ["type", "parameters"],
|
||||
"additionalProperties" : false,
|
||||
"properties" : {
|
||||
"type" : {
|
||||
"type" : "string",
|
||||
"description" : "type"
|
||||
},
|
||||
"parameters": {
|
||||
"type" : "array",
|
||||
"description" : "parameters",
|
||||
"additionalItems" : true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sourceID": {
|
||||
"type":"number",
|
||||
"description": "sourceID"
|
||||
|
@ -115,24 +115,48 @@
|
||||
"additionalItems" : true
|
||||
},
|
||||
"specialty": {
|
||||
"type":"array",
|
||||
"description": "Description of hero specialty using bonus system",
|
||||
"items": {
|
||||
"type":"object",
|
||||
"additionalProperties" : false,
|
||||
"required" : [ "bonuses" ],
|
||||
"properties":{
|
||||
"growsWithLevel" : {
|
||||
"type" : "boolean",
|
||||
"description" : "Specialty growth with level, so far only SECONDARY_SKILL_PREMY and PRIMATY SKILL with creature limiter can grow"
|
||||
},
|
||||
"bonuses": {
|
||||
"type":"array",
|
||||
"description": "List of bonuses",
|
||||
"items": { "$ref" : "vcmi:bonus" }
|
||||
"anyOf" : [
|
||||
{
|
||||
"type":"array",
|
||||
"description": "Description of hero specialty using bonus system (deprecated)",
|
||||
"items": {
|
||||
"type" : "object",
|
||||
"additionalProperties" : false,
|
||||
"required" : [ "bonuses" ],
|
||||
"properties" : {
|
||||
"growsWithLevel" : {
|
||||
"type" : "boolean",
|
||||
"description" : "Specialty growth with level. Deprecated, use bonuses with updaters instead."
|
||||
},
|
||||
"bonuses" : {
|
||||
"type" : "array",
|
||||
"description" : "List of bonuses",
|
||||
"items" : { "$ref" : "vcmi:bonus" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type" : "object",
|
||||
"description": "Description of hero specialty using bonus system",
|
||||
"additionalProperties" : false,
|
||||
"properties" : {
|
||||
"base" : {
|
||||
"type" : "object",
|
||||
"description" : "Will be merged with all bonuses."
|
||||
},
|
||||
"bonuses" : {
|
||||
"type" : "object",
|
||||
"description" : "Set of bonuses",
|
||||
"additionalProperties" : { "$ref" : "vcmi:bonus" }
|
||||
},
|
||||
"creature" : {
|
||||
"type" : "string",
|
||||
"description" : "Name of base creature to grant standard specialty to."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"spellbook": {
|
||||
"type":"array",
|
||||
|
@ -226,7 +226,8 @@
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"type" : "MANA_REGENERATION",
|
||||
"subtype" : "skill.mysticism",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
|
@ -379,31 +379,310 @@ void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node)
|
||||
}
|
||||
}
|
||||
|
||||
// add standard creature specialty to result
|
||||
void AddSpecialtyForCreature(int creatureID, std::shared_ptr<Bonus> bonus, std::vector<std::shared_ptr<Bonus>> &result)
|
||||
{
|
||||
const CCreature &specBaseCreature = *VLC->creh->creatures[creatureID]; //base creature in which we have specialty
|
||||
|
||||
bonus->limiter.reset(new CCreatureTypeLimiter(specBaseCreature, true));
|
||||
bonus->type = Bonus::STACKS_SPEED;
|
||||
bonus->valType = Bonus::ADDITIVE_VALUE;
|
||||
bonus->val = 1;
|
||||
result.push_back(bonus);
|
||||
|
||||
// attack and defense may differ for upgraded creatures => separate bonuses
|
||||
std::vector<int> specTargets;
|
||||
specTargets.push_back(creatureID);
|
||||
specTargets.insert(specTargets.end(), specBaseCreature.upgrades.begin(), specBaseCreature.upgrades.end());
|
||||
|
||||
for(int cid : specTargets)
|
||||
{
|
||||
const CCreature &specCreature = *VLC->creh->creatures[cid];
|
||||
bonus = std::make_shared<Bonus>(*bonus);
|
||||
bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, false));
|
||||
bonus->type = Bonus::PRIMARY_SKILL;
|
||||
bonus->val = 0;
|
||||
|
||||
int stepSize = specCreature.level ? specCreature.level : 5;
|
||||
|
||||
bonus->subtype = PrimarySkill::ATTACK;
|
||||
bonus->updater.reset(new GrowsWithLevelUpdater(specCreature.getAttack(false), stepSize));
|
||||
result.push_back(bonus);
|
||||
|
||||
bonus = std::make_shared<Bonus>(*bonus);
|
||||
bonus->subtype = PrimarySkill::DEFENSE;
|
||||
bonus->updater.reset(new GrowsWithLevelUpdater(specCreature.getDefence(false), stepSize));
|
||||
result.push_back(bonus);
|
||||
}
|
||||
}
|
||||
|
||||
// convert deprecated format
|
||||
std::vector<std::shared_ptr<Bonus>> SpecialtyInfoToBonuses(const SSpecialtyInfo & spec, int sid)
|
||||
{
|
||||
std::vector<std::shared_ptr<Bonus>> result;
|
||||
|
||||
std::shared_ptr<Bonus> bonus = std::make_shared<Bonus>();
|
||||
bonus->duration = Bonus::PERMANENT;
|
||||
bonus->source = Bonus::HERO_SPECIAL;
|
||||
bonus->sid = sid;
|
||||
bonus->val = spec.val;
|
||||
|
||||
switch (spec.type)
|
||||
{
|
||||
case 1: //creature specialty
|
||||
AddSpecialtyForCreature(spec.additionalinfo, bonus, result);
|
||||
break;
|
||||
case 2: //secondary skill
|
||||
bonus->type = Bonus::SECONDARY_SKILL_PREMY;
|
||||
bonus->valType = Bonus::PERCENT_TO_BASE;
|
||||
bonus->subtype = spec.subtype;
|
||||
bonus->updater.reset(new TimesHeroLevelUpdater());
|
||||
result.push_back(bonus);
|
||||
break;
|
||||
case 3: //spell damage bonus, level dependent but calculated elsewhere
|
||||
bonus->type = Bonus::SPECIAL_SPELL_LEV;
|
||||
bonus->subtype = spec.subtype;
|
||||
bonus->updater.reset(new TimesHeroLevelUpdater());
|
||||
result.push_back(bonus);
|
||||
break;
|
||||
case 4: //creature stat boost
|
||||
switch (spec.subtype)
|
||||
{
|
||||
case 1:
|
||||
bonus->type = Bonus::PRIMARY_SKILL;
|
||||
bonus->subtype = PrimarySkill::ATTACK;
|
||||
break;
|
||||
case 2:
|
||||
bonus->type = Bonus::PRIMARY_SKILL;
|
||||
bonus->subtype = PrimarySkill::DEFENSE;
|
||||
break;
|
||||
case 3:
|
||||
bonus->type = Bonus::CREATURE_DAMAGE;
|
||||
bonus->subtype = 0; //both min and max
|
||||
break;
|
||||
case 4:
|
||||
bonus->type = Bonus::STACK_HEALTH;
|
||||
break;
|
||||
case 5:
|
||||
bonus->type = Bonus::STACKS_SPEED;
|
||||
break;
|
||||
default:
|
||||
logMod->warn("Unknown subtype for specialty 4");
|
||||
return result;
|
||||
}
|
||||
bonus->valType = Bonus::ADDITIVE_VALUE;
|
||||
bonus->limiter.reset(new CCreatureTypeLimiter(*VLC->creh->creatures[spec.additionalinfo], true));
|
||||
result.push_back(bonus);
|
||||
break;
|
||||
case 5: //spell damage bonus in percent
|
||||
bonus->type = Bonus::SPECIFIC_SPELL_DAMAGE;
|
||||
bonus->valType = Bonus::BASE_NUMBER; //current spell system is screwed
|
||||
bonus->subtype = spec.subtype; //spell id
|
||||
result.push_back(bonus);
|
||||
break;
|
||||
case 6: //damage bonus for bless (Adela)
|
||||
bonus->type = Bonus::SPECIAL_BLESS_DAMAGE;
|
||||
bonus->subtype = spec.subtype; //spell id if you ever wanted to use it otherwise
|
||||
bonus->additionalInfo = spec.additionalinfo; //damage factor
|
||||
bonus->updater.reset(new TimesHeroLevelUpdater());
|
||||
result.push_back(bonus);
|
||||
break;
|
||||
case 7: //maxed mastery for spell
|
||||
bonus->type = Bonus::MAXED_SPELL;
|
||||
bonus->subtype = spec.subtype; //spell id
|
||||
result.push_back(bonus);
|
||||
break;
|
||||
case 8: //peculiar spells - enchantments
|
||||
bonus->type = Bonus::SPECIAL_PECULIAR_ENCHANT;
|
||||
bonus->subtype = spec.subtype; //spell id
|
||||
bonus->additionalInfo = spec.additionalinfo; //0, 1 for Coronius
|
||||
result.push_back(bonus);
|
||||
break;
|
||||
case 9: //upgrade creatures
|
||||
{
|
||||
const auto &creatures = VLC->creh->creatures;
|
||||
bonus->type = Bonus::SPECIAL_UPGRADE;
|
||||
bonus->subtype = spec.subtype; //base id
|
||||
bonus->additionalInfo = spec.additionalinfo; //target id
|
||||
result.push_back(bonus);
|
||||
//propagate for regular upgrades of base creature
|
||||
for(auto cre_id : creatures[spec.subtype]->upgrades)
|
||||
{
|
||||
std::shared_ptr<Bonus> upgradeUpgradedVersion = std::make_shared<Bonus>(*bonus);
|
||||
upgradeUpgradedVersion->subtype = cre_id;
|
||||
result.push_back(upgradeUpgradedVersion);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 10: //resource generation
|
||||
bonus->type = Bonus::GENERATE_RESOURCE;
|
||||
bonus->subtype = spec.subtype;
|
||||
result.push_back(bonus);
|
||||
break;
|
||||
case 11: //starting skill with mastery (Adrienne)
|
||||
logMod->warn("Secondary skill mastery is no longer supported as specialty.");
|
||||
break;
|
||||
case 12: //army speed
|
||||
bonus->type = Bonus::STACKS_SPEED;
|
||||
result.push_back(bonus);
|
||||
break;
|
||||
case 13: //Dragon bonuses (Mutare)
|
||||
bonus->type = Bonus::PRIMARY_SKILL;
|
||||
bonus->valType = Bonus::ADDITIVE_VALUE;
|
||||
switch(spec.subtype)
|
||||
{
|
||||
case 1:
|
||||
bonus->subtype = PrimarySkill::ATTACK;
|
||||
break;
|
||||
case 2:
|
||||
bonus->subtype = PrimarySkill::DEFENSE;
|
||||
break;
|
||||
}
|
||||
bonus->limiter.reset(new HasAnotherBonusLimiter(Bonus::DRAGON_NATURE));
|
||||
result.push_back(bonus);
|
||||
break;
|
||||
default:
|
||||
logMod->warn("Unknown hero specialty %d", spec.type);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// convert deprecated format
|
||||
std::vector<std::shared_ptr<Bonus>> SpecialtyBonusToBonuses(const SSpecialtyBonus & spec)
|
||||
{
|
||||
std::vector<std::shared_ptr<Bonus>> result;
|
||||
for(std::shared_ptr<Bonus> oldBonus : spec.bonuses)
|
||||
{
|
||||
if(oldBonus->type == Bonus::SPECIAL_SPELL_LEV || oldBonus->type == Bonus::SPECIAL_BLESS_DAMAGE)
|
||||
{
|
||||
// these bonuses used to auto-scale with hero level
|
||||
std::shared_ptr<Bonus> newBonus = std::make_shared<Bonus>(*oldBonus);
|
||||
newBonus->updater = std::make_shared<TimesHeroLevelUpdater>();
|
||||
result.push_back(newBonus);
|
||||
}
|
||||
else if(spec.growsWithLevel)
|
||||
{
|
||||
std::shared_ptr<Bonus> newBonus = std::make_shared<Bonus>(*oldBonus);
|
||||
switch(newBonus->type)
|
||||
{
|
||||
case Bonus::SECONDARY_SKILL_PREMY:
|
||||
break; // ignore - used to be overwritten based on SPECIAL_SECONDARY_SKILL
|
||||
case Bonus::SPECIAL_SECONDARY_SKILL:
|
||||
newBonus->type = Bonus::SECONDARY_SKILL_PREMY;
|
||||
newBonus->updater = std::make_shared<TimesHeroLevelUpdater>();
|
||||
result.push_back(newBonus);
|
||||
break;
|
||||
case Bonus::PRIMARY_SKILL:
|
||||
if((newBonus->subtype == PrimarySkill::ATTACK || newBonus->subtype == PrimarySkill::DEFENSE) && newBonus->limiter)
|
||||
{
|
||||
const std::shared_ptr<CCreatureTypeLimiter> creatureLimiter = std::dynamic_pointer_cast<CCreatureTypeLimiter>(newBonus->limiter);
|
||||
if(creatureLimiter)
|
||||
{
|
||||
const CCreature * cre = creatureLimiter->creature;
|
||||
int creStat = newBonus->subtype == PrimarySkill::ATTACK ? cre->getAttack(false) : cre->getDefence(false);
|
||||
int creLevel = cre->level ? cre->level : 5;
|
||||
newBonus->updater = std::make_shared<GrowsWithLevelUpdater>(creStat, creLevel);
|
||||
}
|
||||
result.push_back(newBonus);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
result.push_back(newBonus);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(oldBonus);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void CHeroHandler::beforeValidate(JsonNode & object)
|
||||
{
|
||||
//handle "base" specialty info
|
||||
JsonNode & specialtyNode = object["specialty"];
|
||||
if(specialtyNode.getType() == JsonNode::JsonType::DATA_STRUCT)
|
||||
{
|
||||
const JsonNode & base = specialtyNode["base"];
|
||||
if(!base.isNull())
|
||||
{
|
||||
if(specialtyNode["bonuses"].isNull())
|
||||
{
|
||||
logMod->warn("specialty has base without bonuses");
|
||||
}
|
||||
else
|
||||
{
|
||||
JsonMap & bonuses = specialtyNode["bonuses"].Struct();
|
||||
for(std::pair<std::string, JsonNode> keyValue : bonuses)
|
||||
JsonUtils::inherit(bonuses[keyValue.first], base);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CHeroHandler::loadHeroSpecialty(CHero * hero, const JsonNode & node)
|
||||
{
|
||||
//deprecated, used only for original spciealties
|
||||
for(const JsonNode &specialty : node["specialties"].Vector())
|
||||
int sid = hero->ID.getNum();
|
||||
auto prepSpec = [=](std::shared_ptr<Bonus> bonus)
|
||||
{
|
||||
SSpecialtyInfo spec;
|
||||
bonus->duration = Bonus::PERMANENT;
|
||||
bonus->source = Bonus::HERO_SPECIAL;
|
||||
bonus->sid = sid;
|
||||
return bonus;
|
||||
};
|
||||
|
||||
spec.type = specialty["type"].Float();
|
||||
spec.val = specialty["val"].Float();
|
||||
spec.subtype = specialty["subtype"].Float();
|
||||
spec.additionalinfo = specialty["info"].Float();
|
||||
|
||||
hero->spec.push_back(spec); //put a copy of dummy
|
||||
}
|
||||
//new format, using bonus system
|
||||
for(const JsonNode &specialty : node["specialty"].Vector())
|
||||
//deprecated, used only for original specialties
|
||||
const JsonNode & specialtiesNode = node["specialties"];
|
||||
if (!specialtiesNode.isNull())
|
||||
{
|
||||
SSpecialtyBonus hs;
|
||||
hs.growsWithLevel = specialty["growsWithLevel"].Bool();
|
||||
for (const JsonNode & bonus : specialty["bonuses"].Vector())
|
||||
logMod->warn("Hero %s has deprecated specialties format.", hero->identifier);
|
||||
for(const JsonNode &specialty : specialtiesNode.Vector())
|
||||
{
|
||||
auto b = JsonUtils::parseBonus(bonus);
|
||||
hs.bonuses.push_back (b);
|
||||
SSpecialtyInfo spec;
|
||||
spec.type = specialty["type"].Integer();
|
||||
spec.val = specialty["val"].Integer();
|
||||
spec.subtype = specialty["subtype"].Integer();
|
||||
spec.additionalinfo = specialty["info"].Integer();
|
||||
//we convert after loading completes, to have all identifiers for json logging
|
||||
hero->specDeprecated.push_back(spec);
|
||||
}
|
||||
}
|
||||
//new(er) format, using bonus system
|
||||
const JsonNode & specialtyNode = node["specialty"];
|
||||
if(specialtyNode.getType() == JsonNode::JsonType::DATA_VECTOR)
|
||||
{
|
||||
//deprecated middle-aged format
|
||||
for(const JsonNode & specialty : node["specialty"].Vector())
|
||||
{
|
||||
SSpecialtyBonus hs;
|
||||
hs.growsWithLevel = specialty["growsWithLevel"].Bool();
|
||||
for (const JsonNode & bonus : specialty["bonuses"].Vector())
|
||||
hs.bonuses.push_back(prepSpec(JsonUtils::parseBonus(bonus)));
|
||||
hero->specialtyDeprecated.push_back(hs);
|
||||
}
|
||||
}
|
||||
else if(specialtyNode.getType() == JsonNode::JsonType::DATA_STRUCT)
|
||||
{
|
||||
//creature specialty - alias for simplicity
|
||||
if(!specialtyNode["creature"].isNull())
|
||||
{
|
||||
VLC->modh->identifiers.requestIdentifier("creature", specialtyNode["creature"], [hero](si32 creature) {
|
||||
// use legacy format for delayed conversion (must have all creature data loaded, also for upgrades)
|
||||
SSpecialtyInfo spec;
|
||||
spec.type = 1;
|
||||
spec.additionalinfo = creature;
|
||||
hero->specDeprecated.push_back(spec);
|
||||
});
|
||||
}
|
||||
if(!specialtyNode["bonuses"].isNull())
|
||||
{
|
||||
//proper new format
|
||||
for(auto keyValue : specialtyNode["bonuses"].Struct())
|
||||
hero->specialty.push_back(prepSpec(JsonUtils::parseBonus(keyValue.second)));
|
||||
}
|
||||
hero->specialty.push_back (hs); //now, how to get CGHeroInstance from it?
|
||||
}
|
||||
}
|
||||
|
||||
@ -561,6 +840,65 @@ void CHeroHandler::loadObject(std::string scope, std::string name, const JsonNod
|
||||
VLC->modh->identifiers.registerObject(scope, "hero", name, object->ID.getNum());
|
||||
}
|
||||
|
||||
void CHeroHandler::afterLoadFinalization()
|
||||
{
|
||||
for(ConstTransitivePtr<CHero> hero : heroes)
|
||||
{
|
||||
if(hero->specDeprecated.size() > 0 || hero->specialtyDeprecated.size() > 0)
|
||||
{
|
||||
logMod->debug("Converting specialty format for hero %s(%s)", hero->identifier, VLC->townh->encodeFaction(hero->heroClass->faction));
|
||||
std::vector<std::shared_ptr<Bonus>> convertedBonuses;
|
||||
for(const SSpecialtyInfo & spec : hero->specDeprecated)
|
||||
{
|
||||
for(std::shared_ptr<Bonus> b : SpecialtyInfoToBonuses(spec, hero->ID.getNum()))
|
||||
convertedBonuses.push_back(b);
|
||||
}
|
||||
for(const SSpecialtyBonus & spec : hero->specialtyDeprecated)
|
||||
{
|
||||
for(std::shared_ptr<Bonus> b : SpecialtyBonusToBonuses(spec))
|
||||
convertedBonuses.push_back(b);
|
||||
}
|
||||
hero->specDeprecated.clear();
|
||||
hero->specialtyDeprecated.clear();
|
||||
// store and create json for logging
|
||||
std::vector<JsonNode> specVec;
|
||||
std::vector<std::string> specNames;
|
||||
for(std::shared_ptr<Bonus> bonus : convertedBonuses)
|
||||
{
|
||||
hero->specialty.push_back(bonus);
|
||||
specVec.push_back(bonus->toJsonNode());
|
||||
// find fitting & unique bonus name
|
||||
std::string bonusName = bonus->nameForBonus();
|
||||
if(vstd::contains(specNames, bonusName))
|
||||
{
|
||||
int suffix = 2;
|
||||
while(vstd::contains(specNames, bonusName + std::to_string(suffix)))
|
||||
suffix++;
|
||||
bonusName += std::to_string(suffix);
|
||||
}
|
||||
specNames.push_back(bonusName);
|
||||
}
|
||||
// log new format for easy copy-and-paste
|
||||
JsonNode specNode(JsonNode::JsonType::DATA_STRUCT);
|
||||
if(specVec.size() > 1)
|
||||
{
|
||||
JsonNode base = JsonUtils::intersect(specVec);
|
||||
if(base.containsBaseData())
|
||||
{
|
||||
specNode["base"] = base;
|
||||
for(JsonNode & node : specVec)
|
||||
node = JsonUtils::difference(node, base);
|
||||
}
|
||||
}
|
||||
// add json for bonuses
|
||||
specNode["bonuses"].Struct();
|
||||
for(int i = 0; i < specVec.size(); i++)
|
||||
specNode["bonuses"][specNames[i]] = specVec[i];
|
||||
logMod->trace("\"specialty\" : %s", specNode.toJson(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui32 CHeroHandler::level (ui64 experience) const
|
||||
{
|
||||
return boost::range::upper_bound(expPerLevel, experience) - std::begin(expPerLevel);
|
||||
|
@ -71,8 +71,9 @@ public:
|
||||
|
||||
CHeroClass * heroClass;
|
||||
std::vector<std::pair<SecondarySkill, ui8> > secSkillsInit; //initial secondary skills; first - ID of skill, second - level of skill (1 - basic, 2 - adv., 3 - expert)
|
||||
std::vector<SSpecialtyInfo> spec;
|
||||
std::vector<SSpecialtyBonus> specialty;
|
||||
std::vector<SSpecialtyInfo> specDeprecated;
|
||||
std::vector<SSpecialtyBonus> specialtyDeprecated;
|
||||
BonusList specialty;
|
||||
std::set<SpellID> spells;
|
||||
bool haveSpellBook;
|
||||
bool special; // hero is special and won't be placed in game (unless preset on map), e.g. campaign heroes
|
||||
@ -98,8 +99,15 @@ public:
|
||||
h & initialArmy;
|
||||
h & heroClass;
|
||||
h & secSkillsInit;
|
||||
h & spec;
|
||||
h & specialty;
|
||||
if(version >= 781)
|
||||
{
|
||||
h & specialty;
|
||||
}
|
||||
else
|
||||
{
|
||||
h & specDeprecated;
|
||||
h & specialtyDeprecated;
|
||||
}
|
||||
h & spells;
|
||||
h & haveSpellBook;
|
||||
h & sex;
|
||||
@ -120,6 +128,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// convert deprecated format
|
||||
std::vector<std::shared_ptr<Bonus>> SpecialtyInfoToBonuses(const SSpecialtyInfo & spec, int sid);
|
||||
std::vector<std::shared_ptr<Bonus>> SpecialtyBonusToBonuses(const SSpecialtyBonus & spec);
|
||||
|
||||
class DLL_LINKAGE CHeroClass
|
||||
{
|
||||
public:
|
||||
@ -289,8 +301,10 @@ public:
|
||||
|
||||
std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
|
||||
|
||||
void beforeValidate(JsonNode & object);
|
||||
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();
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "CSkillHandler.h"
|
||||
#include "CStack.h"
|
||||
#include "CArtHandler.h"
|
||||
#include "StringConstants.h"
|
||||
|
||||
#define FOREACH_PARENT(pname) TNodes lparents; getParents(lparents); for(CBonusSystemNode *pname : lparents)
|
||||
#define FOREACH_CPARENT(pname) TCNodes lparents; getParents(lparents); for(const CBonusSystemNode *pname : lparents)
|
||||
@ -80,6 +81,12 @@ const std::map<std::string, TPropagatorPtr> bonusPropagatorMap =
|
||||
{"GLOBAL_EFFECT", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::GLOBAL_EFFECTS)}
|
||||
}; //untested
|
||||
|
||||
const std::map<std::string, TUpdaterPtr> bonusUpdaterMap =
|
||||
{
|
||||
{"TIMES_HERO_LEVEL", std::make_shared<TimesHeroLevelUpdater>()},
|
||||
{"TIMES_STACK_LEVEL", std::make_shared<TimesStackLevelUpdater>()}
|
||||
};
|
||||
|
||||
///CBonusProxy
|
||||
CBonusProxy::CBonusProxy(const IBonusBearer * Target, CSelector Selector)
|
||||
: cachedLast(0),
|
||||
@ -579,22 +586,28 @@ void CBonusSystemNode::getParents(TNodes &out)
|
||||
|
||||
void CBonusSystemNode::getBonusesRec(BonusList &out, const CSelector &selector, const CSelector &limit) const
|
||||
{
|
||||
BonusList beforeUpdate;
|
||||
FOREACH_CPARENT(p)
|
||||
{
|
||||
p->getBonusesRec(out, selector, limit);
|
||||
p->getBonusesRec(beforeUpdate, selector, limit);
|
||||
}
|
||||
bonuses.getBonuses(beforeUpdate, selector, limit);
|
||||
|
||||
bonuses.getBonuses(out, selector, limit);
|
||||
for(auto b : beforeUpdate)
|
||||
out.push_back(update(b));
|
||||
}
|
||||
|
||||
void CBonusSystemNode::getAllBonusesRec(BonusList &out) const
|
||||
{
|
||||
BonusList beforeUpdate;
|
||||
FOREACH_CPARENT(p)
|
||||
{
|
||||
p->getAllBonusesRec(out);
|
||||
p->getAllBonusesRec(beforeUpdate);
|
||||
}
|
||||
bonuses.getAllBonuses(beforeUpdate);
|
||||
|
||||
bonuses.getAllBonuses(out);
|
||||
for(auto b : beforeUpdate)
|
||||
out.push_back(update(b));
|
||||
}
|
||||
|
||||
const TBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root, const std::string &cachingStr) const
|
||||
@ -686,6 +699,13 @@ const TBonusListPtr CBonusSystemNode::getAllBonusesWithoutCaching(const CSelecto
|
||||
return ret;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Bonus> CBonusSystemNode::update(const std::shared_ptr<Bonus> b) const
|
||||
{
|
||||
if(b->updater)
|
||||
return b->updater->update(b, *this);
|
||||
return b;
|
||||
}
|
||||
|
||||
CBonusSystemNode::CBonusSystemNode()
|
||||
: bonuses(true),
|
||||
exportedBonuses(true),
|
||||
@ -782,7 +802,7 @@ void CBonusSystemNode::popBonuses(const CSelector &s)
|
||||
child->popBonuses(s);
|
||||
}
|
||||
|
||||
void CBonusSystemNode::updateBonuses(const CSelector &s)
|
||||
void CBonusSystemNode::reduceBonusDurations(const CSelector &s)
|
||||
{
|
||||
BonusList bl;
|
||||
exportedBonuses.getBonuses(bl, s, Selector::all);
|
||||
@ -794,7 +814,7 @@ void CBonusSystemNode::updateBonuses(const CSelector &s)
|
||||
}
|
||||
|
||||
for(CBonusSystemNode *child : children)
|
||||
child->updateBonuses(s);
|
||||
child->reduceBonusDurations(s);
|
||||
}
|
||||
|
||||
void CBonusSystemNode::addNewBonus(const std::shared_ptr<Bonus>& b)
|
||||
@ -1142,6 +1162,85 @@ std::string Bonus::Description() const
|
||||
return str.str();
|
||||
}
|
||||
|
||||
JsonNode subtypeToJson(Bonus::BonusType type, int subtype)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case Bonus::PRIMARY_SKILL:
|
||||
return JsonUtils::stringNode("primSkill." + PrimarySkill::names[subtype]);
|
||||
case Bonus::SECONDARY_SKILL_PREMY:
|
||||
return JsonUtils::stringNode("skill." + NSecondarySkill::names[subtype]);
|
||||
case Bonus::SPECIAL_SPELL_LEV:
|
||||
case Bonus::SPECIFIC_SPELL_DAMAGE:
|
||||
case Bonus::SPECIAL_BLESS_DAMAGE:
|
||||
case Bonus::MAXED_SPELL:
|
||||
case Bonus::SPECIAL_PECULIAR_ENCHANT:
|
||||
return JsonUtils::stringNode("spell." + (*VLC->spellh)[SpellID::ESpellID(subtype)]->identifier);
|
||||
case Bonus::SPECIAL_UPGRADE:
|
||||
return JsonUtils::stringNode("creature." + CreatureID::encode(subtype));
|
||||
case Bonus::GENERATE_RESOURCE:
|
||||
return JsonUtils::stringNode("resource." + GameConstants::RESOURCE_NAMES[subtype]);
|
||||
default:
|
||||
return JsonUtils::intNode(subtype);
|
||||
}
|
||||
}
|
||||
|
||||
JsonNode additionalInfoToJson(Bonus::BonusType type, int addInfo)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case Bonus::SPECIAL_UPGRADE:
|
||||
return JsonUtils::stringNode("creature." + CreatureID::encode(addInfo));
|
||||
default:
|
||||
return JsonUtils::intNode(addInfo);
|
||||
}
|
||||
}
|
||||
|
||||
JsonNode Bonus::toJsonNode() const
|
||||
{
|
||||
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
|
||||
|
||||
root["type"].String() = vstd::findKey(bonusNameMap, type);
|
||||
if(subtype != -1)
|
||||
root["subtype"] = subtypeToJson(type, subtype);
|
||||
if(additionalInfo != -1)
|
||||
root["addInfo"] = additionalInfoToJson(type, additionalInfo);
|
||||
if(val != 0)
|
||||
root["val"].Integer() = val;
|
||||
if(valType != ADDITIVE_VALUE)
|
||||
root["valueType"].String() = vstd::findKey(bonusValueMap, valType);
|
||||
if(limiter)
|
||||
root["limiters"].Vector().push_back(limiter->toJsonNode());
|
||||
if(updater)
|
||||
root["updater"] = updater->toJsonNode();
|
||||
return root;
|
||||
}
|
||||
|
||||
std::string Bonus::nameForBonus() const
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case Bonus::PRIMARY_SKILL:
|
||||
return PrimarySkill::names[subtype];
|
||||
case Bonus::SECONDARY_SKILL_PREMY:
|
||||
return NSecondarySkill::names[subtype];
|
||||
case Bonus::SPECIAL_SPELL_LEV:
|
||||
case Bonus::SPECIFIC_SPELL_DAMAGE:
|
||||
case Bonus::SPECIAL_BLESS_DAMAGE:
|
||||
case Bonus::MAXED_SPELL:
|
||||
case Bonus::SPECIAL_PECULIAR_ENCHANT:
|
||||
return (*VLC->spellh)[SpellID::ESpellID(subtype)]->identifier;
|
||||
case Bonus::SPECIAL_UPGRADE:
|
||||
return CreatureID::encode(subtype) + "2" + CreatureID::encode(additionalInfo);
|
||||
case Bonus::GENERATE_RESOURCE:
|
||||
return GameConstants::RESOURCE_NAMES[subtype];
|
||||
case Bonus::STACKS_SPEED:
|
||||
return "speed";
|
||||
default:
|
||||
return vstd::findKey(bonusNameMap, type);
|
||||
}
|
||||
}
|
||||
|
||||
Bonus::Bonus(ui16 Dur, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype)
|
||||
: duration(Dur), type(Type), subtype(Subtype), source(Src), val(Val), sid(ID), description(Desc)
|
||||
{
|
||||
@ -1306,6 +1405,11 @@ DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus)
|
||||
printField(effectRange);
|
||||
#undef printField
|
||||
|
||||
if(bonus.limiter)
|
||||
out << "\tLimiter: " << bonus.limiter->toString() << "\n";
|
||||
if(bonus.updater)
|
||||
out << "\tUpdater: " << bonus.updater->toString() << "\n";
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -1341,6 +1445,18 @@ int ILimiter::limit(const BonusLimitationContext &context) const /*return true t
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string ILimiter::toString() const
|
||||
{
|
||||
return typeid(*this).name();
|
||||
}
|
||||
|
||||
JsonNode ILimiter::toJsonNode() const
|
||||
{
|
||||
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
|
||||
root["type"].String() = toString();
|
||||
return root;
|
||||
}
|
||||
|
||||
int CCreatureTypeLimiter::limit(const BonusLimitationContext &context) const
|
||||
{
|
||||
const CCreature *c = retrieveCreature(&context.node);
|
||||
@ -1366,6 +1482,26 @@ void CCreatureTypeLimiter::setCreature (CreatureID id)
|
||||
creature = VLC->creh->creatures[id];
|
||||
}
|
||||
|
||||
std::string CCreatureTypeLimiter::toString() const
|
||||
{
|
||||
char buf[100];
|
||||
sprintf(buf, "CCreatureTypeLimiter(creature=%s, includeUpgrades=%s)",
|
||||
creature->identifier.c_str(),
|
||||
(includeUpgrades ? "true" : "false"));
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
JsonNode CCreatureTypeLimiter::toJsonNode() const
|
||||
{
|
||||
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
|
||||
|
||||
root["type"].String() = "CREATURE_TYPE_LIMITER";
|
||||
root["parameters"].Vector().push_back(JsonUtils::stringNode(creature->identifier));
|
||||
root["parameters"].Vector().push_back(JsonUtils::boolNode(includeUpgrades));
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
HasAnotherBonusLimiter::HasAnotherBonusLimiter( Bonus::BonusType bonus )
|
||||
: type(bonus), subtype(0), isSubtypeRelevant(false)
|
||||
{
|
||||
@ -1390,6 +1526,32 @@ int HasAnotherBonusLimiter::limit(const BonusLimitationContext &context) const
|
||||
return NOT_SURE;
|
||||
}
|
||||
|
||||
std::string HasAnotherBonusLimiter::toString() const
|
||||
{
|
||||
char buf[100];
|
||||
|
||||
std::string typeName = vstd::findKey(bonusNameMap, type);
|
||||
if(isSubtypeRelevant)
|
||||
sprintf(buf, "HasAnotherBonusLimiter(type=%s, subtype=%d)", typeName.c_str(), subtype);
|
||||
else
|
||||
sprintf(buf, "HasAnotherBonusLimiter(type=%s)", typeName.c_str());
|
||||
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
JsonNode HasAnotherBonusLimiter::toJsonNode() const
|
||||
{
|
||||
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
|
||||
std::string typeName = vstd::findKey(bonusNameMap, type);
|
||||
|
||||
root["type"].String() = "HAS_ANOTHER_BONUS_LIMITER";
|
||||
root["parameters"].Vector().push_back(JsonUtils::stringNode(typeName));
|
||||
if(isSubtypeRelevant)
|
||||
root["parameters"].Vector().push_back(JsonUtils::intNode(subtype));
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
IPropagator::~IPropagator()
|
||||
{
|
||||
|
||||
@ -1543,3 +1705,136 @@ void LimiterList::add( TLimiterPtr limiter )
|
||||
{
|
||||
limiters.push_back(limiter);
|
||||
}
|
||||
|
||||
// Updaters
|
||||
|
||||
std::shared_ptr<Bonus> Bonus::addUpdater(TUpdaterPtr Updater)
|
||||
{
|
||||
updater = Updater;
|
||||
return this->shared_from_this();
|
||||
}
|
||||
|
||||
IUpdater::~IUpdater()
|
||||
{
|
||||
}
|
||||
|
||||
const std::shared_ptr<Bonus> IUpdater::update(const std::shared_ptr<Bonus> b, const CBonusSystemNode & context) const
|
||||
{
|
||||
return b;
|
||||
}
|
||||
|
||||
std::string IUpdater::toString() const
|
||||
{
|
||||
return typeid(*this).name();
|
||||
}
|
||||
|
||||
JsonNode IUpdater::toJsonNode() const
|
||||
{
|
||||
return JsonNode(JsonNode::JsonType::DATA_NULL);
|
||||
}
|
||||
|
||||
GrowsWithLevelUpdater::GrowsWithLevelUpdater() : valPer20(0), stepSize(1)
|
||||
{
|
||||
}
|
||||
|
||||
GrowsWithLevelUpdater::GrowsWithLevelUpdater(int valPer20, int stepSize) : valPer20(valPer20), stepSize(stepSize)
|
||||
{
|
||||
}
|
||||
|
||||
const std::shared_ptr<Bonus> GrowsWithLevelUpdater::update(const std::shared_ptr<Bonus> b, const CBonusSystemNode & context) const
|
||||
{
|
||||
if(context.getNodeType() == CBonusSystemNode::HERO)
|
||||
{
|
||||
int level = static_cast<const CGHeroInstance &>(context).level;
|
||||
int steps = stepSize ? level / stepSize : level;
|
||||
//rounding follows format for HMM3 creature specialty bonus
|
||||
int newVal = (valPer20 * steps + 19) / 20;
|
||||
//return copy of bonus with updated val
|
||||
std::shared_ptr<Bonus> newBonus = std::make_shared<Bonus>(*b);
|
||||
newBonus->val = newVal;
|
||||
return newBonus;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
std::string GrowsWithLevelUpdater::toString() const
|
||||
{
|
||||
return boost::str(boost::format("GrowsWithLevelUpdater(valPer20=%d, stepSize=%d)") % valPer20 % stepSize);
|
||||
}
|
||||
|
||||
JsonNode GrowsWithLevelUpdater::toJsonNode() const
|
||||
{
|
||||
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
|
||||
|
||||
root["type"].String() = "GROWS_WITH_LEVEL";
|
||||
root["parameters"].Vector().push_back(JsonUtils::intNode(valPer20));
|
||||
if(stepSize > 1)
|
||||
root["parameters"].Vector().push_back(JsonUtils::intNode(stepSize));
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
TimesHeroLevelUpdater::TimesHeroLevelUpdater()
|
||||
{
|
||||
}
|
||||
|
||||
const std::shared_ptr<Bonus> TimesHeroLevelUpdater::update(const std::shared_ptr<Bonus> b, const CBonusSystemNode & context) const
|
||||
{
|
||||
if(context.getNodeType() == CBonusSystemNode::HERO)
|
||||
{
|
||||
int level = static_cast<const CGHeroInstance &>(context).level;
|
||||
std::shared_ptr<Bonus> newBonus = std::make_shared<Bonus>(*b);
|
||||
newBonus->val *= level;
|
||||
return newBonus;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
std::string TimesHeroLevelUpdater::toString() const
|
||||
{
|
||||
return "TimesHeroLevelUpdater";
|
||||
}
|
||||
|
||||
JsonNode TimesHeroLevelUpdater::toJsonNode() const
|
||||
{
|
||||
return JsonUtils::stringNode("TIMES_HERO_LEVEL");
|
||||
}
|
||||
|
||||
TimesStackLevelUpdater::TimesStackLevelUpdater()
|
||||
{
|
||||
}
|
||||
|
||||
const std::shared_ptr<Bonus> TimesStackLevelUpdater::update(const std::shared_ptr<Bonus> b, const CBonusSystemNode & context) const
|
||||
{
|
||||
if(context.getNodeType() == CBonusSystemNode::STACK_INSTANCE)
|
||||
{
|
||||
int level = static_cast<const CStackInstance &>(context).getLevel();
|
||||
std::shared_ptr<Bonus> newBonus = std::make_shared<Bonus>(*b);
|
||||
newBonus->val *= level;
|
||||
return newBonus;
|
||||
}
|
||||
else if(context.getNodeType() == CBonusSystemNode::STACK_BATTLE)
|
||||
{
|
||||
const CStack & stack = static_cast<const CStack &>(context);
|
||||
//only update if stack doesn't have an instance (summons, war machines)
|
||||
//otherwise we'd end up multiplying twice
|
||||
if(stack.base == nullptr)
|
||||
{
|
||||
int level = stack.type->level;
|
||||
std::shared_ptr<Bonus> newBonus = std::make_shared<Bonus>(*b);
|
||||
newBonus->val *= level;
|
||||
return newBonus;
|
||||
}
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
std::string TimesStackLevelUpdater::toString() const
|
||||
{
|
||||
return "TimesStackLevelUpdater";
|
||||
}
|
||||
|
||||
JsonNode TimesStackLevelUpdater::toJsonNode() const
|
||||
{
|
||||
return JsonUtils::stringNode("TIMES_STACK_LEVEL");
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "GameConstants.h"
|
||||
#include "JsonNode.h"
|
||||
|
||||
class CCreature;
|
||||
struct Bonus;
|
||||
@ -17,11 +18,13 @@ class IBonusBearer;
|
||||
class CBonusSystemNode;
|
||||
class ILimiter;
|
||||
class IPropagator;
|
||||
class IUpdater;
|
||||
class BonusList;
|
||||
|
||||
typedef std::shared_ptr<BonusList> TBonusListPtr;
|
||||
typedef std::shared_ptr<ILimiter> TLimiterPtr;
|
||||
typedef std::shared_ptr<IPropagator> TPropagatorPtr;
|
||||
typedef std::shared_ptr<IUpdater> TUpdaterPtr;
|
||||
typedef std::set<CBonusSystemNode*> TNodes;
|
||||
typedef std::set<const CBonusSystemNode*> TCNodes;
|
||||
typedef std::vector<CBonusSystemNode *> TNodesVector;
|
||||
@ -204,14 +207,14 @@ private:
|
||||
BONUS_NAME(NO_LUCK) /*eg. when fighting on cursed ground*/ \
|
||||
BONUS_NAME(NO_MORALE) /*eg. when fighting on cursed ground*/ \
|
||||
BONUS_NAME(DARKNESS) /*val = radius */ \
|
||||
BONUS_NAME(SPECIAL_SECONDARY_SKILL) /*val = id, additionalInfo = value per level in percent*/ \
|
||||
BONUS_NAME(SPECIAL_SPELL_LEV) /*val = id, additionalInfo = value per level in percent*/\
|
||||
BONUS_NAME(SPECIAL_SECONDARY_SKILL) /*subtype = id, val = value per level in percent*/ \
|
||||
BONUS_NAME(SPECIAL_SPELL_LEV) /*subtype = id, val = value per level in percent*/\
|
||||
BONUS_NAME(SPELL_DAMAGE) /*val = value*/\
|
||||
BONUS_NAME(SPECIFIC_SPELL_DAMAGE) /*subtype = id of spell, val = value*/\
|
||||
BONUS_NAME(SPECIAL_BLESS_DAMAGE) /*val = spell (bless), additionalInfo = value per level in percent*/\
|
||||
BONUS_NAME(MAXED_SPELL) /*val = id*/\
|
||||
BONUS_NAME(SPECIAL_PECULIAR_ENCHANT) /*blesses and curses with id = val dependent on unit's level, subtype = 0 or 1 for Coronius*/\
|
||||
BONUS_NAME(SPECIAL_UPGRADE) /*val = base, additionalInfo = target */\
|
||||
BONUS_NAME(SPECIAL_UPGRADE) /*subtype = base, additionalInfo = target */\
|
||||
BONUS_NAME(DRAGON_NATURE) \
|
||||
BONUS_NAME(CREATURE_DAMAGE)/*subtype 0 = both, 1 = min, 2 = max*/\
|
||||
BONUS_NAME(EXP_MULTIPLIER)/* val - percent of additional exp gained by stack/commander (base value 100)*/\
|
||||
@ -340,6 +343,7 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
|
||||
|
||||
TLimiterPtr limiter;
|
||||
TPropagatorPtr propagator;
|
||||
TUpdaterPtr updater;
|
||||
|
||||
std::string description;
|
||||
|
||||
@ -362,6 +366,10 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
|
||||
h & effectRange;
|
||||
h & limiter;
|
||||
h & propagator;
|
||||
if(version >= 781)
|
||||
{
|
||||
h & updater;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Ptr>
|
||||
@ -419,9 +427,12 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
|
||||
}
|
||||
|
||||
std::string Description() const;
|
||||
JsonNode toJsonNode() const;
|
||||
std::string nameForBonus() const; // generate suitable name for bonus - e.g. for storing in json struct
|
||||
|
||||
std::shared_ptr<Bonus> addLimiter(TLimiterPtr Limiter); //returns this for convenient chain-calls
|
||||
std::shared_ptr<Bonus> addPropagator(TPropagatorPtr Propagator); //returns this for convenient chain-calls
|
||||
std::shared_ptr<Bonus> addUpdater(TUpdaterPtr Updater); //returns this for convenient chain-calls
|
||||
};
|
||||
|
||||
DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus);
|
||||
@ -583,6 +594,8 @@ public:
|
||||
virtual ~ILimiter();
|
||||
|
||||
virtual int limit(const BonusLimitationContext &context) const; //0 - accept bonus; 1 - drop bonus; 2 - delay (drops eventually)
|
||||
virtual std::string toString() const;
|
||||
virtual JsonNode toJsonNode() const;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
@ -663,6 +676,7 @@ private:
|
||||
void getBonusesRec(BonusList &out, const CSelector &selector, const CSelector &limit) const;
|
||||
void getAllBonusesRec(BonusList &out) const;
|
||||
const TBonusListPtr getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr) const;
|
||||
const std::shared_ptr<Bonus> update(const std::shared_ptr<Bonus> b) const;
|
||||
|
||||
public:
|
||||
explicit CBonusSystemNode();
|
||||
@ -703,7 +717,7 @@ public:
|
||||
///removes bonuses by selector
|
||||
void popBonuses(const CSelector &s);
|
||||
///updates count of remaining turns and removes outdated bonuses by selector
|
||||
void updateBonuses(const CSelector &s);
|
||||
void reduceBonusDurations(const CSelector &s);
|
||||
virtual std::string bonusToString(const std::shared_ptr<Bonus>& bonus, bool description) const {return "";}; //description or bonus name
|
||||
virtual std::string nodeName() const;
|
||||
|
||||
@ -847,6 +861,8 @@ public:
|
||||
void setCreature (CreatureID id);
|
||||
|
||||
int limit(const BonusLimitationContext &context) const override;
|
||||
virtual std::string toString() const override;
|
||||
virtual JsonNode toJsonNode() const override;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
@ -867,6 +883,8 @@ public:
|
||||
HasAnotherBonusLimiter(Bonus::BonusType bonus, TBonusSubtype _subtype);
|
||||
|
||||
int limit(const BonusLimitationContext &context) const override;
|
||||
virtual std::string toString() const override;
|
||||
virtual JsonNode toJsonNode() const override;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
@ -997,7 +1015,7 @@ extern DLL_LINKAGE const std::map<std::string, ui16> bonusDurationMap;
|
||||
extern DLL_LINKAGE const std::map<std::string, Bonus::LimitEffect> bonusLimitEffect;
|
||||
extern DLL_LINKAGE const std::map<std::string, TLimiterPtr> bonusLimiterMap;
|
||||
extern DLL_LINKAGE const std::map<std::string, TPropagatorPtr> bonusPropagatorMap;
|
||||
|
||||
extern DLL_LINKAGE const std::map<std::string, TUpdaterPtr> bonusUpdaterMap;
|
||||
|
||||
// BonusList template that requires full interface of CBonusSystemNode
|
||||
template <class InputIterator>
|
||||
@ -1006,3 +1024,70 @@ void BonusList::insert(const int position, InputIterator first, InputIterator la
|
||||
bonuses.insert(bonuses.begin() + position, first, last);
|
||||
changed();
|
||||
}
|
||||
|
||||
// observers for updating bonuses based on certain events (e.g. hero gaining level)
|
||||
|
||||
class DLL_LINKAGE IUpdater
|
||||
{
|
||||
public:
|
||||
virtual ~IUpdater();
|
||||
|
||||
virtual const std::shared_ptr<Bonus> update(const std::shared_ptr<Bonus> b, const CBonusSystemNode & context) const;
|
||||
virtual std::string toString() const;
|
||||
virtual JsonNode toJsonNode() const;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class DLL_LINKAGE GrowsWithLevelUpdater : public IUpdater
|
||||
{
|
||||
public:
|
||||
int valPer20;
|
||||
int stepSize;
|
||||
|
||||
GrowsWithLevelUpdater();
|
||||
GrowsWithLevelUpdater(int valPer20, int stepSize = 1);
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & static_cast<IUpdater &>(*this);
|
||||
h & valPer20;
|
||||
h & stepSize;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Bonus> update(const std::shared_ptr<Bonus> b, const CBonusSystemNode & context) const override;
|
||||
virtual std::string toString() const override;
|
||||
virtual JsonNode toJsonNode() const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE TimesHeroLevelUpdater : public IUpdater
|
||||
{
|
||||
public:
|
||||
TimesHeroLevelUpdater();
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & static_cast<IUpdater &>(*this);
|
||||
}
|
||||
|
||||
const std::shared_ptr<Bonus> update(const std::shared_ptr<Bonus> b, const CBonusSystemNode & context) const override;
|
||||
virtual std::string toString() const override;
|
||||
virtual JsonNode toJsonNode() const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE TimesStackLevelUpdater : public IUpdater
|
||||
{
|
||||
public:
|
||||
TimesStackLevelUpdater();
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & static_cast<IUpdater &>(*this);
|
||||
}
|
||||
|
||||
const std::shared_ptr<Bonus> update(const std::shared_ptr<Bonus> b, const CBonusSystemNode & context) const override;
|
||||
virtual std::string toString() const override;
|
||||
virtual JsonNode toJsonNode() const override;
|
||||
};
|
||||
|
@ -32,19 +32,22 @@ void JsonWriter::writeContainer(Iterator begin, Iterator end)
|
||||
|
||||
while (begin != end)
|
||||
{
|
||||
out<<",\n";
|
||||
out << (compactMode ? ", " : ",\n");
|
||||
writeEntry(begin++);
|
||||
}
|
||||
|
||||
out<<"\n";
|
||||
out << (compactMode ? "" : "\n");
|
||||
prefix.resize(prefix.size()-1);
|
||||
}
|
||||
|
||||
void JsonWriter::writeEntry(JsonMap::const_iterator entry)
|
||||
{
|
||||
if (!entry->second.meta.empty())
|
||||
out << prefix << " // " << entry->second.meta << "\n";
|
||||
out << prefix;
|
||||
if(!compactMode)
|
||||
{
|
||||
if (!entry->second.meta.empty())
|
||||
out << prefix << " // " << entry->second.meta << "\n";
|
||||
out << prefix;
|
||||
}
|
||||
writeString(entry->first);
|
||||
out << " : ";
|
||||
writeNode(entry->second);
|
||||
@ -52,9 +55,12 @@ void JsonWriter::writeEntry(JsonMap::const_iterator entry)
|
||||
|
||||
void JsonWriter::writeEntry(JsonVector::const_iterator entry)
|
||||
{
|
||||
if (!entry->meta.empty())
|
||||
out << prefix << " // " << entry->meta << "\n";
|
||||
out << prefix;
|
||||
if(!compactMode)
|
||||
{
|
||||
if (!entry->meta.empty())
|
||||
out << prefix << " // " << entry->meta << "\n";
|
||||
out << prefix;
|
||||
}
|
||||
writeNode(*entry);
|
||||
}
|
||||
|
||||
@ -94,6 +100,10 @@ void JsonWriter::writeString(const std::string &string)
|
||||
|
||||
void JsonWriter::writeNode(const JsonNode &node)
|
||||
{
|
||||
bool originalMode = compactMode;
|
||||
if(compact && !compactMode && node.isCompact())
|
||||
compactMode = true;
|
||||
|
||||
switch(node.getType())
|
||||
{
|
||||
break; case JsonNode::JsonType::DATA_NULL:
|
||||
@ -112,21 +122,24 @@ void JsonWriter::writeNode(const JsonNode &node)
|
||||
writeString(node.String());
|
||||
|
||||
break; case JsonNode::JsonType::DATA_VECTOR:
|
||||
out << "[" << "\n";
|
||||
out << "[" << (compactMode ? " " : "\n");
|
||||
writeContainer(node.Vector().begin(), node.Vector().end());
|
||||
out << prefix << "]";
|
||||
out << (compactMode ? " " : prefix) << "]";
|
||||
|
||||
break; case JsonNode::JsonType::DATA_STRUCT:
|
||||
out << "{" << "\n";
|
||||
out << "{" << (compactMode ? " " : "\n");
|
||||
writeContainer(node.Struct().begin(), node.Struct().end());
|
||||
out << prefix << "}";
|
||||
out << (compactMode ? " " : prefix) << "}";
|
||||
|
||||
break; case JsonNode::JsonType::DATA_INTEGER:
|
||||
out << node.Integer();
|
||||
}
|
||||
|
||||
compactMode = originalMode;
|
||||
}
|
||||
|
||||
JsonWriter::JsonWriter(std::ostream & output)
|
||||
: out(output)
|
||||
JsonWriter::JsonWriter(std::ostream & output, bool compact)
|
||||
: out(output), compact(compact)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,10 @@ class JsonWriter
|
||||
//prefix for each line (tabulation)
|
||||
std::string prefix;
|
||||
std::ostream & out;
|
||||
//sets whether compact nodes are written in single-line format
|
||||
bool compact;
|
||||
//tracks whether we are currently using single-line format
|
||||
bool compactMode = false;
|
||||
public:
|
||||
template<typename Iterator>
|
||||
void writeContainer(Iterator begin, Iterator end);
|
||||
@ -23,7 +27,7 @@ public:
|
||||
void writeEntry(JsonVector::const_iterator entry);
|
||||
void writeString(const std::string & string);
|
||||
void writeNode(const JsonNode & node);
|
||||
JsonWriter(std::ostream & output);
|
||||
JsonWriter(std::ostream & output, bool compact = false);
|
||||
};
|
||||
|
||||
//Tiny string class that uses const char* as data for speed, members are private
|
||||
|
196
lib/JsonNode.cpp
196
lib/JsonNode.cpp
@ -214,6 +214,50 @@ bool JsonNode::isNumber() const
|
||||
return type == JsonType::DATA_INTEGER || type == JsonType::DATA_FLOAT;
|
||||
}
|
||||
|
||||
bool JsonNode::containsBaseData() const
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case JsonType::DATA_NULL:
|
||||
return false;
|
||||
case JsonType::DATA_STRUCT:
|
||||
for(auto elem : *data.Struct)
|
||||
{
|
||||
if(elem.second.containsBaseData())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
//other types (including vector) cannot be extended via merge
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool JsonNode::isCompact() const
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case JsonType::DATA_VECTOR:
|
||||
for(JsonNode & elem : *data.Vector)
|
||||
{
|
||||
if(!elem.isCompact())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
case JsonType::DATA_STRUCT:
|
||||
{
|
||||
int propertyCount = data.Struct->size();
|
||||
if(propertyCount == 0)
|
||||
return true;
|
||||
else if(propertyCount == 1)
|
||||
return data.Struct->begin()->second.isCompact();
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void JsonNode::clear()
|
||||
{
|
||||
setType(JsonType::DATA_NULL);
|
||||
@ -367,10 +411,10 @@ JsonNode & JsonNode::resolvePointer(const std::string &jsonPointer)
|
||||
return ::resolvePointer(*this, jsonPointer);
|
||||
}
|
||||
|
||||
std::string JsonNode::toJson() const
|
||||
std::string JsonNode::toJson(bool compact) const
|
||||
{
|
||||
std::ostringstream out;
|
||||
JsonWriter writer(out);
|
||||
JsonWriter writer(out, compact);
|
||||
writer.writeNode(*this);
|
||||
out << "\n";
|
||||
return out.str();
|
||||
@ -395,7 +439,7 @@ std::shared_ptr<Bonus> JsonUtils::parseBonus (const JsonVector &ability_vec) //T
|
||||
auto it = bonusNameMap.find(type);
|
||||
if (it == bonusNameMap.end())
|
||||
{
|
||||
logMod->error("Error: invalid ability type %s", type);
|
||||
logMod->error("Error: invalid ability type %s.", type);
|
||||
return b;
|
||||
}
|
||||
b->type = it->second;
|
||||
@ -413,7 +457,7 @@ const T & parseByMap(const std::map<std::string, T> & map, const JsonNode * val,
|
||||
auto it = map.find(type);
|
||||
if (it == map.end())
|
||||
{
|
||||
logMod->error("Error: invalid %s%s", err, type);
|
||||
logMod->error("Error: invalid %s%s.", err, type);
|
||||
return defaultValue;
|
||||
}
|
||||
else
|
||||
@ -445,7 +489,7 @@ void JsonUtils::resolveIdentifier(si32 &var, const JsonNode &node, std::string n
|
||||
});
|
||||
break;
|
||||
default:
|
||||
logMod->error("Error! Wrong identifier used for value of %s", name);
|
||||
logMod->error("Error! Wrong identifier used for value of %s.", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -489,7 +533,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
||||
auto it = bonusNameMap.find(type);
|
||||
if (it == bonusNameMap.end())
|
||||
{
|
||||
logMod->error("Error: invalid ability type %s", type);
|
||||
logMod->error("Error: invalid ability type %s.", type);
|
||||
return false;
|
||||
}
|
||||
b->type = it->second;
|
||||
@ -580,7 +624,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
||||
auto it = bonusNameMap.find(anotherBonusType);
|
||||
if (it == bonusNameMap.end())
|
||||
{
|
||||
logMod->error("Error: invalid ability type %s", anotherBonusType);
|
||||
logMod->error("Error: invalid ability type %s.", anotherBonusType);
|
||||
continue;
|
||||
}
|
||||
l2->type = it->second;
|
||||
@ -603,6 +647,31 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
||||
if (!value->isNull())
|
||||
b->propagator = parseByMap(bonusPropagatorMap, value, "propagator type ");
|
||||
|
||||
value = &ability["updater"];
|
||||
if(!value->isNull())
|
||||
{
|
||||
const JsonNode & updaterJson = *value;
|
||||
switch(updaterJson.getType())
|
||||
{
|
||||
case JsonNode::JsonType::DATA_STRING:
|
||||
b->addUpdater(parseByMap(bonusUpdaterMap, &updaterJson, "updater type "));
|
||||
break;
|
||||
case JsonNode::JsonType::DATA_STRUCT:
|
||||
if(updaterJson["type"].String() == "GROWS_WITH_LEVEL")
|
||||
{
|
||||
std::shared_ptr<GrowsWithLevelUpdater> updater = std::make_shared<GrowsWithLevelUpdater>();
|
||||
const JsonVector param = updaterJson["parameters"].Vector();
|
||||
updater->valPer20 = param[0].Integer();
|
||||
if(param.size() > 1)
|
||||
updater->stepSize = param[1].Integer();
|
||||
b->addUpdater(updater);
|
||||
}
|
||||
else
|
||||
logMod->warn("Unknown updater type \"%s\"", updaterJson["type"].String());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -725,6 +794,7 @@ bool JsonUtils::validate(const JsonNode &node, std::string schemaName, std::stri
|
||||
{
|
||||
logMod->warn("Data in %s is invalid!", dataName);
|
||||
logMod->warn(log);
|
||||
logMod->trace("%s json: %s", dataName, node.toJson(true));
|
||||
}
|
||||
return log.empty();
|
||||
}
|
||||
@ -822,6 +892,90 @@ void JsonUtils::inherit(JsonNode & descendant, const JsonNode & base)
|
||||
descendant.swap(inheritedNode);
|
||||
}
|
||||
|
||||
JsonNode JsonUtils::intersect(const std::vector<JsonNode> & nodes, bool pruneEmpty)
|
||||
{
|
||||
if(nodes.size() == 0)
|
||||
return nullNode;
|
||||
|
||||
JsonNode result = nodes[0];
|
||||
for(int i = 1; i < nodes.size(); i++)
|
||||
{
|
||||
if(result.isNull())
|
||||
break;
|
||||
result = JsonUtils::intersect(result, nodes[i], pruneEmpty);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
JsonNode JsonUtils::intersect(const JsonNode & a, const JsonNode & b, bool pruneEmpty)
|
||||
{
|
||||
if(a.getType() == JsonNode::JsonType::DATA_STRUCT && b.getType() == JsonNode::JsonType::DATA_STRUCT)
|
||||
{
|
||||
// intersect individual properties
|
||||
JsonNode result(JsonNode::JsonType::DATA_STRUCT);
|
||||
for(auto property : a.Struct())
|
||||
{
|
||||
if(vstd::contains(b.Struct(), property.first))
|
||||
{
|
||||
JsonNode propertyIntersect = JsonUtils::intersect(property.second, b.Struct().find(property.first)->second);
|
||||
if(pruneEmpty && !propertyIntersect.containsBaseData())
|
||||
continue;
|
||||
result[property.first] = propertyIntersect;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not a struct - same or different, no middle ground
|
||||
if(a == b)
|
||||
return a;
|
||||
}
|
||||
return nullNode;
|
||||
}
|
||||
|
||||
JsonNode JsonUtils::difference(const JsonNode & node, const JsonNode & base)
|
||||
{
|
||||
auto addsInfo = [](JsonNode diff) -> bool
|
||||
{
|
||||
switch(diff.getType())
|
||||
{
|
||||
case JsonNode::JsonType::DATA_NULL:
|
||||
return false;
|
||||
case JsonNode::JsonType::DATA_STRUCT:
|
||||
return diff.Struct().size() > 0;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
if(node.getType() == JsonNode::JsonType::DATA_STRUCT && base.getType() == JsonNode::JsonType::DATA_STRUCT)
|
||||
{
|
||||
// subtract individual properties
|
||||
JsonNode result(JsonNode::JsonType::DATA_STRUCT);
|
||||
for(auto property : node.Struct())
|
||||
{
|
||||
if(vstd::contains(base.Struct(), property.first))
|
||||
{
|
||||
const JsonNode propertyDifference = JsonUtils::difference(property.second, base.Struct().find(property.first)->second);
|
||||
if(addsInfo(propertyDifference))
|
||||
result[property.first] = propertyDifference;
|
||||
}
|
||||
else
|
||||
{
|
||||
result[property.first] = property.second;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(node == base)
|
||||
return nullNode;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
JsonNode JsonUtils::assembleFromFiles(std::vector<std::string> files)
|
||||
{
|
||||
bool isValid;
|
||||
@ -860,3 +1014,31 @@ JsonNode JsonUtils::assembleFromFiles(std::string filename)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DLL_LINKAGE JsonNode JsonUtils::boolNode(bool value)
|
||||
{
|
||||
JsonNode node;
|
||||
node.Bool() = value;
|
||||
return node;
|
||||
}
|
||||
|
||||
DLL_LINKAGE JsonNode JsonUtils::floatNode(double value)
|
||||
{
|
||||
JsonNode node;
|
||||
node.Float() = value;
|
||||
return node;
|
||||
}
|
||||
|
||||
DLL_LINKAGE JsonNode JsonUtils::stringNode(std::string value)
|
||||
{
|
||||
JsonNode node;
|
||||
node.String() = value;
|
||||
return node;
|
||||
}
|
||||
|
||||
DLL_LINKAGE JsonNode JsonUtils::intNode(si64 value)
|
||||
{
|
||||
JsonNode node;
|
||||
node.Integer() = value;
|
||||
return node;
|
||||
}
|
||||
|
@ -75,6 +75,10 @@ public:
|
||||
|
||||
bool isNull() const;
|
||||
bool isNumber() const;
|
||||
/// true if node contains not-null data that cannot be extended via merging
|
||||
/// used for generating common base node from multiple nodes (e.g. bonuses)
|
||||
bool containsBaseData() const;
|
||||
bool isCompact() const;
|
||||
/// removes all data from node and sets type to null
|
||||
void clear();
|
||||
|
||||
@ -110,7 +114,7 @@ public:
|
||||
JsonNode & operator[](std::string child);
|
||||
const JsonNode & operator[](std::string child) const;
|
||||
|
||||
std::string toJson() const;
|
||||
std::string toJson(bool compact = false) const;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
@ -188,6 +192,22 @@ namespace JsonUtils
|
||||
*/
|
||||
DLL_LINKAGE void inherit(JsonNode & descendant, const JsonNode & base);
|
||||
|
||||
/**
|
||||
* @brief construct node representing the common structure of input nodes
|
||||
* @param pruneEmpty - omit common properties whose intersection is empty
|
||||
* different types: null
|
||||
* struct: recursive intersect on common properties
|
||||
* other: input if equal, null otherwise
|
||||
*/
|
||||
DLL_LINKAGE JsonNode intersect(const JsonNode & a, const JsonNode & b, bool pruneEmpty = true);
|
||||
DLL_LINKAGE JsonNode intersect(const std::vector<JsonNode> & nodes, bool pruneEmpty = true);
|
||||
|
||||
/**
|
||||
* @brief construct node representing the difference "node - base"
|
||||
* merging difference with base gives node
|
||||
*/
|
||||
DLL_LINKAGE JsonNode difference(const JsonNode & node, const JsonNode & base);
|
||||
|
||||
/**
|
||||
* @brief generate one Json structure from multiple files
|
||||
* @param files - list of filenames with parts of json structure
|
||||
@ -220,6 +240,12 @@ namespace JsonUtils
|
||||
/// get schema by json URI: vcmi:<name of file in schemas directory>#<entry in file, optional>
|
||||
/// example: schema "vcmi:settings" is used to check user settings
|
||||
DLL_LINKAGE const JsonNode & getSchema(std::string URI);
|
||||
|
||||
/// for easy construction of JsonNodes; helps with inserting primitives into vector node
|
||||
DLL_LINKAGE JsonNode boolNode(bool value);
|
||||
DLL_LINKAGE JsonNode floatNode(double value);
|
||||
DLL_LINKAGE JsonNode stringNode(std::string value);
|
||||
DLL_LINKAGE JsonNode intNode(si64 value);
|
||||
}
|
||||
|
||||
namespace JsonDetail
|
||||
|
@ -1085,8 +1085,8 @@ DLL_LINKAGE void NewTurn::applyGs(CGameState *gs)
|
||||
|
||||
// Update bonuses before doing anything else so hero don't get more MP than needed
|
||||
gs->globalEffects.popBonuses(Bonus::OneDay); //works for children -> all game objs
|
||||
gs->globalEffects.updateBonuses(Bonus::NDays);
|
||||
gs->globalEffects.updateBonuses(Bonus::OneWeek);
|
||||
gs->globalEffects.reduceBonusDurations(Bonus::NDays);
|
||||
gs->globalEffects.reduceBonusDurations(Bonus::OneWeek);
|
||||
//TODO not really a single root hierarchy, what about bonuses placed elsewhere? [not an issue with H3 mechanics but in the future...]
|
||||
|
||||
for(NewTurn::Hero h : heroes) //give mana/movement point
|
||||
|
@ -765,7 +765,7 @@ void BattleInfo::nextRound(int32_t roundNr)
|
||||
for(CStack * s : stacks)
|
||||
{
|
||||
// new turn effects
|
||||
s->updateBonuses(Bonus::NTurns);
|
||||
s->reduceBonusDurations(Bonus::NTurns);
|
||||
|
||||
s->afterNewRound();
|
||||
}
|
||||
|
@ -496,9 +496,6 @@ void CGHeroInstance::SecondarySkillsInfo::resetWisdomCounter()
|
||||
void CGHeroInstance::initObj(CRandomGenerator & rand)
|
||||
{
|
||||
blockVisit = true;
|
||||
auto hs = new HeroSpecial();
|
||||
hs->setNodeType(CBonusSystemNode::SPECIALTY);
|
||||
attachTo(hs); //do we ever need to detach it?
|
||||
|
||||
if(!type)
|
||||
initHero(rand); //TODO: set up everything for prison before specialties are configured
|
||||
@ -514,246 +511,23 @@ void CGHeroInstance::initObj(CRandomGenerator & rand)
|
||||
appearance = customApp.get();
|
||||
}
|
||||
|
||||
for(const auto &spec : type->spec) //TODO: unfity with bonus system
|
||||
{
|
||||
auto bonus = std::make_shared<Bonus>();
|
||||
bonus->val = spec.val;
|
||||
bonus->sid = id.getNum(); //from the hero, specialty has no unique id
|
||||
bonus->duration = Bonus::PERMANENT;
|
||||
bonus->source = Bonus::HERO_SPECIAL;
|
||||
switch (spec.type)
|
||||
{
|
||||
case 1:// creature specialty
|
||||
{
|
||||
hs->growsWithLevel = true;
|
||||
|
||||
const CCreature &specCreature = *VLC->creh->creatures[spec.additionalinfo]; //creature in which we have specialty
|
||||
|
||||
//bonus->additionalInfo = spec.additionalinfo; //creature id, should not be used again - this works only with limiter
|
||||
bonus->limiter.reset(new CCreatureTypeLimiter (specCreature, true)); //with upgrades
|
||||
bonus->type = Bonus::PRIMARY_SKILL;
|
||||
bonus->valType = Bonus::ADDITIVE_VALUE;
|
||||
|
||||
bonus->subtype = PrimarySkill::ATTACK;
|
||||
hs->addNewBonus(bonus);
|
||||
|
||||
bonus = std::make_shared<Bonus>(*bonus);
|
||||
bonus->subtype = PrimarySkill::DEFENSE;
|
||||
hs->addNewBonus(bonus);
|
||||
//values will be calculated later
|
||||
|
||||
bonus = std::make_shared<Bonus>(*bonus);
|
||||
bonus->type = Bonus::STACKS_SPEED;
|
||||
bonus->val = 1; //+1 speed
|
||||
hs->addNewBonus(bonus);
|
||||
}
|
||||
break;
|
||||
case 2://secondary skill
|
||||
hs->growsWithLevel = true;
|
||||
bonus->type = Bonus::SPECIAL_SECONDARY_SKILL; //needs to be recalculated with level, based on this value
|
||||
bonus->valType = Bonus::BASE_NUMBER; // to receive nonzero value
|
||||
bonus->subtype = spec.subtype; //skill id
|
||||
bonus->val = spec.val; //value per level, in percent
|
||||
hs->addNewBonus(bonus);
|
||||
bonus = std::make_shared<Bonus>(*bonus);
|
||||
|
||||
switch (spec.additionalinfo)
|
||||
{
|
||||
case 0: //normal
|
||||
bonus->valType = Bonus::PERCENT_TO_BASE;
|
||||
break;
|
||||
case 1: //when it's navigation or there's no 'base' at all
|
||||
bonus->valType = Bonus::PERCENT_TO_ALL;
|
||||
break;
|
||||
}
|
||||
bonus->type = Bonus::SECONDARY_SKILL_PREMY; //value will be calculated later
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
case 3://spell damage bonus, level dependent but calculated elsewhere
|
||||
bonus->type = Bonus::SPECIAL_SPELL_LEV;
|
||||
bonus->subtype = spec.subtype;
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
case 4://creature stat boost
|
||||
switch (spec.subtype)
|
||||
{
|
||||
case 1://attack
|
||||
bonus->type = Bonus::PRIMARY_SKILL;
|
||||
bonus->subtype = PrimarySkill::ATTACK;
|
||||
break;
|
||||
case 2://defense
|
||||
bonus->type = Bonus::PRIMARY_SKILL;
|
||||
bonus->subtype = PrimarySkill::DEFENSE;
|
||||
break;
|
||||
case 3:
|
||||
bonus->type = Bonus::CREATURE_DAMAGE;
|
||||
bonus->subtype = 0; //both min and max
|
||||
break;
|
||||
case 4://hp
|
||||
bonus->type = Bonus::STACK_HEALTH;
|
||||
break;
|
||||
case 5:
|
||||
bonus->type = Bonus::STACKS_SPEED;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
bonus->additionalInfo = spec.additionalinfo; //creature id
|
||||
bonus->valType = Bonus::ADDITIVE_VALUE;
|
||||
bonus->limiter.reset(new CCreatureTypeLimiter (*VLC->creh->creatures[spec.additionalinfo], true));
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
case 5://spell damage bonus in percent
|
||||
bonus->type = Bonus::SPECIFIC_SPELL_DAMAGE;
|
||||
bonus->valType = Bonus::BASE_NUMBER; // current spell system is screwed
|
||||
bonus->subtype = spec.subtype; //spell id
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
case 6://damage bonus for bless (Adela)
|
||||
bonus->type = Bonus::SPECIAL_BLESS_DAMAGE;
|
||||
bonus->subtype = spec.subtype; //spell id if you ever wanted to use it otherwise
|
||||
bonus->additionalInfo = spec.additionalinfo; //damage factor
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
case 7://maxed mastery for spell
|
||||
bonus->type = Bonus::MAXED_SPELL;
|
||||
bonus->subtype = spec.subtype; //spell i
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
case 8://peculiar spells - enchantments
|
||||
bonus->type = Bonus::SPECIAL_PECULIAR_ENCHANT;
|
||||
bonus->subtype = spec.subtype; //spell id
|
||||
bonus->additionalInfo = spec.additionalinfo;//0, 1 for Coronius
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
case 9://upgrade creatures
|
||||
{
|
||||
const auto &creatures = VLC->creh->creatures;
|
||||
bonus->type = Bonus::SPECIAL_UPGRADE;
|
||||
bonus->subtype = spec.subtype; //base id
|
||||
bonus->additionalInfo = spec.additionalinfo; //target id
|
||||
hs->addNewBonus(bonus);
|
||||
bonus = std::make_shared<Bonus>(*bonus);
|
||||
|
||||
for(auto cre_id : creatures[spec.subtype]->upgrades)
|
||||
{
|
||||
bonus->subtype = cre_id; //propagate for regular upgrades of base creature
|
||||
hs->addNewBonus(bonus);
|
||||
bonus = std::make_shared<Bonus>(*bonus);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 10://resource generation
|
||||
bonus->type = Bonus::GENERATE_RESOURCE;
|
||||
bonus->subtype = spec.subtype;
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
case 11://starting skill with mastery (Adrienne)
|
||||
setSecSkillLevel(SecondarySkill(spec.val), spec.additionalinfo, true);
|
||||
break;
|
||||
case 12://army speed
|
||||
bonus->type = Bonus::STACKS_SPEED;
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
case 13://Dragon bonuses (Mutare)
|
||||
bonus->type = Bonus::PRIMARY_SKILL;
|
||||
bonus->valType = Bonus::ADDITIVE_VALUE;
|
||||
switch (spec.subtype)
|
||||
{
|
||||
case 1:
|
||||
bonus->subtype = PrimarySkill::ATTACK;
|
||||
break;
|
||||
case 2:
|
||||
bonus->subtype = PrimarySkill::DEFENSE;
|
||||
break;
|
||||
}
|
||||
bonus->limiter.reset(new HasAnotherBonusLimiter(Bonus::DRAGON_NATURE));
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
default:
|
||||
logGlobal->warn("Unexpected hero %s specialty %d", type->name, spec.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
specialty.push_back(hs); //will it work?
|
||||
|
||||
for (auto hs2 : type->specialty) //copy active (probably growing) bonuses from hero prootype to hero object
|
||||
{
|
||||
auto hs = new HeroSpecial();
|
||||
attachTo(hs); //do we ever need to detach it?
|
||||
|
||||
hs->setNodeType(CBonusSystemNode::SPECIALTY);
|
||||
for (auto bonus : hs2.bonuses)
|
||||
{
|
||||
hs->addNewBonus (bonus);
|
||||
}
|
||||
hs->growsWithLevel = hs2.growsWithLevel;
|
||||
|
||||
specialty.push_back(hs); //will it work?
|
||||
}
|
||||
//copy active (probably growing) bonuses from hero prototype to hero object
|
||||
for(std::shared_ptr<Bonus> b : type->specialty)
|
||||
addNewBonus(b);
|
||||
//dito for old-style bonuses -> compatibility for old savegames
|
||||
for(SSpecialtyBonus & sb : type->specialtyDeprecated)
|
||||
for(std::shared_ptr<Bonus> b : sb.bonuses)
|
||||
addNewBonus(b);
|
||||
for(SSpecialtyInfo & spec : type->specDeprecated)
|
||||
for(std::shared_ptr<Bonus> b : SpecialtyInfoToBonuses(spec, type->ID.getNum()))
|
||||
addNewBonus(b);
|
||||
|
||||
//initialize bonuses
|
||||
recreateSecondarySkillsBonuses();
|
||||
Updatespecialty();
|
||||
|
||||
mana = manaLimit(); //after all bonuses are taken into account, make sure this line is the last one
|
||||
type->name = name;
|
||||
}
|
||||
void CGHeroInstance::Updatespecialty() //TODO: calculate special value of bonuses on-the-fly?
|
||||
{
|
||||
for (auto hs : specialty)
|
||||
{
|
||||
if (hs->growsWithLevel)
|
||||
{
|
||||
//const auto &creatures = VLC->creh->creatures;
|
||||
|
||||
for(auto& b : hs->getBonusList())
|
||||
{
|
||||
switch (b->type)
|
||||
{
|
||||
case Bonus::SECONDARY_SKILL_PREMY:
|
||||
b->val = (hs->valOfBonuses(Bonus::SPECIAL_SECONDARY_SKILL, b->subtype) * level);
|
||||
break; //use only hero skills as bonuses to avoid feedback loop
|
||||
case Bonus::PRIMARY_SKILL: //for creatures, that is
|
||||
{
|
||||
const CCreature * cre = nullptr;
|
||||
int creLevel = 0;
|
||||
if (auto creatureLimiter = std::dynamic_pointer_cast<CCreatureTypeLimiter>(b->limiter)) //TODO: more general eveluation of bonuses?
|
||||
{
|
||||
cre = creatureLimiter->creature;
|
||||
creLevel = cre->level;
|
||||
if (!creLevel)
|
||||
{
|
||||
creLevel = 5; //treat ballista as tier 5
|
||||
}
|
||||
}
|
||||
else //no creature found, can't calculate value
|
||||
{
|
||||
logGlobal->warn("Primary skill specialty growth supported only with creature type limiters");
|
||||
break;
|
||||
}
|
||||
|
||||
double primSkillModifier = (int)(level / creLevel) / 20.0;
|
||||
int param;
|
||||
switch (b->subtype)
|
||||
{
|
||||
case PrimarySkill::ATTACK:
|
||||
param = cre->getPrimSkillLevel(PrimarySkill::ATTACK);
|
||||
break;
|
||||
case PrimarySkill::DEFENSE:
|
||||
param = cre->getPrimSkillLevel(PrimarySkill::DEFENSE);
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
b->val = ceil(param * (1 + primSkillModifier)) - param; //yep, overcomplicated but matches original
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGHeroInstance::recreateSecondarySkillsBonuses()
|
||||
{
|
||||
@ -766,6 +540,23 @@ void CGHeroInstance::recreateSecondarySkillsBonuses()
|
||||
updateSkill(SecondarySkill(skill_info.first), level);
|
||||
}
|
||||
|
||||
void CGHeroInstance::recreateSpecialtyBonuses(std::vector<HeroSpecial *> & specialtyDeprecated)
|
||||
{
|
||||
auto HeroSpecialToSpecialtyBonus = [](HeroSpecial & hs) -> SSpecialtyBonus
|
||||
{
|
||||
SSpecialtyBonus sb;
|
||||
sb.growsWithLevel = hs.growsWithLevel;
|
||||
sb.bonuses = hs.getBonusList();
|
||||
return sb;
|
||||
};
|
||||
|
||||
for(HeroSpecial * hs : specialtyDeprecated)
|
||||
{
|
||||
for(std::shared_ptr<Bonus> b : SpecialtyBonusToBonuses(HeroSpecialToSpecialtyBonus(*hs)))
|
||||
addNewBonus(b);
|
||||
}
|
||||
}
|
||||
|
||||
void CGHeroInstance::updateSkill(SecondarySkill which, int val)
|
||||
{
|
||||
auto skillBonus = (*VLC->skillh)[which]->getBonus(val);
|
||||
@ -863,7 +654,7 @@ int64_t CGHeroInstance::getSpellBonus(const spells::Spell * spell, int64_t base,
|
||||
base *= (100.0 + maxSchoolBonus) / 100.0;
|
||||
|
||||
if(affectedStack && affectedStack->creatureLevel() > 0) //Hero specials like Solmyr, Deemer
|
||||
base *= (100. + ((valOfBonuses(Bonus::SPECIAL_SPELL_LEV, spell->getIndex()) * level) / affectedStack->creatureLevel())) / 100.0;
|
||||
base *= (100. + valOfBonuses(Bonus::SPECIAL_SPELL_LEV, spell->getIndex()) / affectedStack->creatureLevel()) / 100.0;
|
||||
|
||||
return base;
|
||||
}
|
||||
@ -1214,11 +1005,6 @@ int CGHeroInstance::maxSpellLevel() const
|
||||
void CGHeroInstance::deserializationFix()
|
||||
{
|
||||
artDeserializationFix(this);
|
||||
|
||||
for (auto hs : specialty)
|
||||
{
|
||||
attachTo (hs);
|
||||
}
|
||||
}
|
||||
|
||||
CBonusSystemNode * CGHeroInstance::whereShouldBeAttached(CGameState *gs)
|
||||
@ -1471,8 +1257,8 @@ void CGHeroInstance::levelUp(std::vector<SecondarySkill> skills)
|
||||
}
|
||||
}
|
||||
|
||||
//specialty
|
||||
Updatespecialty();
|
||||
//update specialty and other bonuses that scale with level
|
||||
treeHasChanged();
|
||||
}
|
||||
|
||||
void CGHeroInstance::levelUpAutomatically(CRandomGenerator & rand)
|
||||
|
@ -95,7 +95,8 @@ public:
|
||||
}
|
||||
} patrol;
|
||||
|
||||
struct DLL_LINKAGE HeroSpecial : CBonusSystemNode
|
||||
// deprecated - used only for loading of old saves
|
||||
struct HeroSpecial : CBonusSystemNode
|
||||
{
|
||||
bool growsWithLevel;
|
||||
|
||||
@ -108,8 +109,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<HeroSpecial*> specialty;
|
||||
|
||||
struct DLL_LINKAGE SecondarySkillsInfo
|
||||
{
|
||||
//skills are determined, initialized at map start
|
||||
@ -215,7 +214,6 @@ public:
|
||||
void pushPrimSkill(PrimarySkill::PrimarySkill which, int val);
|
||||
ui8 maxlevelsToMagicSchool() const;
|
||||
ui8 maxlevelsToWisdom() const;
|
||||
void Updatespecialty();
|
||||
void recreateSecondarySkillsBonuses();
|
||||
void updateSkill(SecondarySkill which, int val);
|
||||
|
||||
@ -269,6 +267,7 @@ protected:
|
||||
|
||||
private:
|
||||
void levelUpAutomatically(CRandomGenerator & rand);
|
||||
void recreateSpecialtyBonuses(std::vector<HeroSpecial*> & specialtyDeprecated);
|
||||
|
||||
public:
|
||||
std::string getHeroTypeName() const;
|
||||
@ -297,7 +296,13 @@ public:
|
||||
h & visitedTown;
|
||||
h & boat;
|
||||
h & type;
|
||||
h & specialty;
|
||||
if(version < 781)
|
||||
{
|
||||
std::vector<HeroSpecial*> specialtyDeprecated;
|
||||
h & specialtyDeprecated;
|
||||
if(!h.saving)
|
||||
recreateSpecialtyBonuses(specialtyDeprecated);
|
||||
}
|
||||
h & commander;
|
||||
h & visitedObjects;
|
||||
BONUS_TREE_DESERIALIZATION_FIX
|
||||
|
@ -135,6 +135,12 @@ void registerTypesMapObjectTypes(Serializer &s)
|
||||
REGISTER_GENERIC_HANDLER(CGWitchHut);
|
||||
|
||||
#undef REGISTER_GENERIC_HANDLER
|
||||
|
||||
s.template registerType<IUpdater, GrowsWithLevelUpdater>();
|
||||
s.template registerType<IUpdater, TimesHeroLevelUpdater>();
|
||||
s.template registerType<IUpdater, TimesStackLevelUpdater>();
|
||||
//new types (other than netpacks) must register here
|
||||
//order of type registration is critical for loading old savegames
|
||||
}
|
||||
|
||||
template<typename Serializer>
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "../ConstTransitivePtr.h"
|
||||
#include "../GameConstants.h"
|
||||
|
||||
const ui32 SERIALIZATION_VERSION = 780;
|
||||
const ui32 SERIALIZATION_VERSION = 781;
|
||||
const ui32 MINIMAL_SERIALIZATION_VERSION = 753;
|
||||
const std::string SAVEGAME_MAGIC = "VCMISVG";
|
||||
|
||||
|
@ -219,7 +219,7 @@ void Timed::prepareEffects(SetStackEffect & sse, const Mechanics * m, const Effe
|
||||
}
|
||||
if(casterHero && casterHero->hasBonusOfType(Bonus::SPECIAL_BLESS_DAMAGE, m->getSpellIndex())) //TODO: better handling of bonus percentages
|
||||
{
|
||||
int damagePercent = casterHero->level * casterHero->valOfBonuses(Bonus::SPECIAL_BLESS_DAMAGE, m->getSpellIndex()) / tier;
|
||||
int damagePercent = casterHero->valOfBonuses(Bonus::SPECIAL_BLESS_DAMAGE, m->getSpellIndex()) / tier;
|
||||
Bonus specialBonus(Bonus::N_TURNS, Bonus::CREATURE_DAMAGE, Bonus::SPELL_EFFECT, damagePercent, m->getSpellIndex(), 0, Bonus::PERCENT_TO_ALL);
|
||||
specialBonus.turnsRemain = duration;
|
||||
buffer.push_back(specialBonus);
|
||||
|
Loading…
Reference in New Issue
Block a user