OneEnoughDamage
Have you ever run into hardcoded damage values that cannot be modified?
Have you ever struggled to balance damage from projectiles, fangs, magic, summons, or area attacks because there was no practical entry point?
Did you know that the Leviathan has 11 different damage values, while the Ignis has 17?
Does your modpack contain hundreds of mobs whose damage needs balancing?
With OED, adjusting damage becomes simple. Hardcoded damage from every mod is scanned at startup, and matching Attribute entries are automatically registered for the relevant mobs.
You can also edit default values directly in the config files, including vanilla damage attributes, with one-second reloads for modpack authors who need to balance every kind of damage.
Automatic Attribute Registration
By default, this mod scans hardcoded damage in Minecraft itself and all installed mods during startup, then registers any damage that can be assigned to a specific mob as an Attribute.
Ownership is traced through static analysis. For example, evoker fangs summoned by an evoker are assigned to the evoker, and most summon damage, buff damage, and similar cases can also be correctly assigned to every possible source mob.
Example attribute ID: oneenoughdamage:fuzs/illagerinvasion/world/entity/monster/invoker_fangs/damage/2/r
The naming rule is the exact damage-point path, order, and calculation mode. r means fixed damage, while m means multiplier damage. Multiplier attributes are only used for dynamic cases such as damage that changes with difficulty, and default to 1.0.
The more mods you have, the longer scanning takes. A modpack with around 200 mods is expected to take about ten seconds to scan. After the first startup, you can enable readCache in the config so later launches read the cache directly and skip the scan stage.
Quick Damage Tuning
After installing this mod, you have two ways to tune mob damage: edit the default damage values directly in the config files, or dynamically modify Attributes through tools such as KubeJS.
The following files are generated under config/OED/:
damage_points-cache.json: Scan cache, intended for machine reading. It can be large and is usually safe to ignore.
damage-point-dictionary.md: Dictionary of attributed damage points, with built-in Chinese-English names and damage grouped by mob for easier lookup.
damage-point-dictionary.toml: Editable config file with a dictionary-like layout. It also includes vanilla minecraft:generic.attack_damage.
damage-point-unattributed.md: Damage points that cannot currently be safely assigned to a mob.
oneenoughdamage.toml: Mod behavior config.
Changing an attribute value in damage-point-dictionary.toml directly changes the default value used when the attribute is registered, but the game must be restarted for it to take effect. This is well suited for simple balancing that does not require dynamic damage changes.
If you are still testing, you can enable debugMode in the config. Default value changes in the file will then be watched and injected into the game immediately.
The editable entries include vanilla minecraft:generic.attack_damage, so even if hardcoded damage is not your main concern, you can still quickly tune and test damage for many mobs.
damage-point-dictionary.toml is updated incrementally. When you add or remove mods, the existing config is backed up, and entries are added or removed while preserving your attribute value changes.
Dynamic Damage Tuning
If you need to tune damage dynamically, it is recommended to use KubeJS to call Attribute-related methods, or to use commands.
Recommended tutorials:
The following example doubles the area damage dealt by Cataclysm's Amethyst Crab when it retracts underground and jumps back out after taking damage. After installing ProbeJS, typing @attribute can trigger autocomplete for attribute names.
// server_scripts
EntityEvents.hurt(event => {
if (event.getEntity().is("cataclysm:amethyst_crab")) {
/** @type {Internal.LivingEntity} */
let entity = event.getEntity();
let attr = `oneenoughdamage:com/github/l_ender/cataclysm/entity/animation_monster/boss_monsters/amethyst_crab_entity/area_attack/1/m`;
entity.modifyAttribute(attr, "oneenoughdamage:example", 1, 'addition');
}
});
Coverage
Most damage defined by raw numbers or simple variable combinations can be scanned. Since some mod authors have their own implementation approaches, 100% coverage is not possible.
So far, vanilla Minecraft, Cataclysm, and Illager Invasion have been tested with very high coverage. The latter two mods are almost entirely hardcoded damage.
Cataclysm has its own dynamic damage system, so many registered attributes use multiplier form with a default value of 1.0. This part cannot be guaranteed to be perfectly detected and will need continued refinement.
For Cataclysm, the main use case of this mod is balancing different damage values on the same mob. For example, the Leviathan has 11 different damage values and the Ignis has 17, while vanilla only provides the single minecraft:generic.attack_damage, which is nowhere near enough.
In practice, there are still some hardcoded cases that this mod cannot solve yet. For example, a mob may summon a projectile entity with no owner field, and that projectile may deal damage to the player with a null DamageSource.
With some trickery, it is possible to trace what type of mob caused that damage. Unfortunately, when multiple mobs of the same type are present at the same time, tracing the exact target mob is very difficult.
For this, there is a nearby-search fallback that works well enough:
inferAttributeHolder: When damage has no direct LivingEntity attacker, try to infer the owning entity nearby.
inferAttributeHolderSearchRadius: Search radius for ownership inference, measured in blocks.
Feedback is welcome if you run into other coverage issues.
Roadmap
- Continue improving coverage
- Add in-game visual editing for attribute default values
- Port to other versions