1
0
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:
alexvins 2013-01-16 11:19:04 +00:00
parent a86b5c1e2e
commit 53b684180d
12 changed files with 480 additions and 579 deletions

View File

@ -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

View File

@ -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]
}
]
},

View File

@ -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

View File

@ -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;

View File

@ -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?

View File

@ -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 &center, 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

View File

@ -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
};

View File

@ -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

View File

@ -60,6 +60,9 @@ namespace EResType
LIB_SAVEGAME,
SERVER_SAVEGAME,
DIRECTORY,
ERM,
ERT,
ERS,
OTHER
};
}

View File

@ -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);

View File

@ -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 ");

View File

@ -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;