Răsfoiți Sursa

* implemented 206 and 207 from mantis

mateuszb 16 ani în urmă
părinte
comite
db4ee5844f
8 a modificat fișierele cu 159 adăugiri și 84 ștergeri
  1. 19 0
      CCallback.cpp
  2. 4 0
      CCallback.h
  3. 23 0
      client/CBattleInterface.cpp
  4. 11 1
      client/CSpellWindow.cpp
  5. 89 0
      lib/CGameState.cpp
  6. 1 0
      lib/CGameState.h
  7. 2 1
      lib/NetPacks.h
  8. 10 82
      server/CGameHandler.cpp

+ 19 - 0
CCallback.cpp

@@ -124,6 +124,17 @@ int CCallback::getSpellCost(const CSpell * sp, const CGHeroInstance * caster) co
 	return sp->costs[caster->getSpellSchoolLevel(sp)];
 }
 
+int CCallback::estimateSpellDamage(const CSpell * sp) const
+{
+	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
+
+	if(!gs->curB)
+		return 0;
+
+	const CGHeroInstance * ourHero = gs->curB->heroes[0]->tempOwner == player ? gs->curB->heroes[0] : gs->curB->heroes[1];
+	return gs->curB->calculateSpellDmg(sp, ourHero, NULL);
+}
+
 int CCallback::howManyTowns() const
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
@@ -640,6 +651,14 @@ ui8 CCallback::battleGetSiegeLevel()
 	return gs->curB->siege;
 }
 
+const CGHeroInstance * CCallback::battleGetFightingHero(ui8 side) const
+{
+	if(!gs->curB)
+		return 0;
+
+	return gs->curB->heroes[side];
+}
+
 void CCallback::swapGarrisonHero( const CGTownInstance *town )
 {
 	if(town->tempOwner != player) return;

+ 4 - 0
CCallback.h

@@ -131,6 +131,7 @@ public:
 	virtual const StartInfo * getStartInfo()const =0;
 	virtual const CMapHeader * getMapHeader()const =0;
 	virtual int getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const =0; //when called during battle, takes into account creatures' spell cost reduction
+	virtual int estimateSpellDamage(const CSpell * sp) const =0; //estimates damage of given spell; returns 0 if spell causes no dmg
 
 	//hero
 	virtual int howManyHeroes(bool includeGarrisoned = true)const =0;
@@ -188,6 +189,7 @@ public:
 	virtual int battleGetWallUnderHex(int hex)=0; //returns part of destructible wall / gate / keep under given hex or -1 if not found
 	virtual std::pair<ui32, ui32> battleEstimateDamage(int attackerID, int defenderID)=0; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>
 	virtual ui8 battleGetSiegeLevel()=0; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
+	virtual const CGHeroInstance * battleGetFightingHero(ui8 side) const =0; //returns hero corresponding ot given side (0 - attacker, 1 - defender)
 };
 
 struct HeroMoveDetails
@@ -260,6 +262,7 @@ public:
 	const StartInfo * getStartInfo() const;
 	const CMapHeader * getMapHeader()const ;
 	int getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //when called during battle, takes into account creatures' spell cost reduction
+	int estimateSpellDamage(const CSpell * sp) const; //estimates damage of given spell; returns 0 if spell causes no dmg
 
 	std::vector < const CGObjectInstance * > getBlockingObjs(int3 pos) const;
 	std::vector < const CGObjectInstance * > getVisitableObjs(int3 pos) const;
@@ -299,6 +302,7 @@ public:
 	int battleGetWallUnderHex(int hex); //returns part of destructible wall / gate / keep under given hex or -1 if not found
 	std::pair<ui32, ui32> battleEstimateDamage(int attackerID, int defenderID); //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>
 	ui8 battleGetSiegeLevel(); //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
+	const CGHeroInstance * battleGetFightingHero(ui8 side) const; //returns hero corresponding ot given side (0 - attacker, 1 - defender)
 
 //XXX hmmm _tmain on _GNUC_ wtf?
 //friends

+ 23 - 0
client/CBattleInterface.cpp

@@ -2579,6 +2579,29 @@ void CBattleInterface::spellCast(SpellCast * sc)
 		int tile = LOCPLINT->cb->battleGetStackByID(sc->resisted[j])->position;
 		displayEffect(78, tile);
 	}
+
+	//displaying message in console
+	if(sc->affectedCres.size() == 1)
+	{
+		std::string text = CGI->generaltexth->allTexts[195];
+		boost::algorithm::replace_first(text, "%s", LOCPLINT->cb->battleGetFightingHero(sc->side)->name);
+		boost::algorithm::replace_first(text, "%s", CGI->spellh->spells[sc->id].name);
+		boost::algorithm::replace_first(text, "%s", LOCPLINT->cb->battleGetStackByID(*sc->affectedCres.begin(), false)->creature->namePl );
+		console->addText(text);
+	}
+	else
+	{
+		std::string text = CGI->generaltexth->allTexts[196];
+		boost::algorithm::replace_first(text, "%s", LOCPLINT->cb->battleGetFightingHero(sc->side)->name);
+		boost::algorithm::replace_first(text, "%s", CGI->spellh->spells[sc->id].name);
+		console->addText(text);
+	}
+	if(sc->dmgToDisplay != 0)
+	{
+		std::string dmgInfo = CGI->generaltexth->allTexts[343].substr(1, CGI->generaltexth->allTexts[343].size() - 1);
+		boost::algorithm::replace_first(dmgInfo, "%d", boost::lexical_cast<std::string>(sc->dmgToDisplay));
+		console->addText(dmgInfo);
+	}
 }
 
 void CBattleInterface::battleStacksEffectsSet(const SetStackEffect & sse)

+ 11 - 1
client/CSpellWindow.cpp

@@ -677,9 +677,19 @@ void CSpellWindow::SpellArea::clickRight(tribool down, bool previousState)
 {
 	if(down && mySpell != -1)
 	{
+		std::string dmgInfo;
+		int causedDmg = LOCPLINT->cb->estimateSpellDamage( &CGI->spellh->spells[mySpell] );
+		if(causedDmg == 0)
+			dmgInfo = "";
+		else
+		{
+			dmgInfo = CGI->generaltexth->allTexts[343];
+			boost::algorithm::replace_first(dmgInfo, "%d", boost::lexical_cast<std::string>(causedDmg));
+		}
+
 		SDL_Surface *spellBox = CMessage::drawBoxTextBitmapSub(
 			LOCPLINT->playerID,
-			CGI->spellh->spells[mySpell].descriptions[0], this->owner->spells->ourImages[mySpell].bitmap,
+			CGI->spellh->spells[mySpell].descriptions[0] + dmgInfo, this->owner->spells->ourImages[mySpell].bitmap,
 			CGI->spellh->spells[mySpell].name,30,30);
 		CInfoPopup *vinya = new CInfoPopup(spellBox, true);
 		GH.pushInt(vinya);

+ 89 - 0
lib/CGameState.cpp

@@ -22,6 +22,7 @@
 #include <boost/lexical_cast.hpp>
 #include <boost/thread.hpp>
 #include <boost/thread/shared_mutex.hpp>
+#include <boost/assign/list_of.hpp>
 #include "RegisterTypes.cpp"
 
 boost::rand48 ran;
@@ -2739,6 +2740,94 @@ std::pair<const CStack *, int> BattleInfo::getNearestStack(const CStack * closes
 	return std::make_pair<const CStack * , int>(NULL, -1);
 }
 
+ui32 BattleInfo::calculateSpellDmg(const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature) const
+{
+	ui32 ret = 0; //value to return
+
+	//15 - magic arrows, 16 - ice bolt, 17 - lightning bolt, 18 - implosion, 20 - frost ring, 21 - fireball, 22 - inferno, 23 - meteor shower,
+	//24 - death ripple, 25 - destroy undead, 26 - armageddon
+	static std::map <int, int> dmgMultipliers = boost::assign::map_list_of(15, 10)(16, 20)(17, 25)(18, 75)(20, 10)(21, 10)(22, 10)(23, 10)(24, 5)(25, 10)(26, 50);
+
+	//check if spell really does damage - if not, return 0
+	if(dmgMultipliers.find(sp->id) == dmgMultipliers.end())
+		return 0;
+
+	ret = caster->getPrimSkillLevel(2) * dmgMultipliers[sp->id]  +  sp->powers[caster->getSpellSchoolLevel(sp)];
+	
+	//applying sorcerery secondary skill
+	switch(caster->getSecSkillLevel(25))
+	{
+	case 1: //basic
+		ret *= 1.05f;
+		break;
+	case 2: //advanced
+		ret *= 1.1f;
+		break;
+	case 3: //expert
+		ret *= 1.15f;
+		break;
+	}
+	//applying hero bonuses
+	if(sp->air && caster->valOfBonuses(HeroBonus::AIR_SPELL_DMG_PREMY) != 0)
+	{
+		ret *= (100.0f + caster->valOfBonuses(HeroBonus::AIR_SPELL_DMG_PREMY) / 100.0f);
+	}
+	else if(sp->fire && caster->valOfBonuses(HeroBonus::FIRE_SPELL_DMG_PREMY) != 0)
+	{
+		ret *= (100.0f + caster->valOfBonuses(HeroBonus::FIRE_SPELL_DMG_PREMY) / 100.0f);
+	}
+	else if(sp->water && caster->valOfBonuses(HeroBonus::WATER_SPELL_DMG_PREMY) != 0)
+	{
+		ret *= (100.0f + caster->valOfBonuses(HeroBonus::WATER_SPELL_DMG_PREMY) / 100.0f);
+	}
+	else if(sp->earth && caster->valOfBonuses(HeroBonus::EARTH_SPELL_DMG_PREMY) != 0)
+	{
+		ret *= (100.0f + caster->valOfBonuses(HeroBonus::EARTH_SPELL_DMG_PREMY) / 100.0f);
+	}
+
+	//affected creature-specific part
+	if(affectedCreature)
+	{
+		//applying protections - when spell has more then one elements, only one protection should be applied (I think)
+		if(sp->air && affectedCreature->hasFeatureOfType(StackFeature::SPELL_DAMAGE_REDUCTION, 0)) //air spell & protection from air
+		{
+			ret *= affectedCreature->valOfFeatures(StackFeature::SPELL_DAMAGE_REDUCTION, 0);
+			ret /= 100;
+		}
+		else if(sp->fire && affectedCreature->hasFeatureOfType(StackFeature::SPELL_DAMAGE_REDUCTION, 1)) //fire spell & protection from fire
+		{
+			ret *= affectedCreature->valOfFeatures(StackFeature::SPELL_DAMAGE_REDUCTION, 1);
+			ret /= 100;
+		}
+		else if(sp->water && affectedCreature->hasFeatureOfType(StackFeature::SPELL_DAMAGE_REDUCTION, 2)) //water spell & protection from water
+		{
+			ret *= affectedCreature->valOfFeatures(StackFeature::SPELL_DAMAGE_REDUCTION, 2);
+			ret /= 100;
+		}
+		else if (sp->earth && affectedCreature->hasFeatureOfType(StackFeature::SPELL_DAMAGE_REDUCTION, 3)) //earth spell & protection from earth
+		{
+			ret *= affectedCreature->valOfFeatures(StackFeature::SPELL_DAMAGE_REDUCTION, 3);
+			ret /= 100;
+		}
+
+		//general spell dmg reduction
+		if(sp->air && affectedCreature->hasFeatureOfType(StackFeature::SPELL_DAMAGE_REDUCTION, -1)) //air spell & protection from air
+		{
+			ret *= affectedCreature->valOfFeatures(StackFeature::SPELL_DAMAGE_REDUCTION, -1);
+			ret /= 100;
+		}
+
+		//dmg increasing
+		if( affectedCreature->hasFeatureOfType(StackFeature::MORE_DAMAGE_FROM_SPELL, sp->id) )
+		{
+			ret *= 100 + affectedCreature->valOfFeatures(StackFeature::MORE_DAMAGE_FROM_SPELL, sp->id);
+			ret /= 100;
+		}
+	}
+
+	return ret;
+}
+
 bool CGameState::battleCanShoot(int ID, int dest)
 {
 	if(!curB)

+ 1 - 0
lib/CGameState.h

@@ -164,6 +164,7 @@ struct DLL_EXPORT BattleInfo
 	ui32 getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //returns cost of given spell
 	int hexToWallPart(int hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found
 	std::pair<const CStack *, int> getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const; //if attackerOwned is indetermnate, returened stack is of any owner; hex is the number of hex we should be looking from; returns (nerarest creature, predecessorHex)
+	ui32 calculateSpellDmg(const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature) const; //calculates damage inflicted by spell
 };
 
 class DLL_EXPORT CStack

+ 2 - 1
lib/NetPacks.h

@@ -933,6 +933,7 @@ struct SpellCast : public CPackForClient//3009
 	DLL_EXPORT void applyGs(CGameState *gs);
 	void applyCl(CClient *cl);
 
+	si32 dmgToDisplay; //this amount will be displayed as amount of damage dealt by spell
 	ui8 side; //which hero did cast spell: 0 - attacker, 1 - defender
 	ui32 id; //id of spell
 	ui8 skill; //caster's skill level
@@ -941,7 +942,7 @@ struct SpellCast : public CPackForClient//3009
 	std::set<ui32> affectedCres; //ids of creatures affected by this spell, generally used if spell does not set any effect (like dispel or cure)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & side & id & skill & tile & resisted & affectedCres;
+		h & dmgToDisplay & side & id & skill & tile & resisted & affectedCres;
 	}
 };
 

+ 10 - 82
server/CGameHandler.cpp

@@ -2840,87 +2840,6 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message )
 	}
 }
 
-static ui32 calculateSpellDmg(const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature)
-{
-	ui32 ret = 0; //value to return
-
-	//15 - magic arrows, 16 - ice bolt, 17 - lightning bolt, 18 - implosion, 20 - frost ring, 21 - fireball, 22 - inferno, 23 - meteor shower,
-	//24 - death ripple, 25 - destroy undead, 26 - armageddon
-	static std::map <int, int> dmgMultipliers = boost::assign::map_list_of(15, 10)(16, 20)(17, 25)(18, 75)(20, 10)(21, 10)(22, 10)(23, 10)(24, 5)(25, 10)(26, 50);
-
-	ret = caster->getPrimSkillLevel(2) * dmgMultipliers[sp->id]  +  sp->powers[caster->getSpellSchoolLevel(sp)];
-	
-	//applying sorcerery secondary skill
-	switch(caster->getSecSkillLevel(25))
-	{
-	case 1: //basic
-		ret *= 1.05f;
-		break;
-	case 2: //advanced
-		ret *= 1.1f;
-		break;
-	case 3: //expert
-		ret *= 1.15f;
-		break;
-	}
-	//applying hero bonuses
-	if(sp->air && caster->valOfBonuses(HeroBonus::AIR_SPELL_DMG_PREMY) != 0)
-	{
-		ret *= (100.0f + caster->valOfBonuses(HeroBonus::AIR_SPELL_DMG_PREMY) / 100.0f);
-	}
-	else if(sp->fire && caster->valOfBonuses(HeroBonus::FIRE_SPELL_DMG_PREMY) != 0)
-	{
-		ret *= (100.0f + caster->valOfBonuses(HeroBonus::FIRE_SPELL_DMG_PREMY) / 100.0f);
-	}
-	else if(sp->water && caster->valOfBonuses(HeroBonus::WATER_SPELL_DMG_PREMY) != 0)
-	{
-		ret *= (100.0f + caster->valOfBonuses(HeroBonus::WATER_SPELL_DMG_PREMY) / 100.0f);
-	}
-	else if(sp->earth && caster->valOfBonuses(HeroBonus::EARTH_SPELL_DMG_PREMY) != 0)
-	{
-		ret *= (100.0f + caster->valOfBonuses(HeroBonus::EARTH_SPELL_DMG_PREMY) / 100.0f);
-	}
-
-	//applying protections - when spell has more then one elements, only one protection should be applied (I think)
-	if(sp->air && affectedCreature->hasFeatureOfType(StackFeature::SPELL_DAMAGE_REDUCTION, 0)) //air spell & protection from air
-	{
-		ret *= affectedCreature->valOfFeatures(StackFeature::SPELL_DAMAGE_REDUCTION, 0);
-		ret /= 100;
-	}
-	else if(sp->fire && affectedCreature->hasFeatureOfType(StackFeature::SPELL_DAMAGE_REDUCTION, 1)) //fire spell & protection from fire
-	{
-		ret *= affectedCreature->valOfFeatures(StackFeature::SPELL_DAMAGE_REDUCTION, 1);
-		ret /= 100;
-	}
-	else if(sp->water && affectedCreature->hasFeatureOfType(StackFeature::SPELL_DAMAGE_REDUCTION, 2)) //water spell & protection from water
-	{
-		ret *= affectedCreature->valOfFeatures(StackFeature::SPELL_DAMAGE_REDUCTION, 2);
-		ret /= 100;
-	}
-	else if (sp->earth && affectedCreature->hasFeatureOfType(StackFeature::SPELL_DAMAGE_REDUCTION, 3)) //earth spell & protection from earth
-	{
-		ret *= affectedCreature->valOfFeatures(StackFeature::SPELL_DAMAGE_REDUCTION, 3);
-		ret /= 100;
-	}
-
-	//general spell dmg reduction
-	if(sp->air && affectedCreature->hasFeatureOfType(StackFeature::SPELL_DAMAGE_REDUCTION, -1)) //air spell & protection from air
-	{
-		ret *= affectedCreature->valOfFeatures(StackFeature::SPELL_DAMAGE_REDUCTION, -1);
-		ret /= 100;
-	}
-
-	//dmg increasing
-	if( affectedCreature->hasFeatureOfType(StackFeature::MORE_DAMAGE_FROM_SPELL, sp->id) )
-	{
-		ret *= 100 + affectedCreature->valOfFeatures(StackFeature::MORE_DAMAGE_FROM_SPELL, sp->id);
-		ret /= 100;
-	}
-
-
-	return ret;
-}
-
 static ui32 calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack)
 {
 	switch(spell->id)
@@ -3057,6 +2976,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
 			sc.id = ba.additionalInfo;
 			sc.skill = skill;
 			sc.tile = ba.destinationTile;
+			sc.dmgToDisplay = 0;
 
 			//calculating affected creatures for all spells
 			std::set<CStack*> attackedCres = gs->curB->getAttackedCreatures(s, h, ba.destinationTile);
@@ -3067,6 +2987,14 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
 
 			//checking if creatures resist
 			sc.resisted = calculateResistedStacks(s, h, secondHero, attackedCres);
+
+			//calculating dmg to display
+			for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
+			{
+				if(vstd::contains(sc.resisted, (*it)->ID)) //this creature resisted the spell
+					continue;
+				sc.dmgToDisplay += gs->curB->calculateSpellDmg(s, h, *it);
+			}
 			
 			sendAndApply(&sc);
 
@@ -3094,7 +3022,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
 						BattleStackAttacked bsa;
 						bsa.flags |= 2;
 						bsa.effect = VLC->spellh->spells[ba.additionalInfo].mainEffectAnim;
-						bsa.damageAmount = calculateSpellDmg(s, h, *it);
+						bsa.damageAmount = gs->curB->calculateSpellDmg(s, h, *it);
 						bsa.stackAttacked = (*it)->ID;
 						bsa.attackerID = -1;
 						prepareAttacked(bsa,*it);