This guide describes the public API in the current source version 0.8.0. Use the dependency guidance below and check that the mod is loaded when integrating conditionally.
FFEntityDirt — Modding API
The API covers wound marks: what a hit leaves on the Damage layer. You can add your own wounds, restyle the built-in ones, assign item and entity type tags to them, and pick wounds in code. Dirt, soot and snow have no public API; they are built on FFEntityPainting's API.
| Minecraft | 1.21.1, 1.21.11 and 26.x (Fabric 26.1 to 26.3, NeoForge 26.1.2 and 26.2) |
| Loader | NeoForge, Fabric |
| Mod ID | ffentitydirt |
| API package | com.fyxe.ffentitydirt.api |
| Maven | com.fyxe.ffentitydirt:ffentitydirt-<minecraft>-<loader>:0.8.0 on the Fyxe Gitea Maven registry |
Dependency (optional, NeoForge shown; on Fabric use "suggests" or "depends" in fabric.mod.json):
[[dependencies.yourmod]]
modId="ffentitydirt"
type="optional"
versionRange="[0.8.0,)"
ordering="NONE"
side="BOTH"
Call the API from code that only loads when ffentitydirt is installed. Wounds are decided on the server, so register them on both sides (common setup), during mod construction or setup. Everything is thread safe.
How a wound is picked
- Resolvers added with
WoundAPI.addResolver, in order. The first registered wound ID returned wins. - Melee: the attacker's main-hand item (for an Epic Fight attack, the item of the hand that swung) against the wound item tags. Every wound checks
<namespace>:wounds/<path>plus any tags assigned withassignItems, highest wound priority first. - Melee: the item's Better Combat weapon category (when Better Combat is installed), through the
damage.wounds.betterCombatCategoriessetting; then its Epic Fight weapon category (when Epic Fight is installed), throughdamage.wounds.epicFightCategories; then words in the item's ID throughdamage.wounds.itemKeywords. - The projectile's entity type (or, for melee, the attacker's) against the wound entity type tags, the same way.
- Fallbacks:
projectileWoundfor projectiles,weaponWoundfor an item with attack damage or in#c:tools/melee_weapon, elseunarmedWound.
With damage.wounds.enabled off, melee always slashes and projectiles always puncture.
Classes
| Class | What it does |
|---|---|
WoundAPI | register(id, shape[, priority]), replaceShape(id, shape), shape(id), isRegistered(id), ids(), itemTag(id), entityTag(id), assignItems(tag, id), assignEntities(tag, id), addResolver(resolver), resolve(context). Constants for the 15 built-in wounds: SLASH, GASH, CLEAVE, STAB, PUNCTURE, TRIPLE_PUNCTURE, BRUISE, CRUSH, SCRATCH, CLAW, BITE, GORE, LASH, STING, SPLATTER. |
WoundShape | What a wound looks like: strokes and dots in texture pixels, optional spatter, a color tint and a minimum size. Built with WoundShape.builder(Sizing). |
WoundContext | The hit a resolver sees: victim, source, attacker, direct, weapon (never null), projectile, damage. |
WoundResolver | Functional interface: return a registered wound ID for a hit, or null to pass. |
Shapes
Coordinates are texture pixels of the part that was hit, at full size, with the hit spot at (0, 0). x runs along the wound's main axis and y across it. The built-in slash is one stroke from (-2, 0) to (2, 0).
- Sizing:
LINEstroke sizes multiply the slash thickness (damage.melee.slashMin/MaxThickness, growing with damage);BLOTsizes multiply the mark radius (damage.size). Spatter droplets always use the mark radius. - Rotation:
RANDOMturns the wound to any angle;LEVELkeepsxlevel across the body andVERTICALpoints positivexdown the body, each turned by up towobbledegrees (default 15). - Builder:
line(x0, y0, x1, y1, size)(full strength, tapering to 35% at the ends),stroke(x0, y0, x1, y1, size, weight, endWeight),dot(x, y, size, weight),ring(count, radius, size, weight),spatter(count, minDistance, maxDistance, size, weight),tint(rgb, amount),minScale(share),rotation(...),wobble(degrees). At most 32 strokes. - Size: melee wounds shrink with the attacker's distance by
slashLength / 4(damage.melee.slashMinLengthtoslashMaxLength), never belowminScale. Projectile wounds are always full size. - Roughness: every wound gets
damage.roughnessapplied on top: samples wander, vary in strength and size, leave small gaps, brush edges come out ragged and a stray droplet or two lands nearby.
Example: a new wound
import com.fyxe.ffentitydirt.api.WoundAPI;
import com.fyxe.ffentitydirt.api.WoundShape;
public static final ResourceLocation SERRATED = ResourceLocation.fromNamespaceAndPath("mymod", "serrated");
// In common setup, only when ffentitydirt is loaded:
WoundAPI.register(SERRATED, WoundShape.builder(WoundShape.Sizing.LINE)
.line(-2.5f, 0, 2.5f, 0, 1.2f) // the main cut
.stroke(-2, 0.8f, 2, 0.8f, 0.4f, 0.6f, 0.3f) // a torn edge
.spatter(3, 1.5f, 3.5f, 0.3f, 0.6f) // a few drops
.minScale(0.5f)
.build());
// Items in #mymod:wounds/serrated now leave it. Assign more tags if you like:
WoundAPI.assignItems(TagKey.create(Registries.ITEM, ResourceLocation.fromNamespaceAndPath("mymod", "saw_blades")), SERRATED);
On 1.21.11, use Identifier where this shows ResourceLocation.
Example: assign tags or decide in code
// Your claw gauntlets rake like a cat.
WoundAPI.assignItems(MyTags.CLAW_GAUNTLETS, WoundAPI.CLAW);
// Your werewolf bites when unarmed.
WoundAPI.assignEntities(MyTags.WEREWOLVES, WoundAPI.BITE);
// Or decide per hit: a charged spear crushes instead of stabbing.
WoundAPI.addResolver(context -> context.weapon().is(MyItems.SPEAR) && MySpear.isCharged(context.weapon())
? WoundAPI.CRUSH : null);
Notes
registerthrows if the ID is taken; usereplaceShapeto restyle an existing wound (it keeps the priority and tags).- Priority only matters when an item or entity is in several wound tags: highest wins. The trident's wound is 10 and the crush 5; the others are 0.
- A resolver that throws is skipped (and logged once). An ID that is not registered is ignored.
- The built-in wounds register when
WoundAPIfirst loads, so they always exist before your code runs. - Datapacks pick wounds through the same tags; see the Datapacks page.