diff --git a/config/spell_info.json b/config/spell_info.json index 751e361a3..f871c1bc1 100644 --- a/config/spell_info.json +++ b/config/spell_info.json @@ -1,94 +1,920 @@ -{ - // Additional spell info, not included in original heroes III files - // id: spell ID - // effect: -1 -> spell is negative for influenced creatures, - // 0 -> spell is indifferent for them - // 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]) - - "spells": - { - "summonBoat" : { "id": 0, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "scuttleBoat" : { "id": 1, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "visions" : { "id": 2, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "viewEarth" : { "id": 3, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "disguise" : { "id": 4, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "viewAir" : { "id": 5, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "fly" : { "id": 6, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "waterWalk" : { "id": 7, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "dimensionDoor" : { "id": 8, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "townPortal" : { "id": 9, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "quicksand" : { "id": 10, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "landMine" : { "id": 11, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "forceField" : { "id": 12, "effect": 0, "anim": -1, "ranges": [ "0", "0", "0", "0" ] }, - "fireWall" : { "id": 13, "effect": 0, "anim": -1, "ranges": [ "0", "0", "0", "0" ] }, - "earthquake" : { "id": 14, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "magicArrow" : { "id": 15, "effect": -1, "anim": 64, "ranges": [ "0", "0", "0", "0" ] }, - "iceBolt" : { "id": 16, "effect": -1, "anim": 46, "ranges": [ "0", "0", "0", "0" ] }, - "lightningBolt" : { "id": 17, "effect": -1, "anim": 38, "ranges": [ "0", "0", "0", "0" ] }, - "implosion" : { "id": 18, "effect": -1, "anim": 10, "ranges": [ "0", "0", "0", "0" ] }, - "chainLightning" : { "id": 19, "effect": -1, "anim": 38, "ranges": [ "0", "0", "0", "0" ] }, - "frostRing" : { "id": 20, "effect": -1, "anim": 45, "ranges": [ "1", "1", "1", "1" ] }, - "fireball" : { "id": 21, "effect": -1, "anim": 53, "ranges": [ "0,1", "0,1", "0,1", "0,1" ] }, - "inferno" : { "id": 22, "effect": -1, "anim": 9, "ranges": [ "0-2", "0-2", "0-2", "0-2" ] }, - "meteorShower" : { "id": 23, "effect": -1, "anim": 16, "ranges": [ "0,1", "0,1", "0,1", "0,1" ] }, - "deathRipple" : { "id": 24, "effect": -1, "anim": 8, "ranges": [ "X", "X", "X", "X" ] }, - "destroyUndead" : { "id": 25, "effect": -1, "anim": 29, "ranges": [ "X", "X", "X", "X" ] }, - "armageddon" : { "id": 26, "effect": -1, "anim": 12, "ranges": [ "X", "X", "X", "X" ] }, - "shield" : { "id": 27, "effect": 1, "anim": 27, "ranges": [ "0", "0", "0", "X" ] }, - "airShield" : { "id": 28, "effect": 1, "anim": 2, "ranges": [ "0", "0", "0", "X" ] }, - "fireShield" : { "id": 29, "effect": 1, "anim": 11, "ranges": [ "0", "0", "0", "X" ] }, - "protectAir" : { "id": 30, "effect": 1, "anim": 22, "ranges": [ "0", "0", "0", "X" ] }, - "protectFire" : { "id": 31, "effect": 1, "anim": 24, "ranges": [ "0", "0", "0", "X" ] }, - "protectWater" : { "id": 32, "effect": 1, "anim": 23, "ranges": [ "0", "0", "0", "X" ] }, - "protectEarth" : { "id": 33, "effect": 1, "anim": 26, "ranges": [ "0", "0", "0", "X" ] }, - "antiMagic" : { "id": 34, "effect": 1, "anim": 5, "ranges": [ "0", "0", "0", "X" ] }, - "dispel" : { "id": 35, "effect": 0, "anim": 41, "ranges": [ "0", "0", "0", "X" ] }, - "magicMirror" : { "id": 36, "effect": 1, "anim": 3, "ranges": [ "0", "0", "0", "0" ] }, - "cure" : { "id": 37, "effect": 1, "anim": 39, "ranges": [ "0", "0", "0", "0" ] }, - "resurrection" : { "id": 38, "effect": 1, "anim": 79, "ranges": [ "0", "0", "0", "0" ] }, - "animateDead" : { "id": 39, "effect": 1, "anim": 79, "ranges": [ "0", "0", "0", "0" ] }, - "sacrifice" : { "id": 40, "effect": 1, "anim": 79, "ranges": [ "0", "0", "0", "0" ] }, - "bless" : { "id": 41, "effect": 1, "anim": 36, "ranges": [ "0", "0", "0", "X" ], "counters" : [42] }, - "curse" : { "id": 42, "effect": -1, "anim": 40, "ranges": [ "0", "0", "0", "X" ], "counters" : [41] }, - "bloodlust" : { "id": 43, "effect": 1, "anim": 4, "ranges": [ "0", "0", "0", "X" ], "counters" : [45] }, - "precision" : { "id": 44, "effect": 1, "anim": 25, "ranges": [ "0", "0", "0", "X" ] }, - "weakness" : { "id": 45, "effect": -1, "anim": 56, "ranges": [ "0", "0", "0", "X" ], "counters" : [43] }, - "stoneSkin" : { "id": 46, "effect": 1, "anim": 54, "ranges": [ "0", "0", "0", "X" ] }, - "disruptingRay" : { "id": 47, "effect": -1, "anim": 14, "ranges": [ "0", "0", "0", "0" ] }, - "prayer" : { "id": 48, "effect": 1, "anim": 0, "ranges": [ "0", "0", "0", "X" ] }, - "mirth" : { "id": 49, "effect": 1, "anim": 20, "ranges": [ "0", "0", "0", "X" ], "counters" : [50] }, - "sorrow" : { "id": 50, "effect": -1, "anim": 30, "ranges": [ "0", "0", "0", "X" ], "counters" : [49] }, - "fortune" : { "id": 51, "effect": 1, "anim": 18, "ranges": [ "0", "0", "0", "X" ], "counters" : [52] }, - "misfortune" : { "id": 52, "effect": -1, "anim": 48, "ranges": [ "0", "0", "0", "X" ], "counters" : [51] }, - "haste" : { "id": 53, "effect": 1, "anim": 31, "ranges": [ "0", "0", "0", "X" ], "counters" : [54] }, - "slow" : { "id": 54, "effect": -1, "anim": 19, "ranges": [ "0", "0", "0", "X" ], "counters" : [53] }, - "slayer" : { "id": 55, "effect": 1, "anim": 28, "ranges": [ "0", "0", "0", "0" ] }, - "frenzy" : { "id": 56, "effect": 1, "anim": 17, "ranges": [ "0", "0", "0", "0" ] }, - "titanBolt" : { "id": 57, "effect": -1, "anim": 38, "ranges": [ "0", "0", "0", "0" ] }, - "counterstrike" : { "id": 58, "effect": 1, "anim": 7, "ranges": [ "0", "0", "0", "X" ] }, - "berserk" : { "id": 59, "effect": -1, "anim": 35, "ranges": [ "0", "0", "0-1", "0-2" ] }, - "hypnotize" : { "id": 60, "effect": -1, "anim": 21, "ranges": [ "0", "0", "0", "0" ] }, - "forgetfulness" : { "id": 61, "effect": -1, "anim": 42, "ranges": [ "0", "0", "0", "X" ] }, - "blind" : { "id": 62, "effect": -1, "anim": 6, "ranges": [ "0", "0", "0", "0" ] }, - "teleport" : { "id": 63, "effect": 1, "anim": -1, "ranges": [ "0", "0", "0", "0" ] }, - "removeObstacle" : { "id": 64, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "clone" : { "id": 65, "effect": 1, "anim": -1, "ranges": [ "0", "0", "0", "0" ] }, - "fireElemental" : { "id": 66, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "earthElemental" : { "id": 67, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "waterElemental" : { "id": 68, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "airElemental" : { "id": 69, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] }, - "stoneGaze" : { "id": 70, "effect": 0, "anim": 70, "ranges": [ "0", "0", "0", "0" ] }, - "poison" : { "id": 71, "effect": -1, "anim": 67, "ranges": [ "0", "0", "0", "0" ] }, - "bind" : { "id": 72, "effect": 0, "anim": 68, "ranges": [ "0", "0", "0", "0" ] }, - "disease" : { "id": 73, "effect": -1, "anim": 69, "ranges": [ "0", "0", "0", "0" ] }, - "paralyze" : { "id": 74, "effect": -1, "anim": 70, "ranges": [ "0", "0", "0", "0" ] }, - "age" : { "id": 75, "effect": -1, "anim": 71, "ranges": [ "0", "0", "0", "0" ] }, - "deathCloud" : { "id": 76, "effect": 0, "anim": 72, "ranges": [ "0-1", "0-1", "0-1", "0-1" ] }, - "thunderbolt" : { "id": 77, "effect": -1, "anim": 38, "ranges": [ "0", "0", "0", "0" ] }, - "dispelHelpful" : { "id": 78, "effect": -1, "anim": 41, "ranges": [ "0", "0", "0", "0" ] }, - "deathStare" : { "id": 79, "effect": 0, "anim": 80, "ranges": [ "0", "0", "0", "0" ] }, - "acidBreath" : { "id": 80, "effect": 0, "anim": 81, "ranges": [ "0", "0", "0", "0" ] } - } -} +{ + // Additional spell info, not included in original heroes III files + // id: spell ID + // effect: -1 -> spell is negative for influenced creatures, + // 0 -> spell is indifferent for them + // 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]) + + // flags: string array of + // damage + // directDamage //todo + // rising + // mind + + //effects: array of bonus for permanent effects + + "spells": + { + "summonBoat" : + { + "id": 0, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ] + }, + "scuttleBoat" : + { + "id": 1, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ] + }, + "visions" : + { + "id": 2, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ] + }, + "viewEarth" : + { + "id": 3, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ] + }, + "disguise" : + { + "id": 4, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ] + }, + "viewAir" : + { + "id": 5, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ] + }, + "fly" : + { + "id": 6, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ] + }, + "waterWalk" : + { + "id": 7, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ] + }, + "dimensionDoor" : + { + "id": 8, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ] + }, + "townPortal" : + { + "id": 9, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ] + }, + "quicksand" : + { + "id": 10, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ] + }, + "landMine" : + { + "id": 11, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ], + "flags" : ["damage"] + }, + "forceField" : + { + "id": 12, + "effect": 0, + "anim": -1, + "ranges": [ "0", "0", "0", "0" ] + }, + "fireWall" : + { + "id": 13, + "effect": 0, + "anim": -1, + "ranges": [ "0", "0", "0", "0" ], + "flags" : ["damage"] + }, + "earthquake" : + { + "id": 14, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ] + }, + "magicArrow" : + { + "id": 15, + "effect": -1, + "anim": 64, + "ranges": [ "0", "0", "0", "0" ], + "flags" : ["damage"] + }, + "iceBolt" : + { + "id": 16, + "effect": -1, + "anim": 46, + "ranges": [ "0", "0", "0", "0" ], + "flags" : ["damage"] + }, + "lightningBolt" : + { + "id": 17, + "effect": -1, + "anim": 38, + "ranges": [ "0", "0", "0", "0" ], + "flags" : ["damage"] + }, + "implosion" : + { + "id": 18, + "effect": -1, + "anim": 10, + "ranges": [ "0", "0", "0", "0" ], + "flags" : ["damage"] + }, + "chainLightning" : + { + "id": 19, + "effect": -1, + "anim": 38, + "ranges": [ "0", "0", "0", "0" ], + "flags" : ["damage"] + }, + "frostRing" : + { + "id": 20, + "effect": -1, + "anim": 45, + "ranges": [ "1", "1", "1", "1" ], + "flags" : ["damage"] + }, + "fireball" : + { + "id": 21, + "effect": -1, + "anim": 53, + "ranges": [ "0,1", "0,1", "0,1", "0,1" ], + "flags" : ["damage"] + }, + "inferno" : + { + "id": 22, + "effect": -1, + "anim": 9, + "ranges": [ "0-2", "0-2", "0-2", "0-2" ], + "flags" : ["damage"] + }, + "meteorShower" : + { + "id": 23, + "effect": -1, + "anim": 16, + "ranges": [ "0,1", "0,1", "0,1", "0,1" ], + "flags" : ["damage"] + }, + "deathRipple" : + { + "id": 24, + "effect": -1, + "anim": 8, + "ranges": [ "X", "X", "X", "X" ], + "flags" : ["damage"] + }, + "destroyUndead" : + { + "id": 25, + "effect": -1, + "anim": 29, + "ranges": [ "X", "X", "X", "X" ], + "flags" : ["damage"] + }, + "armageddon" : + { + "id": 26, + "effect": -1, + "anim": 12, + "ranges": [ "X", "X", "X", "X" ], + "flags" : ["damage"] + }, + "shield" : + { + "id": 27, + "effect": 1, + "anim": 27, + "ranges": [ "0", "0", "0", "X" ], + "effects": + [ + { + "type": "GENERAL_DAMAGE_REDUCTION", + "subtype":0 + } + ] + }, + "airShield" : + { + "id": 28, + "effect": 1, + "anim": 2, + "ranges": [ "0", "0", "0", "X" ], + "effects": + [ + { + "type": "GENERAL_DAMAGE_REDUCTION", + "subtype":1 + } + ] + }, + "fireShield" : + { + "id": 29, + "effect": 1, + "anim": 11, + "ranges": [ "0", "0", "0", "X" ], + "effects": + [ + { + "type": "FIRE_SHIELD" + } + ] + }, + "protectAir" : + { + "id": 30, + "effect": 1, + "anim": 22, + "ranges": [ "0", "0", "0", "X" ], + "effects": + [ + { + "type": "SPELL_DAMAGE_REDUCTION", + "subtype":0 + } + ] + }, + "protectFire" : + { + "id": 31, + "effect": 1, + "anim": 24, + "ranges": [ "0", "0", "0", "X" ], + "effects": + [ + { + "type": "SPELL_DAMAGE_REDUCTION", + "subtype":1 + } + ] + }, + "protectWater" : + { + "id": 32, + "effect": 1, + "anim": 23, + "ranges": [ "0", "0", "0", "X" ], + "effects": + [ + { + "type": "SPELL_DAMAGE_REDUCTION", + "subtype":2 + } + ] + }, + "protectEarth" : + { + "id": 33, + "effect": 1, + "anim": 26, + "ranges": [ "0", "0", "0", "X" ], + "effects": + [ + { + "type": "SPELL_DAMAGE_REDUCTION", + "subtype":3 + } + ] + }, + "antiMagic" : + { + "id": 34, + "effect": 1, + "anim": 5, + "ranges": [ "0", "0", "0", "X" ], + "effects": + [ + { + "type": "LEVEL_SPELL_IMMUNITY", + "subtype":5, + "valType":"INDEPENDENT_MAX" + } + ] + }, + "dispel" : + { + "id": 35, + "effect": 0, + "anim": 41, + "ranges": [ "0", "0", "0", "X" ] + }, + "magicMirror" : + { + "id": 36, + "effect": 1, + "anim": 3, + "ranges": [ "0", "0", "0", "0" ], + "effects": + [ + { + "type": "MAGIC_MIRROR", + "valType":"INDEPENDENT_MAX" + } + ] + }, + "cure" : + { + "id": 37, + "effect": 1, + "anim": 39, + "ranges": [ "0", "0", "0", "0" ], + "flags" : ["rising"] + }, + "resurrection" : + { + "id": 38, + "effect": 1, + "anim": 79, + "ranges": [ "0", "0", "0", "0" ], + "flags" : ["rising"] + }, + "animateDead" : + { + "id": 39, + "effect": 1, + "anim": 79, + "ranges": [ "0", "0", "0", "0" ], + "flags" : ["rising"] + }, + "sacrifice" : + { + "id": 40, + "effect": 1, + "anim": 79, + "ranges": [ "0", "0", "0", "0" ] + }, + "bless" : + { + "id": 41, + "effect": 1, + "anim": 36, + "ranges": [ "0", "0", "0", "X" ], + "counters" : [42], + "effects": + [ + { + "type": "ALWAYS_MAXIMUM_DAMAGE", + "valType":"INDEPENDENT_MAX" + } + ] + }, + "curse" : + { + "id": 42, + "effect": -1, + "anim": 40, + "ranges": [ "0", "0", "0", "X" ], "counters" : [41], + "effects": + [ + { + "type": "ALWAYS_MINIMUM_DAMAGE", + "valType":"INDEPENDENT_MAX" + } + ] + }, + "bloodlust" : + { + "id": 43, + "effect": 1, + "anim": 4, + "ranges": [ "0", "0", "0", "X" ], "counters" : [45], + "effects": + [ + { + "type": "PRIMARY_SKILL", + "subtype": 0, //ATTACK + "effectRange" : "ONLY_MELEE_FIGHT" + } + ] + }, + "precision" : + { + "id": 44, + "effect": 1, + "anim": 25, + "ranges": [ "0", "0", "0", "X" ], + "effects": + [ + { + "type": "PRIMARY_SKILL", + "subtype": 0, //ATTACK + "effectRange" : "ONLY_DISTANCE_FIGHT" + } + ] + }, + "weakness" : + { + "id": 45, + "effect": -1, + "anim": 56, + "ranges": [ "0", "0", "0", "X" ], "counters" : [43], + "effects": + [ + { + "type": "PRIMARY_SKILL", + "subtype": 0 //ATTACK + } + ] + }, + "stoneSkin" : + { + "id": 46, + "effect": 1, + "anim": 54, + "ranges": [ "0", "0", "0", "X" ], + "effects": + [ + { + "type": "PRIMARY_SKILL", + "subtype": 1 //DEFENSE + } + ] + }, + "disruptingRay" : + { + "id": 47, + "effect": -1, + "anim": 14, + "ranges": [ "0", "0", "0", "0" ], + "effects": + [ + { + "type": "PRIMARY_SKILL", + "subtype": 1 //DEFENSE + } + ] + }, + "prayer" : + { + "id": 48, + "effect": 1, + "anim": 0, + "ranges": [ "0", "0", "0", "X" ], + "effects": + [ + { + "type": "PRIMARY_SKILL", + "subtype": 0 //ATTACK + }, + { + "type": "PRIMARY_SKILL", + "subtype": 1 //DEFENSE + }, + { + "type": "STACKS_SPEED" + } + ] + }, + "mirth" : + { + "id": 49, + "effect": 1, + "anim": 20, + "ranges": [ "0", "0", "0", "X" ], + "counters" : [50], + "effects": + [ + { + "type": "MORALE" + } + ] + }, + "sorrow" : + { + "id": 50, + "effect": -1, + "anim": 30, + "ranges": [ "0", "0", "0", "X" ], "counters" : [49], + "flags" : ["mind"], + "effects": + [ + { + "type": "MORALE" + } + ] + }, + "fortune" : + { + "id": 51, + "effect": 1, + "anim": 18, + "ranges": [ "0", "0", "0", "X" ], + "counters" : [52], + "effects": + [ + { + "type": "LUCK" + } + ] + }, + "misfortune" : + { + "id": 52, + "effect": -1, + "anim": 48, + "ranges": [ "0", "0", "0", "X" ], + "counters" : [51], + "effects": + [ + { + "type": "LUCK" + } + ] + }, + "haste" : + { + "id": 53, + "effect": 1, + "anim": 31, + "ranges": [ "0", "0", "0", "X" ], + "counters" : [54], + "effects": + [ + { + "type": "STACKS_SPEED" + } + ] + }, + "slow" : + { + "id": 54, + "effect": -1, + "anim": 19, + "ranges": [ "0", "0", "0", "X" ], + "counters" : [53], + "effects": + [ + { + "type": "STACKS_SPEED" + } + ] + }, + "slayer" : + { + "id": 55, + "effect": 1, + "anim": 28, + "ranges": [ "0", "0", "0", "0" ], + "effects": + [ + { + "type": "SLAYER" + } + ] + }, + "frenzy" : + { + "id": 56, + "effect": 1, + "anim": 17, + "ranges": [ "0", "0", "0", "0" ], + "effects": + [ + { + "type": "IN_FRENZY" + } + ] + }, + "titanBolt" : + { + "id": 57, + "effect": -1, + "anim": 38, + "ranges": [ "0", "0", "0", "0" ], + "flags" : ["damage"] + }, + "counterstrike" : + { + "id": 58, + "effect": 1, + "anim": 7, + "ranges": [ "0", "0", "0", "X" ], + "effects": + [ + { + "type": "ADDITIONAL_RETALIATION" + } + ] + }, + "berserk" : + { + "id": 59, + "effect": -1, + "anim": 35, + "ranges": [ "0", "0", "0-1", "0-2" ], + "flags" : ["mind"], + "effects": + [ + { + "type": "ATTACKS_NEAREST_CREATURE" + } + ] + }, + "hypnotize" : + { + "id": 60, + "effect": -1, + "anim": 21, + "ranges": [ "0", "0", "0", "0" ], + "flags" : ["mind"], + "effects": + [ + { + "type": "HYPNOTIZED" + } + ] + }, + "forgetfulness" : + { + "id": 61, + "effect": -1, + "anim": 42, + "ranges": [ "0", "0", "0", "X" ], + "flags" : ["mind"], + "effects": + [ + { + "type": "FORGETFULL" + } + ] + }, + "blind" : + { + "id": 62, + "effect": -1, + "anim": 6, + "ranges": [ "0", "0", "0", "0" ], + "flags" : ["mind"], + "effects": + [ + { + "type": "NOT_ACTIVE", + "subtype": 62, //really ase spell id, is it right? + //TODO: duration + }, + { + "type": "GENERAL_ATTACK_REDUCTION" + //TODO: duration + }, + { + "type": "NO_RETALIATION", + "duration": "UNITL_BEING_ATTACKED" + } + ] + }, + "teleport" : + { + "id": 63, + "effect": 1, + "anim": -1, + "ranges": [ "0", "0", "0", "0" ] + }, + "removeObstacle" : + { + "id": 64, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ] + }, + "clone" : + { + "id": 65, + "effect": 1, + "anim": -1, + "ranges": [ "0", "0", "0", "0" ] + }, + "fireElemental" : + { + "id": 66, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ] + }, + "earthElemental" : + { + "id": 67, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ] + }, + "waterElemental" : + { + "id": 68, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ] + }, + "airElemental" : + { + "id": 69, + "effect": 0, + "anim": -1, + "ranges": [ "X", "X", "X", "X" ] + }, + "stoneGaze" : + { + "id": 70, + "effect": 0, + "anim": 70, + "ranges": [ "0", "0", "0", "0" ], + "effects": + [ + { + "type": "NOT_ACTIVE", + "subtype": 62 + //TODO: duration + }, + { + "type": "NO_RETALIATION", + "duration": "UNITL_BEING_ATTACKED" + } + ] + }, + "poison" : + { + "id": 71, + "effect": -1, + "anim": 67, + "ranges": [ "0", "0", "0", "0" ], + "effects": + [ + { + "type": "POISON", + "val" : 30, + "valueType": "INDEPENDENT_MAX" + }, + { + "type": "STACK_HEALTH", + "val" : -10, + "valueType": "PERCENT_TO_ALL" + } + ] + }, + "bind" : + { + "id": 72, + "effect": 0, + "anim": 68, + "ranges": [ "0", "0", "0", "0" ], + "effects": + [ + { + "type": "BIND_EFFECT", + "val" : 30, + "turns" : 1, + "duration" : "PERMANENT" + } + ] + }, + "disease" : + { + "id": 73, + "effect": -1, + "anim": 69, + "ranges": [ "0", "0", "0", "0" ], + "effects": + [ + { + "type": "PRIMARY_SKILL", + "subtype": 0, + "val" : -2, + }, + { + "type": "PRIMARY_SKILL", + "subtype": 1, + "val" : -2, + } + ] + }, + "paralyze" : + { + "id": 74, + "effect": -1, + "anim": 70, + "ranges": [ "0", "0", "0", "0" ], + "effects": + [ + { + "type": "NOT_ACTIVE", + "subtype": 74, + //TODO: duration + }, + { + "type": "NO_RETALIATION", + "duration": "UNITL_BEING_ATTACKED" + } + ] + }, + "age" : + { + "id": 75, + "effect": -1, + "anim": 71, + "ranges": [ "0", "0", "0", "0" ], + "effects": + [ + { + "type": "STACK_HEALTH", + "val" : -50, + "valueType": "PERCENT_TO_ALL" + } + ] + }, + "deathCloud" : + { + "id": 76, + "effect": 0, + "anim": 72, + "ranges": [ "0-1", "0-1", "0-1", "0-1" ] + }, + "thunderbolt" : + { + "id": 77, + "effect": -1, + "anim": 38, + "ranges": [ "0", "0", "0", "0" ], + "flags" : ["damage"] + }, + "dispelHelpful" : + { + "id": 78, + "effect": -1, + "anim": 41, + "ranges": [ "0", "0", "0", "0" ] + }, + "deathStare" : + { + "id": 79, + "effect": 0, + "anim": 80, + "ranges": [ "0", "0", "0", "0" ] + }, + "acidBreath" : + { + "id": 80, + "effect": 0, + "anim": 81, + "ranges": [ "0", "0", "0", "0" ], + "effects": + [ + { + "type": "PRIMARY_SKILL", + "subtype": 1, + "val" : -3, + "duration" : "PERMANENT", + "valueType": "ADDITIVE_VALUE" + } + ] + }, + "acidBreathDamage" : + { + "id": 81, + "effect": 0, + "anim": 81, + "ranges": [ "0", "0", "0", "0" ], + "flags" : ["damage"] + + } + } +} diff --git a/lib/BattleState.cpp b/lib/BattleState.cpp index 44482244a..d526aec56 100644 --- a/lib/BattleState.cpp +++ b/lib/BattleState.cpp @@ -186,7 +186,7 @@ ui32 BattleInfo::calculateHealedHP(const CSpell * spell, int usedSpellPower, int } bool BattleInfo::resurrects(TSpell spellid) const { - return vstd::contains(VLC->spellh->risingSpells, spellid); + return VLC->spellh->spells[spellid]->isRisingSpell(); } const CStack * BattleInfo::battleGetStack(BattleHex pos, bool onlyAlive) @@ -931,170 +931,146 @@ si32 CStack::magicResistance() const void CStack::stackEffectToFeature(std::vector & sf, const Bonus & sse) { - si32 power = VLC->spellh->spells[sse.sid]->powers[sse.val]; + //TODO: get rid of this spaghetti code + + const CSpell * sp = VLC->spellh->spells[sse.sid]; + si32 power = sp->powers[sse.val]; + + auto add = [&](Bonus::BonusType type, si16 subtype, si32 value,si32 additionalInfo = 0, si32 limit = Bonus::NO_LIMIT) + { + sf.push_back(featureGenerator(type, subtype, value, sse.turnsRemain,additionalInfo, limit)); + sf.back().sid = sse.sid; + }; + + auto addVT = [&](Bonus::BonusType type, si16 subtype, si32 value, ui8 valType,si32 additionalInfo = 0, si32 limit = Bonus::NO_LIMIT) + { + add(type, subtype, value, additionalInfo, limit); + sf.back().valType = valType; + }; + + auto addDur = [&](Bonus::BonusType type, si16 subtype, si32 value, ui8 duration ,si32 additionalInfo = 0, si32 limit = Bonus::NO_LIMIT) + { + add(type, subtype, value, additionalInfo, limit); + sf.back().duration = duration; + }; + switch(sse.sid) { - case 27: //shield - sf.push_back(featureGenerator(Bonus::GENERAL_DAMAGE_REDUCTION, 0, power, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 28: //air shield - sf.push_back(featureGenerator(Bonus::GENERAL_DAMAGE_REDUCTION, 1, power, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 29: //fire shield - sf.push_back(featureGenerator(Bonus::FIRE_SHIELD, 0, power, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 30: //protection from air - sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 0, power, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 31: //protection from fire - sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 1, power, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 32: //protection from water - sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 2, power, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 33: //protection from earth - sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 3, power, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 34: //anti-magic - sf.push_back(featureGenerator(Bonus::LEVEL_SPELL_IMMUNITY, GameConstants::SPELL_LEVELS, power - 1, sse.turnsRemain)); - sf.back().valType = Bonus::INDEPENDENT_MAX; - sf.back().sid = sse.sid; - break; - case 36: //magic mirror - sf.push_back(featureGenerator(Bonus::MAGIC_MIRROR, -1, power, sse.turnsRemain)); - sf.back().valType = Bonus::INDEPENDENT_MAX; - sf.back().sid = sse.sid; - case 41: //bless - sf.push_back(featureGenerator(Bonus::ALWAYS_MAXIMUM_DAMAGE, -1, power, sse.turnsRemain)); - sf.back().valType = Bonus::INDEPENDENT_MAX; - sf.back().sid = sse.sid; - break; - case 42: //curse - sf.push_back(featureGenerator(Bonus::ALWAYS_MINIMUM_DAMAGE, -1, power, sse.turnsRemain, sse.val >= 2 ? 20 : 0)); - sf.back().valType = Bonus::INDEPENDENT_MAX; - sf.back().sid = sse.sid; - break; - case 43: //bloodlust - sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, sse.turnsRemain, 0, Bonus::ONLY_MELEE_FIGHT)); - sf.back().sid = sse.sid; - break; - case 44: //precision - sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, sse.turnsRemain, 0, Bonus::ONLY_DISTANCE_FIGHT)); - sf.back().sid = sse.sid; - break; - case 45: //weakness - sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, -1 * power, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 46: //stone skin - sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, power, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 47: //disrupting ray - sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, -1 * power, sse.turnsRemain)); - sf.back().sid = sse.sid; - sf.back().valType = Bonus::ADDITIVE_VALUE; - break; - case 48: //prayer - sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, sse.turnsRemain)); - sf.back().sid = sse.sid; - sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, power, sse.turnsRemain)); - sf.back().sid = sse.sid; - sf.push_back(featureGenerator(Bonus::STACKS_SPEED, 0, power, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 49: //mirth - sf.push_back(featureGenerator(Bonus::MORALE, 0, power, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 50: //sorrow - sf.push_back(featureGenerator(Bonus::MORALE, 0, -1 * power, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 51: //fortune - sf.push_back(featureGenerator(Bonus::LUCK, 0, power, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 52: //misfortune - sf.push_back(featureGenerator(Bonus::LUCK, 0, -1 * power, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 53: //haste - sf.push_back(featureGenerator(Bonus::STACKS_SPEED, 0, power, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 54: //slow - sf.push_back(featureGeneratorVT(Bonus::STACKS_SPEED, 0, -1 * ( 100 - power ), sse.turnsRemain, Bonus::PERCENT_TO_ALL)); - sf.back().sid = sse.sid; - break; - case 55: //slayer - sf.push_back(featureGenerator(Bonus::SLAYER, 0, sse.val, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 56: //frenzy - sf.push_back(featureGenerator(Bonus::IN_FRENZY, 0, VLC->spellh->spells[56]->powers[sse.val]/100.0, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 58: //counterstrike - sf.push_back(featureGenerator(Bonus::ADDITIONAL_RETALIATION, 0, power, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 59: //bersek - sf.push_back(featureGenerator(Bonus::ATTACKS_NEAREST_CREATURE, 0, sse.val, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 60: //hypnotize - sf.push_back(featureGenerator(Bonus::HYPNOTIZED, 0, sse.val, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case 61: //forgetfulness - sf.push_back(featureGenerator(Bonus::FORGETFULL, 0, sse.val, sse.turnsRemain)); - sf.back().sid = sse.sid; - break; - case Spells::BLIND: //blind - sf.push_back(makeFeatureVal(Bonus::NOT_ACTIVE, Bonus::UNITL_BEING_ATTACKED | Bonus::N_TURNS, sse.sid, 0, Bonus::SPELL_EFFECT, sse.turnsRemain)); - sf.back().sid = sse.sid; - sf.push_back(makeFeatureVal(Bonus::GENERAL_ATTACK_REDUCTION, Bonus::UNTIL_ATTACK | Bonus::N_TURNS, 0, power, Bonus::SPELL_EFFECT, sse.turnsRemain)); - sf.back().sid = sse.sid; - sf.push_back(makeFeatureVal(Bonus::NO_RETALIATION, Bonus::UNITL_BEING_ATTACKED, 0, 0, Bonus::SPELL_EFFECT, 0)); // don't retaliate after basilisk / unicorn attack - sf.back().sid = sse.sid; - break; - case Spells::STONE_GAZE: //Stone Gaze - case Spells::PARALYZE: //Paralyze - sf.push_back(makeFeatureVal(Bonus::NOT_ACTIVE, Bonus::UNITL_BEING_ATTACKED | Bonus::N_TURNS, sse.sid, 0, Bonus::SPELL_EFFECT, sse.turnsRemain)); - sf.back().sid = sse.sid; - sf.push_back(makeFeatureVal(Bonus::NO_RETALIATION, Bonus::UNITL_BEING_ATTACKED, 0, 0, Bonus::SPELL_EFFECT, 0)); // don't retaliate after basilisk / unicorn attack - sf.back().sid = sse.sid; + case Spells::SHIELD: + add(Bonus::GENERAL_DAMAGE_REDUCTION, 0, power); break; - case 71: //Poison - sf.push_back(featureGeneratorVT(Bonus::POISON, 0, 30, sse.turnsRemain, Bonus::INDEPENDENT_MAX)); //max hp penalty from this source - sf.back().sid = sse.sid; - sf.push_back(featureGeneratorVT(Bonus::STACK_HEALTH, 0, -10, sse.turnsRemain, Bonus::PERCENT_TO_ALL)); - sf.back().sid = sse.sid; + 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 72: //Bind + case Spells::BLESS: + addVT(Bonus::ALWAYS_MAXIMUM_DAMAGE, -1, power,Bonus::INDEPENDENT_MAX); + break; + case Spells::CURSE: + addVT(Bonus::ALWAYS_MINIMUM_DAMAGE, -1, power, Bonus::INDEPENDENT_MAX, sse.val >= 2 ? 20 : 0); + break; + case Spells::BLOODLUST: + add(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, 0, Bonus::ONLY_MELEE_FIGHT); + break; + case Spells::PRECISION: + add(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 73: //Disease - sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, -2 , sse.turnsRemain)); - sf.back().sid = sse.sid; - sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, -2 , sse.turnsRemain)); - sf.back().sid = sse.sid; + case Spells::DISEASE: + add(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, -2); + add(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, -2); break; - case 75: //Age - sf.push_back(featureGeneratorVT(Bonus::STACK_HEALTH, 0, -50, sse.turnsRemain, Bonus::PERCENT_TO_ALL)); - sf.back().sid = sse.sid; + case Spells::AGE: + addVT(Bonus::STACK_HEALTH, 0, -50, Bonus::PERCENT_TO_ALL); break; - case 80: //Acid Breath + 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; diff --git a/lib/BattleState.h b/lib/BattleState.h index 0f7e72e18..c617179a3 100644 --- a/lib/BattleState.h +++ b/lib/BattleState.h @@ -33,8 +33,8 @@ struct BattleStackAttacked; //only for use in BattleInfo struct DLL_LINKAGE SiegeInfo { - ui8 wallState[EWallParts::PARTS_COUNT]; - + ui8 wallState[EWallParts::PARTS_COUNT]; + template void serialize(Handler &h, const int version) { h & wallState; @@ -101,7 +101,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky, bool deathBlow, bool ballistaDoubleDmg); //charge - number of hexes travelled before attack (for champion's jousting) void calculateCasualties(std::map *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount) - + //void getPotentiallyAttackableHexes(AttackableTiles &at, const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos); //hexes around target that could be attacked in melee //std::set getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks //std::set getAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks @@ -114,7 +114,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb ui32 calculateHealedHP(int healedHealth, const CSpell * spell, const CStack * stack) const; //for Archangel ui32 calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const; //unused bool resurrects(TSpell spellid) const; //TODO: move it to spellHandler? - + const CGHeroInstance * getHero(int player) const; //returns fighting hero that belongs to given player @@ -136,7 +136,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb }; class DLL_LINKAGE CStack : public CBonusSystemNode, public CStackBasicDescriptor -{ +{ public: const CStackInstance *base; //garrison slot from which stack originates (NULL for war machines, summoned cres, etc) @@ -179,7 +179,6 @@ public: { Bonus hb = makeFeatureVal(type, Bonus::N_TURNS, subtype, value, Bonus::SPELL_EFFECT, turnsRemain, additionalInfo); hb.effectRange = limit; - hb.source = Bonus::SPELL_EFFECT; return hb; } @@ -187,7 +186,6 @@ public: { Bonus ret = makeFeatureVal(type, Bonus::N_TURNS, subtype, value, Bonus::SPELL_EFFECT, turnsRemain); ret.valType = valType; - ret.source = Bonus::SPELL_EFFECT; return ret; } diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index 2a716a319..bc5c56edd 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -782,7 +782,7 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c minDmg = info.attackerBonuses->getMinDamage() * info.attackerCount, maxDmg = info.attackerBonuses->getMaxDamage() * info.attackerCount; - const CCreature *attackerType = info.attacker->getCreature(), + const CCreature *attackerType = info.attacker->getCreature(), *defenderType = info.defender->getCreature(); if(attackerType->idNumber == 149) //arrow turret @@ -1591,7 +1591,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C if (battleTestElementalImmunity(Bonus::AIR_IMMUNITY)) return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; } - if (vstd::contains(VLC->spellh->mindSpells, spell->id)) + if (spell->isMindSpell()) { if (subject->hasBonusOfType(Bonus::MIND_IMMUNITY)) return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; @@ -1706,7 +1706,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell switch(spell->getTargetType()) { case CSpell::CREATURE: - case CSpell::CREATURE_EXPERT_MASSIVE: + case CSpell::CREATURE_EXPERT_MASSIVE: if(mode == ECastingMode::HERO_CASTING) { const CGHeroInstance * caster = battleGetFightingHero(side); @@ -1909,7 +1909,7 @@ ui32 CBattleInfoCallback::calculateSpellDmg( const CSpell * sp, const CGHeroInst ui32 ret = 0; //value to return //check if spell really does damage - if not, return 0 - if(VLC->spellh->damageSpells.find(sp->id) == VLC->spellh->damageSpells.end()) + if(!sp->isDamageSpell()) return 0; ret = usedSpellPower * sp->power; @@ -2373,7 +2373,7 @@ const CGHeroInstance * CPlayerBattleCallback::battleGetMyHero() const InfoAboutHero CPlayerBattleCallback::battleGetEnemyHero() const { - InfoAboutHero ret; + InfoAboutHero ret; assert(0); ///TODO implement and replace usages of battleGetFightingHero obtaining enemy hero return ret; diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 22c9cdd03..918d84da5 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -176,7 +176,7 @@ void MetaString::getLocalString(const std::pair &txt, std::string &dst } else if (type == ART_EVNTS) { - dst = VLC->arth->artifacts[ser]->EventText(); + dst = VLC->arth->artifacts[ser]->EventText(); } else { @@ -522,7 +522,7 @@ std::pair CGameState::pickObject (CGObjectInstance *obj) { case Obj::RANDOM_ART: return std::pair(Obj::ARTIFACT, VLC->arth->getRandomArt (CArtifact::ART_TREASURE | CArtifact::ART_MINOR | CArtifact::ART_MAJOR | CArtifact::ART_RELIC)); - case Obj::RANDOM_TREASURE_ART: + case Obj::RANDOM_TREASURE_ART: return std::pair(Obj::ARTIFACT, VLC->arth->getRandomArt (CArtifact::ART_TREASURE)); case Obj::RANDOM_MINOR_ART: return std::pair(Obj::ARTIFACT, VLC->arth->getRandomArt (CArtifact::ART_MINOR)); @@ -573,7 +573,7 @@ std::pair CGameState::pickObject (CGObjectInstance *obj) return std::pair(Obj::MONSTER, VLC->creh->pickRandomMonster(boost::ref(ran), 6)); case Obj::RANDOM_MONSTER_L7: return std::pair(Obj::MONSTER, VLC->creh->pickRandomMonster(boost::ref(ran), 7)); - case Obj::RANDOM_DWELLING: + case Obj::RANDOM_DWELLING: case Obj::RANDOM_DWELLING_LVL: case Obj::RANDOM_DWELLING_FACTION: { @@ -1963,7 +1963,7 @@ std::vector CGameState::guardingCreatures (int3 pos) const { if(obj->blockVisit) { - if (obj->ID == 54) // Monster + if (obj->ID == Obj::MONSTER) // Monster guards.push_back(obj); } } @@ -1976,11 +1976,11 @@ std::vector CGameState::guardingCreatures (int3 pos) const if (map->isInTheMap(pos)) { TerrainTile &tile = map->terrain[pos.x][pos.y][pos.z]; - if (tile.visitable && (tile.terType == ETerrainType::WATER) == (posTile.terType == ETerrainType::WATER)) + if (tile.visitable && (tile.isWater() == posTile.isWater())) { BOOST_FOREACH (CGObjectInstance* obj, tile.visitableObjects) { - if (obj->ID == 54 && checkForVisitableDir(pos, &map->getTile(originalPos), originalPos)) // Monster being able to attack investigated tile + if (obj->ID == Obj::MONSTER && checkForVisitableDir(pos, &map->getTile(originalPos), originalPos)) // Monster being able to attack investigated tile { guards.push_back(obj); } @@ -2010,7 +2010,7 @@ int3 CGameState::guardingCreaturePosition (int3 pos) const { if(obj->blockVisit) { - if (obj->ID == 54) // Monster + if (obj->ID == Obj::MONSTER) // Monster return pos; else return int3(-1, -1, -1); //blockvis objects are not guarded by neighbouring creatures @@ -2027,11 +2027,11 @@ int3 CGameState::guardingCreaturePosition (int3 pos) const if (map->isInTheMap(pos)) { TerrainTile &tile = map->terrain[pos.x][pos.y][pos.z]; - if (tile.visitable && (tile.terType == ETerrainType::WATER) == (posTile.terType == ETerrainType::WATER)) + if (tile.visitable && (tile.isWater() == posTile.isWater())) { BOOST_FOREACH (CGObjectInstance* obj, tile.visitableObjects) { - if (obj->ID == 54 && checkForVisitableDir(pos, &map->getTile(originalPos), originalPos)) // Monster being able to attack investigated tile + if (obj->ID == Obj::MONSTER && checkForVisitableDir(pos, &map->getTile(originalPos), originalPos)) // Monster being able to attack investigated tile { return pos; } @@ -2429,7 +2429,7 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level) } if(level >= 4) //obelisks found { - //TODO + //TODO: obtainPlayersStats - obelisks found } if(level >= 5) //artifacts { @@ -2441,7 +2441,7 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level) } if(level >= 7) //income { - //TODO + //TODO:obtainPlayersStats - income } if(level >= 8) //best hero's stats { @@ -2507,18 +2507,11 @@ int CGameState::lossCheck( ui8 player ) const switch(map->lossCondition.typeOfLossCon) { case ELossConditionType::LOSSCASTLE: - { - const CGTownInstance *t = dynamic_cast(map->lossCondition.obj); - assert(t); - if(t->tempOwner != player) - return 1; - } - break; case ELossConditionType::LOSSHERO: { - const CGHeroInstance *h = dynamic_cast(map->lossCondition.obj); - assert(h); - if(h->tempOwner != player) + const CGObjectInstance *obj = map->lossCondition.obj; + assert(obj); + if(obj->tempOwner != player) return 1; } break; @@ -3177,13 +3170,6 @@ bool CPathfinder::canMoveBetween(const int3 &a, const int3 &b) const return gs->checkForVisitableDir(a, b) && gs->checkForVisitableDir(b, a); } -bool CPathfinder::canStepOntoDst() const -{ - //TODO remove - assert(0); - return false; -} - CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const TerrainTile *tinfo) const { CGPathNode::EAccessibility ret = (tinfo->blocked ? CGPathNode::BLOCKED : CGPathNode::ACCESSIBLE); @@ -3194,7 +3180,7 @@ CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const TerrainTile if(tinfo->visitable) { - if(tinfo->visitableObjects.front()->ID == 80 && tinfo->visitableObjects.back()->ID == Obj::HERO && tinfo->visitableObjects.back()->tempOwner != hero->tempOwner) //non-owned hero stands on Sanctuary + if(tinfo->visitableObjects.front()->ID == Obj::SANCTUARY && tinfo->visitableObjects.back()->ID == Obj::HERO && tinfo->visitableObjects.back()->tempOwner != hero->tempOwner) //non-owned hero stands on Sanctuary { return CGPathNode::BLOCKED; } diff --git a/lib/CGameState.h b/lib/CGameState.h index bf2f2193e..370240e17 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -368,7 +368,6 @@ private: CGPathNode::EAccessibility evaluateAccessibility(const TerrainTile *tinfo) const; bool canMoveBetween(const int3 &a, const int3 &b) const; //checks only for visitable objects that may make moving between tiles impossible, not other conditions (like tiles itself accessibility) - bool canStepOntoDst() const; public: CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero); diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index 058c23b06..ff03d3dc8 100644 --- a/lib/CModHandler.cpp +++ b/lib/CModHandler.cpp @@ -292,6 +292,7 @@ void CModHandler::loadActiveMods() handleData(VLC->townh, config["factions"]); handleData(VLC->creh, config["creatures"]); handleData(VLC->arth, config["artifacts"]); + //todo: spells handleData(&VLC->heroh->classes, config["heroClasses"]); handleData(VLC->heroh, config["heroes"]); diff --git a/lib/CObjectHandler.cpp b/lib/CObjectHandler.cpp index bd016f1fc..fc2bb5aa9 100644 --- a/lib/CObjectHandler.cpp +++ b/lib/CObjectHandler.cpp @@ -1384,13 +1384,12 @@ void CGHeroInstance::showNecromancyDialog(const CStackBasicDescriptor &raisedSta { iw.text.addTxt(MetaString::GENERAL_TXT, 145); iw.text.addReplacement(raisedStack.count); - iw.text.addReplacement(MetaString::CRE_PL_NAMES, raisedStack.type->idNumber); } else // Practicing the dark arts of necromancy, ... (singular) { iw.text.addTxt(MetaString::GENERAL_TXT, 146); - iw.text.addReplacement(MetaString::CRE_SING_NAMES, raisedStack.type->idNumber); } + iw.text.addReplacement(raisedStack); cb->showInfoDialog(&iw); } @@ -4772,7 +4771,7 @@ void CGWitchHut::onHeroVisit( const CGHeroInstance * h ) const const std::string & CGWitchHut::getHoverText() const { hoverName = VLC->generaltexth->names[ID]; - if(wasVisited(cb->getCurrentPlayer())) //TODO: use local player, not current + if(wasVisited(cb->getLocalPlayer())) { hoverName += "\n" + VLC->generaltexth->allTexts[356]; // + (learn %s) boost::algorithm::replace_first(hoverName,"%s",VLC->generaltexth->skillName[ability]); @@ -6113,14 +6112,12 @@ void CBank::endBattle (const CGHeroInstance *h, const BattleResult *result) cons //display loot if (!iw.components.empty()) { + iw.text.addTxt (MetaString::ADVOB_TXT, textID); if (textID == 34) { - iw.text.addTxt(MetaString::ADVOB_TXT, 34);//Heaving defeated %s, you discover %s iw.text.addReplacement(MetaString::CRE_PL_NAMES, result->casualties[1].begin()->first); iw.text.addReplacement(loot.buildList()); } - else - iw.text.addTxt (MetaString::ADVOB_TXT, textID); cb->showInfoDialog(&iw); } loot.clear(); diff --git a/lib/CObjectHandler.h b/lib/CObjectHandler.h index 7c87ed093..e8c706e2e 100644 --- a/lib/CObjectHandler.h +++ b/lib/CObjectHandler.h @@ -167,7 +167,7 @@ public: mutable std::string hoverName; int3 pos; //h3m pos si32 ID, subID; //normal ID (this one from OH3 maps ;]) - eg. town=98; hero=34 - si32 id;//number of object in CObjectHandler's vector + si32 id;//number of object in map's vector CGDefInfo * defInfo; ui8 animPhaseShift; diff --git a/lib/CSpellHandler.cpp b/lib/CSpellHandler.cpp index 87fb8059f..17db4024d 100644 --- a/lib/CSpellHandler.cpp +++ b/lib/CSpellHandler.cpp @@ -96,7 +96,7 @@ namespace SRSLPraserHelpers mainPointForLayer[b] = hexToPair(center); for(int it=1; it<=high; ++it) //it - distance to the center - { + { for(int b=0; b<6; ++b) mainPointForLayer[b] = gotoDir(mainPointForLayer[b], b); @@ -122,11 +122,19 @@ namespace SRSLPraserHelpers return ret; } } + using namespace SRSLPraserHelpers; CSpellHandler::CSpellHandler() { - VLC->spellh = this; } + +CSpell::CSpell() +{ + _isDamage = false; + _isMind = false; + _isRising = false; +} + std::vector CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const { std::vector ret; @@ -238,7 +246,7 @@ CSpell::ETargetType CSpell::getTargetType() const //TODO: parse these at game la if(attributes.find("OBSTACLE_TARGET") != std::string::npos) return OBSTACLE; - + return NO_TARGET; } @@ -254,13 +262,30 @@ bool CSpell::isNegative() const bool CSpell::isRisingSpell() const { - return vstd::contains(VLC->spellh->risingSpells, id); + return _isRising; } bool CSpell::isDamageSpell() const { - return vstd::contains(VLC->spellh->damageSpells, id); + return _isDamage; } + +bool CSpell::isMindSpell() const +{ + return _isMind; +} + +void CSpell::getEffects(std::vector& lst) const +{ + lst.reserve(lst.size() + _effects.size()); + + BOOST_FOREACH (Bonus b, _effects) + { + lst.push_back(b); + } +} + + bool DLL_LINKAGE isInScreenRange(const int3 ¢er, const int3 &pos) { int3 diff = pos - center; @@ -343,7 +368,10 @@ void CSpellHandler::loadSpells() } while (parser.endLine() && !parser.isNextEntryEmpty()); - boost::replace_first (spells[47]->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 + + + spells.push_back(spells[Spells::ACID_BREATH_DEFENSE]); //clone Acid Breath attributes for Acid Breath damage effect //loading of additional spell traits const JsonNode config(ResourceID("config/spell_info.json")); @@ -367,16 +395,50 @@ void CSpellHandler::loadSpells() s->identifier = spell.first; VLC->modh->identifiers.registerObject("spell." + spell.first, spellID); + const JsonNode & flags_node = spell.second["flags"]; + if (!flags_node.isNull()) + { + auto flags = flags_node.convertTo >(); + + BOOST_FOREACH (const auto & flag, flags) + { + if (flag == "damage") + { + s->_isDamage = true; + } + else if (flag == "rising") + { + s->_isRising = true; + } + else if (flag == "mind") + { + s->_isMind = true; + } + + } + } + + const JsonNode & effects_node = spell.second["effects"]; + + if (!effects_node.isNull()) + { + BOOST_FOREACH (const JsonNode & bonus_node, effects_node.Vector()) + { + 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); + } + } + } + //spell fixes - spells.push_back(spells[80]); //clone Acid Breath attributes for Acid Breath damage effect //forgetfulness needs to get targets automatically on expert level - boost::replace_first(spells[61]->attributes, "CREATURE_TARGET", "CREATURE_TARGET_2"); //TODO: use flags instead? - - damageSpells += 11, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 57, 77; - risingSpells += 38, 39, 40; - mindSpells += 50, 59, 60, 61, 62; + boost::replace_first(spells[Spells::FORGETFULNESS]->attributes, "CREATURE_TARGET", "CREATURE_TARGET_2"); //TODO: use flags instead? } std::vector CSpellHandler::getDefaultAllowedSpells() const diff --git a/lib/CSpellHandler.h b/lib/CSpellHandler.h index 1c12e2fdb..f313a4764 100644 --- a/lib/CSpellHandler.h +++ b/lib/CSpellHandler.h @@ -3,6 +3,7 @@ #include "../lib/ConstTransitivePtr.h" #include "int3.h" #include "GameConstants.h" +#include "HeroBonus.h" /* * CSpellHandler.h, part of VCMI engine @@ -44,20 +45,38 @@ public: std::vector range; //description of spell's range in SRSL by magic school level std::vector counteredSpells; //spells that are removed when effect of this spell is placed on creature (for bless-curse, haste-slow, and similar pairs) + CSpell(); + std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = NULL ) const; //convert range to specific hexes; last optional out parameter is set to true, if spell would cover unavailable hexes (that are not included in ret) si16 mainEffectAnim; //main spell effect animation, in AC format (or -1 when none) ETargetType getTargetType() const; bool isPositive() const; bool isNegative() const; + bool isRisingSpell() const; bool isDamageSpell() const; + bool isMindSpell() const; + + + void getEffects(std::vector & lst) const; template 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; } + friend class CSpellHandler; + +private: + bool _isRising; + bool _isDamage; + bool _isMind; + + std::vector _effects; + }; namespace Spells @@ -68,18 +87,18 @@ namespace Spells FLY=6, WATER_WALK=7, DIMENSION_DOOR=8, TOWN_PORTAL=9, QUICKSAND=10, LAND_MINE=11, FORCE_FIELD=12, FIRE_WALL=13, EARTHQUAKE=14, - MAGIC_ARROW=15, ICE_BOLT=16, LIGHTNING_BOLT=17, IMPLOSION=18, - CHAIN_LIGHTNING=19, FROST_RING=20, FIREBALL=21, INFERNO=22, - METEOR_SHOWER=23, DEATH_RIPPLE=24, DESTROY_UNDEAD=25, ARMAGEDDON=26, - SHIELD=27, AIR_SHIELD=28, FIRE_SHIELD=29, PROTECTION_FROM_AIR=30, - PROTECTION_FROM_FIRE=31, PROTECTION_FROM_WATER=32, - PROTECTION_FROM_EARTH=33, ANTI_MAGIC=34, DISPEL=35, MAGIC_MIRROR=36, - CURE=37, RESURRECTION=38, ANIMATE_DEAD=39, SACRIFICE=40, BLESS=41, - CURSE=42, BLOODLUST=43, PRECISION=44, WEAKNESS=45, STONE_SKIN=46, - DISRUPTING_RAY=47, PRAYER=48, MIRTH=49, SORROW=50, FORTUNE=51, - MISFORTUNE=52, HASTE=53, SLOW=54, SLAYER=55, FRENZY=56, - TITANS_LIGHTNING_BOLT=57, COUNTERSTRIKE=58, BERSERK=59, HYPNOTIZE=60, - FORGETFULNESS=61, BLIND=62, TELEPORT=63, REMOVE_OBSTACLE=64, CLONE=65, + MAGIC_ARROW=15, ICE_BOLT=16, LIGHTNING_BOLT=17, IMPLOSION=18, + CHAIN_LIGHTNING=19, FROST_RING=20, FIREBALL=21, INFERNO=22, + METEOR_SHOWER=23, DEATH_RIPPLE=24, DESTROY_UNDEAD=25, ARMAGEDDON=26, + SHIELD=27, AIR_SHIELD=28, FIRE_SHIELD=29, PROTECTION_FROM_AIR=30, + PROTECTION_FROM_FIRE=31, PROTECTION_FROM_WATER=32, + PROTECTION_FROM_EARTH=33, ANTI_MAGIC=34, DISPEL=35, MAGIC_MIRROR=36, + CURE=37, RESURRECTION=38, ANIMATE_DEAD=39, SACRIFICE=40, BLESS=41, + CURSE=42, BLOODLUST=43, PRECISION=44, WEAKNESS=45, STONE_SKIN=46, + DISRUPTING_RAY=47, PRAYER=48, MIRTH=49, SORROW=50, FORTUNE=51, + MISFORTUNE=52, HASTE=53, SLOW=54, SLAYER=55, FRENZY=56, + TITANS_LIGHTNING_BOLT=57, COUNTERSTRIKE=58, BERSERK=59, HYPNOTIZE=60, + FORGETFULNESS=61, BLIND=62, TELEPORT=63, REMOVE_OBSTACLE=64, CLONE=65, SUMMON_FIRE_ELEMENTAL=66, SUMMON_EARTH_ELEMENTAL=67, SUMMON_WATER_ELEMENTAL=68, SUMMON_AIR_ELEMENTAL=69, STONE_GAZE=70, POISON=71, BIND=72, DISEASE=73, PARALYZE=74, AGE=75, DEATH_CLOUD=76, THUNDERBOLT=77, @@ -96,9 +115,7 @@ class DLL_LINKAGE CSpellHandler public: CSpellHandler(); std::vector< ConstTransitivePtr > spells; - std::set damageSpells; //they inflict damage and require particular threatment - std::set risingSpells; //they affect dead stacks and need special target selection - std::set mindSpells; + void loadSpells(); /** @@ -110,6 +127,6 @@ public: template void serialize(Handler &h, const int version) { - h & spells & damageSpells & risingSpells & mindSpells; + h & spells ; } }; diff --git a/lib/Mapping/CMapService.cpp b/lib/Mapping/CMapService.cpp index 197f9ae5b..e68c0def5 100644 --- a/lib/Mapping/CMapService.cpp +++ b/lib/Mapping/CMapService.cpp @@ -955,7 +955,7 @@ void CMapLoaderH3M::readDefInfo() defInfo->visitMap[zi] = reverse(bytes[6 + zi]); } pos += 16; - if(defInfo->id != Obj::HERO && defInfo->id != 70) + if(defInfo->id != Obj::HERO && defInfo->id != Obj::RANDOM_HERO) { CGDefInfo * h = VLC->dobjinfo->gobjs[defInfo->id][defInfo->subid]; if(!h) @@ -1160,11 +1160,22 @@ void CMapLoaderH3M::readObjects() case Obj::FLOTSAM: case Obj::SEA_CHEST: case Obj::SHIPWRECK_SURVIVOR: - case Obj::TREASURE_CHEST: { nobj = new CGPickable(); break; } + case Obj::TREASURE_CHEST: + if(defInfo->subid == 0) + { + nobj = new CGPickable(); + } + else + { + //WoG pickable object + //TODO: possible special handling + nobj = new CGObjectInstance(); + } + break; case Obj::MONSTER: //Monster case Obj::RANDOM_MONSTER: case Obj::RANDOM_MONSTER_L1: diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 516c7b249..02ee3459e 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -4376,7 +4376,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest { BattleStackAttacked bsa; bsa.flags |= BattleStackAttacked::EFFECT; - bsa.effect = VLC->spellh->spells[80]->mainEffectAnim; //use acid breath + bsa.effect = spell->mainEffectAnim; bsa.damageAmount = usedSpellPower; //damage times the number of attackers bsa.stackAttacked = (*it)->ID; bsa.attackerID = -1;