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) SpellTypes spellType(const CSpell *spell)
{ {
switch(spell->id) if (spell->isOffensiveSpell())
{
//offense spell
case Spells::MAGIC_ARROW:
case Spells::ICE_BOLT:
case Spells::LIGHTNING_BOLT:
case Spells::IMPLOSION:
case Spells::CHAIN_LIGHTNING:
case Spells::FROST_RING:
case Spells::FIREBALL:
case Spells::INFERNO:
case Spells::METEOR_SHOWER:
case Spells::DEATH_RIPPLE:
case Spells::DESTROY_UNDEAD:
case Spells::ARMAGEDDON:
case Spells::TITANS_LIGHTNING_BOLT:
case Spells::THUNDERBOLT: //(thunderbirds)
return OFFENSIVE_SPELL; return OFFENSIVE_SPELL;
if (spell->hasEffects())
return TIMED_EFFECT;
return OTHER;
case Spells::SHIELD:
case Spells::AIR_SHIELD:
case Spells::FIRE_SHIELD:
case Spells::PROTECTION_FROM_AIR:
case Spells::PROTECTION_FROM_FIRE:
case Spells::PROTECTION_FROM_WATER:
case Spells::PROTECTION_FROM_EARTH:
case Spells::ANTI_MAGIC:
case Spells::MAGIC_MIRROR:
case Spells::BLESS:
case Spells::CURSE:
case Spells::BLOODLUST:
case Spells::PRECISION:
case Spells::WEAKNESS:
case Spells::STONE_SKIN:
case Spells::DISRUPTING_RAY:
case Spells::PRAYER:
case Spells::MIRTH:
case Spells::SORROW:
case Spells::FORTUNE:
case Spells::MISFORTUNE:
case Spells::HASTE:
case Spells::SLOW:
case Spells::SLAYER:
case Spells::FRENZY:
case Spells::COUNTERSTRIKE:
case Spells::BERSERK:
case Spells::HYPNOTIZE:
case Spells::FORGETFULNESS:
case Spells::BLIND:
case Spells::STONE_GAZE:
case Spells::POISON:
case Spells::BIND:
case Spells::DISEASE:
case Spells::PARALYZE:
case Spells::AGE:
case Spells::ACID_BREATH_DEFENSE:
return TIMED_EFFECT;
default:
return OTHER;
}
} }
struct PossibleSpellcast struct PossibleSpellcast

View File

@ -6,14 +6,25 @@
// 1 -> spell is positive for them // 1 -> spell is positive for them
// anim: main effect animation (AC format), -1 - none // anim: main effect animation (AC format), -1 - none
// ranges: spell range description in SRSL ([no magic] [basic] [advanced] [expert]) // ranges: spell range description in SRSL ([no magic] [basic] [advanced] [expert])
// counters: array of ids of countering spells
// flags: string array of // flags: string array of
// damage // damage
// directDamage //todo // offensive
// rising // rising
// mind // mind
// summoning //todo:
//effects: array of bonus for permanent effects //effects: array of structure for bonuses for permanent effects
// {bonus format} - effect //todo
// + values: [4 int values] (OPTIONAL default from sptraits) values for levels
// + ainfos: [4 int values] (optional) additional infos for levels (atm only CURSE)
//
//immunity - name of bonus granting immunity to this spell
//
// immunity: array any of these bonus grants immunity
// limit: array required bonus to be affected, all required
"spells": "spells":
{ {
@ -130,7 +141,7 @@
"effect": -1, "effect": -1,
"anim": 64, "anim": 64,
"ranges": [ "0", "0", "0", "0" ], "ranges": [ "0", "0", "0", "0" ],
"flags" : ["damage"] "flags" : ["damage, offensive"]
}, },
"iceBolt" : "iceBolt" :
{ {
@ -138,7 +149,7 @@
"effect": -1, "effect": -1,
"anim": 46, "anim": 46,
"ranges": [ "0", "0", "0", "0" ], "ranges": [ "0", "0", "0", "0" ],
"flags" : ["damage"] "flags" : ["damage, offensive"]
}, },
"lightningBolt" : "lightningBolt" :
{ {
@ -146,7 +157,7 @@
"effect": -1, "effect": -1,
"anim": 38, "anim": 38,
"ranges": [ "0", "0", "0", "0" ], "ranges": [ "0", "0", "0", "0" ],
"flags" : ["damage"] "flags" : ["damage, offensive"]
}, },
"implosion" : "implosion" :
{ {
@ -154,7 +165,7 @@
"effect": -1, "effect": -1,
"anim": 10, "anim": 10,
"ranges": [ "0", "0", "0", "0" ], "ranges": [ "0", "0", "0", "0" ],
"flags" : ["damage"] "flags" : ["damage, offensive"]
}, },
"chainLightning" : "chainLightning" :
{ {
@ -162,7 +173,7 @@
"effect": -1, "effect": -1,
"anim": 38, "anim": 38,
"ranges": [ "0", "0", "0", "0" ], "ranges": [ "0", "0", "0", "0" ],
"flags" : ["damage"] "flags" : ["damage, offensive"]
}, },
"frostRing" : "frostRing" :
{ {
@ -170,7 +181,7 @@
"effect": -1, "effect": -1,
"anim": 45, "anim": 45,
"ranges": [ "1", "1", "1", "1" ], "ranges": [ "1", "1", "1", "1" ],
"flags" : ["damage"] "flags" : ["damage, offensive"]
}, },
"fireball" : "fireball" :
{ {
@ -178,7 +189,7 @@
"effect": -1, "effect": -1,
"anim": 53, "anim": 53,
"ranges": [ "0,1", "0,1", "0,1", "0,1" ], "ranges": [ "0,1", "0,1", "0,1", "0,1" ],
"flags" : ["damage"] "flags" : ["damage, offensive"]
}, },
"inferno" : "inferno" :
{ {
@ -186,7 +197,7 @@
"effect": -1, "effect": -1,
"anim": 9, "anim": 9,
"ranges": [ "0-2", "0-2", "0-2", "0-2" ], "ranges": [ "0-2", "0-2", "0-2", "0-2" ],
"flags" : ["damage"] "flags" : ["damage, offensive"]
}, },
"meteorShower" : "meteorShower" :
{ {
@ -194,7 +205,7 @@
"effect": -1, "effect": -1,
"anim": 16, "anim": 16,
"ranges": [ "0,1", "0,1", "0,1", "0,1" ], "ranges": [ "0,1", "0,1", "0,1", "0,1" ],
"flags" : ["damage"] "flags" : ["damage, offensive"]
}, },
"deathRipple" : "deathRipple" :
{ {
@ -202,7 +213,8 @@
"effect": -1, "effect": -1,
"anim": 8, "anim": 8,
"ranges": [ "X", "X", "X", "X" ], "ranges": [ "X", "X", "X", "X" ],
"flags" : ["damage"] "flags" : ["damage, offensive"],
"immunity": ["SIEGE_WEAPON","UNDEAD"]
}, },
"destroyUndead" : "destroyUndead" :
{ {
@ -210,7 +222,9 @@
"effect": -1, "effect": -1,
"anim": 29, "anim": 29,
"ranges": [ "X", "X", "X", "X" ], "ranges": [ "X", "X", "X", "X" ],
"flags" : ["damage"] "flags" : ["damage, offensive"],
"limit":["UNDEAD"]
}, },
"armageddon" : "armageddon" :
{ {
@ -218,7 +232,7 @@
"effect": -1, "effect": -1,
"anim": 12, "anim": 12,
"ranges": [ "X", "X", "X", "X" ], "ranges": [ "X", "X", "X", "X" ],
"flags" : ["damage"] "flags" : ["damage, offensive"]
}, },
"shield" : "shield" :
{ {
@ -228,9 +242,11 @@
"ranges": [ "0", "0", "0", "X" ], "ranges": [ "0", "0", "0", "X" ],
"effects": "effects":
[ [
{ {
"type": "GENERAL_DAMAGE_REDUCTION", "type": "GENERAL_DAMAGE_REDUCTION",
"subtype":0 "subtype":0,
"duration": "N_TURNS"
} }
] ]
}, },
@ -244,7 +260,8 @@
[ [
{ {
"type": "GENERAL_DAMAGE_REDUCTION", "type": "GENERAL_DAMAGE_REDUCTION",
"subtype":1 "subtype":1,
"duration": "N_TURNS"
} }
] ]
}, },
@ -257,7 +274,8 @@
"effects": "effects":
[ [
{ {
"type": "FIRE_SHIELD" "type": "FIRE_SHIELD",
"duration": "N_TURNS"
} }
] ]
}, },
@ -270,10 +288,14 @@
"effects": "effects":
[ [
{ {
"type": "SPELL_DAMAGE_REDUCTION", "type": "SPELL_DAMAGE_REDUCTION",
"subtype":0 "subtype":0,
"duration": "N_TURNS"
} }
] ]
}, },
"protectFire" : "protectFire" :
{ {
@ -285,7 +307,8 @@
[ [
{ {
"type": "SPELL_DAMAGE_REDUCTION", "type": "SPELL_DAMAGE_REDUCTION",
"subtype":1 "subtype":1,
"duration": "N_TURNS"
} }
] ]
}, },
@ -299,7 +322,8 @@
[ [
{ {
"type": "SPELL_DAMAGE_REDUCTION", "type": "SPELL_DAMAGE_REDUCTION",
"subtype":2 "subtype":2,
"duration": "N_TURNS"
} }
] ]
}, },
@ -313,7 +337,8 @@
[ [
{ {
"type": "SPELL_DAMAGE_REDUCTION", "type": "SPELL_DAMAGE_REDUCTION",
"subtype":3 "subtype":3,
"duration": "N_TURNS"
} }
] ]
}, },
@ -327,8 +352,10 @@
[ [
{ {
"type": "LEVEL_SPELL_IMMUNITY", "type": "LEVEL_SPELL_IMMUNITY",
"subtype":5, "subtype":5, //needed?
"valType":"INDEPENDENT_MAX" "valType":"INDEPENDENT_MAX",
"duration": "N_TURNS",
"values":[3,3,4,5]
} }
] ]
}, },
@ -349,7 +376,8 @@
[ [
{ {
"type": "MAGIC_MIRROR", "type": "MAGIC_MIRROR",
"valType":"INDEPENDENT_MAX" "valType":"INDEPENDENT_MAX",
"duration": "N_TURNS"
} }
] ]
}, },
@ -358,8 +386,7 @@
"id": 37, "id": 37,
"effect": 1, "effect": 1,
"anim": 39, "anim": 39,
"ranges": [ "0", "0", "0", "0" ], "ranges": [ "0", "0", "0", "0" ]
"flags" : ["rising"]
}, },
"resurrection" : "resurrection" :
{ {
@ -382,7 +409,8 @@
"id": 40, "id": 40,
"effect": 1, "effect": 1,
"anim": 79, "anim": 79,
"ranges": [ "0", "0", "0", "0" ] "ranges": [ "0", "0", "0", "0" ],
"flags" : ["rising"]
}, },
"bless" : "bless" :
{ {
@ -395,9 +423,11 @@
[ [
{ {
"type": "ALWAYS_MAXIMUM_DAMAGE", "type": "ALWAYS_MAXIMUM_DAMAGE",
"valType":"INDEPENDENT_MAX" "valType":"INDEPENDENT_MAX",
"duration": "N_TURNS"
} }
] ],
"immunity":["UNDEAD"]
}, },
"curse" : "curse" :
{ {
@ -409,9 +439,13 @@
[ [
{ {
"type": "ALWAYS_MINIMUM_DAMAGE", "type": "ALWAYS_MINIMUM_DAMAGE",
"valType":"INDEPENDENT_MAX" "subtype": -1, //any attack
"valType": "INDEPENDENT_MAX",
"duration": "N_TURNS",
"ainfos":[0,0,20,20]
} }
] ],
"immunity":["UNDEAD"]
}, },
"bloodlust" : "bloodlust" :
{ {
@ -424,7 +458,8 @@
{ {
"type": "PRIMARY_SKILL", "type": "PRIMARY_SKILL",
"subtype": 0, //ATTACK "subtype": 0, //ATTACK
"effectRange" : "ONLY_MELEE_FIGHT" "effectRange" : "ONLY_MELEE_FIGHT",
"duration": "N_TURNS"
} }
] ]
}, },
@ -439,9 +474,11 @@
{ {
"type": "PRIMARY_SKILL", "type": "PRIMARY_SKILL",
"subtype": 0, //ATTACK "subtype": 0, //ATTACK
"effectRange" : "ONLY_DISTANCE_FIGHT" "effectRange" : "ONLY_DISTANCE_FIGHT",
"duration": "N_TURNS"
} }
] ],
"limit":["SHOOTER"]
}, },
"weakness" : "weakness" :
{ {
@ -453,7 +490,9 @@
[ [
{ {
"type": "PRIMARY_SKILL", "type": "PRIMARY_SKILL",
"subtype": 0 //ATTACK "subtype": 0, //ATTACK
"duration": "N_TURNS",
"values":[-3,-3,-6,-6]
} }
] ]
}, },
@ -467,7 +506,8 @@
[ [
{ {
"type": "PRIMARY_SKILL", "type": "PRIMARY_SKILL",
"subtype": 1 //DEFENSE "subtype": 1, //DEFENSE
"duration": "N_TURNS"
} }
] ]
}, },
@ -481,7 +521,10 @@
[ [
{ {
"type": "PRIMARY_SKILL", "type": "PRIMARY_SKILL",
"subtype": 1 //DEFENSE "subtype": 1, //DEFENSE
"valueType": "ADDITIVE_VALUE",
"duration": "N_TURNS",
"values":[-3,-3,-4,-5]
} }
] ]
}, },
@ -495,14 +538,17 @@
[ [
{ {
"type": "PRIMARY_SKILL", "type": "PRIMARY_SKILL",
"subtype": 0 //ATTACK "subtype": 0, //ATTACK
"duration": "N_TURNS"
}, },
{ {
"type": "PRIMARY_SKILL", "type": "PRIMARY_SKILL",
"subtype": 1 //DEFENSE "subtype": 1, //DEFENSE
"duration": "N_TURNS"
}, },
{ {
"type": "STACKS_SPEED" "type": "STACKS_SPEED",
"duration": "N_TURNS"
} }
] ]
}, },
@ -516,7 +562,8 @@
"effects": "effects":
[ [
{ {
"type": "MORALE" "type": "MORALE",
"duration": "N_TURNS"
} }
] ]
}, },
@ -530,7 +577,9 @@
"effects": "effects":
[ [
{ {
"type": "MORALE" "type": "MORALE",
"duration": "N_TURNS",
"values":[-1,-1,-2,-2]
} }
] ]
}, },
@ -544,7 +593,8 @@
"effects": "effects":
[ [
{ {
"type": "LUCK" "type": "LUCK",
"duration": "N_TURNS"
} }
] ]
}, },
@ -558,7 +608,9 @@
"effects": "effects":
[ [
{ {
"type": "LUCK" "type": "LUCK",
"duration": "N_TURNS",
"values":[-1,-1,-2,-2]
} }
] ]
}, },
@ -572,9 +624,11 @@
"effects": "effects":
[ [
{ {
"type": "STACKS_SPEED" "type": "STACKS_SPEED",
"duration": "N_TURNS"
} }
] ],
"immunity":["SIEGE_WEAPON"]
}, },
"slow" : "slow" :
{ {
@ -586,9 +640,13 @@
"effects": "effects":
[ [
{ {
"type": "STACKS_SPEED" "type": "STACKS_SPEED",
"valueType": "PERCENT_TO_ALL",
"duration": "N_TURNS"
"values":[-25,-25,-50,-50]
} }
] ],
"immunity":["SIEGE_WEAPON"]
}, },
"slayer" : "slayer" :
{ {
@ -599,7 +657,9 @@
"effects": "effects":
[ [
{ {
"type": "SLAYER" "type": "SLAYER",
"duration": "N_TURNS",
"values":[0,1,2,3]
} }
] ]
}, },
@ -612,7 +672,8 @@
"effects": "effects":
[ [
{ {
"type": "IN_FRENZY" "type": "IN_FRENZY",
"duration": "N_TURNS"
} }
] ]
}, },
@ -622,7 +683,7 @@
"effect": -1, "effect": -1,
"anim": 38, "anim": 38,
"ranges": [ "0", "0", "0", "0" ], "ranges": [ "0", "0", "0", "0" ],
"flags" : ["damage"] "flags" : ["damage, offensive"]
}, },
"counterstrike" : "counterstrike" :
{ {
@ -633,7 +694,8 @@
"effects": "effects":
[ [
{ {
"type": "ADDITIONAL_RETALIATION" "type": "ADDITIONAL_RETALIATION",
"duration": "N_TURNS"
} }
] ]
}, },
@ -647,7 +709,9 @@
"effects": "effects":
[ [
{ {
"type": "ATTACKS_NEAREST_CREATURE" "type": "ATTACKS_NEAREST_CREATURE",
"duration": "N_TURNS",
"values":[0,1,2,3]
} }
] ]
}, },
@ -661,7 +725,9 @@
"effects": "effects":
[ [
{ {
"type": "HYPNOTIZED" "type": "HYPNOTIZED",
"duration": "N_TURNS",
"values":[0,1,2,3]
} }
] ]
}, },
@ -675,9 +741,12 @@
"effects": "effects":
[ [
{ {
"type": "FORGETFULL" "type": "FORGETFULL",
"duration": "N_TURNS",
"values":[0,1,2,3]
} }
] ],
"limit":["SHOOTER"]
}, },
"blind" : "blind" :
{ {
@ -690,16 +759,18 @@
[ [
{ {
"type": "NOT_ACTIVE", "type": "NOT_ACTIVE",
"subtype": 62, //really ase spell id, is it right? "subtype": 62,
//TODO: duration "duration": "UNITL_BEING_ATTACKED N_TURNS",
"values":[0,0,0,0]
}, },
{ {
"type": "GENERAL_ATTACK_REDUCTION" "type": "GENERAL_ATTACK_REDUCTION",
//TODO: duration "duration": "UNITL_BEING_ATTACKED N_TURNS"
}, },
{ {
"type": "NO_RETALIATION", "type": "NO_RETALIATION",
"duration": "UNITL_BEING_ATTACKED" "duration": "UNITL_BEING_ATTACKED",
"values":[0,0,0,0]
} }
] ]
}, },
@ -708,7 +779,8 @@
"id": 63, "id": 63,
"effect": 1, "effect": 1,
"anim": -1, "anim": -1,
"ranges": [ "0", "0", "0", "0" ] "ranges": [ "0", "0", "0", "0" ],
"immunity":["SIEGE_WEAPON"]
}, },
"removeObstacle" : "removeObstacle" :
{ {
@ -722,7 +794,8 @@
"id": 65, "id": 65,
"effect": 1, "effect": 1,
"anim": -1, "anim": -1,
"ranges": [ "0", "0", "0", "0" ] "ranges": [ "0", "0", "0", "0" ],
"immunity":["SIEGE_WEAPON"]
}, },
"fireElemental" : "fireElemental" :
{ {
@ -762,12 +835,14 @@
[ [
{ {
"type": "NOT_ACTIVE", "type": "NOT_ACTIVE",
"subtype": 62 "subtype": 62,
//TODO: duration "duration": "UNITL_BEING_ATTACKED N_TURNS",
"values":[0,0,0,0]
}, },
{ {
"type": "NO_RETALIATION", "type": "NO_RETALIATION",
"duration": "UNITL_BEING_ATTACKED" "duration": "UNITL_BEING_ATTACKED",
"values":[0,0,0,0]
} }
] ]
}, },
@ -781,13 +856,16 @@
[ [
{ {
"type": "POISON", "type": "POISON",
"val" : 30, "valueType": "INDEPENDENT_MAX",
"valueType": "INDEPENDENT_MAX" "duration": "N_TURNS",
"values":[30,30,30,30]
}, },
{ {
"type": "STACK_HEALTH", "type": "STACK_HEALTH",
"val" : -10, "val" : -10,
"valueType": "PERCENT_TO_ALL" "valueType": "PERCENT_TO_ALL",
"duration": "N_TURNS",
"values":[-10,-10,-10,-10]
} }
] ]
}, },
@ -801,9 +879,9 @@
[ [
{ {
"type": "BIND_EFFECT", "type": "BIND_EFFECT",
"val" : 30, "duration" : "PERMANENT",
"turns" : 1, "addInfo" : -1,
"duration" : "PERMANENT" "values":[0,0,0,0]
} }
] ]
}, },
@ -818,12 +896,14 @@
{ {
"type": "PRIMARY_SKILL", "type": "PRIMARY_SKILL",
"subtype": 0, "subtype": 0,
"val" : -2, "duration": "N_TURNS",
"values":[-2,-2,-2,-2]
}, },
{ {
"type": "PRIMARY_SKILL", "type": "PRIMARY_SKILL",
"subtype": 1, "subtype": 1,
"val" : -2, "duration": "N_TURNS",
"values":[-2,-2,-2,-2]
} }
] ]
}, },
@ -838,11 +918,13 @@
{ {
"type": "NOT_ACTIVE", "type": "NOT_ACTIVE",
"subtype": 74, "subtype": 74,
//TODO: duration "duration": "UNITL_BEING_ATTACKED N_TURNS",
"values":[0,0,0,0]
}, },
{ {
"type": "NO_RETALIATION", "type": "NO_RETALIATION",
"duration": "UNITL_BEING_ATTACKED" "duration": "UNITL_BEING_ATTACKED",
"values":[0,0,0,0]
} }
] ]
}, },
@ -856,8 +938,9 @@
[ [
{ {
"type": "STACK_HEALTH", "type": "STACK_HEALTH",
"val" : -50, "valueType": "PERCENT_TO_ALL",
"valueType": "PERCENT_TO_ALL" "duration": "N_TURNS",
"values":[-50,-50,-50,-50]
} }
] ]
}, },
@ -874,7 +957,7 @@
"effect": -1, "effect": -1,
"anim": 38, "anim": 38,
"ranges": [ "0", "0", "0", "0" ], "ranges": [ "0", "0", "0", "0" ],
"flags" : ["damage"] "flags" : ["damage, offensive"]
}, },
"dispelHelpful" : "dispelHelpful" :
{ {
@ -901,9 +984,9 @@
{ {
"type": "PRIMARY_SKILL", "type": "PRIMARY_SKILL",
"subtype": 1, "subtype": 1,
"val" : -3,
"duration" : "PERMANENT", "duration" : "PERMANENT",
"valueType": "ADDITIVE_VALUE" "valueType": "ADDITIVE_VALUE",
"values":[-3,-3,-3,-3]
} }
] ]
}, },

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) int BattleInfo::calculateSpellDuration( const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower)
{ {
if(!caster) if(!caster)
@ -167,9 +166,9 @@ ui32 BattleInfo::calculateHealedHP(const CGHeroInstance * caster, const CSpell *
bool resurrect = resurrects(spell->id); bool resurrect = resurrects(spell->id);
int healedHealth; int healedHealth;
if (spell->id == Spells::SACRIFICE && sacrificedStack) if (spell->id == Spells::SACRIFICE && sacrificedStack)
healedHealth = (caster->getPrimSkillLevel(2) + sacrificedStack->MaxHealth() + spell->powers[caster->getSpellSchoolLevel(spell)]) * sacrificedStack->count; healedHealth = (caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) + sacrificedStack->MaxHealth() + spell->powers[caster->getSpellSchoolLevel(spell)]) * sacrificedStack->count;
else else
healedHealth = caster->getPrimSkillLevel(2) * spell->power + spell->powers[caster->getSpellSchoolLevel(spell)]; healedHealth = caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) * spell->power + spell->powers[caster->getSpellSchoolLevel(spell)];
healedHealth = calculateSpellBonus(healedHealth, spell, caster, stack); healedHealth = calculateSpellBonus(healedHealth, spell, caster, stack);
return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (resurrect ? stack->baseAmount * stack->MaxHealth() : 0)); return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (resurrect ? stack->baseAmount * stack->MaxHealth() : 0));
} }
@ -746,7 +745,7 @@ std::vector<ui32> BattleInfo::calculateResistedStacks(const CSpell * sp, const C
} }
if(sp->id == 60) //hypnotize if(sp->id == Spells::HYPNOTIZE) //hypnotize
{ {
for(auto it = affectedCreatures.begin(); it != affectedCreatures.end(); ++it) for(auto it = affectedCreatures.begin(); it != affectedCreatures.end(); ++it)
{ {
@ -931,163 +930,17 @@ si32 CStack::magicResistance() const
void CStack::stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse) void CStack::stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse)
{ {
//TODO: get rid of this spaghetti code
const CSpell * sp = VLC->spellh->spells[sse.sid]; const CSpell * sp = VLC->spellh->spells[sse.sid];
si32 power = sp->powers[sse.val];
auto addFull = [&](Bonus::BonusType type, si16 subtype, si32 value, si32 additionalInfo, si32 limit) std::vector<Bonus> tmp;
{ sp->getEffects(tmp, sse.val);
sf.push_back(featureGenerator(type, subtype, value, sse.turnsRemain,additionalInfo, limit));
sf.back().sid = sse.sid;
};
auto add = [&](Bonus::BonusType type, si16 subtype, si32 value)
{
addFull(type, subtype, value, 0, Bonus::NO_LIMIT);
};
auto addVT = [&](Bonus::BonusType type, si16 subtype, si32 value, ui8 valType) BOOST_FOREACH(Bonus& b, tmp)
{ {
add(type, subtype, value); b.turnsRemain = sse.turnsRemain;
sf.back().valType = valType; sf.push_back(b);
};
auto addVTFull = [&](Bonus::BonusType type, si16 subtype, si32 value, ui8 valType,si32 additionalInfo)
{
addFull(type, subtype, value, additionalInfo, Bonus::NO_LIMIT);
sf.back().valType = valType;
};
auto addDur = [&](Bonus::BonusType type, si16 subtype, si32 value, ui8 duration)
{
add(type, subtype, value);
sf.back().duration = duration;
};
switch(sse.sid)
{
case Spells::SHIELD:
add(Bonus::GENERAL_DAMAGE_REDUCTION, 0, power);
break;
case Spells::AIR_SHIELD:
add(Bonus::GENERAL_DAMAGE_REDUCTION, 1, power);
break;
case Spells::FIRE_SHIELD:
add(Bonus::FIRE_SHIELD, 0, power);
break;
case Spells::PROTECTION_FROM_AIR:
add(Bonus::SPELL_DAMAGE_REDUCTION, 0, power);
break;
case Spells::PROTECTION_FROM_FIRE:
add(Bonus::SPELL_DAMAGE_REDUCTION, 1, power);
break;
case Spells::PROTECTION_FROM_WATER:
add(Bonus::SPELL_DAMAGE_REDUCTION, 2, power);
break;
case Spells::PROTECTION_FROM_EARTH:
add(Bonus::SPELL_DAMAGE_REDUCTION, 3, power);
break;
case Spells::ANTI_MAGIC:
addVT(Bonus::LEVEL_SPELL_IMMUNITY, GameConstants::SPELL_LEVELS, power - 1, Bonus::INDEPENDENT_MAX);break;
case Spells::MAGIC_MIRROR:
addVT(Bonus::MAGIC_MIRROR, -1, power, Bonus::INDEPENDENT_MAX);
break;
case Spells::BLESS:
addVT(Bonus::ALWAYS_MAXIMUM_DAMAGE, -1, power,Bonus::INDEPENDENT_MAX);
break;
case Spells::CURSE:
addVTFull(Bonus::ALWAYS_MINIMUM_DAMAGE, -1, power, Bonus::INDEPENDENT_MAX, sse.val >= 2 ? 20 : 0);
break;
case Spells::BLOODLUST:
addFull(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, 0, Bonus::ONLY_MELEE_FIGHT);
break;
case Spells::PRECISION:
addFull(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, 0, Bonus::ONLY_DISTANCE_FIGHT);
break;
case Spells::WEAKNESS:
add(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, -1 * power);
break;
case Spells::STONE_SKIN:
add(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, power);
break;
case Spells::DISRUPTING_RAY:
addVT(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, -1 * power,Bonus::ADDITIVE_VALUE);
break;
case Spells::PRAYER:
add(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power);
add(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, power);
add(Bonus::STACKS_SPEED, 0, power);
break;
case Spells::MIRTH:
add(Bonus::MORALE, 0, power);
break;
case Spells::SORROW:
add(Bonus::MORALE, 0, -1 * power);
break;
case Spells::FORTUNE:
add(Bonus::LUCK, 0, power);
break;
case Spells::MISFORTUNE:
add(Bonus::LUCK, 0, -1 * power);
break;
case Spells::HASTE: //haste
add(Bonus::STACKS_SPEED, 0, power);
break;
case Spells::SLOW:
addVT(Bonus::STACKS_SPEED, 0, -1 * ( 100 - power ),Bonus::PERCENT_TO_ALL);
break;
case Spells::SLAYER:
add(Bonus::SLAYER, 0, sse.val);
break;
case Spells::FRENZY:
add(Bonus::IN_FRENZY, 0, power/100.0);
break;
case Spells::COUNTERSTRIKE:
add(Bonus::ADDITIONAL_RETALIATION, 0, power);
break;
case Spells::BERSERK:
add(Bonus::ATTACKS_NEAREST_CREATURE, 0, sse.val);
break;
case Spells::HYPNOTIZE:
add(Bonus::HYPNOTIZED, 0, sse.val);
break;
case Spells::FORGETFULNESS:
add(Bonus::FORGETFULL, 0, sse.val);
break;
case Spells::BLIND:
addDur(Bonus::NOT_ACTIVE, sse.sid, 0, Bonus::UNITL_BEING_ATTACKED | Bonus::N_TURNS);
addDur(Bonus::GENERAL_ATTACK_REDUCTION, 0, power, Bonus::UNITL_BEING_ATTACKED | Bonus::N_TURNS);
addDur(Bonus::NO_RETALIATION,0,0, Bonus::UNITL_BEING_ATTACKED);
break;
case Spells::STONE_GAZE:
case Spells::PARALYZE:
addDur(Bonus::NOT_ACTIVE, sse.sid, 0, Bonus::UNITL_BEING_ATTACKED | Bonus::N_TURNS);
addDur(Bonus::NO_RETALIATION,0,0, Bonus::UNITL_BEING_ATTACKED);
break;
case Spells::POISON: //Poison
addVT(Bonus::POISON, 0, 30,Bonus::INDEPENDENT_MAX); //max hp penalty from this source
addVT(Bonus::STACK_HEALTH, 0, -10, Bonus::PERCENT_TO_ALL);
break;
case Spells::BIND:
sf.push_back(featureGenerator(Bonus::BIND_EFFECT, 0, 0, 1)); //marker
sf.back().duration = Bonus::PERMANENT;
sf.back().sid = sse.sid;
break;
case Spells::DISEASE:
add(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, -2);
add(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, -2);
break;
case Spells::AGE:
addVT(Bonus::STACK_HEALTH, 0, -50, Bonus::PERCENT_TO_ALL);
break;
case Spells::ACID_BREATH_DEFENSE:
sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, -sse.turnsRemain, 1));
sf.back().sid = sse.sid;
sf.back().duration = Bonus::PERMANENT;
sf.back().valType = Bonus::ADDITIVE_VALUE;
break;
} }
} }
bool CStack::willMove(int turn /*= 0*/) const bool CStack::willMove(int turn /*= 0*/) const

View File

@ -182,13 +182,6 @@ public:
return hb; return hb;
} }
static inline Bonus featureGeneratorVT(Bonus::BonusType type, si16 subtype, si32 value, ui16 turnsRemain, ui8 valType)
{
Bonus ret = makeFeatureVal(type, Bonus::N_TURNS, subtype, value, Bonus::SPELL_EFFECT, turnsRemain);
ret.valType = valType;
return ret;
}
static bool isMeleeAttackPossible(const CStack * attacker, const CStack * defender, BattleHex attackerPos = BattleHex::INVALID, BattleHex defenderPos = BattleHex::INVALID); static bool isMeleeAttackPossible(const CStack * attacker, const CStack * defender, BattleHex attackerPos = BattleHex::INVALID, BattleHex defenderPos = BattleHex::INVALID);
bool doubleWide() const; bool doubleWide() const;

View File

@ -835,7 +835,7 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c
if(const Bonus *slayerEffect = info.attackerBonuses->getEffect(Spells::SLAYER)) //slayer handling if(const Bonus *slayerEffect = info.attackerBonuses->getEffect(Spells::SLAYER)) //slayer handling
{ {
std::vector<int> affectedIds; std::vector<int> affectedIds;
int spLevel = slayerEffect->val; int spLevel = slayerEffect->val;
for(int g = 0; g < VLC->creh->creatures.size(); ++g) for(int g = 0; g < VLC->creh->creatures.size(); ++g)
{ {
@ -1487,7 +1487,6 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
{ {
RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID); RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID);
// Get stack at destination hex -> subject of our spell. // Get stack at destination hex -> subject of our spell.
const CStack * subject = battleGetStackByPos(dest, !spell->isRisingSpell()); //only alive if not rising spell const CStack * subject = battleGetStackByPos(dest, !spell->isRisingSpell()); //only alive if not rising spell
@ -1495,28 +1494,14 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
{ {
if (spell->isPositive() && subject->hasBonusOfType(Bonus::RECEPTIVE)) //accept all positive spells if (spell->isPositive() && subject->hasBonusOfType(Bonus::RECEPTIVE)) //accept all positive spells
return ESpellCastProblem::OK; return ESpellCastProblem::OK;
if (spell->isImmuneBy(subject)) //TODO: move all logic to spellhandler
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
switch (spell->id) //TODO: more general logic for new spells? switch (spell->id) //TODO: more general logic for new spells?
{ {
case Spells::DESTROY_UNDEAD:
if (!subject->hasBonusOfType(Bonus::UNDEAD))
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
break;
case Spells::DEATH_RIPPLE:
if (subject->hasBonusOfType(Bonus::SIEGE_WEAPON))
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; //don't break here - undeads and war machines are immune, non-living are not
case Spells::BLESS:
case Spells::CURSE: //undeads are immune to bless & curse
if (subject->hasBonusOfType(Bonus::UNDEAD))
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
break;
case Spells::HASTE:
case Spells::SLOW:
case Spells::TELEPORT:
case Spells::CLONE: case Spells::CLONE:
if (subject->hasBonusOfType(Bonus::SIEGE_WEAPON)) if (caster) //TODO: how about stacks casting Clone?
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; //war machines are immune to some spells than involve movement
if (spell->id == Spells::CLONE && caster) //TODO: how about stacks casting Clone?
{ {
if (vstd::contains(subject->state, EBattleStackState::CLONED)) if (vstd::contains(subject->state, EBattleStackState::CLONED))
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; //can't clone already cloned creature return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; //can't clone already cloned creature
@ -1526,11 +1511,6 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
} }
break; break;
case Spells::FORGETFULNESS:
case Spells::PRECISION:
if (!subject->hasBonusOfType(Bonus::SHOOTER))
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
break;
case Spells::DISPEL_HELPFUL_SPELLS: case Spells::DISPEL_HELPFUL_SPELLS:
{ {
TBonusListPtr spellBon = subject->getSpellBonuses(); TBonusListPtr spellBon = subject->getSpellBonuses();
@ -1551,69 +1531,12 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
break; break;
} }
const bool damageSpell = spell->isDamageSpell();
auto battleTestElementalImmunity = [&](Bonus::BonusType element) -> bool //helper for battleisImmune
{
if (!spell->isPositive()) //negative or indifferent
{
if ((damageSpell && subject->hasBonusOfType(element, 2)) || subject->hasBonusOfType(element, 1))
return true;
}
else if (spell->isPositive()) //positive
{
if (subject->hasBonusOfType(element, 0)) //must be immune to all spells
return true;
}
return false;
};
if (damageSpell && subject->hasBonusOfType(Bonus::DIRECT_DAMAGE_IMMUNITY))
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
if (spell->fire)
{
if (battleTestElementalImmunity(Bonus::FIRE_IMMUNITY))
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
if (spell->water)
{
if (battleTestElementalImmunity(Bonus::WATER_IMMUNITY))
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
if (spell->earth)
{
if (battleTestElementalImmunity(Bonus::EARTH_IMMUNITY))
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
if (spell->air)
{
if (battleTestElementalImmunity(Bonus::AIR_IMMUNITY))
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
if (spell->isMindSpell())
{
if (subject->hasBonusOfType(Bonus::MIND_IMMUNITY))
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
if (spell->isRisingSpell()) if (spell->isRisingSpell())
{ {
if (subject->count >= subject->baseAmount) //TODO: calculate potential hp raised if (subject->count >= subject->baseAmount) //TODO: calculate potential hp raised
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
} }
TBonusListPtr immunities = subject->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY));
if(subject->hasBonusOfType(Bonus::NEGATE_ALL_NATURAL_IMMUNITIES))
{
immunities->remove_if([](const Bonus* b){ return b->source == Bonus::CREATURE_ABILITY; });
}
if(subject->hasBonusOfType(Bonus::SPELL_IMMUNITY, spell->id)
|| ( immunities->size() > 0 && immunities->totalValue() >= spell->level && spell->level))
{
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
} }
else //no target stack on this tile else //no target stack on this tile
{ {
@ -1661,7 +1584,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
} }
if(spell->id < 10) //it's adventure spell (not combat)) if(!spell->combatSpell)
return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL; return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL;
//TODO? //TODO?

View File

@ -130,9 +130,10 @@ CSpellHandler::CSpellHandler()
CSpell::CSpell() CSpell::CSpell()
{ {
_isDamage = false; isDamage = false;
_isMind = false; isMind = false;
_isRising = false; isRising = false;
isOffensive = false;
} }
std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
@ -262,29 +263,118 @@ bool CSpell::isNegative() const
bool CSpell::isRisingSpell() const bool CSpell::isRisingSpell() const
{ {
return _isRising; return isRising;
} }
bool CSpell::isDamageSpell() const bool CSpell::isDamageSpell() const
{ {
return _isDamage; return isDamage;
} }
bool CSpell::isMindSpell() const bool CSpell::isMindSpell() const
{ {
return _isMind; return isMind;
} }
void CSpell::getEffects(std::vector<Bonus>& lst) const bool CSpell::isOffensiveSpell() const
{ {
lst.reserve(lst.size() + _effects.size()); return isOffensive;
}
BOOST_FOREACH (Bonus b, _effects) bool CSpell::hasEffects() const
{
return !effects[0].empty();
}
void CSpell::getEffects(std::vector<Bonus>& lst, const int level) const
{
if (level < 0 || level>3)
{ {
tlog1 << __FUNCTION__ << " invalid school level " << level;
return;
}
lst.reserve(lst.size() + effects[level].size());
BOOST_FOREACH (Bonus b, effects[level])
{
//TODO: value, add value
lst.push_back(b); lst.push_back(b);
} }
} }
bool CSpell::isImmuneBy(const IBonusBearer* obj) const
{
BOOST_FOREACH(auto b, limiters)
{
if (!obj->hasBonusOfType(b))
return true;
}
BOOST_FOREACH(auto b, immunities)
{
if (obj->hasBonusOfType(b))
return true;
}
if (isMindSpell() && obj->hasBonusOfType(Bonus::MIND_IMMUNITY))
return true;
if (isDamageSpell() && obj->hasBonusOfType(Bonus::DIRECT_DAMAGE_IMMUNITY))
return true;
auto battleTestElementalImmunity = [&,this](Bonus::BonusType element) -> bool
{
if (!isPositive()) //negative or indifferent
{
if ((isDamageSpell() && obj->hasBonusOfType(element, 2)) || obj->hasBonusOfType(element, 1))
return true;
}
else if (isPositive()) //positive
{
if (obj->hasBonusOfType(element, 0)) //must be immune to all spells
return true;
}
return false;
};
if (fire)
{
if (battleTestElementalImmunity(Bonus::FIRE_IMMUNITY))
return true;
}
if (water)
{
if (battleTestElementalImmunity(Bonus::WATER_IMMUNITY))
return true;
}
if (earth)
{
if (battleTestElementalImmunity(Bonus::EARTH_IMMUNITY))
return true;
}
if (air)
{
if (battleTestElementalImmunity(Bonus::AIR_IMMUNITY))
return true;
}
TBonusListPtr immunities = obj->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY));
if(obj->hasBonusOfType(Bonus::NEGATE_ALL_NATURAL_IMMUNITIES))
{
immunities->remove_if([](const Bonus* b){ return b->source == Bonus::CREATURE_ABILITY; });
}
if(obj->hasBonusOfType(Bonus::SPELL_IMMUNITY, id)
|| ( immunities->size() > 0 && immunities->totalValue() >= level && level))
{
return true;
}
return false;
}
bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos) bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos)
{ {
@ -329,44 +419,31 @@ void CSpellHandler::loadSpells()
{ {
CLegacyConfigParser parser("DATA/SPTRAITS.TXT"); CLegacyConfigParser parser("DATA/SPTRAITS.TXT");
for(int i=0; i<5; i++) // header auto read = [&,this](bool combat, bool alility)
parser.endLine();
do //read adventure map spells
{ {
CSpell * spell = loadSpell(parser); do
spell->id = spells.size(); {
spell->combatSpell = false; CSpell * spell = loadSpell(parser);
spell->creatureAbility = false; spell->id = spells.size();
spells.push_back(spell); spell->combatSpell = combat;
} spell->creatureAbility = alility;
while (parser.endLine() && !parser.isNextEntryEmpty()); spells.push_back(spell);
}
while (parser.endLine() && !parser.isNextEntryEmpty());
};
for(int i=0; i<3; i++) auto skip = [&](int cnt)
parser.endLine();
do //read battle spells
{ {
CSpell * spell = loadSpell(parser); for(int i=0; i<cnt; i++)
spell->id = spells.size(); parser.endLine();
spell->combatSpell = true; };
spell->creatureAbility = false;
spells.push_back(spell);
}
while (parser.endLine() && !parser.isNextEntryEmpty());
for(int i=0; i<3; i++) skip(5);// header
parser.endLine(); read(false,false); //read adventure map spells
skip(3);
do //read creature abilities read(true,false); //read battle spells
{ skip(3);
CSpell * spell = loadSpell(parser); read(true,true);//read creature abilities
spell->id = spells.size();
spell->combatSpell = true;
spell->creatureAbility = true;
spells.push_back(spell);
}
while (parser.endLine() && !parser.isNextEntryEmpty());
boost::replace_first (spells[Spells::DISRUPTING_RAY]->attributes, "2", ""); // disrupting ray will now affect single creature boost::replace_first (spells[Spells::DISRUPTING_RAY]->attributes, "2", ""); // disrupting ray will now affect single creature
@ -404,35 +481,79 @@ void CSpellHandler::loadSpells()
{ {
if (flag == "damage") if (flag == "damage")
{ {
s->_isDamage = true; s->isDamage = true;
} }
else if (flag == "rising") else if (flag == "rising")
{ {
s->_isRising = true; s->isRising = true;
} }
else if (flag == "mind") else if (flag == "mind")
{ {
s->_isMind = true; s->isMind = true;
}
else if (flag == "offensive")
{
s->isOffensive = true;
} }
} }
} }
const JsonNode & effects_node = spell.second["effects"]; const JsonNode & effects_node = spell.second["effects"];
if (!effects_node.isNull()) BOOST_FOREACH (const JsonNode & bonus_node, effects_node.Vector())
{ {
BOOST_FOREACH (const JsonNode & bonus_node, effects_node.Vector()) auto &v_node = bonus_node["values"];
auto &a_node = bonus_node["ainfos"];
auto v = v_node.convertTo<std::vector<int> >();
auto a = a_node.convertTo<std::vector<int> >();
for (int i=0; i<4 ; i++)
{ {
Bonus * b = JsonUtils::parseBonus(bonus_node); Bonus * b = JsonUtils::parseBonus(bonus_node);
b->sid = s->id; b->sid = s->id; //for all
b->source = Bonus::SPELL_EFFECT; b->source = Bonus::SPELL_EFFECT;//for all
b->duration = Bonus::N_TURNS; //default b->val = s->powers[i];
//TODO: make duration configurable
s->_effects.push_back(*b); if (!v.empty())
b->val = v[i];
if (!a.empty())
b->additionalInfo = a[i];
s->effects[i].push_back(*b);
} }
} }
auto find_in_map = [](std::string name, std::vector<Bonus::BonusType> &vec)
{
auto it = bonusNameMap.find(name);
if (it == bonusNameMap.end())
{
tlog1 << "Error: invalid bonus name" << name << std::endl;
}
else
{
vec.push_back((Bonus::BonusType)it->second);
}
};
auto read_node = [&](std::string name, std::vector<Bonus::BonusType> &vec)
{
const JsonNode & node = spell.second[name];
if (!node.isNull())
{
auto names = node.convertTo<std::vector<std::string> >();
BOOST_FOREACH(auto name, names)
find_in_map(name, vec);
}
};
read_node("immunity",s->immunities);
read_node("limit",s->limiters);
} }
//spell fixes //spell fixes

View File

@ -56,26 +56,33 @@ public:
bool isRisingSpell() const; bool isRisingSpell() const;
bool isDamageSpell() const; bool isDamageSpell() const;
bool isMindSpell() const; bool isMindSpell() const;
bool isOffensiveSpell() const;
bool hasEffects() const;
void getEffects(std::vector<Bonus> &lst, const int level) const;
bool isImmuneBy(const IBonusBearer *obj) const;
void getEffects(std::vector<Bonus> & lst) const;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & identifier & id & name & abbName & descriptions & level & earth & water & fire & air & power & costs h & identifier & id & name & abbName & descriptions & level & earth & water & fire & air & power & costs
& powers & probabilities & AIVals & attributes & combatSpell & creatureAbility & positiveness & range & counteredSpells & mainEffectAnim; & powers & probabilities & AIVals & attributes & combatSpell & creatureAbility & positiveness & range & counteredSpells & mainEffectAnim;
h & _isRising & _isDamage & _isMind; h & isRising & isDamage & isMind;
h & _effects; h & effects & immunities & limiters;
} }
friend class CSpellHandler; friend class CSpellHandler;
private: private:
bool _isRising; bool isRising;
bool _isDamage; bool isDamage;
bool _isMind; bool isMind;
bool isOffensive;
std::vector<Bonus> _effects; std::vector<Bonus> effects [4];
std::vector<Bonus::BonusType> immunities; //any of these hrants immunity
std::vector<Bonus::BonusType> limiters; //all of them are required
}; };

View File

@ -253,7 +253,10 @@ EResType::Type EResTypeHelper::getTypeFromExtension(std::string extension)
(".PAL", EResType::PALETTE) (".PAL", EResType::PALETTE)
(".VCGM1", EResType::CLIENT_SAVEGAME) (".VCGM1", EResType::CLIENT_SAVEGAME)
(".VLGM1", EResType::LIB_SAVEGAME) (".VLGM1", EResType::LIB_SAVEGAME)
(".VSGM1", EResType::SERVER_SAVEGAME); (".VSGM1", EResType::SERVER_SAVEGAME)
(".ERM", EResType::ERM)
(".ERT", EResType::ERT)
(".ERS", EResType::ERS);
auto iter = stringToRes.find(extension); auto iter = stringToRes.find(extension);
if (iter == stringToRes.end()) if (iter == stringToRes.end())
@ -285,6 +288,9 @@ std::string EResTypeHelper::getEResTypeAsString(EResType::Type type)
MAP_ENUM(LIB_SAVEGAME) MAP_ENUM(LIB_SAVEGAME)
MAP_ENUM(SERVER_SAVEGAME) MAP_ENUM(SERVER_SAVEGAME)
MAP_ENUM(DIRECTORY) MAP_ENUM(DIRECTORY)
MAP_ENUM(ERM)
MAP_ENUM(ERT)
MAP_ENUM(ERS)
MAP_ENUM(OTHER); MAP_ENUM(OTHER);
#undef MAP_ENUM #undef MAP_ENUM

View File

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

View File

@ -418,9 +418,9 @@ si32 IBonusBearer::Attack() const
{ {
si32 ret = valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK); si32 ret = valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK);
if (int frenzyPower = valOfBonuses(Bonus::IN_FRENZY)) //frenzy for attacker if (double frenzyPower = valOfBonuses(Bonus::IN_FRENZY)) //frenzy for attacker
{ {
ret += frenzyPower * Defense(false); ret += (frenzyPower/100) * (double)Defense(false);
} }
vstd::amax(ret, 0); vstd::amax(ret, 0);

View File

@ -903,6 +903,25 @@ Bonus * JsonUtils::parseBonus (const JsonVector &ability_vec) //TODO: merge with
b->turnsRemain = 0; b->turnsRemain = 0;
return b; return b;
} }
template <typename T>
const T & parseByMapStr(const std::map<std::string, T> & map, const std::string & val, std::string err)
{
static T defaultValue;
auto it = map.find(val);
if (it == map.end())
{
tlog1 << "Error: invalid " << err << val << std::endl;
return defaultValue;
}
else
{
return it->second;
}
}
template <typename T> template <typename T>
const T & parseByMap(const std::map<std::string, T> & map, const JsonNode * val, std::string err) const T & parseByMap(const std::map<std::string, T> & map, const JsonNode * val, std::string err)
{ {
@ -910,20 +929,11 @@ const T & parseByMap(const std::map<std::string, T> & map, const JsonNode * val,
if (!val->isNull()) if (!val->isNull())
{ {
std::string type = val->String(); std::string type = val->String();
auto it = map.find(type); return parseByMapStr(map, type, err);
if (it == map.end())
{
tlog1 << "Error: invalid " << err << type << std::endl;
return defaultValue;
}
else
{
return it->second;
}
} }
else else
return defaultValue; return defaultValue;
}; }
void JsonUtils::resolveIdentifier (si32 &var, const JsonNode &node, std::string name) void JsonUtils::resolveIdentifier (si32 &var, const JsonNode &node, std::string name)
{ {
@ -937,9 +947,9 @@ void JsonUtils::resolveIdentifier (si32 &var, const JsonNode &node, std::string
var = value->Float(); var = value->Float();
break; break;
case JsonNode::DATA_STRING: case JsonNode::DATA_STRING:
VLC->modh->identifiers.requestIdentifier (value->String(), [&](si32 identifier) VLC->modh->identifiers.requestIdentifier (value->String(), [&](si32 identifier)
{ {
var = identifier; var = identifier;
}); });
break; break;
default: default:
@ -994,7 +1004,18 @@ Bonus * JsonUtils::parseBonus (const JsonNode &ability)
b->valType = parseByMap(bonusLimitEffect, value, "effect range "); b->valType = parseByMap(bonusLimitEffect, value, "effect range ");
value = &ability["duration"]; value = &ability["duration"];
if (!value->isNull()) if (!value->isNull())
b->valType = parseByMap(bonusDurationMap, value, "duration type "); {
int dur = 0;
std::vector<std::string> strs;
boost::split(strs, value->String(), boost::is_any_of("\t "));
BOOST_FOREACH(auto s, strs)
{
dur |=parseByMapStr(bonusDurationMap, s, "duration type ");
}
b->duration = dur;
}
value = &ability["source"]; value = &ability["source"];
if (!value->isNull()) if (!value->isNull())
b->valType = parseByMap(bonusSourceMap, value, "source type "); b->valType = parseByMap(bonusSourceMap, value, "source type ");

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 vstd::amin(sc.dmgToDisplay, (*attackedCres.begin())->count); //stack is already reduced after attack
} }
StacksInjured si; StacksInjured si;
//applying effects //applying effects
switch (spellID)
if (spell->isOffensiveSpell())
{ {
case Spells::QUICKSAND:
case Spells::LAND_MINE:
{
std::vector<BattleHex> availableTiles;
for(int i = 0; i < GameConstants::BFIELD_SIZE; i += 1)
{
BattleHex hex = i;
if(hex.getX() > 2 && hex.getX() < 14 && !battleGetStackByPos(hex, false) & !battleGetObstacleOnPos(hex, false))
availableTiles.push_back(hex);
}
boost::range::random_shuffle(availableTiles);
const int patchesForSkill[] = {4, 4, 6, 8};
const int patchesToPut = std::min<int>(patchesForSkill[spellLvl], availableTiles.size());
//land mines or quicksand patches are handled as spell created obstacles
for (int i = 0; i < patchesToPut; i++)
placeObstacle(availableTiles[i]);
}
break;
case Spells::FORCE_FIELD:
placeObstacle(destination);
break;
case Spells::FIRE_WALL:
{
//fire wall is build from multiple obstacles - one fire piece for each affected hex
auto affectedHexes = spell->rangeInHexes(destination, spellLvl, casterSide);
BOOST_FOREACH(BattleHex hex, affectedHexes)
placeObstacle(hex);
}
break;
//damage spells
case Spells::MAGIC_ARROW:
case Spells::ICE_BOLT:
case Spells::LIGHTNING_BOLT:
case Spells::IMPLOSION:
case Spells::CHAIN_LIGHTNING:
case Spells::FROST_RING:
case Spells::FIREBALL:
case Spells::INFERNO:
case Spells::METEOR_SHOWER:
case Spells::DEATH_RIPPLE:
case Spells::DESTROY_UNDEAD:
case Spells::ARMAGEDDON:
case Spells::TITANS_LIGHTNING_BOLT:
case Spells::THUNDERBOLT: //(thunderbirds)
{
int spellDamage = 0; int spellDamage = 0;
if (stack && mode != ECastingMode::MAGIC_MIRROR) if (stack && mode != ECastingMode::MAGIC_MIRROR)
{ {
@ -4088,48 +4041,10 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
if (spellID == Spells::CHAIN_LIGHTNING) if (spellID == Spells::CHAIN_LIGHTNING)
++chainLightningModifier; ++chainLightningModifier;
} }
break; }
} else if (spell->hasEffects())
// permanent effects {
case Spells::SHIELD:
case Spells::AIR_SHIELD:
case Spells::FIRE_SHIELD:
case Spells::PROTECTION_FROM_AIR:
case Spells::PROTECTION_FROM_FIRE:
case Spells::PROTECTION_FROM_WATER:
case Spells::PROTECTION_FROM_EARTH:
case Spells::ANTI_MAGIC:
case Spells::MAGIC_MIRROR:
case Spells::BLESS:
case Spells::CURSE:
case Spells::BLOODLUST:
case Spells::PRECISION:
case Spells::WEAKNESS:
case Spells::STONE_SKIN:
case Spells::DISRUPTING_RAY:
case Spells::PRAYER:
case Spells::MIRTH:
case Spells::SORROW:
case Spells::FORTUNE:
case Spells::MISFORTUNE:
case Spells::HASTE:
case Spells::SLOW:
case Spells::SLAYER:
case Spells::FRENZY:
case Spells::COUNTERSTRIKE:
case Spells::BERSERK:
case Spells::HYPNOTIZE:
case Spells::FORGETFULNESS:
case Spells::BLIND:
case Spells::STONE_GAZE:
case Spells::POISON:
case Spells::BIND:
case Spells::DISEASE:
case Spells::PARALYZE:
case Spells::AGE:
case Spells::ACID_BREATH_DEFENSE:
{
int stackSpellPower = 0; int stackSpellPower = 0;
if (stack && mode != ECastingMode::MAGIC_MIRROR) if (stack && mode != ECastingMode::MAGIC_MIRROR)
{ {
@ -4204,25 +4119,10 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
if(!sse.stacks.empty()) if(!sse.stacks.empty())
sendAndApply(&sse); sendAndApply(&sse);
break;
} }
case Spells::TELEPORT: else if (spell->isRisingSpell() || spell->id == Spells::CURE)
{ {
BattleStackMoved bsm;
bsm.distance = -1;
bsm.stack = selectedStack;
std::vector<BattleHex> tiles;
tiles.push_back(destination);
bsm.tilesToMove = tiles;
bsm.teleporting = true;
sendAndApply(&bsm);
break;
}
case Spells::CURE:
case Spells::RESURRECTION:
case Spells::ANIMATE_DEAD:
case Spells::SACRIFICE:
{
int hpGained = 0; int hpGained = 0;
if (stack) if (stack)
{ {
@ -4264,7 +4164,53 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
BattleStacksRemoved bsr; BattleStacksRemoved bsr;
bsr.stackIDs.insert (selectedStack); //somehow it works for teleport? bsr.stackIDs.insert (selectedStack); //somehow it works for teleport?
sendAndApply(&bsr); sendAndApply(&bsr);
}
}
else
switch (spellID)
{
case Spells::QUICKSAND:
case Spells::LAND_MINE:
{
std::vector<BattleHex> availableTiles;
for(int i = 0; i < GameConstants::BFIELD_SIZE; i += 1)
{
BattleHex hex = i;
if(hex.getX() > 2 && hex.getX() < 14 && !battleGetStackByPos(hex, false) & !battleGetObstacleOnPos(hex, false))
availableTiles.push_back(hex);
} }
boost::range::random_shuffle(availableTiles);
const int patchesForSkill[] = {4, 4, 6, 8};
const int patchesToPut = std::min<int>(patchesForSkill[spellLvl], availableTiles.size());
//land mines or quicksand patches are handled as spell created obstacles
for (int i = 0; i < patchesToPut; i++)
placeObstacle(availableTiles[i]);
}
break;
case Spells::FORCE_FIELD:
placeObstacle(destination);
break;
case Spells::FIRE_WALL:
{
//fire wall is build from multiple obstacles - one fire piece for each affected hex
auto affectedHexes = spell->rangeInHexes(destination, spellLvl, casterSide);
BOOST_FOREACH(BattleHex hex, affectedHexes)
placeObstacle(hex);
}
break;
case Spells::TELEPORT:
{
BattleStackMoved bsm;
bsm.distance = -1;
bsm.stack = selectedStack;
std::vector<BattleHex> tiles;
tiles.push_back(destination);
bsm.tilesToMove = tiles;
bsm.teleporting = true;
sendAndApply(&bsm);
break; break;
} }
case Spells::SUMMON_FIRE_ELEMENTAL: case Spells::SUMMON_FIRE_ELEMENTAL:
@ -5536,7 +5482,7 @@ bool CGameHandler::castSpell(const CGHeroInstance *h, int spellID, const int3 &p
GiveBonus gb; GiveBonus gb;
gb.id = h->id; gb.id = h->id;
gb.bonus = Bonus(Bonus::ONE_DAY, Bonus::WATER_WALKING, Bonus::SPELL_EFFECT, 0, Spells::FLY, subtype); gb.bonus = Bonus(Bonus::ONE_DAY, Bonus::WATER_WALKING, Bonus::SPELL_EFFECT, 0, Spells::WATER_WALK, subtype);
sendAndApply(&gb); sendAndApply(&gb);
} }
break; break;