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
|
* Various bug fixes
|
||||||
|
|
||||||
Henning Koehler, <henning.koehler.nz@gmail.com>
|
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
|
* Improve support for WoG commander artifacts and skill descriptions
|
||||||
* Added basic support for secondary skill modding
|
* Added basic support for secondary skill modding
|
||||||
* Map object sounds can now be configured via json
|
* Map object sounds can now be configured via json
|
||||||
|
* Added bonus updaters for hero specialties
|
||||||
|
|
||||||
SOUND:
|
SOUND:
|
||||||
* Fixed many mising or wrong pickup and visit sounds for map objects
|
* 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);
|
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
|
//removes element i from container c, returns false if c does not contain i
|
||||||
template <typename Container, typename Item>
|
template <typename Container, typename Item>
|
||||||
typename Container::size_type operator-=(Container &c, const Item &i)
|
typename Container::size_type operator-=(Container &c, const Item &i)
|
||||||
|
@ -9,10 +9,17 @@
|
|||||||
{ "skill" : "leadership", "level": "basic" },
|
{ "skill" : "leadership", "level": "basic" },
|
||||||
{ "skill" : "archery", "level": "basic" }
|
{ "skill" : "archery", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 1, "info": 0 }
|
"archery" : {
|
||||||
]
|
"subtype" : "skill.archery",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"valeska":
|
"valeska":
|
||||||
{
|
{
|
||||||
@ -24,10 +31,9 @@
|
|||||||
{ "skill" : "leadership", "level": "basic" },
|
{ "skill" : "leadership", "level": "basic" },
|
||||||
{ "skill" : "archery", "level": "basic" }
|
{ "skill" : "archery", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "archer"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 2 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"edric":
|
"edric":
|
||||||
{
|
{
|
||||||
@ -39,10 +45,9 @@
|
|||||||
{ "skill" : "leadership", "level": "basic" },
|
{ "skill" : "leadership", "level": "basic" },
|
||||||
{ "skill" : "armorer", "level": "basic" }
|
{ "skill" : "armorer", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "griffin"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 4 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"sylvia":
|
"sylvia":
|
||||||
{
|
{
|
||||||
@ -54,10 +59,17 @@
|
|||||||
{ "skill" : "leadership", "level": "basic" },
|
{ "skill" : "leadership", "level": "basic" },
|
||||||
{ "skill" : "navigation", "level": "basic" }
|
{ "skill" : "navigation", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 2, "subtype": 5, "info": 1 }
|
"navigation" : {
|
||||||
]
|
"subtype" : "skill.navigation",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"lordHaart":
|
"lordHaart":
|
||||||
{
|
{
|
||||||
@ -70,10 +82,17 @@
|
|||||||
{ "skill" : "leadership", "level": "basic" },
|
{ "skill" : "leadership", "level": "basic" },
|
||||||
{ "skill" : "estates", "level": "basic" }
|
{ "skill" : "estates", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 13, "info": 0 }
|
"estates" : {
|
||||||
]
|
"subtype" : "skill.estates",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"sorsha":
|
"sorsha":
|
||||||
{
|
{
|
||||||
@ -85,10 +104,9 @@
|
|||||||
{ "skill" : "leadership", "level": "basic" },
|
{ "skill" : "leadership", "level": "basic" },
|
||||||
{ "skill" : "offence", "level": "basic" }
|
{ "skill" : "offence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "swordsman"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 6 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"christian":
|
"christian":
|
||||||
{
|
{
|
||||||
@ -100,10 +118,9 @@
|
|||||||
{ "skill" : "leadership", "level": "basic" },
|
{ "skill" : "leadership", "level": "basic" },
|
||||||
{ "skill" : "artillery", "level": "basic" }
|
{ "skill" : "artillery", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "ballista"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 146 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"tyris":
|
"tyris":
|
||||||
{
|
{
|
||||||
@ -115,10 +132,9 @@
|
|||||||
{ "skill" : "leadership", "level": "basic" },
|
{ "skill" : "leadership", "level": "basic" },
|
||||||
{ "skill" : "tactics", "level": "basic" }
|
{ "skill" : "tactics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "cavalier"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 10 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"rion":
|
"rion":
|
||||||
{
|
{
|
||||||
@ -131,10 +147,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "firstAid", "level": "basic" }
|
{ "skill" : "firstAid", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 27, "info": 0 }
|
"firstAid" : {
|
||||||
]
|
"subtype" : "skill.firstAid",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"adela":
|
"adela":
|
||||||
{
|
{
|
||||||
@ -147,10 +170,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "diplomacy", "level": "basic" }
|
{ "skill" : "diplomacy", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":6, "val": 3, "subtype": 41, "info": 0 }
|
"bless" : {
|
||||||
]
|
"addInfo" : 0,
|
||||||
|
"subtype" : "spell.bless",
|
||||||
|
"type" : "SPECIAL_BLESS_DAMAGE",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"cuthbert":
|
"cuthbert":
|
||||||
{
|
{
|
||||||
@ -163,10 +193,15 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "estates", "level": "basic" }
|
{ "skill" : "estates", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":8, "val": 0, "subtype": 45, "info": 0 }
|
"weakness" : {
|
||||||
]
|
"addInfo" : 0,
|
||||||
|
"subtype" : "spell.weakness",
|
||||||
|
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"adelaide":
|
"adelaide":
|
||||||
{
|
{
|
||||||
@ -178,10 +213,16 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "wisdom", "level": "advanced" }
|
{ "skill" : "wisdom", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":3, "val": 3, "subtype": 20, "info": 0 }
|
"frostRing" : {
|
||||||
]
|
"subtype" : "spell.frostRing",
|
||||||
|
"type" : "SPECIAL_SPELL_LEV",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"ingham":
|
"ingham":
|
||||||
{
|
{
|
||||||
@ -194,10 +235,9 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "mysticism", "level": "basic" }
|
{ "skill" : "mysticism", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "monk"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 8 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"sanya":
|
"sanya":
|
||||||
{
|
{
|
||||||
@ -210,10 +250,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "eagleEye", "level": "basic" }
|
{ "skill" : "eagleEye", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 11, "info": 0 }
|
"eagleEye" : {
|
||||||
]
|
"subtype" : "skill.eagleEye",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"loynis":
|
"loynis":
|
||||||
{
|
{
|
||||||
@ -226,10 +273,15 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "learning", "level": "basic" }
|
{ "skill" : "learning", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":8, "val": 0, "subtype": 48, "info": 0 }
|
"prayer" : {
|
||||||
]
|
"addInfo" : 0,
|
||||||
|
"subtype" : "spell.prayer",
|
||||||
|
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"caitlin":
|
"caitlin":
|
||||||
{
|
{
|
||||||
@ -242,9 +294,14 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "intelligence", "level": "basic" }
|
{ "skill" : "intelligence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
"gold" : {
|
||||||
]
|
"subtype" : "resource.gold",
|
||||||
|
"type" : "GENERATE_RESOURCE",
|
||||||
|
"val" : 350
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,22 @@
|
|||||||
{ "skill" : "artillery", "level": "basic" },
|
{ "skill" : "artillery", "level": "basic" },
|
||||||
{ "skill" : "offence", "level": "basic" }
|
{ "skill" : "offence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"base" : {
|
||||||
{ "type":4, "val": 3, "subtype": 1, "info": 120 },
|
"limiters" : [
|
||||||
{ "type":4, "val": 3, "subtype": 2, "info": 120 }
|
{
|
||||||
]
|
"parameters" : [ "psychicElemental", true ],
|
||||||
|
"type" : "CREATURE_TYPE_LIMITER"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type" : "PRIMARY_SKILL",
|
||||||
|
"val" : 3
|
||||||
|
},
|
||||||
|
"bonuses" : {
|
||||||
|
"attack" : { "subtype" : "primSkill.attack" },
|
||||||
|
"defence" : { "subtype" : "primSkill.defence" }
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"thunar":
|
"thunar":
|
||||||
{
|
{
|
||||||
@ -25,12 +36,32 @@
|
|||||||
{ "skill" : "estates", "level": "basic" },
|
{ "skill" : "estates", "level": "basic" },
|
||||||
{ "skill" : "tactics", "level": "basic" }
|
{ "skill" : "tactics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"base" : {
|
||||||
{ "type":4, "val": 2, "subtype": 1, "info": 113 },
|
"limiters" : [
|
||||||
{ "type":4, "val": 1, "subtype": 2, "info": 113 },
|
{
|
||||||
{ "type":4, "val": 5, "subtype": 4, "info": 113 }
|
"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":
|
"ignissa":
|
||||||
{
|
{
|
||||||
@ -42,12 +73,33 @@
|
|||||||
{ "skill" : "artillery", "level": "basic" },
|
{ "skill" : "artillery", "level": "basic" },
|
||||||
{ "skill" : "offence", "level": "basic" }
|
{ "skill" : "offence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"base" : {
|
||||||
{ "type":4, "val": 1, "subtype": 1, "info": 114 },
|
"limiters" : [
|
||||||
{ "type":4, "val": 2, "subtype": 1, "info": 114 },
|
{
|
||||||
{ "type":4, "val": 2, "subtype": 3, "info": 114 }
|
"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":
|
"lacus":
|
||||||
{
|
{
|
||||||
@ -58,10 +110,21 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "tactics", "level": "advanced" }
|
{ "skill" : "tactics", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":4, "val": 2, "subtype": 1, "info": 115 }
|
"attack" : {
|
||||||
]
|
"limiters" : [
|
||||||
|
{
|
||||||
|
"parameters" : [ "waterElemental", true ],
|
||||||
|
"type" : "CREATURE_TYPE_LIMITER"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subtype" : "primSkill.attack",
|
||||||
|
"type" : "PRIMARY_SKILL",
|
||||||
|
"val" : 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"monere":
|
"monere":
|
||||||
{
|
{
|
||||||
@ -73,11 +136,22 @@
|
|||||||
{ "skill" : "logistics", "level": "basic" },
|
{ "skill" : "logistics", "level": "basic" },
|
||||||
{ "skill" : "offence", "level": "basic" }
|
{ "skill" : "offence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"base" : {
|
||||||
{ "type":4, "val": 3, "subtype": 1, "info": 120 },
|
"limiters" : [
|
||||||
{ "type":4, "val": 3, "subtype": 2, "info": 120 }
|
{
|
||||||
]
|
"parameters" : [ "psychicElemental", true ],
|
||||||
|
"type" : "CREATURE_TYPE_LIMITER"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type" : "PRIMARY_SKILL",
|
||||||
|
"val" : 3
|
||||||
|
},
|
||||||
|
"bonuses" : {
|
||||||
|
"attack" : { "subtype" : "primSkill.attack" },
|
||||||
|
"defence" : { "subtype" : "primSkill.defence" }
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"erdamon":
|
"erdamon":
|
||||||
{
|
{
|
||||||
@ -89,12 +163,32 @@
|
|||||||
{ "skill" : "estates", "level": "basic" },
|
{ "skill" : "estates", "level": "basic" },
|
||||||
{ "skill" : "tactics", "level": "basic" }
|
{ "skill" : "tactics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"base" : {
|
||||||
{ "type":4, "val": 2, "subtype": 1, "info": 113 },
|
"limiters" : [
|
||||||
{ "type":4, "val": 1, "subtype": 2, "info": 113 },
|
{
|
||||||
{ "type":4, "val": 5, "subtype": 4, "info": 113 }
|
"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":
|
"fiur":
|
||||||
{
|
{
|
||||||
@ -105,12 +199,33 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "offence", "level": "advanced" }
|
{ "skill" : "offence", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"base" : {
|
||||||
{ "type":4, "val": 1, "subtype": 1, "info": 114 },
|
"limiters" : [
|
||||||
{ "type":4, "val": 2, "subtype": 1, "info": 114 },
|
{
|
||||||
{ "type":4, "val": 2, "subtype": 3, "info": 114 }
|
"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":
|
"kalt":
|
||||||
{
|
{
|
||||||
@ -122,10 +237,21 @@
|
|||||||
{ "skill" : "tactics", "level": "basic" },
|
{ "skill" : "tactics", "level": "basic" },
|
||||||
{ "skill" : "learning", "level": "basic" }
|
{ "skill" : "learning", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":4, "val": 2, "subtype": 1, "info": 115 }
|
"attack" : {
|
||||||
]
|
"limiters" : [
|
||||||
|
{
|
||||||
|
"parameters" : [ "waterElemental", true ],
|
||||||
|
"type" : "CREATURE_TYPE_LIMITER"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subtype" : "primSkill.attack",
|
||||||
|
"type" : "PRIMARY_SKILL",
|
||||||
|
"val" : 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"luna":
|
"luna":
|
||||||
{
|
{
|
||||||
@ -138,10 +264,16 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "fireMagic", "level": "basic" }
|
{ "skill" : "fireMagic", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":5, "val": 100, "subtype": 13, "info": 0 }
|
"fireWall" : {
|
||||||
]
|
"subtype" : "spell.fireWall",
|
||||||
|
"type" : "SPECIFIC_SPELL_DAMAGE",
|
||||||
|
"val" : 100,
|
||||||
|
"valueType" : "BASE_NUMBER"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"brissa":
|
"brissa":
|
||||||
{
|
{
|
||||||
@ -154,10 +286,15 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "airMagic", "level": "basic" }
|
{ "skill" : "airMagic", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":8, "val": 0, "subtype": 53, "info": 0 }
|
"haste" : {
|
||||||
]
|
"addInfo" : 0,
|
||||||
|
"subtype" : "spell.haste",
|
||||||
|
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"ciele":
|
"ciele":
|
||||||
{
|
{
|
||||||
@ -170,10 +307,16 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "waterMagic", "level": "basic" }
|
{ "skill" : "waterMagic", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":5, "val": 50, "subtype": 15, "info": 0 }
|
"magicArrow" : {
|
||||||
]
|
"subtype" : "spell.magicArrow",
|
||||||
|
"type" : "SPECIFIC_SPELL_DAMAGE",
|
||||||
|
"val" : 50,
|
||||||
|
"valueType" : "BASE_NUMBER"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"labetha":
|
"labetha":
|
||||||
{
|
{
|
||||||
@ -186,10 +329,15 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "earthMagic", "level": "basic" }
|
{ "skill" : "earthMagic", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":8, "val": 0, "subtype": 46, "info": 0 }
|
"stoneSkin" : {
|
||||||
]
|
"addInfo" : 0,
|
||||||
|
"subtype" : "spell.stoneSkin",
|
||||||
|
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"inteus":
|
"inteus":
|
||||||
{
|
{
|
||||||
@ -202,10 +350,15 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "fireMagic", "level": "basic" }
|
{ "skill" : "fireMagic", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":8, "val": 0, "subtype": 43, "info": 0 }
|
"bloodlust" : {
|
||||||
]
|
"addInfo" : 0,
|
||||||
|
"subtype" : "spell.bloodlust",
|
||||||
|
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"aenain":
|
"aenain":
|
||||||
{
|
{
|
||||||
@ -218,10 +371,15 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "airMagic", "level": "basic" }
|
{ "skill" : "airMagic", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":8, "val": 0, "subtype": 47, "info": 0 }
|
"disruptingRay" : {
|
||||||
]
|
"addInfo" : 0,
|
||||||
|
"subtype" : "spell.disruptingRay",
|
||||||
|
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"gelare":
|
"gelare":
|
||||||
{
|
{
|
||||||
@ -234,10 +392,15 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "waterMagic", "level": "basic" }
|
{ "skill" : "waterMagic", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
"gold" : {
|
||||||
]
|
"subtype" : "resource.gold",
|
||||||
|
"type" : "GENERATE_RESOURCE",
|
||||||
|
"val" : 350
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"grindan":
|
"grindan":
|
||||||
{
|
{
|
||||||
@ -250,9 +413,14 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "earthMagic", "level": "basic" }
|
{ "skill" : "earthMagic", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
"gold" : {
|
||||||
]
|
"subtype" : "resource.gold",
|
||||||
|
"type" : "GENERATE_RESOURCE",
|
||||||
|
"val" : 350
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,9 @@
|
|||||||
{ "skill" : "scouting", "level": "basic" },
|
{ "skill" : "scouting", "level": "basic" },
|
||||||
{ "skill" : "leadership", "level": "basic" }
|
{ "skill" : "leadership", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "harpy"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 72 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"arlach":
|
"arlach":
|
||||||
{
|
{
|
||||||
@ -24,10 +23,9 @@
|
|||||||
{ "skill" : "artillery", "level": "basic" },
|
{ "skill" : "artillery", "level": "basic" },
|
||||||
{ "skill" : "offence", "level": "basic" }
|
{ "skill" : "offence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "ballista"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 146 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"dace":
|
"dace":
|
||||||
{
|
{
|
||||||
@ -39,10 +37,9 @@
|
|||||||
{ "skill" : "tactics", "level": "basic" },
|
{ "skill" : "tactics", "level": "basic" },
|
||||||
{ "skill" : "offence", "level": "basic" }
|
{ "skill" : "offence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "minotaur"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 78 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"ajit":
|
"ajit":
|
||||||
{
|
{
|
||||||
@ -54,10 +51,9 @@
|
|||||||
{ "skill" : "leadership", "level": "basic" },
|
{ "skill" : "leadership", "level": "basic" },
|
||||||
{ "skill" : "resistance", "level": "basic" }
|
{ "skill" : "resistance", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "beholder"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 74 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"damacon":
|
"damacon":
|
||||||
{
|
{
|
||||||
@ -68,10 +64,15 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "offence", "level": "advanced" }
|
{ "skill" : "offence", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
"gold" : {
|
||||||
]
|
"subtype" : "resource.gold",
|
||||||
|
"type" : "GENERATE_RESOURCE",
|
||||||
|
"val" : 350
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"gunnar":
|
"gunnar":
|
||||||
{
|
{
|
||||||
@ -83,10 +84,17 @@
|
|||||||
{ "skill" : "logistics", "level": "basic" },
|
{ "skill" : "logistics", "level": "basic" },
|
||||||
{ "skill" : "tactics", "level": "basic" }
|
{ "skill" : "tactics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 2, "info": 0 }
|
"logistics" : {
|
||||||
]
|
"subtype" : "skill.logistics",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"synca":
|
"synca":
|
||||||
{
|
{
|
||||||
@ -98,10 +106,9 @@
|
|||||||
{ "skill" : "leadership", "level": "basic" },
|
{ "skill" : "leadership", "level": "basic" },
|
||||||
{ "skill" : "scholar", "level": "basic" }
|
{ "skill" : "scholar", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "manticore"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 80 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"shakti":
|
"shakti":
|
||||||
{
|
{
|
||||||
@ -113,10 +120,9 @@
|
|||||||
{ "skill" : "tactics", "level": "basic" },
|
{ "skill" : "tactics", "level": "basic" },
|
||||||
{ "skill" : "offence", "level": "basic" }
|
{ "skill" : "offence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "troglodyte"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 70 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"alamar":
|
"alamar":
|
||||||
{
|
{
|
||||||
@ -129,10 +135,16 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "scholar", "level": "basic" }
|
{ "skill" : "scholar", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":3, "val": 3, "subtype": 38, "info": 0 }
|
"resurrection" : {
|
||||||
]
|
"subtype" : "spell.resurrection",
|
||||||
|
"type" : "SPECIAL_SPELL_LEV",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"jaegar":
|
"jaegar":
|
||||||
{
|
{
|
||||||
@ -145,10 +157,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "mysticism", "level": "basic" }
|
{ "skill" : "mysticism", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 8, "info": 1 }
|
"mysticism" : {
|
||||||
]
|
"subtype" : "skill.mysticism",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"malekith":
|
"malekith":
|
||||||
{
|
{
|
||||||
@ -161,10 +180,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "sorcery", "level": "basic" }
|
{ "skill" : "sorcery", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 25, "info": 0 }
|
"sorcery" : {
|
||||||
]
|
"subtype" : "skill.sorcery",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"jeddite":
|
"jeddite":
|
||||||
{
|
{
|
||||||
@ -176,10 +202,16 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "wisdom", "level": "advanced" }
|
{ "skill" : "wisdom", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":3, "val": 3, "subtype": 38, "info": 0 }
|
"resurrection" : {
|
||||||
]
|
"subtype" : "spell.resurrection",
|
||||||
|
"type" : "SPECIAL_SPELL_LEV",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"geon":
|
"geon":
|
||||||
{
|
{
|
||||||
@ -192,10 +224,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "eagleEye", "level": "basic" }
|
{ "skill" : "eagleEye", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 11, "info": 1 }
|
"eagleEye" : {
|
||||||
]
|
"subtype" : "skill.eagleEye",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"deemer":
|
"deemer":
|
||||||
{
|
{
|
||||||
@ -208,10 +247,16 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "scouting", "level": "advanced" }
|
{ "skill" : "scouting", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":3, "val": 3, "subtype": 23, "info": 0 }
|
"meteorShower" : {
|
||||||
]
|
"subtype" : "spell.meteorShower",
|
||||||
|
"type" : "SPECIAL_SPELL_LEV",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"sephinroth":
|
"sephinroth":
|
||||||
{
|
{
|
||||||
@ -224,10 +269,15 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "intelligence", "level": "basic" }
|
{ "skill" : "intelligence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":10, "val": 1, "subtype": 4, "info": 0 }
|
"crystal" : {
|
||||||
]
|
"subtype" : "resource.crystal",
|
||||||
|
"type" : "GENERATE_RESOURCE",
|
||||||
|
"val" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"darkstorn":
|
"darkstorn":
|
||||||
{
|
{
|
||||||
@ -240,9 +290,14 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "learning", "level": "basic" }
|
{ "skill" : "learning", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":8, "val": 0, "subtype": 46, "info": 0 }
|
"stoneSkin" : {
|
||||||
]
|
"addInfo" : 0,
|
||||||
|
"subtype" : "spell.stoneSkin",
|
||||||
|
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,9 @@
|
|||||||
{ "skill" : "armorer", "level": "basic" },
|
{ "skill" : "armorer", "level": "basic" },
|
||||||
{ "skill" : "resistance", "level": "basic" }
|
{ "skill" : "resistance", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "basilisk"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 106 }
|
},
|
||||||
],
|
|
||||||
"army" :
|
"army" :
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -39,10 +38,9 @@
|
|||||||
{ "skill" : "armorer", "level": "basic" },
|
{ "skill" : "armorer", "level": "basic" },
|
||||||
{ "skill" : "leadership", "level": "basic" }
|
{ "skill" : "leadership", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "gnoll"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 98 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"wystan":
|
"wystan":
|
||||||
{
|
{
|
||||||
@ -54,10 +52,9 @@
|
|||||||
{ "skill" : "armorer", "level": "basic" },
|
{ "skill" : "armorer", "level": "basic" },
|
||||||
{ "skill" : "archery", "level": "basic" }
|
{ "skill" : "archery", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "lizardman"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 100 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"tazar":
|
"tazar":
|
||||||
{
|
{
|
||||||
@ -68,10 +65,17 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "armorer", "level": "advanced" }
|
{ "skill" : "armorer", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 23, "info": 0 }
|
"armorer" : {
|
||||||
]
|
"subtype" : "skill.armorer",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"alkin":
|
"alkin":
|
||||||
{
|
{
|
||||||
@ -83,10 +87,9 @@
|
|||||||
{ "skill" : "armorer", "level": "basic" },
|
{ "skill" : "armorer", "level": "basic" },
|
||||||
{ "skill" : "offence", "level": "basic" }
|
{ "skill" : "offence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "gorgon"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 102 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"korbac":
|
"korbac":
|
||||||
{
|
{
|
||||||
@ -98,10 +101,9 @@
|
|||||||
{ "skill" : "armorer", "level": "basic" },
|
{ "skill" : "armorer", "level": "basic" },
|
||||||
{ "skill" : "pathfinding", "level": "basic" }
|
{ "skill" : "pathfinding", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "serpentFly"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 104 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"gerwulf":
|
"gerwulf":
|
||||||
{
|
{
|
||||||
@ -113,10 +115,9 @@
|
|||||||
{ "skill" : "armorer", "level": "basic" },
|
{ "skill" : "armorer", "level": "basic" },
|
||||||
{ "skill" : "artillery", "level": "basic" }
|
{ "skill" : "artillery", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "ballista"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 146 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"broghild":
|
"broghild":
|
||||||
{
|
{
|
||||||
@ -128,10 +129,9 @@
|
|||||||
{ "skill" : "armorer", "level": "basic" },
|
{ "skill" : "armorer", "level": "basic" },
|
||||||
{ "skill" : "scouting", "level": "basic" }
|
{ "skill" : "scouting", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "wyvern"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 108 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"mirlanda":
|
"mirlanda":
|
||||||
{
|
{
|
||||||
@ -143,10 +143,15 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "wisdom", "level": "advanced" }
|
{ "skill" : "wisdom", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":8, "val": 0, "subtype": 45, "info": 0 }
|
"weakness" : {
|
||||||
]
|
"addInfo" : 0,
|
||||||
|
"subtype" : "spell.weakness",
|
||||||
|
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"rosic":
|
"rosic":
|
||||||
{
|
{
|
||||||
@ -159,10 +164,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "mysticism", "level": "basic" }
|
{ "skill" : "mysticism", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 8, "info": 1 }
|
"mysticism" : {
|
||||||
]
|
"subtype" : "skill.mysticism",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"voy":
|
"voy":
|
||||||
{
|
{
|
||||||
@ -175,10 +187,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "navigation", "level": "basic" }
|
{ "skill" : "navigation", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 2, "subtype": 5, "info": 1 }
|
"navigation" : {
|
||||||
]
|
"subtype" : "skill.navigation",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"verdish":
|
"verdish":
|
||||||
{
|
{
|
||||||
@ -191,10 +210,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "firstAid", "level": "basic" }
|
{ "skill" : "firstAid", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 27, "info": 1 }
|
"firstAid" : {
|
||||||
]
|
"subtype" : "skill.firstAid",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"merist":
|
"merist":
|
||||||
{
|
{
|
||||||
@ -207,10 +233,15 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "learning", "level": "basic" }
|
{ "skill" : "learning", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":8, "val": 0, "subtype": 46, "info": 0 }
|
"stoneSkin" : {
|
||||||
]
|
"addInfo" : 0,
|
||||||
|
"subtype" : "spell.stoneSkin",
|
||||||
|
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"styg":
|
"styg":
|
||||||
{
|
{
|
||||||
@ -223,10 +254,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "sorcery", "level": "basic" }
|
{ "skill" : "sorcery", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 25, "info": 0 }
|
"sorcery" : {
|
||||||
]
|
"subtype" : "skill.sorcery",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"andra":
|
"andra":
|
||||||
{
|
{
|
||||||
@ -239,10 +277,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "intelligence", "level": "basic" }
|
{ "skill" : "intelligence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 24, "info": 0 }
|
"intelligence" : {
|
||||||
]
|
"subtype" : "skill.intelligence",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"tiva":
|
"tiva":
|
||||||
{
|
{
|
||||||
@ -255,9 +300,16 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "eagleEye", "level": "basic" }
|
{ "skill" : "eagleEye", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 11, "info": 1 }
|
"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" }
|
{ "skill" : "scouting", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "hellHound"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 46 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"rashka":
|
"rashka":
|
||||||
{
|
{
|
||||||
@ -23,10 +22,9 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "scholar", "level": "basic" }
|
{ "skill" : "scholar", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "efreet"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 52 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"marius":
|
"marius":
|
||||||
{
|
{
|
||||||
@ -37,10 +35,9 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "armorer", "level": "advanced" }
|
{ "skill" : "armorer", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "demon"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 48 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"ignatius":
|
"ignatius":
|
||||||
{
|
{
|
||||||
@ -52,10 +49,9 @@
|
|||||||
{ "skill" : "tactics", "level": "basic" },
|
{ "skill" : "tactics", "level": "basic" },
|
||||||
{ "skill" : "resistance", "level": "basic" }
|
{ "skill" : "resistance", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "imp"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 42 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"octavia":
|
"octavia":
|
||||||
{
|
{
|
||||||
@ -67,10 +63,15 @@
|
|||||||
{ "skill" : "scholar", "level": "basic" },
|
{ "skill" : "scholar", "level": "basic" },
|
||||||
{ "skill" : "offence", "level": "basic" }
|
{ "skill" : "offence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
"gold" : {
|
||||||
]
|
"subtype" : "resource.gold",
|
||||||
|
"type" : "GENERATE_RESOURCE",
|
||||||
|
"val" : 350
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"calh":
|
"calh":
|
||||||
{
|
{
|
||||||
@ -82,10 +83,9 @@
|
|||||||
{ "skill" : "archery", "level": "basic" },
|
{ "skill" : "archery", "level": "basic" },
|
||||||
{ "skill" : "scouting", "level": "basic" }
|
{ "skill" : "scouting", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "gog"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 42 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"pyre":
|
"pyre":
|
||||||
{
|
{
|
||||||
@ -97,10 +97,9 @@
|
|||||||
{ "skill" : "artillery", "level": "basic" },
|
{ "skill" : "artillery", "level": "basic" },
|
||||||
{ "skill" : "logistics", "level": "basic" }
|
{ "skill" : "logistics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "ballista"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 146 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"nymus":
|
"nymus":
|
||||||
{
|
{
|
||||||
@ -111,10 +110,9 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "offence", "level": "advanced" }
|
{ "skill" : "offence", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "pitFiend"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 50 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"ayden":
|
"ayden":
|
||||||
{
|
{
|
||||||
@ -127,10 +125,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "intelligence", "level": "basic" }
|
{ "skill" : "intelligence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 24, "info": 0 }
|
"intelligence" : {
|
||||||
]
|
"subtype" : "skill.intelligence",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"xyron":
|
"xyron":
|
||||||
{
|
{
|
||||||
@ -143,10 +148,16 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "scholar", "level": "basic" }
|
{ "skill" : "scholar", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":3, "val": 3, "subtype": 22, "info": 0 }
|
"inferno" : {
|
||||||
]
|
"subtype" : "spell.inferno",
|
||||||
|
"type" : "SPECIAL_SPELL_LEV",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"axsis":
|
"axsis":
|
||||||
{
|
{
|
||||||
@ -159,10 +170,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "mysticism", "level": "basic" }
|
{ "skill" : "mysticism", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 8, "info": 1 }
|
"mysticism" : {
|
||||||
]
|
"subtype" : "skill.mysticism",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"olema":
|
"olema":
|
||||||
{
|
{
|
||||||
@ -175,10 +193,15 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "ballistics", "level": "basic" }
|
{ "skill" : "ballistics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":8, "val": 0, "subtype": 45, "info": 0 }
|
"weakness" : {
|
||||||
]
|
"addInfo" : 0,
|
||||||
|
"subtype" : "spell.weakness",
|
||||||
|
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"calid":
|
"calid":
|
||||||
{
|
{
|
||||||
@ -191,10 +214,15 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "learning", "level": "basic" }
|
{ "skill" : "learning", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":10, "val": 1, "subtype": 3, "info": 0 }
|
"sulfur" : {
|
||||||
]
|
"subtype" : "resource.sulfur",
|
||||||
|
"type" : "GENERATE_RESOURCE",
|
||||||
|
"val" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"ash":
|
"ash":
|
||||||
{
|
{
|
||||||
@ -207,10 +235,15 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "eagleEye", "level": "basic" }
|
{ "skill" : "eagleEye", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":8, "val": 0, "subtype": 43, "info": 0 }
|
"bloodlust" : {
|
||||||
]
|
"addInfo" : 0,
|
||||||
|
"subtype" : "spell.bloodlust",
|
||||||
|
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"zydar":
|
"zydar":
|
||||||
{
|
{
|
||||||
@ -223,10 +256,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "sorcery", "level": "basic" }
|
{ "skill" : "sorcery", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 25, "info": 0 }
|
"sorcery" : {
|
||||||
]
|
"subtype" : "skill.sorcery",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"xarfax":
|
"xarfax":
|
||||||
{
|
{
|
||||||
@ -239,9 +279,15 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "leadership", "level": "basic" }
|
{ "skill" : "leadership", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":3, "val": 3, "subtype": 21, "info": 0 }
|
"fireball" : {
|
||||||
]
|
"subtype" : "spell.fireball",
|
||||||
|
"type" : "SPECIAL_SPELL_LEV",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,9 @@
|
|||||||
{ "skill" : "necromancy", "level": "basic" },
|
{ "skill" : "necromancy", "level": "basic" },
|
||||||
{ "skill" : "resistance", "level": "basic" }
|
{ "skill" : "resistance", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "walkingDead"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 58 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"vokial":
|
"vokial":
|
||||||
{
|
{
|
||||||
@ -26,10 +25,9 @@
|
|||||||
{ "skill" : "necromancy", "level": "basic" },
|
{ "skill" : "necromancy", "level": "basic" },
|
||||||
{ "skill" : "artillery", "level": "basic" }
|
{ "skill" : "artillery", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "vampire"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 62 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"moandor":
|
"moandor":
|
||||||
{
|
{
|
||||||
@ -42,10 +40,9 @@
|
|||||||
{ "skill" : "necromancy", "level": "basic" },
|
{ "skill" : "necromancy", "level": "basic" },
|
||||||
{ "skill" : "learning", "level": "basic" }
|
{ "skill" : "learning", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "lich"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 64 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"charna":
|
"charna":
|
||||||
{
|
{
|
||||||
@ -58,10 +55,9 @@
|
|||||||
{ "skill" : "necromancy", "level": "basic" },
|
{ "skill" : "necromancy", "level": "basic" },
|
||||||
{ "skill" : "tactics", "level": "basic" }
|
{ "skill" : "tactics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "wight"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 60 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"tamika":
|
"tamika":
|
||||||
{
|
{
|
||||||
@ -74,10 +70,9 @@
|
|||||||
{ "skill" : "necromancy", "level": "basic" },
|
{ "skill" : "necromancy", "level": "basic" },
|
||||||
{ "skill" : "offence", "level": "basic" }
|
{ "skill" : "offence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "blackKnight"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 66 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"isra":
|
"isra":
|
||||||
{
|
{
|
||||||
@ -89,10 +84,17 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "necromancy", "level": "advanced" }
|
{ "skill" : "necromancy", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 12, "info": 0 }
|
"necromancy" : {
|
||||||
]
|
"subtype" : "skill.necromancy",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"clavius":
|
"clavius":
|
||||||
{
|
{
|
||||||
@ -105,10 +107,15 @@
|
|||||||
{ "skill" : "necromancy", "level": "basic" },
|
{ "skill" : "necromancy", "level": "basic" },
|
||||||
{ "skill" : "offence", "level": "basic" }
|
{ "skill" : "offence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
"gold" : {
|
||||||
]
|
"subtype" : "resource.gold",
|
||||||
|
"type" : "GENERATE_RESOURCE",
|
||||||
|
"val" : 350
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"galthran":
|
"galthran":
|
||||||
{
|
{
|
||||||
@ -121,10 +128,9 @@
|
|||||||
{ "skill" : "necromancy", "level": "basic" },
|
{ "skill" : "necromancy", "level": "basic" },
|
||||||
{ "skill" : "armorer", "level": "basic" }
|
{ "skill" : "armorer", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "skeleton"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 56 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"septienna":
|
"septienna":
|
||||||
{
|
{
|
||||||
@ -137,10 +143,16 @@
|
|||||||
{ "skill" : "necromancy", "level": "basic" },
|
{ "skill" : "necromancy", "level": "basic" },
|
||||||
{ "skill" : "scholar", "level": "basic" }
|
{ "skill" : "scholar", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":3, "val": 3, "subtype": 24, "info": 0 }
|
"deathRipple" : {
|
||||||
]
|
"subtype" : "spell.deathRipple",
|
||||||
|
"type" : "SPECIAL_SPELL_LEV",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"aislinn":
|
"aislinn":
|
||||||
{
|
{
|
||||||
@ -153,10 +165,16 @@
|
|||||||
{ "skill" : "necromancy", "level": "basic" },
|
{ "skill" : "necromancy", "level": "basic" },
|
||||||
{ "skill" : "wisdom", "level": "basic" }
|
{ "skill" : "wisdom", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":3, "val": 3, "subtype": 23, "info": 0 }
|
"meteorShower" : {
|
||||||
]
|
"subtype" : "spell.meteorShower",
|
||||||
|
"type" : "SPECIAL_SPELL_LEV",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"sandro":
|
"sandro":
|
||||||
{
|
{
|
||||||
@ -169,10 +187,17 @@
|
|||||||
{ "skill" : "necromancy", "level": "basic" },
|
{ "skill" : "necromancy", "level": "basic" },
|
||||||
{ "skill" : "sorcery", "level": "basic" }
|
{ "skill" : "sorcery", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 25, "info": 0 }
|
"sorcery" : {
|
||||||
]
|
"subtype" : "skill.sorcery",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"nimbus":
|
"nimbus":
|
||||||
{
|
{
|
||||||
@ -185,10 +210,17 @@
|
|||||||
{ "skill" : "necromancy", "level": "basic" },
|
{ "skill" : "necromancy", "level": "basic" },
|
||||||
{ "skill" : "eagleEye", "level": "basic" }
|
{ "skill" : "eagleEye", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 11, "info": 1 }
|
"eagleEye" : {
|
||||||
]
|
"subtype" : "skill.eagleEye",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"thant":
|
"thant":
|
||||||
{
|
{
|
||||||
@ -201,10 +233,16 @@
|
|||||||
{ "skill" : "necromancy", "level": "basic" },
|
{ "skill" : "necromancy", "level": "basic" },
|
||||||
{ "skill" : "mysticism", "level": "basic" }
|
{ "skill" : "mysticism", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":3, "val": 39, "subtype": 0, "info": 3 }
|
"animateDead" : {
|
||||||
]
|
"subtype" : "spell.animateDead",
|
||||||
|
"type" : "SPECIAL_SPELL_LEV",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"xsi":
|
"xsi":
|
||||||
{
|
{
|
||||||
@ -217,10 +255,15 @@
|
|||||||
{ "skill" : "necromancy", "level": "basic" },
|
{ "skill" : "necromancy", "level": "basic" },
|
||||||
{ "skill" : "learning", "level": "basic" }
|
{ "skill" : "learning", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":8, "val": 0, "subtype": 46, "info": 0 }
|
"stoneSkin" : {
|
||||||
]
|
"addInfo" : 0,
|
||||||
|
"subtype" : "spell.stoneSkin",
|
||||||
|
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"vidomina":
|
"vidomina":
|
||||||
{
|
{
|
||||||
@ -232,10 +275,17 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "necromancy", "level": "advanced" }
|
{ "skill" : "necromancy", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 12, "info": 0 }
|
"necromancy" : {
|
||||||
]
|
"subtype" : "skill.necromancy",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"nagash":
|
"nagash":
|
||||||
{
|
{
|
||||||
@ -248,9 +298,14 @@
|
|||||||
{ "skill" : "necromancy", "level": "basic" },
|
{ "skill" : "necromancy", "level": "basic" },
|
||||||
{ "skill" : "intelligence", "level": "basic" }
|
{ "skill" : "intelligence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
"gold" : {
|
||||||
]
|
"subtype" : "resource.gold",
|
||||||
|
"type" : "GENERATE_RESOURCE",
|
||||||
|
"val" : 350
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,17 @@
|
|||||||
{ "skill" : "leadership", "level": "basic" },
|
{ "skill" : "leadership", "level": "basic" },
|
||||||
{ "skill" : "armorer", "level": "basic" }
|
{ "skill" : "armorer", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 23, "info": 0 }
|
"armorer" : {
|
||||||
]
|
"subtype" : "skill.armorer",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"ufretin":
|
"ufretin":
|
||||||
{
|
{
|
||||||
@ -24,10 +31,9 @@
|
|||||||
{ "skill" : "luck", "level": "basic" },
|
{ "skill" : "luck", "level": "basic" },
|
||||||
{ "skill" : "resistance", "level": "basic" }
|
{ "skill" : "resistance", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "dwarf"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 16 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"jenova":
|
"jenova":
|
||||||
{
|
{
|
||||||
@ -38,10 +44,15 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "archery", "level": "advanced" }
|
{ "skill" : "archery", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
"gold" : {
|
||||||
]
|
"subtype" : "resource.gold",
|
||||||
|
"type" : "GENERATE_RESOURCE",
|
||||||
|
"val" : 350
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"ryland":
|
"ryland":
|
||||||
{
|
{
|
||||||
@ -53,10 +64,9 @@
|
|||||||
{ "skill" : "diplomacy", "level": "basic" },
|
{ "skill" : "diplomacy", "level": "basic" },
|
||||||
{ "skill" : "leadership", "level": "basic" }
|
{ "skill" : "leadership", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "dendroidGuard"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 22 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"thorgrim":
|
"thorgrim":
|
||||||
{
|
{
|
||||||
@ -67,10 +77,17 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "resistance", "level": "advanced" }
|
{ "skill" : "resistance", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 26, "info": 0 }
|
"resistance" : {
|
||||||
]
|
"subtype" : "skill.resistance",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"ivor":
|
"ivor":
|
||||||
{
|
{
|
||||||
@ -82,10 +99,9 @@
|
|||||||
{ "skill" : "archery", "level": "basic" },
|
{ "skill" : "archery", "level": "basic" },
|
||||||
{ "skill" : "offence", "level": "basic" }
|
{ "skill" : "offence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "woodElf"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 18 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"clancy":
|
"clancy":
|
||||||
{
|
{
|
||||||
@ -97,10 +113,9 @@
|
|||||||
{ "skill" : "pathfinding", "level": "basic" },
|
{ "skill" : "pathfinding", "level": "basic" },
|
||||||
{ "skill" : "resistance", "level": "basic" }
|
{ "skill" : "resistance", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "unicorn"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 24 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"kyrre":
|
"kyrre":
|
||||||
{
|
{
|
||||||
@ -112,10 +127,17 @@
|
|||||||
{ "skill" : "archery", "level": "basic" },
|
{ "skill" : "archery", "level": "basic" },
|
||||||
{ "skill" : "logistics", "level": "basic" }
|
{ "skill" : "logistics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 2, "info": 0 }
|
"logistics" : {
|
||||||
]
|
"subtype" : "skill.logistics",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"coronius":
|
"coronius":
|
||||||
{
|
{
|
||||||
@ -128,10 +150,15 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "scholar", "level": "basic" }
|
{ "skill" : "scholar", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":8, "val": 0, "subtype": 55, "info": 1 }
|
"slayer" : {
|
||||||
]
|
"addInfo" : 1,
|
||||||
|
"subtype" : "spell.slayer",
|
||||||
|
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"uland":
|
"uland":
|
||||||
{
|
{
|
||||||
@ -144,10 +171,16 @@
|
|||||||
{ "skill" : "wisdom", "level": "advanced" },
|
{ "skill" : "wisdom", "level": "advanced" },
|
||||||
{ "skill" : "ballistics", "level": "basic" }
|
{ "skill" : "ballistics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":3, "val": 3, "subtype": 37, "info": 0 }
|
"cure" : {
|
||||||
]
|
"subtype" : "spell.cure",
|
||||||
|
"type" : "SPECIAL_SPELL_LEV",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"elleshar":
|
"elleshar":
|
||||||
{
|
{
|
||||||
@ -160,10 +193,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "intelligence", "level": "basic" }
|
{ "skill" : "intelligence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 24, "info": 0 }
|
"intelligence" : {
|
||||||
]
|
"subtype" : "skill.intelligence",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"gem":
|
"gem":
|
||||||
{
|
{
|
||||||
@ -176,10 +216,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "firstAid", "level": "basic" }
|
{ "skill" : "firstAid", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 27, "info": 0 }
|
"firstAid" : {
|
||||||
]
|
"subtype" : "skill.firstAid",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"malcom":
|
"malcom":
|
||||||
{
|
{
|
||||||
@ -192,10 +239,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "eagleEye", "level": "basic" }
|
{ "skill" : "eagleEye", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 11, "info": 0 }
|
"eagleEye" : {
|
||||||
]
|
"subtype" : "skill.eagleEye",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"melodia":
|
"melodia":
|
||||||
{
|
{
|
||||||
@ -208,10 +262,14 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "luck", "level": "basic" }
|
{ "skill" : "luck", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":7, "val": 0, "subtype": 51, "info": 0 }
|
"fortune" : {
|
||||||
]
|
"subtype" : "spell.fortune",
|
||||||
|
"type" : "MAXED_SPELL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"alagar":
|
"alagar":
|
||||||
{
|
{
|
||||||
@ -224,10 +282,16 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "sorcery", "level": "basic" }
|
{ "skill" : "sorcery", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":3, "val": 3, "subtype": 16, "info": 0 }
|
"iceBolt" : {
|
||||||
]
|
"subtype" : "spell.iceBolt",
|
||||||
|
"type" : "SPECIAL_SPELL_LEV",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"aeris":
|
"aeris":
|
||||||
{
|
{
|
||||||
@ -240,9 +304,8 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "scouting", "level": "basic" }
|
{ "skill" : "scouting", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "pegasus"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 20 }
|
}
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,14 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "leadership", "level": "advanced" }
|
{ "skill" : "leadership", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":12, "val": 2, "subtype": 0, "info": 0 }
|
"speed" : {
|
||||||
]
|
"type" : "STACKS_SPEED",
|
||||||
|
"val" : 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"adrienne":
|
"adrienne":
|
||||||
{
|
{
|
||||||
@ -25,11 +29,9 @@
|
|||||||
"skills":
|
"skills":
|
||||||
[
|
[
|
||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "fireMagic", "level": "expert" } ],
|
{ "skill" : "fireMagic", "level": "expert" }
|
||||||
"specialties":
|
],
|
||||||
[
|
"specialty" : { "bonuses" : { } } // has expert fire magic as "specialty"
|
||||||
{ "type":11, "val": 14, "subtype": 0, "info": 3 }
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"catherine":
|
"catherine":
|
||||||
{
|
{
|
||||||
@ -42,10 +44,9 @@
|
|||||||
{ "skill" : "leadership", "level": "basic" },
|
{ "skill" : "leadership", "level": "basic" },
|
||||||
{ "skill" : "offence", "level": "basic" }
|
{ "skill" : "offence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "swordsman"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 4 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"dracon":
|
"dracon":
|
||||||
{
|
{
|
||||||
@ -58,11 +59,18 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "wisdom", "level": "advanced" }
|
{ "skill" : "wisdom", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"base" : {
|
||||||
{ "type":9, "val": 0, "subtype": 8, "info": 136 },
|
"addInfo" : "creature.enchanter",
|
||||||
{ "type":9, "val": 0, "subtype": 34, "info": 136 }
|
"type" : "SPECIAL_UPGRADE"
|
||||||
]
|
},
|
||||||
|
"bonuses" : {
|
||||||
|
"archMage2enchanter" : { "subtype" : "creature.archMage" },
|
||||||
|
"mage2enchanter" : { "subtype" : "creature.mage" },
|
||||||
|
"monk2enchanter" : { "subtype" : "creature.monk" },
|
||||||
|
"zealot2enchanter" : { "subtype" : "creature.zealot" }
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"gelu":
|
"gelu":
|
||||||
{
|
{
|
||||||
@ -75,11 +83,18 @@
|
|||||||
{ "skill" : "archery", "level": "basic" },
|
{ "skill" : "archery", "level": "basic" },
|
||||||
{ "skill" : "leadership", "level": "basic" }
|
{ "skill" : "leadership", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"base" : {
|
||||||
{ "type":9, "val": 0, "subtype": 2, "info": 137 },
|
"addInfo" : "creature.sharpshooter",
|
||||||
{ "type":9, "val": 0, "subtype": 18, "info": 137 }
|
"type" : "SPECIAL_UPGRADE"
|
||||||
]
|
},
|
||||||
|
"bonuses" : {
|
||||||
|
"archer2sharpshooter" : { "subtype" : "creature.archer" },
|
||||||
|
"grandElf2sharpshooter" : { "subtype" : "creature.grandElf" },
|
||||||
|
"marksman2sharpshooter" : { "subtype" : "creature.marksman" },
|
||||||
|
"woodElf2sharpshooter" : { "subtype" : "creature.woodElf" }
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"kilgor":
|
"kilgor":
|
||||||
{
|
{
|
||||||
@ -91,12 +106,33 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "offence", "level": "advanced" }
|
{ "skill" : "offence", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"base" : {
|
||||||
{ "type":4, "val": 5, "subtype": 1, "info": 96 },
|
"limiters" : [
|
||||||
{ "type":4, "val": 5, "subtype": 2, "info": 96 },
|
{
|
||||||
{ "type":4, "val": 10, "subtype": 3, "info": 96 }
|
"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
|
"undeadHaart": // undead version of Lord Haart
|
||||||
{
|
{
|
||||||
@ -109,12 +145,33 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "necromancy", "level": "advanced" }
|
{ "skill" : "necromancy", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"base" : {
|
||||||
{ "type":4, "val": 5, "subtype": 1, "info": 66 },
|
"limiters" : [
|
||||||
{ "type":4, "val": 5, "subtype": 2, "info": 66 },
|
{
|
||||||
{ "type":4, "val": 10, "subtype": 3, "info": 66 }
|
"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":
|
"mutare":
|
||||||
{
|
{
|
||||||
@ -128,11 +185,22 @@
|
|||||||
{ "skill" : "estates", "level": "basic" },
|
{ "skill" : "estates", "level": "basic" },
|
||||||
{ "skill" : "tactics", "level": "basic" }
|
{ "skill" : "tactics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"base" : {
|
||||||
{ "type":13, "val": 5, "subtype": 1, "info": 0 },
|
"limiters" : [
|
||||||
{ "type":13, "val": 5, "subtype": 2, "info": 0 }
|
{
|
||||||
]
|
"parameters" : [ "DRAGON_NATURE" ],
|
||||||
|
"type" : "HAS_ANOTHER_BONUS_LIMITER"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type" : "PRIMARY_SKILL",
|
||||||
|
"val" : 5
|
||||||
|
},
|
||||||
|
"bonuses" : {
|
||||||
|
"attack" : { "subtype" : "primSkill.attack" },
|
||||||
|
"defence" : { "subtype" : "primSkill.defence" }
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"roland":
|
"roland":
|
||||||
{
|
{
|
||||||
@ -145,10 +213,9 @@
|
|||||||
{ "skill" : "leadership", "level": "basic" },
|
{ "skill" : "leadership", "level": "basic" },
|
||||||
{ "skill" : "armorer", "level": "basic" }
|
{ "skill" : "armorer", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "griffin"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 4 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"mutareDrake":
|
"mutareDrake":
|
||||||
{
|
{
|
||||||
@ -162,11 +229,22 @@
|
|||||||
{ "skill" : "estates", "level": "basic" },
|
{ "skill" : "estates", "level": "basic" },
|
||||||
{ "skill" : "tactics", "level": "basic" }
|
{ "skill" : "tactics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"base" : {
|
||||||
{ "type":13, "val": 1, "subtype": 1, "info": 5 },
|
"limiters" : [
|
||||||
{ "type":13, "val": 1, "subtype": 1, "info": 5 }
|
{
|
||||||
],
|
"parameters" : [ "DRAGON_NATURE" ],
|
||||||
|
"type" : "HAS_ANOTHER_BONUS_LIMITER"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type" : "PRIMARY_SKILL",
|
||||||
|
"val" : 5
|
||||||
|
},
|
||||||
|
"bonuses" : {
|
||||||
|
"attack" : { "subtype" : "primSkill.attack" },
|
||||||
|
"defence" : { "subtype" : "primSkill.defence" }
|
||||||
|
}
|
||||||
|
},
|
||||||
"army" :
|
"army" :
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -194,10 +272,9 @@
|
|||||||
{ "skill" : "tactics", "level": "basic" },
|
{ "skill" : "tactics", "level": "basic" },
|
||||||
{ "skill" : "offence", "level": "basic" }
|
{ "skill" : "offence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "ogre"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 90 }
|
},
|
||||||
],
|
|
||||||
"army" :
|
"army" :
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -225,12 +302,32 @@
|
|||||||
{ "skill" : "leadership", "level": "basic" },
|
{ "skill" : "leadership", "level": "basic" },
|
||||||
{ "skill" : "tactics", "level": "basic" }
|
{ "skill" : "tactics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"base" : {
|
||||||
{ "type":4, "val": 4, "subtype": 1, "info": 54 },
|
"limiters" : [
|
||||||
{ "type":4, "val": 2, "subtype": 2, "info": 54 },
|
{
|
||||||
{ "type":4, "val": 1, "subtype": 5, "info": 54 }
|
"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" :
|
"army" :
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -9,10 +9,9 @@
|
|||||||
{ "skill" : "offence", "level": "basic" },
|
{ "skill" : "offence", "level": "basic" },
|
||||||
{ "skill" : "ballistics", "level": "basic" }
|
{ "skill" : "ballistics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "cyclop"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 94 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"gurnisson":
|
"gurnisson":
|
||||||
{
|
{
|
||||||
@ -24,10 +23,9 @@
|
|||||||
{ "skill" : "offence", "level": "basic" },
|
{ "skill" : "offence", "level": "basic" },
|
||||||
{ "skill" : "artillery", "level": "basic" }
|
{ "skill" : "artillery", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "ballista"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 146 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"jabarkas":
|
"jabarkas":
|
||||||
{
|
{
|
||||||
@ -39,10 +37,9 @@
|
|||||||
{ "skill" : "offence", "level": "basic" },
|
{ "skill" : "offence", "level": "basic" },
|
||||||
{ "skill" : "archery", "level": "basic" }
|
{ "skill" : "archery", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "orc"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 88 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"shiva":
|
"shiva":
|
||||||
{
|
{
|
||||||
@ -54,10 +51,9 @@
|
|||||||
{ "skill" : "offence", "level": "basic" },
|
{ "skill" : "offence", "level": "basic" },
|
||||||
{ "skill" : "scouting", "level": "basic" }
|
{ "skill" : "scouting", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "roc"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 92 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"gretchin":
|
"gretchin":
|
||||||
{
|
{
|
||||||
@ -69,10 +65,9 @@
|
|||||||
{ "skill" : "offence", "level": "basic" },
|
{ "skill" : "offence", "level": "basic" },
|
||||||
{ "skill" : "pathfinding", "level": "basic" }
|
{ "skill" : "pathfinding", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "goblin"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 84 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"krellion":
|
"krellion":
|
||||||
{
|
{
|
||||||
@ -84,10 +79,9 @@
|
|||||||
{ "skill" : "offence", "level": "basic" },
|
{ "skill" : "offence", "level": "basic" },
|
||||||
{ "skill" : "resistance", "level": "basic" }
|
{ "skill" : "resistance", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "ogre"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 90 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"cragHack":
|
"cragHack":
|
||||||
{
|
{
|
||||||
@ -98,10 +92,17 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "offence", "level": "advanced" }
|
{ "skill" : "offence", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 22, "info": 0 }
|
"offence" : {
|
||||||
]
|
"subtype" : "skill.offence",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"tyraxor":
|
"tyraxor":
|
||||||
{
|
{
|
||||||
@ -113,10 +114,9 @@
|
|||||||
{ "skill" : "offence", "level": "basic" },
|
{ "skill" : "offence", "level": "basic" },
|
||||||
{ "skill" : "tactics", "level": "basic" }
|
{ "skill" : "tactics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "goblinWolfRider"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 86 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"gird":
|
"gird":
|
||||||
{
|
{
|
||||||
@ -129,10 +129,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "sorcery", "level": "basic" }
|
{ "skill" : "sorcery", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 25, "info": 0 }
|
"sorcery" : {
|
||||||
]
|
"subtype" : "skill.sorcery",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"vey":
|
"vey":
|
||||||
{
|
{
|
||||||
@ -145,10 +152,9 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "leadership", "level": "basic" }
|
{ "skill" : "leadership", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "ogre"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 90 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"dessa":
|
"dessa":
|
||||||
{
|
{
|
||||||
@ -161,10 +167,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "logistics", "level": "basic" }
|
{ "skill" : "logistics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 2, "info": 0 }
|
"logistics" : {
|
||||||
]
|
"subtype" : "skill.logistics",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"terek":
|
"terek":
|
||||||
{
|
{
|
||||||
@ -177,10 +190,15 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "tactics", "level": "basic" }
|
{ "skill" : "tactics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":8, "val": 0, "subtype": 53, "info": 0 }
|
"haste" : {
|
||||||
]
|
"addInfo" : 0,
|
||||||
|
"subtype" : "spell.haste",
|
||||||
|
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"zubin":
|
"zubin":
|
||||||
{
|
{
|
||||||
@ -193,10 +211,15 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "artillery", "level": "basic" }
|
{ "skill" : "artillery", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":8, "val": 0, "subtype": 44, "info": 0 }
|
"precision" : {
|
||||||
]
|
"addInfo" : 0,
|
||||||
|
"subtype" : "spell.precision",
|
||||||
|
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"gundula":
|
"gundula":
|
||||||
{
|
{
|
||||||
@ -209,10 +232,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "offence", "level": "basic" }
|
{ "skill" : "offence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 25, "info": 0 }
|
"sorcery" : {
|
||||||
]
|
"subtype" : "skill.sorcery",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"oris":
|
"oris":
|
||||||
{
|
{
|
||||||
@ -225,10 +255,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "eagleEye", "level": "basic" }
|
{ "skill" : "eagleEye", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 11, "info": 0 }
|
"eagleEye" : {
|
||||||
]
|
"subtype" : "skill.eagleEye",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"saurug":
|
"saurug":
|
||||||
{
|
{
|
||||||
@ -241,9 +278,14 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "resistance", "level": "basic" }
|
{ "skill" : "resistance", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":10, "val": 1, "subtype": 5, "info": 0 }
|
"gems" : {
|
||||||
]
|
"subtype" : "resource.gems",
|
||||||
|
"type" : "GENERATE_RESOURCE",
|
||||||
|
"val" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,9 @@
|
|||||||
{ "skill" : "scouting", "level": "basic" },
|
{ "skill" : "scouting", "level": "basic" },
|
||||||
{ "skill" : "mysticism", "level": "basic" }
|
{ "skill" : "mysticism", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "stoneGargoyle"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 30 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"thane":
|
"thane":
|
||||||
{
|
{
|
||||||
@ -25,10 +24,9 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "scholar", "level": "advanced" }
|
{ "skill" : "scholar", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "genie"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 36 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"josephine":
|
"josephine":
|
||||||
{
|
{
|
||||||
@ -41,10 +39,9 @@
|
|||||||
{ "skill" : "mysticism", "level": "basic" },
|
{ "skill" : "mysticism", "level": "basic" },
|
||||||
{ "skill" : "sorcery", "level": "basic" }
|
{ "skill" : "sorcery", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "ironGolem"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 32 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"neela":
|
"neela":
|
||||||
{
|
{
|
||||||
@ -57,10 +54,17 @@
|
|||||||
{ "skill" : "scholar", "level": "basic" },
|
{ "skill" : "scholar", "level": "basic" },
|
||||||
{ "skill" : "armorer", "level": "basic" }
|
{ "skill" : "armorer", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 23, "info": 0 }
|
"armorer" : {
|
||||||
]
|
"subtype" : "skill.armorer",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"torosar ":
|
"torosar ":
|
||||||
{
|
{
|
||||||
@ -73,10 +77,9 @@
|
|||||||
{ "skill" : "mysticism", "level": "basic" },
|
{ "skill" : "mysticism", "level": "basic" },
|
||||||
{ "skill" : "tactics", "level": "basic" }
|
{ "skill" : "tactics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "ballista"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 146 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"fafner":
|
"fafner":
|
||||||
{
|
{
|
||||||
@ -89,10 +92,9 @@
|
|||||||
{ "skill" : "scholar", "level": "basic" },
|
{ "skill" : "scholar", "level": "basic" },
|
||||||
{ "skill" : "resistance", "level": "basic" }
|
{ "skill" : "resistance", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "naga"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 38 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"rissa":
|
"rissa":
|
||||||
{
|
{
|
||||||
@ -105,10 +107,15 @@
|
|||||||
{ "skill" : "mysticism", "level": "basic" },
|
{ "skill" : "mysticism", "level": "basic" },
|
||||||
{ "skill" : "offence", "level": "basic" }
|
{ "skill" : "offence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":10, "val": 1, "subtype": 1, "info": 0 }
|
"mercury" : {
|
||||||
]
|
"subtype" : "resource.mercury",
|
||||||
|
"type" : "GENERATE_RESOURCE",
|
||||||
|
"val" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"iona":
|
"iona":
|
||||||
{
|
{
|
||||||
@ -121,10 +128,9 @@
|
|||||||
{ "skill" : "scholar", "level": "basic" },
|
{ "skill" : "scholar", "level": "basic" },
|
||||||
{ "skill" : "intelligence", "level": "basic" }
|
{ "skill" : "intelligence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "genie"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 36 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"astral":
|
"astral":
|
||||||
{
|
{
|
||||||
@ -136,10 +142,16 @@
|
|||||||
[
|
[
|
||||||
{ "skill" : "wisdom", "level": "advanced" }
|
{ "skill" : "wisdom", "level": "advanced" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":3, "val": 3, "subtype": 60, "info": 0 }
|
"hypnotize" : {
|
||||||
]
|
"subtype" : "spell.hypnotize",
|
||||||
|
"type" : "SPECIAL_SPELL_LEV",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"halon":
|
"halon":
|
||||||
{
|
{
|
||||||
@ -152,10 +164,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "mysticism", "level": "basic" }
|
{ "skill" : "mysticism", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 8, "info": 1 }
|
"mysticism" : {
|
||||||
]
|
"subtype" : "skill.mysticism",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"serena":
|
"serena":
|
||||||
{
|
{
|
||||||
@ -168,10 +187,17 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "eagleEye", "level": "basic" }
|
{ "skill" : "eagleEye", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":2, "val": 5, "subtype": 11, "info": 0 }
|
"eagleEye" : {
|
||||||
]
|
"subtype" : "skill.eagleEye",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 5,
|
||||||
|
"valueType" : "PERCENT_TO_BASE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"daremyth":
|
"daremyth":
|
||||||
{
|
{
|
||||||
@ -184,10 +210,14 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "intelligence", "level": "basic" }
|
{ "skill" : "intelligence", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":7, "val": 0, "subtype": 51, "info": 0 }
|
"fortune" : {
|
||||||
]
|
"subtype" : "spell.fortune",
|
||||||
|
"type" : "MAXED_SPELL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"theodorus":
|
"theodorus":
|
||||||
{
|
{
|
||||||
@ -200,10 +230,9 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "ballistics", "level": "basic" }
|
{ "skill" : "ballistics", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"creature" : "mage"
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 34 }
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"solmyr":
|
"solmyr":
|
||||||
{
|
{
|
||||||
@ -216,10 +245,16 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "sorcery", "level": "basic" }
|
{ "skill" : "sorcery", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":3, "val": 3, "subtype": 19, "info": 0 }
|
"chainLightning" : {
|
||||||
]
|
"subtype" : "spell.chainLightning",
|
||||||
|
"type" : "SPECIAL_SPELL_LEV",
|
||||||
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
|
"val" : 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"cyra":
|
"cyra":
|
||||||
{
|
{
|
||||||
@ -232,10 +267,15 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "diplomacy", "level": "basic" }
|
{ "skill" : "diplomacy", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":8, "val": 0, "subtype": 53, "info": 0 }
|
"haste" : {
|
||||||
]
|
"addInfo" : 0,
|
||||||
|
"subtype" : "spell.haste",
|
||||||
|
"type" : "SPECIAL_PECULIAR_ENCHANT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"aine":
|
"aine":
|
||||||
{
|
{
|
||||||
@ -248,9 +288,14 @@
|
|||||||
{ "skill" : "wisdom", "level": "basic" },
|
{ "skill" : "wisdom", "level": "basic" },
|
||||||
{ "skill" : "scholar", "level": "basic" }
|
{ "skill" : "scholar", "level": "basic" }
|
||||||
],
|
],
|
||||||
"specialties":
|
"specialty" : {
|
||||||
[
|
"bonuses" : {
|
||||||
{ "type":10, "val": 350, "subtype": 6, "info": 0 }
|
"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": {
|
"sourceID": {
|
||||||
"type":"number",
|
"type":"number",
|
||||||
"description": "sourceID"
|
"description": "sourceID"
|
||||||
|
@ -115,24 +115,48 @@
|
|||||||
"additionalItems" : true
|
"additionalItems" : true
|
||||||
},
|
},
|
||||||
"specialty": {
|
"specialty": {
|
||||||
"type":"array",
|
"anyOf" : [
|
||||||
"description": "Description of hero specialty using bonus system",
|
{
|
||||||
"items": {
|
"type":"array",
|
||||||
"type":"object",
|
"description": "Description of hero specialty using bonus system (deprecated)",
|
||||||
"additionalProperties" : false,
|
"items": {
|
||||||
"required" : [ "bonuses" ],
|
"type" : "object",
|
||||||
"properties":{
|
"additionalProperties" : false,
|
||||||
"growsWithLevel" : {
|
"required" : [ "bonuses" ],
|
||||||
"type" : "boolean",
|
"properties" : {
|
||||||
"description" : "Specialty growth with level, so far only SECONDARY_SKILL_PREMY and PRIMATY SKILL with creature limiter can grow"
|
"growsWithLevel" : {
|
||||||
},
|
"type" : "boolean",
|
||||||
"bonuses": {
|
"description" : "Specialty growth with level. Deprecated, use bonuses with updaters instead."
|
||||||
"type":"array",
|
},
|
||||||
"description": "List of bonuses",
|
"bonuses" : {
|
||||||
"items": { "$ref" : "vcmi:bonus" }
|
"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": {
|
"spellbook": {
|
||||||
"type":"array",
|
"type":"array",
|
||||||
|
@ -226,7 +226,8 @@
|
|||||||
"base" : {
|
"base" : {
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"main" : {
|
"main" : {
|
||||||
"type" : "MANA_REGENERATION",
|
"subtype" : "skill.mysticism",
|
||||||
|
"type" : "SECONDARY_SKILL_PREMY",
|
||||||
"valueType" : "BASE_NUMBER"
|
"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)
|
void CHeroHandler::loadHeroSpecialty(CHero * hero, const JsonNode & node)
|
||||||
{
|
{
|
||||||
//deprecated, used only for original spciealties
|
int sid = hero->ID.getNum();
|
||||||
for(const JsonNode &specialty : node["specialties"].Vector())
|
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();
|
//deprecated, used only for original specialties
|
||||||
spec.val = specialty["val"].Float();
|
const JsonNode & specialtiesNode = node["specialties"];
|
||||||
spec.subtype = specialty["subtype"].Float();
|
if (!specialtiesNode.isNull())
|
||||||
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())
|
|
||||||
{
|
{
|
||||||
SSpecialtyBonus hs;
|
logMod->warn("Hero %s has deprecated specialties format.", hero->identifier);
|
||||||
hs.growsWithLevel = specialty["growsWithLevel"].Bool();
|
for(const JsonNode &specialty : specialtiesNode.Vector())
|
||||||
for (const JsonNode & bonus : specialty["bonuses"].Vector())
|
|
||||||
{
|
{
|
||||||
auto b = JsonUtils::parseBonus(bonus);
|
SSpecialtyInfo spec;
|
||||||
hs.bonuses.push_back (b);
|
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());
|
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
|
ui32 CHeroHandler::level (ui64 experience) const
|
||||||
{
|
{
|
||||||
return boost::range::upper_bound(expPerLevel, experience) - std::begin(expPerLevel);
|
return boost::range::upper_bound(expPerLevel, experience) - std::begin(expPerLevel);
|
||||||
|
@ -71,8 +71,9 @@ public:
|
|||||||
|
|
||||||
CHeroClass * heroClass;
|
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<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<SSpecialtyInfo> specDeprecated;
|
||||||
std::vector<SSpecialtyBonus> specialty;
|
std::vector<SSpecialtyBonus> specialtyDeprecated;
|
||||||
|
BonusList specialty;
|
||||||
std::set<SpellID> spells;
|
std::set<SpellID> spells;
|
||||||
bool haveSpellBook;
|
bool haveSpellBook;
|
||||||
bool special; // hero is special and won't be placed in game (unless preset on map), e.g. campaign heroes
|
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 & initialArmy;
|
||||||
h & heroClass;
|
h & heroClass;
|
||||||
h & secSkillsInit;
|
h & secSkillsInit;
|
||||||
h & spec;
|
if(version >= 781)
|
||||||
h & specialty;
|
{
|
||||||
|
h & specialty;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
h & specDeprecated;
|
||||||
|
h & specialtyDeprecated;
|
||||||
|
}
|
||||||
h & spells;
|
h & spells;
|
||||||
h & haveSpellBook;
|
h & haveSpellBook;
|
||||||
h & sex;
|
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
|
class DLL_LINKAGE CHeroClass
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -289,8 +301,10 @@ public:
|
|||||||
|
|
||||||
std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
|
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) override;
|
||||||
void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override;
|
void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override;
|
||||||
|
void afterLoadFinalization() override;
|
||||||
|
|
||||||
CHeroHandler();
|
CHeroHandler();
|
||||||
~CHeroHandler();
|
~CHeroHandler();
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "CSkillHandler.h"
|
#include "CSkillHandler.h"
|
||||||
#include "CStack.h"
|
#include "CStack.h"
|
||||||
#include "CArtHandler.h"
|
#include "CArtHandler.h"
|
||||||
|
#include "StringConstants.h"
|
||||||
|
|
||||||
#define FOREACH_PARENT(pname) TNodes lparents; getParents(lparents); for(CBonusSystemNode *pname : lparents)
|
#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)
|
#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)}
|
{"GLOBAL_EFFECT", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::GLOBAL_EFFECTS)}
|
||||||
}; //untested
|
}; //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::CBonusProxy(const IBonusBearer * Target, CSelector Selector)
|
CBonusProxy::CBonusProxy(const IBonusBearer * Target, CSelector Selector)
|
||||||
: cachedLast(0),
|
: cachedLast(0),
|
||||||
@ -579,22 +586,28 @@ void CBonusSystemNode::getParents(TNodes &out)
|
|||||||
|
|
||||||
void CBonusSystemNode::getBonusesRec(BonusList &out, const CSelector &selector, const CSelector &limit) const
|
void CBonusSystemNode::getBonusesRec(BonusList &out, const CSelector &selector, const CSelector &limit) const
|
||||||
{
|
{
|
||||||
|
BonusList beforeUpdate;
|
||||||
FOREACH_CPARENT(p)
|
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
|
void CBonusSystemNode::getAllBonusesRec(BonusList &out) const
|
||||||
{
|
{
|
||||||
|
BonusList beforeUpdate;
|
||||||
FOREACH_CPARENT(p)
|
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
|
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;
|
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()
|
CBonusSystemNode::CBonusSystemNode()
|
||||||
: bonuses(true),
|
: bonuses(true),
|
||||||
exportedBonuses(true),
|
exportedBonuses(true),
|
||||||
@ -782,7 +802,7 @@ void CBonusSystemNode::popBonuses(const CSelector &s)
|
|||||||
child->popBonuses(s);
|
child->popBonuses(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBonusSystemNode::updateBonuses(const CSelector &s)
|
void CBonusSystemNode::reduceBonusDurations(const CSelector &s)
|
||||||
{
|
{
|
||||||
BonusList bl;
|
BonusList bl;
|
||||||
exportedBonuses.getBonuses(bl, s, Selector::all);
|
exportedBonuses.getBonuses(bl, s, Selector::all);
|
||||||
@ -794,7 +814,7 @@ void CBonusSystemNode::updateBonuses(const CSelector &s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(CBonusSystemNode *child : children)
|
for(CBonusSystemNode *child : children)
|
||||||
child->updateBonuses(s);
|
child->reduceBonusDurations(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBonusSystemNode::addNewBonus(const std::shared_ptr<Bonus>& b)
|
void CBonusSystemNode::addNewBonus(const std::shared_ptr<Bonus>& b)
|
||||||
@ -1142,6 +1162,85 @@ std::string Bonus::Description() const
|
|||||||
return str.str();
|
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)
|
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)
|
: 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);
|
printField(effectRange);
|
||||||
#undef printField
|
#undef printField
|
||||||
|
|
||||||
|
if(bonus.limiter)
|
||||||
|
out << "\tLimiter: " << bonus.limiter->toString() << "\n";
|
||||||
|
if(bonus.updater)
|
||||||
|
out << "\tUpdater: " << bonus.updater->toString() << "\n";
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1341,6 +1445,18 @@ int ILimiter::limit(const BonusLimitationContext &context) const /*return true t
|
|||||||
return false;
|
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
|
int CCreatureTypeLimiter::limit(const BonusLimitationContext &context) const
|
||||||
{
|
{
|
||||||
const CCreature *c = retrieveCreature(&context.node);
|
const CCreature *c = retrieveCreature(&context.node);
|
||||||
@ -1366,6 +1482,26 @@ void CCreatureTypeLimiter::setCreature (CreatureID id)
|
|||||||
creature = VLC->creh->creatures[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 )
|
HasAnotherBonusLimiter::HasAnotherBonusLimiter( Bonus::BonusType bonus )
|
||||||
: type(bonus), subtype(0), isSubtypeRelevant(false)
|
: type(bonus), subtype(0), isSubtypeRelevant(false)
|
||||||
{
|
{
|
||||||
@ -1390,6 +1526,32 @@ int HasAnotherBonusLimiter::limit(const BonusLimitationContext &context) const
|
|||||||
return NOT_SURE;
|
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()
|
IPropagator::~IPropagator()
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -1543,3 +1705,136 @@ void LimiterList::add( TLimiterPtr limiter )
|
|||||||
{
|
{
|
||||||
limiters.push_back(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
|
#pragma once
|
||||||
|
|
||||||
#include "GameConstants.h"
|
#include "GameConstants.h"
|
||||||
|
#include "JsonNode.h"
|
||||||
|
|
||||||
class CCreature;
|
class CCreature;
|
||||||
struct Bonus;
|
struct Bonus;
|
||||||
@ -17,11 +18,13 @@ class IBonusBearer;
|
|||||||
class CBonusSystemNode;
|
class CBonusSystemNode;
|
||||||
class ILimiter;
|
class ILimiter;
|
||||||
class IPropagator;
|
class IPropagator;
|
||||||
|
class IUpdater;
|
||||||
class BonusList;
|
class BonusList;
|
||||||
|
|
||||||
typedef std::shared_ptr<BonusList> TBonusListPtr;
|
typedef std::shared_ptr<BonusList> TBonusListPtr;
|
||||||
typedef std::shared_ptr<ILimiter> TLimiterPtr;
|
typedef std::shared_ptr<ILimiter> TLimiterPtr;
|
||||||
typedef std::shared_ptr<IPropagator> TPropagatorPtr;
|
typedef std::shared_ptr<IPropagator> TPropagatorPtr;
|
||||||
|
typedef std::shared_ptr<IUpdater> TUpdaterPtr;
|
||||||
typedef std::set<CBonusSystemNode*> TNodes;
|
typedef std::set<CBonusSystemNode*> TNodes;
|
||||||
typedef std::set<const CBonusSystemNode*> TCNodes;
|
typedef std::set<const CBonusSystemNode*> TCNodes;
|
||||||
typedef std::vector<CBonusSystemNode *> TNodesVector;
|
typedef std::vector<CBonusSystemNode *> TNodesVector;
|
||||||
@ -204,14 +207,14 @@ private:
|
|||||||
BONUS_NAME(NO_LUCK) /*eg. when fighting on cursed ground*/ \
|
BONUS_NAME(NO_LUCK) /*eg. when fighting on cursed ground*/ \
|
||||||
BONUS_NAME(NO_MORALE) /*eg. when fighting on cursed ground*/ \
|
BONUS_NAME(NO_MORALE) /*eg. when fighting on cursed ground*/ \
|
||||||
BONUS_NAME(DARKNESS) /*val = radius */ \
|
BONUS_NAME(DARKNESS) /*val = radius */ \
|
||||||
BONUS_NAME(SPECIAL_SECONDARY_SKILL) /*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) /*val = id, additionalInfo = 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(SPELL_DAMAGE) /*val = value*/\
|
||||||
BONUS_NAME(SPECIFIC_SPELL_DAMAGE) /*subtype = id of spell, 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(SPECIAL_BLESS_DAMAGE) /*val = spell (bless), additionalInfo = value per level in percent*/\
|
||||||
BONUS_NAME(MAXED_SPELL) /*val = id*/\
|
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_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(DRAGON_NATURE) \
|
||||||
BONUS_NAME(CREATURE_DAMAGE)/*subtype 0 = both, 1 = min, 2 = max*/\
|
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)*/\
|
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;
|
TLimiterPtr limiter;
|
||||||
TPropagatorPtr propagator;
|
TPropagatorPtr propagator;
|
||||||
|
TUpdaterPtr updater;
|
||||||
|
|
||||||
std::string description;
|
std::string description;
|
||||||
|
|
||||||
@ -362,6 +366,10 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
|
|||||||
h & effectRange;
|
h & effectRange;
|
||||||
h & limiter;
|
h & limiter;
|
||||||
h & propagator;
|
h & propagator;
|
||||||
|
if(version >= 781)
|
||||||
|
{
|
||||||
|
h & updater;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Ptr>
|
template <typename Ptr>
|
||||||
@ -419,9 +427,12 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string Description() const;
|
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> 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> 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);
|
DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus);
|
||||||
@ -583,6 +594,8 @@ public:
|
|||||||
virtual ~ILimiter();
|
virtual ~ILimiter();
|
||||||
|
|
||||||
virtual int limit(const BonusLimitationContext &context) const; //0 - accept bonus; 1 - drop bonus; 2 - delay (drops eventually)
|
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)
|
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 getBonusesRec(BonusList &out, const CSelector &selector, const CSelector &limit) const;
|
||||||
void getAllBonusesRec(BonusList &out) const;
|
void getAllBonusesRec(BonusList &out) const;
|
||||||
const TBonusListPtr getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr) 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:
|
public:
|
||||||
explicit CBonusSystemNode();
|
explicit CBonusSystemNode();
|
||||||
@ -703,7 +717,7 @@ public:
|
|||||||
///removes bonuses by selector
|
///removes bonuses by selector
|
||||||
void popBonuses(const CSelector &s);
|
void popBonuses(const CSelector &s);
|
||||||
///updates count of remaining turns and removes outdated bonuses by selector
|
///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 bonusToString(const std::shared_ptr<Bonus>& bonus, bool description) const {return "";}; //description or bonus name
|
||||||
virtual std::string nodeName() const;
|
virtual std::string nodeName() const;
|
||||||
|
|
||||||
@ -847,6 +861,8 @@ public:
|
|||||||
void setCreature (CreatureID id);
|
void setCreature (CreatureID id);
|
||||||
|
|
||||||
int limit(const BonusLimitationContext &context) const override;
|
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)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
@ -867,6 +883,8 @@ public:
|
|||||||
HasAnotherBonusLimiter(Bonus::BonusType bonus, TBonusSubtype _subtype);
|
HasAnotherBonusLimiter(Bonus::BonusType bonus, TBonusSubtype _subtype);
|
||||||
|
|
||||||
int limit(const BonusLimitationContext &context) const override;
|
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)
|
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, Bonus::LimitEffect> bonusLimitEffect;
|
||||||
extern DLL_LINKAGE const std::map<std::string, TLimiterPtr> bonusLimiterMap;
|
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, TPropagatorPtr> bonusPropagatorMap;
|
||||||
|
extern DLL_LINKAGE const std::map<std::string, TUpdaterPtr> bonusUpdaterMap;
|
||||||
|
|
||||||
// BonusList template that requires full interface of CBonusSystemNode
|
// BonusList template that requires full interface of CBonusSystemNode
|
||||||
template <class InputIterator>
|
template <class InputIterator>
|
||||||
@ -1006,3 +1024,70 @@ void BonusList::insert(const int position, InputIterator first, InputIterator la
|
|||||||
bonuses.insert(bonuses.begin() + position, first, last);
|
bonuses.insert(bonuses.begin() + position, first, last);
|
||||||
changed();
|
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)
|
while (begin != end)
|
||||||
{
|
{
|
||||||
out<<",\n";
|
out << (compactMode ? ", " : ",\n");
|
||||||
writeEntry(begin++);
|
writeEntry(begin++);
|
||||||
}
|
}
|
||||||
|
|
||||||
out<<"\n";
|
out << (compactMode ? "" : "\n");
|
||||||
prefix.resize(prefix.size()-1);
|
prefix.resize(prefix.size()-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsonWriter::writeEntry(JsonMap::const_iterator entry)
|
void JsonWriter::writeEntry(JsonMap::const_iterator entry)
|
||||||
{
|
{
|
||||||
if (!entry->second.meta.empty())
|
if(!compactMode)
|
||||||
out << prefix << " // " << entry->second.meta << "\n";
|
{
|
||||||
out << prefix;
|
if (!entry->second.meta.empty())
|
||||||
|
out << prefix << " // " << entry->second.meta << "\n";
|
||||||
|
out << prefix;
|
||||||
|
}
|
||||||
writeString(entry->first);
|
writeString(entry->first);
|
||||||
out << " : ";
|
out << " : ";
|
||||||
writeNode(entry->second);
|
writeNode(entry->second);
|
||||||
@ -52,9 +55,12 @@ void JsonWriter::writeEntry(JsonMap::const_iterator entry)
|
|||||||
|
|
||||||
void JsonWriter::writeEntry(JsonVector::const_iterator entry)
|
void JsonWriter::writeEntry(JsonVector::const_iterator entry)
|
||||||
{
|
{
|
||||||
if (!entry->meta.empty())
|
if(!compactMode)
|
||||||
out << prefix << " // " << entry->meta << "\n";
|
{
|
||||||
out << prefix;
|
if (!entry->meta.empty())
|
||||||
|
out << prefix << " // " << entry->meta << "\n";
|
||||||
|
out << prefix;
|
||||||
|
}
|
||||||
writeNode(*entry);
|
writeNode(*entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +100,10 @@ void JsonWriter::writeString(const std::string &string)
|
|||||||
|
|
||||||
void JsonWriter::writeNode(const JsonNode &node)
|
void JsonWriter::writeNode(const JsonNode &node)
|
||||||
{
|
{
|
||||||
|
bool originalMode = compactMode;
|
||||||
|
if(compact && !compactMode && node.isCompact())
|
||||||
|
compactMode = true;
|
||||||
|
|
||||||
switch(node.getType())
|
switch(node.getType())
|
||||||
{
|
{
|
||||||
break; case JsonNode::JsonType::DATA_NULL:
|
break; case JsonNode::JsonType::DATA_NULL:
|
||||||
@ -112,21 +122,24 @@ void JsonWriter::writeNode(const JsonNode &node)
|
|||||||
writeString(node.String());
|
writeString(node.String());
|
||||||
|
|
||||||
break; case JsonNode::JsonType::DATA_VECTOR:
|
break; case JsonNode::JsonType::DATA_VECTOR:
|
||||||
out << "[" << "\n";
|
out << "[" << (compactMode ? " " : "\n");
|
||||||
writeContainer(node.Vector().begin(), node.Vector().end());
|
writeContainer(node.Vector().begin(), node.Vector().end());
|
||||||
out << prefix << "]";
|
out << (compactMode ? " " : prefix) << "]";
|
||||||
|
|
||||||
break; case JsonNode::JsonType::DATA_STRUCT:
|
break; case JsonNode::JsonType::DATA_STRUCT:
|
||||||
out << "{" << "\n";
|
out << "{" << (compactMode ? " " : "\n");
|
||||||
writeContainer(node.Struct().begin(), node.Struct().end());
|
writeContainer(node.Struct().begin(), node.Struct().end());
|
||||||
out << prefix << "}";
|
out << (compactMode ? " " : prefix) << "}";
|
||||||
|
|
||||||
break; case JsonNode::JsonType::DATA_INTEGER:
|
break; case JsonNode::JsonType::DATA_INTEGER:
|
||||||
out << node.Integer();
|
out << node.Integer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compactMode = originalMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonWriter::JsonWriter(std::ostream & output)
|
JsonWriter::JsonWriter(std::ostream & output, bool compact)
|
||||||
: out(output)
|
: out(output), compact(compact)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,10 @@ class JsonWriter
|
|||||||
//prefix for each line (tabulation)
|
//prefix for each line (tabulation)
|
||||||
std::string prefix;
|
std::string prefix;
|
||||||
std::ostream & out;
|
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:
|
public:
|
||||||
template<typename Iterator>
|
template<typename Iterator>
|
||||||
void writeContainer(Iterator begin, Iterator end);
|
void writeContainer(Iterator begin, Iterator end);
|
||||||
@ -23,7 +27,7 @@ public:
|
|||||||
void writeEntry(JsonVector::const_iterator entry);
|
void writeEntry(JsonVector::const_iterator entry);
|
||||||
void writeString(const std::string & string);
|
void writeString(const std::string & string);
|
||||||
void writeNode(const JsonNode & node);
|
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
|
//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;
|
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()
|
void JsonNode::clear()
|
||||||
{
|
{
|
||||||
setType(JsonType::DATA_NULL);
|
setType(JsonType::DATA_NULL);
|
||||||
@ -367,10 +411,10 @@ JsonNode & JsonNode::resolvePointer(const std::string &jsonPointer)
|
|||||||
return ::resolvePointer(*this, jsonPointer);
|
return ::resolvePointer(*this, jsonPointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string JsonNode::toJson() const
|
std::string JsonNode::toJson(bool compact) const
|
||||||
{
|
{
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
JsonWriter writer(out);
|
JsonWriter writer(out, compact);
|
||||||
writer.writeNode(*this);
|
writer.writeNode(*this);
|
||||||
out << "\n";
|
out << "\n";
|
||||||
return out.str();
|
return out.str();
|
||||||
@ -395,7 +439,7 @@ std::shared_ptr<Bonus> JsonUtils::parseBonus (const JsonVector &ability_vec) //T
|
|||||||
auto it = bonusNameMap.find(type);
|
auto it = bonusNameMap.find(type);
|
||||||
if (it == bonusNameMap.end())
|
if (it == bonusNameMap.end())
|
||||||
{
|
{
|
||||||
logMod->error("Error: invalid ability type %s", type);
|
logMod->error("Error: invalid ability type %s.", type);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
b->type = it->second;
|
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);
|
auto it = map.find(type);
|
||||||
if (it == map.end())
|
if (it == map.end())
|
||||||
{
|
{
|
||||||
logMod->error("Error: invalid %s%s", err, type);
|
logMod->error("Error: invalid %s%s.", err, type);
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -445,7 +489,7 @@ void JsonUtils::resolveIdentifier(si32 &var, const JsonNode &node, std::string n
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
default:
|
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);
|
auto it = bonusNameMap.find(type);
|
||||||
if (it == bonusNameMap.end())
|
if (it == bonusNameMap.end())
|
||||||
{
|
{
|
||||||
logMod->error("Error: invalid ability type %s", type);
|
logMod->error("Error: invalid ability type %s.", type);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
b->type = it->second;
|
b->type = it->second;
|
||||||
@ -580,7 +624,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
|||||||
auto it = bonusNameMap.find(anotherBonusType);
|
auto it = bonusNameMap.find(anotherBonusType);
|
||||||
if (it == bonusNameMap.end())
|
if (it == bonusNameMap.end())
|
||||||
{
|
{
|
||||||
logMod->error("Error: invalid ability type %s", anotherBonusType);
|
logMod->error("Error: invalid ability type %s.", anotherBonusType);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
l2->type = it->second;
|
l2->type = it->second;
|
||||||
@ -603,6 +647,31 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
|||||||
if (!value->isNull())
|
if (!value->isNull())
|
||||||
b->propagator = parseByMap(bonusPropagatorMap, value, "propagator type ");
|
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;
|
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("Data in %s is invalid!", dataName);
|
||||||
logMod->warn(log);
|
logMod->warn(log);
|
||||||
|
logMod->trace("%s json: %s", dataName, node.toJson(true));
|
||||||
}
|
}
|
||||||
return log.empty();
|
return log.empty();
|
||||||
}
|
}
|
||||||
@ -822,6 +892,90 @@ void JsonUtils::inherit(JsonNode & descendant, const JsonNode & base)
|
|||||||
descendant.swap(inheritedNode);
|
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)
|
JsonNode JsonUtils::assembleFromFiles(std::vector<std::string> files)
|
||||||
{
|
{
|
||||||
bool isValid;
|
bool isValid;
|
||||||
@ -860,3 +1014,31 @@ JsonNode JsonUtils::assembleFromFiles(std::string filename)
|
|||||||
}
|
}
|
||||||
return result;
|
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 isNull() const;
|
||||||
bool isNumber() 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
|
/// removes all data from node and sets type to null
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
@ -110,7 +114,7 @@ public:
|
|||||||
JsonNode & operator[](std::string child);
|
JsonNode & operator[](std::string child);
|
||||||
const JsonNode & operator[](std::string child) const;
|
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)
|
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);
|
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
|
* @brief generate one Json structure from multiple files
|
||||||
* @param files - list of filenames with parts of json structure
|
* @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>
|
/// 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
|
/// example: schema "vcmi:settings" is used to check user settings
|
||||||
DLL_LINKAGE const JsonNode & getSchema(std::string URI);
|
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
|
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
|
// 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.popBonuses(Bonus::OneDay); //works for children -> all game objs
|
||||||
gs->globalEffects.updateBonuses(Bonus::NDays);
|
gs->globalEffects.reduceBonusDurations(Bonus::NDays);
|
||||||
gs->globalEffects.updateBonuses(Bonus::OneWeek);
|
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...]
|
//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
|
for(NewTurn::Hero h : heroes) //give mana/movement point
|
||||||
|
@ -765,7 +765,7 @@ void BattleInfo::nextRound(int32_t roundNr)
|
|||||||
for(CStack * s : stacks)
|
for(CStack * s : stacks)
|
||||||
{
|
{
|
||||||
// new turn effects
|
// new turn effects
|
||||||
s->updateBonuses(Bonus::NTurns);
|
s->reduceBonusDurations(Bonus::NTurns);
|
||||||
|
|
||||||
s->afterNewRound();
|
s->afterNewRound();
|
||||||
}
|
}
|
||||||
|
@ -496,9 +496,6 @@ void CGHeroInstance::SecondarySkillsInfo::resetWisdomCounter()
|
|||||||
void CGHeroInstance::initObj(CRandomGenerator & rand)
|
void CGHeroInstance::initObj(CRandomGenerator & rand)
|
||||||
{
|
{
|
||||||
blockVisit = true;
|
blockVisit = true;
|
||||||
auto hs = new HeroSpecial();
|
|
||||||
hs->setNodeType(CBonusSystemNode::SPECIALTY);
|
|
||||||
attachTo(hs); //do we ever need to detach it?
|
|
||||||
|
|
||||||
if(!type)
|
if(!type)
|
||||||
initHero(rand); //TODO: set up everything for prison before specialties are configured
|
initHero(rand); //TODO: set up everything for prison before specialties are configured
|
||||||
@ -514,246 +511,23 @@ void CGHeroInstance::initObj(CRandomGenerator & rand)
|
|||||||
appearance = customApp.get();
|
appearance = customApp.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const auto &spec : type->spec) //TODO: unfity with bonus system
|
//copy active (probably growing) bonuses from hero prototype to hero object
|
||||||
{
|
for(std::shared_ptr<Bonus> b : type->specialty)
|
||||||
auto bonus = std::make_shared<Bonus>();
|
addNewBonus(b);
|
||||||
bonus->val = spec.val;
|
//dito for old-style bonuses -> compatibility for old savegames
|
||||||
bonus->sid = id.getNum(); //from the hero, specialty has no unique id
|
for(SSpecialtyBonus & sb : type->specialtyDeprecated)
|
||||||
bonus->duration = Bonus::PERMANENT;
|
for(std::shared_ptr<Bonus> b : sb.bonuses)
|
||||||
bonus->source = Bonus::HERO_SPECIAL;
|
addNewBonus(b);
|
||||||
switch (spec.type)
|
for(SSpecialtyInfo & spec : type->specDeprecated)
|
||||||
{
|
for(std::shared_ptr<Bonus> b : SpecialtyInfoToBonuses(spec, type->ID.getNum()))
|
||||||
case 1:// creature specialty
|
addNewBonus(b);
|
||||||
{
|
|
||||||
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?
|
|
||||||
}
|
|
||||||
|
|
||||||
//initialize bonuses
|
//initialize bonuses
|
||||||
recreateSecondarySkillsBonuses();
|
recreateSecondarySkillsBonuses();
|
||||||
Updatespecialty();
|
|
||||||
|
|
||||||
mana = manaLimit(); //after all bonuses are taken into account, make sure this line is the last one
|
mana = manaLimit(); //after all bonuses are taken into account, make sure this line is the last one
|
||||||
type->name = name;
|
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()
|
void CGHeroInstance::recreateSecondarySkillsBonuses()
|
||||||
{
|
{
|
||||||
@ -766,6 +540,23 @@ void CGHeroInstance::recreateSecondarySkillsBonuses()
|
|||||||
updateSkill(SecondarySkill(skill_info.first), level);
|
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)
|
void CGHeroInstance::updateSkill(SecondarySkill which, int val)
|
||||||
{
|
{
|
||||||
auto skillBonus = (*VLC->skillh)[which]->getBonus(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;
|
base *= (100.0 + maxSchoolBonus) / 100.0;
|
||||||
|
|
||||||
if(affectedStack && affectedStack->creatureLevel() > 0) //Hero specials like Solmyr, Deemer
|
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;
|
return base;
|
||||||
}
|
}
|
||||||
@ -1214,11 +1005,6 @@ int CGHeroInstance::maxSpellLevel() const
|
|||||||
void CGHeroInstance::deserializationFix()
|
void CGHeroInstance::deserializationFix()
|
||||||
{
|
{
|
||||||
artDeserializationFix(this);
|
artDeserializationFix(this);
|
||||||
|
|
||||||
for (auto hs : specialty)
|
|
||||||
{
|
|
||||||
attachTo (hs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CBonusSystemNode * CGHeroInstance::whereShouldBeAttached(CGameState *gs)
|
CBonusSystemNode * CGHeroInstance::whereShouldBeAttached(CGameState *gs)
|
||||||
@ -1471,8 +1257,8 @@ void CGHeroInstance::levelUp(std::vector<SecondarySkill> skills)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//specialty
|
//update specialty and other bonuses that scale with level
|
||||||
Updatespecialty();
|
treeHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGHeroInstance::levelUpAutomatically(CRandomGenerator & rand)
|
void CGHeroInstance::levelUpAutomatically(CRandomGenerator & rand)
|
||||||
|
@ -95,7 +95,8 @@ public:
|
|||||||
}
|
}
|
||||||
} patrol;
|
} patrol;
|
||||||
|
|
||||||
struct DLL_LINKAGE HeroSpecial : CBonusSystemNode
|
// deprecated - used only for loading of old saves
|
||||||
|
struct HeroSpecial : CBonusSystemNode
|
||||||
{
|
{
|
||||||
bool growsWithLevel;
|
bool growsWithLevel;
|
||||||
|
|
||||||
@ -108,8 +109,6 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<HeroSpecial*> specialty;
|
|
||||||
|
|
||||||
struct DLL_LINKAGE SecondarySkillsInfo
|
struct DLL_LINKAGE SecondarySkillsInfo
|
||||||
{
|
{
|
||||||
//skills are determined, initialized at map start
|
//skills are determined, initialized at map start
|
||||||
@ -215,7 +214,6 @@ public:
|
|||||||
void pushPrimSkill(PrimarySkill::PrimarySkill which, int val);
|
void pushPrimSkill(PrimarySkill::PrimarySkill which, int val);
|
||||||
ui8 maxlevelsToMagicSchool() const;
|
ui8 maxlevelsToMagicSchool() const;
|
||||||
ui8 maxlevelsToWisdom() const;
|
ui8 maxlevelsToWisdom() const;
|
||||||
void Updatespecialty();
|
|
||||||
void recreateSecondarySkillsBonuses();
|
void recreateSecondarySkillsBonuses();
|
||||||
void updateSkill(SecondarySkill which, int val);
|
void updateSkill(SecondarySkill which, int val);
|
||||||
|
|
||||||
@ -269,6 +267,7 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void levelUpAutomatically(CRandomGenerator & rand);
|
void levelUpAutomatically(CRandomGenerator & rand);
|
||||||
|
void recreateSpecialtyBonuses(std::vector<HeroSpecial*> & specialtyDeprecated);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::string getHeroTypeName() const;
|
std::string getHeroTypeName() const;
|
||||||
@ -297,7 +296,13 @@ public:
|
|||||||
h & visitedTown;
|
h & visitedTown;
|
||||||
h & boat;
|
h & boat;
|
||||||
h & type;
|
h & type;
|
||||||
h & specialty;
|
if(version < 781)
|
||||||
|
{
|
||||||
|
std::vector<HeroSpecial*> specialtyDeprecated;
|
||||||
|
h & specialtyDeprecated;
|
||||||
|
if(!h.saving)
|
||||||
|
recreateSpecialtyBonuses(specialtyDeprecated);
|
||||||
|
}
|
||||||
h & commander;
|
h & commander;
|
||||||
h & visitedObjects;
|
h & visitedObjects;
|
||||||
BONUS_TREE_DESERIALIZATION_FIX
|
BONUS_TREE_DESERIALIZATION_FIX
|
||||||
|
@ -135,6 +135,12 @@ void registerTypesMapObjectTypes(Serializer &s)
|
|||||||
REGISTER_GENERIC_HANDLER(CGWitchHut);
|
REGISTER_GENERIC_HANDLER(CGWitchHut);
|
||||||
|
|
||||||
#undef REGISTER_GENERIC_HANDLER
|
#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>
|
template<typename Serializer>
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
#include "../ConstTransitivePtr.h"
|
#include "../ConstTransitivePtr.h"
|
||||||
#include "../GameConstants.h"
|
#include "../GameConstants.h"
|
||||||
|
|
||||||
const ui32 SERIALIZATION_VERSION = 780;
|
const ui32 SERIALIZATION_VERSION = 781;
|
||||||
const ui32 MINIMAL_SERIALIZATION_VERSION = 753;
|
const ui32 MINIMAL_SERIALIZATION_VERSION = 753;
|
||||||
const std::string SAVEGAME_MAGIC = "VCMISVG";
|
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
|
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);
|
Bonus specialBonus(Bonus::N_TURNS, Bonus::CREATURE_DAMAGE, Bonus::SPELL_EFFECT, damagePercent, m->getSpellIndex(), 0, Bonus::PERCENT_TO_ALL);
|
||||||
specialBonus.turnsRemain = duration;
|
specialBonus.turnsRemain = duration;
|
||||||
buffer.push_back(specialBonus);
|
buffer.push_back(specialBonus);
|
||||||
|
Loading…
Reference in New Issue
Block a user