1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-30 23:18:08 +02:00

[refactor] spell handling

* more config options for spells
  + mind immunity handled by config
  + direct damage immunity handled by config
  + immunity icon configurable
- removed mind_spell flag 
* more use of new spell identifacation
This commit is contained in:
alexvins 2013-02-13 19:35:43 +00:00
parent 6d06710684
commit ceea466f54
18 changed files with 282 additions and 217 deletions

View File

@ -9,10 +9,9 @@
// counters: array of ids of countering spells
// flags: string array of
// damage
// damage - ATM used only in CBattleInfoCallback::calculateSpellDmg
// offensive
// rising
// mind
// summoning //todo:
//effects: array of structure for bonuses for permanent effects
@ -26,6 +25,9 @@
// immunity: array any of these bonus grants immunity
// limit: array required bonus to be affected, all required
//graphics - OPTIONAL; object;
// iconImmune - OPTIONAL; string; resourse path of icon for SPELL_IMMUNITY bonus (relative to DATA or SPRITES)
"spells":
{
"summonBoat" :
@ -111,7 +113,9 @@
"effect": 0,
"anim": -1,
"ranges": [ "X", "X", "X", "X" ],
"flags" : ["damage"]
"flags" : ["damage"],
"immunities" : ["DIRECT_DAMAGE_IMMUNITY"]
},
"forceField" :
{
@ -126,7 +130,8 @@
"effect": 0,
"anim": -1,
"ranges": [ "0", "0", "0", "0" ],
"flags" : ["damage"]
"flags" : ["damage"],
"immunities" : ["DIRECT_DAMAGE_IMMUNITY"]
},
"earthquake" :
{
@ -141,7 +146,8 @@
"effect": -1,
"anim": 64,
"ranges": [ "0", "0", "0", "0" ],
"flags" : ["damage", "offensive"]
"flags" : ["damage", "offensive"],
"immunities" : ["DIRECT_DAMAGE_IMMUNITY"]
},
"iceBolt" :
{
@ -149,7 +155,8 @@
"effect": -1,
"anim": 46,
"ranges": [ "0", "0", "0", "0" ],
"flags" : ["damage", "offensive"]
"flags" : ["damage", "offensive"],
"immunities" : ["DIRECT_DAMAGE_IMMUNITY"]
},
"lightningBolt" :
{
@ -157,7 +164,8 @@
"effect": -1,
"anim": 38,
"ranges": [ "0", "0", "0", "0" ],
"flags" : ["damage", "offensive"]
"flags" : ["damage", "offensive"],
"immunities" : ["DIRECT_DAMAGE_IMMUNITY"]
},
"implosion" :
{
@ -165,7 +173,11 @@
"effect": -1,
"anim": 10,
"ranges": [ "0", "0", "0", "0" ],
"flags" : ["damage", "offensive"]
"graphics":{
"iconImmune":"ZVS/LIB1.RES/E_SPIMP"
},
"flags" : ["damage", "offensive"],
"immunities" : ["DIRECT_DAMAGE_IMMUNITY"]
},
"chainLightning" :
{
@ -181,7 +193,8 @@
"effect": -1,
"anim": 45,
"ranges": [ "1", "1", "1", "1" ],
"flags" : ["damage", "offensive"]
"flags" : ["damage", "offensive"],
"immunities" : ["DIRECT_DAMAGE_IMMUNITY"]
},
"fireball" :
{
@ -189,7 +202,8 @@
"effect": -1,
"anim": 53,
"ranges": [ "0,1", "0,1", "0,1", "0,1" ],
"flags" : ["damage", "offensive"]
"flags" : ["damage", "offensive"],
"immunities" : ["DIRECT_DAMAGE_IMMUNITY"]
},
"inferno" :
{
@ -197,7 +211,8 @@
"effect": -1,
"anim": 9,
"ranges": [ "0-2", "0-2", "0-2", "0-2" ],
"flags" : ["damage", "offensive"]
"flags" : ["damage", "offensive"],
"immunities" : ["DIRECT_DAMAGE_IMMUNITY"]
},
"meteorShower" :
{
@ -205,7 +220,11 @@
"effect": -1,
"anim": 16,
"ranges": [ "0,1", "0,1", "0,1", "0,1" ],
"flags" : ["damage", "offensive"]
"graphics":{
"iconImmune":"ZVS/LIB1.RES/E_SPMET"
},
"flags" : ["damage", "offensive"],
"immunities" : ["DIRECT_DAMAGE_IMMUNITY"]
},
"deathRipple" :
{
@ -214,7 +233,8 @@
"anim": 8,
"ranges": [ "X", "X", "X", "X" ],
"flags" : ["damage", "offensive"],
"immunity": ["SIEGE_WEAPON","UNDEAD"]
"immunity": ["SIEGE_WEAPON","UNDEAD"],
"immunities" : ["DIRECT_DAMAGE_IMMUNITY"]
},
"destroyUndead" :
{
@ -223,7 +243,8 @@
"anim": 29,
"ranges": [ "X", "X", "X", "X" ],
"flags" : ["damage", "offensive"],
"limit":["UNDEAD"]
"limit":["UNDEAD"],
"immunities" : ["DIRECT_DAMAGE_IMMUNITY"]
},
"armageddon" :
@ -232,7 +253,11 @@
"effect": -1,
"anim": 12,
"ranges": [ "X", "X", "X", "X" ],
"flags" : ["damage", "offensive"]
"graphics":{
"iconImmune":"ZVS/LIB1.RES/E_SPARM"
},
"flags" : ["damage", "offensive"],
"immunities" : ["DIRECT_DAMAGE_IMMUNITY"]
},
"shield" :
{
@ -364,7 +389,10 @@
"id": 35,
"effect": 0,
"anim": 41,
"ranges": [ "0", "0", "0", "X" ]
"ranges": [ "0", "0", "0", "X" ],
"graphics":{
"iconImmune":"ZVS/LIB1.RES/E_SPDISP"
}
},
"magicMirror" :
{
@ -573,7 +601,6 @@
"effect": -1,
"anim": 30,
"ranges": [ "0", "0", "0", "X" ], "counters" : [49],
"flags" : ["mind"],
"effects":
[
{
@ -581,7 +608,8 @@
"duration": "N_TURNS",
"values":[-1,-1,-2,-2]
}
]
],
"immunity":["MIND_IMMUNITY"]
},
"fortune" :
{
@ -636,6 +664,9 @@
"effect": -1,
"anim": 19,
"ranges": [ "0", "0", "0", "X" ],
"graphics":{
"iconImmune":"ZVS/LIB1.RES/E_SPSLOW"
},
"counters" : [53],
"effects":
[
@ -705,7 +736,9 @@
"effect": -1,
"anim": 35,
"ranges": [ "0", "0", "0-1", "0-2" ],
"flags" : ["mind"],
"graphics":{
"iconImmune":"ZVS/LIB1.RES/E_SPBERS"
},
"effects":
[
{
@ -713,7 +746,8 @@
"duration": "N_TURNS",
"values":[0,1,2,3]
}
]
],
"immunity":["MIND_IMMUNITY"]
},
"hypnotize" :
{
@ -721,7 +755,9 @@
"effect": -1,
"anim": 21,
"ranges": [ "0", "0", "0", "0" ],
"flags" : ["mind"],
"graphics":{
"iconImmune":"ZVS/LIB1.RES/E_SPHYPN"
},
"effects":
[
{
@ -729,7 +765,8 @@
"duration": "N_TURNS",
"values":[0,1,2,3]
}
]
],
"immunity":["MIND_IMMUNITY"]
},
"forgetfulness" :
{
@ -737,7 +774,6 @@
"effect": -1,
"anim": 42,
"ranges": [ "0", "0", "0", "X" ],
"flags" : ["mind"],
"effects":
[
{
@ -746,7 +782,8 @@
"values":[0,1,2,3]
}
],
"limit":["SHOOTER"]
"limit":["SHOOTER"],
"immunity":["MIND_IMMUNITY"]
},
"blind" :
{
@ -754,7 +791,9 @@
"effect": -1,
"anim": 6,
"ranges": [ "0", "0", "0", "0" ],
"flags" : ["mind"],
"graphics":{
"iconImmune":"ZVS/LIB1.RES/E_SPBLIND"
},
"effects":
[
{
@ -772,7 +811,8 @@
"duration": "UNITL_BEING_ATTACKED",
"values":[0,0,0,0]
}
]
],
"immunity":["MIND_IMMUNITY"]
},
"teleport" :
{
@ -964,7 +1004,10 @@
"id": 78,
"effect": -1,
"anim": 41,
"ranges": [ "0", "0", "0", "0" ]
"ranges": [ "0", "0", "0", "0" ],
"graphics":{
"iconImmune":"ZVS/LIB1.RES/E_SPDISB"
}
},
"deathStare" :
{
@ -996,7 +1039,8 @@
"effect": 0,
"anim": 81,
"ranges": [ "0", "0", "0", "0" ],
"flags" : ["damage"]
"flags" : ["damage"],
"immunities" : ["DIRECT_DAMAGE_IMMUNITY"]
}
}

View File

@ -188,7 +188,7 @@ ui32 BattleInfo::calculateHealedHP(const CSpell * spell, int usedSpellPower, int
}
bool BattleInfo::resurrects(SpellID spellid) const
{
return VLC->spellh->spells[spellid]->isRisingSpell();
return spellid.toSpell()->isRisingSpell();
}
const CStack * BattleInfo::battleGetStack(BattleHex pos, bool onlyAlive)
@ -942,7 +942,7 @@ si32 CStack::magicResistance() const
void CStack::stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse)
{
const CSpell * sp = VLC->spellh->spells[sse.sid];
const CSpell * sp = SpellID(sse.sid).toSpell();
std::vector<Bonus> tmp;
sp->getEffects(tmp, sse.val);

View File

@ -817,7 +817,7 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c
if(const Bonus *slayerEffect = info.attackerBonuses->getEffect(SpellID::SLAYER)) //slayer handling //TODO: apply only ONLY_MELEE_FIGHT / DISTANCE_FIGHT?
{
std::vector<int> affectedIds;
int spLevel = slayerEffect->val;
int spLevel = slayerEffect->val;
for(int g = 0; g < VLC->creh->creatures.size(); ++g)
{
@ -837,7 +837,7 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c
{
if(defenderType->idNumber == affectedIds[g])
{
attackDefenceDifference += VLC->spellh->spells[SpellID::SLAYER]->powers[spLevel];
attackDefenceDifference += SpellID(SpellID::SLAYER).toSpell()->powers[spLevel];
break;
}
}
@ -1476,7 +1476,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
{
if (spell->isPositive() && subject->hasBonusOfType(Bonus::RECEPTIVE)) //accept all positive spells
return ESpellCastProblem::OK;
if (spell->isImmuneBy(subject)) //TODO: move all logic to spellhandler
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
@ -1499,7 +1499,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
bool hasPositiveSpell = false;
BOOST_FOREACH(const Bonus * b, *spellBon)
{
if(VLC->spellh->spells[b->sid]->isPositive())
if(SpellID(b->sid).toSpell()->isPositive())
{
hasPositiveSpell = true;
break;

View File

@ -295,7 +295,7 @@ void CCreatureHandler::loadCreatures()
ncre.addBonus(0, Bonus::ATTACKS_ALL_ADJACENT);
if(boost::algorithm::find_first(ncre.abilityRefs, "IMMUNE_TO_MIND_SPELLS"))
ncre.addBonus(0, Bonus::MIND_IMMUNITY); //giants are immune to mind spells
ncre.addBonus(0, Bonus::MIND_IMMUNITY);
if(boost::algorithm::find_first(ncre.abilityRefs, "IMMUNE_TO_FIRE_SPELLS"))
ncre.addBonus(0, Bonus::FIRE_IMMUNITY);
if(boost::algorithm::find_first(ncre.abilityRefs, "HAS_EXTENDED_ATTACK"))
@ -925,7 +925,7 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigPars
case 'K':
case 'k':
b.type = Bonus::SPELL_AFTER_ATTACK;
b.subtype = stringToNumber(mod);
b.subtype = stringToNumber(mod);
break;
case 'h':
b.type= Bonus::HATE;
@ -1018,7 +1018,7 @@ CreatureID CCreatureHandler::pickRandomMonster(const boost::function<int()> &ran
int r = 0;
if(tier == -1) //pick any allowed creature
{
do
do
{
r = vstd::pickRandomElementOf(creatures, randGen)->idNumber;
} while (vstd::contains(VLC->creh->notUsedMonsters,r));

View File

@ -299,7 +299,7 @@ void CCreatureSet::eraseStack(TSlot slot)
bool CCreatureSet::contains(const CStackInstance *stack) const
{
if(!stack)
if(!stack)
return false;
for(TSlots::const_iterator i = stacks.begin(); i != stacks.end(); ++i)
@ -315,7 +315,7 @@ TSlot CCreatureSet::findStack(const CStackInstance *stack) const
if (h && h->commander == stack)
return -2;
if(!stack)
if(!stack)
return -1;
for(TSlots::const_iterator i = stacks.begin(); i != stacks.end(); ++i)
@ -345,7 +345,7 @@ void CCreatureSet::joinStack(TSlot slot, CStackInstance * stack)
assert(c == stack->type);
assert(c);
//TODO move stuff
//TODO move stuff
changeStackCount(slot, stack->count);
vstd::clear_pointer(stack);
}
@ -486,10 +486,10 @@ void CStackInstance::init()
type = NULL;
idRand = -1;
_armyObj = NULL;
setNodeType(STACK_INSTANCE);
setNodeType(STACK_INSTANCE);
}
int CStackInstance::getQuantityID() const
int CStackInstance::getQuantityID() const
{
return CCreature::getQuantityID(count);
}
@ -703,6 +703,7 @@ std::string CStackInstance::bonusToString(Bonus *bonus, bool description) const
std::string CStackInstance::bonusToGraphics(Bonus *bonus) const
{
std::string fileName;
bool fullPath = false;
switch (bonus->type)
{
//"E_ALIVE.bmp"
@ -800,28 +801,9 @@ std::string CStackInstance::bonusToGraphics(Bonus *bonus) const
//"E_SHOOTN.bmp"
case Bonus::SPELL_IMMUNITY:
{
switch (bonus->subtype)
{
case 62: //Blind
fileName = "E_SPBLIND.bmp"; break;
case 35: // Dispell
fileName = "E_SPDISP.bmp"; break;
case 78: // Dispell beneficial spells
fileName = "E_SPDISB.bmp"; break;
case 60: //Hypnotize
fileName = "E_SPHYPN.bmp"; break;
case 18: //Implosion
fileName = "E_SPIMP.bmp"; break;
case 59: //Berserk
fileName = "E_SPBERS.bmp"; break;
case 23: //Meteor Shower
fileName = "E_SPMET.bmp"; break;
case 26: //Armageddon
fileName = "E_SPARM.bmp"; break;
case 54: //Slow
fileName = "E_SPSLOW.bmp"; break;
//TODO: some generic spell handling?
}
fullPath = true;
const CSpell * sp = SpellID(bonus->subtype).toSpell();
fileName = sp->getIconImmune();
break;
}
//"E_SPAWILL.bmp"
@ -853,7 +835,7 @@ std::string CStackInstance::bonusToGraphics(Bonus *bonus) const
fileName = "E_SPCOLD.bmp"; break; //direct damage
}
break;
case Bonus::AIR_IMMUNITY:
case Bonus::AIR_IMMUNITY:
switch (bonus->subtype)
{
case 0:
@ -864,7 +846,7 @@ std::string CStackInstance::bonusToGraphics(Bonus *bonus) const
fileName = "E_LIGHT.bmp"; break;//direct damage
}
break;
case Bonus::EARTH_IMMUNITY:
case Bonus::EARTH_IMMUNITY:
switch (bonus->subtype)
{
case 0:
@ -909,7 +891,7 @@ std::string CStackInstance::bonusToGraphics(Bonus *bonus) const
case Bonus::LIFE_DRAIN:
fileName = "DrainLife.bmp"; break;
}
if(!fileName.empty())
if(!fileName.empty() && !fullPath)
fileName = "zvs/Lib1.res/" + fileName;
return fileName;
}
@ -981,7 +963,7 @@ CreatureID CStackInstance::getCreatureID() const
{
if(type)
return type->idNumber;
else
else
return CreatureID::NONE;
}

View File

@ -161,10 +161,10 @@ void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst
else if(type == MINE_EVNTS)
{
dst = VLC->generaltexth->mines[ser].second;
}
}
else if(type == SPELL_NAME)
{
dst = VLC->spellh->spells[ser]->name;
dst = SpellID(ser).toSpell()->name;
}
else if(type == CRE_SING_NAMES)
{
@ -819,7 +819,7 @@ void CGameState::init(StartInfo * si)
break;
case CScenarioTravel::STravelBonus::SPELL_SCROLL:
{
CArtifactInstance * scroll = CArtifactInstance::createScroll(VLC->spellh->spells[curBonus->info2]);
CArtifactInstance * scroll = CArtifactInstance::createScroll(SpellID(curBonus->info2).toSpell());
scroll->putAt(ArtifactLocation(hero, scroll->firstAvailableSlot(hero)));
}
break;
@ -1531,10 +1531,10 @@ void CGameState::init(StartInfo * si)
}
//init spells
vti->spells.resize(GameConstants::SPELL_LEVELS);
CSpell *s;
for(ui32 z=0; z<vti->obligatorySpells.size();z++)
{
s = VLC->spellh->spells[vti->obligatorySpells[z]];
CSpell *s = vti->obligatorySpells[z].toSpell();
vti->spells[s->level-1].push_back(s->id);
vti->possibleSpells -= s->id;
}
@ -1544,7 +1544,7 @@ void CGameState::init(StartInfo * si)
int sel = -1;
for(ui32 ps=0;ps<vti->possibleSpells.size();ps++)
total += VLC->spellh->spells[vti->possibleSpells[ps]]->probabilities[vti->subID];
total += vti->possibleSpells[ps].toSpell()->probabilities[vti->subID];
if (total == 0) // remaining spells have 0 probability
break;
@ -1552,7 +1552,7 @@ void CGameState::init(StartInfo * si)
int r = ran()%total;
for(ui32 ps=0; ps<vti->possibleSpells.size();ps++)
{
r -= VLC->spellh->spells[vti->possibleSpells[ps]]->probabilities[vti->subID];
r -= vti->possibleSpells[ps].toSpell()->probabilities[vti->subID];
if(r<0)
{
sel = ps;
@ -1562,7 +1562,7 @@ void CGameState::init(StartInfo * si)
if(sel<0)
sel=0;
CSpell *s = VLC->spellh->spells[vti->possibleSpells[sel]];
CSpell *s = vti->possibleSpells[sel].toSpell();
vti->spells[s->level-1].push_back(s->id);
vti->possibleSpells -= s->id;
}

View File

@ -5445,7 +5445,7 @@ void CGObservatory::onHeroVisit( const CGHeroInstance * h ) const
void CGShrine::onHeroVisit( const CGHeroInstance * h ) const
{
if(spell == 255)
if(spell == SpellID::NONE)
{
tlog1 << "Not initialized shrine visited!\n";
return;
@ -5476,7 +5476,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const
else //give spell
{
std::set<SpellID> spells;
spells.insert(SpellID(spell));
spells.insert(spell);
cb->changeSpells(h, true, spells);
iw.components.push_back(Component(Component::SPELL,spell,0,0));
@ -5487,7 +5487,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const
void CGShrine::initObj()
{
if(spell == 255) //spell not set
if(spell == SpellID::NONE) //spell not set
{
int level = ID-87;
std::vector<SpellID> possibilities;
@ -5509,7 +5509,7 @@ const std::string & CGShrine::getHoverText() const
if(wasVisited(cb->getCurrentPlayer())) //TODO: use local player, not current
{
hoverName += "\n" + VLC->generaltexth->allTexts[355]; // + (learn %s)
boost::algorithm::replace_first(hoverName,"%s",VLC->spellh->spells[spell]->name);
boost::algorithm::replace_first(hoverName,"%s", spell.toSpell()->name);
const CGHeroInstance *h = cb->getSelectedHero(cb->getCurrentPlayer());
if(h && vstd::contains(h->spells,spell)) //hero knows that ability
hoverName += "\n\n" + VLC->generaltexth->allTexts[354]; // (Already learned)
@ -5557,7 +5557,7 @@ void CGScholar::onHeroVisit( const CGHeroInstance * h ) const
if((type == SECONDARY_SKILL
&& ((ssl == 3) || (!ssl && !h->canLearnSkill()))) ////hero already has expert level in the skill or (don't know skill and doesn't have free slot)
|| (type == SPELL && (!h->getArt(ArtifactPosition::SPELLBOOK) || vstd::contains(h->spells, (ui32) bid)
|| (VLC->spellh->spells[bid]->level > h->getSecSkillLevel(SecondarySkill::WISDOM) + 2)
|| ( SpellID(bid).toSpell()->level > h->getSecSkillLevel(SecondarySkill::WISDOM) + 2)
))) //hero doesn't have a spellbook or already knows the spell or doesn't have Wisdom
{
type = PRIM_SKILL;

View File

@ -957,7 +957,7 @@ public:
class DLL_LINKAGE CGShrine : public CPlayersVisited
{
public:
ui8 spell; //number of spell or 255 if random
SpellID spell; //id of spell or NONE if random
void onHeroVisit(const CGHeroInstance * h) const override;
void initObj() override;
const std::string & getHoverText() const override;

View File

@ -129,7 +129,7 @@ std::vector<BattleHex> SpellCreatedObstacle::getAffectedTiles() const
case FIRE_WALL:
return std::vector<BattleHex>(1, pos);
case FORCE_FIELD:
return VLC->spellh->spells[SpellID::FORCE_FIELD]->rangeInHexes(pos, spellLevel, casterSide);
return SpellID(SpellID::FORCE_FIELD).toSpell()->rangeInHexes(pos, spellLevel, casterSide);
//TODO Fire Wall
default:
assert(0);

View File

@ -81,7 +81,7 @@ namespace SRSLPraserHelpers
return xy.first >=0 && xy.first < 17 && xy.second >= 0 && xy.second < 11;
}
//helper fonction for std::set<ui16> CSpell::rangeInHexes(unsigned int centralHex, ui8 schoolLvl ) const
//helper function for std::set<ui16> CSpell::rangeInHexes(unsigned int centralHex, ui8 schoolLvl ) const
static std::set<ui16> getInRange(unsigned int center, int low, int high)
{
std::set<ui16> ret;
@ -123,14 +123,10 @@ namespace SRSLPraserHelpers
}
using namespace SRSLPraserHelpers;
CSpellHandler::CSpellHandler()
{
}
CSpell::CSpell()
{
isDamage = false;
isMind = false;
isRising = false;
isOffensive = false;
}
@ -235,23 +231,12 @@ std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl,
return ret;
}
CSpell::ETargetType CSpell::getTargetType() const //TODO: parse these at game launch
CSpell::ETargetType CSpell::getTargetType() const
{
if(attributes.find("CREATURE_TARGET_1") != std::string::npos
|| attributes.find("CREATURE_TARGET_2") != std::string::npos)
return CREATURE_EXPERT_MASSIVE;
if(attributes.find("CREATURE_TARGET") != std::string::npos)
return CREATURE;
if(attributes.find("OBSTACLE_TARGET") != std::string::npos)
return OBSTACLE;
return NO_TARGET;
return targetType;
}
void CSpell::getEffects(std::vector<Bonus>& lst, const int level) const
{
if (level < 0 || level>3)
@ -270,6 +255,7 @@ void CSpell::getEffects(std::vector<Bonus>& lst, const int level) const
bool CSpell::isImmuneBy(const IBonusBearer* obj) const
{
//todo: use new bonus API
BOOST_FOREACH(auto b, limiters)
{
if (!obj->hasBonusOfType(b))
@ -282,24 +268,18 @@ bool CSpell::isImmuneBy(const IBonusBearer* obj) const
return true;
}
if (isMindSpell() && obj->hasBonusOfType(Bonus::MIND_IMMUNITY))
return true;
if (isDamageSpell() && obj->hasBonusOfType(Bonus::DIRECT_DAMAGE_IMMUNITY))
return true;
auto battleTestElementalImmunity = [&,this](Bonus::BonusType element) -> bool
{
if (!isPositive()) //negative or indifferent
{
if ((isDamageSpell() && obj->hasBonusOfType(element, 2)) || obj->hasBonusOfType(element, 1))
return true;
}
else if (isPositive()) //positive
if (isPositive())
{
if (obj->hasBonusOfType(element, 0)) //must be immune to all spells
return true;
}
else //negative or indifferent
{
if ((isDamageSpell() && obj->hasBonusOfType(element, 2)) || obj->hasBonusOfType(element, 1))
return true;
}
return false;
};
@ -325,14 +305,14 @@ bool CSpell::isImmuneBy(const IBonusBearer* obj) const
return true;
}
TBonusListPtr immunities = obj->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY));
TBonusListPtr levelImmunities = obj->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY));
if(obj->hasBonusOfType(Bonus::NEGATE_ALL_NATURAL_IMMUNITIES))
{
immunities->remove_if([](const Bonus* b){ return b->source == Bonus::CREATURE_ABILITY; });
levelImmunities->remove_if([](const Bonus* b){ return b->source == Bonus::CREATURE_ABILITY; });
}
if(obj->hasBonusOfType(Bonus::SPELL_IMMUNITY, id)
|| ( immunities->size() > 0 && immunities->totalValue() >= level && level))
|| ( levelImmunities->size() > 0 && levelImmunities->totalValue() >= level && level))
{
return true;
}
@ -340,6 +320,20 @@ bool CSpell::isImmuneBy(const IBonusBearer* obj) const
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;
}
bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos)
{
@ -350,10 +344,16 @@ bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos)
return false;
}
CSpell * CSpellHandler::loadSpell(CLegacyConfigParser & parser)
CSpellHandler::CSpellHandler()
{
}
CSpell * CSpellHandler::loadSpell(CLegacyConfigParser & parser, const SpellID id)
{
CSpell * spell = new CSpell; //new currently being read spell
spell->id = id;
spell->name = parser.readString();
spell->abbName = parser.readString();
spell->level = parser.readNumber();
@ -375,7 +375,24 @@ CSpell * CSpellHandler::loadSpell(CLegacyConfigParser & parser)
for (int i = 0; i < 4 ; i++)
spell->descriptions.push_back(parser.readString());
spell->attributes = parser.readString();
std::string attributes = parser.readString();
//spell fixes
if (id == SpellID::FORGETFULNESS)
{
//forgetfulness needs to get targets automatically on expert level
boost::replace_first(attributes, "CREATURE_TARGET", "CREATURE_TARGET_2");
}
if (id == SpellID::DISRUPTING_RAY)
{
// disrupting ray will now affect single creature
boost::replace_first(attributes,"2", "");
}
spell->setAttributes(attributes);
spell->mainEffectAnim = -1;
return spell;
}
@ -388,8 +405,8 @@ void CSpellHandler::loadSpells()
{
do
{
CSpell * spell = loadSpell(parser);
spell->id = SpellID(spells.size());
const SpellID id = SpellID(spells.size());
CSpell * spell = loadSpell(parser,id);
spell->combatSpell = combat;
spell->creatureAbility = alility;
spells.push_back(spell);
@ -410,9 +427,6 @@ void CSpellHandler::loadSpells()
skip(3);
read(true,true);//read creature abilities
boost::replace_first (spells[SpellID::DISRUPTING_RAY]->attributes, "2", ""); // disrupting ray will now affect single creature
spells.push_back(spells[SpellID::ACID_BREATH_DEFENSE]); //clone Acid Breath attributes for Acid Breath damage effect
//loading of additional spell traits
@ -452,10 +466,6 @@ void CSpellHandler::loadSpells()
{
s->isRising = true;
}
else if (flag == "mind")
{
s->isMind = true;
}
else if (flag == "offensive")
{
s->isOffensive = true;
@ -519,12 +529,14 @@ void CSpellHandler::loadSpells()
read_node("immunity",s->immunities);
read_node("limit",s->limiters);
const JsonNode & graphicsNode = spell.second["graphics"];
if (!graphicsNode.isNull())
{
const JsonNode& iconImmune = graphicsNode["iconImmune"];
if (!iconImmune.isNull())
s->iconImmune = iconImmune.String();
}
}
//spell fixes
//forgetfulness needs to get targets automatically on expert level
boost::replace_first(spells[SpellID::FORGETFULNESS]->attributes, "CREATURE_TARGET", "CREATURE_TARGET_2"); //TODO: use flags instead?
}
std::vector<bool> CSpellHandler::getDefaultAllowedSpells() const

View File

@ -38,7 +38,7 @@ public:
std::vector<si32> powers; //[er skill level: 0 - none, 1 - basic, etc
std::map<TFaction, si32> probabilities; //% chance to gain for castles
std::vector<si32> AIVals; //AI values: per skill level: 0 - none, 1 - basic, etc
std::string attributes; //reference only attributes
bool combatSpell; //is this spell combat (true) or adventure (false)
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
@ -60,7 +60,6 @@ public:
inline bool isRisingSpell() const;
inline bool isDamageSpell() const;
inline bool isMindSpell() const; //TODO: deprecated - remove, refactor
inline bool isOffensiveSpell() const;
inline bool hasEffects() const;
@ -68,26 +67,42 @@ public:
bool isImmuneBy(const IBonusBearer *obj) const;
/**
* Returns resource name of icon for SPELL_IMMUNITY bonus
*/
inline const std::string& getIconImmune() const;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & identifier & id & name & abbName & descriptions & level & earth & water & fire & air & power & costs
& powers & probabilities & AIVals & attributes & combatSpell & creatureAbility & positiveness & range & counteredSpells & mainEffectAnim;
h & isRising & isDamage & isMind;
h & isRising & isDamage & isOffensive;
h & targetType;
h & effects & immunities & limiters;
h & iconImmune;
}
friend class CSpellHandler;
private:
bool isRising;
bool isDamage;
bool isMind;
bool isOffensive;
std::string attributes; //reference only attributes
void setAttributes(const std::string& newValue);
ETargetType targetType;
std::vector<Bonus> effects [4];
std::vector<Bonus::BonusType> immunities; //any of these hrants immunity
std::vector<Bonus::BonusType> limiters; //all of them are required
std::vector<Bonus::BonusType> immunities; //any of these grants immunity
std::vector<Bonus::BonusType> limiters; //all of them are required to be affected
///graphics related stuff
std::string iconImmune;
};
///CSpell inlines
@ -127,11 +142,6 @@ bool CSpell::isDamageSpell() const
return isDamage;
}
bool CSpell::isMindSpell() const
{
return isMind;
}
bool CSpell::isOffensiveSpell() const
{
return isOffensive;
@ -142,11 +152,17 @@ bool CSpell::hasEffects() const
return !effects[0].empty();
}
const std::string& CSpell::getIconImmune() const
{
return iconImmune;
}
bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos); //for spells like Dimension Door
class DLL_LINKAGE CSpellHandler
{
CSpell * loadSpell(CLegacyConfigParser & parser);
CSpell * loadSpell(CLegacyConfigParser & parser, const SpellID id);
public:
CSpellHandler();

View File

@ -468,7 +468,7 @@ void CTownHandler::loadTown(CTown &town, const JsonNode & source)
VLC->modh->identifiers.requestIdentifier("spell." + node.first, [=, &town](si32 spellID)
{
VLC->spellh->spells[spellID]->probabilities[town.typeID] = chance;
SpellID(spellID).toSpell()->probabilities[town.typeID] = chance;
});
}

View File

@ -16,7 +16,7 @@ namespace GameConstants
{
const std::string VCMI_VERSION = "VCMI 0.91b";
/*
/*
* DATA_DIR contains the game data (Data/, MP3/, ...).
* BIN_DIR is where the vcmiclient/vcmiserver binaries reside
* LIB_DIR is where the AI libraries reside (linux only)
@ -306,7 +306,7 @@ namespace EMarketMode
{
enum EMarketMode
{
RESOURCE_RESOURCE, RESOURCE_PLAYER, CREATURE_RESOURCE, RESOURCE_ARTIFACT,
RESOURCE_RESOURCE, RESOURCE_PLAYER, CREATURE_RESOURCE, RESOURCE_ARTIFACT,
ARTIFACT_RESOURCE, ARTIFACT_EXP, CREATURE_EXP, CREATURE_UNDEAD, RESOURCE_SKILL,
MARTKET_AFTER_LAST_PLACEHOLDER
};
@ -419,7 +419,7 @@ public:
RANDOM_MAJOR_ART = 68,
RANDOM_RELIC_ART = 69,
RANDOM_HERO = 70,
RANDOM_MONSTER = 71,
RANDOM_MONSTER = 71,
RANDOM_MONSTER_L1 = 72,
RANDOM_MONSTER_L2 = 73,
RANDOM_MONSTER_L3 = 74,
@ -461,7 +461,7 @@ public:
WATERING_HOLE = 110,
WHIRLPOOL = 111,
WINDMILL = 112,
WITCH_HUT = 113,
WITCH_HUT = 113,
HOLE = 124,
RANDOM_MONSTER_L5 = 162,
RANDOM_MONSTER_L6 = 163,
@ -574,7 +574,7 @@ ID_LIKE_OPERATORS_DECLS(ETerrainType, ETerrainType::EETerrainType)
class BFieldType
{
public:
// 1. sand/shore 2. sand/mesas 3. dirt/birches 4. dirt/hills 5. dirt/pines 6. grass/hills 7. grass/pines
// 1. sand/shore 2. sand/mesas 3. dirt/birches 4. dirt/hills 5. dirt/pines 6. grass/hills 7. grass/pines
//8. lava 9. magic plains 10. snow/mountains 11. snow/trees 12. subterranean 13. swamp/trees 14. fiery fields
//15. rock lands 16. magic clouds 17. lucid pools 18. holy ground 19. clover field 20. evil fog
//21. "favourable winds" text on magic plains background 22. cursed ground 23. rough 24. ship to ship 25. ship
@ -744,6 +744,7 @@ public:
SpellID(ESpellID _num = NONE) : num(_num)
{}
//TODO: should this be const?
DLL_LINKAGE CSpell * toSpell() const;
ID_LIKE_CLASS_COMMON(SpellID, ESpellID)

View File

@ -40,7 +40,7 @@
#define BONUS_ITEM(x) ( #x, Bonus::x )
const std::map<std::string, int> bonusDurationMap = boost::assign::map_list_of
const std::map<std::string, int> bonusDurationMap = boost::assign::map_list_of
BONUS_ITEM(PERMANENT)
BONUS_ITEM(ONE_BATTLE)
BONUS_ITEM(ONE_DAY)
@ -156,9 +156,9 @@ int BonusList::totalValue() const
if(hasIndepMin && hasIndepMax)
assert(indepMin < indepMax);
const int notIndepBonuses = boost::count_if(bonuses, [](const Bonus *b)
{
return b->valType != Bonus::INDEPENDENT_MAX && b->valType != Bonus::INDEPENDENT_MIN;
const int notIndepBonuses = boost::count_if(bonuses, [](const Bonus *b)
{
return b->valType != Bonus::INDEPENDENT_MAX && b->valType != Bonus::INDEPENDENT_MIN;
});
if (hasIndepMax)
@ -260,7 +260,7 @@ void BonusList::eliminateDuplicates()
void BonusList::push_back(Bonus* const &x)
{
bonuses.push_back(x);
if (belongsToTree)
CBonusSystemNode::incrementTreeChangedNum();
}
@ -317,7 +317,7 @@ int IBonusBearer::valOfBonuses(Bonus::BonusType type, int subtype /*= -1*/) cons
{
std::stringstream cachingStr;
cachingStr << "type_" << type << "s_" << subtype;
CSelector s = Selector::type(type);
if(subtype != -1)
s = s && Selector::subtype(subtype);
@ -393,7 +393,7 @@ int IBonusBearer::MoraleVal() const
if(hasBonusOfType(Bonus::NON_LIVING) || hasBonusOfType(Bonus::UNDEAD) ||
hasBonusOfType(Bonus::NO_MORALE) || hasBonusOfType(Bonus::SIEGE_WEAPON))
return 0;
int ret = valOfBonuses(Bonus::MORALE);
if(hasBonusOfType(Bonus::SELF_MORALE)) //eg. minotaur
@ -406,9 +406,9 @@ int IBonusBearer::LuckVal() const
{
if(hasBonusOfType(Bonus::NO_LUCK))
return 0;
int ret = valOfBonuses(Bonus::LUCK);
if(hasBonusOfType(Bonus::SELF_LUCK)) //eg. halfling
vstd::amax(ret, +1);
@ -461,8 +461,8 @@ ui32 IBonusBearer::getMaxDamage() const
si32 IBonusBearer::manaLimit() const
{
return si32(getPrimSkillLevel(PrimarySkill::KNOWLEDGE)
* (100.0 + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::INTELLIGENCE))
return si32(getPrimSkillLevel(PrimarySkill::KNOWLEDGE)
* (100.0 + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::INTELLIGENCE))
/ 10.0);
}
@ -580,7 +580,7 @@ void CBonusSystemNode::getParents(TNodes &out)
{
const CBonusSystemNode *parent = parents[i];
out.insert(const_cast<CBonusSystemNode*>(parent));
}
}
}
void CBonusSystemNode::getBonusesRec(BonusList &out, const CSelector &selector, const CSelector &limit) const
@ -606,13 +606,13 @@ void CBonusSystemNode::getAllBonusesRec(BonusList &out) const
const TBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/, const std::string &cachingStr /*= ""*/) const
{
bool limitOnUs = (!root || root == this); //caching won't work when we want to limit bonuses against an external node
if (CBonusSystemNode::cachingEnabled && limitOnUs)
if (CBonusSystemNode::cachingEnabled && limitOnUs)
{
// Exclusive access for one thread
static boost::mutex m;
boost::mutex::scoped_lock lock(m);
// If the bonus system tree changes(state of a single node or the relations to each other) then
// If the bonus system tree changes(state of a single node or the relations to each other) then
// cache all bonus objects. Selector objects doesn't matter.
if (cachedLast != treeChanged)
{
@ -626,7 +626,7 @@ const TBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, c
cachedLast = treeChanged;
}
// If a bonus system request comes with a caching string then look up in the map if there are any
// pre-calculated bonus results. Limiters can't be cached so they have to be calculated.
if (cachingStr != "")
@ -642,7 +642,7 @@ const TBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, c
//We still don't have the bonuses (didn't returned them from cache)
//Perform bonus selection
auto ret = make_shared<BonusList>();
cachedBonuses.getBonuses(*ret, selector, limit);
cachedBonuses.getBonuses(*ret, selector, limit);
// Save the results in the cache
if(cachingStr != "")
@ -672,12 +672,12 @@ const TBonusListPtr CBonusSystemNode::getAllBonusesWithoutCaching(const CSelecto
}
else if(root)
{
//We want to limit our query against an external node. We get all its bonuses,
//We want to limit our query against an external node. We get all its bonuses,
// add the ones we're considering and see if they're cut out by limiters
BonusList rootBonuses, limitedRootBonuses;
getAllBonusesRec(rootBonuses);
BOOST_FOREACH(Bonus *b, beforeLimiting)
BOOST_FOREACH(Bonus *b, beforeLimiting)
rootBonuses.push_back(b);
rootBonuses.eliminateDuplicates();
@ -709,7 +709,7 @@ CBonusSystemNode::~CBonusSystemNode()
while(children.size())
children.front()->detachFrom(this);
}
BOOST_FOREACH(Bonus *b, exportedBonuses)
delete b;
}
@ -855,7 +855,7 @@ bool CBonusSystemNode::isIndependentNode() const
std::string CBonusSystemNode::nodeName() const
{
return description.size()
return description.size()
? description
: std::string("Bonus system node of type ") + typeid(*this).name();
}
@ -1024,7 +1024,7 @@ void CBonusSystemNode::limitBonuses(const BonusList &allBonuses, BonusList &out)
{
assert(&allBonuses != &out); //todo should it work in-place?
BonusList undecided = allBonuses,
BonusList undecided = allBonuses,
&accepted = out;
while(true)
@ -1038,13 +1038,13 @@ void CBonusSystemNode::limitBonuses(const BonusList &allBonuses, BonusList &out)
if(decision == ILimiter::DISCARD)
{
undecided.erase(i);
i--; continue;
i--; continue;
}
else if(decision == ILimiter::ACCEPT)
{
accepted.push_back(b);
undecided.erase(i);
i--; continue;
i--; continue;
}
else
assert(decision == ILimiter::NOT_SURE);
@ -1097,7 +1097,7 @@ int NBonus::getCount(const CBonusSystemNode *obj, Bonus::BonusSource from, int i
const CSpell * Bonus::sourceSpell() const
{
if(source == SPELL_EFFECT)
return VLC->spellh->spells[sid];
return SpellID(sid).toSpell();
return NULL;
}
@ -1115,7 +1115,7 @@ std::string Bonus::Description() const
str << VLC->arth->artifacts[sid]->Name();
break;;
case SPELL_EFFECT:
str << VLC->spellh->spells[sid]->name;
str << SpellID(sid).toSpell()->name;
break;
case CREATURE_ABILITY:
str << VLC->creh->creatures[sid]->namePl;
@ -1124,11 +1124,11 @@ std::string Bonus::Description() const
str << VLC->generaltexth->skillName[sid]/* << " secondary skill"*/;
break;
}
return str.str();
}
Bonus::Bonus(ui16 Dur, ui8 Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype/*=-1*/)
Bonus::Bonus(ui16 Dur, ui8 Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype/*=-1*/)
: duration(Dur), type(Type), subtype(Subtype), source(Src), val(Val), sid(ID), description(Desc)
{
additionalInfo = -1;
@ -1138,7 +1138,7 @@ Bonus::Bonus(ui16 Dur, ui8 Type, BonusSource Src, si32 Val, ui32 ID, std::string
boost::algorithm::trim(description);
}
Bonus::Bonus(ui16 Dur, ui8 Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype/*=-1*/, ValueType ValType /*= ADDITIVE_VALUE*/)
Bonus::Bonus(ui16 Dur, ui8 Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype/*=-1*/, ValueType ValType /*= ADDITIVE_VALUE*/)
: duration(Dur), type(Type), subtype(Subtype), source(Src), val(Val), sid(ID), valType(ValType)
{
additionalInfo = -1;
@ -1228,7 +1228,7 @@ namespace Selector
{
if(b->source == Bonus::SPELL_EFFECT)
{
CSpell *sp = VLC->spellh->spells[b->sid];
CSpell *sp = SpellID(b->sid).toSpell();
return sp->isPositive();
}
return false; //not a spell effect
@ -1373,8 +1373,8 @@ HasAnotherBonusLimiter::HasAnotherBonusLimiter( TBonusType bonus, TBonusSubtype
int HasAnotherBonusLimiter::limit(const BonusLimitationContext &context) const
{
CSelector mySelector = isSubtypeRelevant
? Selector::typeSubtype(type, subtype)
CSelector mySelector = isSubtypeRelevant
? Selector::typeSubtype(type, subtype)
: Selector::type(type);
//if we have a bonus of required type accepted, limiter should accept also this bonus
@ -1421,7 +1421,7 @@ bool CPropagatorNodeType::shouldBeAttached(CBonusSystemNode *dest)
return nodeType == dest->getNodeType();
}
CreatureNativeTerrainLimiter::CreatureNativeTerrainLimiter(int TerrainType)
CreatureNativeTerrainLimiter::CreatureNativeTerrainLimiter(int TerrainType)
: terrainType(TerrainType)
{
}
@ -1465,7 +1465,7 @@ CreatureAlignmentLimiter::CreatureAlignmentLimiter(si8 Alignment)
int CreatureAlignmentLimiter::limit(const BonusLimitationContext &context) const
{
const CCreature *c = retrieveCreature(&context.node);
if(!c)
if(!c)
return true;
switch(alignment)
{
@ -1503,7 +1503,7 @@ int RankRangeLimiter::limit(const BonusLimitationContext &context) const
return true;
}
int StackOwnerLimiter::limit(const BonusLimitationContext &context) const
int StackOwnerLimiter::limit(const BonusLimitationContext &context) const
{
const CStack *s = retreiveStackBattle(&context.node);
if(s)
@ -1524,16 +1524,16 @@ StackOwnerLimiter::StackOwnerLimiter(ui8 Owner)
: owner(Owner)
{
}
// int Bonus::limit(const BonusLimitationContext &context) const
// 1162 {
// 1163 if (limiter)
// 1164 return limiter->callNext(context);
// 1165 else
// 1166 return ILimiter::ACCEPT; //accept if there's no limiter
// 1167 }
//1168
// int Bonus::limit(const BonusLimitationContext &context) const
// 1162 {
// 1163 if (limiter)
// 1164 return limiter->callNext(context);
// 1165 else
// 1166 return ILimiter::ACCEPT; //accept if there's no limiter
// 1167 }
//1168
int LimiterList::limit( const BonusLimitationContext &context ) const
int LimiterList::limit( const BonusLimitationContext &context ) const
{
bool wasntSure = false;

View File

@ -37,7 +37,7 @@
extern boost::rand48 ran;
CGameState * CPrivilagedInfoCallback::gameState ()
{
{
return gs;
}
@ -92,7 +92,7 @@ void CPrivilagedInfoCallback::getTilesInRange( boost::unordered_set<int3, ShashI
double distance = pos.dist2d(int3(xd,yd,pos.z)) - 0.5;
if(distance <= radious)
{
if(player < 0
if(player < 0
|| (mode == 1 && team->fogOfWarMap[xd][yd][pos.z]==0)
|| (mode == -1 && team->fogOfWarMap[xd][yd][pos.z]==1)
)
@ -199,11 +199,10 @@ ArtifactID CPrivilagedInfoCallback::getArtSync (ui32 rand, int flags, bool erase
void CPrivilagedInfoCallback::getAllowedSpells(std::vector<SpellID> &out, ui16 level)
{
CSpell *spell;
for (ui32 i = 0; i < gs->map->allowedSpell.size(); i++) //spellh size appears to be greater (?)
{
spell = VLC->spellh->spells[i];
const CSpell *spell = SpellID(i).toSpell();
if (isAllowed (0, spell->id) && spell->level == level)
{
out.push_back(spell->id);
@ -394,7 +393,7 @@ std::vector<const CGObjectInstance*> CGameInfoCallback::getGuardingCreatures (in
bool CGameInfoCallback::getHeroInfo( const CGObjectInstance *hero, InfoAboutHero &dest ) const
{
const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(hero);
ERROR_RET_VAL_IF(!h, "That's not a hero!", false);
ERROR_RET_VAL_IF(!isVisible(h->getPosition(false)), "That hero is not visible!", false);
@ -448,7 +447,7 @@ bool CGameInfoCallback::isVisible(const CGObjectInstance *obj) const
// const CArmedInstance *armi = dynamic_cast<const CArmedInstance*>(obj);
// if(!armi)
// return NULL;
// else
// else
// return armi;
// }
@ -509,7 +508,7 @@ std::vector<const CGHeroInstance *> CGameInfoCallback::getAvailableHeroes(const
ret.resize(gs->players[*player].availableHeroes.size());
std::copy(gs->players[*player].availableHeroes.begin(),gs->players[*player].availableHeroes.end(),ret.begin());
return ret;
}
}
const TerrainTile * CGameInfoCallback::getTile( int3 tile, bool verbose) const
{
@ -573,7 +572,7 @@ EBuildingState::EBuildingState CGameInfoCallback::canBuildStructure( const CGTow
else if(ID == BuildingID::SHIPYARD)
{
const TerrainTile *tile = getTile(t->bestLocation(), false);
if(!tile || tile->terType != ETerrainType::WATER)
return EBuildingState::NO_WATER; //lack of water
}
@ -653,7 +652,7 @@ int CGameInfoCallback::getHeroCount( TPlayerColor player, bool includeGarrisoned
int ret = 0;
const PlayerState *p = gs->getPlayer(player);
ERROR_RET_VAL_IF(!p, "No such player!", -1);
if(includeGarrisoned)
return p->heroes.size();
else

View File

@ -672,7 +672,7 @@ CArtifactInstance * CMapLoaderH3M::createArtifact(int aid, int spellID /*= -1*/)
}
else
{
a = CArtifactInstance::createScroll(VLC->spellh->spells[spellID]);
a = CArtifactInstance::createScroll(SpellID(spellID).toSpell());
}
}
else
@ -1166,7 +1166,17 @@ void CMapLoaderH3M::readObjects()
{
CGShrine * shr = new CGShrine();
nobj = shr;
shr->spell = reader.readUInt8();
ui8 raw_id = reader.readUInt8();
if (255 == raw_id)
{
shr->spell = SpellID(SpellID::NONE);
}
else
{
shr->spell = SpellID(raw_id);
}
reader.skip(3);
break;
}

View File

@ -1199,7 +1199,7 @@ DLL_LINKAGE void StartAction::applyGs( CGameState *gs )
}
else
{
gs->curB->usedSpellsHistory[ba.side].push_back(VLC->spellh->spells[ba.additionalInfo]);
gs->curB->usedSpellsHistory[ba.side].push_back(SpellID(ba.additionalInfo).toSpell());
}
switch(ba.actionType)
@ -1240,7 +1240,7 @@ DLL_LINKAGE void BattleSpellCast::applyGs( CGameState *gs )
}
//Handle spells removing effects from stacks
const CSpell *spell = VLC->spellh->spells[id];
const CSpell *spell = SpellID(id).toSpell();
const bool removeAllSpells = id == SpellID::DISPEL;
const bool removeHelpful = id == SpellID::DISPEL_HELPFUL_SPELLS;

View File

@ -827,7 +827,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
bat.bsa.front().flags |= BattleStackAttacked::EFFECT;
bat.bsa.front().effect = VLC->spellh->spells[bonus->subtype]->mainEffectAnim; //hopefully it does not interfere with any other effect?
std::set<const CStack*> attackedCreatures = gs->curB->getAffectedCreatures(VLC->spellh->spells[bonus->subtype], bonus->val, att->owner, targetHex);
std::set<const CStack*> attackedCreatures = gs->curB->getAffectedCreatures(SpellID(bonus->subtype).toSpell(), bonus->val, att->owner, targetHex);
//TODO: get exact attacked hex for defender
BOOST_FOREACH(const CStack * stack, attackedCreatures)
@ -3913,7 +3913,7 @@ void CGameHandler::playerMessage( TPlayerColor player, const std::string &messag
void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex destination, ui8 casterSide, TPlayerColor casterColor, const CGHeroInstance * caster, const CGHeroInstance * secHero,
int usedSpellPower, ECastingMode::ECastingMode mode, const CStack * stack, si32 selectedStack)
{
const CSpell *spell = VLC->spellh->spells[spellID];
const CSpell *spell = SpellID(spellID).toSpell();
//Helper local function that creates obstacle on given position. Obstacle type is inferred from spell type.
@ -3977,7 +3977,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
if (caster) //calculate spell cost
{
sc.spellCost = gs->curB->battleGetSpellCost(VLC->spellh->spells[spellID], caster);
sc.spellCost = gs->curB->battleGetSpellCost(SpellID(spellID).toSpell(), caster);
if (secHero && mode == ECastingMode::HERO_CASTING) //handle mana channel
{
@ -4290,8 +4290,9 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
//TODO stack casting -> probably power will be zero; set the proper number of creatures manually
int percentBonus = caster ? caster->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, spellID) : 0;
bsa.amount = usedSpellPower * VLC->spellh->spells[spellID]->powers[spellLvl] *
(100 + percentBonus) / 100.0; //new feature - percentage bonus
bsa.amount = usedSpellPower
* SpellID(spellID).toSpell()->powers[spellLvl]
* (100 + percentBonus) / 100.0; //new feature - percentage bonus
if(bsa.amount)
sendAndApply(&bsa);
else
@ -4440,7 +4441,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
return false;
}
const CSpell *s = VLC->spellh->spells[ba.additionalInfo];
const CSpell *s = SpellID(ba.additionalInfo).toSpell();
if (s->mainEffectAnim > -1 || (s->id >= 66 || s->id <= 69) || s->id == SpellID::CLONE) //allow summon elementals
//TODO: special effects, like Clone
{
@ -4590,7 +4591,7 @@ void CGameHandler::stackTurnTrigger(const CStack * st)
{
int index = rand() % bl.size();
int spellID = bl[index]->subtype; //spell ID
if (gs->curB->battleCanCastThisSpell(st->owner, VLC->spellh->spells[spellID], ECastingMode::ENCHANTER_CASTING)) //TODO: select another?
if (gs->curB->battleCanCastThisSpell(st->owner, SpellID(spellID).toSpell(), ECastingMode::ENCHANTER_CASTING)) //TODO: select another?
{
int spellLeveL = bl[index]->val; //spell level
const CGHeroInstance * enemyHero = gs->curB->getHero(gs->curB->theOtherPlayer(st->owner));
@ -4654,14 +4655,14 @@ void CGameHandler::handleDamageFromObstacle(const CObstacleInstance &obstacle, c
oneTimeObstacle = true;
effect = 82; //makes
damage = gs->curB->calculateSpellDmg(VLC->spellh->spells[SpellID::LAND_MINE], hero, curStack,
damage = gs->curB->calculateSpellDmg(SpellID(SpellID::LAND_MINE).toSpell(), hero, curStack,
spellObstacle->spellLevel, spellObstacle->casterSpellPower);
//TODO even if obstacle wasn't created by hero (Tower "moat") it should deal dmg as if casted by hero,
//if it is bigger than default dmg. Or is it just irrelevant H3 implementation quirk
}
else if(obstacle.obstacleType == CObstacleInstance::FIRE_WALL)
{
damage = gs->curB->calculateSpellDmg(VLC->spellh->spells[SpellID::FIRE_WALL], hero, curStack,
damage = gs->curB->calculateSpellDmg(SpellID(SpellID::FIRE_WALL).toSpell(), hero, curStack,
spellObstacle->spellLevel, spellObstacle->casterSpellPower);
}
else
@ -5302,7 +5303,7 @@ void CGameHandler::attackCasting(const BattleAttack & bat, Bonus::BonusType atta
vstd::amin (chance, 100);
int destination = oneOfAttacked->position;
const CSpell * spell = VLC->spellh->spells[spellID];
const CSpell * spell = SpellID(spellID).toSpell();
if(gs->curB->battleCanCastThisSpellHere(attacker->owner, spell, ECastingMode::AFTER_ATTACK_CASTING, oneOfAttacked->position) != ESpellCastProblem::OK)
continue;