1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-28 08:48:48 +02:00

* new spells: resurrection, animate dead

* a lot of minor fixes in battles
* resolution & depth in settings.txt reverted to 800x600 and 24bpp
This commit is contained in:
mateuszb 2009-08-05 12:46:08 +00:00
parent 64f098017b
commit 507597301f
17 changed files with 199 additions and 112 deletions

View File

@ -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)));
}
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)));
}
@ -304,8 +304,8 @@ std::vector<int> CBattleLogic::GetAvailableHexesForAttacker(const CStack *defend
{
int x = m_battleHelper.DecodeXPosition(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
typedef std::pair<int, int> hexPoint;
std::list<hexPoint> candidates;
@ -558,7 +558,7 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
// if double wide calculate tail
int tail_pos = -1;
if (attackerStack->creature->isDoubleWide())
if (attackerStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
{
int x_pos = m_battleHelper.DecodeXPosition(attackerStack->position);
int y_pos = m_battleHelper.DecodeYPosition(attackerStack->position);

View File

@ -428,17 +428,17 @@ std::vector<CObstacleInstance> CCallback::battleGetAllObstacles()
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);
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);
if(!gs->curB) return NULL;
return gs->curB->getStack(ID);
return gs->curB->getStack(ID, onlyAlive);
}
int CCallback::battleMakeAction(BattleAction* action)
@ -448,10 +448,10 @@ int CCallback::battleMakeAction(BattleAction* action)
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);
return battleGetStackByID(battleGetStack(pos));
return battleGetStackByID(battleGetStack(pos, onlyAlive), onlyAlive);
}
int CCallback::battleGetPos(int stack)

View File

@ -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 battleGetObstaclesAtTile(int tile)=0; //returns bitfield
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 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
@ -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 battleGetObstaclesAtTile(int tile); //returns bitfield
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 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

View File

@ -117,6 +117,7 @@ public:
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 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
{

View File

@ -86,7 +86,7 @@ CBattleInterface::CBattleInterface(CCreatureSet * army1, CCreatureSet * army2, C
std::map<int, CStack> stacks = LOCPLINT->cb->battleGetStacks();
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]->setType(2);
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
{
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);
//setting console text
@ -805,7 +805,12 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
}
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
switch(spellSelMode)
{
@ -880,14 +885,14 @@ bool CBattleInterface::reverseCreature(int number, int hex, bool wideTrick)
}
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.y = coords.second;
if(wideTrick && curs.creature->isDoubleWide())
if(wideTrick && curs->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
{
if(curs.attackerOwned)
if(curs->attackerOwned)
{
if(!creDir[number])
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 steps = creAnims[number]->framesInGroup(0)*getAnimSpeedMultiplier()-1;
int hexWbase = 44, hexHbase = 42;
bool twoTiles = LOCPLINT->cb->battleGetCreature(number).isDoubleWide();
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)
{
@ -1070,7 +1076,7 @@ void CBattleInterface::stackMoved(int number, int destHex, bool endMoving, int d
//step shift calculation
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;
@ -1139,7 +1145,7 @@ void CBattleInterface::stackMoved(int number, int destHex, bool endMoving, int d
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;
if(!endMoving && twoTiles && (movedStack->owner == attackingHeroInstance->tempOwner) && (creDir[number] != (movedStack->owner == attackingHeroInstance->tempOwner))) //big attacker creature is reversed
creAnims[number]->pos.x -= 44;
@ -1200,17 +1206,17 @@ void CBattleInterface::stacksAreAttacked(std::vector<CBattleInterface::SStackAtt
int maxLen = 0;
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)
{
CGI->soundh->playSound(attacked.creature->sounds.killed);
CGI->soundh->playSound(attacked->creature->sounds.killed);
creAnims[attackedInfos[g].ID]->setType(5); //death
}
else
{
// 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
}
}
@ -1275,7 +1281,7 @@ void CBattleInterface::stackAttacking(int ID, int dest)
int reversedShift = 0; //shift of attacking stack's position due to reversing
if(aStack.attackerOwned)
{
if(aStack.creature->isDoubleWide())
if(aStack.hasFeatureOfType(StackFeature::DOUBLE_WIDE))
{
switch(BattleInfo::mutualPosition(aStack.position, dest)) //attack direction
{
@ -1303,7 +1309,7 @@ void CBattleInterface::stackAttacking(int ID, int dest)
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
{
@ -1327,7 +1333,7 @@ void CBattleInterface::stackAttacking(int ID, int dest)
}
else //if(aStack.attackerOwned)
{
if(aStack.creature->isDoubleWide())
if(aStack.hasFeatureOfType(StackFeature::DOUBLE_WIDE))
{
switch(BattleInfo::mutualPosition(aStack.position, dest)) //attack direction
{
@ -1356,7 +1362,7 @@ void CBattleInterface::stackAttacking(int ID, int dest)
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
{
@ -1474,7 +1480,7 @@ void CBattleInterface::handleEndOfMove(int stackNumber, int destinationTile)
CGI->soundh->stopSound(moveSh);
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)
@ -1487,18 +1493,19 @@ void CBattleInterface::hexLclicked(int whichOne)
{
//checking destination
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)
{
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;
break;
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;
break;
case 3:
if(!LOCPLINT->cb->battleGetStackByPos(whichOne))
if(!LOCPLINT->cb->battleGetStackByPos(whichOne, onlyAlive))
allowCasting = false;
break;
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
{
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);
int shiftedDest = whichOne + (LOCPLINT->cb->battleGetStackByID(activeStack)->attackerOwned ? 1 : -1);
@ -1580,7 +1587,7 @@ void CBattleInterface::hexLclicked(int whichOne)
break;
}
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);
if(vstd::contains(acc, whichOne))
@ -1628,7 +1635,7 @@ void CBattleInterface::hexLclicked(int whichOne)
break;
}
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);
if(vstd::contains(acc, whichOne))
@ -1670,8 +1677,8 @@ void CBattleInterface::stackIsShooting(int ID, int dest)
spi.frameNum = 0;
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
if(projectileAngle > straightAngle) //upper shot
@ -1766,7 +1773,7 @@ void CBattleInterface::spellCast(SpellCast * sc)
//initial variables
std::string animToDisplay;
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;
//animation angle
@ -1814,9 +1821,11 @@ void CBattleInterface::spellCast(SpellCast * sc)
break;
case 35: //dispel
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)
{
displayEffect(spell.mainEffectAnim, LOCPLINT->cb->battleGetStackByID(*it)->position);
displayEffect(spell.mainEffectAnim, LOCPLINT->cb->battleGetStackByID(*it, false)->position);
}
break;
} //switch(sc->id)
@ -2002,7 +2011,7 @@ void CBattleInterface::attackingShowHelper()
{
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)
{
// 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
// attackingInfo->sh
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);
}
else
{
// TODO: see comment above
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);
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);
}
@ -2043,7 +2052,7 @@ void CBattleInterface::attackingShowHelper()
CStack aStack = *aStackp;
if(aStack.attackerOwned)
{
if(aStack.creature->isDoubleWide())
if(aStack.hasFeatureOfType(StackFeature::DOUBLE_WIDE))
{
switch(BattleInfo::mutualPosition(aStack.position, attackingInfo->dest)) //attack direction
{
@ -2070,7 +2079,7 @@ void CBattleInterface::attackingShowHelper()
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
{
@ -2094,7 +2103,7 @@ void CBattleInterface::attackingShowHelper()
}
else //if(aStack.attackerOwned)
{
if(aStack.creature->isDoubleWide())
if(aStack.hasFeatureOfType(StackFeature::DOUBLE_WIDE))
{
switch(BattleInfo::mutualPosition(aStack.position, attackingInfo->dest)) //attack direction
{
@ -2122,7 +2131,7 @@ void CBattleInterface::attackingShowHelper()
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
{
@ -2186,20 +2195,20 @@ void CBattleInterface::redrawBackgroundWithHexes(int activeStack)
void CBattleInterface::printConsoleAttacked(int ID, int dmg, int killed, int IDby)
{
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);
if(killed > 0)
{
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
{
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;
}
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
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);
}
//shifting position for double - hex creatures
if(creature && creature->isDoubleWide())
if(stack && stack->hasFeatureOfType(StackFeature::DOUBLE_WIDE))
{
if(attacker)
{

View File

@ -61,7 +61,7 @@ public:
//CStack * ourStack;
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
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
void hover (bool on);
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'
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
void endCastingSpell(); //ends casting spell (eg. when spell has been cast or cancelled)

View File

@ -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
{
boost::unique_lock<boost::recursive_mutex> un(*pim);

View File

@ -192,6 +192,7 @@ public:
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 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
//-------------//

View File

@ -434,6 +434,17 @@ void BattleResultsApplied::applyCl( CClient *cl )
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 )
{
return cl->gs;

View File

@ -3,8 +3,8 @@
clientSettings
{
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
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

View File

@ -38,8 +38,8 @@
35 0 41 0 0 0 X
36 1 -1 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
41 1 36 0 0 0 X
42 -1 40 0 0 0 X

View File

@ -324,10 +324,18 @@ void CCreatureHandler::loadCreatures()
case '-': //remove ability
{
int creatureID;
ui32 type;
std::string type;
reader >> creatureID;
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;
break;

View File

@ -239,24 +239,24 @@ static CGObjectInstance * createObject(int id, int subid, int3 pos, int owner)
nobj->defInfo = VLC->dobjinfo->gobjs[id][subid];
return nobj;
}
CStack * BattleInfo::getStack(int stackID)
CStack * BattleInfo::getStack(int stackID, bool onlyAlive)
{
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 NULL;
}
CStack * BattleInfo::getStackT(int tileID)
CStack * BattleInfo::getStackT(int tileID, bool onlyAlive)
{
for(unsigned int g=0; g<stacks.size(); ++g)
{
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];
}
@ -273,7 +273,7 @@ void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool atta
continue;
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)
accessibility[stacks[g]->position-1] = false;
@ -366,12 +366,12 @@ std::vector<int> BattleInfo::getAccessibility(int stackID, bool addOccupiable)
CStack *s = getStack(stackID);
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];
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)
{
@ -398,7 +398,7 @@ std::vector<int> BattleInfo::getAccessibility(int stackID, bool addOccupiable)
for(int i=0; i < BFIELD_SIZE ; ++i)
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() ) &&
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
)
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
|| 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;
}
int CGameState::battleGetStack(int pos)
int CGameState::battleGetStack(int pos, bool onlyAlive)
{
if(!curB)
return -1;
for(unsigned int g=0; g<curB->stacks.size(); ++g)
{
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]->alive()
&& (!onlyAlive || curB->stacks[g]->alive())
)
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<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
{
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 */
{
CStack * st = getStackT(destinationTile);
CStack * st = getStackT(destinationTile, onlyAlive);
if(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 )
)
{
attackedCres.insert(stacks[it]);
if(!onlyAlive || stacks[it]->alive())
attackedCres.insert(stacks[it]);
}
}
} //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
{
CStack * st = getStackT(destinationTile);
CStack * st = getStackT(destinationTile, onlyAlive);
if(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)
{
CStack * st = getStackT(*it);
CStack * st = getStackT(*it, onlyAlive);
if(st)
attackedCres.insert(st);
}

View File

@ -123,8 +123,8 @@ struct DLL_EXPORT BattleInfo
}
CStack * getNextStack(); //which stack will have turn after current one
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
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
@ -273,7 +273,7 @@ public:
bool battleMoveCreatureStack(int ID, int dest);
bool battleAttackCreatureStack(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
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);

View File

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

View File

@ -882,8 +882,18 @@ DLL_EXPORT void StacksHealedOrResurrected::applyGs( CGameState *gs )
for(int g=0; g<healedStacks.size(); ++g)
{
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
{
for(int h=0; h<changedStack->effects.size(); ++h)

View File

@ -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)
if(!stackAtEnd && curStack->creature->isDoubleWide() && !accessibility[dest])
if(!stackAtEnd && curStack->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && !accessibility[dest])
{
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
// 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)
{
@ -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
{
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;
}
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;
}
@ -2370,11 +2370,11 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
if( !(
(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)
|| (stackAtEnd->creature->isDoubleWide() //front <=> back
|| (stackAtEnd->hasFeatureOfType(StackFeature::DOUBLE_WIDE) //front <=> back
&& 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)
)
)
@ -2646,6 +2646,34 @@ static ui32 calculateSpellDmg(const CSpell * sp, const CGHeroInstance * caster,
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)
{
std::vector<ui32> ret;
@ -2831,17 +2859,19 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
break;
}
case 37: //cure
case 38: //resurrection
case 39: //animate dead
{
StacksHealedOrResurrected shr;
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;
StacksHealedOrResurrected::HealInfo hi;
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);
}
if(!shr.healedStacks.empty())