diff --git a/CGameInterface.h b/CGameInterface.h index 3bb0d9e3d..202d9f53d 100644 --- a/CGameInterface.h +++ b/CGameInterface.h @@ -106,13 +106,14 @@ public: virtual void battleStacksAttacked(std::vector & bsa){}; //called when stack receives damage (after battleAttack()) virtual void battleEnd(BattleResult *br){}; virtual void battleResultsApplied(){}; //called when all effects of last battle are applied + virtual void battleNewRoundFirst(int round){}; //called at the beginning of each turn before changes are applied; virtual void battleNewRound(int round){}; //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn virtual void battleStackMoved(int ID, int dest, int distance, bool end){}; virtual void battleSpellCast(SpellCast *sc){}; virtual void battleStacksEffectsSet(SetStackEffect & sse){};//called when a specific effect is set to stacks virtual void battleStart(const CCreatureSet *army1, const 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 obstacles){}; //called when battlefield is prepared, prior the battle beginning - virtual void battleStacksHealedRes(const std::vector > & healedStacks){}; //called when stacks are healed / resurrected first element of pair - stack id, second - healed hp + virtual void battleStacksHealedRes(const std::vector > & healedStacks, bool lifeDrain, si32 lifeDrainFrom){}; //called when stacks are healed / resurrected first element of pair - stack id, second - healed hp virtual void battleNewStackAppeared(int stackID){}; //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned virtual void battleObstaclesRemoved(const std::set & removedObstacles){}; //called when a certain set of obstacles is removed from batlefield; IDs of them are given virtual void battleCatapultAttacked(const CatapultAttack & ca){}; //called when catapult makes an attack diff --git a/client/CBattleInterface.cpp b/client/CBattleInterface.cpp index 1aeb1bade..33d09413c 100644 --- a/client/CBattleInterface.cpp +++ b/client/CBattleInterface.cpp @@ -2227,18 +2227,18 @@ void CBattleInterface::stackAttacking(int ID, int dest, int attackedID) addNewAnim(new CMeleeAttack(this, ID, dest, attackedID)); } -void CBattleInterface::newRound(int number) +void CBattleInterface::newRoundFirst( int round ) { - console->addText(CGI->generaltexth->allTexts[412]); - - //unlock spellbook - //bSpell->block(!curInt->cb->battleCanCastSpell()); - //don't unlock spellbook - this should be done when we have axctive creature - //handle regeneration std::map stacks = curInt->cb->battleGetStacks(); for(std::map::const_iterator it = stacks.begin(); it != stacks.end(); ++it) { + //don't show animation when no HP is regenerated + if (it->second.firstHPleft == it->second.MaxHealth()) + { + continue; + } + if( it->second.hasBonusOfType(Bonus::HP_REGENERATION) && it->second.alive() ) displayEffect(74, it->second.position); @@ -2250,6 +2250,17 @@ void CBattleInterface::newRound(int number) } } +void CBattleInterface::newRound(int number) +{ + console->addText(CGI->generaltexth->allTexts[412]); + + //unlock spellbook + //bSpell->block(!curInt->cb->battleCanCastSpell()); + //don't unlock spellbook - this should be done when we have axctive creature + + +} + void CBattleInterface::giveCommand(ui8 action, ui16 tile, ui32 stack, si32 additional) { if(!curInt->cb->battleGetStackByID(stack) && action != 1 && action != 4 && action != 5) @@ -3237,6 +3248,7 @@ void CBattleInterface::startAction(const BattleAction* action) } } + void CBattleHero::show(SDL_Surface *to) { //animation of flag diff --git a/client/CBattleInterface.h b/client/CBattleInterface.h index 749eeef44..c9c3013f1 100644 --- a/client/CBattleInterface.h +++ b/client/CBattleInterface.h @@ -495,6 +495,7 @@ public: void stackMoved(int number, int destHex, bool endMoving, int distance); //stack with id number moved to destHex void stacksAreAttacked(std::vector attackedInfos); //called when a certain amount of stacks has been attacked void stackAttacking(int ID, int dest, int attackedID); //called when stack with id ID is attacking something on hex dest + void newRoundFirst( int round ); void newRound(int number); //caled when round is ended; number is the number of round void hexLclicked(int whichOne); //hex only call-in void stackIsShooting(int ID, int dest, int attackedID); //called when stack with id ID is shooting to hex dest diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index f3e836ab5..ac6ac153f 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -534,7 +534,7 @@ void CPlayerInterface::battlefieldPrepared(int battlefieldType, std::vector > & healedStacks) +void CPlayerInterface::battleStacksHealedRes(const std::vector > & healedStacks, bool lifeDrain, si32 lifeDrainFrom) { if(LOCPLINT != this) { //another local interface should do this @@ -550,6 +550,26 @@ void CPlayerInterface::battleStacksHealedRes(const std::vectorcreAnims[healed->ID]->setType(2); } } + + if (lifeDrain) + { + const CStack *attacker = cb->battleGetStackByID(healedStacks[0].first, false); + const CStack *defender = cb->battleGetStackByID(lifeDrainFrom, false); + if (attacker) + { + battleInt->displayEffect(50, attacker->position); + } + //print info about life drain + int textOff = 0; + if (attacker->count > 1) + { + textOff += 1; + } + char textBuf[1000]; + sprintf(textBuf, CGI->generaltexth->allTexts[361 + textOff].c_str(), attacker->getCreature()->nameSing.c_str(), + healedStacks[0].second, defender->getCreature()->namePl.c_str()); + battleInt->console->addText(textBuf); + } } void CPlayerInterface::battleNewStackAppeared(int stackID) @@ -740,6 +760,7 @@ void CPlayerInterface::battleStacksAttacked(std::vector & b } SStackAttackedInfo to_put = {i->stackAttacked, i->damageAmount, i->killedAmount, i->attackerID, LOCPLINT->curAction->actionType==7, i->killed()}; arg.push_back(to_put); + } if(bsa.begin()->isEffect() && bsa.begin()->effect == 12) //for armageddon - I hope this condition is enough @@ -1909,4 +1930,15 @@ void CPlayerInterface::updateInfo(const CGObjectInstance * specific) adventureInt->infoBar.updateSelection(specific); // if (adventureInt->selection == specific) // adventureInt->infoBar.showAll(screen); -} \ No newline at end of file +} + +void CPlayerInterface::battleNewRoundFirst( int round ) +{ + if(LOCPLINT != this) + { //another local interface should do this + return; + } + + boost::unique_lock un(*pim); + battleInt->newRoundFirst(round); +} diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index e84421596..a24d16518 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -184,6 +184,7 @@ public: void battleAttack(BattleAttack *ba); //stack performs attack void battleEnd(BattleResult *br); //end of battle //void battleResultQuited(); + void battleNewRoundFirst(int round); //called at the beginning of each turn before changes are applied; used for HP regen handling void battleNewRound(int round); //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn void battleStackMoved(int ID, int dest, int distance, bool end); void battleSpellCast(SpellCast *sc); @@ -191,7 +192,7 @@ public: void battleStacksAttacked(std::vector & bsa); void battleStart(const CCreatureSet *army1, const 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 obstacles); //called when battlefield is prepared, prior the battle beginning - void battleStacksHealedRes(const std::vector > & healedStacks); //called when stacks are healed / resurrected + void battleStacksHealedRes(const std::vector > & healedStacks, bool lifeDrain, si32 lifeDrainFrom); //called when stacks are healed / resurrected void battleNewStackAppeared(int stackID); //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned void battleObstaclesRemoved(const std::set & removedObstacles); //called when a certain set of obstacles is removed from batlefield; IDs of them are given void battleCatapultAttacked(const CatapultAttack & ca); //called when catapult makes an attack diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 61f6771e9..e4756c700 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -436,6 +436,14 @@ void BattleStart::applyCl( CClient *cl ) cl->playerint[info->side2]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 1); } +void BattleNextRound::applyFirstCl(CClient *cl) +{ + if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end()) + cl->playerint[GS(cl)->curB->side1]->battleNewRoundFirst(round); + if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end()) + cl->playerint[GS(cl)->curB->side2]->battleNewRoundFirst(round); +} + void BattleNextRound::applyCl( CClient *cl ) { if(cl->playerint.find(GS(cl)->curB->side1) != cl->playerint.end()) @@ -489,6 +497,13 @@ void BattleAttack::applyFirstCl( CClient *cl ) cl->playerint[GS(cl)->curB->side1]->battleAttack(this); if(cl->playerint.find(GS(cl)->curB->side2) != cl->playerint.end()) cl->playerint[GS(cl)->curB->side2]->battleAttack(this); + for (int g=0; gcurB->side1, battleStacksHealedRes, shiftedHealed); - INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2, battleStacksHealedRes, shiftedHealed); + INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1, battleStacksHealedRes, shiftedHealed, lifeDrain, drainedFrom); + INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2, battleStacksHealedRes, shiftedHealed, lifeDrain, drainedFrom); } void ObstaclesRemoved::applyCl( CClient *cl ) diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index e52c9b1a8..4c30629f9 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2441,11 +2441,11 @@ std::pair BattleInfo::calculateDmgRange( const CStack* attacker, con float dec = 0.025f * (-attackDefenceDifference); if(dec > 0.7f) { - multBonus *= 1.0f - dec; + multBonus *= 0.3f; //1.0 - 0.7 } else { - multBonus *= dec; + multBonus *= 1.0f - dec; } } else //increasing dmg diff --git a/lib/NetPacks.h b/lib/NetPacks.h index b7a0dab8e..41f050598 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -860,9 +860,9 @@ struct BattleStart : public CPackForClient//3000 struct BattleNextRound : public CPackForClient//3001 { BattleNextRound(){type = 3001;}; + void applyFirstCl(CClient *cl); void applyCl(CClient *cl); - DLL_EXPORT void applyGs(CGameState *gs); - + DLL_EXPORT void applyGs( CGameState *gs ); si32 round; template void serialize(Handler &h, const int version) @@ -916,6 +916,34 @@ struct BattleStackMoved : public CPackForClient//3004 } }; +struct StacksHealedOrResurrected : public CPackForClient //3013 +{ + StacksHealedOrResurrected(){type = 3013;} + + DLL_EXPORT void applyGs(CGameState *gs); + void applyCl(CClient *cl); + + struct HealInfo + { + ui32 stackID; + ui32 healedHP; + ui8 lowLevelResurrection; //in case this stack is resurrected by this heal, it will be marked as SUMMONED + template void serialize(Handler &h, const int version) + { + h & stackID & healedHP & lowLevelResurrection; + } + }; + + std::vector healedStacks; + ui8 lifeDrain; //if true, this heal is an effect of life drain + si32 drainedFrom; //if life drain - then stack life was drain from + + template void serialize(Handler &h, const int version) + { + h & healedStacks & lifeDrain & drainedFrom; + } +}; + struct BattleStackAttacked : public CPackForClient//3005 { BattleStackAttacked(){flags = 0; type = 3005;}; @@ -926,18 +954,24 @@ struct BattleStackAttacked : public CPackForClient//3005 ui32 newAmount, newHP, killedAmount, damageAmount; ui8 flags; //1 - is stack killed; 2 - is there special effect to be shown; ui32 effect; //set only if flag 2 is present + std::vector healedStacks; //used when life drain bool killed() const//if target stack was killed { return flags & 1; } - bool isEffect() const//if target stack was killed + bool isEffect() const//if stack has been attacked by a spell { return flags & 2; } + bool lifeDrain() const //if this attack involves life drain effect + { + return healedStacks.size() > 0; + } template void serialize(Handler &h, const int version) { - h & stackAttacked & attackerID & newAmount & newHP & flags & killedAmount & damageAmount & effect; + h & stackAttacked & attackerID & newAmount & newHP & flags & killedAmount & damageAmount & effect + & healedStacks; } bool operator<(const BattleStackAttacked &b) const { @@ -1067,32 +1101,6 @@ struct BattleResultsApplied : public CPackForClient //3012 } }; -struct StacksHealedOrResurrected : public CPackForClient //3013 -{ - StacksHealedOrResurrected(){type = 3013;} - - DLL_EXPORT void applyGs(CGameState *gs); - void applyCl(CClient *cl); - - struct HealInfo - { - ui32 stackID; - ui32 healedHP; - ui8 lowLevelResurrection; //in case this stack is resurrected by this heal, it will be marked as SUMMONED - template void serialize(Handler &h, const int version) - { - h & stackID & healedHP & lowLevelResurrection; - } - }; - - std::vector healedStacks; - - template void serialize(Handler &h, const int version) - { - h & healedStacks; - } -}; - struct ObstaclesRemoved : public CPackForClient //3014 { ObstaclesRemoved(){type = 3014;} diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 4359fd3d1..e4e7a1d83 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -770,6 +770,12 @@ DLL_EXPORT void BattleStackAttacked::applyGs( CGameState *gs ) at->firstHPleft = newHP; if(killed()) at->state -= ALIVE; + + //life drain handling + for (int g=0; galive(); //indicates if stack is resurrected or just healed - if(!changedStack->alive()) + if(resurrected) { changedStack->state.insert(ALIVE); if(healedStacks[g].lowLevelResurrection) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 4b1417db6..358900976 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -654,6 +654,28 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt int dmg = bsa->damageAmount; prepareAttacked(*bsa, def); + //life drain handling + if (att->hasBonusOfType(Bonus::LIFE_DRAIN)) + { + StacksHealedOrResurrected shi; + shi.lifeDrain = true; + shi.drainedFrom = def->ID; + + StacksHealedOrResurrected::HealInfo hi; + hi.stackID = att->ID; + hi.healedHP = std::min(dmg, att->MaxHealth() - att->firstHPleft + att->MaxHealth() * (att->baseAmount - att->count) ); + hi.lowLevelResurrection = false; + shi.healedStacks.push_back(hi); + + if (hi.healedHP > 0) + { + bsa->healedStacks.push_back(shi); + } + } + else + { + } + //fire shield handling if ( !bat.shot() && def->hasBonusOfType(Bonus::FIRE_SHIELD) && !bsa->killed() ) { @@ -667,6 +689,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt bsa->damageAmount = (dmg * def->valOfBonuses(Bonus::FIRE_SHIELD)) / 100; prepareAttacked(*bsa, att); } + } void CGameHandler::handleConnection(std::set players, CConnection &c) { @@ -3290,6 +3313,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) else { StacksHealedOrResurrected shr; + shr.lifeDrain = false; StacksHealedOrResurrected::HealInfo hi; hi.healedHP = healed; @@ -3647,6 +3671,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, int destinatio case 39: //animate dead { StacksHealedOrResurrected shr; + shr.lifeDrain = false; for(std::set::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) { if(vstd::contains(sc.resisted, (*it)->ID) //this creature resisted the spell