소스 검색

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.
DjWarmonger 15 년 전
부모
커밋
bfe266a377

+ 4 - 3
client/CBattleInterface.cpp

@@ -3010,16 +3010,17 @@ void CBattleInterface::showAliveStack(int ID, const std::map<int, CStack> & 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; c<curStack.effects.size(); ++c)
+			for(BonusList::const_iterator it = curStack.bonuses.begin(); it != curStack.bonuses.end(); it++)
 			{
-				pos += CGI->spellh->spells[ curStack.effects[c].id ].positiveness;
+				if (it->source == Bonus::CASTED_SPELL)
+					pos += CGI->spellh->spells[ it->id ].positiveness;
 			}
 			if(pos > 0)
 			{

+ 8 - 5
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

+ 1 - 1
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())

+ 3 - 3
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

+ 3 - 11
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);

+ 0 - 2
hch/CCreatureHandler.h

@@ -60,8 +60,6 @@ public:
 	si32 maxAmount(const std::vector<si32> &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;

+ 1 - 0
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;

+ 0 - 9
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];

+ 0 - 2
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
 

+ 155 - 14
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<const CGHeroInstance*>(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<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
 	if(attacker->getEffect(55)) //slayer handling
 	{
 		std::vector<int> 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<ui32, ui32> 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<ui32, ui32> 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<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
 	public:
 		static bool hasAdvancedAirShield(const CStack * stack)
 		{
-			for(int g=0; g<stack->effects.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<ui32, ui32> 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

+ 18 - 13
lib/CGameState.h

@@ -261,29 +261,34 @@ public:
 	si16 shots; //how many shots left
 
 	std::set<ECombatInfo> state;
-	struct StackEffect
-	{
-		ui16 id; //spell id
-		ui8 level; //skill level
-		si16 turnsRemain; 
-		template <typename Handler> void serialize(Handler &h, const int version)
-		{
-			h & id & level & turnsRemain;
-		}
-	};
-	std::vector<StackEffect> 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

+ 21 - 0
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
 {

+ 17 - 2
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<modifier value, modifier description>
 	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<typename T>
 class CSelectFieldEqual

+ 1 - 1
lib/NetPacks.h

@@ -1123,7 +1123,7 @@ struct SetStackEffect : public CPackForClient //3010
 	void applyCl(CClient *cl);
 
 	std::vector<ui32> stacks; //affected stacks (IDs)
-	CStack::StackEffect effect; //type of effect
+	Bonus effect; //type of effect
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & stacks & effect;

+ 26 - 181
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<CStack::StackEffect> 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<CStack::StackEffect> 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; g<s->effects.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<CStack::StackEffect> & vec, int effectId)
-{
-	for(int g=0; g<vec.size(); ++g)
-	{
-		if(vec[g].id == effectId)
-			return true;
-	}
-	return false;
-}
-
 DLL_EXPORT void SetStackEffect::applyGs( CGameState *gs )
 {
 	BOOST_FOREACH(ui32 id, stacks)
@@ -1190,10 +1034,11 @@ DLL_EXPORT void SetStackEffect::applyGs( CGameState *gs )
 		CStack *s = gs->curB->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; h<changedStack->effects.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);
 				}
 			}
 			

+ 1 - 1
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);