mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
[refactor]
* use spells configuration (all timed effects for battle spells) * a few more cleanups +register erm resource types (useful in future and less noise in log now)
This commit is contained in:
parent
a86b5c1e2e
commit
53b684180d
@ -545,67 +545,12 @@ enum SpellTypes
|
|||||||
|
|
||||||
SpellTypes spellType(const CSpell *spell)
|
SpellTypes spellType(const CSpell *spell)
|
||||||
{
|
{
|
||||||
switch(spell->id)
|
if (spell->isOffensiveSpell())
|
||||||
{
|
|
||||||
//offense spell
|
|
||||||
case Spells::MAGIC_ARROW:
|
|
||||||
case Spells::ICE_BOLT:
|
|
||||||
case Spells::LIGHTNING_BOLT:
|
|
||||||
case Spells::IMPLOSION:
|
|
||||||
case Spells::CHAIN_LIGHTNING:
|
|
||||||
case Spells::FROST_RING:
|
|
||||||
case Spells::FIREBALL:
|
|
||||||
case Spells::INFERNO:
|
|
||||||
case Spells::METEOR_SHOWER:
|
|
||||||
case Spells::DEATH_RIPPLE:
|
|
||||||
case Spells::DESTROY_UNDEAD:
|
|
||||||
case Spells::ARMAGEDDON:
|
|
||||||
case Spells::TITANS_LIGHTNING_BOLT:
|
|
||||||
case Spells::THUNDERBOLT: //(thunderbirds)
|
|
||||||
return OFFENSIVE_SPELL;
|
return OFFENSIVE_SPELL;
|
||||||
|
if (spell->hasEffects())
|
||||||
|
return TIMED_EFFECT;
|
||||||
|
return OTHER;
|
||||||
|
|
||||||
case Spells::SHIELD:
|
|
||||||
case Spells::AIR_SHIELD:
|
|
||||||
case Spells::FIRE_SHIELD:
|
|
||||||
case Spells::PROTECTION_FROM_AIR:
|
|
||||||
case Spells::PROTECTION_FROM_FIRE:
|
|
||||||
case Spells::PROTECTION_FROM_WATER:
|
|
||||||
case Spells::PROTECTION_FROM_EARTH:
|
|
||||||
case Spells::ANTI_MAGIC:
|
|
||||||
case Spells::MAGIC_MIRROR:
|
|
||||||
case Spells::BLESS:
|
|
||||||
case Spells::CURSE:
|
|
||||||
case Spells::BLOODLUST:
|
|
||||||
case Spells::PRECISION:
|
|
||||||
case Spells::WEAKNESS:
|
|
||||||
case Spells::STONE_SKIN:
|
|
||||||
case Spells::DISRUPTING_RAY:
|
|
||||||
case Spells::PRAYER:
|
|
||||||
case Spells::MIRTH:
|
|
||||||
case Spells::SORROW:
|
|
||||||
case Spells::FORTUNE:
|
|
||||||
case Spells::MISFORTUNE:
|
|
||||||
case Spells::HASTE:
|
|
||||||
case Spells::SLOW:
|
|
||||||
case Spells::SLAYER:
|
|
||||||
case Spells::FRENZY:
|
|
||||||
case Spells::COUNTERSTRIKE:
|
|
||||||
case Spells::BERSERK:
|
|
||||||
case Spells::HYPNOTIZE:
|
|
||||||
case Spells::FORGETFULNESS:
|
|
||||||
case Spells::BLIND:
|
|
||||||
case Spells::STONE_GAZE:
|
|
||||||
case Spells::POISON:
|
|
||||||
case Spells::BIND:
|
|
||||||
case Spells::DISEASE:
|
|
||||||
case Spells::PARALYZE:
|
|
||||||
case Spells::AGE:
|
|
||||||
case Spells::ACID_BREATH_DEFENSE:
|
|
||||||
return TIMED_EFFECT;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return OTHER;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PossibleSpellcast
|
struct PossibleSpellcast
|
||||||
|
@ -6,14 +6,25 @@
|
|||||||
// 1 -> spell is positive for them
|
// 1 -> spell is positive for them
|
||||||
// anim: main effect animation (AC format), -1 - none
|
// anim: main effect animation (AC format), -1 - none
|
||||||
// ranges: spell range description in SRSL ([no magic] [basic] [advanced] [expert])
|
// ranges: spell range description in SRSL ([no magic] [basic] [advanced] [expert])
|
||||||
|
// counters: array of ids of countering spells
|
||||||
|
|
||||||
// flags: string array of
|
// flags: string array of
|
||||||
// damage
|
// damage
|
||||||
// directDamage //todo
|
// offensive
|
||||||
// rising
|
// rising
|
||||||
// mind
|
// mind
|
||||||
|
// summoning //todo:
|
||||||
|
|
||||||
//effects: array of bonus for permanent effects
|
//effects: array of structure for bonuses for permanent effects
|
||||||
|
// {bonus format} - effect //todo
|
||||||
|
// + values: [4 int values] (OPTIONAL default from sptraits) values for levels
|
||||||
|
// + ainfos: [4 int values] (optional) additional infos for levels (atm only CURSE)
|
||||||
|
|
||||||
|
//
|
||||||
|
//immunity - name of bonus granting immunity to this spell
|
||||||
|
//
|
||||||
|
// immunity: array any of these bonus grants immunity
|
||||||
|
// limit: array required bonus to be affected, all required
|
||||||
|
|
||||||
"spells":
|
"spells":
|
||||||
{
|
{
|
||||||
@ -130,7 +141,7 @@
|
|||||||
"effect": -1,
|
"effect": -1,
|
||||||
"anim": 64,
|
"anim": 64,
|
||||||
"ranges": [ "0", "0", "0", "0" ],
|
"ranges": [ "0", "0", "0", "0" ],
|
||||||
"flags" : ["damage"]
|
"flags" : ["damage, offensive"]
|
||||||
},
|
},
|
||||||
"iceBolt" :
|
"iceBolt" :
|
||||||
{
|
{
|
||||||
@ -138,7 +149,7 @@
|
|||||||
"effect": -1,
|
"effect": -1,
|
||||||
"anim": 46,
|
"anim": 46,
|
||||||
"ranges": [ "0", "0", "0", "0" ],
|
"ranges": [ "0", "0", "0", "0" ],
|
||||||
"flags" : ["damage"]
|
"flags" : ["damage, offensive"]
|
||||||
},
|
},
|
||||||
"lightningBolt" :
|
"lightningBolt" :
|
||||||
{
|
{
|
||||||
@ -146,7 +157,7 @@
|
|||||||
"effect": -1,
|
"effect": -1,
|
||||||
"anim": 38,
|
"anim": 38,
|
||||||
"ranges": [ "0", "0", "0", "0" ],
|
"ranges": [ "0", "0", "0", "0" ],
|
||||||
"flags" : ["damage"]
|
"flags" : ["damage, offensive"]
|
||||||
},
|
},
|
||||||
"implosion" :
|
"implosion" :
|
||||||
{
|
{
|
||||||
@ -154,7 +165,7 @@
|
|||||||
"effect": -1,
|
"effect": -1,
|
||||||
"anim": 10,
|
"anim": 10,
|
||||||
"ranges": [ "0", "0", "0", "0" ],
|
"ranges": [ "0", "0", "0", "0" ],
|
||||||
"flags" : ["damage"]
|
"flags" : ["damage, offensive"]
|
||||||
},
|
},
|
||||||
"chainLightning" :
|
"chainLightning" :
|
||||||
{
|
{
|
||||||
@ -162,7 +173,7 @@
|
|||||||
"effect": -1,
|
"effect": -1,
|
||||||
"anim": 38,
|
"anim": 38,
|
||||||
"ranges": [ "0", "0", "0", "0" ],
|
"ranges": [ "0", "0", "0", "0" ],
|
||||||
"flags" : ["damage"]
|
"flags" : ["damage, offensive"]
|
||||||
},
|
},
|
||||||
"frostRing" :
|
"frostRing" :
|
||||||
{
|
{
|
||||||
@ -170,7 +181,7 @@
|
|||||||
"effect": -1,
|
"effect": -1,
|
||||||
"anim": 45,
|
"anim": 45,
|
||||||
"ranges": [ "1", "1", "1", "1" ],
|
"ranges": [ "1", "1", "1", "1" ],
|
||||||
"flags" : ["damage"]
|
"flags" : ["damage, offensive"]
|
||||||
},
|
},
|
||||||
"fireball" :
|
"fireball" :
|
||||||
{
|
{
|
||||||
@ -178,7 +189,7 @@
|
|||||||
"effect": -1,
|
"effect": -1,
|
||||||
"anim": 53,
|
"anim": 53,
|
||||||
"ranges": [ "0,1", "0,1", "0,1", "0,1" ],
|
"ranges": [ "0,1", "0,1", "0,1", "0,1" ],
|
||||||
"flags" : ["damage"]
|
"flags" : ["damage, offensive"]
|
||||||
},
|
},
|
||||||
"inferno" :
|
"inferno" :
|
||||||
{
|
{
|
||||||
@ -186,7 +197,7 @@
|
|||||||
"effect": -1,
|
"effect": -1,
|
||||||
"anim": 9,
|
"anim": 9,
|
||||||
"ranges": [ "0-2", "0-2", "0-2", "0-2" ],
|
"ranges": [ "0-2", "0-2", "0-2", "0-2" ],
|
||||||
"flags" : ["damage"]
|
"flags" : ["damage, offensive"]
|
||||||
},
|
},
|
||||||
"meteorShower" :
|
"meteorShower" :
|
||||||
{
|
{
|
||||||
@ -194,7 +205,7 @@
|
|||||||
"effect": -1,
|
"effect": -1,
|
||||||
"anim": 16,
|
"anim": 16,
|
||||||
"ranges": [ "0,1", "0,1", "0,1", "0,1" ],
|
"ranges": [ "0,1", "0,1", "0,1", "0,1" ],
|
||||||
"flags" : ["damage"]
|
"flags" : ["damage, offensive"]
|
||||||
},
|
},
|
||||||
"deathRipple" :
|
"deathRipple" :
|
||||||
{
|
{
|
||||||
@ -202,7 +213,8 @@
|
|||||||
"effect": -1,
|
"effect": -1,
|
||||||
"anim": 8,
|
"anim": 8,
|
||||||
"ranges": [ "X", "X", "X", "X" ],
|
"ranges": [ "X", "X", "X", "X" ],
|
||||||
"flags" : ["damage"]
|
"flags" : ["damage, offensive"],
|
||||||
|
"immunity": ["SIEGE_WEAPON","UNDEAD"]
|
||||||
},
|
},
|
||||||
"destroyUndead" :
|
"destroyUndead" :
|
||||||
{
|
{
|
||||||
@ -210,7 +222,9 @@
|
|||||||
"effect": -1,
|
"effect": -1,
|
||||||
"anim": 29,
|
"anim": 29,
|
||||||
"ranges": [ "X", "X", "X", "X" ],
|
"ranges": [ "X", "X", "X", "X" ],
|
||||||
"flags" : ["damage"]
|
"flags" : ["damage, offensive"],
|
||||||
|
"limit":["UNDEAD"]
|
||||||
|
|
||||||
},
|
},
|
||||||
"armageddon" :
|
"armageddon" :
|
||||||
{
|
{
|
||||||
@ -218,7 +232,7 @@
|
|||||||
"effect": -1,
|
"effect": -1,
|
||||||
"anim": 12,
|
"anim": 12,
|
||||||
"ranges": [ "X", "X", "X", "X" ],
|
"ranges": [ "X", "X", "X", "X" ],
|
||||||
"flags" : ["damage"]
|
"flags" : ["damage, offensive"]
|
||||||
},
|
},
|
||||||
"shield" :
|
"shield" :
|
||||||
{
|
{
|
||||||
@ -228,9 +242,11 @@
|
|||||||
"ranges": [ "0", "0", "0", "X" ],
|
"ranges": [ "0", "0", "0", "X" ],
|
||||||
"effects":
|
"effects":
|
||||||
[
|
[
|
||||||
|
|
||||||
{
|
{
|
||||||
"type": "GENERAL_DAMAGE_REDUCTION",
|
"type": "GENERAL_DAMAGE_REDUCTION",
|
||||||
"subtype":0
|
"subtype":0,
|
||||||
|
"duration": "N_TURNS"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -244,7 +260,8 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "GENERAL_DAMAGE_REDUCTION",
|
"type": "GENERAL_DAMAGE_REDUCTION",
|
||||||
"subtype":1
|
"subtype":1,
|
||||||
|
"duration": "N_TURNS"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -257,7 +274,8 @@
|
|||||||
"effects":
|
"effects":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "FIRE_SHIELD"
|
"type": "FIRE_SHIELD",
|
||||||
|
"duration": "N_TURNS"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -270,10 +288,14 @@
|
|||||||
"effects":
|
"effects":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
|
||||||
"type": "SPELL_DAMAGE_REDUCTION",
|
"type": "SPELL_DAMAGE_REDUCTION",
|
||||||
"subtype":0
|
"subtype":0,
|
||||||
|
"duration": "N_TURNS"
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
},
|
},
|
||||||
"protectFire" :
|
"protectFire" :
|
||||||
{
|
{
|
||||||
@ -285,7 +307,8 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "SPELL_DAMAGE_REDUCTION",
|
"type": "SPELL_DAMAGE_REDUCTION",
|
||||||
"subtype":1
|
"subtype":1,
|
||||||
|
"duration": "N_TURNS"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -299,7 +322,8 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "SPELL_DAMAGE_REDUCTION",
|
"type": "SPELL_DAMAGE_REDUCTION",
|
||||||
"subtype":2
|
"subtype":2,
|
||||||
|
"duration": "N_TURNS"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -313,7 +337,8 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "SPELL_DAMAGE_REDUCTION",
|
"type": "SPELL_DAMAGE_REDUCTION",
|
||||||
"subtype":3
|
"subtype":3,
|
||||||
|
"duration": "N_TURNS"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -327,8 +352,10 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "LEVEL_SPELL_IMMUNITY",
|
"type": "LEVEL_SPELL_IMMUNITY",
|
||||||
"subtype":5,
|
"subtype":5, //needed?
|
||||||
"valType":"INDEPENDENT_MAX"
|
"valType":"INDEPENDENT_MAX",
|
||||||
|
"duration": "N_TURNS",
|
||||||
|
"values":[3,3,4,5]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -349,7 +376,8 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "MAGIC_MIRROR",
|
"type": "MAGIC_MIRROR",
|
||||||
"valType":"INDEPENDENT_MAX"
|
"valType":"INDEPENDENT_MAX",
|
||||||
|
"duration": "N_TURNS"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -358,8 +386,7 @@
|
|||||||
"id": 37,
|
"id": 37,
|
||||||
"effect": 1,
|
"effect": 1,
|
||||||
"anim": 39,
|
"anim": 39,
|
||||||
"ranges": [ "0", "0", "0", "0" ],
|
"ranges": [ "0", "0", "0", "0" ]
|
||||||
"flags" : ["rising"]
|
|
||||||
},
|
},
|
||||||
"resurrection" :
|
"resurrection" :
|
||||||
{
|
{
|
||||||
@ -382,7 +409,8 @@
|
|||||||
"id": 40,
|
"id": 40,
|
||||||
"effect": 1,
|
"effect": 1,
|
||||||
"anim": 79,
|
"anim": 79,
|
||||||
"ranges": [ "0", "0", "0", "0" ]
|
"ranges": [ "0", "0", "0", "0" ],
|
||||||
|
"flags" : ["rising"]
|
||||||
},
|
},
|
||||||
"bless" :
|
"bless" :
|
||||||
{
|
{
|
||||||
@ -395,9 +423,11 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "ALWAYS_MAXIMUM_DAMAGE",
|
"type": "ALWAYS_MAXIMUM_DAMAGE",
|
||||||
"valType":"INDEPENDENT_MAX"
|
"valType":"INDEPENDENT_MAX",
|
||||||
|
"duration": "N_TURNS"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"immunity":["UNDEAD"]
|
||||||
},
|
},
|
||||||
"curse" :
|
"curse" :
|
||||||
{
|
{
|
||||||
@ -409,9 +439,13 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "ALWAYS_MINIMUM_DAMAGE",
|
"type": "ALWAYS_MINIMUM_DAMAGE",
|
||||||
"valType":"INDEPENDENT_MAX"
|
"subtype": -1, //any attack
|
||||||
|
"valType": "INDEPENDENT_MAX",
|
||||||
|
"duration": "N_TURNS",
|
||||||
|
"ainfos":[0,0,20,20]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"immunity":["UNDEAD"]
|
||||||
},
|
},
|
||||||
"bloodlust" :
|
"bloodlust" :
|
||||||
{
|
{
|
||||||
@ -424,7 +458,8 @@
|
|||||||
{
|
{
|
||||||
"type": "PRIMARY_SKILL",
|
"type": "PRIMARY_SKILL",
|
||||||
"subtype": 0, //ATTACK
|
"subtype": 0, //ATTACK
|
||||||
"effectRange" : "ONLY_MELEE_FIGHT"
|
"effectRange" : "ONLY_MELEE_FIGHT",
|
||||||
|
"duration": "N_TURNS"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -439,9 +474,11 @@
|
|||||||
{
|
{
|
||||||
"type": "PRIMARY_SKILL",
|
"type": "PRIMARY_SKILL",
|
||||||
"subtype": 0, //ATTACK
|
"subtype": 0, //ATTACK
|
||||||
"effectRange" : "ONLY_DISTANCE_FIGHT"
|
"effectRange" : "ONLY_DISTANCE_FIGHT",
|
||||||
|
"duration": "N_TURNS"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"limit":["SHOOTER"]
|
||||||
},
|
},
|
||||||
"weakness" :
|
"weakness" :
|
||||||
{
|
{
|
||||||
@ -453,7 +490,9 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "PRIMARY_SKILL",
|
"type": "PRIMARY_SKILL",
|
||||||
"subtype": 0 //ATTACK
|
"subtype": 0, //ATTACK
|
||||||
|
"duration": "N_TURNS",
|
||||||
|
"values":[-3,-3,-6,-6]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -467,7 +506,8 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "PRIMARY_SKILL",
|
"type": "PRIMARY_SKILL",
|
||||||
"subtype": 1 //DEFENSE
|
"subtype": 1, //DEFENSE
|
||||||
|
"duration": "N_TURNS"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -481,7 +521,10 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "PRIMARY_SKILL",
|
"type": "PRIMARY_SKILL",
|
||||||
"subtype": 1 //DEFENSE
|
"subtype": 1, //DEFENSE
|
||||||
|
"valueType": "ADDITIVE_VALUE",
|
||||||
|
"duration": "N_TURNS",
|
||||||
|
"values":[-3,-3,-4,-5]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -495,14 +538,17 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "PRIMARY_SKILL",
|
"type": "PRIMARY_SKILL",
|
||||||
"subtype": 0 //ATTACK
|
"subtype": 0, //ATTACK
|
||||||
|
"duration": "N_TURNS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "PRIMARY_SKILL",
|
"type": "PRIMARY_SKILL",
|
||||||
"subtype": 1 //DEFENSE
|
"subtype": 1, //DEFENSE
|
||||||
|
"duration": "N_TURNS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "STACKS_SPEED"
|
"type": "STACKS_SPEED",
|
||||||
|
"duration": "N_TURNS"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -516,7 +562,8 @@
|
|||||||
"effects":
|
"effects":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "MORALE"
|
"type": "MORALE",
|
||||||
|
"duration": "N_TURNS"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -530,7 +577,9 @@
|
|||||||
"effects":
|
"effects":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "MORALE"
|
"type": "MORALE",
|
||||||
|
"duration": "N_TURNS",
|
||||||
|
"values":[-1,-1,-2,-2]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -544,7 +593,8 @@
|
|||||||
"effects":
|
"effects":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "LUCK"
|
"type": "LUCK",
|
||||||
|
"duration": "N_TURNS"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -558,7 +608,9 @@
|
|||||||
"effects":
|
"effects":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "LUCK"
|
"type": "LUCK",
|
||||||
|
"duration": "N_TURNS",
|
||||||
|
"values":[-1,-1,-2,-2]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -572,9 +624,11 @@
|
|||||||
"effects":
|
"effects":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "STACKS_SPEED"
|
"type": "STACKS_SPEED",
|
||||||
|
"duration": "N_TURNS"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"immunity":["SIEGE_WEAPON"]
|
||||||
},
|
},
|
||||||
"slow" :
|
"slow" :
|
||||||
{
|
{
|
||||||
@ -586,9 +640,13 @@
|
|||||||
"effects":
|
"effects":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "STACKS_SPEED"
|
"type": "STACKS_SPEED",
|
||||||
|
"valueType": "PERCENT_TO_ALL",
|
||||||
|
"duration": "N_TURNS"
|
||||||
|
"values":[-25,-25,-50,-50]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"immunity":["SIEGE_WEAPON"]
|
||||||
},
|
},
|
||||||
"slayer" :
|
"slayer" :
|
||||||
{
|
{
|
||||||
@ -599,7 +657,9 @@
|
|||||||
"effects":
|
"effects":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "SLAYER"
|
"type": "SLAYER",
|
||||||
|
"duration": "N_TURNS",
|
||||||
|
"values":[0,1,2,3]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -612,7 +672,8 @@
|
|||||||
"effects":
|
"effects":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "IN_FRENZY"
|
"type": "IN_FRENZY",
|
||||||
|
"duration": "N_TURNS"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -622,7 +683,7 @@
|
|||||||
"effect": -1,
|
"effect": -1,
|
||||||
"anim": 38,
|
"anim": 38,
|
||||||
"ranges": [ "0", "0", "0", "0" ],
|
"ranges": [ "0", "0", "0", "0" ],
|
||||||
"flags" : ["damage"]
|
"flags" : ["damage, offensive"]
|
||||||
},
|
},
|
||||||
"counterstrike" :
|
"counterstrike" :
|
||||||
{
|
{
|
||||||
@ -633,7 +694,8 @@
|
|||||||
"effects":
|
"effects":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "ADDITIONAL_RETALIATION"
|
"type": "ADDITIONAL_RETALIATION",
|
||||||
|
"duration": "N_TURNS"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -647,7 +709,9 @@
|
|||||||
"effects":
|
"effects":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "ATTACKS_NEAREST_CREATURE"
|
"type": "ATTACKS_NEAREST_CREATURE",
|
||||||
|
"duration": "N_TURNS",
|
||||||
|
"values":[0,1,2,3]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -661,7 +725,9 @@
|
|||||||
"effects":
|
"effects":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "HYPNOTIZED"
|
"type": "HYPNOTIZED",
|
||||||
|
"duration": "N_TURNS",
|
||||||
|
"values":[0,1,2,3]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -675,9 +741,12 @@
|
|||||||
"effects":
|
"effects":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "FORGETFULL"
|
"type": "FORGETFULL",
|
||||||
|
"duration": "N_TURNS",
|
||||||
|
"values":[0,1,2,3]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"limit":["SHOOTER"]
|
||||||
},
|
},
|
||||||
"blind" :
|
"blind" :
|
||||||
{
|
{
|
||||||
@ -690,16 +759,18 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "NOT_ACTIVE",
|
"type": "NOT_ACTIVE",
|
||||||
"subtype": 62, //really ase spell id, is it right?
|
"subtype": 62,
|
||||||
//TODO: duration
|
"duration": "UNITL_BEING_ATTACKED N_TURNS",
|
||||||
|
"values":[0,0,0,0]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "GENERAL_ATTACK_REDUCTION"
|
"type": "GENERAL_ATTACK_REDUCTION",
|
||||||
//TODO: duration
|
"duration": "UNITL_BEING_ATTACKED N_TURNS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "NO_RETALIATION",
|
"type": "NO_RETALIATION",
|
||||||
"duration": "UNITL_BEING_ATTACKED"
|
"duration": "UNITL_BEING_ATTACKED",
|
||||||
|
"values":[0,0,0,0]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -708,7 +779,8 @@
|
|||||||
"id": 63,
|
"id": 63,
|
||||||
"effect": 1,
|
"effect": 1,
|
||||||
"anim": -1,
|
"anim": -1,
|
||||||
"ranges": [ "0", "0", "0", "0" ]
|
"ranges": [ "0", "0", "0", "0" ],
|
||||||
|
"immunity":["SIEGE_WEAPON"]
|
||||||
},
|
},
|
||||||
"removeObstacle" :
|
"removeObstacle" :
|
||||||
{
|
{
|
||||||
@ -722,7 +794,8 @@
|
|||||||
"id": 65,
|
"id": 65,
|
||||||
"effect": 1,
|
"effect": 1,
|
||||||
"anim": -1,
|
"anim": -1,
|
||||||
"ranges": [ "0", "0", "0", "0" ]
|
"ranges": [ "0", "0", "0", "0" ],
|
||||||
|
"immunity":["SIEGE_WEAPON"]
|
||||||
},
|
},
|
||||||
"fireElemental" :
|
"fireElemental" :
|
||||||
{
|
{
|
||||||
@ -762,12 +835,14 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "NOT_ACTIVE",
|
"type": "NOT_ACTIVE",
|
||||||
"subtype": 62
|
"subtype": 62,
|
||||||
//TODO: duration
|
"duration": "UNITL_BEING_ATTACKED N_TURNS",
|
||||||
|
"values":[0,0,0,0]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "NO_RETALIATION",
|
"type": "NO_RETALIATION",
|
||||||
"duration": "UNITL_BEING_ATTACKED"
|
"duration": "UNITL_BEING_ATTACKED",
|
||||||
|
"values":[0,0,0,0]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -781,13 +856,16 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "POISON",
|
"type": "POISON",
|
||||||
"val" : 30,
|
"valueType": "INDEPENDENT_MAX",
|
||||||
"valueType": "INDEPENDENT_MAX"
|
"duration": "N_TURNS",
|
||||||
|
"values":[30,30,30,30]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "STACK_HEALTH",
|
"type": "STACK_HEALTH",
|
||||||
"val" : -10,
|
"val" : -10,
|
||||||
"valueType": "PERCENT_TO_ALL"
|
"valueType": "PERCENT_TO_ALL",
|
||||||
|
"duration": "N_TURNS",
|
||||||
|
"values":[-10,-10,-10,-10]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -801,9 +879,9 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "BIND_EFFECT",
|
"type": "BIND_EFFECT",
|
||||||
"val" : 30,
|
"duration" : "PERMANENT",
|
||||||
"turns" : 1,
|
"addInfo" : -1,
|
||||||
"duration" : "PERMANENT"
|
"values":[0,0,0,0]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -818,12 +896,14 @@
|
|||||||
{
|
{
|
||||||
"type": "PRIMARY_SKILL",
|
"type": "PRIMARY_SKILL",
|
||||||
"subtype": 0,
|
"subtype": 0,
|
||||||
"val" : -2,
|
"duration": "N_TURNS",
|
||||||
|
"values":[-2,-2,-2,-2]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "PRIMARY_SKILL",
|
"type": "PRIMARY_SKILL",
|
||||||
"subtype": 1,
|
"subtype": 1,
|
||||||
"val" : -2,
|
"duration": "N_TURNS",
|
||||||
|
"values":[-2,-2,-2,-2]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -838,11 +918,13 @@
|
|||||||
{
|
{
|
||||||
"type": "NOT_ACTIVE",
|
"type": "NOT_ACTIVE",
|
||||||
"subtype": 74,
|
"subtype": 74,
|
||||||
//TODO: duration
|
"duration": "UNITL_BEING_ATTACKED N_TURNS",
|
||||||
|
"values":[0,0,0,0]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "NO_RETALIATION",
|
"type": "NO_RETALIATION",
|
||||||
"duration": "UNITL_BEING_ATTACKED"
|
"duration": "UNITL_BEING_ATTACKED",
|
||||||
|
"values":[0,0,0,0]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -856,8 +938,9 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "STACK_HEALTH",
|
"type": "STACK_HEALTH",
|
||||||
"val" : -50,
|
"valueType": "PERCENT_TO_ALL",
|
||||||
"valueType": "PERCENT_TO_ALL"
|
"duration": "N_TURNS",
|
||||||
|
"values":[-50,-50,-50,-50]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -874,7 +957,7 @@
|
|||||||
"effect": -1,
|
"effect": -1,
|
||||||
"anim": 38,
|
"anim": 38,
|
||||||
"ranges": [ "0", "0", "0", "0" ],
|
"ranges": [ "0", "0", "0", "0" ],
|
||||||
"flags" : ["damage"]
|
"flags" : ["damage, offensive"]
|
||||||
},
|
},
|
||||||
"dispelHelpful" :
|
"dispelHelpful" :
|
||||||
{
|
{
|
||||||
@ -901,9 +984,9 @@
|
|||||||
{
|
{
|
||||||
"type": "PRIMARY_SKILL",
|
"type": "PRIMARY_SKILL",
|
||||||
"subtype": 1,
|
"subtype": 1,
|
||||||
"val" : -3,
|
|
||||||
"duration" : "PERMANENT",
|
"duration" : "PERMANENT",
|
||||||
"valueType": "ADDITIVE_VALUE"
|
"valueType": "ADDITIVE_VALUE",
|
||||||
|
"values":[-3,-3,-3,-3]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -120,7 +120,6 @@ void BattleInfo::calculateCasualties( std::map<ui32,si32> *casualties ) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int BattleInfo::calculateSpellDuration( const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower)
|
int BattleInfo::calculateSpellDuration( const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower)
|
||||||
{
|
{
|
||||||
if(!caster)
|
if(!caster)
|
||||||
@ -167,9 +166,9 @@ ui32 BattleInfo::calculateHealedHP(const CGHeroInstance * caster, const CSpell *
|
|||||||
bool resurrect = resurrects(spell->id);
|
bool resurrect = resurrects(spell->id);
|
||||||
int healedHealth;
|
int healedHealth;
|
||||||
if (spell->id == Spells::SACRIFICE && sacrificedStack)
|
if (spell->id == Spells::SACRIFICE && sacrificedStack)
|
||||||
healedHealth = (caster->getPrimSkillLevel(2) + sacrificedStack->MaxHealth() + spell->powers[caster->getSpellSchoolLevel(spell)]) * sacrificedStack->count;
|
healedHealth = (caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) + sacrificedStack->MaxHealth() + spell->powers[caster->getSpellSchoolLevel(spell)]) * sacrificedStack->count;
|
||||||
else
|
else
|
||||||
healedHealth = caster->getPrimSkillLevel(2) * spell->power + spell->powers[caster->getSpellSchoolLevel(spell)];
|
healedHealth = caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) * spell->power + spell->powers[caster->getSpellSchoolLevel(spell)];
|
||||||
healedHealth = calculateSpellBonus(healedHealth, spell, caster, stack);
|
healedHealth = calculateSpellBonus(healedHealth, spell, caster, stack);
|
||||||
return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (resurrect ? stack->baseAmount * stack->MaxHealth() : 0));
|
return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (resurrect ? stack->baseAmount * stack->MaxHealth() : 0));
|
||||||
}
|
}
|
||||||
@ -746,7 +745,7 @@ std::vector<ui32> BattleInfo::calculateResistedStacks(const CSpell * sp, const C
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sp->id == 60) //hypnotize
|
if(sp->id == Spells::HYPNOTIZE) //hypnotize
|
||||||
{
|
{
|
||||||
for(auto it = affectedCreatures.begin(); it != affectedCreatures.end(); ++it)
|
for(auto it = affectedCreatures.begin(); it != affectedCreatures.end(); ++it)
|
||||||
{
|
{
|
||||||
@ -931,163 +930,17 @@ si32 CStack::magicResistance() const
|
|||||||
|
|
||||||
void CStack::stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse)
|
void CStack::stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse)
|
||||||
{
|
{
|
||||||
//TODO: get rid of this spaghetti code
|
|
||||||
|
|
||||||
const CSpell * sp = VLC->spellh->spells[sse.sid];
|
const CSpell * sp = VLC->spellh->spells[sse.sid];
|
||||||
si32 power = sp->powers[sse.val];
|
|
||||||
|
|
||||||
auto addFull = [&](Bonus::BonusType type, si16 subtype, si32 value, si32 additionalInfo, si32 limit)
|
std::vector<Bonus> tmp;
|
||||||
{
|
sp->getEffects(tmp, sse.val);
|
||||||
sf.push_back(featureGenerator(type, subtype, value, sse.turnsRemain,additionalInfo, limit));
|
|
||||||
sf.back().sid = sse.sid;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto add = [&](Bonus::BonusType type, si16 subtype, si32 value)
|
|
||||||
{
|
|
||||||
addFull(type, subtype, value, 0, Bonus::NO_LIMIT);
|
|
||||||
};
|
|
||||||
|
|
||||||
auto addVT = [&](Bonus::BonusType type, si16 subtype, si32 value, ui8 valType)
|
BOOST_FOREACH(Bonus& b, tmp)
|
||||||
{
|
{
|
||||||
add(type, subtype, value);
|
b.turnsRemain = sse.turnsRemain;
|
||||||
sf.back().valType = valType;
|
sf.push_back(b);
|
||||||
};
|
|
||||||
|
|
||||||
auto addVTFull = [&](Bonus::BonusType type, si16 subtype, si32 value, ui8 valType,si32 additionalInfo)
|
|
||||||
{
|
|
||||||
addFull(type, subtype, value, additionalInfo, Bonus::NO_LIMIT);
|
|
||||||
sf.back().valType = valType;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto addDur = [&](Bonus::BonusType type, si16 subtype, si32 value, ui8 duration)
|
|
||||||
{
|
|
||||||
add(type, subtype, value);
|
|
||||||
sf.back().duration = duration;
|
|
||||||
};
|
|
||||||
|
|
||||||
switch(sse.sid)
|
|
||||||
{
|
|
||||||
case Spells::SHIELD:
|
|
||||||
add(Bonus::GENERAL_DAMAGE_REDUCTION, 0, power);
|
|
||||||
break;
|
|
||||||
case Spells::AIR_SHIELD:
|
|
||||||
add(Bonus::GENERAL_DAMAGE_REDUCTION, 1, power);
|
|
||||||
break;
|
|
||||||
case Spells::FIRE_SHIELD:
|
|
||||||
add(Bonus::FIRE_SHIELD, 0, power);
|
|
||||||
break;
|
|
||||||
case Spells::PROTECTION_FROM_AIR:
|
|
||||||
add(Bonus::SPELL_DAMAGE_REDUCTION, 0, power);
|
|
||||||
break;
|
|
||||||
case Spells::PROTECTION_FROM_FIRE:
|
|
||||||
add(Bonus::SPELL_DAMAGE_REDUCTION, 1, power);
|
|
||||||
break;
|
|
||||||
case Spells::PROTECTION_FROM_WATER:
|
|
||||||
add(Bonus::SPELL_DAMAGE_REDUCTION, 2, power);
|
|
||||||
break;
|
|
||||||
case Spells::PROTECTION_FROM_EARTH:
|
|
||||||
add(Bonus::SPELL_DAMAGE_REDUCTION, 3, power);
|
|
||||||
break;
|
|
||||||
case Spells::ANTI_MAGIC:
|
|
||||||
addVT(Bonus::LEVEL_SPELL_IMMUNITY, GameConstants::SPELL_LEVELS, power - 1, Bonus::INDEPENDENT_MAX);break;
|
|
||||||
case Spells::MAGIC_MIRROR:
|
|
||||||
addVT(Bonus::MAGIC_MIRROR, -1, power, Bonus::INDEPENDENT_MAX);
|
|
||||||
break;
|
|
||||||
case Spells::BLESS:
|
|
||||||
addVT(Bonus::ALWAYS_MAXIMUM_DAMAGE, -1, power,Bonus::INDEPENDENT_MAX);
|
|
||||||
break;
|
|
||||||
case Spells::CURSE:
|
|
||||||
addVTFull(Bonus::ALWAYS_MINIMUM_DAMAGE, -1, power, Bonus::INDEPENDENT_MAX, sse.val >= 2 ? 20 : 0);
|
|
||||||
break;
|
|
||||||
case Spells::BLOODLUST:
|
|
||||||
addFull(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, 0, Bonus::ONLY_MELEE_FIGHT);
|
|
||||||
break;
|
|
||||||
case Spells::PRECISION:
|
|
||||||
addFull(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, 0, Bonus::ONLY_DISTANCE_FIGHT);
|
|
||||||
break;
|
|
||||||
case Spells::WEAKNESS:
|
|
||||||
add(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, -1 * power);
|
|
||||||
break;
|
|
||||||
case Spells::STONE_SKIN:
|
|
||||||
add(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, power);
|
|
||||||
break;
|
|
||||||
case Spells::DISRUPTING_RAY:
|
|
||||||
addVT(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, -1 * power,Bonus::ADDITIVE_VALUE);
|
|
||||||
break;
|
|
||||||
case Spells::PRAYER:
|
|
||||||
add(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power);
|
|
||||||
add(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, power);
|
|
||||||
add(Bonus::STACKS_SPEED, 0, power);
|
|
||||||
break;
|
|
||||||
case Spells::MIRTH:
|
|
||||||
add(Bonus::MORALE, 0, power);
|
|
||||||
break;
|
|
||||||
case Spells::SORROW:
|
|
||||||
add(Bonus::MORALE, 0, -1 * power);
|
|
||||||
break;
|
|
||||||
case Spells::FORTUNE:
|
|
||||||
add(Bonus::LUCK, 0, power);
|
|
||||||
break;
|
|
||||||
case Spells::MISFORTUNE:
|
|
||||||
add(Bonus::LUCK, 0, -1 * power);
|
|
||||||
break;
|
|
||||||
case Spells::HASTE: //haste
|
|
||||||
add(Bonus::STACKS_SPEED, 0, power);
|
|
||||||
break;
|
|
||||||
case Spells::SLOW:
|
|
||||||
addVT(Bonus::STACKS_SPEED, 0, -1 * ( 100 - power ),Bonus::PERCENT_TO_ALL);
|
|
||||||
break;
|
|
||||||
case Spells::SLAYER:
|
|
||||||
add(Bonus::SLAYER, 0, sse.val);
|
|
||||||
break;
|
|
||||||
case Spells::FRENZY:
|
|
||||||
add(Bonus::IN_FRENZY, 0, power/100.0);
|
|
||||||
break;
|
|
||||||
case Spells::COUNTERSTRIKE:
|
|
||||||
add(Bonus::ADDITIONAL_RETALIATION, 0, power);
|
|
||||||
break;
|
|
||||||
case Spells::BERSERK:
|
|
||||||
add(Bonus::ATTACKS_NEAREST_CREATURE, 0, sse.val);
|
|
||||||
break;
|
|
||||||
case Spells::HYPNOTIZE:
|
|
||||||
add(Bonus::HYPNOTIZED, 0, sse.val);
|
|
||||||
break;
|
|
||||||
case Spells::FORGETFULNESS:
|
|
||||||
add(Bonus::FORGETFULL, 0, sse.val);
|
|
||||||
break;
|
|
||||||
case Spells::BLIND:
|
|
||||||
addDur(Bonus::NOT_ACTIVE, sse.sid, 0, Bonus::UNITL_BEING_ATTACKED | Bonus::N_TURNS);
|
|
||||||
addDur(Bonus::GENERAL_ATTACK_REDUCTION, 0, power, Bonus::UNITL_BEING_ATTACKED | Bonus::N_TURNS);
|
|
||||||
addDur(Bonus::NO_RETALIATION,0,0, Bonus::UNITL_BEING_ATTACKED);
|
|
||||||
break;
|
|
||||||
case Spells::STONE_GAZE:
|
|
||||||
case Spells::PARALYZE:
|
|
||||||
addDur(Bonus::NOT_ACTIVE, sse.sid, 0, Bonus::UNITL_BEING_ATTACKED | Bonus::N_TURNS);
|
|
||||||
addDur(Bonus::NO_RETALIATION,0,0, Bonus::UNITL_BEING_ATTACKED);
|
|
||||||
break;
|
|
||||||
case Spells::POISON: //Poison
|
|
||||||
addVT(Bonus::POISON, 0, 30,Bonus::INDEPENDENT_MAX); //max hp penalty from this source
|
|
||||||
addVT(Bonus::STACK_HEALTH, 0, -10, Bonus::PERCENT_TO_ALL);
|
|
||||||
break;
|
|
||||||
case Spells::BIND:
|
|
||||||
sf.push_back(featureGenerator(Bonus::BIND_EFFECT, 0, 0, 1)); //marker
|
|
||||||
sf.back().duration = Bonus::PERMANENT;
|
|
||||||
sf.back().sid = sse.sid;
|
|
||||||
break;
|
|
||||||
case Spells::DISEASE:
|
|
||||||
add(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, -2);
|
|
||||||
add(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, -2);
|
|
||||||
break;
|
|
||||||
case Spells::AGE:
|
|
||||||
addVT(Bonus::STACK_HEALTH, 0, -50, Bonus::PERCENT_TO_ALL);
|
|
||||||
break;
|
|
||||||
case Spells::ACID_BREATH_DEFENSE:
|
|
||||||
sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, -sse.turnsRemain, 1));
|
|
||||||
sf.back().sid = sse.sid;
|
|
||||||
sf.back().duration = Bonus::PERMANENT;
|
|
||||||
sf.back().valType = Bonus::ADDITIVE_VALUE;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CStack::willMove(int turn /*= 0*/) const
|
bool CStack::willMove(int turn /*= 0*/) const
|
||||||
|
@ -182,13 +182,6 @@ public:
|
|||||||
return hb;
|
return hb;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline Bonus featureGeneratorVT(Bonus::BonusType type, si16 subtype, si32 value, ui16 turnsRemain, ui8 valType)
|
|
||||||
{
|
|
||||||
Bonus ret = makeFeatureVal(type, Bonus::N_TURNS, subtype, value, Bonus::SPELL_EFFECT, turnsRemain);
|
|
||||||
ret.valType = valType;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isMeleeAttackPossible(const CStack * attacker, const CStack * defender, BattleHex attackerPos = BattleHex::INVALID, BattleHex defenderPos = BattleHex::INVALID);
|
static bool isMeleeAttackPossible(const CStack * attacker, const CStack * defender, BattleHex attackerPos = BattleHex::INVALID, BattleHex defenderPos = BattleHex::INVALID);
|
||||||
|
|
||||||
bool doubleWide() const;
|
bool doubleWide() const;
|
||||||
|
@ -835,7 +835,7 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c
|
|||||||
if(const Bonus *slayerEffect = info.attackerBonuses->getEffect(Spells::SLAYER)) //slayer handling
|
if(const Bonus *slayerEffect = info.attackerBonuses->getEffect(Spells::SLAYER)) //slayer handling
|
||||||
{
|
{
|
||||||
std::vector<int> affectedIds;
|
std::vector<int> affectedIds;
|
||||||
int spLevel = slayerEffect->val;
|
int spLevel = slayerEffect->val;
|
||||||
|
|
||||||
for(int g = 0; g < VLC->creh->creatures.size(); ++g)
|
for(int g = 0; g < VLC->creh->creatures.size(); ++g)
|
||||||
{
|
{
|
||||||
@ -1487,7 +1487,6 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
|
|||||||
{
|
{
|
||||||
RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID);
|
RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID);
|
||||||
|
|
||||||
|
|
||||||
// Get stack at destination hex -> subject of our spell.
|
// Get stack at destination hex -> subject of our spell.
|
||||||
const CStack * subject = battleGetStackByPos(dest, !spell->isRisingSpell()); //only alive if not rising spell
|
const CStack * subject = battleGetStackByPos(dest, !spell->isRisingSpell()); //only alive if not rising spell
|
||||||
|
|
||||||
@ -1495,28 +1494,14 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
|
|||||||
{
|
{
|
||||||
if (spell->isPositive() && subject->hasBonusOfType(Bonus::RECEPTIVE)) //accept all positive spells
|
if (spell->isPositive() && subject->hasBonusOfType(Bonus::RECEPTIVE)) //accept all positive spells
|
||||||
return ESpellCastProblem::OK;
|
return ESpellCastProblem::OK;
|
||||||
|
|
||||||
|
if (spell->isImmuneBy(subject)) //TODO: move all logic to spellhandler
|
||||||
|
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
||||||
|
|
||||||
switch (spell->id) //TODO: more general logic for new spells?
|
switch (spell->id) //TODO: more general logic for new spells?
|
||||||
{
|
{
|
||||||
case Spells::DESTROY_UNDEAD:
|
|
||||||
if (!subject->hasBonusOfType(Bonus::UNDEAD))
|
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
|
||||||
break;
|
|
||||||
case Spells::DEATH_RIPPLE:
|
|
||||||
if (subject->hasBonusOfType(Bonus::SIEGE_WEAPON))
|
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; //don't break here - undeads and war machines are immune, non-living are not
|
|
||||||
case Spells::BLESS:
|
|
||||||
case Spells::CURSE: //undeads are immune to bless & curse
|
|
||||||
if (subject->hasBonusOfType(Bonus::UNDEAD))
|
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
|
||||||
break;
|
|
||||||
case Spells::HASTE:
|
|
||||||
case Spells::SLOW:
|
|
||||||
case Spells::TELEPORT:
|
|
||||||
case Spells::CLONE:
|
case Spells::CLONE:
|
||||||
if (subject->hasBonusOfType(Bonus::SIEGE_WEAPON))
|
if (caster) //TODO: how about stacks casting Clone?
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; //war machines are immune to some spells than involve movement
|
|
||||||
if (spell->id == Spells::CLONE && caster) //TODO: how about stacks casting Clone?
|
|
||||||
{
|
{
|
||||||
if (vstd::contains(subject->state, EBattleStackState::CLONED))
|
if (vstd::contains(subject->state, EBattleStackState::CLONED))
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; //can't clone already cloned creature
|
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; //can't clone already cloned creature
|
||||||
@ -1526,11 +1511,6 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
|
|||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Spells::FORGETFULNESS:
|
|
||||||
case Spells::PRECISION:
|
|
||||||
if (!subject->hasBonusOfType(Bonus::SHOOTER))
|
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
|
||||||
break;
|
|
||||||
case Spells::DISPEL_HELPFUL_SPELLS:
|
case Spells::DISPEL_HELPFUL_SPELLS:
|
||||||
{
|
{
|
||||||
TBonusListPtr spellBon = subject->getSpellBonuses();
|
TBonusListPtr spellBon = subject->getSpellBonuses();
|
||||||
@ -1551,69 +1531,12 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool damageSpell = spell->isDamageSpell();
|
|
||||||
auto battleTestElementalImmunity = [&](Bonus::BonusType element) -> bool //helper for battleisImmune
|
|
||||||
{
|
|
||||||
if (!spell->isPositive()) //negative or indifferent
|
|
||||||
{
|
|
||||||
if ((damageSpell && subject->hasBonusOfType(element, 2)) || subject->hasBonusOfType(element, 1))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (spell->isPositive()) //positive
|
|
||||||
{
|
|
||||||
if (subject->hasBonusOfType(element, 0)) //must be immune to all spells
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
if (damageSpell && subject->hasBonusOfType(Bonus::DIRECT_DAMAGE_IMMUNITY))
|
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
|
||||||
|
|
||||||
if (spell->fire)
|
|
||||||
{
|
|
||||||
if (battleTestElementalImmunity(Bonus::FIRE_IMMUNITY))
|
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
|
||||||
}
|
|
||||||
if (spell->water)
|
|
||||||
{
|
|
||||||
if (battleTestElementalImmunity(Bonus::WATER_IMMUNITY))
|
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
|
||||||
}
|
|
||||||
if (spell->earth)
|
|
||||||
{
|
|
||||||
if (battleTestElementalImmunity(Bonus::EARTH_IMMUNITY))
|
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
|
||||||
}
|
|
||||||
if (spell->air)
|
|
||||||
{
|
|
||||||
if (battleTestElementalImmunity(Bonus::AIR_IMMUNITY))
|
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
|
||||||
}
|
|
||||||
if (spell->isMindSpell())
|
|
||||||
{
|
|
||||||
if (subject->hasBonusOfType(Bonus::MIND_IMMUNITY))
|
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spell->isRisingSpell())
|
if (spell->isRisingSpell())
|
||||||
{
|
{
|
||||||
if (subject->count >= subject->baseAmount) //TODO: calculate potential hp raised
|
if (subject->count >= subject->baseAmount) //TODO: calculate potential hp raised
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
||||||
}
|
}
|
||||||
|
|
||||||
TBonusListPtr immunities = subject->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY));
|
|
||||||
if(subject->hasBonusOfType(Bonus::NEGATE_ALL_NATURAL_IMMUNITIES))
|
|
||||||
{
|
|
||||||
immunities->remove_if([](const Bonus* b){ return b->source == Bonus::CREATURE_ABILITY; });
|
|
||||||
}
|
|
||||||
|
|
||||||
if(subject->hasBonusOfType(Bonus::SPELL_IMMUNITY, spell->id)
|
|
||||||
|| ( immunities->size() > 0 && immunities->totalValue() >= spell->level && spell->level))
|
|
||||||
{
|
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else //no target stack on this tile
|
else //no target stack on this tile
|
||||||
{
|
{
|
||||||
@ -1661,7 +1584,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(spell->id < 10) //it's adventure spell (not combat))
|
if(!spell->combatSpell)
|
||||||
return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL;
|
return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL;
|
||||||
|
|
||||||
//TODO?
|
//TODO?
|
||||||
|
@ -130,9 +130,10 @@ CSpellHandler::CSpellHandler()
|
|||||||
|
|
||||||
CSpell::CSpell()
|
CSpell::CSpell()
|
||||||
{
|
{
|
||||||
_isDamage = false;
|
isDamage = false;
|
||||||
_isMind = false;
|
isMind = false;
|
||||||
_isRising = false;
|
isRising = false;
|
||||||
|
isOffensive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
|
std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
|
||||||
@ -262,29 +263,118 @@ bool CSpell::isNegative() const
|
|||||||
|
|
||||||
bool CSpell::isRisingSpell() const
|
bool CSpell::isRisingSpell() const
|
||||||
{
|
{
|
||||||
return _isRising;
|
return isRising;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSpell::isDamageSpell() const
|
bool CSpell::isDamageSpell() const
|
||||||
{
|
{
|
||||||
return _isDamage;
|
return isDamage;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSpell::isMindSpell() const
|
bool CSpell::isMindSpell() const
|
||||||
{
|
{
|
||||||
return _isMind;
|
return isMind;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSpell::getEffects(std::vector<Bonus>& lst) const
|
bool CSpell::isOffensiveSpell() const
|
||||||
{
|
{
|
||||||
lst.reserve(lst.size() + _effects.size());
|
return isOffensive;
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_FOREACH (Bonus b, _effects)
|
bool CSpell::hasEffects() const
|
||||||
|
{
|
||||||
|
return !effects[0].empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CSpell::getEffects(std::vector<Bonus>& lst, const int level) const
|
||||||
|
{
|
||||||
|
if (level < 0 || level>3)
|
||||||
{
|
{
|
||||||
|
tlog1 << __FUNCTION__ << " invalid school level " << level;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lst.reserve(lst.size() + effects[level].size());
|
||||||
|
|
||||||
|
BOOST_FOREACH (Bonus b, effects[level])
|
||||||
|
{
|
||||||
|
//TODO: value, add value
|
||||||
lst.push_back(b);
|
lst.push_back(b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CSpell::isImmuneBy(const IBonusBearer* obj) const
|
||||||
|
{
|
||||||
|
BOOST_FOREACH(auto b, limiters)
|
||||||
|
{
|
||||||
|
if (!obj->hasBonusOfType(b))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FOREACH(auto b, immunities)
|
||||||
|
{
|
||||||
|
if (obj->hasBonusOfType(b))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMindSpell() && obj->hasBonusOfType(Bonus::MIND_IMMUNITY))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (isDamageSpell() && obj->hasBonusOfType(Bonus::DIRECT_DAMAGE_IMMUNITY))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
auto battleTestElementalImmunity = [&,this](Bonus::BonusType element) -> bool
|
||||||
|
{
|
||||||
|
if (!isPositive()) //negative or indifferent
|
||||||
|
{
|
||||||
|
if ((isDamageSpell() && obj->hasBonusOfType(element, 2)) || obj->hasBonusOfType(element, 1))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (isPositive()) //positive
|
||||||
|
{
|
||||||
|
if (obj->hasBonusOfType(element, 0)) //must be immune to all spells
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (fire)
|
||||||
|
{
|
||||||
|
if (battleTestElementalImmunity(Bonus::FIRE_IMMUNITY))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (water)
|
||||||
|
{
|
||||||
|
if (battleTestElementalImmunity(Bonus::WATER_IMMUNITY))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (earth)
|
||||||
|
{
|
||||||
|
if (battleTestElementalImmunity(Bonus::EARTH_IMMUNITY))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (air)
|
||||||
|
{
|
||||||
|
if (battleTestElementalImmunity(Bonus::AIR_IMMUNITY))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TBonusListPtr immunities = obj->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY));
|
||||||
|
if(obj->hasBonusOfType(Bonus::NEGATE_ALL_NATURAL_IMMUNITIES))
|
||||||
|
{
|
||||||
|
immunities->remove_if([](const Bonus* b){ return b->source == Bonus::CREATURE_ABILITY; });
|
||||||
|
}
|
||||||
|
|
||||||
|
if(obj->hasBonusOfType(Bonus::SPELL_IMMUNITY, id)
|
||||||
|
|| ( immunities->size() > 0 && immunities->totalValue() >= level && level))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool DLL_LINKAGE isInScreenRange(const int3 ¢er, const int3 &pos)
|
bool DLL_LINKAGE isInScreenRange(const int3 ¢er, const int3 &pos)
|
||||||
{
|
{
|
||||||
@ -329,44 +419,31 @@ void CSpellHandler::loadSpells()
|
|||||||
{
|
{
|
||||||
CLegacyConfigParser parser("DATA/SPTRAITS.TXT");
|
CLegacyConfigParser parser("DATA/SPTRAITS.TXT");
|
||||||
|
|
||||||
for(int i=0; i<5; i++) // header
|
auto read = [&,this](bool combat, bool alility)
|
||||||
parser.endLine();
|
|
||||||
|
|
||||||
do //read adventure map spells
|
|
||||||
{
|
{
|
||||||
CSpell * spell = loadSpell(parser);
|
do
|
||||||
spell->id = spells.size();
|
{
|
||||||
spell->combatSpell = false;
|
CSpell * spell = loadSpell(parser);
|
||||||
spell->creatureAbility = false;
|
spell->id = spells.size();
|
||||||
spells.push_back(spell);
|
spell->combatSpell = combat;
|
||||||
}
|
spell->creatureAbility = alility;
|
||||||
while (parser.endLine() && !parser.isNextEntryEmpty());
|
spells.push_back(spell);
|
||||||
|
}
|
||||||
|
while (parser.endLine() && !parser.isNextEntryEmpty());
|
||||||
|
};
|
||||||
|
|
||||||
for(int i=0; i<3; i++)
|
auto skip = [&](int cnt)
|
||||||
parser.endLine();
|
|
||||||
|
|
||||||
do //read battle spells
|
|
||||||
{
|
{
|
||||||
CSpell * spell = loadSpell(parser);
|
for(int i=0; i<cnt; i++)
|
||||||
spell->id = spells.size();
|
parser.endLine();
|
||||||
spell->combatSpell = true;
|
};
|
||||||
spell->creatureAbility = false;
|
|
||||||
spells.push_back(spell);
|
|
||||||
}
|
|
||||||
while (parser.endLine() && !parser.isNextEntryEmpty());
|
|
||||||
|
|
||||||
for(int i=0; i<3; i++)
|
skip(5);// header
|
||||||
parser.endLine();
|
read(false,false); //read adventure map spells
|
||||||
|
skip(3);
|
||||||
do //read creature abilities
|
read(true,false); //read battle spells
|
||||||
{
|
skip(3);
|
||||||
CSpell * spell = loadSpell(parser);
|
read(true,true);//read creature abilities
|
||||||
spell->id = spells.size();
|
|
||||||
spell->combatSpell = true;
|
|
||||||
spell->creatureAbility = true;
|
|
||||||
spells.push_back(spell);
|
|
||||||
}
|
|
||||||
while (parser.endLine() && !parser.isNextEntryEmpty());
|
|
||||||
|
|
||||||
boost::replace_first (spells[Spells::DISRUPTING_RAY]->attributes, "2", ""); // disrupting ray will now affect single creature
|
boost::replace_first (spells[Spells::DISRUPTING_RAY]->attributes, "2", ""); // disrupting ray will now affect single creature
|
||||||
|
|
||||||
@ -404,35 +481,79 @@ void CSpellHandler::loadSpells()
|
|||||||
{
|
{
|
||||||
if (flag == "damage")
|
if (flag == "damage")
|
||||||
{
|
{
|
||||||
s->_isDamage = true;
|
s->isDamage = true;
|
||||||
}
|
}
|
||||||
else if (flag == "rising")
|
else if (flag == "rising")
|
||||||
{
|
{
|
||||||
s->_isRising = true;
|
s->isRising = true;
|
||||||
}
|
}
|
||||||
else if (flag == "mind")
|
else if (flag == "mind")
|
||||||
{
|
{
|
||||||
s->_isMind = true;
|
s->isMind = true;
|
||||||
|
}
|
||||||
|
else if (flag == "offensive")
|
||||||
|
{
|
||||||
|
s->isOffensive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const JsonNode & effects_node = spell.second["effects"];
|
const JsonNode & effects_node = spell.second["effects"];
|
||||||
|
|
||||||
if (!effects_node.isNull())
|
BOOST_FOREACH (const JsonNode & bonus_node, effects_node.Vector())
|
||||||
{
|
{
|
||||||
BOOST_FOREACH (const JsonNode & bonus_node, effects_node.Vector())
|
auto &v_node = bonus_node["values"];
|
||||||
|
auto &a_node = bonus_node["ainfos"];
|
||||||
|
|
||||||
|
auto v = v_node.convertTo<std::vector<int> >();
|
||||||
|
auto a = a_node.convertTo<std::vector<int> >();
|
||||||
|
|
||||||
|
for (int i=0; i<4 ; i++)
|
||||||
{
|
{
|
||||||
Bonus * b = JsonUtils::parseBonus(bonus_node);
|
Bonus * b = JsonUtils::parseBonus(bonus_node);
|
||||||
b->sid = s->id;
|
b->sid = s->id; //for all
|
||||||
b->source = Bonus::SPELL_EFFECT;
|
b->source = Bonus::SPELL_EFFECT;//for all
|
||||||
b->duration = Bonus::N_TURNS; //default
|
b->val = s->powers[i];
|
||||||
//TODO: make duration configurable
|
|
||||||
s->_effects.push_back(*b);
|
if (!v.empty())
|
||||||
|
b->val = v[i];
|
||||||
|
if (!a.empty())
|
||||||
|
b->additionalInfo = a[i];
|
||||||
|
|
||||||
|
s->effects[i].push_back(*b);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
auto find_in_map = [](std::string name, std::vector<Bonus::BonusType> &vec)
|
||||||
|
{
|
||||||
|
auto it = bonusNameMap.find(name);
|
||||||
|
if (it == bonusNameMap.end())
|
||||||
|
{
|
||||||
|
tlog1 << "Error: invalid bonus name" << name << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vec.push_back((Bonus::BonusType)it->second);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto read_node = [&](std::string name, std::vector<Bonus::BonusType> &vec)
|
||||||
|
{
|
||||||
|
const JsonNode & node = spell.second[name];
|
||||||
|
|
||||||
|
if (!node.isNull())
|
||||||
|
{
|
||||||
|
auto names = node.convertTo<std::vector<std::string> >();
|
||||||
|
BOOST_FOREACH(auto name, names)
|
||||||
|
find_in_map(name, vec);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
read_node("immunity",s->immunities);
|
||||||
|
read_node("limit",s->limiters);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//spell fixes
|
//spell fixes
|
||||||
|
@ -56,26 +56,33 @@ public:
|
|||||||
|
|
||||||
bool isRisingSpell() const;
|
bool isRisingSpell() const;
|
||||||
bool isDamageSpell() const;
|
bool isDamageSpell() const;
|
||||||
bool isMindSpell() const;
|
bool isMindSpell() const;
|
||||||
|
bool isOffensiveSpell() const;
|
||||||
|
|
||||||
|
bool hasEffects() const;
|
||||||
|
void getEffects(std::vector<Bonus> &lst, const int level) const;
|
||||||
|
|
||||||
|
bool isImmuneBy(const IBonusBearer *obj) const;
|
||||||
void getEffects(std::vector<Bonus> & lst) const;
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
h & identifier & id & name & abbName & descriptions & level & earth & water & fire & air & power & costs
|
h & identifier & id & name & abbName & descriptions & level & earth & water & fire & air & power & costs
|
||||||
& powers & probabilities & AIVals & attributes & combatSpell & creatureAbility & positiveness & range & counteredSpells & mainEffectAnim;
|
& powers & probabilities & AIVals & attributes & combatSpell & creatureAbility & positiveness & range & counteredSpells & mainEffectAnim;
|
||||||
h & _isRising & _isDamage & _isMind;
|
h & isRising & isDamage & isMind;
|
||||||
h & _effects;
|
h & effects & immunities & limiters;
|
||||||
}
|
}
|
||||||
friend class CSpellHandler;
|
friend class CSpellHandler;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _isRising;
|
bool isRising;
|
||||||
bool _isDamage;
|
bool isDamage;
|
||||||
bool _isMind;
|
bool isMind;
|
||||||
|
bool isOffensive;
|
||||||
|
|
||||||
std::vector<Bonus> _effects;
|
std::vector<Bonus> effects [4];
|
||||||
|
std::vector<Bonus::BonusType> immunities; //any of these hrants immunity
|
||||||
|
std::vector<Bonus::BonusType> limiters; //all of them are required
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -253,7 +253,10 @@ EResType::Type EResTypeHelper::getTypeFromExtension(std::string extension)
|
|||||||
(".PAL", EResType::PALETTE)
|
(".PAL", EResType::PALETTE)
|
||||||
(".VCGM1", EResType::CLIENT_SAVEGAME)
|
(".VCGM1", EResType::CLIENT_SAVEGAME)
|
||||||
(".VLGM1", EResType::LIB_SAVEGAME)
|
(".VLGM1", EResType::LIB_SAVEGAME)
|
||||||
(".VSGM1", EResType::SERVER_SAVEGAME);
|
(".VSGM1", EResType::SERVER_SAVEGAME)
|
||||||
|
(".ERM", EResType::ERM)
|
||||||
|
(".ERT", EResType::ERT)
|
||||||
|
(".ERS", EResType::ERS);
|
||||||
|
|
||||||
auto iter = stringToRes.find(extension);
|
auto iter = stringToRes.find(extension);
|
||||||
if (iter == stringToRes.end())
|
if (iter == stringToRes.end())
|
||||||
@ -285,6 +288,9 @@ std::string EResTypeHelper::getEResTypeAsString(EResType::Type type)
|
|||||||
MAP_ENUM(LIB_SAVEGAME)
|
MAP_ENUM(LIB_SAVEGAME)
|
||||||
MAP_ENUM(SERVER_SAVEGAME)
|
MAP_ENUM(SERVER_SAVEGAME)
|
||||||
MAP_ENUM(DIRECTORY)
|
MAP_ENUM(DIRECTORY)
|
||||||
|
MAP_ENUM(ERM)
|
||||||
|
MAP_ENUM(ERT)
|
||||||
|
MAP_ENUM(ERS)
|
||||||
MAP_ENUM(OTHER);
|
MAP_ENUM(OTHER);
|
||||||
|
|
||||||
#undef MAP_ENUM
|
#undef MAP_ENUM
|
||||||
|
@ -60,6 +60,9 @@ namespace EResType
|
|||||||
LIB_SAVEGAME,
|
LIB_SAVEGAME,
|
||||||
SERVER_SAVEGAME,
|
SERVER_SAVEGAME,
|
||||||
DIRECTORY,
|
DIRECTORY,
|
||||||
|
ERM,
|
||||||
|
ERT,
|
||||||
|
ERS,
|
||||||
OTHER
|
OTHER
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -418,9 +418,9 @@ si32 IBonusBearer::Attack() const
|
|||||||
{
|
{
|
||||||
si32 ret = valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK);
|
si32 ret = valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK);
|
||||||
|
|
||||||
if (int frenzyPower = valOfBonuses(Bonus::IN_FRENZY)) //frenzy for attacker
|
if (double frenzyPower = valOfBonuses(Bonus::IN_FRENZY)) //frenzy for attacker
|
||||||
{
|
{
|
||||||
ret += frenzyPower * Defense(false);
|
ret += (frenzyPower/100) * (double)Defense(false);
|
||||||
}
|
}
|
||||||
vstd::amax(ret, 0);
|
vstd::amax(ret, 0);
|
||||||
|
|
||||||
|
@ -903,6 +903,25 @@ Bonus * JsonUtils::parseBonus (const JsonVector &ability_vec) //TODO: merge with
|
|||||||
b->turnsRemain = 0;
|
b->turnsRemain = 0;
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T & parseByMapStr(const std::map<std::string, T> & map, const std::string & val, std::string err)
|
||||||
|
{
|
||||||
|
static T defaultValue;
|
||||||
|
|
||||||
|
auto it = map.find(val);
|
||||||
|
if (it == map.end())
|
||||||
|
{
|
||||||
|
tlog1 << "Error: invalid " << err << val << std::endl;
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const T & parseByMap(const std::map<std::string, T> & map, const JsonNode * val, std::string err)
|
const T & parseByMap(const std::map<std::string, T> & map, const JsonNode * val, std::string err)
|
||||||
{
|
{
|
||||||
@ -910,20 +929,11 @@ const T & parseByMap(const std::map<std::string, T> & map, const JsonNode * val,
|
|||||||
if (!val->isNull())
|
if (!val->isNull())
|
||||||
{
|
{
|
||||||
std::string type = val->String();
|
std::string type = val->String();
|
||||||
auto it = map.find(type);
|
return parseByMapStr(map, type, err);
|
||||||
if (it == map.end())
|
|
||||||
{
|
|
||||||
tlog1 << "Error: invalid " << err << type << std::endl;
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
};
|
}
|
||||||
|
|
||||||
void JsonUtils::resolveIdentifier (si32 &var, const JsonNode &node, std::string name)
|
void JsonUtils::resolveIdentifier (si32 &var, const JsonNode &node, std::string name)
|
||||||
{
|
{
|
||||||
@ -937,9 +947,9 @@ void JsonUtils::resolveIdentifier (si32 &var, const JsonNode &node, std::string
|
|||||||
var = value->Float();
|
var = value->Float();
|
||||||
break;
|
break;
|
||||||
case JsonNode::DATA_STRING:
|
case JsonNode::DATA_STRING:
|
||||||
VLC->modh->identifiers.requestIdentifier (value->String(), [&](si32 identifier)
|
VLC->modh->identifiers.requestIdentifier (value->String(), [&](si32 identifier)
|
||||||
{
|
{
|
||||||
var = identifier;
|
var = identifier;
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -994,7 +1004,18 @@ Bonus * JsonUtils::parseBonus (const JsonNode &ability)
|
|||||||
b->valType = parseByMap(bonusLimitEffect, value, "effect range ");
|
b->valType = parseByMap(bonusLimitEffect, value, "effect range ");
|
||||||
value = &ability["duration"];
|
value = &ability["duration"];
|
||||||
if (!value->isNull())
|
if (!value->isNull())
|
||||||
b->valType = parseByMap(bonusDurationMap, value, "duration type ");
|
{
|
||||||
|
int dur = 0;
|
||||||
|
std::vector<std::string> strs;
|
||||||
|
boost::split(strs, value->String(), boost::is_any_of("\t "));
|
||||||
|
BOOST_FOREACH(auto s, strs)
|
||||||
|
{
|
||||||
|
dur |=parseByMapStr(bonusDurationMap, s, "duration type ");
|
||||||
|
}
|
||||||
|
|
||||||
|
b->duration = dur;
|
||||||
|
}
|
||||||
|
|
||||||
value = &ability["source"];
|
value = &ability["source"];
|
||||||
if (!value->isNull())
|
if (!value->isNull())
|
||||||
b->valType = parseByMap(bonusSourceMap, value, "source type ");
|
b->valType = parseByMap(bonusSourceMap, value, "source type ");
|
||||||
|
@ -3994,58 +3994,11 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
|
|||||||
vstd::amin(sc.dmgToDisplay, (*attackedCres.begin())->count); //stack is already reduced after attack
|
vstd::amin(sc.dmgToDisplay, (*attackedCres.begin())->count); //stack is already reduced after attack
|
||||||
}
|
}
|
||||||
StacksInjured si;
|
StacksInjured si;
|
||||||
|
|
||||||
//applying effects
|
//applying effects
|
||||||
switch (spellID)
|
|
||||||
|
if (spell->isOffensiveSpell())
|
||||||
{
|
{
|
||||||
case Spells::QUICKSAND:
|
|
||||||
case Spells::LAND_MINE:
|
|
||||||
{
|
|
||||||
std::vector<BattleHex> availableTiles;
|
|
||||||
for(int i = 0; i < GameConstants::BFIELD_SIZE; i += 1)
|
|
||||||
{
|
|
||||||
BattleHex hex = i;
|
|
||||||
if(hex.getX() > 2 && hex.getX() < 14 && !battleGetStackByPos(hex, false) & !battleGetObstacleOnPos(hex, false))
|
|
||||||
availableTiles.push_back(hex);
|
|
||||||
}
|
|
||||||
boost::range::random_shuffle(availableTiles);
|
|
||||||
|
|
||||||
const int patchesForSkill[] = {4, 4, 6, 8};
|
|
||||||
const int patchesToPut = std::min<int>(patchesForSkill[spellLvl], availableTiles.size());
|
|
||||||
|
|
||||||
//land mines or quicksand patches are handled as spell created obstacles
|
|
||||||
for (int i = 0; i < patchesToPut; i++)
|
|
||||||
placeObstacle(availableTiles[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Spells::FORCE_FIELD:
|
|
||||||
placeObstacle(destination);
|
|
||||||
break;
|
|
||||||
case Spells::FIRE_WALL:
|
|
||||||
{
|
|
||||||
//fire wall is build from multiple obstacles - one fire piece for each affected hex
|
|
||||||
auto affectedHexes = spell->rangeInHexes(destination, spellLvl, casterSide);
|
|
||||||
BOOST_FOREACH(BattleHex hex, affectedHexes)
|
|
||||||
placeObstacle(hex);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
//damage spells
|
|
||||||
case Spells::MAGIC_ARROW:
|
|
||||||
case Spells::ICE_BOLT:
|
|
||||||
case Spells::LIGHTNING_BOLT:
|
|
||||||
case Spells::IMPLOSION:
|
|
||||||
case Spells::CHAIN_LIGHTNING:
|
|
||||||
case Spells::FROST_RING:
|
|
||||||
case Spells::FIREBALL:
|
|
||||||
case Spells::INFERNO:
|
|
||||||
case Spells::METEOR_SHOWER:
|
|
||||||
case Spells::DEATH_RIPPLE:
|
|
||||||
case Spells::DESTROY_UNDEAD:
|
|
||||||
case Spells::ARMAGEDDON:
|
|
||||||
case Spells::TITANS_LIGHTNING_BOLT:
|
|
||||||
case Spells::THUNDERBOLT: //(thunderbirds)
|
|
||||||
{
|
|
||||||
int spellDamage = 0;
|
int spellDamage = 0;
|
||||||
if (stack && mode != ECastingMode::MAGIC_MIRROR)
|
if (stack && mode != ECastingMode::MAGIC_MIRROR)
|
||||||
{
|
{
|
||||||
@ -4088,48 +4041,10 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
|
|||||||
|
|
||||||
if (spellID == Spells::CHAIN_LIGHTNING)
|
if (spellID == Spells::CHAIN_LIGHTNING)
|
||||||
++chainLightningModifier;
|
++chainLightningModifier;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
}
|
else if (spell->hasEffects())
|
||||||
// permanent effects
|
{
|
||||||
case Spells::SHIELD:
|
|
||||||
case Spells::AIR_SHIELD:
|
|
||||||
case Spells::FIRE_SHIELD:
|
|
||||||
case Spells::PROTECTION_FROM_AIR:
|
|
||||||
case Spells::PROTECTION_FROM_FIRE:
|
|
||||||
case Spells::PROTECTION_FROM_WATER:
|
|
||||||
case Spells::PROTECTION_FROM_EARTH:
|
|
||||||
case Spells::ANTI_MAGIC:
|
|
||||||
case Spells::MAGIC_MIRROR:
|
|
||||||
case Spells::BLESS:
|
|
||||||
case Spells::CURSE:
|
|
||||||
case Spells::BLOODLUST:
|
|
||||||
case Spells::PRECISION:
|
|
||||||
case Spells::WEAKNESS:
|
|
||||||
case Spells::STONE_SKIN:
|
|
||||||
case Spells::DISRUPTING_RAY:
|
|
||||||
case Spells::PRAYER:
|
|
||||||
case Spells::MIRTH:
|
|
||||||
case Spells::SORROW:
|
|
||||||
case Spells::FORTUNE:
|
|
||||||
case Spells::MISFORTUNE:
|
|
||||||
case Spells::HASTE:
|
|
||||||
case Spells::SLOW:
|
|
||||||
case Spells::SLAYER:
|
|
||||||
case Spells::FRENZY:
|
|
||||||
case Spells::COUNTERSTRIKE:
|
|
||||||
case Spells::BERSERK:
|
|
||||||
case Spells::HYPNOTIZE:
|
|
||||||
case Spells::FORGETFULNESS:
|
|
||||||
case Spells::BLIND:
|
|
||||||
case Spells::STONE_GAZE:
|
|
||||||
case Spells::POISON:
|
|
||||||
case Spells::BIND:
|
|
||||||
case Spells::DISEASE:
|
|
||||||
case Spells::PARALYZE:
|
|
||||||
case Spells::AGE:
|
|
||||||
case Spells::ACID_BREATH_DEFENSE:
|
|
||||||
{
|
|
||||||
int stackSpellPower = 0;
|
int stackSpellPower = 0;
|
||||||
if (stack && mode != ECastingMode::MAGIC_MIRROR)
|
if (stack && mode != ECastingMode::MAGIC_MIRROR)
|
||||||
{
|
{
|
||||||
@ -4204,25 +4119,10 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
|
|||||||
|
|
||||||
if(!sse.stacks.empty())
|
if(!sse.stacks.empty())
|
||||||
sendAndApply(&sse);
|
sendAndApply(&sse);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case Spells::TELEPORT:
|
else if (spell->isRisingSpell() || spell->id == Spells::CURE)
|
||||||
{
|
{
|
||||||
BattleStackMoved bsm;
|
|
||||||
bsm.distance = -1;
|
|
||||||
bsm.stack = selectedStack;
|
|
||||||
std::vector<BattleHex> tiles;
|
|
||||||
tiles.push_back(destination);
|
|
||||||
bsm.tilesToMove = tiles;
|
|
||||||
bsm.teleporting = true;
|
|
||||||
sendAndApply(&bsm);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Spells::CURE:
|
|
||||||
case Spells::RESURRECTION:
|
|
||||||
case Spells::ANIMATE_DEAD:
|
|
||||||
case Spells::SACRIFICE:
|
|
||||||
{
|
|
||||||
int hpGained = 0;
|
int hpGained = 0;
|
||||||
if (stack)
|
if (stack)
|
||||||
{
|
{
|
||||||
@ -4264,7 +4164,53 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
|
|||||||
BattleStacksRemoved bsr;
|
BattleStacksRemoved bsr;
|
||||||
bsr.stackIDs.insert (selectedStack); //somehow it works for teleport?
|
bsr.stackIDs.insert (selectedStack); //somehow it works for teleport?
|
||||||
sendAndApply(&bsr);
|
sendAndApply(&bsr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
switch (spellID)
|
||||||
|
{
|
||||||
|
case Spells::QUICKSAND:
|
||||||
|
case Spells::LAND_MINE:
|
||||||
|
{
|
||||||
|
std::vector<BattleHex> availableTiles;
|
||||||
|
for(int i = 0; i < GameConstants::BFIELD_SIZE; i += 1)
|
||||||
|
{
|
||||||
|
BattleHex hex = i;
|
||||||
|
if(hex.getX() > 2 && hex.getX() < 14 && !battleGetStackByPos(hex, false) & !battleGetObstacleOnPos(hex, false))
|
||||||
|
availableTiles.push_back(hex);
|
||||||
}
|
}
|
||||||
|
boost::range::random_shuffle(availableTiles);
|
||||||
|
|
||||||
|
const int patchesForSkill[] = {4, 4, 6, 8};
|
||||||
|
const int patchesToPut = std::min<int>(patchesForSkill[spellLvl], availableTiles.size());
|
||||||
|
|
||||||
|
//land mines or quicksand patches are handled as spell created obstacles
|
||||||
|
for (int i = 0; i < patchesToPut; i++)
|
||||||
|
placeObstacle(availableTiles[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case Spells::FORCE_FIELD:
|
||||||
|
placeObstacle(destination);
|
||||||
|
break;
|
||||||
|
case Spells::FIRE_WALL:
|
||||||
|
{
|
||||||
|
//fire wall is build from multiple obstacles - one fire piece for each affected hex
|
||||||
|
auto affectedHexes = spell->rangeInHexes(destination, spellLvl, casterSide);
|
||||||
|
BOOST_FOREACH(BattleHex hex, affectedHexes)
|
||||||
|
placeObstacle(hex);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Spells::TELEPORT:
|
||||||
|
{
|
||||||
|
BattleStackMoved bsm;
|
||||||
|
bsm.distance = -1;
|
||||||
|
bsm.stack = selectedStack;
|
||||||
|
std::vector<BattleHex> tiles;
|
||||||
|
tiles.push_back(destination);
|
||||||
|
bsm.tilesToMove = tiles;
|
||||||
|
bsm.teleporting = true;
|
||||||
|
sendAndApply(&bsm);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Spells::SUMMON_FIRE_ELEMENTAL:
|
case Spells::SUMMON_FIRE_ELEMENTAL:
|
||||||
@ -5536,7 +5482,7 @@ bool CGameHandler::castSpell(const CGHeroInstance *h, int spellID, const int3 &p
|
|||||||
|
|
||||||
GiveBonus gb;
|
GiveBonus gb;
|
||||||
gb.id = h->id;
|
gb.id = h->id;
|
||||||
gb.bonus = Bonus(Bonus::ONE_DAY, Bonus::WATER_WALKING, Bonus::SPELL_EFFECT, 0, Spells::FLY, subtype);
|
gb.bonus = Bonus(Bonus::ONE_DAY, Bonus::WATER_WALKING, Bonus::SPELL_EFFECT, 0, Spells::WATER_WALK, subtype);
|
||||||
sendAndApply(&gb);
|
sendAndApply(&gb);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user