Bondage test items : placeholder assets for Phase 3 pipeline validation

Adds minimum viable assets to exercise the Phase 3 anim pipeline
in-game :

- 5 placeholder anim JSON files in assets/tiedup/animmodels/animations/
  (identity keyframes matching context_stand_idle template) :
  armbinder_idle, armbinder_walk, armbinder_struggle,
  armbinder_equip_oneshot, classic_collar_idle
- armbinder.json enriched with 'animations' block binding IDLE + WALK
  + WALK_BOUND + STRUGGLE_BOUND living_motions + on_equip oneshot
- classic_collar.json enriched with IDLE binding for multi-item
  composition test

Purpose : validate end-to-end that the data-driven pipeline fires
correctly (rebuild, motion switch, oneshot sound, rehydrate) via F6
debug overlay. Visual pose does not change (identity anims) — body
keyframe authoring remains blocked on Blender artist assets.

Runtime note : the placeholder JSONs lack the EF 'constructor' block,
so readResourcepackAnimation does not register them in
AnimationManager.animationByName. resolveWithFallback will return
EMPTY_ANIMATION with a dedup WARN per missing ID — this exercises
the binding resolution path without needing a real StaticAnimation
(the map livingAnimations still receives the accessor, F6 overlay
still reports bindings:N). Real registration will land when either
InstantiateInvoker.getArmature() is implemented (Phase 2 armature
registry) or a dedicated AnimationRegistryEvent handler is added.

Gameday verification steps documented in docs/plans/rig/GAMEDAY_CHECKLIST.md
(gitignored, local only).
This commit is contained in:
notevil
2026-04-24 11:57:15 +02:00
parent dd30a8d4f9
commit efba00d005
7 changed files with 88 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
{
"_comment": "PLACEHOLDER gameday — oneshot d'équipement pour armbinder (2 keyframes Root, 0.5s non-repeat). Testera le trigger on_equip via PacketPlayRigAnim.handleOnClient + LivingEntityPatch.playAnimationSync. F6 overlay doit montrer une brève spike d'animation HIGH priority au moment de l'équipement, puis retour au base layer IDLE. Remplacement Blender (bras qui se replient derrière le dos) quand dispo.",
"_comment_registration": "Même statut que les autres placeholders : résolution vers EMPTY_ANIMATION. La spike visuelle via PacketPlayRigAnim testera le binding du oneshot même si l'effet rendu est nul.",
"format": "ATTRIBUTES",
"animation": [
{
"name": "Root",
"time": [0.0, 0.5],
"transform": [
{ "loc": [0.0, 0.0, 0.0], "rot": [1.0, 0.0, 0.0, 0.0], "sca": [1.0, 1.0, 1.0] },
{ "loc": [0.0, 0.0, 0.0], "rot": [1.0, 0.0, 0.0, 0.0], "sca": [1.0, 1.0, 1.0] }
]
}
]
}

View File

@@ -0,0 +1,16 @@
{
"_comment": "PLACEHOLDER gameday — identity idle pour armbinder (2 keyframes Root, 2s loop). Même structure que context_stand_idle.json : le but n'est PAS d'avoir un rendu visuel different mais de valider le pipeline P3-05 end-to-end (binding résolu, anim jouée, F6 overlay 'motion: IDLE, bindings: N'). À remplacer par un export Blender authored (bras derrière le dos, épaules sanglées) quand dispo.",
"_comment_registration": "Ce JSON n'est PAS auto-registered dans AnimationManager.animationByName : readResourcepackAnimation exige un bloc 'constructor' absent ici, et InstantiateInvoker.getArmature() throw actuellement (Armature registry pas Phase 2). resolveWithFallback retourne donc EMPTY_ANIMATION, ce qui est OK pour le smoke test du pipeline (la map livingAnimations reçoit bien un accessor, juste vide).",
"_comment_armature": "Pas de champ 'armature' ici — le loader ne lit pas ce champ au runtime. L'armature est résolue par le call site.",
"format": "ATTRIBUTES",
"animation": [
{
"name": "Root",
"time": [0.0, 2.0],
"transform": [
{ "loc": [0.0, 0.0, 0.0], "rot": [1.0, 0.0, 0.0, 0.0], "sca": [1.0, 1.0, 1.0] },
{ "loc": [0.0, 0.0, 0.0], "rot": [1.0, 0.0, 0.0, 0.0], "sca": [1.0, 1.0, 1.0] }
]
}
]
}

View File

@@ -0,0 +1,14 @@
{
"_comment": "PLACEHOLDER gameday — struggle bondage pour armbinder (2 keyframes Root, 0.8s loop). Testera le binding de la motion custom TiedUpLivingMotions.STRUGGLE_BOUND. Remplacement Blender (gesticulation limitée bras sanglés) quand dispo.",
"format": "ATTRIBUTES",
"animation": [
{
"name": "Root",
"time": [0.0, 0.8],
"transform": [
{ "loc": [0.0, 0.0, 0.0], "rot": [1.0, 0.0, 0.0, 0.0], "sca": [1.0, 1.0, 1.0] },
{ "loc": [0.0, 0.0, 0.0], "rot": [1.0, 0.0, 0.0, 0.0], "sca": [1.0, 1.0, 1.0] }
]
}
]
}

View File

@@ -0,0 +1,15 @@
{
"_comment": "PLACEHOLDER gameday — identity walk pour armbinder (2 keyframes Root, 1s loop). Le but est de tester le switch de motion IDLE→WALK au runtime : F6 overlay doit afficher 'motion: WALK' quand le joueur bouge. Identité visuelle pour l'instant, remplacement Blender-authored (démarche contrainte, épaules raides) quand dispo.",
"_comment_registration": "Même statut que armbinder_idle.json : pas de bloc 'constructor' => resolveWithFallback retourne EMPTY_ANIMATION. Valide le switch de motion côté binding map sans besoin d'un StaticAnimation réel.",
"format": "ATTRIBUTES",
"animation": [
{
"name": "Root",
"time": [0.0, 1.0],
"transform": [
{ "loc": [0.0, 0.0, 0.0], "rot": [1.0, 0.0, 0.0, 0.0], "sca": [1.0, 1.0, 1.0] },
{ "loc": [0.0, 0.0, 0.0], "rot": [1.0, 0.0, 0.0, 0.0], "sca": [1.0, 1.0, 1.0] }
]
}
]
}

View File

@@ -0,0 +1,14 @@
{
"_comment": "PLACEHOLDER gameday — identity idle pour classic_collar (NECK region). Apairé avec armbinder_* pour tester le cas multi-item composition : armbinder (ARMS pose_priority 30) + classic_collar (NECK pose_priority 5) doivent tous deux écrire dans la map livingAnimations (pose_priority ASC sort => collar appliqué d'abord, armbinder écrase). F6 overlay doit afficher 'bindings: 2+' quand les deux items sont équipés.",
"format": "ATTRIBUTES",
"animation": [
{
"name": "Root",
"time": [0.0, 2.0],
"transform": [
{ "loc": [0.0, 0.0, 0.0], "rot": [1.0, 0.0, 0.0, 0.0], "sca": [1.0, 1.0, 1.0] },
{ "loc": [0.0, 0.0, 0.0], "rot": [1.0, 0.0, 0.0, 0.0], "sca": [1.0, 1.0, 1.0] }
]
}
]
}

View File

@@ -22,6 +22,15 @@
"leftArm" "leftArm"
] ]
}, },
"animations": {
"living_motions": {
"IDLE": "tiedup:armbinder_idle",
"WALK": "tiedup:armbinder_walk",
"WALK_BOUND": "tiedup:armbinder_walk",
"STRUGGLE_BOUND": "tiedup:armbinder_struggle"
},
"on_equip": "tiedup:armbinder_equip_oneshot"
},
"components": { "components": {
"lockable": {}, "lockable": {},
"resistance": { "resistance": {

View File

@@ -11,6 +11,11 @@
"body" "body"
] ]
}, },
"animations": {
"living_motions": {
"IDLE": "tiedup:classic_collar_idle"
}
},
"display_name": "Classic Collar", "display_name": "Classic Collar",
"translation_key": "item.tiedup.classic_collar", "translation_key": "item.tiedup.classic_collar",
"model": "tiedup:models/gltf/v2/collars/classic_collar.glb", "model": "tiedup:models/gltf/v2/collars/classic_collar.glb",