Selaa lähdekoodia

* Improved battle and bonus system performance (significantly faster in debug build, tip: use vs 2008, turn of hex field presentation)
* Implemented caching for the bonus system

beegee1 14 vuotta sitten
vanhempi
sitoutus
5fdb5aa494

+ 9 - 7
client/CBattleInterface.cpp

@@ -1651,16 +1651,18 @@ void CBattleInterface::show(SDL_Surface * to)
 	////showing units //a lot of work...
 	std::vector<const CStack *> stackAliveByHex[BFIELD_SIZE];
 	//double loop because dead stacks should be printed first
-	BOOST_FOREACH(const CStack *s, stacks)
+	for (int i = 0; i < stacks.size(); i++)
 	{
+		const CStack *s = stacks[i];
 		if(creAnims.find(s->ID) == creAnims.end()) //e.g. for summoned but not yet handled stacks
 			continue;
 		if(creAnims[s->ID]->getType() != 5 && s->position >= 0) //don't show turrets here
 			stackAliveByHex[s->position].push_back(s);
 	}
 	std::vector<const CStack *> stackDeadByHex[BFIELD_SIZE];
-	BOOST_FOREACH(const CStack *s, stacks)
+	for (int i = 0; i < stacks.size(); i++)
 	{
+		const CStack *s = stacks[i];
 		if(creAnims.find(s->ID) == creAnims.end()) //e.g. for summoned but not yet handled stacks
 			continue;
 		if(creAnims[s->ID]->getType() == 5)
@@ -3093,9 +3095,9 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
 						boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin())->type->nameSing);
 					}
 					//The %s shrivel with age, and lose %d hit points."	
-					BonusList bl = curInt->cb->battleGetStackByID(*sc->affectedCres.begin(), false)->getBonuses(Selector::type(Bonus::STACK_HEALTH));
-					bl.remove_if(Selector::source(Bonus::SPELL_EFFECT, 75));
-					boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(bl.totalValue()/2));
+					boost::shared_ptr<BonusList> bl = curInt->cb->battleGetStackByID(*sc->affectedCres.begin(), false)->getBonuses(Selector::type(Bonus::STACK_HEALTH));
+					bl->remove_if(Selector::source(Bonus::SPELL_EFFECT, 75));
+					boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(bl->totalValue()/2));
 				}
 					break;
 				case 78: //Dispell helpful spells
@@ -3428,8 +3430,8 @@ void CBattleInterface::showAliveStack(const CStack *stack, SDL_Surface * to)
 
 		//blitting amount background box
 		SDL_Surface *amountBG = NULL;
-		BonusList spellEffects = stack->getSpellBonuses();
-		if(!spellEffects.size())
+		boost::shared_ptr<BonusList> spellEffects = stack->getSpellBonuses();
+		if(!spellEffects->size())
 		{
 			amountBG = amountNormal;
 		}

+ 10 - 7
client/CCastleInterface.cpp

@@ -1136,19 +1136,22 @@ void CCreaInfo::clickRight(tribool down, bool previousState)
 					cnt++;//external dwellings count to summ
 			summ+=AddToString(CGI->generaltexth->allTexts[591],descr,cnt);
 
-			BonusList bl;
+			boost::shared_ptr<BonusList> bl;
 			const CGHeroInstance *hero = town->garrisonHero;
 			if (hero)
-				hero->getBonuses(bl, Selector::type(Bonus::CREATURE_GROWTH) && Selector::subtype(level) 
-			                      && Selector::sourceType(Bonus::ARTIFACT), hero);
+				bl = hero->getAllBonuses(Selector::type(Bonus::CREATURE_GROWTH) && Selector::subtype(level) 
+			                      && Selector::sourceType(Bonus::ARTIFACT), 0, hero);
 			
 			hero = town->visitingHero;
 			if (hero)
-				hero->getBonuses(bl, Selector::type(Bonus::CREATURE_GROWTH) && Selector::subtype(level) 
-			                      && Selector::sourceType(Bonus::ARTIFACT), hero);
+			{
+				boost::shared_ptr<BonusList> blAppend = hero->getAllBonuses(Selector::type(Bonus::CREATURE_GROWTH) && Selector::subtype(level) 
+					&& Selector::sourceType(Bonus::ARTIFACT), 0, hero);
+				bl->insert(bl->end(), blAppend->begin(), blAppend->end()) ;
+			}
 			
-			if (bl.size())
-				summ+=AddToString (CGI->arth->artifacts[bl.front()->sid]->Name()+" %+d", descr, bl.totalValue());
+			if (bl->size())
+				summ+=AddToString (CGI->arth->artifacts[bl->front()->sid]->Name()+" %+d", descr, bl->totalValue());
 
 			//TODO: player bonuses
 

+ 1 - 1
client/CCreatureWindow.cpp

@@ -134,7 +134,7 @@ void CCreatureWindow::init(const CStackInstance *Stack, const CBonusSystemNode *
 	//Basic graphics - need to calculate size
 
 	CBonusSystemNode node = CBonusSystemNode() ;
-	node.bonuses = stackNode->getBonuses(Selector::durationType(Bonus::PERMANENT));
+	node.bonuses = *(stackNode->getBonuses(Selector::durationType(Bonus::PERMANENT)));
 	BonusList bl;
 
 	while (node.bonuses.size())

+ 12 - 8
client/CHeroWindow.cpp

@@ -44,21 +44,25 @@
 extern SDL_Surface * screen;
 using namespace boost::assign;
 
-void CHeroWithMaybePickedArtifact::getAllBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/) const
+const boost::shared_ptr<BonusList> CHeroWithMaybePickedArtifact::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/) const
 {
-	BonusList heroBonuses, bonusesFromPickedUpArtifact;
-	hero->getAllBonuses(heroBonuses, selector, limit, hero);
+	boost::shared_ptr<BonusList> out(new BonusList);
+	boost::shared_ptr<BonusList> heroBonuses = hero->getAllBonuses(selector, limit, hero);
+	boost::shared_ptr<BonusList> bonusesFromPickedUpArtifact;
 
 	CArtifactsOfHero::SCommonPart *cp = cww->artSets.size() ? cww->artSets.front()->commonInfo : NULL;
 	if(cp && cp->src.art && cp->src.AOH && cp->src.AOH->getHero() == hero)
 	{
-		cp->src.art->getAllBonuses(bonusesFromPickedUpArtifact, selector, limit, hero);
+		bonusesFromPickedUpArtifact = cp->src.art->getAllBonuses(selector, limit, hero);
 	}
+	else
+		bonusesFromPickedUpArtifact = boost::shared_ptr<BonusList>(new BonusList);
 
-	BOOST_FOREACH(Bonus *b, bonusesFromPickedUpArtifact)
-		heroBonuses -= b;
-	BOOST_FOREACH(Bonus *b, heroBonuses)
-		out.push_back(b);
+	BOOST_FOREACH(Bonus *b, *bonusesFromPickedUpArtifact)
+		*heroBonuses -= b;
+	BOOST_FOREACH(Bonus *b, *heroBonuses)
+		out->push_back(b);
+	return out;
 }
 
 CHeroWithMaybePickedArtifact::CHeroWithMaybePickedArtifact(CWindowWithArtifacts *Cww, const CGHeroInstance *Hero)

+ 1 - 1
client/CHeroWindow.h

@@ -46,7 +46,7 @@ public:
 	CWindowWithArtifacts *cww;
 
 	CHeroWithMaybePickedArtifact(CWindowWithArtifacts *Cww, const CGHeroInstance *Hero);
-	void getAllBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = NULL) const OVERRIDE;
+	const boost::shared_ptr<BonusList> getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = NULL) const OVERRIDE;
 };
 
 class CHeroWindow: public CWindowWithGarrison, public CWindowWithArtifacts

+ 2 - 1
client/GUIBase.cpp

@@ -352,7 +352,8 @@ void CGuiHandler::run()
 	setThreadName(-1, "CGuiHandler::run");
 	try
 	{
-		CCS->curh->centerCursor();
+		if (conf.cc.fullscreen)
+			CCS->curh->centerCursor();
 
 		mainFPSmng->init(); // resets internal clock, needed for FPS manager
 		while(!terminate)

+ 9 - 13
lib/BattleState.cpp

@@ -476,9 +476,9 @@ TDmgRange BattleInfo::calculateDmgRange( const CStack* attacker, const CStack* d
 	//calculating total attack/defense skills modifier
 
 	if(shooting) //precision handling (etc.)
-		attackDefenceDifference += attacker->getBonuses(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), Selector::effectRange(Bonus::ONLY_DISTANCE_FIGHT)).totalValue();
+		attackDefenceDifference += attacker->getBonuses(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), Selector::effectRange(Bonus::ONLY_DISTANCE_FIGHT))->totalValue();
 	else //bloodlust handling (etc.)
-		attackDefenceDifference += attacker->getBonuses(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), Selector::effectRange(Bonus::ONLY_MELEE_FIGHT)).totalValue();
+		attackDefenceDifference += attacker->getBonuses(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), Selector::effectRange(Bonus::ONLY_MELEE_FIGHT))->totalValue();
 
 
 	if(attacker->getEffect(55)) //slayer handling
@@ -1830,23 +1830,23 @@ SpellCasting::ESpellCastProblem BattleInfo::battleIsImmune(const CGHeroInstance
 				return SpellCasting::STACK_IMMUNE_TO_SPELL;
 		}
 
-		BonusList immunities = subject->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY));
+		boost::shared_ptr<BonusList> immunities = subject->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY));
 		if(subject->hasBonusOfType(Bonus::NEGATE_ALL_NATURAL_IMMUNITIES))
 		{
-			std::remove_if(immunities.begin(), immunities.end(), NegateRemover);
+			std::remove_if(immunities->begin(), immunities->end(), NegateRemover);
 		}
 
 		if(subject->hasBonusOfType(Bonus::SPELL_IMMUNITY, spell->id) ||
-			( immunities.size() > 0 && immunities.totalValue() >= spell->level && spell->level))
+			( immunities->size() > 0 && immunities->totalValue() >= spell->level && spell->level))
 		{ 
 			return SpellCasting::STACK_IMMUNE_TO_SPELL;
 		}
 		//dispel helpful spells
 		if(spell->id == 78)
 		{
-			BonusList spellBon = subject->getSpellBonuses();
+			boost::shared_ptr<BonusList> spellBon = subject->getSpellBonuses();
 			bool hasPositiveSpell = false;
-			BOOST_FOREACH(const Bonus * b, spellBon)
+			BOOST_FOREACH(const Bonus * b, *spellBon)
 			{
 				if(VLC->spellh->spells[b->sid]->positiveness > 0)
 				{
@@ -2265,17 +2265,13 @@ THex CStack::occupiedHex() const
 		return THex::INVALID;
 	}
 }
-BonusList CStack::getSpellBonuses() const
-{
-	return getBonuses(Selector::sourceTypeSel(Bonus::SPELL_EFFECT));
-}
 
 std::vector<si32> CStack::activeSpells() const
 {
 	std::vector<si32> ret;
 
-	BonusList spellEffects = getSpellBonuses();
-	BOOST_FOREACH(const Bonus *it, spellEffects)
+	boost::shared_ptr<BonusList> spellEffects = getSpellBonuses();
+	BOOST_FOREACH(const Bonus *it, *spellEffects)
 	{
 		if (!vstd::contains(ret, it->sid)) //do not duplicate spells with multiple effects
 			ret.push_back(it->sid);

+ 0 - 1
lib/BattleState.h

@@ -159,7 +159,6 @@ public:
 	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
-	BonusList getSpellBonuses() const;
 	static void stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse);
 	std::vector<si32> activeSpells() const; //returns vector of active spell IDs sorted by time of cast
 	const CGHeroInstance *getMyHero() const; //if stack belongs to hero (directly or was by him summoned) returns hero, NULL otherwise

+ 2 - 2
lib/CGameState.cpp

@@ -1678,8 +1678,8 @@ UpgradeInfo CGameState::getUpgradeInfo(const CStackInstance &stack)
 		t = static_cast<const CGTownInstance *>(stack.armyObj);
 	else if(h)
 	{	//hero speciality
-		BonusList lista = h->speciality.getBonuses(Selector::typeSubtype(Bonus::SPECIAL_UPGRADE, base->idNumber));
-		BOOST_FOREACH(const Bonus *it, lista)
+		boost::shared_ptr<BonusList> lista = h->speciality.getBonuses(Selector::typeSubtype(Bonus::SPECIAL_UPGRADE, base->idNumber));
+		BOOST_FOREACH(const Bonus *it, *lista)
 		{
 			ui16 nid = it->additionalInfo;
 			if (nid != base->idNumber) //in very specific case the upgrade is avaliable by default (?)

+ 2 - 2
lib/CObjectHandler.cpp

@@ -2261,9 +2261,9 @@ void CGTownInstance::deserializationFix()
 
 void CGTownInstance::recreateBuildingsBonuses()
 {
-	BonusList bl;
+	boost::shared_ptr<BonusList> bl(new BonusList);
 	exportedBonuses.getBonuses(bl, Selector::sourceType(Bonus::TOWN_STRUCTURE));
-	BOOST_FOREACH(Bonus *b, bl)
+	BOOST_FOREACH(Bonus *b, *bl)
 		removeBonus(b);
 
 

+ 135 - 66
lib/HeroBonus.cpp

@@ -14,7 +14,6 @@
 #include "BattleState.h"
 #include "CArtHandler.h"
 
-#define FOREACH_CONST_PARENT(pname) 	TCNodes lparents; getParents(lparents); BOOST_FOREACH(const CBonusSystemNode *pname, lparents)
 #define FOREACH_PARENT(pname) 	TNodes lparents; getParents(lparents); BOOST_FOREACH(CBonusSystemNode *pname, lparents)
 #define FOREACH_RED_CHILD(pname) 	TNodes lchildren; getRedChildren(lchildren); BOOST_FOREACH(CBonusSystemNode *pname, lchildren)
 #define FOREACH_RED_PARENT(pname) 	TNodes lparents; getRedParents(lparents); BOOST_FOREACH(CBonusSystemNode *pname, lparents)
@@ -25,6 +24,9 @@
 
 #define BONUS_LOG_LINE(x) tlog5 << x << std::endl
 
+int CBonusSystemNode::treeChanged = 1;
+const bool CBonusSystemNode::cachingEnabled = true;
+
 int DLL_EXPORT BonusList::totalValue() const
 {
 	int base = 0;
@@ -93,17 +95,23 @@ int DLL_EXPORT BonusList::totalValue() const
 }
 const DLL_EXPORT Bonus * BonusList::getFirst(const CSelector &selector) const
 {
-	BOOST_FOREACH(Bonus *i, *this)
-		if(selector(i))
-			return &*i;
+	for (int i = 0; i < this->size(); i++)
+	{
+		const Bonus *b = (*this)[i];
+		if(selector(b))
+			return &*b;
+	}
 	return NULL;
 }
 
 DLL_EXPORT Bonus * BonusList::getFirst(const CSelector &select)
 {
-	BOOST_FOREACH(Bonus *i, *this)
-		if(select(i))
-			return &*i;
+	for (int i = 0; i < this->size(); i++)
+	{
+		Bonus *b = (*this)[i];
+		if(select(b))
+			return &*b;
+	}
 	return NULL;
 }
 
@@ -113,7 +121,7 @@ void DLL_EXPORT BonusList::getModifiersWDescr(TModDescr &out) const
 		out.push_back(std::make_pair(i->val, i->Description()));
 }
 
-void DLL_EXPORT BonusList::getBonuses(BonusList &out, const CSelector &selector) const
+void DLL_EXPORT BonusList::getBonuses(boost::shared_ptr<BonusList> out, const CSelector &selector) const
 {
 // 	BOOST_FOREACH(Bonus *i, *this)
 // 		if(selector(i) && i->effectRange == Bonus::NO_LIMIT)
@@ -122,11 +130,16 @@ void DLL_EXPORT BonusList::getBonuses(BonusList &out, const CSelector &selector)
 	getBonuses(out, selector, 0);
 }
 
-void DLL_EXPORT BonusList::getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit) const
+void DLL_EXPORT BonusList::getBonuses(boost::shared_ptr<BonusList> out, const CSelector &selector, const CSelector &limit, const bool caching /*= false*/) const
 {
-	BOOST_FOREACH(Bonus *i, *this)
-		if(selector(i) && ((!limit && i->effectRange == Bonus::NO_LIMIT) || (limit && limit(i)))) //add matching bonuses that matches limit predicate or have NO_LIMIT if no given predicate
-			out.push_back(i);
+	for (int i = 0; i < this->size(); i++)
+	{
+		Bonus *b = (*this)[i];
+
+		//add matching bonuses that matches limit predicate or have NO_LIMIT if no given predicate
+		if(caching || (selector(b) && ((!limit && b->effectRange == Bonus::NO_LIMIT) || (limit && limit(b)))))
+			out->push_back(b);
+	}
 }
 
 void BonusList::limit(const CBonusSystemNode &node)
@@ -134,10 +147,11 @@ void BonusList::limit(const CBonusSystemNode &node)
 	remove_if(boost::bind(&CBonusSystemNode::isLimitedOnUs, boost::ref(node), _1));
 }
 
+
 void DLL_EXPORT BonusList::eliminateDuplicates()
 {
-	sort();
-	unique();
+	sort( begin(), end() );
+	erase( unique( begin(), end() ), end() );
 }
 
 int IBonusBearer::valOfBonuses(Bonus::BonusType type, const CSelector &selector) const
@@ -156,13 +170,13 @@ int IBonusBearer::valOfBonuses(Bonus::BonusType type, int subtype /*= -1*/) cons
 
 int IBonusBearer::valOfBonuses(const CSelector &selector) const
 {
-	BonusList hlp;
-	getBonuses(hlp, selector);
-	return hlp.totalValue();
+	CSelector limit = 0;
+	boost::shared_ptr<BonusList> hlp = getAllBonuses(selector, limit, NULL);
+	return hlp->totalValue();
 }
 bool IBonusBearer::hasBonus(const CSelector &selector) const
 {
-	return getBonuses(selector).size() > 0;
+	return getBonuses(selector)->size() > 0;
 }
 
 bool IBonusBearer::hasBonusOfType(Bonus::BonusType type, int subtype /*= -1*/) const
@@ -170,6 +184,12 @@ bool IBonusBearer::hasBonusOfType(Bonus::BonusType type, int subtype /*= -1*/) c
 	CSelector s = Selector::type(type);
 	if(subtype != -1)
 		s = s && Selector::subtype(subtype);
+	else
+	{
+		std::string str = "type_";
+		str += ((char) type);
+		setCachingStr(str);
+	}
 
 	return hasBonus(s);
 }
@@ -181,7 +201,7 @@ void IBonusBearer::getModifiersWDescr(TModDescr &out, Bonus::BonusType type, int
 
 void IBonusBearer::getModifiersWDescr(TModDescr &out, const CSelector &selector) const
 {
-	getBonuses(selector).getModifiersWDescr(out);
+	getBonuses(selector)->getModifiersWDescr(out);
 }
 int IBonusBearer::getBonusesCount(int from, int id) const
 {
@@ -190,43 +210,17 @@ int IBonusBearer::getBonusesCount(int from, int id) const
 
 int IBonusBearer::getBonusesCount(const CSelector &selector) const
 {
-	return getBonuses(selector).size();
+	return getBonuses(selector)->size();
 }
 
-void IBonusBearer::getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root /*= NULL*/) const
+const boost::shared_ptr<BonusList> IBonusBearer::getBonuses(const CSelector &selector) const
 {
-	getBonuses(out, selector, 0, root);
-// 	FOREACH_CONST_PARENT(p)
-// 		p->getBonuses(out, selector, root ? root : this);
-// 
-// 	bonuses.getBonuses(out, selector);
-// 	
-// 	if(!root)
-// 		out.limit(*this);
+	return getAllBonuses(selector, 0, NULL);
 }
 
-BonusList IBonusBearer::getBonuses(const CSelector &selector) const
+const boost::shared_ptr<BonusList> IBonusBearer::getBonuses(const CSelector &selector, const CSelector &limit) const
 {
-	BonusList ret;
-	getBonuses(ret, selector);
-	return ret;
-}
-
-void IBonusBearer::getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/) const
-{
-	getAllBonuses(out, selector, limit, root);
-	out.eliminateDuplicates();
-// 
-// 	getBonuses(out, selector); //first get all the bonuses
-// 	out.remove_if(std::not1(limit)); //now remove the ones we don't like
-// 	out.limit(*this); //apply bonuses' limiters
-}
-
-BonusList IBonusBearer::getBonuses(const CSelector &selector, const CSelector &limit) const
-{
-	BonusList ret;
-	getBonuses(ret, selector, limit);
-	return ret;
+	return getAllBonuses(selector, limit, NULL);
 }
 
 bool IBonusBearer::hasBonusFrom(ui8 source, ui32 sourceID) const
@@ -325,6 +319,23 @@ si32 IBonusBearer::magicResistance() const
 	return valOfBonuses(Selector::type(Bonus::MAGIC_RESISTANCE));
 }
 
+void IBonusBearer::setCachingStr(const std::string &request) const
+{
+}
+
+const boost::shared_ptr<BonusList> IBonusBearer::getSpellBonuses() const
+{
+	std::string str = "source_";
+	str += (char) Bonus::SPELL_EFFECT;
+	setCachingStr(str);
+	return getBonuses(Selector::sourceType(Bonus::SPELL_EFFECT));
+}
+
+void CBonusSystemNode::setCachingStr(const std::string &request) const
+{
+	cachingStr = request;
+}
+
 Bonus * CBonusSystemNode::getBonus(const CSelector &selector)
 {
 	Bonus *ret = bonuses.getFirst(selector);
@@ -348,30 +359,82 @@ const Bonus * CBonusSystemNode::getBonus( const CSelector &selector ) const
 
 void CBonusSystemNode::getParents(TCNodes &out) const /*retreives list of parent nodes (nodes to inherit bonuses from) */
 {
-	BOOST_FOREACH(const CBonusSystemNode *parent, parents)
+	for (int i = 0; i < parents.size(); i++)
+	{
+		const CBonusSystemNode *parent = parents[i];
 		out.insert(parent);
+	}
 }
 
 void CBonusSystemNode::getParents(TNodes &out)
 {
-	BOOST_FOREACH(const CBonusSystemNode *parent, parents)
+	for (int i = 0; i < parents.size(); i++)
+	{
+		const CBonusSystemNode *parent = parents[i];
 		out.insert(const_cast<CBonusSystemNode*>(parent));
+	}	
 }
 
-void CBonusSystemNode::getAllBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/) const
+void CBonusSystemNode::getAllBonusesRec(boost::shared_ptr<BonusList> out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/, const bool caching /*= false*/) const
 {
-	FOREACH_CONST_PARENT(p)
-		p->getBonuses(out, selector, limit, root ? root : this);
-
-	bonuses.getBonuses(out, selector, limit);
+	TCNodes lparents; 
+	getParents(lparents); 
+	BOOST_FOREACH(const CBonusSystemNode *p, lparents)
+		p->getAllBonusesRec(out, selector, limit, root ? root : this, caching);
+	
+	bonuses.getBonuses(out, selector, limit, caching);
 
 	if(!root)
-		out.limit(*this);
+		out->limit(*this);
 }
 
-CBonusSystemNode::CBonusSystemNode()
+const boost::shared_ptr<BonusList> CBonusSystemNode::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/) const
+{
+	boost::shared_ptr<BonusList> ret(new BonusList());
+	if (CBonusSystemNode::cachingEnabled)
+	{
+		if (cachedLast != treeChanged)
+		{
+			getAllBonusesRec(ret, selector, limit, this, true);
+			ret->eliminateDuplicates();
+			cachedBonuses = *ret;
+			ret->clear();
+			cachedRequests.clear();
+			cachedLast = treeChanged;
+		}
+	
+		if (cachingStr != "")
+		{
+			std::map<std::string, boost::shared_ptr<BonusList>>::iterator it(cachedRequests.find(cachingStr));
+			if (cachedRequests.size() > 0 && it != cachedRequests.end())
+			{
+				ret = it->second;
+				cachingStr = "";
+				return ret;
+			}
+		}
+
+		cachedBonuses.getBonuses(ret, selector, limit, false);
+		if (!root)
+			ret->limit(*this);
+
+		if (cachingStr != "")
+			cachedRequests[cachingStr] = ret;
+
+		cachingStr = "";
+
+		return ret;
+	}
+	else
+	{
+		getAllBonusesRec(ret, selector, limit, root, false);
+		ret->eliminateDuplicates();
+		return ret;
+	}
+}
+
+CBonusSystemNode::CBonusSystemNode() : nodeType(UNKNOWN), cachedLast(0)
 {
-	nodeType = UNKNOWN;
 }
 
 CBonusSystemNode::~CBonusSystemNode()
@@ -400,6 +463,7 @@ void CBonusSystemNode::attachTo(CBonusSystemNode *parent)
 		newRedDescendant(parent);
 
 	parent->newChildAttached(this);
+	CBonusSystemNode::treeChanged++;
 }
 
 void CBonusSystemNode::detachFrom(CBonusSystemNode *parent)
@@ -413,13 +477,14 @@ void CBonusSystemNode::detachFrom(CBonusSystemNode *parent)
 
 	parents -= parent;
 	parent->childDetached(this);
+	CBonusSystemNode::treeChanged++;
 }
 
 void CBonusSystemNode::popBonuses(const CSelector &s)
 {
-	BonusList bl;
+	boost::shared_ptr<BonusList> bl(new BonusList);
 	exportedBonuses.getBonuses(bl, s);
-	BOOST_FOREACH(Bonus *b, bl)
+	BOOST_FOREACH(Bonus *b, *bl)
 		removeBonus(b);
 
 	BOOST_FOREACH(CBonusSystemNode *child, children)
@@ -436,6 +501,7 @@ void CBonusSystemNode::addNewBonus(Bonus *b)
 	assert(!vstd::contains(exportedBonuses,b));
 	exportedBonuses.push_back(b);
 	exportBonus(b);
+	CBonusSystemNode::treeChanged++;
 }
 
 void CBonusSystemNode::removeBonus(Bonus *b)
@@ -446,6 +512,7 @@ void CBonusSystemNode::removeBonus(Bonus *b)
 	else
 		bonuses -= b;
 	delNull(b);
+	CBonusSystemNode::treeChanged++;
 }
 
 bool CBonusSystemNode::isLimitedOnUs(Bonus *b) const
@@ -603,8 +670,10 @@ void CBonusSystemNode::getRedDescendants(TNodes &out)
 void CBonusSystemNode::battleTurnPassed()
 {
 	BonusList bonusesCpy = exportedBonuses; //copy, because removing bonuses invalidates iters
-	BOOST_FOREACH(Bonus *b, bonusesCpy)
+	for (int i = 0; i < bonusesCpy.size(); i++)
 	{
+		Bonus *b = bonusesCpy[i];
+
 		if(b->duration & Bonus::N_TURNS)
 		{
 			b->turnsRemain--;
@@ -852,10 +921,10 @@ const CCreature * retrieveCreature(const CBonusSystemNode *node)
 
 DLL_EXPORT std::ostream & operator<<(std::ostream &out, const BonusList &bonusList)
 {
-	int i = 0;
-	BOOST_FOREACH(const Bonus *b, bonusList)
+	for (int i = 0; i < bonusList.size(); i++)
 	{
-		out << "Bonus " << i++ << "\n" << *b << std::endl;
+		Bonus *b = bonusList[i];
+		out << "Bonus " << i << "\n" << *b << std::endl;
 	}
 	return out;
 }

+ 42 - 16
lib/HeroBonus.h

@@ -1,7 +1,6 @@
 #pragma once
 #include "../global.h"
 #include <string>
-#include <list>
 #include <set>
 #include <boost/function.hpp>
 #include <boost/shared_ptr.hpp>
@@ -318,14 +317,14 @@ struct DLL_EXPORT Bonus
 
 DLL_EXPORT std::ostream & operator<<(std::ostream &out, const Bonus &bonus);
 
-class BonusList : public std::list<Bonus*>
+class BonusList : public std::vector<Bonus*>
 {
 public:
 	int DLL_EXPORT totalValue() const; //subtype -> subtype of bonus, if -1 then any
-	void DLL_EXPORT getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit) const;
+	void DLL_EXPORT getBonuses(boost::shared_ptr<BonusList> out, const CSelector &selector, const CSelector &limit, const bool caching = false) const;
 	void DLL_EXPORT getModifiersWDescr(TModDescr &out) const;
 
-	void DLL_EXPORT getBonuses(BonusList &out, const CSelector &selector) const;
+	void DLL_EXPORT getBonuses(boost::shared_ptr<BonusList> out, const CSelector &selector) const;
 
 	//special find functions
 	DLL_EXPORT Bonus * getFirst(const CSelector &select);
@@ -333,10 +332,26 @@ public:
 
 	void limit(const CBonusSystemNode &node); //erases bonuses using limitor
 	void DLL_EXPORT eliminateDuplicates();
+	
+	// remove_if implementation for STL vector types
+	template <class Predicate>
+	void remove_if(Predicate pred)
+	{
+		BonusList newList;
+		for (int i = 0; i < this->size(); i++)
+		{
+			Bonus *b = (*this)[i];
+			if (!pred(b))
+				newList.push_back(b);
+		}
+		this->clear();
+		this->resize(newList.size());
+		std::copy(newList.begin(), newList.end(), this->begin());
+	}
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & static_cast<std::list<Bonus*>&>(*this);
+		h & static_cast<std::vector<Bonus*>&>(*this);
 	}
 };
 
@@ -382,20 +397,18 @@ public:
 class DLL_EXPORT IBonusBearer
 {
 public:
-	virtual void getAllBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = NULL) const = 0; 
-
 	//new bonusing node interface
 	// * selector is predicate that tests if HeroBonus matches our criteria
 	// * root is node on which call was made (NULL will be replaced with this)
 	//interface
-	void getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = NULL) const; //as above but without duplicates
-	void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
+	virtual const boost::shared_ptr<BonusList> getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = NULL) const = 0; 
+	virtual void setCachingStr(const std::string &request) const;
 	void getModifiersWDescr(TModDescr &out, const CSelector &selector) const;  //out: pairs<modifier value, modifier description>
 	int getBonusesCount(const CSelector &selector) const;
 	int valOfBonuses(const CSelector &selector) const;
 	bool hasBonus(const CSelector &selector) const;
-	BonusList getBonuses(const CSelector &selector, const CSelector &limit) const;
-	BonusList getBonuses(const CSelector &selector) const;
+	const boost::shared_ptr<BonusList> getBonuses(const CSelector &selector, const CSelector &limit) const;
+	const boost::shared_ptr<BonusList> getBonuses(const CSelector &selector) const;
 
 	//legacy interface 
 	int valOfBonuses(Bonus::BonusType type, const CSelector &selector) const;
@@ -417,11 +430,24 @@ public:
 
 	si32 manaLimit() const; //maximum mana value for this hero (basically 10*knowledge)
 	int getPrimSkillLevel(int id) const; //0-attack, 1-defence, 2-spell power, 3-knowledge
-
+	const boost::shared_ptr<BonusList> getSpellBonuses() const;
 };
 
 class DLL_EXPORT CBonusSystemNode : public IBonusBearer
 {
+	static const bool cachingEnabled; 
+	mutable BonusList cachedBonuses;
+	mutable int cachedLast;	
+	static int treeChanged;
+
+	// Setting a value to cachingStr before getting any bonuses caches the result for later requests. 
+	// This string needs to be unique, that's why it has to be setted in the following manner:
+	// [property key]_[value] => only for selector
+	mutable std::string cachingStr;
+	mutable std::map<std::string, boost::shared_ptr<BonusList>> cachedRequests;
+
+	void getAllBonusesRec(boost::shared_ptr<BonusList> out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = NULL, const bool caching = false) const;
+
 public:
 	BonusList bonuses; //wielded bonuses (local or up-propagated here)
 	BonusList exportedBonuses; //bonuses coming from this node (wielded or propagated away)
@@ -432,11 +458,11 @@ public:
 	ui8 nodeType;
 	std::string description;
 
-	CBonusSystemNode();
+	explicit CBonusSystemNode();
 	virtual ~CBonusSystemNode();
-
-	void getAllBonuses(BonusList &out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = NULL) const;
-
+	
+	const boost::shared_ptr<BonusList> getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = NULL) const;
+	void setCachingStr(const std::string &request) const;
 	void getParents(TCNodes &out) const;  //retrieves list of parent nodes (nodes to inherit bonuses from),
 	const Bonus *getBonus(const CSelector &selector) const;
 

+ 8 - 6
server/CGameHandler.cpp

@@ -4265,7 +4265,8 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
 	if( attacker->hasBonusOfType(Bonus::SPELL_AFTER_ATTACK) )
 	{
 		std::set<ui32> spellsToCast;
-		BOOST_FOREACH(const Bonus *sf, attacker->getBonuses(Selector::type(Bonus::SPELL_AFTER_ATTACK)))
+		boost::shared_ptr<BonusList> spells = attacker->getBonuses(Selector::type(Bonus::SPELL_AFTER_ATTACK));
+		BOOST_FOREACH(const Bonus *sf, *spells)
 		{
 			spellsToCast.insert (sf->subtype);
 		}
@@ -4285,7 +4286,8 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
 			if(oneOfAttacked == NULL) //all attacked creatures have been killed
 				return;
 			int spellLevel = 0;
-			BOOST_FOREACH(const Bonus *sf, attacker->getBonuses(Selector::typeSubtype(Bonus::SPELL_AFTER_ATTACK, spellID)))
+			boost::shared_ptr<BonusList> spellsByType = attacker->getBonuses(Selector::typeSubtype(Bonus::SPELL_AFTER_ATTACK, spellID));
+			BOOST_FOREACH(const Bonus *sf, *spellsByType)
 			{
 				amax(spellLevel, sf->additionalInfo % 1000); //pick highest level
 				meleeRanged = sf->additionalInfo / 1000;
@@ -4333,7 +4335,8 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
 		}
 	}
 	int acidDamage = 0;
-	BOOST_FOREACH(const Bonus *b, attacker->getBonuses(Selector::type(Bonus::ACID_BREATH)))
+	boost::shared_ptr<BonusList> acidBreath = attacker->getBonuses(Selector::type(Bonus::ACID_BREATH));
+	BOOST_FOREACH(const Bonus *b, *acidBreath)
 	{
 		if (b->additionalInfo > rand()%100)
 			acidDamage += b->val;
@@ -4815,9 +4818,8 @@ void CGameHandler::runBattle()
 	{
 		if(gs->curB->heroes[i] && gs->curB->heroes[i]->hasBonusOfType(Bonus::OPENING_BATTLE_SPELL))
 		{
-			BonusList bl;
-			gs->curB->heroes[i]->getBonuses(bl, Selector::type(Bonus::OPENING_BATTLE_SPELL));
-			BOOST_FOREACH (Bonus *b, bl)
+			boost::shared_ptr<BonusList> bl = gs->curB->heroes[i]->getBonuses(Selector::type(Bonus::OPENING_BATTLE_SPELL));
+			BOOST_FOREACH (Bonus *b, *bl)
 			{
 				handleSpellCasting(b->subtype, 3, -1, 0, gs->curB->heroes[i]->tempOwner, NULL, gs->curB->heroes[1-i], b->val, SpellCasting::HERO_CASTING, NULL);
 			}