mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-02 00:10:22 +02:00
Cumulative spell effects
* Added experimental support for cumulative effects for ENCHANTED bonus * Updated and fixed SPECIAL_PECULIAR_ENCHANT processing * Initial implementation of cumulative spell effects. * Scheme for new spell feature - cumulative bonus.
This commit is contained in:
parent
0c7eeeaa5c
commit
0f5202689e
@ -259,11 +259,9 @@ void CBattleAI::attemptCastingSpell()
|
||||
{
|
||||
StackWithBonuses swb;
|
||||
swb.stack = sta;
|
||||
Bonus pseudoBonus;
|
||||
pseudoBonus.sid = ps.spell->id;
|
||||
pseudoBonus.val = skillLevel;
|
||||
pseudoBonus.turnsRemain = 1; //TODO
|
||||
CStack::stackEffectToFeature(swb.bonusesToAdd, pseudoBonus);
|
||||
//todo: handle effect actualization in HypotheticChangesToBattleState
|
||||
ps.spell->getEffects(swb.bonusesToAdd, skillLevel, false, hero->getEnchantPower(ps.spell));
|
||||
ps.spell->getEffects(swb.bonusesToAdd, skillLevel, true, hero->getEnchantPower(ps.spell));
|
||||
HypotheticChangesToBattleState state;
|
||||
state.bonusesOfStacks[swb.stack] = &swb;
|
||||
PotentialTargets pt(swb.stack, state);
|
||||
|
@ -3,6 +3,9 @@
|
||||
GENERAL:
|
||||
* Spectator mode was implemented through command-line options
|
||||
|
||||
SPELLS:
|
||||
* Implemented cumulative effects for spells
|
||||
|
||||
0.98 -> 0.99
|
||||
|
||||
GENERAL:
|
||||
|
@ -1359,7 +1359,7 @@ void CBattleInterface::spellCast(const BattleSpellCast *sc)
|
||||
|
||||
void CBattleInterface::battleStacksEffectsSet(const SetStackEffect & sse)
|
||||
{
|
||||
if (sse.effect.back().sid == -1 && sse.stacks.size() == 1 && sse.effect.size() == 2)
|
||||
if(sse.stacks.size() == 1 && sse.effect.size() == 2 && sse.effect.back().sid == -1)
|
||||
{
|
||||
const Bonus & bns = sse.effect.front();
|
||||
if(bns.source == Bonus::OTHER && bns.type == Bonus::PRIMARY_SKILL)
|
||||
@ -1379,11 +1379,9 @@ void CBattleInterface::battleStacksEffectsSet(const SetStackEffect & sse)
|
||||
}
|
||||
}
|
||||
|
||||
if (activeStack != nullptr) //it can be -1 when a creature casts effect
|
||||
{
|
||||
if(activeStack != nullptr)
|
||||
redrawBackgroundWithHexes(activeStack);
|
||||
}
|
||||
}
|
||||
|
||||
CBattleInterface::PossibleActions CBattleInterface::getCasterAction(const CSpell * spell, const ISpellCaster * caster, ECastingMode::ECastingMode mode) const
|
||||
{
|
||||
@ -1394,17 +1392,11 @@ CBattleInterface::PossibleActions CBattleInterface::getCasterAction(const CSpell
|
||||
if(ti.massive || ti.type == CSpell::NO_TARGET)
|
||||
spellSelMode = NO_LOCATION;
|
||||
else if(ti.type == CSpell::LOCATION && ti.clearAffected)
|
||||
{
|
||||
spellSelMode = FREE_LOCATION;
|
||||
}
|
||||
else if(ti.type == CSpell::CREATURE)
|
||||
{
|
||||
spellSelMode = AIMED_SPELL_CREATURE;
|
||||
}
|
||||
else if(ti.type == CSpell::OBSTACLE)
|
||||
{
|
||||
spellSelMode = OBSTACLE;
|
||||
}
|
||||
|
||||
return spellSelMode;
|
||||
}
|
||||
|
@ -85,7 +85,14 @@
|
||||
},
|
||||
"effects":{
|
||||
"type": "object",
|
||||
"description": "Timed effects",
|
||||
"description": "Timed effects (updated by prolongation)",
|
||||
"additionalProperties" : {
|
||||
"$ref" : "vcmi:bonus"
|
||||
}
|
||||
},
|
||||
"cumulativeEffects":{
|
||||
"type": "object",
|
||||
"description": "Timed effects (updated by unique bonus)",
|
||||
"additionalProperties" : {
|
||||
"$ref" : "vcmi:bonus"
|
||||
}
|
||||
|
@ -101,6 +101,7 @@
|
||||
"levels" : {
|
||||
"base":{
|
||||
"range" : "0",
|
||||
//no cumulative effect even with mods here
|
||||
"effects" : {
|
||||
"bindEffect" : {
|
||||
"val" : 0,
|
||||
@ -335,7 +336,7 @@
|
||||
"base":{
|
||||
"range" : "0",
|
||||
"targetModifier":{"smart":true},
|
||||
"effects" : {
|
||||
"cumulativeEffects" : {
|
||||
"primarySkill" : {
|
||||
"val" : -3,
|
||||
"type" : "PRIMARY_SKILL",
|
||||
|
@ -13,6 +13,7 @@
|
||||
"base":{
|
||||
"range" : "0",
|
||||
"targetModifier":{"smart":true},
|
||||
//no cumulative effect even with mods here
|
||||
"effects" : {
|
||||
"generalDamageReduction" : {
|
||||
"type" : "GENERAL_DAMAGE_REDUCTION",
|
||||
@ -43,6 +44,7 @@
|
||||
"base":{
|
||||
"range" : "0",
|
||||
"targetModifier":{"smart":true},
|
||||
//no cumulative effect even with mods here
|
||||
"effects" : {
|
||||
"generalDamageReduction" : {
|
||||
"type" : "GENERAL_DAMAGE_REDUCTION",
|
||||
@ -558,7 +560,7 @@
|
||||
"base":{
|
||||
"range" : "0",
|
||||
"targetModifier":{"smart":true},
|
||||
"effects" : {
|
||||
"cumulativeEffects" : {
|
||||
"primarySkill" : {
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"subtype" : "primSkill.defence",
|
||||
@ -569,14 +571,14 @@
|
||||
}
|
||||
},
|
||||
"advanced":{
|
||||
"effects" : {
|
||||
"cumulativeEffects" : {
|
||||
"primarySkill" : {
|
||||
"val" : -4
|
||||
}
|
||||
}
|
||||
},
|
||||
"expert":{
|
||||
"effects" : {
|
||||
"cumulativeEffects" : {
|
||||
"primarySkill" : {
|
||||
"val" : -5
|
||||
}
|
||||
|
@ -104,21 +104,6 @@ si32 CStack::magicResistance() const
|
||||
return magicResistance;
|
||||
}
|
||||
|
||||
void CStack::stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse)
|
||||
{
|
||||
const CSpell * sp = SpellID(sse.sid).toSpell();
|
||||
|
||||
std::vector<Bonus> tmp;
|
||||
sp->getEffects(tmp, sse.val);
|
||||
|
||||
for(Bonus& b : tmp)
|
||||
{
|
||||
if(b.turnsRemain == 0)
|
||||
b.turnsRemain = sse.turnsRemain;
|
||||
sf.push_back(b);
|
||||
}
|
||||
}
|
||||
|
||||
bool CStack::willMove(int turn /*= 0*/) const
|
||||
{
|
||||
return ( turn ? true : !vstd::contains(state, EBattleStackState::DEFENDING) )
|
||||
|
@ -62,7 +62,6 @@ public:
|
||||
ui32 calculateHealedHealthPoints(ui32 toHeal, const bool resurrect) const;
|
||||
ui32 level() const;
|
||||
si32 magicResistance() const override; //include aura of resistance
|
||||
static void stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse);
|
||||
std::vector<si32> activeSpells() const; //returns vector of active spell IDs sorted by time of cast
|
||||
const CGHeroInstance *getMyHero() const; //if stack belongs to hero (directly or was by him summoned) returns hero, nullptr otherwise
|
||||
ui32 totalHealth() const; // total health for all creatures in stack;
|
||||
|
@ -1519,11 +1519,19 @@ struct SetStackEffect : public CPackForClient
|
||||
void applyCl(CClient *cl);
|
||||
|
||||
std::vector<ui32> stacks; //affected stacks (IDs)
|
||||
|
||||
//regular effects
|
||||
std::vector<Bonus> effect; //bonuses to apply
|
||||
std::vector<std::pair<ui32, Bonus> > uniqueBonuses; //bonuses per single stack
|
||||
|
||||
//cumulative effects
|
||||
std::vector<Bonus> cumulativeEffects; //bonuses to apply
|
||||
std::vector<std::pair<ui32, Bonus> > cumulativeUniqueBonuses; //bonuses per single stack
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & stacks & effect & uniqueBonuses;
|
||||
h & cumulativeEffects & cumulativeUniqueBonuses;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1554,33 +1554,39 @@ void actualizeEffect(CStack * s, const std::vector<Bonus> & ef)
|
||||
|
||||
DLL_LINKAGE void SetStackEffect::applyGs(CGameState *gs)
|
||||
{
|
||||
if(effect.empty())
|
||||
if(effect.empty() && cumulativeEffects.empty())
|
||||
{
|
||||
logGlobal->errorStream() << "Trying to apply SetStackEffect with no effects";
|
||||
return;
|
||||
}
|
||||
|
||||
int spellid = effect.begin()->sid; //effects' source ID
|
||||
si32 spellid = effect.empty() ? cumulativeEffects.begin()->sid : effect.begin()->sid; //effects' source ID
|
||||
|
||||
auto processEffect = [spellid, this](CStack * sta, const Bonus & effect)
|
||||
auto processEffect = [spellid, this](CStack * sta, const Bonus & effect, bool cumulative)
|
||||
{
|
||||
if(!sta->hasBonus(Selector::source(Bonus::SPELL_EFFECT, spellid).And(Selector::typeSubtype(effect.type, effect.subtype)))
|
||||
|| spellid == SpellID::DISRUPTING_RAY || spellid == SpellID::ACID_BREATH_DEFENSE)
|
||||
if(cumulative || !sta->hasBonus(Selector::source(Bonus::SPELL_EFFECT, spellid).And(Selector::typeSubtype(effect.type, effect.subtype))))
|
||||
{
|
||||
//no such effect or cumulative - add new
|
||||
logBonus->traceStream() << sta->nodeName() << " receives a new bonus: " << effect.Description();
|
||||
sta->addNewBonus(std::make_shared<Bonus>(effect));
|
||||
}
|
||||
else
|
||||
{
|
||||
logBonus->traceStream() << sta->nodeName() << " updated bonus: " << effect.Description();
|
||||
actualizeEffect(sta, effect);
|
||||
}
|
||||
};
|
||||
|
||||
for(ui32 id : stacks)
|
||||
{
|
||||
CStack *s = gs->curB->getStack(id);
|
||||
if(s)
|
||||
{
|
||||
for(const Bonus & fromEffect : effect)
|
||||
processEffect(s, fromEffect);
|
||||
processEffect(s, fromEffect, false);
|
||||
for(const Bonus & fromEffect : cumulativeEffects)
|
||||
processEffect(s, fromEffect, true);
|
||||
}
|
||||
else
|
||||
logNetwork->errorStream() << "Cannot find stack " << id;
|
||||
}
|
||||
@ -1589,7 +1595,16 @@ DLL_LINKAGE void SetStackEffect::applyGs(CGameState *gs)
|
||||
{
|
||||
CStack *s = gs->curB->getStack(para.first);
|
||||
if(s)
|
||||
processEffect(s, para.second);
|
||||
processEffect(s, para.second, false);
|
||||
else
|
||||
logNetwork->errorStream() << "Cannot find stack " << para.first;
|
||||
}
|
||||
|
||||
for(auto & para : cumulativeUniqueBonuses)
|
||||
{
|
||||
CStack *s = gs->curB->getStack(para.first);
|
||||
if(s)
|
||||
processEffect(s, para.second, true);
|
||||
else
|
||||
logNetwork->errorStream() << "Cannot find stack " << para.first;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "../ConstTransitivePtr.h"
|
||||
#include "../GameConstants.h"
|
||||
|
||||
const ui32 SERIALIZATION_VERSION = 771;
|
||||
const ui32 SERIALIZATION_VERSION = 773;
|
||||
const ui32 MINIMAL_SERIALIZATION_VERSION = 753;
|
||||
const std::string SAVEGAME_MAGIC = "VCMISVG";
|
||||
|
||||
|
@ -79,11 +79,12 @@ ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(const SpellCastE
|
||||
{
|
||||
if(owner->hasEffects())
|
||||
{
|
||||
//todo: cumulative effects support
|
||||
const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner);
|
||||
|
||||
std::vector<Bonus> bonuses;
|
||||
|
||||
owner->getEffects(bonuses, schoolLevel);
|
||||
owner->getEffects(bonuses, schoolLevel, false, parameters.caster->getEnchantPower(owner));
|
||||
|
||||
for(Bonus b : bonuses)
|
||||
{
|
||||
|
@ -445,8 +445,14 @@ void DefaultSpellMechanics::battleLogDefault(std::vector<MetaString> & logLines,
|
||||
|
||||
void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
//applying effects
|
||||
if(owner->isOffensiveSpell())
|
||||
defaultDamageEffect(env, parameters, ctx);
|
||||
|
||||
if(owner->hasEffects())
|
||||
defaultTimedEffect(env, parameters, ctx);
|
||||
}
|
||||
|
||||
void DefaultSpellMechanics::defaultDamageEffect(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
const int rawDamage = parameters.getEffectValue();
|
||||
int chainLightningModifier = 0;
|
||||
@ -469,45 +475,39 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
|
||||
}
|
||||
}
|
||||
|
||||
if(owner->hasEffects())
|
||||
void DefaultSpellMechanics::defaultTimedEffect(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
SetStackEffect sse;
|
||||
//get default spell duration (spell power with bonuses for heroes)
|
||||
int duration = parameters.enchantPower;
|
||||
//generate actual stack bonuses
|
||||
{
|
||||
int maxDuration = 0;
|
||||
std::vector<Bonus> tmp;
|
||||
owner->getEffects(tmp, parameters.effectLevel);
|
||||
for(Bonus& b : tmp)
|
||||
{
|
||||
//use configured duration if present
|
||||
if(b.turnsRemain == 0)
|
||||
b.turnsRemain = duration;
|
||||
vstd::amax(maxDuration, b.turnsRemain);
|
||||
sse.effect.push_back(b);
|
||||
}
|
||||
//if all spell effects have special duration, use it
|
||||
si32 maxDuration = 0;
|
||||
|
||||
owner->getEffects(sse.effect, parameters.effectLevel, false, duration, &maxDuration);
|
||||
owner->getEffects(sse.cumulativeEffects, parameters.effectLevel, true, duration, &maxDuration);
|
||||
|
||||
//if all spell effects have special duration, use it later for special bonuses
|
||||
duration = maxDuration;
|
||||
}
|
||||
//fix to original config: shield should display damage reduction
|
||||
if(owner->id == SpellID::SHIELD || owner->id == SpellID::AIR_SHIELD)
|
||||
{
|
||||
sse.effect.back().val = (100 - sse.effect.back().val);
|
||||
sse.effect.at(sse.effect.size() - 1).val = (100 - sse.effect.back().val);
|
||||
}
|
||||
//we need to know who cast Bind
|
||||
if(owner->id == SpellID::BIND && parameters.casterStack)
|
||||
else if(owner->id == SpellID::BIND && parameters.casterStack)
|
||||
{
|
||||
sse.effect.back().additionalInfo = parameters.casterStack->ID;
|
||||
sse.effect.at(sse.effect.size() - 1).additionalInfo = parameters.casterStack->ID;
|
||||
}
|
||||
std::shared_ptr<Bonus> bonus = nullptr;
|
||||
if(parameters.casterHero)
|
||||
bonus = parameters.casterHero->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPECIAL_PECULIAR_ENCHANT, owner->id));
|
||||
//TODO does hero specialty should affects his stack casting spells?
|
||||
|
||||
si32 power = 0;
|
||||
for(const CStack * affected : ctx.attackedCres)
|
||||
{
|
||||
si32 power = 0;
|
||||
sse.stacks.push_back(affected->ID);
|
||||
|
||||
//Apply hero specials - peculiar enchants
|
||||
@ -520,20 +520,34 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
|
||||
{
|
||||
switch(tier)
|
||||
{
|
||||
case 1: case 2:
|
||||
case 1:
|
||||
case 2:
|
||||
power = 3;
|
||||
break;
|
||||
case 3: case 4:
|
||||
case 3:
|
||||
case 4:
|
||||
power = 2;
|
||||
break;
|
||||
case 5: case 6:
|
||||
case 5:
|
||||
case 6:
|
||||
power = 1;
|
||||
break;
|
||||
}
|
||||
Bonus specialBonus(sse.effect.back());
|
||||
for(const Bonus & b : sse.effect)
|
||||
{
|
||||
Bonus specialBonus(b);
|
||||
specialBonus.val = power; //it doesn't necessarily make sense for some spells, use it wisely
|
||||
specialBonus.turnsRemain = duration;
|
||||
sse.uniqueBonuses.push_back(std::pair<ui32, Bonus>(affected->ID, specialBonus)); //additional premy to given effect
|
||||
}
|
||||
for(const Bonus & b : sse.cumulativeEffects)
|
||||
{
|
||||
Bonus specialBonus(b);
|
||||
specialBonus.val = power; //it doesn't necessarily make sense for some spells, use it wisely
|
||||
specialBonus.turnsRemain = duration;
|
||||
sse.cumulativeUniqueBonuses.push_back(std::pair<ui32, Bonus>(affected->ID, specialBonus)); //additional premy to given effect
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1: //only Coronius as yet
|
||||
{
|
||||
@ -557,7 +571,6 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
|
||||
if(!sse.stacks.empty())
|
||||
env->sendAndApply(&sse);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<BattleHex> DefaultSpellMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
|
||||
{
|
||||
|
@ -75,6 +75,9 @@ protected:
|
||||
protected:
|
||||
void doDispell(BattleInfo * battle, const BattleSpellCast * packet, const CSelector & selector) const;
|
||||
bool canDispell(const IBonusBearer * obj, const CSelector & selector, const std::string &cachingStr = "") const;
|
||||
|
||||
void defaultDamageEffect(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const;
|
||||
void defaultTimedEffect(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const;
|
||||
private:
|
||||
void cast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, std::vector <const CStack*> & reflected) const;
|
||||
|
||||
|
@ -350,7 +350,7 @@ bool CSpell::isSpecialSpell() const
|
||||
|
||||
bool CSpell::hasEffects() const
|
||||
{
|
||||
return !levels[0].effects.empty();
|
||||
return !levels[0].effects.empty() || !levels[0].cumulativeEffects.empty();
|
||||
}
|
||||
|
||||
const std::string & CSpell::getIconImmune() const
|
||||
@ -382,7 +382,7 @@ si32 CSpell::getProbability(const TFaction factionId) const
|
||||
return probabilities.at(factionId);
|
||||
}
|
||||
|
||||
void CSpell::getEffects(std::vector<Bonus> & lst, const int level) const
|
||||
void CSpell::getEffects(std::vector<Bonus> & lst, const int level, const bool cumulative, const si32 duration, boost::optional<si32 *> maxDuration/* = boost::none*/) const
|
||||
{
|
||||
if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS)
|
||||
{
|
||||
@ -390,19 +390,29 @@ void CSpell::getEffects(std::vector<Bonus> & lst, const int level) const
|
||||
return;
|
||||
}
|
||||
|
||||
const std::vector<Bonus> & effects = levels[level].effects;
|
||||
const auto & levelObject = levels.at(level);
|
||||
|
||||
if(effects.empty())
|
||||
if(levelObject.effects.empty() && levelObject.cumulativeEffects.empty())
|
||||
{
|
||||
logGlobal->errorStream() << __FUNCTION__ << " This spell (" + name + ") has no effects for level " << level;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto & effects = cumulative ? levelObject.cumulativeEffects : levelObject.effects;
|
||||
|
||||
lst.reserve(lst.size() + effects.size());
|
||||
|
||||
for(const Bonus & b : effects)
|
||||
for(const auto b : effects)
|
||||
{
|
||||
lst.push_back(Bonus(b));
|
||||
Bonus nb(*b);
|
||||
|
||||
//use configured duration if present
|
||||
if(nb.turnsRemain == 0)
|
||||
nb.turnsRemain = duration;
|
||||
if(maxDuration)
|
||||
vstd::amax(*(maxDuration.get()), nb.turnsRemain);
|
||||
|
||||
lst.push_back(nb);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1029,9 +1039,25 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string &
|
||||
if(usePowerAsValue)
|
||||
b->val = levelPower;
|
||||
|
||||
levelObject.effectsTmp.push_back(b);
|
||||
levelObject.effects.push_back(b);
|
||||
}
|
||||
|
||||
for(const auto & elem : levelNode["cumulativeEffects"].Struct())
|
||||
{
|
||||
const JsonNode & bonusNode = elem.second;
|
||||
auto b = JsonUtils::parseBonus(bonusNode);
|
||||
const bool usePowerAsValue = bonusNode["val"].isNull();
|
||||
|
||||
//TODO: make this work. see CSpellHandler::afterLoadFinalization()
|
||||
//b->sid = spell->id; //for all
|
||||
|
||||
b->source = Bonus::SPELL_EFFECT;//for all
|
||||
|
||||
if(usePowerAsValue)
|
||||
b->val = levelPower;
|
||||
|
||||
levelObject.cumulativeEffects.push_back(b);
|
||||
}
|
||||
}
|
||||
|
||||
return spell;
|
||||
@ -1044,14 +1070,10 @@ void CSpellHandler::afterLoadFinalization()
|
||||
{
|
||||
for(auto & level: spell->levels)
|
||||
{
|
||||
for(auto bonus : level.effectsTmp)
|
||||
{
|
||||
level.effects.push_back(*bonus);
|
||||
}
|
||||
level.effectsTmp.clear();
|
||||
|
||||
for(auto & bonus: level.effects)
|
||||
bonus.sid = spell->id;
|
||||
bonus->sid = spell->id;
|
||||
for(auto & bonus: level.cumulativeEffects)
|
||||
bonus->sid = spell->id;
|
||||
}
|
||||
spell->setup();
|
||||
}
|
||||
|
@ -130,16 +130,35 @@ public:
|
||||
bool clearAffected;
|
||||
std::string range;
|
||||
|
||||
std::vector<Bonus> effects;
|
||||
|
||||
std::vector<std::shared_ptr<Bonus>> effectsTmp; //TODO: this should replace effects
|
||||
std::vector<std::shared_ptr<Bonus>> effects;
|
||||
std::vector<std::shared_ptr<Bonus>> cumulativeEffects;
|
||||
|
||||
LevelInfo();
|
||||
~LevelInfo();
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & description & cost & power & AIValue & smartTarget & range & effects;
|
||||
h & description & cost & power & AIValue & smartTarget & range;
|
||||
|
||||
if(version >= 773)
|
||||
{
|
||||
h & effects & cumulativeEffects;
|
||||
}
|
||||
else
|
||||
{
|
||||
//all old effects treated as not cumulative, special cases handled by CSpell::serialize
|
||||
std::vector<Bonus> old;
|
||||
h & old;
|
||||
|
||||
if(!h.saving)
|
||||
{
|
||||
effects.clear();
|
||||
cumulativeEffects.clear();
|
||||
for(const Bonus & oldBonus : old)
|
||||
effects.push_back(std::make_shared<Bonus>(oldBonus));
|
||||
}
|
||||
}
|
||||
|
||||
h & clearTarget & clearAffected;
|
||||
}
|
||||
};
|
||||
@ -180,7 +199,7 @@ public:
|
||||
|
||||
si32 level;
|
||||
|
||||
std::map<ESpellSchool, bool> school; //todo: use this instead of separate boolean fields
|
||||
std::map<ESpellSchool, bool> school;
|
||||
|
||||
si32 power; //spell's power
|
||||
|
||||
@ -215,8 +234,7 @@ public:
|
||||
bool isSpecialSpell() const;
|
||||
|
||||
bool hasEffects() const;
|
||||
void getEffects(std::vector<Bonus> &lst, const int level) const;
|
||||
|
||||
void getEffects(std::vector<Bonus> & lst, const int level, const bool cumulative, const si32 duration, boost::optional<si32 *> maxDuration = boost::none) const;
|
||||
|
||||
///calculate spell damage on stack taking caster`s secondary skills and affectedCreature`s bonuses into account
|
||||
ui32 calculateDamage(const ISpellCaster * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const;
|
||||
@ -264,6 +282,12 @@ public:
|
||||
|
||||
if(!h.saving)
|
||||
setup();
|
||||
//backward compatibility
|
||||
//can not be added to level structure as level structure does not know spell id
|
||||
if(!h.saving && version < 773)
|
||||
if(id == SpellID::DISRUPTING_RAY || id == SpellID::ACID_BREATH_DEFENSE)
|
||||
for(auto & level : levels)
|
||||
std::swap(level.effects, level.cumulativeEffects);
|
||||
}
|
||||
friend class CSpellHandler;
|
||||
friend class Graphics;
|
||||
|
@ -4489,7 +4489,7 @@ bool CGameHandler::makeCustomAction(BattleAction &ba)
|
||||
}
|
||||
|
||||
|
||||
void CGameHandler::stackAppearTrigger(const CStack *st)
|
||||
void CGameHandler::stackEnchantedTrigger(const CStack * st)
|
||||
{
|
||||
auto bl = *(st->getBonuses(Selector::type(Bonus::ENCHANTED)));
|
||||
for(auto b : bl)
|
||||
@ -4507,12 +4507,14 @@ void CGameHandler::stackAppearTrigger(const CStack *st)
|
||||
else
|
||||
sse.stacks.push_back (st->ID);
|
||||
|
||||
Bonus pseudoBonus;
|
||||
pseudoBonus.sid = b->subtype;
|
||||
pseudoBonus.val = ((val > 3) ? (val - 3) : val);
|
||||
pseudoBonus.turnsRemain = 50;
|
||||
st->stackEffectToFeature(sse.effect, pseudoBonus);
|
||||
if (sse.effect.size())
|
||||
const CSpell * sp = SpellID(b->subtype).toSpell();
|
||||
const int level = ((val > 3) ? (val - 3) : val);
|
||||
|
||||
sp->getEffects(sse.effect, level, false, 50);
|
||||
//this makes effect accumulate for at most 50 turns by default, but effect may be permanent and last till the end of battle
|
||||
sp->getEffects(sse.cumulativeEffects, level, true, 50);
|
||||
|
||||
if(!sse.effect.empty() || !sse.cumulativeEffects.empty())
|
||||
sendAndApply(&sse);
|
||||
}
|
||||
}
|
||||
@ -4526,7 +4528,6 @@ void CGameHandler::stackTurnTrigger(const CStack *st)
|
||||
bte.additionalInfo = 0;
|
||||
if (st->alive())
|
||||
{
|
||||
stackAppearTrigger(st);
|
||||
//unbind
|
||||
if (st->hasBonus(Selector::type(Bonus::BIND_EFFECT)))
|
||||
{
|
||||
@ -5724,7 +5725,7 @@ void CGameHandler::runBattle()
|
||||
}
|
||||
}
|
||||
|
||||
stackAppearTrigger(stack);
|
||||
stackEnchantedTrigger(stack);
|
||||
}
|
||||
|
||||
//spells opening battle
|
||||
@ -5766,6 +5767,12 @@ void CGameHandler::runBattle()
|
||||
|
||||
const BattleInfo & curB = *gs->curB;
|
||||
|
||||
for(auto stack : curB.stacks)
|
||||
{
|
||||
if(stack->alive() && curB.round > 1)
|
||||
stackEnchantedTrigger(stack);
|
||||
}
|
||||
|
||||
//stack loop
|
||||
|
||||
const CStack *next;
|
||||
|
@ -196,7 +196,7 @@ public:
|
||||
bool makeBattleAction(BattleAction &ba);
|
||||
bool makeAutomaticAction(const CStack *stack, BattleAction &ba); //used when action is taken by stack without volition of player (eg. unguided catapult attack)
|
||||
bool makeCustomAction(BattleAction &ba);
|
||||
void stackAppearTrigger(const CStack *stack);
|
||||
void stackEnchantedTrigger(const CStack * stack);
|
||||
void stackTurnTrigger(const CStack *stack);
|
||||
void handleDamageFromObstacle(const CObstacleInstance &obstacle, const CStack * curStack); //checks if obstacle is land mine and handles possible consequences
|
||||
void removeObstacle(const CObstacleInstance &obstacle);
|
||||
|
Loading…
Reference in New Issue
Block a user