1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-25 22:42:04 +02:00

More generic support for bonus subtypes descriptions

This commit is contained in:
Ivan Savenko
2025-05-23 19:19:03 +03:00
parent 747cf21cab
commit 6c5b2b8e63
4 changed files with 76 additions and 23 deletions

View File

@@ -621,9 +621,8 @@
"mapObject.core.hillFort.object.description" : "Upgrades creatures. Levels 1 - 4 are less expensive than in associated town.",
"core.bonus.ADDITIONAL_ATTACK.description" : "{Additional attacks}\nUnit can attack an additional {$val} times",
"core.bonus.ADDITIONAL_ATTACK.description" : "{Additional attacks}\nUnit can attack an additional {$val} times", // TODO: alternative descriptions for effect range
"core.bonus.ADDITIONAL_RETALIATION.description" : "{Additional retaliations}\nUnit can retaliate ${val} extra times",
"core.bonus.AIR_IMMUNITY.description" : "{Immune to Air Magic}\nImmune to all spells from the school of Air magic",
"core.bonus.ATTACKS_ALL_ADJACENT.description" : "{Attack all around}\nAttacks all adjacent enemies in addition to the primary target",
"core.bonus.BLOCKS_RANGED_RETALIATION.description" : "{No ranged retaliation}\nThe enemy cannot retaliate when shot by this unit",
"core.bonus.BLOCKS_RETALIATION.description" : "{No retaliation}\nThe enemy cannot retaliate when attacked in melee by this unit",
@@ -637,7 +636,6 @@
"core.bonus.DISINTEGRATE.description" : "{Disintegrate}\nWhen this unit dies, it will leave no corpse behind",
"core.bonus.DOUBLE_DAMAGE_CHANCE.description" : "{Death Blow}\nHas a ${val}% chance of dealing double base damage when attacking",
"core.bonus.DRAGON_NATURE.description" : "{Dragon}\nThis creature is a Dragon",
"core.bonus.EARTH_IMMUNITY.description" : "{Immune to Earth Magic}\nImmune to all spells from the school of Earth magic",
"core.bonus.ENCHANTED.description" : "{Enchanted}\nPermanently affected by ${subtype.spell}",
"core.bonus.ENCHANTER.description" : "{Enchanter}\nCan cast ${subtype.spell} every turn",
"core.bonus.ENEMY_ATTACK_REDUCTION.description" : "{Ignore Attack (${val}%) }\nWhen being attacked, ${val}% of the attacker's attack is ignored",
@@ -645,19 +643,25 @@
"core.bonus.FEAR.description" : "{Fear}\nEnemy units have a 10% chance of freezing in fear",
"core.bonus.FEARLESS.description" : "{Fearless}\nImmune to Fear ability",
"core.bonus.FEROCITY.description" : "{Ferocity}\nAttacks ${val} additional times if killed anybody",
"core.bonus.FIRE_IMMUNITY.description" : "{Immune to Fire Magic}\nImmune to all spells from the school of Fire magic",
"core.bonus.FIRE_SHIELD.description" : "{Fire Shield (${val}%) }\nThe unit reflects ${val} of the melee damage received",
"core.bonus.FIRST_STRIKE.description" : "{First Strike}\nThe unit retaliates before being attacked",
"core.bonus.FIRST_STRIKE.description.bonusSubtype.damageTypeRanged" : "{First Strike}\nThe unit retaliates before being attacked by ranged attack",
"core.bonus.FIRST_STRIKE.description.bonusSubtype.damageTypeMelee" : "{First Strike}\nThe unit retaliates before being attacked in melee",
"core.bonus.FLYING.description" : "{Can Fly}\nThis unit can fly while moving and will ignore battlefield obstacles",
"core.bonus.FLYING.description.bonusSubtype.movementTeleporting" : "{Teleportation}\nThis unit can teleport to any hex and ignore battlefield obstacles",
"core.bonus.FREE_SHOOTING.description" : "{Shoot Close}\nRanged attacks of this unit can not be blocked by adjacent enemies",
"core.bonus.GARGOYLE.description" : "{Gargoyle}\nThis unit cannot be raised from the dead or healed",
"core.bonus.GENERAL_DAMAGE_REDUCTION.description" : "{Reduce Damage (${val}%) }\nReduces physical damage from ranged or melee attacks by ${val}%",
"core.bonus.GENERAL_DAMAGE_REDUCTION.description.bonusSubtype.damageTypeRanged" : "{Reduce Damage (${val}%) }\nReduces physical damage from ranged attacks by ${val}%",
"core.bonus.GENERAL_DAMAGE_REDUCTION.description.bonusSubtype.damageTypeMelee" : "{Reduce Damage (${val}%) }\nReduces physical damage from melee attacks by ${val}%",
"core.bonus.HATE.description" : "{Hates ${subtype.creature}}\nDoes ${val}% more damage to ${subtype.creature}",
"core.bonus.HEALER.description" : "{Healer}\nHeals allied units",
"core.bonus.HP_REGENERATION.description" : "{Regeneration}\nHeals ${val} hit points every round",
"core.bonus.INVINCIBLE.description" : "{Invincible}\nCannot be affected by anything",
"core.bonus.JOUSTING.description" : "{Jousting bonus}\nMoving before an attack increases the damage by ${val} for each hex travelled",
"core.bonus.KING.description" : "{King}\nReceives additional damage from units under Slayer effect of level ${val} or higher",
"core.bonus.KING.description" : "{King}\nReceives additional damage from units under Slayer spell",
"core.bonus.KING.description.2" : "{Advanced King}\nReceives additional damage from units under Advanced Slayer spell",
"core.bonus.KING.description.3" : "{Expert King}\nReceives additional damage from units under Expert Slayer spell",
"core.bonus.LEVEL_SPELL_IMMUNITY.description" : "{Immune to spells level 1-${val}}\nThis unit cannot be targeted by spells of levels 1-${val}",
"core.bonus.LIFE_DRAIN.description" : "{Drain life}\nDrains ${val}% of damage dealt",
"core.bonus.LIMITED_SHOOTING_RANGE.description" : "{Limited shooting range}\nUnable to use a ranged attack against units more than ${val} hexes away",
@@ -667,7 +671,7 @@
"core.bonus.MANA_DRAIN.description" : "{Drains enemy mana}\nDrains ${val} mana every turn from enemy hero",
"core.bonus.MECHANICAL.description" : "{Mechanical}\nThis unit is immune to effects that only affect living and can be repaired",
"core.bonus.MIND_IMMUNITY.description" : "{Mind Spell Immunity}\nThis unit cannot be targeted by spells that affect its mind",
"core.bonus.NO_DISTANCE_PENALTY.description" : "{No distance penalty}\nRanged attacks deal full damage at any range",
"core.bonus.NO_DISTANCE_PENALTY.description" : "{No distance penalty}\nRanged attacks deal full damage at any distance",
"core.bonus.NO_MELEE_PENALTY.description" : "{No melee penalty}\nThis ranged unit deals full damage with melee attacks",
"core.bonus.NO_MORALE.description" : "{Neutral Morale}\nCreature is immune to morale effects",
"core.bonus.NO_WALL_PENALTY.description" : "{No wall penalty}\nRanged attacks deal full damage to units behind walls",
@@ -685,30 +689,28 @@
"core.bonus.SPELL_AFTER_ATTACK.description" : "{Cast After Attack}\nHas a ${val}% chance to cast ${subtype.spell} after it attacks",
"core.bonus.SPELL_BEFORE_ATTACK.description" : "{Cast Before Attack}\nHas a ${val}% chance to cast ${subtype.spell} before it attacks",
"core.bonus.SPELL_DAMAGE_REDUCTION.description" : "{Spell Resistance}\nDamage from all spells reduced by ${val}%",
"core.bonus.SPELL_DAMAGE_REDUCTION.description.air" : "{Air Spells Resistance}\nDamage from Air Magic spells reduced by ${val}%",
"core.bonus.SPELL_DAMAGE_REDUCTION.description.earth" : "{Earth Spells Resistance}\nDamage from Earth Magic spells reduced by ${val}%",
"core.bonus.SPELL_DAMAGE_REDUCTION.description.fire" : "{Fire Spells Resistance}\nDamage from Fire Magic spells reduced by ${val}%",
"core.bonus.SPELL_DAMAGE_REDUCTION.description.water" : "{Water Spells Resistance}\nDamage from Water Magic spells reduced by ${val}%",
"core.bonus.SPELL_DAMAGE_REDUCTION.description.spellSchool.air" : "{Air Spells Resistance}\nDamage from Air Magic spells reduced by ${val}%",
"core.bonus.SPELL_DAMAGE_REDUCTION.description.spellSchool.earth" : "{Earth Spells Resistance}\nDamage from Earth Magic spells reduced by ${val}%",
"core.bonus.SPELL_DAMAGE_REDUCTION.description.spellSchool.fire" : "{Fire Spells Resistance}\nDamage from Fire Magic spells reduced by ${val}%",
"core.bonus.SPELL_DAMAGE_REDUCTION.description.spellSchool.water" : "{Water Spells Resistance}\nDamage from Water Magic spells reduced by ${val}%",
"core.bonus.SPELL_IMMUNITY.description" : "{Spell immunity}\nThis unit can not be affected by ${subtype.spell}",
"core.bonus.SPELL_LIKE_ATTACK.description" : "{Spell-like attack}\nAttacks with ${subtype.spell}",
"core.bonus.SPELL_RESISTANCE_AURA.description" : "{Aura of Resistance}\nAdjacent units get ${val}% magic resistance",
"core.bonus.SPELL_SCHOOL_IMMUNITY.description" : "{Spell immunity}\nThis unit is immune to all spells",
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.air" : "{Air immunity}\nThis unit is immune to all Air school spells",
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.earth" : "{Earth immunity}\nThis unit is immune to all Earth school spells",
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.fire" : "{Fire immunity}\nThis unit is immune to all Fire school spells",
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.water" : "{Water immunity}\nThis unit is immune to all Water school spells",
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.air" : "{Immune to Air Magic}\nImmune to all spells from the school of Air Magic",
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.earth" : "{Immune to Earth Magic}\nImmune to all spells from the school of Earth Magic",
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.fire" : "{Immune to Fire Magic}\nImmune to all spells from the school of Fire Magic",
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.spellSchool.water" : "{Immune to Water Magic}\nImmune to all spells from the school of Water Magic",
"core.bonus.SPELLCASTER.description" : "{Spellcaster}\nCan cast ${subtype.spell}",
"core.bonus.SUMMON_GUARDIANS.description" : "{Summon guardians}\nAt the start of battle summons ${subtype.creature} (${val}%)",
"core.bonus.SYNERGY_TARGET.description" : "{Synergizable}\nThis creature is vulnerable to synergy effect",
"core.bonus.THREE_HEADED_ATTACK.description" : "{Three-headed attack}\nAttacks three adjacent units",
"core.bonus.TRANSMUTATION.description" : "{Transmutation}\n${val}% chance to transform attacked unit to a different type",
"core.bonus.TWO_HEX_ATTACK_BREATH.description" : "{Dragon Breath}\nAttacks by this unit will also hit any unit positioned immediately behind the target",
"core.bonus.TWO_HEX_ATTACK_BREATH.description" : "{Breath Attack}\nAttacks by this unit will also hit any unit positioned immediately behind the target",
"core.bonus.UNDEAD.description" : "{Undead}\nCreature is Undead and is immune to effects that only affect living",
"core.bonus.UNLIMITED_RETALIATIONS.description" : "{Unlimited retaliations}\nThis unit can retaliate against an unlimited number of attacks",
"core.bonus.WATER_IMMUNITY.description" : "{Immune to Water Magic}\nImmune to all spells from the school of Water magic",
"core.bonus.WIDE_BREATH.description" : "{Wide breath}\nThis unit attacks all units around its target",
"spell.core.castleMoat.name" : "Moat",
"spell.core.castleMoatTrigger.name" : "Moat",
"spell.core.catapultShot.name" : "Catapult shot",

View File

@@ -92,6 +92,10 @@
"FIRST_STRIKE":
{
"subtypeDescriptions" : {
"bonusSubtype.damageTypeRanged" : null,
"bonusSubtype.damageTypeMelee" : null,
}
},
"FEAR":
@@ -108,6 +112,9 @@
"FLYING":
{
"subtypeDescriptions" : {
"bonusSubtype.movementTeleporting" : null,
}
},
"FREE_SHOOTING":
@@ -120,6 +127,10 @@
"GENERAL_DAMAGE_REDUCTION":
{
"subtypeDescriptions" : {
"bonusSubtype.damageTypeRanged" : null,
"bonusSubtype.damageTypeMelee" : null,
}
},
"HATE":
@@ -140,6 +151,10 @@
"KING":
{
"valueDescriptions" : {
"2" : null,
"3" : null
}
},
"LEARN_BATTLE_SPELL_CHANCE":
@@ -281,6 +296,12 @@
"SPELL_DAMAGE_REDUCTION":
{
"subtypeDescriptions" : {
"spellSchool.air" : null,
"spellSchool.earth" : null,
"spellSchool.fire" : null,
"spellSchool.water" : null,
}
},
"SPELL_IMMUNITY":
@@ -293,6 +314,12 @@
"SPELL_SCHOOL_IMMUNITY":
{
"subtypeDescriptions" : {
"spellSchool.air" : null,
"spellSchool.earth" : null,
"spellSchool.fire" : null,
"spellSchool.water" : null,
}
},
"SPELL_RESISTANCE_AURA":

View File

@@ -60,23 +60,27 @@ CBonusTypeHandler::~CBonusTypeHandler() = default;
std::string CBonusTypeHandler::bonusToString(const std::shared_ptr<Bonus> & bonus, const IBonusBearer * bearer) const
{
const CBonusType & bt = bonusTypes[vstd::to_underlying(bonus->type)];
int bonusValue = bearer->valOfBonuses(bonus->type, bonus->subtype);
if(bt.hidden)
return "";
std::string textID = bt.getDescriptionTextID();
std::string text = LIBRARY->generaltexth->translate(textID);
auto school = bonus->subtype.as<SpellSchool>();
if (school.hasValue() && school != SpellSchool::ANY)
auto subtype = bonus->subtype.getNum();
if (bt.subtypeDescriptions.count(subtype))
{
std::string schoolName = school.encode(school.getNum());
std::string baseTextID = bt.getDescriptionTextID();
std::string fullTextID = baseTextID + '.' + schoolName;
std::string fullTextID = textID + '.' + bt.subtypeDescriptions.at(subtype);
text = LIBRARY->generaltexth->translate(fullTextID);
}
else if (bt.valueDescriptions.count(bonusValue))
{
std::string fullTextID = textID + '.' + bt.valueDescriptions.at(bonusValue);
text = LIBRARY->generaltexth->translate(fullTextID);
}
if (text.find("${val}") != std::string::npos)
boost::algorithm::replace_all(text, "${val}", std::to_string(bearer->valOfBonuses(bonus->type, bonus->subtype)));
boost::algorithm::replace_all(text, "${val}", std::to_string(bonusValue));
if (text.find("${subtype.creature}") != std::string::npos && bonus->subtype.as<CreatureID>().hasValue())
boost::algorithm::replace_all(text, "${subtype.creature}", bonus->subtype.as<CreatureID>().toCreature()->getNamePluralTranslated());
@@ -161,6 +165,24 @@ void CBonusTypeHandler::loadItem(const JsonNode & source, CBonusType & dest, con
int value = std::stoi(additionalIcon.first);
dest.valueIcons[value] = path;
}
for (const auto & additionalDescription : source["subtypeDescriptions"].Struct())
{
LIBRARY->generaltexth->registerString( "vcmi", dest.getDescriptionTextID() + "." + additionalDescription.first, additionalDescription.second);
auto stringID = additionalDescription.first;
LIBRARY->identifiers()->requestIdentifier(additionalDescription.second.getModScope(), additionalDescription.first, [&dest, stringID](int32_t index)
{
dest.subtypeDescriptions[index] = stringID;
});
}
for (const auto & additionalDescription : source["valueDescriptions"].Struct())
{
LIBRARY->generaltexth->registerString( "vcmi", dest.getDescriptionTextID() + "." + additionalDescription.first, additionalDescription.second);
auto stringID = additionalDescription.first;
int value = std::stoi(additionalDescription.first);
dest.valueDescriptions[value] = stringID;
}
}
VCMI_LIB_NAMESPACE_END

View File

@@ -32,6 +32,8 @@ private:
ImagePath icon;
std::map<int, ImagePath> subtypeIcons;
std::map<int, ImagePath> valueIcons;
std::map<int, std::string> subtypeDescriptions;
std::map<int, std::string> valueDescriptions;
std::string identifier;
bool hidden;