diff --git a/AnimSources/quaternius/pack.json b/AnimSources/quaternius/pack.json
new file mode 100644
index 0000000..9ed1a9e
--- /dev/null
+++ b/AnimSources/quaternius/pack.json
@@ -0,0 +1,607 @@
+{
+ "pack": "quaternius",
+ "prefixe": "qua",
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "auteur": "Quaternius",
+ "licence": "CC0 1.0",
+ "url": "https://quaternius.com",
+ "fichiers": [
+ "D:/Game/Universal Animation Library[Standard]/Unreal-Godot/UAL1_Standard.glb"
+ ],
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "exclus": [
+ "A_TPose"
+ ],
+ "animations": {
+ "qua_crouch_fwd_loop": {
+ "clip": "Crouch_Fwd_Loop",
+ "nom": {
+ "fr": "Marche accroupie",
+ "en": "Crouch walk"
+ },
+ "categorie": "locomotion",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de marche accroupie sur place ; à combiner avec un déplacement pour une scène furtive.",
+ "en": "Looping crouched walk in place; combine with a character movement for a sneaking scene."
+ }
+ },
+ "qua_crouch_idle_loop": {
+ "clip": "Crouch_Idle_Loop",
+ "nom": {
+ "fr": "Attente accroupie",
+ "en": "Crouch idle"
+ },
+ "categorie": "attente",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle d'attente accroupie, pour se cacher ou observer discrètement.",
+ "en": "Looping crouched idle, for hiding or watching discreetly."
+ }
+ },
+ "qua_dance_loop": {
+ "clip": "Dance_Loop",
+ "nom": {
+ "fr": "Danse",
+ "en": "Dance"
+ },
+ "categorie": "danse",
+ "accessoire": "",
+ "style": "détendu",
+ "usage": {
+ "fr": "Boucle de danse libre, pour une scène de fête ou de club.",
+ "en": "Looping free dance, for a party or club scene."
+ }
+ },
+ "qua_death01": {
+ "clip": "Death01",
+ "nom": {
+ "fr": "Mort",
+ "en": "Death"
+ },
+ "categorie": "chute",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : le personnage s'effondre et reste au sol ; à utiliser pour une mort ou un évanouissement.",
+ "en": "Played once: the character collapses and stays down; use for a death or a faint."
+ }
+ },
+ "qua_driving_loop": {
+ "clip": "Driving_Loop",
+ "nom": {
+ "fr": "Conduite",
+ "en": "Driving"
+ },
+ "categorie": "conduite",
+ "accessoire": "volant",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de conduite, mains sur le volant ; pour un personnage assis dans un véhicule.",
+ "en": "Looping driving pose, hands on the wheel; for a character seated in a vehicle."
+ }
+ },
+ "qua_fixing_kneeling": {
+ "clip": "Fixing_Kneeling",
+ "nom": {
+ "fr": "Réparer à genoux",
+ "en": "Fix kneeling"
+ },
+ "categorie": "interaction",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de bricolage à genoux, pour une réparation ou un travail au sol.",
+ "en": "Looping kneeling repair work, for fixing something or working at ground level."
+ }
+ },
+ "qua_hit_chest": {
+ "clip": "Hit_Chest",
+ "nom": {
+ "fr": "Coup à la poitrine",
+ "en": "Hit in chest"
+ },
+ "categorie": "combat",
+ "accessoire": "",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : réaction à un coup reçu au torse ; à placer après une attaque.",
+ "en": "Played once: reaction to a blow to the chest; place it after an attack."
+ }
+ },
+ "qua_hit_head": {
+ "clip": "Hit_Head",
+ "nom": {
+ "fr": "Coup à la tête",
+ "en": "Hit in head"
+ },
+ "categorie": "combat",
+ "accessoire": "",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : réaction à un coup reçu à la tête ; à placer après une attaque.",
+ "en": "Played once: reaction to a blow to the head; place it after an attack."
+ }
+ },
+ "qua_idle_loop": {
+ "clip": "Idle_Loop",
+ "nom": {
+ "fr": "Attente",
+ "en": "Idle"
+ },
+ "categorie": "attente",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle d'attente debout par défaut, pour un personnage qui patiente.",
+ "en": "Default standing idle loop, for a waiting character."
+ }
+ },
+ "qua_idle_talking_loop": {
+ "clip": "Idle_Talking_Loop",
+ "nom": {
+ "fr": "Attente en parlant",
+ "en": "Idle talking"
+ },
+ "categorie": "conversation",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle debout avec gestes de la main, pour un personnage qui parle dans un dialogue.",
+ "en": "Standing loop with hand gestures, for a character speaking in a dialogue."
+ }
+ },
+ "qua_idle_torch_loop": {
+ "clip": "Idle_Torch_Loop",
+ "nom": {
+ "fr": "Attente avec torche",
+ "en": "Idle with torch"
+ },
+ "categorie": "attente",
+ "accessoire": "torche",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle d'attente debout en tenant une torche ; pour une scène de nuit ou d'exploration.",
+ "en": "Standing idle loop holding a torch; for a night or exploration scene."
+ }
+ },
+ "qua_interact": {
+ "clip": "Interact",
+ "nom": {
+ "fr": "Interagir",
+ "en": "Interact"
+ },
+ "categorie": "interaction",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : geste pour actionner un objet (bouton, porte, levier).",
+ "en": "Played once: gesture to operate an object (button, door, lever)."
+ }
+ },
+ "qua_jog_fwd_loop": {
+ "clip": "Jog_Fwd_Loop",
+ "nom": {
+ "fr": "Jogging",
+ "en": "Jog"
+ },
+ "categorie": "locomotion",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de course légère sur place ; à combiner avec un déplacement du personnage.",
+ "en": "Looping light jog in place; combine with a character movement."
+ }
+ },
+ "qua_jump_land": {
+ "clip": "Jump_Land",
+ "nom": {
+ "fr": "Réception de saut",
+ "en": "Jump landing"
+ },
+ "categorie": "chute",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : réception au sol ; dernière phase d'un saut (après la boucle en l'air).",
+ "en": "Played once: landing on the ground; last phase of a jump (after the airborne loop)."
+ }
+ },
+ "qua_jump_loop": {
+ "clip": "Jump_Loop",
+ "nom": {
+ "fr": "Saut en l'air",
+ "en": "Jump airborne"
+ },
+ "categorie": "chute",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de la phase en l'air d'un saut, entre le départ et la réception.",
+ "en": "Looping airborne phase of a jump, between the start and the landing."
+ }
+ },
+ "qua_jump_start": {
+ "clip": "Jump_Start",
+ "nom": {
+ "fr": "Départ de saut",
+ "en": "Jump start"
+ },
+ "categorie": "chute",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : impulsion au départ d'un saut ; première phase avant la boucle en l'air.",
+ "en": "Played once: take-off impulse; first phase before the airborne loop."
+ }
+ },
+ "qua_pickup_table": {
+ "clip": "PickUp_Table",
+ "nom": {
+ "fr": "Ramasser sur une table",
+ "en": "Pick up from table"
+ },
+ "categorie": "interaction",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : le personnage prend un objet posé sur une table.",
+ "en": "Played once: the character picks up an object lying on a table."
+ }
+ },
+ "qua_pistol_aim_down": {
+ "clip": "Pistol_Aim_Down",
+ "nom": {
+ "fr": "Viser vers le bas",
+ "en": "Aim down"
+ },
+ "categorie": "combat",
+ "accessoire": "pistolet",
+ "style": "agressif",
+ "usage": {
+ "fr": "Pose de visée pistolet vers le bas ; à enchaîner avec les autres visées ou le tir.",
+ "en": "Pistol aiming pose pointing down; chain with the other aims or the shot."
+ }
+ },
+ "qua_pistol_aim_neutral": {
+ "clip": "Pistol_Aim_Neutral",
+ "nom": {
+ "fr": "Viser droit",
+ "en": "Aim straight"
+ },
+ "categorie": "combat",
+ "accessoire": "pistolet",
+ "style": "agressif",
+ "usage": {
+ "fr": "Pose de visée pistolet à l'horizontale ; pose de base avant un tir.",
+ "en": "Pistol aiming pose at level; base pose before a shot."
+ }
+ },
+ "qua_pistol_aim_up": {
+ "clip": "Pistol_Aim_Up",
+ "nom": {
+ "fr": "Viser vers le haut",
+ "en": "Aim up"
+ },
+ "categorie": "combat",
+ "accessoire": "pistolet",
+ "style": "agressif",
+ "usage": {
+ "fr": "Pose de visée pistolet vers le haut ; à enchaîner avec les autres visées ou le tir.",
+ "en": "Pistol aiming pose pointing up; chain with the other aims or the shot."
+ }
+ },
+ "qua_pistol_idle_loop": {
+ "clip": "Pistol_Idle_Loop",
+ "nom": {
+ "fr": "Attente au pistolet",
+ "en": "Pistol idle"
+ },
+ "categorie": "combat",
+ "accessoire": "pistolet",
+ "style": "agressif",
+ "usage": {
+ "fr": "Boucle d'attente debout, pistolet en main ; pour un personnage armé qui guette.",
+ "en": "Standing idle loop with a pistol in hand; for an armed character on guard."
+ }
+ },
+ "qua_pistol_reload": {
+ "clip": "Pistol_Reload",
+ "nom": {
+ "fr": "Recharger le pistolet",
+ "en": "Pistol reload"
+ },
+ "categorie": "combat",
+ "accessoire": "pistolet",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : rechargement du pistolet ; à placer entre deux tirs.",
+ "en": "Played once: pistol reload; place it between two shots."
+ }
+ },
+ "qua_pistol_shoot": {
+ "clip": "Pistol_Shoot",
+ "nom": {
+ "fr": "Tir au pistolet",
+ "en": "Pistol shot"
+ },
+ "categorie": "combat",
+ "accessoire": "pistolet",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : un tir de pistolet avec recul.",
+ "en": "Played once: one pistol shot with recoil."
+ }
+ },
+ "qua_punch_cross": {
+ "clip": "Punch_Cross",
+ "nom": {
+ "fr": "Direct",
+ "en": "Cross punch"
+ },
+ "categorie": "combat",
+ "accessoire": "",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : coup de poing direct ; pour une bagarre.",
+ "en": "Played once: cross punch; for a fight."
+ }
+ },
+ "qua_punch_jab": {
+ "clip": "Punch_Jab",
+ "nom": {
+ "fr": "Jab",
+ "en": "Jab"
+ },
+ "categorie": "combat",
+ "accessoire": "",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : coup de poing rapide ; pour une bagarre.",
+ "en": "Played once: quick jab punch; for a fight."
+ }
+ },
+ "qua_push_loop": {
+ "clip": "Push_Loop",
+ "nom": {
+ "fr": "Pousser",
+ "en": "Push"
+ },
+ "categorie": "interaction",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de poussée à deux mains sur place ; pour pousser un meuble ou une porte lourde.",
+ "en": "Looping two-handed push in place; for pushing furniture or a heavy door."
+ }
+ },
+ "qua_roll": {
+ "clip": "Roll",
+ "nom": {
+ "fr": "Roulade",
+ "en": "Roll"
+ },
+ "categorie": "sport",
+ "accessoire": "",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : roulade au sol ; pour esquiver ou plonger.",
+ "en": "Played once: ground roll; for dodging or diving."
+ }
+ },
+ "qua_sitting_enter": {
+ "clip": "Sitting_Enter",
+ "nom": {
+ "fr": "S'asseoir",
+ "en": "Sit down"
+ },
+ "categorie": "assis",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : entrée, le personnage passe de debout à assis ; à suivre d'une boucle assise.",
+ "en": "Played once: entry, the character goes from standing to seated; follow with a seated loop."
+ }
+ },
+ "qua_sitting_exit": {
+ "clip": "Sitting_Exit",
+ "nom": {
+ "fr": "Se lever",
+ "en": "Stand up"
+ },
+ "categorie": "assis",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : sortie, le personnage passe d'assis à debout ; à placer après une boucle assise.",
+ "en": "Played once: exit, the character goes from seated to standing; place after a seated loop."
+ }
+ },
+ "qua_sitting_idle_loop": {
+ "clip": "Sitting_Idle_Loop",
+ "nom": {
+ "fr": "Assis, attente",
+ "en": "Seated idle"
+ },
+ "categorie": "assis",
+ "accessoire": "",
+ "style": "détendu",
+ "usage": {
+ "fr": "Boucle d'attente assise, après « s'asseoir » ; pour un personnage installé sur une chaise.",
+ "en": "Seated idle loop, after the sit-down clip; for a character settled on a chair."
+ }
+ },
+ "qua_sitting_talking_loop": {
+ "clip": "Sitting_Talking_Loop",
+ "nom": {
+ "fr": "Assis, en parlant",
+ "en": "Seated talking"
+ },
+ "categorie": "assis",
+ "accessoire": "",
+ "style": "détendu",
+ "usage": {
+ "fr": "Boucle assise avec gestes de la main, pour un dialogue entre personnages assis.",
+ "en": "Seated loop with hand gestures, for a dialogue between seated characters."
+ }
+ },
+ "qua_spell_simple_enter": {
+ "clip": "Spell_Simple_Enter",
+ "nom": {
+ "fr": "Début d'incantation",
+ "en": "Spell start"
+ },
+ "categorie": "magie",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : mise en position pour lancer un sort ; à suivre de la boucle d'incantation.",
+ "en": "Played once: stance to begin casting a spell; follow with the spell idle loop."
+ }
+ },
+ "qua_spell_simple_exit": {
+ "clip": "Spell_Simple_Exit",
+ "nom": {
+ "fr": "Fin d'incantation",
+ "en": "Spell end"
+ },
+ "categorie": "magie",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : retour à la posture normale après un sort.",
+ "en": "Played once: return to normal stance after a spell."
+ }
+ },
+ "qua_spell_simple_idle_loop": {
+ "clip": "Spell_Simple_Idle_Loop",
+ "nom": {
+ "fr": "Incantation",
+ "en": "Spell idle"
+ },
+ "categorie": "magie",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de concentration, mains levées, entre l'entrée et le lancer du sort.",
+ "en": "Looping concentration pose, hands raised, between the entry and the cast."
+ }
+ },
+ "qua_spell_simple_shoot": {
+ "clip": "Spell_Simple_Shoot",
+ "nom": {
+ "fr": "Lancer un sort",
+ "en": "Cast spell"
+ },
+ "categorie": "magie",
+ "accessoire": "",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : le geste qui lance le sort ; à placer après la boucle d'incantation.",
+ "en": "Played once: the gesture that casts the spell; place after the spell idle loop."
+ }
+ },
+ "qua_sprint_loop": {
+ "clip": "Sprint_Loop",
+ "nom": {
+ "fr": "Sprint",
+ "en": "Sprint"
+ },
+ "categorie": "locomotion",
+ "accessoire": "",
+ "style": "agressif",
+ "usage": {
+ "fr": "Boucle de course rapide sur place ; à combiner avec un déplacement pour une fuite ou une poursuite.",
+ "en": "Looping fast run in place; combine with a movement for an escape or chase."
+ }
+ },
+ "qua_swim_fwd_loop": {
+ "clip": "Swim_Fwd_Loop",
+ "nom": {
+ "fr": "Nage",
+ "en": "Swim"
+ },
+ "categorie": "nage",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de nage en avant ; à combiner avec un déplacement dans l'eau.",
+ "en": "Looping forward swim; combine with a movement in water."
+ },
+ "posture": "dans l'eau"
+ },
+ "qua_swim_idle_loop": {
+ "clip": "Swim_Idle_Loop",
+ "nom": {
+ "fr": "Surplace dans l'eau",
+ "en": "Treading water"
+ },
+ "categorie": "nage",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de flottaison sur place dans l'eau.",
+ "en": "Looping treading water in place."
+ },
+ "posture": "dans l'eau"
+ },
+ "qua_sword_attack": {
+ "clip": "Sword_Attack",
+ "nom": {
+ "fr": "Attaque à l'épée",
+ "en": "Sword attack"
+ },
+ "categorie": "combat",
+ "accessoire": "épée",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : un coup d'épée ; à enchaîner après l'attente à l'épée.",
+ "en": "Played once: a sword strike; chain after the sword idle."
+ }
+ },
+ "qua_sword_idle": {
+ "clip": "Sword_Idle",
+ "nom": {
+ "fr": "Attente à l'épée",
+ "en": "Sword idle"
+ },
+ "categorie": "combat",
+ "accessoire": "épée",
+ "style": "agressif",
+ "usage": {
+ "fr": "Boucle d'attente debout, épée en main, en garde.",
+ "en": "Standing idle loop, sword in hand, on guard."
+ }
+ },
+ "qua_walk_formal_loop": {
+ "clip": "Walk_Formal_Loop",
+ "nom": {
+ "fr": "Marche formelle",
+ "en": "Formal walk"
+ },
+ "categorie": "locomotion",
+ "accessoire": "",
+ "style": "formel",
+ "usage": {
+ "fr": "Boucle de marche tenue et digne sur place ; à combiner avec un déplacement (cérémonie, travail).",
+ "en": "Looping upright, dignified walk in place; combine with a movement (ceremony, office)."
+ }
+ },
+ "qua_walk_loop": {
+ "clip": "Walk_Loop",
+ "nom": {
+ "fr": "Marche",
+ "en": "Walk"
+ },
+ "categorie": "locomotion",
+ "accessoire": "",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de marche sur place ; à combiner avec un déplacement du personnage.",
+ "en": "Looping walk in place; combine with a character movement."
+ }
+ }
+ }
+}
diff --git a/ModSources/lpr_anim_quaternius/Module/Animations/catalogue.html b/ModSources/lpr_anim_quaternius/Module/Animations/catalogue.html
new file mode 100644
index 0000000..2b93620
--- /dev/null
+++ b/ModSources/lpr_anim_quaternius/Module/Animations/catalogue.html
@@ -0,0 +1,106 @@
+
+
+
+
+
+Animations — lpr_anim_quaternius
+
+
+
+
+Animations — lpr_anim_quaternius
+Version 1.0 — 42 animations — généré le 2026-10-06. Copiez la clé puis écrivez Actor.animate(clé) dans une scène ou ANIMATION: clé dans une action. Survolez une vignette pour voir le mouvement.
+
+
+Toutes catégories
+Toutes postures
+Toutes durées Courte (< 2 s) Moyenne (2 à 5 s) Longue (> 5 s)
+
+
+
+Aucune animation ne correspond aux filtres.
+
+Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com
+
+
+
+
diff --git a/ModSources/lpr_anim_quaternius/Module/Animations/catalogue.json b/ModSources/lpr_anim_quaternius/Module/Animations/catalogue.json
new file mode 100644
index 0000000..8d092d3
--- /dev/null
+++ b/ModSources/lpr_anim_quaternius/Module/Animations/catalogue.json
@@ -0,0 +1,3187 @@
+{
+ "pack": "quaternius",
+ "mod": "lpr_anim_quaternius",
+ "version": "1.0",
+ "genere": "2026-10-06T16:30:06",
+ "nombre_animations": 42,
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "animations": [
+ {
+ "cle": "qua_crouch_fwd_loop",
+ "appel": {
+ "script": "Actor.animate(qua_crouch_fwd_loop)",
+ "action": "ANIMATION: qua_crouch_fwd_loop"
+ },
+ "nom": {
+ "fr": "Marche accroupie",
+ "en": "Crouch walk"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_crouch_fwd_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_crouch_fwd_loop_M"
+ },
+ "duree_s": 2.0,
+ "images": 61,
+ "planche": {
+ "fichier": "planches/qua_crouch_fwd_loop.jpg",
+ "images": 24,
+ "taille": 256,
+ "ips": 12.0
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 531,
+ "masculin": 521,
+ "total": 1052
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "variable",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 79,
+ "droite": 74
+ },
+ "mains": {
+ "gauche": "repos",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "locomotion",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de marche accroupie sur place ; à combiner avec un déplacement pour une scène furtive.",
+ "en": "Looping crouched walk in place; combine with a character movement for a sneaking scene."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "9B579DF07915651F7F712B108C24E86C4626A41056D91B77AC4FA7D0B6A0C35F"
+ },
+ {
+ "cle": "qua_crouch_idle_loop",
+ "appel": {
+ "script": "Actor.animate(qua_crouch_idle_loop)",
+ "action": "ANIMATION: qua_crouch_idle_loop"
+ },
+ "nom": {
+ "fr": "Attente accroupie",
+ "en": "Crouch idle"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_crouch_idle_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_crouch_idle_loop_M"
+ },
+ "duree_s": 2.93,
+ "images": 89,
+ "planche": {
+ "fichier": "planches/qua_crouch_idle_loop.jpg",
+ "images": 24,
+ "taille": 256,
+ "ips": 8.2
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 817,
+ "masculin": 808,
+ "total": 1625
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "accroupi",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "repos",
+ "droite": "repos"
+ }
+ },
+ "accessoire": "",
+ "categorie": "attente",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle d'attente accroupie, pour se cacher ou observer discrètement.",
+ "en": "Looping crouched idle, for hiding or watching discreetly."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "0DABE423B515ABB6335D03BF58640351F9978B6C1798E69B0DE3E99BBF139821"
+ },
+ {
+ "cle": "qua_dance_loop",
+ "appel": {
+ "script": "Actor.animate(qua_dance_loop)",
+ "action": "ANIMATION: qua_dance_loop"
+ },
+ "nom": {
+ "fr": "Danse",
+ "en": "Dance"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_dance_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_dance_loop_M"
+ },
+ "duree_s": 1.0,
+ "images": 31,
+ "planche": {
+ "fichier": "planches/qua_dance_loop.jpg",
+ "images": 12,
+ "taille": 256,
+ "ips": 12.0
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 500,
+ "masculin": 492,
+ "total": 992
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "danse",
+ "style": "détendu",
+ "usage": {
+ "fr": "Boucle de danse libre, pour une scène de fête ou de club.",
+ "en": "Looping free dance, for a party or club scene."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "51A7F64FB68A91BE68D2CE8FEDFA635DF66556529605E65D5E9C5DA5E71A987E"
+ },
+ {
+ "cle": "qua_death01",
+ "appel": {
+ "script": "Actor.animate(qua_death01)",
+ "action": "ANIMATION: qua_death01"
+ },
+ "nom": {
+ "fr": "Mort",
+ "en": "Death"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_death01_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_death01_M"
+ },
+ "duree_s": 2.4,
+ "images": 73,
+ "planche": {
+ "fichier": "planches/qua_death01.jpg",
+ "images": 24,
+ "taille": 256,
+ "ips": 10.0
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 786,
+ "masculin": 777,
+ "total": 1563
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": false,
+ "ecart_pct": 95.63
+ },
+ "deplacement": {
+ "cm": 53,
+ "cm_s": 22,
+ "sens": "arriere"
+ },
+ "posture": {
+ "principale": "variable",
+ "posture_origine": "calculee",
+ "debut": "debout",
+ "fin": "allonge"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 86,
+ "droite": 81
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "chute",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : le personnage s'effondre et reste au sol ; à utiliser pour une mort ou un évanouissement.",
+ "en": "Played once: the character collapses and stays down; use for a death or a faint."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "à revoir",
+ "motifs": [
+ "Bridget : sol cm 5.6 (lFoot)",
+ "Michael : sol cm 5.6 (lFoot)"
+ ],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "5C5C89D9FB3304A71CC3B2CB3018EF2781EACA968CDA0A7F2B6358AEE4CC48D7"
+ },
+ {
+ "cle": "qua_driving_loop",
+ "appel": {
+ "script": "Actor.animate(qua_driving_loop)",
+ "action": "ANIMATION: qua_driving_loop"
+ },
+ "nom": {
+ "fr": "Conduite",
+ "en": "Driving"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_driving_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_driving_loop_M"
+ },
+ "duree_s": 1.67,
+ "images": 51,
+ "planche": {
+ "fichier": "planches/qua_driving_loop.jpg",
+ "images": 20,
+ "taille": 256,
+ "ips": 12.0
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 470,
+ "masculin": 460,
+ "total": 930
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "assis",
+ "posture_origine": "calculee",
+ "hauteur_assise_cm": 45
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "repos",
+ "droite": "repos"
+ }
+ },
+ "accessoire": "volant",
+ "categorie": "conduite",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de conduite, mains sur le volant ; pour un personnage assis dans un véhicule.",
+ "en": "Looping driving pose, hands on the wheel; for a character seated in a vehicle."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "3992745D7BAFD326EBC7150447823724D78E1BC48DCABFA21435BCF3DCD4D2B5"
+ },
+ {
+ "cle": "qua_fixing_kneeling",
+ "appel": {
+ "script": "Actor.animate(qua_fixing_kneeling)",
+ "action": "ANIMATION: qua_fixing_kneeling"
+ },
+ "nom": {
+ "fr": "Réparer à genoux",
+ "en": "Fix kneeling"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_fixing_kneeling_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_fixing_kneeling_M"
+ },
+ "duree_s": 5.2,
+ "images": 157,
+ "planche": {
+ "fichier": "planches/qua_fixing_kneeling.jpg",
+ "images": 24,
+ "taille": 256,
+ "ips": 4.6
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 1231,
+ "masculin": 1221,
+ "total": 2452
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.03
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "a genoux",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 94
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "interaction",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de bricolage à genoux, pour une réparation ou un travail au sol.",
+ "en": "Looping kneeling repair work, for fixing something or working at ground level."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "à revoir",
+ "motifs": [
+ "Bridget : sol cm 5.2 (rFoot)",
+ "Michael : sol cm 4.4 (rFoot)"
+ ],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "0DE675BC7314E651A21431333C791649AAED186DECC116E022F3BEC2D111FF1F"
+ },
+ {
+ "cle": "qua_hit_chest",
+ "appel": {
+ "script": "Actor.animate(qua_hit_chest)",
+ "action": "ANIMATION: qua_hit_chest"
+ },
+ "nom": {
+ "fr": "Coup à la poitrine",
+ "en": "Hit in chest"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_hit_chest_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_hit_chest_M"
+ },
+ "duree_s": 0.33,
+ "images": 11,
+ "planche": {
+ "fichier": "planches/qua_hit_chest.jpg",
+ "images": 4,
+ "taille": 256,
+ "ips": 12.1
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 445,
+ "masculin": 436,
+ "total": 881
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": false,
+ "ecart_pct": 15.27
+ },
+ "deplacement": {
+ "cm": 3,
+ "cm_s": 9,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "combat",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : réaction à un coup reçu au torse ; à placer après une attaque.",
+ "en": "Played once: reaction to a blow to the chest; place it after an attack."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.01,
+ "pire_torsion_deg": 0.02
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "413F5B0E2BAAD564C4BD39659C8D93378CDC9F70E0C0C29DC8A2EC66026A55E5"
+ },
+ {
+ "cle": "qua_hit_head",
+ "appel": {
+ "script": "Actor.animate(qua_hit_head)",
+ "action": "ANIMATION: qua_hit_head"
+ },
+ "nom": {
+ "fr": "Coup à la tête",
+ "en": "Hit in head"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_hit_head_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_hit_head_M"
+ },
+ "duree_s": 0.43,
+ "images": 14,
+ "planche": {
+ "fichier": "planches/qua_hit_head.jpg",
+ "images": 5,
+ "taille": 256,
+ "ips": 11.6
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 459,
+ "masculin": 449,
+ "total": 908
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": false,
+ "ecart_pct": 5.01
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "combat",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : réaction à un coup reçu à la tête ; à placer après une attaque.",
+ "en": "Played once: reaction to a blow to the head; place it after an attack."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "45F001A3E71833E01151647FF8A7D9EB3ACE6FF6B5D478B3F7ED77F0EAC1FA1A"
+ },
+ {
+ "cle": "qua_idle_loop",
+ "appel": {
+ "script": "Actor.animate(qua_idle_loop)",
+ "action": "ANIMATION: qua_idle_loop"
+ },
+ "nom": {
+ "fr": "Attente",
+ "en": "Idle"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_idle_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_idle_loop_M"
+ },
+ "duree_s": 2.5,
+ "images": 76,
+ "planche": {
+ "fichier": "planches/qua_idle_loop.jpg",
+ "images": 24,
+ "taille": 256,
+ "ips": 9.6
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 570,
+ "masculin": 560,
+ "total": 1130
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.19
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "repos",
+ "droite": "repos"
+ }
+ },
+ "accessoire": "",
+ "categorie": "attente",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle d'attente debout par défaut, pour un personnage qui patiente.",
+ "en": "Default standing idle loop, for a waiting character."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "CDF1F85493A49722295790E16CE55F85535389CBC8F31E38C994FB3D02B0E36B"
+ },
+ {
+ "cle": "qua_idle_talking_loop",
+ "appel": {
+ "script": "Actor.animate(qua_idle_talking_loop)",
+ "action": "ANIMATION: qua_idle_talking_loop"
+ },
+ "nom": {
+ "fr": "Attente en parlant",
+ "en": "Idle talking"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_idle_talking_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_idle_talking_loop_M"
+ },
+ "duree_s": 2.93,
+ "images": 89,
+ "planche": {
+ "fichier": "planches/qua_idle_talking_loop.jpg",
+ "images": 24,
+ "taille": 256,
+ "ips": 8.2
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 792,
+ "masculin": 780,
+ "total": 1573
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "repos"
+ }
+ },
+ "accessoire": "",
+ "categorie": "conversation",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle debout avec gestes de la main, pour un personnage qui parle dans un dialogue.",
+ "en": "Standing loop with hand gestures, for a character speaking in a dialogue."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "2E481B14FFB95D4205B451A75218ECB6932F7E11E7412195BB599C6234839CD8"
+ },
+ {
+ "cle": "qua_idle_torch_loop",
+ "appel": {
+ "script": "Actor.animate(qua_idle_torch_loop)",
+ "action": "ANIMATION: qua_idle_torch_loop"
+ },
+ "nom": {
+ "fr": "Attente avec torche",
+ "en": "Idle with torch"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_idle_torch_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_idle_torch_loop_M"
+ },
+ "duree_s": 1.27,
+ "images": 39,
+ "planche": {
+ "fichier": "planches/qua_idle_torch_loop.jpg",
+ "images": 15,
+ "taille": 256,
+ "ips": 11.8
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 526,
+ "masculin": 516,
+ "total": 1042
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "repos",
+ "droite": "repos"
+ }
+ },
+ "accessoire": "torche",
+ "categorie": "attente",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle d'attente debout en tenant une torche ; pour une scène de nuit ou d'exploration.",
+ "en": "Standing idle loop holding a torch; for a night or exploration scene."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "202DF6A92CF7638F8BD8380C6683F7E6E286C030E831E4D4A43EA06359EFC5B5"
+ },
+ {
+ "cle": "qua_interact",
+ "appel": {
+ "script": "Actor.animate(qua_interact)",
+ "action": "ANIMATION: qua_interact"
+ },
+ "nom": {
+ "fr": "Interagir",
+ "en": "Interact"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_interact_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_interact_M"
+ },
+ "duree_s": 2.0,
+ "images": 61,
+ "planche": {
+ "fichier": "planches/qua_interact.jpg",
+ "images": 24,
+ "taille": 256,
+ "ips": 12.0
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 544,
+ "masculin": 531,
+ "total": 1075
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "repos"
+ }
+ },
+ "accessoire": "",
+ "categorie": "interaction",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : geste pour actionner un objet (bouton, porte, levier).",
+ "en": "Played once: gesture to operate an object (button, door, lever)."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "6787AD53457B4527F8B8E30B487CCF43524D4DF2DA7C9CBADB5E1102B73AC306"
+ },
+ {
+ "cle": "qua_jog_fwd_loop",
+ "appel": {
+ "script": "Actor.animate(qua_jog_fwd_loop)",
+ "action": "ANIMATION: qua_jog_fwd_loop"
+ },
+ "nom": {
+ "fr": "Jogging",
+ "en": "Jog"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_jog_fwd_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_jog_fwd_loop_M"
+ },
+ "duree_s": 0.93,
+ "images": 29,
+ "planche": {
+ "fichier": "planches/qua_jog_fwd_loop.jpg",
+ "images": 11,
+ "taille": 256,
+ "ips": 11.8
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 488,
+ "masculin": 477,
+ "total": 965
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.26
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 17,
+ "droite": 17
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "locomotion",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de course légère sur place ; à combiner avec un déplacement du personnage.",
+ "en": "Looping light jog in place; combine with a character movement."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.06,
+ "pire_torsion_deg": 0.11
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "2BF6C3A9BCA1BFE725F30895126F78AD0700411E76198B132F11524E0176A29A"
+ },
+ {
+ "cle": "qua_jump_land",
+ "appel": {
+ "script": "Actor.animate(qua_jump_land)",
+ "action": "ANIMATION: qua_jump_land"
+ },
+ "nom": {
+ "fr": "Réception de saut",
+ "en": "Jump landing"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_jump_land_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_jump_land_M"
+ },
+ "duree_s": 1.27,
+ "images": 39,
+ "planche": {
+ "fichier": "planches/qua_jump_land.jpg",
+ "images": 15,
+ "taille": 256,
+ "ips": 11.8
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 598,
+ "masculin": 588,
+ "total": 1186
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": false,
+ "ecart_pct": 31.62
+ },
+ "deplacement": {
+ "cm": 3,
+ "cm_s": 3,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "chute",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : réception au sol ; dernière phase d'un saut (après la boucle en l'air).",
+ "en": "Played once: landing on the ground; last phase of a jump (after the airborne loop)."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.02,
+ "pire_torsion_deg": 0.03
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "C847CE338880ED7A506CA2CFD6ADC04567D735E061F211D23881A8BF826C99A1"
+ },
+ {
+ "cle": "qua_jump_loop",
+ "appel": {
+ "script": "Actor.animate(qua_jump_loop)",
+ "action": "ANIMATION: qua_jump_loop"
+ },
+ "nom": {
+ "fr": "Saut en l'air",
+ "en": "Jump airborne"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_jump_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_jump_loop_M"
+ },
+ "duree_s": 2.5,
+ "images": 76,
+ "planche": {
+ "fichier": "planches/qua_jump_loop.jpg",
+ "images": 24,
+ "taille": 256,
+ "ips": 9.6
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 606,
+ "masculin": 594,
+ "total": 1199
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 0,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "repos",
+ "droite": "repos"
+ }
+ },
+ "accessoire": "",
+ "categorie": "chute",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de la phase en l'air d'un saut, entre le départ et la réception.",
+ "en": "Looping airborne phase of a jump, between the start and the landing."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "E74A50B4A340C003209EBB1131EA7369D77D03C7A9A2016A2DA190C16F672674"
+ },
+ {
+ "cle": "qua_jump_start",
+ "appel": {
+ "script": "Actor.animate(qua_jump_start)",
+ "action": "ANIMATION: qua_jump_start"
+ },
+ "nom": {
+ "fr": "Départ de saut",
+ "en": "Jump start"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_jump_start_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_jump_start_M"
+ },
+ "duree_s": 1.33,
+ "images": 41,
+ "planche": {
+ "fichier": "planches/qua_jump_start.jpg",
+ "images": 16,
+ "taille": 256,
+ "ips": 12.0
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 599,
+ "masculin": 591,
+ "total": 1191
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": false,
+ "ecart_pct": 37.45
+ },
+ "deplacement": {
+ "cm": 10,
+ "cm_s": 7,
+ "sens": "avant"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee",
+ "debut": "accroupi",
+ "fin": "debout"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 12,
+ "droite": 56
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "chute",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : impulsion au départ d'un saut ; première phase avant la boucle en l'air.",
+ "en": "Played once: take-off impulse; first phase before the airborne loop."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "à revoir",
+ "motifs": [
+ "Bridget : saut deg 23.1 (lForearmBend)",
+ "Michael : saut deg 23.1 (lForearmBend)"
+ ],
+ "pire_brut_pct": 0.01,
+ "pire_torsion_deg": 0.02
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "5EB89CFEC6D1329CA7AF815224B653BB7CBFC910EFD27C3817F46E73B4492E57"
+ },
+ {
+ "cle": "qua_pickup_table",
+ "appel": {
+ "script": "Actor.animate(qua_pickup_table)",
+ "action": "ANIMATION: qua_pickup_table"
+ },
+ "nom": {
+ "fr": "Ramasser sur une table",
+ "en": "Pick up from table"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_pickup_table_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_pickup_table_M"
+ },
+ "duree_s": 0.83,
+ "images": 26,
+ "planche": {
+ "fichier": "planches/qua_pickup_table.jpg",
+ "images": 10,
+ "taille": 256,
+ "ips": 12.0
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 481,
+ "masculin": 471,
+ "total": 951
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "interaction",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : le personnage prend un objet posé sur une table.",
+ "en": "Played once: the character picks up an object lying on a table."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.02,
+ "pire_torsion_deg": 0.05
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "A128DEBD8A93E4C2654160F6CD6F53D53C01B72C1D19B50B0406873A3F05D650"
+ },
+ {
+ "cle": "qua_pistol_aim_down",
+ "appel": {
+ "script": "Actor.animate(qua_pistol_aim_down)",
+ "action": "ANIMATION: qua_pistol_aim_down"
+ },
+ "nom": {
+ "fr": "Viser vers le bas",
+ "en": "Aim down"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_pistol_aim_down_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_pistol_aim_down_M"
+ },
+ "duree_s": 0.17,
+ "images": 6,
+ "planche": {
+ "fichier": "planches/qua_pistol_aim_down.jpg",
+ "images": 4,
+ "taille": 256,
+ "ips": 23.5
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 392,
+ "masculin": 382,
+ "total": 774
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "repos",
+ "droite": "repos"
+ }
+ },
+ "accessoire": "pistolet",
+ "categorie": "combat",
+ "style": "agressif",
+ "usage": {
+ "fr": "Pose de visée pistolet vers le bas ; à enchaîner avec les autres visées ou le tir.",
+ "en": "Pistol aiming pose pointing down; chain with the other aims or the shot."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "9E23F1BCCDDC168D8DAB59C22E593285A51CDC2962BDF83B03C28A447DA0BECE"
+ },
+ {
+ "cle": "qua_pistol_aim_neutral",
+ "appel": {
+ "script": "Actor.animate(qua_pistol_aim_neutral)",
+ "action": "ANIMATION: qua_pistol_aim_neutral"
+ },
+ "nom": {
+ "fr": "Viser droit",
+ "en": "Aim straight"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_pistol_aim_neutral_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_pistol_aim_neutral_M"
+ },
+ "duree_s": 0.17,
+ "images": 6,
+ "planche": {
+ "fichier": "planches/qua_pistol_aim_neutral.jpg",
+ "images": 4,
+ "taille": 256,
+ "ips": 23.5
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 392,
+ "masculin": 382,
+ "total": 774
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "repos",
+ "droite": "repos"
+ }
+ },
+ "accessoire": "pistolet",
+ "categorie": "combat",
+ "style": "agressif",
+ "usage": {
+ "fr": "Pose de visée pistolet à l'horizontale ; pose de base avant un tir.",
+ "en": "Pistol aiming pose at level; base pose before a shot."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "984A55AF0B4AB7B5456D237F9F28BE24624C5C8AF8672514045460B1D9FDDA7F"
+ },
+ {
+ "cle": "qua_pistol_aim_up",
+ "appel": {
+ "script": "Actor.animate(qua_pistol_aim_up)",
+ "action": "ANIMATION: qua_pistol_aim_up"
+ },
+ "nom": {
+ "fr": "Viser vers le haut",
+ "en": "Aim up"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_pistol_aim_up_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_pistol_aim_up_M"
+ },
+ "duree_s": 0.17,
+ "images": 6,
+ "planche": {
+ "fichier": "planches/qua_pistol_aim_up.jpg",
+ "images": 4,
+ "taille": 256,
+ "ips": 23.5
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 392,
+ "masculin": 382,
+ "total": 774
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "levee",
+ "droite": "levee"
+ }
+ },
+ "accessoire": "pistolet",
+ "categorie": "combat",
+ "style": "agressif",
+ "usage": {
+ "fr": "Pose de visée pistolet vers le haut ; à enchaîner avec les autres visées ou le tir.",
+ "en": "Pistol aiming pose pointing up; chain with the other aims or the shot."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "47FDF5F57483E4831F45C7E8C75BFC5794002B18EC71F35615E0535267964356"
+ },
+ {
+ "cle": "qua_pistol_idle_loop",
+ "appel": {
+ "script": "Actor.animate(qua_pistol_idle_loop)",
+ "action": "ANIMATION: qua_pistol_idle_loop"
+ },
+ "nom": {
+ "fr": "Attente au pistolet",
+ "en": "Pistol idle"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_pistol_idle_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_pistol_idle_loop_M"
+ },
+ "duree_s": 1.67,
+ "images": 51,
+ "planche": {
+ "fichier": "planches/qua_pistol_idle_loop.jpg",
+ "images": 20,
+ "taille": 256,
+ "ips": 12.0
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 473,
+ "masculin": 464,
+ "total": 937
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "repos",
+ "droite": "repos"
+ }
+ },
+ "accessoire": "pistolet",
+ "categorie": "combat",
+ "style": "agressif",
+ "usage": {
+ "fr": "Boucle d'attente debout, pistolet en main ; pour un personnage armé qui guette.",
+ "en": "Standing idle loop with a pistol in hand; for an armed character on guard."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "25AC610F52C00AEF05B6379BF801A004A002B1C1897E2B5E91A2FAAE5BAC6B56"
+ },
+ {
+ "cle": "qua_pistol_reload",
+ "appel": {
+ "script": "Actor.animate(qua_pistol_reload)",
+ "action": "ANIMATION: qua_pistol_reload"
+ },
+ "nom": {
+ "fr": "Recharger le pistolet",
+ "en": "Pistol reload"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_pistol_reload_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_pistol_reload_M"
+ },
+ "duree_s": 1.67,
+ "images": 51,
+ "planche": {
+ "fichier": "planches/qua_pistol_reload.jpg",
+ "images": 20,
+ "taille": 256,
+ "ips": 12.0
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 633,
+ "masculin": 621,
+ "total": 1254
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "pistolet",
+ "categorie": "combat",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : rechargement du pistolet ; à placer entre deux tirs.",
+ "en": "Played once: pistol reload; place it between two shots."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.16,
+ "pire_torsion_deg": 0.3
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "24AEE8EB7849A51A2FC222845C7847896F5EE48F5F2364FF9BEF05355B39AB47"
+ },
+ {
+ "cle": "qua_pistol_shoot",
+ "appel": {
+ "script": "Actor.animate(qua_pistol_shoot)",
+ "action": "ANIMATION: qua_pistol_shoot"
+ },
+ "nom": {
+ "fr": "Tir au pistolet",
+ "en": "Pistol shot"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_pistol_shoot_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_pistol_shoot_M"
+ },
+ "duree_s": 0.63,
+ "images": 20,
+ "planche": {
+ "fichier": "planches/qua_pistol_shoot.jpg",
+ "images": 8,
+ "taille": 256,
+ "ips": 12.7
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 439,
+ "masculin": 430,
+ "total": 869
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "pistolet",
+ "categorie": "combat",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : un tir de pistolet avec recul.",
+ "en": "Played once: one pistol shot with recoil."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.01
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "A5EF75F6DDBC1CBAD89869DC0D022761993EBF88D49E4193421F42CF4991CAC4"
+ },
+ {
+ "cle": "qua_punch_cross",
+ "appel": {
+ "script": "Actor.animate(qua_punch_cross)",
+ "action": "ANIMATION: qua_punch_cross"
+ },
+ "nom": {
+ "fr": "Direct",
+ "en": "Cross punch"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_punch_cross_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_punch_cross_M"
+ },
+ "duree_s": 1.0,
+ "images": 31,
+ "planche": {
+ "fichier": "planches/qua_punch_cross.jpg",
+ "images": 12,
+ "taille": 256,
+ "ips": 12.0
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 498,
+ "masculin": 489,
+ "total": 987
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "combat",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : coup de poing direct ; pour une bagarre.",
+ "en": "Played once: cross punch; for a fight."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "727894040989E2BEF757B2DC50E57447886F5C6CEC90004C4EF5D1F72A5F468E"
+ },
+ {
+ "cle": "qua_punch_jab",
+ "appel": {
+ "script": "Actor.animate(qua_punch_jab)",
+ "action": "ANIMATION: qua_punch_jab"
+ },
+ "nom": {
+ "fr": "Jab",
+ "en": "Jab"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_punch_jab_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_punch_jab_M"
+ },
+ "duree_s": 0.87,
+ "images": 27,
+ "planche": {
+ "fichier": "planches/qua_punch_jab.jpg",
+ "images": 10,
+ "taille": 256,
+ "ips": 11.5
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 472,
+ "masculin": 462,
+ "total": 934
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "combat",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : coup de poing rapide ; pour une bagarre.",
+ "en": "Played once: quick jab punch; for a fight."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.01
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "0286B49557A3CF43FE37DE88E892E3BF43DDF36157A43049AEB514630F6E3279"
+ },
+ {
+ "cle": "qua_push_loop",
+ "appel": {
+ "script": "Actor.animate(qua_push_loop)",
+ "action": "ANIMATION: qua_push_loop"
+ },
+ "nom": {
+ "fr": "Pousser",
+ "en": "Push"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_push_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_push_loop_M"
+ },
+ "duree_s": 2.67,
+ "images": 81,
+ "planche": {
+ "fichier": "planches/qua_push_loop.jpg",
+ "images": 24,
+ "taille": 256,
+ "ips": 9.0
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 584,
+ "masculin": 577,
+ "total": 1161
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 78,
+ "droite": 80
+ },
+ "mains": {
+ "gauche": "repos",
+ "droite": "repos"
+ }
+ },
+ "accessoire": "",
+ "categorie": "interaction",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de poussée à deux mains sur place ; pour pousser un meuble ou une porte lourde.",
+ "en": "Looping two-handed push in place; for pushing furniture or a heavy door."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "49D7186234EC47E66A9993FDEA064F4EC4D6A515B2AFA70B7A0E3083B9B87DE2"
+ },
+ {
+ "cle": "qua_roll",
+ "appel": {
+ "script": "Actor.animate(qua_roll)",
+ "action": "ANIMATION: qua_roll"
+ },
+ "nom": {
+ "fr": "Roulade",
+ "en": "Roll"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_roll_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_roll_M"
+ },
+ "duree_s": 1.47,
+ "images": 45,
+ "planche": {
+ "fichier": "planches/qua_roll.jpg",
+ "images": 18,
+ "taille": 256,
+ "ips": 12.2
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 631,
+ "masculin": 621,
+ "total": 1252
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": false,
+ "ecart_pct": 29.75
+ },
+ "deplacement": {
+ "cm": 11,
+ "cm_s": 8,
+ "sens": "avant"
+ },
+ "posture": {
+ "principale": "variable",
+ "posture_origine": "calculee",
+ "debut": "accroupi",
+ "fin": "debout"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 53,
+ "droite": 53
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "sport",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : roulade au sol ; pour esquiver ou plonger.",
+ "en": "Played once: ground roll; for dodging or diving."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "à revoir",
+ "motifs": [
+ "Bridget : sol cm 4.8 (rFoot)",
+ "Michael : sol cm 5.0 (rFoot)"
+ ],
+ "pire_brut_pct": 0.19,
+ "pire_torsion_deg": 0.49
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "C86A5D1A1728CB05A99461B1DF39691D07C5011F01C53DDFE7C2E44C9B97733E"
+ },
+ {
+ "cle": "qua_sitting_enter",
+ "appel": {
+ "script": "Actor.animate(qua_sitting_enter)",
+ "action": "ANIMATION: qua_sitting_enter"
+ },
+ "nom": {
+ "fr": "S'asseoir",
+ "en": "Sit down"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_sitting_enter_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_sitting_enter_M"
+ },
+ "duree_s": 1.3,
+ "images": 40,
+ "planche": {
+ "fichier": "planches/qua_sitting_enter.jpg",
+ "images": 16,
+ "taille": 256,
+ "ips": 12.3
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 597,
+ "masculin": 587,
+ "total": 1184
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": false,
+ "ecart_pct": 52.14
+ },
+ "deplacement": {
+ "cm": 28,
+ "cm_s": 22,
+ "sens": "arriere"
+ },
+ "posture": {
+ "principale": "variable",
+ "posture_origine": "calculee",
+ "debut": "debout",
+ "fin": "assis"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 90
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "assis",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : entrée, le personnage passe de debout à assis ; à suivre d'une boucle assise.",
+ "en": "Played once: entry, the character goes from standing to seated; follow with a seated loop."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "F71563458C2AE60FECDBEFD8203FEBD96D7318E607FF4B0E6038D4D258E7A98D"
+ },
+ {
+ "cle": "qua_sitting_exit",
+ "appel": {
+ "script": "Actor.animate(qua_sitting_exit)",
+ "action": "ANIMATION: qua_sitting_exit"
+ },
+ "nom": {
+ "fr": "Se lever",
+ "en": "Stand up"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_sitting_exit_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_sitting_exit_M"
+ },
+ "duree_s": 1.03,
+ "images": 32,
+ "planche": {
+ "fichier": "planches/qua_sitting_exit.jpg",
+ "images": 12,
+ "taille": 256,
+ "ips": 11.7
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 556,
+ "masculin": 546,
+ "total": 1102
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": false,
+ "ecart_pct": 52.14
+ },
+ "deplacement": {
+ "cm": 28,
+ "cm_s": 27,
+ "sens": "avant"
+ },
+ "posture": {
+ "principale": "variable",
+ "posture_origine": "calculee",
+ "debut": "assis",
+ "fin": "debout"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 94
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "assis",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : sortie, le personnage passe d'assis à debout ; à placer après une boucle assise.",
+ "en": "Played once: exit, the character goes from seated to standing; place after a seated loop."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.01
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "6639D68DBFB5833681D25EAFFD8D9043AFBCDA5B75098B100187C4E1FD0BB744"
+ },
+ {
+ "cle": "qua_sitting_idle_loop",
+ "appel": {
+ "script": "Actor.animate(qua_sitting_idle_loop)",
+ "action": "ANIMATION: qua_sitting_idle_loop"
+ },
+ "nom": {
+ "fr": "Assis, attente",
+ "en": "Seated idle"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_sitting_idle_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_sitting_idle_loop_M"
+ },
+ "duree_s": 1.67,
+ "images": 51,
+ "planche": {
+ "fichier": "planches/qua_sitting_idle_loop.jpg",
+ "images": 20,
+ "taille": 256,
+ "ips": 12.0
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 558,
+ "masculin": 548,
+ "total": 1106
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "assis",
+ "posture_origine": "calculee",
+ "hauteur_assise_cm": 45
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "repos",
+ "droite": "repos"
+ }
+ },
+ "accessoire": "",
+ "categorie": "assis",
+ "style": "détendu",
+ "usage": {
+ "fr": "Boucle d'attente assise, après « s'asseoir » ; pour un personnage installé sur une chaise.",
+ "en": "Seated idle loop, after the sit-down clip; for a character settled on a chair."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "5B70EDA8CEA21B2B83DD66B311149600F248EE9F8243340D70F4AABF26E62FC5"
+ },
+ {
+ "cle": "qua_sitting_talking_loop",
+ "appel": {
+ "script": "Actor.animate(qua_sitting_talking_loop)",
+ "action": "ANIMATION: qua_sitting_talking_loop"
+ },
+ "nom": {
+ "fr": "Assis, en parlant",
+ "en": "Seated talking"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_sitting_talking_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_sitting_talking_loop_M"
+ },
+ "duree_s": 2.93,
+ "images": 89,
+ "planche": {
+ "fichier": "planches/qua_sitting_talking_loop.jpg",
+ "images": 24,
+ "taille": 256,
+ "ips": 8.2
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 770,
+ "masculin": 761,
+ "total": 1531
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "assis",
+ "posture_origine": "calculee",
+ "hauteur_assise_cm": 45
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "repos"
+ }
+ },
+ "accessoire": "",
+ "categorie": "assis",
+ "style": "détendu",
+ "usage": {
+ "fr": "Boucle assise avec gestes de la main, pour un dialogue entre personnages assis.",
+ "en": "Seated loop with hand gestures, for a dialogue between seated characters."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "F0A4091AE1A72C4ACB9466DFC39D74E5EFACE0BABAC06C12BE428120F69495B8"
+ },
+ {
+ "cle": "qua_spell_simple_enter",
+ "appel": {
+ "script": "Actor.animate(qua_spell_simple_enter)",
+ "action": "ANIMATION: qua_spell_simple_enter"
+ },
+ "nom": {
+ "fr": "Début d'incantation",
+ "en": "Spell start"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_spell_simple_enter_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_spell_simple_enter_M"
+ },
+ "duree_s": 0.53,
+ "images": 17,
+ "planche": {
+ "fichier": "planches/qua_spell_simple_enter.jpg",
+ "images": 6,
+ "taille": 256,
+ "ips": 11.3
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 478,
+ "masculin": 468,
+ "total": 946
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": false,
+ "ecart_pct": 52.78
+ },
+ "deplacement": {
+ "cm": 3,
+ "cm_s": 5,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "magie",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : mise en position pour lancer un sort ; à suivre de la boucle d'incantation.",
+ "en": "Played once: stance to begin casting a spell; follow with the spell idle loop."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.01,
+ "pire_torsion_deg": 0.02
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "B3965AAE6955B739C33AA88DC46BE1C8DE44DEB81A2EC5B01C15AA1AF700E680"
+ },
+ {
+ "cle": "qua_spell_simple_exit",
+ "appel": {
+ "script": "Actor.animate(qua_spell_simple_exit)",
+ "action": "ANIMATION: qua_spell_simple_exit"
+ },
+ "nom": {
+ "fr": "Fin d'incantation",
+ "en": "Spell end"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_spell_simple_exit_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_spell_simple_exit_M"
+ },
+ "duree_s": 0.43,
+ "images": 14,
+ "planche": {
+ "fichier": "planches/qua_spell_simple_exit.jpg",
+ "images": 5,
+ "taille": 256,
+ "ips": 11.6
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 463,
+ "masculin": 453,
+ "total": 916
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": false,
+ "ecart_pct": 52.78
+ },
+ "deplacement": {
+ "cm": 3,
+ "cm_s": 7,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "magie",
+ "style": "neutre",
+ "usage": {
+ "fr": "Joué une fois : retour à la posture normale après un sort.",
+ "en": "Played once: return to normal stance after a spell."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.02,
+ "pire_torsion_deg": 0.04
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "45F84F02B7A818D42F18CC0CA4B52EC182A423C8C60CA036AF02092A04F675C2"
+ },
+ {
+ "cle": "qua_spell_simple_idle_loop",
+ "appel": {
+ "script": "Actor.animate(qua_spell_simple_idle_loop)",
+ "action": "ANIMATION: qua_spell_simple_idle_loop"
+ },
+ "nom": {
+ "fr": "Incantation",
+ "en": "Spell idle"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_spell_simple_idle_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_spell_simple_idle_loop_M"
+ },
+ "duree_s": 2.1,
+ "images": 64,
+ "planche": {
+ "fichier": "planches/qua_spell_simple_idle_loop.jpg",
+ "images": 24,
+ "taille": 256,
+ "ips": 11.4
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 574,
+ "masculin": 566,
+ "total": 1140
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "repos",
+ "droite": "repos"
+ }
+ },
+ "accessoire": "",
+ "categorie": "magie",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de concentration, mains levées, entre l'entrée et le lancer du sort.",
+ "en": "Looping concentration pose, hands raised, between the entry and the cast."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "07BAC68BF07A9A6E0E007DEFE06ACF73D3F2245D7D0733EAF0EC321989A5175D"
+ },
+ {
+ "cle": "qua_spell_simple_shoot",
+ "appel": {
+ "script": "Actor.animate(qua_spell_simple_shoot)",
+ "action": "ANIMATION: qua_spell_simple_shoot"
+ },
+ "nom": {
+ "fr": "Lancer un sort",
+ "en": "Cast spell"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_spell_simple_shoot_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_spell_simple_shoot_M"
+ },
+ "duree_s": 0.5,
+ "images": 16,
+ "planche": {
+ "fichier": "planches/qua_spell_simple_shoot.jpg",
+ "images": 6,
+ "taille": 256,
+ "ips": 12.0
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 462,
+ "masculin": 452,
+ "total": 914
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "repos",
+ "droite": "repos"
+ }
+ },
+ "accessoire": "",
+ "categorie": "magie",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : le geste qui lance le sort ; à placer après la boucle d'incantation.",
+ "en": "Played once: the gesture that casts the spell; place after the spell idle loop."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "76FB42C31F2C6B3A01C986DE1C2CE6A887A1E0D6BD1E5E9A8CA6E91FFB7FFE6F"
+ },
+ {
+ "cle": "qua_sprint_loop",
+ "appel": {
+ "script": "Actor.animate(qua_sprint_loop)",
+ "action": "ANIMATION: qua_sprint_loop"
+ },
+ "nom": {
+ "fr": "Sprint",
+ "en": "Sprint"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_sprint_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_sprint_loop_M"
+ },
+ "duree_s": 0.67,
+ "images": 21,
+ "planche": {
+ "fichier": "planches/qua_sprint_loop.jpg",
+ "images": 8,
+ "taille": 256,
+ "ips": 11.9
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 452,
+ "masculin": 443,
+ "total": 895
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.26
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 29,
+ "droite": 14
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "locomotion",
+ "style": "agressif",
+ "usage": {
+ "fr": "Boucle de course rapide sur place ; à combiner avec un déplacement pour une fuite ou une poursuite.",
+ "en": "Looping fast run in place; combine with a movement for an escape or chase."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.08,
+ "pire_torsion_deg": 0.14
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "9A341C0B31F5EAF0B93E5F9001AB1246D339EF83B4AB3BEB3F17A4A0916F1CDE"
+ },
+ {
+ "cle": "qua_swim_fwd_loop",
+ "appel": {
+ "script": "Actor.animate(qua_swim_fwd_loop)",
+ "action": "ANIMATION: qua_swim_fwd_loop"
+ },
+ "nom": {
+ "fr": "Nage",
+ "en": "Swim"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_swim_fwd_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_swim_fwd_loop_M"
+ },
+ "duree_s": 1.33,
+ "images": 41,
+ "planche": {
+ "fichier": "planches/qua_swim_fwd_loop.jpg",
+ "images": 16,
+ "taille": 256,
+ "ips": 12.0
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 498,
+ "masculin": 489,
+ "total": 987
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "dans l'eau",
+ "posture_origine": "declaree",
+ "posture_calculee": "allonge"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "nage",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de nage en avant ; à combiner avec un déplacement dans l'eau.",
+ "en": "Looping forward swim; combine with a movement in water."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.02,
+ "pire_torsion_deg": 0.04
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "260E19E1A794EFDE2A8AE788482FE22F03EC3EEC7BE7EE74BC1BDFE01CFCCD65"
+ },
+ {
+ "cle": "qua_swim_idle_loop",
+ "appel": {
+ "script": "Actor.animate(qua_swim_idle_loop)",
+ "action": "ANIMATION: qua_swim_idle_loop"
+ },
+ "nom": {
+ "fr": "Surplace dans l'eau",
+ "en": "Treading water"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_swim_idle_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_swim_idle_loop_M"
+ },
+ "duree_s": 3.33,
+ "images": 101,
+ "planche": {
+ "fichier": "planches/qua_swim_idle_loop.jpg",
+ "images": 24,
+ "taille": 256,
+ "ips": 7.2
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 865,
+ "masculin": 856,
+ "total": 1721
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "dans l'eau",
+ "posture_origine": "declaree",
+ "posture_calculee": "a genoux"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "repos",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "nage",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de flottaison sur place dans l'eau.",
+ "en": "Looping treading water in place."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "181DE0C7C89DE68256F6F592DF1E0BC91765207D7460946715CF8DE385CDD277"
+ },
+ {
+ "cle": "qua_sword_attack",
+ "appel": {
+ "script": "Actor.animate(qua_sword_attack)",
+ "action": "ANIMATION: qua_sword_attack"
+ },
+ "nom": {
+ "fr": "Attaque à l'épée",
+ "en": "Sword attack"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_sword_attack_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_sword_attack_M"
+ },
+ "duree_s": 1.53,
+ "images": 47,
+ "planche": {
+ "fichier": "planches/qua_sword_attack.jpg",
+ "images": 18,
+ "taille": 256,
+ "ips": 11.8
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 549,
+ "masculin": 539,
+ "total": 1089
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 89,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "épée",
+ "categorie": "combat",
+ "style": "agressif",
+ "usage": {
+ "fr": "Joué une fois : un coup d'épée ; à enchaîner après l'attente à l'épée.",
+ "en": "Played once: a sword strike; chain after the sword idle."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.05,
+ "pire_torsion_deg": 0.1
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "412F8748FC0F434155C1312F601C68BACBF025BF20450BE7DBF70524C0BA21D6"
+ },
+ {
+ "cle": "qua_sword_idle",
+ "appel": {
+ "script": "Actor.animate(qua_sword_idle)",
+ "action": "ANIMATION: qua_sword_idle"
+ },
+ "nom": {
+ "fr": "Attente à l'épée",
+ "en": "Sword idle"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_sword_idle_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_sword_idle_M"
+ },
+ "duree_s": 1.67,
+ "images": 51,
+ "planche": {
+ "fichier": "planches/qua_sword_idle.jpg",
+ "images": 20,
+ "taille": 256,
+ "ips": 12.0
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 569,
+ "masculin": 558,
+ "total": 1126
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 100,
+ "droite": 100
+ },
+ "mains": {
+ "gauche": "repos",
+ "droite": "repos"
+ }
+ },
+ "accessoire": "épée",
+ "categorie": "combat",
+ "style": "agressif",
+ "usage": {
+ "fr": "Boucle d'attente debout, épée en main, en garde.",
+ "en": "Standing idle loop, sword in hand, on guard."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "60227B1417B16DFE5D64C01AF23CFC3731A39B49737FC7B8A6F92AA664F1D18C"
+ },
+ {
+ "cle": "qua_walk_formal_loop",
+ "appel": {
+ "script": "Actor.animate(qua_walk_formal_loop)",
+ "action": "ANIMATION: qua_walk_formal_loop"
+ },
+ "nom": {
+ "fr": "Marche formelle",
+ "en": "Formal walk"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_walk_formal_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_walk_formal_loop_M"
+ },
+ "duree_s": 1.33,
+ "images": 41,
+ "planche": {
+ "fichier": "planches/qua_walk_formal_loop.jpg",
+ "images": 16,
+ "taille": 256,
+ "ips": 12.0
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 500,
+ "masculin": 490,
+ "total": 990
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.36
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 68,
+ "droite": 68
+ },
+ "mains": {
+ "gauche": "repos",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "locomotion",
+ "style": "formel",
+ "usage": {
+ "fr": "Boucle de marche tenue et digne sur place ; à combiner avec un déplacement (cérémonie, travail).",
+ "en": "Looping upright, dignified walk in place; combine with a movement (ceremony, office)."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.0
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "B6F21793C592498783A5A4FA7BE20889307484B8A86660DBE2537EF3201F13F1"
+ },
+ {
+ "cle": "qua_walk_loop",
+ "appel": {
+ "script": "Actor.animate(qua_walk_loop)",
+ "action": "ANIMATION: qua_walk_loop"
+ },
+ "nom": {
+ "fr": "Marche",
+ "en": "Walk"
+ },
+ "assets": {
+ "feminin": "/lpr_anim_quaternius/Anims/Female/qua_walk_loop_F",
+ "masculin": "/lpr_anim_quaternius/Anims/Male/qua_walk_loop_M"
+ },
+ "duree_s": 1.33,
+ "images": 41,
+ "planche": {
+ "fichier": "planches/qua_walk_loop.jpg",
+ "images": 16,
+ "taille": 256,
+ "ips": 12.0
+ },
+ "cadence": 30,
+ "taille_ko": {
+ "feminin": 506,
+ "masculin": 496,
+ "total": 1002
+ },
+ "taille_cuite": false,
+ "boucle": {
+ "oui": true,
+ "ecart_pct": 0.0
+ },
+ "deplacement": {
+ "cm": 0,
+ "cm_s": 0,
+ "sens": "sur place"
+ },
+ "posture": {
+ "principale": "debout",
+ "posture_origine": "calculee"
+ },
+ "contacts": {
+ "pieds_au_sol_pct": {
+ "gauche": 68,
+ "droite": 68
+ },
+ "mains": {
+ "gauche": "mouvement",
+ "droite": "mouvement"
+ }
+ },
+ "accessoire": "",
+ "categorie": "locomotion",
+ "style": "neutre",
+ "usage": {
+ "fr": "Boucle de marche sur place ; à combiner avec un déplacement du personnage.",
+ "en": "Looping walk in place; combine with a character movement."
+ },
+ "corps": [
+ "feminin",
+ "masculin"
+ ],
+ "qualite": {
+ "verdict": "accepté",
+ "motifs": [],
+ "pire_brut_pct": 0.0,
+ "pire_torsion_deg": 0.01
+ },
+ "source": "Quaternius Universal Animation Library (Standard)",
+ "licence": "CC0 1.0",
+ "credit": "Animations : Universal Animation Library (Standard) par Quaternius, licence CC0 1.0, https://quaternius.com",
+ "version": "1.0",
+ "empreinte": "2C83B802CC2E1CE528DACCED02E8DBD6C19D37575694D309DE3615A68E5F2A0B"
+ }
+ ]
+}
diff --git a/Tools/anim/Catalogue.py b/Tools/anim/Catalogue.py
new file mode 100644
index 0000000..81c4e45
--- /dev/null
+++ b/Tools/anim/Catalogue.py
@@ -0,0 +1,461 @@
+#!/usr/bin/env python3
+"""Catalogue des animations d'un mod (lot AP6) : catalogue.json, catalogue.html, vignettes.
+
+Entrees : pack.json (declare), livraison.json + fiches_calculees.json + rapport.json + csv de qualite (calcule),
+vignettes Saved/Measurement/vignettes/.png (carres 512 px), planches Saved/Measurement/planches/.jpg + .json
+(bande de N images carrees de 256 px, animee au survol), clips du mod ModSources//Content/Anims.
+Controles bloquants : code de sortie 1 et liste des causes ; rien n'est ecrit dans --out en cas d'echec.
+Python 3 standard, sans dependance.
+"""
+import argparse
+import csv
+import datetime
+import hashlib
+import html
+import json
+import os
+import re
+import shutil
+import sys
+
+BASE_KEYS = ["martialart", "sneak", "dance", "smoke", "write", "drinkbottle", "drinkglass", "drinktea",
+ "camera", "call", "browse", "makeup", "eat", "sweep", "fightlost", "gun", "knockedout"]
+TRIAL_MODULE = "lpr_anim_essai"
+SENS_FR = {"sur place": "sur place", "avant": "avance", "arriere": "recule", "cote": "de côté"}
+
+
+def read_json(path):
+ with open(path, encoding="utf-8-sig") as f:
+ return json.load(f)
+
+
+def sha256_of(paths):
+ h = hashlib.sha256()
+ for p in paths:
+ with open(p, "rb") as f:
+ for chunk in iter(lambda: f.read(1 << 20), b""):
+ h.update(chunk)
+ return h.hexdigest().upper()
+
+
+def read_csv_rows(path):
+ if not os.path.isfile(path):
+ return []
+ with open(path, encoding="utf-8-sig", newline="") as f:
+ return list(csv.DictReader(f))
+
+
+def num(v):
+ try:
+ return float(v)
+ except (TypeError, ValueError):
+ return 0.0
+
+
+def game_keys(root):
+ """Cles deja utilisees par le jeu (hors module d'essai) : ANIMATION: et animate()."""
+ keys = set()
+ mods = os.path.join(root, "Content", "Modules")
+ pat = re.compile(r"ANIMATION:\s*([A-Za-z0-9_]+)|animate\(\s*([A-Za-z0-9_]+)\s*\)")
+ for dp, dn, fn in os.walk(mods):
+ rel = os.path.relpath(dp, mods).replace("\\", "/")
+ if rel.split("/")[0] == TRIAL_MODULE:
+ dn[:] = []
+ continue
+ for name in fn:
+ if name.lower().endswith((".lpaction", ".lpscene")):
+ try:
+ with open(os.path.join(dp, name), encoding="utf-8-sig", errors="replace") as f:
+ text = f.read()
+ except OSError:
+ continue
+ for m in pat.finditer(text):
+ keys.add((m.group(1) or m.group(2)).lower())
+ return keys
+
+
+def other_catalogue_keys(root, mod, out_dir):
+ keys = {}
+ base = os.path.join(root, "ModSources")
+ if not os.path.isdir(base):
+ return keys
+ own = os.path.normcase(os.path.abspath(os.path.join(out_dir, "catalogue.json")))
+ for d in sorted(os.listdir(base)):
+ p = os.path.join(base, d, "Module", "Animations", "catalogue.json")
+ if d == mod or not os.path.isfile(p) or os.path.normcase(os.path.abspath(p)) == own:
+ continue
+ try:
+ for a in read_json(p).get("animations", []):
+ keys[str(a.get("cle", "")).lower()] = d
+ except (ValueError, OSError):
+ pass
+ return keys
+
+
+def build(args, errors):
+ root = os.path.abspath(args.root)
+ pipe = os.path.abspath(args.pipeline)
+ pack_path = os.path.abspath(args.pack)
+ out = os.path.abspath(args.out)
+ mod_dir = os.path.abspath(args.mod_dir) if args.mod_dir else os.path.join(root, "ModSources", args.mod)
+ vign = os.path.abspath(args.vignettes) if args.vignettes else os.path.join(root, "Saved", "Measurement", "vignettes")
+ plan = os.path.abspath(args.planches) if args.planches else os.path.join(root, "Saved", "Measurement", "planches")
+
+ def load(path, what):
+ if not os.path.isfile(path):
+ errors.append("%s introuvable : %s" % (what, path))
+ return None
+ try:
+ return read_json(path)
+ except ValueError as e:
+ errors.append("%s illisible : %s (%s)" % (what, path, e))
+ return None
+
+ pack = load(pack_path, "pack.json")
+ liv_path = os.path.join(pipe, "livraison.json")
+ fic_path = os.path.join(pipe, "fiches_calculees.json")
+ liv = load(liv_path, "livraison.json")
+ fic = load(fic_path, "fiches_calculees.json")
+ rap = load(os.path.join(pipe, "rapport.json"), "rapport.json")
+ if None in (pack, liv, fic, rap):
+ return None
+
+ # Rapport et fiches calculees.
+ if rap.get("valide") is not True:
+ errors.append("rapport.json non valide : %s" % "; ".join(map(str, rap.get("echecs") or [])))
+ if fic.get("echecs"):
+ errors.append("fiches_calculees.json contient des echecs : %s" % "; ".join(map(str, fic["echecs"])))
+ if os.path.getmtime(fic_path) <= os.path.getmtime(liv_path):
+ errors.append("fiches_calculees.json (%s) n'est pas plus recent que livraison.json (%s) : "
+ "le catalogue ne decrit pas les clips livres"
+ % (datetime.datetime.fromtimestamp(os.path.getmtime(fic_path)).isoformat(" ", "seconds"),
+ datetime.datetime.fromtimestamp(os.path.getmtime(liv_path)).isoformat(" ", "seconds")))
+ fiches = fic.get("fiches", {})
+ declared = pack.get("animations", {})
+
+ # Cles : uniques, sans collision.
+ keys = [str(e.get("cle", "")) for e in liv]
+ seen = set()
+ for k in keys:
+ if k in seen:
+ errors.append("cle en double dans livraison.json : %s" % k)
+ seen.add(k)
+ taken = {k: "jeu de base" for k in BASE_KEYS}
+ for k in game_keys(root):
+ taken.setdefault(k, "scenario du jeu")
+ for k, d in other_catalogue_keys(root, args.mod, out).items():
+ taken.setdefault(k, "catalogue de %s" % d)
+ for k in keys:
+ if k.lower() in taken:
+ errors.append("collision de cle : %s deja utilisee (%s)" % (k, taken[k.lower()]))
+
+ # Qualite.
+ a_revoir = {}
+ for line in rap.get("a_revoir") or []:
+ parts = [p.strip() for p in str(line).split(" : ", 2)]
+ if len(parts) == 3:
+ a_revoir.setdefault(parts[1], []).append("%s : %s" % (parts[0], parts[2]))
+ qual = {}
+ for corps in ("Bridget", "Michael"):
+ for r in read_csv_rows(os.path.join(pipe, "eval_%s.csv" % corps)):
+ q = qual.setdefault(r["clip"], {"brut": 0.0, "torsion": 0.0})
+ q["brut"] = max(q["brut"], num(r.get("brut_pire")))
+ q["torsion"] = max(q["torsion"], num(r.get("torsion_deg")))
+
+ animations = []
+ thumbs = []
+ for e in liv:
+ k = str(e.get("cle", ""))
+ d = declared.get(k)
+ f = fiches.get(k)
+ if d is None:
+ errors.append("%s : absente de pack.json" % k)
+ if f is None:
+ errors.append("%s : fiche calculee absente" % k)
+ if d is None or f is None:
+ continue
+ clip = d.get("clip", "")
+ miss = []
+ for name, val in (("clip", clip), ("nom.fr", (d.get("nom") or {}).get("fr")), ("nom.en", (d.get("nom") or {}).get("en")),
+ ("categorie", d.get("categorie")), ("style", d.get("style")),
+ ("usage.fr", (d.get("usage") or {}).get("fr")), ("usage.en", (d.get("usage") or {}).get("en"))):
+ if not val:
+ miss.append(name)
+ for name in ("images", "cadence", "duree_s", "boucle", "deplacement_cm", "vitesse_cm_s", "deplacement_sens",
+ "posture", "pieds_au_sol_pct", "mains"):
+ if f.get(name) in (None, "", {}):
+ miss.append("fiche." + name)
+ for name in ("source", "licence", "credit"):
+ if not pack.get({"source": "source", "licence": "licence", "credit": "credit"}[name]):
+ miss.append("pack." + name)
+ for name in ("asset_F", "asset_M"):
+ if not e.get(name):
+ miss.append("livraison." + name)
+ if miss:
+ errors.append("%s : champs obligatoires vides : %s" % (k, ", ".join(miss)))
+ thumb = os.path.join(vign, "%s.png" % clip)
+ if not clip or not os.path.isfile(thumb):
+ errors.append("%s : vignette absente (%s)" % (k, thumb))
+ pj = os.path.join(plan, "%s.jpg" % clip)
+ pm = os.path.join(plan, "%s.json" % clip)
+ pinfo = None
+ if not clip or not os.path.isfile(pj):
+ errors.append("%s : planche absente (%s)" % (k, pj))
+ elif not os.path.isfile(pm):
+ errors.append("%s : description de planche absente (%s)" % (k, pm))
+ else:
+ try:
+ pinfo = read_json(pm)
+ n_img = pinfo["images"]
+ if isinstance(n_img, bool) or not isinstance(n_img, int) or n_img < 2:
+ errors.append("%s : planche invalide, images = %r (au moins 2 attendues) : %s" % (k, n_img, pm))
+ pinfo = None
+ elif pinfo.get("taille") != 256:
+ errors.append("%s : planche invalide, taille = %r (256 attendu) : %s" % (k, pinfo.get("taille"), pm))
+ pinfo = None
+ except (ValueError, KeyError, TypeError, AttributeError) as e:
+ errors.append("%s : description de planche illisible : %s (%r)" % (k, pm, e))
+ pinfo = None
+ files = [os.path.join(mod_dir, "Content", "Anims", "Female", k + "_F.uasset"),
+ os.path.join(mod_dir, "Content", "Anims", "Male", k + "_M.uasset")]
+ for p in files:
+ if not os.path.isfile(p):
+ errors.append("%s : clip du mod absent : %s" % (k, p))
+ if miss or not os.path.isfile(files[0]) or not os.path.isfile(files[1]) or not os.path.isfile(thumb) or pinfo is None:
+ continue # causes deja listees ci-dessus
+
+ tf, tm = os.path.getsize(files[0]), os.path.getsize(files[1])
+ motifs = a_revoir.get(clip, [])
+ q = qual.get(clip, {"brut": 0.0, "torsion": 0.0})
+ decl = d.get("posture")
+ if decl is not None and (not isinstance(decl, str) or not decl.strip()):
+ errors.append("%s : posture declaree invalide dans pack.json" % k)
+ continue
+ posture = {"principale": decl if decl else f["posture"],
+ "posture_origine": "declaree" if decl else "calculee"}
+ if decl:
+ posture["posture_calculee"] = f["posture"]
+ if f.get("posture_debut") != f.get("posture_fin"):
+ posture["debut"], posture["fin"] = f.get("posture_debut"), f.get("posture_fin")
+ # Hauteur d'assise seulement pour une posture assise (une chute traverse des images "assis").
+ if f.get("hauteur_assise_cm") is not None and posture["principale"] == "assis":
+ posture["hauteur_assise_cm"] = f["hauteur_assise_cm"]
+ dur_p = float(f["duree_s"]) or num(pinfo.get("duree_s"))
+ if dur_p <= 0:
+ errors.append("%s : duree nulle, cadence de planche incalculable" % k)
+ continue
+ animations.append({
+ "cle": k,
+ "appel": {"script": "Actor.animate(%s)" % k, "action": "ANIMATION: %s" % k},
+ "nom": {"fr": d["nom"]["fr"], "en": d["nom"]["en"]},
+ "assets": {"feminin": e["asset_F"], "masculin": e["asset_M"]},
+ "duree_s": round(float(f["duree_s"]), 2),
+ "images": f["images"],
+ "planche": {"fichier": "planches/%s.jpg" % k, "images": pinfo["images"], "taille": 256,
+ "ips": round(pinfo["images"] / dur_p, 1)},
+ "cadence": f["cadence"],
+ "taille_ko": {"feminin": round(tf / 1024), "masculin": round(tm / 1024), "total": round((tf + tm) / 1024)},
+ "taille_cuite": False,
+ "boucle": {"oui": bool(f["boucle"]), "ecart_pct": round(f.get("boucle_ecart_pct") or 0.0, 2)},
+ "deplacement": {"cm": f["deplacement_cm"], "cm_s": f["vitesse_cm_s"], "sens": f["deplacement_sens"]},
+ "posture": posture,
+ "contacts": {"pieds_au_sol_pct": f["pieds_au_sol_pct"], "mains": f["mains"]},
+ "accessoire": d.get("accessoire", ""),
+ "categorie": d["categorie"],
+ "style": d["style"],
+ "usage": {"fr": d["usage"]["fr"], "en": d["usage"]["en"]},
+ "corps": ["feminin", "masculin"],
+ "qualite": {"verdict": "à revoir" if motifs else "accepté", "motifs": motifs,
+ "pire_brut_pct": round(q["brut"], 2), "pire_torsion_deg": round(q["torsion"], 2)},
+ "source": pack.get("source", ""),
+ "licence": pack.get("licence", ""),
+ "credit": pack.get("credit", ""),
+ "version": args.version,
+ "empreinte": sha256_of(files),
+ })
+ thumbs.append((k, thumb, pj))
+ if errors:
+ return None
+ header = {
+ "pack": pack.get("pack", ""), "mod": args.mod, "version": args.version,
+ "genere": datetime.datetime.now().isoformat(timespec="seconds"),
+ "nombre_animations": len(animations), "credit": pack.get("credit", ""),
+ "animations": animations,
+ }
+ return header, thumbs, out
+
+
+# ---------------------------------------------------------------------------- HTML
+
+CSS = """
+:root{--bg:#f6f7f9;--fg:#1d2330;--mut:#5d6677;--card:#fff;--bd:#d9dde5;--acc:#2057c9;--ok:#1d7a3f;--warn:#a85b00;--chip:#eef1f6}
+@media (prefers-color-scheme:dark){:root{--bg:#14171d;--fg:#e6e9ef;--mut:#9aa3b4;--card:#1d222b;--bd:#323a48;--acc:#7aa7ff;--ok:#5fcf8a;--warn:#f0a850;--chip:#262c37}}
+*{box-sizing:border-box}
+body{margin:0;background:var(--bg);color:var(--fg);font:15px/1.45 system-ui,Segoe UI,sans-serif;overflow-wrap:anywhere}
+header{padding:16px;max-width:1200px;margin:0 auto}
+h1{margin:0 0 4px;font-size:1.4rem}
+.sub{color:var(--mut);font-size:.9rem}
+.bar{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
+.bar input,.bar select{font:inherit;padding:6px 8px;border:1px solid var(--bd);border-radius:6px;background:var(--card);color:var(--fg);min-width:0;max-width:100%}
+.bar input{flex:1 1 220px}
+main{max-width:1200px;margin:0 auto;padding:0 16px 24px;display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,300px),1fr));gap:12px}
+.card{background:var(--card);border:1px solid var(--bd);border-radius:10px;overflow:hidden;display:flex;flex-direction:column}
+.card[hidden],.card img[hidden],.card .anim[hidden]{display:none}
+.card img{width:100%;aspect-ratio:1/1;object-fit:contain;background:var(--chip);display:block}
+.card .anim{width:100%;aspect-ratio:1/1;background-color:var(--chip);background-repeat:no-repeat}
+.in{padding:10px 12px;display:flex;flex-direction:column;gap:6px}
+.in h2{margin:0;font-size:1.05rem}
+.key{font-family:Consolas,monospace;background:var(--chip);border:1px solid var(--bd);border-radius:6px;padding:3px 6px;cursor:pointer;text-align:left;color:var(--fg);font-size:.85rem}
+.call{font-family:Consolas,monospace;font-size:.8rem;color:var(--mut)}
+.tags{display:flex;flex-wrap:wrap;gap:4px}
+.tags span{background:var(--chip);border-radius:10px;padding:1px 8px;font-size:.78rem}
+.use{font-size:.88rem}
+.q-ok{color:var(--ok)}.q-rev{color:var(--warn);font-weight:600}
+footer{max-width:1200px;margin:0 auto;padding:0 16px 24px;color:var(--mut);font-size:.8rem}
+#none{display:none;padding:16px;max-width:1200px;margin:0 auto}
+"""
+
+JS = """
+const A=JSON.parse(document.getElementById('data').textContent).animations;
+const $=id=>document.getElementById(id);
+const cap=s=>s.charAt(0).toUpperCase()+s.slice(1);
+function dur(a){return a.duree_s<2?'courte':(a.duree_s>5?'longue':'moyenne')}
+function fill(sel,vals){[...new Set(vals)].sort().forEach(v=>{const o=document.createElement('option');o.value=v;o.textContent=v;sel.appendChild(o)})}
+fill($('fcat'),A.map(a=>a.categorie));fill($('fpos'),A.map(a=>a.posture.principale));
+const SENS={'sur place':'sur place','avant':'avance','arriere':'recule','cote':'de côté'};
+function esc(s){return String(s).replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c]))}
+function card(a){
+ const dp=a.deplacement,p=a.posture;
+ let pos=p.principale;if(p.debut)pos+=' ('+p.debut+' → '+p.fin+')';if(p.hauteur_assise_cm!=null)pos+=', assise '+p.hauteur_assise_cm+' cm';
+ const mv=dp.sens==='sur place'?'sur place':(SENS[dp.sens]||dp.sens)+' '+Math.round(dp.cm)+' cm ('+Math.round(dp.cm_s)+' cm/s)';
+ const q=a.qualite,qc=q.verdict==='accepté'?'q-ok':'q-rev';
+ const el=document.createElement('article');el.className='card';
+ el.innerHTML=' '
+ +''+esc(a.nom.fr)+' / '+esc(a.nom.en)+' '
+ +'
'+esc(a.cle)+' '
+ +'
'+esc(a.appel.script)+' '+esc(a.appel.action)+'
'
+ +'
'+esc(a.categorie)+' '+esc(pos)+' '+a.duree_s.toString().replace('.',',')+' s ('+dur(a)+') '
+ +''+(a.boucle.oui?'boucle':'une fois')+' '+esc(mv)+' '+(a.accessoire?''+esc(a.accessoire)+' ':'')+'
'
+ +'
'+esc(a.usage.fr)+'
'
+ +'
Qualité : '+esc(q.verdict)+(q.motifs.length?' ('+esc(q.motifs.join(' ; '))+')':'')+'
';
+ hover(el,a);
+ const b=el.querySelector('.key');
+ b.onclick=()=>{const done=()=>{b.textContent='copié';setTimeout(()=>b.textContent=a.cle,900)};
+ if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(a.cle).then(done,()=>sel(b))}else sel(b)};
+ return el}
+const reduce=matchMedia('(prefers-reduced-motion: reduce)');
+function hover(el,a){
+ const pl=a.planche,img=el.querySelector('img');let div=null,timer=null,ready=false,want=false,last='mouse';
+ function show(){if(!want||!ready)return;img.hidden=true;div.hidden=false;let i=0;div.style.backgroundPosition='0% 0%';
+ clearInterval(timer);timer=setInterval(()=>{i=(i+1)%pl.images;div.style.backgroundPosition=(i*100/(pl.images-1))+'% 0%'},1000/pl.ips)}
+ function start(){want=true;
+ if(!div){div=document.createElement('div');div.className='anim';div.hidden=true;div.setAttribute('role','img');div.setAttribute('aria-label',a.nom.fr);
+ div.style.backgroundSize=(pl.images*100)+'% 100%';img.parentNode.insertBefore(div,img);
+ const im=new Image();im.onload=()=>{div.style.backgroundImage='url("'+pl.fichier+'")';ready=true;show()};im.src=pl.fichier}
+ else show()}
+ function stop(){want=false;clearInterval(timer);timer=null;if(div)div.hidden=true;img.hidden=false}
+ el.addEventListener('pointerenter',e=>{if(e.pointerType!=='touch'&&!reduce.matches)start()});
+ el.addEventListener('pointerleave',e=>{if(e.pointerType!=='touch')stop()});
+ el.addEventListener('pointerdown',e=>{last=e.pointerType});
+ el.addEventListener('click',e=>{if(!e.target.closest('img,.anim'))return;if(last==='touch'||reduce.matches){want?stop():start()}})}
+function sel(b){const r=document.createRange();r.selectNodeContents(b);const s=getSelection();s.removeAllRanges();s.addRange(r)}
+const main=$('list'),cards=A.map(a=>({a,el:card(a)}));cards.forEach(c=>main.appendChild(c.el));
+function apply(){
+ const t=$('q').value.trim().toLowerCase(),c=$('fcat').value,p=$('fpos').value,d=$('fdur').value;let n=0;
+ cards.forEach(x=>{const a=x.a;
+ const hay=(a.cle+' '+a.nom.fr+' '+a.nom.en+' '+a.usage.fr+' '+a.usage.en+' '+a.accessoire+' '+a.style).toLowerCase();
+ const ok=(!t||hay.includes(t))&&(!c||a.categorie===c)&&(!p||a.posture.principale===p)&&(!d||dur(a)===d);
+ x.el.hidden=!ok;if(ok)n++});
+ $('count').textContent=n+' / '+A.length+' animation(s)';$('none').style.display=n?'none':'block'}
+['q','fcat','fpos','fdur'].forEach(i=>$(i).addEventListener('input',apply));apply();
+"""
+
+
+def render_html(cat):
+ data = json.dumps(cat, ensure_ascii=False).replace("", "<\\/")
+ title = "Animations — %s" % cat["mod"]
+ return """
+
+
+
+
+%(title)s
+
+
+
+
+%(title)s
+Version %(version)s — %(n)d animations — généré le %(date)s. Copiez la clé puis écrivez Actor.animate(clé) dans une scène ou ANIMATION: clé dans une action. Survolez une vignette pour voir le mouvement.
+
+
+Toutes catégories
+Toutes postures
+Toutes durées Courte (< 2 s) Moyenne (2 à 5 s) Longue (> 5 s)
+
+
+
+Aucune animation ne correspond aux filtres.
+
+
+
+
+
+
+""" % {"title": html.escape(title), "css": CSS, "js": JS, "data": data, "version": html.escape(cat["version"]),
+ "n": cat["nombre_animations"], "date": html.escape(cat["genere"][:10]), "credit": html.escape(cat["credit"])}
+
+
+def main():
+ ap = argparse.ArgumentParser(description=__doc__)
+ ap.add_argument("--pack", required=True)
+ ap.add_argument("--pipeline", required=True)
+ ap.add_argument("--mod", required=True)
+ ap.add_argument("--version", required=True)
+ ap.add_argument("--out", required=True)
+ here = os.path.dirname(os.path.abspath(__file__))
+ ap.add_argument("--root", default=os.path.dirname(os.path.dirname(here)), help="racine du projet")
+ ap.add_argument("--mod-dir", default="", help="dossier du mod (defaut : /ModSources/)")
+ ap.add_argument("--planches", default="", help="dossier des planches .jpg/.json (defaut : /Saved/Measurement/planches)")
+ ap.add_argument("--vignettes", default="", help="dossier des vignettes .png (defaut : /Saved/Measurement/vignettes)")
+ args = ap.parse_args()
+
+ errors = []
+ result = None
+ try:
+ result = build(args, errors)
+ except Exception as e: # noqa: BLE001
+ errors.append("erreur inattendue : %r" % (e,))
+ rapport = os.path.join(os.path.abspath(args.pipeline), "catalogue_rapport.json")
+ info = {"date": datetime.datetime.now().isoformat(timespec="seconds"), "mod": args.mod, "version": args.version,
+ "ok": result is not None and not errors, "fiches": len(result[0]["animations"]) if result else 0,
+ "echecs": errors}
+ if result is None or errors:
+ try:
+ os.makedirs(os.path.dirname(rapport), exist_ok=True)
+ with open(rapport, "w", encoding="utf-8", newline="\n") as f:
+ json.dump(info, f, ensure_ascii=False, indent=2)
+ except OSError:
+ pass
+ print("CATALOGUE ECHEC : %d cause(s)" % len(errors), file=sys.stderr)
+ for e in errors:
+ print(" - " + e, file=sys.stderr)
+ return 1
+ cat, thumbs, out = result
+ os.makedirs(os.path.join(out, "vignettes"), exist_ok=True)
+ os.makedirs(os.path.join(out, "planches"), exist_ok=True)
+ for k, src, psrc in thumbs:
+ shutil.copyfile(src, os.path.join(out, "vignettes", k + ".png"))
+ shutil.copyfile(psrc, os.path.join(out, "planches", k + ".jpg"))
+ with open(os.path.join(out, "catalogue.json"), "w", encoding="utf-8", newline="\n") as f:
+ json.dump(cat, f, ensure_ascii=False, indent=2)
+ f.write("\n")
+ with open(os.path.join(out, "catalogue.html"), "w", encoding="utf-8", newline="\n") as f:
+ f.write(render_html(cat))
+ with open(rapport, "w", encoding="utf-8", newline="\n") as f:
+ json.dump(info, f, ensure_ascii=False, indent=2)
+ print("CATALOGUE OK : %d fiche(s) -> %s" % (len(cat["animations"]), out))
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/Tools/anim/DeliverAnims.ps1 b/Tools/anim/DeliverAnims.ps1
index 3d83a8c..0f2be07 100644
--- a/Tools/anim/DeliverAnims.ps1
+++ b/Tools/anim/DeliverAnims.ps1
@@ -13,6 +13,9 @@
dans UnrealEditor-Cmd (journal Saved\Logs\portage\AP5_DeliverAnims_.log), remet le mod en place,
controle la sortie (LIVRAISON copies 2n erreurs 0, n fichiers .uasset par sexe). Code de sortie 1 au moindre ecart.
+ -Catalogue : apres une livraison reussie, lance Tools\anim\Catalogue.py (lot AP6).
+ -CatalogueOnly : saute la copie et ne produit que le catalogue (rapport valide exige), sans lancer Unreal.
+
.EXAMPLE
.\Tools\anim\DeliverAnims.ps1 -DryRun
.\Tools\anim\DeliverAnims.ps1
@@ -23,7 +26,9 @@ param(
[string] $Prefix = 'qua',
[string] $Exclude = 'A_TPose',
[string] $Report = '',
- [switch] $DryRun
+ [switch] $DryRun,
+ [switch] $Catalogue,
+ [switch] $CatalogueOnly
)
$ErrorActionPreference = 'Stop'
@@ -39,6 +44,21 @@ function New-IfMissing([string] $Path, [string] $Text) {
Write-Host " cree : $Path"
}
+# Lot AP6 : catalogue du mod (Tools\anim\Catalogue.py), version lue dans le .lpmod.
+function Invoke-Catalogue {
+ $lpmodPath = Join-Path $Root "ModSources\$Name\Module\$Name.lpmod"
+ if (-not (Test-Path $lpmodPath)) { throw "lpmod introuvable : $lpmodPath" }
+ $line = Select-String -Path $lpmodPath -Pattern '^MODULE_VERSION:\s*(\S+)' | Select-Object -First 1
+ if (-not $line) { throw "MODULE_VERSION absente de $lpmodPath" }
+ $version = $line.Matches[0].Groups[1].Value
+ $pipeline = Split-Path $Report -Parent
+ $out = Join-Path $Root "ModSources\$Name\Module\Animations"
+ Write-Host "Catalogue : version $version -> $out"
+ & python (Join-Path $Root 'Tools\anim\Catalogue.py') --pack (Join-Path $Root 'AnimSources\quaternius\pack.json') `
+ --pipeline $pipeline --mod $Name --version $version --out $out
+ if ($LASTEXITCODE -ne 0) { throw "Catalogue.py a echoue (code $LASTEXITCODE)" }
+}
+
try {
# 1. Verdict des controles qualite.
if (-not (Test-Path $Report)) { throw "rapport.json introuvable : $Report" }
@@ -49,6 +69,11 @@ try {
}
foreach ($r in @($rapport.a_revoir)) { Write-Warning "a revoir : $r" }
+ if ($CatalogueOnly -and -not $DryRun) {
+ Invoke-Catalogue
+ exit 0
+ }
+
# 2. Controles d'environnement.
$running = @(Get-Process UnrealEditor, UnrealEditor-Cmd -ErrorAction SilentlyContinue)
if ($running) { throw "UnrealEditor est ouvert (pid $($running[0].Id)) : le fermer avant la livraison" }
@@ -140,6 +165,7 @@ try {
if ($count -ne $n) { throw "$dir : $count fichier(s) .uasset pour $n cle(s)" }
}
Write-Host "Livraison terminee : $n cle(s), $($n * 2) copies"
+ if ($Catalogue) { Invoke-Catalogue }
exit 0
}
catch {