Browse Source

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 years ago
parent
commit
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
 		//blitting amoutn background box
 		SDL_Surface *amountBG = NULL;
 		SDL_Surface *amountBG = NULL;
-		if(curStack.effects.size() == 0)
+		if(!curStack.bonuses.size())
 		{
 		{
 			amountBG = amountNormal;
 			amountBG = amountNormal;
 		}
 		}
 		else
 		else
 		{
 		{
 			int pos=0; //determining total positiveness of effects
 			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)
 			if(pos > 0)
 			{
 			{

+ 8 - 5
client/GUIClasses.cpp

@@ -2148,12 +2148,15 @@ CCreInfoWindow::CCreInfoWindow(const CStackInstance &st, int Type, boost::functi
 	{
 	{
 		//spell effects
 		//spell effects
 		int printed=0; //how many effect pics have been printed
 		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
 		//print current health

+ 1 - 1
client/NetPacksClient.cpp

@@ -574,7 +574,7 @@ void SetStackEffect::applyCl( CClient *cl )
 	BattleSpellCast sc;
 	BattleSpellCast sc;
 	sc.id = effect.id;
 	sc.id = effect.id;
 	sc.side = 3; //doesn't matter
 	sc.side = 3; //doesn't matter
-	sc.skill = effect.level;
+	sc.skill = effect.val;
 
 
 	//informing about effects
 	//informing about effects
 	if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end())
 	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
 6	1	0	0	146
 7	1	0	0	10
 7	1	0	0	10
 8	2	5	27	0
 8	2	5	27	0
-9	6	41	0	3
+9	6	3	41	0
 10	8	45	0	0
 10	8	45	0	0
 11	3	3	20	0
 11	3	3	20	0
 12	1	0	0	8
 12	1	0	0	8
@@ -28,7 +28,7 @@
 26	2	5	24	0
 26	2	5	24	0
 27	2	5	27	0
 27	2	5	27	0
 28	2	5	11	0
 28	2	5	11	0
-29	7	51	0	0
+29	7	0	51	0
 30	3	3	16	0
 30	3	3	16	0
 31	1	0	0	20
 31	1	0	0	20
 32	1	0	0	30
 32	1	0	0	30
@@ -42,7 +42,7 @@
 40	3	3	60	0
 40	3	3	60	0
 41	2	5	8	1
 41	2	5	8	1
 42	2	5	11	0
 42	2	5	11	0
-43	7	51	0	0
+43	7	0	51	0
 44	1	0	0	34
 44	1	0	0	34
 45	3	3	19	0
 45	3	3	19	0
 46	8	53	0	0
 46	8	53	0	0

+ 3 - 11
hch/CCreatureHandler.cpp

@@ -124,16 +124,6 @@ CCreature::CCreature()
 	doubleWide = false;
 	doubleWide = false;
 	nodeType = CBonusSystemNode::CREATURE;
 	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*/)
 void CCreature::addBonus(int val, int type, int subtype /*= -1*/)
 {
 {
 	Bonus added(Bonus::PERMANENT, type, Bonus::CREATURE_ABILITY, val, idNumber, subtype, Bonus::BASE_NUMBER);
 	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.addBonus(ncre.attack, Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK);
 		ncre.defence = readNumber(befi, i, andame, buf);
 		ncre.defence = readNumber(befi, i, andame, buf);
 		ncre.addBonus(ncre.defence, Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE);
 		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.damageMax = readNumber(befi, i, andame, buf);
+		ncre.addBonus(ncre.damageMax, Bonus::CREATURE_DAMAGE, 2);
 
 
 		ncre.shots = readNumber(befi, i, andame, buf);
 		ncre.shots = readNumber(befi, i, andame, buf);
 		ncre.spells = 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
 	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
 	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;
 	bool isMyUpgrade(const CCreature *anotherCre) const;
-	ui32 getMinDamage() const;
-	ui32 getMaxDamage() const;
 
 
 	void addBonus(int val, int type, int subtype = -1);
 	void addBonus(int val, int type, int subtype = -1);
 	void getParents(TCNodes &out, const CBonusSystemNode *root /*= NULL*/) const;
 	void getParents(TCNodes &out, const CBonusSystemNode *root /*= NULL*/) const;

+ 1 - 0
hch/CObjectHandler.cpp

@@ -1091,6 +1091,7 @@ void CGHeroInstance::initObj()
 				break;
 				break;
 			case 6://damage bonus for bless (Adela)
 			case 6://damage bonus for bless (Adela)
 				bonus.type = Bonus::SPECIAL_BLESS_DAMAGE;
 				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
 				bonus.additionalInfo = it->additionalinfo; //damage factor
 				speciality.bonuses.push_back (bonus);
 				speciality.bonuses.push_back (bonus);
 				break;
 				break;

+ 0 - 9
lib/CCreatureSet.cpp

@@ -278,15 +278,6 @@ void CStackInstance::getParents(TCNodes &out, const CBonusSystemNode *source /*=
 		out.insert(&IObjectInterface::cb->gameState()->globalEffects);
 		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
 std::string CStackInstance::getQuantityTXT(bool capitalized /*= true*/) const
 {
 {
 	return VLC->generaltexth->arraytxt[174 + getQuantityID()*3 + 2 - capitalized];
 	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 CArmedInstance *armyObj; //stack must be part of some army, army must be part of some object
 	const CCreature *type;
 	const CCreature *type;
 	TQuantity count;
 	TQuantity count;
-	ui32 getMinDamage() const;
-	ui32 getMaxDamage() const;
 	ui32 experience; //TODO: handle
 	ui32 experience; //TODO: handle
 	//TODO: stack artifacts
 	//TODO: stack artifacts
 
 

+ 155 - 14
lib/CGameState.cpp

@@ -742,20 +742,161 @@ ui32 CStack::Speed( int turn /*= 0*/ ) const
 	return speed;
 	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;
 	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 CStack::howManyEffectsSet(ui16 id) const
 {
 {
 	ui8 ret = 0;
 	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;
 			++ret;
 		}
 		}
@@ -2941,7 +3082,7 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
 	if(attacker->getEffect(55)) //slayer handling
 	if(attacker->getEffect(55)) //slayer handling
 	{
 	{
 		std::vector<int> affectedIds;
 		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)
 		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])
 			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;
 				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)
 	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
 	class HLP
@@ -3046,9 +3187,9 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
 	public:
 	public:
 		static bool hasAdvancedAirShield(const CStack * stack)
 		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;
 					return true;
 				}
 				}
@@ -3077,12 +3218,12 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
 
 
 	if(attacker->getEffect(42)) //curse handling (rest)
 	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));
 		returnedVal = std::make_pair(int(minDmg), int(minDmg));
 	}
 	}
 	else if(attacker->getEffect(41)) //bless handling
 	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));
 		returnedVal =  std::make_pair(int(maxDmg), int(maxDmg));
 	}
 	}
 	else
 	else

+ 18 - 13
lib/CGameState.h

@@ -261,29 +261,34 @@ public:
 	si16 shots; //how many shots left
 	si16 shots; //how many shots left
 
 
 	std::set<ECombatInfo> state;
 	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
 	//overrides
 	const CCreature* getCreature() const {return type;}
 	const CCreature* getCreature() const {return type;}
 
 
 	CStack(const CStackInstance *base, int O, int I, bool AO, int S); //c-tor
 	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
 	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
 	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 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 moved(int turn = 0) const; //if stack was already moved this turn
 	bool canMove(int turn = 0) const; //if stack can move
 	bool canMove(int turn = 0) const; //if stack can move
 	ui32 Speed(int turn = 0) const; //get speed of creature with all modificators
 	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;
 	bool doubleWide() const;
 	int occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1
 	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)))
 		if(selector(*i) && (!limit || limit(*i)))
 			out.push_back(*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)
 void BonusList::limit(const CBonusSystemNode &node)
 {
 {
@@ -294,6 +302,15 @@ ui16 CBonusSystemNode::MaxHealth() const
 	return valOfBonuses(Bonus::STACK_HEALTH);
 	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()
 CBonusSystemNode::CBonusSystemNode()
 {
 {
 	nodeType = UNKNOWN;
 	nodeType = UNKNOWN;
@@ -395,6 +412,10 @@ CSelector DLL_EXPORT operator&&(const CSelector &first, const CSelector &second)
 {
 {
 	return CSelectorsConjunction(first, second);
 	return CSelectorsConjunction(first, second);
 }
 }
+CSelector DLL_EXPORT operator||(const CSelector &first, const CSelector &second)
+{
+	return CSelectorsAlternative(first, second);
+}
 
 
 namespace Selector
 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 CBonusSystemNode *source = NULL) const;
 	void DLL_EXPORT getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, 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 getModifiersWDescr(TModDescr &out) const;
+	void DLL_EXPORT removeSpells(Bonus::BonusSource sourceType);
 
 
 	//special find functions
 	//special find functions
 	DLL_EXPORT Bonus * getFirst(const CSelector &select);
 	DLL_EXPORT Bonus * getFirst(const CSelector &select);
@@ -359,8 +360,8 @@ public:
 	bool hasBonusFrom(ui8 source, ui32 sourceID) const;
 	bool hasBonusFrom(ui8 source, ui32 sourceID) const;
 	void getModifiersWDescr( TModDescr &out, Bonus::BonusType type, int subtype = -1 ) const;  //out: pairs<modifier value, modifier description>
 	void getModifiersWDescr( TModDescr &out, Bonus::BonusType type, int subtype = -1 ) const;  //out: pairs<modifier value, modifier description>
 	int getBonusesCount(int from, int id) const;
 	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 MoraleVal() const; //range [-3, +3]
 	int LuckVal() 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);
 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>
 template<typename T>
 class CSelectFieldEqual
 class CSelectFieldEqual

+ 1 - 1
lib/NetPacks.h

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

+ 26 - 181
lib/NetPacksLib.cpp

@@ -760,13 +760,13 @@ DLL_EXPORT void BattleNextRound::applyGs( CGameState *gs )
 			s->firstHPleft = s->MaxHealth();
 			s->firstHPleft = s->MaxHealth();
 
 
 		//remove effects and restore only those with remaining turns in duration
 		//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
 		//the same as above for features
@@ -929,17 +929,17 @@ DLL_EXPORT void BattleSpellCast::applyGs( CGameState *gs )
 			CStack *s = gs->curB->getStack(*it);
 			CStack *s = gs->curB->getStack(*it);
 			if(s && !vstd::contains(resisted, s->ID)) //if stack exists and it didn't resist
 			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
 				//removing all features from spells
 				BonusList tmpFeatures = s->bonuses;
 				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
 	//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
 	//actualizing features vector
-	BonusList sf = stackEffectToFeature(ef);
+	BonusList sf;
+	s->stackEffectToFeature(sf, ef);
 
 
 	BOOST_FOREACH(const Bonus &fromEffect, sf)
 	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)
 			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 )
 DLL_EXPORT void SetStackEffect::applyGs( CGameState *gs )
 {
 {
 	BOOST_FOREACH(ui32 id, stacks)
 	BOOST_FOREACH(ui32 id, stacks)
@@ -1190,10 +1034,11 @@ DLL_EXPORT void SetStackEffect::applyGs( CGameState *gs )
 		CStack *s = gs->curB->getStack(id);
 		CStack *s = gs->curB->getStack(id);
 		if(s)
 		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)
 				BOOST_FOREACH(const Bonus &fromEffect, sf)
 				{
 				{
 					s->bonuses.push_back(fromEffect);
 					s->bonuses.push_back(fromEffect);
@@ -1258,11 +1103,11 @@ DLL_EXPORT void StacksHealedOrResurrected::applyGs( CGameState *gs )
 		//removal of negative effects
 		//removal of negative effects
 		if(resurrected)
 		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.stacks.push_back((*it)->ID);
 			}
 			}
 			sse.effect.id = spellID;
 			sse.effect.id = spellID;
-			sse.effect.level = spellLvl;
+			sse.effect.val = spellLvl;
 			sse.effect.turnsRemain = gs->curB->calculateSpellDuration(spell, caster, usedSpellPower);
 			sse.effect.turnsRemain = gs->curB->calculateSpellDuration(spell, caster, usedSpellPower);
 			if(!sse.stacks.empty())
 			if(!sse.stacks.empty())
 				sendAndApply(&sse);
 				sendAndApply(&sse);