P3-02 : add AnimationBindings record + DataDrivenItemDefinition.animations field

New record AnimationBindings (v2/bondage/datadriven/) carries the JSON
item -> rig animation bindings that P3-03 (parser) will populate and
P3-05 (ClientRigEquipmentHandler.rebuildBondageAnimations) will consume
to rebuild the player's livingAnimations map on equip/unequip.

Fields :
- livingMotions : Map<LivingMotion, ResourceLocation> (immutable, never null,
  Map.copyOf defensive copy in compact ctor, null input collapsed to emptyMap)
- onEquip / onUnequip : optional nullable one-shot triggers
- EMPTY constant + isEmpty() for the "no binding" fast path

DataDrivenItemDefinition gains a nullable "animations" field (null = vanilla
behavior, preserves backward compat for items without rig bindings).
DataDrivenItemParser call-site updated to pass null for now — wiring happens
in P3-03. Single call-site updated (parser only), no factories or tests
constructed the record directly.

7 unit tests cover construction paths, null tolerance, defensive map copy,
isEmpty semantics and the EMPTY constant. Full rig suite still 65 GREEN.
This commit is contained in:
notevil
2026-04-23 13:22:25 +02:00
parent ddaa25b971
commit 5d108f51b4
4 changed files with 226 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
/*
* © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
*/
package com.tiedup.remake.v2.bondage.datadriven;
import java.util.Collections;
import java.util.Map;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable;
import com.tiedup.remake.rig.anim.LivingMotion;
/**
* Bindings animation pour un item bondage data-driven.
*
* <p>Porte le mapping {@link LivingMotion} &rarr; {@link ResourceLocation} animation
* + eventuels one-shots {@code onEquip} / {@code onUnequip}. Consomme par
* {@code ClientRigEquipmentHandler.rebuildBondageAnimations} (P3-05) pour
* builder la map {@code livingAnimations} de l'animator quand un item
* bondage est equipe.
*
* <p>Parse via {@code DataDrivenItemParser} (P3-03) depuis JSON :
* <pre>{@code
* "animations": {
* "living_motions": { "IDLE": "mymod:arms_cuffed_idle", ... },
* "on_equip": "mymod:cuffs_equip_oneshot",
* "on_unequip": "mymod:cuffs_unequip_oneshot"
* }
* }</pre>
*
* @param livingMotions map immutable motion &rarr; anim ID (jamais null, peut etre vide)
* @param onEquip optionnel one-shot au moment de l'equipement (null = pas de trigger)
* @param onUnequip optionnel one-shot au moment du desequipement (null = pas de trigger)
*/
public record AnimationBindings(
Map<LivingMotion, ResourceLocation> livingMotions,
@Nullable ResourceLocation onEquip,
@Nullable ResourceLocation onUnequip
) {
/** Shortcut pour bindings totalement vides (equivalent no-op). */
public static final AnimationBindings EMPTY = new AnimationBindings(Map.of(), null, null);
/** Constructor canonique : force livingMotions immutable + non-null. */
public AnimationBindings {
livingMotions = livingMotions == null ? Collections.emptyMap() : Map.copyOf(livingMotions);
}
/** {@code true} si aucun binding n'est defini (ni living motions, ni one-shots). */
public boolean isEmpty() {
return livingMotions.isEmpty() && onEquip == null && onUnequip == null;
}
}

View File

@@ -105,6 +105,20 @@ public record DataDrivenItemDefinition(
*/
Map<String, Set<String>> animationBones,
/**
* Optional living-motion animation bindings (P3 rig system).
*
* <p>When non-null, the client rig equipment handler rebuilds the player's
* living animation map with these bindings whenever this item is equipped.
* When null, no animation bindings are applied (vanilla behavior).</p>
*
* <p>Distinct from {@link #animationBones} : {@code animationBones} is a
* per-clip bone whitelist for the PlayerAnimator pipeline (V1-era),
* whereas {@code animations} drives the Epic Fight-style rig animator
* (P3-05).</p>
*/
@Nullable AnimationBindings animations,
/** Raw component configs from JSON, keyed by ComponentType. */
Map<ComponentType, JsonObject> componentConfigs
) {

View File

@@ -347,6 +347,9 @@ public final class DataDrivenItemParser {
movementModifier,
creator,
animationBones,
// P3-02 : animations parsing will be wired in P3-03. For now pass
// null so existing items keep their vanilla animator behavior.
null,
componentConfigs
);
}