浏览代码

* new spells: resurrection, animate dead
* a lot of minor fixes in battles
* resolution & depth in settings.txt reverted to 800x600 and 24bpp

mateuszb 16 年之前
父节点
当前提交
507597301f

+ 4 - 4
AI/GeniusAI/BattleLogic.cpp

@@ -190,7 +190,7 @@ void CBattleLogic::MakeStatistics(int currentCreatureId)
 				m_statDistanceFromShooters.push_back(std::pair<int, int>(id, m_battleHelper.GetShortestDistance(currentStack->position, st->position)));
 				m_statDistanceFromShooters.push_back(std::pair<int, int>(id, m_battleHelper.GetShortestDistance(currentStack->position, st->position)));
 			}
 			}
 
 
-			if (currentStack->creature->isFlying() || (currentStack->creature->isShooting() && currentStack->shots > 0))
+			if (currentStack->hasFeatureOfType(StackFeature::FLYING) || (currentStack->creature->isShooting() && currentStack->shots > 0))
 			{
 			{
 				m_statDistance.push_back(std::pair<int, int>(id, m_battleHelper.GetShortestDistance(currentStack->position, st->position)));
 				m_statDistance.push_back(std::pair<int, int>(id, m_battleHelper.GetShortestDistance(currentStack->position, st->position)));
 			}
 			}
@@ -304,8 +304,8 @@ std::vector<int> CBattleLogic::GetAvailableHexesForAttacker(const CStack *defend
 {
 {
 	int x = m_battleHelper.DecodeXPosition(defender->position);
 	int x = m_battleHelper.DecodeXPosition(defender->position);
 	int y = m_battleHelper.DecodeYPosition(defender->position);
 	int y = m_battleHelper.DecodeYPosition(defender->position);
-	bool defenderIsDW = defender->creature->isDoubleWide();
-	bool attackerIsDW = attacker->creature->isDoubleWide();
+	bool defenderIsDW = defender->hasFeatureOfType(StackFeature::DOUBLE_WIDE);
+	bool attackerIsDW = attacker->hasFeatureOfType(StackFeature::DOUBLE_WIDE);
 	// TOTO: should be std::vector<int> but for debug purpose std::pair is used
 	// TOTO: should be std::vector<int> but for debug purpose std::pair is used
 	typedef std::pair<int, int> hexPoint;
 	typedef std::pair<int, int> hexPoint;
 	std::list<hexPoint> candidates;
 	std::list<hexPoint> candidates;
@@ -558,7 +558,7 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
 
 
 		// if double wide calculate tail
 		// if double wide calculate tail
 		int tail_pos = -1;
 		int tail_pos = -1;
-		if (attackerStack->creature->isDoubleWide())
+		if (attackerStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 		{
 		{
 			int x_pos = m_battleHelper.DecodeXPosition(attackerStack->position);
 			int x_pos = m_battleHelper.DecodeXPosition(attackerStack->position);
 			int y_pos = m_battleHelper.DecodeYPosition(attackerStack->position);
 			int y_pos = m_battleHelper.DecodeYPosition(attackerStack->position);

+ 6 - 6
CCallback.cpp

@@ -428,17 +428,17 @@ std::vector<CObstacleInstance> CCallback::battleGetAllObstacles()
 		return std::vector<CObstacleInstance>();
 		return std::vector<CObstacleInstance>();
 }
 }
 
 
-int CCallback::battleGetStack(int pos)
+int CCallback::battleGetStack(int pos, bool onlyAlive)
 {
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
-	return gs->battleGetStack(pos);
+	return gs->battleGetStack(pos, onlyAlive);
 }
 }
 
 
-const CStack* CCallback::battleGetStackByID(int ID)
+const CStack* CCallback::battleGetStackByID(int ID, bool onlyAlive)
 {
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
 	if(!gs->curB) return NULL;
 	if(!gs->curB) return NULL;
-	return gs->curB->getStack(ID);
+	return gs->curB->getStack(ID, onlyAlive);
 }
 }
 
 
 int CCallback::battleMakeAction(BattleAction* action)
 int CCallback::battleMakeAction(BattleAction* action)
@@ -448,10 +448,10 @@ int CCallback::battleMakeAction(BattleAction* action)
 	return 0;
 	return 0;
 }
 }
 
 
-const CStack* CCallback::battleGetStackByPos(int pos)
+const CStack* CCallback::battleGetStackByPos(int pos, bool onlyAlive)
 {
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
-	return battleGetStackByID(battleGetStack(pos));
+	return battleGetStackByID(battleGetStack(pos, onlyAlive), onlyAlive);
 }
 }
 
 
 int CCallback::battleGetPos(int stack)
 int CCallback::battleGetPos(int stack)

+ 6 - 6
CCallback.h

@@ -159,9 +159,9 @@ public:
 	virtual int battleGetBattlefieldType()=0; //   1. sand/shore   2. sand/mesas   3. dirt/birches   4. dirt/hills   5. dirt/pines   6. grass/hills   7. grass/pines   8. lava   9. magic plains   10. snow/mountains   11. snow/trees   12. subterranean   13. swamp/trees   14. fiery fields   15. rock lands   16. magic clouds   17. lucid pools   18. holy ground   19. clover field   20. evil fog   21. "favourable winds" text on magic plains background   22. cursed ground   23. rough   24. ship to ship   25. ship
 	virtual int battleGetBattlefieldType()=0; //   1. sand/shore   2. sand/mesas   3. dirt/birches   4. dirt/hills   5. dirt/pines   6. grass/hills   7. grass/pines   8. lava   9. magic plains   10. snow/mountains   11. snow/trees   12. subterranean   13. swamp/trees   14. fiery fields   15. rock lands   16. magic clouds   17. lucid pools   18. holy ground   19. clover field   20. evil fog   21. "favourable winds" text on magic plains background   22. cursed ground   23. rough   24. ship to ship   25. ship
 	virtual int battleGetObstaclesAtTile(int tile)=0; //returns bitfield
 	virtual int battleGetObstaclesAtTile(int tile)=0; //returns bitfield
 	virtual std::vector<CObstacleInstance> battleGetAllObstacles()=0; //returns all obstacles on the battlefield
 	virtual std::vector<CObstacleInstance> battleGetAllObstacles()=0; //returns all obstacles on the battlefield
-	virtual int battleGetStack(int pos)=0; //returns ID of stack on the tile
-	virtual const CStack * battleGetStackByID(int ID)=0; //returns stack info by given ID
-	virtual const CStack * battleGetStackByPos(int pos)=0; //returns stack info by given pos
+	virtual int battleGetStack(int pos, bool onlyAlive)=0; //returns ID of stack on the tile
+	virtual const CStack * battleGetStackByID(int ID, bool onlyAlive = true)=0; //returns stack info by given ID
+	virtual const CStack * battleGetStackByPos(int pos, bool onlyAlive = true)=0; //returns stack info by given pos
 	virtual int battleGetPos(int stack)=0; //returns position (tile ID) of stack
 	virtual int battleGetPos(int stack)=0; //returns position (tile ID) of stack
 	virtual int battleMakeAction(BattleAction* action)=0;//for casting spells by hero - DO NOT use it for moving active stack
 	virtual int battleMakeAction(BattleAction* action)=0;//for casting spells by hero - DO NOT use it for moving active stack
 	virtual std::map<int, CStack> battleGetStacks()=0; //returns stacks on battlefield
 	virtual std::map<int, CStack> battleGetStacks()=0; //returns stacks on battlefield
@@ -258,9 +258,9 @@ public:
 	int battleGetBattlefieldType(); //   1. sand/shore   2. sand/mesas   3. dirt/birches   4. dirt/hills   5. dirt/pines   6. grass/hills   7. grass/pines   8. lava   9. magic plains   10. snow/mountains   11. snow/trees   12. subterranean   13. swamp/trees   14. fiery fields   15. rock lands   16. magic clouds   17. lucid pools   18. holy ground   19. clover field   20. evil fog   21. "favourable winds" text on magic plains background   22. cursed ground   23. rough   24. ship to ship   25. ship
 	int battleGetBattlefieldType(); //   1. sand/shore   2. sand/mesas   3. dirt/birches   4. dirt/hills   5. dirt/pines   6. grass/hills   7. grass/pines   8. lava   9. magic plains   10. snow/mountains   11. snow/trees   12. subterranean   13. swamp/trees   14. fiery fields   15. rock lands   16. magic clouds   17. lucid pools   18. holy ground   19. clover field   20. evil fog   21. "favourable winds" text on magic plains background   22. cursed ground   23. rough   24. ship to ship   25. ship
 	int battleGetObstaclesAtTile(int tile); //returns bitfield
 	int battleGetObstaclesAtTile(int tile); //returns bitfield
 	std::vector<CObstacleInstance> battleGetAllObstacles(); //returns all obstacles on the battlefield
 	std::vector<CObstacleInstance> battleGetAllObstacles(); //returns all obstacles on the battlefield
-	int battleGetStack(int pos); //returns ID of stack on the tile
-	const CStack * battleGetStackByID(int ID); //returns stack info by given ID
-	const CStack * battleGetStackByPos(int pos); //returns stack info by given pos
+	int battleGetStack(int pos, bool onlyAlive = true); //returns ID of stack on the tile
+	const CStack * battleGetStackByID(int ID, bool onlyAlive = true); //returns stack info by given ID
+	const CStack * battleGetStackByPos(int pos, bool onlyAlive = true); //returns stack info by given pos
 	int battleGetPos(int stack); //returns position (tile ID) of stack
 	int battleGetPos(int stack); //returns position (tile ID) of stack
 	int battleMakeAction(BattleAction* action);//for casting spells by hero - DO NOT use it for moving active stack
 	int battleMakeAction(BattleAction* action);//for casting spells by hero - DO NOT use it for moving active stack
 	std::map<int, CStack> battleGetStacks(); //returns stacks on battlefield
 	std::map<int, CStack> battleGetStacks(); //returns stacks on battlefield

+ 1 - 0
CGameInterface.h

@@ -117,6 +117,7 @@ public:
 	virtual void battleStacksEffectsSet(SetStackEffect & sse){};//called when a specific effect is set to stacks
 	virtual void battleStacksEffectsSet(SetStackEffect & sse){};//called when a specific effect is set to stacks
 	virtual void battleStart(CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side){}; //called by engine when battle starts; side=0 - left, side=1 - right
 	virtual void battleStart(CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side){}; //called by engine when battle starts; side=0 - left, side=1 - right
 	virtual void battlefieldPrepared(int battlefieldType, std::vector<CObstacle*> obstacles){}; //called when battlefield is prepared, prior the battle beginning
 	virtual void battlefieldPrepared(int battlefieldType, std::vector<CObstacle*> obstacles){}; //called when battlefield is prepared, prior the battle beginning
+	virtual void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32>> & healedStacks){}; //called when stacks are healed / resurrected first element of pair - stack id, second - healed hp
 };
 };
 class CAIHandler
 class CAIHandler
 {
 {

+ 58 - 49
client/CBattleInterface.cpp

@@ -86,7 +86,7 @@ CBattleInterface::CBattleInterface(CCreatureSet * army1, CCreatureSet * army2, C
 	std::map<int, CStack> stacks = LOCPLINT->cb->battleGetStacks();
 	std::map<int, CStack> stacks = LOCPLINT->cb->battleGetStacks();
 	for(std::map<int, CStack>::iterator b=stacks.begin(); b!=stacks.end(); ++b)
 	for(std::map<int, CStack>::iterator b=stacks.begin(); b!=stacks.end(); ++b)
 	{
 	{
-		std::pair <int, int> coords = CBattleHex::getXYUnitAnim(b->second.position, b->second.owner == attackingHeroInstance->tempOwner, b->second.creature);
+		std::pair <int, int> coords = CBattleHex::getXYUnitAnim(b->second.position, b->second.owner == attackingHeroInstance->tempOwner, &b->second);
 		creAnims[b->second.ID] = (new CCreatureAnimation(b->second.creature->animDefName));
 		creAnims[b->second.ID] = (new CCreatureAnimation(b->second.creature->animDefName));
 		creAnims[b->second.ID]->setType(2);
 		creAnims[b->second.ID]->setType(2);
 		creAnims[b->second.ID]->pos = genRect(creAnims[b->second.ID]->fullHeight, creAnims[b->second.ID]->fullWidth, coords.first, coords.second);
 		creAnims[b->second.ID]->pos = genRect(creAnims[b->second.ID]->fullHeight, creAnims[b->second.ID]->fullWidth, coords.first, coords.second);
@@ -763,7 +763,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 			else //available tile
 			else //available tile
 			{
 			{
 				const CStack *sactive = LOCPLINT->cb->battleGetStackByID(activeStack);
 				const CStack *sactive = LOCPLINT->cb->battleGetStackByID(activeStack);
-				if(LOCPLINT->cb->battleGetStackByID(activeStack)->creature->isFlying())
+				if(LOCPLINT->cb->battleGetStackByID(activeStack)->hasFeatureOfType(StackFeature::FLYING))
 				{
 				{
 					CGI->curh->changeGraphic(1,2);
 					CGI->curh->changeGraphic(1,2);
 					//setting console text
 					//setting console text
@@ -805,7 +805,12 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 		}
 		}
 		else
 		else
 		{
 		{
-			const CStack * stackUnder = LOCPLINT->cb->battleGetStackByPos(myNumber);
+			//get dead stack if we cast resurrection or animate dead
+			const CStack * stackUnder = LOCPLINT->cb->battleGetStackByPos(myNumber, spellToCast->additionalInfo != 38 && spellToCast->additionalInfo != 39);
+
+			if(stackUnder && spellToCast->additionalInfo == 39 && !stackUnder->hasFeatureOfType(StackFeature::UNDEAD)) //animate dead can be cast only on living creatures
+				stackUnder = NULL;
+
 			bool whichCase; //for cases 1, 2 and 3
 			bool whichCase; //for cases 1, 2 and 3
 			switch(spellSelMode)
 			switch(spellSelMode)
 			{
 			{
@@ -880,14 +885,14 @@ bool CBattleInterface::reverseCreature(int number, int hex, bool wideTrick)
 	}
 	}
 	creDir[number] = !creDir[number];
 	creDir[number] = !creDir[number];
 
 
-	CStack curs = *LOCPLINT->cb->battleGetStackByID(number);
-	std::pair <int, int> coords = CBattleHex::getXYUnitAnim(hex, creDir[number], curs.creature);
+	const CStack * curs = LOCPLINT->cb->battleGetStackByID(number);
+	std::pair <int, int> coords = CBattleHex::getXYUnitAnim(hex, creDir[number], curs);
 	creAnims[number]->pos.x = coords.first;
 	creAnims[number]->pos.x = coords.first;
 	//creAnims[number]->pos.y = coords.second;
 	//creAnims[number]->pos.y = coords.second;
 
 
-	if(wideTrick && curs.creature->isDoubleWide())
+	if(wideTrick && curs->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 	{
 	{
-		if(curs.attackerOwned)
+		if(curs->attackerOwned)
 		{
 		{
 			if(!creDir[number])
 			if(!creDir[number])
 				creAnims[number]->pos.x -= 44;
 				creAnims[number]->pos.x -= 44;
@@ -1030,11 +1035,12 @@ void CBattleInterface::stackMoved(int number, int destHex, bool endMoving, int d
 	int curStackPos = LOCPLINT->cb->battleGetPos(number);
 	int curStackPos = LOCPLINT->cb->battleGetPos(number);
 	int steps = creAnims[number]->framesInGroup(0)*getAnimSpeedMultiplier()-1;
 	int steps = creAnims[number]->framesInGroup(0)*getAnimSpeedMultiplier()-1;
 	int hexWbase = 44, hexHbase = 42;
 	int hexWbase = 44, hexHbase = 42;
-	bool twoTiles = LOCPLINT->cb->battleGetCreature(number).isDoubleWide();
 	const CStack * movedStack = LOCPLINT->cb->battleGetStackByID(number);
 	const CStack * movedStack = LOCPLINT->cb->battleGetStackByID(number);
+	bool twoTiles = movedStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE);
+
 	
 	
-	std::pair<int, int> begPosition = CBattleHex::getXYUnitAnim(curStackPos, movedStack->attackerOwned, movedStack->creature);
-	std::pair<int, int> endPosition = CBattleHex::getXYUnitAnim(destHex, movedStack->attackerOwned, movedStack->creature);
+	std::pair<int, int> begPosition = CBattleHex::getXYUnitAnim(curStackPos, movedStack->attackerOwned, movedStack);
+	std::pair<int, int> endPosition = CBattleHex::getXYUnitAnim(destHex, movedStack->attackerOwned, movedStack);
 
 
 	if(startMoving) //animation of starting move; some units don't have this animation (ie. halberdier)
 	if(startMoving) //animation of starting move; some units don't have this animation (ie. halberdier)
 	{
 	{
@@ -1070,7 +1076,7 @@ void CBattleInterface::stackMoved(int number, int destHex, bool endMoving, int d
 
 
 	//step shift calculation
 	//step shift calculation
 	float posX = creAnims[number]->pos.x, posY = creAnims[number]->pos.y; // for precise calculations ;]
 	float posX = creAnims[number]->pos.x, posY = creAnims[number]->pos.y; // for precise calculations ;]
-	if(mutPos == -1 && movedStack->creature->isFlying()) 
+	if(mutPos == -1 && movedStack->hasFeatureOfType(StackFeature::FLYING)) 
 	{
 	{
 		steps *= distance;
 		steps *= distance;
 
 
@@ -1139,7 +1145,7 @@ void CBattleInterface::stackMoved(int number, int destHex, bool endMoving, int d
 		handleEndOfMove(number, destHex);
 		handleEndOfMove(number, destHex);
 	}
 	}
 
 
-	std::pair <int, int> coords = CBattleHex::getXYUnitAnim(destHex, creDir[number], movedStack->creature);
+	std::pair <int, int> coords = CBattleHex::getXYUnitAnim(destHex, creDir[number], movedStack);
 	creAnims[number]->pos.x = coords.first;
 	creAnims[number]->pos.x = coords.first;
 	if(!endMoving && twoTiles && (movedStack->owner == attackingHeroInstance->tempOwner) && (creDir[number] != (movedStack->owner == attackingHeroInstance->tempOwner))) //big attacker creature is reversed
 	if(!endMoving && twoTiles && (movedStack->owner == attackingHeroInstance->tempOwner) && (creDir[number] != (movedStack->owner == attackingHeroInstance->tempOwner))) //big attacker creature is reversed
 		creAnims[number]->pos.x -= 44;
 		creAnims[number]->pos.x -= 44;
@@ -1200,17 +1206,17 @@ void CBattleInterface::stacksAreAttacked(std::vector<CBattleInterface::SStackAtt
 	int maxLen = 0;
 	int maxLen = 0;
 	for(size_t g=0; g<attackedInfos.size(); ++g)
 	for(size_t g=0; g<attackedInfos.size(); ++g)
 	{
 	{
-		CStack attacked = *LOCPLINT->cb->battleGetStackByID(attackedInfos[g].ID);
+		const CStack * attacked = LOCPLINT->cb->battleGetStackByID(attackedInfos[g].ID, false);
 			
 			
 		if(attackedInfos[g].killed)
 		if(attackedInfos[g].killed)
 		{
 		{
-			CGI->soundh->playSound(attacked.creature->sounds.killed);
+			CGI->soundh->playSound(attacked->creature->sounds.killed);
 			creAnims[attackedInfos[g].ID]->setType(5); //death
 			creAnims[attackedInfos[g].ID]->setType(5); //death
 		}
 		}
 		else
 		else
 		{
 		{
 			// TODO: this block doesn't seems correct if the unit is defending.
 			// TODO: this block doesn't seems correct if the unit is defending.
-			CGI->soundh->playSound(attacked.creature->sounds.wince);
+			CGI->soundh->playSound(attacked->creature->sounds.wince);
 			creAnims[attackedInfos[g].ID]->setType(3); //getting hit
 			creAnims[attackedInfos[g].ID]->setType(3); //getting hit
 		}
 		}
 	}
 	}
@@ -1275,7 +1281,7 @@ void CBattleInterface::stackAttacking(int ID, int dest)
 	int reversedShift = 0; //shift of attacking stack's position due to reversing
 	int reversedShift = 0; //shift of attacking stack's position due to reversing
 	if(aStack.attackerOwned)
 	if(aStack.attackerOwned)
 	{
 	{
-		if(aStack.creature->isDoubleWide())
+		if(aStack.hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 		{
 		{
 			switch(BattleInfo::mutualPosition(aStack.position, dest)) //attack direction
 			switch(BattleInfo::mutualPosition(aStack.position, dest)) //attack direction
 			{
 			{
@@ -1303,7 +1309,7 @@ void CBattleInterface::stackAttacking(int ID, int dest)
 					break;
 					break;
 			}
 			}
 		}
 		}
-		else //else for if(aStack.creature->isDoubleWide())
+		else //else for if(aStack.hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 		{
 		{
 			switch(BattleInfo::mutualPosition(aStack.position, dest)) //attack direction
 			switch(BattleInfo::mutualPosition(aStack.position, dest)) //attack direction
 			{
 			{
@@ -1327,7 +1333,7 @@ void CBattleInterface::stackAttacking(int ID, int dest)
 	}
 	}
 	else //if(aStack.attackerOwned)
 	else //if(aStack.attackerOwned)
 	{
 	{
-		if(aStack.creature->isDoubleWide())
+		if(aStack.hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 		{
 		{
 			switch(BattleInfo::mutualPosition(aStack.position, dest)) //attack direction
 			switch(BattleInfo::mutualPosition(aStack.position, dest)) //attack direction
 			{
 			{
@@ -1356,7 +1362,7 @@ void CBattleInterface::stackAttacking(int ID, int dest)
 					break;
 					break;
 			}
 			}
 		}
 		}
-		else //else for if(aStack.creature->isDoubleWide())
+		else //else for if(aStack.hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 		{
 		{
 			switch(BattleInfo::mutualPosition(aStack.position, dest)) //attack direction
 			switch(BattleInfo::mutualPosition(aStack.position, dest)) //attack direction
 			{
 			{
@@ -1474,7 +1480,7 @@ void CBattleInterface::handleEndOfMove(int stackNumber, int destinationTile)
 	CGI->soundh->stopSound(moveSh);
 	CGI->soundh->stopSound(moveSh);
 
 
 	if(creDir[stackNumber] != (movedStack->owner == attackingHeroInstance->tempOwner))
 	if(creDir[stackNumber] != (movedStack->owner == attackingHeroInstance->tempOwner))
-		reverseCreature(stackNumber, destinationTile, movedStack->creature->isDoubleWide());	
+		reverseCreature(stackNumber, destinationTile, movedStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE));	
 }
 }
 
 
 void CBattleInterface::hexLclicked(int whichOne)
 void CBattleInterface::hexLclicked(int whichOne)
@@ -1487,18 +1493,19 @@ void CBattleInterface::hexLclicked(int whichOne)
 		{
 		{
 			//checking destination
 			//checking destination
 			bool allowCasting = true;
 			bool allowCasting = true;
+			bool onlyAlive = spellToCast->additionalInfo != 38 && spellToCast->additionalInfo != 39; //when casting resurrection or animate dead we should be allow to select dead stack
 			switch(spellSelMode)
 			switch(spellSelMode)
 			{
 			{
 			case 1:
 			case 1:
-				if(!LOCPLINT->cb->battleGetStackByPos(whichOne) || LOCPLINT->playerID != LOCPLINT->cb->battleGetStackByPos(whichOne)->owner )
+				if(!LOCPLINT->cb->battleGetStackByPos(whichOne, onlyAlive) || LOCPLINT->playerID != LOCPLINT->cb->battleGetStackByPos(whichOne, onlyAlive)->owner )
 					allowCasting = false;
 					allowCasting = false;
 				break;
 				break;
 			case 2:
 			case 2:
-				if(!LOCPLINT->cb->battleGetStackByPos(whichOne) || LOCPLINT->playerID == LOCPLINT->cb->battleGetStackByPos(whichOne)->owner )
+				if(!LOCPLINT->cb->battleGetStackByPos(whichOne, onlyAlive) || LOCPLINT->playerID == LOCPLINT->cb->battleGetStackByPos(whichOne, onlyAlive)->owner )
 					allowCasting = false;
 					allowCasting = false;
 				break;
 				break;
 			case 3:
 			case 3:
-				if(!LOCPLINT->cb->battleGetStackByPos(whichOne))
+				if(!LOCPLINT->cb->battleGetStackByPos(whichOne, onlyAlive))
 					allowCasting = false;
 					allowCasting = false;
 				break;
 				break;
 			case 4: //TODO: implement this case
 			case 4: //TODO: implement this case
@@ -1520,7 +1527,7 @@ void CBattleInterface::hexLclicked(int whichOne)
 				if(std::find(shadedHexes.begin(),shadedHexes.end(),whichOne)!=shadedHexes.end())// and it's in our range
 				if(std::find(shadedHexes.begin(),shadedHexes.end(),whichOne)!=shadedHexes.end())// and it's in our range
 				{
 				{
 					CGI->curh->changeGraphic(1, 6); //cursor should be changed
 					CGI->curh->changeGraphic(1, 6); //cursor should be changed
-					if(LOCPLINT->cb->battleGetStackByID(activeStack)->creature->isDoubleWide())
+					if(LOCPLINT->cb->battleGetStackByID(activeStack)->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 					{
 					{
 						std::vector<int> acc = LOCPLINT->cb->battleGetAvailableHexes(activeStack, false);
 						std::vector<int> acc = LOCPLINT->cb->battleGetAvailableHexes(activeStack, false);
 						int shiftedDest = whichOne + (LOCPLINT->cb->battleGetStackByID(activeStack)->attackerOwned ? 1 : -1);
 						int shiftedDest = whichOne + (LOCPLINT->cb->battleGetStackByID(activeStack)->attackerOwned ? 1 : -1);
@@ -1580,7 +1587,7 @@ void CBattleInterface::hexLclicked(int whichOne)
 						break;
 						break;
 					}
 					}
 				case 8: //from left
 				case 8: //from left
-					if(LOCPLINT->cb->battleGetStackByID(activeStack)->creature->isDoubleWide() && !LOCPLINT->cb->battleGetStackByID(activeStack)->attackerOwned)
+					if(LOCPLINT->cb->battleGetStackByID(activeStack)->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && !LOCPLINT->cb->battleGetStackByID(activeStack)->attackerOwned)
 					{
 					{
 						std::vector<int> acc = LOCPLINT->cb->battleGetAvailableHexes(activeStack, false);
 						std::vector<int> acc = LOCPLINT->cb->battleGetAvailableHexes(activeStack, false);
 						if(vstd::contains(acc, whichOne))
 						if(vstd::contains(acc, whichOne))
@@ -1628,7 +1635,7 @@ void CBattleInterface::hexLclicked(int whichOne)
 						break;
 						break;
 					}
 					}
 				case 11: //from right
 				case 11: //from right
-					if(LOCPLINT->cb->battleGetStackByID(activeStack)->creature->isDoubleWide() && LOCPLINT->cb->battleGetStackByID(activeStack)->attackerOwned)
+					if(LOCPLINT->cb->battleGetStackByID(activeStack)->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && LOCPLINT->cb->battleGetStackByID(activeStack)->attackerOwned)
 					{
 					{
 						std::vector<int> acc = LOCPLINT->cb->battleGetAvailableHexes(activeStack, false);
 						std::vector<int> acc = LOCPLINT->cb->battleGetAvailableHexes(activeStack, false);
 						if(vstd::contains(acc, whichOne))
 						if(vstd::contains(acc, whichOne))
@@ -1670,8 +1677,8 @@ void CBattleInterface::stackIsShooting(int ID, int dest)
 	spi.frameNum = 0;
 	spi.frameNum = 0;
 	spi.spin = CGI->creh->idToProjectileSpin[spi.creID];
 	spi.spin = CGI->creh->idToProjectileSpin[spi.creID];
 
 
-	std::pair<int, int> xycoord = CBattleHex::getXYUnitAnim(LOCPLINT->cb->battleGetPos(ID), true, &LOCPLINT->cb->battleGetCreature(ID));
-	std::pair<int, int> destcoord = CBattleHex::getXYUnitAnim(dest, false, &LOCPLINT->cb->battleGetCreature(ID)); 
+	std::pair<int, int> xycoord = CBattleHex::getXYUnitAnim(LOCPLINT->cb->battleGetPos(ID), true, LOCPLINT->cb->battleGetStackByID(ID));
+	std::pair<int, int> destcoord = CBattleHex::getXYUnitAnim(dest, false, LOCPLINT->cb->battleGetStackByID(ID)); 
 	destcoord.first += 250; destcoord.second += 210; //TODO: find a better place to shoot
 	destcoord.first += 250; destcoord.second += 210; //TODO: find a better place to shoot
 
 
 	if(projectileAngle > straightAngle) //upper shot
 	if(projectileAngle > straightAngle) //upper shot
@@ -1766,7 +1773,7 @@ void CBattleInterface::spellCast(SpellCast * sc)
 			//initial variables
 			//initial variables
 			std::string animToDisplay;
 			std::string animToDisplay;
 			std::pair<int, int> srccoord = sc->side ? std::make_pair(770, 60) : std::make_pair(30, 60);
 			std::pair<int, int> srccoord = sc->side ? std::make_pair(770, 60) : std::make_pair(30, 60);
-			std::pair<int, int> destcoord = CBattleHex::getXYUnitAnim(sc->tile, !sc->side, LOCPLINT->cb->battleGetStackByPos(sc->tile)->creature); //position attacked by arrow
+			std::pair<int, int> destcoord = CBattleHex::getXYUnitAnim(sc->tile, !sc->side, LOCPLINT->cb->battleGetStackByPos(sc->tile)); //position attacked by arrow
 			destcoord.first += 250; destcoord.second += 240;
 			destcoord.first += 250; destcoord.second += 240;
 
 
 			//animation angle
 			//animation angle
@@ -1814,9 +1821,11 @@ void CBattleInterface::spellCast(SpellCast * sc)
 		break;
 		break;
 	case 35: //dispel
 	case 35: //dispel
 	case 37: //cure
 	case 37: //cure
+	case 38: //resurrection
+	case 39: //animate dead
 		for(std::set<ui32>::const_iterator it = sc->affectedCres.begin(); it != sc->affectedCres.end(); ++it)
 		for(std::set<ui32>::const_iterator it = sc->affectedCres.begin(); it != sc->affectedCres.end(); ++it)
 		{
 		{
-			displayEffect(spell.mainEffectAnim, LOCPLINT->cb->battleGetStackByID(*it)->position);
+			displayEffect(spell.mainEffectAnim, LOCPLINT->cb->battleGetStackByID(*it, false)->position);
 		}
 		}
 		break;
 		break;
 	} //switch(sc->id)
 	} //switch(sc->id)
@@ -2002,7 +2011,7 @@ void CBattleInterface::attackingShowHelper()
 	{
 	{
 		if(attackingInfo->frame == 0)
 		if(attackingInfo->frame == 0)
 		{
 		{
-			CStack aStack = *LOCPLINT->cb->battleGetStackByID(attackingInfo->ID); //attacking stack
+			const CStack * aStack = LOCPLINT->cb->battleGetStackByID(attackingInfo->ID); //attacking stack
 			if(attackingInfo->shooting)
 			if(attackingInfo->shooting)
 			{
 			{
 				// TODO: I see that we enter this function twice with
 				// TODO: I see that we enter this function twice with
@@ -2011,24 +2020,24 @@ void CBattleInterface::attackingShowHelper()
 				// that is fixed. Once done, we can get rid of
 				// that is fixed. Once done, we can get rid of
 				// attackingInfo->sh
 				// attackingInfo->sh
 				if (attackingInfo->sh == -1)
 				if (attackingInfo->sh == -1)
-					attackingInfo->sh = CGI->soundh->playSound(aStack.creature->sounds.shoot);
+					attackingInfo->sh = CGI->soundh->playSound(aStack->creature->sounds.shoot);
 				creAnims[attackingInfo->ID]->setType(attackingInfo->shootingGroup);
 				creAnims[attackingInfo->ID]->setType(attackingInfo->shootingGroup);
 			}
 			}
 			else
 			else
 			{
 			{
 				// TODO: see comment above
 				// TODO: see comment above
 				if (attackingInfo->sh == -1)
 				if (attackingInfo->sh == -1)
-					attackingInfo->sh = CGI->soundh->playSound(aStack.creature->sounds.attack);
+					attackingInfo->sh = CGI->soundh->playSound(aStack->creature->sounds.attack);
 
 
 				std::map<int, int> dirToType = boost::assign::map_list_of (0, 11)(1, 11)(2, 12)(3, 13)(4, 13)(5, 12);
 				std::map<int, int> dirToType = boost::assign::map_list_of (0, 11)(1, 11)(2, 12)(3, 13)(4, 13)(5, 12);
 				int type; //dependent on attack direction
 				int type; //dependent on attack direction
-				if(aStack.creature->isDoubleWide())
+				if(aStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 				{
 				{
-					type = dirToType[ BattleInfo::mutualPosition(aStack.position + attackingInfo->posShiftDueToDist, attackingInfo->dest) ]; //attack direction
+					type = dirToType[ BattleInfo::mutualPosition(aStack->position + attackingInfo->posShiftDueToDist, attackingInfo->dest) ]; //attack direction
 				}
 				}
-				else //else for if(aStack.creature->isDoubleWide())
+				else //else for if(aStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 				{
 				{
-					type = BattleInfo::mutualPosition(aStack.position, attackingInfo->dest);
+					type = BattleInfo::mutualPosition(aStack->position, attackingInfo->dest);
 				}
 				}
 				creAnims[attackingInfo->ID]->setType(type);
 				creAnims[attackingInfo->ID]->setType(type);
 			}
 			}
@@ -2043,7 +2052,7 @@ void CBattleInterface::attackingShowHelper()
 			CStack aStack = *aStackp;
 			CStack aStack = *aStackp;
 			if(aStack.attackerOwned)
 			if(aStack.attackerOwned)
 			{
 			{
-				if(aStack.creature->isDoubleWide())
+				if(aStack.hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 				{
 				{
 					switch(BattleInfo::mutualPosition(aStack.position, attackingInfo->dest)) //attack direction
 					switch(BattleInfo::mutualPosition(aStack.position, attackingInfo->dest)) //attack direction
 					{
 					{
@@ -2070,7 +2079,7 @@ void CBattleInterface::attackingShowHelper()
 							break;
 							break;
 					}
 					}
 				}
 				}
-				else //else for if(aStack.creature->isDoubleWide())
+				else //else for if(aStack.hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 				{
 				{
 					switch(BattleInfo::mutualPosition(aStack.position, attackingInfo->dest)) //attack direction
 					switch(BattleInfo::mutualPosition(aStack.position, attackingInfo->dest)) //attack direction
 					{
 					{
@@ -2094,7 +2103,7 @@ void CBattleInterface::attackingShowHelper()
 			}
 			}
 			else //if(aStack.attackerOwned)
 			else //if(aStack.attackerOwned)
 			{
 			{
-				if(aStack.creature->isDoubleWide())
+				if(aStack.hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 				{
 				{
 					switch(BattleInfo::mutualPosition(aStack.position, attackingInfo->dest)) //attack direction
 					switch(BattleInfo::mutualPosition(aStack.position, attackingInfo->dest)) //attack direction
 					{
 					{
@@ -2122,7 +2131,7 @@ void CBattleInterface::attackingShowHelper()
 							break;
 							break;
 					}
 					}
 				}
 				}
-				else //else for if(aStack.creature->isDoubleWide())
+				else //else for if(aStack.hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 				{
 				{
 					switch(BattleInfo::mutualPosition(aStack.position, attackingInfo->dest)) //attack direction
 					switch(BattleInfo::mutualPosition(aStack.position, attackingInfo->dest)) //attack direction
 					{
 					{
@@ -2186,20 +2195,20 @@ void CBattleInterface::redrawBackgroundWithHexes(int activeStack)
 void CBattleInterface::printConsoleAttacked(int ID, int dmg, int killed, int IDby)
 void CBattleInterface::printConsoleAttacked(int ID, int dmg, int killed, int IDby)
 {
 {
 	char tabh[200];
 	char tabh[200];
-	CStack attacker = *LOCPLINT->cb->battleGetStackByID(IDby);
-	CStack defender = *LOCPLINT->cb->battleGetStackByID(ID);
-	int end = sprintf(tabh, CGI->generaltexth->allTexts[attacker.amount > 1 ? 377 : 376].c_str(),
-		(attacker.amount > 1 ? attacker.creature->namePl.c_str() : attacker.creature->nameSing.c_str()),
+	const CStack * attacker = LOCPLINT->cb->battleGetStackByID(IDby);
+	const CStack * defender = LOCPLINT->cb->battleGetStackByID(ID, false);
+	int end = sprintf(tabh, CGI->generaltexth->allTexts[attacker->amount > 1 ? 377 : 376].c_str(),
+		(attacker->amount > 1 ? attacker->creature->namePl.c_str() : attacker->creature->nameSing.c_str()),
 		dmg);
 		dmg);
 	if(killed > 0)
 	if(killed > 0)
 	{
 	{
 		if(killed > 1)
 		if(killed > 1)
 		{
 		{
-			sprintf(tabh + end, CGI->generaltexth->allTexts[379].c_str(), killed, defender.creature->namePl.c_str());
+			sprintf(tabh + end, CGI->generaltexth->allTexts[379].c_str(), killed, defender->creature->namePl.c_str());
 		}
 		}
 		else //killed == 1
 		else //killed == 1
 		{
 		{
-			sprintf(tabh + end, CGI->generaltexth->allTexts[378].c_str(), defender.creature->nameSing.c_str());
+			sprintf(tabh + end, CGI->generaltexth->allTexts[378].c_str(), defender->creature->nameSing.c_str());
 		}
 		}
 	}
 	}
 
 
@@ -2397,7 +2406,7 @@ CBattleHero::~CBattleHero()
 	delete flag;
 	delete flag;
 }
 }
 
 
-std::pair<int, int> CBattleHex::getXYUnitAnim(const int & hexNum, const bool & attacker, const CCreature * creature)
+std::pair<int, int> CBattleHex::getXYUnitAnim(const int & hexNum, const bool & attacker, const CStack * stack)
 {
 {
 	std::pair<int, int> ret = std::make_pair(-500, -500); //returned value
 	std::pair<int, int> ret = std::make_pair(-500, -500); //returned value
 	ret.second = -139 + 42 * (hexNum/BFIELD_WIDTH); //counting y
 	ret.second = -139 + 42 * (hexNum/BFIELD_WIDTH); //counting y
@@ -2411,7 +2420,7 @@ std::pair<int, int> CBattleHex::getXYUnitAnim(const int & hexNum, const bool & a
 		ret.first = -219 + 22 * ( ((hexNum/BFIELD_WIDTH) + 1)%2 ) + 44 * (hexNum % BFIELD_WIDTH);
 		ret.first = -219 + 22 * ( ((hexNum/BFIELD_WIDTH) + 1)%2 ) + 44 * (hexNum % BFIELD_WIDTH);
 	}
 	}
 	//shifting position for double - hex creatures
 	//shifting position for double - hex creatures
-	if(creature && creature->isDoubleWide())
+	if(stack && stack->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 	{
 	{
 		if(attacker)
 		if(attacker)
 		{
 		{

+ 2 - 2
client/CBattleInterface.h

@@ -61,7 +61,7 @@ public:
 	//CStack * ourStack;
 	//CStack * ourStack;
 	bool hovered, strictHovered; //for determining if hex is hovered by mouse (this is different problem than hex's graphic hovering)
 	bool hovered, strictHovered; //for determining if hex is hovered by mouse (this is different problem than hex's graphic hovering)
 	CBattleInterface * myInterface; //interface that owns me
 	CBattleInterface * myInterface; //interface that owns me
-	static std::pair<int, int> getXYUnitAnim(const int & hexNum, const bool & attacker, const CCreature * creature); //returns (x, y) of left top corner of animation
+	static std::pair<int, int> getXYUnitAnim(const int & hexNum, const bool & attacker, const CStack * creature); //returns (x, y) of left top corner of animation
 	//for user interactions
 	//for user interactions
 	void hover (bool on);
 	void hover (bool on);
 	void activate();
 	void activate();
@@ -177,7 +177,7 @@ private:
 	std::map<int, int> standingFrame; //number of frame in standing animation by stack ID, helps in showing 'random moves'
 	std::map<int, int> standingFrame; //number of frame in standing animation by stack ID, helps in showing 'random moves'
 
 
 	bool spellDestSelectMode; //if true, player is choosing destination for his spell
 	bool spellDestSelectMode; //if true, player is choosing destination for his spell
-	int spellSelMode; //0 - any location, 1 - any firendly creature, 2 - any hostile creature, 3 - any creature, 4 - obstacle, -1 - no location
+	int spellSelMode; //0 - any location, 1 - any firendly creature, 2 - any hostile creature, 3 - any creature, 4 - obstacle,z -1 - no location
 	BattleAction * spellToCast; //spell for which player is choosing destination
 	BattleAction * spellToCast; //spell for which player is choosing destination
 	void endCastingSpell(); //ends casting spell (eg. when spell has been cast or cancelled)
 	void endCastingSpell(); //ends casting spell (eg. when spell has been cast or cancelled)
 
 

+ 13 - 0
client/CPlayerInterface.cpp

@@ -1158,6 +1158,19 @@ void CPlayerInterface::battlefieldPrepared(int battlefieldType, std::vector<CObs
 {
 {
 }
 }
 
 
+void CPlayerInterface::battleStacksHealedRes(const std::vector<std::pair<ui32, ui32>> & healedStacks)
+{
+	for(int b=0; b<healedStacks.size(); ++b)
+	{
+		const CStack * healed = cb->battleGetStackByID(healedStacks[b].first);
+		if(battleInt->creAnims[healed->ID]->getType() == 5)
+		{
+			//stack has been resurrected
+			battleInt->creAnims[healed->ID]->setType(2);
+		}
+	}
+}
+
 void CPlayerInterface::battleNewRound(int round) //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
 void CPlayerInterface::battleNewRound(int round) //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
 {
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	boost::unique_lock<boost::recursive_mutex> un(*pim);

+ 1 - 0
client/CPlayerInterface.h

@@ -192,6 +192,7 @@ public:
 	void battleStacksAttacked(std::set<BattleStackAttacked> & bsa);
 	void battleStacksAttacked(std::set<BattleStackAttacked> & bsa);
 	void battleStart(CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side); //called by engine when battle starts; side=0 - left, side=1 - right
 	void battleStart(CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side); //called by engine when battle starts; side=0 - left, side=1 - right
 	void battlefieldPrepared(int battlefieldType, std::vector<CObstacle*> obstacles); //called when battlefield is prepared, prior the battle beginning
 	void battlefieldPrepared(int battlefieldType, std::vector<CObstacle*> obstacles); //called when battlefield is prepared, prior the battle beginning
+	void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32>> & healedStacks); //called when stacks are healed / resurrected
 
 
 
 
 	//-------------//
 	//-------------//

+ 11 - 0
client/NetPacksClient.cpp

@@ -434,6 +434,17 @@ void BattleResultsApplied::applyCl( CClient *cl )
 	INTERFACE_CALL_IF_PRESENT(player2,battleResultsApplied);
 	INTERFACE_CALL_IF_PRESENT(player2,battleResultsApplied);
 }
 }
 
 
+void StacksHealedOrResurrected::applyCl( CClient *cl )
+{
+	std::vector<std::pair<ui32, ui32>> shiftedHealed;
+	for(int v=0; v<healedStacks.size(); ++v)
+	{
+		shiftedHealed.push_back(std::make_pair(healedStacks[v].stackID, healedStacks[v].healedHP));
+	}
+	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1, battleStacksHealedRes, shiftedHealed);
+	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2, battleStacksHealedRes, shiftedHealed);
+}
+
 CGameState* CPackForClient::GS( CClient *cl )
 CGameState* CPackForClient::GS( CClient *cl )
 {
 {
 	return cl->gs;
 	return cl->gs;

+ 2 - 2
config/settings.txt

@@ -3,8 +3,8 @@
 clientSettings
 clientSettings
 {
 {
 	port=3030;
 	port=3030;
-	resolution=1024x768; // format: WxH
-	bpp=32; // bytes per pixels: 24 or 32
+	resolution=800x600; // format: WxH
+	bpp=24; // bytes per pixels: 24 or 32
 	fullscreen=0; //0 - windowed mode, 1 - fullscreen
 	fullscreen=0; //0 - windowed mode, 1 - fullscreen
 	server=127.0.0.1; //use 127.0.0.1 for localhost
 	server=127.0.0.1; //use 127.0.0.1 for localhost
 	localInformation=2; //0 - *all* information sent from server (safest and slowest); 1 - map information sent from server; 2 - all information local-storaged
 	localInformation=2; //0 - *all* information sent from server (safest and slowest); 1 - map information sent from server; 2 - all information local-storaged

+ 2 - 2
config/spell_info.txt

@@ -38,8 +38,8 @@
 35 0 41 0 0 0 X
 35 0 41 0 0 0 X
 36 1 -1 0 0 0 0
 36 1 -1 0 0 0 0
 37 1 39 0 0 0 0
 37 1 39 0 0 0 0
-38 1 -1 0 0 0 0
-39 1 -1 0 0 0 0
+38 1 79 0 0 0 0
+39 1 79 0 0 0 0
 40 1 -1 0 0 0 0
 40 1 -1 0 0 0 0
 41 1 36 0 0 0 X
 41 1 36 0 0 0 X
 42 -1 40 0 0 0 X
 42 -1 40 0 0 0 X

+ 10 - 2
hch/CCreatureHandler.cpp

@@ -324,10 +324,18 @@ void CCreatureHandler::loadCreatures()
 		case '-': //remove ability
 		case '-': //remove ability
 			{
 			{
 				int creatureID;
 				int creatureID;
-				ui32 type;
+				std::string type;
 				reader >> creatureID;
 				reader >> creatureID;
 				reader >> type;
 				reader >> type;
-				StackFeature::ECombatFeatures ecf = static_cast<StackFeature::ECombatFeatures>(type);
+				std::map<std::string, int>::iterator it = type_list.find(type);
+				if (it == type_list.end())
+				{
+					tlog1 << "Error: invalid type " << type << " in cr_abils.txt" << std::endl;
+					break;
+				}
+				int typeNo = it->second;
+
+				StackFeature::ECombatFeatures ecf = static_cast<StackFeature::ECombatFeatures>(typeNo);
 
 
 				creatures[creatureID].abilities -= ecf;
 				creatures[creatureID].abilities -= ecf;
 				break;
 				break;

+ 23 - 19
lib/CGameState.cpp

@@ -239,24 +239,24 @@ static CGObjectInstance * createObject(int id, int subid, int3 pos, int owner)
 	nobj->defInfo = VLC->dobjinfo->gobjs[id][subid];
 	nobj->defInfo = VLC->dobjinfo->gobjs[id][subid];
 	return nobj;
 	return nobj;
 }
 }
-CStack * BattleInfo::getStack(int stackID)
+CStack * BattleInfo::getStack(int stackID, bool onlyAlive)
 {
 {
 	for(unsigned int g=0; g<stacks.size(); ++g)
 	for(unsigned int g=0; g<stacks.size(); ++g)
 	{
 	{
-		if(stacks[g]->ID == stackID)
+		if(stacks[g]->ID == stackID && (!onlyAlive || stacks[g]->alive()))
 			return stacks[g];
 			return stacks[g];
 	}
 	}
 	return NULL;
 	return NULL;
 }
 }
-CStack * BattleInfo::getStackT(int tileID)
+CStack * BattleInfo::getStackT(int tileID, bool onlyAlive)
 {
 {
 	for(unsigned int g=0; g<stacks.size(); ++g)
 	for(unsigned int g=0; g<stacks.size(); ++g)
 	{
 	{
 		if(stacks[g]->position == tileID 
 		if(stacks[g]->position == tileID 
-			|| (stacks[g]->creature->isDoubleWide() && stacks[g]->attackerOwned && stacks[g]->position-1 == tileID)
-			|| (stacks[g]->creature->isDoubleWide() && !stacks[g]->attackerOwned && stacks[g]->position+1 == tileID))
+			|| (stacks[g]->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && stacks[g]->attackerOwned && stacks[g]->position-1 == tileID)
+			|| (stacks[g]->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && !stacks[g]->attackerOwned && stacks[g]->position+1 == tileID))
 		{
 		{
-			if(stacks[g]->alive())
+			if(!onlyAlive || stacks[g]->alive())
 			{
 			{
 				return stacks[g];
 				return stacks[g];
 			}
 			}
@@ -273,7 +273,7 @@ void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool atta
 			continue;
 			continue;
 
 
 		accessibility[stacks[g]->position] = false;
 		accessibility[stacks[g]->position] = false;
-		if(stacks[g]->creature->isDoubleWide()) //if it's a double hex creature
+		if(stacks[g]->hasFeatureOfType(StackFeature::DOUBLE_WIDE)) //if it's a double hex creature
 		{
 		{
 			if(stacks[g]->attackerOwned)
 			if(stacks[g]->attackerOwned)
 				accessibility[stacks[g]->position-1] = false;
 				accessibility[stacks[g]->position-1] = false;
@@ -366,12 +366,12 @@ std::vector<int> BattleInfo::getAccessibility(int stackID, bool addOccupiable)
 	CStack *s = getStack(stackID);
 	CStack *s = getStack(stackID);
 	std::set<int> occupyable;
 	std::set<int> occupyable;
 
 
-	getAccessibilityMap(ac, s->creature->isDoubleWide(), s->attackerOwned, addOccupiable, occupyable, stackID);
+	getAccessibilityMap(ac, s->hasFeatureOfType(StackFeature::DOUBLE_WIDE), s->attackerOwned, addOccupiable, occupyable, s->hasFeatureOfType(StackFeature::FLYING), stackID);
 
 
 	int pr[BFIELD_SIZE], dist[BFIELD_SIZE];
 	int pr[BFIELD_SIZE], dist[BFIELD_SIZE];
-	makeBFS(s->position, ac, pr, dist, s->creature->isDoubleWide(), s->attackerOwned, s->creature->isFlying());
+	makeBFS(s->position, ac, pr, dist, s->hasFeatureOfType(StackFeature::DOUBLE_WIDE), s->attackerOwned, s->hasFeatureOfType(StackFeature::FLYING));
 
 
-	if(s->creature->isDoubleWide())
+	if(s->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 	{
 	{
 		if(!addOccupiable)
 		if(!addOccupiable)
 		{
 		{
@@ -398,7 +398,7 @@ std::vector<int> BattleInfo::getAccessibility(int stackID, bool addOccupiable)
 	
 	
 	for(int i=0; i < BFIELD_SIZE ; ++i)
 	for(int i=0; i < BFIELD_SIZE ; ++i)
 		if(
 		if(
-			( ( !addOccupiable && dist[i] <= s->Speed() && ac[i] ) || ( addOccupiable && dist[i] <= s->Speed() && isAccessible(i, ac, s->creature->isDoubleWide(), s->attackerOwned, s->creature->isFlying(), true) ) )//we can reach it
+			( ( !addOccupiable && dist[i] <= s->Speed() && ac[i] ) || ( addOccupiable && dist[i] <= s->Speed() && isAccessible(i, ac, s->hasFeatureOfType(StackFeature::DOUBLE_WIDE), s->attackerOwned, s->hasFeatureOfType(StackFeature::FLYING), true) ) )//we can reach it
 			|| (vstd::contains(occupyable, i) && ( dist[ i + (s->attackerOwned ? 1 : -1 ) ] <= s->Speed() ) &&
 			|| (vstd::contains(occupyable, i) && ( dist[ i + (s->attackerOwned ? 1 : -1 ) ] <= s->Speed() ) &&
 				ac[i + (s->attackerOwned ? 1 : -1 )] ) //it's occupyable and we can reach adjacent hex
 				ac[i + (s->attackerOwned ? 1 : -1 )] ) //it's occupyable and we can reach adjacent hex
 			)
 			)
@@ -417,7 +417,7 @@ bool BattleInfo::isStackBlocked(int ID)
 			|| stacks[i]->owner==our->owner
 			|| stacks[i]->owner==our->owner
 		  )
 		  )
 			continue; //we omit dead and allied stacks
 			continue; //we omit dead and allied stacks
-		if(stacks[i]->creature->isDoubleWide())
+		if(stacks[i]->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 		{
 		{
 			if( mutualPosition(stacks[i]->position, our->position) >= 0  
 			if( mutualPosition(stacks[i]->position, our->position) >= 0  
 			  || mutualPosition(stacks[i]->position + (stacks[i]->attackerOwned ? -1 : 1), our->position) >= 0)
 			  || mutualPosition(stacks[i]->position + (stacks[i]->attackerOwned ? -1 : 1), our->position) >= 0)
@@ -1370,18 +1370,18 @@ bool CGameState::battleShootCreatureStack(int ID, int dest)
 	return true;
 	return true;
 }
 }
 
 
-int CGameState::battleGetStack(int pos)
+int CGameState::battleGetStack(int pos, bool onlyAlive)
 {
 {
 	if(!curB)
 	if(!curB)
 		return -1;
 		return -1;
 	for(unsigned int g=0; g<curB->stacks.size(); ++g)
 	for(unsigned int g=0; g<curB->stacks.size(); ++g)
 	{
 	{
 		if((curB->stacks[g]->position == pos 
 		if((curB->stacks[g]->position == pos 
-			  || (curB->stacks[g]->creature->isDoubleWide() 
+			  || (curB->stacks[g]->hasFeatureOfType(StackFeature::DOUBLE_WIDE) 
 					&&( (curB->stacks[g]->attackerOwned && curB->stacks[g]->position-1 == pos) 
 					&&( (curB->stacks[g]->attackerOwned && curB->stacks[g]->position-1 == pos) 
 					||	(!curB->stacks[g]->attackerOwned && curB->stacks[g]->position+1 == pos)	)
 					||	(!curB->stacks[g]->attackerOwned && curB->stacks[g]->position+1 == pos)	)
 			 ))
 			 ))
-		  && curB->stacks[g]->alive()
+			 && (!onlyAlive || curB->stacks[g]->alive())
 		  )
 		  )
 			return curB->stacks[g]->ID;
 			return curB->stacks[g]->ID;
 	}
 	}
@@ -2101,6 +2101,9 @@ std::set<CStack*> BattleInfo::getAttackedCreatures(const CSpell * s, const CGHer
 {
 {
 	std::set<ui16> attackedHexes = s->rangeInHexes(destinationTile, caster->getSpellSchoolLevel(s));
 	std::set<ui16> attackedHexes = s->rangeInHexes(destinationTile, caster->getSpellSchoolLevel(s));
 	std::set<CStack*> attackedCres; /*std::set to exclude multiple occurences of two hex creatures*/
 	std::set<CStack*> attackedCres; /*std::set to exclude multiple occurences of two hex creatures*/
+
+	bool onlyAlive = s->id != 38 && s->id != 39; //when casting resurrection or animate dead we should be allow to select dead stack
+
 	if(s->id == 24 || s->id == 25 || s->id == 26) //death ripple, destroy undead and armageddon
 	if(s->id == 24 || s->id == 25 || s->id == 26) //death ripple, destroy undead and armageddon
 	{
 	{
 		for(int it=0; it<stacks.size(); ++it)
 		for(int it=0; it<stacks.size(); ++it)
@@ -2119,7 +2122,7 @@ std::set<CStack*> BattleInfo::getAttackedCreatures(const CSpell * s, const CGHer
 	{
 	{
 		if(caster->getSpellSchoolLevel(s) < 3)  /*not expert */
 		if(caster->getSpellSchoolLevel(s) < 3)  /*not expert */
 		{
 		{
-			CStack * st = getStackT(destinationTile);
+			CStack * st = getStackT(destinationTile, onlyAlive);
 			if(st)
 			if(st)
 				attackedCres.insert(st);
 				attackedCres.insert(st);
 		}
 		}
@@ -2132,14 +2135,15 @@ std::set<CStack*> BattleInfo::getAttackedCreatures(const CSpell * s, const CGHer
 					||(VLC->spellh->spells[s->id].positiveness <= 0 && stacks[it]->owner != caster->tempOwner )
 					||(VLC->spellh->spells[s->id].positiveness <= 0 && stacks[it]->owner != caster->tempOwner )
 					)
 					)
 				{
 				{
-					attackedCres.insert(stacks[it]);
+					if(!onlyAlive || stacks[it]->alive())
+						attackedCres.insert(stacks[it]);
 				}
 				}
 			}
 			}
 		} //if(caster->getSpellSchoolLevel(s) < 3)
 		} //if(caster->getSpellSchoolLevel(s) < 3)
 	}
 	}
 	else if(VLC->spellh->spells[s->id].attributes.find("CREATURE_TARGET") != std::string::npos) //spell to be cast on one specific creature
 	else if(VLC->spellh->spells[s->id].attributes.find("CREATURE_TARGET") != std::string::npos) //spell to be cast on one specific creature
 	{
 	{
-		CStack * st = getStackT(destinationTile);
+		CStack * st = getStackT(destinationTile, onlyAlive);
 		if(st)
 		if(st)
 			attackedCres.insert(st);
 			attackedCres.insert(st);
 	}
 	}
@@ -2147,7 +2151,7 @@ std::set<CStack*> BattleInfo::getAttackedCreatures(const CSpell * s, const CGHer
 	{
 	{
 		for(std::set<ui16>::iterator it = attackedHexes.begin(); it != attackedHexes.end(); ++it)
 		for(std::set<ui16>::iterator it = attackedHexes.begin(); it != attackedHexes.end(); ++it)
 		{
 		{
-			CStack * st = getStackT(*it);
+			CStack * st = getStackT(*it, onlyAlive);
 			if(st)
 			if(st)
 				attackedCres.insert(st);
 				attackedCres.insert(st);
 		}
 		}

+ 3 - 3
lib/CGameState.h

@@ -123,8 +123,8 @@ struct DLL_EXPORT BattleInfo
 	}
 	}
 	CStack * getNextStack(); //which stack will have turn after current one
 	CStack * getNextStack(); //which stack will have turn after current one
 	std::vector<CStack> getStackQueue(); //returns stack in order of their movement action
 	std::vector<CStack> getStackQueue(); //returns stack in order of their movement action
-	CStack * getStack(int stackID);
-	CStack * getStackT(int tileID);
+	CStack * getStack(int stackID, bool onlyAlive = true);
+	CStack * getStackT(int tileID, bool onlyAlive = true);
 	void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<int> & occupyable, bool flying, int stackToOmmit=-1); //send pointer to at least 187 allocated bytes
 	void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<int> & occupyable, bool flying, int stackToOmmit=-1); //send pointer to at least 187 allocated bytes
 	static bool isAccessible(int hex, bool * accessibility, bool twoHex, bool attackerOwned, bool flying, bool lastPos); //helper for makeBFS
 	static bool isAccessible(int hex, bool * accessibility, bool twoHex, bool attackerOwned, bool flying, bool lastPos); //helper for makeBFS
 	void makeBFS(int start, bool*accessibility, int *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying); //*accessibility must be prepared bool[187] array; last two pointers must point to the at least 187-elements int arrays - there is written result
 	void makeBFS(int start, bool*accessibility, int *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying); //*accessibility must be prepared bool[187] array; last two pointers must point to the at least 187-elements int arrays - there is written result
@@ -273,7 +273,7 @@ public:
 	bool battleMoveCreatureStack(int ID, int dest);
 	bool battleMoveCreatureStack(int ID, int dest);
 	bool battleAttackCreatureStack(int ID, int dest);
 	bool battleAttackCreatureStack(int ID, int dest);
 	bool battleShootCreatureStack(int ID, int dest);
 	bool battleShootCreatureStack(int ID, int dest);
-	int battleGetStack(int pos); //returns ID of stack at given tile
+	int battleGetStack(int pos, bool onlyAlive); //returns ID of stack at given tile
 	int battleGetBattlefieldType(int3 tile = int3());//   1. sand/shore   2. sand/mesas   3. dirt/birches   4. dirt/hills   5. dirt/pines   6. grass/hills   7. grass/pines   8. lava   9. magic plains   10. snow/mountains   11. snow/trees   12. subterranean   13. swamp/trees   14. fiery fields   15. rock lands   16. magic clouds   17. lucid pools   18. holy ground   19. clover field   20. evil fog   21. "favourable winds" text on magic plains background   22. cursed ground   23. rough   24. ship to ship   25. ship
 	int battleGetBattlefieldType(int3 tile = int3());//   1. sand/shore   2. sand/mesas   3. dirt/birches   4. dirt/hills   5. dirt/pines   6. grass/hills   7. grass/pines   8. lava   9. magic plains   10. snow/mountains   11. snow/trees   12. subterranean   13. swamp/trees   14. fiery fields   15. rock lands   16. magic clouds   17. lucid pools   18. holy ground   19. clover field   20. evil fog   21. "favourable winds" text on magic plains background   22. cursed ground   23. rough   24. ship to ship   25. ship
 	si8 battleMaxSpellLevel(); //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, SPELL_LEVELS is returned
 	si8 battleMaxSpellLevel(); //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, SPELL_LEVELS is returned
 	UpgradeInfo getUpgradeInfo(CArmedInstance *obj, int stackPos);
 	UpgradeInfo getUpgradeInfo(CArmedInstance *obj, int stackPos);

+ 3 - 3
lib/NetPacks.h

@@ -956,15 +956,15 @@ struct StacksHealedOrResurrected : public CPackForClient //3013
 	StacksHealedOrResurrected(){type = 3013;}
 	StacksHealedOrResurrected(){type = 3013;}
 
 
 	DLL_EXPORT void applyGs(CGameState *gs);
 	DLL_EXPORT void applyGs(CGameState *gs);
+	void applyCl(CClient *cl);
 
 
 	struct HealInfo
 	struct HealInfo
 	{
 	{
 		ui32 stackID;
 		ui32 stackID;
-		ui32 healForFirstStack;
-		ui32 resurrectedCres;
+		ui32 healedHP;
 		template <typename Handler> void serialize(Handler &h, const int version)
 		template <typename Handler> void serialize(Handler &h, const int version)
 		{
 		{
-			h & stackID & healForFirstStack & resurrectedCres;
+			h & stackID & healedHP;
 		}
 		}
 	};
 	};
 
 

+ 12 - 2
lib/NetPacksLib.cpp

@@ -882,8 +882,18 @@ DLL_EXPORT void StacksHealedOrResurrected::applyGs( CGameState *gs )
 	for(int g=0; g<healedStacks.size(); ++g)
 	for(int g=0; g<healedStacks.size(); ++g)
 	{
 	{
 		CStack * changedStack = gs->curB->stacks[healedStacks[g].stackID];
 		CStack * changedStack = gs->curB->stacks[healedStacks[g].stackID];
-		changedStack->firstHPleft += healedStacks[g].healForFirstStack;
-		changedStack->amount += healedStacks[g].resurrectedCres;
+		if(!changedStack->alive())
+		{
+			changedStack->state.insert(ALIVE);
+		}
+		int missingHPfirst = changedStack->MaxHealth() - changedStack->firstHPleft;
+		changedStack->amount += healedStacks[g].healedHP / changedStack->MaxHealth();
+		changedStack->firstHPleft += healedStacks[g].healedHP - changedStack->amount * changedStack->MaxHealth();
+		if(changedStack->firstHPleft > changedStack->MaxHealth())
+		{
+			changedStack->firstHPleft -= changedStack->MaxHealth();
+			changedStack->amount += 1;
+		}
 		//removal of negative effects
 		//removal of negative effects
 		{
 		{
 			for(int h=0; h<changedStack->effects.size(); ++h)
 			for(int h=0; h<changedStack->effects.size(); ++h)

+ 42 - 12
server/CGameHandler.cpp

@@ -561,7 +561,7 @@ void CGameHandler::moveStack(int stack, int dest)
 	}
 	}
 
 
 	//shifting destination (if we have double wide stack and we can occupy dest but not be exactly there)
 	//shifting destination (if we have double wide stack and we can occupy dest but not be exactly there)
-	if(!stackAtEnd && curStack->creature->isDoubleWide() && !accessibility[dest])
+	if(!stackAtEnd && curStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && !accessibility[dest])
 	{
 	{
 		if(curStack->attackerOwned)
 		if(curStack->attackerOwned)
 		{
 		{
@@ -592,8 +592,8 @@ void CGameHandler::moveStack(int stack, int dest)
 	//if(dists[dest] > curStack->creature->speed && !(stackAtEnd && dists[dest] == curStack->creature->speed+1)) //we can attack a stack if we can go to adjacent hex
 	//if(dists[dest] > curStack->creature->speed && !(stackAtEnd && dists[dest] == curStack->creature->speed+1)) //we can attack a stack if we can go to adjacent hex
 	//	return false;
 	//	return false;
 
 
-	std::pair< std::vector<int>, int > path = gs->curB->getPath(curStack->position, dest, accessibilityWithOccupyable, curStack->creature->isFlying(), curStack->creature->isDoubleWide(), curStack->attackerOwned);
-	if(curStack->creature->isFlying())
+	std::pair< std::vector<int>, int > path = gs->curB->getPath(curStack->position, dest, accessibilityWithOccupyable, curStack->hasFeatureOfType(StackFeature::FLYING), curStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE), curStack->attackerOwned);
+	if(curStack->hasFeatureOfType(StackFeature::FLYING))
 	{
 	{
 		if(path.second <= curStack->Speed() && path.first.size() > 0)
 		if(path.second <= curStack->Speed() && path.first.size() > 0)
 		{
 		{
@@ -957,11 +957,11 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 
 
 	for(unsigned g=0; g<stacks.size(); ++g) //shifting positions of two-hex creatures
 	for(unsigned g=0; g<stacks.size(); ++g) //shifting positions of two-hex creatures
 	{
 	{
-		if((stacks[g]->position%17)==1 && stacks[g]->creature->isDoubleWide())
+		if((stacks[g]->position%17)==1 && stacks[g]->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 		{
 		{
 			stacks[g]->position += 1;
 			stacks[g]->position += 1;
 		}
 		}
-		else if((stacks[g]->position%17)==15 && stacks[g]->creature->isDoubleWide())
+		else if((stacks[g]->position%17)==15 && stacks[g]->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
 		{
 		{
 			stacks[g]->position -= 1;
 			stacks[g]->position -= 1;
 		}
 		}
@@ -2370,11 +2370,11 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 
 
 			if( !(
 			if( !(
 				(BattleInfo::mutualPosition(curpos, enemypos) >= 0)						//front <=> front
 				(BattleInfo::mutualPosition(curpos, enemypos) >= 0)						//front <=> front
-				|| (curStack->creature->isDoubleWide()									//back <=> front
+				|| (curStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE)									//back <=> front
 					&& BattleInfo::mutualPosition(curpos + (curStack->attackerOwned ? -1 : 1), enemypos) >= 0)
 					&& BattleInfo::mutualPosition(curpos + (curStack->attackerOwned ? -1 : 1), enemypos) >= 0)
-				|| (stackAtEnd->creature->isDoubleWide()									//front <=> back
+				|| (stackAtEnd->hasFeatureOfType(StackFeature::DOUBLE_WIDE)									//front <=> back
 					&& BattleInfo::mutualPosition(curpos, enemypos + (stackAtEnd->attackerOwned ? -1 : 1)) >= 0)
 					&& BattleInfo::mutualPosition(curpos, enemypos + (stackAtEnd->attackerOwned ? -1 : 1)) >= 0)
-				|| (stackAtEnd->creature->isDoubleWide() && curStack->creature->isDoubleWide()//back <=> back
+				|| (stackAtEnd->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && curStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE)//back <=> back
 					&& BattleInfo::mutualPosition(curpos + (curStack->attackerOwned ? -1 : 1), enemypos + (stackAtEnd->attackerOwned ? -1 : 1)) >= 0)
 					&& BattleInfo::mutualPosition(curpos + (curStack->attackerOwned ? -1 : 1), enemypos + (stackAtEnd->attackerOwned ? -1 : 1)) >= 0)
 				)
 				)
 				)
 				)
@@ -2646,6 +2646,34 @@ static ui32 calculateSpellDmg(const CSpell * sp, const CGHeroInstance * caster,
 	return ret;
 	return ret;
 }
 }
 
 
+static ui32 calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack)
+{
+	switch(spell->id)
+	{
+	case 37: //cure
+		{
+			int healedHealth = caster->getPrimSkillLevel(2) * 5 + spell->powers[caster->getSpellSchoolLevel(spell)];
+			return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft);
+			break;
+		}
+	case 38: //resurrection
+		{
+			int healedHealth = caster->getPrimSkillLevel(2) * 50 + spell->powers[caster->getSpellSchoolLevel(spell)];
+			return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + stack->baseAmount * stack->MaxHealth());
+			break;
+		}
+	case 39: //animate dead
+		{
+			int healedHealth = caster->getPrimSkillLevel(2) * 50 + spell->powers[caster->getSpellSchoolLevel(spell)];
+			return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + stack->baseAmount * stack->MaxHealth());
+			break;
+		}
+	}
+	//we shouldn't be here
+	tlog1 << "calculateHealedHP called for non-healing spell: " << spell->name << std::endl;
+	return 0;
+}
+
 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)
 {
 {
 	std::vector<ui32> ret;
 	std::vector<ui32> ret;
@@ -2831,17 +2859,19 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
 					break;
 					break;
 				}
 				}
 			case 37: //cure
 			case 37: //cure
+			case 38: //resurrection
+			case 39: //animate dead
 				{
 				{
 					StacksHealedOrResurrected shr;
 					StacksHealedOrResurrected shr;
 					for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
 					for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
 					{
 					{
-						if(vstd::contains(sc.resisted, (*it)->ID)) //this creature resisted the spell
+						if(vstd::contains(sc.resisted, (*it)->ID) //this creature resisted the spell
+							|| (s->id == 39 && !(*it)->hasFeatureOfType(StackFeature::UNDEAD)) //we try to cast animate dead on living stack
+							) 
 							continue;
 							continue;
 						StacksHealedOrResurrected::HealInfo hi;
 						StacksHealedOrResurrected::HealInfo hi;
 						hi.stackID = (*it)->ID;
 						hi.stackID = (*it)->ID;
-						int healedHP = h->getPrimSkillLevel(2) * 5 + s->powers[h->getSpellSchoolLevel(s)];
-						hi.healForFirstStack = std::min<ui32>(healedHP, (*it)->MaxHealth() - (*it)->firstHPleft);
-						hi.resurrectedCres = 0;
+						hi.healedHP = calculateHealedHP(h, s, *it);
 						shr.healedStacks.push_back(hi);
 						shr.healedStacks.push_back(hi);
 					}
 					}
 					if(!shr.healedStacks.empty())
 					if(!shr.healedStacks.empty())