Sfoglia il codice sorgente

Prepare battle log for spell-cast on server side.

AlexVinS 9 anni fa
parent
commit
62abde6c46

+ 3 - 6
client/battle/CBattleInterface.cpp

@@ -1323,12 +1323,9 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
 	}
 
 	//displaying message in console
-	std::vector<std::string> logLines;
-
-	spell.prepareBattleLog(curInt->cb.get(), sc, logLines);
-
-	for(auto line : logLines)
-		console->addText(line);
+	for(const auto & line : sc->battleLog)
+		if(!console->addText(line.toString()))
+			logGlobal->warn("Too long battle log line");
 
 	waitForAnims();
 	//mana absorption

+ 5 - 0
lib/BattleState.cpp

@@ -1243,6 +1243,11 @@ const PlayerColor CStack::getOwner() const
 	return owner;
 }
 
+void CStack::getCasterName(MetaString & text) const
+{
+	//always plural name in case of spell cast.
+	text.addReplacement(MetaString::CRE_PL_NAMES, type->idNumber.num);
+}
 
 bool CMP_stack::operator()( const CStack* a, const CStack* b )
 {

+ 2 - 0
lib/BattleState.h

@@ -249,6 +249,8 @@ public:
 
 	const PlayerColor getOwner() const override;
 
+	void getCasterName(MetaString & text) const override;
+
 	///stack will be ghost in next battle state update
 	void makeGhost();
 

+ 4 - 2
lib/NetPacks.h

@@ -1492,7 +1492,6 @@ struct BattleSpellCast : public CPackForClient//3009
 	DLL_LINKAGE 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
@@ -1502,9 +1501,12 @@ struct BattleSpellCast : 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)
 	si32 casterStack;// -1 if not cated by creature, >=0 caster stack ID
 	bool castByHero; //if true - spell has been cast by hero, otherwise by a creature
+
+	std::vector<MetaString> battleLog;
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & dmgToDisplay & side & id & skill & manaGained & tile & customEffects & affectedCres & casterStack & castByHero;
+		h & side & id & skill & manaGained & tile & customEffects & affectedCres & casterStack & castByHero;
+		h & battleLog;
 	}
 };
 

+ 7 - 0
lib/mapObjects/CGHeroInstance.cpp

@@ -953,6 +953,13 @@ const PlayerColor CGHeroInstance::getOwner() const
 	return tempOwner;
 }
 
+void CGHeroInstance::getCasterName(MetaString & text) const
+{
+	//FIXME: use local name, MetaString need access to gamestate as hero name is part of map object
+
+    text.addReplacement(name);
+}
+
 bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const
 {
 	if(nullptr == getArt(ArtifactPosition::SPELLBOOK))

+ 2 - 0
lib/mapObjects/CGHeroInstance.h

@@ -246,6 +246,8 @@ public:
 
 	const PlayerColor getOwner() const override;
 
+	void getCasterName(MetaString & text) const override;
+
 	void deserializationFix();
 
 	void initObj() override;

+ 99 - 44
lib/spells/CDefaultSpellMechanics.cpp

@@ -119,13 +119,12 @@ namespace SRSLPraserHelpers
 }
 
 SpellCastContext::SpellCastContext(const DefaultSpellMechanics * mechanics_, const SpellCastEnvironment * env_, const BattleSpellCastParameters & parameters_):
-	mechanics(mechanics_), env(env_), attackedCres(), sc(), si(), parameters(parameters_), otherHero(nullptr), spellCost(0)
+	mechanics(mechanics_), env(env_), attackedCres(), sc(), si(), parameters(parameters_), otherHero(nullptr), spellCost(0), damageToDisplay(0)
 {
 	sc.side = parameters.casterSide;
 	sc.id = mechanics->owner->id;
 	sc.skill = parameters.spellLvl;
 	sc.tile = parameters.getFirstDestinationHex();
-	sc.dmgToDisplay = 0;
 	sc.castByHero = parameters.mode == ECastingMode::HERO_CASTING;
 	sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1);
 	sc.manaGained = 0;
@@ -146,22 +145,63 @@ SpellCastContext::~SpellCastContext()
 
 void SpellCastContext::addDamageToDisplay(const si32 value)
 {
-	sc.dmgToDisplay += value;
+	damageToDisplay += value;
 }
 
 void SpellCastContext::setDamageToDisplay(const si32 value)
 {
-	sc.dmgToDisplay = value;
+	damageToDisplay = value;
 }
 
-void SpellCastContext::sendCastPacket()
+void SpellCastContext::prepareBattleLog()
 {
-	for(auto sta : attackedCres)
+	//todo: prepare battle log
+	bool displayDamage = true;
+
+	if(attackedCres.size() == 1)
 	{
-		sc.affectedCres.insert(sta->ID);
+		const CStack * attackedStack = *attackedCres.begin();
+
+		switch(parameters.mode)
+		{
+		case ECastingMode::HERO_CASTING:
+			{
+				MetaString line;
+				line.addTxt(MetaString::GENERAL_TXT, 195);
+				parameters.caster->getCasterName(line);
+				line.addReplacement(MetaString::SPELL_NAME, mechanics->owner->id.toEnum());
+				line.addReplacement(MetaString::CRE_PL_NAMES, attackedStack->getCreature()->idNumber.num);
+			}
+			break;
+
+		default:
+			{
+				mechanics->battleLogSingleTarget(sc.battleLog, parameters, attackedStack, damageToDisplay, displayDamage);
+			}
+			break;
+		}
+	}
+	else
+	{
+		MetaString line;
+		line.addTxt(MetaString::GENERAL_TXT, 196);
+        parameters.caster->getCasterName(line);
+		line.addReplacement(MetaString::SPELL_NAME, mechanics->owner->id.toEnum());
+		sc.battleLog.push_back(line);
 	}
 
-	env->sendAndApply(&sc);
+	displayDamage = displayDamage && damageToDisplay > 0;
+
+	if(displayDamage)
+	{
+        MetaString line;
+
+        line.addTxt(MetaString::GENERAL_TXT, 376);
+        line.addReplacement(MetaString::SPELL_NAME, mechanics->owner->id.toEnum());
+        line.addReplacement(damageToDisplay);
+
+        sc.battleLog.push_back(line);
+	}
 }
 
 void SpellCastContext::beforeCast()
@@ -190,7 +230,14 @@ void SpellCastContext::beforeCast()
 
 void SpellCastContext::afterCast()
 {
-	sendCastPacket();
+	for(auto sta : attackedCres)
+	{
+		sc.affectedCres.insert(sta->ID);
+	}
+
+	prepareBattleLog();
+
+	env->sendAndApply(&sc);
 
 	if(parameters.mode == ECastingMode::HERO_CASTING)
 	{
@@ -312,33 +359,32 @@ void DefaultSpellMechanics::cast(const SpellCastEnvironment * env, const BattleS
 	ctx.afterCast();
 }
 
-void DefaultSpellMechanics::battleLogSingleTarget(std::vector<std::string> & logLines, const BattleSpellCast * packet,
-	const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const
+void DefaultSpellMechanics::battleLogSingleTarget(std::vector<MetaString>&  logLines, const BattleSpellCastParameters & parameters,
+	const CStack * attackedStack, const si32 damageToDisplay, bool & displayDamage) const
 {
-	const std::string attackedName = attackedStack->getName();
-	const std::string attackedNameSing = attackedStack->getCreature()->nameSing;
-	const std::string attackedNamePl = attackedStack->getCreature()->namePl;
-
-	auto getPluralFormat = [attackedStack](const int baseTextID) -> boost::format
+	auto getPluralFormat = [attackedStack](const int baseTextID) -> si32
 	{
-		return boost::format(VLC->generaltexth->allTexts[(attackedStack->count > 1 ? baseTextID + 1 : baseTextID)]);
+		return attackedStack->count > 1 ? baseTextID + 1 : baseTextID;
 	};
 
-	auto logSimple = [&logLines, getPluralFormat, attackedName](const int baseTextID)
+	auto logSimple = [attackedStack, &logLines, getPluralFormat](const int baseTextID)
 	{
-		boost::format fmt = getPluralFormat(baseTextID);
-		fmt % attackedName;
-		logLines.push_back(fmt.str());
+		MetaString line;
+		line.addTxt(MetaString::GENERAL_TXT, getPluralFormat(baseTextID));
+		line.addReplacement(*attackedStack);
+		logLines.push_back(line);
 	};
 
-	auto logPlural = [&logLines, attackedNamePl](const int baseTextID)
+	auto logPlural = [attackedStack, &logLines, getPluralFormat](const int baseTextID)
 	{
-		boost::format fmt(VLC->generaltexth->allTexts[baseTextID]);
-		fmt % attackedNamePl;
-		logLines.push_back(fmt.str());
+		MetaString line;
+		line.addTxt(MetaString::GENERAL_TXT, baseTextID);
+		line.addReplacement(MetaString::CRE_PL_NAMES, attackedStack->getCreature()->idNumber.num);
+		logLines.push_back(line);
 	};
 
 	displayDamage = false; //in most following cases damage info text is custom
+
 	switch(owner->id)
 	{
 	case SpellID::STONE_GAZE:
@@ -358,52 +404,61 @@ void DefaultSpellMechanics::battleLogSingleTarget(std::vector<std::string> & log
 		break;
 	case SpellID::AGE:
 		{
-			boost::format text = getPluralFormat(551);
-			text % attackedName;
 			//The %s shrivel with age, and lose %d hit points."
+			MetaString line;
+			line.addTxt(MetaString::GENERAL_TXT, getPluralFormat(551));
+			line.addReplacement(MetaString::CRE_PL_NAMES, attackedStack->getCreature()->idNumber.num);
+
+			//todo: display effect from only this cast
 			TBonusListPtr bl = attackedStack->getBonuses(Selector::type(Bonus::STACK_HEALTH));
 			const int fullHP = bl->totalValue();
 			bl->remove_if(Selector::source(Bonus::SPELL_EFFECT, SpellID::AGE));
-			text % (fullHP - bl->totalValue());
-			logLines.push_back(text.str());
+			line.addReplacement(fullHP - bl->totalValue());
+			logLines.push_back(line);
 		}
 		break;
 	case SpellID::THUNDERBOLT:
 		{
 			logPlural(367);
+			MetaString line;
+			//todo: handle newlines in metastring
 			std::string text = VLC->generaltexth->allTexts[343].substr(1, VLC->generaltexth->allTexts[343].size() - 1); //Does %d points of damage.
-			boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(packet->dmgToDisplay)); //no more text afterwards
-			logLines.push_back(text);
+			line << text;
+			line.addReplacement(damageToDisplay); //no more text afterwards
+			logLines.push_back(line);
 		}
 		break;
 	case SpellID::DISPEL_HELPFUL_SPELLS:
 		logPlural(555);
 		break;
 	case SpellID::DEATH_STARE:
-		if (packet->dmgToDisplay > 0)
+		if (damageToDisplay > 0)
 		{
-			std::string text;
-			if (packet->dmgToDisplay > 1)
+			MetaString line;
+			if (damageToDisplay > 1)
 			{
-				text = VLC->generaltexth->allTexts[119]; //%d %s die under the terrible gaze of the %s.
-				boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(packet->dmgToDisplay));
-				boost::algorithm::replace_first(text, "%s", attackedNamePl);
+				line.addTxt(MetaString::GENERAL_TXT, 119); //%d %s die under the terrible gaze of the %s.
+				line.addReplacement(damageToDisplay);
+				line.addReplacement(MetaString::CRE_PL_NAMES, attackedStack->getCreature()->idNumber.num);
 			}
 			else
 			{
-				text = VLC->generaltexth->allTexts[118]; //One %s dies under the terrible gaze of the %s.
-				boost::algorithm::replace_first(text, "%s", attackedNameSing);
+				line.addTxt(MetaString::GENERAL_TXT, 118); //One %s dies under the terrible gaze of the %s.
+				line.addReplacement(MetaString::CRE_SING_NAMES, attackedStack->getCreature()->idNumber.num);
 			}
-			boost::algorithm::replace_first(text, "%s", casterName); //casting stack
-			logLines.push_back(text);
+			parameters.caster->getCasterName(line);
+			logLines.push_back(line);
 		}
 		break;
 	default:
 		{
-			boost::format text(VLC->generaltexth->allTexts[565]); //The %s casts %s
-			text % casterName % owner->name;
+			MetaString line;
+			line.addTxt(MetaString::GENERAL_TXT, 565);//The %s casts %s
+			//todo: use text 566 for single creature
+			parameters.caster->getCasterName(line);
+			line.addReplacement(MetaString::SPELL_NAME, owner->id.toEnum());
 			displayDamage = true;
-			logLines.push_back(text.str());
+			logLines.push_back(line);
 		}
 		break;
 	}

+ 4 - 3
lib/spells/CDefaultSpellMechanics.h

@@ -36,8 +36,9 @@ public:
 private:
 	const CGHeroInstance * otherHero;
 	int spellCost;
+	si32 damageToDisplay;
 
-	void sendCastPacket();
+	void prepareBattleLog();
 };
 
 ///all combat spells
@@ -58,8 +59,8 @@ public:
 
 	void battleCast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const override final;
 
-	void battleLogSingleTarget(std::vector<std::string> & logLines, const BattleSpellCast * packet,
-		const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const override;
+	void battleLogSingleTarget(std::vector<MetaString> & logLines, const BattleSpellCastParameters & parameters,
+		const CStack * attackedStack, const si32 damageToDisplay, bool & displayDamage) const;
 
 	bool requiresCreatureTarget() const	override;
 protected:

+ 0 - 53
lib/spells/CSpellHandler.cpp

@@ -586,59 +586,6 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneByStack(const ISpellCaster
 	return ESpellCastProblem::OK;
 }
 
-void CSpell::prepareBattleLog(const CBattleInfoCallback * cb,  const BattleSpellCast * packet, std::vector<std::string> & logLines) const
-{
-	bool displayDamage = true;
-
-	std::string casterName("Something"); //todo: localize
-
-	if(packet->castByHero)
-		casterName = cb->battleGetHeroInfo(packet->side).name;
-
-	{
-		const auto casterStackID = packet->casterStack;
-
-		if(casterStackID > 0)
-		{
-			const CStack * casterStack = cb->battleGetStackByID(casterStackID);
-			if(casterStack != nullptr)
-				casterName = casterStack->type->namePl;
-		}
-	}
-
-	if(packet->affectedCres.size() == 1)
-	{
-		const CStack * attackedStack = cb->battleGetStackByID(*packet->affectedCres.begin(), false);
-
-		const std::string attackedNamePl = attackedStack->getCreature()->namePl;
-
-		if(packet->castByHero)
-		{
-			const std::string fmt = VLC->generaltexth->allTexts[195];
-			logLines.push_back(boost::to_string(boost::format(fmt) % casterName % this->name % attackedNamePl));
-		}
-		else
-		{
-			mechanics->battleLogSingleTarget(logLines, packet, casterName, attackedStack, displayDamage);
-		}
-	}
-	else
-	{
-		boost::format text(VLC->generaltexth->allTexts[196]);
-		text % casterName % this->name;
-		logLines.push_back(text.str());
-	}
-
-
-	if(packet->dmgToDisplay > 0 && displayDamage)
-	{
-		boost::format dmgInfo(VLC->generaltexth->allTexts[376]);
-		dmgInfo % this->name % packet->dmgToDisplay;
-		logLines.push_back(dmgInfo.str());
-	}
-}
-
-
 void CSpell::setIsOffensive(const bool val)
 {
 	isOffensive = val;

+ 0 - 4
lib/spells/CSpellHandler.h

@@ -287,10 +287,6 @@ public:
 
 	///implementation of BattleSpellCast applying
 	void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const;
-
-public://Client logic.
-	void prepareBattleLog(const CBattleInfoCallback * cb, const BattleSpellCast * packet, std::vector<std::string> & logLines) const;
-
 public://internal, for use only by Mechanics classes
 	///applies caster`s secondary skills and affectedCreature`s to raw damage
 	int adjustRawDamage(const ISpellCaster * caster, const CStack * affectedCreature, int rawDamage) const;

+ 3 - 3
lib/spells/CreatureSpellMechanics.cpp

@@ -58,12 +58,12 @@ ESpellCastProblem::ESpellCastProblem AcidBreathDamageMechanics::isImmuneByStack(
 void DeathStareMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
 {
 	//calculating dmg to display
-	si32 dmgToDisplay = parameters.effectPower;
+	si32 damageToDisplay = parameters.effectPower;
 
 	if(!ctx.attackedCres.empty())
-		vstd::amin(dmgToDisplay, (*ctx.attackedCres.begin())->count); //stack is already reduced after attack
+		vstd::amin(damageToDisplay, (*ctx.attackedCres.begin())->count); //stack is already reduced after attack
 
-	ctx.setDamageToDisplay(dmgToDisplay);
+	ctx.setDamageToDisplay(damageToDisplay);
 
 	for(auto & attackedCre : ctx.attackedCres)
 	{

+ 0 - 3
lib/spells/ISpellMechanics.h

@@ -114,9 +114,6 @@ public:
 	virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const = 0;
 	virtual void battleCast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const = 0;
 
-	virtual void battleLogSingleTarget(std::vector<std::string> & logLines, const BattleSpellCast * packet,
-		const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const = 0;
-
 	//if true use generic algorithm for target existence check, see CSpell::canBeCast
 	virtual bool requiresCreatureTarget() const = 0;
 

+ 3 - 0
lib/spells/Magic.h

@@ -18,6 +18,7 @@
 class CSpell;
 class CStack;
 class PlayerColor;
+struct MetaString;
 
 class DLL_LINKAGE ISpellCaster
 {
@@ -45,4 +46,6 @@ public:
 	virtual int getEffectValue(const CSpell * spell) const = 0;
 
 	virtual const PlayerColor getOwner() const = 0;
+
+	virtual void getCasterName(MetaString & text) const = 0;
 };