Browse Source

More development around bonus system: building hierarchy, managing morale bonuses. Fully functional Spell Scroll and Angelic Alliance artifacts. Fixed recruiting hero and moving hero to garrison.

Michał W. Urbańczyk 14 years ago
parent
commit
c6db92c0be

+ 2 - 2
client/CCastleInterface.cpp

@@ -1319,7 +1319,7 @@ void CCastleInterface::keyPressed( const SDL_KeyboardEvent & key )
 		}
 		}
 		break;
 		break;
 	case SDLK_SPACE:
 	case SDLK_SPACE:
-		if(town->visitingHero && town->garrisonHero)
+		if(!!town->visitingHero && town->garrisonHero)
 		{
 		{
 			LOCPLINT->cb->swapGarrisonHero(town);
 			LOCPLINT->cb->swapGarrisonHero(town);
 		}
 		}
@@ -1331,7 +1331,7 @@ void CCastleInterface::keyPressed( const SDL_KeyboardEvent & key )
 
 
 void CCastleInterface::splitClicked()
 void CCastleInterface::splitClicked()
 {
 {
-	if(town->visitingHero && town->garrisonHero && (hslotdown.highlight || hslotup.highlight))
+	if(!!town->visitingHero && town->garrisonHero && (hslotdown.highlight || hslotup.highlight))
 	{
 	{
 		LOCPLINT->heroExchangeStarted(town->visitingHero->id, town->garrisonHero->id);
 		LOCPLINT->heroExchangeStarted(town->visitingHero->id, town->garrisonHero->id);
 	}
 	}

+ 1 - 1
client/NetPacksClient.cpp

@@ -84,7 +84,7 @@ void SetSecSkill::applyCl( CClient *cl )
 
 
 void HeroVisitCastle::applyCl( CClient *cl )
 void HeroVisitCastle::applyCl( CClient *cl )
 {
 {
-	if(start() && !garrison() && vstd::contains(cl->playerint,GS(cl)->getHero(hid)->tempOwner))
+	if(start() && vstd::contains(cl->playerint,GS(cl)->getHero(hid)->tempOwner))
 	{
 	{
 		cl->playerint[GS(cl)->getHero(hid)->tempOwner]->heroVisitsTown(GS(cl)->getHero(hid),GS(cl)->getTown(tid));
 		cl->playerint[GS(cl)->getHero(hid)->tempOwner]->heroVisitsTown(GS(cl)->getHero(hid),GS(cl)->getTown(tid));
 	}
 	}

+ 14 - 3
lib/CArtHandler.cpp

@@ -699,7 +699,7 @@ void CArtHandler::addBonuses()
 
 
 	//Angelic Alliance
 	//Angelic Alliance
 	giveArtBonus(129, Bonus::NONEVIL_ALIGNMENT_MIX, 0);
 	giveArtBonus(129, Bonus::NONEVIL_ALIGNMENT_MIX, 0);
-	giveArtBonus(129, Bonus::OPENING_BATTLE_SPELL, 10, 29); // Prayer
+	giveArtBonus(129, Bonus::OPENING_BATTLE_SPELL, 10, 48); // Prayer
 
 
 	//Cloak of the Undead King
 	//Cloak of the Undead King
 	giveArtBonus(130, Bonus::IMPROVED_NECROMANCY, 0);
 	giveArtBonus(130, Bonus::IMPROVED_NECROMANCY, 0);
@@ -894,8 +894,8 @@ std::string CArtifactInstance::nodeName() const
 
 
 CArtifactInstance * CArtifactInstance::createScroll( const CSpell *s)
 CArtifactInstance * CArtifactInstance::createScroll( const CSpell *s)
 {
 {
-	CArtifactInstance *ret = new CArtifactInstance(VLC->arth->artifacts[93]);
-	Bonus *b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, s->id	);
+	CArtifactInstance *ret = new CArtifactInstance(VLC->arth->artifacts[1]);
+	Bonus *b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, 1, s->id);
 	ret->addNewBonus(b);
 	ret->addNewBonus(b);
 	return ret;
 	return ret;
 }
 }
@@ -1021,6 +1021,11 @@ CArtifactInstance * CArtifactInstance::createNewArtifactInstance(int aid)
 	return createNewArtifactInstance(VLC->arth->artifacts[aid]);
 	return createNewArtifactInstance(VLC->arth->artifacts[aid]);
 }
 }
 
 
+void CArtifactInstance::deserializationFix()
+{
+	setType(artType);
+}
+
 bool CCombinedArtifactInstance::canBePutAt(const ArtifactLocation &al, bool assumeDestRemoved /*= false*/) const
 bool CCombinedArtifactInstance::canBePutAt(const ArtifactLocation &al, bool assumeDestRemoved /*= false*/) const
 {
 {
 	bool canMainArtifactBePlaced = CArtifactInstance::canBePutAt(al, assumeDestRemoved);
 	bool canMainArtifactBePlaced = CArtifactInstance::canBePutAt(al, assumeDestRemoved);
@@ -1162,6 +1167,12 @@ CArtifactInstance * CCombinedArtifactInstance::figureMainConstituent(ui16 slot)
 	return mainConstituent;
 	return mainConstituent;
 }
 }
 
 
+void CCombinedArtifactInstance::deserializationFix()
+{
+	BOOST_FOREACH(ConstituentInfo &ci, constituentsInfo)
+		attachTo(ci.art);
+}
+
 CCombinedArtifactInstance::ConstituentInfo::ConstituentInfo(CArtifactInstance *Art /*= NULL*/, ui16 Slot /*= -1*/)
 CCombinedArtifactInstance::ConstituentInfo::ConstituentInfo(CArtifactInstance *Art /*= NULL*/, ui16 Slot /*= -1*/)
 {
 {
 	art = Art;
 	art = Art;

+ 3 - 0
lib/CArtHandler.h

@@ -74,6 +74,7 @@ public:
 	//CArtifactInstance(int aid);
 	//CArtifactInstance(int aid);
 
 
 	std::string nodeName() const OVERRIDE;
 	std::string nodeName() const OVERRIDE;
+	void deserializationFix();
 	void setType(CArtifact *Art);
 	void setType(CArtifact *Art);
 
 
 	int firstAvailableSlot(const CGHeroInstance *h) const;
 	int firstAvailableSlot(const CGHeroInstance *h) const;
@@ -127,6 +128,8 @@ public:
 
 
 	CCombinedArtifactInstance();
 	CCombinedArtifactInstance();
 
 
+	void deserializationFix();
+
 	friend class CArtifactInstance;
 	friend class CArtifactInstance;
 	friend class AssembledArtifact;
 	friend class AssembledArtifact;
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)

+ 26 - 13
lib/CCreatureSet.cpp

@@ -41,7 +41,7 @@ bool CCreatureSet::setCreature(TSlot slot, TCreature type, TQuantity quantity) /
 		return true;
 		return true;
 	}
 	}
 
 
-	if(vstd::contains(stacks, slot)) //remove old creature
+	if(hasStackAtSlot(slot)) //remove old creature
 		eraseStack(slot);
 		eraseStack(slot);
 
 
 	putStack(slot, new CStackInstance(type, quantity));
 	putStack(slot, new CStackInstance(type, quantity));
@@ -132,7 +132,7 @@ void CCreatureSet::addToSlot(TSlot slot, TCreature cre, TQuantity count, bool al
 {
 {
 	const CCreature *c = VLC->creh->creatures[cre];
 	const CCreature *c = VLC->creh->creatures[cre];
 
 
-	if(!vstd::contains(stacks, slot))
+	if(!hasStackAtSlot(slot))
 	{
 	{
 		setCreature(slot, cre, count);
 		setCreature(slot, cre, count);
 	}
 	}
@@ -150,7 +150,7 @@ void CCreatureSet::addToSlot(TSlot slot, CStackInstance *stack, bool allowMergin
 {
 {
 	assert(stack->valid(true));
 	assert(stack->valid(true));
 
 
-	if(!vstd::contains(stacks, slot))
+	if(!hasStackAtSlot(slot))
 	{
 	{
 		putStack(slot, stack);
 		putStack(slot, stack);
 	}
 	}
@@ -176,7 +176,7 @@ bool CCreatureSet::validTypes(bool allowUnrandomized /*= false*/) const
 
 
 bool CCreatureSet::slotEmpty(TSlot slot) const
 bool CCreatureSet::slotEmpty(TSlot slot) const
 {
 {
-	return !vstd::contains(stacks, slot);
+	return !hasStackAtSlot(slot);
 }
 }
 
 
 bool CCreatureSet::needsLastStack() const
 bool CCreatureSet::needsLastStack() const
@@ -213,9 +213,10 @@ void CCreatureSet::setFormation(bool tight)
 
 
 void CCreatureSet::setStackCount(TSlot slot, TQuantity count)
 void CCreatureSet::setStackCount(TSlot slot, TQuantity count)
 {
 {
-	assert(vstd::contains(stacks, slot));
+	assert(hasStackAtSlot(slot));
 	assert(count > 0);
 	assert(count > 0);
 	stacks[slot]->count = count;
 	stacks[slot]->count = count;
+	armyChanged();
 }
 }
 
 
 void CCreatureSet::clear()
 void CCreatureSet::clear()
@@ -228,15 +229,15 @@ void CCreatureSet::clear()
 
 
 const CStackInstance& CCreatureSet::getStack(TSlot slot) const
 const CStackInstance& CCreatureSet::getStack(TSlot slot) const
 {
 {
-	assert(vstd::contains(stacks, slot));
+	assert(hasStackAtSlot(slot));
 	return *stacks.find(slot)->second;
 	return *stacks.find(slot)->second;
 }
 }
 
 
 void CCreatureSet::eraseStack(TSlot slot)
 void CCreatureSet::eraseStack(TSlot slot)
 {
 {
-	assert(vstd::contains(stacks, slot));
-	delNull(stacks[slot]);
-	stacks.erase(slot);
+	assert(hasStackAtSlot(slot));
+	CStackInstance *toErase = detachStack(slot);
+	delNull(toErase);
 }
 }
 
 
 bool CCreatureSet::contains(const CStackInstance *stack) const
 bool CCreatureSet::contains(const CStackInstance *stack) const
@@ -270,9 +271,10 @@ CArmedInstance * CCreatureSet::castToArmyObj()
 
 
 void CCreatureSet::putStack(TSlot slot, CStackInstance *stack)
 void CCreatureSet::putStack(TSlot slot, CStackInstance *stack)
 {
 {
-	assert(!vstd::contains(stacks, slot));
+	assert(!hasStackAtSlot(slot));
 	stacks[slot] = stack;
 	stacks[slot] = stack;
 	stack->setArmyObj(castToArmyObj());
 	stack->setArmyObj(castToArmyObj());
+	armyChanged();
 }
 }
 
 
 void CCreatureSet::joinStack(TSlot slot, CStackInstance * stack)
 void CCreatureSet::joinStack(TSlot slot, CStackInstance * stack)
@@ -323,7 +325,7 @@ void CCreatureSet::setToArmy(CSimpleArmy &src)
 
 
 CStackInstance * CCreatureSet::detachStack(TSlot slot)
 CStackInstance * CCreatureSet::detachStack(TSlot slot)
 {
 {
-	assert(vstd::contains(stacks, slot));
+	assert(hasStackAtSlot(slot));
 	CStackInstance *ret = stacks[slot];
 	CStackInstance *ret = stacks[slot];
 
 
 	if(CArmedInstance *armedObj = castToArmyObj())
 	if(CArmedInstance *armedObj = castToArmyObj())
@@ -332,16 +334,17 @@ CStackInstance * CCreatureSet::detachStack(TSlot slot)
 	}
 	}
 
 
 	assert(!ret->armyObj); //we failed detaching?
 	assert(!ret->armyObj); //we failed detaching?
-
 	stacks.erase(slot);
 	stacks.erase(slot);
+	armyChanged();
 	return ret;
 	return ret;
 }
 }
 
 
 void CCreatureSet::setStackType(TSlot slot, const CCreature *type)
 void CCreatureSet::setStackType(TSlot slot, const CCreature *type)
 {
 {
-	assert(vstd::contains(stacks, slot));
+	assert(hasStackAtSlot(slot));
 	CStackInstance *s = stacks[slot];
 	CStackInstance *s = stacks[slot];
 	s->setType(type->idNumber);
 	s->setType(type->idNumber);
+	armyChanged();
 }
 }
 
 
 bool CCreatureSet::canBeMergedWith(const CCreatureSet &cs, bool allowMergingStacks) const
 bool CCreatureSet::canBeMergedWith(const CCreatureSet &cs, bool allowMergingStacks) const
@@ -383,6 +386,10 @@ CCreatureSet & CCreatureSet::operator=(const CCreatureSet&cs)
 	return *this;
 	return *this;
 }
 }
 
 
+void CCreatureSet::armyChanged()
+{
+}
+
 CStackInstance::CStackInstance()
 CStackInstance::CStackInstance()
 	: armyObj(_armyObj)
 	: armyObj(_armyObj)
 {
 {
@@ -494,6 +501,12 @@ std::string CStackInstance::nodeName() const
 	return oss.str();
 	return oss.str();
 }
 }
 
 
+void CStackInstance::deserializationFix()
+{
+	setType(type);
+	setArmyObj(armyObj);
+}
+
 CStackBasicDescriptor::CStackBasicDescriptor()
 CStackBasicDescriptor::CStackBasicDescriptor()
 {
 {
 	type = NULL;
 	type = NULL;

+ 5 - 3
lib/CCreatureSet.h

@@ -59,6 +59,7 @@ public:
 	void setArmyObj(const CArmedInstance *ArmyObj);
 	void setArmyObj(const CArmedInstance *ArmyObj);
 	bool valid(bool allowUnrandomized) const;
 	bool valid(bool allowUnrandomized) const;
 	virtual std::string nodeName() const OVERRIDE;
 	virtual std::string nodeName() const OVERRIDE;
+	void deserializationFix();
 };
 };
 
 
 DLL_EXPORT std::ostream & operator<<(std::ostream & str, const CStackInstance & sth);
 DLL_EXPORT std::ostream & operator<<(std::ostream & str, const CStackInstance & sth);
@@ -98,6 +99,7 @@ public:
 
 
 	CCreatureSet();
 	CCreatureSet();
 	virtual ~CCreatureSet();
 	virtual ~CCreatureSet();
+	virtual void armyChanged();
 
 
 	const CStackInstance &operator[](TSlot slot) const; 
 	const CStackInstance &operator[](TSlot slot) const; 
 
 
@@ -110,14 +112,14 @@ public:
 	CArmedInstance *castToArmyObj();
 	CArmedInstance *castToArmyObj();
 
 
 	//basic operations
 	//basic operations
-	void eraseStack(TSlot slot); //slot must be occupied
 	void putStack(TSlot slot, CStackInstance *stack); //adds new stack to the army, slot must be empty
 	void putStack(TSlot slot, CStackInstance *stack); //adds new stack to the army, slot must be empty
-	void joinStack(TSlot slot, CStackInstance * stack); //adds new stack to the existing stack of the same type
 	void setStackCount(TSlot slot, TQuantity count); //stack must exist!
 	void setStackCount(TSlot slot, TQuantity count); //stack must exist!
-	CStackInstance *detachStack(TSlot slot); //removes stack from army but doesn't destroy it (so it can be moved somewhere else)
+	CStackInstance *detachStack(TSlot slot); //removes stack from army but doesn't destroy it (so it can be moved somewhere else or safely deleted)
 	void setStackType(TSlot slot, const CCreature *type);
 	void setStackType(TSlot slot, const CCreature *type);
 
 
 	//derivative 
 	//derivative 
+	void eraseStack(TSlot slot); //slot must be occupied
+	void joinStack(TSlot slot, CStackInstance * stack); //adds new stack to the existing stack of the same type
 	void changeStackCount(TSlot slot, TQuantity toAdd); //stack must exist!
 	void changeStackCount(TSlot slot, TQuantity toAdd); //stack must exist!
 	bool setCreature (TSlot slot, TCreature type, TQuantity quantity) OVERRIDE; //replaces creature in stack; slots 0 to 6, if quantity=0 erases stack
 	bool setCreature (TSlot slot, TCreature type, TQuantity quantity) OVERRIDE; //replaces creature in stack; slots 0 to 6, if quantity=0 erases stack
 	void setToArmy(CSimpleArmy &src); //erases all our army and moves stacks from src to us; src MUST NOT be an armed object! WARNING: use it wisely. Or better do not use at all.
 	void setToArmy(CSimpleArmy &src); //erases all our army and moves stacks from src to us; src MUST NOT be an armed object! WARNING: use it wisely. Or better do not use at all.

+ 59 - 49
lib/CGameState.cpp

@@ -941,19 +941,6 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
 				}
 				}
 			}
 			}
 
 
-
-// 			h->setCreature(0, 110, 1);
-// 			h->setCreature(1, 69, 1);
-// 
-// 			CGHeroInstance *h = new CGHeroInstance();
-// 
-// 			CGCreature *c = new CGCreature();
-// 			c->setOwner(1);
-// 			c->putStack(0, new CStackInstance(69, 6));
-// 			c->putStack(1, new CStackInstance(11, 3));
-// 			c->subID = 34;
-// 			c->initObj();
-
 			curB = BattleInfo::setupBattle(int3(), dp.bfieldType, dp.terType, armies, heroes, false, town);
 			curB = BattleInfo::setupBattle(int3(), dp.bfieldType, dp.terType, armies, heroes, false, town);
 			curB->localInit();
 			curB->localInit();
 			return;
 			return;
@@ -1069,32 +1056,19 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
 	/*********give starting hero****************************************/
 	/*********give starting hero****************************************/
 	for(int i=0;i<PLAYER_LIMIT;i++)
 	for(int i=0;i<PLAYER_LIMIT;i++)
 	{
 	{
-		if((map->players[i].generateHeroAtMainTown && map->players[i].hasMainTown) ||  (map->players[i].hasMainTown && map->version==CMapHeader::RoE))
+		const PlayerInfo &p = map->players[i];
+		bool generateHero = (p.generateHeroAtMainTown && p.hasMainTown)  ||  (p.hasMainTown && map->version==CMapHeader::RoE);
+		if(generateHero && vstd::contains(scenarioOps->playerInfos, i))
 		{
 		{
-			int3 hpos = map->players[i].posOfMainTown;
-			hpos.x+=1;// hpos.y+=1;
-			if (scenarioOps->playerInfos.find(i) == scenarioOps->playerInfos.end())
-			{
-				continue;
-			}
+			int3 hpos = p.posOfMainTown;
+			hpos.x+=1;
 
 
-			int h=pickHero(i);
+			int h = pickHero(i);
 			if(scenarioOps->playerInfos[i].hero == -1)
 			if(scenarioOps->playerInfos[i].hero == -1)
 				scenarioOps->playerInfos[i].hero = h;
 				scenarioOps->playerInfos[i].hero = h;
 
 
 			CGHeroInstance * nnn =  static_cast<CGHeroInstance*>(createObject(HEROI_TYPE,h,hpos,i));
 			CGHeroInstance * nnn =  static_cast<CGHeroInstance*>(createObject(HEROI_TYPE,h,hpos,i));
 			nnn->id = map->objects.size();
 			nnn->id = map->objects.size();
-			hpos = map->players[i].posOfMainTown;hpos.x+=2;
-			for(unsigned int o=0;o<map->towns.size();o++) //find main town
-			{
-				if(map->towns[o]->pos == hpos)
-				{
-					map->towns[o]->visitingHero = nnn;
-					nnn->visitedTown = map->towns[o];
-					nnn->inTownGarrison = false;
-					break;
-				}
-			}
 			nnn->initHero();
 			nnn->initHero();
 			map->heroes.push_back(nnn);
 			map->heroes.push_back(nnn);
 			map->objects.push_back(nnn);
 			map->objects.push_back(nnn);
@@ -1533,24 +1507,31 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
 
 
 	}
 	}
 
 
+	objCaller->preInit();
+	BOOST_FOREACH(CGObjectInstance *obj, map->objects)
+	{
+		obj->initObj();
+		if(obj->ID == 62) //prison also needs to initialize hero
+			static_cast<CGHeroInstance*>(obj)->initHero();
+	}
+	CGTeleport::postInit(); //pairing subterranean gates
+
+	buildBonusSystemTree();
+
 	for(std::map<ui8, PlayerState>::iterator k=players.begin(); k!=players.end(); ++k)
 	for(std::map<ui8, PlayerState>::iterator k=players.begin(); k!=players.end(); ++k)
 	{
 	{
 		if(k->first==-1 || k->first==255)
 		if(k->first==-1 || k->first==255)
 			continue;
 			continue;
 
 
 		//init visiting and garrisoned heroes
 		//init visiting and garrisoned heroes
-		for(unsigned int l=0; l<k->second.heroes.size();l++)
+		BOOST_FOREACH(CGHeroInstance *h, k->second.heroes)
 		{ 
 		{ 
-			CGHeroInstance *h = k->second.heroes[l];
-			for(unsigned int m=0; m<k->second.towns.size();m++)
+			BOOST_FOREACH(CGTownInstance *t, k->second.towns)
 			{
 			{
-				CGTownInstance *t = k->second.towns[m];
 				int3 vistile = t->pos; vistile.x--; //tile next to the entrance
 				int3 vistile = t->pos; vistile.x--; //tile next to the entrance
 				if(vistile == h->pos || h->pos==t->pos)
 				if(vistile == h->pos || h->pos==t->pos)
 				{
 				{
-					t->visitingHero = h;
-					h->visitedTown = t;
-					h->inTownGarrison = false;
+					t->setVisitingHero(h);
 					if(h->pos == t->pos) //visiting hero placed in the editor has same pos as the town - we need to correct it
 					if(h->pos == t->pos) //visiting hero placed in the editor has same pos as the town - we need to correct it
 					{
 					{
 						map->removeBlockVisTiles(h);
 						map->removeBlockVisTiles(h);
@@ -1562,15 +1543,6 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
 			}
 			}
 		}
 		}
 	}
 	}
-
-	objCaller->preInit();
-	for(unsigned int i=0; i<map->objects.size(); i++)
-	{
-		map->objects[i]->initObj();
-		if(map->objects[i]->ID == 62) //prison also needs to initialize hero
-			static_cast<CGHeroInstance*>(map->objects[i].get())->initHero();
-	}
-	CGTeleport::postInit(); //pairing subterranean gates
 }
 }
 
 
 int CGameState::battleGetBattlefieldType(int3 tile)
 int CGameState::battleGetBattlefieldType(int3 tile)
@@ -2882,8 +2854,46 @@ bmap<ui32, ConstTransitivePtr<CGHeroInstance> > CGameState::unusedHeroesFromPool
 	return pool;
 	return pool;
 }
 }
 
 
-void CGameState::buildGameLogicTree()
+void CGameState::buildBonusSystemTree()
 {
 {
+	for(std::map<ui8, TeamState>::iterator k=teams.begin(); k!=teams.end(); ++k)
+	{
+		TeamState *t = &k->second;
+		t->attachTo(&globalEffects);
+
+		BOOST_FOREACH(ui8 teamMember, k->second.players)
+		{
+			PlayerState *p = getPlayer(teamMember);
+			assert(p);
+			p->attachTo(t);
+		}
+
+	}
+
+	BOOST_FOREACH(CGObjectInstance *obj, map->objects)
+	{
+		if(CArmedInstance *armed = dynamic_cast<CArmedInstance*>(obj))
+		{
+			CBonusSystemNode *whereToAttach = armed->tempOwner < PLAYER_LIMIT 
+				? getPlayer(armed->tempOwner)
+				: &globalEffects;
+
+			if(armed->ID == TOWNI_TYPE)
+			{
+				CGTownInstance *town = static_cast<CGTownInstance*>(armed);
+				town->townAndVis.attachTo(whereToAttach);
+			}
+			else
+				armed->attachTo(whereToAttach);
+		}
+	}
+
+	BOOST_FOREACH(CGTownInstance *t, map->towns)
+	{
+		t->deserializationFix();
+	}
+	// CStackInstance <-> CCreature, CStackInstance <-> CArmedInstance, CArtifactInstance <-> CArtifact 
+	// are provided on initializing / deserializing
 }
 }
 
 
 int3 CPath::startPos() const
 int3 CPath::startPos() const

+ 1 - 1
lib/CGameState.h

@@ -321,7 +321,6 @@ public:
 	const TeamState *getTeam(ui8 teamID) const;
 	const TeamState *getTeam(ui8 teamID) const;
 	const TeamState *getPlayerTeam(ui8 color) const;
 	const TeamState *getPlayerTeam(ui8 color) const;
 	void init(StartInfo * si, ui32 checksum, int Seed);
 	void init(StartInfo * si, ui32 checksum, int Seed);
-	void buildGameLogicTree();
 	void loadTownDInfos();
 	void loadTownDInfos();
 	void randomizeObject(CGObjectInstance *cur);
 	void randomizeObject(CGObjectInstance *cur);
 	std::pair<int,int> pickObject(CGObjectInstance *obj); //chooses type of object to be randomized, returns <type, subtype>
 	std::pair<int,int> pickObject(CGObjectInstance *obj); //chooses type of object to be randomized, returns <type, subtype>
@@ -350,6 +349,7 @@ public:
 	bmap<ui32, ConstTransitivePtr<CGHeroInstance> > unusedHeroesFromPool(); //heroes pool without heroes that are available in taverns
 	bmap<ui32, ConstTransitivePtr<CGHeroInstance> > unusedHeroesFromPool(); //heroes pool without heroes that are available in taverns
 	BattleInfo * setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town);
 	BattleInfo * setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town);
 
 
+	void buildBonusSystemTree();
 
 
 	bool isVisible(int3 pos, int player);
 	bool isVisible(int3 pos, int player);
 	bool isVisible(const CGObjectInstance *obj, int player);
 	bool isVisible(const CGObjectInstance *obj, int player);

+ 139 - 153
lib/CObjectHandler.cpp

@@ -271,34 +271,6 @@ CGObjectInstance::~CGObjectInstance()
 	//	delete state;
 	//	delete state;
 	//state=NULL;
 	//state=NULL;
 }
 }
-//CGObjectInstance::CGObjectInstance(const CGObjectInstance & right)
-//{
-//	pos = right.pos;
-//	ID = right.ID;
-//	subID = right.subID;
-//	id	= right.id;
-//	defInfo = right.defInfo;
-//	info = right.info;
-//	blockVisit = right.blockVisit;
-//	//state = new CLuaObjectScript(right.state->);
-//	//*state = *right.state;
-//	//state = right.state;
-//	tempOwner = right.tempOwner;
-//}
-//CGObjectInstance& CGObjectInstance::operator=(const CGObjectInstance & right)
-//{
-//	pos = right.pos;
-//	ID = right.ID;
-//	subID = right.subID;
-//	id	= right.id;
-//	defInfo = right.defInfo;
-//	info = right.info;
-//	blockVisit = right.blockVisit;
-//	//state = new CLuaObjectScript();
-//	//*state = *right.state;
-//	tempOwner = right.tempOwner;
-//	return *this;
-//}
 
 
 const std::string & CGObjectInstance::getHoverText() const
 const std::string & CGObjectInstance::getHoverText() const
 {
 {
@@ -1431,22 +1403,6 @@ void CGHeroInstance::giveArtifact (ui32 aid) //use only for fixed artifacts
 	CArtifact * const artifact = VLC->arth->artifacts[aid]; //pointer to constant object
 	CArtifact * const artifact = VLC->arth->artifacts[aid]; //pointer to constant object
 	CArtifactInstance *ai = CArtifactInstance::createNewArtifactInstance(artifact);
 	CArtifactInstance *ai = CArtifactInstance::createNewArtifactInstance(artifact);
 	ai->putAt(this, ai->firstAvailableSlot(this));
 	ai->putAt(this, ai->firstAvailableSlot(this));
-// 
-// 	if (artifact->isBig()) 
-// 	{
-// 		for (std::vector<ui16>::const_iterator it = artifact->possibleSlots.begin(); it != artifact->possibleSlots.end(); ++it) 
-// 		{
-// 			if (!vstd::contains(artifWorn, *it)) 
-// 			{
-// 				VLC->arth->equipArtifact(artifWorn, *it, artifact);
-// 				break;
-// 			}
-// 		}
-// 	} 
-// 	else 
-// 	{
-// 		artifacts.push_back(artifact);
-// 	}
 }
 }
 
 
 int CGHeroInstance::getBoatType() const
 int CGHeroInstance::getBoatType() const
@@ -1477,21 +1433,6 @@ int CGHeroInstance::getSpellCost(const CSpell *sp) const
 	return sp->costs[getSpellSchoolLevel(sp)];
 	return sp->costs[getSpellSchoolLevel(sp)];
 }
 }
 
 
-// void CGHeroInstance::getParents(TCNodes &out, const CBonusSystemNode *root /*= NULL*/) const
-// {
-// 	CArmedInstance::getParents(out, root);
-// 
-// 	if((root == this || contains(static_cast<const CStackInstance *>(root))) &&  visitedTown && !dynamic_cast<const PlayerState*>(root))
-// 	{
-// 			out.insert(visitedTown);
-// 	}
-// 
-// 	for (std::map<ui16,CArtifact*>::const_iterator i = artifWorn.begin(); i != artifWorn.end(); i++)
-// 		out.insert(i->second);
-// 
-// 	out.insert(&speciality);
-// }
-
 void CGHeroInstance::pushPrimSkill(int which, int val)
 void CGHeroInstance::pushPrimSkill(int which, int val)
 {
 {
 	addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::PRIMARY_SKILL, Bonus::HERO_BASE_SKILL, val, id, which));
 	addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::PRIMARY_SKILL, Bonus::HERO_BASE_SKILL, val, id, which));
@@ -1583,6 +1524,15 @@ bool CGHeroInstance::hasSpellbook() const
 	return getArt(Arts::SPELLBOOK);
 	return getArt(Arts::SPELLBOOK);
 }
 }
 
 
+void CGHeroInstance::deserializationFix()
+{
+	for(bmap<ui16, ArtSlotInfo>::iterator i = artifactsWorn.begin(); i != artifactsWorn.end(); i++)
+		if(i->second.artifact && !i->second.locked)
+			attachTo(i->second.artifact);
+
+	attachTo(&speciality);
+}
+
 void CGDwelling::initObj()
 void CGDwelling::initObj()
 {
 {
 	switch(ID)
 	switch(ID)
@@ -2008,9 +1958,7 @@ CGTownInstance::CGTownInstance()
 {
 {
 	builded=-1;
 	builded=-1;
 	destroyed=-1;
 	destroyed=-1;
-	garrisonHero=NULL;
 	town=NULL;
 	town=NULL;
-	visitingHero = NULL;
 }
 }
 
 
 CGTownInstance::~CGTownInstance()
 CGTownInstance::~CGTownInstance()
@@ -2137,10 +2085,8 @@ void CGTownInstance::initObj()
 			break;
 			break;
 	}
 	}
 	//add special bonuses from buildings
 	//add special bonuses from buildings
-	if(subID == 4 && vstd::contains(builtBuildings, 17))
-	{
-		addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::DARKNESS, Bonus::TOWN_STRUCTURE, 20, 17) );
-	}
+
+	recreateBuildingsBonuses();
 }
 }
 
 
 void CGTownInstance::newTurn() const
 void CGTownInstance::newTurn() const
@@ -2241,27 +2187,6 @@ void CGTownInstance::fightOver( const CGHeroInstance *h, BattleResult *result )
 {
 {
 	if(result->winner == 0)
 	if(result->winner == 0)
 	{
 	{
-		if (hasBonusOfType(Bonus::DARKNESS))
-		{
-			//TODO: Make some 'change owner' function for bonus, or bonuses independent of player
-			/*
-			RemoveBonus rb(RemoveBonus::PLAYER);
-			rb.whoID = getOwner();
-			rb.source = Bonus::TOWN_STRUCTURE;
-			rb.id = id;
-			cb->sendAndApply(&rb);
-
-			GiveBonus gb(GiveBonus::PLAYER);
-			gb.bonus.type = Bonus::DARKNESS;
-			gb.bonus.val = 20;
-			gb.id = h->tempOwner;
-			gb.bonus.duration = Bonus::PERMANENT;
-			gb.bonus.source = Bonus::TOWN_STRUCTURE;
-			gb.bonus.id = id;
-			cb->sendAndApply(&gb);
-			*/
-		}
-
 		removeCapitols (h->getOwner());
 		removeCapitols (h->getOwner());
 		cb->setOwner (id, h->tempOwner); //give control after checkout is done
 		cb->setOwner (id, h->tempOwner); //give control after checkout is done
 		FoWChange fw;
 		FoWChange fw;
@@ -2269,8 +2194,6 @@ void CGTownInstance::fightOver( const CGHeroInstance *h, BattleResult *result )
 		fw.mode = 1;
 		fw.mode = 1;
 		getSightTiles (fw.tiles); //update visibility for castle structures
 		getSightTiles (fw.tiles); //update visibility for castle structures
 		cb->sendAndApply (&fw);
 		cb->sendAndApply (&fw);
-
-
 	}
 	}
 }
 }
 
 
@@ -2305,33 +2228,6 @@ int CGTownInstance::getBoatType() const
 		return 2;
 		return 2;
 }
 }
 
 
-// void CGTownInstance::getParents(TCNodes &out, const CBonusSystemNode *root /*= NULL*/) const
-// {
-// 	CArmedInstance::getParents(out, root);
-// 	if(root == this  &&  visitingHero && visitingHero != root)
-// 		out.insert(visitingHero);
-// }
-
-// void CGTownInstance::getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root /*= NULL*/) const
-// {
-// 	CArmedInstance::getBonuses(out, selector, root);
-// 	//TODO eliminate by moving structures effects to bonus system
-// 
-// 	if(Selector::matchesType(selector, Bonus::LUCK))
-// 	{
-// 		if(subID == 1  &&  vstd::contains(builtBuildings,21)) //rampart, fountain of fortune
-// 			out.push_back(Bonus(Bonus::PERMANENT, Bonus::LUCK, Bonus::TOWN_STRUCTURE, +2, 21, VLC->generaltexth->buildings[1][21].first + " +2"));
-// 	}
-// 
-// 	if(Selector::matchesType(selector, Bonus::MORALE))
-// 	{
-// 		if(subID == 0  &&  vstd::contains(builtBuildings,22)) //castle, brotherhood of sword built
-// 			out.push_back(Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::TOWN_STRUCTURE, +2, 22, VLC->generaltexth->buildings[0][22].first + " +2"));
-// 		else if(vstd::contains(builtBuildings,5)) //tavern is built
-// 			out.push_back(Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::TOWN_STRUCTURE, +1, 5, VLC->generaltexth->buildings[0][5].first + " +1"));
-// 	}
-// }
-
 int CGTownInstance::getMarketEfficiency() const
 int CGTownInstance::getMarketEfficiency() const
 {
 {
 	if(!vstd::contains(builtBuildings, 14)) 
 	if(!vstd::contains(builtBuildings, 14)) 
@@ -2390,6 +2286,83 @@ std::vector<int> CGTownInstance::availableItemsIds(EMarketMode mode) const
 		return IMarket::availableItemsIds(mode);
 		return IMarket::availableItemsIds(mode);
 }
 }
 
 
+std::string CGTownInstance::nodeName() const
+{
+	return "Town (" + (town ? town->Name() : "unknown") + ") of " +  name;
+}
+
+void CGTownInstance::deserializationFix()
+{
+	attachTo(&townAndVis);
+	if(visitingHero)
+		visitingHero->attachTo(&townAndVis);
+	if(garrisonHero)
+		garrisonHero->attachTo(this);
+}
+
+void CGTownInstance::recreateBuildingsBonuses()
+{
+	bonuses.remove_if(Selector::sourceType(Bonus::TOWN_STRUCTURE)); //TODO memory leak
+
+	if(subID == 4 && vstd::contains(builtBuildings, 17))
+		addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::DARKNESS, Bonus::TOWN_STRUCTURE, 20, 17));
+
+	if(subID == 1  &&  vstd::contains(builtBuildings,21)) //rampart, fountain of fortune
+	 	addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::LUCK, Bonus::TOWN_STRUCTURE, +2, 21, VLC->generaltexth->buildings[1][21].first + " +2"));
+
+	if(subID == 0  &&  vstd::contains(builtBuildings,22)) //castle, brotherhood of sword built
+	 	addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::TOWN_STRUCTURE, +2, 22, VLC->generaltexth->buildings[0][22].first + " +2"));
+	else if(vstd::contains(builtBuildings,5)) //tavern is built
+	 	addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::TOWN_STRUCTURE, +1, 5, VLC->generaltexth->buildings[0][5].first + " +1"));
+}
+
+void CGTownInstance::setVisitingHero(CGHeroInstance *h)
+{
+	assert(!!visitingHero == !h);
+	if(h)
+	{
+		PlayerState *p = cb->gameState()->getPlayer(h->tempOwner);
+		assert(p);
+		h->detachFrom(p);
+		h->attachTo(&townAndVis);
+		visitingHero = h;
+		h->visitedTown = this;
+		h->inTownGarrison = false;
+	}
+	else
+	{
+		PlayerState *p = cb->gameState()->getPlayer(visitingHero->tempOwner);
+		visitingHero->visitedTown = NULL;
+		visitingHero->detachFrom(&townAndVis);
+		visitingHero->attachTo(p);
+		visitingHero = NULL;
+	}
+}
+
+void CGTownInstance::setGarrisonedHero(CGHeroInstance *h)
+{
+	assert(!!garrisonHero == !h);
+	if(h)
+	{
+		PlayerState *p = cb->gameState()->getPlayer(h->tempOwner);
+		assert(p);
+		h->detachFrom(p);
+		h->attachTo(this);
+		garrisonHero = h;
+		h->visitedTown = this;
+		h->inTownGarrison = true;
+	}
+	else
+	{
+		PlayerState *p = cb->gameState()->getPlayer(garrisonHero->tempOwner);
+		garrisonHero->visitedTown = NULL;
+		garrisonHero->inTownGarrison = false;
+		garrisonHero->detachFrom(this);
+		garrisonHero->attachTo(p);
+		garrisonHero = NULL;
+	}
+}
+
 void CGVisitableOPH::onHeroVisit( const CGHeroInstance * h ) const
 void CGVisitableOPH::onHeroVisit( const CGHeroInstance * h ) const
 {
 {
 	if(visitors.find(h->id)==visitors.end())
 	if(visitors.find(h->id)==visitors.end())
@@ -6584,44 +6557,7 @@ CArmedInstance::CArmedInstance()
 // 		}
 // 		}
 // 	}
 // 	}
 // 
 // 
-// 	if(Selector::matchesType(selector, Bonus::MORALE))
-// 	{
-// 		//number of alignments and presence of undead
-// 		if(contains(dynamic_cast<const CStackInstance*>(root)))
-// 		{
-// 			bool archangelInArmy = false;
-// 			bool canMix = hasBonusOfType(Bonus::NONEVIL_ALIGNMENT_MIX);
-// 			std::set<si8> factions;
-// 			for(TSlots::const_iterator i=Slots().begin(); i!=Slots().end(); i++)
-// 			{
-// 				// Take Angelic Alliance troop-mixing freedom of non-evil, non-Conflux units into account.
-// 				const si8 faction = i->second.type->faction;
-// 				if (canMix
-// 					&& ((faction >= 0 && faction <= 2) || faction == 6 || faction == 7))
-// 				{
-// 					factions.insert(0); // Insert a single faction of the affected group, Castle will do.
-// 				}
-// 				else
-// 				{
-// 					factions.insert(faction);
-// 				}
-// 
-// 				if(i->second.type->idNumber == 13)
-// 					archangelInArmy = true;
-// 			}
-// 
-// 			if(factions.size() == 1)
-// 				out.push_back(Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, +1, id, VLC->generaltexth->arraytxt[115]));//All troops of one alignment +1
-// 			else
-// 			{
-// 				int fcountModifier = 2-factions.size();
-// 				out.push_back(Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, fcountModifier, id, boost::str(boost::format(VLC->generaltexth->arraytxt[114]) % factions.size() % fcountModifier)));//Troops of %d alignments %d
-// 			}
-// 
-// 			if(vstd::contains(factions,4))
-// 				out.push_back(Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, -1, id, VLC->generaltexth->arraytxt[116]));//Undead in group -1
-// 		}
-// 	}
+
 // }
 // }
 
 
 int CArmedInstance::valOfGlobalBonuses(CSelector selector) const
 int CArmedInstance::valOfGlobalBonuses(CSelector selector) const
@@ -6630,6 +6566,56 @@ int CArmedInstance::valOfGlobalBonuses(CSelector selector) const
 	return cb->gameState()->players[tempOwner].valOfBonuses(selector);
 	return cb->gameState()->players[tempOwner].valOfBonuses(selector);
 }
 }
 
 
+void CArmedInstance::updateMoraleBonusFromArmy()
+{
+	if(!validTypes(false)) //object not randomized, don't bother
+		return;
+
+	Bonus *b = bonuses.getFirst(Selector::sourceType(Bonus::ARMY) && Selector::type(Bonus::MORALE));
+	if(!b)
+	{
+		b = new Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, 0, -1);
+		addNewBonus(b);
+	}
+
+	//number of alignments and presence of undead
+	bool canMix = hasBonusOfType(Bonus::NONEVIL_ALIGNMENT_MIX);
+	std::set<si8> factions;
+	for(TSlots::const_iterator i=Slots().begin(); i!=Slots().end(); i++)
+	{
+	 	// Take Angelic Alliance troop-mixing freedom of non-evil, non-Conflux units into account.
+	 	const si8 faction = i->second->type->faction;
+	 	if (canMix
+	 		&& ((faction >= 0 && faction <= 2) || faction == 6 || faction == 7))
+	 	{
+	 		factions.insert(0); // Insert a single faction of the affected group, Castle will do.
+	 	}
+	 	else
+	 	{
+	 		factions.insert(faction);
+	 	}
+	}
+	 
+	if(factions.size() == 1)
+	{
+		b->val = +1;
+		b->description = VLC->generaltexth->arraytxt[115]; //All troops of one alignment +1
+	}
+	else
+	{
+	 	b->val = 2-factions.size();
+		b->description = boost::str(boost::format(VLC->generaltexth->arraytxt[114]) % factions.size() % b->val); //Troops of %d alignments %d
+	}
+	 
+// 	if(vstd::contains(factions,4))
+// 	 	out.push_back(Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, -1, id, VLC->generaltexth->arraytxt[116]));//Undead in group -1
+}
+
+void CArmedInstance::armyChanged()
+{
+	updateMoraleBonusFromArmy();
+}
+
 bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode) const
 bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode) const
 {
 {
 	switch(mode)
 	switch(mode)

+ 17 - 2
lib/CObjectHandler.h

@@ -227,6 +227,9 @@ public:
 
 
 	CCreatureSet& getArmy() const;
 	CCreatureSet& getArmy() const;
 	void randomizeArmy(int type);
 	void randomizeArmy(int type);
+	void updateMoraleBonusFromArmy();
+
+	void armyChanged() OVERRIDE;
 
 
 	//////////////////////////////////////////////////////////////////////////
 	//////////////////////////////////////////////////////////////////////////
 	//void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const;
 	//void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const;
@@ -432,6 +435,7 @@ public:
 
 
 
 
 	virtual std::string nodeName() const OVERRIDE;
 	virtual std::string nodeName() const OVERRIDE;
+	void deserializationFix();
 	void setPropertyDer(ui8 what, ui32 val);//synchr
 	void setPropertyDer(ui8 what, ui32 val);//synchr
 	void initObj();
 	void initObj();
 	void onHeroVisit(const CGHeroInstance * h) const;
 	void onHeroVisit(const CGHeroInstance * h) const;
@@ -556,14 +560,19 @@ public:
 	}
 	}
 };
 };
 
 
+class DLL_EXPORT CTownAndVisitingHero : public CBonusSystemNode
+{
+};
+
 class DLL_EXPORT CGTownInstance : public CGDwelling, public IShipyard, public IMarket
 class DLL_EXPORT CGTownInstance : public CGDwelling, public IShipyard, public IMarket
 {
 {
 public:
 public:
+	CTownAndVisitingHero townAndVis;
 	CTown * town;
 	CTown * town;
 	std::string name; // name of town
 	std::string name; // name of town
 	si32 builded; //how many buildings has been built this turn
 	si32 builded; //how many buildings has been built this turn
 	si32 destroyed; //how many buildings has been destroyed this turn
 	si32 destroyed; //how many buildings has been destroyed this turn
-	const CGHeroInstance * garrisonHero, *visitingHero;
+	ConstTransitivePtr<CGHeroInstance> garrisonHero, visitingHero;
 	ui32 identifier; //special identifier from h3m (only > RoE maps)
 	ui32 identifier; //special identifier from h3m (only > RoE maps)
 	si32 alignment;
 	si32 alignment;
 	std::set<si32> forbiddenBuildings, builtBuildings;
 	std::set<si32> forbiddenBuildings, builtBuildings;
@@ -586,10 +595,16 @@ public:
 		for (std::vector<CGTownBuilding*>::iterator i = bonusingBuildings.begin(); i!=bonusingBuildings.end(); i++)
 		for (std::vector<CGTownBuilding*>::iterator i = bonusingBuildings.begin(); i!=bonusingBuildings.end(); i++)
 			(*i)->town = this;
 			(*i)->town = this;
 
 
-		h & town;
+		h & town & townAndVis;
 		//garrison/visiting hero pointers will be restored in the map serialization
 		//garrison/visiting hero pointers will be restored in the map serialization
 	}
 	}
 	//////////////////////////////////////////////////////////////////////////
 	//////////////////////////////////////////////////////////////////////////
+
+	std::string nodeName() const OVERRIDE;
+	void deserializationFix();
+	void recreateBuildingsBonuses();
+	void setVisitingHero(CGHeroInstance *h);
+	void setGarrisonedHero(CGHeroInstance *h);
 // 	void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const;
 // 	void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const;
 // 	void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
 // 	void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
 	//////////////////////////////////////////////////////////////////////////
 	//////////////////////////////////////////////////////////////////////////

+ 2 - 0
lib/Connection.h

@@ -944,4 +944,6 @@ public:
 
 
 };
 };
 
 
+#define BONUS_TREE_DESERIALIZATION_FIX if(!h.saving) deserializationFix();
+
 #endif // __CONNECTION_H__
 #endif // __CONNECTION_H__

+ 4 - 0
lib/ConstTransitivePtr.h

@@ -46,6 +46,10 @@ public:
 	{
 	{
 		return ptr;
 		return ptr;
 	}
 	}
+	const T*operator=(T *t)
+	{
+		return ptr = t;
+	}
 
 
 	void dellNull()
 	void dellNull()
 	{
 	{

+ 7 - 0
lib/HeroBonus.cpp

@@ -315,6 +315,8 @@ CBonusSystemNode::~CBonusSystemNode()
 	if(children.size())
 	if(children.size())
 	{
 	{
 		tlog2 << "Warning: an orphaned child!\n";
 		tlog2 << "Warning: an orphaned child!\n";
+		while(children.size())
+			children.front()->detachFrom(this);
 	}
 	}
 }
 }
 
 
@@ -440,6 +442,11 @@ std::string CBonusSystemNode::nodeName() const
 	return std::string("Bonus system node of type ") + typeid(*this).name();
 	return std::string("Bonus system node of type ") + typeid(*this).name();
 }
 }
 
 
+void CBonusSystemNode::deserializationFix()
+{
+	tlog2 << "Deserialization fix called on bare CBSN? Shouldn't be...\n";
+}
+
 int NBonus::valOf(const CBonusSystemNode *obj, Bonus::BonusType type, int subtype /*= -1*/)
 int NBonus::valOf(const CBonusSystemNode *obj, Bonus::BonusType type, int subtype /*= -1*/)
 {
 {
 	if(obj)
 	if(obj)

+ 1 - 0
lib/HeroBonus.h

@@ -434,6 +434,7 @@ public:
 
 
 	void popBonuses(const CSelector &s);
 	void popBonuses(const CSelector &s);
 	virtual std::string nodeName() const;
 	virtual std::string nodeName() const;
+	void deserializationFix();
 
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{

+ 5 - 5
lib/NetPacks.h

@@ -287,17 +287,17 @@ struct HeroVisitCastle : public CPackForClient //108
 	void applyCl(CClient *cl);
 	void applyCl(CClient *cl);
 	DLL_EXPORT void applyGs(CGameState *gs);
 	DLL_EXPORT void applyGs(CGameState *gs);
 
 
-	ui8 flags; //1 - start, 2 - garrison
+	ui8 flags; //1 - start
 	ui32 tid, hid;
 	ui32 tid, hid;
 
 
 	bool start() //if hero is entering castle (if false - leaving)
 	bool start() //if hero is entering castle (if false - leaving)
 	{
 	{
 		return flags & 1;
 		return flags & 1;
 	}
 	}
-	bool garrison() //if hero is entering/leaving garrison (if false - it's only visiting hero)
-	{
-		return flags & 2;
-	}
+// 	bool garrison() //if hero is entering/leaving garrison (if false - it's only visiting hero)
+// 	{
+// 		return flags & 2;
+// 	}
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
 		h & flags & tid & hid;
 		h & flags & tid & hid;

+ 22 - 101
lib/NetPacksLib.cpp

@@ -86,36 +86,11 @@ DLL_EXPORT void HeroVisitCastle::applyGs( CGameState *gs )
 {
 {
 	CGHeroInstance *h = gs->getHero(hid);
 	CGHeroInstance *h = gs->getHero(hid);
 	CGTownInstance *t = gs->getTown(tid);
 	CGTownInstance *t = gs->getTown(tid);
+
 	if(start())
 	if(start())
-	{
-		if(garrison())
-		{
-			t->garrisonHero = h;
-			h->visitedTown = t;
-			h->inTownGarrison = true;
-		}
-		else
-		{
-			t->visitingHero = h;
-			h->visitedTown = t;
-			h->inTownGarrison = false;
-		}
-	}
+		t->setVisitingHero(h);
 	else
 	else
-	{
-		if(garrison())
-		{
-			t->garrisonHero = NULL;
-			h->visitedTown = NULL;
-			h->inTownGarrison = false;
-		}
-		else
-		{
-			t->visitingHero = NULL;
-			h->visitedTown = NULL;
-			h->inTownGarrison = false;
-		}
-	}
+		t->setVisitingHero(NULL);
 }
 }
 
 
 DLL_EXPORT void ChangeSpells::applyGs( CGameState *gs )
 DLL_EXPORT void ChangeSpells::applyGs( CGameState *gs )
@@ -379,24 +354,6 @@ void TryMoveHero::applyGs( CGameState *gs )
 		gs->getPlayerTeam(h->getOwner())->fogOfWarMap[t.x][t.y][t.z] = 1;
 		gs->getPlayerTeam(h->getOwner())->fogOfWarMap[t.x][t.y][t.z] = 1;
 }
 }
 
 
-// DLL_EXPORT void SetGarrisons::applyGs( CGameState *gs )
-// {
-// 	for(std::map<ui32,CCreatureSet>::iterator i = garrs.begin(); i!=garrs.end(); i++)
-// 	{
-// 		CArmedInstance *ai = static_cast<CArmedInstance*>(gs->map->objects[i->first]);
-// 		ai->setToArmy(i->second);
-// 		if(ai->ID==TOWNI_TYPE && (static_cast<CGTownInstance*>(ai))->garrisonHero) //if there is a hero in garrison then we must update also his army
-// 			const_cast<CGHeroInstance*>((static_cast<CGTownInstance*>(ai))->garrisonHero)->setToArmy(i->second);
-// 		else if(ai->ID==HEROI_TYPE)
-// 		{
-// 			CGHeroInstance *h =  static_cast<CGHeroInstance*>(ai);
-// 			CGTownInstance *t = const_cast<CGTownInstance *>(h->visitedTown);
-// 			if(t && h->inTownGarrison)
-// 			t->setToArmy(i->second);
-// 		}
-// 	}
-// }
-
 DLL_EXPORT void NewStructures::applyGs( CGameState *gs )
 DLL_EXPORT void NewStructures::applyGs( CGameState *gs )
 {
 {
 	CGTownInstance *t = gs->getTown(tid);
 	CGTownInstance *t = gs->getTown(tid);
@@ -405,6 +362,7 @@ DLL_EXPORT void NewStructures::applyGs( CGameState *gs )
 		t->builtBuildings.insert(id);
 		t->builtBuildings.insert(id);
 	}
 	}
 	t->builded = builded;
 	t->builded = builded;
+	t->recreateBuildingsBonuses();
 }
 }
 DLL_EXPORT void RazeStructures::applyGs( CGameState *gs )
 DLL_EXPORT void RazeStructures::applyGs( CGameState *gs )
 {
 {
@@ -414,7 +372,9 @@ DLL_EXPORT void RazeStructures::applyGs( CGameState *gs )
 		t->builtBuildings.erase(id);
 		t->builtBuildings.erase(id);
 	}
 	}
 	t->destroyed = destroyed; //yeaha
 	t->destroyed = destroyed; //yeaha
+	t->recreateBuildingsBonuses();
 }
 }
+
 DLL_EXPORT void SetAvailableCreatures::applyGs( CGameState *gs )
 DLL_EXPORT void SetAvailableCreatures::applyGs( CGameState *gs )
 {
 {
 	CGDwelling *dw = dynamic_cast<CGDwelling*>(gs->map->objects[tid].get());
 	CGDwelling *dw = dynamic_cast<CGDwelling*>(gs->map->objects[tid].get());
@@ -429,71 +389,34 @@ DLL_EXPORT void SetHeroesInTown::applyGs( CGameState *gs )
 	CGHeroInstance *v  = gs->getHero(visiting), 
 	CGHeroInstance *v  = gs->getHero(visiting), 
 		*g = gs->getHero(garrison);
 		*g = gs->getHero(garrison);
 
 
-	t->visitingHero = v;
-	t->garrisonHero = g;
+	bool newVisitorComesFromGarrison = v && v == t->garrisonHero;
+	bool newGarrisonComesFromVisiting = g && g == t->visitingHero;
+
+	if(newVisitorComesFromGarrison)
+		t->setGarrisonedHero(NULL);
+	if(newGarrisonComesFromVisiting)
+		t->setVisitingHero(NULL);
+	if(!newGarrisonComesFromVisiting || v)
+		t->setVisitingHero(v);
+	if(!newVisitorComesFromGarrison || g)
+		t->setGarrisonedHero(g);
+
 	if(v)
 	if(v)
 	{
 	{
-		v->visitedTown = t;
-		v->inTownGarrison = false;
 		gs->map->addBlockVisTiles(v);
 		gs->map->addBlockVisTiles(v);
 	}
 	}
 	if(g)
 	if(g)
 	{
 	{
-		g->visitedTown = t;
-		g->inTownGarrison = true;
 		gs->map->removeBlockVisTiles(g);
 		gs->map->removeBlockVisTiles(g);
 	}
 	}
 }
 }
 
 
-// DLL_EXPORT void SetHeroArtifacts::applyGs( CGameState *gs )
-// {
-// 	CGHeroInstance *h = gs->getHero(hid);
-// 	for(std::map<ui16, const CArtifact*>::const_iterator i = h->artifWorn.begin(); i != h->artifWorn.end(); i++)
-// 		if(!vstd::contains(artifWorn,i->first)  ||  artifWorn[i->first] != i->second)
-// 			unequiped.push_back(i->second);
-// 
-// 	for(std::map<ui16, const CArtifact*>::iterator i = artifWorn.begin(); i != artifWorn.end(); i++)
-// 		if(!vstd::contains(h->artifWorn,i->first)  ||  h->artifWorn[i->first] != i->second)
-// 		{
-// 			equiped.push_back(i->second);
-// 		}
-// 
-// 	//update hero data
-// 	h->artifacts = artifacts;
-// 	h->artifWorn = artifWorn;
-// }
-// 
-// DLL_EXPORT void SetHeroArtifacts::setArtAtPos(ui16 pos, const CArtifact* art)
-// {
-// 	if(!art)
-// 	{
-// 		if(pos<19)
-// 			VLC->arth->unequipArtifact(artifWorn, pos);
-// 		else if (pos - 19 < artifacts.size())
-// 			artifacts.erase(artifacts.begin() + (pos - 19));
-// 	}
-// 	else
-// 	{
-// 		if (pos < 19) 
-// 		{
-// 			VLC->arth->equipArtifact(artifWorn, pos, art);
-// 		} 
-// 		else // Goes into the backpack.
-// 		{ 
-// 			if(pos - 19 < artifacts.size())
-// 				artifacts.insert(artifacts.begin() + (pos - 19), art);
-// 			else
-// 				artifacts.push_back(art);
-// 		}
-// 	}
-// }
-
-
 DLL_EXPORT void HeroRecruited::applyGs( CGameState *gs )
 DLL_EXPORT void HeroRecruited::applyGs( CGameState *gs )
 {
 {
 	assert(vstd::contains(gs->hpool.heroesPool, hid));
 	assert(vstd::contains(gs->hpool.heroesPool, hid));
 	CGHeroInstance *h = gs->hpool.heroesPool[hid];
 	CGHeroInstance *h = gs->hpool.heroesPool[hid];
 	CGTownInstance *t = gs->getTown(tid);
 	CGTownInstance *t = gs->getTown(tid);
+	PlayerState *p = gs->getPlayer(player);
 	h->setOwner(player);
 	h->setOwner(player);
 	h->pos = tile;
 	h->pos = tile;
 	h->movement =  h->maxMovePoints(true);
 	h->movement =  h->maxMovePoints(true);
@@ -509,16 +432,15 @@ DLL_EXPORT void HeroRecruited::applyGs( CGameState *gs )
 
 
 	h->initHeroDefInfo();
 	h->initHeroDefInfo();
 	gs->map->heroes.push_back(h);
 	gs->map->heroes.push_back(h);
-	gs->getPlayer(h->getOwner())->heroes.push_back(h);
+	p->heroes.push_back(h);
+	h->attachTo(p);
 	h->initObj();
 	h->initObj();
 	gs->map->addBlockVisTiles(h);
 	gs->map->addBlockVisTiles(h);
 
 
 	if(t)
 	if(t)
 	{
 	{
-		t->visitingHero = h;
-		h->visitedTown = t;
+		t->setVisitingHero(h);
 	}
 	}
-	h->inTownGarrison = false;
 }
 }
 
 
 DLL_EXPORT void GiveHero::applyGs( CGameState *gs )
 DLL_EXPORT void GiveHero::applyGs( CGameState *gs )
@@ -702,7 +624,6 @@ DLL_EXPORT void PutArtifact::applyGs( CGameState *gs )
 {
 {
 	assert(art->canBePutAt(al));
 	assert(art->canBePutAt(al));
 	al.hero->putArtifact(al.slot, art);
 	al.hero->putArtifact(al.slot, art);
-	//art->putAt(al.hero, al.slot);
 }
 }
 
 
 DLL_EXPORT void EraseArtifact::applyGs( CGameState *gs )
 DLL_EXPORT void EraseArtifact::applyGs( CGameState *gs )

+ 23 - 22
server/CGameHandler.cpp

@@ -2142,14 +2142,15 @@ bool CGameHandler::buildStructure( si32 tid, si32 bid, bool force /*=false*/ )
 		ns.bid.insert(29);
 		ns.bid.insert(29);
 	else if (t->subID == 4 && bid == 17) //veil of darkness
 	else if (t->subID == 4 && bid == 17) //veil of darkness
 	{
 	{
-		GiveBonus gb(GiveBonus::TOWN);
-		gb.bonus.type = Bonus::DARKNESS;
-		gb.bonus.val = 20;
-		gb.id = t->id;
-		gb.bonus.duration = Bonus::PERMANENT;
-		gb.bonus.source = Bonus::TOWN_STRUCTURE;
-		gb.bonus.id = 17;
-		sendAndApply(&gb);
+		//handled via town->reacreateBonuses in apply
+// 		GiveBonus gb(GiveBonus::TOWN);
+// 		gb.bonus.type = Bonus::DARKNESS;
+// 		gb.bonus.val = 20;
+// 		gb.id = t->id;
+// 		gb.bonus.duration = Bonus::PERMANENT;
+// 		gb.bonus.source = Bonus::TOWN_STRUCTURE;
+// 		gb.bonus.id = 17;
+// 		sendAndApply(&gb);
 	}
 	}
 	else if ( t->subID == 5 && bid == 22 )
 	else if ( t->subID == 5 && bid == 22 )
 	{
 	{
@@ -2204,14 +2205,14 @@ bool CGameHandler::razeStructure (si32 tid, si32 bid)
 	rs.destroyed = t->destroyed + 1;
 	rs.destroyed = t->destroyed + 1;
 	sendAndApply(&rs);
 	sendAndApply(&rs);
 //TODO: Remove dwellers
 //TODO: Remove dwellers
-	if (t->subID == 4 && bid == 17) //Veil of Darkness
-	{
-		RemoveBonus rb(RemoveBonus::TOWN);
-		rb.whoID = t->id;
-		rb.source = Bonus::TOWN_STRUCTURE;
-		rb.id = 17;
-		sendAndApply(&rb);
-	}
+// 	if (t->subID == 4 && bid == 17) //Veil of Darkness
+// 	{
+// 		RemoveBonus rb(RemoveBonus::TOWN);
+// 		rb.whoID = t->id;
+// 		rb.source = Bonus::TOWN_STRUCTURE;
+// 		rb.id = 17;
+// 		sendAndApply(&rb);
+// 	}
 	return true;
 	return true;
 }
 }
 
 
@@ -2371,7 +2372,7 @@ bool CGameHandler::changeStackType(const StackLocation &sl, CCreature *c)
 void CGameHandler::moveArmy(const CArmedInstance *src, const CArmedInstance *dst, bool allowMerging) 
 void CGameHandler::moveArmy(const CArmedInstance *src, const CArmedInstance *dst, bool allowMerging) 
 {
 {
 	assert(src->canBeMergedWith(*dst, allowMerging));
 	assert(src->canBeMergedWith(*dst, allowMerging));
-	while(!src->stacksCount())//while there are unmoved creatures
+	while(src->stacksCount())//while there are unmoved creatures
 	{
 	{
 		TSlots::const_iterator i = src->Slots().begin(); //iterator to stack to move
 		TSlots::const_iterator i = src->Slots().begin(); //iterator to stack to move
 		StackLocation sl(src, i->first); //location of stack to move
 		StackLocation sl(src, i->first); //location of stack to move
@@ -2437,7 +2438,7 @@ bool CGameHandler::garrisonSwap( si32 tid )
 		sendAndApply(&intown);
 		sendAndApply(&intown);
 		return true;
 		return true;
 	}
 	}
-	else if (town->garrisonHero && town->visitingHero) //swap visiting and garrison hero
+	else if(!!town->garrisonHero && town->visitingHero) //swap visiting and garrison hero
 	{
 	{
 		SetHeroesInTown intown;
 		SetHeroesInTown intown;
 		intown.tid = tid;
 		intown.tid = tid;
@@ -3480,7 +3481,7 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message )
 	}
 	}
 }
 }
 
 
-static std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::set<CStack*> affectedCreatures)
+static std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::set<CStack*> affectedCreatures, int casterSideOwner)
 {
 {
 	std::vector<ui32> ret;
 	std::vector<ui32> ret;
 	for(std::set<CStack*>::const_iterator it = affectedCreatures.begin(); it != affectedCreatures.end(); ++it)
 	for(std::set<CStack*>::const_iterator it = affectedCreatures.begin(); it != affectedCreatures.end(); ++it)
@@ -3518,11 +3519,11 @@ static std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHero
 		
 		
 
 
 		//non-negative spells on friendly stacks should always succeed, unless immune
 		//non-negative spells on friendly stacks should always succeed, unless immune
-		if(sp->positiveness >= 0 && (*it)->owner == caster->tempOwner)
+		if(sp->positiveness >= 0 && (*it)->owner == casterSideOwner)
 			continue;
 			continue;
 
 
 		const CGHeroInstance * bonusHero; //hero we should take bonuses from
 		const CGHeroInstance * bonusHero; //hero we should take bonuses from
-		if(caster && (*it)->owner == caster->tempOwner)
+		if((*it)->owner == casterSideOwner)
 			bonusHero = caster;
 			bonusHero = caster;
 		else
 		else
 			bonusHero = hero2;
 			bonusHero = hero2;
@@ -3582,7 +3583,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, int destinatio
 	}
 	}
 
 
 	//checking if creatures resist
 	//checking if creatures resist
-	sc.resisted = calculateResistedStacks(spell, caster, secHero, attackedCres);
+	sc.resisted = calculateResistedStacks(spell, caster, secHero, attackedCres, casterColor);
 
 
 	//calculating dmg to display
 	//calculating dmg to display
 	for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
 	for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)