Explorar o código

* life drain implemented
* fixed #134
* fixed damage calculation formula

mateuszb %!s(int64=15) %!d(string=hai) anos
pai
achega
dbbbaa1e18

+ 2 - 1
CGameInterface.h

@@ -106,13 +106,14 @@ public:
 	virtual void battleStacksAttacked(std::vector<BattleStackAttacked> & 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<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
+	virtual void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & 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<si32> & 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

+ 19 - 7
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<int, CStack> stacks = curInt->cb->battleGetStacks();
 	for(std::map<int, CStack>::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

+ 1 - 0
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<SStackAttackedInfo> 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

+ 34 - 2
client/CPlayerInterface.cpp

@@ -534,7 +534,7 @@ void CPlayerInterface::battlefieldPrepared(int battlefieldType, std::vector<CObs
 {
 }
 
-void CPlayerInterface::battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks)
+void CPlayerInterface::battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, si32 lifeDrainFrom)
 {
 	if(LOCPLINT != this)
 	{ //another local interface should do this
@@ -550,6 +550,26 @@ void CPlayerInterface::battleStacksHealedRes(const std::vector<std::pair<ui32, u
 			battleInt->creAnims[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<BattleStackAttacked> & 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);
-}
+}
+
+void CPlayerInterface::battleNewRoundFirst( int round )
+{
+	if(LOCPLINT != this)
+	{ //another local interface should do this
+		return;
+	}
+
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
+	battleInt->newRoundFirst(round);
+}

+ 2 - 1
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<BattleStackAttacked> & 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<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
+	void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & 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<si32> & 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

+ 17 - 2
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; g<bsa.size(); ++g)
+	{
+		for (int z=0; z<bsa[g].healedStacks.size(); ++z)
+		{
+			bsa[g].healedStacks[z].applyCl(cl);
+		}
+	}
 }
 
 void BattleAttack::applyCl( CClient *cl )
@@ -555,8 +570,8 @@ void StacksHealedOrResurrected::applyCl( CClient *cl )
 	{
 		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);
+	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 )

+ 2 - 2
lib/CGameState.cpp

@@ -2441,11 +2441,11 @@ std::pair<ui32, ui32> 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

+ 38 - 30
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 <typename Handler> 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 <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & stackID & healedHP & lowLevelResurrection;
+		}
+	};
+
+	std::vector<HealInfo> 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 <typename Handler> 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<StacksHealedOrResurrected> 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 <typename Handler> 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 <typename Handler> void serialize(Handler &h, const int version)
-		{
-			h & stackID & healedHP & lowLevelResurrection;
-		}
-	};
-
-	std::vector<HealInfo> healedStacks;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & healedStacks;
-	}
-};
-
 struct ObstaclesRemoved : public CPackForClient //3014
 {
 	ObstaclesRemoved(){type = 3014;}

+ 7 - 1
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; g<healedStacks.size(); ++g)
+	{
+		healedStacks[g].applyGs(gs);
+	}
 }
 
 DLL_EXPORT void BattleAttack::applyGs( CGameState *gs )
@@ -1121,7 +1127,7 @@ DLL_EXPORT void StacksHealedOrResurrected::applyGs( CGameState *gs )
 
 		//applying changes
 		bool resurrected = !changedStack->alive(); //indicates if stack is resurrected or just healed
-		if(!changedStack->alive())
+		if(resurrected)
 		{
 			changedStack->state.insert(ALIVE);
 			if(healedStacks[g].lowLevelResurrection)

+ 25 - 0
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<int>(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<int> 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<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
 			{
 				if(vstd::contains(sc.resisted, (*it)->ID) //this creature resisted the spell