- You only get incorrect values if you don’t specify separate bonuses for base and upgraded creature where needed (assuming this is required for strict HMM3 compatibility). So with the new version you can correct this (though I haven’t in my json files), whereas the current implementation has the bug in it (and to fix this, we would still need to add separate bonuses for base and upgraded creatures).
- It’s complicated text because it exposes the full complexity of the HMM3 creature specialties. Also, the new system still accepts the simpler (and now bug-free after conversion to the new system) current format used by mods.
Why is that?
All creature specialties in OH3 work in same way and could be replaced with a single formula… this is atcually how it was done.
Because you want base and upgraded creature to get different bonuses (e.g. +7 attack vs +8).
The current implementation adds a single attack bonus to the hero (based on the base creatures) which gets applied to both the base and upgraded creature. This would need to be replaced with a +7 attack bonus which only applies to the base creature, and a +8 attack bonus that only applies to the upgraded creature.
The relevant code is below. The bonus is updated based on the base creature (creatureLimiter->creature), but applied to the upgraded version as well.
void CGHeroInstance::initObj(CRandomGenerator & rand)
{
...
case 1:// creature specialty
{
hs->growsWithLevel = true;
const CCreature &specCreature = *VLC->creh->creatures[spec.additionalinfo]; //creature in which we have specialty
//bonus->additionalInfo = spec.additionalinfo; //creature id, should not be used again - this works only with limiter
bonus->limiter.reset(new CCreatureTypeLimiter (specCreature, true)); //with upgrades
bonus->type = Bonus::PRIMARY_SKILL;
bonus->valType = Bonus::ADDITIVE_VALUE;
bonus->subtype = PrimarySkill::ATTACK;
hs->addNewBonus(bonus);
bonus = std::make_shared<Bonus>(*bonus);
bonus->subtype = PrimarySkill::DEFENSE;
hs->addNewBonus(bonus);
//values will be calculated later
bonus = std::make_shared<Bonus>(*bonus);
bonus->type = Bonus::STACKS_SPEED;
bonus->val = 1; //+1 speed
hs->addNewBonus(bonus);
}
break;
...
}
void CGHeroInstance::Updatespecialty() //TODO: calculate special value of bonuses on-the-fly?
{
for (auto hs : specialty)
{
if (hs->growsWithLevel)
{
//const auto &creatures = VLC->creh->creatures;
for(auto& b : hs->getBonusList())
{
switch (b->type)
{
case Bonus::SECONDARY_SKILL_PREMY:
b->val = (hs->valOfBonuses(Bonus::SPECIAL_SECONDARY_SKILL, b->subtype) * level);
break; //use only hero skills as bonuses to avoid feedback loop
case Bonus::PRIMARY_SKILL: //for creatures, that is
{
const CCreature * cre = nullptr;
int creLevel = 0;
if (auto creatureLimiter = std::dynamic_pointer_cast<CCreatureTypeLimiter>(b->limiter)) //TODO: more general eveluation of bonuses?
{
cre = creatureLimiter->creature;
creLevel = cre->level;
if (!creLevel)
{
creLevel = 5; //treat ballista as tier 5
}
}
else //no creature found, can't calculate value
{
logGlobal->warn("Primary skill specialty growth supported only with creature type limiters");
break;
}
double primSkillModifier = (int)(level / creLevel) / 20.0;
int param;
switch (b->subtype)
{
case PrimarySkill::ATTACK:
param = cre->Attack();
break;
case PrimarySkill::DEFENSE:
param = cre->Defense();
break;
default:
continue;
}
b->val = ceil(param * (1 + primSkillModifier)) - param; //yep, overcomplicated but matches original
break;
}
}
}
}
}
}
Ahh, I see! A bug indeed. So the bonus should be duplicated for upgrade.
Still, I think that this individual part of mechanics should be implemented in code and not in config. Duplicating the bonus in code is rather trivial, multiplying bonuses in all configs (incuding existing and future mods) - not.
Alternative solution would be to calculate the value fo bonus on the fly, via separate updater which knows both hero level and creature stats. This is probably simplest to understand and elegant.
The point here is that you may not want to use the standard formula in your mods, but create your own. IMO the current bonuses from create specialties are pretty pathetic, and I want to be able to mod them to my liking to make heroes with creature specialties worth selecting.
Specifying them explicitly for each creature in the json config files may be verbose, but serves as a good example for modders how the system works. And as mentioned before, if you just want to use the “standard” formula from vanilla HMM3 in your mod, you can use the current format (where the bonus value needn’t be specified), which is still supported.
But these are standard formulas for standard OH3 and any deviation is considered a bug. So the simpler it gets, the better. Nobody will understand it as it looks now, anyway.
I also want to mod the hell out of this game, but these are extra options and not default behavior.
It’s not hard to understand really. While it could be simpler, the current complexity is needed to support the crazy rounding in the HMM3 formula - i.e. to avoid any deviation in the default.
I don’t get what you mean by “extra options and not default behaviour”. The default behaviour of the game remains unchanged with the update. I used the new format in the config files as IMO the best way to learn is via example. Alternatively we could create an alias for the default formula (taking only the creature as parameter, and deriving all other values from that).
But I think exactly the opposite - the complexity shoud be hidden from modders who don’t know and don’t want to know the formula. In fact the formula exists to be calculated automatically and not by hand in first place. Config files are not a place for any formulas.
If you want them to be moddable, they have to be in config files - or at least it must be possible to put them there. For simplicity (i.e. modders who don’t want to know) we can have an alias which hides this complexity. The current “new” format that is used in mods becomes such an alias, though you could simplify it further of course.
Added a one-line option for specifying creature specialties using default bonuses:
"specialty" : {
"creature" : "archer"
}
The full format is still available to specify creature specialties that don’t follow the HMM3 default.