1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-02-03 13:01:33 +02:00

vcmi: deprecated bonus converter

It converts almost all sorts of deprecated bonuses from mods
when loading json. It can print to console correct new variant
or bonus.

Also removed a bunch of deprecated bonuses from list.
It will break saves!!!
This commit is contained in:
Konstantin 2023-03-05 03:16:05 +03:00
parent 95503d0623
commit 930955f268
5 changed files with 318 additions and 30 deletions

View File

@ -1,5 +1,4 @@
//TODO: selector-based config
// SECONDARY_SKILL_PREMY
// school immunities
// LEVEL_SPELL_IMMUNITY
@ -432,12 +431,6 @@
}
},
"SECONDARY_SKILL_PREMY":
{
"hidden": true
//todo: selector based config
},
"SELF_LUCK":
{
"graphics":

View File

@ -555,12 +555,17 @@ std::vector<std::shared_ptr<Bonus>> SpecialtyInfoToBonuses(const SSpecialtyInfo
AddSpecialtyForCreature(spec.additionalinfo, bonus, result);
break;
case 2: //secondary skill
bonus->type = Bonus::SECONDARY_SKILL_PREMY;
bonus->valType = Bonus::PERCENT_TO_BASE;
bonus->subtype = spec.subtype;
{
auto params = BonusParams("SECONDARY_SKILL_PREMY", "", spec.subtype);
bonus->type = params.type;
if(params.subtypeRelevant)
bonus->subtype = params.subtype;
bonus->valType = Bonus::PERCENT_TO_TARGET_TYPE;
bonus->targetSourceType = Bonus::SECONDARY_SKILL;
bonus->updater.reset(new TimesHeroLevelUpdater());
result.push_back(bonus);
break;
}
case 3: //spell damage bonus, level dependent but calculated elsewhere
bonus->type = Bonus::SPECIAL_SPELL_LEV;
bonus->subtype = spec.subtype;

View File

@ -34,7 +34,6 @@ VCMI_LIB_NAMESPACE_BEGIN
#define BONUS_NAME(x) { #x, Bonus::x },
const std::map<std::string, Bonus::BonusType> bonusNameMap = {
BONUS_LIST
{"SIGHT_RADIOUS", Bonus::SIGHT_RADIUS} /*the correct word is RADIUS, but this one's already used in mods. Deprecated. */
};
#undef BONUS_NAME
@ -99,6 +98,21 @@ const std::map<std::string, TUpdaterPtr> bonusUpdaterMap =
{"ARMY_MOVEMENT", std::make_shared<ArmyMovementUpdater>()}
};
const std::set<std::string> deprecatedBonusSet = {
"SECONDARY_SKILL_PREMY",
"SECONDARY_SKILL_VAL2",
"MAXED_SPELL",
"LAND_MOVEMENT",
"SEA_MOVEMENT",
"SIGHT_RADIOUS",
"NO_TYPE",
"SPECIAL_SECONDARY_SKILL",
"FULL_HP_REGENERATION",
"KING1",
"KING2",
"KING3",
};
///CBonusProxy
CBonusProxy::CBonusProxy(const IBonusBearer * Target, CSelector Selector)
: bonusListCachedLast(0),
@ -1655,8 +1669,6 @@ JsonNode subtypeToJson(Bonus::BonusType type, int subtype)
{
case Bonus::PRIMARY_SKILL:
return JsonUtils::stringNode("primSkill." + PrimarySkill::names[subtype]);
case Bonus::SECONDARY_SKILL_PREMY:
return JsonUtils::stringNode(CSkillHandler::encodeSkillWithType(subtype));
case Bonus::SPECIAL_SPELL_LEV:
case Bonus::SPECIFIC_SPELL_DAMAGE:
case Bonus::SPELL:
@ -1762,8 +1774,6 @@ std::string Bonus::nameForBonus() const
{
case Bonus::PRIMARY_SKILL:
return PrimarySkill::names[subtype];
case Bonus::SECONDARY_SKILL_PREMY:
return CSkillHandler::encodeSkill(subtype);
case Bonus::SPECIAL_SPELL_LEV:
case Bonus::SPECIFIC_SPELL_DAMAGE:
case Bonus::SPELL:
@ -1782,6 +1792,212 @@ std::string Bonus::nameForBonus() const
}
}
BonusParams::BonusParams(std::string deprecatedTypeStr, std::string deprecatedSubtypeStr, int deprecatedSubtype):
isConverted(true)
{
if(deprecatedTypeStr == "SECONDARY_SKILL_PREMY" || deprecatedTypeStr == "SPECIAL_SECONDARY_SKILL")
{
if(deprecatedSubtype == SecondarySkill::PATHFINDING || deprecatedSubtypeStr == "skill.pathfinding")
type = Bonus::ROUGH_TERRAIN_DISCOUNT;
else if(deprecatedSubtype == SecondarySkill::DIPLOMACY || deprecatedSubtypeStr == "skill.diplomacy")
type = Bonus::WANDERING_CREATURES_JOIN_BONUS;
else if(deprecatedSubtype == SecondarySkill::WISDOM || deprecatedSubtypeStr == "skill.wisdom")
type = Bonus::MAX_LEARNABLE_SPELL_LEVEL;
else if(deprecatedSubtype == SecondarySkill::MYSTICISM || deprecatedSubtypeStr == "skill.mysticism")
type = Bonus::MANA_REGENERATION;
else if(deprecatedSubtype == SecondarySkill::NECROMANCY || deprecatedSubtypeStr == "skill.necromancy")
type = Bonus::UNDEAD_RAISE_PERCENTAGE;
else if(deprecatedSubtype == SecondarySkill::LEARNING || deprecatedSubtypeStr == "skill.learning")
type = Bonus::HERO_EXPERIENCE_GAIN_PERCENT;
else if(deprecatedSubtype == SecondarySkill::RESISTANCE || deprecatedSubtypeStr == "skill.resistance")
type = Bonus::MAGIC_RESISTANCE;
else if(deprecatedSubtype == SecondarySkill::EAGLE_EYE || deprecatedSubtypeStr == "skill.eagleEye")
type = Bonus::LEARN_BATTLE_SPELL_CHANCE;
else if(deprecatedSubtype == SecondarySkill::INTELLIGENCE || deprecatedSubtypeStr == "skill.intelligence")
{
type = Bonus::MANA_PER_KNOWLEDGE;
valueType = Bonus::PERCENT_TO_BASE;
valueTypeRelevant = true;
}
else if(deprecatedSubtype == SecondarySkill::SORCERY || deprecatedSubtypeStr == "skill.sorcery")
type = Bonus::SPELL_DAMAGE;
else if(deprecatedSubtype == SecondarySkill::SCHOLAR || deprecatedSubtypeStr == "skill.scholar")
type = Bonus::LEARN_MEETING_SPELL_LIMIT;
else if(deprecatedSubtype == SecondarySkill::ARCHERY|| deprecatedSubtypeStr == "skill.archery")
{
subtype = 1;
subtypeRelevant = true;
type = Bonus::PERCENTAGE_DAMAGE_BOOST;
}
else if(deprecatedSubtype == SecondarySkill::OFFENCE || deprecatedSubtypeStr == "skill.offence")
{
subtype = 0;
subtypeRelevant = true;
type = Bonus::PERCENTAGE_DAMAGE_BOOST;
}
else if(deprecatedSubtype == SecondarySkill::ARMORER || deprecatedSubtypeStr == "skill.armorer")
{
subtype = -1;
subtypeRelevant = true;
type = Bonus::GENERAL_DAMAGE_REDUCTION;
}
else if(deprecatedSubtype == SecondarySkill::NAVIGATION || deprecatedSubtypeStr == "skill.navigation")
{
subtype = 0;
subtypeRelevant = true;
valueType = Bonus::PERCENT_TO_BASE;
valueTypeRelevant = true;
type = Bonus::MOVEMENT;
}
else if(deprecatedSubtype == SecondarySkill::LOGISTICS || deprecatedSubtypeStr == "skill.logistics")
{
subtype = 0;
subtypeRelevant = true;
valueType = Bonus::PERCENT_TO_BASE;
valueTypeRelevant = true;
type = Bonus::MOVEMENT;
}
else if(deprecatedSubtype == SecondarySkill::ESTATES || deprecatedSubtypeStr == "skill.estates")
{
type = Bonus::GENERATE_RESOURCE;
subtype = Res::GOLD;
subtypeRelevant = true;
}
else if(deprecatedSubtype == SecondarySkill::AIR_MAGIC || deprecatedSubtypeStr == "skill.airMagic")
{
type = Bonus::MAGIC_SCHOOL_SKILL;
subtypeRelevant = true;
subtype = 4;
}
else if(deprecatedSubtype == SecondarySkill::WATER_MAGIC || deprecatedSubtypeStr == "skill.waterMagic")
{
type = Bonus::MAGIC_SCHOOL_SKILL;
subtypeRelevant = true;
subtype = 1;
}
else if(deprecatedSubtype == SecondarySkill::FIRE_MAGIC || deprecatedSubtypeStr == "skill.fireMagic")
{
type = Bonus::MAGIC_SCHOOL_SKILL;
subtypeRelevant = true;
subtype = 2;
}
else if(deprecatedSubtype == SecondarySkill::EARTH_MAGIC || deprecatedSubtypeStr == "skill.earthMagic")
{
type = Bonus::MAGIC_SCHOOL_SKILL;
subtypeRelevant = true;
subtype = 8;
}
else if (deprecatedSubtype == SecondarySkill::ARTILLERY || deprecatedSubtypeStr == "skill.artillery")
{
type = Bonus::BONUS_DAMAGE_CHANCE;
subtypeRelevant = true;
subtypeStr = "core:creature.ballista";
}
else if (deprecatedSubtype == SecondarySkill::FIRST_AID || deprecatedSubtypeStr == "skill.firstAid")
{
type = Bonus::SPECIFIC_SPELL_POWER;
subtypeRelevant = true;
subtypeStr = "core:spell.firstAid";
}
else if (deprecatedSubtype == SecondarySkill::BALLISTICS || deprecatedSubtypeStr == "skill.ballistics")
{
type = Bonus::CATAPULT_EXTRA_SHOTS;
subtypeRelevant = true;
subtypeStr = "core:spell.catapultShot";
}
else
isConverted = false;
}
else if (deprecatedTypeStr == "SECONDARY_SKILL_VAL2")
{
if(deprecatedSubtype == SecondarySkill::EAGLE_EYE || deprecatedSubtypeStr == "skill.eagleEye")
type = Bonus::LEARN_BATTLE_SPELL_LEVEL_LIMIT;
else if (deprecatedSubtype == SecondarySkill::ARTILLERY || deprecatedSubtypeStr == "skill.artillery")
{
type = Bonus::HERO_GRANTS_ATTACKS;
subtypeRelevant = true;
subtypeStr = "core:creature.ballista";
}
else
isConverted = false;
}
else if (deprecatedTypeStr == "SEA_MOVEMENT")
{
subtype = 0;
subtypeRelevant = true;
valueType = Bonus::ADDITIVE_VALUE;
valueTypeRelevant = true;
type = Bonus::MOVEMENT;
}
else if (deprecatedTypeStr == "LAND_MOVEMENT")
{
subtype = 1;
subtypeRelevant = true;
valueType = Bonus::ADDITIVE_VALUE;
valueTypeRelevant = true;
type = Bonus::MOVEMENT;
}
else if (deprecatedTypeStr == "MAXED_SPELL")
{
type = Bonus::SPELL;
subtypeStr = deprecatedSubtypeStr;
subtypeRelevant = true;
valueType = Bonus::INDEPENDENT_MAX;
valueTypeRelevant = true;
val = 3;
valRelevant = true;
}
else if (deprecatedTypeStr == "FULL_HP_REGENERATION")
{
type = Bonus::HP_REGENERATION;
val = 100000; //very high value to always chose stack health
valRelevant = true;
}
else if (deprecatedTypeStr == "KING1")
{
type = Bonus::KING;
val = 0;
valRelevant = true;
}
else if (deprecatedTypeStr == "KING2")
{
type = Bonus::KING;
val = 2;
valRelevant = true;
}
else if (deprecatedTypeStr == "KING3")
{
type = Bonus::KING;
val = 3;
valRelevant = true;
}
else if (deprecatedTypeStr == "SIGHT_RADIOUS")
type = Bonus::SIGHT_RADIUS;
else
isConverted = false;
}
const JsonNode & BonusParams::toJson()
{
assert(isConverted);
if(ret.isNull())
{
ret["type"].String() = vstd::findKey(bonusNameMap, type);
if(subtypeRelevant && !subtypeStr.empty())
ret["subtype"].String() = subtypeStr;
else if(subtypeRelevant)
ret["subtype"].Integer() = subtype;
if(valueTypeRelevant)
ret["valueType"].String() = vstd::findKey(bonusValueMap, valueType);
if(valRelevant)
ret["val"].Float() = val;
if(targetTypeRelevant)
ret["targetSourceType"].String() = vstd::findKey(bonusSourceMap, targetType);
jsonCreated = true;
}
return ret;
};
Bonus::Bonus(Bonus::BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype)
: duration((ui16)Duration), type(Type), subtype(Subtype), source(Src), val(Val), sid(ID), description(Desc)
{

View File

@ -179,7 +179,6 @@ public:
BONUS_NAME(MANA_REGENERATION) /*points per turn apart from normal (1 + mysticism)*/ \
BONUS_NAME(FULL_MANA_REGENERATION) /*all mana points are replenished every day*/ \
BONUS_NAME(NONEVIL_ALIGNMENT_MIX) /*good and neutral creatures can be mixed without morale penalty*/ \
BONUS_NAME(SECONDARY_SKILL_PREMY) /*%*/ \
BONUS_NAME(SURRENDER_DISCOUNT) /*%*/ \
BONUS_NAME(STACKS_SPEED) /*additional info - percent of speed bonus applied after direct bonuses; >0 - added, <0 - subtracted to this part*/ \
BONUS_NAME(FLYING_MOVEMENT) /*value - penalty percentage*/ \
@ -209,7 +208,6 @@ public:
BONUS_NAME(IMPROVED_NECROMANCY) /* raise more powerful creatures: subtype - creature type raised, addInfo - [required necromancy level, required stack level], val - necromancy level for this purpose */ \
BONUS_NAME(CREATURE_GROWTH_PERCENT) /*increases growth of all units in all towns, val - percentage*/ \
BONUS_NAME(FREE_SHIP_BOARDING) /*movement points preserved with ship boarding and landing*/ \
BONUS_NAME(NO_TYPE) \
BONUS_NAME(FLYING) \
BONUS_NAME(SHOOTER) \
BONUS_NAME(CHARGE_IMMUNITY) \
@ -281,7 +279,6 @@ public:
BONUS_NAME(NO_LUCK) /*eg. when fighting on cursed ground*/ \
BONUS_NAME(NO_MORALE) /*eg. when fighting on cursed ground*/ \
BONUS_NAME(DARKNESS) /*val = radius */ \
BONUS_NAME(SPECIAL_SECONDARY_SKILL) /*subtype = id, val = value per level in percent*/ \
BONUS_NAME(SPECIAL_SPELL_LEV) /*subtype = id, val = value per level in percent*/\
BONUS_NAME(SPELL_DAMAGE) /*val = value, now works for sorcery*/\
BONUS_NAME(SPECIFIC_SPELL_DAMAGE) /*subtype = id of spell, val = value*/\
@ -315,7 +312,6 @@ public:
BONUS_NAME(CATAPULT_EXTRA_SHOTS) /*val - power of catapult effect, requires CATAPULT bonus to work*/\
BONUS_NAME(RANGED_RETALIATION) /*allows shooters to perform ranged retaliation*/\
BONUS_NAME(BLOCKS_RANGED_RETALIATION) /*disallows ranged retaliation for shooter unit, BLOCKS_RETALIATION bonus is for melee retaliation only*/\
BONUS_NAME(SECONDARY_SKILL_VAL2) /*deprecated. has no effect, will be converted to actual bonus*/ \
BONUS_NAME(MANUAL_CONTROL) /* manually control warmachine with id = subtype, chance = val */ \
BONUS_NAME(WIDE_BREATH) /* initial desigh: dragon breath affecting multiple nearby hexes */\
BONUS_NAME(FIRST_STRIKE) /* first counterattack, then attack if possible */\
@ -542,6 +538,27 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus);
struct DLL_LINKAGE BonusParams {
bool isConverted;
Bonus::BonusType type = Bonus::NONE;
TBonusSubtype subtype = -1;
std::string subtypeStr = "";
bool subtypeRelevant = false;
Bonus::ValueType valueType = Bonus::BASE_NUMBER;
bool valueTypeRelevant = false;
si32 val = 0;
bool valRelevant = false;
Bonus::BonusSource targetType = Bonus::SECONDARY_SKILL;
bool targetTypeRelevant = false;
BonusParams(bool isConverted = true) : isConverted(isConverted) {};
BonusParams(std::string deprecatedTypeStr, std::string deprecatedSubtypeStr = "", int deprecatedSubtype = 0);
const JsonNode & toJson();
private:
JsonNode ret;
bool jsonCreated = false;
};
class DLL_LINKAGE BonusList
{
public:
@ -1232,6 +1249,7 @@ extern DLL_LINKAGE const std::map<std::string, Bonus::LimitEffect> bonusLimitEff
extern DLL_LINKAGE const std::map<std::string, TLimiterPtr> bonusLimiterMap;
extern DLL_LINKAGE const std::map<std::string, TPropagatorPtr> bonusPropagatorMap;
extern DLL_LINKAGE const std::map<std::string, TUpdaterPtr> bonusUpdaterMap;
extern DLL_LINKAGE const std::set<std::string> deprecatedBonusSet;
// BonusList template that requires full interface of CBonusSystemNode
template <class InputIterator>

View File

@ -805,26 +805,82 @@ std::shared_ptr<Bonus> JsonUtils::parseBuildingBonus(const JsonNode &ability, Bu
return b;
}
static BonusParams convertDeprecatedBonus(const JsonNode &ability)
{
if(vstd::contains(deprecatedBonusSet, ability["type"].String()))
{
logMod->warn("There is deprecated bonus found:\n%s\nTrying to convert...", ability.toJson());
auto params = BonusParams(ability["type"].String(),
ability["subtype"].isString() ? ability["subtype"].String() : "",
ability["subtype"].isNumber() ? ability["subtype"].Integer() : -1);
if(params.isConverted)
{
if(!params.valRelevant) {
params.val = static_cast<si32>(ability["val"].Float());
params.valRelevant = true;
if(params.type == Bonus::SPECIFIC_SPELL_POWER) //First Aid value should be substracted by 10
params.val -= 10; //Base First Aid value
}
Bonus::ValueType valueType = Bonus::ADDITIVE_VALUE;
if(!ability["valueType"].isNull())
valueType = bonusValueMap.find(ability["valueType"].String())->second;
if(ability["type"].String() == "SECONDARY_SKILL_PREMY" && valueType == Bonus::PERCENT_TO_BASE) //assume secondary skill special
{
params.valueType = Bonus::PERCENT_TO_TARGET_TYPE;
params.targetType = Bonus::SECONDARY_SKILL;
params.targetTypeRelevant = true;
}
if(!params.valueTypeRelevant) {
params.valueType = valueType;
params.valueTypeRelevant = true;
}
logMod->warn("Please, use this bonus:\n%s\nConverted sucessfully!", params.toJson().toJson());
return params;
}
else
logMod->error("Cannot convert bonus!\n%s", ability.toJson());
}
BonusParams ret;
ret.isConverted = false;
return ret;
}
bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
{
const JsonNode *value;
std::string type = ability["type"].String();
auto it = bonusNameMap.find(type);
auto params = std::make_unique<BonusParams>(false);
if (it == bonusNameMap.end())
{
params = std::make_unique<BonusParams>(convertDeprecatedBonus(ability));
if(!params->isConverted)
{
logMod->error("Error: invalid ability type %s.", type);
return false;
}
b->type = params->type;
b->val = params->val;
b->valType = params->valueType;
if(params->targetTypeRelevant)
b->targetSourceType = params->targetType;
}
else
b->type = it->second;
resolveIdentifier(b->subtype, ability, "subtype");
resolveIdentifier(b->subtype, params->isConverted ? params->toJson() : ability, "subtype");
if(!params->isConverted)
{
b->val = static_cast<si32>(ability["val"].Float());
value = &ability["valueType"];
if (!value->isNull())
b->valType = static_cast<Bonus::ValueType>(parseByMapN(bonusValueMap, value, "value type "));
}
b->stacking = ability["stacking"].String();