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

Merge pull request #3736 from IvanSavenko/fix_server_translations

Do not translate strings on server side
This commit is contained in:
Ivan Savenko 2024-04-10 19:12:34 +03:00 committed by GitHub
commit bcd4a8c961
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 175 additions and 207 deletions

View File

@ -208,8 +208,7 @@ void StackWithBonuses::removeUnitBonus(const std::vector<Bonus> & bonus)
&& one.sid == b->sid
&& one.valType == b->valType
&& one.additionalInfo == b->additionalInfo
&& one.effectRange == b->effectRange
&& one.description == b->description;
&& one.effectRange == b->effectRange;
});
removeUnitBonus(selector);

View File

@ -217,7 +217,6 @@ public:
void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) override {};
void showInfoDialog(InfoWindow * iw) override {};
void showInfoDialog(const std::string & msg, PlayerColor player) override {};
void removeGUI() const;
#if SCRIPTING_ENABLED

View File

@ -79,14 +79,14 @@
"type" : "MORALE",
"val" : 1,
"valueType" : "BASE_NUMBER",
"description" : "Creatures of good town alignment on Holly Ground",
"description" : "core.arraytxt.123",
"limiters": [{ "type" : "CREATURE_ALIGNMENT_LIMITER", "parameters" : ["good"] }]
},
{
"type" : "MORALE",
"val" : -1,
"valueType" : "BASE_NUMBER",
"description" : "Creatures of evil town alignment on Holly Ground",
"description" : "core.arraytxt.124",
"limiters": [{ "type" : "CREATURE_ALIGNMENT_LIMITER", "parameters" : ["evil"] }]
}
]
@ -99,7 +99,7 @@
"type" : "LUCK",
"val" : 2,
"valueType" : "BASE_NUMBER",
"description" : "Creatures of neutral town alignment on Clover Field",
"description" : "core.arraytxt.83",
"limiters": [{ "type" : "CREATURE_ALIGNMENT_LIMITER", "parameters" : ["neutral"] }]
}
]
@ -112,14 +112,14 @@
"type" : "MORALE",
"val" : -1,
"valueType" : "BASE_NUMBER",
"description" : "Creatures of good town alignment on Evil Fog",
"description" : "core.arraytxt.126",
"limiters": [{ "type" : "CREATURE_ALIGNMENT_LIMITER", "parameters" : ["good"] }]
},
{
"type" : "MORALE",
"val" : 1,
"valueType" : "BASE_NUMBER",
"description" : "Creatures of evil town alignment on Evil Fog",
"description" : "core.arraytxt.125",
"limiters": [{ "type" : "CREATURE_ALIGNMENT_LIMITER", "parameters" : ["evil"] }]
}
]
@ -132,13 +132,13 @@
"type" : "NO_MORALE",
"val" : 0,
"valueType" : "INDEPENDENT_MIN",
"description" : "Creatures on Cursed Ground"
"description" : "core.arraytxt.112"
},
{
"type" : "NO_LUCK",
"val" : 0,
"valueType" : "INDEPENDENT_MIN",
"description" : "Creatures on Cursed Ground"
"description" : "core.arraytxt.81"
},
{
"type" : "BLOCK_MAGIC_ABOVE",

View File

@ -294,7 +294,8 @@ void CArtifact::addNewBonus(const std::shared_ptr<Bonus>& b)
{
b->source = BonusSource::ARTIFACT;
b->duration = BonusDuration::PERMANENT;
b->description = getNameTranslated();
b->description.appendTextID(getNameTextID());
b->description.appendRawString(" %+d");
CBonusSystemNode::addNewBonus(b);
}

View File

@ -94,7 +94,8 @@ void CSkill::addNewBonus(const std::shared_ptr<Bonus> & b, int level)
b->source = BonusSource::SECONDARY_SKILL;
b->sid = BonusSourceID(id);
b->duration = BonusDuration::PERMANENT;
b->description = getNameTranslated();
b->description.appendTextID(getNameTextID());
b->description.appendRawString(" %+d");
levels[level-1].effects.push_back(b);
}

View File

@ -575,20 +575,9 @@ std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, BonusType ty
std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, BonusType type, int val, BonusSubtypeID subtype, const TPropagatorPtr & prop) const
{
std::ostringstream descr;
descr << build->getNameTranslated();
return createBonusImpl(build->bid, build->town->faction->getId(), type, val, prop, descr.str(), subtype);
}
auto b = std::make_shared<Bonus>(BonusDuration::PERMANENT, type, BonusSource::TOWN_STRUCTURE, val, build->getUniqueTypeID(), subtype);
std::shared_ptr<Bonus> CTownHandler::createBonusImpl(const BuildingID & building,
const FactionID & faction,
BonusType type,
int val,
const TPropagatorPtr & prop,
const std::string & description,
BonusSubtypeID subtype) const
{
auto b = std::make_shared<Bonus>(BonusDuration::PERMANENT, type, BonusSource::TOWN_STRUCTURE, val, BuildingTypeUniqueID(faction, building), subtype, description);
b->description.appendTextID(build->getNameTextID());
if(prop)
b->addPropagator(prop);
@ -600,12 +589,13 @@ void CTownHandler::loadSpecialBuildingBonuses(const JsonNode & source, BonusList
{
for(const auto & b : source.Vector())
{
auto bonus = JsonUtils::parseBuildingBonus(b, building->town->faction->getId(), building->bid, building->getNameTranslated());
auto bonus = std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::NONE, BonusSource::TOWN_STRUCTURE, 0, BonusSourceID(building->getUniqueTypeID()));
if(bonus == nullptr)
if(!JsonUtils::parseBonus(b, bonus.get()))
continue;
bonus->sid = BonusSourceID(building->getUniqueTypeID());
bonus->description.appendTextID(building->getNameTextID());
//JsonUtils::parseBuildingBonus produces UNKNOWN type propagator instead of empty.
if(bonus->propagator != nullptr
&& bonus->propagator->getPropagatorType() == CBonusSystemNode::ENodeTypes::UNKNOWN)

View File

@ -303,13 +303,6 @@ class DLL_LINKAGE CTownHandler : public CHandlerBase<FactionID, Faction, CFactio
std::shared_ptr<Bonus> createBonus(CBuilding * build, BonusType type, int val) const;
std::shared_ptr<Bonus> createBonus(CBuilding * build, BonusType type, int val, BonusSubtypeID subtype) const;
std::shared_ptr<Bonus> createBonus(CBuilding * build, BonusType type, int val, BonusSubtypeID subtype, const TPropagatorPtr & prop) const;
std::shared_ptr<Bonus> createBonusImpl(const BuildingID & building,
const FactionID & faction,
BonusType type,
int val,
const TPropagatorPtr & prop,
const std::string & description,
BonusSubtypeID subtype) const;
/// loads CStructure's into town
void loadStructure(CTown & town, const std::string & stringID, const JsonNode & source) const;

View File

@ -78,7 +78,6 @@ public:
virtual void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) = 0;
virtual void showInfoDialog(InfoWindow * iw) = 0;
virtual void showInfoDialog(const std::string & msg, PlayerColor player) = 0;
virtual void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> &spells)=0;
virtual bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;

View File

@ -169,7 +169,15 @@ DLL_LINKAGE std::string MetaString::toString() const
break;
case EMessage::REPLACE_POSITIVE_NUMBER:
if (dst.find("%+d") != std::string::npos)
boost::replace_first(dst, "%+d", '+' + std::to_string(numbers[nums++]));
{
int64_t value = numbers[nums];
if (value > 0)
boost::replace_first(dst, "%+d", '+' + std::to_string(value));
else
boost::replace_first(dst, "%+d", std::to_string(value));
nums++;
}
else
boost::replace_first(dst, "%d", std::to_string(numbers[nums++]));
break;

View File

@ -917,8 +917,7 @@ void BattleInfo::removeUnitBonus(uint32_t id, const std::vector<Bonus> & bonus)
&& one.sid == b->sid
&& one.valType == b->valType
&& one.additionalInfo == b->additionalInfo
&& one.effectRange == b->effectRange
&& one.description == b->description;
&& one.effectRange == b->effectRange;
};
sta->removeBonusesRecursive(selector);
}

View File

@ -90,51 +90,66 @@ JsonNode CAddInfo::toJsonNode() const
}
std::string Bonus::Description(std::optional<si32> customValue) const
{
std::string str;
MetaString descriptionHelper = description;
auto valueToShow = customValue.value_or(val);
if(description.empty())
if(descriptionHelper.empty())
{
if(stacking.empty() || stacking == "ALWAYS")
// no custom description - try to generate one based on bonus source
switch(source)
{
switch(source)
{
case BonusSource::ARTIFACT:
str = sid.as<ArtifactID>().toEntity(VLC)->getNameTranslated();
descriptionHelper.appendName(sid.as<ArtifactID>());
break;
case BonusSource::SPELL_EFFECT:
str = sid.as<SpellID>().toEntity(VLC)->getNameTranslated();
descriptionHelper.appendName(sid.as<SpellID>());
break;
case BonusSource::CREATURE_ABILITY:
str = sid.as<CreatureID>().toEntity(VLC)->getNamePluralTranslated();
descriptionHelper.appendNamePlural(sid.as<CreatureID>());
break;
case BonusSource::SECONDARY_SKILL:
str = VLC->skills()->getById(sid.as<SecondarySkill>())->getNameTranslated();
descriptionHelper.appendTextID(sid.as<SecondarySkill>().toEntity(VLC)->getNameTextID());
break;
case BonusSource::HERO_SPECIAL:
str = VLC->heroTypes()->getById(sid.as<HeroTypeID>())->getNameTranslated();
descriptionHelper.appendTextID(sid.as<HeroTypeID>().toEntity(VLC)->getNameTextID());
break;
default:
//todo: handle all possible sources
str = "Unknown";
break;
}
}
else
str = stacking;
}
else
if(descriptionHelper.empty())
{
str = description;
// still no description - try to generate one based on duration
if ((duration & BonusDuration::ONE_BATTLE).any())
{
if (val > 0)
descriptionHelper.appendTextID("core.arraytxt.110"); //+%d Temporary until next battle"
else
descriptionHelper.appendTextID("core.arraytxt.109"); //-%d Temporary until next battle"
// erase sign - already present in description string
valueToShow = std::abs(valueToShow);
}
}
if(auto value = customValue.value_or(val)) {
//arraytxt already contains +-value
std::string valueString = boost::str(boost::format(" %+d") % value);
if(!boost::algorithm::ends_with(str, valueString))
str += valueString;
if(descriptionHelper.empty())
{
// still no description - generate placeholder one
descriptionHelper.appendRawString("Unknown");
}
return str;
if(valueToShow != 0)
{
descriptionHelper.replacePositiveNumber(valueToShow);
// there is one known string that uses '%s' placeholder for bonus value:
// "core.arraytxt.69" : "\nFountain of Fortune Visited %s",
// So also add string replacement to handle this case
if (valueToShow > 0)
descriptionHelper.replaceRawString(std::to_string(valueToShow));
else
descriptionHelper.replaceRawString("-" + std::to_string(valueToShow));
}
return descriptionHelper.toString();
}
static JsonNode additionalInfoToJson(BonusType type, CAddInfo addInfo)
@ -170,7 +185,7 @@ JsonNode Bonus::toJsonNode() const
if(!stacking.empty())
root["stacking"].String() = stacking;
if(!description.empty())
root["description"].String() = description;
root["description"].String() = description.toString();
if(effectRange != BonusLimitEffect::NO_LIMIT)
root["effectRange"].String() = vstd::findKey(bonusLimitEffect, effectRange);
if(duration != BonusDuration::PERMANENT)
@ -187,27 +202,17 @@ JsonNode Bonus::toJsonNode() const
}
Bonus::Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, BonusSourceID ID)
: Bonus(Duration, Type, Src, Val, ID, BonusSubtypeID(), std::string())
: Bonus(Duration, Type, Src, Val, ID, BonusSubtypeID())
{}
Bonus::Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, BonusSourceID ID, std::string Desc)
: Bonus(Duration, Type, Src, Val, ID, BonusSubtypeID(), Desc)
{}
Bonus::Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, BonusSourceID ID, BonusSubtypeID Subtype)
: Bonus(Duration, Type, Src, Val, ID, Subtype, std::string())
{}
Bonus::Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, BonusSourceID ID, BonusSubtypeID Subtype, std::string Desc):
Bonus::Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, BonusSourceID ID, BonusSubtypeID Subtype):
duration(Duration),
type(Type),
subtype(Subtype),
source(Src),
val(Val),
sid(ID),
description(std::move(Desc))
sid(ID)
{
boost::algorithm::trim(description);
targetSourceType = BonusSource::OTHER;
}

View File

@ -13,6 +13,7 @@
#include "BonusCustomTypes.h"
#include "../constants/VariantIdentifier.h"
#include "../constants/EntityIdentifiers.h"
#include "../MetaString.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -77,12 +78,10 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
TUpdaterPtr updater;
TUpdaterPtr propagationUpdater;
std::string description;
MetaString description;
Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, BonusSourceID sourceID);
Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, BonusSourceID sourceID, std::string Desc);
Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, BonusSourceID sourceID, BonusSubtypeID subtype);
Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, BonusSourceID sourceID, BonusSubtypeID subtype, std::string Desc);
Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, BonusSourceID sourceID, BonusSubtypeID subtype, BonusValueType ValType);
Bonus() = default;
@ -94,7 +93,15 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
h & source;
h & val;
h & sid;
h & description;
if (h.version < Handler::Version::BONUS_META_STRING)
{
std::string oldDescription;
h & oldDescription;
description = MetaString::createFromRawString(oldDescription);
}
else
h & description;
h & additionalInfo;
h & turnsRemain;
h & valType;

View File

@ -625,19 +625,6 @@ std::shared_ptr<Bonus> JsonUtils::parseBonus(const JsonNode &ability)
return b;
}
std::shared_ptr<Bonus> JsonUtils::parseBuildingBonus(const JsonNode & ability, const FactionID & faction, const BuildingID & building, const std::string & description)
{
/* duration = BonusDuration::PERMANENT
source = BonusSource::TOWN_STRUCTURE
bonusType, val, subtype - get from json
*/
auto b = std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::NONE, BonusSource::TOWN_STRUCTURE, 0, BuildingTypeUniqueID(faction, building), description);
if(!parseBonus(ability, b.get()))
return nullptr;
return b;
}
bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
{
const JsonNode * value = nullptr;
@ -682,9 +669,9 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
if(!ability["description"].isNull())
{
if (ability["description"].isString())
b->description = ability["description"].String();
b->description.appendTextID(ability["description"].String());
if (ability["description"].isNumber())
b->description = VLC->generaltexth->translate("core.arraytxt", ability["description"].Integer());
b->description.appendTextID("core.arraytxt." + std::to_string(ability["description"].Integer()));
}
value = &ability["effectRange"];

View File

@ -23,7 +23,6 @@ namespace JsonUtils
{
std::shared_ptr<Bonus> parseBonus(const JsonVector & ability_vec);
std::shared_ptr<Bonus> parseBonus(const JsonNode & ability);
std::shared_ptr<Bonus> parseBuildingBonus(const JsonNode & ability, const FactionID & faction, const BuildingID & building, const std::string & description);
bool parseBonus(const JsonNode & ability, Bonus * placement);
std::shared_ptr<ILimiter> parseLimiter(const JsonNode & limiter);
CSelector parseSelector(const JsonNode &ability);

View File

@ -101,28 +101,21 @@ void CArmedInstance::updateMoraleBonusFromArmy()
factionsInArmy -= mixableFactions - 1;
}
std::string description;
MetaString bonusDescription;
if(factionsInArmy == 1)
{
b->val = +1;
description = VLC->generaltexth->arraytxt[115]; //All troops of one alignment +1
description = description.substr(0, description.size()-3);//trim "+1"
bonusDescription.appendTextID("core.arraytxt.115"); //All troops of one alignment +1
}
else if (!factions.empty()) // no bonus from empty garrison
{
b->val = 2 - static_cast<si32>(factionsInArmy);
MetaString formatter;
formatter.appendTextID("core.arraytxt.114"); //Troops of %d alignments %d
formatter.replaceNumber(factionsInArmy);
formatter.replaceNumber(b->val);
description = formatter.toString();
description = description.substr(0, description.size()-3);//trim value
bonusDescription.appendTextID("core.arraytxt.114"); //Troops of %d alignments %d
bonusDescription.replaceNumber(factionsInArmy);
}
boost::algorithm::trim(description);
b->description = description;
b->description = bonusDescription;
CBonusSystemNode::treeHasChanged();
@ -132,8 +125,8 @@ void CArmedInstance::updateMoraleBonusFromArmy()
{
if(!undeadModifier)
{
undeadModifier = std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::MORALE, BonusSource::ARMY, -1, BonusCustomSource::undeadMoraleDebuff, VLC->generaltexth->arraytxt[116]);
undeadModifier->description = undeadModifier->description.substr(0, undeadModifier->description.size()-2);//trim value
undeadModifier = std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::MORALE, BonusSource::ARMY, -1, BonusCustomSource::undeadMoraleDebuff);
undeadModifier->description.appendTextID("core.arraytxt.116");
addNewBonus(undeadModifier);
}
}

View File

@ -225,15 +225,15 @@ void CBank::doVisit(const CGHeroInstance * hero) const
{
case Obj::SHIPWRECK:
textID = 123;
gbonus.bdescr.appendRawString(VLC->generaltexth->arraytxt[99]);
gbonus.bonus.description = MetaString::createFromTextID("core.arraytxt.99");
break;
case Obj::DERELICT_SHIP:
textID = 42;
gbonus.bdescr.appendRawString(VLC->generaltexth->arraytxt[101]);
gbonus.bonus.description = MetaString::createFromTextID("core.arraytxt.101");
break;
case Obj::CRYPT:
textID = 120;
gbonus.bdescr.appendRawString(VLC->generaltexth->arraytxt[98]);
gbonus.bonus.description = MetaString::createFromTextID("core.arraytxt.98");
break;
}
cb->giveHeroBonus(&gbonus);
@ -244,7 +244,8 @@ void CBank::doVisit(const CGHeroInstance * hero) const
case Obj::PYRAMID:
{
GiveBonus gb;
gb.bonus = Bonus(BonusDuration::ONE_BATTLE, BonusType::LUCK, BonusSource::OBJECT_INSTANCE, -2, BonusSourceID(id), VLC->generaltexth->arraytxt[70]);
gb.bonus = Bonus(BonusDuration::ONE_BATTLE, BonusType::LUCK, BonusSource::OBJECT_INSTANCE, -2, BonusSourceID(id));
gb.bonus.description = MetaString::createFromTextID("core.arraytxt.70");
gb.id = hero->id;
cb->giveHeroBonus(&gb);
textID = 107;

View File

@ -144,6 +144,9 @@ public:
//////////////////////////////////////////////////////////////////////////
std::string getBiographyTranslated() const;
std::string getBiographyTextID() const;
std::string getNameTextID() const;
std::string getNameTranslated() const;
HeroTypeID getPortraitSource() const;
@ -152,11 +155,6 @@ public:
std::string getClassNameTranslated() const;
std::string getClassNameTextID() const;
private:
std::string getNameTextID() const;
std::string getBiographyTextID() const;
public:
bool hasSpellbook() const;
int maxSpellLevel() const;
void addSpellToSpellbook(const SpellID & spell);

View File

@ -98,27 +98,33 @@ std::string CGTownBuilding::getCustomBonusGreeting(const Bonus & bonus) const
{
if(bonus.type == BonusType::TOWN_MAGIC_WELL)
{
auto bonusGreeting = std::string(VLC->generaltexth->translate("vcmi.townHall.greetingInTownMagicWell"));
auto buildingName = town->getTown()->getSpecialBuilding(bType)->getNameTranslated();
boost::algorithm::replace_first(bonusGreeting, "%s", buildingName);
return bonusGreeting;
MetaString wellGreeting = MetaString::createFromTextID("vcmi.townHall.greetingInTownMagicWell");
wellGreeting.replaceTextID(town->getTown()->getSpecialBuilding(bType)->getNameTextID());
return wellGreeting.toString();
}
auto bonusGreeting = std::string(VLC->generaltexth->translate("vcmi.townHall.greetingCustomBonus")); //"%s gives you +%d %s%s"
std::string param;
MetaString greeting = MetaString::createFromTextID("vcmi.townHall.greetingCustomBonus");
std::string paramTextID;
std::string until;
if(bonus.type == BonusType::MORALE)
param = VLC->generaltexth->allTexts[384];
else if(bonus.type == BonusType::LUCK)
param = VLC->generaltexth->allTexts[385];
paramTextID = "core.genrltxt.384"; // Morale
until = bonus.duration == BonusDuration::ONE_BATTLE
? VLC->generaltexth->translate("vcmi.townHall.greetingCustomUntil")
: ".";
if(bonus.type == BonusType::LUCK)
paramTextID = "core.genrltxt.385"; // Luck
boost::format fmt = boost::format(bonusGreeting) % bonus.description % bonus.val % param % until;
std::string greeting = fmt.str();
return greeting;
greeting.replaceTextID(town->getTown()->getSpecialBuilding(bType)->getNameTextID());
greeting.replaceNumber(bonus.val);
greeting.replaceTextID(paramTextID);
if (bonus.duration == BonusDuration::ONE_BATTLE)
greeting.replaceTextID("vcmi.townHall.greetingCustomUntil");
else
greeting.replaceRawString(".");
return greeting.toString();
}
COPWBonus::COPWBonus(IGameCallback *cb)
@ -160,7 +166,7 @@ void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
if(!h->hasBonusFrom(BonusSource::OBJECT_TYPE, BonusSourceID(Obj(Obj::STABLES)))) //does not stack with advMap Stables
{
GiveBonus gb;
gb.bonus = Bonus(BonusDuration::ONE_WEEK, BonusType::MOVEMENT, BonusSource::OBJECT_TYPE, 600, BonusSourceID(Obj(Obj::STABLES)), BonusCustomSubtype::heroMovementLand, VLC->generaltexth->arraytxt[100]);
gb.bonus = Bonus(BonusDuration::ONE_WEEK, BonusType::MOVEMENT, BonusSource::OBJECT_TYPE, 600, BonusSourceID(Obj(Obj::STABLES)), BonusCustomSubtype::heroMovementLand);
gb.id = heroID;
cb->giveHeroBonus(&gb);

View File

@ -1004,31 +1004,6 @@ void GiveBonus::applyGs(CGameState *gs)
auto b = std::make_shared<Bonus>(bonus);
cbsn->addNewBonus(b);
std::string &descr = b->description;
if(!bdescr.empty())
{
descr = bdescr.toString();
}
else if(!descr.empty())
{
//use preseet description
}
else if((bonus.type == BonusType::LUCK || bonus.type == BonusType::MORALE)
&& (bonus.source == BonusSource::OBJECT_TYPE || bonus.source == BonusSource::OBJECT_INSTANCE))
{
//no description, use generic
//?could use allways when Type == BonusDuration::Type::ONE_BATTLE
descr = VLC->generaltexth->arraytxt[bonus.val > 0 ? 110 : 109]; //+/-%d Temporary until next battle"
}
else
{
logGlobal->debug("Empty bonus decription. Type=%d", (int) bonus.type);
}
// Some of(?) versions of H3 use " %s" here instead of %d. Try to replace both of them
boost::replace_first(descr, "%d", std::to_string(std::abs(bonus.val))); // " +/-%d Temporary until next battle
boost::replace_first(descr, " %s", boost::str(boost::format(" %+d") % bonus.val)); // " %s" in arraytxt.69, fountian of fortune
}
void ChangeObjPos::applyGs(CGameState *gs)

View File

@ -382,7 +382,6 @@ struct DLL_LINKAGE GiveBonus : public CPackForClient
ETarget who = ETarget::OBJECT;
VariantIdentifier<ObjectInstanceID, PlayerColor, BattleID> id;
Bonus bonus;
MetaString bdescr;
void visitTyped(ICPackVisitor & visitor) override;
@ -390,7 +389,6 @@ struct DLL_LINKAGE GiveBonus : public CPackForClient
{
h & bonus;
h & id;
h & bdescr;
h & who;
assert(id.getNum() != -1);
}

View File

@ -38,6 +38,7 @@ enum class ESerializationVersion : int32_t
CAMPAIGN_MAP_TRANSLATIONS, // 835 +campaigns include translations for its maps
JSON_FLAGS, // 836 json uses new format for flags
MANA_LIMIT, // 837 change MANA_PER_KNOWLEGDE to percentage
BONUS_META_STRING, // 838 bonuses use MetaString instead of std::string for descriptions
CURRENT = MANA_LIMIT
CURRENT = BONUS_META_STRING
};

View File

@ -207,8 +207,7 @@ void JsonUpdater::serializeBonuses(const std::string & fieldName, CBonusSystemNo
&& mask->sid == b->sid
&& mask->valType == b->valType
&& mask->additionalInfo == b->additionalInfo
&& mask->effectRange == b->effectRange
&& mask->description == b->description;
&& mask->effectRange == b->effectRange;
};
value->removeBonuses(selector);

View File

@ -16,6 +16,9 @@
#include "../MetaString.h"
#include "../battle/Unit.h"
#include "../bonuses/Bonus.h"
#include "../VCMI_Lib.h"
#include "../CSkillHandler.h"
#include "../CHeroHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -32,10 +35,27 @@ BonusCaster::~BonusCaster() = default;
void BonusCaster::getCasterName(MetaString & text) const
{
if(!bonus->description.empty())
text.replaceRawString(bonus->description);
else
actualCaster->getCasterName(text);
switch(bonus->source)
{
case BonusSource::ARTIFACT:
text.replaceName(bonus->sid.as<ArtifactID>());
break;
case BonusSource::SPELL_EFFECT:
text.replaceName(bonus->sid.as<SpellID>());
break;
case BonusSource::CREATURE_ABILITY:
text.replaceNamePlural(bonus->sid.as<CreatureID>());
break;
case BonusSource::SECONDARY_SKILL:
text.replaceTextID(bonus->sid.as<SecondarySkill>().toEntity(VLC)->getNameTextID());
break;
case BonusSource::HERO_SPECIAL:
text.replaceTextID(bonus->sid.as<HeroTypeID>().toEntity(VLC)->getNameTextID());
break;
default:
actualCaster->getCasterName(text);
break;
}
}
void BonusCaster::getCastDescription(const Spell * spell, const std::vector<const battle::Unit*> & attacked, MetaString & text) const

View File

@ -143,7 +143,7 @@ int BonusProxy::getDescription(lua_State * L)
std::shared_ptr<const Bonus> object;
if(!S.tryGet(1, object))
return S.retNil();
return LuaStack::quickRetStr(L, object->description);
return LuaStack::quickRetStr(L, object->description.toString());
}
int BonusProxy::toJsonNode(lua_State * L)

View File

@ -388,7 +388,7 @@ void CGameHandler::giveExperience(const CGHeroInstance * hero, TExpType amountTo
InfoWindow iw;
iw.player = hero->tempOwner;
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 1); //can gain no more XP
iw.text.replaceRawString(hero->getNameTranslated());
iw.text.replaceTextID(hero->getNameTextID());
sendAndApply(&iw);
}
@ -1597,7 +1597,7 @@ void CGameHandler::useScholarSkill(ObjectInstanceID fromHero, ObjectInstanceID t
iw.components.emplace_back(ComponentType::SEC_SKILL, scholarSkill, scholarSkillLevel);
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 139);//"%s, who has studied magic extensively,
iw.text.replaceRawString(h1->getNameTranslated());
iw.text.replaceTextID(h1->getNameTextID());
if (!cs2.spells.empty())//if found new spell - apply
{
@ -1618,7 +1618,7 @@ void CGameHandler::useScholarSkill(ObjectInstanceID fromHero, ObjectInstanceID t
}
}
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 142);//from %s
iw.text.replaceRawString(h2->getNameTranslated());
iw.text.replaceTextID(h2->getNameTextID());
sendAndApply(&cs2);
}
@ -1646,7 +1646,7 @@ void CGameHandler::useScholarSkill(ObjectInstanceID fromHero, ObjectInstanceID t
}
}
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 148);//from %s
iw.text.replaceRawString(h2->getNameTranslated());
iw.text.replaceTextID(h2->getNameTextID());
sendAndApply(&cs1);
}
sendAndApply(&iw);
@ -4269,14 +4269,6 @@ void CGameHandler::showInfoDialog(InfoWindow * iw)
sendAndApply(iw);
}
void CGameHandler::showInfoDialog(const std::string & msg, PlayerColor player)
{
InfoWindow iw;
iw.player = player;
iw.text.appendRawString(msg);
showInfoDialog(&iw);
}
CRandomGenerator & CGameHandler::getRandomGenerator()
{
return CRandomGenerator::getDefault();

View File

@ -160,7 +160,6 @@ public:
void setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value) override;
void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) override;
void showInfoDialog(InfoWindow * iw) override;
void showInfoDialog(const std::string & msg, PlayerColor player) override;
//////////////////////////////////////////////////////////////////////////
void useScholarSkill(ObjectInstanceID hero1, ObjectInstanceID hero2);

View File

@ -1523,26 +1523,27 @@ void BattleActionProcessor::addGenericKilledLog(BattleLogMessage & blm, const CS
{
if(killed > 0)
{
const int32_t txtIndex = (killed > 1) ? 379 : 378;
std::string formatString = VLC->generaltexth->allTexts[txtIndex];
// these default h3 texts have unnecessary new lines, so get rid of them before displaying (and trim just in case, trimming newlines does not works for some reason)
formatString.erase(std::remove(formatString.begin(), formatString.end(), '\n'), formatString.end());
formatString.erase(std::remove(formatString.begin(), formatString.end(), '\r'), formatString.end());
boost::algorithm::trim(formatString);
boost::format txt(formatString);
if(killed > 1)
{
txt % killed % (multiple ? VLC->generaltexth->allTexts[43] : defender->unitType()->getNamePluralTranslated()); // creatures perish
}
else //killed == 1
{
txt % (multiple ? VLC->generaltexth->allTexts[42] : defender->unitType()->getNameSingularTranslated()); // creature perishes
}
MetaString line;
line.appendRawString(txt.str());
blm.lines.push_back(std::move(line));
if (killed > 1)
{
line.appendTextID("core.genrltxt.379"); // %d %s perished
line.replaceNumber(killed);
}
else
line.appendTextID("core.genrltxt.378"); // One %s perishes
if (multiple)
{
if (killed > 1)
line.replaceTextID("core.genrltxt.43"); // creatures
else
line.replaceTextID("core.genrltxt.42"); // creature
}
else
line.replaceName(defender->unitType()->getId(), killed);
blm.lines.push_back(line);
}
}

View File

@ -38,7 +38,6 @@ public:
void setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value = 0) override {}
void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) override {}
void showInfoDialog(InfoWindow * iw) override {}
void showInfoDialog(const std::string & msg, PlayerColor player) override {}
void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> &spells) override {}
bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override {return false;}

View File

@ -33,8 +33,7 @@ bool operator==(const Bonus & b1, const Bonus & b2)
&& b1.sid == b2.sid
&& b1.valType == b2.valType
&& b1.additionalInfo == b2.additionalInfo
&& b1.effectRange == b2.effectRange
&& b1.description == b2.description;
&& b1.effectRange == b2.effectRange;
}
namespace test