From bfe266a37757df0b30ce2a6aadab916b6bc02cca Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Mon, 30 Aug 2010 18:06:17 +0000 Subject: [PATCH] Moved battle effects to bonus system. May be buggy. Moved creature damage to bonus system, simplified its handling. Some changes and tweaks. Support for Adela's bless. --- client/CBattleInterface.cpp | 7 +- client/GUIClasses.cpp | 13 ++- client/NetPacksClient.cpp | 2 +- config/specials.txt | 6 +- hch/CCreatureHandler.cpp | 14 +-- hch/CCreatureHandler.h | 2 - hch/CObjectHandler.cpp | 1 + lib/CCreatureSet.cpp | 9 -- lib/CCreatureSet.h | 2 - lib/CGameState.cpp | 169 ++++++++++++++++++++++++++--- lib/CGameState.h | 31 +++--- lib/HeroBonus.cpp | 21 ++++ lib/HeroBonus.h | 19 +++- lib/NetPacks.h | 2 +- lib/NetPacksLib.cpp | 207 +++++------------------------------- server/CGameHandler.cpp | 2 +- 16 files changed, 259 insertions(+), 248 deletions(-) diff --git a/client/CBattleInterface.cpp b/client/CBattleInterface.cpp index ac10f1bc5..81eb28913 100644 --- a/client/CBattleInterface.cpp +++ b/client/CBattleInterface.cpp @@ -3010,16 +3010,17 @@ void CBattleInterface::showAliveStack(int ID, const std::map & stac //blitting amoutn background box SDL_Surface *amountBG = NULL; - if(curStack.effects.size() == 0) + if(!curStack.bonuses.size()) { amountBG = amountNormal; } else { int pos=0; //determining total positiveness of effects - for(int c=0; cspellh->spells[ curStack.effects[c].id ].positiveness; + if (it->source == Bonus::CASTED_SPELL) + pos += CGI->spellh->spells[ it->id ].positiveness; } if(pos > 0) { diff --git a/client/GUIClasses.cpp b/client/GUIClasses.cpp index ca707bb53..18783ad59 100644 --- a/client/GUIClasses.cpp +++ b/client/GUIClasses.cpp @@ -2148,12 +2148,15 @@ CCreInfoWindow::CCreInfoWindow(const CStackInstance &st, int Type, boost::functi { //spell effects int printed=0; //how many effect pics have been printed - BOOST_FOREACH(const CStack::StackEffect &effect, battleStack->effects) + BOOST_FOREACH(const Bonus effect, battleStack->bonuses) { - blitAt(graphics->spellEffectsPics->ourImages[effect.id + 1].bitmap, 127 + 52 * printed, 186, *bitmap); - ++printed; - if(printed >= 3) //we can fit only 3 effects - break; + if (effect.source == Bonus::CASTED_SPELL) + { + blitAt(graphics->spellEffectsPics->ourImages[effect.id + 1].bitmap, 127 + 52 * printed, 186, *bitmap); + ++printed; + if(printed >= 3) //we can fit only 3 effects + break; + } } //print current health diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index d87ef8882..d33e6cc89 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -574,7 +574,7 @@ void SetStackEffect::applyCl( CClient *cl ) BattleSpellCast sc; sc.id = effect.id; sc.side = 3; //doesn't matter - sc.skill = effect.level; + sc.skill = effect.val; //informing about effects if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end()) diff --git a/config/specials.txt b/config/specials.txt index 7ca11eaac..f2dcd224e 100644 --- a/config/specials.txt +++ b/config/specials.txt @@ -8,7 +8,7 @@ 6 1 0 0 146 7 1 0 0 10 8 2 5 27 0 -9 6 41 0 3 +9 6 3 41 0 10 8 45 0 0 11 3 3 20 0 12 1 0 0 8 @@ -28,7 +28,7 @@ 26 2 5 24 0 27 2 5 27 0 28 2 5 11 0 -29 7 51 0 0 +29 7 0 51 0 30 3 3 16 0 31 1 0 0 20 32 1 0 0 30 @@ -42,7 +42,7 @@ 40 3 3 60 0 41 2 5 8 1 42 2 5 11 0 -43 7 51 0 0 +43 7 0 51 0 44 1 0 0 34 45 3 3 19 0 46 8 53 0 0 diff --git a/hch/CCreatureHandler.cpp b/hch/CCreatureHandler.cpp index b3e2ff375..9651e0022 100644 --- a/hch/CCreatureHandler.cpp +++ b/hch/CCreatureHandler.cpp @@ -124,16 +124,6 @@ CCreature::CCreature() doubleWide = false; nodeType = CBonusSystemNode::CREATURE; } - -ui32 CCreature::getMinDamage() const -{ - return damageMin + valOfBonuses(Bonus::CREATURE_DAMAGE, 0) + valOfBonuses(Bonus::CREATURE_DAMAGE, 1); -} -ui32 CCreature::getMaxDamage() const -{ - return damageMax + valOfBonuses(Bonus::CREATURE_DAMAGE, 0) + valOfBonuses(Bonus::CREATURE_DAMAGE, 2); -} - void CCreature::addBonus(int val, int type, int subtype /*= -1*/) { Bonus added(Bonus::PERMANENT, type, Bonus::CREATURE_ABILITY, val, idNumber, subtype, Bonus::BASE_NUMBER); @@ -272,8 +262,10 @@ void CCreatureHandler::loadCreatures() ncre.addBonus(ncre.attack, Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK); ncre.defence = readNumber(befi, i, andame, buf); ncre.addBonus(ncre.defence, Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE); - ncre.damageMin = readNumber(befi, i, andame, buf); + ncre.damageMin = readNumber(befi, i, andame, buf); //not used anymore? + ncre.addBonus(ncre.damageMin, Bonus::CREATURE_DAMAGE, 1); ncre.damageMax = readNumber(befi, i, andame, buf); + ncre.addBonus(ncre.damageMax, Bonus::CREATURE_DAMAGE, 2); ncre.shots = readNumber(befi, i, andame, buf); ncre.spells = readNumber(befi, i, andame, buf); diff --git a/hch/CCreatureHandler.h b/hch/CCreatureHandler.h index bafe79d3f..49d7d6fe7 100644 --- a/hch/CCreatureHandler.h +++ b/hch/CCreatureHandler.h @@ -60,8 +60,6 @@ public: si32 maxAmount(const std::vector &res) const; //how many creatures can be bought static int getQuantityID(const int & quantity); //0 - a few, 1 - several, 2 - pack, 3 - lots, 4 - horde, 5 - throng, 6 - swarm, 7 - zounds, 8 - legion bool isMyUpgrade(const CCreature *anotherCre) const; - ui32 getMinDamage() const; - ui32 getMaxDamage() const; void addBonus(int val, int type, int subtype = -1); void getParents(TCNodes &out, const CBonusSystemNode *root /*= NULL*/) const; diff --git a/hch/CObjectHandler.cpp b/hch/CObjectHandler.cpp index ae645f956..a7d2a00cf 100644 --- a/hch/CObjectHandler.cpp +++ b/hch/CObjectHandler.cpp @@ -1091,6 +1091,7 @@ void CGHeroInstance::initObj() break; case 6://damage bonus for bless (Adela) bonus.type = Bonus::SPECIAL_BLESS_DAMAGE; + bonus.subtype = it->subtype; //spell id if you ever wanted to use it otherwise bonus.additionalInfo = it->additionalinfo; //damage factor speciality.bonuses.push_back (bonus); break; diff --git a/lib/CCreatureSet.cpp b/lib/CCreatureSet.cpp index b866096c2..c586ba84e 100644 --- a/lib/CCreatureSet.cpp +++ b/lib/CCreatureSet.cpp @@ -278,15 +278,6 @@ void CStackInstance::getParents(TCNodes &out, const CBonusSystemNode *source /*= out.insert(&IObjectInterface::cb->gameState()->globalEffects); } -ui32 CStackInstance::getMinDamage() const -{ - return type->damageMin + valOfBonuses(Bonus::CREATURE_DAMAGE, 0) + valOfBonuses(Bonus::CREATURE_DAMAGE, 1); -} -ui32 CStackInstance::getMaxDamage() const -{ - return type->damageMax + valOfBonuses(Bonus::CREATURE_DAMAGE, 0) + valOfBonuses(Bonus::CREATURE_DAMAGE, 2); -} - std::string CStackInstance::getQuantityTXT(bool capitalized /*= true*/) const { return VLC->generaltexth->arraytxt[174 + getQuantityID()*3 + 2 - capitalized]; diff --git a/lib/CCreatureSet.h b/lib/CCreatureSet.h index d0485b299..be3ce8dd7 100644 --- a/lib/CCreatureSet.h +++ b/lib/CCreatureSet.h @@ -25,8 +25,6 @@ public: const CArmedInstance *armyObj; //stack must be part of some army, army must be part of some object const CCreature *type; TQuantity count; - ui32 getMinDamage() const; - ui32 getMaxDamage() const; ui32 experience; //TODO: handle //TODO: stack artifacts diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 38dec6030..fbcd0f968 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -742,20 +742,161 @@ ui32 CStack::Speed( int turn /*= 0*/ ) const return speed; } -const CStack::StackEffect * CStack::getEffect( ui16 id, int turn /*= 0*/ ) const +const Bonus * CStack::getEffect( ui16 id, int turn /*= 0*/ ) const { - for (unsigned int i=0; i< effects.size(); i++) - if(effects[i].id == id) - if(!turn || effects[i].turnsRemain > turn) - return &effects[i]; + for (BonusList::const_iterator it = bonuses.begin(); it != bonuses.end(); it++) + { + if(it->id == id) + { + if(!turn || it->turnsRemain > turn) + return &(*it); + } + } return NULL; } +void CStack::stackEffectToFeature(BonusList & sf, const Bonus & sse) +{ + si32 power = VLC->spellh->spells[sse.id].powers[sse.val]; + switch(sse.id) + { + case 27: //shield + sf.push_back(featureGenerator(Bonus::GENERAL_DAMAGE_REDUCTION, 0, power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 28: //air shield + sf.push_back(featureGenerator(Bonus::GENERAL_DAMAGE_REDUCTION, 1, power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 29: //fire shield + sf.push_back(featureGenerator(Bonus::FIRE_SHIELD, 0, power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 30: //protection from air + sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 0, power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 31: //protection from fire + sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 1, power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 32: //protection from water + sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 2, power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 33: //protection from earth + sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 3, power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 34: //anti-magic + sf.push_back(featureGenerator(Bonus::LEVEL_SPELL_IMMUNITY, 0, power - 1, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 41: //bless + if (hasBonusOfType(Bonus::SPECIAL_BLESS_DAMAGE, 41)) //TODO: better handling of bonus percentages + { + int damagePercent = dynamic_cast(armyObj)->level * valOfBonuses(Bonus::SPECIAL_BLESS_DAMAGE, 41) / type->level; + sf.push_back(featureGenerator(Bonus::CREATURE_DAMAGE, 0, damagePercent, sse.turnsRemain)); + sf.back().id = sse.id; + sf.back().valType = Bonus::PERCENT_TO_ALL; + } + sf.push_back(featureGenerator(Bonus::ALWAYS_MAXIMUM_DAMAGE, -1, power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 42: //curse + sf.push_back(featureGenerator(Bonus::ALWAYS_MINIMUM_DAMAGE, -1, -1 * power, sse.turnsRemain, sse.val >= 2 ? 20 : 0)); + sf.back().id = sse.id; + break; + case 43: //bloodlust + sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, sse.turnsRemain, 0, Bonus::ONLY_MELEE_FIGHT)); + sf.back().id = sse.id; + break; + case 44: //precision + sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, sse.turnsRemain, 0, Bonus::ONLY_DISTANCE_FIGHT)); + sf.back().id = sse.id; + break; + case 45: //weakness + sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, -1 * power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 46: //stone skin + sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 47: //disrupting ray + sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, -1 * power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 48: //prayer + sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, sse.turnsRemain)); + sf.back().id = sse.id; + sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, power, sse.turnsRemain)); + sf.back().id = sse.id; + sf.push_back(featureGenerator(Bonus::STACKS_SPEED, 0, power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 49: //mirth + sf.push_back(featureGenerator(Bonus::MORALE, 0, power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 50: //sorrow + sf.push_back(featureGenerator(Bonus::MORALE, 0, -1 * power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 51: //fortune + sf.push_back(featureGenerator(Bonus::LUCK, 0, power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 52: //misfortune + sf.push_back(featureGenerator(Bonus::LUCK, 0, -1 * power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 53: //haste + sf.push_back(featureGenerator(Bonus::STACKS_SPEED, 0, power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 54: //slow + sf.push_back(featureGeneratorVT(Bonus::STACKS_SPEED, 0, -1 * ( 100 - power ), sse.turnsRemain, Bonus::PERCENT_TO_ALL)); + sf.back().id = sse.id; + break; + case 55: //slayer + sf.push_back(featureGenerator(Bonus::SLAYER, 0, sse.val, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 56: //frenzy + sf.push_back(featureGenerator(Bonus::IN_FRENZY, 0, VLC->spellh->spells[56].powers[sse.val]/100.0, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 58: //counterstrike + sf.push_back(featureGenerator(Bonus::ADDITIONAL_RETALIATION, 0, power, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 59: //bersek + sf.push_back(featureGenerator(Bonus::ATTACKS_NEAREST_CREATURE, 0, sse.val, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 60: //hypnotize + sf.push_back(featureGenerator(Bonus::HYPNOTIZED, 0, sse.val, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 61: //forgetfulness + sf.push_back(featureGenerator(Bonus::FORGETFULL, 0, sse.val, sse.turnsRemain)); + sf.back().id = sse.id; + break; + case 62: //blind + sf.push_back(makeFeature(Bonus::NOT_ACTIVE, Bonus::UNITL_BEING_ATTACKED | Bonus::N_TURNS, 0, 0, Bonus::SPELL_EFFECT, sse.turnsRemain)); + sf.back().id = sse.id; + sf.push_back(makeFeature(Bonus::GENERAL_ATTACK_REDUCTION, Bonus::UNTIL_ATTACK | Bonus::N_TURNS, 0, power, Bonus::SPELL_EFFECT, sse.turnsRemain)); + sf.back().id = sse.id; + break; + } +} + ui8 CStack::howManyEffectsSet(ui16 id) const { ui8 ret = 0; - for (unsigned int i=0; i< effects.size(); i++) - if(effects[i].id == id) //effect found + for (BonusList::const_iterator it = bonuses.begin(); it != bonuses.end(); it++) + if(it->id == id) //effect found { ++ret; } @@ -2941,7 +3082,7 @@ std::pair BattleInfo::calculateDmgRange( const CStack* attacker, con if(attacker->getEffect(55)) //slayer handling { std::vector affectedIds; - int spLevel = attacker->getEffect(55)->level; + int spLevel = attacker->getEffect(55)->val; for(int g = 0; g < VLC->creh->creatures.size(); ++g) { @@ -2961,7 +3102,7 @@ std::pair BattleInfo::calculateDmgRange( const CStack* attacker, con { if(defender->type->idNumber == affectedIds[g]) { - attackDefenceDifference += VLC->spellh->spells[55].powers[attacker->getEffect(55)->level]; + attackDefenceDifference += VLC->spellh->spells[55].powers[attacker->getEffect(55)->val]; break; } } @@ -3038,7 +3179,7 @@ std::pair BattleInfo::calculateDmgRange( const CStack* attacker, con } if(attacker->getEffect(42)) //curse handling (partial, the rest is below) { - multBonus *= 0.8f * float(VLC->spellh->spells[42].powers[attacker->getEffect(42)->level]); //the second factor is 1 or 0 + multBonus *= 0.8f * float(VLC->spellh->spells[42].powers[attacker->getEffect(42)->val]); //the second factor is 1 or 0 } class HLP @@ -3046,9 +3187,9 @@ std::pair BattleInfo::calculateDmgRange( const CStack* attacker, con public: static bool hasAdvancedAirShield(const CStack * stack) { - for(int g=0; geffects.size(); ++g) + for (BonusList::const_iterator it = stack->bonuses.begin(); it != stack->bonuses.end(); it++) { - if (stack->effects[g].id == 28 && stack->effects[g].level >= 2) + if (it->id == 28 && it->val >= 2) { return true; } @@ -3077,12 +3218,12 @@ std::pair BattleInfo::calculateDmgRange( const CStack* attacker, con if(attacker->getEffect(42)) //curse handling (rest) { - minDmg -= VLC->spellh->spells[42].powers[attacker->getEffect(42)->level]; + minDmg -= VLC->spellh->spells[42].powers[attacker->getEffect(42)->val]; returnedVal = std::make_pair(int(minDmg), int(minDmg)); } else if(attacker->getEffect(41)) //bless handling { - maxDmg += VLC->spellh->spells[41].powers[attacker->getEffect(41)->level]; + maxDmg += VLC->spellh->spells[41].powers[attacker->getEffect(41)->val]; returnedVal = std::make_pair(int(maxDmg), int(maxDmg)); } else diff --git a/lib/CGameState.h b/lib/CGameState.h index 072d55c85..a53456bd3 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -261,29 +261,34 @@ public: si16 shots; //how many shots left std::set state; - struct StackEffect - { - ui16 id; //spell id - ui8 level; //skill level - si16 turnsRemain; - template void serialize(Handler &h, const int version) - { - h & id & level & turnsRemain; - } - }; - std::vector effects; - //overrides const CCreature* getCreature() const {return type;} CStack(const CStackInstance *base, int O, int I, bool AO, int S); //c-tor CStack() : ID(-1), baseAmount(-1), firstHPleft(-1), owner(255), slot(255), attackerOwned(true), position(-1), counterAttacks(1) {} //c-tor - const StackEffect * getEffect(ui16 id, int turn = 0) const; //effect id (SP) + const Bonus * getEffect(ui16 id, int turn = 0) const; //effect id (SP) ui8 howManyEffectsSet(ui16 id) const; //returns amount of effects with given id set for this stack bool willMove(int turn = 0) const; //if stack has remaining move this turn bool moved(int turn = 0) const; //if stack was already moved this turn bool canMove(int turn = 0) const; //if stack can move ui32 Speed(int turn = 0) const; //get speed of creature with all modificators + void stackEffectToFeature(BonusList & sf, const Bonus & sse); + + static inline Bonus featureGenerator(Bonus::BonusType type, si16 subtype, si32 value, ui16 turnsRemain, si32 additionalInfo = 0, si32 limit = Bonus::NO_LIMIT) + { + Bonus hb(makeFeature(type, Bonus::N_TURNS, subtype, value, Bonus::SPELL_EFFECT, turnsRemain, additionalInfo)); + hb.effectRange = limit; + hb.source = Bonus::SPELL; //right? + return hb; + } + + static inline Bonus featureGeneratorVT(Bonus::BonusType type, si16 subtype, si32 value, ui16 turnsRemain, ui8 valType) + { + Bonus ret(makeFeature(type, Bonus::N_TURNS, subtype, value, Bonus::SPELL_EFFECT, turnsRemain)); + ret.valType = valType; + ret.source = Bonus::SPELL; //right? + return ret; + } bool doubleWide() const; int occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1 diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index c68b4ca0a..2223b2e08 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -98,6 +98,14 @@ void DLL_EXPORT BonusList::getBonuses(BonusList &out, const CSelector &selector, if(selector(*i) && (!limit || limit(*i))) out.push_back(*i); } +void DLL_EXPORT BonusList::removeSpells(Bonus::BonusSource sourceType) +{ + for(iterator i = begin(); i != end(); i++) + { + if (i->source == sourceType) + erase(i); + } +} void BonusList::limit(const CBonusSystemNode &node) { @@ -294,6 +302,15 @@ ui16 CBonusSystemNode::MaxHealth() const return valOfBonuses(Bonus::STACK_HEALTH); } +ui32 CBonusSystemNode::getMinDamage() const +{ + return valOfBonuses(Selector::typeSybtype(Bonus::CREATURE_DAMAGE, 0) || Selector::typeSybtype(Bonus::CREATURE_DAMAGE, 1)); +} +ui32 CBonusSystemNode::getMaxDamage() const +{ + return valOfBonuses(Selector::typeSybtype(Bonus::CREATURE_DAMAGE, 0) || Selector::typeSybtype(Bonus::CREATURE_DAMAGE, 2)); +} + CBonusSystemNode::CBonusSystemNode() { nodeType = UNKNOWN; @@ -395,6 +412,10 @@ CSelector DLL_EXPORT operator&&(const CSelector &first, const CSelector &second) { return CSelectorsConjunction(first, second); } +CSelector DLL_EXPORT operator||(const CSelector &first, const CSelector &second) +{ + return CSelectorsAlternative(first, second); +} namespace Selector { diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index fabb431d6..1dd52c5c5 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -302,6 +302,7 @@ public: void DLL_EXPORT getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *source = NULL) const; void DLL_EXPORT getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *source = NULL) const; void DLL_EXPORT getModifiersWDescr(TModDescr &out) const; + void DLL_EXPORT removeSpells(Bonus::BonusSource sourceType); //special find functions DLL_EXPORT Bonus * getFirst(const CSelector &select); @@ -359,8 +360,8 @@ public: bool hasBonusFrom(ui8 source, ui32 sourceID) const; void getModifiersWDescr( TModDescr &out, Bonus::BonusType type, int subtype = -1 ) const; //out: pairs int getBonusesCount(int from, int id) const; - virtual ui32 getMinDamage() const {return 0;}; //used for stacks and creatures only - virtual ui32 getMaxDamage() const {return 0;}; + virtual ui32 getMinDamage() const; //used for stacks and creatures only + virtual ui32 getMaxDamage() const; int MoraleVal() const; //range [-3, +3] int LuckVal() const; //range [-3, +3] @@ -424,6 +425,20 @@ public: }; CSelector DLL_EXPORT operator&&(const CSelector &first, const CSelector &second); +class DLL_EXPORT CSelectorsAlternative +{ + const CSelector first, second; +public: + CSelectorsAlternative(const CSelector &First, const CSelector &Second) + :first(First), second(Second) + { + } + bool operator()(const Bonus &bonus) const + { + return first(bonus) || second(bonus); + } +}; +CSelector DLL_EXPORT operator||(const CSelector &first, const CSelector &second); template class CSelectFieldEqual diff --git a/lib/NetPacks.h b/lib/NetPacks.h index ea2427755..3b806dfa8 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -1123,7 +1123,7 @@ struct SetStackEffect : public CPackForClient //3010 void applyCl(CClient *cl); std::vector stacks; //affected stacks (IDs) - CStack::StackEffect effect; //type of effect + Bonus effect; //type of effect template void serialize(Handler &h, const int version) { h & stacks & effect; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 5dc7844f5..629b8dc9a 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -760,13 +760,13 @@ DLL_EXPORT void BattleNextRound::applyGs( CGameState *gs ) s->firstHPleft = s->MaxHealth(); //remove effects and restore only those with remaining turns in duration - std::vector tmpEffects = s->effects; - s->effects.clear(); - for(int i=0; i < tmpEffects.size(); i++) + BonusList tmpEffects = s->bonuses; + s->bonuses.removeSpells(Bonus::CASTED_SPELL); + for (BonusList::iterator it = tmpEffects.begin(); it != tmpEffects.end(); it++) { - tmpEffects[i].turnsRemain--; - if(tmpEffects[i].turnsRemain > 0) - s->effects.push_back(tmpEffects[i]); + it->turnsRemain--; + if(it->turnsRemain > 0) + s->bonuses.push_back(*it); } //the same as above for features @@ -929,17 +929,17 @@ DLL_EXPORT void BattleSpellCast::applyGs( CGameState *gs ) CStack *s = gs->curB->getStack(*it); if(s && !vstd::contains(resisted, s->ID)) //if stack exists and it didn't resist { - std::vector remainingEff; - for(int g=0; g< s->effects.size(); ++g) + BonusList remainingEff; + for (BonusList::iterator it = remainingEff.begin(); it != remainingEff.end(); it++) { - if (onlyHelpful && VLC->spellh->spells[ s->effects[g].id ].positiveness != 1) + if (onlyHelpful && VLC->spellh->spells[ it->id ].positiveness != 1) { - remainingEff.push_back(s->effects[g]); + remainingEff.push_back(*it); } } - s->effects.clear(); //removing all effects - s->effects = remainingEff; //assigning effects that should remain + s->bonuses.removeSpells(Bonus::CASTED_SPELL); //removing all effects + s->bonuses = remainingEff; //assigning effects that should remain //removing all features from spells BonusList tmpFeatures = s->bonuses; @@ -1000,169 +1000,23 @@ DLL_EXPORT void BattleSpellCast::applyGs( CGameState *gs ) } } -static inline Bonus featureGenerator(Bonus::BonusType type, si16 subtype, si32 value, ui16 turnsRemain, si32 additionalInfo = 0, si32 limit = Bonus::NO_LIMIT) -{ - Bonus hb(makeFeature(type, Bonus::N_TURNS, subtype, value, Bonus::SPELL_EFFECT, turnsRemain, additionalInfo)); - hb.effectRange = limit; - return hb; -} - -static inline Bonus featureGeneratorVT(Bonus::BonusType type, si16 subtype, si32 value, ui16 turnsRemain, ui8 valType) -{ - Bonus ret(makeFeature(type, Bonus::N_TURNS, subtype, value, Bonus::SPELL_EFFECT, turnsRemain)); - ret.valType = valType; - return ret; -} - -static BonusList stackEffectToFeature(const CStack::StackEffect & sse) -{ - BonusList sf; - si32 power = VLC->spellh->spells[sse.id].powers[sse.level]; - switch(sse.id) - { - case 27: //shield - sf.push_back(featureGenerator(Bonus::GENERAL_DAMAGE_REDUCTION, 0, power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 28: //air shield - sf.push_back(featureGenerator(Bonus::GENERAL_DAMAGE_REDUCTION, 1, power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 29: //fire shield - sf.push_back(featureGenerator(Bonus::FIRE_SHIELD, 0, power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 30: //protection from air - sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 0, power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 31: //protection from fire - sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 1, power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 32: //protection from water - sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 2, power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 33: //protection from earth - sf.push_back(featureGenerator(Bonus::SPELL_DAMAGE_REDUCTION, 3, power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 34: //anti-magic - sf.push_back(featureGenerator(Bonus::LEVEL_SPELL_IMMUNITY, 0, power - 1, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 41: //bless - sf.push_back(featureGenerator(Bonus::ALWAYS_MAXIMUM_DAMAGE, -1, power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 42: //curse - sf.push_back(featureGenerator(Bonus::ALWAYS_MINIMUM_DAMAGE, -1, -1 * power, sse.turnsRemain, sse.level >= 2 ? 20 : 0)); - sf.back().id = sse.id; - break; - case 43: //bloodlust - sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, sse.turnsRemain, 0, Bonus::ONLY_MELEE_FIGHT)); - sf.back().id = sse.id; - break; - case 44: //precision - sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, sse.turnsRemain, 0, Bonus::ONLY_DISTANCE_FIGHT)); - sf.back().id = sse.id; - break; - case 45: //weakness - sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, -1 * power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 46: //stone skin - sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 47: //disrupting ray - sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, -1 * power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 48: //prayer - sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, sse.turnsRemain)); - sf.back().id = sse.id; - sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, power, sse.turnsRemain)); - sf.back().id = sse.id; - sf.push_back(featureGenerator(Bonus::STACKS_SPEED, 0, power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 49: //mirth - sf.push_back(featureGenerator(Bonus::MORALE, 0, power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 50: //sorrow - sf.push_back(featureGenerator(Bonus::MORALE, 0, -1 * power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 51: //fortune - sf.push_back(featureGenerator(Bonus::LUCK, 0, power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 52: //misfortune - sf.push_back(featureGenerator(Bonus::LUCK, 0, -1 * power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 53: //haste - sf.push_back(featureGenerator(Bonus::STACKS_SPEED, 0, power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 54: //slow - sf.push_back(featureGeneratorVT(Bonus::STACKS_SPEED, 0, -1 * ( 100 - power ), sse.turnsRemain, Bonus::PERCENT_TO_ALL)); - sf.back().id = sse.id; - break; - case 55: //slayer - sf.push_back(featureGenerator(Bonus::SLAYER, 0, sse.level, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 56: //frenzy - sf.push_back(featureGenerator(Bonus::IN_FRENZY, 0, VLC->spellh->spells[56].powers[sse.level]/100.0, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 58: //counterstrike - sf.push_back(featureGenerator(Bonus::ADDITIONAL_RETALIATION, 0, power, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 59: //bersek - sf.push_back(featureGenerator(Bonus::ATTACKS_NEAREST_CREATURE, 0, sse.level, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 60: //hypnotize - sf.push_back(featureGenerator(Bonus::HYPNOTIZED, 0, sse.level, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 61: //forgetfulness - sf.push_back(featureGenerator(Bonus::FORGETFULL, 0, sse.level, sse.turnsRemain)); - sf.back().id = sse.id; - break; - case 62: //blind - sf.push_back(makeFeature(Bonus::NOT_ACTIVE, Bonus::UNITL_BEING_ATTACKED | Bonus::N_TURNS, 0, 0, Bonus::SPELL_EFFECT, sse.turnsRemain)); - sf.back().id = sse.id; - sf.push_back(makeFeature(Bonus::GENERAL_ATTACK_REDUCTION, Bonus::UNTIL_ATTACK | Bonus::N_TURNS, 0, power, Bonus::SPELL_EFFECT, sse.turnsRemain)); - sf.back().id = sse.id; - break; - } - - return sf; -} - -void actualizeEffect(CStack * s, CStack::StackEffect & ef) +void actualizeEffect(CStack * s, Bonus & ef) { //actualizing effects vector - for(int g=0; geffects.size(); ++g) + for (BonusList::iterator it = s->bonuses.begin(); it != s->bonuses.end(); it++) { - if(s->effects[g].id == ef.id) + if(it->id == ef.id) { - s->effects[g].turnsRemain = std::max(s->effects[g].turnsRemain, ef.turnsRemain); + it->turnsRemain = std::max(it->turnsRemain, ef.turnsRemain); } } //actualizing features vector - BonusList sf = stackEffectToFeature(ef); + BonusList sf; + s->stackEffectToFeature(sf, ef); BOOST_FOREACH(const Bonus &fromEffect, sf) { - BOOST_FOREACH(Bonus &stackBonus, s->bonuses) + BOOST_FOREACH(Bonus &stackBonus, s->bonuses) //TODO: optimize { if(stackBonus.source == Bonus::SPELL_EFFECT && stackBonus.type == fromEffect.type && stackBonus.subtype == fromEffect.subtype) { @@ -1173,16 +1027,6 @@ void actualizeEffect(CStack * s, CStack::StackEffect & ef) } -bool containsEff(const std::vector & vec, int effectId) -{ - for(int g=0; gcurB->getStack(id); if(s) { - if(effect.id == 42 || !containsEff(s->effects, effect.id))//disrupting ray or not on the list - just add + if(effect.id == 42 || !s->hasBonus(Selector::source(Bonus::CASTED_SPELL, effect.id)))//disrupting ray or not on the list - just add { - s->effects.push_back(effect); - BonusList sf = stackEffectToFeature(effect); + s->bonuses.push_back(effect); + BonusList sf; + s->stackEffectToFeature(sf, effect); BOOST_FOREACH(const Bonus &fromEffect, sf) { s->bonuses.push_back(fromEffect); @@ -1258,11 +1103,11 @@ DLL_EXPORT void StacksHealedOrResurrected::applyGs( CGameState *gs ) //removal of negative effects if(resurrected) { - for(int h=0; heffects.size(); ++h) + for (BonusList::iterator it = changedStack->bonuses.begin(); it != changedStack->bonuses.end(); it++) { - if(VLC->spellh->spells[changedStack->effects[h].id].positiveness < 0) + if(VLC->spellh->spells[it->id].positiveness < 0) { - changedStack->effects.erase(changedStack->effects.begin() + h); + changedStack->bonuses.erase(it); } } diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index eacd6114a..fcbf21a08 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -4305,7 +4305,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, int destinatio sse.stacks.push_back((*it)->ID); } sse.effect.id = spellID; - sse.effect.level = spellLvl; + sse.effect.val = spellLvl; sse.effect.turnsRemain = gs->curB->calculateSpellDuration(spell, caster, usedSpellPower); if(!sse.stacks.empty()) sendAndApply(&sse);