Răsfoiți Sursa

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 ani în urmă
părinte
comite
c6db92c0be

+ 2 - 2
client/CCastleInterface.cpp

@@ -1319,7 +1319,7 @@ void CCastleInterface::keyPressed( const SDL_KeyboardEvent & key )
 		}
 		break;
 	case SDLK_SPACE:
-		if(town->visitingHero && town->garrisonHero)
+		if(!!town->visitingHero && town->garrisonHero)
 		{
 			LOCPLINT->cb->swapGarrisonHero(town);
 		}
@@ -1331,7 +1331,7 @@ void CCastleInterface::keyPressed( const SDL_KeyboardEvent & key )
 
 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);
 	}

+ 1 - 1
client/NetPacksClient.cpp

@@ -84,7 +84,7 @@ void SetSecSkill::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));
 	}

+ 14 - 3
lib/CArtHandler.cpp

@@ -699,7 +699,7 @@ void CArtHandler::addBonuses()
 
 	//Angelic Alliance
 	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
 	giveArtBonus(130, Bonus::IMPROVED_NECROMANCY, 0);
@@ -894,8 +894,8 @@ std::string CArtifactInstance::nodeName() const
 
 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);
 	return ret;
 }
@@ -1021,6 +1021,11 @@ CArtifactInstance * CArtifactInstance::createNewArtifactInstance(int aid)
 	return createNewArtifactInstance(VLC->arth->artifacts[aid]);
 }
 
+void CArtifactInstance::deserializationFix()
+{
+	setType(artType);
+}
+
 bool CCombinedArtifactInstance::canBePutAt(const ArtifactLocation &al, bool assumeDestRemoved /*= false*/) const
 {
 	bool canMainArtifactBePlaced = CArtifactInstance::canBePutAt(al, assumeDestRemoved);
@@ -1162,6 +1167,12 @@ CArtifactInstance * CCombinedArtifactInstance::figureMainConstituent(ui16 slot)
 	return mainConstituent;
 }
 
+void CCombinedArtifactInstance::deserializationFix()
+{
+	BOOST_FOREACH(ConstituentInfo &ci, constituentsInfo)
+		attachTo(ci.art);
+}
+
 CCombinedArtifactInstance::ConstituentInfo::ConstituentInfo(CArtifactInstance *Art /*= NULL*/, ui16 Slot /*= -1*/)
 {
 	art = Art;

+ 3 - 0
lib/CArtHandler.h

@@ -74,6 +74,7 @@ public:
 	//CArtifactInstance(int aid);
 
 	std::string nodeName() const OVERRIDE;
+	void deserializationFix();
 	void setType(CArtifact *Art);
 
 	int firstAvailableSlot(const CGHeroInstance *h) const;
@@ -127,6 +128,8 @@ public:
 
 	CCombinedArtifactInstance();
 
+	void deserializationFix();
+
 	friend class CArtifactInstance;
 	friend class AssembledArtifact;
 	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;
 	}
 
-	if(vstd::contains(stacks, slot)) //remove old creature
+	if(hasStackAtSlot(slot)) //remove old creature
 		eraseStack(slot);
 
 	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];
 
-	if(!vstd::contains(stacks, slot))
+	if(!hasStackAtSlot(slot))
 	{
 		setCreature(slot, cre, count);
 	}
@@ -150,7 +150,7 @@ void CCreatureSet::addToSlot(TSlot slot, CStackInstance *stack, bool allowMergin
 {
 	assert(stack->valid(true));
 
-	if(!vstd::contains(stacks, slot))
+	if(!hasStackAtSlot(slot))
 	{
 		putStack(slot, stack);
 	}
@@ -176,7 +176,7 @@ bool CCreatureSet::validTypes(bool allowUnrandomized /*= false*/) const
 
 bool CCreatureSet::slotEmpty(TSlot slot) const
 {
-	return !vstd::contains(stacks, slot);
+	return !hasStackAtSlot(slot);
 }
 
 bool CCreatureSet::needsLastStack() const
@@ -213,9 +213,10 @@ void CCreatureSet::setFormation(bool tight)
 
 void CCreatureSet::setStackCount(TSlot slot, TQuantity count)
 {
-	assert(vstd::contains(stacks, slot));
+	assert(hasStackAtSlot(slot));
 	assert(count > 0);
 	stacks[slot]->count = count;
+	armyChanged();
 }
 
 void CCreatureSet::clear()
@@ -228,15 +229,15 @@ void CCreatureSet::clear()
 
 const CStackInstance& CCreatureSet::getStack(TSlot slot) const
 {
-	assert(vstd::contains(stacks, slot));
+	assert(hasStackAtSlot(slot));
 	return *stacks.find(slot)->second;
 }
 
 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
@@ -270,9 +271,10 @@ CArmedInstance * CCreatureSet::castToArmyObj()
 
 void CCreatureSet::putStack(TSlot slot, CStackInstance *stack)
 {
-	assert(!vstd::contains(stacks, slot));
+	assert(!hasStackAtSlot(slot));
 	stacks[slot] = stack;
 	stack->setArmyObj(castToArmyObj());
+	armyChanged();
 }
 
 void CCreatureSet::joinStack(TSlot slot, CStackInstance * stack)
@@ -323,7 +325,7 @@ void CCreatureSet::setToArmy(CSimpleArmy &src)
 
 CStackInstance * CCreatureSet::detachStack(TSlot slot)
 {
-	assert(vstd::contains(stacks, slot));
+	assert(hasStackAtSlot(slot));
 	CStackInstance *ret = stacks[slot];
 
 	if(CArmedInstance *armedObj = castToArmyObj())
@@ -332,16 +334,17 @@ CStackInstance * CCreatureSet::detachStack(TSlot slot)
 	}
 
 	assert(!ret->armyObj); //we failed detaching?
-
 	stacks.erase(slot);
+	armyChanged();
 	return ret;
 }
 
 void CCreatureSet::setStackType(TSlot slot, const CCreature *type)
 {
-	assert(vstd::contains(stacks, slot));
+	assert(hasStackAtSlot(slot));
 	CStackInstance *s = stacks[slot];
 	s->setType(type->idNumber);
+	armyChanged();
 }
 
 bool CCreatureSet::canBeMergedWith(const CCreatureSet &cs, bool allowMergingStacks) const
@@ -383,6 +386,10 @@ CCreatureSet & CCreatureSet::operator=(const CCreatureSet&cs)
 	return *this;
 }
 
+void CCreatureSet::armyChanged()
+{
+}
+
 CStackInstance::CStackInstance()
 	: armyObj(_armyObj)
 {
@@ -494,6 +501,12 @@ std::string CStackInstance::nodeName() const
 	return oss.str();
 }
 
+void CStackInstance::deserializationFix()
+{
+	setType(type);
+	setArmyObj(armyObj);
+}
+
 CStackBasicDescriptor::CStackBasicDescriptor()
 {
 	type = NULL;

+ 5 - 3
lib/CCreatureSet.h

@@ -59,6 +59,7 @@ public:
 	void setArmyObj(const CArmedInstance *ArmyObj);
 	bool valid(bool allowUnrandomized) const;
 	virtual std::string nodeName() const OVERRIDE;
+	void deserializationFix();
 };
 
 DLL_EXPORT std::ostream & operator<<(std::ostream & str, const CStackInstance & sth);
@@ -98,6 +99,7 @@ public:
 
 	CCreatureSet();
 	virtual ~CCreatureSet();
+	virtual void armyChanged();
 
 	const CStackInstance &operator[](TSlot slot) const; 
 
@@ -110,14 +112,14 @@ public:
 	CArmedInstance *castToArmyObj();
 
 	//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 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!
-	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);
 
 	//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!
 	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.

+ 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->localInit();
 			return;
@@ -1069,32 +1056,19 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
 	/*********give starting hero****************************************/
 	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)
 				scenarioOps->playerInfos[i].hero = h;
 
 			CGHeroInstance * nnn =  static_cast<CGHeroInstance*>(createObject(HEROI_TYPE,h,hpos,i));
 			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();
 			map->heroes.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)
 	{
 		if(k->first==-1 || k->first==255)
 			continue;
 
 		//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
 				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
 					{
 						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)
@@ -2882,8 +2854,46 @@ bmap<ui32, ConstTransitivePtr<CGHeroInstance> > CGameState::unusedHeroesFromPool
 	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

+ 1 - 1
lib/CGameState.h

@@ -321,7 +321,6 @@ public:
 	const TeamState *getTeam(ui8 teamID) const;
 	const TeamState *getPlayerTeam(ui8 color) const;
 	void init(StartInfo * si, ui32 checksum, int Seed);
-	void buildGameLogicTree();
 	void loadTownDInfos();
 	void randomizeObject(CGObjectInstance *cur);
 	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
 	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(const CGObjectInstance *obj, int player);

+ 139 - 153
lib/CObjectHandler.cpp

@@ -271,34 +271,6 @@ CGObjectInstance::~CGObjectInstance()
 	//	delete state;
 	//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
 {
@@ -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
 	CArtifactInstance *ai = CArtifactInstance::createNewArtifactInstance(artifact);
 	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
@@ -1477,21 +1433,6 @@ int CGHeroInstance::getSpellCost(const CSpell *sp) const
 	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)
 {
 	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);
 }
 
+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()
 {
 	switch(ID)
@@ -2008,9 +1958,7 @@ CGTownInstance::CGTownInstance()
 {
 	builded=-1;
 	destroyed=-1;
-	garrisonHero=NULL;
 	town=NULL;
-	visitingHero = NULL;
 }
 
 CGTownInstance::~CGTownInstance()
@@ -2137,10 +2085,8 @@ void CGTownInstance::initObj()
 			break;
 	}
 	//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
@@ -2241,27 +2187,6 @@ void CGTownInstance::fightOver( const CGHeroInstance *h, BattleResult *result )
 {
 	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());
 		cb->setOwner (id, h->tempOwner); //give control after checkout is done
 		FoWChange fw;
@@ -2269,8 +2194,6 @@ void CGTownInstance::fightOver( const CGHeroInstance *h, BattleResult *result )
 		fw.mode = 1;
 		getSightTiles (fw.tiles); //update visibility for castle structures
 		cb->sendAndApply (&fw);
-
-
 	}
 }
 
@@ -2305,33 +2228,6 @@ int CGTownInstance::getBoatType() const
 		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
 {
 	if(!vstd::contains(builtBuildings, 14)) 
@@ -2390,6 +2286,83 @@ std::vector<int> CGTownInstance::availableItemsIds(EMarketMode mode) const
 		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
 {
 	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
@@ -6630,6 +6566,56 @@ int CArmedInstance::valOfGlobalBonuses(CSelector selector) const
 	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
 {
 	switch(mode)

+ 17 - 2
lib/CObjectHandler.h

@@ -227,6 +227,9 @@ public:
 
 	CCreatureSet& getArmy() const;
 	void randomizeArmy(int type);
+	void updateMoraleBonusFromArmy();
+
+	void armyChanged() OVERRIDE;
 
 	//////////////////////////////////////////////////////////////////////////
 	//void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const;
@@ -432,6 +435,7 @@ public:
 
 
 	virtual std::string nodeName() const OVERRIDE;
+	void deserializationFix();
 	void setPropertyDer(ui8 what, ui32 val);//synchr
 	void initObj();
 	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
 {
 public:
+	CTownAndVisitingHero townAndVis;
 	CTown * town;
 	std::string name; // name of town
 	si32 builded; //how many buildings has been built 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)
 	si32 alignment;
 	std::set<si32> forbiddenBuildings, builtBuildings;
@@ -586,10 +595,16 @@ public:
 		for (std::vector<CGTownBuilding*>::iterator i = bonusingBuildings.begin(); i!=bonusingBuildings.end(); i++)
 			(*i)->town = this;
 
-		h & town;
+		h & town & townAndVis;
 		//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 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__

+ 4 - 0
lib/ConstTransitivePtr.h

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

+ 7 - 0
lib/HeroBonus.cpp

@@ -315,6 +315,8 @@ CBonusSystemNode::~CBonusSystemNode()
 	if(children.size())
 	{
 		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();
 }
 
+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*/)
 {
 	if(obj)

+ 1 - 0
lib/HeroBonus.h

@@ -434,6 +434,7 @@ public:
 
 	void popBonuses(const CSelector &s);
 	virtual std::string nodeName() const;
+	void deserializationFix();
 
 	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);
 	DLL_EXPORT void applyGs(CGameState *gs);
 
-	ui8 flags; //1 - start, 2 - garrison
+	ui8 flags; //1 - start
 	ui32 tid, hid;
 
 	bool start() //if hero is entering castle (if false - leaving)
 	{
 		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)
 	{
 		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);
 	CGTownInstance *t = gs->getTown(tid);
+
 	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
-	{
-		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 )
@@ -379,24 +354,6 @@ void TryMoveHero::applyGs( CGameState *gs )
 		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 )
 {
 	CGTownInstance *t = gs->getTown(tid);
@@ -405,6 +362,7 @@ DLL_EXPORT void NewStructures::applyGs( CGameState *gs )
 		t->builtBuildings.insert(id);
 	}
 	t->builded = builded;
+	t->recreateBuildingsBonuses();
 }
 DLL_EXPORT void RazeStructures::applyGs( CGameState *gs )
 {
@@ -414,7 +372,9 @@ DLL_EXPORT void RazeStructures::applyGs( CGameState *gs )
 		t->builtBuildings.erase(id);
 	}
 	t->destroyed = destroyed; //yeaha
+	t->recreateBuildingsBonuses();
 }
+
 DLL_EXPORT void SetAvailableCreatures::applyGs( CGameState *gs )
 {
 	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), 
 		*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)
 	{
-		v->visitedTown = t;
-		v->inTownGarrison = false;
 		gs->map->addBlockVisTiles(v);
 	}
 	if(g)
 	{
-		g->visitedTown = t;
-		g->inTownGarrison = true;
 		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 )
 {
 	assert(vstd::contains(gs->hpool.heroesPool, hid));
 	CGHeroInstance *h = gs->hpool.heroesPool[hid];
 	CGTownInstance *t = gs->getTown(tid);
+	PlayerState *p = gs->getPlayer(player);
 	h->setOwner(player);
 	h->pos = tile;
 	h->movement =  h->maxMovePoints(true);
@@ -509,16 +432,15 @@ DLL_EXPORT void HeroRecruited::applyGs( CGameState *gs )
 
 	h->initHeroDefInfo();
 	gs->map->heroes.push_back(h);
-	gs->getPlayer(h->getOwner())->heroes.push_back(h);
+	p->heroes.push_back(h);
+	h->attachTo(p);
 	h->initObj();
 	gs->map->addBlockVisTiles(h);
 
 	if(t)
 	{
-		t->visitingHero = h;
-		h->visitedTown = t;
+		t->setVisitingHero(h);
 	}
-	h->inTownGarrison = false;
 }
 
 DLL_EXPORT void GiveHero::applyGs( CGameState *gs )
@@ -702,7 +624,6 @@ DLL_EXPORT void PutArtifact::applyGs( CGameState *gs )
 {
 	assert(art->canBePutAt(al));
 	al.hero->putArtifact(al.slot, art);
-	//art->putAt(al.hero, al.slot);
 }
 
 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);
 	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 )
 	{
@@ -2204,14 +2205,14 @@ bool CGameHandler::razeStructure (si32 tid, si32 bid)
 	rs.destroyed = t->destroyed + 1;
 	sendAndApply(&rs);
 //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;
 }
 
@@ -2371,7 +2372,7 @@ bool CGameHandler::changeStackType(const StackLocation &sl, CCreature *c)
 void CGameHandler::moveArmy(const CArmedInstance *src, const CArmedInstance *dst, bool 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
 		StackLocation sl(src, i->first); //location of stack to move
@@ -2437,7 +2438,7 @@ bool CGameHandler::garrisonSwap( si32 tid )
 		sendAndApply(&intown);
 		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;
 		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;
 	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
-		if(sp->positiveness >= 0 && (*it)->owner == caster->tempOwner)
+		if(sp->positiveness >= 0 && (*it)->owner == casterSideOwner)
 			continue;
 
 		const CGHeroInstance * bonusHero; //hero we should take bonuses from
-		if(caster && (*it)->owner == caster->tempOwner)
+		if((*it)->owner == casterSideOwner)
 			bonusHero = caster;
 		else
 			bonusHero = hero2;
@@ -3582,7 +3583,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, int destinatio
 	}
 
 	//checking if creatures resist
-	sc.resisted = calculateResistedStacks(spell, caster, secHero, attackedCres);
+	sc.resisted = calculateResistedStacks(spell, caster, secHero, attackedCres, casterColor);
 
 	//calculating dmg to display
 	for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)