mirror of
https://github.com/vcmi/vcmi.git
synced 2025-06-04 23:17:41 +02:00
[Spells] More spell related refactoring
+ smart target modifier - CREATURE_EXPERT_MASSIVE target type * save format changed spell format changes already documented in http://wiki.vcmi.eu/index.php?title=Spell_Format
This commit is contained in:
parent
7f6f125b4c
commit
9cac0af7be
@ -1687,13 +1687,13 @@ CMageGuildScreen::Scroll::Scroll(Point position, const CSpell *Spell)
|
|||||||
void CMageGuildScreen::Scroll::clickLeft(tribool down, bool previousState)
|
void CMageGuildScreen::Scroll::clickLeft(tribool down, bool previousState)
|
||||||
{
|
{
|
||||||
if(down)
|
if(down)
|
||||||
LOCPLINT->showInfoDialog(spell->descriptions[0], new CComponent(CComponent::spell,spell->id));
|
LOCPLINT->showInfoDialog(spell->getLevelInfo(0).description, new CComponent(CComponent::spell,spell->id));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMageGuildScreen::Scroll::clickRight(tribool down, bool previousState)
|
void CMageGuildScreen::Scroll::clickRight(tribool down, bool previousState)
|
||||||
{
|
{
|
||||||
if(down)
|
if(down)
|
||||||
CRClickPopup::createAndPush(spell->descriptions[0], new CComponent(CComponent::spell, spell->id));
|
CRClickPopup::createAndPush(spell->getLevelInfo(0).description, new CComponent(CComponent::spell, spell->id));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMageGuildScreen::Scroll::hover(bool on)
|
void CMageGuildScreen::Scroll::hover(bool on)
|
||||||
|
@ -630,7 +630,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
|
|||||||
|| (!sp->combatSpell && owner->myInt->battleInt))
|
|| (!sp->combatSpell && owner->myInt->battleInt))
|
||||||
{
|
{
|
||||||
std::vector<CComponent*> hlp(1, new CComponent(CComponent::spell, mySpell, 0));
|
std::vector<CComponent*> hlp(1, new CComponent(CComponent::spell, mySpell, 0));
|
||||||
LOCPLINT->showInfoDialog(sp->descriptions[schoolLevel], hlp);
|
LOCPLINT->showInfoDialog(sp->getLevelInfo(schoolLevel).description, hlp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -810,7 +810,7 @@ void CSpellWindow::SpellArea::clickRight(tribool down, bool previousState)
|
|||||||
boost::algorithm::replace_first(dmgInfo, "%d", boost::lexical_cast<std::string>(causedDmg));
|
boost::algorithm::replace_first(dmgInfo, "%d", boost::lexical_cast<std::string>(causedDmg));
|
||||||
}
|
}
|
||||||
|
|
||||||
CRClickPopup::createAndPush(CGI->spellh->objects[mySpell]->descriptions[schoolLevel] + dmgInfo,
|
CRClickPopup::createAndPush(CGI->spellh->objects[mySpell]->getLevelInfo(schoolLevel).description + dmgInfo,
|
||||||
new CComponent(CComponent::spell, mySpell));
|
new CComponent(CComponent::spell, mySpell));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -977,7 +977,7 @@ std::string CComponent::getDescription()
|
|||||||
case creature: return "";
|
case creature: return "";
|
||||||
case artifact: return CGI->arth->artifacts[subtype]->Description();
|
case artifact: return CGI->arth->artifacts[subtype]->Description();
|
||||||
case experience: return CGI->generaltexth->allTexts[241];
|
case experience: return CGI->generaltexth->allTexts[241];
|
||||||
case spell: return CGI->spellh->objects[subtype]->descriptions[val];
|
case spell: return CGI->spellh->objects[subtype]->getLevelInfo(val).description;
|
||||||
case morale: return CGI->generaltexth->heroscrn[ 4 - (val>0) + (val<0)];
|
case morale: return CGI->generaltexth->heroscrn[ 4 - (val>0) + (val<0)];
|
||||||
case luck: return CGI->generaltexth->heroscrn[ 7 - (val>0) + (val<0)];
|
case luck: return CGI->generaltexth->heroscrn[ 7 - (val>0) + (val<0)];
|
||||||
case building: return CGI->townh->factions[subtype]->town->buildings[BuildingID(val)]->Description();
|
case building: return CGI->townh->factions[subtype]->town->buildings[BuildingID(val)]->Description();
|
||||||
|
@ -1549,31 +1549,23 @@ void CBattleInterface::castThisSpell(int spellID)
|
|||||||
assert(castingHero); // code below assumes non-null hero
|
assert(castingHero); // code below assumes non-null hero
|
||||||
sp = CGI->spellh->objects[spellID];
|
sp = CGI->spellh->objects[spellID];
|
||||||
spellSelMode = ANY_LOCATION;
|
spellSelMode = ANY_LOCATION;
|
||||||
if(sp->getTargetType() == CSpell::CREATURE)
|
|
||||||
|
const CSpell::TargetInfo ti = sp->getTargetInfo(castingHero->getSpellSchoolLevel(sp));
|
||||||
|
|
||||||
|
if(ti.massive)
|
||||||
|
spellSelMode = NO_LOCATION;
|
||||||
|
else if(ti.type == CSpell::CREATURE)
|
||||||
{
|
{
|
||||||
spellSelMode = selectionTypeByPositiveness(*sp);
|
if(ti.smart)
|
||||||
}
|
|
||||||
if(sp->getTargetType() == CSpell::CREATURE_EXPERT_MASSIVE)
|
|
||||||
{
|
|
||||||
if(castingHero->getSpellSchoolLevel(sp) < 3)
|
|
||||||
spellSelMode = selectionTypeByPositiveness(*sp);
|
spellSelMode = selectionTypeByPositiveness(*sp);
|
||||||
else
|
else
|
||||||
spellSelMode = NO_LOCATION;
|
spellSelMode = ANY_CREATURE;
|
||||||
}
|
}
|
||||||
if(sp->getTargetType() == CSpell::OBSTACLE)
|
else if(ti.type == CSpell::OBSTACLE)
|
||||||
{
|
{
|
||||||
spellSelMode = OBSTACLE;
|
spellSelMode = OBSTACLE;
|
||||||
} //FIXME: Remove Obstacle has range X, unfortunatelly :(
|
}
|
||||||
else if(sp->range[ castingHero->getSpellSchoolLevel(sp) ] == "X") //spell has no range
|
//todo: move to JSON config
|
||||||
{
|
|
||||||
spellSelMode = NO_LOCATION;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(sp->range[ castingHero->getSpellSchoolLevel(sp) ].size() > 1) //spell has many-hex range
|
|
||||||
{
|
|
||||||
spellSelMode = ANY_LOCATION;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(spellID == SpellID::FIRE_WALL || spellID == SpellID::FORCE_FIELD)
|
if(spellID == SpellID::FIRE_WALL || spellID == SpellID::FORCE_FIELD)
|
||||||
{
|
{
|
||||||
spellSelMode = FREE_LOCATION;
|
spellSelMode = FREE_LOCATION;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
|
|
||||||
"type":"object",
|
"type":"object",
|
||||||
"$schema": "http://json-schema.org/draft-04/schema",
|
"$schema": "http://json-schema.org/draft-04/schema",
|
||||||
@ -7,235 +7,241 @@
|
|||||||
"description" : "Format used to define new spells in VCMI",
|
"description" : "Format used to define new spells in VCMI",
|
||||||
|
|
||||||
|
|
||||||
"definitions" : {
|
"definitions" : {
|
||||||
"flags" :{
|
"flags" :{
|
||||||
"type" : "object",
|
"type" : "object",
|
||||||
"additionalProperties" : {
|
"additionalProperties" : {
|
||||||
"type":"boolean"
|
"type":"boolean"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"levelInfo":{
|
"levelInfo":{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required":["range","description","cost","power","aiValue","range"],
|
"required":["range","description","cost","power","aiValue","range"],
|
||||||
|
|
||||||
"additionalProperties" : false,
|
"additionalProperties" : false,
|
||||||
"properties":{
|
"properties":{
|
||||||
"description":{
|
"description":{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Localizable description. Use {xxx} for formatting"
|
"description": "Localizable description. Use {xxx} for formatting"
|
||||||
},
|
},
|
||||||
"cost":{
|
"cost":{
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"description":"Cost in mana points"
|
"description":"Cost in mana points"
|
||||||
},
|
},
|
||||||
"power":{
|
"power":{
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"aiValue":{
|
"aiValue":{
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
|
|
||||||
"range":{
|
"range":{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "spell range description in SRSL"
|
"description": "spell range description in SRSL"
|
||||||
},
|
},
|
||||||
"effects":{
|
"effects":{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Timed effects",
|
"description": "Timed effects",
|
||||||
"additionalProperties" : {
|
"additionalProperties" : {
|
||||||
"$ref" : "vcmi:bonus"
|
"$ref" : "vcmi:bonus"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"targetModifier":{
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties":{
|
||||||
|
"smart":{
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "true: friendly/hostile based on positiveness; false: all targets"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
}
|
"required" : ["type", "name", "school", "level", "power","gainChance","flags","levels"],
|
||||||
}
|
"additionalProperties" : false,
|
||||||
},
|
|
||||||
|
|
||||||
"required" : ["type", "name", "school", "level", "power","gainChance","flags","levels"],
|
"properties": {
|
||||||
"additionalProperties" : false,
|
"index":{
|
||||||
|
"type": "number",
|
||||||
|
"description": "numeric id of spell required only for original spells, prohibited for new spells"
|
||||||
|
},
|
||||||
|
"type":{
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["adventure", "combat", "ability"],
|
||||||
|
"description":"Spell type"
|
||||||
|
},
|
||||||
|
"name":{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Localizable name"
|
||||||
|
|
||||||
"properties": {
|
},
|
||||||
"index":{
|
"school":{
|
||||||
"type": "number",
|
"type": "object",
|
||||||
"description": "numeric id of spell required only for original spells, prohibited for new spells"
|
"description": "Spell schools",
|
||||||
},
|
"additionalProperties": false,
|
||||||
"type":{
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["adventure", "combat", "ability"],
|
|
||||||
"description":"Spell type"
|
|
||||||
},
|
|
||||||
"name":{
|
|
||||||
"type": "string",
|
|
||||||
"description": "Localizable name"
|
|
||||||
|
|
||||||
},
|
"properties":{
|
||||||
"school":{
|
|
||||||
"type": "object",
|
|
||||||
"description": "Spell schools",
|
|
||||||
"additionalProperties": false,
|
|
||||||
|
|
||||||
"properties":{
|
"air":{"type": "boolean"},
|
||||||
|
"fire":{"type": "boolean"},
|
||||||
|
"earth":{"type": "boolean"},
|
||||||
|
"water":{"type": "boolean"}
|
||||||
|
}
|
||||||
|
|
||||||
"air":{"type": "boolean"},
|
},
|
||||||
"fire":{"type": "boolean"},
|
"level":{
|
||||||
"earth":{"type": "boolean"},
|
"type": "number",
|
||||||
"water":{"type": "boolean"}
|
"description": "Spell level",
|
||||||
}
|
"minimum" : 0,
|
||||||
|
"maximum" : 5
|
||||||
|
},
|
||||||
|
|
||||||
},
|
"power":{
|
||||||
"level":{
|
"type": "number",
|
||||||
"type": "number",
|
"description": "Base power",
|
||||||
"description": "Spell level",
|
},
|
||||||
"minimum" : 0,
|
|
||||||
"maximum" : 5
|
|
||||||
},
|
|
||||||
|
|
||||||
"power":{
|
"defaultGainChance":{
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"description": "Base power",
|
"description": "Gain chance by default for all factions"
|
||||||
},
|
|
||||||
|
|
||||||
"defaultGainChance":{
|
},
|
||||||
"type": "number",
|
|
||||||
"description": "Gain chance by default for all factions"
|
|
||||||
|
|
||||||
},
|
"gainChance":{
|
||||||
|
"type": "object",
|
||||||
|
"description": "Chance in % to gain for faction. NOTE: this field is merged with faction config",
|
||||||
|
"additionalProperties" : {
|
||||||
|
"type": "number",
|
||||||
|
"minimum" : 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"targetType":{
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["NO_TARGET","CREATURE","OBSTACLE"]
|
||||||
|
},
|
||||||
|
"anim":{
|
||||||
|
"type": "number",
|
||||||
|
"description": "Main effect animation (AC format), -1 - none, deprecated",
|
||||||
|
"minimum": -1
|
||||||
|
},
|
||||||
|
"counters":{
|
||||||
|
"$ref" : "#/definitions/flags",
|
||||||
|
"description": "Flags structure ids of countering spells"
|
||||||
|
},
|
||||||
|
"flags":{
|
||||||
|
"type": "object",
|
||||||
|
"description": "Various flags",
|
||||||
|
"additionalProperties" : false,
|
||||||
|
"properties":{
|
||||||
|
"indifferent": {
|
||||||
|
"type":"boolean",
|
||||||
|
"description": "Spell is indifferent for target"
|
||||||
|
},
|
||||||
|
"negative": {
|
||||||
|
"type":"boolean",
|
||||||
|
"description": "Spell is negative for target"
|
||||||
|
},
|
||||||
|
"positive": {
|
||||||
|
"type":"boolean",
|
||||||
|
"description": "Spell is positive for target"
|
||||||
|
},
|
||||||
|
"damage": {
|
||||||
|
"type":"boolean",
|
||||||
|
"description": "Spell does damage (direct or indirect)"
|
||||||
|
},
|
||||||
|
"offensive": {
|
||||||
|
"type":"boolean",
|
||||||
|
"description": "Spell does direct damage (implicitly sets damage and negative)"
|
||||||
|
},
|
||||||
|
"rising":{
|
||||||
|
"type":"boolean",
|
||||||
|
"description": "Rising spell (implicitly sets positive)"
|
||||||
|
},
|
||||||
|
"special":{
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Special spell. Can be given only by Bonus::SPELL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"immunity":{
|
||||||
|
"$ref" : "#/definitions/flags",
|
||||||
|
"description": "flags structure of bonus names, any one of these bonus grants immunity"
|
||||||
|
},
|
||||||
|
"absoluteImmunity":{
|
||||||
|
"$ref" : "#/definitions/flags",
|
||||||
|
"description": "flags structure of bonus names. Any one of these bonus grants immunity, cant be negated"
|
||||||
|
},
|
||||||
|
"limit":{
|
||||||
|
"$ref" : "#/definitions/flags",
|
||||||
|
"description": "flags structure of bonus names, presence of all bonuses required to be affected by"
|
||||||
|
},
|
||||||
|
|
||||||
"gainChance":{
|
"graphics":{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Chance in % to gain for faction. NOTE: this field is merged with faction config",
|
"additionalProperties" : false,
|
||||||
"additionalProperties" : {
|
"properties":{
|
||||||
"type": "number",
|
"iconImmune":{
|
||||||
"minimum" : 0
|
"type": "string",
|
||||||
}
|
"description": "Resourse path of icon for SPELL_IMMUNITY bonus (relative to DATA or SPRITES)",
|
||||||
},
|
"format" : "imageFile"
|
||||||
"targetType":{
|
},
|
||||||
"type": "string",
|
"iconScenarioBonus":{
|
||||||
"enum": ["NO_TARGET","CREATURE","OBSTACLE","CREATURE_EXPERT_MASSIVE"]
|
"type": "string",
|
||||||
},
|
"description": "Resourse path of icon for scenario bonus" ,
|
||||||
"anim":{
|
"format" : "imageFile"
|
||||||
"type": "number",
|
},
|
||||||
"description": "Main effect animation (AC format), -1 - none, deprecated",
|
"iconEffect":{
|
||||||
"minimum": -1
|
"type": "string",
|
||||||
},
|
"description": "Resourse path of icon for spell effects during battle" ,
|
||||||
"counters":{
|
"format" : "imageFile"
|
||||||
"$ref" : "#/definitions/flags",
|
},
|
||||||
"description": "Flags structure ids of countering spells"
|
"iconBook":{
|
||||||
},
|
"type": "string",
|
||||||
"flags":{
|
"description":"Resourse path of icon for spellbook" ,
|
||||||
"type": "object",
|
"format" : "imageFile"
|
||||||
"description": "Various flags",
|
},
|
||||||
"additionalProperties" : false,
|
"iconScroll":{
|
||||||
"properties":{
|
"type": "string",
|
||||||
"indifferent": {
|
"description": "Resourse path of icon for spell scrolls",
|
||||||
"type":"boolean",
|
"format": "imageFile"
|
||||||
"description": "Spell is indifferent for target"
|
}
|
||||||
},
|
}
|
||||||
"negative": {
|
|
||||||
"type":"boolean",
|
|
||||||
"description": "Spell is negative for target"
|
|
||||||
},
|
|
||||||
"positive": {
|
|
||||||
"type":"boolean",
|
|
||||||
"description": "Spell is positive for target"
|
|
||||||
},
|
|
||||||
"damage": {
|
|
||||||
"type":"boolean",
|
|
||||||
"description": "Spell does damage (direct or indirect)"
|
|
||||||
},
|
|
||||||
"offensive": {
|
|
||||||
"type":"boolean",
|
|
||||||
"description": "Spell does direct damage (implicitly sets damage and negative)"
|
|
||||||
},
|
|
||||||
"rising":{
|
|
||||||
"type":"boolean",
|
|
||||||
"description": "Rising spell (implicitly sets positive)"
|
|
||||||
},
|
|
||||||
"special":{
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Special spell. Can be given only by Bonus::SPELL"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"immunity":{
|
|
||||||
"$ref" : "#/definitions/flags",
|
|
||||||
"description": "flags structure of bonus names, any one of these bonus grants immunity"
|
|
||||||
},
|
|
||||||
"absoluteImmunity":{
|
|
||||||
"$ref" : "#/definitions/flags",
|
|
||||||
"description": "flags structure of bonus names. Any one of these bonus grants immunity, cant be negated"
|
|
||||||
},
|
|
||||||
"limit":{
|
|
||||||
"$ref" : "#/definitions/flags",
|
|
||||||
"description": "flags structure of bonus names, presence of all bonuses required to be affected by"
|
|
||||||
},
|
|
||||||
|
|
||||||
"graphics":{
|
},
|
||||||
"type": "object",
|
|
||||||
"additionalProperties" : false,
|
|
||||||
"properties":{
|
|
||||||
"iconImmune":{
|
|
||||||
"type": "string",
|
|
||||||
"description": "Resourse path of icon for SPELL_IMMUNITY bonus (relative to DATA or SPRITES)",
|
|
||||||
"format" : "imageFile"
|
|
||||||
},
|
|
||||||
"iconScenarioBonus":{
|
|
||||||
"type": "string",
|
|
||||||
"description": "Resourse path of icon for scenario bonus" ,
|
|
||||||
"format" : "imageFile"
|
|
||||||
},
|
|
||||||
"iconEffect":{
|
|
||||||
"type": "string",
|
|
||||||
"description": "Resourse path of icon for spell effects during battle" ,
|
|
||||||
"format" : "imageFile"
|
|
||||||
},
|
|
||||||
"iconBook":{
|
|
||||||
"type": "string",
|
|
||||||
"description":"Resourse path of icon for spellbook" ,
|
|
||||||
"format" : "imageFile"
|
|
||||||
},
|
|
||||||
"iconScroll":{
|
|
||||||
"type": "string",
|
|
||||||
"description": "Resourse path of icon for spell scrolls",
|
|
||||||
"format": "imageFile"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
"sounds":{
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties" : false,
|
||||||
|
"properties":{
|
||||||
|
"cast":{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Resourse path of cast sound"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"sounds":{
|
"levels":{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties" : false,
|
"additionalProperties" : false,
|
||||||
"properties":{
|
"required" : ["none", "basic", "advanced", "expert"],
|
||||||
"cast":{
|
|
||||||
"type": "string",
|
|
||||||
"description": "Resourse path of cast sound"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"levels":{
|
"properties":{
|
||||||
"type": "object",
|
"none":{
|
||||||
"additionalProperties" : false,
|
"$ref" : "#/definitions/levelInfo"
|
||||||
"required" : ["none", "basic", "advanced", "expert"],
|
},
|
||||||
|
"basic":{
|
||||||
"properties":{
|
"$ref" : "#/definitions/levelInfo"
|
||||||
"none":{
|
},
|
||||||
"$ref" : "#/definitions/levelInfo"
|
"advanced":{
|
||||||
},
|
"$ref" : "#/definitions/levelInfo"
|
||||||
"basic":{
|
},
|
||||||
"$ref" : "#/definitions/levelInfo"
|
"expert":{
|
||||||
},
|
"$ref" : "#/definitions/levelInfo"
|
||||||
"advanced":{
|
}
|
||||||
"$ref" : "#/definitions/levelInfo"
|
}
|
||||||
},
|
}
|
||||||
"expert":{
|
}
|
||||||
"$ref" : "#/definitions/levelInfo"
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1617,10 +1617,13 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
|
|||||||
|
|
||||||
if (spell->isRisingSpell())
|
if (spell->isRisingSpell())
|
||||||
{
|
{
|
||||||
if (caster) //TODO: what with Archangels?
|
if(subject->count >= subject->baseAmount)
|
||||||
|
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
||||||
|
|
||||||
|
if (caster) //FIXME: Archangels can cast immune stack
|
||||||
{
|
{
|
||||||
auto maxHealth = calculateHealedHP (caster, spell, subject);
|
auto maxHealth = calculateHealedHP (caster, spell, subject);
|
||||||
if (subject->count >= subject->baseAmount || maxHealth < subject->MaxHealth()) //must be able to rise at least one full creature
|
if (maxHealth < subject->MaxHealth()) //must be able to rise at least one full creature
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1637,13 +1640,20 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
|
|||||||
}
|
}
|
||||||
else //no target stack on this tile
|
else //no target stack on this tile
|
||||||
{
|
{
|
||||||
if(spell->getTargetType() == CSpell::CREATURE
|
if(spell->getTargetType() == CSpell::CREATURE)
|
||||||
|| (spell->getTargetType() == CSpell::CREATURE_EXPERT_MASSIVE
|
|
||||||
&& mode == ECastingMode::HERO_CASTING //TODO why???
|
|
||||||
&& caster
|
|
||||||
&& caster->getSpellSchoolLevel(spell) < SecSkillLevel::EXPERT))
|
|
||||||
{
|
{
|
||||||
return ESpellCastProblem::WRONG_SPELL_TARGET;
|
if(caster && mode == ECastingMode::HERO_CASTING) //TODO why???
|
||||||
|
{
|
||||||
|
const CSpell::TargetInfo ti = spell->getTargetInfo(caster->getSpellSchoolLevel(spell));
|
||||||
|
|
||||||
|
if(!ti.massive)
|
||||||
|
return ESpellCastProblem::WRONG_SPELL_TARGET;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ESpellCastProblem::WRONG_SPELL_TARGET;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1688,6 +1698,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
|
|||||||
{
|
{
|
||||||
bool allStacksImmune = true;
|
bool allStacksImmune = true;
|
||||||
//we are interested only in enemy stacks when casting offensive spells
|
//we are interested only in enemy stacks when casting offensive spells
|
||||||
|
//TODO: should hero be able to cast non-smart negative spell if all enemy stacks are immune?
|
||||||
auto stacks = spell->isNegative() ? battleAliveStacks(!side) : battleAliveStacks();
|
auto stacks = spell->isNegative() ? battleAliveStacks(!side) : battleAliveStacks();
|
||||||
for(auto stack : stacks)
|
for(auto stack : stacks)
|
||||||
{
|
{
|
||||||
@ -1726,43 +1737,38 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
|
|||||||
switch(spell->getTargetType())
|
switch(spell->getTargetType())
|
||||||
{
|
{
|
||||||
case CSpell::CREATURE:
|
case CSpell::CREATURE:
|
||||||
case CSpell::CREATURE_EXPERT_MASSIVE:
|
|
||||||
if(mode == ECastingMode::HERO_CASTING)
|
if(mode == ECastingMode::HERO_CASTING)
|
||||||
{
|
{
|
||||||
const CGHeroInstance * caster = battleGetFightingHero(side);
|
const CGHeroInstance * caster = battleGetFightingHero(side);
|
||||||
|
const CSpell::TargetInfo ti = spell->getTargetInfo(caster->getSpellSchoolLevel(spell));
|
||||||
bool targetExists = false;
|
bool targetExists = false;
|
||||||
for(const CStack * stack : battleGetAllStacks()) //dead stacks will be immune anyway
|
for(const CStack * stack : battleGetAllStacks()) //dead stacks will be immune anyway
|
||||||
{
|
{
|
||||||
switch (spell->positiveness)
|
bool immune = battleIsImmune(caster, spell, mode, stack->position) != ESpellCastProblem::OK;
|
||||||
{
|
bool casterStack = stack->owner == caster->getOwner();
|
||||||
case CSpell::POSITIVE:
|
|
||||||
if(stack->owner == caster->getOwner())
|
if(!immune)
|
||||||
|
switch (spell->positiveness)
|
||||||
{
|
{
|
||||||
if(battleIsImmune(caster, spell, mode, stack->position) == ESpellCastProblem::OK)
|
case CSpell::POSITIVE:
|
||||||
|
if(casterStack || !ti.smart)
|
||||||
|
{
|
||||||
|
targetExists = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CSpell::NEUTRAL:
|
||||||
|
targetExists = true;
|
||||||
|
break;
|
||||||
|
break;
|
||||||
|
case CSpell::NEGATIVE:
|
||||||
|
if(!casterStack || !ti.smart)
|
||||||
{
|
{
|
||||||
targetExists = true;
|
targetExists = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CSpell::NEUTRAL:
|
|
||||||
if(battleIsImmune(caster, spell, mode, stack->position) == ESpellCastProblem::OK)
|
|
||||||
{
|
|
||||||
targetExists = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case CSpell::NEGATIVE:
|
|
||||||
if(stack->owner != caster->getOwner())
|
|
||||||
{
|
|
||||||
if(battleIsImmune(caster, spell, mode, stack->position) == ESpellCastProblem::OK)
|
|
||||||
{
|
|
||||||
targetExists = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if(!targetExists)
|
if(!targetExists)
|
||||||
{
|
{
|
||||||
@ -1787,31 +1793,32 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetPossibleTargets(PlayerColor
|
|||||||
switch(spell->getTargetType())
|
switch(spell->getTargetType())
|
||||||
{
|
{
|
||||||
case CSpell::CREATURE:
|
case CSpell::CREATURE:
|
||||||
case CSpell::CREATURE_EXPERT_MASSIVE:
|
|
||||||
{
|
{
|
||||||
const CGHeroInstance * caster = battleGetFightingHero(playerToSide(player)); //TODO
|
const CGHeroInstance * caster = battleGetFightingHero(playerToSide(player)); //TODO
|
||||||
|
const CSpell::TargetInfo ti = spell->getTargetInfo(caster->getSpellSchoolLevel(spell));
|
||||||
|
|
||||||
for(const CStack * stack : battleAliveStacks())
|
for(const CStack * stack : battleAliveStacks())
|
||||||
{
|
{
|
||||||
switch (spell->positiveness)
|
bool immune = battleIsImmune(caster, spell, mode, stack->position) != ESpellCastProblem::OK;
|
||||||
{
|
bool casterStack = stack->owner == caster->getOwner();
|
||||||
case CSpell::POSITIVE:
|
|
||||||
if(stack->owner == caster->getOwner())
|
if(!immune)
|
||||||
if(battleIsImmune(caster, spell, mode, stack->position) == ESpellCastProblem::OK)
|
switch (spell->positiveness)
|
||||||
|
{
|
||||||
|
case CSpell::POSITIVE:
|
||||||
|
if(casterStack || ti.smart)
|
||||||
ret.push_back(stack->position);
|
ret.push_back(stack->position);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CSpell::NEUTRAL:
|
case CSpell::NEUTRAL:
|
||||||
if(battleIsImmune(caster, spell, mode, stack->position) == ESpellCastProblem::OK)
|
|
||||||
ret.push_back(stack->position);
|
ret.push_back(stack->position);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CSpell::NEGATIVE:
|
case CSpell::NEGATIVE:
|
||||||
if(stack->owner != caster->getOwner())
|
if(!casterStack || ti.smart)
|
||||||
if(battleIsImmune(caster, spell, mode, stack->position) == ESpellCastProblem::OK)
|
|
||||||
ret.push_back(stack->position);
|
ret.push_back(stack->position);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1909,7 +1916,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
|
|||||||
if(deadStack && deadStack->owner != player) //you can resurrect only your own stacks //FIXME: it includes alive stacks as well
|
if(deadStack && deadStack->owner != player) //you can resurrect only your own stacks //FIXME: it includes alive stacks as well
|
||||||
return ESpellCastProblem::NO_APPROPRIATE_TARGET;
|
return ESpellCastProblem::NO_APPROPRIATE_TARGET;
|
||||||
}
|
}
|
||||||
else if(spell->getTargetType() == CSpell::CREATURE || spell->getTargetType() == CSpell::CREATURE_EXPERT_MASSIVE)
|
else if(spell->getTargetType() == CSpell::CREATURE)
|
||||||
{
|
{
|
||||||
if(!aliveStack)
|
if(!aliveStack)
|
||||||
return ESpellCastProblem::NO_APPROPRIATE_TARGET;
|
return ESpellCastProblem::NO_APPROPRIATE_TARGET;
|
||||||
@ -2011,14 +2018,14 @@ std::set<const CStack*> CBattleInfoCallback::getAffectedCreatures(const CSpell *
|
|||||||
const auto attackedHexes = spell->rangeInHexes(destinationTile, skillLevel, attackerSide);
|
const auto attackedHexes = spell->rangeInHexes(destinationTile, skillLevel, attackerSide);
|
||||||
const bool onlyAlive = !spell->isRisingSpell(); //when casting resurrection or animate dead we should be allow to select dead stack
|
const bool onlyAlive = !spell->isRisingSpell(); //when casting resurrection or animate dead we should be allow to select dead stack
|
||||||
|
|
||||||
|
const CSpell::TargetInfo ti = spell->getTargetInfo(skillLevel);
|
||||||
//TODO: more generic solution for mass spells
|
//TODO: more generic solution for mass spells
|
||||||
if(spell->id == SpellID::DEATH_RIPPLE || spell->id == SpellID::DESTROY_UNDEAD || spell->id == SpellID::ARMAGEDDON)
|
if(spell->id == SpellID::DEATH_RIPPLE || spell->id == SpellID::DESTROY_UNDEAD )
|
||||||
{
|
{
|
||||||
for(const CStack *stack : battleGetAllStacks())
|
for(const CStack *stack : battleGetAllStacks())
|
||||||
{
|
{
|
||||||
if((spell->id == SpellID::DEATH_RIPPLE && !stack->getCreature()->isUndead()) //death ripple
|
if((spell->id == SpellID::DEATH_RIPPLE && !stack->getCreature()->isUndead()) //death ripple
|
||||||
|| (spell->id == SpellID::DESTROY_UNDEAD && stack->getCreature()->isUndead()) //destroy undead
|
|| (spell->id == SpellID::DESTROY_UNDEAD && stack->getCreature()->isUndead()) //destroy undead
|
||||||
|| (spell->id == SpellID::ARMAGEDDON) //Armageddon
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if(stack->isValidTarget())
|
if(stack->isValidTarget())
|
||||||
@ -2057,7 +2064,7 @@ std::set<const CStack*> CBattleInfoCallback::getAffectedCreatures(const CSpell *
|
|||||||
lightningHex = BattleHex::getClosestTile (stack->attackerOwned, destinationTile, possibleHexes);
|
lightningHex = BattleHex::getClosestTile (stack->attackerOwned, destinationTile, possibleHexes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (spell->range[skillLevel].size() > 1) //custom many-hex range
|
else if (spell->getLevelInfo(skillLevel).range.size() > 1) //custom many-hex range
|
||||||
{
|
{
|
||||||
for(BattleHex hex : attackedHexes)
|
for(BattleHex hex : attackedHexes)
|
||||||
{
|
{
|
||||||
@ -2075,33 +2082,34 @@ std::set<const CStack*> CBattleInfoCallback::getAffectedCreatures(const CSpell *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(spell->getTargetType() == CSpell::CREATURE_EXPERT_MASSIVE)
|
else if(spell->getTargetType() == CSpell::CREATURE)
|
||||||
{
|
{
|
||||||
if(skillLevel < 3) /*not expert */
|
if (ti.massive)
|
||||||
{
|
{
|
||||||
const CStack * st = battleGetStackByPos(destinationTile, onlyAlive);
|
TStacks stacks = battleGetAllStacks();
|
||||||
if(st)
|
|
||||||
attackedCres.insert(st);
|
vstd::erase_if(stacks,[&](const CStack * stack){
|
||||||
|
return ti.smart && spell->isNegative() && stack->owner == attackerOwner);
|
||||||
|
});
|
||||||
|
|
||||||
|
vstd::erase_if(stacks,[&](const CStack * stack){
|
||||||
|
return ti.smart && spell->isPositive() && stack->owner != attackerOwner);
|
||||||
|
});
|
||||||
|
|
||||||
|
vstd::erase_if(stacks,[&](const CStack * stack){
|
||||||
|
return !stack->isValidTarget(!onlyAlive);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (auto stack : stacks)
|
||||||
|
attackedCres.insert(stack);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (auto stack : battleGetAllStacks())
|
if(const CStack * st = battleGetStackByPos(destinationTile, onlyAlive))
|
||||||
{
|
attackedCres.insert(st);
|
||||||
/*if it's non negative spell and our unit or non positive spell and hostile unit */
|
}
|
||||||
if((!spell->isNegative() && stack->owner == attackerOwner)
|
|
||||||
||(!spell->isPositive() && stack->owner != attackerOwner )
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if(stack->isValidTarget(!onlyAlive))
|
|
||||||
attackedCres.insert(stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} //if(caster->getSpellSchoolLevel(s) < 3)
|
|
||||||
}
|
|
||||||
else if(spell->getTargetType() == CSpell::CREATURE)
|
|
||||||
{
|
|
||||||
if(const CStack * st = battleGetStackByPos(destinationTile, onlyAlive))
|
|
||||||
attackedCres.insert(st);
|
|
||||||
}
|
}
|
||||||
else //custom range from attackedHexes
|
else //custom range from attackedHexes
|
||||||
{
|
{
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
namespace SpellConfig
|
namespace SpellConfig
|
||||||
{
|
{
|
||||||
static const std::string LEVEL_NAMES[] = {"none", "basic", "advanced", "expert"};
|
static const std::string LEVEL_NAMES[] = {"none", "basic", "advanced", "expert"};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace boost::assign;
|
using namespace boost::assign;
|
||||||
@ -130,12 +130,21 @@ namespace SRSLPraserHelpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace SRSLPraserHelpers;
|
CSpell::LevelInfo::LevelInfo()
|
||||||
|
:description(""),cost(0),power(0),AIValue(0),smartTarget(true),range("0")
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CSpell::LevelInfo::~LevelInfo()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
CSpell::CSpell():
|
CSpell::CSpell():
|
||||||
id(SpellID::NONE), level(0),
|
id(SpellID::NONE), level(0),
|
||||||
earth(false), water(false), fire(false), air(false),
|
earth(false), water(false), fire(false), air(false),
|
||||||
power(0),
|
|
||||||
combatSpell(false), creatureAbility(false),
|
combatSpell(false), creatureAbility(false),
|
||||||
positiveness(ESpellPositiveness::NEUTRAL),
|
positiveness(ESpellPositiveness::NEUTRAL),
|
||||||
mainEffectAnim(-1),
|
mainEffectAnim(-1),
|
||||||
@ -143,18 +152,29 @@ CSpell::CSpell():
|
|||||||
isRising(false), isDamage(false), isOffensive(false),
|
isRising(false), isDamage(false), isOffensive(false),
|
||||||
targetType(ETargetType::NO_TARGET)
|
targetType(ETargetType::NO_TARGET)
|
||||||
{
|
{
|
||||||
|
levels.resize(GameConstants::SPELL_SCHOOL_LEVELS);
|
||||||
}
|
}
|
||||||
|
|
||||||
CSpell::~CSpell()
|
CSpell::~CSpell()
|
||||||
{
|
{
|
||||||
for(auto & elem : effects)
|
|
||||||
for(size_t j=0; j<elem.size(); j++)
|
|
||||||
delete elem[j];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const
|
||||||
|
{
|
||||||
|
if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS)
|
||||||
|
{
|
||||||
|
logGlobal->errorStream() << __FUNCTION__ << " invalid school level " << level;
|
||||||
|
throw new std::runtime_error("Invalid school level");
|
||||||
|
}
|
||||||
|
|
||||||
|
return levels.at(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
{
|
{
|
||||||
|
using namespace SRSLPraserHelpers;
|
||||||
|
|
||||||
std::vector<BattleHex> ret;
|
std::vector<BattleHex> ret;
|
||||||
|
|
||||||
if(id == SpellID::FIRE_WALL || id == SpellID::FORCE_FIELD)
|
if(id == SpellID::FIRE_WALL || id == SpellID::FORCE_FIELD)
|
||||||
@ -192,7 +212,7 @@ std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string rng = range[schoolLvl] + ','; //copy + artificial comma for easier handling
|
std::string rng = getLevelInfo(schoolLvl).range + ','; //copy + artificial comma for easier handling
|
||||||
|
|
||||||
if(rng.size() >= 1 && rng[0] != 'X') //there is at lest one hex in range
|
if(rng.size() >= 1 && rng[0] != 'X') //there is at lest one hex in range
|
||||||
{
|
{
|
||||||
@ -201,7 +221,7 @@ std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl,
|
|||||||
bool readingFirst = true;
|
bool readingFirst = true;
|
||||||
for(auto & elem : rng)
|
for(auto & elem : rng)
|
||||||
{
|
{
|
||||||
if(std::isdigit(elem) ) //reading numer
|
if(std::isdigit(elem) ) //reading number
|
||||||
{
|
{
|
||||||
if(readingFirst)
|
if(readingFirst)
|
||||||
number1 += elem;
|
number1 += elem;
|
||||||
@ -258,6 +278,20 @@ CSpell::ETargetType CSpell::getTargetType() const
|
|||||||
return targetType;
|
return targetType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CSpell::TargetInfo CSpell::getTargetInfo(const int level) const
|
||||||
|
{
|
||||||
|
TargetInfo info;
|
||||||
|
|
||||||
|
auto & levelInfo = getLevelInfo(level);
|
||||||
|
|
||||||
|
info.type = getTargetType();
|
||||||
|
info.smart = levelInfo.smartTarget;
|
||||||
|
info.massive = levelInfo.range == "X";
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool CSpell::isCombatSpell() const
|
bool CSpell::isCombatSpell() const
|
||||||
{
|
{
|
||||||
return combatSpell;
|
return combatSpell;
|
||||||
@ -305,7 +339,7 @@ bool CSpell::isSpecialSpell() const
|
|||||||
|
|
||||||
bool CSpell::hasEffects() const
|
bool CSpell::hasEffects() const
|
||||||
{
|
{
|
||||||
return effects.size() && effects[0].size();
|
return !levels[0].effects.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& CSpell::getIconImmune() const
|
const std::string& CSpell::getIconImmune() const
|
||||||
@ -322,12 +356,12 @@ const std::string& CSpell::getCastSound() const
|
|||||||
|
|
||||||
si32 CSpell::getCost(const int skillLevel) const
|
si32 CSpell::getCost(const int skillLevel) const
|
||||||
{
|
{
|
||||||
return costs[skillLevel];
|
return getLevelInfo(skillLevel).cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
si32 CSpell::getPower(const int skillLevel) const
|
si32 CSpell::getPower(const int skillLevel) const
|
||||||
{
|
{
|
||||||
return powers[skillLevel];
|
return getLevelInfo(skillLevel).power;
|
||||||
}
|
}
|
||||||
|
|
||||||
//si32 CSpell::calculatePower(const int skillLevel) const
|
//si32 CSpell::calculatePower(const int skillLevel) const
|
||||||
@ -352,27 +386,20 @@ void CSpell::getEffects(std::vector<Bonus>& lst, const int level) const
|
|||||||
logGlobal->errorStream() << __FUNCTION__ << " invalid school level " << level;
|
logGlobal->errorStream() << __FUNCTION__ << " invalid school level " << level;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<Bonus> & effects = levels[level].effects;
|
||||||
|
|
||||||
if(effects.empty())
|
if(effects.empty())
|
||||||
{
|
|
||||||
logGlobal->errorStream() << __FUNCTION__ << " This spell (" + name + ") has no bonus effects! " << name;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(effects.size() <= level)
|
|
||||||
{
|
|
||||||
logGlobal->errorStream() << __FUNCTION__ << " This spell (" + name + ") is missing entry for level " << level;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(effects[level].empty())
|
|
||||||
{
|
{
|
||||||
logGlobal->errorStream() << __FUNCTION__ << " This spell (" + name + ") has no effects for level " << level;
|
logGlobal->errorStream() << __FUNCTION__ << " This spell (" + name + ") has no effects for level " << level;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lst.reserve(lst.size() + effects[level].size());
|
lst.reserve(lst.size() + effects.size());
|
||||||
|
|
||||||
for(Bonus *b : effects[level])
|
for(const Bonus & b : effects)
|
||||||
{
|
{
|
||||||
lst.push_back(Bonus(*b));
|
lst.push_back(Bonus(b));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,20 +478,6 @@ bool CSpell::isImmuneBy(const IBonusBearer* obj) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSpell::setAttributes(const std::string& newValue)
|
|
||||||
{
|
|
||||||
attributes = newValue;
|
|
||||||
if(attributes.find("CREATURE_TARGET_1") != std::string::npos
|
|
||||||
|| attributes.find("CREATURE_TARGET_2") != std::string::npos)
|
|
||||||
targetType = CREATURE_EXPERT_MASSIVE;
|
|
||||||
else if(attributes.find("CREATURE_TARGET") != std::string::npos)
|
|
||||||
targetType = CREATURE;
|
|
||||||
else if(attributes.find("OBSTACLE_TARGET") != std::string::npos)
|
|
||||||
targetType = OBSTACLE;
|
|
||||||
else
|
|
||||||
targetType = NO_TARGET;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSpell::setIsOffensive(const bool val)
|
void CSpell::setIsOffensive(const bool val)
|
||||||
{
|
{
|
||||||
isOffensive = val;
|
isOffensive = val;
|
||||||
@ -576,8 +589,6 @@ std::vector<JsonNode> CSpellHandler::loadLegacyData(size_t dataSize)
|
|||||||
else if(attributes.find("OBSTACLE_TARGET") != std::string::npos)
|
else if(attributes.find("OBSTACLE_TARGET") != std::string::npos)
|
||||||
targetType = "OBSTACLE";
|
targetType = "OBSTACLE";
|
||||||
|
|
||||||
lineNode["targetType"].String() = targetType;
|
|
||||||
|
|
||||||
//save parsed level specific data
|
//save parsed level specific data
|
||||||
for(size_t i = 0; i < GameConstants::SPELL_SCHOOL_LEVELS; i++)
|
for(size_t i = 0; i < GameConstants::SPELL_SCHOOL_LEVELS; i++)
|
||||||
{
|
{
|
||||||
@ -588,6 +599,16 @@ std::vector<JsonNode> CSpellHandler::loadLegacyData(size_t dataSize)
|
|||||||
level["aiValue"].Float() = AIVals[i];
|
level["aiValue"].Float() = AIVals[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(targetType == "CREATURE_EXPERT_MASSIVE")
|
||||||
|
{
|
||||||
|
lineNode["targetType"].String() = "CREATURE";
|
||||||
|
getLevel(3)["range"].String() = "X";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lineNode["targetType"].String() = targetType;
|
||||||
|
}
|
||||||
|
|
||||||
legacyData.push_back(lineNode);
|
legacyData.push_back(lineNode);
|
||||||
|
|
||||||
|
|
||||||
@ -677,8 +698,6 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json)
|
|||||||
spell->targetType = CSpell::CREATURE;
|
spell->targetType = CSpell::CREATURE;
|
||||||
else if(targetType == "OBSTACLE")
|
else if(targetType == "OBSTACLE")
|
||||||
spell->targetType = CSpell::OBSTACLE;
|
spell->targetType = CSpell::OBSTACLE;
|
||||||
else if(targetType == "CREATURE_EXPERT_MASSIVE")
|
|
||||||
spell->targetType = CSpell::CREATURE_EXPERT_MASSIVE;
|
|
||||||
|
|
||||||
|
|
||||||
spell->mainEffectAnim = json["anim"].Float();
|
spell->mainEffectAnim = json["anim"].Float();
|
||||||
@ -767,17 +786,17 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json)
|
|||||||
|
|
||||||
|
|
||||||
const JsonNode & graphicsNode = json["graphics"];
|
const JsonNode & graphicsNode = json["graphics"];
|
||||||
|
|
||||||
spell->iconImmune = graphicsNode["iconImmune"].String();
|
spell->iconImmune = graphicsNode["iconImmune"].String();
|
||||||
spell->iconBook = graphicsNode["iconBook"].String();
|
spell->iconBook = graphicsNode["iconBook"].String();
|
||||||
spell->iconEffect = graphicsNode["iconEffect"].String();
|
spell->iconEffect = graphicsNode["iconEffect"].String();
|
||||||
spell->iconScenarioBonus = graphicsNode["iconScenarioBonus"].String();
|
spell->iconScenarioBonus = graphicsNode["iconScenarioBonus"].String();
|
||||||
spell->iconScroll = graphicsNode["iconScroll"].String();
|
spell->iconScroll = graphicsNode["iconScroll"].String();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const JsonNode & soundsNode = json["sounds"];
|
const JsonNode & soundsNode = json["sounds"];
|
||||||
|
|
||||||
spell->castSound = soundsNode["cast"].String();
|
spell->castSound = soundsNode["cast"].String();
|
||||||
|
|
||||||
|
|
||||||
@ -785,48 +804,37 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json)
|
|||||||
|
|
||||||
const int levelsCount = GameConstants::SPELL_SCHOOL_LEVELS;
|
const int levelsCount = GameConstants::SPELL_SCHOOL_LEVELS;
|
||||||
|
|
||||||
spell->AIVals.resize(levelsCount);
|
|
||||||
spell->costs.resize(levelsCount);
|
|
||||||
spell->descriptions.resize(levelsCount);
|
|
||||||
|
|
||||||
spell->powers.resize(levelsCount);
|
|
||||||
spell->range.resize(levelsCount);
|
|
||||||
|
|
||||||
for(int levelIndex = 0; levelIndex < levelsCount; levelIndex++)
|
for(int levelIndex = 0; levelIndex < levelsCount; levelIndex++)
|
||||||
{
|
{
|
||||||
const JsonNode & levelNode = json["levels"][LEVEL_NAMES[levelIndex]];
|
const JsonNode & levelNode = json["levels"][LEVEL_NAMES[levelIndex]];
|
||||||
|
|
||||||
|
CSpell::LevelInfo & levelObject = spell->levels[levelIndex];
|
||||||
|
|
||||||
spell->descriptions[levelIndex] = levelNode["description"].String();
|
const si32 levelPower = levelNode["power"].Float();
|
||||||
spell->costs[levelIndex] = levelNode["cost"].Float();
|
|
||||||
spell->powers[levelIndex] = levelNode["power"].Float();
|
levelObject.description = levelNode["description"].String();
|
||||||
spell->AIVals[levelIndex] = levelNode["aiValue"].Float();
|
levelObject.cost = levelNode["cost"].Float();
|
||||||
|
levelObject.power = levelPower;
|
||||||
const JsonNode & effectsNode = levelNode["effects"];
|
levelObject.AIValue = levelNode["aiValue"].Float();
|
||||||
|
levelObject.smartTarget = levelNode["targetModifier"]["smart"].Bool();
|
||||||
if(!effectsNode.isNull())
|
|
||||||
|
for(const auto & elem : levelNode["effects"].Struct())
|
||||||
{
|
{
|
||||||
if(spell->effects.empty())
|
const JsonNode & bonusNode = elem.second;
|
||||||
spell->effects.resize(levelsCount);
|
Bonus * b = JsonUtils::parseBonus(bonusNode);
|
||||||
|
const bool usePowerAsValue = bonusNode["val"].isNull();
|
||||||
|
|
||||||
for(const auto & elem : effectsNode.Struct())
|
//TODO: make this work. see CSpellHandler::afterLoadFinalization()
|
||||||
{
|
//b->sid = spell->id; //for all
|
||||||
const JsonNode & bonusNode = elem.second;
|
|
||||||
Bonus * b = JsonUtils::parseBonus(bonusNode);
|
|
||||||
const bool usePowerAsValue = bonusNode["val"].isNull();
|
|
||||||
|
|
||||||
//TODO: make this work. see CSpellHandler::afterLoadFinalization()
|
b->source = Bonus::SPELL_EFFECT;//for all
|
||||||
//b->sid = spell->id; //for all
|
|
||||||
|
|
||||||
b->source = Bonus::SPELL_EFFECT;//for all
|
|
||||||
|
|
||||||
if(usePowerAsValue)
|
if(usePowerAsValue)
|
||||||
{
|
b->val = levelPower;
|
||||||
b->val = spell->powers[levelIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
spell->effects[levelIndex].push_back(b);
|
levelObject.effects.push_back(*b);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return spell;
|
return spell;
|
||||||
@ -836,9 +844,9 @@ void CSpellHandler::afterLoadFinalization()
|
|||||||
{
|
{
|
||||||
//FIXME: it is a bad place for this code, should refactor loadFromJson to know object id during loading
|
//FIXME: it is a bad place for this code, should refactor loadFromJson to know object id during loading
|
||||||
for(auto spell: objects)
|
for(auto spell: objects)
|
||||||
for(auto & level: spell->effects)
|
for(auto & level: spell->levels)
|
||||||
for(auto * bonus: level)
|
for(auto & bonus: level.effects)
|
||||||
bonus->sid = spell->id;
|
bonus.sid = spell->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,26 +23,49 @@ struct BattleHex;
|
|||||||
class DLL_LINKAGE CSpell
|
class DLL_LINKAGE CSpell
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// struct LevelInfo
|
struct LevelInfo
|
||||||
// {
|
{
|
||||||
//
|
std::string description; //descriptions of spell for skill level
|
||||||
// };
|
si32 cost; //per skill level: 0 - none, 1 - basic, etc
|
||||||
//
|
si32 power; //per skill level: 0 - none, 1 - basic, etc
|
||||||
// /** \brief Low level accessor. Don`t use it if absolutely necessary
|
si32 AIValue; //AI values: per skill level: 0 - none, 1 - basic, etc
|
||||||
// *
|
|
||||||
// * \param level. spell school level
|
bool smartTarget;
|
||||||
// * \return Spell level info structure
|
std::string range;
|
||||||
// *
|
|
||||||
// */
|
std::vector<Bonus> effects;
|
||||||
// const LevelInfo& getLevelInfo(const int level);
|
|
||||||
|
LevelInfo();
|
||||||
|
~LevelInfo();
|
||||||
|
|
||||||
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
|
{
|
||||||
|
h & description & cost & power & AIValue & smartTarget & range & effects;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** \brief Low level accessor. Don`t use it if absolutely necessary
|
||||||
|
*
|
||||||
|
* \param level. spell school level
|
||||||
|
* \return Spell level info structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const CSpell::LevelInfo& getLevelInfo(const int level) const;
|
||||||
public:
|
public:
|
||||||
enum ETargetType {NO_TARGET, CREATURE, CREATURE_EXPERT_MASSIVE, OBSTACLE};
|
enum ETargetType {NO_TARGET, CREATURE, OBSTACLE};
|
||||||
enum ESpellPositiveness {NEGATIVE = -1, NEUTRAL = 0, POSITIVE = 1};
|
enum ESpellPositiveness {NEGATIVE = -1, NEUTRAL = 0, POSITIVE = 1};
|
||||||
|
|
||||||
|
struct TargetInfo
|
||||||
|
{
|
||||||
|
ETargetType type;
|
||||||
|
bool smart;
|
||||||
|
bool massive;
|
||||||
|
};
|
||||||
|
|
||||||
SpellID id;
|
SpellID id;
|
||||||
std::string identifier; //???
|
std::string identifier; //???
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string abbName; //abbreviated name
|
|
||||||
std::vector<std::string> descriptions; //descriptions of spell for skill levels: 0 - none, 1 - basic, etc
|
|
||||||
si32 level;
|
si32 level;
|
||||||
bool earth;
|
bool earth;
|
||||||
bool water;
|
bool water;
|
||||||
@ -55,7 +78,7 @@ public:
|
|||||||
bool combatSpell; //is this spell combat (true) or adventure (false)
|
bool combatSpell; //is this spell combat (true) or adventure (false)
|
||||||
bool creatureAbility; //if true, only creatures can use this spell
|
bool creatureAbility; //if true, only creatures can use this spell
|
||||||
si8 positiveness; //1 if spell is positive for influenced stacks, 0 if it is indifferent, -1 if it's negative
|
si8 positiveness; //1 if spell is positive for influenced stacks, 0 if it is indifferent, -1 if it's negative
|
||||||
std::vector<std::string> range; //description of spell's range in SRSL by magic school level
|
|
||||||
std::vector<SpellID> counteredSpells; //spells that are removed when effect of this spell is placed on creature (for bless-curse, haste-slow, and similar pairs)
|
std::vector<SpellID> counteredSpells; //spells that are removed when effect of this spell is placed on creature (for bless-curse, haste-slow, and similar pairs)
|
||||||
|
|
||||||
CSpell();
|
CSpell();
|
||||||
@ -63,7 +86,9 @@ public:
|
|||||||
|
|
||||||
std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = nullptr ) 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)
|
std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = nullptr ) 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)
|
si16 mainEffectAnim; //main spell effect animation, in AC format (or -1 when none)
|
||||||
ETargetType getTargetType() const;
|
ETargetType getTargetType() const; //deprecated
|
||||||
|
|
||||||
|
const CSpell::TargetInfo getTargetInfo(const int level) const;
|
||||||
|
|
||||||
bool isCombatSpell() const;
|
bool isCombatSpell() const;
|
||||||
bool isAdventureSpell() const;
|
bool isAdventureSpell() const;
|
||||||
@ -102,23 +127,25 @@ public:
|
|||||||
* Returns resource name of icon for SPELL_IMMUNITY bonus
|
* Returns resource name of icon for SPELL_IMMUNITY bonus
|
||||||
*/
|
*/
|
||||||
const std::string& getIconImmune() const;
|
const std::string& getIconImmune() const;
|
||||||
|
|
||||||
const std::string& getCastSound() const;
|
const std::string& getCastSound() 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 & level & earth & water & fire & air & power
|
||||||
& powers & probabilities & AIVals & attributes & combatSpell & creatureAbility & positiveness & range & counteredSpells & mainEffectAnim;
|
& probabilities & attributes & combatSpell & creatureAbility & positiveness & counteredSpells & mainEffectAnim;
|
||||||
h & isRising & isDamage & isOffensive;
|
h & isRising & isDamage & isOffensive;
|
||||||
h & targetType;
|
h & targetType;
|
||||||
h & effects & immunities & limiters;
|
h & immunities & limiters;
|
||||||
h & iconImmune;
|
h & iconImmune;
|
||||||
h & absoluteImmunities & defaultProbability;
|
h & absoluteImmunities & defaultProbability;
|
||||||
|
|
||||||
h & isSpecial;
|
h & isSpecial;
|
||||||
|
|
||||||
h & castSound & iconBook & iconEffect & iconScenarioBonus & iconScroll ;
|
h & castSound & iconBook & iconEffect & iconScenarioBonus & iconScroll;
|
||||||
|
|
||||||
|
h & levels;
|
||||||
|
|
||||||
}
|
}
|
||||||
friend class CSpellHandler;
|
friend class CSpellHandler;
|
||||||
friend class Graphics;
|
friend class Graphics;
|
||||||
@ -126,13 +153,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
void setIsOffensive(const bool val);
|
void setIsOffensive(const bool val);
|
||||||
void setIsRising(const bool val);
|
void setIsRising(const bool val);
|
||||||
void setAttributes(const std::string& newValue);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
si32 defaultProbability;
|
si32 defaultProbability;
|
||||||
std::vector<si32> costs; //per skill level: 0 - none, 1 - basic, etc
|
|
||||||
std::vector<si32> powers; //per skill level: 0 - none, 1 - basic, etc
|
|
||||||
std::vector<si32> AIVals; //AI values: per skill level: 0 - none, 1 - basic, etc
|
|
||||||
|
|
||||||
bool isRising;
|
bool isRising;
|
||||||
bool isDamage;
|
bool isDamage;
|
||||||
@ -141,11 +164,8 @@ private:
|
|||||||
|
|
||||||
std::string attributes; //reference only attributes //todo: remove or include in configuration format, currently unused
|
std::string attributes; //reference only attributes //todo: remove or include in configuration format, currently unused
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ETargetType targetType;
|
ETargetType targetType;
|
||||||
|
|
||||||
std::vector<std::vector<Bonus *> > effects; // [level 0-3][list of effects]
|
|
||||||
std::vector<Bonus::BonusType> immunities; //any of these grants immunity
|
std::vector<Bonus::BonusType> immunities; //any of these grants immunity
|
||||||
std::vector<Bonus::BonusType> absoluteImmunities; //any of these grants immunity, cant be negated
|
std::vector<Bonus::BonusType> absoluteImmunities; //any of these grants immunity, cant be negated
|
||||||
std::vector<Bonus::BonusType> limiters; //all of them are required to be affected
|
std::vector<Bonus::BonusType> limiters; //all of them are required to be affected
|
||||||
@ -153,14 +173,16 @@ private:
|
|||||||
///graphics related stuff
|
///graphics related stuff
|
||||||
|
|
||||||
std::string iconImmune;
|
std::string iconImmune;
|
||||||
|
|
||||||
std::string iconBook;
|
std::string iconBook;
|
||||||
std::string iconEffect;
|
std::string iconEffect;
|
||||||
std::string iconScenarioBonus;
|
std::string iconScenarioBonus;
|
||||||
std::string iconScroll;
|
std::string iconScroll;
|
||||||
|
|
||||||
///sound related stuff
|
///sound related stuff
|
||||||
std::string castSound;
|
std::string castSound;
|
||||||
|
|
||||||
|
std::vector<LevelInfo> levels;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
#include "mapping/CCampaignHandler.h" //for CCampaignState
|
#include "mapping/CCampaignHandler.h" //for CCampaignState
|
||||||
#include "rmg/CMapGenerator.h" // for CMapGenOptions
|
#include "rmg/CMapGenerator.h" // for CMapGenOptions
|
||||||
|
|
||||||
const ui32 version = 747;
|
const ui32 version = 748;
|
||||||
const ui32 minSupportedVersion = version;
|
const ui32 minSupportedVersion = version;
|
||||||
|
|
||||||
class CConnection;
|
class CConnection;
|
||||||
|
@ -4082,50 +4082,50 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
|
|||||||
|
|
||||||
if (spell->isOffensiveSpell())
|
if (spell->isOffensiveSpell())
|
||||||
{
|
{
|
||||||
int spellDamage = 0;
|
int spellDamage = 0;
|
||||||
if (stack && mode != ECastingMode::MAGIC_MIRROR)
|
if (stack && mode != ECastingMode::MAGIC_MIRROR)
|
||||||
|
{
|
||||||
|
int unitSpellPower = stack->valOfBonuses(Bonus::SPECIFIC_SPELL_POWER, spellID.toEnum());
|
||||||
|
if (unitSpellPower)
|
||||||
|
sc.dmgToDisplay = spellDamage = stack->count * unitSpellPower; //TODO: handle immunities
|
||||||
|
else //Faerie Dragon
|
||||||
{
|
{
|
||||||
int unitSpellPower = stack->valOfBonuses(Bonus::SPECIFIC_SPELL_POWER, spellID.toEnum());
|
usedSpellPower = stack->valOfBonuses(Bonus::CREATURE_SPELL_POWER) * stack->count / 100;
|
||||||
if (unitSpellPower)
|
sc.dmgToDisplay = 0;
|
||||||
sc.dmgToDisplay = spellDamage = stack->count * unitSpellPower; //TODO: handle immunities
|
|
||||||
else //Faerie Dragon
|
|
||||||
{
|
|
||||||
usedSpellPower = stack->valOfBonuses(Bonus::CREATURE_SPELL_POWER) * stack->count / 100;
|
|
||||||
sc.dmgToDisplay = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
int chainLightningModifier = 0;
|
}
|
||||||
for(auto & attackedCre : attackedCres)
|
int chainLightningModifier = 0;
|
||||||
|
for(auto & attackedCre : attackedCres)
|
||||||
|
{
|
||||||
|
if(vstd::contains(sc.resisted, (attackedCre)->ID)) //this creature resisted the spell
|
||||||
|
continue;
|
||||||
|
|
||||||
|
BattleStackAttacked bsa;
|
||||||
|
if ((destination > -1 && (attackedCre)->coversPos(destination)) || (spell->getLevelInfo(spellLvl).range == "X" || mode == ECastingMode::ENCHANTER_CASTING))
|
||||||
|
//display effect only upon primary target of area spell
|
||||||
|
//FIXME: if no stack is attacked, there is no animation and interface freezes
|
||||||
{
|
{
|
||||||
if(vstd::contains(sc.resisted, (attackedCre)->ID)) //this creature resisted the spell
|
bsa.flags |= BattleStackAttacked::EFFECT;
|
||||||
continue;
|
bsa.effect = spell->mainEffectAnim;
|
||||||
|
|
||||||
BattleStackAttacked bsa;
|
|
||||||
if ((destination > -1 && (attackedCre)->coversPos(destination)) || (spell->range.at(spellLvl) == "X" || mode == ECastingMode::ENCHANTER_CASTING))
|
|
||||||
//display effect only upon primary target of area spell
|
|
||||||
//FIXME: if no stack is attacked, ther eis no animation and interface freezes
|
|
||||||
{
|
|
||||||
bsa.flags |= BattleStackAttacked::EFFECT;
|
|
||||||
bsa.effect = spell->mainEffectAnim;
|
|
||||||
}
|
|
||||||
if (spellDamage)
|
|
||||||
bsa.damageAmount = spellDamage >> chainLightningModifier;
|
|
||||||
else
|
|
||||||
bsa.damageAmount = gs->curB->calculateSpellDmg(spell, caster, attackedCre, spellLvl, usedSpellPower) >> chainLightningModifier;
|
|
||||||
|
|
||||||
sc.dmgToDisplay += bsa.damageAmount;
|
|
||||||
|
|
||||||
bsa.stackAttacked = (attackedCre)->ID;
|
|
||||||
if (mode == ECastingMode::ENCHANTER_CASTING) //multiple damage spells cast
|
|
||||||
bsa.attackerID = stack->ID;
|
|
||||||
else
|
|
||||||
bsa.attackerID = -1;
|
|
||||||
(attackedCre)->prepareAttacked(bsa);
|
|
||||||
si.stacks.push_back(bsa);
|
|
||||||
|
|
||||||
if (spellID == SpellID::CHAIN_LIGHTNING)
|
|
||||||
++chainLightningModifier;
|
|
||||||
}
|
}
|
||||||
|
if (spellDamage)
|
||||||
|
bsa.damageAmount = spellDamage >> chainLightningModifier;
|
||||||
|
else
|
||||||
|
bsa.damageAmount = gs->curB->calculateSpellDmg(spell, caster, attackedCre, spellLvl, usedSpellPower) >> chainLightningModifier;
|
||||||
|
|
||||||
|
sc.dmgToDisplay += bsa.damageAmount;
|
||||||
|
|
||||||
|
bsa.stackAttacked = (attackedCre)->ID;
|
||||||
|
if (mode == ECastingMode::ENCHANTER_CASTING) //multiple damage spells cast
|
||||||
|
bsa.attackerID = stack->ID;
|
||||||
|
else
|
||||||
|
bsa.attackerID = -1;
|
||||||
|
(attackedCre)->prepareAttacked(bsa);
|
||||||
|
si.stacks.push_back(bsa);
|
||||||
|
|
||||||
|
if (spellID == SpellID::CHAIN_LIGHTNING)
|
||||||
|
++chainLightningModifier;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell->hasEffects())
|
if (spell->hasEffects())
|
||||||
@ -4456,7 +4456,7 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Magic Mirror effect
|
//Magic Mirror effect
|
||||||
if (spell->isNegative() && mode != ECastingMode::MAGIC_MIRROR && spell->level && spell->range.at(0) == "0") //it is actual spell and can be reflected to single target, no recurrence
|
if (spell->isNegative() && mode != ECastingMode::MAGIC_MIRROR && spell->level && spell->getLevelInfo(0).range == "0") //it is actual spell and can be reflected to single target, no recurrence
|
||||||
{
|
{
|
||||||
for(auto & attackedCre : attackedCres)
|
for(auto & attackedCre : attackedCres)
|
||||||
{
|
{
|
||||||
@ -5354,7 +5354,7 @@ void CGameHandler::attackCasting(const BattleAttack & bat, Bonus::BonusType atta
|
|||||||
void CGameHandler::handleAttackBeforeCasting (const BattleAttack & bat)
|
void CGameHandler::handleAttackBeforeCasting (const BattleAttack & bat)
|
||||||
{
|
{
|
||||||
const CStack * attacker = gs->curB->battleGetStackByID(bat.stackAttacking);
|
const CStack * attacker = gs->curB->battleGetStackByID(bat.stackAttacking);
|
||||||
attackCasting(bat, Bonus::SPELL_BEFORE_ATTACK, attacker); //no detah stare / acid bretah needed?
|
attackCasting(bat, Bonus::SPELL_BEFORE_ATTACK, attacker); //no death stare / acid breath needed?
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
|
void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
|
||||||
|
Loading…
x
Reference in New Issue
Block a user