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)
|
||||
{
|
||||
switch(spell->id)
|
||||
{
|
||||
//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)
|
||||
if (spell->isOffensiveSpell())
|
||||
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
|
||||
|
@ -6,14 +6,25 @@
|
||||
// 1 -> spell is positive for them
|
||||
// anim: main effect animation (AC format), -1 - none
|
||||
// ranges: spell range description in SRSL ([no magic] [basic] [advanced] [expert])
|
||||
// counters: array of ids of countering spells
|
||||
|
||||
// flags: string array of
|
||||
// damage
|
||||
// directDamage //todo
|
||||
// offensive
|
||||
// rising
|
||||
// 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":
|
||||
{
|
||||
@ -130,7 +141,7 @@
|
||||
"effect": -1,
|
||||
"anim": 64,
|
||||
"ranges": [ "0", "0", "0", "0" ],
|
||||
"flags" : ["damage"]
|
||||
"flags" : ["damage, offensive"]
|
||||
},
|
||||
"iceBolt" :
|
||||
{
|
||||
@ -138,7 +149,7 @@
|
||||
"effect": -1,
|
||||
"anim": 46,
|
||||
"ranges": [ "0", "0", "0", "0" ],
|
||||
"flags" : ["damage"]
|
||||
"flags" : ["damage, offensive"]
|
||||
},
|
||||
"lightningBolt" :
|
||||
{
|
||||
@ -146,7 +157,7 @@
|
||||
"effect": -1,
|
||||
"anim": 38,
|
||||
"ranges": [ "0", "0", "0", "0" ],
|
||||
"flags" : ["damage"]
|
||||
"flags" : ["damage, offensive"]
|
||||
},
|
||||
"implosion" :
|
||||
{
|
||||
@ -154,7 +165,7 @@
|
||||
"effect": -1,
|
||||
"anim": 10,
|
||||
"ranges": [ "0", "0", "0", "0" ],
|
||||
"flags" : ["damage"]
|
||||
"flags" : ["damage, offensive"]
|
||||
},
|
||||
"chainLightning" :
|
||||
{
|
||||
@ -162,7 +173,7 @@
|
||||
"effect": -1,
|
||||
"anim": 38,
|
||||
"ranges": [ "0", "0", "0", "0" ],
|
||||
"flags" : ["damage"]
|
||||
"flags" : ["damage, offensive"]
|
||||
},
|
||||
"frostRing" :
|
||||
{
|
||||
@ -170,7 +181,7 @@
|
||||
"effect": -1,
|
||||
"anim": 45,
|
||||
"ranges": [ "1", "1", "1", "1" ],
|
||||
"flags" : ["damage"]
|
||||
"flags" : ["damage, offensive"]
|
||||
},
|
||||
"fireball" :
|
||||
{
|
||||
@ -178,7 +189,7 @@
|
||||
"effect": -1,
|
||||
"anim": 53,
|
||||
"ranges": [ "0,1", "0,1", "0,1", "0,1" ],
|
||||
"flags" : ["damage"]
|
||||
"flags" : ["damage, offensive"]
|
||||
},
|
||||
"inferno" :
|
||||
{
|
||||
@ -186,7 +197,7 @@
|
||||
"effect": -1,
|
||||
"anim": 9,
|
||||
"ranges": [ "0-2", "0-2", "0-2", "0-2" ],
|
||||
"flags" : ["damage"]
|
||||
"flags" : ["damage, offensive"]
|
||||
},
|
||||
"meteorShower" :
|
||||
{
|
||||
@ -194,7 +205,7 @@
|
||||
"effect": -1,
|
||||
"anim": 16,
|
||||
"ranges": [ "0,1", "0,1", "0,1", "0,1" ],
|
||||
"flags" : ["damage"]
|
||||
"flags" : ["damage, offensive"]
|
||||
},
|
||||
"deathRipple" :
|
||||
{
|
||||
@ -202,7 +213,8 @@
|
||||
"effect": -1,
|
||||
"anim": 8,
|
||||
"ranges": [ "X", "X", "X", "X" ],
|
||||
"flags" : ["damage"]
|
||||
"flags" : ["damage, offensive"],
|
||||
"immunity": ["SIEGE_WEAPON","UNDEAD"]
|
||||
},
|
||||
"destroyUndead" :
|
||||
{
|
||||
@ -210,7 +222,9 @@
|
||||
"effect": -1,
|
||||
"anim": 29,
|
||||
"ranges": [ "X", "X", "X", "X" ],
|
||||
"flags" : ["damage"]
|
||||
"flags" : ["damage, offensive"],
|
||||
"limit":["UNDEAD"]
|
||||
|
||||
},
|
||||
"armageddon" :
|
||||
{
|
||||
@ -218,7 +232,7 @@
|
||||
"effect": -1,
|
||||
"anim": 12,
|
||||
"ranges": [ "X", "X", "X", "X" ],
|
||||
"flags" : ["damage"]
|
||||
"flags" : ["damage, offensive"]
|
||||
},
|
||||
"shield" :
|
||||
{
|
||||
@ -228,9 +242,11 @@
|
||||
"ranges": [ "0", "0", "0", "X" ],
|
||||
"effects":
|
||||
[
|
||||
|
||||
{
|
||||
"type": "GENERAL_DAMAGE_REDUCTION",
|
||||
"subtype":0
|
||||
"subtype":0,
|
||||
"duration": "N_TURNS"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -244,7 +260,8 @@
|
||||
[
|
||||
{
|
||||
"type": "GENERAL_DAMAGE_REDUCTION",
|
||||
"subtype":1
|
||||
"subtype":1,
|
||||
"duration": "N_TURNS"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -257,7 +274,8 @@
|
||||
"effects":
|
||||
[
|
||||
{
|
||||
"type": "FIRE_SHIELD"
|
||||
"type": "FIRE_SHIELD",
|
||||
"duration": "N_TURNS"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -270,10 +288,14 @@
|
||||
"effects":
|
||||
[
|
||||
{
|
||||
|
||||
"type": "SPELL_DAMAGE_REDUCTION",
|
||||
"subtype":0
|
||||
"subtype":0,
|
||||
"duration": "N_TURNS"
|
||||
|
||||
}
|
||||
]
|
||||
|
||||
},
|
||||
"protectFire" :
|
||||
{
|
||||
@ -285,7 +307,8 @@
|
||||
[
|
||||
{
|
||||
"type": "SPELL_DAMAGE_REDUCTION",
|
||||
"subtype":1
|
||||
"subtype":1,
|
||||
"duration": "N_TURNS"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -299,7 +322,8 @@
|
||||
[
|
||||
{
|
||||
"type": "SPELL_DAMAGE_REDUCTION",
|
||||
"subtype":2
|
||||
"subtype":2,
|
||||
"duration": "N_TURNS"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -313,7 +337,8 @@
|
||||
[
|
||||
{
|
||||
"type": "SPELL_DAMAGE_REDUCTION",
|
||||
"subtype":3
|
||||
"subtype":3,
|
||||
"duration": "N_TURNS"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -327,8 +352,10 @@
|
||||
[
|
||||
{
|
||||
"type": "LEVEL_SPELL_IMMUNITY",
|
||||
"subtype":5,
|
||||
"valType":"INDEPENDENT_MAX"
|
||||
"subtype":5, //needed?
|
||||
"valType":"INDEPENDENT_MAX",
|
||||
"duration": "N_TURNS",
|
||||
"values":[3,3,4,5]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -349,7 +376,8 @@
|
||||
[
|
||||
{
|
||||
"type": "MAGIC_MIRROR",
|
||||
"valType":"INDEPENDENT_MAX"
|
||||
"valType":"INDEPENDENT_MAX",
|
||||
"duration": "N_TURNS"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -358,8 +386,7 @@
|
||||
"id": 37,
|
||||
"effect": 1,
|
||||
"anim": 39,
|
||||
"ranges": [ "0", "0", "0", "0" ],
|
||||
"flags" : ["rising"]
|
||||
"ranges": [ "0", "0", "0", "0" ]
|
||||
},
|
||||
"resurrection" :
|
||||
{
|
||||
@ -382,7 +409,8 @@
|
||||
"id": 40,
|
||||
"effect": 1,
|
||||
"anim": 79,
|
||||
"ranges": [ "0", "0", "0", "0" ]
|
||||
"ranges": [ "0", "0", "0", "0" ],
|
||||
"flags" : ["rising"]
|
||||
},
|
||||
"bless" :
|
||||
{
|
||||
@ -395,9 +423,11 @@
|
||||
[
|
||||
{
|
||||
"type": "ALWAYS_MAXIMUM_DAMAGE",
|
||||
"valType":"INDEPENDENT_MAX"
|
||||
"valType":"INDEPENDENT_MAX",
|
||||
"duration": "N_TURNS"
|
||||
}
|
||||
]
|
||||
],
|
||||
"immunity":["UNDEAD"]
|
||||
},
|
||||
"curse" :
|
||||
{
|
||||
@ -409,9 +439,13 @@
|
||||
[
|
||||
{
|
||||
"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" :
|
||||
{
|
||||
@ -424,7 +458,8 @@
|
||||
{
|
||||
"type": "PRIMARY_SKILL",
|
||||
"subtype": 0, //ATTACK
|
||||
"effectRange" : "ONLY_MELEE_FIGHT"
|
||||
"effectRange" : "ONLY_MELEE_FIGHT",
|
||||
"duration": "N_TURNS"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -439,9 +474,11 @@
|
||||
{
|
||||
"type": "PRIMARY_SKILL",
|
||||
"subtype": 0, //ATTACK
|
||||
"effectRange" : "ONLY_DISTANCE_FIGHT"
|
||||
"effectRange" : "ONLY_DISTANCE_FIGHT",
|
||||
"duration": "N_TURNS"
|
||||
}
|
||||
]
|
||||
],
|
||||
"limit":["SHOOTER"]
|
||||
},
|
||||
"weakness" :
|
||||
{
|
||||
@ -453,7 +490,9 @@
|
||||
[
|
||||
{
|
||||
"type": "PRIMARY_SKILL",
|
||||
"subtype": 0 //ATTACK
|
||||
"subtype": 0, //ATTACK
|
||||
"duration": "N_TURNS",
|
||||
"values":[-3,-3,-6,-6]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -467,7 +506,8 @@
|
||||
[
|
||||
{
|
||||
"type": "PRIMARY_SKILL",
|
||||
"subtype": 1 //DEFENSE
|
||||
"subtype": 1, //DEFENSE
|
||||
"duration": "N_TURNS"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -481,7 +521,10 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"subtype": 0 //ATTACK
|
||||
"subtype": 0, //ATTACK
|
||||
"duration": "N_TURNS"
|
||||
},
|
||||
{
|
||||
"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":
|
||||
[
|
||||
{
|
||||
"type": "MORALE"
|
||||
"type": "MORALE",
|
||||
"duration": "N_TURNS"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -530,7 +577,9 @@
|
||||
"effects":
|
||||
[
|
||||
{
|
||||
"type": "MORALE"
|
||||
"type": "MORALE",
|
||||
"duration": "N_TURNS",
|
||||
"values":[-1,-1,-2,-2]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -544,7 +593,8 @@
|
||||
"effects":
|
||||
[
|
||||
{
|
||||
"type": "LUCK"
|
||||
"type": "LUCK",
|
||||
"duration": "N_TURNS"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -558,7 +608,9 @@
|
||||
"effects":
|
||||
[
|
||||
{
|
||||
"type": "LUCK"
|
||||
"type": "LUCK",
|
||||
"duration": "N_TURNS",
|
||||
"values":[-1,-1,-2,-2]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -572,9 +624,11 @@
|
||||
"effects":
|
||||
[
|
||||
{
|
||||
"type": "STACKS_SPEED"
|
||||
"type": "STACKS_SPEED",
|
||||
"duration": "N_TURNS"
|
||||
}
|
||||
]
|
||||
],
|
||||
"immunity":["SIEGE_WEAPON"]
|
||||
},
|
||||
"slow" :
|
||||
{
|
||||
@ -586,9 +640,13 @@
|
||||
"effects":
|
||||
[
|
||||
{
|
||||
"type": "STACKS_SPEED"
|
||||
"type": "STACKS_SPEED",
|
||||
"valueType": "PERCENT_TO_ALL",
|
||||
"duration": "N_TURNS"
|
||||
"values":[-25,-25,-50,-50]
|
||||
}
|
||||
]
|
||||
],
|
||||
"immunity":["SIEGE_WEAPON"]
|
||||
},
|
||||
"slayer" :
|
||||
{
|
||||
@ -599,7 +657,9 @@
|
||||
"effects":
|
||||
[
|
||||
{
|
||||
"type": "SLAYER"
|
||||
"type": "SLAYER",
|
||||
"duration": "N_TURNS",
|
||||
"values":[0,1,2,3]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -612,7 +672,8 @@
|
||||
"effects":
|
||||
[
|
||||
{
|
||||
"type": "IN_FRENZY"
|
||||
"type": "IN_FRENZY",
|
||||
"duration": "N_TURNS"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -622,7 +683,7 @@
|
||||
"effect": -1,
|
||||
"anim": 38,
|
||||
"ranges": [ "0", "0", "0", "0" ],
|
||||
"flags" : ["damage"]
|
||||
"flags" : ["damage, offensive"]
|
||||
},
|
||||
"counterstrike" :
|
||||
{
|
||||
@ -633,7 +694,8 @@
|
||||
"effects":
|
||||
[
|
||||
{
|
||||
"type": "ADDITIONAL_RETALIATION"
|
||||
"type": "ADDITIONAL_RETALIATION",
|
||||
"duration": "N_TURNS"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -647,7 +709,9 @@
|
||||
"effects":
|
||||
[
|
||||
{
|
||||
"type": "ATTACKS_NEAREST_CREATURE"
|
||||
"type": "ATTACKS_NEAREST_CREATURE",
|
||||
"duration": "N_TURNS",
|
||||
"values":[0,1,2,3]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -661,7 +725,9 @@
|
||||
"effects":
|
||||
[
|
||||
{
|
||||
"type": "HYPNOTIZED"
|
||||
"type": "HYPNOTIZED",
|
||||
"duration": "N_TURNS",
|
||||
"values":[0,1,2,3]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -675,9 +741,12 @@
|
||||
"effects":
|
||||
[
|
||||
{
|
||||
"type": "FORGETFULL"
|
||||
"type": "FORGETFULL",
|
||||
"duration": "N_TURNS",
|
||||
"values":[0,1,2,3]
|
||||
}
|
||||
]
|
||||
],
|
||||
"limit":["SHOOTER"]
|
||||
},
|
||||
"blind" :
|
||||
{
|
||||
@ -690,16 +759,18 @@
|
||||
[
|
||||
{
|
||||
"type": "NOT_ACTIVE",
|
||||
"subtype": 62, //really ase spell id, is it right?
|
||||
//TODO: duration
|
||||
"subtype": 62,
|
||||
"duration": "UNITL_BEING_ATTACKED N_TURNS",
|
||||
"values":[0,0,0,0]
|
||||
},
|
||||
{
|
||||
"type": "GENERAL_ATTACK_REDUCTION"
|
||||
//TODO: duration
|
||||
"type": "GENERAL_ATTACK_REDUCTION",
|
||||
"duration": "UNITL_BEING_ATTACKED N_TURNS"
|
||||
},
|
||||
{
|
||||
"type": "NO_RETALIATION",
|
||||
"duration": "UNITL_BEING_ATTACKED"
|
||||
"duration": "UNITL_BEING_ATTACKED",
|
||||
"values":[0,0,0,0]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -708,7 +779,8 @@
|
||||
"id": 63,
|
||||
"effect": 1,
|
||||
"anim": -1,
|
||||
"ranges": [ "0", "0", "0", "0" ]
|
||||
"ranges": [ "0", "0", "0", "0" ],
|
||||
"immunity":["SIEGE_WEAPON"]
|
||||
},
|
||||
"removeObstacle" :
|
||||
{
|
||||
@ -722,7 +794,8 @@
|
||||
"id": 65,
|
||||
"effect": 1,
|
||||
"anim": -1,
|
||||
"ranges": [ "0", "0", "0", "0" ]
|
||||
"ranges": [ "0", "0", "0", "0" ],
|
||||
"immunity":["SIEGE_WEAPON"]
|
||||
},
|
||||
"fireElemental" :
|
||||
{
|
||||
@ -762,12 +835,14 @@
|
||||
[
|
||||
{
|
||||
"type": "NOT_ACTIVE",
|
||||
"subtype": 62
|
||||
//TODO: duration
|
||||
"subtype": 62,
|
||||
"duration": "UNITL_BEING_ATTACKED N_TURNS",
|
||||
"values":[0,0,0,0]
|
||||
},
|
||||
{
|
||||
"type": "NO_RETALIATION",
|
||||
"duration": "UNITL_BEING_ATTACKED"
|
||||
"duration": "UNITL_BEING_ATTACKED",
|
||||
"values":[0,0,0,0]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -781,13 +856,16 @@
|
||||
[
|
||||
{
|
||||
"type": "POISON",
|
||||
"val" : 30,
|
||||
"valueType": "INDEPENDENT_MAX"
|
||||
"valueType": "INDEPENDENT_MAX",
|
||||
"duration": "N_TURNS",
|
||||
"values":[30,30,30,30]
|
||||
},
|
||||
{
|
||||
"type": "STACK_HEALTH",
|
||||
"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",
|
||||
"val" : 30,
|
||||
"turns" : 1,
|
||||
"duration" : "PERMANENT"
|
||||
"duration" : "PERMANENT",
|
||||
"addInfo" : -1,
|
||||
"values":[0,0,0,0]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -818,12 +896,14 @@
|
||||
{
|
||||
"type": "PRIMARY_SKILL",
|
||||
"subtype": 0,
|
||||
"val" : -2,
|
||||
"duration": "N_TURNS",
|
||||
"values":[-2,-2,-2,-2]
|
||||
},
|
||||
{
|
||||
"type": "PRIMARY_SKILL",
|
||||
"subtype": 1,
|
||||
"val" : -2,
|
||||
"duration": "N_TURNS",
|
||||
"values":[-2,-2,-2,-2]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -838,11 +918,13 @@
|
||||
{
|
||||
"type": "NOT_ACTIVE",
|
||||
"subtype": 74,
|
||||
//TODO: duration
|
||||
"duration": "UNITL_BEING_ATTACKED N_TURNS",
|
||||
"values":[0,0,0,0]
|
||||
},
|
||||
{
|
||||
"type": "NO_RETALIATION",
|
||||
"duration": "UNITL_BEING_ATTACKED"
|
||||
"duration": "UNITL_BEING_ATTACKED",
|
||||
"values":[0,0,0,0]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -856,8 +938,9 @@
|
||||
[
|
||||
{
|
||||
"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,
|
||||
"anim": 38,
|
||||
"ranges": [ "0", "0", "0", "0" ],
|
||||
"flags" : ["damage"]
|
||||
"flags" : ["damage, offensive"]
|
||||
},
|
||||
"dispelHelpful" :
|
||||
{
|
||||
@ -901,9 +984,9 @@
|
||||
{
|
||||
"type": "PRIMARY_SKILL",
|
||||
"subtype": 1,
|
||||
"val" : -3,
|
||||
"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)
|
||||
{
|
||||
if(!caster)
|
||||
@ -167,9 +166,9 @@ ui32 BattleInfo::calculateHealedHP(const CGHeroInstance * caster, const CSpell *
|
||||
bool resurrect = resurrects(spell->id);
|
||||
int healedHealth;
|
||||
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
|
||||
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);
|
||||
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)
|
||||
{
|
||||
@ -931,163 +930,17 @@ si32 CStack::magicResistance() const
|
||||
|
||||
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];
|
||||
si32 power = sp->powers[sse.val];
|
||||
|
||||
auto addFull = [&](Bonus::BonusType type, si16 subtype, si32 value, si32 additionalInfo, si32 limit)
|
||||
{
|
||||
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);
|
||||
};
|
||||
std::vector<Bonus> tmp;
|
||||
sp->getEffects(tmp, sse.val);
|
||||
|
||||
auto addVT = [&](Bonus::BonusType type, si16 subtype, si32 value, ui8 valType)
|
||||
BOOST_FOREACH(Bonus& b, tmp)
|
||||
{
|
||||
add(type, subtype, value);
|
||||
sf.back().valType = valType;
|
||||
};
|
||||
|
||||
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;
|
||||
b.turnsRemain = sse.turnsRemain;
|
||||
sf.push_back(b);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool CStack::willMove(int turn /*= 0*/) const
|
||||
|
@ -182,13 +182,6 @@ public:
|
||||
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);
|
||||
|
||||
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
|
||||
{
|
||||
std::vector<int> affectedIds;
|
||||
int spLevel = slayerEffect->val;
|
||||
int spLevel = slayerEffect->val;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
// Get stack at destination hex -> subject of our 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
|
||||
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?
|
||||
{
|
||||
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:
|
||||
if (subject->hasBonusOfType(Bonus::SIEGE_WEAPON))
|
||||
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 (caster) //TODO: how about stacks casting Clone?
|
||||
{
|
||||
if (vstd::contains(subject->state, EBattleStackState::CLONED))
|
||||
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;
|
||||
}
|
||||
break;
|
||||
case Spells::FORGETFULNESS:
|
||||
case Spells::PRECISION:
|
||||
if (!subject->hasBonusOfType(Bonus::SHOOTER))
|
||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
||||
break;
|
||||
case Spells::DISPEL_HELPFUL_SPELLS:
|
||||
{
|
||||
TBonusListPtr spellBon = subject->getSpellBonuses();
|
||||
@ -1551,69 +1531,12 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
|
||||
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 (subject->count >= subject->baseAmount) //TODO: calculate potential hp raised
|
||||
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
|
||||
{
|
||||
@ -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;
|
||||
|
||||
//TODO?
|
||||
|
@ -130,9 +130,10 @@ CSpellHandler::CSpellHandler()
|
||||
|
||||
CSpell::CSpell()
|
||||
{
|
||||
_isDamage = false;
|
||||
_isMind = false;
|
||||
_isRising = false;
|
||||
isDamage = false;
|
||||
isMind = false;
|
||||
isRising = false;
|
||||
isOffensive = false;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
return _isRising;
|
||||
return isRising;
|
||||
}
|
||||
|
||||
bool CSpell::isDamageSpell() const
|
||||
{
|
||||
return _isDamage;
|
||||
return isDamage;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@ -329,44 +419,31 @@ void CSpellHandler::loadSpells()
|
||||
{
|
||||
CLegacyConfigParser parser("DATA/SPTRAITS.TXT");
|
||||
|
||||
for(int i=0; i<5; i++) // header
|
||||
parser.endLine();
|
||||
|
||||
do //read adventure map spells
|
||||
auto read = [&,this](bool combat, bool alility)
|
||||
{
|
||||
CSpell * spell = loadSpell(parser);
|
||||
spell->id = spells.size();
|
||||
spell->combatSpell = false;
|
||||
spell->creatureAbility = false;
|
||||
spells.push_back(spell);
|
||||
}
|
||||
while (parser.endLine() && !parser.isNextEntryEmpty());
|
||||
do
|
||||
{
|
||||
CSpell * spell = loadSpell(parser);
|
||||
spell->id = spells.size();
|
||||
spell->combatSpell = combat;
|
||||
spell->creatureAbility = alility;
|
||||
spells.push_back(spell);
|
||||
}
|
||||
while (parser.endLine() && !parser.isNextEntryEmpty());
|
||||
};
|
||||
|
||||
for(int i=0; i<3; i++)
|
||||
parser.endLine();
|
||||
|
||||
do //read battle spells
|
||||
auto skip = [&](int cnt)
|
||||
{
|
||||
CSpell * spell = loadSpell(parser);
|
||||
spell->id = spells.size();
|
||||
spell->combatSpell = true;
|
||||
spell->creatureAbility = false;
|
||||
spells.push_back(spell);
|
||||
}
|
||||
while (parser.endLine() && !parser.isNextEntryEmpty());
|
||||
for(int i=0; i<cnt; i++)
|
||||
parser.endLine();
|
||||
};
|
||||
|
||||
for(int i=0; i<3; i++)
|
||||
parser.endLine();
|
||||
|
||||
do //read creature abilities
|
||||
{
|
||||
CSpell * spell = loadSpell(parser);
|
||||
spell->id = spells.size();
|
||||
spell->combatSpell = true;
|
||||
spell->creatureAbility = true;
|
||||
spells.push_back(spell);
|
||||
}
|
||||
while (parser.endLine() && !parser.isNextEntryEmpty());
|
||||
skip(5);// header
|
||||
read(false,false); //read adventure map spells
|
||||
skip(3);
|
||||
read(true,false); //read battle spells
|
||||
skip(3);
|
||||
read(true,true);//read creature abilities
|
||||
|
||||
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")
|
||||
{
|
||||
s->_isDamage = true;
|
||||
s->isDamage = true;
|
||||
}
|
||||
else if (flag == "rising")
|
||||
{
|
||||
s->_isRising = true;
|
||||
s->isRising = true;
|
||||
}
|
||||
else if (flag == "mind")
|
||||
{
|
||||
s->_isMind = true;
|
||||
s->isMind = true;
|
||||
}
|
||||
else if (flag == "offensive")
|
||||
{
|
||||
s->isOffensive = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
b->sid = s->id;
|
||||
b->source = Bonus::SPELL_EFFECT;
|
||||
b->duration = Bonus::N_TURNS; //default
|
||||
//TODO: make duration configurable
|
||||
s->_effects.push_back(*b);
|
||||
b->sid = s->id; //for all
|
||||
b->source = Bonus::SPELL_EFFECT;//for all
|
||||
b->val = s->powers[i];
|
||||
|
||||
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
|
||||
|
@ -56,26 +56,33 @@ public:
|
||||
|
||||
bool isRisingSpell() 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;
|
||||
|
||||
|
||||
void getEffects(std::vector<Bonus> & lst) const;
|
||||
bool isImmuneBy(const IBonusBearer *obj) const;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & identifier & id & name & abbName & descriptions & level & earth & water & fire & air & power & costs
|
||||
& powers & probabilities & AIVals & attributes & combatSpell & creatureAbility & positiveness & range & counteredSpells & mainEffectAnim;
|
||||
h & _isRising & _isDamage & _isMind;
|
||||
h & _effects;
|
||||
h & isRising & isDamage & isMind;
|
||||
h & effects & immunities & limiters;
|
||||
}
|
||||
friend class CSpellHandler;
|
||||
|
||||
private:
|
||||
bool _isRising;
|
||||
bool _isDamage;
|
||||
bool _isMind;
|
||||
bool isRising;
|
||||
bool isDamage;
|
||||
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)
|
||||
(".VCGM1", EResType::CLIENT_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);
|
||||
if (iter == stringToRes.end())
|
||||
@ -285,6 +288,9 @@ std::string EResTypeHelper::getEResTypeAsString(EResType::Type type)
|
||||
MAP_ENUM(LIB_SAVEGAME)
|
||||
MAP_ENUM(SERVER_SAVEGAME)
|
||||
MAP_ENUM(DIRECTORY)
|
||||
MAP_ENUM(ERM)
|
||||
MAP_ENUM(ERT)
|
||||
MAP_ENUM(ERS)
|
||||
MAP_ENUM(OTHER);
|
||||
|
||||
#undef MAP_ENUM
|
||||
|
@ -60,6 +60,9 @@ namespace EResType
|
||||
LIB_SAVEGAME,
|
||||
SERVER_SAVEGAME,
|
||||
DIRECTORY,
|
||||
ERM,
|
||||
ERT,
|
||||
ERS,
|
||||
OTHER
|
||||
};
|
||||
}
|
||||
|
@ -418,9 +418,9 @@ si32 IBonusBearer::Attack() const
|
||||
{
|
||||
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);
|
||||
|
||||
|
@ -903,6 +903,25 @@ Bonus * JsonUtils::parseBonus (const JsonVector &ability_vec) //TODO: merge with
|
||||
b->turnsRemain = 0;
|
||||
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>
|
||||
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())
|
||||
{
|
||||
std::string type = val->String();
|
||||
auto it = map.find(type);
|
||||
if (it == map.end())
|
||||
{
|
||||
tlog1 << "Error: invalid " << err << type << std::endl;
|
||||
return defaultValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
return parseByMapStr(map, type, err);
|
||||
}
|
||||
else
|
||||
return defaultValue;
|
||||
};
|
||||
}
|
||||
|
||||
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();
|
||||
break;
|
||||
case JsonNode::DATA_STRING:
|
||||
VLC->modh->identifiers.requestIdentifier (value->String(), [&](si32 identifier)
|
||||
{
|
||||
var = identifier;
|
||||
VLC->modh->identifiers.requestIdentifier (value->String(), [&](si32 identifier)
|
||||
{
|
||||
var = identifier;
|
||||
});
|
||||
break;
|
||||
default:
|
||||
@ -994,7 +1004,18 @@ Bonus * JsonUtils::parseBonus (const JsonNode &ability)
|
||||
b->valType = parseByMap(bonusLimitEffect, value, "effect range ");
|
||||
value = &ability["duration"];
|
||||
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"];
|
||||
if (!value->isNull())
|
||||
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
|
||||
}
|
||||
StacksInjured si;
|
||||
|
||||
|
||||
//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;
|
||||
if (stack && mode != ECastingMode::MAGIC_MIRROR)
|
||||
{
|
||||
@ -4088,48 +4041,10 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
|
||||
|
||||
if (spellID == Spells::CHAIN_LIGHTNING)
|
||||
++chainLightningModifier;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// 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:
|
||||
{
|
||||
}
|
||||
}
|
||||
else if (spell->hasEffects())
|
||||
{
|
||||
int stackSpellPower = 0;
|
||||
if (stack && mode != ECastingMode::MAGIC_MIRROR)
|
||||
{
|
||||
@ -4204,25 +4119,10 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
|
||||
|
||||
if(!sse.stacks.empty())
|
||||
sendAndApply(&sse);
|
||||
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;
|
||||
}
|
||||
case Spells::CURE:
|
||||
case Spells::RESURRECTION:
|
||||
case Spells::ANIMATE_DEAD:
|
||||
case Spells::SACRIFICE:
|
||||
{
|
||||
|
||||
}
|
||||
else if (spell->isRisingSpell() || spell->id == Spells::CURE)
|
||||
{
|
||||
int hpGained = 0;
|
||||
if (stack)
|
||||
{
|
||||
@ -4264,7 +4164,53 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
|
||||
BattleStacksRemoved bsr;
|
||||
bsr.stackIDs.insert (selectedStack); //somehow it works for teleport?
|
||||
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;
|
||||
}
|
||||
case Spells::SUMMON_FIRE_ELEMENTAL:
|
||||
@ -5536,7 +5482,7 @@ bool CGameHandler::castSpell(const CGHeroInstance *h, int spellID, const int3 &p
|
||||
|
||||
GiveBonus gb;
|
||||
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);
|
||||
}
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user